Project import
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7491d7f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,93 @@
+#
+#    Copyright (c) 2010-2011 Nest, Inc.
+#    All rights reserved.
+#
+#    This document is the property of Nest. It is considered
+#    confidential and proprietary information.
+#
+#    This document may not be reproduced or transmitted in any form,
+#    in whole or in part, without the express written permission of
+#    Nest.
+#
+#    Description:
+#      This file is the makefile for embedded Linux "Swiss Army Knife"
+#      application, BusyBox.
+#
+
+include pre.mak
+
+PackageName		:= busybox
+
+PackageExtension	:= tar.bz2
+PackageSeparator	:= -
+
+PackagePatchArgs	:= -p1
+
+PackageArchive		:= $(PackageName).$(PackageExtension)
+PackageSourceDir	:= $(PackageName)$(PackageSeparator)$(PackageVersion)
+
+PackageBuildMakefile	= $(call GenerateBuildPaths,Makefile)
+
+CleanPaths		+= $(PackageLicenseFile)
+
+all: $(PackageDefaultGoal)
+
+# Generate the package license contents.
+
+$(PackageSourceDir)/LICENSE: source
+
+$(PackageLicenseFile): $(PackageSourceDir)/LICENSE
+	$(copy-result)
+
+# Extract the source from the archive and apply patches, if any.
+
+$(PackageSourceDir): $(PackageArchive) $(PackagePatchPaths)
+	$(expand-and-patch-package)
+
+# Prepare the sources.
+
+.PHONY: source
+source: | $(PackageSourceDir)
+
+# Patch the sources, if necessary.
+
+.PHONY: patch
+patch: source
+
+# BusyBox has no way of explicitly setting CC, LD, OBJCOPY, et al and
+# instead relies on the value of CROSS_COMPILE. Consequently, we have
+# to ensure that 'ToolBinDir' is in 'PATH' so that the kernel build
+# infrastructure can find $(CROSS_COMPILE)gcc, $(CROSS_COMPILE)ld, et
+# al.
+
+configure build stage: PATH := $(PATH):$(ToolBinDir)
+
+# Generate the package build makefile and default configuration based
+# on the build configuration.
+
+$(PackageBuildMakefile): $(CURDIR)/configs/$(BuildConfig)_defconfig | $(PackageSourceDir) $(BuildDirectory)
+	$(Verbose)$(MAKE) $(JOBSFLAG) -C $(PackageSourceDir) O=$(CURDIR)/$(BuildDirectory) $<
+
+# Configure the source for building.
+
+.PHONY: configure
+configure: source $(PackageBuildMakefile)
+
+# Build the source.
+
+.PHONY: build
+build: configure
+	$(Verbose)$(MAKE) $(JOBSFLAG) -C $(BuildDirectory) busybox
+
+# Stage the build to a temporary installation area.
+
+.PHONY: stage
+stage: build | $(ResultDirectory)
+	$(Verbose)$(MAKE) $(JOBSFLAG) -C $(BuildDirectory) INSTALL="$(INSTALL) $(INSTALLFLAGS)" CONFIG_PREFIX=$(ResultDirectory) install
+
+clean:
+	$(Verbose)$(RM) $(RMFLAGS) -r $(PackageSourceDir)
+	$(Verbose)$(RM) $(RMFLAGS) -r $(BuildDirectory)
+	$(Verbose)$(RM) $(RMFLAGS) -r $(ResultDirectory)
+
+include post.mak
diff --git a/busybox-1.19.3/.indent.pro b/busybox-1.19.3/.indent.pro
new file mode 100644
index 0000000..492ecf1
--- /dev/null
+++ b/busybox-1.19.3/.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/busybox-1.19.3/AUTHORS b/busybox-1.19.3/AUTHORS
new file mode 100644
index 0000000..0908fc7
--- /dev/null
+++ b/busybox-1.19.3/AUTHORS
@@ -0,0 +1,176 @@
+List of the authors of code contained in BusyBox.
+
+If you have code in BusyBox, you should be listed here.  If you should be
+listed, or the description of what you have done needs more detail, or is
+incorrect, _please_ let me know.
+
+ -Erik
+
+-----------
+
+Peter Willis <psyphreak@phreaker.net>
+    eject
+
+Emanuele Aina <emanuele.aina@tiscali.it>
+    run-parts
+
+Erik Andersen <andersen@codepoet.org>
+    Tons of new stuff, major rewrite of most of the
+    core apps, tons of new apps as noted in header files.
+    Lots of tedious effort writing these boring docs that
+    nobody is going to actually read.
+
+Laurence Anderson <l.d.anderson@warwick.ac.uk>
+    rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
+
+Jeff Angielski <jeff@theptrgroup.com>
+    ftpput, ftpget
+
+Enrik Berkhan <Enrik.Berkhan@inka.de>
+    setconsole
+
+Jim Bauer <jfbauer@nfr.com>
+    modprobe shell dependency
+
+Edward Betts <edward@debian.org>
+    expr, hostid, logname, whoami
+
+John Beppu <beppu@codepoet.org>
+    du, nslookup, sort
+
+David Brownell <dbrownell@users.sourceforge.net>
+    zcip
+
+Brian Candler <B.Candler@pobox.com>
+    tiny-ls(ls)
+
+Randolph Chung <tausq@debian.org>
+    fbset, ping, hostname
+
+Dave Cinege <dcinege@psychosis.com>
+    more(v2), makedevs, dutmp, modularization, auto links file,
+    various fixes, Linux Router Project maintenance
+
+Jordan Crouse <jordan@cosmicpenguin.net>
+    ipcalc
+
+Magnus Damm <damm@opensource.se>
+    tftp client
+    insmod powerpc support
+
+Larry Doolittle <ldoolitt@recycle.lbl.gov>
+    pristine source directory compilation, lots of patches and fixes.
+
+Glenn Engel <glenne@engel.org>
+    httpd
+
+Gennady Feldman <gfeldman@gena01.com>
+    Sysklogd (single threaded syslogd, IPC Circular buffer support,
+    logread), various fixes.
+
+Robert Griebl <sandman@handhelds.org>
+    modprobe, hwclock, suid/sgid handling, tinylogin integration
+    many bugfixes and enhancements
+
+Karl M. Hegbloom <karlheg@debian.org>
+    cp_mv.c, the test suite, various fixes to utility.c, &c.
+
+Daniel Jacobowitz <dan@debian.org>
+    mktemp.c
+
+Matt Kraai <kraai@alumni.cmu.edu>
+    documentation, bugfixes, test suite
+
+Rob Landley <rob@landley.net>
+    Became busybox maintainer in 2006.
+
+    sed (major rewrite in 2003, and I now maintain the thing)
+    bunzip2 (complete from-scratch rewrite, then mjn3 optimized the result)
+    sort (more or less from scratch rewrite in 2004, I now maintain it)
+    mount (rewrite in 2005, I maintain the new one)
+
+Stephan Linz <linz@li-pro.net>
+    ipcalc, Red Hat equivalence
+
+John Lombardo <john@deltanet.com>
+    tr
+
+Glenn McGrath <glenn.l.mcgrath@gmail.com>
+    Common unarchiving code and unarchiving applets, ifupdown, ftpgetput,
+    nameif, sed, patch, fold, install, uudecode.
+    Various bugfixes, review and apply numerous patches.
+
+Manuel Novoa III <mjn3@codepoet.org>
+    cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
+    mesg, vconfig, nice, renice,
+    make_directory, parse_mode, dirname, mode_string,
+    get_last_path_component, simplify_path, and a number trivial libbb routines
+
+    also bug fixes, partial rewrites, and size optimizations in
+    ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
+    mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
+    interface, dutmp, ifconfig, route
+
+Vladimir Oleynik <dzo@simtreas.ru>
+    cmdedit; bb_mkdep, xargs(current), httpd(current);
+    ports: ash, crond, fdisk (initial, unmaintained now), inetd, stty, traceroute,
+    top;
+    locale, various fixes
+    and irreconcilable critic of everything not perfect.
+
+Bruce Perens <bruce@pixar.com>
+    Original author of BusyBox in 1995, 1996. Some of his code can
+    still be found hiding here and there...
+
+Rodney Radford <rradford@mindspring.com>
+    ipcs, ipcrm
+
+Tim Riker <Tim@Rikers.org>
+    bug fixes, member of fan club
+
+Kent Robotti <robotti@metconnect.com>
+    reset, tons and tons of bug reports and patches.
+
+Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
+    wget - Contributed by permission of Covad Communications
+
+Pavel Roskin <proski@gnu.org>
+    Lots of bugs fixes and patches.
+
+Gyepi Sam <gyepi@praxis-sw.com>
+    Remote logging feature for syslogd
+
+Rob Sullivan <cogito.ergo.cogito@gmail.com>
+    comm
+
+Linus Torvalds
+    mkswap, fsck.minix, mkfs.minix
+
+Mark Whitley <markw@codepoet.org>
+    grep, sed, cut, xargs(previous),
+    style-guide, new-applet-HOWTO, bug fixes, etc.
+
+Charles P. Wright <cpwright@villagenet.com>
+    gzip, mini-netcat(nc)
+
+Enrique Zanardi <ezanardi@ull.es>
+    tarcat (since removed), loadkmap, various fixes, Debian maintenance
+
+Tito Ragusa <farmatito@tiscali.it>
+    devfsd and size optimizations in strings, openvt, chvt, deallocvt, hdparm,
+    fdformat, lsattr, chattr, id and eject.
+
+Paul Fox <pgf@foxharp.boston.ma.us>
+    vi editing mode for ash, various other patches/fixes
+
+Roberto A. Foglietta <me@roberto.foglietta.name>
+    port: dnsd
+
+Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
+    misc
+
+Mike Frysinger <vapier@gentoo.org>
+    initial e2fsprogs, printenv, setarch, sum, misc
+
+Jie Zhang <jie.zhang@analog.com>
+    fixed two bugs in msh and hush (exitcode of killed processes)
diff --git a/busybox-1.19.3/Config.in b/busybox-1.19.3/Config.in
new file mode 100644
index 0000000..1e71812
--- /dev/null
+++ b/busybox-1.19.3/Config.in
@@ -0,0 +1,779 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+mainmenu "BusyBox Configuration"
+
+config HAVE_DOT_CONFIG
+	bool
+	default y
+
+menu "Busybox Settings"
+
+menu "General Configuration"
+
+config DESKTOP
+	bool "Enable options for full-blown desktop systems"
+	default y
+	help
+	  Enable options and features which are not essential.
+	  Select this only if you plan to use busybox on full-blown
+	  desktop machine with common Linux distro, not on an embedded box.
+
+config EXTRA_COMPAT
+	bool "Provide compatible behavior for rare corner cases (bigger code)"
+	default n
+	help
+	  This option makes grep, sed etc handle rare corner cases
+	  (embedded NUL bytes and such). This makes code bigger and uses
+	  some GNU extensions in libc. You probably only need this option
+	  if you plan to run busybox on desktop.
+
+config INCLUDE_SUSv2
+	bool "Enable obsolete features removed before SUSv3"
+	default y
+	help
+	  This option will enable backwards compatibility with SuSv2,
+	  specifically, old-style numeric options ('command -1 <file>')
+	  will be supported in head, tail, and fold. (Note: should
+	  affect renice too.)
+
+config USE_PORTABLE_CODE
+	bool "Avoid using GCC-specific code constructs"
+	default n
+	help
+	  Use this option if you are trying to compile busybox with
+	  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
+	help
+	  There are 3 ways BusyBox can handle buffer allocations:
+	  - Use malloc. This costs code size for the call to xmalloc.
+	  - Put them on stack. For some very small machines with limited stack
+	    space, this can be deadly. For most folks, this works just fine.
+	  - Put them in BSS. This works beautifully for computers with a real
+	    MMU (and OS support), but wastes runtime RAM for uCLinux. This
+	    behavior was the only one available for BusyBox versions 0.48 and
+	    earlier.
+
+config FEATURE_BUFFERS_USE_MALLOC
+	bool "Allocate with Malloc"
+
+config FEATURE_BUFFERS_GO_ON_STACK
+	bool "Allocate on the Stack"
+
+config FEATURE_BUFFERS_GO_IN_BSS
+	bool "Allocate in the .bss section"
+
+endchoice
+
+config SHOW_USAGE
+	bool "Show terse 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.
+
+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
+	  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.
+
+config FEATURE_COMPRESS_USAGE
+	bool "Store applet usage messages in compressed form"
+	default y
+	depends on SHOW_USAGE
+	help
+	  Store usage messages in 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
+	  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 FEATURE_INSTALLER
+	bool "Support --install [-s] to install applet links at runtime"
+	default y
+	help
+	  Enable 'busybox --install [-s]' support. This will allow you to use
+	  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
+	help
+	  Enable this if your system has locale support and you would like
+	  busybox to support locale settings.
+
+config UNICODE_SUPPORT
+	bool "Support Unicode"
+	default y
+	help
+	  This makes various applets aware that one byte is not
+	  one character on screen.
+
+	  Busybox aims to eventually work correctly with Unicode displays.
+	  Any older encodings are not guaranteed to work.
+	  Probably by the time when busybox will be fully Unicode-clean,
+	  other encodings will be mainly of historic interest.
+
+config UNICODE_USING_LOCALE
+	bool "Use libc routines for Unicode (else uses internal ones)"
+	default n
+	depends on UNICODE_SUPPORT && LOCALE_SUPPORT
+	help
+	  With this option on, Unicode support is implemented using libc
+	  routines. Otherwise, internal implementation is used.
+	  Internal implementation is smaller.
+
+config FEATURE_CHECK_UNICODE_IN_ENV
+	bool "Check $LANG environment variable"
+	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"
+
+	  Otherwise, Unicode support will be always enabled and active.
+
+config SUBST_WCHAR
+	int "Character code to substitute unprintable characters with"
+	depends on UNICODE_SUPPORT
+	default 63
+	help
+	  Typical values are 63 for '?' (works with any output device),
+	  30 for ASCII substitute control code,
+	  65533 (0xfffd) for Unicode replacement character.
+
+config LAST_SUPPORTED_WCHAR
+	int "Range of supported Unicode characters"
+	depends on UNICODE_SUPPORT
+	default 767
+	help
+	  Any character with Unicode value bigger than this is assumed
+	  to be non-printable on output device. Many applets replace
+	  such chars with substitution character.
+
+	  The idea is that many valid printable Unicode chars are
+	  nevertheless are not displayed correctly. Think about
+	  combining charachers, double-wide hieroglyphs, obscure
+	  characters in dozens of ancient scripts...
+	  Many terminals, terminal emulators, xterms etc will fail
+	  to handle them correctly. Choose the smallest value
+	  which suits your needs.
+
+	  Typical values are:
+	  126 - ASCII only
+	  767 (0x2ff) - there are no combining chars in [0..767] range
+			(the range includes Latin 1, Latin Ext. A and B),
+			code is ~700 bytes smaller for this case.
+	  4351 (0x10ff) - there are no double-wide chars in [0..4351] range,
+			code is ~300 bytes smaller for this case.
+	  12799 (0x31ff) - nearly all non-ideographic characters are
+			available in [0..12799] range, including
+			East Asian scripts like katakana, hiragana, hangul,
+			bopomofo...
+	  0 - off, any valid printable Unicode character will be printed.
+
+config UNICODE_COMBINING_WCHARS
+	bool "Allow zero-width Unicode characters on output"
+	default n
+	depends on UNICODE_SUPPORT
+	help
+	  With this option off, any Unicode char with width of 0
+	  is substituted on output.
+
+config UNICODE_WIDE_WCHARS
+	bool "Allow wide Unicode characters on output"
+	default n
+	depends on UNICODE_SUPPORT
+	help
+	  With this option off, any Unicode char with width > 1
+	  is substituted on output.
+
+config UNICODE_BIDI_SUPPORT
+	bool "Bidirectional character-aware line input"
+	default n
+	depends on UNICODE_SUPPORT && !UNICODE_USING_LOCALE
+	help
+	  With this option on, right-to-left Unicode characters
+	  are treated differently on input (e.g. cursor movement).
+
+config UNICODE_NEUTRAL_TABLE
+	bool "In bidi input, support non-ASCII neutral chars too"
+	default n
+	depends on UNICODE_BIDI_SUPPORT
+	help
+	  In most cases it's enough to treat only ASCII non-letters
+	  (i.e. punctuation, numbers and space) as characters
+	  with neutral directionality.
+	  With this option on, more extensive (and bigger) table
+	  of neutral chars will be used.
+
+config UNICODE_PRESERVE_BROKEN
+	bool "Make it possible to enter sequences of chars which are not Unicode"
+	default n
+	depends on UNICODE_SUPPORT
+	help
+	  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 '?'.
+
+config LONG_OPTS
+	bool "Support for --long-options"
+	default y
+	help
+	  Enable this if you want busybox applets to use the gnu --long-option
+	  style, in addition to single character -a -b -c style options.
+
+config FEATURE_DEVPTS
+	bool "Use the devpts filesystem for Unix98 PTYs"
+	default y
+	help
+	  Enable if you want BusyBox to use Unix98 PTY support. If enabled,
+	  busybox will use /dev/ptmx for the master side of the pseudoterminal
+	  and /dev/pts/<number> for the slave side. Otherwise, BSD style
+	  /dev/ttyp<number> will be used. To use this option, you should have
+	  devpts mounted.
+
+config FEATURE_CLEAN_UP
+	bool "Clean up all memory before exiting (usually not needed)"
+	default n
+	help
+	  As a size optimization, busybox normally exits without explicitly
+	  freeing dynamically allocated memory or closing files. This saves
+	  space since the OS will clean up for us, but it can confuse debuggers
+	  like valgrind, which report tons of memory and resource leaks.
+
+	  Don't enable this unless you have a really good reason to clean
+	  things up manually.
+
+config FEATURE_UTMP
+	bool "Support utmp file"
+	default y
+	help
+	  The file /var/run/utmp is used to track who is currently logged in.
+	  With this option on, certain applets (getty, login, telnetd etc)
+	  will create and delete entries there.
+	  "who" applet requires this option.
+
+config FEATURE_WTMP
+	bool "Support wtmp file"
+	default y
+	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.
+	  With this option on, certain applets (getty, login, telnetd etc)
+	  will append new entries there.
+	  "last" applet requires this option.
+
+config FEATURE_PIDFILE
+	bool "Support writing pidfiles"
+	default y
+	help
+	  This option makes some applets (e.g. crond, syslogd, inetd) write
+	  a pidfile in /var/run. Some applications rely on them.
+
+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, 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 priviledges 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 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.
+
+	  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
+	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-] [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:
+
+	  [SUID]
+	  su = ssx root.0 # applet su can be run by anyone and runs with
+	                  # euid=0/egid=0
+	  su = ssx        # exactly the same
+
+	  mount = sx- root.disk # applet mount can be run by root and members
+	                        # of group disk (but not anyone else)
+	                        # and runs with euid=0 (egid is not changed)
+
+	  cp = --- # disable applet cp for everyone
+
+	  The file has to be owned by user root, group root and has to be
+	  writeable only by root:
+	        (chown 0.0 /etc/busybox.conf; chmod 600 /etc/busybox.conf)
+	  The busybox executable has to be owned by user root, group
+	  root and has to be setuid root for this to work:
+	        (chown 0.0 /bin/busybox; chmod 4755 /bin/busybox)
+
+	  Robert 'sandman' Griebl has more information here:
+	  <url: http://www.softforge.de/bb/suid.html >.
+
+config FEATURE_SUID_CONFIG_QUIET
+	bool "Suppress warning message if /etc/busybox.conf is not readable"
+	default y
+	depends on FEATURE_SUID_CONFIG
+	help
+	  /etc/busybox.conf should be readable by the user needing the SUID,
+	  check this option to avoid users to be notified about missing
+	  permissions.
+
+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.
+
+	  If you do not have a complete SELinux userland installed, this stuff
+	  will not compile. Go visit
+		http://www.nsa.gov/selinux/index.html
+	  to download the necessary stuff to allow busybox to compile with
+	  this option enabled. Specifially, libselinux 1.28 or better is
+	  directly required by busybox. If the installation is located in a
+	  non-standard directory, provide it by invoking make as follows:
+		CFLAGS=-I<libselinux-include-path> \
+		LDFLAGS=-L<libselinux-lib-path> \
+		make
+
+	  Most people will leave this set to 'N'.
+
+config FEATURE_PREFER_APPLETS
+	bool "exec prefers applets"
+	default n
+	help
+	  This is an experimental option which directs applets about to
+	  call 'exec' to try and find an applicable busybox applet before
+	  searching the PATH. This is typically done by exec'ing
+	  /proc/self/exe.
+	  This may affect shell, find -exec, xargs and similar applets.
+	  They will use applets even if /bin/<applet> -> busybox link
+	  is missing (or is not a link to busybox). However, this causes
+	  problems in chroot jails without mounted /proc and with ps/top
+	  (command name can be shown as 'exe' for applets started this way).
+
+config BUSYBOX_EXEC_PATH
+	string "Path to BusyBox executable"
+	default "/proc/self/exe"
+	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.
+
+# These are auto-selected by other options
+
+config FEATURE_SYSLOG
+	bool #No description makes it a hidden option
+	default n
+	#help
+	#  This option is auto-selected when you select any applet which may
+	#  send its output to syslog. You do not need to select it manually.
+
+config FEATURE_HAVE_RPC
+	bool #No description makes it a hidden option
+	default n
+	#help
+	#  This is automatically selected if any of enabled applets need it.
+	#  You do not need to select it manually.
+
+endmenu
+
+menu 'Build Options'
+
+config STATIC
+	bool "Build BusyBox as a static binary (no shared libs)"
+	default n
+	help
+	  If you want to build a static BusyBox binary, which does not
+	  use or require any shared libraries, then enable this option.
+	  This can cause BusyBox to be considerably larger, so you should
+	  leave this option false unless you have a good reason (i.e.
+	  your target platform does not support shared libraries, or
+	  you are building an initrd which doesn't need anything but
+	  BusyBox, etc).
+
+	  Most people will leave this set to 'N'.
+
+config PIE
+	bool "Build BusyBox as a position independent executable"
+	default n
+	depends on !STATIC
+	help
+	  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
+	bool "Force NOMMU build"
+	default n
+	help
+	  Busybox tries to detect whether architecture it is being
+	  built against supports MMU or not. If this detection fails,
+	  or if you want to build NOMMU version of busybox for testing,
+	  you may force NOMMU build here.
+
+	  Most people will leave this set to 'N'.
+
+# PIE can be made to work with BUILD_LIBBUSYBOX, but currently
+# build system does not support that
+config BUILD_LIBBUSYBOX
+	bool "Build shared libbusybox"
+	default n
+	depends on !FEATURE_PREFER_APPLETS && !PIE && !STATIC
+	help
+	  Build a shared library libbusybox.so.N.N.N which contains all
+	  busybox code.
+
+	  This feature allows every applet to be built as a tiny
+	  separate executable. Enabling it for "one big busybox binary"
+	  approach serves no purpose and increases code size.
+	  You should almost certainly say "no" to this.
+
+### config FEATURE_FULL_LIBBUSYBOX
+###	bool "Feature-complete libbusybox"
+###	default n if !FEATURE_SHARED_BUSYBOX
+###	depends on BUILD_LIBBUSYBOX
+###	help
+###	  Build a libbusybox with the complete feature-set, disregarding
+###	  the actually selected config.
+###
+###	  Normally, libbusybox will only contain the features which are
+###	  used by busybox itself. If you plan to write a separate
+###	  standalone application which uses libbusybox say 'Y'.
+###
+###	  Note: libbusybox is GPL, not LGPL, and exports no stable API that
+###	  might act as a copyright barrier. We can and will modify the
+###	  exported function set between releases (even minor version number
+###	  changes), and happily break out-of-tree features.
+###
+###	  Say 'N' if in doubt.
+
+config FEATURE_INDIVIDUAL
+	bool "Produce a binary for each applet, linked against libbusybox"
+	default y
+	depends on BUILD_LIBBUSYBOX
+	help
+	  If your CPU architecture doesn't allow for sharing text/rodata
+	  sections of running binaries, but allows for runtime dynamic
+	  libraries, this option will allow you to reduce memory footprint
+	  when you have many different applets running at once.
+
+	  If your CPU architecture allows for sharing text/rodata,
+	  having single binary is more optimal.
+
+	  Each applet will be a tiny program, dynamically linked
+	  against libbusybox.so.N.N.N.
+
+	  You need to have a working dynamic linker.
+
+config FEATURE_SHARED_BUSYBOX
+	bool "Produce additional busybox binary linked against libbusybox"
+	default y
+	depends on BUILD_LIBBUSYBOX
+	help
+	  Build busybox, dynamically linked against libbusybox.so.N.N.N.
+
+	  You need to have a working dynamic linker.
+
+### config BUILD_AT_ONCE
+###	bool "Compile all sources at once"
+###	default n
+###	help
+###	  Normally each source-file is compiled with one invocation of
+###	  the compiler.
+###	  If you set this option, all sources are compiled at once.
+###	  This gives the compiler more opportunities to optimize which can
+###	  result in smaller and/or faster binaries.
+###
+###	  Setting this option will consume alot of memory, e.g. if you
+###	  enable all applets with all features, gcc uses more than 300MB
+###	  RAM during compilation of busybox.
+###
+###	  This option is most likely only beneficial for newer compilers
+###	  such as gcc-4.1 and above.
+###
+###	  Say 'N' unless you know what you are doing.
+
+config LFS
+	bool "Build with Large File Support (for accessing files > 2 GB)"
+	default y
+	help
+	  If you want to build BusyBox with large file support, then enable
+	  this option. This will have no effect if your kernel or your C
+	  library lacks large file support for large files. Some of the
+	  programs that can benefit from large file support include dd, gzip,
+	  cp, mount, tar, and many others. If you want to access files larger
+	  than 2 Gigabytes, enable this option. Otherwise, leave it set to 'N'.
+
+config CROSS_COMPILER_PREFIX
+	string "Cross Compiler prefix"
+	default ""
+	help
+	  If you want to build BusyBox with a cross compiler, then you
+	  will need to set this to the cross-compiler prefix, for example,
+	  "i386-uclibc-".
+
+	  Note that CROSS_COMPILE environment variable or
+	  "make CROSS_COMPILE=xxx ..." will override this selection.
+
+	  Native builds leave this empty.
+
+config EXTRA_CFLAGS
+	string "Additional CFLAGS"
+	default ""
+	help
+	  Additional CFLAGS to pass to the compiler verbatim.
+
+endmenu
+
+menu 'Debugging Options'
+
+config DEBUG
+	bool "Build BusyBox with extra Debugging symbols"
+	default n
+	help
+	  Say Y here if you wish to examine BusyBox internals while applets are
+	  running. This increases the size of the binary considerably, and
+	  should only be used when doing development. If you are doing
+	  development and want to debug BusyBox, answer Y.
+
+	  Most people should answer N.
+
+config DEBUG_PESSIMIZE
+	bool "Disable compiler optimizations"
+	default n
+	depends on DEBUG
+	help
+	  The compiler's optimization of source code can eliminate and reorder
+	  code, resulting in an executable that's hard to understand when
+	  stepping through it with a debugger. This switches it off, resulting
+	  in a much bigger executable that more closely matches the source
+	  code.
+
+config WERROR
+	bool "Abort compilation on any warning"
+	default n
+	help
+	  Selecting this will add -Werror to gcc command line.
+
+	  Most people should answer N.
+
+choice
+	prompt "Additional debugging library"
+	default NO_DEBUG_LIB
+	help
+	  Using an additional debugging library will make BusyBox become
+	  considerable larger and will cause it to run more slowly. You
+	  should always leave this option disabled for production use.
+
+	  dmalloc support:
+	  ----------------
+	  This enables compiling with dmalloc ( http://dmalloc.com/ )
+	  which is an excellent public domain mem leak and malloc problem
+	  detector. To enable dmalloc, before running busybox you will
+	  want to properly set your environment, for example:
+	    export DMALLOC_OPTIONS=debug=0x34f47d83,inter=100,log=logfile
+	  The 'debug=' value is generated using the following command
+	    dmalloc -p log-stats -p log-non-free -p log-bad-space \
+	       -p log-elapsed-time -p check-fence -p check-heap \
+	       -p check-lists -p check-blank -p check-funcs -p realloc-copy \
+	       -p allow-free-null
+
+	  Electric-fence support:
+	  -----------------------
+	  This enables compiling with Electric-fence support. Electric
+	  fence is another very useful malloc debugging library which uses
+	  your computer's virtual memory hardware to detect illegal memory
+	  accesses. This support will make BusyBox be considerable larger
+	  and run slower, so you should leave this option disabled unless
+	  you are hunting a hard to find memory problem.
+
+
+config NO_DEBUG_LIB
+	bool "None"
+
+config DMALLOC
+	bool "Dmalloc"
+
+config EFENCE
+	bool "Electric-fence"
+
+endchoice
+
+endmenu
+
+menu 'Installation Options ("make install" behavior)'
+
+choice
+	prompt "What kind of applet links to install"
+	default INSTALL_APPLET_SYMLINKS
+	help
+	  Choose what kind of links to applets are created by "make install".
+
+config INSTALL_APPLET_SYMLINKS
+	bool "as soft-links"
+	help
+	  Install applets as soft-links to the busybox binary. This needs some
+	  free inodes on the filesystem, but might help with filesystem
+	  generators that can't cope with hard-links.
+
+config INSTALL_APPLET_HARDLINKS
+	bool "as hard-links"
+	help
+	  Install applets as hard-links to the busybox binary. This might
+	  count on a filesystem with few inodes.
+
+config INSTALL_APPLET_SCRIPT_WRAPPERS
+	bool "as script wrappers"
+	help
+	  Install applets as script wrappers that call the busybox binary.
+
+config INSTALL_APPLET_DONT
+	bool "not installed"
+	help
+	  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
+
+choice
+	prompt "/bin/sh applet link"
+	default INSTALL_SH_APPLET_SYMLINK
+	depends on INSTALL_APPLET_SCRIPT_WRAPPERS
+	help
+	  Choose how you install /bin/sh applet link.
+
+config INSTALL_SH_APPLET_SYMLINK
+	bool "as soft-link"
+	help
+	  Install /bin/sh applet as soft-link to the busybox binary.
+
+config INSTALL_SH_APPLET_HARDLINK
+	bool "as hard-link"
+	help
+	  Install /bin/sh applet as hard-link to the busybox binary.
+
+config INSTALL_SH_APPLET_SCRIPT_WRAPPER
+	bool "as script wrapper"
+	help
+	  Install /bin/sh applet as script wrapper that calls
+	  the busybox binary.
+
+endchoice
+
+config PREFIX
+	string "BusyBox installation prefix"
+	default "./_install"
+	help
+	  Define your directory to install BusyBox files/subdirs in.
+
+endmenu
+
+source libbb/Config.in
+
+endmenu
+
+comment "Applets"
+
+source archival/Config.in
+source coreutils/Config.in
+source console-tools/Config.in
+source debianutils/Config.in
+source editors/Config.in
+source findutils/Config.in
+source init/Config.in
+source loginutils/Config.in
+source e2fsprogs/Config.in
+source modutils/Config.in
+source util-linux/Config.in
+source miscutils/Config.in
+source networking/Config.in
+source printutils/Config.in
+source mailutils/Config.in
+source procps/Config.in
+source runit/Config.in
+source selinux/Config.in
+source shell/Config.in
+source sysklogd/Config.in
diff --git a/busybox-1.19.3/INSTALL b/busybox-1.19.3/INSTALL
new file mode 100644
index 0000000..f93e5fb
--- /dev/null
+++ b/busybox-1.19.3/INSTALL
@@ -0,0 +1,139 @@
+Building:
+=========
+
+The BusyBox build process is similar to the Linux kernel build:
+
+  make menuconfig     # This creates a file called ".config"
+  make                # This creates the "busybox" executable
+  make install        # or make CONFIG_PREFIX=/path/from/root install
+
+The full list of configuration and install options is available by typing:
+
+  make help
+
+Quick Start:
+============
+
+The easy way to try out BusyBox for the first time, without having to install
+it, is to enable all features and then use "standalone shell" mode with a
+blank command $PATH.
+
+To enable all features, use "make defconfig", which produces the largest
+general-purpose configuration.  It's allyesconfig minus debugging options,
+optional packaging choices, and a few special-purpose features requiring
+extra configuration to use.  Then enable "standalone shell" feature:
+
+  make defconfig
+  make menuconfig
+  # select Busybox Settings
+  #   then General Configuration
+  #     then exec prefers applets
+  #   exit back to top level menu
+  #   select Shells
+  #     then Standalone shell
+  #   exit back to top level menu
+  # exit and save new configuration
+  #   OR
+  # use these commands to modify .config directly:
+  sed -e 's/.*FEATURE_PREFER_APPLETS.*/CONFIG_FEATURE_PREFER_APPLETS=y/' -i .config
+  sed -e 's/.*FEATURE_SH_STANDALONE.*/CONFIG_FEATURE_SH_STANDALONE=y/' -i .config
+  make
+  PATH= ./busybox ash
+
+Standalone shell mode causes busybox's built-in command shell to run
+any built-in busybox applets directly, without looking for external
+programs by that name.  Supplying an empty command path (as above) means
+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
+to the location of your busybox binary, usually /bin/busybox.
+
+Configuring Busybox:
+====================
+
+Busybox is optimized for size, but enabling the full set of functionality
+still results in a fairly large executable -- more than 1 megabyte when
+statically linked.  To save space, busybox can be configured with only the
+set of applets needed for each environment.  The minimal configuration, with
+all applets disabled, produces a 4k executable.  (It's useless, but very small.)
+
+The manual configurator "make menuconfig" modifies the existing configuration.
+(For systems without ncurses, try "make config" instead.) The two most
+interesting starting configurations are "make allnoconfig" (to start with
+everything disabled and add just what you need), and "make defconfig" (to
+start with everything enabled and remove what you don't need).  If menuconfig
+is run without an existing configuration, make defconfig will run first to
+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).  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
+busybox up to date.
+
+Installing Busybox:
+===================
+
+Busybox is a single executable that can behave like many different commands,
+and BusyBox uses the name it was invoked under to determine the desired
+behavior.  (Try "mv busybox ls" and then "./ls -l".)
+
+Installing busybox consists of creating symlinks (or hardlinks) to the busybox
+binary for each applet enabled in busybox, and making sure these symlinks are
+in the shell's command $PATH.  Running "make install" creates these symlinks,
+or "make install-hardlinks" creates hardlinks instead (useful on systems with
+a limited number of inodes).  This install process uses the file
+"busybox.links" (created by make), which contains the list of enabled applets
+and the path at which to install them.
+
+Installing links to busybox is not always necessary.  The special applet name
+"busybox" (or with any optional suffix, such as "busybox-static") uses the
+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 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).
+
+If you enabled the busybox shared library feature (libbusybox.so) and want
+to run tests without installing, set your LD_LIBRARY_PATH accordingly when
+running the executable:
+
+  LD_LIBRARY_PATH=`pwd` ./busybox
+
+Building out-of-tree:
+=====================
+
+By default, the BusyBox build puts its temporary files in the source tree.
+Building from a read-only source tree, or building multiple configurations from
+the same source directory, requires the ability to put the temporary files
+somewhere else.
+
+To build out of tree, cd to an empty directory and configure busybox from there:
+
+  make KBUILD_SRC=/path/to/source -f /path/to/source/Makefile defconfig
+  make
+  make install
+
+Alternately, use the O=$BUILDPATH option (with an absolute path) during the
+configuration step, as in:
+
+  make O=/some/empty/directory allyesconfig
+  cd /some/empty/directory
+  make
+  make CONFIG_PREFIX=. install
+
+More Information:
+=================
+
+Se also the busybox FAQ, under the questions "How can I get started using
+BusyBox" and "How do I build a BusyBox-based system?"  The BusyBox FAQ is
+available from http://www.busybox.net/FAQ.html
diff --git a/busybox-1.19.3/LICENSE b/busybox-1.19.3/LICENSE
new file mode 100644
index 0000000..6f50a71
--- /dev/null
+++ b/busybox-1.19.3/LICENSE
@@ -0,0 +1,348 @@
+--- A note on GPL versions
+
+BusyBox is distributed under version 2 of the General Public License (included
+in its entirety, below).  Version 2 is the only version of this license which
+this version of BusyBox (or modified versions derived from this one) may be
+distributed under.
+
+------------------------------------------------------------------------
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 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.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) 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; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, 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 executable.  However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+  5. 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 Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+  7. 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 Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  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 program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+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.
diff --git a/busybox-1.19.3/Makefile b/busybox-1.19.3/Makefile
new file mode 100644
index 0000000..9648c9c
--- /dev/null
+++ b/busybox-1.19.3/Makefile
@@ -0,0 +1,1341 @@
+VERSION = 1
+PATCHLEVEL = 19
+SUBLEVEL = 3
+EXTRAVERSION =
+NAME = Unnamed
+
+# *DOCUMENTATION*
+# To see a list of typical targets execute "make help"
+# More info can be located in ./README
+# Comments in this file are targeted only to the developer, do not
+# expect to learn how to build the kernel reading this file.
+
+# Do not print "Entering directory ..."
+MAKEFLAGS += --no-print-directory
+
+# We are using a recursive build, so we need to do a little thinking
+# to get the ordering right.
+#
+# Most importantly: sub-Makefiles should only ever modify files in
+# their own directory. If in some directory we have a dependency on
+# a file in another dir (which doesn't happen often, but it's often
+# unavoidable when linking the built-in.o targets which finally
+# turn into busybox), we will call a sub make in that other dir, and
+# after that we are sure that everything which is in that other dir
+# is now up to date.
+#
+# The only cases where we need to modify files which have global
+# effects are thus separated out and done before the recursive
+# descending is started. They are now explicitly listed as the
+# prepare rule.
+
+# To put more focus on warnings, be less verbose as default
+# Use 'make V=1' to see the full commands
+
+ifdef V
+  ifeq ("$(origin V)", "command line")
+    KBUILD_VERBOSE = $(V)
+  endif
+endif
+ifndef KBUILD_VERBOSE
+  KBUILD_VERBOSE = 0
+endif
+
+# Call sparse as part of compilation of C files
+# Use 'make C=1' to enable sparse checking
+
+ifdef C
+  ifeq ("$(origin C)", "command line")
+    KBUILD_CHECKSRC = $(C)
+  endif
+endif
+ifndef KBUILD_CHECKSRC
+  KBUILD_CHECKSRC = 0
+endif
+
+# Use make M=dir to specify directory of external module to build
+# Old syntax make ... SUBDIRS=$PWD is still supported
+# Setting the environment variable KBUILD_EXTMOD take precedence
+ifdef SUBDIRS
+  KBUILD_EXTMOD ?= $(SUBDIRS)
+endif
+ifdef M
+  ifeq ("$(origin M)", "command line")
+    KBUILD_EXTMOD := $(M)
+  endif
+endif
+
+
+# kbuild supports saving output files in a separate directory.
+# To locate output files in a separate directory two syntaxes are supported.
+# In both cases the working directory must be the root of the kernel src.
+# 1) O=
+# Use "make O=dir/to/store/output/files/"
+#
+# 2) Set KBUILD_OUTPUT
+# Set the environment variable KBUILD_OUTPUT to point to the directory
+# where the output files shall be placed.
+# export KBUILD_OUTPUT=dir/to/store/output/files/
+# make
+#
+# The O= assignment takes precedence over the KBUILD_OUTPUT environment
+# variable.
+
+
+# KBUILD_SRC is set on invocation of make in OBJ directory
+# KBUILD_SRC is not intended to be used by the regular user (for now)
+ifeq ($(KBUILD_SRC),)
+
+# OK, Make called in directory where kernel src resides
+# Do we want to locate output files in a separate directory?
+ifdef O
+  ifeq ("$(origin O)", "command line")
+    KBUILD_OUTPUT := $(O)
+  endif
+endif
+
+# That's our default target when none is given on the command line
+PHONY := _all
+_all:
+
+ifneq ($(KBUILD_OUTPUT),)
+# Invoke a second make in the output directory, passing relevant variables
+# check that the output directory actually exists
+saved-output := $(KBUILD_OUTPUT)
+KBUILD_OUTPUT := $(shell cd $(KBUILD_OUTPUT) && /bin/pwd)
+$(if $(KBUILD_OUTPUT),, \
+     $(error output directory "$(saved-output)" does not exist))
+
+PHONY += $(MAKECMDGOALS)
+
+$(filter-out _all,$(MAKECMDGOALS)) _all:
+	$(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \
+	KBUILD_SRC=$(CURDIR) \
+	KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile $@
+
+# Leave processing to above invocation of make
+skip-makefile := 1
+endif # ifneq ($(KBUILD_OUTPUT),)
+endif # ifeq ($(KBUILD_SRC),)
+
+# We process the rest of the Makefile if this is the final invocation of make
+ifeq ($(skip-makefile),)
+
+# If building an external module we do not care about the all: rule
+# but instead _all depend on modules
+PHONY += all
+ifeq ($(KBUILD_EXTMOD),)
+_all: all
+else
+_all: modules
+endif
+
+srctree		:= $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
+TOPDIR		:= $(srctree)
+# FIXME - TOPDIR is obsolete, use srctree/objtree
+objtree		:= $(CURDIR)
+src		:= $(srctree)
+obj		:= $(objtree)
+
+VPATH		:= $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
+
+export srctree objtree VPATH TOPDIR
+
+
+# Cross compiling and selecting different set of gcc/bin-utils
+# ---------------------------------------------------------------------------
+#
+# When performing cross compilation for other architectures ARCH shall be set
+# to the target architecture. (See arch/* for the possibilities).
+# ARCH can be set during invocation of make:
+# make ARCH=ia64
+# Another way is to have ARCH set in the environment.
+# The default ARCH is the host where make is executed.
+
+# CROSS_COMPILE specify the prefix used for all executables used
+# during compilation. Only gcc and related bin-utils executables
+# are prefixed with $(CROSS_COMPILE).
+# CROSS_COMPILE can be set on the command line
+# make CROSS_COMPILE=ia64-linux-
+# Alternatively CROSS_COMPILE can be set in the environment.
+# Default value for CROSS_COMPILE is not to prefix executables
+# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
+
+CROSS_COMPILE ?=
+# bbox: we may have CONFIG_CROSS_COMPILER_PREFIX in .config,
+# and it has not been included yet... thus using an awkward syntax.
+ifeq ($(CROSS_COMPILE),)
+CROSS_COMPILE := $(shell grep ^CONFIG_CROSS_COMPILER_PREFIX .config 2>/dev/null)
+CROSS_COMPILE := $(subst CONFIG_CROSS_COMPILER_PREFIX=,,$(CROSS_COMPILE))
+CROSS_COMPILE := $(subst ",,$(CROSS_COMPILE))
+#")
+endif
+
+# SUBARCH tells the usermode build what the underlying arch is.  That is set
+# first, and if a usermode build is happening, the "ARCH=um" on the command
+# line overrides the setting of ARCH below.  If a native build is happening,
+# then ARCH is assigned, getting whatever value it gets normally, and
+# SUBARCH is subsequently ignored.
+
+ifneq ($(CROSS_COMPILE),)
+SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1)
+else
+SUBARCH := $(shell uname -m)
+endif
+SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
+					 -e s/arm.*/arm/ -e s/sa110/arm/ \
+					 -e s/s390x/s390/ -e s/parisc64/parisc/ \
+					 -e s/ppc.*/powerpc/ -e s/mips.*/mips/ )
+
+ARCH ?= $(SUBARCH)
+
+# Architecture as present in compile.h
+UTS_MACHINE := $(ARCH)
+
+# SHELL used by kbuild
+CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
+	  else if [ -x /bin/bash ]; then echo /bin/bash; \
+	  else echo sh; fi ; fi)
+
+# 	Decide whether to build built-in, modular, or both.
+#	Normally, just do built-in.
+
+KBUILD_MODULES :=
+KBUILD_BUILTIN := 1
+
+#	If we have only "make modules", don't compile built-in objects.
+#	When we're building modules with modversions, we need to consider
+#	the built-in objects during the descend as well, in order to
+#	make sure the checksums are uptodate before we record them.
+
+ifeq ($(MAKECMDGOALS),modules)
+  KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
+endif
+
+#	If we have "make <whatever> modules", compile modules
+#	in addition to whatever we do anyway.
+#	Just "make" or "make all" shall build modules as well
+
+ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
+  KBUILD_MODULES := 1
+endif
+
+ifeq ($(MAKECMDGOALS),)
+  KBUILD_MODULES := 1
+endif
+
+export KBUILD_MODULES KBUILD_BUILTIN
+export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD
+
+# Beautify output
+# ---------------------------------------------------------------------------
+#
+# Normally, we echo the whole command before executing it. By making
+# that echo $($(quiet)$(cmd)), we now have the possibility to set
+# $(quiet) to choose other forms of output instead, e.g.
+#
+#         quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@
+#         cmd_cc_o_c       = $(CC) $(c_flags) -c -o $@ $<
+#
+# If $(quiet) is empty, the whole command will be printed.
+# If it is set to "quiet_", only the short version will be printed.
+# If it is set to "silent_", nothing wil be printed at all, since
+# the variable $(silent_cmd_cc_o_c) doesn't exist.
+#
+# A simple variant is to prefix commands with $(Q) - that's useful
+# for commands that shall be hidden in non-verbose mode.
+#
+#	$(Q)ln $@ :<
+#
+# If KBUILD_VERBOSE equals 0 then the above command will be hidden.
+# If KBUILD_VERBOSE equals 1 then the above command is displayed.
+
+ifeq ($(KBUILD_VERBOSE),1)
+  quiet =
+  Q =
+else
+  quiet=quiet_
+  Q = @
+endif
+
+# If the user is running make -s (silent mode), suppress echoing of
+# commands
+
+ifneq ($(findstring s,$(MAKEFLAGS)),)
+  quiet=silent_
+endif
+
+export quiet Q KBUILD_VERBOSE
+
+
+# Look for make include files relative to root of kernel src
+MAKEFLAGS += --include-dir=$(srctree)
+
+HOSTCC  	= gcc
+HOSTCXX  	= g++
+HOSTCFLAGS	:=
+HOSTCXXFLAGS	:=
+# We need some generic definitions
+include $(srctree)/scripts/Kbuild.include
+
+HOSTCFLAGS	+= $(call hostcc-option,-Wall -Wstrict-prototypes -O2 -fomit-frame-pointer,)
+HOSTCXXFLAGS	+= -O2
+
+# For maximum performance (+ possibly random breakage, uncomment
+# the following)
+
+MAKEFLAGS += -rR
+
+# Make variables (CC, etc...)
+
+AS		= $(CROSS_COMPILE)as
+CC		= $(CROSS_COMPILE)gcc
+LD		= $(CC) -nostdlib
+CPP		= $(CC) -E
+AR		= $(CROSS_COMPILE)ar
+NM		= $(CROSS_COMPILE)nm
+STRIP		= $(CROSS_COMPILE)strip
+OBJCOPY		= $(CROSS_COMPILE)objcopy
+OBJDUMP		= $(CROSS_COMPILE)objdump
+AWK		= awk
+GENKSYMS	= scripts/genksyms/genksyms
+DEPMOD		= /sbin/depmod
+KALLSYMS	= scripts/kallsyms
+PERL		= perl
+CHECK		= sparse
+
+CHECKFLAGS     := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF)
+MODFLAGS	= -DMODULE
+CFLAGS_MODULE   = $(MODFLAGS)
+AFLAGS_MODULE   = $(MODFLAGS)
+LDFLAGS_MODULE  = -r
+CFLAGS_KERNEL	=
+AFLAGS_KERNEL	=
+
+
+# Use LINUXINCLUDE when you must reference the include/ directory.
+# Needed to be compatible with the O= option
+CFLAGS		:= $(CFLAGS)
+# Added only to final link stage of busybox binary
+CFLAGS_busybox	:= $(CFLAGS_busybox)
+CPPFLAGS	:= $(CPPFLAGS)
+AFLAGS		:= $(AFLAGS)
+LDFLAGS		:= $(LDFLAGS)
+LDLIBS		:=
+
+# Read KERNELRELEASE from .kernelrelease (if it exists)
+KERNELRELEASE = $(shell cat .kernelrelease 2> /dev/null)
+KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+
+export	VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \
+	ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
+	CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \
+	HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
+
+export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
+export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE
+export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
+export FLTFLAGS
+
+# When compiling out-of-tree modules, put MODVERDIR in the module
+# tree rather than in the kernel tree. The kernel tree might
+# even be read-only.
+export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions
+
+# Files to ignore in find ... statements
+
+RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS -o -name .pc -o -name .hg -o -name .git \) -prune -o
+export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn --exclude CVS --exclude .pc --exclude .hg --exclude .git
+
+# ===========================================================================
+# Rules shared between *config targets and build targets
+
+# Basic helpers built in scripts/
+PHONY += scripts_basic
+scripts_basic:
+	$(Q)$(MAKE) $(build)=scripts/basic
+
+# To avoid any implicit rule to kick in, define an empty command.
+scripts/basic/%: scripts_basic ;
+
+# This target generates Kbuild's and Config.in's from *.c files
+PHONY += gen_build_files
+gen_build_files: $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c)
+	$(Q)$(srctree)/scripts/gen_build_files.sh $(srctree) $(objtree)
+
+# bbox: we have helpers in applets/
+# we depend on scripts_basic, since scripts/basic/fixdep
+# must be built before any other host prog
+PHONY += applets_dir
+applets_dir: scripts_basic gen_build_files
+	$(Q)$(MAKE) $(build)=applets
+
+applets/%: applets_dir ;
+
+PHONY += outputmakefile
+# outputmakefile generates a Makefile in the output directory, if using a
+# separate output directory. This allows convenient use of make in the
+# output directory.
+outputmakefile:
+ifneq ($(KBUILD_SRC),)
+	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
+	    $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
+endif
+
+# To make sure we do not include .config for any of the *config targets
+# catch them early, and hand them over to scripts/kconfig/Makefile
+# It is allowed to specify more targets when calling make, including
+# mixing *config targets and build targets.
+# For example 'make oldconfig all'.
+# Detect when mixed targets is specified, and make a second invocation
+# of make so .config is not included in this case either (for *config).
+
+no-dot-config-targets := clean mrproper distclean \
+			 cscope TAGS tags help %docs
+#bbox# check% is removed from above
+
+config-targets := 0
+mixed-targets  := 0
+dot-config     := 1
+
+ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
+	ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
+		dot-config := 0
+	endif
+endif
+
+ifeq ($(KBUILD_EXTMOD),)
+        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
+                config-targets := 1
+                ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
+                        mixed-targets := 1
+                endif
+        endif
+endif
+
+ifeq ($(mixed-targets),1)
+# ===========================================================================
+# We're called with mixed targets (*config and build targets).
+# Handle them one by one.
+
+%:: FORCE
+	$(Q)$(MAKE) -C $(srctree) KBUILD_SRC= $@
+
+else
+ifeq ($(config-targets),1)
+# ===========================================================================
+# *config targets only - make sure prerequisites are updated, and descend
+# in scripts/kconfig to make the *config target
+
+# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
+# KBUILD_DEFCONFIG may point out an alternative default configuration
+# used for 'make defconfig'
+-include $(srctree)/arch/$(ARCH)/Makefile
+export KBUILD_DEFCONFIG
+
+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
+
+else
+# ===========================================================================
+# Build targets only - this includes busybox, arch specific targets, clean
+# targets and others. In general all targets except *config targets.
+
+ifeq ($(KBUILD_EXTMOD),)
+# Additional helpers built in scripts/
+# Carefully list dependencies so we do not try to build scripts twice
+# in parallel
+PHONY += scripts
+scripts: gen_build_files scripts_basic include/config/MARKER
+	$(Q)$(MAKE) $(build)=$(@)
+
+scripts_basic: include/autoconf.h
+
+# Objects we will link into busybox / subdirs we need to visit
+core-y		:= \
+		applets/ \
+
+libs-y		:= \
+		archival/ \
+		archival/libarchive/ \
+		console-tools/ \
+		coreutils/ \
+		coreutils/libcoreutils/ \
+		debianutils/ \
+		e2fsprogs/ \
+		editors/ \
+		findutils/ \
+		init/ \
+		libbb/ \
+		libpwdgrp/ \
+		loginutils/ \
+		mailutils/ \
+		miscutils/ \
+		modutils/ \
+		networking/ \
+		networking/libiproute/ \
+		networking/udhcp/ \
+		printutils/ \
+		procps/ \
+		runit/ \
+		selinux/ \
+		shell/ \
+		sysklogd/ \
+		util-linux/ \
+		util-linux/volume_id/ \
+
+endif # KBUILD_EXTMOD
+
+ifeq ($(dot-config),1)
+# In this section, we need .config
+
+# Read in dependencies to all Kconfig* files, make sure to run
+# oldconfig if changes are detected.
+-include .kconfig.d
+
+-include .config
+
+# If .config needs to be updated, it will be done via the dependency
+# that autoconf has on .config.
+# To avoid any implicit rule to kick in, define an empty command
+.config .kconfig.d: ;
+
+# Now we can define CFLAGS etc according to .config
+include $(srctree)/Makefile.flags
+
+# If .config is newer than include/autoconf.h, someone tinkered
+# with it and forgot to run make oldconfig.
+# If kconfig.d is missing then we are probarly in a cleaned tree so
+# we execute the config step to be sure to catch updated Kconfig files
+include/autoconf.h: .kconfig.d .config $(wildcard $(srctree)/*/*.c) $(wildcard $(srctree)/*/*/*.c) | gen_build_files
+	$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig
+
+include/usage.h: gen_build_files
+
+else
+# Dummy target needed, because used as prerequisite
+include/autoconf.h: ;
+endif
+
+# The all: target is the default when no target is given on the
+# command line.
+# This allow a user to issue only 'make' to build a kernel including modules
+# Defaults busybox but it is usually overridden in the arch makefile
+all: busybox doc
+
+-include $(srctree)/arch/$(ARCH)/Makefile
+
+# arch Makefile may override CC so keep this after arch Makefile is included
+#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
+CHECKFLAGS += $(NOSTDINC_FLAGS)
+
+# Default kernel image to build when no specific target is given.
+# KBUILD_IMAGE may be overruled on the commandline or
+# set in the environment
+# Also any assignments in arch/$(ARCH)/Makefile take precedence over
+# this default value
+export KBUILD_IMAGE ?= busybox
+
+#
+# INSTALL_PATH specifies where to place the updated kernel and system map
+# images. Default is /boot, but you can set it to other values
+export	INSTALL_PATH ?= /boot
+
+#
+# INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory
+# relocations required by build roots.  This is not defined in the
+# makefile but the arguement can be passed to make if needed.
+#
+
+MODLIB	= $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
+export MODLIB
+
+
+ifeq ($(KBUILD_EXTMOD),)
+busybox-dirs	:= $(patsubst %/,%,$(filter %/, $(core-y) $(core-m) $(libs-y) $(libs-m)))
+
+busybox-alldirs	:= $(sort $(busybox-dirs) $(patsubst %/,%,$(filter %/, \
+		     $(core-n) $(core-) $(libs-n) $(libs-) \
+		)))
+
+core-y		:= $(patsubst %/, %/built-in.o, $(core-y))
+libs-y1		:= $(patsubst %/, %/lib.a, $(libs-y))
+libs-y2		:= $(patsubst %/, %/built-in.o, $(libs-y))
+libs-y		:= $(libs-y1) $(libs-y2)
+
+# Build busybox
+# ---------------------------------------------------------------------------
+# busybox is build from the objects selected by $(busybox-init) and
+# $(busybox-main). Most are built-in.o files from top-level directories
+# in the kernel tree, others are specified in arch/$(ARCH)Makefile.
+# Ordering when linking is important, and $(busybox-init) must be first.
+#
+# busybox
+#   ^
+#   |
+#   +-< $(busybox-init)
+#   |   +--< init/version.o + more
+#   |
+#   +--< $(busybox-main)
+#   |    +--< driver/built-in.o mm/built-in.o + more
+#   |
+#   +-< kallsyms.o (see description in CONFIG_KALLSYMS section)
+#
+# busybox version (uname -v) cannot be updated during normal
+# descending-into-subdirs phase since we do not yet know if we need to
+# update busybox.
+# Therefore this step is delayed until just before final link of busybox -
+# except in the kallsyms case where it is done just before adding the
+# symbols to the kernel.
+#
+# System.map is generated to document addresses of all kernel symbols
+
+busybox-all  := $(core-y) $(libs-y)
+
+# Rule to link busybox - also used during CONFIG_KALLSYMS
+# May be overridden by arch/$(ARCH)/Makefile
+quiet_cmd_busybox__ ?= LINK    $@
+      cmd_busybox__ ?= $(srctree)/scripts/trylink \
+      "$@" \
+      "$(CC)" \
+      "$(CFLAGS) $(CFLAGS_busybox)" \
+      "$(LDFLAGS) $(EXTRA_LDFLAGS)" \
+      "$(core-y)" \
+      "$(libs-y)" \
+      "$(LDLIBS)"
+
+# Generate System.map
+quiet_cmd_sysmap = SYSMAP
+      cmd_sysmap = $(CONFIG_SHELL) $(srctree)/scripts/mksysmap
+
+# Link of busybox
+# If CONFIG_KALLSYMS is set .version is already updated
+# Generate System.map and verify that the content is consistent
+# Use + in front of the busybox_version rule to silent warning with make -j2
+# First command is ':' to allow us to use + in front of the rule
+define rule_busybox__
+	:
+	$(call cmd,busybox__)
+	$(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd
+endef
+
+
+ifdef CONFIG_KALLSYMS
+# Generate section listing all symbols and add it into busybox $(kallsyms.o)
+# It's a three stage process:
+# o .tmp_busybox1 has all symbols and sections, but __kallsyms is
+#   empty
+#   Running kallsyms on that gives us .tmp_kallsyms1.o with
+#   the right size - busybox version (uname -v) is updated during this step
+# o .tmp_busybox2 now has a __kallsyms section of the right size,
+#   but due to the added section, some addresses have shifted.
+#   From here, we generate a correct .tmp_kallsyms2.o
+# o The correct .tmp_kallsyms2.o is linked into the final busybox.
+# o Verify that the System.map from busybox matches the map from
+#   .tmp_busybox2, just in case we did not generate kallsyms correctly.
+# o If CONFIG_KALLSYMS_EXTRA_PASS is set, do an extra pass using
+#   .tmp_busybox3 and .tmp_kallsyms3.o.  This is only meant as a
+#   temporary bypass to allow the kernel to be built while the
+#   maintainers work out what went wrong with kallsyms.
+
+ifdef CONFIG_KALLSYMS_EXTRA_PASS
+last_kallsyms := 3
+else
+last_kallsyms := 2
+endif
+
+kallsyms.o := .tmp_kallsyms$(last_kallsyms).o
+
+define verify_kallsyms
+	$(Q)$(if $($(quiet)cmd_sysmap),                       \
+	  echo '  $($(quiet)cmd_sysmap) .tmp_System.map' &&)  \
+	  $(cmd_sysmap) .tmp_busybox$(last_kallsyms) .tmp_System.map
+	$(Q)cmp -s System.map .tmp_System.map ||              \
+		(echo Inconsistent kallsyms data;             \
+		 echo Try setting CONFIG_KALLSYMS_EXTRA_PASS; \
+		 rm .tmp_kallsyms* ; /bin/false )
+endef
+
+# Update busybox version before link
+# Use + in front of this rule to silent warning about make -j1
+# First command is ':' to allow us to use + in front of this rule
+cmd_ksym_ld = $(cmd_busybox__)
+define rule_ksym_ld
+	:
+	+$(call cmd,busybox_version)
+	$(call cmd,busybox__)
+	$(Q)echo 'cmd_$@ := $(cmd_busybox__)' > $(@D)/.$(@F).cmd
+endef
+
+# Generate .S file with all kernel symbols
+quiet_cmd_kallsyms = KSYM    $@
+      cmd_kallsyms = $(NM) -n $< | $(KALLSYMS) \
+                     $(if $(CONFIG_KALLSYMS_ALL),--all-symbols) > $@
+
+.tmp_kallsyms1.o .tmp_kallsyms2.o .tmp_kallsyms3.o: %.o: %.S scripts FORCE
+	$(call if_changed_dep,as_o_S)
+
+.tmp_kallsyms%.S: .tmp_busybox% $(KALLSYMS)
+	$(call cmd,kallsyms)
+
+# .tmp_busybox1 must be complete except kallsyms, so update busybox version
+.tmp_busybox1: $(busybox-lds) $(busybox-all) FORCE
+	$(call if_changed_rule,ksym_ld)
+
+.tmp_busybox2: $(busybox-lds) $(busybox-all) .tmp_kallsyms1.o FORCE
+	$(call if_changed,busybox__)
+
+.tmp_busybox3: $(busybox-lds) $(busybox-all) .tmp_kallsyms2.o FORCE
+	$(call if_changed,busybox__)
+
+# Needs to visit scripts/ before $(KALLSYMS) can be used.
+$(KALLSYMS): scripts ;
+
+# Generate some data for debugging strange kallsyms problems
+debug_kallsyms: .tmp_map$(last_kallsyms)
+
+.tmp_map%: .tmp_busybox% FORCE
+	($(OBJDUMP) -h $< | $(AWK) '/^ +[0-9]/{print $$4 " 0 " $$2}'; $(NM) $<) | sort > $@
+
+.tmp_map3: .tmp_map2
+
+.tmp_map2: .tmp_map1
+
+endif # ifdef CONFIG_KALLSYMS
+
+# busybox image - including updated kernel symbols
+busybox_unstripped: $(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) ;
+
+# Handle descending into subdirectories listed in $(busybox-dirs)
+# Preset locale variables to speed up the build process. Limit locale
+# tweaks to this spot to avoid wrong language settings when running
+# make menuconfig etc.
+# Error messages still appears in the original language
+
+PHONY += $(busybox-dirs)
+$(busybox-dirs): prepare scripts
+	$(Q)$(MAKE) $(build)=$@
+
+# Build the kernel release string
+# The KERNELRELEASE is stored in a file named .kernelrelease
+# to be used when executing for example make install or make modules_install
+#
+# Take the contents of any files called localversion* and the config
+# variable CONFIG_LOCALVERSION and append them to KERNELRELEASE.
+# LOCALVERSION from the command line override all of this
+
+nullstring :=
+space      := $(nullstring) # end of line
+
+___localver = $(objtree)/localversion* $(srctree)/localversion*
+__localver  = $(sort $(wildcard $(___localver)))
+# skip backup files (containing '~')
+_localver = $(foreach f, $(__localver), $(if $(findstring ~, $(f)),,$(f)))
+
+localver = $(subst $(space),, \
+	   $(shell cat /dev/null $(_localver)) \
+	   $(patsubst "%",%,$(CONFIG_LOCALVERSION)))
+
+# If CONFIG_LOCALVERSION_AUTO is set scripts/setlocalversion is called
+# and if the SCM is know a tag from the SCM is appended.
+# The appended tag is determinded by the SCM used.
+#
+# Currently, only git is supported.
+# Other SCMs can edit scripts/setlocalversion and add the appropriate
+# checks as needed.
+ifdef _BB_DISABLED_CONFIG_LOCALVERSION_AUTO
+	_localver-auto = $(shell $(CONFIG_SHELL) \
+	                  $(srctree)/scripts/setlocalversion $(srctree))
+	localver-auto  = $(LOCALVERSION)$(_localver-auto)
+endif
+
+localver-full = $(localver)$(localver-auto)
+
+# Store (new) KERNELRELASE string in .kernelrelease
+kernelrelease = $(KERNELVERSION)$(localver-full)
+.kernelrelease: FORCE
+	$(Q)rm -f $@
+	$(Q)echo $(kernelrelease) > $@
+
+
+# Things we need to do before we recursively start building the kernel
+# or the modules are listed in "prepare".
+# A multi level approach is used. prepareN is processed before prepareN-1.
+# archprepare is used in arch Makefiles and when processed asm symlink,
+# version.h and scripts_basic is processed / created.
+
+# Listed in dependency order
+PHONY += prepare archprepare prepare0 prepare1 prepare2 prepare3
+
+# prepare-all is deprecated, use prepare as valid replacement
+PHONY += prepare-all
+
+# prepare3 is used to check if we are building in a separate output directory,
+# and if so do:
+# 1) Check that make has not been executed in the kernel src $(srctree)
+# 2) Create the include2 directory, used for the second asm symlink
+prepare3: .kernelrelease
+ifneq ($(KBUILD_SRC),)
+	@echo '  Using $(srctree) as source for busybox'
+	$(Q)if [ -f $(srctree)/.config ]; then \
+		echo "  $(srctree) is not clean, please run 'make mrproper'";\
+		echo "  in the '$(srctree)' directory.";\
+		/bin/false; \
+	fi;
+	$(Q)if [ ! -d include2 ]; then mkdir -p include2; fi;
+	$(Q)ln -fsn $(srctree)/include/asm-$(ARCH) include2/asm
+endif
+
+# prepare2 creates a makefile if using a separate output directory
+prepare2: prepare3 outputmakefile
+
+prepare1: prepare2 include/config/MARKER
+ifneq ($(KBUILD_MODULES),)
+	$(Q)mkdir -p $(MODVERDIR)
+	$(Q)rm -f $(MODVERDIR)/*
+endif
+
+archprepare: prepare1 scripts_basic applets_dir
+
+prepare0: archprepare FORCE
+	$(Q)$(MAKE) $(build)=.
+
+# All the preparing..
+prepare prepare-all: prepare0
+
+#	Leave this as default for preprocessing busybox.lds.S, which is now
+#	done in arch/$(ARCH)/kernel/Makefile
+
+export CPPFLAGS_busybox.lds += -P -C -U$(ARCH)
+
+# 	FIXME: The asm symlink changes when $(ARCH) changes. That's
+#	hard to detect, but I suppose "make mrproper" is a good idea
+#	before switching between archs anyway.
+
+#bbox# include/asm:
+#bbox# 	@echo '  SYMLINK $@ -> include/asm-$(ARCH)'
+#bbox# 	$(Q)if [ ! -d include ]; then mkdir -p include; fi;
+#bbox# 	@ln -fsn asm-$(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 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
+include/config/MARKER: scripts/basic/split-include include/autoconf.h
+	$(call cmd,split_autoconf)
+	$(call cmd,gen_bbconfigopts)
+	@touch $@
+
+# Generate some files
+# ---------------------------------------------------------------------------
+
+# KERNELRELEASE can change from a few different places, meaning version.h
+# needs to be updated, so this check is forced on all builds
+
+uts_len := 64
+
+define filechk_version.h
+	if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
+	  echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2; \
+	  exit 1; \
+	fi; \
+	(echo \#define UTS_RELEASE \"$(KERNELRELEASE)\"; \
+	  echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)`; \
+	 echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))'; \
+	)
+endef
+
+# ---------------------------------------------------------------------------
+
+PHONY += depend dep
+depend dep:
+	@echo '*** Warning: make $@ is unnecessary now.'
+
+# ---------------------------------------------------------------------------
+# Modules
+
+ifdef _BB_DISABLED_CONFIG_MODULES
+
+# 	By default, build modules as well
+
+all: modules
+
+#	Build modules
+
+PHONY += modules
+modules: $(busybox-dirs) $(if $(KBUILD_BUILTIN),busybox)
+	@echo '  Building modules, stage 2.';
+	$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
+
+
+# Target to prepare building external modules
+PHONY += modules_prepare
+modules_prepare: prepare scripts
+
+# Target to install modules
+PHONY += modules_install
+modules_install: _modinst_ _modinst_post
+
+PHONY += _modinst_
+_modinst_:
+	@if [ -z "`$(DEPMOD) -V 2>/dev/null | grep module-init-tools`" ]; then \
+		echo "Warning: you may need to install module-init-tools"; \
+		echo "See http://www.codemonkey.org.uk/docs/post-halloween-2.6.txt";\
+		sleep 1; \
+	fi
+	@rm -rf $(MODLIB)/kernel
+	@rm -f $(MODLIB)/source
+	@mkdir -p $(MODLIB)/kernel
+	@ln -s $(srctree) $(MODLIB)/source
+	@if [ ! $(objtree) -ef  $(MODLIB)/build ]; then \
+		rm -f $(MODLIB)/build ; \
+		ln -s $(objtree) $(MODLIB)/build ; \
+	fi
+	$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst
+
+# If System.map exists, run depmod.  This deliberately does not have a
+# dependency on System.map since that would run the dependency tree on
+# busybox.  This depmod is only for convenience to give the initial
+# boot a modules.dep even before / is mounted read-write.  However the
+# boot script depmod is the master version.
+ifeq "$(strip $(INSTALL_MOD_PATH))" ""
+depmod_opts	:=
+else
+depmod_opts	:= -b $(INSTALL_MOD_PATH) -r
+endif
+PHONY += _modinst_post
+_modinst_post: _modinst_
+	if [ -r System.map -a -x $(DEPMOD) ]; then $(DEPMOD) -ae -F System.map $(depmod_opts) $(KERNELRELEASE); fi
+
+else # CONFIG_MODULES
+
+# Modules not configured
+# ---------------------------------------------------------------------------
+
+modules modules_install: FORCE
+	@echo
+	@echo "The present busybox configuration has modules disabled."
+	@echo "Type 'make config' and enable loadable module support."
+	@echo "Then build a kernel with module support enabled."
+	@echo
+	@exit 1
+
+endif # CONFIG_MODULES
+
+###
+# Cleaning is done on three levels.
+# make clean     Delete most generated files
+#                Leave enough to build external modules
+# make mrproper  Delete the current configuration, and all generated files
+# make distclean Remove editor backup files, patch leftover files and the like
+
+# Directories & files removed with 'make clean'
+CLEAN_DIRS  += $(MODVERDIR) _install 0_lib
+CLEAN_FILES +=	busybox busybox_unstripped* busybox.links \
+                System.map .kernelrelease \
+                .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map
+
+# 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
+
+# clean - Delete most, but leave enough to build external modules
+#
+clean: rm-dirs  := $(CLEAN_DIRS)
+clean: rm-files := $(CLEAN_FILES)
+clean-dirs      := $(addprefix _clean_,$(srctree) $(busybox-alldirs))
+
+PHONY += $(clean-dirs) clean archclean
+$(clean-dirs):
+	$(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
+
+clean: archclean $(clean-dirs)
+	$(call cmd,rmdirs)
+	$(call cmd,rmfiles)
+	@find . $(RCS_FIND_IGNORE) \
+		\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
+		-type f -print | xargs rm -f
+
+PHONY += doc-clean
+doc-clean: rm-files := docs/busybox.pod \
+		  docs/BusyBox.html docs/busybox.1 docs/BusyBox.txt
+doc-clean:
+	$(call cmd,rmfiles)
+
+# mrproper - Delete all generated files, including .config
+#
+mrproper: rm-dirs  := $(wildcard $(MRPROPER_DIRS))
+mrproper: rm-files := $(wildcard $(MRPROPER_FILES))
+mrproper-dirs      := $(addprefix _mrproper_,scripts)
+
+PHONY += $(mrproper-dirs) mrproper archmrproper
+$(mrproper-dirs):
+	$(Q)$(MAKE) $(clean)=$(patsubst _mrproper_%,%,$@)
+
+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
+
+# distclean
+#
+PHONY += distclean
+
+distclean: mrproper
+	@find $(srctree) $(RCS_FIND_IGNORE) \
+		\( -name '*.orig' -o -name '*.rej' -o -name '*~' \
+		-o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
+		-o -name '.*.rej' -o -name '*.tmp' -o -size 0 \
+		-o -name '*%' -o -name '.*.cmd' -o -name 'core' \) \
+		-type f -print | xargs rm -f
+
+
+# Packaging of the kernel to various formats
+# ---------------------------------------------------------------------------
+# rpm target kept for backward compatibility
+package-dir	:= $(srctree)/scripts/package
+
+%pkg: FORCE
+	$(Q)$(MAKE) $(build)=$(package-dir) $@
+rpm: FORCE
+	$(Q)$(MAKE) $(build)=$(package-dir) $@
+
+
+# Brief documentation of the typical targets used
+# ---------------------------------------------------------------------------
+
+boards := $(wildcard $(srctree)/configs/*_defconfig)
+boards := $(notdir $(boards))
+
+-include $(srctree)/Makefile.help
+
+# Documentation targets
+# ---------------------------------------------------------------------------
+%docs: scripts_basic FORCE
+	$(Q)$(MAKE) $(build)=Documentation/DocBook $@
+
+else # KBUILD_EXTMOD
+
+###
+# External module support.
+# When building external modules the kernel used as basis is considered
+# read-only, and no consistency checks are made and the make
+# system is not used on the basis kernel. If updates are required
+# in the basis kernel ordinary make commands (without M=...) must
+# be used.
+#
+# The following are the only valid targets when building external
+# modules.
+# make M=dir clean     Delete all automatically generated files
+# make M=dir modules   Make all modules in specified dir
+# make M=dir	       Same as 'make M=dir modules'
+# make M=dir modules_install
+#                      Install the modules build in the module directory
+#                      Assumes install directory is already created
+
+# We are always building modules
+KBUILD_MODULES := 1
+PHONY += crmodverdir
+crmodverdir:
+	$(Q)mkdir -p $(MODVERDIR)
+	$(Q)rm -f $(MODVERDIR)/*
+
+PHONY += $(objtree)/Module.symvers
+$(objtree)/Module.symvers:
+	@test -e $(objtree)/Module.symvers || ( \
+	echo; \
+	echo "  WARNING: Symbol version dump $(objtree)/Module.symvers"; \
+	echo "           is missing; modules will have no dependencies and modversions."; \
+	echo )
+
+module-dirs := $(addprefix _module_,$(KBUILD_EXTMOD))
+PHONY += $(module-dirs) modules
+$(module-dirs): crmodverdir $(objtree)/Module.symvers
+	$(Q)$(MAKE) $(build)=$(patsubst _module_%,%,$@)
+
+modules: $(module-dirs)
+	@echo '  Building modules, stage 2.';
+	$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
+
+PHONY += modules_install
+modules_install: _emodinst_ _emodinst_post
+
+install-dir := $(if $(INSTALL_MOD_DIR),$(INSTALL_MOD_DIR),extra)
+PHONY += _emodinst_
+_emodinst_:
+	$(Q)mkdir -p $(MODLIB)/$(install-dir)
+	$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modinst
+
+# Run depmod only is we have System.map and depmod is executable
+quiet_cmd_depmod = DEPMOD  $(KERNELRELEASE)
+      cmd_depmod = if [ -r System.map -a -x $(DEPMOD) ]; then \
+                      $(DEPMOD) -ae -F System.map             \
+                      $(if $(strip $(INSTALL_MOD_PATH)),      \
+		      -b $(INSTALL_MOD_PATH) -r)              \
+		      $(KERNELRELEASE);                       \
+                   fi
+
+PHONY += _emodinst_post
+_emodinst_post: _emodinst_
+	$(call cmd,depmod)
+
+clean-dirs := $(addprefix _clean_,$(KBUILD_EXTMOD))
+
+PHONY += $(clean-dirs) clean
+$(clean-dirs):
+	$(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@)
+
+clean:	rm-dirs := $(MODVERDIR)
+clean: $(clean-dirs)
+	$(call cmd,rmdirs)
+	@find $(KBUILD_EXTMOD) $(RCS_FIND_IGNORE) \
+		\( -name '*.[oas]' -o -name '*.ko' -o -name '.*.cmd' \
+		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \) \
+		-type f -print | xargs rm -f
+
+# Dummies...
+PHONY += prepare scripts
+prepare: ;
+scripts: ;
+endif # KBUILD_EXTMOD
+
+# Generate tags for editors
+# ---------------------------------------------------------------------------
+
+#We want __srctree to totally vanish out when KBUILD_OUTPUT is not set
+#(which is the most common case IMHO) to avoid unneeded clutter in the big tags file.
+#Adding $(srctree) adds about 20M on i386 to the size of the output file!
+
+ifeq ($(src),$(obj))
+__srctree =
+else
+__srctree = $(srctree)/
+endif
+
+ifeq ($(ALLSOURCE_ARCHS),)
+ifeq ($(ARCH),um)
+ALLINCLUDE_ARCHS := $(ARCH) $(SUBARCH)
+else
+ALLINCLUDE_ARCHS := $(ARCH)
+endif
+else
+#Allow user to specify only ALLSOURCE_PATHS on the command line, keeping existing behaviour.
+ALLINCLUDE_ARCHS := $(ALLSOURCE_ARCHS)
+endif
+
+ALLSOURCE_ARCHS := $(ARCH)
+
+define all-sources
+	( find $(__srctree) $(RCS_FIND_IGNORE) \
+	       \( -name include -o -name arch \) -prune -o \
+	       -name '*.[chS]' -print; \
+	  for ARCH in $(ALLSOURCE_ARCHS) ; do \
+	       find $(__srctree)arch/$${ARCH} $(RCS_FIND_IGNORE) \
+	            -name '*.[chS]' -print; \
+	  done ; \
+	  find $(__srctree)security/selinux/include $(RCS_FIND_IGNORE) \
+	       -name '*.[chS]' -print; \
+	  find $(__srctree)include $(RCS_FIND_IGNORE) \
+	       \( -name config -o -name 'asm-*' \) -prune \
+	       -o -name '*.[chS]' -print; \
+	  for ARCH in $(ALLINCLUDE_ARCHS) ; do \
+	       find $(__srctree)include/asm-$${ARCH} $(RCS_FIND_IGNORE) \
+	            -name '*.[chS]' -print; \
+	  done ; \
+	  find $(__srctree)include/asm-generic $(RCS_FIND_IGNORE) \
+	       -name '*.[chS]' -print )
+endef
+
+quiet_cmd_cscope-file = FILELST cscope.files
+      cmd_cscope-file = (echo \-k; echo \-q; $(all-sources)) > cscope.files
+
+quiet_cmd_cscope = MAKE    cscope.out
+      cmd_cscope = cscope -b
+
+cscope: FORCE
+	$(call cmd,cscope-file)
+	$(call cmd,cscope)
+
+quiet_cmd_TAGS = MAKE   $@
+define cmd_TAGS
+	rm -f $@; \
+	ETAGSF=`etags --version | grep -i exuberant >/dev/null &&     \
+                echo "-I __initdata,__exitdata,__acquires,__releases  \
+                      -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL              \
+                      --extra=+f --c-kinds=+px"`;                     \
+                $(all-sources) | xargs etags $$ETAGSF -a
+endef
+
+TAGS: FORCE
+	$(call cmd,TAGS)
+
+
+quiet_cmd_tags = MAKE   $@
+define cmd_tags
+	rm -f $@; \
+	CTAGSF=`ctags --version | grep -i exuberant >/dev/null &&     \
+                echo "-I __initdata,__exitdata,__acquires,__releases  \
+                      -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL              \
+                      --extra=+f --c-kinds=+px"`;                     \
+                $(all-sources) | xargs ctags $$CTAGSF -a
+endef
+
+tags: FORCE
+	$(call cmd,tags)
+
+
+# Scripts to check various things for consistency
+# ---------------------------------------------------------------------------
+
+includecheck:
+	find * $(RCS_FIND_IGNORE) \
+		-name '*.[hcS]' -type f -print | sort \
+		| xargs $(PERL) -w scripts/checkincludes.pl
+
+versioncheck:
+	find * $(RCS_FIND_IGNORE) \
+		-name '*.[hcS]' -type f -print | sort \
+		| xargs $(PERL) -w scripts/checkversion.pl
+
+namespacecheck:
+	$(PERL) $(srctree)/scripts/namespace.pl
+
+endif #ifeq ($(config-targets),1)
+endif #ifeq ($(mixed-targets),1)
+
+PHONY += checkstack
+checkstack:
+	$(OBJDUMP) -d busybox $$(find . -name '*.ko') | \
+	$(PERL) $(src)/scripts/checkstack.pl $(ARCH)
+
+kernelrelease:
+	$(if $(wildcard .kernelrelease), $(Q)echo $(KERNELRELEASE), \
+	$(error kernelrelease not valid - run 'make *config' to update it))
+kernelversion:
+	@echo $(KERNELVERSION)
+
+# Single targets
+# ---------------------------------------------------------------------------
+# Single targets are compatible with:
+# - build whith mixed source and output
+# - build with separate output dir 'make O=...'
+# - external modules
+#
+#  target-dir => where to store outputfile
+#  build-dir  => directory in kernel source tree to use
+
+ifeq ($(KBUILD_EXTMOD),)
+        build-dir  = $(patsubst %/,%,$(dir $@))
+        target-dir = $(dir $@)
+else
+        zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@)))
+        build-dir  = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash))
+        target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@))
+endif
+
+%.s: %.c prepare scripts FORCE
+	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.i: %.c prepare scripts FORCE
+	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.o: %.c prepare scripts FORCE
+	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.lst: %.c prepare scripts FORCE
+	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.s: %.S prepare scripts FORCE
+	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+%.o: %.S prepare scripts FORCE
+	$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)
+
+# Modules
+%/: 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)
+	$(Q)$(MAKE) -rR -f $(srctree)/scripts/Makefile.modpost
+
+# FIXME Should go into a make.lib or something
+# ===========================================================================
+
+quiet_cmd_rmdirs = $(if $(wildcard $(rm-dirs)),CLEAN   $(wildcard $(rm-dirs)))
+      cmd_rmdirs = rm -rf $(rm-dirs)
+
+quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN   $(wildcard $(rm-files)))
+      cmd_rmfiles = rm -f $(rm-files)
+
+
+a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(AFLAGS_KERNEL) \
+	  $(NOSTDINC_FLAGS) $(CPPFLAGS) \
+	  $(modkern_aflags) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o)
+
+quiet_cmd_as_o_S = AS      $@
+cmd_as_o_S       = $(CC) $(a_flags) -c -o $@ $<
+
+# read all saved command lines
+
+targets := $(wildcard $(sort $(targets)))
+cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd))
+
+ifneq ($(cmd_files),)
+  $(cmd_files): ;	# Do not try to update included dependency files
+  include $(cmd_files)
+endif
+
+# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=dir
+# Usage:
+# $(Q)$(MAKE) $(clean)=dir
+clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj
+
+endif	# skip-makefile
+
+PHONY += FORCE
+FORCE:
+
+-include $(srctree)/Makefile.custom
+
+# Declare the contents of the .PHONY variable as phony.  We keep that
+# information in a variable se we can use it in if_changed and friends.
+.PHONY: $(PHONY)
diff --git a/busybox-1.19.3/Makefile.custom b/busybox-1.19.3/Makefile.custom
new file mode 100644
index 0000000..6da79e6
--- /dev/null
+++ b/busybox-1.19.3/Makefile.custom
@@ -0,0 +1,178 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+busybox.links: $(srctree)/applets/busybox.mkll $(objtree)/include/autoconf.h include/applets.h
+	$(Q)-$(SHELL) $^ >$@
+
+.PHONY: install
+ifeq ($(CONFIG_INSTALL_APPLET_SYMLINKS),y)
+INSTALL_OPTS:= --symlinks
+endif
+ifeq ($(CONFIG_INSTALL_APPLET_HARDLINKS),y)
+INSTALL_OPTS:= --hardlinks
+endif
+ifeq ($(CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS),y)
+ifeq ($(CONFIG_INSTALL_SH_APPLET_SYMLINK),y)
+INSTALL_OPTS:= --sw-sh-sym
+endif
+ifeq ($(CONFIG_INSTALL_SH_APPLET_HARDLINK),y)
+INSTALL_OPTS:= --sw-sh-hard
+endif
+ifeq ($(CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER),y)
+INSTALL_OPTS:= --scriptwrapper
+endif
+endif
+install: $(srctree)/applets/install.sh busybox busybox.links
+	$(Q)DO_INSTALL_LIBS="$(strip $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS))" \
+		$(SHELL) $< $(CONFIG_PREFIX) $(INSTALL_OPTS)
+ifeq ($(strip $(CONFIG_FEATURE_SUID)),y)
+	@echo
+	@echo
+	@echo --------------------------------------------------
+	@echo You will probably need to make your busybox binary
+	@echo setuid root to ensure all configured applets will
+	@echo work properly.
+	@echo --------------------------------------------------
+	@echo
+endif
+
+uninstall: busybox.links
+	rm -f $(CONFIG_PREFIX)/bin/busybox
+	for i in `cat busybox.links` ; do rm -f $(CONFIG_PREFIX)$$i; done
+ifneq ($(strip $(DO_INSTALL_LIBS)),n)
+	for i in $(LIBBUSYBOX_SONAME) $(DO_INSTALL_LIBS); do \
+		rm -f $(CONFIG_PREFIX)$$i; \
+	done
+endif
+
+# Not very elegant: copies testsuite to objdir...
+# (cp -pPR is POSIX-compliant (cp -dpR or cp -a would not be))
+.PHONY: check
+.PHONY: test
+check test: busybox busybox.links
+	test -d $(objtree)/testsuite || cp -pPR $(srctree)/testsuite $(objtree)
+	bindir=$(objtree) srcdir=$(srctree)/testsuite \
+	$(SHELL) -c "cd $(objtree)/testsuite && $(srctree)/testsuite/runtest $(if $(KBUILD_VERBOSE:0=),-v)"
+
+.PHONY: release
+release: distclean
+	cd ..; \
+	rm -r -f busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION); \
+	cp -pPR busybox busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) && { \
+	find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \
+		-name .svn \
+		-print \
+		-exec rm -r -f {} \; ; \
+	find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type d \
+		-name .git \
+		-print \
+		-exec rm -r -f {} \; ; \
+	find busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ -type f \
+		-name .\#* \
+		-print \
+		-exec rm -f {} \; ; \
+	tar -czf busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION).tar.gz \
+		busybox-$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)/ ; }
+
+.PHONY: checkhelp
+checkhelp:
+	$(Q)$(srctree)/scripts/checkhelp.awk \
+		$(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./)))
+
+.PHONY: sizes
+sizes: busybox_unstripped
+	$(NM) --size-sort $(<)
+
+.PHONY: bloatcheck
+bloatcheck: busybox_old busybox_unstripped
+	@$(srctree)/scripts/bloat-o-meter busybox_old busybox_unstripped
+	@$(CROSS_COMPILE)size busybox_old busybox_unstripped
+
+.PHONY: baseline
+baseline: busybox_unstripped
+	@mv busybox_unstripped busybox_old
+
+.PHONY: objsizes
+objsizes: busybox_unstripped
+	$(srctree)/scripts/objsizes
+
+.PHONY: stksizes
+stksizes: busybox_unstripped
+	$(CROSS_COMPILE)objdump -d busybox_unstripped | $(srctree)/scripts/checkstack.pl $(ARCH) | uniq
+
+.PHONY: bigdata
+bigdata: busybox_unstripped
+	$(CROSS_COMPILE)nm --size-sort busybox_unstripped | grep -vi ' [trw] '
+
+# Documentation Targets
+.PHONY: doc
+doc: docs/busybox.pod docs/BusyBox.txt docs/busybox.1 docs/BusyBox.html
+
+# FIXME: Doesn't belong here
+       cmd_doc =
+ quiet_cmd_doc = $(Q)echo "  DOC     $(@F)"
+silent_cmd_doc =
+disp_doc       = $($(quiet)cmd_doc)
+
+# sed adds newlines after "Options:" etc,
+# this is needed in order to get good BusyBox.{1,txt,html}
+docs/busybox.pod: $(srctree)/docs/busybox_header.pod \
+		include/usage.h \
+		$(srctree)/docs/busybox_footer.pod \
+		applets/usage_pod
+	$(disp_doc)
+	$(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
+
+docs/BusyBox.txt: docs/busybox.pod
+	$(disp_doc)
+	$(Q)-mkdir -p docs
+	$(Q)-pod2text $< > $@
+
+docs/busybox.1: docs/busybox.pod
+	$(disp_doc)
+	$(Q)-mkdir -p docs
+	$(Q)-pod2man --center=busybox --release="version $(KERNELVERSION)" $< > $@
+
+docs/BusyBox.html: docs/busybox.net/BusyBox.html
+	$(disp_doc)
+	$(Q)-mkdir -p docs
+	$(Q)-rm -f docs/BusyBox.html
+	$(Q)-cp docs/busybox.net/BusyBox.html docs/BusyBox.html
+
+docs/busybox.net/BusyBox.html: docs/busybox.pod
+	$(Q)-mkdir -p docs/busybox.net
+	$(Q)-pod2html --noindex $< > $@
+	$(Q)-rm -f pod2htm*
+
+# documentation, cross-reference
+# Modern distributions already ship synopsis packages (e.g. debian)
+# If you have an old distribution go to http://synopsis.fresco.org/
+syn_tgt = $(wildcard $(patsubst %,%/*.c,$(busybox-alldirs)))
+syn     = $(patsubst %.c, %.syn, $(syn_tgt))
+
+comma:= ,
+brace_open:= (
+brace_close:= )
+
+SYN_CPPFLAGS := $(strip $(CPPFLAGS) $(EXTRA_CPPFLAGS))
+SYN_CPPFLAGS := $(subst $(brace_open),\$(brace_open),$(SYN_CPPFLAGS))
+SYN_CPPFLAGS := $(subst $(brace_close),\$(brace_close),$(SYN_CPPFLAGS))
+#SYN_CPPFLAGS := $(subst ",\",$(SYN_CPPFLAGS))
+#")
+#SYN_CPPFLAGS := [$(patsubst %,'%'$(comma),$(SYN_CPPFLAGS))'']
+
+%.syn: %.c
+	synopsis -p C -l Comments.SSDFilter,Comments.Previous -Wp,preprocess=True,cppflags="'$(SYN_CPPFLAGS)'" -o $@ $<
+
+.PHONY: html
+html: $(syn)
+	synopsis -f HTML -Wf,title="'BusyBox Documentation'" -o $@ $^
+
+-include $(srctree)/Makefile.local
diff --git a/busybox-1.19.3/Makefile.flags b/busybox-1.19.3/Makefile.flags
new file mode 100644
index 0000000..4ef5318
--- /dev/null
+++ b/busybox-1.19.3/Makefile.flags
@@ -0,0 +1,132 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+BB_VER = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
+export BB_VER
+SKIP_STRIP ?= n
+
+# -std=gnu99 needed for [U]LLONG_MAX on some systems
+CPPFLAGS += $(call cc-option,-std=gnu99,)
+
+CPPFLAGS += \
+	-Iinclude -Ilibbb \
+	$(if $(KBUILD_SRC),-Iinclude2 -I$(srctree)/include -I$(srctree)/libbb) \
+	-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
+
+CFLAGS += $(call cc-option,-Wall,)
+CFLAGS += $(call cc-option,-Wshadow,)
+CFLAGS += $(call cc-option,-Wwrite-strings,)
+CFLAGS += $(call cc-option,-Wundef,)
+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,)
+# 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
+# still possible to build bbox without warnings.
+
+ifeq ($(CONFIG_WERROR),y)
+CFLAGS += $(call cc-option,-Werror,)
+## TODO:
+## gcc version 4.4.0 20090506 (Red Hat 4.4.0-4) (GCC) is a PITA:
+## const char *ptr; ... off_t v = *(off_t*)ptr; -> BOOM
+## and no easy way to convince it to shut the hell up.
+## We have a lot of such things all over the place.
+## Classic *(off_t*)(void*)ptr does not work,
+## and I am unwilling to do crazy gcc specific ({ void *ppp = ...; })
+## stuff in macros. This would obfuscate the code too much.
+## Maybe try __attribute__((__may_alias__))?
+CFLAGS += $(call cc-ifversion, -ge, 0404, -fno-strict-aliasing)
+endif
+# gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action()
+CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition)
+
+CFLAGS += $(call cc-option,-fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer -ffunction-sections -fdata-sections,)
+# -fno-guess-branch-probability: prohibit pseudo-random guessing
+# 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,)
+
+# 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,$(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,$(call cc-option,-O2,))
+endif
+endif
+
+# If arch/$(ARCH)/Makefile did not override it (with, say, -fPIC)...
+ARCH_FPIC ?= -fpic
+ARCH_FPIE ?= -fpie
+ARCH_PIE ?= -pie
+
+ifeq ($(CONFIG_BUILD_LIBBUSYBOX),y)
+# on i386: 14% smaller libbusybox.so
+# (code itself is 9% bigger, we save on relocs/PLT/GOT)
+CFLAGS += $(ARCH_FPIC)
+# and another 4% reduction of libbusybox.so:
+# (external entry points must be marked EXTERNALLY_VISIBLE)
+CFLAGS += $(call cc-option,-fvisibility=hidden)
+endif
+
+ifeq ($(CONFIG_STATIC),y)
+CFLAGS_busybox += -static
+endif
+
+ifeq ($(CONFIG_PIE),y)
+CFLAGS_busybox += $(ARCH_PIE)
+CFLAGS += $(ARCH_FPIE)
+endif
+
+ifneq ($(CONFIG_EXTRA_CFLAGS),)
+CFLAGS += $(strip $(subst ",,$(CONFIG_EXTRA_CFLAGS)))
+#"))
+endif
+
+LDLIBS += m crypt
+
+ifeq ($(CONFIG_PAM),y)
+LDLIBS += pam pam_misc
+endif
+
+ifeq ($(CONFIG_SELINUX),y)
+LDLIBS += selinux sepol
+endif
+
+ifeq ($(CONFIG_EFENCE),y)
+LDLIBS += efence
+endif
+
+ifeq ($(CONFIG_DMALLOC),y)
+LDLIBS += dmalloc
+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.
+# (For historical reasons, we also check LDFLAGS, which doesn't
+# seem to be entirely correct variable to put "-elf2flt" into).
+W_ELF2FLT = -elf2flt
+ifneq (,$(findstring $(W_ELF2FLT),$(LDFLAGS) $(CFLAGS_busybox)))
+SKIP_STRIP = y
+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!)
+# Reduced from 20k to 16k in 1.9.0.
+FLTFLAGS += -s 16000
diff --git a/busybox-1.19.3/Makefile.help b/busybox-1.19.3/Makefile.help
new file mode 100644
index 0000000..119dd6f
--- /dev/null
+++ b/busybox-1.19.3/Makefile.help
@@ -0,0 +1,48 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+help:
+	@echo 'Cleaning:'
+	@echo '  clean			- delete temporary files created by build'
+	@echo '  distclean		- delete all non-source files (including .config)'
+	@echo '  doc-clean		- delete all generated documentation'
+	@echo
+	@echo 'Build:'
+	@echo '  all			- Executable and documentation'
+	@echo '  busybox		- the swiss-army executable'
+	@echo '  doc			- docs/BusyBox.{txt,html,1}'
+	@echo '  html			- create html-based cross-reference'
+	@echo
+	@echo 'Configuration:'
+	@echo '  allnoconfig		- disable all symbols in .config'
+	@echo '  allyesconfig		- enable all symbols in .config (see defconfig)'
+	@echo '  config		- text based configurator (of last resort)'
+	@echo '  defconfig		- set .config to largest generic configuration'
+	@echo '  menuconfig		- interactive curses-based configurator'
+	@echo '  oldconfig		- resolve any unresolved symbols in .config'
+	@echo '  hosttools  		- build sed for the host.'
+	@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'
+	@echo '  uninstall'
+	@echo
+	@echo 'Development:'
+	@echo '  baseline		- create busybox_old for bloatcheck.'
+	@echo '  bloatcheck		- show size difference between old and new versions'
+	@echo '  check			- run the test suite for all applets'
+	@echo '  checkhelp		- check for missing help-entries in Config.in'
+	@echo '  randconfig		- generate a random configuration'
+	@echo '  release		- create a distribution tarball'
+	@echo '  sizes			- show size of all enabled busybox symbols'
+	@echo '  objsizes		- show size of each .o object built'
+	@echo '  bigdata		- show data objects, biggest first'
+	@echo '  stksizes		- show stack users, biggest first'
+	@echo
diff --git a/busybox-1.19.3/README b/busybox-1.19.3/README
new file mode 100644
index 0000000..b940e35
--- /dev/null
+++ b/busybox-1.19.3/README
@@ -0,0 +1,204 @@
+Please see the LICENSE file for details on copying and usage.
+Please refer to the INSTALL file for instructions on how to build.
+
+What is busybox:
+
+  BusyBox combines tiny versions of many common UNIX utilities into a single
+  small executable.  It provides minimalist replacements for most of the
+  utilities you usually find in bzip2, coreutils, dhcp, diffutils, e2fsprogs,
+  file, findutils, gawk, grep, inetutils, less, modutils, net-tools, procps,
+  sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim.  The utilities
+  in BusyBox often have fewer options than their full-featured cousins;
+  however, the options that are included provide the expected functionality
+  and behave very much like their larger counterparts.
+
+  BusyBox has been written with size-optimization and limited resources in
+  mind, both to produce small binaries and to reduce run-time memory usage.
+  Busybox is also extremely modular so you can easily include or exclude
+  commands (or features) at compile time.  This makes it easy to customize
+  embedded systems; to create a working system, just add /dev, /etc, and a
+  Linux kernel.  Busybox (usually together with uClibc) has also been used as
+  a component of "thin client" desktop systems, live-CD distributions, rescue
+  disks, installers, and so on.
+
+  BusyBox provides a fairly complete POSIX environment for any small system,
+  both embedded environments and more full featured systems concerned about
+  space.  Busybox is slowly working towards implementing the full Single Unix
+  Specification V3 (http://www.opengroup.org/onlinepubs/009695399/), but isn't
+  there yet (and for size reasons will probably support at most UTF-8 for
+  internationalization).  We are also interested in passing the Linux Test
+  Project (http://ltp.sourceforge.net).
+
+----------------
+
+Using busybox:
+
+  BusyBox is extremely configurable.  This allows you to include only the
+  components and options you need, thereby reducing binary size.  Run 'make
+  config' or 'make menuconfig' to select the functionality that you wish to
+  enable.  (See 'make help' for more commands.)
+
+  The behavior of busybox is determined by the name it's called under: as
+  "cp" it behaves like cp, as "sed" it behaves like sed, and so on.  Called
+  as "busybox" it takes the second argument as the name of the applet to
+  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 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.)
+
+  The build automatically generates a file "busybox.links", which is used by
+  'make install' to create symlinks to the BusyBox binary for all compiled in
+  commands.  This uses the CONFIG_PREFIX environment variable to specify
+  where to install, and installs hardlinks or symlinks depending
+  on the configuration preferences.  (You can also manually run
+  the install script at "applets/install.sh").
+
+----------------
+
+Downloading the current source code:
+
+  Source for the latest released version, as well as daily snapshots, can always
+  be downloaded from
+
+    http://busybox.net/downloads/
+
+  You can browse the up to the minute source code and change history online.
+
+    http://git.busybox.net/busybox/
+
+  Anonymous GIT access is available.  For instructions, check out:
+
+    http://www.busybox.net/source.html
+
+  For those that are actively contributing and would like to check files in,
+  see:
+
+    http://busybox.net/developer.html
+
+  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 git changelog.
+
+  Note: if you want to compile busybox in a busybox environment you must
+  select CONFIG_DESKTOP.
+
+----------------
+
+Getting help:
+
+  when you find you need help, you can check out the busybox mailing list
+  archives at http://busybox.net/lists/busybox/ or even join
+  the mailing list if you are interested.
+
+----------------
+
+Bugs:
+
+  if you find bugs, please submit a detailed bug report to the busybox mailing
+  list at busybox@busybox.net.  a well-written bug report should include a
+  transcript of a shell session that demonstrates the bad behavior and enables
+  anyone else to duplicate the bug on their own machine. the following is such
+  an example:
+
+    to: busybox@busybox.net
+    from: diligent@testing.linux.org
+    subject: /bin/date doesn't work
+
+    package: busybox
+    version: 1.00
+
+    when i execute busybox 'date' it produces unexpected results.
+    with gnu date i get the following output:
+
+	$ date
+	fri oct  8 14:19:41 mdt 2004
+
+    but when i use busybox date i get this instead:
+
+	$ date
+	illegal instruction
+
+    i am using debian unstable, kernel version 2.4.25-vrs2 on a netwinder,
+    and the latest uclibc from cvs.
+
+	-diligent
+
+  note the careful description and use of examples showing not only what
+  busybox does, but also a counter example showing what an equivalent app
+  does (or pointing to the text of a relevant standard).  Bug reports lacking
+  such detail may never be fixed...  Thanks for understanding.
+
+----------------
+
+Portability:
+
+  Busybox is developed and tested on Linux 2.4 and 2.6 kernels, compiled
+  with gcc (the unit-at-a-time optimizations in version 3.4 and later are
+  worth upgrading to get, but older versions should work), and linked against
+  uClibc (0.9.27 or greater) or glibc (2.2 or greater).  In such an
+  environment, the full set of busybox features should work, and if
+  anything doesn't we want to know about it so we can fix it.
+
+  There are many other environments out there, in which busybox may build
+  and run just fine.  We just don't test them.  Since busybox consists of a
+  large number of more or less independent applets, portability is a question
+  of which features work where.  Some busybox applets (such as cat and rm) are
+  highly portable and likely to work just about anywhere, while others (such as
+  insmod and losetup) require recent Linux kernels with recent C libraries.
+
+  Earlier versions of Linux and glibc may or may not work, for any given
+  configuration.  Linux 2.2 or earlier should mostly work (there's still
+  some support code in things like mount.c) but this is no longer regularly
+  tested, and inherently won't support certain features (such as long files
+  and --bind mounts).  The same is true for glibc 2.0 and 2.1: expect a higher
+  testing and debugging burden using such old infrastructure.  (The busybox
+  developers are not very interested in supporting these older versions, but
+  will probably accept small self-contained patches to fix simple problems.)
+
+  Some environments are not recommended.  Early versions of uClibc were buggy
+  and missing many features: upgrade.  Linking against libc5 or dietlibc is
+  not supported and not interesting to the busybox developers.  (The first is
+  obsolete and has no known size or feature advantages over uClibc, the second
+  has known bugs that its developers have actively refused to fix.)  Ancient
+  Linux kernels (2.0.x and earlier) are similarly uninteresting.
+
+  In theory it's possible to use Busybox under other operating systems (such as
+  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 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.
+
+  In 2005 Shaun Jackman has ported busybox to a combination of newlib
+  and libgloss, and some of his patches have been integrated.
+
+Supported hardware:
+
+  BusyBox in general will build on any architecture supported by gcc.  We
+  support both 32 and 64 bit platforms, and both big and little endian
+  systems.
+
+  Under 2.4 Linux kernels, kernel module loading was implemented in a
+  platform-specific manner.  Busybox's insmod utility has been reported to
+  work under ARM, CRIS, H8/300, x86, ia64, x86_64, m68k, MIPS, PowerPC, S390,
+  SH3/4/5, Sparc, v850e, and x86_64.  Anything else probably won't work.
+
+  The module loading mechanism for the 2.6 kernel is much more generic, and
+  we believe 2.6.x kernel module loading support should work on all
+  architectures supported by the kernel.
+
+----------------
+
+Please feed suggestions, bug reports, insults, and bribes back to the busybox
+mailing list:
+
+	busybox@busybox.net
+
+and/or maintainer:
+
+	Denys Vlasenko
+	<vda.linux@googlemail.com>
diff --git a/busybox-1.19.3/TODO b/busybox-1.19.3/TODO
new file mode 100644
index 0000000..8b9f87f
--- /dev/null
+++ b/busybox-1.19.3/TODO
@@ -0,0 +1,276 @@
+Busybox TODO
+
+Harvest patches from
+http://git.openembedded.org/cgit.cgi/openembedded/tree/recipes/busybox/
+
+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
+do one of these bounce an email off the person it's listed under to see if they
+have any suggestions how they plan to go about it, and to minimize conflicts
+between your work and theirs.  But otherwise, all of these are fair game.
+
+Rob Landley suggested this:
+  Implement bb_realpath() that can handle NULL on non-glibc.
+
+  sh
+    The command shell situation is a mess.  We have two different
+    shells that don't really share any code, and the "standalone shell" doesn't
+    work all that well (especially not in a chroot environment), due to apps not
+    being reentrant.
+
+  Do a SUSv3 audit
+    Look at the full Single Unix Specification version 3 (available online at
+    "http://www.opengroup.org/onlinepubs/009695399/nfindex.html") and
+    figure out which of our apps are compliant, and what we're missing that
+    we might actually care about.
+
+    Even better would be some kind of automated compliance test harness that
+    exercises each command line option and the various corner cases.
+
+  Internationalization
+    How much internationalization should we do?
+
+    The low hanging fruit is UTF-8 character set support.  We should do this.
+    See TODO_unicode file.
+
+    We also have lots of hardwired english text messages.  Consolidating this
+    into some kind of message table not only makes translation easier, but
+    also allows us to consolidate redundant (or close) strings.
+
+    We probably don't want to be bloated with locale support.  (Not unless we
+    can cleanly export it from our underlying C library without having to
+    concern ourselves with it directly.  Perhaps a few specific things like a
+    config option for "date" are low hanging fruit here?)
+
+    What level should things happen at?  How much do we care about
+    internationalizing the text console when X11 and xterms are so much better
+    at it?  (There's some infrastructure here we don't implement: The
+    "unicode_start" and "unicode_stop" shell scripts need "vt-is-UTF8" and a
+    --unicode option to loadkeys.  That implies a real loadkeys/dumpkeys
+    implementation to replace loadkmap/dumpkmap.  Plus messing with console font
+    loading.  Is it worth it, or do we just say "use X"?)
+
+  Individual compilation of applets.
+    It would be nice if busybox had the option to compile to individual applets,
+    for people who want an alternate implementation less bloated than the gnu
+    utils (or simply with less political baggage), but without it being one big
+    executable.
+
+    Turning libbb into a real dll is another possibility, especially if libbb
+    could export some of the other library interfaces we've already more or less
+    got the code for (like zlib).
+
+  buildroot - Make a "dogfood" option
+    Busybox 1.1 will be capable of replacing most gnu packages for real world
+    use, such as developing software or in a live CD.  It needs wider testing.
+
+    Busybox should now be able to replace bzip2, coreutils, e2fsprogs, file,
+    findutils, gawk, grep, inetutils, less, modutils, net-tools, patch, procps,
+    sed, shadow, sysklogd, sysvinit, tar, util-linux, and vim.  The resulting
+    system should be self-hosting (I.E. able to rebuild itself from source
+    code).  This means it would need (at least) binutils, gcc, and make, or
+    equivalents.
+
+    It would be a good "eating our own dogfood" test if buildroot had the option
+    of using a "make allyesconfig" busybox instead of the all of the above
+    packages.  Anything that's wrong with the resulting system, we can fix.  (It
+    would be nice to be able to upgrade busybox to be able to replace bash and
+    diffutils as well, but we're not there yet.)
+
+    One example of an existing system that does this already is Firmware Linux:
+      http://www.landley.net/code/firmware
+
+  initramfs
+    Busybox should have a sample initramfs build script.  This depends on
+    shell, mdev, and switch_root.
+
+  mkdep
+    Write a mkdep that doesn't segfault if there's a directory it doesn't
+    have permission to read, isn't based on manually editing the output of
+    lexx and yacc, doesn't make such a mess under include/config, etc.
+
+  Group globals into unions of structures.
+    Go through and turn all the global and static variables into structures,
+    and have all those structures be in a big union shared between processes,
+    so busybox uses less bss.  (This is a big win on nommu machines.)  See
+    sed.c and mdev.c for examples.
+
+  Go through bugs.busybox.net and close out all of that somehow.
+    This one's open to everybody, but I'll wind up doing it...
+
+Bernhard Reutner-Fischer <busybox@busybox.net> suggests to look at these:
+  New debug options:
+    -Wlarger-than-127
+    Cleanup any big users
+  Collate BUFSIZ IOBUF_SIZE MY_BUF_SIZE PIPE_PROGRESS_SIZE BUFSIZE PIPESIZE
+    make bb_common_bufsiz1 configurable, size wise.
+    make pipesize configurable, size wise.
+    Use bb_common_bufsiz1 throughout applets!
+
+As yet unclaimed:
+
+----
+diff
+  Make sure we handle empty files properly:
+    From the patch man page:
+
+    you can remove a file by sending out a context diff that compares
+    the file to be deleted with an empty file dated the Epoch.  The
+    file will be removed unless patch is conforming to POSIX and the
+    -E or --remove-empty-files option is not given.
+---
+patch
+  Should have simple fuzz factor support to apply patches at an offset which
+  shouldn't take up too much space.
+
+  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:
+
+bb_close() with fsync()
+  We should have a bb_close() in place of normal close, with a CONFIG_ option
+  to not just check the return value of close() for an error, but fsync().
+  Close can't reliably report anything useful because if write() accepted the
+  data then it either went out to the network or it's in cache or a pipe
+  buffer.  Either way, there's no guarantee it'll make it to its final
+  destination before close() gets called, so there's no guarantee that any
+  error will be reported.
+
+  You need to call fsync() if you care about errors that occur after write(),
+  but that can have a big performance impact.  So make it a config option.
+---
+Unify archivers
+  Lots of archivers have the same general infrastructure.  The directory
+  traversal code should be factored out, and the guts of each archiver could
+  be some setup code and a series of callbacks for "add this file",
+  "add this directory", "add this symlink" and so on.
+
+  This could clean up tar and zip, and make it cheaper to add cpio and ar
+  write support, and possibly even cheaply add things like mkisofs or
+  mksquashfs someday, if they become relevant.
+---
+Text buffer support.
+  Several existing applets (sort, vi, less...) read
+  a whole file into memory and act on it.  Use open_read_close().
+---
+Memory Allocation
+  We have a CONFIG_BUFFER mechanism that lets us select whether to do memory
+  allocation on the stack or the heap.  Unfortunately, we're not using it much.
+  We need to audit our memory allocations and turn a lot of malloc/free calls
+  into RESERVE_CONFIG_BUFFER/RELEASE_CONFIG_BUFFER.
+  For a start, see e.g. make EXTRA_CFLAGS=-Wlarger-than-64
+
+  And while we're at it, many of the CONFIG_FEATURE_CLEAN_UP #ifdefs will be
+  optimized out by the compiler in the stack allocation case (since there's no
+  free for an alloca()), and this means that various cleanup loops that just
+  call free might also be optimized out by the compiler if written right, so
+  we can yank those #ifdefs too, and generally clean up the code.
+---
+FEATURE_CLEAN_UP
+  This is more an unresolved issue than a to-do item.  More thought is needed.
+
+  Normally we rely on exit() to free memory, close files and unmap segments
+  for us.  This makes most calls to free(), close(), and unmap() optional in
+  busybox applets that don't intend to run for very long, and optional stuff
+  can be omitted to save size.
+
+  The idea was raised that we could simulate fork/exit with setjmp/longjmp
+  for _really_ brainless embedded systems, or speed up the standalone shell
+  by not forking.  Doing so would require a reliable FEATURE_CLEAN_UP.
+  Unfortunately, this isn't as easy as it sounds.
+
+  The problem is, lots of things exit(), sometimes unexpectedly (xmalloc())
+  and sometimes reliably (bb_perror_msg_and_die() or show_usage()).  This
+  jumps out of the normal flow control and bypasses any cleanup code we
+  put at the end of our applets.
+
+  It's possible to add hooks to libbb functions like xmalloc() and xopen()
+  to add their entries to a linked list, which could be traversed and
+  freed/closed automatically.  (This would need to be able to free just the
+  entries after a checkpoint to be usable for a forkless standalone shell.
+  You don't want to free the shell's own resources.)
+
+  Right now, FEATURE_CLEAN_UP is more or less a debugging aid, to make things
+  like valgrind happy.  It's also documentation of _what_ we're trusting
+  exit() to clean up for us.  But new infrastructure to auto-free stuff would
+  render the existing FEATURE_CLEAN_UP code redundant.
+
+  For right now, exit() handles it just fine.
+
+
+Minor stuff:
+  watchdog.c could autodetect the timer duration via:
+    if(!ioctl (fd, WDIOC_GETTIMEOUT, &tmo)) timer_duration = 1 + (tmo / 2);
+  Unfortunately, that needs linux/watchdog.h and that contains unfiltered
+  kernel types on some distros, which breaks the build.
+---
+  use bb_error_msg where appropriate: See
+  egrep "(printf.*\([[:space:]]*(stderr|2)|[^_]write.*\([[:space:]]*(stderr|2))"
+---
+  use bb_perror_msg where appropriate: See
+  egrep "[^_]perror"
+---
+  possible code duplication ingroup() and is_a_group_member()
+---
+  Move __get_hz() to a better place and (re)use it in route.c, ash.c
+---
+  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>
+---
+vdprintf() -> similar sized functionality
+---
+
+(TODO list after discussion 11.05.2009)
+
+* shrink tc/brctl/ip
+  tc/brctl seem like fairly large things to try and tackle in your timeframe,
+  and i think people have posted attempts in the past. Adding additional
+  options to ip though seems reasonable.
+
+* add tests for some applets
+
+* implement POSIX utilities and audit them for POSIX conformance. then
+  audit them for GNU conformance. then document all your findings in a new
+  doc/conformance.txt file while perhaps implementing some of the missing
+  features.
+  you can find the latest POSIX documentation (1003.1-2008) here:
+  http://www.opengroup.org/onlinepubs/9699919799/
+  and the complete list of all utilities that POSIX covers:
+  http://www.opengroup.org/onlinepubs/9699919799/idx/utilities.html
+  The first step would to generate a file/matrix what is already archived
+  (also IPV6)
+
+* implement 'at'
+
+* rpcbind (former portmap) or equivalent
+  so that we don't have to use -o nolock on nfs mounts
+
+* check IPV6 compliance
+
+* generate a mini example using kernel+busybox only (+libc) for example
+
+* more support for advanced linux 2.6.x features, see: iotop
+  most likely there is more
+
+* even more support for statistics: mpstat, iostat, powertop....
diff --git a/busybox-1.19.3/TODO_unicode b/busybox-1.19.3/TODO_unicode
new file mode 100644
index 0000000..b310e8d
--- /dev/null
+++ b/busybox-1.19.3/TODO_unicode
@@ -0,0 +1,45 @@
+Already fixed applets:
+cal
+lsmod
+df
+dumpleases
+
+Applets which may need unicode handling (more extensive than sanitizing
+of filenames in error messages):
+
+ls - work in progress
+expand, unexpand - uses unicode_strlen, not scrlen
+ash, hush through lineedit - uses unicode_strlen, not scrlen
+top - need to sanitize process args
+ps - need to sanitize process args
+less
+more
+vi
+ed
+cut
+awk
+sed
+tr
+grep egrep fgrep
+fold
+sort
+head, tail
+catv - "display nonprinting chars" - what this could mean for unicode?
+wc
+chat
+dumpkmap
+last - just line up columns
+man
+microcom
+strings
+watch
+
+Unsure, may need fixing:
+
+hostname - do we really want to protect against bad chars in it?
+patch
+addgroup, adduser, delgroup, deluser
+telnet
+telnetd
+od
+printf
diff --git a/busybox-1.19.3/applets/Kbuild.src b/busybox-1.19.3/applets/Kbuild.src
new file mode 100644
index 0000000..b612399
--- /dev/null
+++ b/busybox-1.19.3/applets/Kbuild.src
@@ -0,0 +1,47 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+obj-y :=
+obj-y += applets.o
+
+hostprogs-y:=
+hostprogs-y += usage usage_pod applet_tables
+
+always:= $(hostprogs-y)
+
+# Generated files need additional love
+
+# This trick decreases amount of rebuilds
+# if tree is merely renamed/copied
+ifeq ($(srctree),$(objtree))
+srctree_slash =
+else
+srctree_slash = $(srctree)/
+endif
+
+HOSTCFLAGS_usage.o = -I$(srctree_slash)include -Iinclude
+HOSTCFLAGS_usage_pod.o = -I$(srctree_slash)include -Iinclude
+
+applets/applets.o: include/usage_compressed.h include/applet_tables.h
+
+applets/applet_tables: .config include/applets.h
+applets/usage:         .config include/applets.h
+applets/usage_pod:     .config include/applet_tables.h include/applets.h
+
+quiet_cmd_gen_usage_compressed = GEN     include/usage_compressed.h
+      cmd_gen_usage_compressed = $(srctree_slash)applets/usage_compressed include/usage_compressed.h applets
+
+include/usage_compressed.h: applets/usage $(srctree_slash)applets/usage_compressed
+	$(call cmd,gen_usage_compressed)
+
+quiet_cmd_gen_applet_tables = GEN     include/applet_tables.h
+      cmd_gen_applet_tables = applets/applet_tables include/applet_tables.h include/NUM_APPLETS.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)
diff --git a/busybox-1.19.3/applets/applet_tables.c b/busybox-1.19.3/applets/applet_tables.c
new file mode 100644
index 0000000..a475747
--- /dev/null
+++ b/busybox-1.19.3/applets/applet_tables.c
@@ -0,0 +1,164 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Applet table generator.
+ * Runs on host and produces include/applet_tables.h
+ *
+ * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * 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/applet_metadata.h"
+
+struct bb_applet {
+	const char *name;
+	const char *main;
+	enum bb_install_loc_t install_loc;
+	enum bb_suid_t need_suid;
+	/* true if instead of fork(); exec("applet"); waitpid();
+	 * one can do fork(); exit(applet_main(argc,argv)); waitpid(); */
+	unsigned char noexec;
+	/* Even nicer */
+	/* true if instead of fork(); exec("applet"); waitpid();
+	 * one can simply call applet_main(argc,argv); */
+	unsigned char nofork;
+};
+
+/* Define struct bb_applet applets[] */
+#include "../include/applets.h"
+
+enum { NUM_APPLETS = ARRAY_SIZE(applets) };
+
+static int offset[NUM_APPLETS];
+
+static int cmp_name(const void *a, const void *b)
+{
+	const struct bb_applet *aa = a;
+	const struct bb_applet *bb = b;
+	return strcmp(aa->name, bb->name);
+}
+
+int main(int argc, char **argv)
+{
+	int i;
+	int ofs;
+	unsigned MAX_APPLET_NAME_LEN = 1;
+
+	qsort(applets, NUM_APPLETS, sizeof(applets[0]), cmp_name);
+
+	ofs = 0;
+	for (i = 0; i < NUM_APPLETS; i++) {
+		offset[i] = ofs;
+		ofs += strlen(applets[i].name) + 1;
+	}
+	/* We reuse 4 high-order bits of offset array for other purposes,
+	 * so if they are indeed needed, refuse to proceed */
+	if (ofs > 0xfff)
+		return 1;
+	if (!argv[1])
+		return 1;
+
+	i = open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0666);
+	if (i < 0)
+		return 1;
+	dup2(i, 1);
+
+	/* Keep in sync with include/busybox.h! */
+
+	printf("/* This is a generated file, don't edit */\n\n");
+
+	printf("#define NUM_APPLETS %u\n", NUM_APPLETS);
+	if (NUM_APPLETS == 1) {
+		char *dash_to_underscore, *p;
+		printf("#define SINGLE_APPLET_STR \"%s\"\n", applets[0].name);
+		/* Example: "ether-wake" -> "ether_wake" */
+		p = dash_to_underscore = strdup(applets[0].name);
+		p--;
+		while (*++p)
+			if (*p == '-')
+				*p = '_';
+		printf("#define SINGLE_APPLET_MAIN %s_main\n", dash_to_underscore);
+	}
+	printf("\n");
+
+	//printf("#ifndef SKIP_definitions\n");
+	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);
+	}
+	printf(";\n\n");
+
+	printf("#ifndef SKIP_applet_main\n");
+	printf("int (*const applet_main[])(int argc, char **argv) = {\n");
+	for (i = 0; i < NUM_APPLETS; i++) {
+		printf("%s_main,\n", applets[i].main);
+	}
+	printf("};\n");
+	printf("#endif\n\n");
+
+	printf("const uint16_t applet_nameofs[] ALIGN2 = {\n");
+	for (i = 0; i < NUM_APPLETS; i++) {
+		printf("0x%04x,\n",
+			offset[i]
+#if ENABLE_FEATURE_PREFER_APPLETS
+			+ (applets[i].nofork << 12)
+			+ (applets[i].noexec << 13)
+#endif
+#if ENABLE_FEATURE_SUID
+			+ (applets[i].need_suid << 14) /* 2 bits */
+#endif
+		);
+	}
+	printf("};\n\n");
+
+#if ENABLE_FEATURE_INSTALLER
+	printf("const uint8_t applet_install_loc[] ALIGN1 = {\n");
+	i = 0;
+	while (i < NUM_APPLETS) {
+		int v = applets[i].install_loc; /* 3 bits */
+		if (++i < NUM_APPLETS)
+			v |= applets[i].install_loc << 4; /* 3 bits */
+		printf("0x%02x,\n", v);
+		i++;
+	}
+	printf("};\n");
+#endif
+	//printf("#endif /* SKIP_definitions */\n");
+	printf("\n");
+	printf("#define MAX_APPLET_NAME_LEN %u\n", MAX_APPLET_NAME_LEN);
+
+	if (argv[2]) {
+		char line_old[80];
+		char line_new[80];
+		FILE *fp;
+
+		line_old[0] = 0;
+		fp = fopen(argv[2], "r");
+		if (fp) {
+			fgets(line_old, sizeof(line_old), fp);
+			fclose(fp);
+		}
+		sprintf(line_new, "#define NUM_APPLETS %u\n", NUM_APPLETS);
+		if (strcmp(line_old, line_new) != 0) {
+			fp = fopen(argv[2], "w");
+			if (!fp)
+				return 1;
+			fputs(line_new, fp);
+		}
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/applets/applets.c b/busybox-1.19.3/applets/applets.c
new file mode 100644
index 0000000..98c2b44
--- /dev/null
+++ b/busybox-1.19.3/applets/applets.c
@@ -0,0 +1,16 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Stub for linking busybox binary against libbusybox.
+ *
+ * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "busybox.h"
+
+#if ENABLE_BUILD_LIBBUSYBOX
+int main(int argc UNUSED_PARAM, char **argv)
+{
+	return lbb_main(argv);
+}
+#endif
diff --git a/busybox-1.19.3/applets/busybox.mkll b/busybox-1.19.3/applets/busybox.mkll
new file mode 100755
index 0000000..68dbf21
--- /dev/null
+++ b/busybox-1.19.3/applets/busybox.mkll
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Make busybox links list file.
+
+# input $1: full path to Config.h
+# input $2: full path to applets.h
+# output (stdout): list of pathnames that should be linked to busybox
+
+# Maintainer: Larry Doolittle <ldoolitt@recycle.lbl.gov>
+
+export LC_ALL=POSIX
+export LC_CTYPE=POSIX
+
+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,7)
+	gsub("_","/",dir)
+	if(dir=="/ROOT") dir=""
+	file=$3
+	gsub("\"","",file)
+	if (file=="busybox") next
+	print tolower(dir) "/" file
+  }'
diff --git a/busybox-1.19.3/applets/individual.c b/busybox-1.19.3/applets/individual.c
new file mode 100644
index 0000000..1e74e4c
--- /dev/null
+++ b/busybox-1.19.3/applets/individual.c
@@ -0,0 +1,24 @@
+/* Minimal wrapper to build an individual busybox applet.
+ *
+ * Copyright 2005 Rob Landley <rob@landley.net
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+const char *applet_name;
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "usage.h"
+
+int main(int argc, char **argv)
+{
+	applet_name = argv[0];
+	return APPLET_main(argc,argv);
+}
+
+void bb_show_usage(void)
+{
+	fputs(APPLET_full_usage "\n", stdout);
+	exit(EXIT_FAILURE);
+}
diff --git a/busybox-1.19.3/applets/install.sh b/busybox-1.19.3/applets/install.sh
new file mode 100755
index 0000000..95b4719
--- /dev/null
+++ b/busybox-1.19.3/applets/install.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+export LC_ALL=POSIX
+export LC_CTYPE=POSIX
+
+prefix=$1
+if [ -z "$prefix" ]; then
+	echo "usage: applets/install.sh DESTINATION [--symlinks/--hardlinks/--scriptwrapper]"
+	exit 1
+fi
+
+h=`sort busybox.links | uniq`
+
+linkopts=""
+scriptwrapper="n"
+cleanup="0"
+noclobber="0"
+case "$2" in
+	--hardlinks)     linkopts="-f";;
+	--symlinks)      linkopts="-fs";;
+	--scriptwrapper) scriptwrapper="y";swrapall="y";;
+	--sw-sh-hard)    scriptwrapper="y";linkopts="-f";;
+	--sw-sh-sym)     scriptwrapper="y";linkopts="-fs";;
+	--cleanup)       cleanup="1";;
+	--noclobber)     noclobber="1";;
+	"")              h="";;
+	*)               echo "Unknown install option: $2"; exit 1;;
+esac
+
+if [ -n "$DO_INSTALL_LIBS" ] && [ "$DO_INSTALL_LIBS" != "n" ]; then
+	# get the target dir for the libs
+	# assume it starts with lib
+	libdir=$($CC -print-file-name=libc.so | \
+		 sed -n 's%^.*\(/lib[^\/]*\)/libc.so%\1%p')
+	if test -z "$libdir"; then
+		libdir=/lib
+	fi
+
+	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
+		fi
+	done
+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
+		`
+	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
+
+for i in $h; do
+	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
+		else
+			rm -f "$prefix/$i"
+			echo "#!/bin/busybox" >"$prefix/$i"
+			chmod +x "$prefix/$i"
+		fi
+		echo "	$prefix/$i"
+	else
+		if [ "$2" = "--hardlinks" ]; then
+			bb_path="$prefix/bin/busybox"
+		else
+			case "$appdir" in
+			/)
+				bb_path="bin/busybox"
+			;;
+			/bin)
+				bb_path="busybox"
+			;;
+			/sbin)
+				bb_path="../bin/busybox"
+			;;
+			/usr/bin | /usr/sbin)
+				bb_path="../../bin/busybox"
+			;;
+			*)
+				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
+		else
+			echo "  $prefix/$i already exists"
+		fi
+	fi
+done
+
+exit 0
diff --git a/busybox-1.19.3/applets/usage.c b/busybox-1.19.3/applets/usage.c
new file mode 100644
index 0000000..94520ff
--- /dev/null
+++ b/busybox-1.19.3/applets/usage.c
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2008 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "autoconf.h"
+
+/* Since we can't use platform.h, have to do this again by hand: */
+#if ENABLE_NOMMU
+# define BB_MMU 0
+# define USE_FOR_NOMMU(...) __VA_ARGS__
+# define USE_FOR_MMU(...)
+#else
+# define BB_MMU 1
+# define USE_FOR_NOMMU(...)
+# define USE_FOR_MMU(...) __VA_ARGS__
+#endif
+
+#include "usage.h"
+#define MAKE_USAGE(aname, usage) { aname, usage },
+static struct usage_data {
+	const char *aname;
+	const char *usage;
+} usage_array[] = {
+#include "applets.h"
+};
+
+static int compare_func(const void *a, const void *b)
+{
+	const struct usage_data *ua = a;
+	const struct usage_data *ub = b;
+	return strcmp(ua->aname, ub->aname);
+}
+
+int main(void)
+{
+	int i;
+	int num_messages = sizeof(usage_array) / sizeof(usage_array[0]);
+
+	if (num_messages == 0)
+		return 0;
+
+	qsort(usage_array,
+		num_messages, sizeof(usage_array[0]),
+		compare_func);
+	for (i = 0; i < num_messages; i++)
+		write(STDOUT_FILENO, usage_array[i].usage, strlen(usage_array[i].usage) + 1);
+
+	return 0;
+}
diff --git a/busybox-1.19.3/applets/usage_compressed b/busybox-1.19.3/applets/usage_compressed
new file mode 100755
index 0000000..af66bc5
--- /dev/null
+++ b/busybox-1.19.3/applets/usage_compressed
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+target="$1"
+loc="$2"
+
+test "$target" || exit 1
+test "$loc" || loc=.
+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 -t x1 </dev/null >/dev/null
+if test $? != 0; then
+	echo 'od tool is not installed or cannot accept "-v -t x1" options'
+	exit 1
+fi
+
+exec >"$target.$$"
+
+echo '#define UNPACKED_USAGE "" \'
+"$loc/usage" | od -v -t x1 \
+| $SED -e 's/^[^ ]*//' \
+	-e 's/ //g' \
+	-e '/^$/d' \
+	-e 's/\(..\)/\\x\1/g' \
+	-e 's/^/"/' \
+	-e 's/$/" \\/'
+echo ''
+
+echo '#define PACKED_USAGE \'
+## Breaks on big-endian systems!
+## # Extra effort to avoid using "od -t x1": -t is not available
+## # in non-CONFIG_DESKTOPed busybox od
+##
+## "$loc/usage" | bzip2 -1 | od -v -x \
+## | $SED -e 's/^[^ ]*//' \
+##	-e 's/ //g' \
+##	-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 \
+| $SED -e 's/^[^ ]*//' \
+	-e 's/ //g' \
+	-e '/^$/d' \
+	-e 's/\(..\)/0x\1,/g' \
+	-e 's/$/ \\/'
+echo ''
+
+mv -- "$target.$$" "$target"
diff --git a/busybox-1.19.3/applets/usage_pod.c b/busybox-1.19.3/applets/usage_pod.c
new file mode 100644
index 0000000..0b1c4aa
--- /dev/null
+++ b/busybox-1.19.3/applets/usage_pod.c
@@ -0,0 +1,111 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2009 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include <unistd.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "autoconf.h"
+
+#define SKIP_applet_main
+#define ALIGN1 /* nothing, just to placate applet_tables.h */
+#define ALIGN2 /* nothing, just to placate applet_tables.h */
+#include "applet_tables.h"
+
+/* Since we can't use platform.h, have to do this again by hand: */
+#if ENABLE_NOMMU
+# define BB_MMU 0
+# define USE_FOR_NOMMU(...) __VA_ARGS__
+# define USE_FOR_MMU(...)
+#else
+# define BB_MMU 1
+# define USE_FOR_NOMMU(...)
+# define USE_FOR_MMU(...) __VA_ARGS__
+#endif
+
+#include "usage.h"
+#define MAKE_USAGE(aname, usage) { aname, usage },
+static struct usage_data {
+	const char *aname;
+	const char *usage;
+} usage_array[] = {
+#include "applets.h"
+};
+
+static int compare_func(const void *a, const void *b)
+{
+	const struct usage_data *ua = a;
+	const struct usage_data *ub = b;
+	return strcmp(ua->aname, ub->aname);
+}
+
+int main(void)
+{
+	int col, len2;
+
+	int i;
+	int num_messages = sizeof(usage_array) / sizeof(usage_array[0]);
+
+	if (num_messages == 0)
+		return 0;
+
+	qsort(usage_array,
+		num_messages, sizeof(usage_array[0]),
+		compare_func);
+
+	col = 0;
+	for (i = 0; i < num_messages; i++) {
+		len2 = strlen(usage_array[i].aname) + 2;
+		if (col >= 76 - len2) {
+			printf(",\n");
+			col = 0;
+		}
+		if (col == 0) {
+			col = 6;
+			printf("\t");
+		} else {
+			printf(", ");
+		}
+		printf(usage_array[i].aname);
+		col += len2;
+	}
+	printf("\n\n");
+
+	printf("=head1 COMMAND DESCRIPTIONS\n\n");
+	printf("=over 4\n\n");
+
+	for (i = 0; i < num_messages; i++) {
+		if (usage_array[i].aname[0] >= 'a' && usage_array[i].aname[0] <= 'z'
+		 && usage_array[i].usage[0] != NOUSAGE_STR[0]
+		) {
+			printf("=item B<%s>\n\n", usage_array[i].aname);
+			if (usage_array[i].usage[0])
+				printf("%s %s\n\n", usage_array[i].aname, usage_array[i].usage);
+			else
+				printf("%s\n\n", usage_array[i].aname);
+		}
+	}
+	return 0;
+}
+
+/* TODO: we used to make options bold with B<> and output an example too:
+
+=item B<cat>
+
+cat [B<-u>] [FILE]...
+
+Concatenate FILE(s) and print them to stdout
+
+Options:
+        -u      Use unbuffered i/o (ignored)
+
+Example:
+        $ cat /proc/uptime
+        110716.72 17.67
+
+*/
diff --git a/busybox-1.19.3/arch/i386/Makefile b/busybox-1.19.3/arch/i386/Makefile
new file mode 100644
index 0000000..e6c99c6
--- /dev/null
+++ b/busybox-1.19.3/arch/i386/Makefile
@@ -0,0 +1,7 @@
+# ==========================================================================
+# Build system
+# ==========================================================================
+
+# -mpreferred-stack-boundary=2 is essential in preventing gcc 4.2.x
+# from aligning stack to 16 bytes. (Which is gcc's way of supporting SSE).
+CFLAGS += $(call cc-option,-march=i386 -mpreferred-stack-boundary=2,)
diff --git a/busybox-1.19.3/archival/Config.src b/busybox-1.19.3/archival/Config.src
new file mode 100644
index 0000000..81788ec
--- /dev/null
+++ b/busybox-1.19.3/archival/Config.src
@@ -0,0 +1,379 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Archival Utilities"
+
+INSERT
+
+config FEATURE_SEAMLESS_XZ
+	bool "Make tar, rpm, modprobe etc understand .xz data"
+	default y
+	help
+	  Make tar, rpm, modprobe etc understand .xz data.
+
+config FEATURE_SEAMLESS_LZMA
+	bool "Make tar, rpm, modprobe etc understand .lzma data"
+	default y
+	help
+	  Make tar, rpm, modprobe etc understand .lzma data.
+
+config FEATURE_SEAMLESS_BZ2
+	bool "Make tar, rpm, modprobe etc understand .bz2 data"
+	default y
+	help
+	  Make tar, rpm, modprobe etc understand .bz2 data.
+
+config FEATURE_SEAMLESS_GZ
+	bool "Make tar, rpm, modprobe etc understand .gz data"
+	default y
+	help
+	  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 compression 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 a 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"
+	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.
+
+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.
+
+endmenu
diff --git a/busybox-1.19.3/archival/Kbuild.src b/busybox-1.19.3/archival/Kbuild.src
new file mode 100644
index 0000000..3466452
--- /dev/null
+++ b/busybox-1.19.3/archival/Kbuild.src
@@ -0,0 +1,30 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+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 bbunzip.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
diff --git a/busybox-1.19.3/archival/ar.c b/busybox-1.19.3/archival/ar.c
new file mode 100644
index 0000000..acad20f
--- /dev/null
+++ b/busybox-1.19.3/archival/ar.c
@@ -0,0 +1,261 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ar implementation for busybox
+ *
+ * Copyright (C) 2000 by Glenn McGrath
+ *
+ * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Archive creation support:
+ * Copyright (C) 2010 Nokia Corporation. All rights reserved.
+ * Written by Alexander Shishkin.
+ *
+ * There is no single standard to adhere to so ar may not portable
+ * between different systems
+ * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
+ */
+
+//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 "archive.h"
+#include "ar.h"
+
+#if ENABLE_FEATURE_AR_CREATE
+/* filter out entries with same names as specified on the command line */
+static char FAST_FUNC filter_replaceable(archive_handle_t *handle)
+{
+	if (find_list_entry(handle->accept, handle->file_header->name))
+		return EXIT_FAILURE;
+
+	return EXIT_SUCCESS;
+}
+
+static void output_ar_header(archive_handle_t *handle)
+{
+	/* GNU ar 2.19.51.0.14 creates malformed archives
+	 * if input files are >10G. It also truncates files >4GB
+	 * (uses "size mod 4G"). We abort in this case:
+	 * We could add support for up to 10G files, but this is unlikely to be useful.
+	 * Note that unpacking side limits all fields to "unsigned int" data type,
+	 * and treats "all ones" as an error indicator. Thus max we allow here is UINT_MAX-1.
+	 */
+	enum {
+		/* for 2nd field: mtime */
+		MAX11CHARS = UINT_MAX > 0xffffffff ? (unsigned)99999999999 : UINT_MAX-1,
+		/* for last field: filesize */
+		MAX10CHARS = UINT_MAX > 0xffffffff ? (unsigned)9999999999 : UINT_MAX-1,
+	};
+
+	struct file_header_t *fh = handle->file_header;
+
+	if (handle->offset & 1) {
+		xwrite(handle->src_fd, "\n", 1);
+		handle->offset++;
+	}
+
+	/* Careful! The widths should be exact. Fields must be separated */
+	if (sizeof(off_t) > 4 && fh->size > (off_t)MAX10CHARS) {
+		bb_error_msg_and_die("'%s' is bigger than ar can handle", fh->name);
+	}
+	fdprintf(handle->src_fd, "%-16.16s%-12lu%-6u%-6u%-8o%-10"OFF_FMT"u`\n",
+			fh->name,
+			(sizeof(time_t) > 4 && fh->mtime > MAX11CHARS) ? (long)0 : (long)fh->mtime,
+			fh->uid > 99999 ? 0 : (int)fh->uid,
+			fh->gid > 99999 ? 0 : (int)fh->gid,
+			(int)fh->mode & 07777777,
+			fh->size
+	);
+
+	handle->offset += AR_HEADER_LEN;
+}
+
+/*
+ * 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)
+{
+	archive_handle_t *out_handle = handle->ar__out;
+	struct file_header_t *fh = handle->file_header;
+
+	out_handle->file_header = fh;
+	output_ar_header(out_handle);
+
+	bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size);
+	out_handle->offset += fh->size;
+}
+
+static int write_ar_header(archive_handle_t *handle)
+{
+	char *fn;
+	char fn_h[17]; /* 15 + "/" + NUL */
+	struct stat st;
+	int fd;
+
+	fn = llist_pop(&handle->accept);
+	if (!fn)
+		return -1;
+
+	xstat(fn, &st);
+
+	handle->file_header->mtime = st.st_mtime;
+	handle->file_header->uid = st.st_uid;
+	handle->file_header->gid = st.st_gid;
+	handle->file_header->mode = st.st_mode;
+	handle->file_header->size = st.st_size;
+	handle->file_header->name = fn_h;
+//TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES...
+	sprintf(fn_h, "%.15s/", bb_basename(fn));
+
+	output_ar_header(handle);
+
+	fd = xopen(fn, O_RDONLY);
+	bb_copyfd_exact_size(fd, handle->src_fd, st.st_size);
+	close(fd);
+	handle->offset += st.st_size;
+
+	return 0;
+}
+
+static int write_ar_archive(archive_handle_t *handle)
+{
+	struct stat st;
+	archive_handle_t *out_handle;
+
+	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.
+	 */
+	if (st.st_size != 0) {
+		out_handle = init_handle();
+		xunlink(handle->ar__name);
+		out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC);
+		out_handle->accept = handle->accept;
+	} else {
+		out_handle = handle;
+	}
+
+	handle->ar__out = out_handle;
+
+	xwrite(out_handle->src_fd, AR_MAGIC "\n", AR_MAGIC_LEN + 1);
+	out_handle->offset += AR_MAGIC_LEN + 1;
+
+	/* skip to the end of the archive if we have to append stuff */
+	if (st.st_size != 0) {
+		handle->filter = filter_replaceable;
+		handle->action_data = copy_data;
+		unpack_ar_archive(handle);
+	}
+
+	while (write_ar_header(out_handle) == 0)
+		continue;
+
+	/* optional, since we exit right after we return */
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(handle->src_fd);
+		if (out_handle->src_fd != handle->src_fd)
+			close(out_handle->src_fd);
+	}
+
+	return EXIT_SUCCESS;
+}
+#endif /* FEATURE_AR_CREATE */
+
+static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header)
+{
+	const char *mode = bb_mode_string(file_header->mode);
+	char *mtime;
+
+	mtime = ctime(&file_header->mtime);
+	mtime[16] = ' ';
+	memmove(&mtime[17], &mtime[20], 4);
+	mtime[21] = '\0';
+	printf("%s %u/%u%7"OFF_FMT"u %s %s\n", &mode[1],
+			(int)file_header->uid, (int)file_header->gid,
+			file_header->size,
+			&mtime[4], file_header->name
+	);
+}
+
+#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_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)
+{
+	archive_handle_t *archive_handle;
+	unsigned opt, t;
+
+	archive_handle = init_handle();
+
+	/* --: prepend '-' to the first argument if required */
+	/* -1: at least one param is reqd */
+	/* one of p,t,x[,r] is required */
+	opt_complementary = "--:-1:p:t:x"IF_FEATURE_AR_CREATE(":r");
+	opt = getopt32(argv, "voc""ptx"IF_FEATURE_AR_CREATE("r"));
+	argv += optind;
+
+	t = opt / FIRST_CMD;
+	if (t & (t-1)) /* more than one of p,t,x[,r] are specified */
+		bb_show_usage();
+
+	if (opt & AR_CMD_PRINT) {
+		archive_handle->action_data = data_extract_to_stdout;
+	}
+	if (opt & AR_CMD_LIST) {
+		archive_handle->action_header = header_list;
+	}
+	if (opt & AR_CMD_EXTRACT) {
+		archive_handle->action_data = data_extract_all;
+	}
+	if (opt & AR_OPT_PRESERVE_DATE) {
+		archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
+	}
+	if (opt & AR_OPT_VERBOSE) {
+		archive_handle->action_header = header_verbose_list_ar;
+	}
+#if ENABLE_FEATURE_AR_CREATE
+	archive_handle->ar__name = *argv;
+#endif
+	archive_handle->src_fd = xopen(*argv++,
+			(opt & AR_CMD_INSERT)
+				? O_RDWR | O_CREAT
+				: O_RDONLY
+	);
+
+	if (*argv)
+		archive_handle->filter = filter_accept_list;
+	while (*argv) {
+		llist_add_to_end(&archive_handle->accept, *argv++);
+	}
+
+#if ENABLE_FEATURE_AR_CREATE
+	if (opt & AR_CMD_INSERT)
+		return write_ar_archive(archive_handle);
+#endif
+
+	unpack_ar_archive(archive_handle);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/bbunzip.c b/busybox-1.19.3/archival/bbunzip.c
new file mode 100644
index 0000000..bb1ec0e
--- /dev/null
+++ b/busybox-1.19.3/archival/bbunzip.c
@@ -0,0 +1,472 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Common code for gunzip-like applets
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "archive.h"
+
+enum {
+	OPT_STDOUT     = 1 << 0,
+	OPT_FORCE      = 1 << 1,
+	/* only some decompressors: */
+	OPT_VERBOSE    = 1 << 2,
+	OPT_DECOMPRESS = 1 << 3,
+	OPT_TEST       = 1 << 4,
+};
+
+static
+int open_to_or_warn(int to_fd, const char *filename, int flags, int mode)
+{
+	int fd = open3_or_warn(filename, flags, mode);
+	if (fd < 0) {
+		return 1;
+	}
+	xmove_fd(fd, to_fd);
+	return 0;
+}
+
+char* FAST_FUNC append_ext(char *filename, const char *expected_ext)
+{
+	return xasprintf("%s.%s", filename, expected_ext);
+}
+
+int FAST_FUNC 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
+)
+{
+	struct stat stat_buf;
+	IF_DESKTOP(long long) int status;
+	char *filename, *new_name;
+	smallint exitcode = 0;
+	unpack_info_t info;
+
+	do {
+		/* NB: new_name is *maybe* malloc'ed! */
+		new_name = NULL;
+		filename = *argv; /* can be NULL - 'streaming' bunzip2 */
+
+		if (filename && LONE_DASH(filename))
+			filename = NULL;
+
+		/* Open src */
+		if (filename) {
+			if (stat(filename, &stat_buf) != 0) {
+				bb_simple_perror_msg(filename);
+ err:
+				exitcode = 1;
+				goto free_name;
+			}
+			if (open_to_or_warn(STDIN_FILENO, filename, O_RDONLY, 0))
+				goto err;
+		}
+
+		/* Special cases: test, stdout */
+		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;
+			filename = NULL;
+		}
+
+		/* Open dst if we are going to unpack to file */
+		if (filename) {
+			new_name = make_new_name(filename, expected_ext);
+			if (!new_name) {
+				bb_error_msg("%s: unknown suffix - ignored", filename);
+				goto err;
+			}
+
+			/* -f: overwrite existing output files */
+			if (option_mask32 & OPT_FORCE) {
+				unlink(new_name);
+			}
+
+			/* O_EXCL: "real" bunzip2 doesn't overwrite files */
+			/* GNU gunzip does not bail out, but goes to next file */
+			if (open_to_or_warn(STDOUT_FILENO, new_name, O_WRONLY | O_CREAT | O_EXCL,
+					stat_buf.st_mode))
+				goto err;
+		}
+
+		/* Check that the input is sane */
+		if (isatty(STDIN_FILENO) && (option_mask32 & OPT_FORCE) == 0) {
+			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 (filename) {
+			char *del = new_name;
+			if (status >= 0) {
+				/* TODO: restore other things? */
+				if (info.mtime) {
+					struct timeval times[2];
+
+					times[1].tv_sec = times[0].tv_sec = info.mtime;
+					times[1].tv_usec = times[0].tv_usec = 0;
+					/* Note: we closed it first.
+					 * On some systems calling utimes
+					 * then closing resets the mtime
+					 * back to current time. */
+					utimes(new_name, times); /* ignoring errors */
+				}
+
+				/* Delete _compressed_ 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);
+
+	return exitcode;
+}
+
+#if ENABLE_UNCOMPRESS || ENABLE_BUNZIP2 || ENABLE_UNLZMA || ENABLE_UNXZ
+static
+char* FAST_FUNC make_new_name_generic(char *filename, const char *expected_ext)
+{
+	char *extension = strrchr(filename, '.');
+	if (!extension || strcmp(extension + 1, expected_ext) != 0) {
+		/* Mimic GNU gunzip - "real" bunzip2 tries to */
+		/* unpack file anyway, to file.out */
+		return NULL;
+	}
+	*extension = '\0';
+	return filename;
+}
+#endif
+
+
+/*
+ * Uncompress applet for busybox (c) 2002 Glenn McGrath
+ *
+ * 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"
+
+#if ENABLE_UNCOMPRESS
+static
+IF_DESKTOP(long long) int FAST_FUNC unpack_uncompress(unpack_info_t *info UNUSED_PARAM)
+{
+	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;
+}
+int uncompress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int uncompress_main(int argc UNUSED_PARAM, char **argv)
+{
+	getopt32(argv, "cf");
+	argv += optind;
+
+	return bbunpack(argv, unpack_uncompress, make_new_name_generic, "Z");
+}
+#endif
+
+
+/*
+ * Gzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support files as
+ * well as stdin/stdout, and to generally behave itself wrt command line
+ * handling.
+ *
+ * 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 source tree.
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * The unzip code was written and put in the public domain by Mark Adler.
+ * 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"
+
+#if ENABLE_GUNZIP
+static
+char* FAST_FUNC make_new_name_gunzip(char *filename, const char *expected_ext UNUSED_PARAM)
+{
+	char *extension = strrchr(filename, '.');
+
+	if (!extension)
+		return NULL;
+
+	extension++;
+	if (strcmp(extension, "tgz" + 1) == 0
+#if ENABLE_FEATURE_SEAMLESS_Z
+	 || (extension[0] == 'Z' && extension[1] == '\0')
+#endif
+	) {
+		extension[-1] = '\0';
+	} else if (strcmp(extension, "tgz") == 0) {
+		filename = xstrdup(filename);
+		extension = strrchr(filename, '.');
+		extension[2] = 'a';
+		extension[3] = 'r';
+	} else {
+		return NULL;
+	}
+	return filename;
+}
+static
+IF_DESKTOP(long long) int FAST_FUNC unpack_gunzip(unpack_info_t *info)
+{
+	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;
+}
+/*
+ * Linux kernel build uses gzip -d -n. We accept and ignore it.
+ * Man page says:
+ * -n --no-name
+ * gzip: do not save the original file name and time stamp.
+ * (The original name is always saved if the name had to be truncated.)
+ * gunzip: do not restore the original file name/time even if present
+ * (remove only the gzip suffix from the compressed file name).
+ * This option is the default when decompressing.
+ * -N --name
+ * gzip: always save the original file name and time stamp (this is the default)
+ * gunzip: restore the original file name and time stamp if present.
+ */
+int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int gunzip_main(int argc UNUSED_PARAM, char **argv)
+{
+	getopt32(argv, "cfvdtn");
+	argv += optind;
+	/* if called as zcat */
+	if (applet_name[1] == 'c')
+		option_mask32 |= OPT_STDOUT;
+
+	return bbunpack(argv, unpack_gunzip, make_new_name_gunzip, /*unused:*/ NULL);
+}
+#endif
+
+
+/*
+ * 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 source tree.
+ */
+//usage:#define bunzip2_trivial_usage
+//usage:       "[-cf] [FILE]..."
+//usage:#define bunzip2_full_usage "\n\n"
+//usage:       "Decompress FILEs (or stdin)\n"
+//usage:     "\n	-c	Write to stdout"
+//usage:     "\n	-f	Force"
+//usage:#define bzcat_trivial_usage
+//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))
+#if ENABLE_BUNZIP2
+static
+IF_DESKTOP(long long) int FAST_FUNC unpack_bunzip2(unpack_info_t *info UNUSED_PARAM)
+{
+	return unpack_bz2_stream_prime(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");
+	argv += optind;
+	if (applet_name[2] == 'c') /* bzcat */
+		option_mask32 |= OPT_STDOUT;
+
+	return bbunpack(argv, unpack_bunzip2, make_new_name_generic, "bz2");
+}
+#endif
+
+
+/*
+ * Small lzma deflate implementation.
+ * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * Based on bunzip.c from busybox
+ *
+ * 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"
+
+#if ENABLE_UNLZMA
+static
+IF_DESKTOP(long long) int FAST_FUNC unpack_unlzma(unpack_info_t *info UNUSED_PARAM)
+{
+	return unpack_lzma_stream(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 ENABLE_LZMA
+	/* lzma without -d or -t? */
+	if (applet_name[2] == 'm' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
+		bb_show_usage();
+# endif
+	/* lzcat? */
+	if (applet_name[2] == 'c')
+		option_mask32 |= OPT_STDOUT;
+
+	argv += optind;
+	return bbunpack(argv, unpack_unlzma, make_new_name_generic, "lzma");
+}
+#endif
+
+
+#if ENABLE_UNXZ
+static
+IF_DESKTOP(long long) int FAST_FUNC unpack_unxz(unpack_info_t *info UNUSED_PARAM)
+{
+	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);
+}
+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 ENABLE_XZ
+	/* xz without -d or -t? */
+	if (applet_name[2] == '\0' && !(opts & (OPT_DECOMPRESS|OPT_TEST)))
+		bb_show_usage();
+# endif
+	/* xzcat? */
+	if (applet_name[2] == 'c')
+		option_mask32 |= OPT_STDOUT;
+
+	argv += optind;
+	return bbunpack(argv, unpack_unxz, make_new_name_generic, "xz");
+}
+#endif
diff --git a/busybox-1.19.3/archival/bbunzip_test.sh b/busybox-1.19.3/archival/bbunzip_test.sh
new file mode 100644
index 0000000..b8e31bf
--- /dev/null
+++ b/busybox-1.19.3/archival/bbunzip_test.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+# Test that concatenated gz files are unpacking correctly.
+# It also tests that unpacking in general is working right.
+# Since zip code has many corner cases, run it for a few hours
+# to get a decent coverage (200000 tests or more).
+
+gzip="gzip"
+gunzip="../busybox gunzip"
+# Or the other way around:
+#gzip="../busybox gzip"
+#gunzip="gunzip"
+
+c=0
+i=$PID
+while true; do
+    c=$((c+1))
+
+    # RANDOM is not very random on some shells. Spice it up.
+    # 100003 is prime
+    len1=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 ))
+    i=$((i * 1664525 + 1013904223))
+    len2=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 ))
+
+    # Just using urandom will make gzip use method 0 (store) -
+    # not good for test coverage!
+    cat /dev/urandom | while true; do read junk; echo "junk $c $i $junk"; done \
+    | dd bs=$len1 count=1 >z1 2>/dev/null
+    cat /dev/urandom | while true; do read junk; echo "junk $c $i $junk"; done \
+    | dd bs=$len2 count=1 >z2 2>/dev/null
+
+    $gzip <z1 >zz.gz
+    $gzip <z2 >>zz.gz
+    $gunzip -c zz.gz >z9 || {
+	echo "Exitcode $?"
+	exit
+    }
+    sum=`cat z1 z2 | md5sum`
+    sum9=`md5sum <z9`
+    test "$sum" == "$sum9" || {
+	echo "md5sums don't match"
+	exit
+    }
+    echo "Test $c ok: len1=$len1 len2=$len2 sum=$sum"
+
+    sum=`cat z1 z2 z1 z2 | md5sum`
+    rm z1.gz z2.gz 2>/dev/null
+    $gzip z1
+    $gzip z2
+    cat z1.gz z2.gz z1.gz z2.gz >zz.gz
+    $gunzip -c zz.gz >z9 || {
+	echo "Exitcode $? (2)"
+	exit
+    }
+    sum9=`md5sum <z9`
+    test "$sum" == "$sum9" || {
+	echo "md5sums don't match (1)"
+	exit
+    }
+
+    echo "Test $c ok: len1=$len1 len2=$len2 sum=$sum (2)"
+done
diff --git a/busybox-1.19.3/archival/bbunzip_test2.sh b/busybox-1.19.3/archival/bbunzip_test2.sh
new file mode 100644
index 0000000..5b7e83e
--- /dev/null
+++ b/busybox-1.19.3/archival/bbunzip_test2.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Leak test for gunzip. Watch top for growing process size.
+
+# Just using urandom will make gzip use method 0 (store) -
+# not good for test coverage!
+
+cat /dev/urandom \
+| while true; do read junk; echo "junk $RANDOM $junk"; done \
+| ../busybox gzip \
+| ../busybox gunzip -c >/dev/null
diff --git a/busybox-1.19.3/archival/bbunzip_test3.sh b/busybox-1.19.3/archival/bbunzip_test3.sh
new file mode 100644
index 0000000..2dc4afd
--- /dev/null
+++ b/busybox-1.19.3/archival/bbunzip_test3.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Leak test for gunzip. Watch top for growing process size.
+# In this case we look for leaks in "concatenated .gz" code -
+# we feed gunzip with a stream of .gz files.
+
+i=$PID
+c=0
+while true; do
+    c=$((c + 1))
+    echo "Block# $c" >&2
+    # RANDOM is not very random on some shells. Spice it up.
+    i=$((i * 1664525 + 1013904223))
+    # 100003 is prime
+    len=$(( (((RANDOM*RANDOM)^i) & 0x7ffffff) % 100003 ))
+
+    # Just using urandom will make gzip use method 0 (store) -
+    # not good for test coverage!
+    cat /dev/urandom \
+    | while true; do read junk; echo "junk $c $i $junk"; done \
+    | dd bs=$len count=1 2>/dev/null \
+    | gzip >xxx.gz
+    cat xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz xxx.gz
+done | ../busybox gunzip -c >/dev/null
diff --git a/busybox-1.19.3/archival/bzip2.c b/busybox-1.19.3/archival/bzip2.c
new file mode 100644
index 0000000..e39d7f7
--- /dev/null
+++ b/busybox-1.19.3/archival/bzip2.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2007 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * This file uses bzip2 library code which is written
+ * by Julian Seward <jseward@bzip.org>.
+ * See README and LICENSE files in bz/ directory for more information
+ * about bzip2 library code.
+ */
+
+//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 "archive.h"
+
+#define CONFIG_BZIP2_FEATURE_SPEED 1
+
+/* Speed test:
+ * Compiled with gcc 4.2.1, run on Athlon 64 1800 MHz (512K L2 cache).
+ * Stock bzip2 is 26.4% slower than bbox bzip2 at SPEED 1
+ * (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:
+ *     Size   Time (3 runs)
+ * 0:  10828  4.145 4.146 4.148
+ * 1:  11097  3.845 3.860 3.861
+ * 2:  11392  3.763 3.767 3.768
+ * 3:  11892  3.722 3.724 3.727
+ * 4:  12740  3.637 3.640 3.644
+ * 5:  17273  3.497 3.509 3.509
+ */
+
+
+#define BZ_DEBUG 0
+/* Takes ~300 bytes, detects corruption caused by bad RAM etc */
+#define BZ_LIGHT_DEBUG 0
+
+#include "libarchive/bz/bzlib.h"
+
+#include "libarchive/bz/bzlib_private.h"
+
+#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.
+ * If iobuf is several pages long, malloc() may use mmap,
+ * making iobuf is page aligned and thus (maybe) have one memcpy less
+ * if kernel is clever enough.
+ */
+enum {
+	IOBUF_SIZE = 8 * 1024
+};
+
+static uint8_t level;
+
+/* NB: compressStream() has to return -1 on errors, not die.
+ * bbunpack() will correctly clean up in this case
+ * (delete incomplete .bz2 file)
+ */
+
+/* Returns:
+ * -1 on errors
+ * total written bytes so far otherwise
+ */
+static
+IF_DESKTOP(long long) int bz_write(bz_stream *strm, void* rbuf, ssize_t rlen, void *wbuf)
+{
+	int n, n2, ret;
+
+	strm->avail_in = rlen;
+	strm->next_in = rbuf;
+	while (1) {
+		strm->avail_out = IOBUF_SIZE;
+		strm->next_out = wbuf;
+
+		ret = BZ2_bzCompress(strm, rlen ? BZ_RUN : BZ_FINISH);
+		if (ret != BZ_RUN_OK /* BZ_RUNning */
+		 && ret != BZ_FINISH_OK /* BZ_FINISHing, but not done yet */
+		 && ret != BZ_STREAM_END /* BZ_FINISHed */
+		) {
+			bb_error_msg_and_die("internal error %d", ret);
+		}
+
+		n = IOBUF_SIZE - strm->avail_out;
+		if (n) {
+			n2 = full_write(STDOUT_FILENO, wbuf, n);
+			if (n2 != n) {
+				if (n2 >= 0)
+					errno = 0; /* prevent bogus error message */
+				bb_perror_msg(n2 >= 0 ? "short write" : bb_msg_write_error);
+				return -1;
+			}
+		}
+
+		if (ret == BZ_STREAM_END)
+			break;
+		if (rlen && strm->avail_in == 0)
+			break;
+	}
+	return 0 IF_DESKTOP( + strm->total_out );
+}
+
+static
+IF_DESKTOP(long long) int FAST_FUNC compressStream(unpack_info_t *info UNUSED_PARAM)
+{
+	IF_DESKTOP(long long) int total;
+	ssize_t count;
+	bz_stream bzs; /* it's small */
+#define strm (&bzs)
+	char *iobuf;
+#define rbuf iobuf
+#define wbuf (iobuf + IOBUF_SIZE)
+
+	iobuf = xmalloc(2 * IOBUF_SIZE);
+	BZ2_bzCompressInit(strm, level);
+
+	while (1) {
+		count = full_read(STDIN_FILENO, rbuf, IOBUF_SIZE);
+		if (count < 0) {
+			bb_perror_msg(bb_msg_read_error);
+			total = -1;
+			break;
+		}
+		/* if count == 0, bz_write finalizes compression */
+		total = bz_write(strm, rbuf, count, wbuf);
+		if (count == 0 || total < 0)
+			break;
+	}
+
+	/* Can't be conditional on ENABLE_FEATURE_CLEAN_UP -
+	 * we are called repeatedly
+	 */
+	BZ2_bzCompressEnd(strm);
+	free(iobuf);
+
+	return total;
+}
+
+int bzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int bzip2_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+
+	/* standard bzip2 flags
+	 * -d --decompress force decompression
+	 * -z --compress force compression
+	 * -k --keep     keep (don't delete) input files
+	 * -f --force    overwrite existing output files
+	 * -t --test     test compressed file integrity
+	 * -c --stdout   output to standard out
+	 * -q --quiet    suppress noncritical error messages
+	 * -v --verbose  be verbose (a 2nd -v gives more)
+	 * -s --small    use less memory (at most 2500k)
+	 * -1 .. -9      set block size to 100k .. 900k
+	 * --fast        alias for -1
+	 * --best        alias for -9
+	 */
+
+	opt_complementary = "s2"; /* -s means -2 (compatibility) */
+	/* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
+	opt = getopt32(argv, "cfv" IF_BUNZIP2("dt") "123456789qzs");
+#if ENABLE_BUNZIP2 /* bunzip2_main may not be visible... */
+	if (opt & 0x18) // -d and/or -t
+		return bunzip2_main(argc, argv);
+	opt >>= 5;
+#else
+	opt >>= 3;
+#endif
+	opt = (uint8_t)opt; /* isolate bits for -1..-8 */
+	opt |= 0x100; /* if nothing else, assume -9 */
+	level = 1;
+	while (!(opt & 1)) {
+		level++;
+		opt >>= 1;
+	}
+
+	argv += optind;
+	option_mask32 &= 0x7; /* ignore all except -cfv */
+	return bbunpack(argv, compressStream, append_ext, "bz2");
+}
diff --git a/busybox-1.19.3/archival/cpio.c b/busybox-1.19.3/archival/cpio.c
new file mode 100644
index 0000000..9674a04
--- /dev/null
+++ b/busybox-1.19.3/archival/cpio.c
@@ -0,0 +1,458 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cpio implementation for busybox
+ *
+ * Copyright (C) 2001 by Glenn McGrath
+ *
+ * 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 "archive.h"
+
+//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):
+
+ Modes:
+  -t, --list                 List the archive
+  -i, --extract              Extract files from an archive
+  -o, --create               Create the archive
+  -p, --pass-through         Copy-pass mode
+
+ Options valid in any mode:
+      --block-size=SIZE      I/O block size = SIZE * 512 bytes
+  -B                         I/O block size = 5120 bytes
+  -c                         Use the old portable (ASCII) archive format
+  -C, --io-size=NUMBER       I/O block size in bytes
+  -f, --nonmatching          Only copy files that do not match given pattern
+  -F, --file=FILE            Use FILE instead of standard input or output
+  -H, --format=FORMAT        Use given archive FORMAT
+  -M, --message=STRING       Print STRING when the end of a volume of the
+                             backup media is reached
+  -n, --numeric-uid-gid      If -v, show numeric UID and GID
+      --quiet                Do not print the number of blocks copied
+      --rsh-command=COMMAND  Use remote COMMAND instead of rsh
+  -v, --verbose              Verbosely list the files processed
+  -V, --dot                  Print a "." for each file processed
+  -W, --warning=FLAG         Control warning display: 'none','truncate','all';
+                             multiple options accumulate
+
+ Options valid only in --extract mode:
+  -b, --swap                 Swap both halfwords of words and bytes of
+                             halfwords in the data (equivalent to -sS)
+  -r, --rename               Interactively rename files
+  -s, --swap-bytes           Swap the bytes of each halfword in the files
+  -S, --swap-halfwords       Swap the halfwords of each word (4 bytes)
+      --to-stdout            Extract files to standard output
+  -E, --pattern-file=FILE    Read additional patterns specifying filenames to
+                             extract or list from FILE
+      --only-verify-crc      Verify CRC's, don't actually extract the files
+
+ Options valid only in --create mode:
+  -A, --append               Append to an existing archive
+  -O FILE                    File to use instead of standard output
+
+ Options valid only in --pass-through mode:
+  -l, --link                 Link files instead of copying them, when possible
+
+ Options valid in --extract and --create modes:
+      --absolute-filenames   Do not strip file system prefix components from
+                             the file names
+      --no-absolute-filenames Create all files relative to the current dir
+
+ Options valid in --create and --pass-through modes:
+  -0, --null                 A list of filenames is terminated by a NUL
+  -a, --reset-access-time    Reset the access times of files after reading them
+  -I FILE                    File to use instead of standard input
+  -L, --dereference          Dereference symbolic links (copy the files
+                             that they point to instead of copying the links)
+  -R, --owner=[USER][:.][GROUP] Set owner of created files
+
+ Options valid in --extract and --pass-through modes:
+  -d, --make-directories     Create leading directories where needed
+  -m, --preserve-modification-time  Retain mtime when creating files
+      --no-preserve-owner    Do not change the ownership of the files
+      --sparse               Write files with blocks of zeros as sparse files
+  -u, --unconditional        Replace all files unconditionally
+ */
+
+enum {
+	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    ,)
+	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:"
+
+#if ENABLE_FEATURE_CPIO_O
+static off_t cpio_pad4(off_t size)
+{
+	int i;
+
+	i = (- size) & 3;
+	size += i;
+	while (--i >= 0)
+		bb_putchar('\0');
+	return size;
+}
+
+/* Return value will become exit code.
+ * It's ok to exit instead of return. */
+static NOINLINE int cpio_o(void)
+{
+	static const char trailer[] ALIGN1 = "TRAILER!!!";
+	struct name_s {
+		struct name_s *next;
+		char name[1];
+	};
+	struct inodes_s {
+		struct inodes_s *next;
+		struct name_s *names;
+		struct stat st;
+	};
+
+	struct inodes_s *links = NULL;
+	off_t bytes = 0; /* output bytes count */
+
+	while (1) {
+		const char *name;
+		char *line;
+		struct stat st;
+
+		line = (option_mask32 & OPT_NUL_TERMINATED)
+				? bb_get_chunk_from_file(stdin, NULL)
+				: xmalloc_fgetline(stdin);
+
+		if (line) {
+			/* Strip leading "./[./]..." from the filename */
+			name = line;
+			while (name[0] == '.' && name[1] == '/') {
+				while (*++name == '/')
+					continue;
+			}
+			if (!*name) { /* line is empty */
+				free(line);
+				continue;
+			}
+			if ((option_mask32 & OPT_DEREF)
+					? stat(name, &st)
+					: lstat(name, &st)
+			) {
+ abort_cpio_o:
+				bb_simple_perror_msg_and_die(name);
+			}
+
+			if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode)))
+				st.st_size = 0; /* paranoia */
+
+			/* Store hardlinks for later processing, dont output them */
+			if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) {
+				struct name_s *n;
+				struct inodes_s *l;
+
+				/* Do we have this hardlink remembered? */
+				l = links;
+				while (1) {
+					if (l == NULL) {
+						/* Not found: add new item to "links" list */
+						l = xzalloc(sizeof(*l));
+						l->st = st;
+						l->next = links;
+						links = l;
+						break;
+					}
+					if (l->st.st_ino == st.st_ino) {
+						/* found */
+						break;
+					}
+					l = l->next;
+				}
+				/* Add new name to "l->names" list */
+				n = xmalloc(sizeof(*n) + strlen(name));
+				strcpy(n->name, name);
+				n->next = l->names;
+				l->names = n;
+
+				free(line);
+				continue;
+			}
+
+		} else { /* line == NULL: EOF */
+ next_link:
+			if (links) {
+				/* Output hardlink's data */
+				st = links->st;
+				name = links->names->name;
+				links->names = links->names->next;
+				/* GNU cpio is reported to emit file data
+				 * only for the last instance. Mimic that. */
+				if (links->names == NULL)
+					links = links->next;
+				else
+					st.st_size = 0;
+				/* NB: we leak links->names and/or links,
+				 * this is intended (we exit soon anyway) */
+			} else {
+				/* If no (more) hardlinks to output,
+				 * output "trailer" entry */
+				name = trailer;
+				/* st.st_size == 0 is a must, but for uniformity
+				 * in the output, we zero out everything */
+				memset(&st, 0, sizeof(st));
+				/* st.st_nlink = 1; - GNU cpio does this */
+			}
+		}
+
+		bytes += printf("070701"
+		                "%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');
+		bytes = cpio_pad4(bytes);
+
+		if (st.st_size) {
+			if (S_ISLNK(st.st_mode)) {
+				char *lpath = xmalloc_readlink_or_warn(name);
+				if (!lpath)
+					goto abort_cpio_o;
+				bytes += printf("%s", lpath);
+				free(lpath);
+			} else { /* S_ISREG */
+				int fd = xopen(name, O_RDONLY);
+				fflush_all();
+				/* We must abort if file got shorter too! */
+				bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size);
+				bytes += st.st_size;
+				close(fd);
+			}
+			bytes = cpio_pad4(bytes);
+		}
+
+		if (!line) {
+			if (name != trailer)
+				goto next_link;
+			/* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */
+			return EXIT_SUCCESS;
+		}
+
+		free(line);
+	} /* end of "while (1)" */
+}
+#endif
+
+int cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cpio_main(int argc UNUSED_PARAM, char **argv)
+{
+	archive_handle_t *archive_handle;
+	char *cpio_filename;
+	IF_FEATURE_CPIO_O(const char *cpio_fmt = "";)
+	unsigned opt;
+
+#if ENABLE_LONG_OPTS
+	applet_long_options =
+		"extract\0"      No_argument       "i"
+		"list\0"         No_argument       "t"
+#if ENABLE_FEATURE_CPIO_O
+		"create\0"       No_argument       "o"
+		"format\0"       Required_argument "H"
+#if ENABLE_FEATURE_CPIO_P
+		"pass-through\0" No_argument       "p"
+#endif
+#endif
+		"verbose\0"      No_argument       "v"
+		"quiet\0"        No_argument       "\xff"
+		"to-stdout\0"    No_argument       "\xfe"
+		;
+#endif
+
+	archive_handle = init_handle();
+	/* archive_handle->src_fd = STDIN_FILENO; - done by init_handle */
+	archive_handle->ah_flags = ARCHIVE_EXTRACT_NEWER;
+
+	/* As of now we do not enforce this: */
+	/* -i,-t,-o,-p are mutually exclusive */
+	/* -u,-d,-m make sense only with -i or -p */
+	/* -L makes sense only with -o or -p */
+
+#if !ENABLE_FEATURE_CPIO_O
+	opt = getopt32(argv, OPTION_STR, &cpio_filename);
+	argv += optind;
+	if (opt & OPT_FILE) { /* -F */
+		xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
+	}
+#else
+	opt = getopt32(argv, OPTION_STR "oH:" IF_FEATURE_CPIO_P("p"), &cpio_filename, &cpio_fmt);
+	argv += optind;
+	if ((opt & (OPT_FILE|OPT_CREATE)) == OPT_FILE) { /* -F without -o */
+		xmove_fd(xopen(cpio_filename, O_RDONLY), STDIN_FILENO);
+	}
+	if (opt & OPT_PASSTHROUGH) {
+		pid_t pid;
+		struct fd_pair pp;
+
+		if (argv[0] == NULL)
+			bb_show_usage();
+		if (opt & OPT_CREATE_LEADING_DIR)
+			mkdir(argv[0], 0777);
+		/* Crude existence check:
+		 * close(xopen(argv[0], O_RDONLY | O_DIRECTORY));
+		 * We can also xopen, fstat, IS_DIR, later fchdir.
+		 * This would check for existence earlier and cleaner.
+		 * As it stands now, if we fail xchdir later,
+		 * child dies on EPIPE, unless it caught
+		 * a diffrerent problem earlier.
+		 * This is good enough for now.
+		 */
+#if !BB_MMU
+		pp.rd = 3;
+		pp.wr = 4;
+		if (!re_execed) {
+			close(3);
+			close(4);
+			xpiped_pair(pp);
+		}
+#else
+		xpiped_pair(pp);
+#endif
+		pid = fork_or_rexec(argv - optind);
+		if (pid == 0) { /* child */
+			close(pp.rd);
+			xmove_fd(pp.wr, STDOUT_FILENO);
+			goto dump;
+		}
+		/* parent */
+		xchdir(*argv++);
+		close(pp.wr);
+		xmove_fd(pp.rd, STDIN_FILENO);
+		//opt &= ~OPT_PASSTHROUGH;
+		opt |= OPT_EXTRACT;
+		goto skip;
+	}
+	/* -o */
+	if (opt & OPT_CREATE) {
+		if (cpio_fmt[0] != 'n') /* we _require_ "-H newc" */
+			bb_show_usage();
+		if (opt & OPT_FILE) {
+			xmove_fd(xopen(cpio_filename, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
+		}
+ dump:
+		return cpio_o();
+	}
+ skip:
+#endif
+
+	/* One of either extract or test options must be given */
+	if ((opt & (OPT_TEST | OPT_EXTRACT)) == 0) {
+		bb_show_usage();
+	}
+
+	if (opt & OPT_TEST) {
+		/* if both extract and test options are given, ignore extract option */
+		opt &= ~OPT_EXTRACT;
+		archive_handle->action_header = header_list;
+	}
+	if (opt & OPT_EXTRACT) {
+		archive_handle->action_data = data_extract_all;
+		if (opt & OPT_2STDOUT)
+			archive_handle->action_data = data_extract_to_stdout;
+	}
+	if (opt & OPT_UNCONDITIONAL) {
+		archive_handle->ah_flags |= ARCHIVE_UNLINK_OLD;
+		archive_handle->ah_flags &= ~ARCHIVE_EXTRACT_NEWER;
+	}
+	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 & OPT_CREATE_LEADING_DIR) {
+		archive_handle->ah_flags |= ARCHIVE_CREATE_LEADING_DIRS;
+	}
+	if (opt & OPT_PRESERVE_MTIME) {
+		archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
+	}
+
+	while (*argv) {
+		archive_handle->filter = filter_accept_list;
+		llist_add_to(&archive_handle->accept, *argv);
+		argv++;
+	}
+
+	/* see get_header_cpio */
+	archive_handle->cpio__blocks = (off_t)-1;
+	while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
+		continue;
+
+	if (archive_handle->cpio__blocks != (off_t)-1
+	 && !(opt & OPT_QUIET)
+	) {
+		fprintf(stderr, "%"OFF_FMT"u blocks\n", archive_handle->cpio__blocks);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/dpkg.c b/busybox-1.19.3/archival/dpkg.c
new file mode 100644
index 0000000..2a6a7b3
--- /dev/null
+++ b/busybox-1.19.3/archival/dpkg.c
@@ -0,0 +1,1920 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  mini dpkg implementation for busybox.
+ *  this is not meant as a replacement for dpkg
+ *
+ *  written by glenn mcgrath with the help of others
+ *  copyright (c) 2001 by glenn mcgrath
+ *
+ *  parts of the version comparison code is plucked from the real dpkg
+ *  application which is licensed GPLv2 and
+ *  copyright (c) 1995 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ *  started life as a busybox implementation of udpkg
+ *
+ * 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
+ * make it easier to maintain.
+ *  - the first value for the confflile: field isnt placed on a new line.
+ *  - when installing a package the status: field is placed at the end of the
+ *      section, rather than just after the package: field.
+ *
+ * bugs that need to be fixed
+ *  - (unknown, please let me know when you find any)
+ *
+ */
+
+//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 "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.
+ * 2) for computational efficiency these hash tables should be at least 20%
+ *    larger than the maximum number of elements stored in it.
+ * 3) all _hash_prime's must be a prime number or chaos is assured, if your looking
+ *    for a prime, try http://www.utm.edu/research/primes/lists/small/10000.txt
+ * 4) if you go bigger than 15 bits you may get into trouble (untested) as its
+ *    sometimes cast to an unsigned, if you go to 16 bit you will overlap
+ *    int's and chaos is assured, 16381 is the max prime for 14 bit field
+ */
+
+/* NAME_HASH_PRIME, Stores package names and versions,
+ * I estimate it should be at least 50% bigger than PACKAGE_HASH_PRIME,
+ * as there a lot of duplicate version numbers */
+#define NAME_HASH_PRIME 16381
+
+/* PACKAGE_HASH_PRIME, Maximum number of unique packages,
+ * It must not be smaller than STATUS_HASH_PRIME,
+ * Currently only packages from status_hashtable are stored in here, but in
+ * future this may be used to store packages not only from a status file,
+ * but an available_hashtable, and even multiple packages files.
+ * Package can be stored more than once if they have different versions.
+ * e.g. The same package may have different versions in the status file
+ *      and available file */
+#define PACKAGE_HASH_PRIME 10007
+typedef struct edge_s {
+	unsigned operator:4; /* was:3 */
+	unsigned type:4;
+	unsigned name:16; /* was:14 */
+	unsigned version:16; /* was:14 */
+} edge_t;
+
+typedef struct common_node_s {
+	unsigned name:16; /* was:14 */
+	unsigned version:16; /* was:14 */
+	unsigned num_of_edges:16; /* was:14 */
+	edge_t **edge;
+} common_node_t;
+
+/* Currently it doesnt store packages that have state-status of not-installed
+ * So it only really has to be the size of the maximum number of packages
+ * likely to be installed at any one time, so there is a bit of leeway here */
+#define STATUS_HASH_PRIME 8191
+typedef struct status_node_s {
+	unsigned package:16; /* was:14 */       /* has to fit PACKAGE_HASH_PRIME */
+	unsigned status:16; /* was:14 */        /* has to fit STATUS_HASH_PRIME */
+} status_node_t;
+
+
+/* Globals */
+struct globals {
+	char          *name_hashtable[NAME_HASH_PRIME + 1];
+	common_node_t *package_hashtable[PACKAGE_HASH_PRIME + 1];
+	status_node_t *status_hashtable[STATUS_HASH_PRIME + 1];
+};
+#define G (*ptr_to_globals)
+#define name_hashtable    (G.name_hashtable   )
+#define package_hashtable (G.package_hashtable)
+#define status_hashtable  (G.status_hashtable )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+/* Even numbers are for 'extras', like ored dependencies or null */
+enum edge_type_e {
+	EDGE_NULL = 0,
+	EDGE_PRE_DEPENDS = 1,
+	EDGE_OR_PRE_DEPENDS = 2,
+	EDGE_DEPENDS = 3,
+	EDGE_OR_DEPENDS = 4,
+	EDGE_REPLACES = 5,
+	EDGE_PROVIDES = 7,
+	EDGE_CONFLICTS = 9,
+	EDGE_SUGGESTS = 11,
+	EDGE_RECOMMENDS = 13,
+	EDGE_ENHANCES = 15
+};
+enum operator_e {
+	VER_NULL = 0,
+	VER_EQUAL = 1,
+	VER_LESS = 2,
+	VER_LESS_EQUAL = 3,
+	VER_MORE = 4,
+	VER_MORE_EQUAL = 5,
+	VER_ANY = 6
+};
+
+typedef struct deb_file_s {
+	char *control_file;
+	char *filename;
+	unsigned package:16; /* was:14 */
+} deb_file_t;
+
+
+static void make_hash(const char *key, unsigned *start, unsigned *decrement, const int hash_prime)
+{
+	unsigned long hash_num = key[0];
+	int len = strlen(key);
+	int i;
+
+	/* Maybe i should have uses a "proper" hashing algorithm here instead
+	 * of making one up myself, seems to be working ok though. */
+	for (i = 1; i < len; i++) {
+		/* shifts the ascii based value and adds it to previous value
+		 * shift amount is mod 24 because long int is 32 bit and data
+		 * to be shifted is 8, don't want to shift data to where it has
+		 * no effect */
+		hash_num += (key[i] + key[i-1]) << ((key[i] * i) % 24);
+	}
+	*start = (unsigned) hash_num % hash_prime;
+	*decrement = (unsigned) 1 + (hash_num % (hash_prime - 1));
+}
+
+/* this adds the key to the hash table */
+static int search_name_hashtable(const char *key)
+{
+	unsigned probe_address;
+	unsigned probe_decrement;
+
+	make_hash(key, &probe_address, &probe_decrement, NAME_HASH_PRIME);
+	while (name_hashtable[probe_address] != NULL) {
+		if (strcmp(name_hashtable[probe_address], key) == 0) {
+			return probe_address;
+		}
+		probe_address -= probe_decrement;
+		if ((int)probe_address < 0) {
+			probe_address += NAME_HASH_PRIME;
+		}
+	}
+	name_hashtable[probe_address] = xstrdup(key);
+	return probe_address;
+}
+
+/* this DOESNT add the key to the hashtable
+ * TODO make it consistent with search_name_hashtable
+ */
+static unsigned search_status_hashtable(const char *key)
+{
+	unsigned probe_address;
+	unsigned probe_decrement;
+
+	make_hash(key, &probe_address, &probe_decrement, STATUS_HASH_PRIME);
+	while (status_hashtable[probe_address] != NULL) {
+		if (strcmp(key, name_hashtable[package_hashtable[status_hashtable[probe_address]->package]->name]) == 0) {
+			break;
+		}
+		probe_address -= probe_decrement;
+		if ((int)probe_address < 0) {
+			probe_address += STATUS_HASH_PRIME;
+		}
+	}
+	return probe_address;
+}
+
+static int order(char x)
+{
+	return (x == '~' ? -1
+		: x == '\0' ? 0
+		: isdigit(x) ? 0
+		: isalpha(x) ? x
+		: (unsigned char)x + 256
+	);
+}
+
+/* This code is taken from dpkg and modified slightly to work with busybox */
+static int version_compare_part(const char *val, const char *ref)
+{
+	if (!val) val = "";
+	if (!ref) ref = "";
+
+	while (*val || *ref) {
+		int first_diff;
+
+		while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) {
+			int vc = order(*val);
+			int rc = order(*ref);
+			if (vc != rc)
+				return vc - rc;
+			val++;
+			ref++;
+		}
+
+		while (*val == '0')
+			val++;
+		while (*ref == '0')
+			ref++;
+
+		first_diff = 0;
+		while (isdigit(*val) && isdigit(*ref)) {
+			if (first_diff == 0)
+				first_diff = *val - *ref;
+			val++;
+			ref++;
+		}
+		if (isdigit(*val))
+			return 1;
+		if (isdigit(*ref))
+			return -1;
+		if (first_diff)
+			return first_diff;
+	}
+	return 0;
+}
+
+/* if ver1 < ver2 return -1,
+ * if ver1 = ver2 return 0,
+ * if ver1 > ver2 return 1,
+ */
+static int version_compare(const unsigned ver1, const unsigned ver2)
+{
+	char *ch_ver1 = name_hashtable[ver1];
+	char *ch_ver2 = name_hashtable[ver2];
+	unsigned epoch1 = 0, epoch2 = 0;
+	char *colon;
+	char *deb_ver1, *deb_ver2;
+	char *upstream_ver1;
+	char *upstream_ver2;
+	int result;
+
+	/* Compare epoch */
+	colon = strchr(ch_ver1, ':');
+	if (colon) {
+		epoch1 = atoi(ch_ver1);
+		ch_ver1 = colon + 1;
+	}
+	colon = strchr(ch_ver2, ':');
+	if (colon) {
+		epoch2 = atoi(ch_ver2);
+		ch_ver2 = colon + 1;
+	}
+	if (epoch1 < epoch2) {
+		return -1;
+	}
+	if (epoch1 > epoch2) {
+		return 1;
+	}
+
+	/* Compare upstream version */
+	upstream_ver1 = xstrdup(ch_ver1);
+	upstream_ver2 = xstrdup(ch_ver2);
+
+	/* Chop off debian version, and store for later use */
+	deb_ver1 = strrchr(upstream_ver1, '-');
+	deb_ver2 = strrchr(upstream_ver2, '-');
+	if (deb_ver1) {
+		*deb_ver1++ = '\0';
+	}
+	if (deb_ver2) {
+		*deb_ver2++ = '\0';
+	}
+	result = version_compare_part(upstream_ver1, upstream_ver2);
+	if (result == 0) {
+		/* Compare debian versions */
+		result = version_compare_part(deb_ver1, deb_ver2);
+	}
+
+	free(upstream_ver1);
+	free(upstream_ver2);
+	return result;
+}
+
+static int test_version(const unsigned version1, const unsigned version2, const unsigned operator)
+{
+	const int version_result = version_compare(version1, version2);
+	switch (operator) {
+	case VER_ANY:
+		return TRUE;
+	case VER_EQUAL:
+		return (version_result == 0);
+	case VER_LESS:
+		return (version_result < 0);
+	case VER_LESS_EQUAL:
+		return (version_result <= 0);
+	case VER_MORE:
+		return (version_result > 0);
+	case VER_MORE_EQUAL:
+		return (version_result >= 0);
+	}
+	return FALSE;
+}
+
+static int search_package_hashtable(const unsigned name, const unsigned version, const unsigned operator)
+{
+	unsigned probe_address;
+	unsigned probe_decrement;
+
+	make_hash(name_hashtable[name], &probe_address, &probe_decrement, PACKAGE_HASH_PRIME);
+	while (package_hashtable[probe_address] != NULL) {
+		if (package_hashtable[probe_address]->name == name) {
+			if (operator == VER_ANY) {
+				return probe_address;
+			}
+			if (test_version(package_hashtable[probe_address]->version, version, operator)) {
+				return probe_address;
+			}
+		}
+		probe_address -= probe_decrement;
+		if ((int)probe_address < 0) {
+			probe_address += PACKAGE_HASH_PRIME;
+		}
+	}
+	return probe_address;
+}
+
+/*
+ * This function searches through the entire package_hashtable looking
+ * for a package which provides "needle". It returns the index into
+ * the package_hashtable for the providing package.
+ *
+ * needle is the index into name_hashtable of the package we are
+ * looking for.
+ *
+ * start_at is the index in the package_hashtable to start looking
+ * at. If start_at is -1 then start at the beginning. This is to allow
+ * for repeated searches since more than one package might provide
+ * needle.
+ *
+ * FIXME: I don't think this is very efficient, but I thought I'd keep
+ * it simple for now until it proves to be a problem.
+ */
+static int search_for_provides(int needle, int start_at)
+{
+	int i, j;
+	common_node_t *p;
+	for (i = start_at + 1; i < PACKAGE_HASH_PRIME; i++) {
+		p = package_hashtable[i];
+		if (p == NULL)
+			continue;
+		for (j = 0; j < p->num_of_edges; j++)
+			if (p->edge[j]->type == EDGE_PROVIDES && p->edge[j]->name == needle)
+				return i;
+	}
+	return -1;
+}
+
+/*
+ * Add an edge to a node
+ */
+static void add_edge_to_node(common_node_t *node, edge_t *edge)
+{
+	node->edge = xrealloc_vector(node->edge, 2, node->num_of_edges);
+	node->edge[node->num_of_edges++] = edge;
+}
+
+/*
+ * Create one new node and one new edge for every dependency.
+ *
+ * Dependencies which contain multiple alternatives are represented as
+ * an EDGE_OR_PRE_DEPENDS or EDGE_OR_DEPENDS node, followed by a
+ * number of EDGE_PRE_DEPENDS or EDGE_DEPENDS nodes. The name field of
+ * the OR edge contains the full dependency string while the version
+ * field contains the number of EDGE nodes which follow as part of
+ * this alternative.
+ */
+static void add_split_dependencies(common_node_t *parent_node, const char *whole_line, unsigned edge_type)
+{
+	char *line = xstrdup(whole_line);
+	char *line2;
+	char *line_ptr1 = NULL;
+	char *line_ptr2 = NULL;
+	char *field;
+	char *field2;
+	char *version;
+	edge_t *edge;
+	edge_t *or_edge;
+	int offset_ch;
+
+	field = strtok_r(line, ",", &line_ptr1);
+	do {
+		/* skip leading spaces */
+		field += strspn(field, " ");
+		line2 = xstrdup(field);
+		field2 = strtok_r(line2, "|", &line_ptr2);
+		or_edge = NULL;
+		if ((edge_type == EDGE_DEPENDS || edge_type == EDGE_PRE_DEPENDS)
+		 && (strcmp(field, field2) != 0)
+		) {
+			or_edge = xzalloc(sizeof(edge_t));
+			or_edge->type = edge_type + 1;
+			or_edge->name = search_name_hashtable(field);
+			//or_edge->version = 0; // tracks the number of alternatives
+			add_edge_to_node(parent_node, or_edge);
+		}
+
+		do {
+			edge = xmalloc(sizeof(edge_t));
+			edge->type = edge_type;
+
+			/* Skip any extra leading spaces */
+			field2 += strspn(field2, " ");
+
+			/* Get dependency version info */
+			version = strchr(field2, '(');
+			if (version == NULL) {
+				edge->operator = VER_ANY;
+				/* Get the versions hash number, adding it if the number isnt already in there */
+				edge->version = search_name_hashtable("ANY");
+			} else {
+				/* Skip leading ' ' or '(' */
+				version += strspn(version, " (");
+				/* Calculate length of any operator characters */
+				offset_ch = strspn(version, "<=>");
+				/* Determine operator */
+				if (offset_ch > 0) {
+					if (strncmp(version, "=", offset_ch) == 0) {
+						edge->operator = VER_EQUAL;
+					} else if (strncmp(version, "<<", offset_ch) == 0) {
+						edge->operator = VER_LESS;
+					} else if (strncmp(version, "<=", offset_ch) == 0) {
+						edge->operator = VER_LESS_EQUAL;
+					} else if (strncmp(version, ">>", offset_ch) == 0) {
+						edge->operator = VER_MORE;
+					} else if (strncmp(version, ">=", offset_ch) == 0) {
+						edge->operator = VER_MORE_EQUAL;
+					} else {
+						bb_error_msg_and_die("illegal operator");
+					}
+				}
+				/* skip to start of version numbers */
+				version += offset_ch;
+				version += strspn(version, " ");
+
+				/* Truncate version at trailing ' ' or ')' */
+				version[strcspn(version, " )")] = '\0';
+				/* Get the versions hash number, adding it if the number isnt already in there */
+				edge->version = search_name_hashtable(version);
+			}
+
+			/* Get the dependency name */
+			field2[strcspn(field2, " (")] = '\0';
+			edge->name = search_name_hashtable(field2);
+
+			if (or_edge)
+				or_edge->version++;
+
+			add_edge_to_node(parent_node, edge);
+			field2 = strtok_r(NULL, "|", &line_ptr2);
+		} while (field2 != NULL);
+
+		free(line2);
+		field = strtok_r(NULL, ",", &line_ptr1);
+	} while (field != NULL);
+
+	free(line);
+}
+
+static void free_package(common_node_t *node)
+{
+	unsigned i;
+	if (node) {
+		for (i = 0; i < node->num_of_edges; i++) {
+			free(node->edge[i]);
+		}
+		free(node->edge);
+		free(node);
+	}
+}
+
+/*
+ * Gets the next package field from package_buffer, separated into the field name
+ * and field value, it returns the int offset to the first character of the next field
+ */
+static int read_package_field(const char *package_buffer, char **field_name, char **field_value)
+{
+	int offset_name_start = 0;
+	int offset_name_end = 0;
+	int offset_value_start = 0;
+	int offset_value_end = 0;
+	int offset = 0;
+	int next_offset;
+	int name_length;
+	int value_length;
+	int exit_flag = FALSE;
+
+	if (package_buffer == NULL) {
+		*field_name = NULL;
+		*field_value = NULL;
+		return -1;
+	}
+	while (1) {
+		next_offset = offset + 1;
+		switch (package_buffer[offset]) {
+			case '\0':
+				exit_flag = TRUE;
+				break;
+			case ':':
+				if (offset_name_end == 0) {
+					offset_name_end = offset;
+					offset_value_start = next_offset;
+				}
+				/* TODO: Name might still have trailing spaces if ':' isnt
+				 * immediately after name */
+				break;
+			case '\n':
+				/* TODO: The char next_offset may be out of bounds */
+				if (package_buffer[next_offset] != ' ') {
+					exit_flag = TRUE;
+					break;
+				}
+			case '\t':
+			case ' ':
+				/* increment the value start point if its a just filler */
+				if (offset_name_start == offset) {
+					offset_name_start++;
+				}
+				if (offset_value_start == offset) {
+					offset_value_start++;
+				}
+				break;
+		}
+		if (exit_flag) {
+			/* Check that the names are valid */
+			offset_value_end = offset;
+			name_length = offset_name_end - offset_name_start;
+			value_length = offset_value_end - offset_value_start;
+			if (name_length == 0) {
+				break;
+			}
+			if ((name_length > 0) && (value_length > 0)) {
+				break;
+			}
+
+			/* If not valid, start fresh with next field */
+			exit_flag = FALSE;
+			offset_name_start = offset + 1;
+			offset_name_end = 0;
+			offset_value_start = offset + 1;
+			offset_value_end = offset + 1;
+			offset++;
+		}
+		offset++;
+	}
+	*field_name = NULL;
+	if (name_length) {
+		*field_name = xstrndup(&package_buffer[offset_name_start], name_length);
+	}
+	*field_value = NULL;
+	if (value_length > 0) {
+		*field_value = xstrndup(&package_buffer[offset_value_start], value_length);
+	}
+	return next_offset;
+}
+
+static unsigned fill_package_struct(char *control_buffer)
+{
+	static const char field_names[] ALIGN1 =
+		"Package\0""Version\0"
+		"Pre-Depends\0""Depends\0""Replaces\0""Provides\0"
+		"Conflicts\0""Suggests\0""Recommends\0""Enhances\0";
+
+	common_node_t *new_node = xzalloc(sizeof(common_node_t));
+	char *field_name;
+	char *field_value;
+	int field_start = 0;
+	int num = -1;
+	int buffer_length = strlen(control_buffer);
+
+	new_node->version = search_name_hashtable("unknown");
+	while (field_start < buffer_length) {
+		unsigned field_num;
+
+		field_start += read_package_field(&control_buffer[field_start],
+				&field_name, &field_value);
+
+		if (field_name == NULL) {
+			goto fill_package_struct_cleanup;
+		}
+
+		field_num = index_in_strings(field_names, field_name);
+		switch (field_num) {
+		case 0: /* Package */
+			new_node->name = search_name_hashtable(field_value);
+			break;
+		case 1: /* Version */
+			new_node->version = search_name_hashtable(field_value);
+			break;
+		case 2: /* Pre-Depends */
+			add_split_dependencies(new_node, field_value, EDGE_PRE_DEPENDS);
+			break;
+		case 3: /* Depends */
+			add_split_dependencies(new_node, field_value, EDGE_DEPENDS);
+			break;
+		case 4: /* Replaces */
+			add_split_dependencies(new_node, field_value, EDGE_REPLACES);
+			break;
+		case 5: /* Provides */
+			add_split_dependencies(new_node, field_value, EDGE_PROVIDES);
+			break;
+		case 6: /* Conflicts */
+			add_split_dependencies(new_node, field_value, EDGE_CONFLICTS);
+			break;
+		case 7: /* Suggests */
+			add_split_dependencies(new_node, field_value, EDGE_SUGGESTS);
+			break;
+		case 8: /* Recommends */
+			add_split_dependencies(new_node, field_value, EDGE_RECOMMENDS);
+			break;
+		case 9: /* Enhances */
+			add_split_dependencies(new_node, field_value, EDGE_ENHANCES);
+			break;
+		}
+ fill_package_struct_cleanup:
+		free(field_name);
+		free(field_value);
+	}
+
+	if (new_node->version == search_name_hashtable("unknown")) {
+		free_package(new_node);
+		return -1;
+	}
+	num = search_package_hashtable(new_node->name, new_node->version, VER_EQUAL);
+	free_package(package_hashtable[num]);
+	package_hashtable[num] = new_node;
+	return num;
+}
+
+/* if num = 1, it returns the want status, 2 returns flag, 3 returns status */
+static unsigned get_status(const unsigned status_node, const int num)
+{
+	char *status_string = name_hashtable[status_hashtable[status_node]->status];
+	char *state_sub_string;
+	unsigned state_sub_num;
+	int len;
+	int i;
+
+	/* set tmp_string to point to the start of the word number */
+	for (i = 1; i < num; i++) {
+		/* skip past a word */
+		status_string += strcspn(status_string, " ");
+		/* skip past the separating spaces */
+		status_string += strspn(status_string, " ");
+	}
+	len = strcspn(status_string, " \n");
+	state_sub_string = xstrndup(status_string, len);
+	state_sub_num = search_name_hashtable(state_sub_string);
+	free(state_sub_string);
+	return state_sub_num;
+}
+
+static void set_status(const unsigned status_node_num, const char *new_value, const int position)
+{
+	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);
+	char *new_status;
+
+	switch (position) {
+		case 1:
+			want = new_value_num;
+			break;
+		case 2:
+			flag = new_value_num;
+			break;
+		case 3:
+			status = new_value_num;
+			break;
+		default:
+			bb_error_msg_and_die("DEBUG ONLY: this shouldnt happen");
+	}
+
+	new_status = xasprintf("%s %s %s", name_hashtable[want], name_hashtable[flag], name_hashtable[status]);
+	status_hashtable[status_node_num]->status = search_name_hashtable(new_status);
+	free(new_status);
+}
+
+static const char *describe_status(int status_num)
+{
+	int status_want, status_state;
+	if (status_hashtable[status_num] == NULL || status_hashtable[status_num]->status == 0)
+		return "is not installed or flagged to be installed";
+
+	status_want = get_status(status_num, 1);
+	status_state = get_status(status_num, 3);
+
+	if (status_state == search_name_hashtable("installed")) {
+		if (status_want == search_name_hashtable("install"))
+			return "is installed";
+		if (status_want == search_name_hashtable("deinstall"))
+			return "is marked to be removed";
+		if (status_want == search_name_hashtable("purge"))
+			return "is marked to be purged";
+	}
+	if (status_want == search_name_hashtable("unknown"))
+		return "is in an indeterminate state";
+	if (status_want == search_name_hashtable("install"))
+		return "is marked to be installed";
+
+	return "is not installed or flagged to be installed";
+}
+
+static void index_status_file(const char *filename)
+{
+	FILE *status_file;
+	char *control_buffer;
+	char *status_line;
+	status_node_t *status_node = NULL;
+	unsigned status_num;
+
+	status_file = xfopen_for_read(filename);
+	while ((control_buffer = xmalloc_fgetline_str(status_file, "\n\n")) != NULL) {
+		const unsigned package_num = fill_package_struct(control_buffer);
+		if (package_num != -1) {
+			status_node = xmalloc(sizeof(status_node_t));
+			/* fill_package_struct doesnt handle the status field */
+			status_line = strstr(control_buffer, "Status:");
+			if (status_line != NULL) {
+				status_line += 7;
+				status_line += strspn(status_line, " \n\t");
+				status_line = xstrndup(status_line, strcspn(status_line, "\n"));
+				status_node->status = search_name_hashtable(status_line);
+				free(status_line);
+			}
+			status_node->package = package_num;
+			status_num = search_status_hashtable(name_hashtable[package_hashtable[status_node->package]->name]);
+			status_hashtable[status_num] = status_node;
+		}
+		free(control_buffer);
+	}
+	fclose(status_file);
+}
+
+static void write_buffer_no_status(FILE *new_status_file, const char *control_buffer)
+{
+	char *name;
+	char *value;
+	int start = 0;
+	while (1) {
+		start += read_package_field(&control_buffer[start], &name, &value);
+		if (name == NULL) {
+			break;
+		}
+		if (strcmp(name, "Status") != 0) {
+			fprintf(new_status_file, "%s: %s\n", name, value);
+		}
+	}
+}
+
+/* This could do with a cleanup */
+static void write_status_file(deb_file_t **deb_file)
+{
+	FILE *old_status_file = xfopen_for_read("/var/lib/dpkg/status");
+	FILE *new_status_file = xfopen_for_write("/var/lib/dpkg/status.udeb");
+	char *package_name;
+	char *status_from_file;
+	char *control_buffer = NULL;
+	char *tmp_string;
+	int status_num;
+	int field_start = 0;
+	int write_flag;
+	int i = 0;
+
+	/* Update previously known packages */
+	while ((control_buffer = xmalloc_fgetline_str(old_status_file, "\n\n")) != NULL) {
+		tmp_string = strstr(control_buffer, "Package:");
+		if (tmp_string == NULL) {
+			continue;
+		}
+
+		tmp_string += 8;
+		tmp_string += strspn(tmp_string, " \n\t");
+		package_name = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+		write_flag = FALSE;
+		tmp_string = strstr(control_buffer, "Status:");
+		if (tmp_string != NULL) {
+			/* Separate the status value from the control buffer */
+			tmp_string += 7;
+			tmp_string += strspn(tmp_string, " \n\t");
+			status_from_file = xstrndup(tmp_string, strcspn(tmp_string, "\n"));
+		} else {
+			status_from_file = NULL;
+		}
+
+		/* Find this package in the status hashtable */
+		status_num = search_status_hashtable(package_name);
+		if (status_hashtable[status_num] != NULL) {
+			const char *status_from_hashtable = name_hashtable[status_hashtable[status_num]->status];
+			if (strcmp(status_from_file, status_from_hashtable) != 0) {
+				/* New status isnt exactly the same as old status */
+				const int state_status = get_status(status_num, 3);
+				if ((strcmp("installed", name_hashtable[state_status]) == 0)
+				 || (strcmp("unpacked", name_hashtable[state_status]) == 0)
+				) {
+					/* We need to add the control file from the package */
+					i = 0;
+					while (deb_file[i] != NULL) {
+						if (strcmp(package_name, name_hashtable[package_hashtable[deb_file[i]->package]->name]) == 0) {
+							/* Write a status file entry with a modified status */
+							/* remove trailing \n's */
+							write_buffer_no_status(new_status_file, deb_file[i]->control_file);
+							set_status(status_num, "ok", 2);
+							fprintf(new_status_file, "Status: %s\n\n",
+									name_hashtable[status_hashtable[status_num]->status]);
+							write_flag = TRUE;
+							break;
+						}
+						i++;
+					}
+					/* This is temperary, debugging only */
+					if (deb_file[i] == NULL) {
+						bb_error_msg_and_die("ALERT: cannot find a control file, "
+							"your status file may be broken, status may be "
+							"incorrect for %s", package_name);
+					}
+				}
+				else if (strcmp("not-installed", name_hashtable[state_status]) == 0) {
+					/* Only write the Package, Status, Priority and Section lines */
+					fprintf(new_status_file, "Package: %s\n", package_name);
+					fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+
+					while (1) {
+						char *field_name;
+						char *field_value;
+						field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+						if (field_name == NULL) {
+							break;
+						}
+						if ((strcmp(field_name, "Priority") == 0)
+						 || (strcmp(field_name, "Section") == 0)
+						) {
+							fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+						}
+					}
+					write_flag = TRUE;
+					fputs("\n", new_status_file);
+				}
+				else if (strcmp("config-files", name_hashtable[state_status]) == 0) {
+					/* only change the status line */
+					while (1) {
+						char *field_name;
+						char *field_value;
+						field_start += read_package_field(&control_buffer[field_start], &field_name, &field_value);
+						if (field_name == NULL) {
+							break;
+						}
+						/* Setup start point for next field */
+						if (strcmp(field_name, "Status") == 0) {
+							fprintf(new_status_file, "Status: %s\n", status_from_hashtable);
+						} else {
+							fprintf(new_status_file, "%s: %s\n", field_name, field_value);
+						}
+					}
+					write_flag = TRUE;
+					fputs("\n", new_status_file);
+				}
+			}
+		}
+		/* If the package from the status file wasnt handle above, do it now*/
+		if (!write_flag) {
+			fprintf(new_status_file, "%s\n\n", control_buffer);
+		}
+
+		free(status_from_file);
+		free(package_name);
+		free(control_buffer);
+	}
+
+	/* Write any new packages */
+	for (i = 0; deb_file[i] != NULL; i++) {
+		status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[i]->package]->name]);
+		if (strcmp("reinstreq", name_hashtable[get_status(status_num, 2)]) == 0) {
+			write_buffer_no_status(new_status_file, deb_file[i]->control_file);
+			set_status(status_num, "ok", 2);
+			fprintf(new_status_file, "Status: %s\n\n", name_hashtable[status_hashtable[status_num]->status]);
+		}
+	}
+	fclose(old_status_file);
+	fclose(new_status_file);
+
+	/* Create a separate backfile to dpkg */
+	if (rename("/var/lib/dpkg/status", "/var/lib/dpkg/status.udeb.bak") == -1) {
+		if (errno != ENOENT)
+			bb_error_msg_and_die("can't create backup status file");
+		/* Its ok if renaming the status file fails because status
+		 * file doesnt exist, maybe we are starting from scratch */
+		bb_error_msg("no status file found, creating new one");
+	}
+
+	xrename("/var/lib/dpkg/status.udeb", "/var/lib/dpkg/status");
+}
+
+/* This function returns TRUE if the given package can satisfy a
+ * dependency of type depend_type.
+ *
+ * A pre-depends is satisfied only if a package is already installed,
+ * which a regular depends can be satisfied by a package which we want
+ * to install.
+ */
+static int package_satisfies_dependency(int package, int depend_type)
+{
+	int status_num = search_status_hashtable(name_hashtable[package_hashtable[package]->name]);
+
+	/* status could be unknown if package is a pure virtual
+	 * provides which cannot satisfy any dependency by itself.
+	 */
+	if (status_hashtable[status_num] == NULL)
+		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");
+	}
+	return 0;
+}
+
+static int check_deps(deb_file_t **deb_file, int deb_start /*, int dep_max_count - ?? */)
+{
+	int *conflicts = NULL;
+	int conflicts_num = 0;
+	int i = deb_start;
+	int j;
+
+	/* Check for conflicts
+	 * TODO: TEST if conflicts with other packages to be installed
+	 *
+	 * Add install packages and the packages they provide
+	 * to the list of files to check conflicts for
+	 */
+
+	/* Create array of package numbers to check against
+	 * installed package for conflicts*/
+	while (deb_file[i] != NULL) {
+		const unsigned package_num = deb_file[i]->package;
+		conflicts = xrealloc_vector(conflicts, 2, conflicts_num);
+		conflicts[conflicts_num] = package_num;
+		conflicts_num++;
+		/* add provides to conflicts list */
+		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,
+					package_hashtable[package_num]->edge[j]->version,
+					package_hashtable[package_num]->edge[j]->operator);
+				if (package_hashtable[conflicts_package_num] == NULL) {
+					/* create a new package */
+					common_node_t *new_node = xzalloc(sizeof(common_node_t));
+					new_node->name = package_hashtable[package_num]->edge[j]->name;
+					new_node->version = package_hashtable[package_num]->edge[j]->version;
+					package_hashtable[conflicts_package_num] = new_node;
+				}
+				conflicts = xrealloc_vector(conflicts, 2, conflicts_num);
+				conflicts[conflicts_num] = conflicts_package_num;
+				conflicts_num++;
+			}
+		}
+		i++;
+	}
+
+	/* Check conflicts */
+	i = 0;
+	while (deb_file[i] != NULL) {
+		const common_node_t *package_node = package_hashtable[deb_file[i]->package];
+		int status_num = 0;
+		status_num = search_status_hashtable(name_hashtable[package_node->name]);
+
+		if (get_status(status_num, 3) == search_name_hashtable("installed")) {
+			i++;
+			continue;
+		}
+
+		for (j = 0; j < package_node->num_of_edges; j++) {
+			const edge_t *package_edge = package_node->edge[j];
+
+			if (package_edge->type == EDGE_CONFLICTS) {
+				const unsigned package_num =
+					search_package_hashtable(package_edge->name,
+								 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]);
+
+					if (get_status(status_num, 1) == search_name_hashtable("install")) {
+						result = test_version(package_hashtable[deb_file[i]->package]->version,
+							package_edge->version, package_edge->operator);
+					}
+				}
+
+				if (result) {
+					bb_error_msg_and_die("package %s conflicts with %s",
+						name_hashtable[package_node->name],
+						name_hashtable[package_edge->name]);
+				}
+			}
+		}
+		i++;
+	}
+
+
+	/* Check dependendcies */
+	for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
+		int status_num = 0;
+		int number_of_alternatives = 0;
+		const edge_t * root_of_alternatives = NULL;
+		const common_node_t *package_node = package_hashtable[i];
+
+		/* If the package node does not exist then this
+		 * package is a virtual one. In which case there are
+		 * no dependencies to check.
+		 */
+		if (package_node == NULL) continue;
+
+		status_num = search_status_hashtable(name_hashtable[package_node->name]);
+
+		/* If there is no status then this package is a
+		 * virtual one provided by something else. In which
+		 * case there are no dependencies to check.
+		 */
+		if (status_hashtable[status_num] == NULL) continue;
+
+		/* If we don't want this package installed then we may
+		 * as well ignore it's dependencies.
+		 */
+		if (get_status(status_num, 1) != search_name_hashtable("install")) {
+			continue;
+		}
+
+		/* This code is tested only for EDGE_DEPENDS, since I
+		 * have no suitable pre-depends available. There is no
+		 * reason that it shouldn't work though :-)
+		 */
+		for (j = 0; j < package_node->num_of_edges; j++) {
+			const edge_t *package_edge = package_node->edge[j];
+			unsigned package_num;
+
+			if (package_edge->type == EDGE_OR_PRE_DEPENDS
+			 || package_edge->type == EDGE_OR_DEPENDS
+			) {
+				/* 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 */
+				number_of_alternatives = 1;
+				root_of_alternatives = NULL;
+			}
+
+			package_num = search_package_hashtable(package_edge->name, package_edge->version, package_edge->operator);
+
+			if (package_edge->type == EDGE_PRE_DEPENDS
+			 || package_edge->type == EDGE_DEPENDS
+			) {
+				int result=1;
+				status_num = 0;
+
+				/* If we are inside an alternative then check
+				 * this edge is the right type.
+				 *
+				 * EDGE_DEPENDS == OR_DEPENDS -1
+				 * EDGE_PRE_DEPENDS == OR_PRE_DEPENDS -1
+				 */
+				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);
+
+				if (package_hashtable[package_num] != NULL)
+					result = !package_satisfies_dependency(package_num, package_edge->type);
+
+				if (result) { /* check for other package which provide what we are looking for */
+					int provider = -1;
+
+					while ((provider = search_for_provides(package_edge->name, provider)) > -1) {
+						if (package_hashtable[provider] == NULL) {
+							puts("Have a provider but no package information for it");
+							continue;
+						}
+						result = !package_satisfies_dependency(provider, package_edge->type);
+
+						if (result == 0)
+							break;
+					}
+				}
+
+				/* It must be already installed, or to be installed */
+				number_of_alternatives--;
+				if (result && number_of_alternatives == 0) {
+					if (root_of_alternatives)
+						bb_error_msg_and_die(
+							"package %s %sdepends on %s, "
+							"which cannot be satisfied",
+							name_hashtable[package_node->name],
+							package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
+							name_hashtable[root_of_alternatives->name]);
+					bb_error_msg_and_die(
+						"package %s %sdepends on %s, which %s\n",
+						name_hashtable[package_node->name],
+						package_edge->type == EDGE_PRE_DEPENDS ? "pre-" : "",
+						name_hashtable[package_edge->name],
+						describe_status(status_num));
+				}
+				if (result == 0 && number_of_alternatives) {
+					/* we've found a package which
+					 * satisfies the dependency,
+					 * so skip over the rest of
+					 * the alternatives.
+					 */
+					j += number_of_alternatives;
+					number_of_alternatives = 0;
+				}
+			}
+		}
+	}
+	free(conflicts);
+	return TRUE;
+}
+
+static char **create_list(const char *filename)
+{
+	FILE *list_stream;
+	char **file_list;
+	char *line;
+	int count;
+
+	/* don't use [xw]fopen here, handle error ourself */
+	list_stream = fopen_for_read(filename);
+	if (list_stream == NULL) {
+		return NULL;
+	}
+
+	file_list = NULL;
+	count = 0;
+	while ((line = xmalloc_fgetline(list_stream)) != NULL) {
+		file_list = xrealloc_vector(file_list, 2, count);
+		file_list[count++] = line;
+		/*file_list[count] = NULL; - xrealloc_vector did it */
+	}
+	fclose(list_stream);
+
+	return file_list;
+}
+
+/* maybe i should try and hook this into remove_file.c somehow */
+static int remove_file_array(char **remove_names, char **exclude_names)
+{
+	struct stat path_stat;
+	int remove_flag = 1; /* not removed anything yet */
+	int i, j;
+
+	if (remove_names == NULL) {
+		return 0;
+	}
+	for (i = 0; remove_names[i] != NULL; i++) {
+		if (exclude_names != NULL) {
+			for (j = 0; exclude_names[j] != NULL; j++) {
+				if (strcmp(remove_names[i], exclude_names[j]) == 0) {
+					goto skip;
+				}
+			}
+		}
+		/* TODO: why we are checking lstat? we can just try rm/rmdir */
+		if (lstat(remove_names[i], &path_stat) < 0) {
+			continue;
+		}
+		if (S_ISDIR(path_stat.st_mode)) {
+			remove_flag &= rmdir(remove_names[i]); /* 0 if no error */
+		} else {
+			remove_flag &= unlink(remove_names[i]); /* 0 if no error */
+		}
+ skip:
+		continue;
+	}
+	return (remove_flag == 0);
+}
+
+static void run_package_script_or_die(const char *package_name, const char *script_type)
+{
+	char *script_path;
+	int result;
+
+	script_path = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, script_type);
+
+	/* If the file doesnt exist is isnt fatal */
+	result = access(script_path, F_OK) ? EXIT_SUCCESS : system(script_path);
+	free(script_path);
+	if (result)
+		bb_error_msg_and_die("%s failed, exit code %d", script_type, result);
+}
+
+/*
+The policy manual defines what scripts get called when and with
+what arguments. I realize that busybox does not support all of
+these scenarios, but it does support some of them; it does not,
+however, run them with any parameters in run_package_script_or_die().
+Here are the scripts:
+
+preinst install
+preinst install <old_version>
+preinst upgrade <old_version>
+preinst abort_upgrade <new_version>
+postinst configure <most_recent_version>
+postinst abort-upgade <new_version>
+postinst abort-remove
+postinst abort-remove in-favour <package> <version>
+postinst abort-deconfigure in-favor <failed_install_package> removing <conflicting_package> <version>
+prerm remove
+prerm upgrade <new_version>
+prerm failed-upgrade <old_version>
+prerm remove in-favor <package> <new_version>
+prerm deconfigure in-favour <package> <version> removing <package> <version>
+postrm remove
+postrm purge
+postrm upgrade <new_version>
+postrm failed-upgrade <old_version>
+postrm abort-install
+postrm abort-install <old_version>
+postrm abort-upgrade <old_version>
+postrm disappear <overwriter> <version>
+*/
+static const char *const all_control_files[] = {
+	"preinst", "postinst", "prerm", "postrm",
+	"list", "md5sums", "shlibs", "conffiles",
+	"config", "templates"
+};
+
+static char **all_control_list(const char *package_name)
+{
+	unsigned i = 0;
+	char **remove_files;
+
+	/* Create a list of all /var/lib/dpkg/info/<package> files */
+	remove_files = xzalloc(sizeof(all_control_files) + sizeof(char*));
+	while (i < ARRAY_SIZE(all_control_files)) {
+		remove_files[i] = xasprintf("/var/lib/dpkg/info/%s.%s",
+				package_name, all_control_files[i]);
+		i++;
+	}
+
+	return remove_files;
+}
+
+static void free_array(char **array)
+{
+	if (array) {
+		unsigned i = 0;
+		while (array[i]) {
+			free(array[i]);
+			i++;
+		}
+		free(array);
+	}
+}
+
+/* This function lists information on the installed packages. It loops through
+ * the status_hashtable to retrieve the info. This results in smaller code than
+ * scanning the status file. The resulting list, however, is unsorted.
+ */
+static void list_packages(const char *pattern)
+{
+	int i;
+
+	puts("    Name           Version");
+	puts("+++-==============-==============");
+
+	/* go through status hash, dereference package hash and finally strings */
+	for (i = 0; i < STATUS_HASH_PRIME+1; i++) {
+		if (status_hashtable[i]) {
+			const char *stat_str;  /* status string */
+			const char *name_str;  /* package name */
+			const char *vers_str;  /* version */
+			char  s1, s2;          /* status abbreviations */
+			int   spccnt;          /* space count */
+			int   j;
+
+			stat_str = name_hashtable[status_hashtable[i]->status];
+			name_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->name];
+			vers_str = name_hashtable[package_hashtable[status_hashtable[i]->package]->version];
+
+			if (pattern && fnmatch(pattern, name_str, 0) != 0)
+				continue;
+
+			/* get abbreviation for status field 1 */
+			s1 = stat_str[0] == 'i' ? 'i' : 'r';
+
+			/* get abbreviation for status field 2 */
+			for (j = 0, spccnt = 0; stat_str[j] && spccnt < 2; j++) {
+				if (stat_str[j] == ' ') spccnt++;
+			}
+			s2 = stat_str[j];
+
+			/* print out the line formatted like Debian dpkg */
+			printf("%c%c  %-14s %s\n", s1, s2, name_str, vers_str);
+		}
+	}
+}
+
+static void remove_package(const unsigned package_num, int noisy)
+{
+	const char *package_name = name_hashtable[package_hashtable[package_num]->name];
+	const char *package_version = name_hashtable[package_hashtable[package_num]->version];
+	const unsigned status_num = search_status_hashtable(package_name);
+	const int package_name_length = strlen(package_name);
+	char **remove_files;
+	char **exclude_files;
+	char list_name[package_name_length + 25];
+	char conffile_name[package_name_length + 30];
+
+	if (noisy)
+		printf("Removing %s (%s)...\n", package_name, package_version);
+
+	/* Run prerm script */
+	run_package_script_or_die(package_name, "prerm");
+
+	/* Create a list of files to remove, and a separate list of those to keep */
+	sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list");
+	remove_files = create_list(list_name);
+
+	sprintf(conffile_name, "/var/lib/dpkg/info/%s.%s", package_name, "conffiles");
+	exclude_files = create_list(conffile_name);
+
+	/* Some directories can't be removed straight away, so do multiple passes */
+	while (remove_file_array(remove_files, exclude_files))
+		continue;
+	free_array(exclude_files);
+	free_array(remove_files);
+
+	/* Create a list of files in /var/lib/dpkg/info/<package>.* to keep */
+	exclude_files = xzalloc(sizeof(exclude_files[0]) * 3);
+	exclude_files[0] = xstrdup(conffile_name);
+	exclude_files[1] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm");
+
+	/* Create a list of all /var/lib/dpkg/info/<package> files */
+	remove_files = all_control_list(package_name);
+
+	remove_file_array(remove_files, exclude_files);
+	free_array(remove_files);
+	free_array(exclude_files);
+
+	/* rename <package>.conffiles to <package>.list
+	 * The conffiles control file isn't required in Debian packages, so don't
+	 * error out if it's missing.  */
+	rename(conffile_name, list_name);
+
+	/* Change package status */
+	set_status(status_num, "config-files", 3);
+}
+
+static void purge_package(const unsigned package_num)
+{
+	const char *package_name = name_hashtable[package_hashtable[package_num]->name];
+	const char *package_version = name_hashtable[package_hashtable[package_num]->version];
+	const unsigned status_num = search_status_hashtable(package_name);
+	char **remove_files;
+	char **exclude_files;
+	char list_name[strlen(package_name) + 25];
+
+	printf("Purging %s (%s)...\n", package_name, package_version);
+
+	/* Run prerm script */
+	run_package_script_or_die(package_name, "prerm");
+
+	/* Create a list of files to remove */
+	sprintf(list_name, "/var/lib/dpkg/info/%s.%s", package_name, "list");
+	remove_files = create_list(list_name);
+
+	/* Some directories cant be removed straight away, so do multiple passes */
+	while (remove_file_array(remove_files, NULL))
+		continue;
+	free_array(remove_files);
+
+	/* Create a list of all /var/lib/dpkg/info/<package> files */
+	remove_files = all_control_list(package_name);
+
+	/* Delete all of them except the postrm script */
+	exclude_files = xzalloc(sizeof(exclude_files[0]) * 2);
+	exclude_files[0] = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "postrm");
+	remove_file_array(remove_files, exclude_files);
+	free_array(exclude_files);
+
+	/* Run and remove postrm script */
+	run_package_script_or_die(package_name, "postrm");
+	remove_file_array(remove_files, NULL);
+
+	free_array(remove_files);
+
+	/* Change package status */
+	set_status(status_num, "not-installed", 3);
+}
+
+static archive_handle_t *init_archive_deb_ar(const char *filename)
+{
+	archive_handle_t *ar_handle;
+
+	/* Setup an ar archive handle that refers to the gzip sub archive */
+	ar_handle = init_handle();
+	ar_handle->filter = filter_accept_list_reassign;
+	ar_handle->src_fd = xopen(filename, O_RDONLY);
+
+	return ar_handle;
+}
+
+static void init_archive_deb_control(archive_handle_t *ar_handle)
+{
+	archive_handle_t *tar_handle;
+
+	/* Setup the tar archive handle */
+	tar_handle = init_handle();
+	tar_handle->src_fd = ar_handle->src_fd;
+
+	/* We don't care about data.tar.* or debian-binary, just control.tar.* */
+#if ENABLE_FEATURE_SEAMLESS_GZ
+	llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz");
+#endif
+#if ENABLE_FEATURE_SEAMLESS_BZ2
+	llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2");
+#endif
+
+	/* Assign the tar handle as a subarchive of the ar handle */
+	ar_handle->dpkg__sub_archive = tar_handle;
+}
+
+static void init_archive_deb_data(archive_handle_t *ar_handle)
+{
+	archive_handle_t *tar_handle;
+
+	/* Setup the tar archive handle */
+	tar_handle = init_handle();
+	tar_handle->src_fd = ar_handle->src_fd;
+
+	/* We don't care about control.tar.* or debian-binary, just data.tar.* */
+#if ENABLE_FEATURE_SEAMLESS_GZ
+	llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz");
+#endif
+#if ENABLE_FEATURE_SEAMLESS_BZ2
+	llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2");
+#endif
+
+	/* Assign the tar handle as a subarchive of the ar handle */
+	ar_handle->dpkg__sub_archive = tar_handle;
+}
+
+static void FAST_FUNC data_extract_to_buffer(archive_handle_t *archive_handle)
+{
+	unsigned size = archive_handle->file_header->size;
+
+	archive_handle->dpkg__buffer = xzalloc(size + 1);
+	xread(archive_handle->src_fd, archive_handle->dpkg__buffer, size);
+}
+
+static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept)
+{
+	ar_handle->dpkg__sub_archive->action_data = data_extract_to_buffer;
+	ar_handle->dpkg__sub_archive->accept = myaccept;
+	ar_handle->dpkg__sub_archive->filter = filter_accept_list;
+
+	unpack_ar_archive(ar_handle);
+	close(ar_handle->src_fd);
+
+	return ar_handle->dpkg__sub_archive->dpkg__buffer;
+}
+
+static void append_control_file_to_llist(const char *package_name, const char *control_name, llist_t **ll)
+{
+	FILE *fp;
+	char *filename, *line;
+
+	filename = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, control_name);
+	fp = fopen_for_read(filename);
+	free(filename);
+	if (fp != NULL) {
+		while ((line = xmalloc_fgetline(fp)) != NULL)
+			llist_add_to(ll, line);
+		fclose(fp);
+	}
+}
+
+static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle)
+{
+	int fd;
+	char *name_ptr = archive_handle->file_header->name + 1;
+
+	/* Is this file marked as config file? */
+	if (!find_list_entry(archive_handle->accept, name_ptr))
+		return EXIT_SUCCESS; /* no */
+
+	fd = open(name_ptr, O_RDONLY);
+	if (fd >= 0) {
+		md5_ctx_t md5;
+		char *md5line, *buf;
+		int count;
+
+		/* Calculate MD5 of existing file */
+		buf = xmalloc(4096);
+		md5_begin(&md5);
+		while ((count = safe_read(fd, buf, 4096)) > 0)
+			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);
+		sprintf(bin2hex(md5line, buf, 16), "  %s", name_ptr);
+		free(buf);
+
+		/* Is it changed after install? */
+		if (find_list_entry(archive_handle->accept, md5line) == NULL) {
+			printf("Warning: Creating %s as %s.dpkg-new\n", name_ptr, name_ptr);
+			archive_handle->file_header->name = xasprintf("%s.dpkg-new", archive_handle->file_header->name);
+		}
+		free(md5line);
+	}
+	return EXIT_SUCCESS;
+}
+
+static void FAST_FUNC data_extract_all_prefix(archive_handle_t *archive_handle)
+{
+	char *name_ptr = archive_handle->file_header->name;
+
+	/* Skip all leading "/" */
+	while (*name_ptr == '/')
+		name_ptr++;
+	/* Skip all leading "./" and "../" */
+	while (name_ptr[0] == '.') {
+		if (name_ptr[1] == '.')
+			name_ptr++;
+		if (name_ptr[1] != '/')
+			break;
+		name_ptr += 2;
+	}
+
+	if (name_ptr[0] != '\0') {
+		archive_handle->file_header->name = xasprintf("%s%s", archive_handle->dpkg__buffer, name_ptr);
+		data_extract_all(archive_handle);
+		if (fnmatch("*.dpkg-new", archive_handle->file_header->name, 0) == 0) {
+			/* remove .dpkg-new suffix */
+			archive_handle->file_header->name[strlen(archive_handle->file_header->name) - 9] = '\0';
+		}
+	}
+}
+
+enum {
+	/* Commands */
+	OPT_configure            = (1 << 0),
+	OPT_install              = (1 << 1),
+	OPT_list_installed       = (1 << 2),
+	OPT_purge                = (1 << 3),
+	OPT_remove               = (1 << 4),
+	OPT_unpack               = (1 << 5),
+	OPTMASK_cmd              = (1 << 6) - 1,
+	/* Options */
+	OPT_force                = (1 << 6),
+	OPT_force_ignore_depends = (1 << 7),
+	OPT_force_confnew        = (1 << 8),
+	OPT_force_confold        = (1 << 9),
+};
+
+static void unpack_package(deb_file_t *deb_file)
+{
+	const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
+	const unsigned status_num = search_status_hashtable(package_name);
+	const unsigned status_package_num = status_hashtable[status_num]->package;
+	char *info_prefix;
+	char *list_filename;
+	archive_handle_t *archive_handle;
+	FILE *out_stream;
+	llist_t *accept_list;
+	llist_t *conffile_list;
+	int i;
+
+	/* If existing version, remove it first */
+	conffile_list = NULL;
+	if (strcmp(name_hashtable[get_status(status_num, 3)], "installed") == 0) {
+		/* Package is already installed, remove old version first */
+		printf("Preparing to replace %s %s (using %s)...\n", package_name,
+			name_hashtable[package_hashtable[status_package_num]->version],
+			deb_file->filename);
+
+		/* Read md5sums from old package */
+		if (!(option_mask32 & OPT_force_confold))
+			append_control_file_to_llist(package_name, "md5sums", &conffile_list);
+
+		remove_package(status_package_num, 0);
+	} else {
+		printf("Unpacking %s (from %s)...\n", package_name, deb_file->filename);
+	}
+
+	/* Extract control.tar.gz to /var/lib/dpkg/info/<package>.filename */
+	info_prefix = xasprintf("/var/lib/dpkg/info/%s.%s", package_name, "");
+	archive_handle = init_archive_deb_ar(deb_file->filename);
+	init_archive_deb_control(archive_handle);
+
+	accept_list = NULL;
+	i = 0;
+	while (i < ARRAY_SIZE(all_control_files)) {
+		char *c = xasprintf("./%s", all_control_files[i]);
+		llist_add_to(&accept_list, c);
+		i++;
+	}
+	archive_handle->dpkg__sub_archive->accept = accept_list;
+	archive_handle->dpkg__sub_archive->filter = filter_accept_list;
+	archive_handle->dpkg__sub_archive->action_data = data_extract_all_prefix;
+	archive_handle->dpkg__sub_archive->dpkg__buffer = info_prefix;
+	archive_handle->dpkg__sub_archive->ah_flags |= ARCHIVE_UNLINK_OLD;
+	unpack_ar_archive(archive_handle);
+
+	/* Run the preinst prior to extracting */
+	run_package_script_or_die(package_name, "preinst");
+
+	/* Don't overwrite existing config files */
+	if (!(option_mask32 & OPT_force_confnew))
+		append_control_file_to_llist(package_name, "conffiles", &conffile_list);
+
+	/* Extract data.tar.gz to the root directory */
+	archive_handle = init_archive_deb_ar(deb_file->filename);
+	init_archive_deb_data(archive_handle);
+	archive_handle->dpkg__sub_archive->accept = conffile_list;
+	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);
+	while (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;
+	}
+	fclose(out_stream);
+
+	/* change status */
+	set_status(status_num, "install", 1);
+	set_status(status_num, "unpacked", 3);
+
+	free(info_prefix);
+	free(list_filename);
+}
+
+static void configure_package(deb_file_t *deb_file)
+{
+	const char *package_name = name_hashtable[package_hashtable[deb_file->package]->name];
+	const char *package_version = name_hashtable[package_hashtable[deb_file->package]->version];
+	const int status_num = search_status_hashtable(package_name);
+
+	printf("Setting up %s (%s)...\n", package_name, package_version);
+
+	/* Run the postinst script */
+	/* TODO: handle failure gracefully */
+	run_package_script_or_die(package_name, "postinst");
+
+	/* Change status to reflect success */
+	set_status(status_num, "install", 1);
+	set_status(status_num, "installed", 3);
+}
+
+int dpkg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dpkg_main(int argc UNUSED_PARAM, char **argv)
+{
+	deb_file_t **deb_file = NULL;
+	status_node_t *status_node;
+	char *str_f;
+	int opt;
+	int package_num;
+	int deb_count = 0;
+	int state_status;
+	int status_num;
+	int i;
+#if ENABLE_LONG_OPTS
+	static const char dpkg_longopts[] ALIGN1 =
+// FIXME: we use -C non-compatibly, should be:
+// "-C|--audit Check for broken package(s)"
+		"configure\0"      No_argument        "C"
+		"force\0"          Required_argument  "F"
+		"install\0"        No_argument        "i"
+		"list\0"           No_argument        "l"
+		"purge\0"          No_argument        "P"
+		"remove\0"         No_argument        "r"
+		"unpack\0"         No_argument        "u"
+		"force-depends\0"  No_argument        "\xff"
+		"force-confnew\0"  No_argument        "\xfe"
+		"force-confold\0"  No_argument        "\xfd"
+		;
+#endif
+
+	INIT_G();
+
+	IF_LONG_OPTS(applet_long_options = dpkg_longopts);
+	opt = getopt32(argv, "CilPruF:", &str_f);
+	argv += optind;
+	//if (opt & OPT_configure) ... // -C
+	if (opt & OPT_force) { // -F (--force in official dpkg)
+		if (strcmp(str_f, "depends") == 0)
+			opt |= OPT_force_ignore_depends;
+		else if (strcmp(str_f, "confnew") == 0)
+			opt |= OPT_force_confnew;
+		else if (strcmp(str_f, "confold") == 0)
+			opt |= OPT_force_confold;
+		else
+			bb_show_usage();
+		option_mask32 = opt;
+	}
+	//if (opt & OPT_install) ... // -i
+	//if (opt & OPT_list_installed) ... // -l
+	//if (opt & OPT_purge) ... // -P
+	//if (opt & OPT_remove) ... // -r
+	//if (opt & OPT_unpack) ... // -u (--unpack in official dpkg)
+	if (!(opt & OPTMASK_cmd) /* no cmd */
+	 || ((opt & OPTMASK_cmd) & ((opt & OPTMASK_cmd)-1)) /* more than one cmd */
+	) {
+		bb_show_usage();
+	}
+
+/*	puts("(Reading database ... xxxxx files and directories installed.)"); */
+	index_status_file("/var/lib/dpkg/status");
+
+	/* if the list action was given print the installed packages and exit */
+	if (opt & OPT_list_installed) {
+		list_packages(argv[0]); /* param can be NULL */
+		return EXIT_SUCCESS;
+	}
+
+	/* Read arguments and store relevant info in structs */
+	while (*argv) {
+		/* deb_count = nb_elem - 1 and we need nb_elem + 1 to allocate terminal node [NULL pointer] */
+		deb_file = xrealloc_vector(deb_file, 2, deb_count);
+		deb_file[deb_count] = xzalloc(sizeof(deb_file[0][0]));
+		if (opt & (OPT_install | OPT_unpack)) {
+			/* -i/-u: require filename */
+			archive_handle_t *archive_handle;
+			llist_t *control_list = NULL;
+
+			/* Extract the control file */
+			llist_add_to(&control_list, (char*)"./control");
+			archive_handle = init_archive_deb_ar(argv[0]);
+			init_archive_deb_control(archive_handle);
+			deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list);
+			if (deb_file[deb_count]->control_file == NULL) {
+				bb_error_msg_and_die("can't extract control file");
+			}
+			deb_file[deb_count]->filename = xstrdup(argv[0]);
+			package_num = fill_package_struct(deb_file[deb_count]->control_file);
+
+			if (package_num == -1) {
+				bb_error_msg("invalid control file in %s", argv[0]);
+				argv++;
+				continue;
+			}
+			deb_file[deb_count]->package = (unsigned) package_num;
+
+			/* Add the package to the status hashtable */
+			if (opt & (OPT_unpack | OPT_install)) {
+				/* Try and find a currently installed version of this package */
+				status_num = search_status_hashtable(name_hashtable[package_hashtable[deb_file[deb_count]->package]->name]);
+				/* If no previous entry was found initialise a new entry */
+				if (status_hashtable[status_num] == NULL
+				 || status_hashtable[status_num]->status == 0
+				) {
+					status_node = xmalloc(sizeof(status_node_t));
+					status_node->package = deb_file[deb_count]->package;
+					/* reinstreq isnt changed to "ok" until the package control info
+					 * is written to the status file*/
+					status_node->status = search_name_hashtable("install reinstreq not-installed");
+					status_hashtable[status_num] = status_node;
+				} else {
+					set_status(status_num, "install", 1);
+					set_status(status_num, "reinstreq", 2);
+				}
+			}
+		} else if (opt & (OPT_configure | OPT_purge | OPT_remove)) {
+			/* -C/-p/-r: require package name */
+			deb_file[deb_count]->package = search_package_hashtable(
+					search_name_hashtable(argv[0]),
+					search_name_hashtable("ANY"), VER_ANY);
+			if (package_hashtable[deb_file[deb_count]->package] == NULL) {
+				bb_error_msg_and_die("package %s is uninstalled or unknown", argv[0]);
+			}
+			package_num = deb_file[deb_count]->package;
+			status_num = search_status_hashtable(name_hashtable[package_hashtable[package_num]->name]);
+			state_status = get_status(status_num, 3);
+
+			/* check package status is "installed" */
+			if (opt & OPT_remove) {
+				if (strcmp(name_hashtable[state_status], "not-installed") == 0
+				 || strcmp(name_hashtable[state_status], "config-files") == 0
+				) {
+					bb_error_msg_and_die("%s is already removed", name_hashtable[package_hashtable[package_num]->name]);
+				}
+				set_status(status_num, "deinstall", 1);
+			} else if (opt & OPT_purge) {
+				/* if package status is "conf-files" then its ok */
+				if (strcmp(name_hashtable[state_status], "not-installed") == 0) {
+					bb_error_msg_and_die("%s is already purged", name_hashtable[package_hashtable[package_num]->name]);
+				}
+				set_status(status_num, "purge", 1);
+			}
+		}
+		deb_count++;
+		argv++;
+	}
+	if (!deb_count)
+		bb_error_msg_and_die("no package files specified");
+	deb_file[deb_count] = NULL;
+
+	/* Check that the deb file arguments are installable */
+	if (!(opt & OPT_force_ignore_depends)) {
+		if (!check_deps(deb_file, 0 /*, deb_count*/)) {
+			bb_error_msg_and_die("dependency check failed");
+		}
+	}
+
+	/* TODO: install or remove packages in the correct dependency order */
+	for (i = 0; i < deb_count; i++) {
+		/* Remove or purge packages */
+		if (opt & OPT_remove) {
+			remove_package(deb_file[i]->package, 1);
+		}
+		else if (opt & OPT_purge) {
+			purge_package(deb_file[i]->package);
+		}
+		else if (opt & OPT_unpack) {
+			unpack_package(deb_file[i]);
+		}
+		else if (opt & OPT_install) {
+			unpack_package(deb_file[i]);
+			/* package is configured in second pass below */
+		}
+		else if (opt & OPT_configure) {
+			configure_package(deb_file[i]);
+		}
+	}
+	/* configure installed packages */
+	if (opt & OPT_install) {
+		for (i = 0; i < deb_count; i++)
+			configure_package(deb_file[i]);
+	}
+
+	write_status_file(deb_file);
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		for (i = 0; i < deb_count; i++) {
+			free(deb_file[i]->control_file);
+			free(deb_file[i]->filename);
+			free(deb_file[i]);
+		}
+
+		free(deb_file);
+
+		for (i = 0; i < NAME_HASH_PRIME; i++) {
+			free(name_hashtable[i]);
+		}
+
+		for (i = 0; i < PACKAGE_HASH_PRIME; i++) {
+			free_package(package_hashtable[i]);
+		}
+
+		for (i = 0; i < STATUS_HASH_PRIME; i++) {
+			free(status_hashtable[i]);
+		}
+
+		free(status_hashtable);
+		free(package_hashtable);
+		free(name_hashtable);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/dpkg_deb.c b/busybox-1.19.3/archival/dpkg_deb.c
new file mode 100644
index 0000000..5d814d7
--- /dev/null
+++ b/busybox-1.19.3/archival/dpkg_deb.c
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dpkg-deb packs, unpacks and provides information about Debian archives.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//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 "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
+
+int dpkg_deb_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dpkg_deb_main(int argc, char **argv)
+{
+	archive_handle_t *ar_archive;
+	archive_handle_t *tar_archive;
+	llist_t *control_tar_llist = NULL;
+	unsigned opt;
+	const char *extract_dir;
+	int need_args;
+
+	/* Setup the tar archive handle */
+	tar_archive = init_handle();
+
+	/* Setup an ar archive handle that refers to the gzip sub archive */
+	ar_archive = init_handle();
+	ar_archive->dpkg__sub_archive = tar_archive;
+	ar_archive->filter = filter_accept_list_reassign;
+
+#if ENABLE_FEATURE_SEAMLESS_GZ
+	llist_add_to(&ar_archive->accept, (char*)"data.tar.gz");
+	llist_add_to(&control_tar_llist, (char*)"control.tar.gz");
+#endif
+#if ENABLE_FEATURE_SEAMLESS_BZ2
+	llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2");
+	llist_add_to(&control_tar_llist, (char*)"control.tar.bz2");
+#endif
+#if ENABLE_FEATURE_SEAMLESS_LZMA
+	llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma");
+	llist_add_to(&control_tar_llist, (char*)"control.tar.lzma");
+#endif
+
+	opt_complementary = "c--efXx:e--cfXx:f--ceXx:X--cefx:x--cefX";
+	opt = getopt32(argv, "cefXx");
+	argv += optind;
+	argc -= optind;
+
+	if (opt & DPKG_DEB_OPT_CONTENTS) {
+		tar_archive->action_header = header_verbose_list;
+	}
+	extract_dir = NULL;
+	need_args = 1;
+	if (opt & DPKG_DEB_OPT_CONTROL) {
+		ar_archive->accept = control_tar_llist;
+		tar_archive->action_data = data_extract_all;
+		if (1 == argc) {
+			extract_dir = "./DEBIAN";
+		} else {
+			need_args++;
+		}
+	}
+	if (opt & DPKG_DEB_OPT_FIELD) {
+		/* Print the entire control file
+		 * it should accept a second argument which specifies a
+		 * specific field to print */
+		ar_archive->accept = control_tar_llist;
+		llist_add_to(&(tar_archive->accept), (char*)"./control");
+		tar_archive->filter = filter_accept_list;
+		tar_archive->action_data = data_extract_to_stdout;
+	}
+	if (opt & DPKG_DEB_OPT_EXTRACT) {
+		tar_archive->action_header = header_list;
+	}
+	if (opt & (DPKG_DEB_OPT_EXTRACT_VERBOSE | DPKG_DEB_OPT_EXTRACT)) {
+		tar_archive->action_data = data_extract_all;
+		need_args = 2;
+	}
+
+	if (need_args != argc) {
+		bb_show_usage();
+	}
+
+	tar_archive->src_fd = ar_archive->src_fd = xopen(argv[0], O_RDONLY);
+
+	/* Work out where to extract the files */
+	/* 2nd argument is a dir name */
+	if (argv[1]) {
+		extract_dir = argv[1];
+	}
+	if (extract_dir) {
+		mkdir(extract_dir, 0777); /* bb_make_directory(extract_dir, 0777, 0) */
+		xchdir(extract_dir);
+	}
+
+	/* Do it */
+	unpack_ar_archive(ar_archive);
+
+	/* Cleanup */
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(ar_archive->src_fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/gzip.c b/busybox-1.19.3/archival/gzip.c
new file mode 100644
index 0000000..403eb4d
--- /dev/null
+++ b/busybox-1.19.3/archival/gzip.c
@@ -0,0 +1,2120 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Gzip implementation for busybox
+ *
+ * Based on GNU gzip Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Charles P. Wright <cpw@unix.asb.com>
+ * "this is a stripped down version of gzip I put into busybox, it does
+ * only standard in to standard out with -9 compression.  It also requires
+ * the zcat module for some important functions."
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
+ * 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 source tree.
+ */
+
+/* big objects in bss:
+ * 00000020 b bl_count
+ * 00000074 b base_length
+ * 00000078 b base_dist
+ * 00000078 b static_dtree
+ * 0000009c b bl_tree
+ * 000000f4 b dyn_dtree
+ * 00000100 b length_code
+ * 00000200 b dist_code
+ * 0000023d b depth
+ * 00000400 b flag_buf
+ * 0000047a b heap
+ * 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
+gzip: bogus: No such file or directory
+aa:      85.1% -- replaced with aa.gz
+*/
+
+//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 "archive.h"
+
+
+/* ===========================================================================
+ */
+//#define DEBUG 1
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) { if (!(cond)) bb_error_msg(msg); }
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x; }
+#  define Tracevv(x) {if (verbose > 1) fprintf x; }
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x; }
+#  define Tracecv(c,x) {if (verbose > 1 && (c)) fprintf x; }
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+
+/* ===========================================================================
+ */
+#define SMALL_MEM
+
+#ifndef INBUFSIZ
+#  ifdef SMALL_MEM
+#    define INBUFSIZ  0x2000	/* input buffer size */
+#  else
+#    define INBUFSIZ  0x8000	/* input buffer size */
+#  endif
+#endif
+
+#ifndef OUTBUFSIZ
+#  ifdef SMALL_MEM
+#    define OUTBUFSIZ   8192	/* output buffer size */
+#  else
+#    define OUTBUFSIZ  16384	/* output buffer size */
+#  endif
+#endif
+
+#ifndef DIST_BUFSIZE
+#  ifdef SMALL_MEM
+#    define DIST_BUFSIZE 0x2000	/* buffer for distances, see trees.c */
+#  else
+#    define DIST_BUFSIZE 0x8000	/* buffer for distances, see trees.c */
+#  endif
+#endif
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01	/* bit 0 set: file probably ascii text */
+#define CONTINUATION 0x02	/* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04	/* bit 2 set: extra field present */
+#define ORIG_NAME    0x08	/* bit 3 set: original file name present */
+#define COMMENT      0x10	/* bit 4 set: file comment present */
+#define RESERVED     0xC0	/* bit 6,7:   reserved */
+
+/* internal file attribute */
+#define UNKNOWN 0xffff
+#define BINARY  0
+#define ASCII   1
+
+#ifndef WSIZE
+#  define WSIZE 0x8000  /* window size--must be a power of two, and */
+#endif                  /*  at least 32K for zip's deflate method */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST  (WSIZE-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+#ifndef MAX_PATH_LEN
+#  define MAX_PATH_LEN   1024	/* max pathname length */
+#endif
+
+#define seekable()    0	/* force sequential output */
+#define translate_eol 0	/* no option -a yet */
+
+#ifndef BITS
+#  define BITS 16
+#endif
+#define INIT_BITS 9		/* Initial number of bits per code */
+
+#define BIT_MASK    0x1f	/* Mask for 'number of compression bits' */
+/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
+ * It's a pity that old uncompress does not check bit 0x20. That makes
+ * extension of the format actually undesirable because old compress
+ * would just crash on the new format instead of giving a meaningful
+ * error message. It does check the number of bits, but it's more
+ * helpful to say "unsupported format, get a new version" than
+ * "can only handle 16 bits".
+ */
+
+#ifdef MAX_EXT_CHARS
+#  define MAX_SUFFIX  MAX_EXT_CHARS
+#else
+#  define MAX_SUFFIX  30
+#endif
+
+
+/* ===========================================================================
+ * Compile with MEDIUM_MEM to reduce the memory requirements or
+ * with SMALL_MEM to use as little memory as possible. Use BIG_MEM if the
+ * entire input file can be held in memory (not possible on 16 bit systems).
+ * Warning: defining these symbols affects HASH_BITS (see below) and thus
+ * affects the compression ratio. The compressed output
+ * is still correct, and might even be smaller in some cases.
+ */
+
+#ifdef SMALL_MEM
+#   define HASH_BITS  13	/* Number of bits used to hash strings */
+#endif
+#ifdef MEDIUM_MEM
+#   define HASH_BITS  14
+#endif
+#ifndef HASH_BITS
+#   define HASH_BITS  15
+   /* For portability to 16 bit machines, do not use values above 15. */
+#endif
+
+#define HASH_SIZE (unsigned)(1<<HASH_BITS)
+#define HASH_MASK (HASH_SIZE-1)
+#define WMASK     (WSIZE-1)
+/* HASH_SIZE and WSIZE must be powers of two */
+#ifndef TOO_FAR
+#  define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+
+/* ===========================================================================
+ * These types are not really 'char', 'short' and 'long'
+ */
+typedef uint8_t uch;
+typedef uint16_t ush;
+typedef uint32_t ulg;
+typedef int32_t lng;
+
+typedef ush Pos;
+typedef unsigned IPos;
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+enum {
+	WINDOW_SIZE = 2 * WSIZE,
+/* window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the
+ * input file length plus MIN_LOOKAHEAD.
+ */
+
+	max_chain_length = 4096,
+/* To speed up deflation, hash chains are never searched beyond this length.
+ * A higher limit improves compression ratio but degrades the speed.
+ */
+
+	max_lazy_match = 258,
+/* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+
+	max_insert_length = max_lazy_match,
+/* Insert new strings in the hash table only if the match length
+ * is not greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+	good_match = 32,
+/* Use a faster search when the previous match is longer than this */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+
+	nice_match = 258,	/* Stop searching when current match exceeds this */
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+};
+
+
+struct globals {
+
+	lng block_start;
+
+/* window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+	unsigned ins_h;	/* hash index of string to be inserted */
+
+#define H_SHIFT  ((HASH_BITS+MIN_MATCH-1) / MIN_MATCH)
+/* Number of bits by which ins_h and del_h must be shifted at each
+ * input step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * H_SHIFT * MIN_MATCH >= HASH_BITS
+ */
+
+	unsigned prev_length;
+
+/* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+	unsigned strstart;	/* start of string to insert */
+	unsigned match_start;	/* start of matching string */
+	unsigned lookahead;	/* number of valid bytes ahead in window */
+
+/* ===========================================================================
+ */
+#define DECLARE(type, array, size) \
+	type * array
+#define ALLOC(type, array, size) \
+	array = xzalloc((size_t)(((size)+1L)/2) * 2*sizeof(type))
+#define FREE(array) \
+	do { free(array); array = NULL; } while (0)
+
+	/* global buffers */
+
+	/* buffer for literals or lengths */
+	/* DECLARE(uch, l_buf, LIT_BUFSIZE); */
+	DECLARE(uch, l_buf, INBUFSIZ);
+
+	DECLARE(ush, d_buf, DIST_BUFSIZE);
+	DECLARE(uch, outbuf, OUTBUFSIZ);
+
+/* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least WSIZE
+ * bytes. With this organization, matches are limited to a distance of
+ * WSIZE-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size. Also, it limits
+ * the window size to 64K, which is quite useful on MSDOS.
+ * To do: limit the window size to WSIZE+BSZ if SMALL_MEM (the code would
+ * be less efficient).
+ */
+	DECLARE(uch, window, 2L * WSIZE);
+
+/* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+	/* DECLARE(Pos, prev, WSIZE); */
+	DECLARE(ush, prev, 1L << BITS);
+
+/* Heads of the hash chains or 0. */
+	/* DECLARE(Pos, head, 1<<HASH_BITS); */
+#define head (G1.prev + WSIZE) /* hash head (see deflate.c) */
+
+/* number of input bytes */
+	ulg isize;		/* only 32 bits stored in .gz file */
+
+/* bbox always use stdin/stdout */
+#define ifd STDIN_FILENO	/* input file descriptor */
+#define ofd STDOUT_FILENO	/* output file descriptor */
+
+#ifdef DEBUG
+	unsigned insize;	/* valid bytes in l_buf */
+#endif
+	unsigned outcnt;	/* bytes in output buffer */
+
+	smallint eofile;	/* flag set at end of input file */
+
+/* ===========================================================================
+ * Local data used by the "bit string" routines.
+ */
+
+	unsigned short bi_buf;
+
+/* Output buffer. bits are inserted starting at the bottom (least significant
+ * bits).
+ */
+
+#undef BUF_SIZE
+#define BUF_SIZE (8 * sizeof(G1.bi_buf))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+	int bi_valid;
+
+/* Current input function. Set to mem_read for in-memory compression */
+
+#ifdef DEBUG
+	ulg bits_sent;			/* bit length of the compressed data */
+#endif
+
+	/*uint32_t *crc_32_tab;*/
+	uint32_t crc;	/* shift register contents */
+};
+
+#define G1 (*(ptr_to_globals - 1))
+
+
+/* ===========================================================================
+ * Write the output buffer outbuf[0..outcnt-1] and update bytes_out.
+ * (used for the compressed data only)
+ */
+static void flush_outbuf(void)
+{
+	if (G1.outcnt == 0)
+		return;
+
+	xwrite(ofd, (char *) G1.outbuf, G1.outcnt);
+	G1.outcnt = 0;
+}
+
+
+/* ===========================================================================
+ */
+/* put_8bit is used for the compressed output */
+#define put_8bit(c) \
+do { \
+	G1.outbuf[G1.outcnt++] = (c); \
+	if (G1.outcnt == OUTBUFSIZ) flush_outbuf(); \
+} while (0)
+
+/* Output a 16 bit value, lsb first */
+static void put_16bit(ush w)
+{
+	if (G1.outcnt < OUTBUFSIZ - 2) {
+		G1.outbuf[G1.outcnt++] = w;
+		G1.outbuf[G1.outcnt++] = w >> 8;
+	} else {
+		put_8bit(w);
+		put_8bit(w >> 8);
+	}
+}
+
+static void put_32bit(ulg n)
+{
+	put_16bit(n);
+	put_16bit(n >> 16);
+}
+
+/* ===========================================================================
+ * Run a set of bytes through the crc shift register.  If s is a NULL
+ * pointer, then initialize the crc shift register contents instead.
+ * Return the current crc in either case.
+ */
+static void updcrc(uch * s, unsigned n)
+{
+	G1.crc = crc32_block_endian0(G1.crc, s, n, global_crc32_table /*G1.crc_32_tab*/);
+}
+
+
+/* ===========================================================================
+ * Read a new buffer from the current input file, perform end-of-line
+ * translation, and update the crc and input file size.
+ * IN assertion: size >= 2 (for end-of-line translation)
+ */
+static unsigned file_read(void *buf, unsigned size)
+{
+	unsigned len;
+
+	Assert(G1.insize == 0, "l_buf not empty");
+
+	len = safe_read(ifd, buf, size);
+	if (len == (unsigned)(-1) || len == 0)
+		return len;
+
+	updcrc(buf, len);
+	G1.isize += len;
+	return len;
+}
+
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+static void send_bits(int value, int length)
+{
+#ifdef DEBUG
+	Tracev((stderr, " l %2d v %4x ", length, value));
+	Assert(length > 0 && length <= 15, "invalid length");
+	G1.bits_sent += length;
+#endif
+	/* If not enough room in bi_buf, use (valid) bits from bi_buf and
+	 * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+	 * unused bits in value.
+	 */
+	if (G1.bi_valid > (int) BUF_SIZE - length) {
+		G1.bi_buf |= (value << G1.bi_valid);
+		put_16bit(G1.bi_buf);
+		G1.bi_buf = (ush) value >> (BUF_SIZE - G1.bi_valid);
+		G1.bi_valid += length - BUF_SIZE;
+	} else {
+		G1.bi_buf |= value << G1.bi_valid;
+		G1.bi_valid += length;
+	}
+}
+
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+static unsigned bi_reverse(unsigned code, int len)
+{
+	unsigned res = 0;
+
+	while (1) {
+		res |= code & 1;
+		if (--len <= 0) return res;
+		code >>= 1;
+		res <<= 1;
+	}
+}
+
+
+/* ===========================================================================
+ * Write out any remaining bits in an incomplete byte.
+ */
+static void bi_windup(void)
+{
+	if (G1.bi_valid > 8) {
+		put_16bit(G1.bi_buf);
+	} else if (G1.bi_valid > 0) {
+		put_8bit(G1.bi_buf);
+	}
+	G1.bi_buf = 0;
+	G1.bi_valid = 0;
+#ifdef DEBUG
+	G1.bits_sent = (G1.bits_sent + 7) & ~7;
+#endif
+}
+
+
+/* ===========================================================================
+ * Copy a stored block to the zip file, storing first the length and its
+ * one's complement if requested.
+ */
+static void copy_block(char *buf, unsigned len, int header)
+{
+	bi_windup();		/* align on byte boundary */
+
+	if (header) {
+		put_16bit(len);
+		put_16bit(~len);
+#ifdef DEBUG
+		G1.bits_sent += 2 * 16;
+#endif
+	}
+#ifdef DEBUG
+	G1.bits_sent += (ulg) len << 3;
+#endif
+	while (len--) {
+		put_8bit(*buf++);
+	}
+}
+
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead, and sets eofile if end of input file.
+ * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0
+ * OUT assertions: at least one byte has been read, or eofile is set;
+ *    file reads are performed for at least two bytes (required for the
+ *    translate_eol option).
+ */
+static void fill_window(void)
+{
+	unsigned n, m;
+	unsigned more =	WINDOW_SIZE - G1.lookahead - G1.strstart;
+	/* Amount of free space at the end of the window. */
+
+	/* If the window is almost full and there is insufficient lookahead,
+	 * move the upper half to the lower one to make room in the upper half.
+	 */
+	if (more == (unsigned) -1) {
+		/* Very unlikely, but possible on 16 bit machine if strstart == 0
+		 * and lookahead == 1 (input done one byte at time)
+		 */
+		more--;
+	} else if (G1.strstart >= WSIZE + MAX_DIST) {
+		/* By the IN assertion, the window is not empty so we can't confuse
+		 * more == 0 with more == 64K on a 16 bit machine.
+		 */
+		Assert(WINDOW_SIZE == 2 * WSIZE, "no sliding with BIG_MEM");
+
+		memcpy(G1.window, G1.window + WSIZE, WSIZE);
+		G1.match_start -= WSIZE;
+		G1.strstart -= WSIZE;	/* we now have strstart >= MAX_DIST: */
+
+		G1.block_start -= WSIZE;
+
+		for (n = 0; n < HASH_SIZE; n++) {
+			m = head[n];
+			head[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0);
+		}
+		for (n = 0; n < WSIZE; n++) {
+			m = G1.prev[n];
+			G1.prev[n] = (Pos) (m >= WSIZE ? m - WSIZE : 0);
+			/* If n is not on any hash chain, prev[n] is garbage but
+			 * its value will never be used.
+			 */
+		}
+		more += WSIZE;
+	}
+	/* At this point, more >= 2 */
+	if (!G1.eofile) {
+		n = file_read(G1.window + G1.strstart + G1.lookahead, more);
+		if (n == 0 || n == (unsigned) -1) {
+			G1.eofile = 1;
+		} else {
+			G1.lookahead += n;
+		}
+	}
+}
+
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ */
+
+/* For MSDOS, OS/2 and 386 Unix, an optimized version is in match.asm or
+ * match.s. The code is functionally equivalent, so you can use the C version
+ * if desired.
+ */
+static int longest_match(IPos cur_match)
+{
+	unsigned chain_length = max_chain_length;	/* max hash chain length */
+	uch *scan = G1.window + G1.strstart;	/* current string */
+	uch *match;	/* matched string */
+	int len;	/* length of current match */
+	int best_len = G1.prev_length;	/* best match length so far */
+	IPos limit = G1.strstart > (IPos) MAX_DIST ? G1.strstart - (IPos) MAX_DIST : 0;
+	/* Stop when cur_match becomes <= limit. To simplify the code,
+	 * we prevent matches with the string of window index 0.
+	 */
+
+/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+#if HASH_BITS < 8 || MAX_MATCH != 258
+#  error Code too clever
+#endif
+	uch *strend = G1.window + G1.strstart + MAX_MATCH;
+	uch scan_end1 = scan[best_len - 1];
+	uch scan_end = scan[best_len];
+
+	/* Do not waste too much time if we already have a good match: */
+	if (G1.prev_length >= good_match) {
+		chain_length >>= 2;
+	}
+	Assert(G1.strstart <= WINDOW_SIZE - MIN_LOOKAHEAD, "insufficient lookahead");
+
+	do {
+		Assert(cur_match < G1.strstart, "no future");
+		match = G1.window + cur_match;
+
+		/* Skip to next match if the match length cannot increase
+		 * or if the match length is less than 2:
+		 */
+		if (match[best_len] != scan_end
+		 || match[best_len - 1] != scan_end1
+		 || *match != *scan || *++match != scan[1]
+		) {
+			continue;
+		}
+
+		/* The check at best_len-1 can be removed because it will be made
+		 * again later. (This heuristic is not always a win.)
+		 * It is not necessary to compare scan[2] and match[2] since they
+		 * are always equal when the other bytes match, given that
+		 * the hash keys are equal and that HASH_BITS >= 8.
+		 */
+		scan += 2, match++;
+
+		/* We check for insufficient lookahead only every 8th comparison;
+		 * the 256th check will be made at strstart+258.
+		 */
+		do {
+		} while (*++scan == *++match && *++scan == *++match &&
+				 *++scan == *++match && *++scan == *++match &&
+				 *++scan == *++match && *++scan == *++match &&
+				 *++scan == *++match && *++scan == *++match && scan < strend);
+
+		len = MAX_MATCH - (int) (strend - scan);
+		scan = strend - MAX_MATCH;
+
+		if (len > best_len) {
+			G1.match_start = cur_match;
+			best_len = len;
+			if (len >= nice_match)
+				break;
+			scan_end1 = scan[best_len - 1];
+			scan_end = scan[best_len];
+		}
+	} while ((cur_match = G1.prev[cur_match & WMASK]) > limit
+			 && --chain_length != 0);
+
+	return best_len;
+}
+
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+static void check_match(IPos start, IPos match, int length)
+{
+	/* check that the match is indeed a match */
+	if (memcmp(G1.window + match, G1.window + start, length) != 0) {
+		bb_error_msg(" start %d, match %d, length %d", start, match, length);
+		bb_error_msg("invalid match");
+	}
+	if (verbose > 1) {
+		bb_error_msg("\\[%d,%d]", start - match, length);
+		do {
+			bb_putchar_stderr(G1.window[start++]);
+		} while (--length != 0);
+	}
+}
+#else
+#  define check_match(start, match, length) ((void)0)
+#endif
+
+
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * This is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License, see the file COPYING.
+ */
+
+/*  PURPOSE
+ *      Encode various sets of source values using variable-length
+ *      binary code trees.
+ *
+ *  DISCUSSION
+ *      The PKZIP "deflation" process uses several Huffman trees. The more
+ *      common source values are represented by shorter bit sequences.
+ *
+ *      Each code tree is stored in the ZIP file in a compressed form
+ *      which is itself a Huffman encoding of the lengths of
+ *      all the code strings (in ascending order by source values).
+ *      The actual code strings are reconstructed from the lengths in
+ *      the UNZIP process, as described in the "application note"
+ *      (APPNOTE.TXT) distributed as part of PKWARE's PKZIP program.
+ *
+ *  REFERENCES
+ *      Lynch, Thomas J.
+ *          Data Compression:  Techniques and Applications, pp. 53-55.
+ *          Lifetime Learning Publications, 1985.  ISBN 0-534-03418-7.
+ *
+ *      Storer, James A.
+ *          Data Compression:  Methods and Theory, pp. 49-50.
+ *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.
+ *
+ *      Sedgewick, R.
+ *          Algorithms, p290.
+ *          Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ *
+ *  INTERFACE
+ *      void ct_init()
+ *          Allocate the match buffer, initialize the various tables [and save
+ *          the location of the internal file attribute (ascii/binary) and
+ *          method (DEFLATE/STORE) -- deleted in bbox]
+ *
+ *      void ct_tally(int dist, int lc);
+ *          Save the match info and tally the frequency counts.
+ *
+ *      ulg flush_block(char *buf, ulg stored_len, int eof)
+ *          Determine the best encoding for the current block: dynamic trees,
+ *          static trees or store, and output the encoded block to the zip
+ *          file. Returns the total compressed length for the file so far.
+ */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS  256
+/* number of literal bytes 0..255 */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES   30
+/* number of distance codes */
+
+#define BL_CODES  19
+/* number of codes used to transfer the bit lengths */
+
+/* extra bits for each length code */
+static const uint8_t extra_lbits[LENGTH_CODES] ALIGN1 = {
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4,
+	4, 4, 5, 5, 5, 5, 0
+};
+
+/* extra bits for each distance code */
+static const uint8_t extra_dbits[D_CODES] ALIGN1 = {
+	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,
+	10, 10, 11, 11, 12, 12, 13, 13
+};
+
+/* extra bits for each bit length code */
+static const uint8_t extra_blbits[BL_CODES] ALIGN1 = {
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7 };
+
+/* number of codes at each bit length for an optimal tree */
+static const uint8_t bl_order[BL_CODES] ALIGN1 = {
+	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#ifndef LIT_BUFSIZE
+#  ifdef SMALL_MEM
+#    define LIT_BUFSIZE  0x2000
+#  else
+#  ifdef MEDIUM_MEM
+#    define LIT_BUFSIZE  0x4000
+#  else
+#    define LIT_BUFSIZE  0x8000
+#  endif
+#  endif
+#endif
+#ifndef DIST_BUFSIZE
+#  define DIST_BUFSIZE  LIT_BUFSIZE
+#endif
+/* Sizes of match buffers for literals/lengths and distances.  There are
+ * 4 reasons for limiting LIT_BUFSIZE to 64K:
+ *   - frequencies can be kept in 16 bit counters
+ *   - if compression is not successful for the first block, all input data is
+ *     still in the window so we can still emit a stored block even when input
+ *     comes from standard input.  (This can also be done for all blocks if
+ *     LIT_BUFSIZE is not greater than 32K.)
+ *   - if compression is not successful for a file smaller than 64K, we can
+ *     even emit a stored file instead of a stored block (saving 5 bytes).
+ *   - creating new Huffman trees less frequently may not provide fast
+ *     adaptation to changes in the input data statistics. (Take for
+ *     example a binary file with poorly compressible code followed by
+ *     a highly compressible string table.) Smaller buffer sizes give
+ *     fast adaptation but have of course the overhead of transmitting trees
+ *     more frequently.
+ *   - I can't count above 4
+ * The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save
+ * memory at the expense of compression). Some optimizations would be possible
+ * if we rely on DIST_BUFSIZE == LIT_BUFSIZE.
+ */
+#define REP_3_6      16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+#define REPZ_3_10    17
+/* repeat a zero length 3-10 times  (3 bits of repeat count) */
+#define REPZ_11_138  18
+/* repeat a zero length 11-138 times  (7 bits of repeat count) */
+
+/* ===========================================================================
+*/
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data {
+	union {
+		ush freq;		/* frequency count */
+		ush code;		/* bit string */
+	} fc;
+	union {
+		ush dad;		/* father node in Huffman tree */
+		ush len;		/* length of bit string */
+	} dl;
+} ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad  dl.dad
+#define Len  dl.len
+
+#define HEAP_SIZE (2*L_CODES + 1)
+/* maximum heap size */
+
+typedef struct tree_desc {
+	ct_data *dyn_tree;	/* the dynamic tree */
+	ct_data *static_tree;	/* corresponding static tree or NULL */
+	const uint8_t *extra_bits;	/* extra bits for each code or NULL */
+	int extra_base;		/* base index for extra_bits */
+	int elems;			/* max number of elements in the tree */
+	int max_length;		/* max bit length for the codes */
+	int max_code;		/* largest code with non zero frequency */
+} tree_desc;
+
+struct globals2 {
+
+	ush heap[HEAP_SIZE];     /* heap used to build the Huffman trees */
+	int heap_len;            /* number of elements in the heap */
+	int heap_max;            /* element of largest frequency */
+
+/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+	ct_data dyn_ltree[HEAP_SIZE];	/* literal and length tree */
+	ct_data dyn_dtree[2 * D_CODES + 1];	/* distance tree */
+
+	ct_data static_ltree[L_CODES + 2];
+
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see ct_init
+ * below).
+ */
+
+	ct_data static_dtree[D_CODES];
+
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+	ct_data bl_tree[2 * BL_CODES + 1];
+
+/* Huffman tree for the bit lengths */
+
+	tree_desc l_desc;
+	tree_desc d_desc;
+	tree_desc bl_desc;
+
+	ush bl_count[MAX_BITS + 1];
+
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+	uch depth[2 * L_CODES + 1];
+
+/* Depth of each subtree used as tie breaker for trees of equal frequency */
+
+	uch length_code[MAX_MATCH - MIN_MATCH + 1];
+
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+	uch dist_code[512];
+
+/* distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+	int base_length[LENGTH_CODES];
+
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+	int base_dist[D_CODES];
+
+/* First normalized distance for each code (0 = distance of 1) */
+
+	uch flag_buf[LIT_BUFSIZE / 8];
+
+/* flag_buf is a bit array distinguishing literals from lengths in
+ * l_buf, thus indicating the presence or absence of a distance.
+ */
+
+	unsigned last_lit;       /* running index in l_buf */
+	unsigned last_dist;      /* running index in d_buf */
+	unsigned last_flags;     /* running index in flag_buf */
+	uch flags;               /* current flags not yet saved in flag_buf */
+	uch flag_bit;            /* current bit used in flags */
+
+/* bits are filled in flags starting at bit 0 (least significant).
+ * Note: these flags are overkill in the current code since we don't
+ * take advantage of DIST_BUFSIZE == LIT_BUFSIZE.
+ */
+
+	ulg opt_len;             /* bit length of current block with optimal trees */
+	ulg static_len;          /* bit length of current block with static trees */
+
+	ulg compressed_len;      /* total bit length of compressed file */
+};
+
+#define G2ptr ((struct globals2*)(ptr_to_globals))
+#define G2 (*G2ptr)
+
+
+/* ===========================================================================
+ */
+static void gen_codes(ct_data * tree, int max_code);
+static void build_tree(tree_desc * desc);
+static void scan_tree(ct_data * tree, int max_code);
+static void send_tree(ct_data * tree, int max_code);
+static int build_bl_tree(void);
+static void send_all_trees(int lcodes, int dcodes, int blcodes);
+static void compress_block(ct_data * ltree, ct_data * dtree);
+
+
+#ifndef DEBUG
+/* Send a code of the given tree. c and tree must not have side effects */
+#  define SEND_CODE(c, tree) send_bits(tree[c].Code, tree[c].Len)
+#else
+#  define SEND_CODE(c, tree) \
+{ \
+	if (verbose > 1) bb_error_msg("\ncd %3d ", (c)); \
+	send_bits(tree[c].Code, tree[c].Len); \
+}
+#endif
+
+#define D_CODE(dist) \
+	((dist) < 256 ? G2.dist_code[dist] : G2.dist_code[256 + ((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. dist_code[256] and dist_code[257] are never
+ * used.
+ * The arguments must not have side effects.
+ */
+
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+static void init_block(void)
+{
+	int n; /* iterates over tree elements */
+
+	/* Initialize the trees. */
+	for (n = 0; n < L_CODES; n++)
+		G2.dyn_ltree[n].Freq = 0;
+	for (n = 0; n < D_CODES; n++)
+		G2.dyn_dtree[n].Freq = 0;
+	for (n = 0; n < BL_CODES; n++)
+		G2.bl_tree[n].Freq = 0;
+
+	G2.dyn_ltree[END_BLOCK].Freq = 1;
+	G2.opt_len = G2.static_len = 0;
+	G2.last_lit = G2.last_dist = G2.last_flags = 0;
+	G2.flags = 0;
+	G2.flag_bit = 1;
+}
+
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+
+/* Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length. */
+#define SMALLER(tree, n, m) \
+	(tree[n].Freq < tree[m].Freq \
+	|| (tree[n].Freq == tree[m].Freq && G2.depth[n] <= G2.depth[m]))
+
+static void pqdownheap(ct_data * tree, int k)
+{
+	int v = G2.heap[k];
+	int j = k << 1;		/* left son of k */
+
+	while (j <= G2.heap_len) {
+		/* Set j to the smallest of the two sons: */
+		if (j < G2.heap_len && SMALLER(tree, G2.heap[j + 1], G2.heap[j]))
+			j++;
+
+		/* Exit if v is smaller than both sons */
+		if (SMALLER(tree, v, G2.heap[j]))
+			break;
+
+		/* Exchange v with the smallest son */
+		G2.heap[k] = G2.heap[j];
+		k = j;
+
+		/* And continue down the tree, setting j to the left son of k */
+		j <<= 1;
+	}
+	G2.heap[k] = v;
+}
+
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ *    above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ *     array bl_count contains the frequencies for each bit length.
+ *     The length opt_len is updated; static_len is also updated if stree is
+ *     not null.
+ */
+static void gen_bitlen(tree_desc * desc)
+{
+	ct_data *tree = desc->dyn_tree;
+	const uint8_t *extra = desc->extra_bits;
+	int base = desc->extra_base;
+	int max_code = desc->max_code;
+	int max_length = desc->max_length;
+	ct_data *stree = desc->static_tree;
+	int h;				/* heap index */
+	int n, m;			/* iterate over the tree elements */
+	int bits;			/* bit length */
+	int xbits;			/* extra bits */
+	ush f;				/* frequency */
+	int overflow = 0;	/* number of elements with bit length too large */
+
+	for (bits = 0; bits <= MAX_BITS; bits++)
+		G2.bl_count[bits] = 0;
+
+	/* In a first pass, compute the optimal bit lengths (which may
+	 * overflow in the case of the bit length tree).
+	 */
+	tree[G2.heap[G2.heap_max]].Len = 0;	/* root of the heap */
+
+	for (h = G2.heap_max + 1; h < HEAP_SIZE; h++) {
+		n = G2.heap[h];
+		bits = tree[tree[n].Dad].Len + 1;
+		if (bits > max_length) {
+			bits = max_length;
+			overflow++;
+		}
+		tree[n].Len = (ush) bits;
+		/* We overwrite tree[n].Dad which is no longer needed */
+
+		if (n > max_code)
+			continue;	/* not a leaf node */
+
+		G2.bl_count[bits]++;
+		xbits = 0;
+		if (n >= base)
+			xbits = extra[n - base];
+		f = tree[n].Freq;
+		G2.opt_len += (ulg) f *(bits + xbits);
+
+		if (stree)
+			G2.static_len += (ulg) f * (stree[n].Len + xbits);
+	}
+	if (overflow == 0)
+		return;
+
+	Trace((stderr, "\nbit length overflow\n"));
+	/* This happens for example on obj2 and pic of the Calgary corpus */
+
+	/* Find the first bit length which could increase: */
+	do {
+		bits = max_length - 1;
+		while (G2.bl_count[bits] == 0)
+			bits--;
+		G2.bl_count[bits]--;	/* move one leaf down the tree */
+		G2.bl_count[bits + 1] += 2;	/* move one overflow item as its brother */
+		G2.bl_count[max_length]--;
+		/* The brother of the overflow item also moves one step up,
+		 * but this does not affect bl_count[max_length]
+		 */
+		overflow -= 2;
+	} while (overflow > 0);
+
+	/* Now recompute all bit lengths, scanning in increasing frequency.
+	 * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+	 * lengths instead of fixing only the wrong ones. This idea is taken
+	 * from 'ar' written by Haruhiko Okumura.)
+	 */
+	for (bits = max_length; bits != 0; bits--) {
+		n = G2.bl_count[bits];
+		while (n != 0) {
+			m = G2.heap[--h];
+			if (m > max_code)
+				continue;
+			if (tree[m].Len != (unsigned) bits) {
+				Trace((stderr, "code %d bits %d->%d\n", m, tree[m].Len, bits));
+				G2.opt_len += ((int32_t) bits - tree[m].Len) * tree[m].Freq;
+				tree[m].Len = bits;
+			}
+			n--;
+		}
+	}
+}
+
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ *     zero code length.
+ */
+static void gen_codes(ct_data * tree, int max_code)
+{
+	ush next_code[MAX_BITS + 1];	/* next code value for each bit length */
+	ush code = 0;		/* running code value */
+	int bits;			/* bit index */
+	int n;				/* code index */
+
+	/* The distribution counts are first used to generate the code values
+	 * without bit reversal.
+	 */
+	for (bits = 1; bits <= MAX_BITS; bits++) {
+		next_code[bits] = code = (code + G2.bl_count[bits - 1]) << 1;
+	}
+	/* Check that the bit counts in bl_count are consistent. The last code
+	 * must be all ones.
+	 */
+	Assert(code + G2.bl_count[MAX_BITS] - 1 == (1 << MAX_BITS) - 1,
+		   "inconsistent bit counts");
+	Tracev((stderr, "\ngen_codes: max_code %d ", max_code));
+
+	for (n = 0; n <= max_code; n++) {
+		int len = tree[n].Len;
+
+		if (len == 0)
+			continue;
+		/* Now reverse the bits */
+		tree[n].Code = bi_reverse(next_code[len]++, len);
+
+		Tracec(tree != G2.static_ltree,
+			   (stderr, "\nn %3d %c l %2d c %4x (%x) ", n,
+				(n > ' ' ? n : ' '), len, tree[n].Code,
+				next_code[len] - 1));
+	}
+}
+
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ *     and corresponding code. The length opt_len is updated; static_len is
+ *     also updated if stree is not null. The field max_code is set.
+ */
+
+/* Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len. */
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+#define PQREMOVE(tree, top) \
+do { \
+	top = G2.heap[SMALLEST]; \
+	G2.heap[SMALLEST] = G2.heap[G2.heap_len--]; \
+	pqdownheap(tree, SMALLEST); \
+} while (0)
+
+static void build_tree(tree_desc * desc)
+{
+	ct_data *tree = desc->dyn_tree;
+	ct_data *stree = desc->static_tree;
+	int elems = desc->elems;
+	int n, m;			/* iterate over heap elements */
+	int max_code = -1;	/* largest code with non zero frequency */
+	int node = elems;	/* next internal node of the tree */
+
+	/* Construct the initial heap, with least frequent element in
+	 * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+	 * heap[0] is not used.
+	 */
+	G2.heap_len = 0;
+	G2.heap_max = HEAP_SIZE;
+
+	for (n = 0; n < elems; n++) {
+		if (tree[n].Freq != 0) {
+			G2.heap[++G2.heap_len] = max_code = n;
+			G2.depth[n] = 0;
+		} else {
+			tree[n].Len = 0;
+		}
+	}
+
+	/* The pkzip format requires that at least one distance code exists,
+	 * and that at least one bit should be sent even if there is only one
+	 * possible code. So to avoid special checks later on we force at least
+	 * two codes of non zero frequency.
+	 */
+	while (G2.heap_len < 2) {
+		int new = G2.heap[++G2.heap_len] = (max_code < 2 ? ++max_code : 0);
+
+		tree[new].Freq = 1;
+		G2.depth[new] = 0;
+		G2.opt_len--;
+		if (stree)
+			G2.static_len -= stree[new].Len;
+		/* new is 0 or 1 so it does not have extra bits */
+	}
+	desc->max_code = max_code;
+
+	/* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+	 * establish sub-heaps of increasing lengths:
+	 */
+	for (n = G2.heap_len / 2; n >= 1; n--)
+		pqdownheap(tree, n);
+
+	/* Construct the Huffman tree by repeatedly combining the least two
+	 * frequent nodes.
+	 */
+	do {
+		PQREMOVE(tree, n);	/* n = node of least frequency */
+		m = G2.heap[SMALLEST];	/* m = node of next least frequency */
+
+		G2.heap[--G2.heap_max] = n;	/* keep the nodes sorted by frequency */
+		G2.heap[--G2.heap_max] = m;
+
+		/* Create a new node father of n and m */
+		tree[node].Freq = tree[n].Freq + tree[m].Freq;
+		G2.depth[node] = MAX(G2.depth[n], G2.depth[m]) + 1;
+		tree[n].Dad = tree[m].Dad = (ush) node;
+#ifdef DUMP_BL_TREE
+		if (tree == G2.bl_tree) {
+			bb_error_msg("\nnode %d(%d), sons %d(%d) %d(%d)",
+					node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+		}
+#endif
+		/* and insert the new node in the heap */
+		G2.heap[SMALLEST] = node++;
+		pqdownheap(tree, SMALLEST);
+
+	} while (G2.heap_len >= 2);
+
+	G2.heap[--G2.heap_max] = G2.heap[SMALLEST];
+
+	/* At this point, the fields freq and dad are set. We can now
+	 * generate the bit lengths.
+	 */
+	gen_bitlen((tree_desc *) desc);
+
+	/* The field len is now set, we can generate the bit codes */
+	gen_codes((ct_data *) tree, max_code);
+}
+
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree. Updates opt_len to take into account the repeat
+ * counts. (The contribution of the bit length codes will be added later
+ * during the construction of bl_tree.)
+ */
+static void scan_tree(ct_data * tree, int max_code)
+{
+	int n;				/* iterates over all tree elements */
+	int prevlen = -1;	/* last emitted length */
+	int curlen;			/* length of current code */
+	int nextlen = tree[0].Len;	/* length of next code */
+	int count = 0;		/* repeat count of the current code */
+	int max_count = 7;	/* max repeat count */
+	int min_count = 4;	/* min repeat count */
+
+	if (nextlen == 0) {
+		max_count = 138;
+		min_count = 3;
+	}
+	tree[max_code + 1].Len = 0xffff; /* guard */
+
+	for (n = 0; n <= max_code; n++) {
+		curlen = nextlen;
+		nextlen = tree[n + 1].Len;
+		if (++count < max_count && curlen == nextlen)
+			continue;
+
+		if (count < min_count) {
+			G2.bl_tree[curlen].Freq += count;
+		} else if (curlen != 0) {
+			if (curlen != prevlen)
+				G2.bl_tree[curlen].Freq++;
+			G2.bl_tree[REP_3_6].Freq++;
+		} else if (count <= 10) {
+			G2.bl_tree[REPZ_3_10].Freq++;
+		} else {
+			G2.bl_tree[REPZ_11_138].Freq++;
+		}
+		count = 0;
+		prevlen = curlen;
+
+		max_count = 7;
+		min_count = 4;
+		if (nextlen == 0) {
+			max_count = 138;
+			min_count = 3;
+		} else if (curlen == nextlen) {
+			max_count = 6;
+			min_count = 3;
+		}
+	}
+}
+
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+static void send_tree(ct_data * tree, int max_code)
+{
+	int n;				/* iterates over all tree elements */
+	int prevlen = -1;	/* last emitted length */
+	int curlen;			/* length of current code */
+	int nextlen = tree[0].Len;	/* length of next code */
+	int count = 0;		/* repeat count of the current code */
+	int max_count = 7;	/* max repeat count */
+	int min_count = 4;	/* min repeat count */
+
+/* tree[max_code+1].Len = -1; *//* guard already set */
+	if (nextlen == 0)
+		max_count = 138, min_count = 3;
+
+	for (n = 0; n <= max_code; n++) {
+		curlen = nextlen;
+		nextlen = tree[n + 1].Len;
+		if (++count < max_count && curlen == nextlen) {
+			continue;
+		} else if (count < min_count) {
+			do {
+				SEND_CODE(curlen, G2.bl_tree);
+			} while (--count);
+		} else if (curlen != 0) {
+			if (curlen != prevlen) {
+				SEND_CODE(curlen, G2.bl_tree);
+				count--;
+			}
+			Assert(count >= 3 && count <= 6, " 3_6?");
+			SEND_CODE(REP_3_6, G2.bl_tree);
+			send_bits(count - 3, 2);
+		} else if (count <= 10) {
+			SEND_CODE(REPZ_3_10, G2.bl_tree);
+			send_bits(count - 3, 3);
+		} else {
+			SEND_CODE(REPZ_11_138, G2.bl_tree);
+			send_bits(count - 11, 7);
+		}
+		count = 0;
+		prevlen = curlen;
+		if (nextlen == 0) {
+			max_count = 138;
+			min_count = 3;
+		} else if (curlen == nextlen) {
+			max_count = 6;
+			min_count = 3;
+		} else {
+			max_count = 7;
+			min_count = 4;
+		}
+	}
+}
+
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+static int build_bl_tree(void)
+{
+	int max_blindex;	/* index of last bit length code of non zero freq */
+
+	/* Determine the bit length frequencies for literal and distance trees */
+	scan_tree(G2.dyn_ltree, G2.l_desc.max_code);
+	scan_tree(G2.dyn_dtree, G2.d_desc.max_code);
+
+	/* Build the bit length tree: */
+	build_tree(&G2.bl_desc);
+	/* opt_len now includes the length of the tree representations, except
+	 * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+	 */
+
+	/* Determine the number of bit length codes to send. The pkzip format
+	 * requires that at least 4 bit length codes be sent. (appnote.txt says
+	 * 3 but the actual value used is 4.)
+	 */
+	for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
+		if (G2.bl_tree[bl_order[max_blindex]].Len != 0)
+			break;
+	}
+	/* Update opt_len to include the bit length tree and counts */
+	G2.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
+	Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", G2.opt_len, G2.static_len));
+
+	return max_blindex;
+}
+
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+static void send_all_trees(int lcodes, int dcodes, int blcodes)
+{
+	int rank;			/* index in bl_order */
+
+	Assert(lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+	Assert(lcodes <= L_CODES && dcodes <= D_CODES
+		   && blcodes <= BL_CODES, "too many codes");
+	Tracev((stderr, "\nbl counts: "));
+	send_bits(lcodes - 257, 5);	/* not +255 as stated in appnote.txt */
+	send_bits(dcodes - 1, 5);
+	send_bits(blcodes - 4, 4);	/* not -3 as stated in appnote.txt */
+	for (rank = 0; rank < blcodes; rank++) {
+		Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+		send_bits(G2.bl_tree[bl_order[rank]].Len, 3);
+	}
+	Tracev((stderr, "\nbl tree: sent %ld", G1.bits_sent));
+
+	send_tree((ct_data *) G2.dyn_ltree, lcodes - 1);	/* send the literal tree */
+	Tracev((stderr, "\nlit tree: sent %ld", G1.bits_sent));
+
+	send_tree((ct_data *) G2.dyn_dtree, dcodes - 1);	/* send the distance tree */
+	Tracev((stderr, "\ndist tree: sent %ld", G1.bits_sent));
+}
+
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+static int ct_tally(int dist, int lc)
+{
+	G1.l_buf[G2.last_lit++] = lc;
+	if (dist == 0) {
+		/* lc is the unmatched char */
+		G2.dyn_ltree[lc].Freq++;
+	} else {
+		/* Here, lc is the match length - MIN_MATCH */
+		dist--;			/* dist = match distance - 1 */
+		Assert((ush) dist < (ush) MAX_DIST
+		 && (ush) lc <= (ush) (MAX_MATCH - MIN_MATCH)
+		 && (ush) D_CODE(dist) < (ush) D_CODES, "ct_tally: bad match"
+		);
+
+		G2.dyn_ltree[G2.length_code[lc] + LITERALS + 1].Freq++;
+		G2.dyn_dtree[D_CODE(dist)].Freq++;
+
+		G1.d_buf[G2.last_dist++] = dist;
+		G2.flags |= G2.flag_bit;
+	}
+	G2.flag_bit <<= 1;
+
+	/* Output the flags if they fill a byte: */
+	if ((G2.last_lit & 7) == 0) {
+		G2.flag_buf[G2.last_flags++] = G2.flags;
+		G2.flags = 0;
+		G2.flag_bit = 1;
+	}
+	/* Try to guess if it is profitable to stop the current block here */
+	if ((G2.last_lit & 0xfff) == 0) {
+		/* Compute an upper bound for the compressed length */
+		ulg out_length = G2.last_lit * 8L;
+		ulg in_length = (ulg) G1.strstart - G1.block_start;
+		int dcode;
+
+		for (dcode = 0; dcode < D_CODES; dcode++) {
+			out_length += G2.dyn_dtree[dcode].Freq * (5L + extra_dbits[dcode]);
+		}
+		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));
+		if (G2.last_dist < G2.last_lit / 2 && out_length < in_length / 2)
+			return 1;
+	}
+	return (G2.last_lit == LIT_BUFSIZE - 1 || G2.last_dist == DIST_BUFSIZE);
+	/* We avoid equality with LIT_BUFSIZE because of wraparound at 64K
+	 * on 16 bit machines and because stored blocks are restricted to
+	 * 64K-1 bytes.
+	 */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+static void compress_block(ct_data * ltree, ct_data * dtree)
+{
+	unsigned dist;          /* distance of matched string */
+	int lc;                 /* match length or unmatched char (if dist == 0) */
+	unsigned lx = 0;        /* running index in l_buf */
+	unsigned dx = 0;        /* running index in d_buf */
+	unsigned fx = 0;        /* running index in flag_buf */
+	uch flag = 0;           /* current flags */
+	unsigned code;          /* the code to send */
+	int extra;              /* number of extra bits to send */
+
+	if (G2.last_lit != 0) do {
+		if ((lx & 7) == 0)
+			flag = G2.flag_buf[fx++];
+		lc = G1.l_buf[lx++];
+		if ((flag & 1) == 0) {
+			SEND_CODE(lc, ltree);	/* send a literal byte */
+			Tracecv(lc > ' ', (stderr, " '%c' ", lc));
+		} else {
+			/* Here, lc is the match length - MIN_MATCH */
+			code = G2.length_code[lc];
+			SEND_CODE(code + LITERALS + 1, ltree);	/* send the length code */
+			extra = extra_lbits[code];
+			if (extra != 0) {
+				lc -= G2.base_length[code];
+				send_bits(lc, extra);	/* send the extra length bits */
+			}
+			dist = G1.d_buf[dx++];
+			/* Here, dist is the match distance - 1 */
+			code = D_CODE(dist);
+			Assert(code < D_CODES, "bad d_code");
+
+			SEND_CODE(code, dtree);	/* send the distance code */
+			extra = extra_dbits[code];
+			if (extra != 0) {
+				dist -= G2.base_dist[code];
+				send_bits(dist, extra);	/* send the extra distance bits */
+			}
+		}			/* literal or match pair ? */
+		flag >>= 1;
+	} while (lx < G2.last_lit);
+
+	SEND_CODE(END_BLOCK, ltree);
+}
+
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file. This function
+ * returns the total compressed length for the file so far.
+ */
+static ulg flush_block(char *buf, ulg stored_len, int eof)
+{
+	ulg opt_lenb, static_lenb;      /* opt_len and static_len in bytes */
+	int max_blindex;                /* index of last bit length code of non zero freq */
+
+	G2.flag_buf[G2.last_flags] = G2.flags;   /* Save the flags for the last 8 items */
+
+	/* Construct the literal and distance trees */
+	build_tree(&G2.l_desc);
+	Tracev((stderr, "\nlit data: dyn %ld, stat %ld", G2.opt_len, G2.static_len));
+
+	build_tree(&G2.d_desc);
+	Tracev((stderr, "\ndist data: dyn %ld, stat %ld", G2.opt_len, G2.static_len));
+	/* At this point, opt_len and static_len are the total bit lengths of
+	 * the compressed block data, excluding the tree representations.
+	 */
+
+	/* Build the bit length tree for the above two trees, and get the index
+	 * in bl_order of the last bit length code to send.
+	 */
+	max_blindex = build_bl_tree();
+
+	/* Determine the best encoding. Compute first the block length in bytes */
+	opt_lenb = (G2.opt_len + 3 + 7) >> 3;
+	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));
+
+	if (static_lenb <= opt_lenb)
+		opt_lenb = static_lenb;
+
+	/* If compression failed and this is the first and last block,
+	 * and if the zip file can be seeked (to rewrite the local header),
+	 * the whole file is transformed into a stored file:
+	 */
+	if (stored_len <= opt_lenb && eof && G2.compressed_len == 0L && seekable()) {
+		/* Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: */
+		if (buf == NULL)
+			bb_error_msg("block vanished");
+
+		copy_block(buf, (unsigned) stored_len, 0);	/* without header */
+		G2.compressed_len = stored_len << 3;
+
+	} else if (stored_len + 4 <= opt_lenb && buf != NULL) {
+		/* 4: two words for the lengths */
+		/* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+		 * Otherwise we can't have processed more than WSIZE input bytes since
+		 * the last block flush, because compression would have been
+		 * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+		 * transform a block into a stored block.
+		 */
+		send_bits((STORED_BLOCK << 1) + eof, 3);	/* send block type */
+		G2.compressed_len = (G2.compressed_len + 3 + 7) & ~7L;
+		G2.compressed_len += (stored_len + 4) << 3;
+
+		copy_block(buf, (unsigned) stored_len, 1);	/* with header */
+
+	} else if (static_lenb == opt_lenb) {
+		send_bits((STATIC_TREES << 1) + eof, 3);
+		compress_block((ct_data *) G2.static_ltree, (ct_data *) G2.static_dtree);
+		G2.compressed_len += 3 + G2.static_len;
+	} 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);
+		compress_block((ct_data *) G2.dyn_ltree, (ct_data *) G2.dyn_dtree);
+		G2.compressed_len += 3 + G2.opt_len;
+	}
+	Assert(G2.compressed_len == G1.bits_sent, "bad compressed size");
+	init_block();
+
+	if (eof) {
+		bi_windup();
+		G2.compressed_len += 7;	/* align on byte boundary */
+	}
+	Tracev((stderr, "\ncomprlen %lu(%lu) ", G2.compressed_len >> 3,
+			G2.compressed_len - 7 * eof));
+
+	return G2.compressed_len >> 3;
+}
+
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * 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.
+ */
+#define UPDATE_HASH(h, c) (h = (((h)<<H_SHIFT) ^ (c)) & HASH_MASK)
+
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ *
+ * Processes a new input file and return its compressed length. Sets
+ * the compressed length, crc, deflate flags and internal file
+ * attributes.
+ */
+
+/* Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match. */
+#define FLUSH_BLOCK(eof) \
+	flush_block( \
+		G1.block_start >= 0L \
+			? (char*)&G1.window[(unsigned)G1.block_start] \
+			: (char*)NULL, \
+		(ulg)G1.strstart - G1.block_start, \
+		(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 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) \
+do { \
+	UPDATE_HASH(G1.ins_h, G1.window[(s) + MIN_MATCH-1]); \
+	G1.prev[(s) & WMASK] = match_head = head[G1.ins_h]; \
+	head[G1.ins_h] = (s); \
+} while (0)
+
+static ulg deflate(void)
+{
+	IPos hash_head;		/* head of hash chain */
+	IPos prev_match;	/* previous match */
+	int flush;			/* set if current block must be flushed */
+	int match_available = 0;	/* set if previous match exists */
+	unsigned match_length = MIN_MATCH - 1;	/* length of best match */
+
+	/* Process the input block. */
+	while (G1.lookahead != 0) {
+		/* Insert the string window[strstart .. strstart+2] in the
+		 * dictionary, and set hash_head to the head of the hash chain:
+		 */
+		INSERT_STRING(G1.strstart, hash_head);
+
+		/* Find the longest match, discarding those <= prev_length.
+		 */
+		G1.prev_length = match_length;
+		prev_match = G1.match_start;
+		match_length = MIN_MATCH - 1;
+
+		if (hash_head != 0 && G1.prev_length < max_lazy_match
+		 && G1.strstart - hash_head <= MAX_DIST
+		) {
+			/* To simplify the code, we prevent matches with the string
+			 * of window index 0 (in particular we have to avoid a match
+			 * of the string with itself at the start of the input file).
+			 */
+			match_length = longest_match(hash_head);
+			/* longest_match() sets match_start */
+			if (match_length > G1.lookahead)
+				match_length = G1.lookahead;
+
+			/* Ignore a length 3 match if it is too distant: */
+			if (match_length == MIN_MATCH && G1.strstart - G1.match_start > TOO_FAR) {
+				/* If prev_match is also MIN_MATCH, G1.match_start is garbage
+				 * but we will ignore the current match anyway.
+				 */
+				match_length--;
+			}
+		}
+		/* If there was a match at the previous step and the current
+		 * match is not better, output the previous match:
+		 */
+		if (G1.prev_length >= MIN_MATCH && match_length <= G1.prev_length) {
+			check_match(G1.strstart - 1, prev_match, G1.prev_length);
+			flush = ct_tally(G1.strstart - 1 - prev_match, G1.prev_length - MIN_MATCH);
+
+			/* Insert in hash table all strings up to the end of the match.
+			 * strstart-1 and strstart are already inserted.
+			 */
+			G1.lookahead -= G1.prev_length - 1;
+			G1.prev_length -= 2;
+			do {
+				G1.strstart++;
+				INSERT_STRING(G1.strstart, hash_head);
+				/* strstart never exceeds WSIZE-MAX_MATCH, so there are
+				 * always MIN_MATCH bytes ahead. If lookahead < MIN_MATCH
+				 * these bytes are garbage, but it does not matter since the
+				 * next lookahead bytes will always be emitted as literals.
+				 */
+			} while (--G1.prev_length != 0);
+			match_available = 0;
+			match_length = MIN_MATCH - 1;
+			G1.strstart++;
+			if (flush) {
+				FLUSH_BLOCK(0);
+				G1.block_start = G1.strstart;
+			}
+		} else if (match_available) {
+			/* If there was no match at the previous position, output a
+			 * single literal. If there was a match but the current match
+			 * is longer, truncate the previous match to a single literal.
+			 */
+			Tracevv((stderr, "%c", G1.window[G1.strstart - 1]));
+			if (ct_tally(0, G1.window[G1.strstart - 1])) {
+				FLUSH_BLOCK(0);
+				G1.block_start = G1.strstart;
+			}
+			G1.strstart++;
+			G1.lookahead--;
+		} else {
+			/* There is no previous match to compare with, wait for
+			 * the next step to decide.
+			 */
+			match_available = 1;
+			G1.strstart++;
+			G1.lookahead--;
+		}
+		Assert(G1.strstart <= G1.isize && lookahead <= G1.isize, "a bit too far");
+
+		/* Make sure that we always have enough lookahead, except
+		 * at the end of the input file. We need MAX_MATCH bytes
+		 * for the next match, plus MIN_MATCH bytes to insert the
+		 * string following the next match.
+		 */
+		while (G1.lookahead < MIN_LOOKAHEAD && !G1.eofile)
+			fill_window();
+	}
+	if (match_available)
+		ct_tally(0, G1.window[G1.strstart - 1]);
+
+	return FLUSH_BLOCK(1);	/* eof */
+}
+
+
+/* ===========================================================================
+ * Initialize the bit string routines.
+ */
+static void bi_init(void)
+{
+	G1.bi_buf = 0;
+	G1.bi_valid = 0;
+#ifdef DEBUG
+	G1.bits_sent = 0L;
+#endif
+}
+
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new file
+ */
+static void lm_init(ush * flagsp)
+{
+	unsigned j;
+
+	/* Initialize the hash table. */
+	memset(head, 0, HASH_SIZE * sizeof(*head));
+	/* prev will be initialized on the fly */
+
+	/* speed options for the general purpose bit flag */
+	*flagsp |= 2;	/* FAST 4, SLOW 2 */
+	/* ??? reduce max_chain_length for binary files */
+
+	G1.strstart = 0;
+	G1.block_start = 0L;
+
+	G1.lookahead = file_read(G1.window,
+			sizeof(int) <= 2 ? (unsigned) WSIZE : 2 * WSIZE);
+
+	if (G1.lookahead == 0 || G1.lookahead == (unsigned) -1) {
+		G1.eofile = 1;
+		G1.lookahead = 0;
+		return;
+	}
+	G1.eofile = 0;
+	/* Make sure that we always have enough lookahead. This is important
+	 * if input comes from a device such as a tty.
+	 */
+	while (G1.lookahead < MIN_LOOKAHEAD && !G1.eofile)
+		fill_window();
+
+	G1.ins_h = 0;
+	for (j = 0; j < MIN_MATCH - 1; j++)
+		UPDATE_HASH(G1.ins_h, G1.window[j]);
+	/* If lookahead < MIN_MATCH, ins_h is garbage, but this is
+	 * not important since only literal bytes will be emitted.
+	 */
+}
+
+
+/* ===========================================================================
+ * Allocate the match buffer, initialize the various tables and save the
+ * location of the internal file attribute (ascii/binary) and method
+ * (DEFLATE/STORE).
+ * One callsite in zip()
+ */
+static void ct_init(void)
+{
+	int n;				/* iterates over tree elements */
+	int length;			/* length value */
+	int code;			/* code value */
+	int dist;			/* distance index */
+
+	G2.compressed_len = 0L;
+
+#ifdef NOT_NEEDED
+	if (G2.static_dtree[0].Len != 0)
+		return;			/* ct_init already called */
+#endif
+
+	/* Initialize the mapping length (0..255) -> length code (0..28) */
+	length = 0;
+	for (code = 0; code < LENGTH_CODES - 1; code++) {
+		G2.base_length[code] = length;
+		for (n = 0; n < (1 << extra_lbits[code]); n++) {
+			G2.length_code[length++] = code;
+		}
+	}
+	Assert(length == 256, "ct_init: length != 256");
+	/* Note that the length 255 (match length 258) can be represented
+	 * in two different ways: code 284 + 5 bits or code 285, so we
+	 * overwrite length_code[255] to use the best encoding:
+	 */
+	G2.length_code[length - 1] = code;
+
+	/* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+	dist = 0;
+	for (code = 0; code < 16; code++) {
+		G2.base_dist[code] = dist;
+		for (n = 0; n < (1 << extra_dbits[code]); n++) {
+			G2.dist_code[dist++] = code;
+		}
+	}
+	Assert(dist == 256, "ct_init: dist != 256");
+	dist >>= 7;			/* from now on, all distances are divided by 128 */
+	for (; code < D_CODES; code++) {
+		G2.base_dist[code] = dist << 7;
+		for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
+			G2.dist_code[256 + dist++] = code;
+		}
+	}
+	Assert(dist == 256, "ct_init: 256+dist != 512");
+
+	/* Construct the codes of the static literal tree */
+	/* already zeroed - it's in bss
+	for (n = 0; n <= MAX_BITS; n++)
+		G2.bl_count[n] = 0; */
+
+	n = 0;
+	while (n <= 143) {
+		G2.static_ltree[n++].Len = 8;
+		G2.bl_count[8]++;
+	}
+	while (n <= 255) {
+		G2.static_ltree[n++].Len = 9;
+		G2.bl_count[9]++;
+	}
+	while (n <= 279) {
+		G2.static_ltree[n++].Len = 7;
+		G2.bl_count[7]++;
+	}
+	while (n <= 287) {
+		G2.static_ltree[n++].Len = 8;
+		G2.bl_count[8]++;
+	}
+	/* Codes 286 and 287 do not exist, but we must include them in the
+	 * tree construction to get a canonical Huffman tree (longest code
+	 * all ones)
+	 */
+	gen_codes((ct_data *) G2.static_ltree, L_CODES + 1);
+
+	/* The static distance tree is trivial: */
+	for (n = 0; n < D_CODES; n++) {
+		G2.static_dtree[n].Len = 5;
+		G2.static_dtree[n].Code = bi_reverse(n, 5);
+	}
+
+	/* Initialize the first block of the first file: */
+	init_block();
+}
+
+
+/* ===========================================================================
+ * Deflate in to out.
+ * IN assertions: the input and output buffers are cleared.
+ */
+
+static void zip(ulg time_stamp)
+{
+	ush deflate_flags = 0;  /* pkzip -es, -en or -ex equivalent */
+
+	G1.outcnt = 0;
+
+	/* Write the header to the gzip file. See algorithm.doc for the format */
+	/* magic header for gzip files: 1F 8B */
+	/* compression method: 8 (DEFLATED) */
+	/* general flags: 0 */
+	put_32bit(0x00088b1f);
+	put_32bit(time_stamp);
+
+	/* Write deflated file to zip file */
+	G1.crc = ~0;
+
+	bi_init();
+	ct_init();
+	lm_init(&deflate_flags);
+
+	put_8bit(deflate_flags);	/* extra flags */
+	put_8bit(3);	/* OS identifier = 3 (Unix) */
+
+	deflate();
+
+	/* Write the crc and uncompressed size */
+	put_32bit(~G1.crc);
+	put_32bit(G1.isize);
+
+	flush_outbuf();
+}
+
+
+/* ======================================================================== */
+static
+IF_DESKTOP(long long) int FAST_FUNC pack_gzip(unpack_info_t *info UNUSED_PARAM)
+{
+	struct stat s;
+
+	/* Clear input and output buffers */
+	G1.outcnt = 0;
+#ifdef DEBUG
+	G1.insize = 0;
+#endif
+	G1.isize = 0;
+
+	/* Reinit G2.xxx */
+	memset(&G2, 0, sizeof(G2));
+	G2.l_desc.dyn_tree     = G2.dyn_ltree;
+	G2.l_desc.static_tree  = G2.static_ltree;
+	G2.l_desc.extra_bits   = extra_lbits;
+	G2.l_desc.extra_base   = LITERALS + 1;
+	G2.l_desc.elems        = L_CODES;
+	G2.l_desc.max_length   = MAX_BITS;
+	//G2.l_desc.max_code     = 0;
+	G2.d_desc.dyn_tree     = G2.dyn_dtree;
+	G2.d_desc.static_tree  = G2.static_dtree;
+	G2.d_desc.extra_bits   = extra_dbits;
+	//G2.d_desc.extra_base   = 0;
+	G2.d_desc.elems        = D_CODES;
+	G2.d_desc.max_length   = MAX_BITS;
+	//G2.d_desc.max_code     = 0;
+	G2.bl_desc.dyn_tree    = G2.bl_tree;
+	//G2.bl_desc.static_tree = NULL;
+	G2.bl_desc.extra_bits  = extra_blbits,
+	//G2.bl_desc.extra_base  = 0;
+	G2.bl_desc.elems       = BL_CODES;
+	G2.bl_desc.max_length  = MAX_BL_BITS;
+	//G2.bl_desc.max_code    = 0;
+
+	s.st_ctime = 0;
+	fstat(STDIN_FILENO, &s);
+	zip(s.st_ctime);
+	return 0;
+}
+
+#if ENABLE_FEATURE_GZIP_LONG_OPTIONS
+static const char gzip_longopts[] ALIGN1 =
+	"stdout\0"              No_argument       "c"
+	"to-stdout\0"           No_argument       "c"
+	"force\0"               No_argument       "f"
+	"verbose\0"             No_argument       "v"
+#if ENABLE_GUNZIP
+	"decompress\0"          No_argument       "d"
+	"uncompress\0"          No_argument       "d"
+	"test\0"                No_argument       "t"
+#endif
+	"quiet\0"               No_argument       "q"
+	"fast\0"                No_argument       "1"
+	"best\0"                No_argument       "9"
+	;
+#endif
+
+/*
+ * Linux kernel build uses gzip -d -n. We accept and ignore -n.
+ * Man page says:
+ * -n --no-name
+ * gzip: do not save the original file name and time stamp.
+ * (The original name is always saved if the name had to be truncated.)
+ * gunzip: do not restore the original file name/time even if present
+ * (remove only the gzip suffix from the compressed file name).
+ * This option is the default when decompressing.
+ * -N --name
+ * gzip: always save the original file name and time stamp (this is the default)
+ * gunzip: restore the original file name and time stamp if present.
+ */
+
+int gzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+#if ENABLE_GUNZIP
+int gzip_main(int argc, char **argv)
+#else
+int gzip_main(int argc UNUSED_PARAM, char **argv)
+#endif
+{
+	unsigned opt;
+
+#if ENABLE_FEATURE_GZIP_LONG_OPTIONS
+	applet_long_options = gzip_longopts;
+#endif
+	/* Must match bbunzip's constants OPT_STDOUT, OPT_FORCE! */
+	opt = getopt32(argv, "cfv" IF_GUNZIP("dt") "q123456789n");
+#if ENABLE_GUNZIP /* gunzip_main may not be visible... */
+	if (opt & 0x18) // -d and/or -t
+		return gunzip_main(argc, argv);
+#endif
+	option_mask32 &= 0x7; /* ignore -q, -0..9 */
+	//if (opt & 0x1) // -c
+	//if (opt & 0x2) // -f
+	//if (opt & 0x4) // -v
+	argv += optind;
+
+	SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2))
+			+ sizeof(struct globals));
+
+	/* Allocate all global buffers (for DYN_ALLOC option) */
+	ALLOC(uch, G1.l_buf, INBUFSIZ);
+	ALLOC(uch, G1.outbuf, OUTBUFSIZ);
+	ALLOC(ush, G1.d_buf, DIST_BUFSIZE);
+	ALLOC(uch, G1.window, 2L * WSIZE);
+	ALLOC(ush, G1.prev, 1L << BITS);
+
+	/* Initialize the CRC32 table */
+	global_crc32_table = crc32_filltable(NULL, 0);
+
+	return bbunpack(argv, pack_gzip, append_ext, "gz");
+}
diff --git a/busybox-1.19.3/archival/libarchive/Kbuild.src b/busybox-1.19.3/archival/libarchive/Kbuild.src
new file mode 100644
index 0000000..b0bc4e5
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/Kbuild.src
@@ -0,0 +1,64 @@
+# 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:= \
+	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_LZOP)                      += lzo1x_1.o lzo1x_1o.o lzo1x_d.o
+lib-$(CONFIG_LZOP_COMPR_HIGH)           += lzo1x_9x.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_COMPRESS_BBCONFIG) += 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/busybox-1.19.3/archival/libarchive/bz/LICENSE b/busybox-1.19.3/archival/libarchive/bz/LICENSE
new file mode 100644
index 0000000..da43465
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/LICENSE
@@ -0,0 +1,44 @@
+bzip2 applet in busybox is based on lightly-modified source
+of bzip2 version 1.0.4. bzip2 source is distributed
+under the following conditions (copied verbatim from LICENSE file)
+===========================================================
+
+
+This program, "bzip2", the associated library "libbzip2", and all
+documentation, are copyright (C) 1996-2006 Julian R Seward.  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. The origin of this software must not be misrepresented; you must
+   not claim that you wrote the original software.  If you use this
+   software in a product, an acknowledgment in the product
+   documentation would be appreciated but is not required.
+
+3. Altered source versions must be plainly marked as such, and must
+   not be misrepresented as being the original software.
+
+4. 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.
+
+Julian Seward, Cambridge, UK.
+jseward@bzip.org
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
diff --git a/busybox-1.19.3/archival/libarchive/bz/README b/busybox-1.19.3/archival/libarchive/bz/README
new file mode 100644
index 0000000..fffd47b
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/README
@@ -0,0 +1,90 @@
+This file is an abridged version of README from bzip2 1.0.4
+Build instructions (which are not relevant to busyboxed bzip2)
+are removed.
+===========================================================
+
+
+This is the README for bzip2/libzip2.
+This version is fully compatible with the previous public releases.
+
+------------------------------------------------------------------
+This file is part of bzip2/libbzip2, a program and library for
+lossless, block-sorting data compression.
+
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
+Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org>
+
+Please read the WARNING, DISCLAIMER and PATENTS sections in this file.
+
+This program is released under the terms of the license contained
+in the file LICENSE.
+------------------------------------------------------------------
+
+Please read and be aware of the following:
+
+
+WARNING:
+
+   This program and library (attempts to) compress data by
+   performing several non-trivial transformations on it.
+   Unless you are 100% familiar with *all* the algorithms
+   contained herein, and with the consequences of modifying them,
+   you should NOT meddle with the compression or decompression
+   machinery.  Incorrect changes can and very likely *will*
+   lead to disastrous loss of data.
+
+
+DISCLAIMER:
+
+   I TAKE NO RESPONSIBILITY FOR ANY LOSS OF DATA ARISING FROM THE
+   USE OF THIS PROGRAM/LIBRARY, HOWSOEVER CAUSED.
+
+   Every compression of a file implies an assumption that the
+   compressed file can be decompressed to reproduce the original.
+   Great efforts in design, coding and testing have been made to
+   ensure that this program works correctly.  However, the complexity
+   of the algorithms, and, in particular, the presence of various
+   special cases in the code which occur with very low but non-zero
+   probability make it impossible to rule out the possibility of bugs
+   remaining in the program.  DO NOT COMPRESS ANY DATA WITH THIS
+   PROGRAM UNLESS YOU ARE PREPARED TO ACCEPT THE POSSIBILITY, HOWEVER
+   SMALL, THAT THE DATA WILL NOT BE RECOVERABLE.
+
+   That is not to say this program is inherently unreliable.
+   Indeed, I very much hope the opposite is true.  bzip2/libbzip2
+   has been carefully constructed and extensively tested.
+
+
+PATENTS:
+
+   To the best of my knowledge, bzip2/libbzip2 does not use any
+   patented algorithms.  However, I do not have the resources
+   to carry out a patent search.  Therefore I cannot give any
+   guarantee of the above statement.
+
+
+I hope you find bzip2 useful.  Feel free to contact me at
+   jseward@bzip.org
+if you have any suggestions or queries.  Many people mailed me with
+comments, suggestions and patches after the releases of bzip-0.15,
+bzip-0.21, and bzip2 versions 0.1pl2, 0.9.0, 0.9.5, 1.0.0, 1.0.1,
+1.0.2 and 1.0.3, and the changes in bzip2 are largely a result of this
+feedback.  I thank you for your comments.
+
+bzip2's "home" is http://www.bzip.org/
+
+Julian Seward
+jseward@bzip.org
+Cambridge, UK.
+
+18     July 1996 (version 0.15)
+25   August 1996 (version 0.21)
+ 7   August 1997 (bzip2, version 0.1)
+29   August 1997 (bzip2, version 0.1pl2)
+23   August 1998 (bzip2, version 0.9.0)
+ 8     June 1999 (bzip2, version 0.9.5)
+ 4     Sept 1999 (bzip2, version 0.9.5d)
+ 5      May 2000 (bzip2, version 1.0pre8)
+30 December 2001 (bzip2, version 1.0.2pre1)
+15 February 2005 (bzip2, version 1.0.3)
+20 December 2006 (bzip2, version 1.0.4)
diff --git a/busybox-1.19.3/archival/libarchive/bz/blocksort.c b/busybox-1.19.3/archival/libarchive/bz/blocksort.c
new file mode 100644
index 0000000..f70c370
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/blocksort.c
@@ -0,0 +1,1072 @@
+/*
+ * bzip2 is written by Julian Seward <jseward@bzip.org>.
+ * Adapted for busybox by Denys Vlasenko <vda.linux@googlemail.com>.
+ * See README and LICENSE files in this directory for more information.
+ */
+
+/*-------------------------------------------------------------*/
+/*--- Block sorting machinery                               ---*/
+/*---                                           blocksort.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+This file is part of bzip2/libbzip2, a program and library for
+lossless, block-sorting data compression.
+
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
+Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org>
+
+Please read the WARNING, DISCLAIMER and PATENTS sections in the
+README file.
+
+This program is released under the terms of the license contained
+in the file LICENSE.
+------------------------------------------------------------------ */
+
+/* #include "bzlib_private.h" */
+
+#define mswap(zz1, zz2) \
+{ \
+	int32_t zztmp = zz1; \
+	zz1 = zz2; \
+	zz2 = zztmp; \
+}
+
+static
+/* No measurable speed gain with inlining */
+/* ALWAYS_INLINE */
+void mvswap(uint32_t* ptr, int32_t zzp1, int32_t zzp2, int32_t zzn)
+{
+	while (zzn > 0) {
+		mswap(ptr[zzp1], ptr[zzp2]);
+		zzp1++;
+		zzp2++;
+		zzn--;
+	}
+}
+
+static
+ALWAYS_INLINE
+int32_t mmin(int32_t a, int32_t b)
+{
+	return (a < b) ? a : b;
+}
+
+
+/*---------------------------------------------*/
+/*--- Fallback O(N log(N)^2) sorting        ---*/
+/*--- algorithm, for repetitive blocks      ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+inline
+void fallbackSimpleSort(uint32_t* fmap,
+		uint32_t* eclass,
+		int32_t   lo,
+		int32_t   hi)
+{
+	int32_t i, j, tmp;
+	uint32_t ec_tmp;
+
+	if (lo == hi) return;
+
+	if (hi - lo > 3) {
+		for (i = hi-4; i >= lo; i--) {
+			tmp = fmap[i];
+			ec_tmp = eclass[tmp];
+			for (j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4)
+				fmap[j-4] = fmap[j];
+			fmap[j-4] = tmp;
+		}
+	}
+
+	for (i = hi-1; i >= lo; i--) {
+		tmp = fmap[i];
+		ec_tmp = eclass[tmp];
+		for (j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++)
+			fmap[j-1] = fmap[j];
+		fmap[j-1] = tmp;
+	}
+}
+
+
+/*---------------------------------------------*/
+#define fpush(lz,hz) { \
+	stackLo[sp] = lz; \
+	stackHi[sp] = hz; \
+	sp++; \
+}
+
+#define fpop(lz,hz) { \
+	sp--; \
+	lz = stackLo[sp]; \
+	hz = stackHi[sp]; \
+}
+
+#define FALLBACK_QSORT_SMALL_THRESH 10
+#define FALLBACK_QSORT_STACK_SIZE   100
+
+static
+void fallbackQSort3(uint32_t* fmap,
+		uint32_t* eclass,
+		int32_t   loSt,
+		int32_t   hiSt)
+{
+	int32_t unLo, unHi, ltLo, gtHi, n, m;
+	int32_t sp, lo, hi;
+	uint32_t med, r, r3;
+	int32_t stackLo[FALLBACK_QSORT_STACK_SIZE];
+	int32_t stackHi[FALLBACK_QSORT_STACK_SIZE];
+
+	r = 0;
+
+	sp = 0;
+	fpush(loSt, hiSt);
+
+	while (sp > 0) {
+		AssertH(sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004);
+
+		fpop(lo, hi);
+		if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) {
+			fallbackSimpleSort(fmap, eclass, lo, hi);
+			continue;
+		}
+
+		/* Random partitioning.  Median of 3 sometimes fails to
+		 * avoid bad cases.  Median of 9 seems to help but
+		 * looks rather expensive.  This too seems to work but
+		 * is cheaper.  Guidance for the magic constants
+		 * 7621 and 32768 is taken from Sedgewick's algorithms
+		 * book, chapter 35.
+		 */
+		r = ((r * 7621) + 1) % 32768;
+		r3 = r % 3;
+		if (r3 == 0)
+			med = eclass[fmap[lo]];
+		else if (r3 == 1)
+			med = eclass[fmap[(lo+hi)>>1]];
+		else
+			med = eclass[fmap[hi]];
+
+		unLo = ltLo = lo;
+		unHi = gtHi = hi;
+
+		while (1) {
+			while (1) {
+				if (unLo > unHi) break;
+				n = (int32_t)eclass[fmap[unLo]] - (int32_t)med;
+				if (n == 0) {
+					mswap(fmap[unLo], fmap[ltLo]);
+					ltLo++;
+					unLo++;
+					continue;
+				};
+				if (n > 0) break;
+				unLo++;
+			}
+			while (1) {
+				if (unLo > unHi) break;
+				n = (int32_t)eclass[fmap[unHi]] - (int32_t)med;
+				if (n == 0) {
+					mswap(fmap[unHi], fmap[gtHi]);
+					gtHi--; unHi--;
+					continue;
+				};
+				if (n < 0) break;
+				unHi--;
+			}
+			if (unLo > unHi) break;
+			mswap(fmap[unLo], fmap[unHi]); unLo++; unHi--;
+		}
+
+		AssertD(unHi == unLo-1, "fallbackQSort3(2)");
+
+		if (gtHi < ltLo) continue;
+
+		n = mmin(ltLo-lo, unLo-ltLo); mvswap(fmap, lo, unLo-n, n);
+		m = mmin(hi-gtHi, gtHi-unHi); mvswap(fmap, unLo, hi-m+1, m);
+
+		n = lo + unLo - ltLo - 1;
+		m = hi - (gtHi - unHi) + 1;
+
+		if (n - lo > hi - m) {
+			fpush(lo, n);
+			fpush(m, hi);
+		} else {
+			fpush(m, hi);
+			fpush(lo, n);
+		}
+	}
+}
+
+#undef fpush
+#undef fpop
+#undef FALLBACK_QSORT_SMALL_THRESH
+#undef FALLBACK_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+ *	nblock > 0
+ *	eclass exists for [0 .. nblock-1]
+ *	((uint8_t*)eclass) [0 .. nblock-1] holds block
+ *	ptr exists for [0 .. nblock-1]
+ *
+ * Post:
+ *	((uint8_t*)eclass) [0 .. nblock-1] holds block
+ *	All other areas of eclass destroyed
+ *	fmap [0 .. nblock-1] holds sorted order
+ *	bhtab[0 .. 2+(nblock/32)] destroyed
+*/
+
+#define       SET_BH(zz)  bhtab[(zz) >> 5] |= (1 << ((zz) & 31))
+#define     CLEAR_BH(zz)  bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31))
+#define     ISSET_BH(zz)  (bhtab[(zz) >> 5] & (1 << ((zz) & 31)))
+#define      WORD_BH(zz)  bhtab[(zz) >> 5]
+#define UNALIGNED_BH(zz)  ((zz) & 0x01f)
+
+static
+void fallbackSort(uint32_t* fmap,
+		uint32_t* eclass,
+		uint32_t* bhtab,
+		int32_t   nblock)
+{
+	int32_t ftab[257];
+	int32_t ftabCopy[256];
+	int32_t H, i, j, k, l, r, cc, cc1;
+	int32_t nNotDone;
+	int32_t nBhtab;
+	uint8_t* eclass8 = (uint8_t*)eclass;
+
+	/*
+	 * Initial 1-char radix sort to generate
+	 * initial fmap and initial BH bits.
+	 */
+	for (i = 0; i < 257;    i++) ftab[i] = 0;
+	for (i = 0; i < nblock; i++) ftab[eclass8[i]]++;
+	for (i = 0; i < 256;    i++) ftabCopy[i] = ftab[i];
+
+	j = ftab[0];  /* bbox: optimized */
+	for (i = 1; i < 257;    i++) {
+		j += ftab[i];
+		ftab[i] = j;
+	}
+
+	for (i = 0; i < nblock; i++) {
+		j = eclass8[i];
+		k = ftab[j] - 1;
+		ftab[j] = k;
+		fmap[k] = i;
+	}
+
+	nBhtab = 2 + ((uint32_t)nblock / 32); /* bbox: unsigned div is easier */
+	for (i = 0; i < nBhtab; i++) bhtab[i] = 0;
+	for (i = 0; i < 256; i++) SET_BH(ftab[i]);
+
+	/*
+	 * Inductively refine the buckets.  Kind-of an
+	 * "exponential radix sort" (!), inspired by the
+	 * Manber-Myers suffix array construction algorithm.
+	 */
+
+	/*-- set sentinel bits for block-end detection --*/
+	for (i = 0; i < 32; i++) {
+		SET_BH(nblock + 2*i);
+		CLEAR_BH(nblock + 2*i + 1);
+	}
+
+	/*-- the log(N) loop --*/
+	H = 1;
+	while (1) {
+		j = 0;
+		for (i = 0; i < nblock; i++) {
+			if (ISSET_BH(i))
+				j = i;
+			k = fmap[i] - H;
+			if (k < 0)
+				k += nblock;
+			eclass[k] = j;
+		}
+
+		nNotDone = 0;
+		r = -1;
+		while (1) {
+
+		/*-- find the next non-singleton bucket --*/
+			k = r + 1;
+			while (ISSET_BH(k) && UNALIGNED_BH(k))
+				k++;
+			if (ISSET_BH(k)) {
+				while (WORD_BH(k) == 0xffffffff) k += 32;
+				while (ISSET_BH(k)) k++;
+			}
+			l = k - 1;
+			if (l >= nblock)
+				break;
+			while (!ISSET_BH(k) && UNALIGNED_BH(k))
+				k++;
+			if (!ISSET_BH(k)) {
+				while (WORD_BH(k) == 0x00000000) k += 32;
+				while (!ISSET_BH(k)) k++;
+			}
+			r = k - 1;
+			if (r >= nblock)
+				break;
+
+			/*-- now [l, r] bracket current bucket --*/
+			if (r > l) {
+				nNotDone += (r - l + 1);
+				fallbackQSort3(fmap, eclass, l, r);
+
+				/*-- scan bucket and generate header bits-- */
+				cc = -1;
+				for (i = l; i <= r; i++) {
+					cc1 = eclass[fmap[i]];
+					if (cc != cc1) {
+						SET_BH(i);
+						cc = cc1;
+					};
+				}
+			}
+		}
+
+		H *= 2;
+		if (H > nblock || nNotDone == 0)
+			break;
+	}
+
+	/*
+	 * Reconstruct the original block in
+	 * eclass8 [0 .. nblock-1], since the
+	 * previous phase destroyed it.
+	 */
+	j = 0;
+	for (i = 0; i < nblock; i++) {
+		while (ftabCopy[j] == 0)
+			j++;
+		ftabCopy[j]--;
+		eclass8[fmap[i]] = (uint8_t)j;
+	}
+	AssertH(j < 256, 1005);
+}
+
+#undef       SET_BH
+#undef     CLEAR_BH
+#undef     ISSET_BH
+#undef      WORD_BH
+#undef UNALIGNED_BH
+
+
+/*---------------------------------------------*/
+/*--- The main, O(N^2 log(N)) sorting       ---*/
+/*--- algorithm.  Faster for "normal"       ---*/
+/*--- non-repetitive blocks.                ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+NOINLINE
+int mainGtU(
+		uint32_t  i1,
+		uint32_t  i2,
+		uint8_t*  block,
+		uint16_t* quadrant,
+		uint32_t  nblock,
+		int32_t*  budget)
+{
+	int32_t  k;
+	uint8_t  c1, c2;
+	uint16_t s1, s2;
+
+/* Loop unrolling here is actually very useful
+ * (generated code is much simpler),
+ * code size increase is only 270 bytes (i386)
+ * but speeds up compression 10% overall
+ */
+
+#if CONFIG_BZIP2_FEATURE_SPEED >= 1
+
+#define TIMES_8(code) \
+	code; code; code; code; \
+	code; code; code; code;
+#define TIMES_12(code) \
+	code; code; code; code; \
+	code; code; code; code; \
+	code; code; code; code;
+
+#else
+
+#define TIMES_8(code) \
+{ \
+	int nn = 8; \
+	do { \
+		code; \
+	} while (--nn); \
+}
+#define TIMES_12(code) \
+{ \
+	int nn = 12; \
+	do { \
+		code; \
+	} while (--nn); \
+}
+
+#endif
+
+	AssertD(i1 != i2, "mainGtU");
+	TIMES_12(
+		c1 = block[i1]; c2 = block[i2];
+		if (c1 != c2) return (c1 > c2);
+		i1++; i2++;
+	)
+
+	k = nblock + 8;
+
+	do {
+		TIMES_8(
+			c1 = block[i1]; c2 = block[i2];
+			if (c1 != c2) return (c1 > c2);
+			s1 = quadrant[i1]; s2 = quadrant[i2];
+			if (s1 != s2) return (s1 > s2);
+			i1++; i2++;
+		)
+
+		if (i1 >= nblock) i1 -= nblock;
+		if (i2 >= nblock) i2 -= nblock;
+
+		(*budget)--;
+		k -= 8;
+	} while (k >= 0);
+
+	return False;
+}
+#undef TIMES_8
+#undef TIMES_12
+
+/*---------------------------------------------*/
+/*
+ * Knuth's increments seem to work better
+ * than Incerpi-Sedgewick here.  Possibly
+ * because the number of elems to sort is
+ * usually small, typically <= 20.
+ */
+static
+const int32_t incs[14] = {
+	1, 4, 13, 40, 121, 364, 1093, 3280,
+	9841, 29524, 88573, 265720,
+	797161, 2391484
+};
+
+static
+void mainSimpleSort(uint32_t* ptr,
+		uint8_t*  block,
+		uint16_t* quadrant,
+		int32_t   nblock,
+		int32_t   lo,
+		int32_t   hi,
+		int32_t   d,
+		int32_t*  budget)
+{
+	int32_t i, j, h, bigN, hp;
+	uint32_t v;
+
+	bigN = hi - lo + 1;
+	if (bigN < 2) return;
+
+	hp = 0;
+	while (incs[hp] < bigN) hp++;
+	hp--;
+
+	for (; hp >= 0; hp--) {
+		h = incs[hp];
+
+		i = lo + h;
+		while (1) {
+			/*-- copy 1 --*/
+			if (i > hi) break;
+			v = ptr[i];
+			j = i;
+			while (mainGtU(ptr[j-h]+d, v+d, block, quadrant, nblock, budget)) {
+				ptr[j] = ptr[j-h];
+				j = j - h;
+				if (j <= (lo + h - 1)) break;
+			}
+			ptr[j] = v;
+			i++;
+
+/* 1.5% overall speedup, +290 bytes */
+#if CONFIG_BZIP2_FEATURE_SPEED >= 3
+			/*-- copy 2 --*/
+			if (i > hi) break;
+			v = ptr[i];
+			j = i;
+			while (mainGtU(ptr[j-h]+d, v+d, block, quadrant, nblock, budget)) {
+				ptr[j] = ptr[j-h];
+				j = j - h;
+				if (j <= (lo + h - 1)) break;
+			}
+			ptr[j] = v;
+			i++;
+
+			/*-- copy 3 --*/
+			if (i > hi) break;
+			v = ptr[i];
+			j = i;
+			while (mainGtU(ptr[j-h]+d, v+d, block, quadrant, nblock, budget)) {
+				ptr[j] = ptr[j-h];
+				j = j - h;
+				if (j <= (lo + h - 1)) break;
+			}
+			ptr[j] = v;
+			i++;
+#endif
+			if (*budget < 0) return;
+		}
+	}
+}
+
+
+/*---------------------------------------------*/
+/*
+ * The following is an implementation of
+ * an elegant 3-way quicksort for strings,
+ * described in a paper "Fast Algorithms for
+ * Sorting and Searching Strings", by Robert
+ * Sedgewick and Jon L. Bentley.
+ */
+
+static
+ALWAYS_INLINE
+uint8_t mmed3(uint8_t a, uint8_t b, uint8_t c)
+{
+	uint8_t t;
+	if (a > b) {
+		t = a;
+		a = b;
+		b = t;
+	};
+	/* here b >= a */
+	if (b > c) {
+		b = c;
+		if (a > b)
+			b = a;
+	}
+	return b;
+}
+
+#define mpush(lz,hz,dz) \
+{ \
+	stackLo[sp] = lz; \
+	stackHi[sp] = hz; \
+	stackD [sp] = dz; \
+	sp++; \
+}
+
+#define mpop(lz,hz,dz) \
+{ \
+	sp--; \
+	lz = stackLo[sp]; \
+	hz = stackHi[sp]; \
+	dz = stackD [sp]; \
+}
+
+#define mnextsize(az) (nextHi[az] - nextLo[az])
+
+#define mnextswap(az,bz) \
+{ \
+	int32_t tz; \
+	tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \
+	tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \
+	tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; \
+}
+
+#define MAIN_QSORT_SMALL_THRESH 20
+#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT)
+#define MAIN_QSORT_STACK_SIZE   100
+
+static NOINLINE
+void mainQSort3(uint32_t* ptr,
+		uint8_t*  block,
+		uint16_t* quadrant,
+		int32_t   nblock,
+		int32_t   loSt,
+		int32_t   hiSt,
+		int32_t   dSt,
+		int32_t*  budget)
+{
+	int32_t unLo, unHi, ltLo, gtHi, n, m, med;
+	int32_t sp, lo, hi, d;
+
+	int32_t stackLo[MAIN_QSORT_STACK_SIZE];
+	int32_t stackHi[MAIN_QSORT_STACK_SIZE];
+	int32_t stackD [MAIN_QSORT_STACK_SIZE];
+
+	int32_t nextLo[3];
+	int32_t nextHi[3];
+	int32_t nextD [3];
+
+	sp = 0;
+	mpush(loSt, hiSt, dSt);
+
+	while (sp > 0) {
+		AssertH(sp < MAIN_QSORT_STACK_SIZE - 2, 1001);
+
+		mpop(lo, hi, d);
+		if (hi - lo < MAIN_QSORT_SMALL_THRESH
+		 || d > MAIN_QSORT_DEPTH_THRESH
+		) {
+			mainSimpleSort(ptr, block, quadrant, nblock, lo, hi, d, budget);
+			if (*budget < 0)
+				return;
+			continue;
+		}
+		med = (int32_t)	mmed3(block[ptr[lo          ] + d],
+		                      block[ptr[hi          ] + d],
+		                      block[ptr[(lo+hi) >> 1] + d]);
+
+		unLo = ltLo = lo;
+		unHi = gtHi = hi;
+
+		while (1) {
+			while (1) {
+				if (unLo > unHi)
+					break;
+				n = ((int32_t)block[ptr[unLo]+d]) - med;
+				if (n == 0) {
+					mswap(ptr[unLo], ptr[ltLo]);
+					ltLo++;
+					unLo++;
+					continue;
+				};
+				if (n >  0) break;
+				unLo++;
+			}
+			while (1) {
+				if (unLo > unHi)
+					break;
+				n = ((int32_t)block[ptr[unHi]+d]) - med;
+				if (n == 0) {
+					mswap(ptr[unHi], ptr[gtHi]);
+					gtHi--;
+					unHi--;
+					continue;
+				};
+				if (n <  0) break;
+				unHi--;
+			}
+			if (unLo > unHi)
+				break;
+			mswap(ptr[unLo], ptr[unHi]);
+			unLo++;
+			unHi--;
+		}
+
+		AssertD(unHi == unLo-1, "mainQSort3(2)");
+
+		if (gtHi < ltLo) {
+			mpush(lo, hi, d + 1);
+			continue;
+		}
+
+		n = mmin(ltLo-lo, unLo-ltLo); mvswap(ptr, lo, unLo-n, n);
+		m = mmin(hi-gtHi, gtHi-unHi); mvswap(ptr, unLo, hi-m+1, m);
+
+		n = lo + unLo - ltLo - 1;
+		m = hi - (gtHi - unHi) + 1;
+
+		nextLo[0] = lo;  nextHi[0] = n;   nextD[0] = d;
+		nextLo[1] = m;   nextHi[1] = hi;  nextD[1] = d;
+		nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1;
+
+		if (mnextsize(0) < mnextsize(1)) mnextswap(0, 1);
+		if (mnextsize(1) < mnextsize(2)) mnextswap(1, 2);
+		if (mnextsize(0) < mnextsize(1)) mnextswap(0, 1);
+
+		AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)");
+		AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)");
+
+		mpush(nextLo[0], nextHi[0], nextD[0]);
+		mpush(nextLo[1], nextHi[1], nextD[1]);
+		mpush(nextLo[2], nextHi[2], nextD[2]);
+	}
+}
+
+#undef mpush
+#undef mpop
+#undef mnextsize
+#undef mnextswap
+#undef MAIN_QSORT_SMALL_THRESH
+#undef MAIN_QSORT_DEPTH_THRESH
+#undef MAIN_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+ *	nblock > N_OVERSHOOT
+ *	block32 exists for [0 .. nblock-1 +N_OVERSHOOT]
+ *	((uint8_t*)block32) [0 .. nblock-1] holds block
+ *	ptr exists for [0 .. nblock-1]
+ *
+ * Post:
+ *	((uint8_t*)block32) [0 .. nblock-1] holds block
+ *	All other areas of block32 destroyed
+ *	ftab[0 .. 65536] destroyed
+ *	ptr [0 .. nblock-1] holds sorted order
+ *	if (*budget < 0), sorting was abandoned
+ */
+
+#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8])
+#define SETMASK (1 << 21)
+#define CLEARMASK (~(SETMASK))
+
+static NOINLINE
+void mainSort(EState* state,
+		uint32_t* ptr,
+		uint8_t*  block,
+		uint16_t* quadrant,
+		uint32_t* ftab,
+		int32_t   nblock,
+		int32_t*  budget)
+{
+	int32_t  i, j, k, ss, sb;
+	uint8_t  c1;
+	int32_t  numQSorted;
+	uint16_t s;
+	Bool     bigDone[256];
+	/* bbox: moved to EState to save stack
+	int32_t  runningOrder[256];
+	int32_t  copyStart[256];
+	int32_t  copyEnd  [256];
+	*/
+#define runningOrder (state->mainSort__runningOrder)
+#define copyStart    (state->mainSort__copyStart)
+#define copyEnd      (state->mainSort__copyEnd)
+
+	/*-- set up the 2-byte frequency table --*/
+	/* was: for (i = 65536; i >= 0; i--) ftab[i] = 0; */
+	memset(ftab, 0, 65537 * sizeof(ftab[0]));
+
+	j = block[0] << 8;
+	i = nblock - 1;
+/* 3%, +300 bytes */
+#if CONFIG_BZIP2_FEATURE_SPEED >= 2
+	for (; i >= 3; i -= 4) {
+		quadrant[i] = 0;
+		j = (j >> 8) | (((uint16_t)block[i]) << 8);
+		ftab[j]++;
+		quadrant[i-1] = 0;
+		j = (j >> 8) | (((uint16_t)block[i-1]) << 8);
+		ftab[j]++;
+		quadrant[i-2] = 0;
+		j = (j >> 8) | (((uint16_t)block[i-2]) << 8);
+		ftab[j]++;
+		quadrant[i-3] = 0;
+		j = (j >> 8) | (((uint16_t)block[i-3]) << 8);
+		ftab[j]++;
+	}
+#endif
+	for (; i >= 0; i--) {
+		quadrant[i] = 0;
+		j = (j >> 8) | (((uint16_t)block[i]) << 8);
+		ftab[j]++;
+	}
+
+	/*-- (emphasises close relationship of block & quadrant) --*/
+	for (i = 0; i < BZ_N_OVERSHOOT; i++) {
+		block   [nblock+i] = block[i];
+		quadrant[nblock+i] = 0;
+	}
+
+	/*-- Complete the initial radix sort --*/
+	j = ftab[0]; /* bbox: optimized */
+	for (i = 1; i <= 65536; i++) {
+		j += ftab[i];
+		ftab[i] = j;
+	}
+
+	s = block[0] << 8;
+	i = nblock - 1;
+#if CONFIG_BZIP2_FEATURE_SPEED >= 2
+	for (; i >= 3; i -= 4) {
+		s = (s >> 8) | (block[i] << 8);
+		j = ftab[s] - 1;
+		ftab[s] = j;
+		ptr[j] = i;
+		s = (s >> 8) | (block[i-1] << 8);
+		j = ftab[s] - 1;
+		ftab[s] = j;
+		ptr[j] = i-1;
+		s = (s >> 8) | (block[i-2] << 8);
+		j = ftab[s] - 1;
+		ftab[s] = j;
+		ptr[j] = i-2;
+		s = (s >> 8) | (block[i-3] << 8);
+		j = ftab[s] - 1;
+		ftab[s] = j;
+		ptr[j] = i-3;
+	}
+#endif
+	for (; i >= 0; i--) {
+		s = (s >> 8) | (block[i] << 8);
+		j = ftab[s] - 1;
+		ftab[s] = j;
+		ptr[j] = i;
+	}
+
+	/*
+	 * Now ftab contains the first loc of every small bucket.
+	 * Calculate the running order, from smallest to largest
+	 * big bucket.
+	 */
+	for (i = 0; i <= 255; i++) {
+		bigDone     [i] = False;
+		runningOrder[i] = i;
+	}
+
+	{
+		int32_t vv;
+		/* bbox: was: int32_t h = 1; */
+		/* do h = 3 * h + 1; while (h <= 256); */
+		uint32_t h = 364;
+
+		do {
+			/*h = h / 3;*/
+			h = (h * 171) >> 9; /* bbox: fast h/3 */
+			for (i = h; i <= 255; i++) {
+				vv = runningOrder[i];
+				j = i;
+				while (BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv)) {
+					runningOrder[j] = runningOrder[j-h];
+					j = j - h;
+					if (j <= (h - 1))
+						goto zero;
+				}
+ zero:
+				runningOrder[j] = vv;
+			}
+		} while (h != 1);
+	}
+
+	/*
+	 * The main sorting loop.
+	 */
+
+	numQSorted = 0;
+
+	for (i = 0; i <= 255; i++) {
+
+		/*
+		 * Process big buckets, starting with the least full.
+		 * Basically this is a 3-step process in which we call
+		 * mainQSort3 to sort the small buckets [ss, j], but
+		 * also make a big effort to avoid the calls if we can.
+		 */
+		ss = runningOrder[i];
+
+		/*
+		 * Step 1:
+		 * Complete the big bucket [ss] by quicksorting
+		 * any unsorted small buckets [ss, j], for j != ss.
+		 * Hopefully previous pointer-scanning phases have already
+		 * completed many of the small buckets [ss, j], so
+		 * we don't have to sort them at all.
+		 */
+		for (j = 0; j <= 255; j++) {
+			if (j != ss) {
+				sb = (ss << 8) + j;
+				if (!(ftab[sb] & SETMASK)) {
+					int32_t lo =  ftab[sb]   & CLEARMASK;
+					int32_t hi = (ftab[sb+1] & CLEARMASK) - 1;
+					if (hi > lo) {
+						mainQSort3(
+							ptr, block, quadrant, nblock,
+							lo, hi, BZ_N_RADIX, budget
+						);
+						if (*budget < 0) return;
+						numQSorted += (hi - lo + 1);
+					}
+				}
+				ftab[sb] |= SETMASK;
+			}
+		}
+
+		AssertH(!bigDone[ss], 1006);
+
+		/*
+		 * Step 2:
+		 * Now scan this big bucket [ss] so as to synthesise the
+		 * sorted order for small buckets [t, ss] for all t,
+		 * including, magically, the bucket [ss,ss] too.
+		 * This will avoid doing Real Work in subsequent Step 1's.
+		 */
+		{
+			for (j = 0; j <= 255; j++) {
+				copyStart[j] =  ftab[(j << 8) + ss]     & CLEARMASK;
+				copyEnd  [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1;
+			}
+			for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) {
+				k = ptr[j] - 1;
+				if (k < 0)
+					k += nblock;
+				c1 = block[k];
+				if (!bigDone[c1])
+					ptr[copyStart[c1]++] = k;
+			}
+			for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) {
+				k = ptr[j]-1;
+				if (k < 0)
+					k += nblock;
+				c1 = block[k];
+				if (!bigDone[c1])
+					ptr[copyEnd[c1]--] = k;
+			}
+		}
+
+		/* Extremely rare case missing in bzip2-1.0.0 and 1.0.1.
+		 * Necessity for this case is demonstrated by compressing
+		 * a sequence of approximately 48.5 million of character
+		 * 251; 1.0.0/1.0.1 will then die here. */
+		AssertH((copyStart[ss]-1 == copyEnd[ss]) \
+		     || (copyStart[ss] == 0 && copyEnd[ss] == nblock-1), 1007);
+
+		for (j = 0; j <= 255; j++)
+			ftab[(j << 8) + ss] |= SETMASK;
+
+		/*
+		 * Step 3:
+		 * The [ss] big bucket is now done.  Record this fact,
+		 * and update the quadrant descriptors.  Remember to
+		 * update quadrants in the overshoot area too, if
+		 * necessary.  The "if (i < 255)" test merely skips
+		 * this updating for the last bucket processed, since
+		 * updating for the last bucket is pointless.
+		 *
+		 * The quadrant array provides a way to incrementally
+		 * cache sort orderings, as they appear, so as to
+		 * make subsequent comparisons in fullGtU() complete
+		 * faster.  For repetitive blocks this makes a big
+		 * difference (but not big enough to be able to avoid
+		 * the fallback sorting mechanism, exponential radix sort).
+		 *
+		 * The precise meaning is: at all times:
+		 *
+		 *	for 0 <= i < nblock and 0 <= j <= nblock
+		 *
+		 *	if block[i] != block[j],
+		 *
+		 *		then the relative values of quadrant[i] and
+		 *			  quadrant[j] are meaningless.
+		 *
+		 *		else {
+		 *			if quadrant[i] < quadrant[j]
+		 *				then the string starting at i lexicographically
+		 *				precedes the string starting at j
+		 *
+		 *			else if quadrant[i] > quadrant[j]
+		 *				then the string starting at j lexicographically
+		 *				precedes the string starting at i
+		 *
+		 *			else
+		 *				the relative ordering of the strings starting
+		 *				at i and j has not yet been determined.
+		 *		}
+		 */
+		bigDone[ss] = True;
+
+		if (i < 255) {
+			int32_t bbStart = ftab[ss << 8] & CLEARMASK;
+			int32_t bbSize  = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart;
+			int32_t shifts  = 0;
+
+			while ((bbSize >> shifts) > 65534) shifts++;
+
+			for (j = bbSize-1; j >= 0; j--) {
+				int32_t a2update   = ptr[bbStart + j];
+				uint16_t qVal      = (uint16_t)(j >> shifts);
+				quadrant[a2update] = qVal;
+				if (a2update < BZ_N_OVERSHOOT)
+					quadrant[a2update + nblock] = qVal;
+			}
+			AssertH(((bbSize-1) >> shifts) <= 65535, 1002);
+		}
+	}
+#undef runningOrder
+#undef copyStart
+#undef copyEnd
+}
+
+#undef BIGFREQ
+#undef SETMASK
+#undef CLEARMASK
+
+
+/*---------------------------------------------*/
+/* Pre:
+ *	nblock > 0
+ *	arr2 exists for [0 .. nblock-1 +N_OVERSHOOT]
+ *	  ((uint8_t*)arr2)[0 .. nblock-1] holds block
+ *	arr1 exists for [0 .. nblock-1]
+ *
+ * Post:
+ *	((uint8_t*)arr2) [0 .. nblock-1] holds block
+ *	All other areas of block destroyed
+ *	ftab[0 .. 65536] destroyed
+ *	arr1[0 .. nblock-1] holds sorted order
+ */
+static NOINLINE
+void BZ2_blockSort(EState* s)
+{
+	/* In original bzip2 1.0.4, it's a parameter, but 30
+	 * (which was the default) should work ok. */
+	enum { wfact = 30 };
+
+	uint32_t* ptr    = s->ptr;
+	uint8_t*  block  = s->block;
+	uint32_t* ftab   = s->ftab;
+	int32_t   nblock = s->nblock;
+	uint16_t* quadrant;
+	int32_t   budget;
+	int32_t   i;
+
+	if (nblock < 10000) {
+		fallbackSort(s->arr1, s->arr2, ftab, nblock);
+	} else {
+		/* Calculate the location for quadrant, remembering to get
+		 * the alignment right.  Assumes that &(block[0]) is at least
+		 * 2-byte aligned -- this should be ok since block is really
+		 * the first section of arr2.
+		 */
+		i = nblock + BZ_N_OVERSHOOT;
+		if (i & 1) i++;
+		quadrant = (uint16_t*)(&(block[i]));
+
+		/* (wfact-1) / 3 puts the default-factor-30
+		 * transition point at very roughly the same place as
+		 * with v0.1 and v0.9.0.
+		 * Not that it particularly matters any more, since the
+		 * resulting compressed stream is now the same regardless
+		 * of whether or not we use the main sort or fallback sort.
+		 */
+		budget = nblock * ((wfact-1) / 3);
+
+		mainSort(s, ptr, block, quadrant, ftab, nblock, &budget);
+		if (budget < 0) {
+			fallbackSort(s->arr1, s->arr2, ftab, nblock);
+		}
+	}
+
+	s->origPtr = -1;
+	for (i = 0; i < s->nblock; i++)
+		if (ptr[i] == 0) {
+			s->origPtr = i;
+			break;
+		};
+
+	AssertH(s->origPtr != -1, 1003);
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                       blocksort.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/busybox-1.19.3/archival/libarchive/bz/bzlib.c b/busybox-1.19.3/archival/libarchive/bz/bzlib.c
new file mode 100644
index 0000000..5f7db74
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/bzlib.c
@@ -0,0 +1,429 @@
+/*
+ * bzip2 is written by Julian Seward <jseward@bzip.org>.
+ * Adapted for busybox by Denys Vlasenko <vda.linux@googlemail.com>.
+ * See README and LICENSE files in this directory for more information.
+ */
+
+/*-------------------------------------------------------------*/
+/*--- Library top-level functions.                          ---*/
+/*---                                               bzlib.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+This file is part of bzip2/libbzip2, a program and library for
+lossless, block-sorting data compression.
+
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
+Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org>
+
+Please read the WARNING, DISCLAIMER and PATENTS sections in the
+README file.
+
+This program is released under the terms of the license contained
+in the file LICENSE.
+------------------------------------------------------------------ */
+
+/* CHANGES
+ * 0.9.0    -- original version.
+ * 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.
+ *             wrong parameter order in call to bzDecompressInit in
+ *             bzBuffToBuffDecompress.  Fixed.
+ */
+
+/* #include "bzlib_private.h" */
+
+/*---------------------------------------------------*/
+/*--- Compression stuff                           ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+#if BZ_LIGHT_DEBUG
+static
+void bz_assert_fail(int errcode)
+{
+	/* if (errcode == 1007) bb_error_msg_and_die("probably bad RAM"); */
+	bb_error_msg_and_die("internal error %d", errcode);
+}
+#endif
+
+/*---------------------------------------------------*/
+static
+void prepare_new_block(EState* s)
+{
+	int i;
+	s->nblock = 0;
+	s->numZ = 0;
+	s->state_out_pos = 0;
+	BZ_INITIALISE_CRC(s->blockCRC);
+	/* inlined memset would be nice to have here */
+	for (i = 0; i < 256; i++)
+		s->inUse[i] = 0;
+	s->blockNo++;
+}
+
+
+/*---------------------------------------------------*/
+static
+ALWAYS_INLINE
+void init_RL(EState* s)
+{
+	s->state_in_ch = 256;
+	s->state_in_len = 0;
+}
+
+
+static
+int isempty_RL(EState* s)
+{
+	return (s->state_in_ch >= 256 || s->state_in_len <= 0);
+}
+
+
+/*---------------------------------------------------*/
+static
+void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k)
+{
+	int32_t n;
+	EState* s;
+
+	s = xzalloc(sizeof(EState));
+	s->strm = strm;
+
+	n        = 100000 * blockSize100k;
+	s->arr1  = xmalloc(n                    * sizeof(uint32_t));
+	s->mtfv  = (uint16_t*)s->arr1;
+	s->ptr   = (uint32_t*)s->arr1;
+	s->arr2  = xmalloc((n + BZ_N_OVERSHOOT) * sizeof(uint32_t));
+	s->block = (uint8_t*)s->arr2;
+	s->ftab  = xmalloc(65537                * sizeof(uint32_t));
+
+	s->crc32table = crc32_filltable(NULL, 1);
+
+	s->state             = BZ_S_INPUT;
+	s->mode              = BZ_M_RUNNING;
+	s->blockSize100k     = blockSize100k;
+	s->nblockMAX         = n - 19;
+
+	strm->state          = s;
+	/*strm->total_in     = 0;*/
+	strm->total_out      = 0;
+	init_RL(s);
+	prepare_new_block(s);
+}
+
+
+/*---------------------------------------------------*/
+static
+void add_pair_to_block(EState* s)
+{
+	int32_t i;
+	uint8_t ch = (uint8_t)(s->state_in_ch);
+	for (i = 0; i < s->state_in_len; i++) {
+		BZ_UPDATE_CRC(s, s->blockCRC, ch);
+	}
+	s->inUse[s->state_in_ch] = 1;
+	switch (s->state_in_len) {
+		case 3:
+			s->block[s->nblock] = (uint8_t)ch; s->nblock++;
+			/* fall through */
+		case 2:
+			s->block[s->nblock] = (uint8_t)ch; s->nblock++;
+			/* fall through */
+		case 1:
+			s->block[s->nblock] = (uint8_t)ch; s->nblock++;
+			break;
+		default:
+			s->inUse[s->state_in_len - 4] = 1;
+			s->block[s->nblock] = (uint8_t)ch; s->nblock++;
+			s->block[s->nblock] = (uint8_t)ch; s->nblock++;
+			s->block[s->nblock] = (uint8_t)ch; s->nblock++;
+			s->block[s->nblock] = (uint8_t)ch; s->nblock++;
+			s->block[s->nblock] = (uint8_t)(s->state_in_len - 4);
+			s->nblock++;
+			break;
+	}
+}
+
+
+/*---------------------------------------------------*/
+static
+void flush_RL(EState* s)
+{
+	if (s->state_in_ch < 256) add_pair_to_block(s);
+	init_RL(s);
+}
+
+
+/*---------------------------------------------------*/
+#define ADD_CHAR_TO_BLOCK(zs, zchh0) \
+{ \
+	uint32_t zchh = (uint32_t)(zchh0); \
+	/*-- fast track the common case --*/ \
+	if (zchh != zs->state_in_ch && zs->state_in_len == 1) { \
+		uint8_t ch = (uint8_t)(zs->state_in_ch); \
+		BZ_UPDATE_CRC(zs, zs->blockCRC, ch); \
+		zs->inUse[zs->state_in_ch] = 1; \
+		zs->block[zs->nblock] = (uint8_t)ch; \
+		zs->nblock++; \
+		zs->state_in_ch = zchh; \
+	} \
+	else \
+	/*-- general, uncommon cases --*/ \
+	if (zchh != zs->state_in_ch || zs->state_in_len == 255) { \
+		if (zs->state_in_ch < 256) \
+			add_pair_to_block(zs); \
+		zs->state_in_ch = zchh; \
+		zs->state_in_len = 1; \
+	} else { \
+		zs->state_in_len++; \
+	} \
+}
+
+
+/*---------------------------------------------------*/
+static
+void /*Bool*/ copy_input_until_stop(EState* s)
+{
+	/*Bool progress_in = False;*/
+
+#ifdef SAME_CODE_AS_BELOW
+	if (s->mode == BZ_M_RUNNING) {
+		/*-- fast track the common case --*/
+		while (1) {
+			/*-- no input? --*/
+			if (s->strm->avail_in == 0) break;
+			/*-- block full? --*/
+			if (s->nblock >= s->nblockMAX) break;
+			/*progress_in = True;*/
+			ADD_CHAR_TO_BLOCK(s, (uint32_t)(*(uint8_t*)(s->strm->next_in)));
+			s->strm->next_in++;
+			s->strm->avail_in--;
+			/*s->strm->total_in++;*/
+		}
+	} else
+#endif
+	{
+		/*-- general, uncommon case --*/
+		while (1) {
+			/*-- no input? --*/
+			if (s->strm->avail_in == 0) break;
+			/*-- block full? --*/
+			if (s->nblock >= s->nblockMAX) break;
+		//#	/*-- flush/finish end? --*/
+		//#	if (s->avail_in_expect == 0) break;
+			/*progress_in = True;*/
+			ADD_CHAR_TO_BLOCK(s, *(uint8_t*)(s->strm->next_in));
+			s->strm->next_in++;
+			s->strm->avail_in--;
+			/*s->strm->total_in++;*/
+		//#	s->avail_in_expect--;
+		}
+	}
+	/*return progress_in;*/
+}
+
+
+/*---------------------------------------------------*/
+static
+void /*Bool*/ copy_output_until_stop(EState* s)
+{
+	/*Bool progress_out = False;*/
+
+	while (1) {
+		/*-- no output space? --*/
+		if (s->strm->avail_out == 0) break;
+
+		/*-- block done? --*/
+		if (s->state_out_pos >= s->numZ) break;
+
+		/*progress_out = True;*/
+		*(s->strm->next_out) = s->zbits[s->state_out_pos];
+		s->state_out_pos++;
+		s->strm->avail_out--;
+		s->strm->next_out++;
+		s->strm->total_out++;
+	}
+	/*return progress_out;*/
+}
+
+
+/*---------------------------------------------------*/
+static
+void /*Bool*/ handle_compress(bz_stream *strm)
+{
+	/*Bool progress_in  = False;*/
+	/*Bool progress_out = False;*/
+	EState* s = strm->state;
+
+	while (1) {
+		if (s->state == BZ_S_OUTPUT) {
+			/*progress_out |=*/ copy_output_until_stop(s);
+			if (s->state_out_pos < s->numZ) break;
+			if (s->mode == BZ_M_FINISHING
+			//# && s->avail_in_expect == 0
+			 && s->strm->avail_in == 0
+			 && isempty_RL(s))
+				break;
+			prepare_new_block(s);
+			s->state = BZ_S_INPUT;
+#ifdef FLUSH_IS_UNUSED
+			if (s->mode == BZ_M_FLUSHING
+			 && s->avail_in_expect == 0
+			 && isempty_RL(s))
+				break;
+#endif
+		}
+
+		if (s->state == BZ_S_INPUT) {
+			/*progress_in |=*/ copy_input_until_stop(s);
+			//#if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) {
+			if (s->mode != BZ_M_RUNNING && s->strm->avail_in == 0) {
+				flush_RL(s);
+				BZ2_compressBlock(s, (s->mode == BZ_M_FINISHING));
+				s->state = BZ_S_OUTPUT;
+			} else
+			if (s->nblock >= s->nblockMAX) {
+				BZ2_compressBlock(s, 0);
+				s->state = BZ_S_OUTPUT;
+			} else
+			if (s->strm->avail_in == 0) {
+				break;
+			}
+		}
+	}
+
+	/*return progress_in || progress_out;*/
+}
+
+
+/*---------------------------------------------------*/
+static
+int BZ2_bzCompress(bz_stream *strm, int action)
+{
+	/*Bool progress;*/
+	EState* s;
+
+	s = strm->state;
+
+	switch (s->mode) {
+		case BZ_M_RUNNING:
+			if (action == BZ_RUN) {
+				/*progress =*/ handle_compress(strm);
+				/*return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;*/
+				return BZ_RUN_OK;
+			}
+#ifdef FLUSH_IS_UNUSED
+			else
+			if (action == BZ_FLUSH) {
+				//#s->avail_in_expect = strm->avail_in;
+				s->mode = BZ_M_FLUSHING;
+				goto case_BZ_M_FLUSHING;
+			}
+#endif
+			else
+			/*if (action == BZ_FINISH)*/ {
+				//#s->avail_in_expect = strm->avail_in;
+				s->mode = BZ_M_FINISHING;
+				goto case_BZ_M_FINISHING;
+			}
+
+#ifdef FLUSH_IS_UNUSED
+ case_BZ_M_FLUSHING:
+		case BZ_M_FLUSHING:
+			/*if (s->avail_in_expect != s->strm->avail_in)
+				return BZ_SEQUENCE_ERROR;*/
+			/*progress =*/ handle_compress(strm);
+			if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ)
+				return BZ_FLUSH_OK;
+			s->mode = BZ_M_RUNNING;
+			return BZ_RUN_OK;
+#endif
+
+ case_BZ_M_FINISHING:
+		/*case BZ_M_FINISHING:*/
+		default:
+			/*if (s->avail_in_expect != s->strm->avail_in)
+				return BZ_SEQUENCE_ERROR;*/
+			/*progress =*/ handle_compress(strm);
+			/*if (!progress) return BZ_SEQUENCE_ERROR;*/
+			//#if (s->avail_in_expect > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ)
+			//#	return BZ_FINISH_OK;
+			if (s->strm->avail_in > 0 || !isempty_RL(s) || s->state_out_pos < s->numZ)
+				return BZ_FINISH_OK;
+			/*s->mode = BZ_M_IDLE;*/
+			return BZ_STREAM_END;
+	}
+	/* return BZ_OK; --not reached--*/
+}
+
+
+/*---------------------------------------------------*/
+static
+void BZ2_bzCompressEnd(bz_stream *strm)
+{
+	EState* s;
+
+	s = strm->state;
+	free(s->arr1);
+	free(s->arr2);
+	free(s->ftab);
+	free(s->crc32table);
+	free(s);
+}
+
+
+/*---------------------------------------------------*/
+/*--- Misc convenience stuff                      ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+#ifdef EXAMPLE_CODE_FOR_MEM_TO_MEM_COMPRESSION
+static
+int BZ2_bzBuffToBuffCompress(char* dest,
+		unsigned int* destLen,
+		char*         source,
+		unsigned int  sourceLen,
+		int           blockSize100k)
+{
+	bz_stream strm;
+	int ret;
+
+	if (dest == NULL || destLen == NULL
+	 || source == NULL
+	 || blockSize100k < 1 || blockSize100k > 9
+	) {
+		return BZ_PARAM_ERROR;
+	}
+
+	BZ2_bzCompressInit(&strm, blockSize100k);
+
+	strm.next_in = source;
+	strm.next_out = dest;
+	strm.avail_in = sourceLen;
+	strm.avail_out = *destLen;
+
+	ret = BZ2_bzCompress(&strm, BZ_FINISH);
+	if (ret == BZ_FINISH_OK) goto output_overflow;
+	if (ret != BZ_STREAM_END) goto errhandler;
+
+	/* normal termination */
+	*destLen -= strm.avail_out;
+	BZ2_bzCompressEnd(&strm);
+	return BZ_OK;
+
+ output_overflow:
+	BZ2_bzCompressEnd(&strm);
+	return BZ_OUTBUFF_FULL;
+
+ errhandler:
+	BZ2_bzCompressEnd(&strm);
+	return ret;
+}
+#endif
+
+/*-------------------------------------------------------------*/
+/*--- end                                           bzlib.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/busybox-1.19.3/archival/libarchive/bz/bzlib.h b/busybox-1.19.3/archival/libarchive/bz/bzlib.h
new file mode 100644
index 0000000..1bb811c
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/bzlib.h
@@ -0,0 +1,65 @@
+/*
+ * bzip2 is written by Julian Seward <jseward@bzip.org>.
+ * Adapted for busybox by Denys Vlasenko <vda.linux@googlemail.com>.
+ * See README and LICENSE files in this directory for more information.
+ */
+
+/*-------------------------------------------------------------*/
+/*--- Public header file for the library.                   ---*/
+/*---                                               bzlib.h ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+This file is part of bzip2/libbzip2, a program and library for
+lossless, block-sorting data compression.
+
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
+Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org>
+
+Please read the WARNING, DISCLAIMER and PATENTS sections in the
+README file.
+
+This program is released under the terms of the license contained
+in the file LICENSE.
+------------------------------------------------------------------ */
+
+#define BZ_RUN               0
+#define BZ_FLUSH             1
+#define BZ_FINISH            2
+
+#define BZ_OK                0
+#define BZ_RUN_OK            1
+#define BZ_FLUSH_OK          2
+#define BZ_FINISH_OK         3
+#define BZ_STREAM_END        4
+#define BZ_SEQUENCE_ERROR    (-1)
+#define BZ_PARAM_ERROR       (-2)
+#define BZ_MEM_ERROR         (-3)
+#define BZ_DATA_ERROR        (-4)
+#define BZ_DATA_ERROR_MAGIC  (-5)
+#define BZ_IO_ERROR          (-6)
+#define BZ_UNEXPECTED_EOF    (-7)
+#define BZ_OUTBUFF_FULL      (-8)
+#define BZ_CONFIG_ERROR      (-9)
+
+typedef struct bz_stream {
+	void *state;
+	char *next_in;
+	char *next_out;
+	unsigned avail_in;
+	unsigned avail_out;
+	/*unsigned long long total_in;*/
+	unsigned long long total_out;
+} bz_stream;
+
+/*-- Core (low-level) library functions --*/
+
+static void BZ2_bzCompressInit(bz_stream *strm, int blockSize100k);
+static int BZ2_bzCompress(bz_stream *strm, int action);
+#if ENABLE_FEATURE_CLEAN_UP
+static void BZ2_bzCompressEnd(bz_stream *strm);
+#endif
+
+/*-------------------------------------------------------------*/
+/*--- end                                           bzlib.h ---*/
+/*-------------------------------------------------------------*/
diff --git a/busybox-1.19.3/archival/libarchive/bz/bzlib_private.h b/busybox-1.19.3/archival/libarchive/bz/bzlib_private.h
new file mode 100644
index 0000000..6430ce4
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/bzlib_private.h
@@ -0,0 +1,219 @@
+/*
+ * bzip2 is written by Julian Seward <jseward@bzip.org>.
+ * Adapted for busybox by Denys Vlasenko <vda.linux@googlemail.com>.
+ * See README and LICENSE files in this directory for more information.
+ */
+
+/*-------------------------------------------------------------*/
+/*--- Private header file for the library.                  ---*/
+/*---                                       bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+This file is part of bzip2/libbzip2, a program and library for
+lossless, block-sorting data compression.
+
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
+Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org>
+
+Please read the WARNING, DISCLAIMER and PATENTS sections in the
+README file.
+
+This program is released under the terms of the license contained
+in the file LICENSE.
+------------------------------------------------------------------ */
+
+/* #include "bzlib.h" */
+
+/*-- General stuff. --*/
+
+typedef unsigned char Bool;
+
+#define True  ((Bool)1)
+#define False ((Bool)0)
+
+#if BZ_LIGHT_DEBUG
+static void bz_assert_fail(int errcode) NORETURN;
+#define AssertH(cond, errcode) \
+do { \
+	if (!(cond)) \
+		bz_assert_fail(errcode); \
+} while (0)
+#else
+#define AssertH(cond, msg) do { } while (0)
+#endif
+
+#if BZ_DEBUG
+#define AssertD(cond, msg) \
+do { \
+	if (!(cond)) \
+		bb_error_msg_and_die("(debug build): internal error %s", msg); \
+} while (0)
+#else
+#define AssertD(cond, msg) do { } while (0)
+#endif
+
+
+/*-- Header bytes. --*/
+
+#define BZ_HDR_B 0x42   /* 'B' */
+#define BZ_HDR_Z 0x5a   /* 'Z' */
+#define BZ_HDR_h 0x68   /* 'h' */
+#define BZ_HDR_0 0x30   /* '0' */
+
+#define BZ_HDR_BZh0 0x425a6830
+
+/*-- Constants for the back end. --*/
+
+#define BZ_MAX_ALPHA_SIZE 258
+#define BZ_MAX_CODE_LEN    23
+
+#define BZ_RUNA 0
+#define BZ_RUNB 1
+
+#define BZ_N_GROUPS 6
+#define BZ_G_SIZE   50
+#define BZ_N_ITERS  4
+
+#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE))
+
+
+/*-- Stuff for doing CRCs. --*/
+
+#define BZ_INITIALISE_CRC(crcVar) \
+{ \
+	crcVar = 0xffffffffL; \
+}
+
+#define BZ_FINALISE_CRC(crcVar) \
+{ \
+	crcVar = ~(crcVar); \
+}
+
+#define BZ_UPDATE_CRC(s, crcVar, cha) \
+{ \
+	crcVar = (crcVar << 8) ^ s->crc32table[(crcVar >> 24) ^ ((uint8_t)cha)]; \
+}
+
+
+/*-- States and modes for compression. --*/
+
+#define BZ_M_IDLE      1
+#define BZ_M_RUNNING   2
+#define BZ_M_FLUSHING  3
+#define BZ_M_FINISHING 4
+
+#define BZ_S_OUTPUT    1
+#define BZ_S_INPUT     2
+
+#define BZ_N_RADIX 2
+#define BZ_N_QSORT 12
+#define BZ_N_SHELL 18
+#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2)
+
+
+/*-- Structure holding all the compression-side stuff. --*/
+
+typedef struct EState {
+	/* pointer back to the struct bz_stream */
+	bz_stream *strm;
+
+	/* mode this stream is in, and whether inputting */
+	/* or outputting data */
+	int32_t  mode;
+	int32_t  state;
+
+	/* remembers avail_in when flush/finish requested */
+/* bbox: not needed, strm->avail_in always has the same value */
+/* commented out with '//#' throughout the code */
+	/* uint32_t avail_in_expect; */
+
+	/* for doing the block sorting */
+	int32_t  origPtr;
+	uint32_t *arr1;
+	uint32_t *arr2;
+	uint32_t *ftab;
+
+	/* aliases for arr1 and arr2 */
+	uint32_t *ptr;
+	uint8_t  *block;
+	uint16_t *mtfv;
+	uint8_t  *zbits;
+
+	/* guess what */
+	uint32_t *crc32table;
+
+	/* run-length-encoding of the input */
+	uint32_t state_in_ch;
+	int32_t  state_in_len;
+
+	/* input and output limits and current posns */
+	int32_t  nblock;
+	int32_t  nblockMAX;
+	int32_t  numZ;
+	int32_t  state_out_pos;
+
+	/* the buffer for bit stream creation */
+	uint32_t bsBuff;
+	int32_t  bsLive;
+
+	/* block and combined CRCs */
+	uint32_t blockCRC;
+	uint32_t combinedCRC;
+
+	/* misc administratium */
+	int32_t  blockNo;
+	int32_t  blockSize100k;
+
+	/* stuff for coding the MTF values */
+	int32_t  nMTF;
+
+	/* map of bytes used in block */
+	int32_t  nInUse;
+	Bool     inUse[256] ALIGNED(sizeof(long));
+	uint8_t  unseqToSeq[256];
+
+	/* stuff for coding the MTF values */
+	int32_t  mtfFreq    [BZ_MAX_ALPHA_SIZE];
+	uint8_t  selector   [BZ_MAX_SELECTORS];
+	uint8_t  selectorMtf[BZ_MAX_SELECTORS];
+
+	uint8_t  len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+
+	/* 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
+	/* second dimension: only 3 needed; 4 makes index calculations faster */
+	uint32_t sendMTFValues__len_pack[BZ_MAX_ALPHA_SIZE][4];
+#endif
+	int32_t  BZ2_hbMakeCodeLengths__heap  [BZ_MAX_ALPHA_SIZE + 2];
+	int32_t  BZ2_hbMakeCodeLengths__weight[BZ_MAX_ALPHA_SIZE * 2];
+	int32_t  BZ2_hbMakeCodeLengths__parent[BZ_MAX_ALPHA_SIZE * 2];
+
+	int32_t  mainSort__runningOrder[256];
+	int32_t  mainSort__copyStart[256];
+	int32_t  mainSort__copyEnd[256];
+} EState;
+
+
+/*-- compression. --*/
+
+static void
+BZ2_blockSort(EState*);
+
+static void
+BZ2_compressBlock(EState*, int);
+
+static void
+BZ2_bsInitWrite(EState*);
+
+static void
+BZ2_hbAssignCodes(int32_t*, uint8_t*, int32_t, int32_t, int32_t);
+
+static void
+BZ2_hbMakeCodeLengths(EState*, uint8_t*, int32_t*, int32_t, int32_t);
+
+/*-------------------------------------------------------------*/
+/*--- end                                   bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
diff --git a/busybox-1.19.3/archival/libarchive/bz/compress.c b/busybox-1.19.3/archival/libarchive/bz/compress.c
new file mode 100644
index 0000000..f936717
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/compress.c
@@ -0,0 +1,680 @@
+/*
+ * bzip2 is written by Julian Seward <jseward@bzip.org>.
+ * Adapted for busybox by Denys Vlasenko <vda.linux@googlemail.com>.
+ * See README and LICENSE files in this directory for more information.
+ */
+
+/*-------------------------------------------------------------*/
+/*--- Compression machinery (not incl block sorting)        ---*/
+/*---                                            compress.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+This file is part of bzip2/libbzip2, a program and library for
+lossless, block-sorting data compression.
+
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
+Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org>
+
+Please read the WARNING, DISCLAIMER and PATENTS sections in the
+README file.
+
+This program is released under the terms of the license contained
+in the file LICENSE.
+------------------------------------------------------------------ */
+
+/* CHANGES
+ * 0.9.0    -- original version.
+ * 0.9.0a/b -- no changes in this file.
+ * 0.9.0c   -- changed setting of nGroups in sendMTFValues()
+ *             so as to do a bit better on small files
+*/
+
+/* #include "bzlib_private.h" */
+
+/*---------------------------------------------------*/
+/*--- Bit stream I/O                              ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+static
+void BZ2_bsInitWrite(EState* s)
+{
+	s->bsLive = 0;
+	s->bsBuff = 0;
+}
+
+
+/*---------------------------------------------------*/
+static NOINLINE
+void bsFinishWrite(EState* s)
+{
+	while (s->bsLive > 0) {
+		s->zbits[s->numZ] = (uint8_t)(s->bsBuff >> 24);
+		s->numZ++;
+		s->bsBuff <<= 8;
+		s->bsLive -= 8;
+	}
+}
+
+
+/*---------------------------------------------------*/
+static
+/* Helps only on level 5, on other levels hurts. ? */
+#if CONFIG_BZIP2_FEATURE_SPEED >= 5
+ALWAYS_INLINE
+#endif
+void bsW(EState* s, int32_t n, uint32_t v)
+{
+	while (s->bsLive >= 8) {
+		s->zbits[s->numZ] = (uint8_t)(s->bsBuff >> 24);
+		s->numZ++;
+		s->bsBuff <<= 8;
+		s->bsLive -= 8;
+	}
+	s->bsBuff |= (v << (32 - s->bsLive - n));
+	s->bsLive += n;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutU32(EState* s, unsigned u)
+{
+	bsW(s, 8, (u >> 24) & 0xff);
+	bsW(s, 8, (u >> 16) & 0xff);
+	bsW(s, 8, (u >>  8) & 0xff);
+	bsW(s, 8,  u        & 0xff);
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutU16(EState* s, unsigned u)
+{
+	bsW(s, 8, (u >>  8) & 0xff);
+	bsW(s, 8,  u        & 0xff);
+}
+
+
+/*---------------------------------------------------*/
+/*--- The back end proper                         ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+static
+void makeMaps_e(EState* s)
+{
+	int i;
+	s->nInUse = 0;
+	for (i = 0; i < 256; i++) {
+		if (s->inUse[i]) {
+			s->unseqToSeq[i] = s->nInUse;
+			s->nInUse++;
+		}
+	}
+}
+
+
+/*---------------------------------------------------*/
+static NOINLINE
+void generateMTFValues(EState* s)
+{
+	uint8_t yy[256];
+	int32_t i, j;
+	int32_t zPend;
+	int32_t wr;
+	int32_t EOB;
+
+	/*
+	 * After sorting (eg, here),
+	 * s->arr1[0 .. s->nblock-1] holds sorted order,
+	 * and
+	 * ((uint8_t*)s->arr2)[0 .. s->nblock-1]
+	 * 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].
+	 *
+	 * 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]
+	 *
+	 * These storage aliases are set up in bzCompressInit(),
+	 * except for the last one, which is arranged in
+	 * compressBlock().
+	 */
+	uint32_t* ptr   = s->ptr;
+	uint8_t*  block = s->block;
+	uint16_t* mtfv  = s->mtfv;
+
+	makeMaps_e(s);
+	EOB = s->nInUse+1;
+
+	for (i = 0; i <= EOB; i++)
+		s->mtfFreq[i] = 0;
+
+	wr = 0;
+	zPend = 0;
+	for (i = 0; i < s->nInUse; i++)
+		yy[i] = (uint8_t) i;
+
+	for (i = 0; i < s->nblock; i++) {
+		uint8_t ll_i;
+		AssertD(wr <= i, "generateMTFValues(1)");
+		j = ptr[i] - 1;
+		if (j < 0)
+			j += s->nblock;
+		ll_i = s->unseqToSeq[block[j]];
+		AssertD(ll_i < s->nInUse, "generateMTFValues(2a)");
+
+		if (yy[0] == ll_i) {
+			zPend++;
+		} else {
+			if (zPend > 0) {
+				zPend--;
+				while (1) {
+					if (zPend & 1) {
+						mtfv[wr] = BZ_RUNB; wr++;
+						s->mtfFreq[BZ_RUNB]++;
+					} else {
+						mtfv[wr] = BZ_RUNA; wr++;
+						s->mtfFreq[BZ_RUNA]++;
+					}
+					if (zPend < 2) break;
+					zPend = (uint32_t)(zPend - 2) / 2;
+					/* bbox: unsigned div is easier */
+				};
+				zPend = 0;
+			}
+			{
+				register uint8_t  rtmp;
+				register uint8_t* ryy_j;
+				register uint8_t  rll_i;
+				rtmp  = yy[1];
+				yy[1] = yy[0];
+				ryy_j = &(yy[1]);
+				rll_i = ll_i;
+				while (rll_i != rtmp) {
+					register uint8_t rtmp2;
+					ryy_j++;
+					rtmp2  = rtmp;
+					rtmp   = *ryy_j;
+					*ryy_j = rtmp2;
+				};
+				yy[0] = rtmp;
+				j = ryy_j - &(yy[0]);
+				mtfv[wr] = j+1;
+				wr++;
+				s->mtfFreq[j+1]++;
+			}
+		}
+	}
+
+	if (zPend > 0) {
+		zPend--;
+		while (1) {
+			if (zPend & 1) {
+				mtfv[wr] = BZ_RUNB;
+				wr++;
+				s->mtfFreq[BZ_RUNB]++;
+			} else {
+				mtfv[wr] = BZ_RUNA;
+				wr++;
+				s->mtfFreq[BZ_RUNA]++;
+			}
+			if (zPend < 2)
+				break;
+			zPend = (uint32_t)(zPend - 2) / 2;
+			/* bbox: unsigned div is easier */
+		};
+		zPend = 0;
+	}
+
+	mtfv[wr] = EOB;
+	wr++;
+	s->mtfFreq[EOB]++;
+
+	s->nMTF = wr;
+}
+
+
+/*---------------------------------------------------*/
+#define BZ_LESSER_ICOST  0
+#define BZ_GREATER_ICOST 15
+
+static NOINLINE
+void sendMTFValues(EState* s)
+{
+	int32_t v, t, i, j, gs, ge, totc, bt, bc, iter;
+	int32_t nSelectors, alphaSize, minLen, maxLen, selCtr;
+	int32_t nGroups;
+
+	/*
+	 * uint8_t len[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+	 * is a global since the decoder also needs it.
+	 *
+	 * int32_t  code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+	 * int32_t  rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+	 * are also globals only used in this proc.
+	 * Made global to keep stack frame size small.
+	 */
+#define code     sendMTFValues__code
+#define rfreq    sendMTFValues__rfreq
+#define len_pack sendMTFValues__len_pack
+
+	uint16_t cost[BZ_N_GROUPS];
+	int32_t  fave[BZ_N_GROUPS];
+
+	uint16_t* mtfv = s->mtfv;
+
+	alphaSize = s->nInUse + 2;
+	for (t = 0; t < BZ_N_GROUPS; t++)
+		for (v = 0; v < alphaSize; v++)
+			s->len[t][v] = BZ_GREATER_ICOST;
+
+	/*--- Decide how many coding tables to use ---*/
+	AssertH(s->nMTF > 0, 3001);
+	if (s->nMTF < 200)  nGroups = 2; else
+	if (s->nMTF < 600)  nGroups = 3; else
+	if (s->nMTF < 1200) nGroups = 4; else
+	if (s->nMTF < 2400) nGroups = 5; else
+	nGroups = 6;
+
+	/*--- Generate an initial set of coding tables ---*/
+	{
+		int32_t nPart, remF, tFreq, aFreq;
+
+		nPart = nGroups;
+		remF  = s->nMTF;
+		gs = 0;
+		while (nPart > 0) {
+			tFreq = remF / nPart;
+			ge = gs - 1;
+			aFreq = 0;
+			while (aFreq < tFreq && ge < alphaSize-1) {
+				ge++;
+				aFreq += s->mtfFreq[ge];
+			}
+
+			if (ge > gs
+			 && nPart != nGroups && nPart != 1
+			 && ((nGroups - nPart) % 2 == 1) /* bbox: can this be replaced by x & 1? */
+			) {
+				aFreq -= s->mtfFreq[ge];
+				ge--;
+			}
+
+			for (v = 0; v < alphaSize; v++)
+				if (v >= gs && v <= ge)
+					s->len[nPart-1][v] = BZ_LESSER_ICOST;
+				else
+					s->len[nPart-1][v] = BZ_GREATER_ICOST;
+
+			nPart--;
+			gs = ge + 1;
+			remF -= aFreq;
+		}
+	}
+
+	/*
+	 * Iterate up to BZ_N_ITERS times to improve the tables.
+	 */
+	for (iter = 0; iter < BZ_N_ITERS; iter++) {
+		for (t = 0; t < nGroups; t++)
+			fave[t] = 0;
+
+		for (t = 0; t < nGroups; t++)
+			for (v = 0; v < alphaSize; v++)
+				s->rfreq[t][v] = 0;
+
+#if CONFIG_BZIP2_FEATURE_SPEED >= 5
+		/*
+		 * Set up an auxiliary length table which is used to fast-track
+		 * the common case (nGroups == 6).
+		 */
+		if (nGroups == 6) {
+			for (v = 0; v < alphaSize; v++) {
+				s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
+				s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
+				s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
+			}
+		}
+#endif
+		nSelectors = 0;
+		totc = 0;
+		gs = 0;
+		while (1) {
+			/*--- Set group start & end marks. --*/
+			if (gs >= s->nMTF)
+				break;
+			ge = gs + BZ_G_SIZE - 1;
+			if (ge >= s->nMTF)
+				ge = s->nMTF-1;
+
+			/*
+			 * Calculate the cost of this group as coded
+			 * by each of the coding tables.
+			 */
+			for (t = 0; t < nGroups; t++)
+				cost[t] = 0;
+#if CONFIG_BZIP2_FEATURE_SPEED >= 5
+			if (nGroups == 6 && 50 == ge-gs+1) {
+				/*--- fast track the common case ---*/
+				register uint32_t cost01, cost23, cost45;
+				register uint16_t icv;
+				cost01 = cost23 = cost45 = 0;
+#define BZ_ITER(nn) \
+	icv = mtfv[gs+(nn)]; \
+	cost01 += s->len_pack[icv][0]; \
+	cost23 += s->len_pack[icv][1]; \
+	cost45 += s->len_pack[icv][2];
+				BZ_ITER(0);  BZ_ITER(1);  BZ_ITER(2);  BZ_ITER(3);  BZ_ITER(4);
+				BZ_ITER(5);  BZ_ITER(6);  BZ_ITER(7);  BZ_ITER(8);  BZ_ITER(9);
+				BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
+				BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
+				BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
+				BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
+				BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
+				BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
+				BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
+				BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);
+#undef BZ_ITER
+				cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
+				cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
+				cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
+
+			} else
+#endif
+			{
+				/*--- slow version which correctly handles all situations ---*/
+				for (i = gs; i <= ge; i++) {
+					uint16_t icv = mtfv[i];
+					for (t = 0; t < nGroups; t++)
+						cost[t] += s->len[t][icv];
+				}
+			}
+			/*
+			 * Find the coding table which is best for this group,
+			 * and record its identity in the selector table.
+			 */
+			/*bc = 999999999;*/
+			/*bt = -1;*/
+			bc = cost[0];
+			bt = 0;
+			for (t = 1 /*0*/; t < nGroups; t++) {
+				if (cost[t] < bc) {
+					bc = cost[t];
+					bt = t;
+				}
+			}
+			totc += bc;
+			fave[bt]++;
+			s->selector[nSelectors] = bt;
+			nSelectors++;
+
+			/*
+			 * Increment the symbol frequencies for the selected table.
+			 */
+/* 1% faster compress. +800 bytes */
+#if CONFIG_BZIP2_FEATURE_SPEED >= 4
+			if (nGroups == 6 && 50 == ge-gs+1) {
+				/*--- fast track the common case ---*/
+#define BZ_ITUR(nn) s->rfreq[bt][mtfv[gs + (nn)]]++
+				BZ_ITUR(0);  BZ_ITUR(1);  BZ_ITUR(2);  BZ_ITUR(3);  BZ_ITUR(4);
+				BZ_ITUR(5);  BZ_ITUR(6);  BZ_ITUR(7);  BZ_ITUR(8);  BZ_ITUR(9);
+				BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
+				BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
+				BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
+				BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
+				BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
+				BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
+				BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
+				BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);
+#undef BZ_ITUR
+				gs = ge + 1;
+			} else
+#endif
+			{
+				/*--- slow version which correctly handles all situations ---*/
+				while (gs <= ge) {
+					s->rfreq[bt][mtfv[gs]]++;
+					gs++;
+				}
+				/* already is: gs = ge + 1; */
+			}
+		}
+
+		/*
+		 * Recompute the tables based on the accumulated frequencies.
+		 */
+		/* maxLen was changed from 20 to 17 in bzip2-1.0.3.  See
+		 * comment in huffman.c for details. */
+		for (t = 0; t < nGroups; t++)
+			BZ2_hbMakeCodeLengths(s, &(s->len[t][0]), &(s->rfreq[t][0]), alphaSize, 17 /*20*/);
+	}
+
+	AssertH(nGroups < 8, 3002);
+	AssertH(nSelectors < 32768 && nSelectors <= (2 + (900000 / BZ_G_SIZE)), 3003);
+
+	/*--- Compute MTF values for the selectors. ---*/
+	{
+		uint8_t pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
+
+		for (i = 0; i < nGroups; i++)
+			pos[i] = i;
+		for (i = 0; i < nSelectors; i++) {
+			ll_i = s->selector[i];
+			j = 0;
+			tmp = pos[j];
+			while (ll_i != tmp) {
+				j++;
+				tmp2 = tmp;
+				tmp = pos[j];
+				pos[j] = tmp2;
+			};
+			pos[0] = tmp;
+			s->selectorMtf[i] = j;
+		}
+	};
+
+	/*--- Assign actual codes for the tables. --*/
+	for (t = 0; t < nGroups; t++) {
+		minLen = 32;
+		maxLen = 0;
+		for (i = 0; i < alphaSize; i++) {
+			if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+			if (s->len[t][i] < minLen) minLen = s->len[t][i];
+		}
+		AssertH(!(maxLen > 17 /*20*/), 3004);
+		AssertH(!(minLen < 1), 3005);
+		BZ2_hbAssignCodes(&(s->code[t][0]), &(s->len[t][0]), minLen, maxLen, alphaSize);
+	}
+
+	/*--- Transmit the mapping table. ---*/
+	{
+		/* bbox: optimized a bit more than in bzip2 */
+		int inUse16 = 0;
+		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);
+			} else { /* Our CPU can do better */
+				inUse16 = inUse16*2 +
+					((*(uint64_t*)&(s->inUse[i * 16 + 0])
+					| *(uint64_t*)&(s->inUse[i * 16 + 8])) != 0);
+			}
+		}
+
+		bsW(s, 16, inUse16);
+
+		inUse16 <<= (sizeof(int)*8 - 16); /* move 15th bit into sign bit */
+		for (i = 0; i < 16; i++) {
+			if (inUse16 < 0) {
+				unsigned v16 = 0;
+				for (j = 0; j < 16; j++)
+					v16 = v16*2 + s->inUse[i * 16 + j];
+				bsW(s, 16, v16);
+			}
+			inUse16 <<= 1;
+		}
+	}
+
+	/*--- Now the selectors. ---*/
+	bsW(s, 3, nGroups);
+	bsW(s, 15, nSelectors);
+	for (i = 0; i < nSelectors; i++) {
+		for (j = 0; j < s->selectorMtf[i]; j++)
+			bsW(s, 1, 1);
+		bsW(s, 1, 0);
+	}
+
+	/*--- Now the coding tables. ---*/
+	for (t = 0; t < nGroups; t++) {
+		int32_t curr = s->len[t][0];
+		bsW(s, 5, curr);
+		for (i = 0; i < alphaSize; i++) {
+			while (curr < s->len[t][i]) { bsW(s, 2, 2); curr++; /* 10 */ };
+			while (curr > s->len[t][i]) { bsW(s, 2, 3); curr--; /* 11 */ };
+			bsW(s, 1, 0);
+		}
+	}
+
+	/*--- And finally, the block data proper ---*/
+	selCtr = 0;
+	gs = 0;
+	while (1) {
+		if (gs >= s->nMTF)
+			break;
+		ge = gs + BZ_G_SIZE - 1;
+		if (ge >= s->nMTF)
+			ge = s->nMTF-1;
+		AssertH(s->selector[selCtr] < nGroups, 3006);
+
+/* Costs 1300 bytes and is _slower_ (on Intel Core 2) */
+#if 0
+		if (nGroups == 6 && 50 == ge-gs+1) {
+			/*--- fast track the common case ---*/
+			uint16_t mtfv_i;
+			uint8_t* s_len_sel_selCtr  = &(s->len[s->selector[selCtr]][0]);
+			int32_t* s_code_sel_selCtr = &(s->code[s->selector[selCtr]][0]);
+#define BZ_ITAH(nn) \
+	mtfv_i = mtfv[gs+(nn)]; \
+	bsW(s, s_len_sel_selCtr[mtfv_i], s_code_sel_selCtr[mtfv_i])
+			BZ_ITAH(0);  BZ_ITAH(1);  BZ_ITAH(2);  BZ_ITAH(3);  BZ_ITAH(4);
+			BZ_ITAH(5);  BZ_ITAH(6);  BZ_ITAH(7);  BZ_ITAH(8);  BZ_ITAH(9);
+			BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
+			BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
+			BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
+			BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
+			BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
+			BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
+			BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
+			BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
+#undef BZ_ITAH
+			gs = ge+1;
+		} else
+#endif
+		{
+			/*--- slow version which correctly handles all situations ---*/
+			/* code is bit bigger, but moves multiply out of the loop */
+			uint8_t* s_len_sel_selCtr  = &(s->len [s->selector[selCtr]][0]);
+			int32_t* s_code_sel_selCtr = &(s->code[s->selector[selCtr]][0]);
+			while (gs <= ge) {
+				bsW(s,
+					s_len_sel_selCtr[mtfv[gs]],
+					s_code_sel_selCtr[mtfv[gs]]
+				);
+				gs++;
+			}
+			/* already is: gs = ge+1; */
+		}
+		selCtr++;
+	}
+	AssertH(selCtr == nSelectors, 3007);
+#undef code
+#undef rfreq
+#undef len_pack
+}
+
+
+/*---------------------------------------------------*/
+static
+void BZ2_compressBlock(EState* s, int is_last_block)
+{
+	if (s->nblock > 0) {
+		BZ_FINALISE_CRC(s->blockCRC);
+		s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
+		s->combinedCRC ^= s->blockCRC;
+		if (s->blockNo > 1)
+			s->numZ = 0;
+
+		BZ2_blockSort(s);
+	}
+
+	s->zbits = &((uint8_t*)s->arr2)[s->nblock];
+
+	/*-- If this is the first block, create the stream header. --*/
+	if (s->blockNo == 1) {
+		BZ2_bsInitWrite(s);
+		/*bsPutU8(s, BZ_HDR_B);*/
+		/*bsPutU8(s, BZ_HDR_Z);*/
+		/*bsPutU8(s, BZ_HDR_h);*/
+		/*bsPutU8(s, BZ_HDR_0 + s->blockSize100k);*/
+		bsPutU32(s, BZ_HDR_BZh0 + s->blockSize100k);
+	}
+
+	if (s->nblock > 0) {
+		/*bsPutU8(s, 0x31);*/
+		/*bsPutU8(s, 0x41);*/
+		/*bsPutU8(s, 0x59);*/
+		/*bsPutU8(s, 0x26);*/
+		bsPutU32(s, 0x31415926);
+		/*bsPutU8(s, 0x53);*/
+		/*bsPutU8(s, 0x59);*/
+		bsPutU16(s, 0x5359);
+
+		/*-- Now the block's CRC, so it is in a known place. --*/
+		bsPutU32(s, s->blockCRC);
+
+		/*
+		 * Now a single bit indicating (non-)randomisation.
+		 * As of version 0.9.5, we use a better sorting algorithm
+		 * which makes randomisation unnecessary.  So always set
+		 * the randomised bit to 'no'.  Of course, the decoder
+		 * still needs to be able to handle randomised blocks
+		 * so as to maintain backwards compatibility with
+		 * older versions of bzip2.
+		 */
+		bsW(s, 1, 0);
+
+		bsW(s, 24, s->origPtr);
+		generateMTFValues(s);
+		sendMTFValues(s);
+	}
+
+	/*-- If this is the last block, add the stream trailer. --*/
+	if (is_last_block) {
+		/*bsPutU8(s, 0x17);*/
+		/*bsPutU8(s, 0x72);*/
+		/*bsPutU8(s, 0x45);*/
+		/*bsPutU8(s, 0x38);*/
+		bsPutU32(s, 0x17724538);
+		/*bsPutU8(s, 0x50);*/
+		/*bsPutU8(s, 0x90);*/
+		bsPutU16(s, 0x5090);
+		bsPutU32(s, s->combinedCRC);
+		bsFinishWrite(s);
+	}
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                        compress.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/busybox-1.19.3/archival/libarchive/bz/huffman.c b/busybox-1.19.3/archival/libarchive/bz/huffman.c
new file mode 100644
index 0000000..676b1af
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/bz/huffman.c
@@ -0,0 +1,229 @@
+/*
+ * bzip2 is written by Julian Seward <jseward@bzip.org>.
+ * Adapted for busybox by Denys Vlasenko <vda.linux@googlemail.com>.
+ * See README and LICENSE files in this directory for more information.
+ */
+
+/*-------------------------------------------------------------*/
+/*--- Huffman coding low-level stuff                        ---*/
+/*---                                             huffman.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+This file is part of bzip2/libbzip2, a program and library for
+lossless, block-sorting data compression.
+
+bzip2/libbzip2 version 1.0.4 of 20 December 2006
+Copyright (C) 1996-2006 Julian Seward <jseward@bzip.org>
+
+Please read the WARNING, DISCLAIMER and PATENTS sections in the
+README file.
+
+This program is released under the terms of the license contained
+in the file LICENSE.
+------------------------------------------------------------------ */
+
+/* #include "bzlib_private.h" */
+
+/*---------------------------------------------------*/
+#define WEIGHTOF(zz0)  ((zz0) & 0xffffff00)
+#define DEPTHOF(zz1)   ((zz1) & 0x000000ff)
+#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3))
+
+#define ADDWEIGHTS(zw1,zw2) \
+	(WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \
+	(1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2)))
+
+#define UPHEAP(z) \
+{ \
+	int32_t zz, tmp; \
+	zz = z; \
+	tmp = heap[zz]; \
+	while (weight[tmp] < weight[heap[zz >> 1]]) { \
+		heap[zz] = heap[zz >> 1]; \
+		zz >>= 1; \
+	} \
+	heap[zz] = tmp; \
+}
+
+
+/* 90 bytes, 0.3% of overall compress speed */
+#if CONFIG_BZIP2_FEATURE_SPEED >= 1
+
+/* macro works better than inline (gcc 4.2.1) */
+#define DOWNHEAP1(heap, weight, Heap) \
+{ \
+	int32_t zz, yy, tmp; \
+	zz = 1; \
+	tmp = heap[zz]; \
+	while (1) { \
+		yy = zz << 1; \
+		if (yy > nHeap) \
+			break; \
+		if (yy < nHeap \
+		 && weight[heap[yy+1]] < weight[heap[yy]]) \
+			yy++; \
+		if (weight[tmp] < weight[heap[yy]]) \
+			break; \
+		heap[zz] = heap[yy]; \
+		zz = yy; \
+	} \
+	heap[zz] = tmp; \
+}
+
+#else
+
+static
+void DOWNHEAP1(int32_t *heap, int32_t *weight, int32_t nHeap)
+{
+	int32_t zz, yy, tmp;
+	zz = 1;
+	tmp = heap[zz];
+	while (1) {
+		yy = zz << 1;
+		if (yy > nHeap)
+			break;
+		if (yy < nHeap
+		 && weight[heap[yy + 1]] < weight[heap[yy]])
+			yy++;
+		if (weight[tmp] < weight[heap[yy]])
+			break;
+		heap[zz] = heap[yy];
+		zz = yy;
+	}
+	heap[zz] = tmp;
+}
+
+#endif
+
+/*---------------------------------------------------*/
+static
+void BZ2_hbMakeCodeLengths(EState *s,
+		uint8_t *len,
+		int32_t *freq,
+		int32_t alphaSize,
+		int32_t maxLen)
+{
+	/*
+	 * Nodes and heap entries run from 1.  Entry 0
+	 * for both the heap and nodes is a sentinel.
+	 */
+	int32_t nNodes, nHeap, n1, n2, i, j, k;
+	Bool  tooLong;
+
+	/* bbox: moved to EState to save stack
+	int32_t heap  [BZ_MAX_ALPHA_SIZE + 2];
+	int32_t weight[BZ_MAX_ALPHA_SIZE * 2];
+	int32_t parent[BZ_MAX_ALPHA_SIZE * 2];
+	*/
+#define heap   (s->BZ2_hbMakeCodeLengths__heap)
+#define weight (s->BZ2_hbMakeCodeLengths__weight)
+#define parent (s->BZ2_hbMakeCodeLengths__parent)
+
+	for (i = 0; i < alphaSize; i++)
+		weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+
+	while (1) {
+		nNodes = alphaSize;
+		nHeap = 0;
+
+		heap[0] = 0;
+		weight[0] = 0;
+		parent[0] = -2;
+
+		for (i = 1; i <= alphaSize; i++) {
+			parent[i] = -1;
+			nHeap++;
+			heap[nHeap] = i;
+			UPHEAP(nHeap);
+		}
+
+		AssertH(nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001);
+
+		while (nHeap > 1) {
+			n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP1(heap, weight, nHeap);
+			n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP1(heap, weight, nHeap);
+			nNodes++;
+			parent[n1] = parent[n2] = nNodes;
+			weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]);
+			parent[nNodes] = -1;
+			nHeap++;
+			heap[nHeap] = nNodes;
+			UPHEAP(nHeap);
+		}
+
+		AssertH(nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002);
+
+		tooLong = False;
+		for (i = 1; i <= alphaSize; i++) {
+			j = 0;
+			k = i;
+			while (parent[k] >= 0) {
+				k = parent[k];
+				j++;
+			}
+			len[i-1] = j;
+			if (j > maxLen)
+				tooLong = True;
+		}
+
+		if (!tooLong)
+			break;
+
+		/* 17 Oct 04: keep-going condition for the following loop used
+		to be 'i < alphaSize', which missed the last element,
+		theoretically leading to the possibility of the compressor
+		looping.  However, this count-scaling step is only needed if
+		one of the generated Huffman code words is longer than
+		maxLen, which up to and including version 1.0.2 was 20 bits,
+		which is extremely unlikely.  In version 1.0.3 maxLen was
+		changed to 17 bits, which has minimal effect on compression
+		ratio, but does mean this scaling step is used from time to
+		time, enough to verify that it works.
+
+		This means that bzip2-1.0.3 and later will only produce
+		Huffman codes with a maximum length of 17 bits.  However, in
+		order to preserve backwards compatibility with bitstreams
+		produced by versions pre-1.0.3, the decompressor must still
+		handle lengths of up to 20. */
+
+		for (i = 1; i <= alphaSize; i++) {
+			j = weight[i] >> 8;
+			/* bbox: yes, it is a signed division.
+			 * don't replace with shift! */
+			j = 1 + (j / 2);
+			weight[i] = j << 8;
+		}
+	}
+#undef heap
+#undef weight
+#undef parent
+}
+
+
+/*---------------------------------------------------*/
+static
+void BZ2_hbAssignCodes(int32_t *code,
+		uint8_t *length,
+		int32_t minLen,
+		int32_t maxLen,
+		int32_t alphaSize)
+{
+	int32_t n, vec, i;
+
+	vec = 0;
+	for (n = minLen; n <= maxLen; n++) {
+		for (i = 0; i < alphaSize; i++) {
+			if (length[i] == n) {
+				code[i] = vec;
+				vec++;
+			};
+		}
+		vec <<= 1;
+	}
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end                                         huffman.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/busybox-1.19.3/archival/libarchive/data_align.c b/busybox-1.19.3/archival/libarchive/data_align.c
new file mode 100644
index 0000000..2e56fa8
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/data_align.c
@@ -0,0 +1,15 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC data_align(archive_handle_t *archive_handle, unsigned boundary)
+{
+	unsigned skip_amount = (boundary - (archive_handle->offset % boundary)) % boundary;
+
+	archive_handle->seek(archive_handle->src_fd, skip_amount);
+	archive_handle->offset += skip_amount;
+}
diff --git a/busybox-1.19.3/archival/libarchive/data_extract_all.c b/busybox-1.19.3/archival/libarchive/data_extract_all.c
new file mode 100644
index 0000000..1b25c8b
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/data_extract_all.c
@@ -0,0 +1,200 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
+{
+	file_header_t *file_header = archive_handle->file_header;
+	int dst_fd;
+	int res;
+
+#if ENABLE_FEATURE_TAR_SELINUX
+	char *sctx = archive_handle->tar__next_file_sctx;
+	if (!sctx)
+		sctx = archive_handle->tar__global_sctx;
+	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;
+	}
+#endif
+
+	if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) {
+		char *slash = strrchr(file_header->name, '/');
+		if (slash) {
+			*slash = '\0';
+			bb_make_directory(file_header->name, -1, FILEUTILS_RECUR);
+			*slash = '/';
+		}
+	}
+
+	if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
+		/* Remove the entry if it exists */
+		if (!S_ISDIR(file_header->mode)) {
+			/* Is it hardlink?
+			 * We encode hard links as regular files of size 0 with a symlink */
+			if (S_ISREG(file_header->mode)
+			 && file_header->link_target
+			 && file_header->size == 0
+			) {
+				/* Ugly special case:
+				 * tar cf t.tar hardlink1 hardlink2 hardlink1
+				 * results in this tarball structure:
+				 * hardlink1
+				 * hardlink2 -> hardlink1
+				 * hardlink1 -> hardlink1 <== !!!
+				 */
+				if (strcmp(file_header->link_target, file_header->name) == 0)
+					goto ret;
+			}
+			/* Proceed with deleting */
+			if (unlink(file_header->name) == -1
+			 && errno != ENOENT
+			) {
+				bb_perror_msg_and_die("can't remove old file %s",
+						file_header->name);
+			}
+		}
+	}
+	else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
+		/* Remove the existing entry if its older than the extracted entry */
+		struct stat existing_sb;
+		if (lstat(file_header->name, &existing_sb) == -1) {
+			if (errno != ENOENT) {
+				bb_perror_msg_and_die("can't stat old file");
+			}
+		}
+		else if (existing_sb.st_mtime >= file_header->mtime) {
+			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);
+			}
+			data_skip(archive_handle);
+			goto ret;
+		}
+		else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) {
+			bb_perror_msg_and_die("can't remove old file %s",
+					file_header->name);
+		}
+	}
+
+	/* Handle hard links separately
+	 * We encode hard links as regular files of size 0 with a symlink */
+	if (S_ISREG(file_header->mode)
+	 && file_header->link_target
+	 && file_header->size == 0
+	) {
+		/* hard link */
+		res = link(file_header->link_target, file_header->name);
+		if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) {
+			bb_perror_msg("can't create %slink "
+					"from %s to %s", "hard",
+					file_header->name,
+					file_header->link_target);
+		}
+		/* Hardlinks have no separate mode/ownership, skip chown/chmod */
+		goto ret;
+	}
+
+	/* Create the filesystem entry */
+	switch (file_header->mode & S_IFMT) {
+	case S_IFREG: {
+		/* Regular file */
+		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,
+			flags,
+			file_header->mode
+			);
+		bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size);
+		close(dst_fd);
+		break;
+	}
+	case S_IFDIR:
+		res = mkdir(file_header->name, file_header->mode);
+		if ((res == -1)
+		 && (errno != EISDIR) /* btw, Linux doesn't return this */
+		 && (errno != EEXIST)
+		 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
+		) {
+			bb_perror_msg("can't make dir %s", file_header->name);
+		}
+		break;
+	case S_IFLNK:
+		/* Symlink */
+//TODO: what if file_header->link_target == NULL (say, corrupted tarball?)
+		res = symlink(file_header->link_target, file_header->name);
+		if ((res == -1)
+		 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
+		) {
+			bb_perror_msg("can't create %slink "
+				"from %s to %s", "sym",
+				file_header->name,
+				file_header->link_target);
+		}
+		break;
+	case S_IFSOCK:
+	case S_IFBLK:
+	case S_IFCHR:
+	case S_IFIFO:
+		res = mknod(file_header->name, file_header->mode, file_header->device);
+		if ((res == -1)
+		 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)
+		) {
+			bb_perror_msg("can't create node %s", file_header->name);
+		}
+		break;
+	default:
+		bb_error_msg_and_die("unrecognized file type");
+	}
+
+	if (!S_ISLNK(file_header->mode)) {
+		if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) {
+			uid_t uid = file_header->uid;
+			gid_t gid = file_header->gid;
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+			if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) {
+				if (file_header->tar__uname) {
+//TODO: cache last name/id pair?
+					struct passwd *pwd = getpwnam(file_header->tar__uname);
+					if (pwd) uid = pwd->pw_uid;
+				}
+				if (file_header->tar__gname) {
+					struct group *grp = getgrnam(file_header->tar__gname);
+					if (grp) gid = grp->gr_gid;
+				}
+			}
+#endif
+			/* GNU tar 1.15.1 uses chown, not lchown */
+			chown(file_header->name, uid, gid);
+		}
+		/* uclibc has no lchmod, glibc is even stranger -
+		 * it has lchmod which seems to do nothing!
+		 * so we use chmod... */
+		if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) {
+			chmod(file_header->name, file_header->mode);
+		}
+		if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) {
+			struct timeval t[2];
+
+			t[1].tv_sec = t[0].tv_sec = file_header->mtime;
+			t[1].tv_usec = t[0].tv_usec = 0;
+			utimes(file_header->name, t);
+		}
+	}
+
+ ret: ;
+#if ENABLE_FEATURE_TAR_SELINUX
+	if (sctx) {
+		/* reset the context after creating an entry */
+		setfscreatecon(NULL);
+	}
+#endif
+}
diff --git a/busybox-1.19.3/archival/libarchive/data_extract_to_command.c b/busybox-1.19.3/archival/libarchive/data_extract_to_command.c
new file mode 100644
index 0000000..0e97704
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/data_extract_to_command.c
@@ -0,0 +1,138 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+enum {
+	//TAR_FILETYPE,
+	TAR_MODE,
+	TAR_FILENAME,
+	TAR_REALNAME,
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+	TAR_UNAME,
+	TAR_GNAME,
+#endif
+	TAR_SIZE,
+	TAR_UID,
+	TAR_GID,
+	TAR_MAX,
+};
+
+static const char *const tar_var[] = {
+	// "FILETYPE",
+	"MODE",
+	"FILENAME",
+	"REALNAME",
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+	"UNAME",
+	"GNAME",
+#endif
+	"SIZE",
+	"UID",
+	"GID",
+};
+
+static void xputenv(char *str)
+{
+	if (putenv(str))
+		bb_error_msg_and_die(bb_msg_memory_exhausted);
+}
+
+static void str2env(char *env[], int idx, const char *str)
+{
+	env[idx] = xasprintf("TAR_%s=%s", tar_var[idx], str);
+	xputenv(env[idx]);
+}
+
+static void dec2env(char *env[], int idx, unsigned long long val)
+{
+	env[idx] = xasprintf("TAR_%s=%llu", tar_var[idx], val);
+	xputenv(env[idx]);
+}
+
+static void oct2env(char *env[], int idx, unsigned long val)
+{
+	env[idx] = xasprintf("TAR_%s=%lo", tar_var[idx], val);
+	xputenv(env[idx]);
+}
+
+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;
+	if (!sctx)
+		sctx = archive_handle->tar__global_sctx;
+	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;
+	}
+#endif
+
+	if ((file_header->mode & S_IFMT) == S_IFREG) {
+		pid_t pid;
+		int p[2], status;
+		char *tar_env[TAR_MAX];
+
+		memset(tar_env, 0, sizeof(tar_env));
+
+		xpipe(p);
+		pid = BB_MMU ? xfork() : xvfork();
+		if (pid == 0) {
+			/* Child */
+			/* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */
+			oct2env(tar_env, TAR_MODE, file_header->mode);
+			str2env(tar_env, TAR_FILENAME, file_header->name);
+			str2env(tar_env, TAR_REALNAME, file_header->name);
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+			str2env(tar_env, TAR_UNAME, file_header->tar__uname);
+			str2env(tar_env, TAR_GNAME, file_header->tar__gname);
+#endif
+			dec2env(tar_env, TAR_SIZE, file_header->size);
+			dec2env(tar_env, TAR_UID, file_header->uid);
+			dec2env(tar_env, TAR_GID, file_header->gid);
+			close(p[1]);
+			xdup2(p[0], STDIN_FILENO);
+			signal(SIGPIPE, SIG_DFL);
+			execl(archive_handle->tar__to_command_shell,
+				archive_handle->tar__to_command_shell,
+				"-c",
+				archive_handle->tar__to_command,
+				NULL);
+			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)
+		 * so that we don't die if child don't read all the input: */
+		bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size);
+		close(p[1]);
+
+		if (safe_waitpid(pid, &status, 0) == -1)
+			bb_perror_msg_and_die("waitpid");
+		if (WIFEXITED(status) && WEXITSTATUS(status))
+			bb_error_msg_and_die("'%s' returned status %d",
+				archive_handle->tar__to_command, WEXITSTATUS(status));
+		if (WIFSIGNALED(status))
+			bb_error_msg_and_die("'%s' terminated on signal %d",
+				archive_handle->tar__to_command, WTERMSIG(status));
+
+		if (!BB_MMU) {
+			int i;
+			for (i = 0; i < TAR_MAX; i++) {
+				if (tar_env[i])
+					bb_unsetenv_and_free(tar_env[i]);
+			}
+		}
+	}
+
+#if 0 /* ENABLE_FEATURE_TAR_SELINUX */
+	if (sctx)
+		/* reset the context after creating an entry */
+		setfscreatecon(NULL);
+#endif
+}
diff --git a/busybox-1.19.3/archival/libarchive/data_extract_to_stdout.c b/busybox-1.19.3/archival/libarchive/data_extract_to_stdout.c
new file mode 100644
index 0000000..91f3f35
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/data_extract_to_stdout.c
@@ -0,0 +1,14 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC data_extract_to_stdout(archive_handle_t *archive_handle)
+{
+	bb_copyfd_exact_size(archive_handle->src_fd,
+			STDOUT_FILENO,
+			archive_handle->file_header->size);
+}
diff --git a/busybox-1.19.3/archival/libarchive/data_skip.c b/busybox-1.19.3/archival/libarchive/data_skip.c
new file mode 100644
index 0000000..a055424
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/data_skip.c
@@ -0,0 +1,12 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC data_skip(archive_handle_t *archive_handle)
+{
+	archive_handle->seek(archive_handle->src_fd, archive_handle->file_header->size);
+}
diff --git a/busybox-1.19.3/archival/libarchive/decompress_bunzip2.c b/busybox-1.19.3/archival/libarchive/decompress_bunzip2.c
new file mode 100644
index 0000000..4e46e68
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/decompress_bunzip2.c
@@ -0,0 +1,822 @@
+/* vi: set sw=4 ts=4: */
+/* Small bzip2 deflate implementation, by Rob Landley (rob@landley.net).
+
+   Based on bzip2 decompression code by Julian R Seward (jseward@acm.org),
+   which also acknowledges contributions by Mike Burrows, David Wheeler,
+   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 source tree.
+*/
+
+/*
+	Size and speed optimizations by Manuel Novoa III  (mjn3@codepoet.org).
+
+	More efficient reading of Huffman codes, a streamlined read_bunzip()
+	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_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
+	the woman I loved, Toni W. Hagan, who passed away Feb. 12, 2003.
+
+	Manuel
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+/* Constants for Huffman coding */
+#define MAX_GROUPS          6
+#define GROUP_SIZE          50      /* 64 would have been more efficient */
+#define MAX_HUFCODE_BITS    20      /* Longest Huffman code allowed */
+#define MAX_SYMBOLS         258     /* 256 literals + RUNA + RUNB */
+#define SYMBOL_RUNA         0
+#define SYMBOL_RUNB         1
+
+/* 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)
+
+/* Other housekeeping constants */
+#define IOBUF_SIZE          4096
+
+/* This is what we know about each Huffman coding group */
+struct group_data {
+	/* We have an extra slot at the end of limit[] for a sentinel value. */
+	int limit[MAX_HUFCODE_BITS+1], base[MAX_HUFCODE_BITS], permute[MAX_SYMBOLS];
+	int minLen, maxLen;
+};
+
+/* Structure holding all the housekeeping data, including IO buffers and
+ * memory that persists between calls to bunzip
+ * Found the most used member:
+ *  cat this_file.c | sed -e 's/"/ /g' -e "s/'/ /g" | xargs -n1 \
+ *  | grep 'bd->' | sed 's/^.*bd->/bd->/' | sort | $PAGER
+ * and moved it (inbufBitCount) to offset 0.
+ */
+struct bunzip_data {
+	/* I/O tracking data (file handles, buffers, positions, etc.) */
+	unsigned inbufBitCount, inbufBits;
+	int in_fd, out_fd, inbufCount, inbufPos /*, outbufPos*/;
+	uint8_t *inbuf /*,*outbuf*/;
+
+	/* State for interrupting output loop */
+	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) */
+	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];
+	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 (bit_count < bits_wanted) {
+
+		/* If we need to read more data from file into byte buffer, do so */
+		if (bd->inbufPos == bd->inbufCount) {
+			/* if "no input fd" case: in_fd == -1, read fails, we jump */
+			bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE);
+			if (bd->inbufCount <= 0)
+				longjmp(bd->jmpbuf, RETVAL_UNEXPECTED_INPUT_EOF);
+			bd->inbufPos = 0;
+		}
+
+		/* Avoid 32-bit overflow (dump bit buffer to top of output) */
+		if (bit_count >= 24) {
+			bits = bd->inbufBits & ((1 << bit_count) - 1);
+			bits_wanted -= bit_count;
+			bits <<= bits_wanted;
+			bit_count = 0;
+		}
+
+		/* Grab next 8 bits of input from buffer. */
+		bd->inbufBits = (bd->inbufBits << 8) | bd->inbuf[bd->inbufPos++];
+		bit_count += 8;
+	}
+
+	/* Calculate result */
+	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. */
+static int get_next_block(bunzip_data *bd)
+{
+	struct group_data *hufGroup;
+	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) */
+	i = get_bits(bd, 24);
+	j = get_bits(bd, 24);
+	bd->headerCRC = get_bits(bd, 32);
+	if ((i == 0x177245) && (j == 0x385090)) return RETVAL_LAST_BLOCK;
+	if ((i != 0x314159) || (j != 0x265359)) return RETVAL_NOT_BZIP_DATA;
+
+	/* We can add support for blockRandomised if anybody complains.  There was
+	   some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
+	   it didn't actually work. */
+	if (get_bits(bd, 1)) return RETVAL_OBSOLETE_INPUT;
+	origPtr = get_bits(bd, 24);
+	if ((int)origPtr > dbufSize) return RETVAL_DATA_ERROR;
+
+	/* mapping table: if some byte values are never used (encoding things
+	   like ascii text), the compression code removes the gaps to have fewer
+	   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. */
+	symTotal = 0;
+	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);
+	if (groupCount < 2 || groupCount > MAX_GROUPS)
+		return RETVAL_DATA_ERROR;
+
+	/* nSelectors: Every GROUP_SIZE many symbols we select a new Huffman coding
+	   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 < nSelectors; i++) {
+		uint8_t tmp_byte;
+		/* Get next value */
+		int n = 0;
+		while (get_bits(bd, 1)) {
+			if (n >= groupCount) return RETVAL_DATA_ERROR;
+			n++;
+		}
+		/* Decode MTF to get the next selector */
+		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++) {
+		uint8_t length[MAX_SYMBOLS];
+		/* 8 bits is ALMOST enough for temp[], see below */
+		unsigned temp[MAX_HUFCODE_BITS+1];
+		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 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.) */
+		len_m1 = get_bits(bd, 5) - 1;
+		for (i = 0; i < symCount; i++) {
+			for (;;) {
+				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. */
+				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 */
+				len_m1 += (((two_bits+1) & 2) - 1);
+			}
+
+			/* Correct for the initial -1, to get the final symbol length */
+			length[i] = len_m1 + 1;
+		}
+
+		/* Find largest and smallest lengths in this group */
+		minLen = maxLen = length[0];
+		for (i = 1; i < symCount; i++) {
+			if (length[i] > maxLen) maxLen = length[i];
+			else if (length[i] < minLen) minLen = length[i];
+		}
+
+		/* Calculate permute[], base[], and limit[] tables from length[].
+		 *
+		 * permute[] is the lookup table for converting Huffman coded symbols
+		 * into decoded symbols.  base[] is the amount to subtract from the
+		 * value of a Huffman symbol of a given length when using permute[].
+		 *
+		 * limit[] indicates the largest numerical value a symbol with a given
+		 * number of bits can have.  This is how the Huffman codes can vary in
+		 * length: each code with a value>limit[length] needs another bit.
+		 */
+		hufGroup = bd->groups + j;
+		hufGroup->minLen = minLen;
+		hufGroup->maxLen = maxLen;
+
+		/* 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). */
+		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 (k = 0; k < symCount; k++)
+				if (length[k] == i)
+					hufGroup->permute[pp++] = k;
+		}
+
+		/* Count symbols coded for at each bit length */
+		/* NB: in pathological cases, temp[8] can end ip being 256.
+		 * That's why uint8_t is too small for temp[]. */
+		for (i = 0; i < symCount; i++) temp[length[i]]++;
+
+		/* Calculate limit[] (the largest symbol-coding value at each bit
+		 * length, which is (previous limit<<1)+symbols at this level), and
+		 * 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;) {
+			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
+			   be set to anything.  (They're noise from future symbols.)  At
+			   each level we're really only interested in the first few bits,
+			   so here we set all the trailing to-be-ignored bits to 1 so they
+			   don't affect the value>limit[length] comparison. */
+			limit[i] = (pp << (maxLen - i)) - 1;
+			pp <<= 1;
+			t += temp_i;
+			base[++i] = pp - t;
+		}
+		limit[maxLen] = pp + temp[maxLen] - 1;
+		limit[maxLen+1] = INT_MAX; /* Sentinel value for reading next sym. */
+		base[minLen] = 0;
+	}
+
+	/* We've finished reading and digesting the block header.  Now read this
+	   block's Huffman coded symbols from the file and undo the Huffman coding
+	   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, but slower */
+	for (i = 0; i < 256; 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;
+		if (selector >= nSelectors) return RETVAL_DATA_ERROR;
+		hufGroup = bd->groups + selectors[selector++];
+		base = hufGroup->base - 1;
+		limit = hufGroup->limit - 1;
+
+ 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 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.
+		 */
+		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 (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) */
+		nextSym = (nextSym >> j) - base[i];
+		if ((unsigned)nextSym >= MAX_SYMBOLS)
+			return RETVAL_DATA_ERROR;
+		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,
+		   check if nextSym indicates a repeated run, and if so loop collecting
+		   how many times to repeat the last literal. */
+		if ((unsigned)nextSym <= SYMBOL_RUNB) { /* RUNA or RUNB */
+
+			/* If this is the start of a new run, zero out counter */
+			if (runPos == 0) {
+				runPos = 1;
+				runCnt = 0;
+			}
+
+			/* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
+			   each bit position, add 1 or 2 instead.  For example,
+			   1011 is 1<<0 + 1<<1 + 2<<2.  1010 is 2<<0 + 2<<1 + 1<<2.
+			   You can make any bit pattern that way using 1 less symbol than
+			   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. */
+			runCnt += (runPos << nextSym); /* +runPos if RUNA; +2*runPos if RUNB */
+			if (runPos < dbufSize) runPos <<= 1;
+			goto end_of_huffman_loop;
+		}
+
+		/* When we hit the first non-run symbol after a run, we now know
+		   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 != 0) {
+			uint8_t tmp_byte;
+			if (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;
+		}
+
+		/* Is this the terminating symbol? */
+		if (nextSym > symTotal) break;
+
+		/* At this point, nextSym indicates a new literal character.  Subtract
+		   one to get the position in the MTF array at which this literal is
+		   currently to be found.  (Note that the result can't be -1 or 0,
+		   because 0 and 1 are RUNA and RUNB.  But another instance of the
+		   first symbol in the mtf array, position 0, would have been handled
+		   as part of a run above.  Therefore 1 unused mtf position minus
+		   2 non-literal nextSym values equals -1.) */
+		if (dbufCount >= dbufSize) return RETVAL_DATA_ERROR;
+		i = nextSym - 1;
+		uc = mtfSymbol[i];
+
+		/* Adjust the MTF array.  Since we typically expect to move only a
+		 * small number of symbols, and are bound by 256 in any case, using
+		 * memmove here would typically be bigger and slower due to function
+		 * call overhead and other assorted setup costs. */
+		do {
+			mtfSymbol[i] = mtfSymbol[i-1];
+		} while (--i);
+		mtfSymbol[0] = uc;
+		uc = symToByte[uc];
+
+		/* We have our literal byte.  Save it into dbuf. */
+		byteCount[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 >= 0) goto continue_this_group;
+	}
+
+	/* At this point, we've read all the Huffman-coded symbols (and repeated
+	   runs) for this block from the input stream, and decoded them into the
+	   intermediate buffer.  There are dbufCount many decoded bytes in dbuf[].
+	   Now undo the Burrows-Wheeler transform on dbuf.
+	   See http://dogma.net/markn/articles/bwt/bwt.htm
+	 */
+
+	/* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
+	j = 0;
+	for (i = 0; i < 256; i++) {
+		int tmp_count = j + byteCount[i];
+		byteCount[i] = j;
+		j = tmp_count;
+	}
+
+	/* Figure out what order dbuf would be in if we sorted it. */
+	for (i = 0; i < dbufCount; i++) {
+		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;
+		tmp = dbuf[origPtr];
+		bd->writeCurrent = (uint8_t)tmp;
+		bd->writePos = (tmp >> 8);
+		bd->writeRunCountdown = 5;
+	}
+	bd->writeCount = dbufCount;
+
+	return RETVAL_OK;
+}
+
+/* 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 uint32_t *dbuf;
+	int pos, current, previous;
+	uint32_t CRC;
+
+	/* If we already have error/end indicator, return it */
+	if (bd->writeCount < 0)
+		return bd->writeCount;
+
+	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, 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++ = current;
+			CRC = (CRC << 8) ^ bd->crc32Table[(CRC >> 24) ^ current];
+
+			/* Loop now if we're outputting multiple copies of this byte */
+			if (bd->writeCopies) {
+				/* 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 < 0)
+				break; /* input block is fully consumed, need next one */
+
+			/* Follow sequence vector to undo Burrows-Wheeler transform */
+			previous = current;
+			pos = dbuf[pos];
+			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 != 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;
+				bd->writeRunCountdown = 5;
+
+				/* Sometimes there are just 3 bytes (run length 0) */
+				if (!bd->writeCopies) goto decode_next_byte;
+
+				/* Subtract the 1 copy we'd output anyway to get extras */
+				--bd->writeCopies;
+			}
+		} /* for(;;) */
+
+		/* 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 (CRC != bd->headerCRC) {
+			bd->totalCRC = bd->headerCRC + 1;
+			return RETVAL_LAST_BLOCK;
+		}
+	}
+
+	/* Refill the intermediate buffer by Huffman-decoding next block of input */
+	{
+		int r = get_next_block(bd);
+		if (r) { /* error/end */
+			bd->writeCount = r;
+			return (r != RETVAL_LAST_BLOCK) ? r : len;
+		}
+	}
+
+	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
+   a complete bunzip file (len bytes long).  If in_fd!=-1, inbuf and len are
+   ignored, and data is read from file handle into temporary buffer. */
+
+/* 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 void *inbuf, int len)
+{
+	bunzip_data *bd;
+	unsigned i;
+	enum {
+		BZh0 = ('B' << 24) + ('Z' << 16) + ('h' << 8) + '0',
+		h0 = ('h' << 8) + '0',
+	};
+
+	/* Figure out how much data to allocate */
+	i = sizeof(bunzip_data);
+	if (in_fd != -1) i += IOBUF_SIZE;
+
+	/* Allocate bunzip_data.  Most fields initialize to zero. */
+	bd = *bdp = xzalloc(i);
+
+	/* Setup input buffer */
+	bd->in_fd = in_fd;
+	if (-1 == in_fd) {
+		/* in this case, bd->inbuf is read-only */
+		bd->inbuf = (void*)inbuf; /* cast away const-ness */
+	} 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);
+
+	/* Setup for I/O error handling via longjmp */
+	i = setjmp(bd->jmpbuf);
+	if (i) return i;
+
+	/* Ensure that file starts with "BZh['1'-'9']." */
+	/* Update: now caller verifies 1st two bytes, makes .gz/.bz2
+	 * integration easier */
+	/* was: */
+	/* i = get_bits(bd, 32); */
+	/* if ((unsigned)(i - BZh0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA; */
+	i = get_bits(bd, 16);
+	if ((unsigned)(i - h0 - 1) >= 9) return RETVAL_NOT_BZIP_DATA;
+
+	/* Fourth byte (ascii '1'-'9') indicates block size in units of 100k of
+	   uncompressed data.  Allocate intermediate buffer for block. */
+	/* bd->dbufSize = 100000 * (i - BZh0); */
+	bd->dbufSize = 100000 * (i - h0);
+
+	/* Cannot use xmalloc - may leak bd in NOFORK case! */
+	bd->dbuf = malloc_or_warn(bd->dbufSize * sizeof(bd->dbuf[0]));
+	if (!bd->dbuf) {
+		free(bd);
+		xfunc_die();
+	}
+	return RETVAL_OK;
+}
+
+void FAST_FUNC dealloc_bunzip(bunzip_data *bd)
+{
+	free(bd->dbuf);
+	free(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)
+{
+	IF_DESKTOP(long long total_written = 0;)
+	bunzip_data *bd;
+	char *outbuf;
+	int i;
+	unsigned len;
+
+	outbuf = xmalloc(IOBUF_SIZE);
+	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 (i != RETVAL_LAST_BLOCK) {
+			bb_error_msg("bunzip error %d", i);
+			break;
+		}
+		if (bd->headerCRC != bd->totalCRC) {
+			bb_error_msg("CRC error");
+			break;
+		}
+
+		/* 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[] = {
+	NULL, "Bad file checksum", "Not bzip data",
+	"Unexpected input EOF", "Unexpected output EOF", "Data error",
+	"Out of memory", "Obsolete (pre 0.9.5) bzip format not supported"
+};
+
+/* 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);
+	if (i < 0)
+		fprintf(stderr, "%s\n", bunzip_errors[-i]);
+	else if (read(STDIN_FILENO, &c, 1))
+		fprintf(stderr, "Trailing garbage ignored\n");
+	return -i;
+}
+#endif
diff --git a/busybox-1.19.3/archival/libarchive/decompress_uncompress.c b/busybox-1.19.3/archival/libarchive/decompress_uncompress.c
new file mode 100644
index 0000000..d1061a2
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/decompress_uncompress.c
@@ -0,0 +1,310 @@
+/* vi: set sw=4 ts=4: */
+/* uncompress for busybox -- (c) 2002 Robert Griebl
+ *
+ * based on the original compress42.c source
+ * (see disclaimer below)
+ */
+
+/* (N)compress42.c - File compression ala IEEE Computer, Mar 1992.
+ *
+ * Authors:
+ *   Spencer W. Thomas   (decvax!harpo!utah-cs!utah-gr!thomas)
+ *   Jim McKie           (decvax!mcvax!jim)
+ *   Steve Davies        (decvax!vax135!petsd!peora!srd)
+ *   Ken Turkowski       (decvax!decwrl!turtlevax!ken)
+ *   James A. Woods      (decvax!ihnp4!ames!jaw)
+ *   Joe Orost           (decvax!vax135!petsd!joe)
+ *   Dave Mack           (csu@alembic.acs.com)
+ *   Peter Jannesen, Network Communication Systems
+ *                       (peter@ncs.nl)
+ *
+ * marc@suse.de : a small security fix for a buffer overflow
+ *
+ * [... History snipped ...]
+ *
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+
+/* Default input buffer size */
+#define IBUFSIZ 2048
+
+/* Default output buffer size */
+#define OBUFSIZ 2048
+
+/* Defines for third byte of header */
+#define BIT_MASK        0x1f    /* Mask for 'number of compresssion bits'       */
+                                /* Masks 0x20 and 0x40 are free.                */
+                                /* I think 0x20 should mean that there is       */
+                                /* a fourth header byte (for expansion).        */
+#define BLOCK_MODE      0x80    /* Block compression if table is full and       */
+                                /* compression rate is dropping flush tables    */
+                                /* the next two codes should not be changed lightly, as they must not   */
+                                /* lie within the contiguous general code space.                        */
+#define FIRST   257     /* first free entry */
+#define CLEAR   256     /* table clear output code */
+
+#define INIT_BITS 9     /* initial number of bits/code */
+
+
+/* machine variants which require cc -Dmachine:  pdp11, z8000, DOS */
+#define HBITS      17   /* 50% occupancy */
+#define HSIZE      (1<<HBITS)
+#define HMASK      (HSIZE-1)    /* unused */
+#define HPRIME     9941         /* unused */
+#define BITS       16
+#define BITS_STR   "16"
+#undef  MAXSEG_64K              /* unused */
+#define MAXCODE(n) (1L << (n))
+
+#define htabof(i)               htab[i]
+#define codetabof(i)            codetab[i]
+#define tab_prefixof(i)         codetabof(i)
+#define tab_suffixof(i)         ((unsigned char *)(htab))[i]
+#define de_stack                ((unsigned char *)&(htab[HSIZE-1]))
+#define clear_tab_prefixof()    memset(codetab, 0, 256)
+
+/*
+ * Decompress stdin to stdout.  This routine adapts to the codes in the
+ * file building the "string" table on-the-fly; requiring no table to
+ * be stored in the compressed file.
+ */
+
+IF_DESKTOP(long long) int FAST_FUNC
+unpack_Z_stream(int fd_in, int fd_out)
+{
+	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;
+	int inbits;
+	int posbits;
+	int outpos;
+	int insize;
+	int bitmask;
+	long free_ent;
+	long maxcode;
+	long maxmaxcode;
+	int n_bits;
+	int rsize = 0;
+	unsigned char *inbuf; /* were eating insane amounts of stack - */
+	unsigned char *outbuf; /* bad for some embedded targets */
+	unsigned char *htab;
+	unsigned short *codetab;
+
+	/* Hmm, these were statics - why?! */
+	/* user settable max # bits/code */
+	int maxbits; /* = BITS; */
+	/* block compress mode -C compatible with 2.0 */
+	int block_mode; /* = BLOCK_MODE; */
+
+	inbuf = xzalloc(IBUFSIZ + 64);
+	outbuf = xzalloc(OBUFSIZ + 2048);
+	htab = xzalloc(HSIZE);  /* wsn'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) {
+		bb_error_msg("short read");
+		goto err;
+	}
+
+	maxbits = inbuf[0] & BIT_MASK;
+	block_mode = inbuf[0] & BLOCK_MODE;
+	maxmaxcode = MAXCODE(maxbits);
+
+	if (maxbits > BITS) {
+		bb_error_msg("compressed with %d bits, can only handle "
+				BITS_STR" bits", maxbits);
+		goto err;
+	}
+
+	n_bits = INIT_BITS;
+	maxcode = MAXCODE(INIT_BITS) - 1;
+	bitmask = (1 << INIT_BITS) - 1;
+	oldcode = -1;
+	finchar = 0;
+	outpos = 0;
+	posbits = 0 << 3;
+
+	free_ent = ((block_mode) ? FIRST : 256);
+
+	/* 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;
+	}
+
+	do {
+ resetbuf:
+		{
+			int i;
+			int e;
+			int o;
+
+			o = posbits >> 3;
+			e = insize - o;
+
+			for (i = 0; i < e; ++i)
+				inbuf[i] = inbuf[i + o];
+
+			insize = e;
+			posbits = 0;
+		}
+
+		if (insize < (int) (IBUFSIZ + 64) - IBUFSIZ) {
+			rsize = safe_read(fd_in, inbuf + insize, IBUFSIZ);
+			if (rsize < 0)
+				bb_error_msg_and_die(bb_msg_read_error);
+			insize += rsize;
+		}
+
+		inbits = ((rsize > 0) ? (insize - insize % n_bits) << 3 :
+				  (insize << 3) - (n_bits - 1));
+
+		while (inbits > posbits) {
+			if (free_ent > maxcode) {
+				posbits =
+					((posbits - 1) +
+					 ((n_bits << 3) -
+					  (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
+				++n_bits;
+				if (n_bits == maxbits) {
+					maxcode = maxmaxcode;
+				} else {
+					maxcode = MAXCODE(n_bits) - 1;
+				}
+				bitmask = (1 << n_bits) - 1;
+				goto resetbuf;
+			}
+			{
+				unsigned char *p = &inbuf[posbits >> 3];
+
+				code = ((((long) (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;
+				continue;
+			}
+
+			if (code == CLEAR && block_mode) {
+				clear_tab_prefixof();
+				free_ent = FIRST - 1;
+				posbits =
+					((posbits - 1) +
+					 ((n_bits << 3) -
+					  (posbits - 1 + (n_bits << 3)) % (n_bits << 3)));
+				n_bits = INIT_BITS;
+				maxcode = MAXCODE(INIT_BITS) - 1;
+				bitmask = (1 << INIT_BITS) - 1;
+				goto resetbuf;
+			}
+
+			incode = code;
+			stackp = de_stack;
+
+			/* 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));
+					bb_error_msg("corrupted data");
+					goto err;
+				}
+
+				*--stackp = (unsigned char) finchar;
+				code = oldcode;
+			}
+
+			/* Generate output characters in reverse order */
+			while ((long) code >= (long) 256) {
+				if (stackp <= &htabof(0))
+					bb_error_msg_and_die("corrupted data");
+				*--stackp = tab_suffixof(code);
+				code = tab_prefixof(code);
+			}
+
+			finchar = tab_suffixof(code);
+			*--stackp = (unsigned char) finchar;
+
+			/* And put them out in forward order */
+			{
+				int i;
+
+				i = de_stack - stackp;
+				if (outpos + i >= OBUFSIZ) {
+					do {
+						if (i > OBUFSIZ - outpos) {
+							i = OBUFSIZ - outpos;
+						}
+
+						if (i > 0) {
+							memcpy(outbuf + outpos, stackp, i);
+							outpos += i;
+						}
+
+						if (outpos >= OBUFSIZ) {
+							xwrite(fd_out, outbuf, outpos);
+							IF_DESKTOP(total_written += outpos;)
+							outpos = 0;
+						}
+						stackp += i;
+						i = de_stack - stackp;
+					} while (i > 0);
+				} else {
+					memcpy(outbuf + outpos, stackp, i);
+					outpos += i;
+				}
+			}
+
+			/* 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;
+			}
+
+			/* Remember previous code.  */
+			oldcode = incode;
+		}
+
+	} while (rsize > 0);
+
+	if (outpos > 0) {
+		xwrite(fd_out, outbuf, outpos);
+		IF_DESKTOP(total_written += outpos;)
+	}
+
+	retval = IF_DESKTOP(total_written) + 0;
+ err:
+	free(inbuf);
+	free(outbuf);
+	free(htab);
+	free(codetab);
+	return retval;
+}
diff --git a/busybox-1.19.3/archival/libarchive/decompress_unlzma.c b/busybox-1.19.3/archival/libarchive/decompress_unlzma.c
new file mode 100644
index 0000000..a047143
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/decompress_unlzma.c
@@ -0,0 +1,465 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Small lzma deflate implementation.
+ * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * 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 source tree.
+ */
+#include "libbb.h"
+#include "archive.h"
+
+#if ENABLE_FEATURE_LZMA_FAST
+#  define speed_inline ALWAYS_INLINE
+#  define size_inline
+#else
+#  define speed_inline
+#  define size_inline ALWAYS_INLINE
+#endif
+
+
+typedef struct {
+	int fd;
+	uint8_t *ptr;
+
+/* Was keeping rc on stack in unlzma and separately allocating buffer,
+ * but with "buffer 'attached to' allocated rc" code is smaller: */
+	/* uint8_t *buffer; */
+#define RC_BUFFER ((uint8_t*)(rc+1))
+
+	uint8_t *buffer_end;
+
+/* Had provisions for variable buffer, but we don't need it here */
+	/* int buffer_size; */
+#define RC_BUFFER_SIZE 0x10000
+
+	uint32_t code;
+	uint32_t range;
+	uint32_t bound;
+} rc_t;
+
+#define RC_TOP_BITS 24
+#define RC_MOVE_BITS 5
+#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)
+{
+	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;
+}
+
+/* Called twice, but one callsite is in speed_inline'd rc_is_bit_1() */
+static void rc_do_normalize(rc_t *rc)
+{
+	if (rc->ptr >= rc->buffer_end)
+		rc_read(rc);
+	rc->range <<= 8;
+	rc->code = (rc->code << 8) | *rc->ptr++;
+}
+
+/* Called once */
+static ALWAYS_INLINE rc_t* rc_init(int fd) /*, int buffer_size) */
+{
+	int i;
+	rc_t *rc;
+
+	rc = xzalloc(sizeof(*rc) + RC_BUFFER_SIZE);
+
+	rc->fd = fd;
+	/* 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;
+	return rc;
+}
+
+/* Called once  */
+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)
+{
+	rc_normalize(rc);
+	rc->bound = *p * (rc->range >> RC_MODEL_TOTAL_BITS);
+	if (rc->code < rc->bound) {
+		rc->range = rc->bound;
+		*p += ((1 << RC_MODEL_TOTAL_BITS) - *p) >> RC_MOVE_BITS;
+		return 0;
+	}
+	rc->range -= rc->bound;
+	rc->code -= rc->bound;
+	*p -= *p >> RC_MOVE_BITS;
+	return 1;
+}
+
+/* Called 4 times in unlzma loop */
+static speed_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;
+	return ret;
+}
+
+/* Called once */
+static ALWAYS_INLINE int rc_direct_bit(rc_t *rc)
+{
+	rc_normalize(rc);
+	rc->range >>= 1;
+	if (rc->code >= rc->range) {
+		rc->code -= rc->range;
+		return 1;
+	}
+	return 0;
+}
+
+/* Called twice */
+static speed_inline void
+rc_bit_tree_decode(rc_t *rc, uint16_t *p, int num_levels, int *symbol)
+{
+	int i = num_levels;
+
+	*symbol = 1;
+	while (i--)
+		rc_get_bit(rc, p + *symbol, symbol);
+	*symbol -= 1 << num_levels;
+}
+
+
+typedef struct {
+	uint8_t pos;
+	uint32_t dict_size;
+	uint64_t dst_size;
+} PACKED lzma_header_t;
+
+
+/* #defines will force compiler to compute/optimize each one with each usage.
+ * Have heart and use enum instead. */
+enum {
+	LZMA_BASE_SIZE = 1846,
+	LZMA_LIT_SIZE  = 768,
+
+	LZMA_NUM_POS_BITS_MAX = 4,
+
+	LZMA_LEN_NUM_LOW_BITS  = 3,
+	LZMA_LEN_NUM_MID_BITS  = 3,
+	LZMA_LEN_NUM_HIGH_BITS = 8,
+
+	LZMA_LEN_CHOICE     = 0,
+	LZMA_LEN_CHOICE_2   = (LZMA_LEN_CHOICE + 1),
+	LZMA_LEN_LOW        = (LZMA_LEN_CHOICE_2 + 1),
+	LZMA_LEN_MID        = (LZMA_LEN_LOW \
+	                      + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_LOW_BITS))),
+	LZMA_LEN_HIGH       = (LZMA_LEN_MID \
+	                      + (1 << (LZMA_NUM_POS_BITS_MAX + LZMA_LEN_NUM_MID_BITS))),
+	LZMA_NUM_LEN_PROBS  = (LZMA_LEN_HIGH + (1 << LZMA_LEN_NUM_HIGH_BITS)),
+
+	LZMA_NUM_STATES     = 12,
+	LZMA_NUM_LIT_STATES = 7,
+
+	LZMA_START_POS_MODEL_INDEX = 4,
+	LZMA_END_POS_MODEL_INDEX   = 14,
+	LZMA_NUM_FULL_DISTANCES    = (1 << (LZMA_END_POS_MODEL_INDEX >> 1)),
+
+	LZMA_NUM_POS_SLOT_BITS = 6,
+	LZMA_NUM_LEN_TO_POS_STATES = 4,
+
+	LZMA_NUM_ALIGN_BITS = 4,
+
+	LZMA_MATCH_MIN_LEN  = 2,
+
+	LZMA_IS_MATCH       = 0,
+	LZMA_IS_REP         = (LZMA_IS_MATCH + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)),
+	LZMA_IS_REP_G0      = (LZMA_IS_REP + LZMA_NUM_STATES),
+	LZMA_IS_REP_G1      = (LZMA_IS_REP_G0 + LZMA_NUM_STATES),
+	LZMA_IS_REP_G2      = (LZMA_IS_REP_G1 + LZMA_NUM_STATES),
+	LZMA_IS_REP_0_LONG  = (LZMA_IS_REP_G2 + LZMA_NUM_STATES),
+	LZMA_POS_SLOT       = (LZMA_IS_REP_0_LONG \
+	                      + (LZMA_NUM_STATES << LZMA_NUM_POS_BITS_MAX)),
+	LZMA_SPEC_POS       = (LZMA_POS_SLOT \
+	                      + (LZMA_NUM_LEN_TO_POS_STATES << LZMA_NUM_POS_SLOT_BITS)),
+	LZMA_ALIGN          = (LZMA_SPEC_POS \
+	                      + LZMA_NUM_FULL_DISTANCES - LZMA_END_POS_MODEL_INDEX),
+	LZMA_LEN_CODER      = (LZMA_ALIGN + (1 << LZMA_NUM_ALIGN_BITS)),
+	LZMA_REP_LEN_CODER  = (LZMA_LEN_CODER + LZMA_NUM_LEN_PROBS),
+	LZMA_LITERAL        = (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS),
+};
+
+
+IF_DESKTOP(long long) int FAST_FUNC
+unpack_lzma_stream(int src_fd, int dst_fd)
+{
+	IF_DESKTOP(long long total_written = 0;)
+	lzma_header_t header;
+	int lc, pb, lp;
+	uint32_t pos_state_mask;
+	uint32_t literal_pos_mask;
+	uint16_t *p;
+	int num_bits;
+	int num_probs;
+	rc_t *rc;
+	int i;
+	uint8_t *buffer;
+	uint8_t previous_byte = 0;
+	size_t buffer_pos = 0, global_pos = 0;
+	int len = 0;
+	int state = 0;
+	uint32_t rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
+
+	if (full_read(src_fd, &header, sizeof(header)) != sizeof(header)
+	 || header.pos >= (9 * 5 * 5)
+	) {
+		bb_error_msg("bad lzma header");
+		return -1;
+	}
+
+	i = header.pos / 9;
+	lc = header.pos % 9;
+	pb = i / 5;
+	lp = i % 5;
+	pos_state_mask = (1 << pb) - 1;
+	literal_pos_mask = (1 << lp) - 1;
+
+	header.dict_size = SWAP_LE32(header.dict_size);
+	header.dst_size = SWAP_LE64(header.dst_size);
+
+	if (header.dict_size == 0)
+		header.dict_size++;
+
+	buffer = xmalloc(MIN(header.dst_size, header.dict_size));
+
+	num_probs = LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp));
+	p = xmalloc(num_probs * sizeof(*p));
+	num_probs += LZMA_LITERAL - LZMA_BASE_SIZE;
+	for (i = 0; i < num_probs; i++)
+		p[i] = (1 << RC_MODEL_TOTAL_BITS) >> 1;
+
+	rc = rc_init(src_fd); /*, RC_BUFFER_SIZE); */
+
+	while (global_pos + buffer_pos < header.dst_size) {
+		int pos_state = (buffer_pos + global_pos) & pos_state_mask;
+		uint16_t *prob = p + LZMA_IS_MATCH + (state << LZMA_NUM_POS_BITS_MAX) + pos_state;
+
+		if (!rc_is_bit_1(rc, prob)) {
+			static const char next_state[LZMA_NUM_STATES] =
+				{ 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 };
+			int mi = 1;
+
+			prob = (p + LZMA_LITERAL
+			        + (LZMA_LIT_SIZE * ((((buffer_pos + global_pos) & literal_pos_mask) << lc)
+			                            + (previous_byte >> (8 - lc))
+			                           )
+			          )
+			);
+
+			if (state >= LZMA_NUM_LIT_STATES) {
+				int match_byte;
+				uint32_t pos = buffer_pos - rep0;
+
+				while (pos >= header.dict_size)
+					pos += header.dict_size;
+				match_byte = buffer[pos];
+				do {
+					int bit;
+
+					match_byte <<= 1;
+					bit = match_byte & 0x100;
+					bit ^= (rc_get_bit(rc, prob + 0x100 + bit + mi, &mi) << 8); /* 0x100 or 0 */
+					if (bit)
+						break;
+				} while (mi < 0x100);
+			}
+			while (mi < 0x100) {
+				rc_get_bit(rc, prob + mi, &mi);
+			}
+
+			state = next_state[state];
+
+			previous_byte = (uint8_t) mi;
+#if ENABLE_FEATURE_LZMA_FAST
+ one_byte1:
+			buffer[buffer_pos++] = previous_byte;
+			if (buffer_pos == header.dict_size) {
+				buffer_pos = 0;
+				global_pos += header.dict_size;
+				if (full_write(dst_fd, buffer, header.dict_size) != (ssize_t)header.dict_size)
+					goto bad;
+				IF_DESKTOP(total_written += header.dict_size;)
+			}
+#else
+			len = 1;
+			goto one_byte2;
+#endif
+		} else {
+			int offset;
+			uint16_t *prob2;
+#define prob_len prob2
+
+			prob2 = p + LZMA_IS_REP + state;
+			if (!rc_is_bit_1(rc, prob2)) {
+				rep3 = rep2;
+				rep2 = rep1;
+				rep1 = rep0;
+				state = state < LZMA_NUM_LIT_STATES ? 0 : 3;
+				prob2 = p + LZMA_LEN_CODER;
+			} else {
+				prob2 += LZMA_IS_REP_G0 - LZMA_IS_REP;
+				if (!rc_is_bit_1(rc, prob2)) {
+					prob2 = (p + LZMA_IS_REP_0_LONG
+					        + (state << LZMA_NUM_POS_BITS_MAX)
+					        + pos_state
+					);
+					if (!rc_is_bit_1(rc, prob2)) {
+#if ENABLE_FEATURE_LZMA_FAST
+						uint32_t pos = buffer_pos - rep0;
+						state = state < LZMA_NUM_LIT_STATES ? 9 : 11;
+						while (pos >= header.dict_size)
+							pos += header.dict_size;
+						previous_byte = buffer[pos];
+						goto one_byte1;
+#else
+						state = state < LZMA_NUM_LIT_STATES ? 9 : 11;
+						len = 1;
+						goto string;
+#endif
+					}
+				} else {
+					uint32_t distance;
+
+					prob2 += LZMA_IS_REP_G1 - LZMA_IS_REP_G0;
+					distance = rep1;
+					if (rc_is_bit_1(rc, prob2)) {
+						prob2 += LZMA_IS_REP_G2 - LZMA_IS_REP_G1;
+						distance = rep2;
+						if (rc_is_bit_1(rc, prob2)) {
+							distance = rep3;
+							rep3 = rep2;
+						}
+						rep2 = rep1;
+					}
+					rep1 = rep0;
+					rep0 = distance;
+				}
+				state = state < LZMA_NUM_LIT_STATES ? 8 : 11;
+				prob2 = p + LZMA_REP_LEN_CODER;
+			}
+
+			prob_len = prob2 + LZMA_LEN_CHOICE;
+			num_bits = LZMA_LEN_NUM_LOW_BITS;
+			if (!rc_is_bit_1(rc, prob_len)) {
+				prob_len += LZMA_LEN_LOW - LZMA_LEN_CHOICE
+				            + (pos_state << LZMA_LEN_NUM_LOW_BITS);
+				offset = 0;
+			} else {
+				prob_len += LZMA_LEN_CHOICE_2 - LZMA_LEN_CHOICE;
+				if (!rc_is_bit_1(rc, prob_len)) {
+					prob_len += LZMA_LEN_MID - LZMA_LEN_CHOICE_2
+					            + (pos_state << LZMA_LEN_NUM_MID_BITS);
+					offset = 1 << LZMA_LEN_NUM_LOW_BITS;
+					num_bits += LZMA_LEN_NUM_MID_BITS - LZMA_LEN_NUM_LOW_BITS;
+				} else {
+					prob_len += LZMA_LEN_HIGH - LZMA_LEN_CHOICE_2;
+					offset = ((1 << LZMA_LEN_NUM_LOW_BITS)
+					          + (1 << LZMA_LEN_NUM_MID_BITS));
+					num_bits += LZMA_LEN_NUM_HIGH_BITS - LZMA_LEN_NUM_LOW_BITS;
+				}
+			}
+			rc_bit_tree_decode(rc, prob_len, num_bits, &len);
+			len += offset;
+
+			if (state < 4) {
+				int pos_slot;
+				uint16_t *prob3;
+
+				state += LZMA_NUM_LIT_STATES;
+				prob3 = p + LZMA_POS_SLOT +
+				       ((len < LZMA_NUM_LEN_TO_POS_STATES ? len :
+				         LZMA_NUM_LEN_TO_POS_STATES - 1)
+				         << LZMA_NUM_POS_SLOT_BITS);
+				rc_bit_tree_decode(rc, prob3,
+					LZMA_NUM_POS_SLOT_BITS, &pos_slot);
+				rep0 = pos_slot;
+				if (pos_slot >= LZMA_START_POS_MODEL_INDEX) {
+					int i2, mi2, num_bits2 = (pos_slot >> 1) - 1;
+					rep0 = 2 | (pos_slot & 1);
+					if (pos_slot < LZMA_END_POS_MODEL_INDEX) {
+						rep0 <<= num_bits2;
+						prob3 = p + LZMA_SPEC_POS + rep0 - pos_slot - 1;
+					} else {
+						for (; num_bits2 != LZMA_NUM_ALIGN_BITS; num_bits2--)
+							rep0 = (rep0 << 1) | rc_direct_bit(rc);
+						rep0 <<= LZMA_NUM_ALIGN_BITS;
+						prob3 = p + LZMA_ALIGN;
+					}
+					i2 = 1;
+					mi2 = 1;
+					while (num_bits2--) {
+						if (rc_get_bit(rc, prob3 + mi2, &mi2))
+							rep0 |= i2;
+						i2 <<= 1;
+					}
+				}
+				if (++rep0 == 0)
+					break;
+			}
+
+			len += LZMA_MATCH_MIN_LEN;
+ IF_NOT_FEATURE_LZMA_FAST(string:)
+			do {
+				uint32_t pos = buffer_pos - rep0;
+				while (pos >= header.dict_size)
+					pos += header.dict_size;
+				previous_byte = buffer[pos];
+ IF_NOT_FEATURE_LZMA_FAST(one_byte2:)
+				buffer[buffer_pos++] = previous_byte;
+				if (buffer_pos == header.dict_size) {
+					buffer_pos = 0;
+					global_pos += header.dict_size;
+					if (full_write(dst_fd, buffer, header.dict_size) != (ssize_t)header.dict_size)
+						goto bad;
+					IF_DESKTOP(total_written += header.dict_size;)
+				}
+				len--;
+			} while (len != 0 && buffer_pos < header.dst_size);
+		}
+	}
+
+	{
+		IF_NOT_DESKTOP(int total_written = 0; /* success */)
+		IF_DESKTOP(total_written += buffer_pos;)
+		if (full_write(dst_fd, buffer, buffer_pos) != (ssize_t)buffer_pos) {
+ bad:
+			total_written = -1; /* failure */
+		}
+		rc_free(rc);
+		free(p);
+		free(buffer);
+		return total_written;
+	}
+}
diff --git a/busybox-1.19.3/archival/libarchive/decompress_unxz.c b/busybox-1.19.3/archival/libarchive/decompress_unxz.c
new file mode 100644
index 0000000..e90dfb0
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/decompress_unxz.c
@@ -0,0 +1,98 @@
+/*
+ * This file uses XZ Embedded library code which is written
+ * by Lasse Collin <lasse.collin@tukaani.org>
+ * and Igor Pavlov <http://7-zip.org/>
+ *
+ * See README file in unxz/ directory for more information.
+ *
+ * This file is:
+ * Copyright (C) 2010 Denys Vlasenko <vda.linux@googlemail.com>
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "archive.h"
+
+#define XZ_FUNC FAST_FUNC
+#define XZ_EXTERN static
+
+#define XZ_DEC_DYNALLOC
+
+/* Skip check (rather than fail) of unsupported hash functions */
+#define XZ_DEC_ANY_CHECK  1
+
+/* We use our own crc32 function */
+#define XZ_INTERNAL_CRC32 0
+static uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t 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))
+
+#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)
+{
+	struct xz_buf iobuf;
+	struct xz_dec *state;
+	unsigned char *membuf;
+	IF_DESKTOP(long long) int total = 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);
+	iobuf.in = membuf;
+	iobuf.in_size = HEADER_MAGIC_SIZE;
+	iobuf.out = membuf + BUFSIZ;
+	iobuf.out_size = BUFSIZ;
+
+	/* Limit memory usage to about 64 MiB. */
+	state = xz_dec_init(XZ_DYNALLOC, 64*1024*1024);
+
+	while (1) {
+		enum xz_ret r;
+
+		if (iobuf.in_pos == iobuf.in_size) {
+			int rd = safe_read(src_fd, membuf, BUFSIZ);
+			if (rd < 0) {
+				bb_error_msg(bb_msg_read_error);
+				total = -1;
+				break;
+			}
+			iobuf.in_size = rd;
+			iobuf.in_pos = 0;
+		}
+//		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);
+//		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);
+		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 (r != XZ_OK && r != XZ_UNSUPPORTED_CHECK) {
+			bb_error_msg("corrupted data");
+			total = -1;
+			break;
+		}
+	}
+	xz_dec_end(state);
+	free(membuf);
+
+	return total;
+}
diff --git a/busybox-1.19.3/archival/libarchive/decompress_unzip.c b/busybox-1.19.3/archival/libarchive/decompress_unzip.c
new file mode 100644
index 0000000..a29eef8
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/decompress_unzip.c
@@ -0,0 +1,1252 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * gunzip implementation for busybox
+ *
+ * Based on GNU gzip v1.2.4 Copyright (C) 1992-1993 Jean-loup Gailly.
+ *
+ * Originally adjusted for busybox by Sven Rudolph <sr1@inf.tu-dresden.de>
+ * based on gzip sources
+ *
+ * Adjusted further by Erik Andersen <andersen@codepoet.org> to support
+ * files as well as stdin/stdout, and to generally behave itself wrt
+ * command line handling.
+ *
+ * General cleanup to better adhere to the style guide and make use of standard
+ * busybox functions by Glenn McGrath
+ *
+ * read_gz interface + associated hacking by Laurence Anderson
+ *
+ * Fixed huft_build() so decoding end-of-block code does not grab more bits
+ * than necessary (this is required by unzip applet), added inflate_cleanup()
+ * to free leaked bytebuffer memory (used in unzip.c), and some minor style
+ * guide cleanups by Ed Clark
+ *
+ * gzip (GNU zip) -- compress files with zip algorithm and 'compress' interface
+ * Copyright (C) 1992-1993 Jean-loup Gailly
+ * The unzip code was written and put in the public domain by Mark Adler.
+ * 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 file algorithm.doc for the compression algorithms and file formats.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include <setjmp.h>
+#include "libbb.h"
+#include "archive.h"
+
+typedef struct huft_t {
+	unsigned char e;	/* number of extra bits or operation */
+	unsigned char b;	/* number of bits in this code or subcode */
+	union {
+		unsigned short n;	/* literal, length base, or distance base */
+		struct huft_t *t;	/* pointer to next level of table */
+	} v;
+} huft_t;
+
+enum {
+	/* gunzip_window size--must be a power of two, and
+	 * at least 32K for zip's deflate method */
+	GUNZIP_WSIZE = 0x8000,
+	/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
+	BMAX = 16,	/* maximum bit length of any code (16 for explode) */
+	N_MAX = 288,	/* maximum number of codes in any set */
+};
+
+
+/* This is somewhat complex-looking arrangement, but it allows
+ * to place decompressor state either in bss or in
+ * malloc'ed space simply by changing #defines below.
+ * Sizes on i386:
+ * text    data     bss     dec     hex
+ * 5256       0     108    5364    14f4 - bss
+ * 4915       0       0    4915    1333 - malloc
+ */
+#define STATE_IN_BSS 0
+#define STATE_IN_MALLOC 1
+
+
+typedef struct state_t {
+	off_t gunzip_bytes_out; /* number of output bytes */
+	uint32_t gunzip_crc;
+
+	int gunzip_src_fd;
+	unsigned gunzip_outbuf_count; /* bytes in output buffer */
+
+	unsigned char *gunzip_window;
+
+	uint32_t *gunzip_crc_table;
+
+	/* bitbuffer */
+	unsigned gunzip_bb; /* bit buffer */
+	unsigned char gunzip_bk; /* bits in bit buffer */
+
+	/* input (compressed) data */
+	unsigned char *bytebuffer;      /* buffer itself */
+	off_t to_read;			/* compressed bytes to read (unzip only, -1 for gunzip) */
+//	unsigned bytebuffer_max;        /* buffer size */
+	unsigned bytebuffer_offset;     /* buffer position */
+	unsigned bytebuffer_size;       /* how much data is there (size <= max) */
+
+	/* private data of inflate_codes() */
+	unsigned inflate_codes_ml; /* masks for bl and bd bits */
+	unsigned inflate_codes_md; /* masks for bl and bd bits */
+	unsigned inflate_codes_bb; /* bit buffer */
+	unsigned inflate_codes_k; /* number of bits in bit buffer */
+	unsigned inflate_codes_w; /* current gunzip_window position */
+	huft_t *inflate_codes_tl;
+	huft_t *inflate_codes_td;
+	unsigned inflate_codes_bl;
+	unsigned inflate_codes_bd;
+	unsigned inflate_codes_nn; /* length and index for copy */
+	unsigned inflate_codes_dd;
+
+	smallint resume_copy;
+
+	/* private data of inflate_get_next_window() */
+	smallint method; /* method == -1 for stored, -2 for codes */
+	smallint need_another_block;
+	smallint end_reached;
+
+	/* private data of inflate_stored() */
+	unsigned inflate_stored_n;
+	unsigned inflate_stored_b;
+	unsigned inflate_stored_k;
+	unsigned inflate_stored_w;
+
+	const char *error_msg;
+	jmp_buf error_jmp;
+} state_t;
+#define gunzip_bytes_out    (S()gunzip_bytes_out   )
+#define gunzip_crc          (S()gunzip_crc         )
+#define gunzip_src_fd       (S()gunzip_src_fd      )
+#define gunzip_outbuf_count (S()gunzip_outbuf_count)
+#define gunzip_window       (S()gunzip_window      )
+#define gunzip_crc_table    (S()gunzip_crc_table   )
+#define gunzip_bb           (S()gunzip_bb          )
+#define gunzip_bk           (S()gunzip_bk          )
+#define to_read             (S()to_read            )
+// #define bytebuffer_max   (S()bytebuffer_max     )
+// Both gunzip and unzip can use constant buffer size now (16k):
+#define bytebuffer_max      0x4000
+#define bytebuffer          (S()bytebuffer         )
+#define bytebuffer_offset   (S()bytebuffer_offset  )
+#define bytebuffer_size     (S()bytebuffer_size    )
+#define inflate_codes_ml    (S()inflate_codes_ml   )
+#define inflate_codes_md    (S()inflate_codes_md   )
+#define inflate_codes_bb    (S()inflate_codes_bb   )
+#define inflate_codes_k     (S()inflate_codes_k    )
+#define inflate_codes_w     (S()inflate_codes_w    )
+#define inflate_codes_tl    (S()inflate_codes_tl   )
+#define inflate_codes_td    (S()inflate_codes_td   )
+#define inflate_codes_bl    (S()inflate_codes_bl   )
+#define inflate_codes_bd    (S()inflate_codes_bd   )
+#define inflate_codes_nn    (S()inflate_codes_nn   )
+#define inflate_codes_dd    (S()inflate_codes_dd   )
+#define resume_copy         (S()resume_copy        )
+#define method              (S()method             )
+#define need_another_block  (S()need_another_block )
+#define end_reached         (S()end_reached        )
+#define inflate_stored_n    (S()inflate_stored_n   )
+#define inflate_stored_b    (S()inflate_stored_b   )
+#define inflate_stored_k    (S()inflate_stored_k   )
+#define inflate_stored_w    (S()inflate_stored_w   )
+#define error_msg           (S()error_msg          )
+#define error_jmp           (S()error_jmp          )
+
+/* This is a generic part */
+#if STATE_IN_BSS /* Use global data segment */
+#define DECLARE_STATE /*nothing*/
+#define ALLOC_STATE /*nothing*/
+#define DEALLOC_STATE ((void)0)
+#define S() state.
+#define PASS_STATE /*nothing*/
+#define PASS_STATE_ONLY /*nothing*/
+#define STATE_PARAM /*nothing*/
+#define STATE_PARAM_ONLY void
+static state_t state;
+#endif
+
+#if STATE_IN_MALLOC /* Use malloc space */
+#define DECLARE_STATE state_t *state
+#define ALLOC_STATE (state = xzalloc(sizeof(*state)))
+#define DEALLOC_STATE free(state)
+#define S() state->
+#define PASS_STATE state,
+#define PASS_STATE_ONLY state
+#define STATE_PARAM state_t *state,
+#define STATE_PARAM_ONLY state_t *state
+#endif
+
+
+static const uint16_t mask_bits[] ALIGN2 = {
+	0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+	0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* Copy lengths for literal codes 257..285 */
+static const uint16_t cplens[] ALIGN2 = {
+	3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+	67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+};
+
+/* note: see note #13 above about the 258 in this list. */
+/* Extra bits for literal codes 257..285 */
+static const uint8_t cplext[] ALIGN1 = {
+	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5,
+	5, 5, 5, 0, 99, 99
+}; /* 99 == invalid */
+
+/* Copy offsets for distance codes 0..29 */
+static const uint16_t cpdist[] ALIGN2 = {
+	1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+	769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577
+};
+
+/* Extra bits for distance codes */
+static const uint8_t cpdext[] ALIGN1 = {
+	0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
+	11, 11, 12, 12, 13, 13
+};
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+/* Order of the bit length code lengths */
+static const uint8_t border[] ALIGN1 = {
+	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
+};
+
+
+/*
+ * Free the malloc'ed tables built by huft_build(), which makes a linked
+ * list of the tables it made, with the links in a dummy first entry of
+ * each table.
+ * t: table to free
+ */
+static void huft_free(huft_t *p)
+{
+	huft_t *q;
+
+	/* Go through linked list, freeing from the malloced (t[-1]) address. */
+	while (p) {
+		q = (--p)->v.t;
+		free(p);
+		p = q;
+	}
+}
+
+static void huft_free_all(STATE_PARAM_ONLY)
+{
+	huft_free(inflate_codes_tl);
+	huft_free(inflate_codes_td);
+	inflate_codes_tl = NULL;
+	inflate_codes_td = NULL;
+}
+
+static void abort_unzip(STATE_PARAM_ONLY) NORETURN;
+static void abort_unzip(STATE_PARAM_ONLY)
+{
+	huft_free_all(PASS_STATE_ONLY);
+	longjmp(error_jmp, 1);
+}
+
+static unsigned fill_bitbuffer(STATE_PARAM unsigned bitbuffer, unsigned *current, const unsigned required)
+{
+	while (*current < required) {
+		if (bytebuffer_offset >= bytebuffer_size) {
+			unsigned sz = bytebuffer_max - 4;
+			if (to_read >= 0 && to_read < sz) /* unzip only */
+				sz = to_read;
+			/* Leave the first 4 bytes empty so we can always unwind the bitbuffer
+			 * to the front of the bytebuffer */
+			bytebuffer_size = safe_read(gunzip_src_fd, &bytebuffer[4], sz);
+			if ((int)bytebuffer_size < 1) {
+				error_msg = "unexpected end of file";
+				abort_unzip(PASS_STATE_ONLY);
+			}
+			if (to_read >= 0) /* unzip only */
+				to_read -= bytebuffer_size;
+			bytebuffer_size += 4;
+			bytebuffer_offset = 4;
+		}
+		bitbuffer |= ((unsigned) bytebuffer[bytebuffer_offset]) << *current;
+		bytebuffer_offset++;
+		*current += 8;
+	}
+	return bitbuffer;
+}
+
+
+/* Given a list of code lengths and a maximum table size, make a set of
+ * tables to decode that set of codes.  Return zero on success, one if
+ * the given code set is incomplete (the tables are still built in this
+ * case), two if the input is invalid (all zero length codes or an
+ * oversubscribed set of lengths) - in this case stores NULL in *t.
+ *
+ * b:	code lengths in bits (all assumed <= BMAX)
+ * n:	number of codes (assumed <= N_MAX)
+ * s:	number of simple-valued codes (0..s-1)
+ * d:	list of base values for non-simple codes
+ * e:	list of extra bits for non-simple codes
+ * t:	result: starting table
+ * 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)
+{
+	unsigned a;             /* counter for codes of length k */
+	unsigned c[BMAX + 1];   /* bit length count table */
+	unsigned eob_len;       /* length of end-of-block code (value 256) */
+	unsigned f;             /* i repeats in table every f entries */
+	int g;                  /* maximum code length */
+	int htl;                /* table level */
+	unsigned i;             /* counter, current code */
+	unsigned j;             /* counter */
+	int k;                  /* number of bits in current code */
+	unsigned *p;            /* pointer into c[], b[], or v[] */
+	huft_t *q;              /* points to current table */
+	huft_t r;               /* table entry for structure assignment */
+	huft_t *u[BMAX];        /* table stack */
+	unsigned v[N_MAX];      /* values in order of bit length */
+	int ws[BMAX + 1];       /* bits decoded stack */
+	int w;                  /* bits decoded */
+	unsigned x[BMAX + 1];   /* bit offsets, then code stack */
+	unsigned *xp;           /* pointer into x */
+	int y;                  /* number of dummy codes added */
+	unsigned z;             /* number of entries in current table */
+
+	/* Length of EOB code, if any */
+	eob_len = n > 256 ? b[256] : BMAX;
+
+	*t = NULL;
+
+	/* Generate counts for each bit length */
+	memset(c, 0, sizeof(c));
+	p = (unsigned *) b; /* cast allows us to reuse p for pointing to b */
+	i = n;
+	do {
+		c[*p]++; /* assume all entries <= BMAX */
+		p++;     /* can't combine with above line (Solaris bug) */
+	} while (--i);
+	if (c[0] == n) {  /* null input - all zero length codes */
+		*m = 0;
+		return 2;
+	}
+
+	/* Find minimum and maximum length, bound *m by those */
+	for (j = 1; (c[j] == 0) && (j <= BMAX); j++)
+		continue;
+	k = j; /* minimum code length */
+	for (i = BMAX; (c[i] == 0) && i; i--)
+		continue;
+	g = i; /* maximum code length */
+	*m = (*m < j) ? j : ((*m > i) ? i : *m);
+
+	/* Adjust last length count to fill out codes, if needed */
+	for (y = 1 << j; j < i; j++, y <<= 1) {
+		y -= c[j];
+		if (y < 0)
+			return 2; /* bad input: more codes than bits */
+	}
+	y -= c[i];
+	if (y < 0)
+		return 2;
+	c[i] += y;
+
+	/* Generate starting offsets into the value table for each length */
+	x[1] = j = 0;
+	p = c + 1;
+	xp = x + 2;
+	while (--i) { /* note that i == g from above */
+		j += *p++;
+		*xp++ = j;
+	}
+
+	/* Make a table of values in order of bit lengths */
+	p = (unsigned *) b;
+	i = 0;
+	do {
+		j = *p++;
+		if (j != 0) {
+			v[x[j]++] = i;
+		}
+	} while (++i < n);
+
+	/* Generate the Huffman codes and for each, make the table entries */
+	x[0] = i = 0;   /* first Huffman code is zero */
+	p = v;          /* grab values in bit order */
+	htl = -1;       /* no tables yet--level -1 */
+	w = ws[0] = 0;  /* bits decoded */
+	u[0] = NULL;    /* just to keep compilers happy */
+	q = NULL;       /* ditto */
+	z = 0;          /* ditto */
+
+	/* go through the bit lengths (k already is bits in shortest code) */
+	for (; k <= g; k++) {
+		a = c[k];
+		while (a--) {
+			/* here i is the Huffman code of length k bits for value *p */
+			/* make tables up to required level */
+			while (k > ws[htl + 1]) {
+				w = ws[++htl];
+
+				/* compute minimum size table less than or equal to *m bits */
+				z = g - w;
+				z = z > *m ? *m : z; /* upper limit on table size */
+				j = k - w;
+				f = 1 << j;
+				if (f > a + 1) { /* try a k-w bit table */
+					/* too few codes for k-w bit table */
+					f -= a + 1; /* deduct codes from patterns left */
+					xp = c + k;
+					while (++j < z) { /* try smaller tables up to z bits */
+						f <<= 1;
+						if (f <= *++xp) {
+							break; /* enough codes to use up j bits */
+						}
+						f -= *xp; /* else deduct codes from patterns */
+					}
+				}
+				j = (w + j > eob_len && w < eob_len) ? eob_len - w : j;	/* make EOB code end at table */
+				z = 1 << j;	/* table entries for j-bit table */
+				ws[htl+1] = w + j;	/* set bits decoded in stack */
+
+				/* allocate and link in new table */
+				q = xzalloc((z + 1) * sizeof(huft_t));
+				*t = q + 1;	/* link to list for huft_free() */
+				t = &(q->v.t);
+				u[htl] = ++q;	/* table starts after link */
+
+				/* connect to last table, if there is one */
+				if (htl) {
+					x[htl] = i; /* save pattern for backing up */
+					r.b = (unsigned char) (w - ws[htl - 1]); /* bits to dump before this table */
+					r.e = (unsigned char) (16 + j); /* bits in this table */
+					r.v.t = q; /* pointer to this table */
+					j = (i & ((1 << w) - 1)) >> ws[htl - 1];
+					u[htl - 1][j] = r; /* connect to last table */
+				}
+			}
+
+			/* set up table entry in r */
+			r.b = (unsigned char) (k - w);
+			if (p >= v + n) {
+				r.e = 99; /* out of values--invalid code */
+			} else if (*p < s) {
+				r.e = (unsigned char) (*p < 256 ? 16 : 15);	/* 256 is EOB code */
+				r.v.n = (unsigned short) (*p++); /* simple code is just the value */
+			} else {
+				r.e = (unsigned char) e[*p - s]; /* non-simple--look up in lists */
+				r.v.n = d[*p++ - s];
+			}
+
+			/* fill code-like entries with r */
+			f = 1 << (k - w);
+			for (j = i >> w; j < z; j += f) {
+				q[j] = r;
+			}
+
+			/* backwards increment the k-bit code i */
+			for (j = 1 << (k - 1); i & j; j >>= 1) {
+				i ^= j;
+			}
+			i ^= j;
+
+			/* backup over finished tables */
+			while ((i & ((1 << w) - 1)) != x[htl]) {
+				w = ws[--htl];
+			}
+		}
+	}
+
+	/* return actual size of base table */
+	*m = ws[1];
+
+	/* Return 1 if we were given an incomplete table */
+	return y != 0 && g != 1;
+}
+
+
+/*
+ * inflate (decompress) the codes in a deflated (compressed) block.
+ * Return an error code or zero if it all goes ok.
+ *
+ * tl, td: literal/length and distance decoder tables
+ * bl, bd: number of bits decoded by tl[] and td[]
+ */
+/* called once from inflate_block */
+
+/* map formerly local static variables to globals */
+#define ml inflate_codes_ml
+#define md inflate_codes_md
+#define bb inflate_codes_bb
+#define k  inflate_codes_k
+#define w  inflate_codes_w
+#define tl inflate_codes_tl
+#define td inflate_codes_td
+#define bl inflate_codes_bl
+#define bd inflate_codes_bd
+#define nn inflate_codes_nn
+#define dd inflate_codes_dd
+static void inflate_codes_setup(STATE_PARAM unsigned my_bl, unsigned my_bd)
+{
+	bl = my_bl;
+	bd = my_bd;
+	/* make local copies of globals */
+	bb = gunzip_bb;			/* initialize bit buffer */
+	k = gunzip_bk;
+	w = gunzip_outbuf_count;	/* initialize gunzip_window position */
+	/* inflate the coded data */
+	ml = mask_bits[bl];		/* precompute masks for speed */
+	md = mask_bits[bd];
+}
+/* called once from inflate_get_next_window */
+static NOINLINE int inflate_codes(STATE_PARAM_ONLY)
+{
+	unsigned e;	/* table entry flag/number of extra bits */
+	huft_t *t;	/* pointer to table entry */
+
+	if (resume_copy)
+		goto do_copy;
+
+	while (1) {			/* do until end of block */
+		bb = fill_bitbuffer(PASS_STATE bb, &k, bl);
+		t = tl + ((unsigned) bb & ml);
+		e = t->e;
+		if (e > 16)
+			do {
+				if (e == 99)
+					abort_unzip(PASS_STATE_ONLY);;
+				bb >>= t->b;
+				k -= t->b;
+				e -= 16;
+				bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+				t = t->v.t + ((unsigned) bb & mask_bits[e]);
+				e = t->e;
+			} while (e > 16);
+		bb >>= t->b;
+		k -= t->b;
+		if (e == 16) {	/* then it's a literal */
+			gunzip_window[w++] = (unsigned char) t->v.n;
+			if (w == GUNZIP_WSIZE) {
+				gunzip_outbuf_count = w;
+				//flush_gunzip_window();
+				w = 0;
+				return 1; // We have a block to read
+			}
+		} else {		/* it's an EOB or a length */
+			/* exit if end of block */
+			if (e == 15) {
+				break;
+			}
+
+			/* get length of block to copy */
+			bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+			nn = t->v.n + ((unsigned) bb & mask_bits[e]);
+			bb >>= e;
+			k -= e;
+
+			/* decode distance of block to copy */
+			bb = fill_bitbuffer(PASS_STATE bb, &k, bd);
+			t = td + ((unsigned) bb & md);
+			e = t->e;
+			if (e > 16)
+				do {
+					if (e == 99)
+						abort_unzip(PASS_STATE_ONLY);
+					bb >>= t->b;
+					k -= t->b;
+					e -= 16;
+					bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+					t = t->v.t + ((unsigned) bb & mask_bits[e]);
+					e = t->e;
+				} while (e > 16);
+			bb >>= t->b;
+			k -= t->b;
+			bb = fill_bitbuffer(PASS_STATE bb, &k, e);
+			dd = w - t->v.n - ((unsigned) bb & mask_bits[e]);
+			bb >>= e;
+			k -= e;
+
+			/* do the copy */
+ do_copy:
+			do {
+				/* Was: nn -= (e = (e = GUNZIP_WSIZE - ((dd &= GUNZIP_WSIZE - 1) > w ? dd : w)) > nn ? nn : e); */
+				/* Who wrote THAT?? rewritten as: */
+				unsigned delta;
+
+				dd &= GUNZIP_WSIZE - 1;
+				e = GUNZIP_WSIZE - (dd > w ? dd : w);
+				delta = w > dd ? w - dd : dd - w;
+				if (e > nn) e = nn;
+				nn -= e;
+
+				/* copy to new buffer to prevent possible overwrite */
+				if (delta >= e) {
+					memcpy(gunzip_window + w, gunzip_window + dd, e);
+					w += e;
+					dd += e;
+				} else {
+					/* do it slow to avoid memcpy() overlap */
+					/* !NOMEMCPY */
+					do {
+						gunzip_window[w++] = gunzip_window[dd++];
+					} while (--e);
+				}
+				if (w == GUNZIP_WSIZE) {
+					gunzip_outbuf_count = w;
+					resume_copy = (nn != 0);
+					//flush_gunzip_window();
+					w = 0;
+					return 1;
+				}
+			} while (nn);
+			resume_copy = 0;
+		}
+	}
+
+	/* restore the globals from the locals */
+	gunzip_outbuf_count = w;	/* restore global gunzip_window pointer */
+	gunzip_bb = bb;			/* restore global bit buffer */
+	gunzip_bk = k;
+
+	/* normally just after call to inflate_codes, but save code by putting it here */
+	/* free the decoding tables (tl and td), return */
+	huft_free_all(PASS_STATE_ONLY);
+
+	/* done */
+	return 0;
+}
+#undef ml
+#undef md
+#undef bb
+#undef k
+#undef w
+#undef tl
+#undef td
+#undef bl
+#undef bd
+#undef nn
+#undef dd
+
+
+/* called once from inflate_block */
+static void inflate_stored_setup(STATE_PARAM int my_n, int my_b, int my_k)
+{
+	inflate_stored_n = my_n;
+	inflate_stored_b = my_b;
+	inflate_stored_k = my_k;
+	/* initialize gunzip_window position */
+	inflate_stored_w = gunzip_outbuf_count;
+}
+/* called once from inflate_get_next_window */
+static int inflate_stored(STATE_PARAM_ONLY)
+{
+	/* read and output the compressed data */
+	while (inflate_stored_n--) {
+		inflate_stored_b = fill_bitbuffer(PASS_STATE inflate_stored_b, &inflate_stored_k, 8);
+		gunzip_window[inflate_stored_w++] = (unsigned char) inflate_stored_b;
+		if (inflate_stored_w == GUNZIP_WSIZE) {
+			gunzip_outbuf_count = inflate_stored_w;
+			//flush_gunzip_window();
+			inflate_stored_w = 0;
+			inflate_stored_b >>= 8;
+			inflate_stored_k -= 8;
+			return 1; /* We have a block */
+		}
+		inflate_stored_b >>= 8;
+		inflate_stored_k -= 8;
+	}
+
+	/* restore the globals from the locals */
+	gunzip_outbuf_count = inflate_stored_w;		/* restore global gunzip_window pointer */
+	gunzip_bb = inflate_stored_b;	/* restore global bit buffer */
+	gunzip_bk = inflate_stored_k;
+	return 0; /* Finished */
+}
+
+
+/*
+ * decompress an inflated block
+ * e: last block flag
+ *
+ * GLOBAL VARIABLES: bb, kk,
+ */
+/* Return values: -1 = inflate_stored, -2 = inflate_codes */
+/* One callsite in inflate_get_next_window */
+static int inflate_block(STATE_PARAM smallint *e)
+{
+	unsigned ll[286 + 30];  /* literal/length and distance code lengths */
+	unsigned t;     /* block type */
+	unsigned b;     /* bit buffer */
+	unsigned k;     /* number of bits in bit buffer */
+
+	/* make local bit buffer */
+
+	b = gunzip_bb;
+	k = gunzip_bk;
+
+	/* read in last block bit */
+	b = fill_bitbuffer(PASS_STATE b, &k, 1);
+	*e = b & 1;
+	b >>= 1;
+	k -= 1;
+
+	/* read in block type */
+	b = fill_bitbuffer(PASS_STATE b, &k, 2);
+	t = (unsigned) b & 3;
+	b >>= 2;
+	k -= 2;
+
+	/* restore the global bit buffer */
+	gunzip_bb = b;
+	gunzip_bk = k;
+
+	/* Do we see block type 1 often? Yes!
+	 * TODO: fix performance problem (see below) */
+	//bb_error_msg("blktype %d", t);
+
+	/* inflate that block type */
+	switch (t) {
+	case 0: /* Inflate stored */
+	{
+		unsigned n;	/* number of bytes in block */
+		unsigned b_stored;	/* bit buffer */
+		unsigned k_stored;	/* number of bits in bit buffer */
+
+		/* make local copies of globals */
+		b_stored = gunzip_bb;	/* initialize bit buffer */
+		k_stored = gunzip_bk;
+
+		/* go to byte boundary */
+		n = k_stored & 7;
+		b_stored >>= n;
+		k_stored -= n;
+
+		/* get the length and its complement */
+		b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16);
+		n = ((unsigned) b_stored & 0xffff);
+		b_stored >>= 16;
+		k_stored -= 16;
+
+		b_stored = fill_bitbuffer(PASS_STATE b_stored, &k_stored, 16);
+		if (n != (unsigned) ((~b_stored) & 0xffff)) {
+			abort_unzip(PASS_STATE_ONLY);	/* error in compressed data */
+		}
+		b_stored >>= 16;
+		k_stored -= 16;
+
+		inflate_stored_setup(PASS_STATE n, b_stored, k_stored);
+
+		return -1;
+	}
+	case 1:
+	/* Inflate fixed
+	 * decompress an inflated type 1 (fixed Huffman codes) block. We should
+	 * either replace this with a custom decoder, or at least precompute the
+	 * Huffman tables. TODO */
+	{
+		int i;                  /* temporary variable */
+		unsigned bl;            /* lookup bits for tl */
+		unsigned bd;            /* lookup bits for td */
+		/* gcc 4.2.1 is too dumb to reuse stackspace. Moved up... */
+		//unsigned ll[288];     /* length list for huft_build */
+
+		/* set up literal table */
+		for (i = 0; i < 144; i++)
+			ll[i] = 8;
+		for (; i < 256; i++)
+			ll[i] = 9;
+		for (; i < 280; i++)
+			ll[i] = 7;
+		for (; i < 288; i++) /* make a complete, but wrong code set */
+			ll[i] = 8;
+		bl = 7;
+		huft_build(ll, 288, 257, cplens, cplext, &inflate_codes_tl, &bl);
+		/* huft_build() never return nonzero - we use known data */
+
+		/* set up distance table */
+		for (i = 0; i < 30; i++) /* make an incomplete code set */
+			ll[i] = 5;
+		bd = 5;
+		huft_build(ll, 30, 0, cpdist, cpdext, &inflate_codes_td, &bd);
+
+		/* set up data for inflate_codes() */
+		inflate_codes_setup(PASS_STATE bl, bd);
+
+		/* huft_free code moved into inflate_codes */
+
+		return -2;
+	}
+	case 2: /* Inflate dynamic */
+	{
+		enum { dbits = 6 };     /* bits in base distance lookup table */
+		enum { lbits = 9 };     /* bits in base literal/length lookup table */
+
+		huft_t *td;             /* distance code table */
+		unsigned i;             /* temporary variables */
+		unsigned j;
+		unsigned l;             /* last length */
+		unsigned m;             /* mask for bit lengths table */
+		unsigned n;             /* number of lengths to get */
+		unsigned bl;            /* lookup bits for tl */
+		unsigned bd;            /* lookup bits for td */
+		unsigned nb;            /* number of bit length codes */
+		unsigned nl;            /* number of literal/length codes */
+		unsigned nd;            /* number of distance codes */
+
+		//unsigned ll[286 + 30];/* literal/length and distance code lengths */
+		unsigned b_dynamic;     /* bit buffer */
+		unsigned k_dynamic;     /* number of bits in bit buffer */
+
+		/* make local bit buffer */
+		b_dynamic = gunzip_bb;
+		k_dynamic = gunzip_bk;
+
+		/* read in table lengths */
+		b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5);
+		nl = 257 + ((unsigned) b_dynamic & 0x1f);	/* number of literal/length codes */
+
+		b_dynamic >>= 5;
+		k_dynamic -= 5;
+		b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 5);
+		nd = 1 + ((unsigned) b_dynamic & 0x1f);	/* number of distance codes */
+
+		b_dynamic >>= 5;
+		k_dynamic -= 5;
+		b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 4);
+		nb = 4 + ((unsigned) b_dynamic & 0xf);	/* number of bit length codes */
+
+		b_dynamic >>= 4;
+		k_dynamic -= 4;
+		if (nl > 286 || nd > 30)
+			abort_unzip(PASS_STATE_ONLY);	/* bad lengths */
+
+		/* read in bit-length-code lengths */
+		for (j = 0; j < nb; j++) {
+			b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3);
+			ll[border[j]] = (unsigned) b_dynamic & 7;
+			b_dynamic >>= 3;
+			k_dynamic -= 3;
+		}
+		for (; j < 19; j++)
+			ll[border[j]] = 0;
+
+		/* build decoding table for trees - single level, 7 bit lookup */
+		bl = 7;
+		i = huft_build(ll, 19, 19, NULL, NULL, &inflate_codes_tl, &bl);
+		if (i != 0) {
+			abort_unzip(PASS_STATE_ONLY); //return i;	/* incomplete code set */
+		}
+
+		/* read in literal and distance code lengths */
+		n = nl + nd;
+		m = mask_bits[bl];
+		i = l = 0;
+		while ((unsigned) i < n) {
+			b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, (unsigned)bl);
+			td = inflate_codes_tl + ((unsigned) b_dynamic & m);
+			j = td->b;
+			b_dynamic >>= j;
+			k_dynamic -= j;
+			j = td->v.n;
+			if (j < 16) {	/* length of code in bits (0..15) */
+				ll[i++] = l = j;	/* save last length in l */
+			} else if (j == 16) {	/* repeat last length 3 to 6 times */
+				b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 2);
+				j = 3 + ((unsigned) b_dynamic & 3);
+				b_dynamic >>= 2;
+				k_dynamic -= 2;
+				if ((unsigned) i + j > n) {
+					abort_unzip(PASS_STATE_ONLY); //return 1;
+				}
+				while (j--) {
+					ll[i++] = l;
+				}
+			} else if (j == 17) {	/* 3 to 10 zero length codes */
+				b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 3);
+				j = 3 + ((unsigned) b_dynamic & 7);
+				b_dynamic >>= 3;
+				k_dynamic -= 3;
+				if ((unsigned) i + j > n) {
+					abort_unzip(PASS_STATE_ONLY); //return 1;
+				}
+				while (j--) {
+					ll[i++] = 0;
+				}
+				l = 0;
+			} else {	/* j == 18: 11 to 138 zero length codes */
+				b_dynamic = fill_bitbuffer(PASS_STATE b_dynamic, &k_dynamic, 7);
+				j = 11 + ((unsigned) b_dynamic & 0x7f);
+				b_dynamic >>= 7;
+				k_dynamic -= 7;
+				if ((unsigned) i + j > n) {
+					abort_unzip(PASS_STATE_ONLY); //return 1;
+				}
+				while (j--) {
+					ll[i++] = 0;
+				}
+				l = 0;
+			}
+		}
+
+		/* free decoding table for trees */
+		huft_free(inflate_codes_tl);
+
+		/* restore the global bit buffer */
+		gunzip_bb = b_dynamic;
+		gunzip_bk = k_dynamic;
+
+		/* build the decoding tables for literal/length and distance codes */
+		bl = lbits;
+
+		i = huft_build(ll, nl, 257, cplens, cplext, &inflate_codes_tl, &bl);
+		if (i != 0)
+			abort_unzip(PASS_STATE_ONLY);
+		bd = dbits;
+		i = huft_build(ll + nl, nd, 0, cpdist, cpdext, &inflate_codes_td, &bd);
+		if (i != 0)
+			abort_unzip(PASS_STATE_ONLY);
+
+		/* set up data for inflate_codes() */
+		inflate_codes_setup(PASS_STATE bl, bd);
+
+		/* huft_free code moved into inflate_codes */
+
+		return -2;
+	}
+	default:
+		abort_unzip(PASS_STATE_ONLY);
+	}
+}
+
+/* Two callsites, both in inflate_get_next_window */
+static void calculate_gunzip_crc(STATE_PARAM_ONLY)
+{
+	gunzip_crc = crc32_block_endian0(gunzip_crc, gunzip_window, gunzip_outbuf_count, gunzip_crc_table);
+	gunzip_bytes_out += gunzip_outbuf_count;
+}
+
+/* One callsite in inflate_unzip_internal */
+static int inflate_get_next_window(STATE_PARAM_ONLY)
+{
+	gunzip_outbuf_count = 0;
+
+	while (1) {
+		int ret;
+
+		if (need_another_block) {
+			if (end_reached) {
+				calculate_gunzip_crc(PASS_STATE_ONLY);
+				end_reached = 0;
+				/* NB: need_another_block is still set */
+				return 0; /* Last block */
+			}
+			method = inflate_block(PASS_STATE &end_reached);
+			need_another_block = 0;
+		}
+
+		switch (method) {
+		case -1:
+			ret = inflate_stored(PASS_STATE_ONLY);
+			break;
+		case -2:
+			ret = inflate_codes(PASS_STATE_ONLY);
+			break;
+		default: /* cannot happen */
+			abort_unzip(PASS_STATE_ONLY);
+		}
+
+		if (ret == 1) {
+			calculate_gunzip_crc(PASS_STATE_ONLY);
+			return 1; /* more data left */
+		}
+		need_another_block = 1; /* end of that block */
+	}
+	/* Doesnt get here */
+}
+
+
+/* Called from unpack_gz_stream() and inflate_unzip() */
+static IF_DESKTOP(long long) int
+inflate_unzip_internal(STATE_PARAM int in, int out)
+{
+	IF_DESKTOP(long long) int n = 0;
+	ssize_t nwrote;
+
+	/* Allocate all global buffers (for DYN_ALLOC option) */
+	gunzip_window = xmalloc(GUNZIP_WSIZE);
+	gunzip_outbuf_count = 0;
+	gunzip_bytes_out = 0;
+	gunzip_src_fd = in;
+
+	/* (re) initialize state */
+	method = -1;
+	need_another_block = 1;
+	resume_copy = 0;
+	gunzip_bk = 0;
+	gunzip_bb = 0;
+
+	/* Create the crc table */
+	gunzip_crc_table = crc32_filltable(NULL, 0);
+	gunzip_crc = ~0;
+
+	error_msg = "corrupted data";
+	if (setjmp(error_jmp)) {
+		/* Error from deep inside zip machinery */
+		n = -1;
+		goto ret;
+	}
+
+	while (1) {
+		int r = inflate_get_next_window(PASS_STATE_ONLY);
+		nwrote = full_write(out, gunzip_window, gunzip_outbuf_count);
+		if (nwrote != (ssize_t)gunzip_outbuf_count) {
+			bb_perror_msg("write");
+			n = -1;
+			goto ret;
+		}
+		IF_DESKTOP(n += nwrote;)
+		if (r == 0) break;
+	}
+
+	/* Store unused bytes in a global buffer so calling applets can access it */
+	if (gunzip_bk >= 8) {
+		/* Undo too much lookahead. The next read will be byte aligned
+		 * so we can discard unused bits in the last meaningful byte. */
+		bytebuffer_offset--;
+		bytebuffer[bytebuffer_offset] = gunzip_bb & 0xff;
+		gunzip_bb >>= 8;
+		gunzip_bk -= 8;
+	}
+ ret:
+	/* Cleanup */
+	free(gunzip_window);
+	free(gunzip_crc_table);
+	return n;
+}
+
+
+/* External entry points */
+
+/* For unzip */
+
+IF_DESKTOP(long long) int FAST_FUNC
+inflate_unzip(inflate_unzip_result *res, off_t compr_size, int in, int out)
+{
+	IF_DESKTOP(long long) int n;
+	DECLARE_STATE;
+
+	ALLOC_STATE;
+
+	to_read = compr_size;
+//	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;
+	DEALLOC_STATE;
+	return n;
+}
+
+
+/* For gunzip */
+
+/* helpers first */
+
+/* Top up the input buffer with at least n bytes. */
+static int top_up(STATE_PARAM unsigned n)
+{
+	int count = bytebuffer_size - bytebuffer_offset;
+
+	if (count < (int)n) {
+		memmove(bytebuffer, &bytebuffer[bytebuffer_offset], count);
+		bytebuffer_offset = 0;
+		bytebuffer_size = full_read(gunzip_src_fd, &bytebuffer[count], bytebuffer_max - count);
+		if ((int)bytebuffer_size < 0) {
+			bb_error_msg(bb_msg_read_error);
+			return 0;
+		}
+		bytebuffer_size += count;
+		if (bytebuffer_size < n)
+			return 0;
+	}
+	return 1;
+}
+
+static uint16_t buffer_read_le_u16(STATE_PARAM_ONLY)
+{
+	uint16_t res;
+#if BB_LITTLE_ENDIAN
+	move_from_unaligned16(res, &bytebuffer[bytebuffer_offset]);
+#else
+	res = bytebuffer[bytebuffer_offset];
+	res |= bytebuffer[bytebuffer_offset + 1] << 8;
+#endif
+	bytebuffer_offset += 2;
+	return res;
+}
+
+static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY)
+{
+	uint32_t res;
+#if BB_LITTLE_ENDIAN
+	move_from_unaligned32(res, &bytebuffer[bytebuffer_offset]);
+#else
+	res = bytebuffer[bytebuffer_offset];
+	res |= bytebuffer[bytebuffer_offset + 1] << 8;
+	res |= bytebuffer[bytebuffer_offset + 2] << 16;
+	res |= bytebuffer[bytebuffer_offset + 3] << 24;
+#endif
+	bytebuffer_offset += 4;
+	return res;
+}
+
+static int check_header_gzip(STATE_PARAM unpack_info_t *info)
+{
+	union {
+		unsigned char raw[8];
+		struct {
+			uint8_t gz_method;
+			uint8_t flags;
+			uint32_t mtime;
+			uint8_t xtra_flags_UNUSED;
+			uint8_t os_flags_UNUSED;
+		} PACKED formatted;
+	} header;
+	struct BUG_header {
+		char BUG_header[sizeof(header) == 8 ? 1 : -1];
+	};
+
+	/*
+	 * Rewind bytebuffer. We use the beginning because the header has 8
+	 * bytes, leaving enough for unwinding afterwards.
+	 */
+	bytebuffer_size -= bytebuffer_offset;
+	memmove(bytebuffer, &bytebuffer[bytebuffer_offset], bytebuffer_size);
+	bytebuffer_offset = 0;
+
+	if (!top_up(PASS_STATE 8))
+		return 0;
+	memcpy(header.raw, &bytebuffer[bytebuffer_offset], 8);
+	bytebuffer_offset += 8;
+
+	/* Check the compression method */
+	if (header.formatted.gz_method != 8) {
+		return 0;
+	}
+
+	if (header.formatted.flags & 0x04) {
+		/* bit 2 set: extra field present */
+		unsigned extra_short;
+
+		if (!top_up(PASS_STATE 2))
+			return 0;
+		extra_short = buffer_read_le_u16(PASS_STATE_ONLY);
+		if (!top_up(PASS_STATE extra_short))
+			return 0;
+		/* Ignore extra field */
+		bytebuffer_offset += extra_short;
+	}
+
+	/* Discard original name and file comment if any */
+	/* bit 3 set: original file name present */
+	/* bit 4 set: file comment present */
+	if (header.formatted.flags & 0x18) {
+		while (1) {
+			do {
+				if (!top_up(PASS_STATE 1))
+					return 0;
+			} while (bytebuffer[bytebuffer_offset++] != 0);
+			if ((header.formatted.flags & 0x18) != 0x18)
+				break;
+			header.formatted.flags &= ~0x18;
+		}
+	}
+
+	if (info)
+		info->mtime = SWAP_LE32(header.formatted.mtime);
+
+	/* Read the header checksum */
+	if (header.formatted.flags & 0x02) {
+		if (!top_up(PASS_STATE 2))
+			return 0;
+		bytebuffer_offset += 2;
+	}
+	return 1;
+}
+
+IF_DESKTOP(long long) int FAST_FUNC
+unpack_gz_stream_with_info(int in, int out, unpack_info_t *info)
+{
+	uint32_t v32;
+	IF_DESKTOP(long long) int n;
+	DECLARE_STATE;
+
+	n = 0;
+
+	ALLOC_STATE;
+	to_read = -1;
+//	bytebuffer_max = 0x8000;
+	bytebuffer = xmalloc(bytebuffer_max);
+	gunzip_src_fd = in;
+
+ again:
+	if (!check_header_gzip(PASS_STATE info)) {
+		bb_error_msg("corrupted data");
+		n = -1;
+		goto ret;
+	}
+	n += inflate_unzip_internal(PASS_STATE in, out);
+	if (n < 0)
+		goto ret;
+
+	if (!top_up(PASS_STATE 8)) {
+		bb_error_msg("corrupted data");
+		n = -1;
+		goto ret;
+	}
+
+	/* Validate decompression - crc */
+	v32 = buffer_read_le_u32(PASS_STATE_ONLY);
+	if ((~gunzip_crc) != v32) {
+		bb_error_msg("crc error");
+		n = -1;
+		goto ret;
+	}
+
+	/* Validate decompression - size */
+	v32 = buffer_read_le_u32(PASS_STATE_ONLY);
+	if ((uint32_t)gunzip_bytes_out != v32) {
+		bb_error_msg("incorrect length");
+		n = -1;
+	}
+
+	if (!top_up(PASS_STATE 2))
+		goto ret; /* EOF */
+
+	if (bytebuffer[bytebuffer_offset] == 0x1f
+	 && bytebuffer[bytebuffer_offset + 1] == 0x8b
+	) {
+		bytebuffer_offset += 2;
+		goto again;
+	}
+	/* GNU gzip says: */
+	/*bb_error_msg("decompression OK, trailing garbage ignored");*/
+
+ 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);
+}
diff --git a/busybox-1.19.3/archival/libarchive/filter_accept_all.c b/busybox-1.19.3/archival/libarchive/filter_accept_all.c
new file mode 100644
index 0000000..e69deb6
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/filter_accept_all.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "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)
+{
+	if (archive_handle->file_header->name)
+		return EXIT_SUCCESS;
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/archival/libarchive/filter_accept_list.c b/busybox-1.19.3/archival/libarchive/filter_accept_list.c
new file mode 100644
index 0000000..a7640af
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/filter_accept_list.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+/*
+ * Accept names that are in the accept list, ignoring reject list.
+ */
+char FAST_FUNC filter_accept_list(archive_handle_t *archive_handle)
+{
+	if (find_list_entry(archive_handle->accept, archive_handle->file_header->name))
+		return EXIT_SUCCESS;
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/archival/libarchive/filter_accept_list_reassign.c b/busybox-1.19.3/archival/libarchive/filter_accept_list_reassign.c
new file mode 100644
index 0000000..d80f716
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/filter_accept_list_reassign.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+/* Built and used only if ENABLE_DPKG || ENABLE_DPKG_DEB */
+
+/*
+ * Reassign the subarchive metadata parser based on the filename extension
+ * e.g. if its a .tar.gz modify archive_handle->sub_archive to process a .tar.gz
+ * or if its a .tar.bz2 make archive_handle->sub_archive handle that
+ */
+char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle)
+{
+	/* Check the file entry is in the accept list */
+	if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
+		const char *name_ptr;
+
+		/* Find extension */
+		name_ptr = strrchr(archive_handle->file_header->name, '.');
+		if (!name_ptr)
+			return EXIT_FAILURE;
+		name_ptr++;
+
+		/* Modify the subarchive handler based on the extension */
+		if (ENABLE_FEATURE_SEAMLESS_GZ
+		 && strcmp(name_ptr, "gz") == 0
+		) {
+			archive_handle->dpkg__action_data_subarchive = get_header_tar_gz;
+			return EXIT_SUCCESS;
+		}
+		if (ENABLE_FEATURE_SEAMLESS_BZ2
+		 && strcmp(name_ptr, "bz2") == 0
+		) {
+			archive_handle->dpkg__action_data_subarchive = get_header_tar_bz2;
+			return EXIT_SUCCESS;
+		}
+		if (ENABLE_FEATURE_SEAMLESS_LZMA
+		 && strcmp(name_ptr, "lzma") == 0
+		) {
+			archive_handle->dpkg__action_data_subarchive = get_header_tar_lzma;
+			return EXIT_SUCCESS;
+		}
+	}
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/archival/libarchive/filter_accept_reject_list.c b/busybox-1.19.3/archival/libarchive/filter_accept_reject_list.c
new file mode 100644
index 0000000..3e86cca
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/filter_accept_reject_list.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+/*
+ * Accept names that are in the accept list and not in the reject list
+ */
+char FAST_FUNC filter_accept_reject_list(archive_handle_t *archive_handle)
+{
+	const char *key;
+	const llist_t *reject_entry;
+	const llist_t *accept_entry;
+
+	key = archive_handle->file_header->name;
+
+	/* If the key is in a reject list fail */
+	reject_entry = find_list_entry2(archive_handle->reject, key);
+	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;
+	}
+
+	/* Accepted */
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/libarchive/find_list_entry.c b/busybox-1.19.3/archival/libarchive/find_list_entry.c
new file mode 100644
index 0000000..5efd1af
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/find_list_entry.c
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2002 by Glenn McGrath
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include <fnmatch.h>
+#include "libbb.h"
+#include "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)
+{
+	while (list) {
+		if (fnmatch(list->data, filename, 0) == 0) {
+			return list;
+		}
+		list = list->link;
+	}
+	return NULL;
+}
+
+/* Same, but compares only path components present in pattern
+ * (extra trailing path components in filename are assumed to match)
+ */
+const llist_t* FAST_FUNC find_list_entry2(const llist_t *list, const char *filename)
+{
+	char buf[PATH_MAX];
+	int pattern_slash_cnt;
+	const char *c;
+	char *d;
+
+	while (list) {
+		c = list->data;
+		pattern_slash_cnt = 0;
+		while (*c)
+			if (*c++ == '/') pattern_slash_cnt++;
+		c = filename;
+		d = buf;
+		/* paranoia is better than buffer overflows */
+		while (*c && d != buf + sizeof(buf)-1) {
+			if (*c == '/' && --pattern_slash_cnt < 0)
+				break;
+			*d++ = *c++;
+		}
+		*d = '\0';
+		if (fnmatch(list->data, buf, 0) == 0) {
+			return list;
+		}
+		list = list->link;
+	}
+	return NULL;
+}
diff --git a/busybox-1.19.3/archival/libarchive/get_header_ar.c b/busybox-1.19.3/archival/libarchive/get_header_ar.c
new file mode 100644
index 0000000..df603b1
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/get_header_ar.c
@@ -0,0 +1,133 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright 2001 Glenn McGrath.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+#include "ar.h"
+
+static unsigned read_num(const char *str, int base)
+{
+	/* This code works because
+	 * on misformatted numbers bb_strtou returns all-ones */
+	int err = bb_strtou(str, NULL, base);
+	if (err == -1)
+		bb_error_msg_and_die("invalid ar header");
+	return err;
+}
+
+char FAST_FUNC get_header_ar(archive_handle_t *archive_handle)
+{
+	file_header_t *typed = archive_handle->file_header;
+	unsigned size;
+	union {
+		char raw[60];
+		struct ar_header formatted;
+	} ar;
+#if ENABLE_FEATURE_AR_LONG_FILENAMES
+	static char *ar_long_names;
+	static unsigned ar_long_name_size;
+#endif
+
+	/* dont use xread as we want to handle the error ourself */
+	if (read(archive_handle->src_fd, ar.raw, 60) != 60) {
+		/* End Of File */
+		return EXIT_FAILURE;
+	}
+
+	/* ar header starts on an even byte (2 byte aligned)
+	 * '\n' is used for padding
+	 */
+	if (ar.raw[0] == '\n') {
+		/* fix up the header, we started reading 1 byte too early */
+		memmove(ar.raw, &ar.raw[1], 59);
+		ar.raw[59] = xread_char(archive_handle->src_fd);
+		archive_handle->offset++;
+	}
+	archive_handle->offset += 60;
+
+	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);
+
+	/* special filenames have '/' as the first character */
+	if (ar.formatted.name[0] == '/') {
+		if (ar.formatted.name[1] == ' ') {
+			/* This is the index of symbols in the file for compilers */
+			data_skip(archive_handle);
+			archive_handle->offset += size;
+			return get_header_ar(archive_handle); /* Return next header */
+		}
+#if ENABLE_FEATURE_AR_LONG_FILENAMES
+		if (ar.formatted.name[1] == '/') {
+			/* If the second char is a '/' then this entries data section
+			 * stores long filename for multiple entries, they are stored
+			 * in static variable long_names for use in future entries
+			 */
+			ar_long_name_size = size;
+			free(ar_long_names);
+			ar_long_names = xmalloc(size);
+			xread(archive_handle->src_fd, ar_long_names, size);
+			archive_handle->offset += size;
+			/* Return next header */
+			return get_header_ar(archive_handle);
+		}
+#else
+		bb_error_msg_and_die("long filenames not supported");
+#endif
+	}
+	/* Only size is always present, the rest may be missing in
+	 * 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);
+
+#if ENABLE_FEATURE_AR_LONG_FILENAMES
+	if (ar.formatted.name[0] == '/') {
+		unsigned long_offset;
+
+		/* 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);
+		if (long_offset >= ar_long_name_size) {
+			bb_error_msg_and_die("can't resolve long filename");
+		}
+		typed->name = xstrdup(ar_long_names + long_offset);
+	} else
+#endif
+	{
+		/* short filenames */
+		typed->name = xstrndup(ar.formatted.name, 16);
+	}
+
+	typed->name[strcspn(typed->name, " /")] = '\0';
+
+	if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+		archive_handle->action_header(typed);
+#if ENABLE_DPKG || ENABLE_DPKG_DEB
+		if (archive_handle->dpkg__sub_archive) {
+			while (archive_handle->dpkg__action_data_subarchive(archive_handle->dpkg__sub_archive) == EXIT_SUCCESS)
+				continue;
+		} else
+#endif
+			archive_handle->action_data(archive_handle);
+	} else {
+		data_skip(archive_handle);
+	}
+
+	archive_handle->offset += typed->size;
+	/* Set the file pointer to the correct spot, we may have been reading a compressed file */
+	lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/libarchive/get_header_cpio.c b/busybox-1.19.3/archival/libarchive/get_header_cpio.c
new file mode 100644
index 0000000..3d99b49
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/get_header_cpio.c
@@ -0,0 +1,186 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright 2002 Laurence Anderson
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+typedef struct hardlinks_t {
+	struct hardlinks_t *next;
+	int inode; /* TODO: must match maj/min too! */
+	int mode ;
+	int mtime; /* These three are useful only in corner case */
+	int uid  ; /* of hardlinks with zero size body */
+	int gid  ;
+	char name[1];
+} hardlinks_t;
+
+char FAST_FUNC get_header_cpio(archive_handle_t *archive_handle)
+{
+	file_header_t *file_header = archive_handle->file_header;
+	char cpio_header[110];
+	int namesize;
+	int major, minor, nlink, mode, inode;
+	unsigned size, uid, gid, mtime;
+
+	/* There can be padding before archive header */
+	data_align(archive_handle, 4);
+
+	size = full_read(archive_handle->src_fd, cpio_header, 110);
+	if (size == 0) {
+		goto create_hardlinks;
+	}
+	if (size != 110) {
+		bb_error_msg_and_die("short read");
+	}
+	archive_handle->offset += 110;
+
+	if (strncmp(&cpio_header[0], "07070", 5) != 0
+	 || (cpio_header[5] != '1' && cpio_header[5] != '2')
+	) {
+		bb_error_msg_and_die("unsupported cpio format, use newc or crc");
+	}
+
+	if (sscanf(cpio_header + 6,
+			"%8x" "%8x" "%8x" "%8x"
+			"%8x" "%8x" "%8x" /*maj,min:*/ "%*16c"
+			/*rmaj,rmin:*/"%8x" "%8x" "%8x" /*chksum: "%*8c"*/,
+			&inode, &mode, &uid, &gid,
+			&nlink, &mtime, &size,
+			&major, &minor, &namesize) != 10)
+		bb_error_msg_and_die("damaged cpio file");
+	file_header->mode = mode;
+	file_header->uid = uid;
+	file_header->gid = gid;
+	file_header->mtime = mtime;
+	file_header->size = size;
+
+	namesize &= 0x1fff; /* paranoia: limit names to 8k chars */
+	file_header->name = xzalloc(namesize + 1);
+	/* Read in filename */
+	xread(archive_handle->src_fd, file_header->name, namesize);
+	if (file_header->name[0] == '/') {
+		/* Testcase: echo /etc/hosts | cpio -pvd /tmp
+		 * Without this code, it tries to unpack /etc/hosts
+		 * into "/etc/hosts", not "etc/hosts".
+		 */
+		char *p = file_header->name;
+		do p++; while (*p == '/');
+		overlapping_strcpy(file_header->name, p);
+	}
+	archive_handle->offset += namesize;
+
+	/* Update offset amount and skip padding before file contents */
+	data_align(archive_handle, 4);
+
+	if (strcmp(file_header->name, "TRAILER!!!") == 0) {
+		/* Always round up. ">> 9" divides by 512 */
+		archive_handle->cpio__blocks = (uoff_t)(archive_handle->offset + 511) >> 9;
+		goto create_hardlinks;
+	}
+
+	file_header->link_target = NULL;
+	if (S_ISLNK(file_header->mode)) {
+		file_header->size &= 0x1fff; /* paranoia: limit names to 8k chars */
+		file_header->link_target = xzalloc(file_header->size + 1);
+		xread(archive_handle->src_fd, file_header->link_target, file_header->size);
+		archive_handle->offset += file_header->size;
+		file_header->size = 0; /* Stop possible seeks in future */
+	}
+
+// TODO: data_extract_all can't deal with hardlinks to non-files...
+// when fixed, change S_ISREG to !S_ISDIR here
+
+	if (nlink > 1 && S_ISREG(file_header->mode)) {
+		hardlinks_t *new = xmalloc(sizeof(*new) + namesize);
+		new->inode = inode;
+		new->mode  = mode ;
+		new->mtime = mtime;
+		new->uid   = uid  ;
+		new->gid   = gid  ;
+		strcpy(new->name, file_header->name);
+		/* Put file on a linked list for later */
+		if (size == 0) {
+			new->next = archive_handle->cpio__hardlinks_to_create;
+			archive_handle->cpio__hardlinks_to_create = new;
+			return EXIT_SUCCESS; /* Skip this one */
+			/* TODO: this breaks cpio -t (it does not show hardlinks) */
+		}
+		new->next = archive_handle->cpio__created_hardlinks;
+		archive_handle->cpio__created_hardlinks = new;
+	}
+	file_header->device = makedev(major, minor);
+
+	if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+		archive_handle->action_data(archive_handle);
+//TODO: run "echo /etc/hosts | cpio -pv /tmp" twice. On 2nd run:
+//cpio: etc/hosts not created: newer or same age file exists
+//etc/hosts  <-- should NOT show it
+//2 blocks <-- should say "0 blocks"
+		archive_handle->action_header(file_header);
+	} else {
+		data_skip(archive_handle);
+	}
+
+	archive_handle->offset += file_header->size;
+
+	free(file_header->link_target);
+	free(file_header->name);
+	file_header->link_target = NULL;
+	file_header->name = NULL;
+
+	return EXIT_SUCCESS;
+
+ create_hardlinks:
+	free(file_header->link_target);
+	free(file_header->name);
+
+	while (archive_handle->cpio__hardlinks_to_create) {
+		hardlinks_t *cur;
+		hardlinks_t *make_me = archive_handle->cpio__hardlinks_to_create;
+
+		archive_handle->cpio__hardlinks_to_create = make_me->next;
+
+		memset(file_header, 0, sizeof(*file_header));
+		file_header->mtime = make_me->mtime;
+		file_header->name = make_me->name;
+		file_header->mode = make_me->mode;
+		file_header->uid = make_me->uid;
+		file_header->gid = make_me->gid;
+		/*file_header->size = 0;*/
+		/*file_header->link_target = NULL;*/
+
+		/* Try to find a file we are hardlinked to */
+		cur = archive_handle->cpio__created_hardlinks;
+		while (cur) {
+			/* TODO: must match maj/min too! */
+			if (cur->inode == make_me->inode) {
+				file_header->link_target = cur->name;
+				 /* link_target != NULL, size = 0: "I am a hardlink" */
+				if (archive_handle->filter(archive_handle) == EXIT_SUCCESS)
+					archive_handle->action_data(archive_handle);
+				free(make_me);
+				goto next_link;
+			}
+			cur = cur->next;
+		}
+		/* Oops... no file with such inode was created... do it now
+		 * (happens when hardlinked files are empty (zero length)) */
+		if (archive_handle->filter(archive_handle) == EXIT_SUCCESS)
+			archive_handle->action_data(archive_handle);
+		/* Move to the list of created hardlinked files */
+		make_me->next = archive_handle->cpio__created_hardlinks;
+		archive_handle->cpio__created_hardlinks = make_me;
+ next_link: ;
+	}
+
+	while (archive_handle->cpio__created_hardlinks) {
+		hardlinks_t *p = archive_handle->cpio__created_hardlinks;
+		archive_handle->cpio__created_hardlinks = p->next;
+		free(p);
+	}
+
+	return EXIT_FAILURE; /* "No more files to process" */
+}
diff --git a/busybox-1.19.3/archival/libarchive/get_header_tar.c b/busybox-1.19.3/archival/libarchive/get_header_tar.c
new file mode 100644
index 0000000..79caff5
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/get_header_tar.c
@@ -0,0 +1,497 @@
+/* vi: set sw=4 ts=4: */
+/* Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * 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:
+ *    GNU tar and star man pages,
+ *    Opengroup's ustar interchange format,
+ *    http://www.opengroup.org/onlinepubs/007904975/utilities/pax.html
+ */
+
+#include "libbb.h"
+#include "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)
+{
+	unsigned long long v;
+	char *end;
+	/* NB: leading spaces are allowed. Using strtoull to handle that.
+	 * The downside is that we accept e.g. "-123" too :(
+	 */
+	str[len] = '\0';
+	v = strtoull(str, &end, 8);
+	/* std: "Each numeric field is terminated by one or more
+	 * <space> or NUL characters". We must support ' '! */
+	if (*end != '\0' && *end != ' ') {
+		int8_t first = str[0];
+		if (!(first & 0x80))
+			bb_error_msg_and_die("corrupted octal value in tar header");
+		/*
+		 * 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
+		 *
+		 * Example of tar file with 8914993153 (0x213600001) byte file.
+		 * Field starts at offset 7c:
+		 * 00070  30 30 30 00 30 30 30 30  30 30 30 00 80 00 00 00  |000.0000000.....|
+		 * 00080  00 00 00 02 13 60 00 01  31 31 31 32 30 33 33 36  |.....`..11120336|
+		 *
+		 * 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);
+		while (--len != 0)
+			v = (v << 8) + (unsigned char) *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)
+{
+	char *buf, *p;
+	char *result;
+
+	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;
+
+		/* Every record has this format: "LEN NAME=VALUE\n" */
+		len = bb_strtou(p, &end, 10);
+		/* expect errno to be EINVAL, because the character
+		 * following the digits should be a space
+		 */
+		p += len;
+		sz -= len;
+		if ((int)sz < 0
+		 || len == 0
+		 || errno != EINVAL
+		 || *end != ' '
+		) {
+			bb_error_msg("malformed extended header, skipped");
+			// More verbose version:
+			//bb_error_msg("malformed extended header at %"OFF_FMT"d, skipped",
+			//		archive_handle->offset - (sz + len));
+			break;
+		}
+		/* overwrite the terminating newline with NUL
+		 * (we do not bother to check that it *was* a newline)
+		 */
+		p[-1] = '\0';
+		/* Is it selinux security context? */
+		value = end + 1;
+		if (strncmp(value, SELINUX_CONTEXT_KEYWORD"=", sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1) == 0) {
+			value += sizeof(SELINUX_CONTEXT_KEYWORD"=") - 1;
+			result = xstrdup(value);
+			break;
+		}
+	}
+
+	free(buf);
+	return result;
+}
+#endif
+
+char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
+{
+	file_header_t *file_header = archive_handle->file_header;
+	struct tar_header_t tar;
+	char *cp;
+	int i, sum_u, sum;
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
+	int sum_s;
+#endif
+	int parse_names;
+
+	/* Our "private data" */
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+# define p_longname (archive_handle->tar__longname)
+# define p_linkname (archive_handle->tar__linkname)
+#else
+# define p_longname 0
+# define p_linkname 0
+#endif
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX
+ again:
+#endif
+	/* Align header */
+	data_align(archive_handle, 512);
+
+ again_after_align:
+
+#if ENABLE_DESKTOP || ENABLE_FEATURE_TAR_AUTODETECT
+	/* to prevent misdetection of bz2 sig */
+	*(aliased_uint32_t*)&tar = 0;
+	i = full_read(archive_handle->src_fd, &tar, 512);
+	/* If GNU tar sees EOF in above read, it says:
+	 * "tar: A lone zero block at N", where N = kilobyte
+	 * where EOF was met (not EOF block, actual EOF!),
+	 * and exits with EXIT_SUCCESS.
+	 * We will mimic exit(EXIT_SUCCESS), although we will not mimic
+	 * 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");
+	}
+	if (i != 512) {
+		IF_FEATURE_TAR_AUTODETECT(goto autodetect;)
+		goto short_read;
+	}
+
+#else
+	i = 512;
+	xread(archive_handle->src_fd, &tar, i);
+#endif
+	archive_handle->offset += i;
+
+	/* If there is no filename its an empty header */
+	if (tar.name[0] == 0 && tar.prefix[0] == 0) {
+		if (archive_handle->tar__end) {
+			/* Second consecutive empty header - end of archive.
+			 * Read until the end to empty the pipe from gz or bz2
+			 */
+			while (full_read(archive_handle->src_fd, &tar, 512) == 512)
+				continue;
+			return EXIT_FAILURE;
+		}
+		archive_handle->tar__end = 1;
+		return EXIT_SUCCESS;
+	}
+	archive_handle->tar__end = 0;
+
+	/* Check header has valid magic, "ustar" is for the proper tar,
+	 * five NULs are for the old tar format  */
+	if (strncmp(tar.magic, "ustar", 5) != 0
+	 && (!ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
+	     || 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 = *(bb__aliased_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;
+ err:
+#endif /* FEATURE_TAR_AUTODETECT */
+		bb_error_msg_and_die("invalid tar magic");
+	}
+
+	/* Do checksum on headers.
+	 * POSIX says that checksum is done on unsigned bytes, but
+	 * Sun and HP-UX gets it wrong... more details in
+	 * GNU tar source. */
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
+	sum_s = ' ' * sizeof(tar.chksum);
+#endif
+	sum_u = ' ' * sizeof(tar.chksum);
+	for (i = 0; i < 148; i++) {
+		sum_u += ((unsigned char*)&tar)[i];
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
+		sum_s += ((signed char*)&tar)[i];
+#endif
+	}
+	for (i = 156; i < 512; i++) {
+		sum_u += ((unsigned char*)&tar)[i];
+#if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
+		sum_s += ((signed char*)&tar)[i];
+#endif
+	}
+	/* This field does not need special treatment (getOctal) */
+	{
+		char *endp; /* gcc likes temp var for &endp */
+		sum = strtoul(tar.chksum, &endp, 8);
+		if ((*endp != '\0' && *endp != ' ')
+		 || (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum))
+		) {
+			bb_error_msg_and_die("invalid tar header checksum");
+		}
+	}
+	/* don't use xstrtoul, tar.chksum may have leading spaces */
+	sum = strtoul(tar.chksum, NULL, 8);
+	if (sum_u != sum IF_FEATURE_TAR_OLDSUN_COMPATIBILITY(&& sum_s != sum)) {
+		bb_error_msg_and_die("invalid tar header checksum");
+	}
+
+	/* 0 is reserved for high perf file, treat as normal file */
+	if (!tar.typeflag) tar.typeflag = '0';
+	parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7');
+
+	/* getOctal trashes subsequent field, therefore we call it
+	 * on fields in reverse order */
+	if (tar.devmajor[0]) {
+		char t = tar.prefix[0];
+		/* we trash prefix[0] here, but we DO need it later! */
+		unsigned minor = GET_OCTAL(tar.devminor);
+		unsigned major = GET_OCTAL(tar.devmajor);
+		file_header->device = makedev(major, minor);
+		tar.prefix[0] = t;
+	}
+	file_header->link_target = NULL;
+	if (!p_linkname && parse_names && tar.linkname[0]) {
+		file_header->link_target = xstrndup(tar.linkname, sizeof(tar.linkname));
+		/* FIXME: what if we have non-link object with link_target? */
+		/* Will link_target be free()ed? */
+	}
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+	file_header->tar__uname = tar.uname[0] ? xstrndup(tar.uname, sizeof(tar.uname)) : NULL;
+	file_header->tar__gname = tar.gname[0] ? xstrndup(tar.gname, sizeof(tar.gname)) : NULL;
+#endif
+	file_header->mtime = GET_OCTAL(tar.mtime);
+	file_header->size = GET_OCTAL(tar.size);
+	file_header->gid = GET_OCTAL(tar.gid);
+	file_header->uid = GET_OCTAL(tar.uid);
+	/* Set bits 0-11 of the files mode */
+	file_header->mode = 07777 & GET_OCTAL(tar.mode);
+
+	file_header->name = NULL;
+	if (!p_longname && parse_names) {
+		/* we trash mode[0] here, it's ok */
+		//tar.name[sizeof(tar.name)] = '\0'; - gcc 4.3.0 would complain
+		tar.mode[0] = '\0';
+		if (tar.prefix[0]) {
+			/* and padding[0] */
+			//tar.prefix[sizeof(tar.prefix)] = '\0'; - gcc 4.3.0 would complain
+			tar.padding[0] = '\0';
+			file_header->name = concat_path_file(tar.prefix, tar.name);
+		} else
+			file_header->name = xstrdup(tar.name);
+	}
+
+	/* Set bits 12-15 of the files mode */
+	/* (typeflag was not trashed because chksum does not use getOctal) */
+	switch (tar.typeflag) {
+	case '1': /* hardlink */
+		/* we mark hardlinks as regular files with zero size and a link name */
+		file_header->mode |= S_IFREG;
+		/* 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':
+#if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY
+		if (last_char_is(file_header->name, '/')) {
+			goto set_dir;
+		}
+#endif
+		file_header->mode |= S_IFREG;
+		break;
+	case '2':
+		file_header->mode |= S_IFLNK;
+		/* have seen tarballs with size field containing
+		 * the size of the link target's name */
+ size0:
+		file_header->size = 0;
+		break;
+	case '3':
+		file_header->mode |= S_IFCHR;
+		goto size0; /* paranoia */
+	case '4':
+		file_header->mode |= S_IFBLK;
+		goto size0;
+	case '5':
+ IF_FEATURE_TAR_OLDGNU_COMPATIBILITY(set_dir:)
+		file_header->mode |= S_IFDIR;
+		goto size0;
+	case '6':
+		file_header->mode |= S_IFIFO;
+		goto size0;
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+	case 'L':
+		/* free: paranoia: tar with several consecutive longnames */
+		free(p_longname);
+		/* For paranoia reasons we allocate extra NUL char */
+		p_longname = xzalloc(file_header->size + 1);
+		/* We read ASCIZ string, including NUL */
+		xread(archive_handle->src_fd, p_longname, file_header->size);
+		archive_handle->offset += file_header->size;
+		/* return get_header_tar(archive_handle); */
+		/* gcc 4.1.1 didn't optimize it into jump */
+		/* so we will do it ourself, this also saves stack */
+		goto again;
+	case 'K':
+		free(p_linkname);
+		p_linkname = xzalloc(file_header->size + 1);
+		xread(archive_handle->src_fd, p_linkname, file_header->size);
+		archive_handle->offset += file_header->size;
+		/* return get_header_tar(archive_handle); */
+		goto again;
+	case 'D':	/* GNU dump dir */
+	case 'M':	/* Continuation of multi volume archive */
+	case 'N':	/* Old GNU for names > 100 characters */
+	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
+ skip_ext_hdr:
+#endif
+	{
+		off_t sz;
+		bb_error_msg("warning: skipping header '%c'", tar.typeflag);
+		sz = (file_header->size + 511) & ~(off_t)511;
+		archive_handle->offset += sz;
+		sz >>= 9; /* sz /= 512 but w/o contortions for signed div */
+		while (sz--)
+			xread(archive_handle->src_fd, &tar, 512);
+		/* 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);
+	}
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+	if (p_longname) {
+		file_header->name = p_longname;
+		p_longname = NULL;
+	}
+	if (p_linkname) {
+		file_header->link_target = p_linkname;
+		p_linkname = NULL;
+	}
+#endif
+
+	/* 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 */
+	cp = last_char_is(file_header->name, '/');
+
+	if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
+		archive_handle->action_header(/*archive_handle->*/ file_header);
+		/* Note that we kill the '/' only after action_header() */
+		/* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
+		if (cp)
+			*cp = '\0';
+		archive_handle->action_data(archive_handle);
+		if (archive_handle->accept || archive_handle->reject)
+			llist_add_to(&archive_handle->passed, file_header->name);
+		else /* Caller isn't interested in list of unpacked files */
+			free(file_header->name);
+	} else {
+		data_skip(archive_handle);
+		free(file_header->name);
+	}
+	archive_handle->offset += file_header->size;
+
+	free(file_header->link_target);
+	/* Do not free(file_header->name)!
+	 * It might be inserted in archive_handle->passed - see above */
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+	free(file_header->tar__uname);
+	free(file_header->tar__gname);
+#endif
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/libarchive/get_header_tar_bz2.c b/busybox-1.19.3/archival/libarchive/get_header_tar_bz2.c
new file mode 100644
index 0000000..60d3206
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/get_header_tar_bz2.c
@@ -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 "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");
+	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/busybox-1.19.3/archival/libarchive/get_header_tar_gz.c b/busybox-1.19.3/archival/libarchive/get_header_tar_gz.c
new file mode 100644
index 0000000..889fed0
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/get_header_tar_gz.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+char FAST_FUNC get_header_tar_gz(archive_handle_t *archive_handle)
+{
+#if BB_MMU
+	uint16_t magic;
+#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 != GZIP_MAGIC) {
+		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/busybox-1.19.3/archival/libarchive/get_header_tar_lzma.c b/busybox-1.19.3/archival/libarchive/get_header_tar_lzma.c
new file mode 100644
index 0000000..da08e0c
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/get_header_tar_lzma.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Small lzma deflate implementation.
+ * Copyright (C) 2006  Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "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");
+	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/busybox-1.19.3/archival/libarchive/header_list.c b/busybox-1.19.3/archival/libarchive/header_list.c
new file mode 100644
index 0000000..c4fc75f
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/header_list.c
@@ -0,0 +1,12 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC header_list(const file_header_t *file_header)
+{
+//TODO: cpio -vp DIR should output "DIR/NAME", not just "NAME" */
+	puts(file_header->name);
+}
diff --git a/busybox-1.19.3/archival/libarchive/header_skip.c b/busybox-1.19.3/archival/libarchive/header_skip.c
new file mode 100644
index 0000000..2bfc525
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/header_skip.c
@@ -0,0 +1,10 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC header_skip(const file_header_t *file_header UNUSED_PARAM)
+{
+}
diff --git a/busybox-1.19.3/archival/libarchive/header_verbose_list.c b/busybox-1.19.3/archival/libarchive/header_verbose_list.c
new file mode 100644
index 0000000..bc4e415
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/header_verbose_list.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC header_verbose_list(const file_header_t *file_header)
+{
+	struct tm tm_time;
+	struct tm *ptm = &tm_time; //localtime(&file_header->mtime);
+
+#if ENABLE_FEATURE_TAR_UNAME_GNAME
+	char uid[sizeof(int)*3 + 2];
+	/*char gid[sizeof(int)*3 + 2];*/
+	char *user;
+	char *group;
+
+	localtime_r(&file_header->mtime, ptm);
+
+	user = file_header->tar__uname;
+	if (user == NULL) {
+		sprintf(uid, "%u", (unsigned)file_header->uid);
+		user = uid;
+	}
+	group = file_header->tar__gname;
+	if (group == NULL) {
+		/*sprintf(gid, "%u", (unsigned)file_header->gid);*/
+		group = utoa(file_header->gid);
+	}
+	printf("%s %s/%s %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s",
+		bb_mode_string(file_header->mode),
+		user,
+		group,
+		file_header->size,
+		1900 + ptm->tm_year,
+		1 + ptm->tm_mon,
+		ptm->tm_mday,
+		ptm->tm_hour,
+		ptm->tm_min,
+		ptm->tm_sec,
+		file_header->name);
+
+#else /* !FEATURE_TAR_UNAME_GNAME */
+
+	localtime_r(&file_header->mtime, ptm);
+
+	printf("%s %u/%u %9"OFF_FMT"u %4u-%02u-%02u %02u:%02u:%02u %s",
+		bb_mode_string(file_header->mode),
+		(unsigned)file_header->uid,
+		(unsigned)file_header->gid,
+		file_header->size,
+		1900 + ptm->tm_year,
+		1 + ptm->tm_mon,
+		ptm->tm_mday,
+		ptm->tm_hour,
+		ptm->tm_min,
+		ptm->tm_sec,
+		file_header->name);
+
+#endif /* FEATURE_TAR_UNAME_GNAME */
+
+	/* NB: GNU tar shows "->" for symlinks and "link to" for hardlinks */
+	if (file_header->link_target) {
+		printf(" -> %s", file_header->link_target);
+	}
+	bb_putchar('\n');
+}
diff --git a/busybox-1.19.3/archival/libarchive/init_handle.c b/busybox-1.19.3/archival/libarchive/init_handle.c
new file mode 100644
index 0000000..6644ea1
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/init_handle.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+archive_handle_t* FAST_FUNC init_handle(void)
+{
+	archive_handle_t *archive_handle;
+
+	/* Initialize default values */
+	archive_handle = xzalloc(sizeof(archive_handle_t));
+	archive_handle->file_header = xzalloc(sizeof(file_header_t));
+	archive_handle->action_header = header_skip;
+	archive_handle->action_data = data_skip;
+	archive_handle->filter = filter_accept_all;
+	archive_handle->seek = seek_by_jump;
+
+	return archive_handle;
+}
diff --git a/busybox-1.19.3/archival/libarchive/liblzo.h b/busybox-1.19.3/archival/libarchive/liblzo.h
new file mode 100644
index 0000000..843997c
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/liblzo.h
@@ -0,0 +1,93 @@
+/*
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   Markus F.X.J. Oberhumer <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzo/
+
+   The LZO library 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.
+
+   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
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "liblzo_interface.h"
+
+/* lzo-2.03/src/config1x.h */
+#define M2_MIN_LEN      3
+#define M2_MAX_LEN      8
+#define M3_MAX_LEN      33
+#define M4_MAX_LEN      9
+#define M1_MAX_OFFSET   0x0400
+#define M2_MAX_OFFSET   0x0800
+#define M3_MAX_OFFSET   0x4000
+#define M4_MAX_OFFSET   0xbfff
+#define M1_MARKER       0
+#define M3_MARKER       32
+#define M4_MARKER       16
+
+#define MX_MAX_OFFSET   (M1_MAX_OFFSET + M2_MAX_OFFSET)
+#define MIN_LOOKAHEAD   (M2_MAX_LEN + 1)
+
+#define LZO_EOF_CODE
+
+/* lzo-2.03/src/lzo_dict.h */
+#define GINDEX(m_pos,m_off,dict,dindex,in)    m_pos = dict[dindex]
+#define DX2(p,s1,s2) \
+        (((((unsigned)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0])
+//#define DA3(p,s1,s2,s3) ((DA2((p)+1,s2,s3) << (s1)) + (p)[0])
+//#define DS3(p,s1,s2,s3) ((DS2((p)+1,s2,s3) << (s1)) - (p)[0])
+#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0])
+
+#define D_SIZE        (1U << D_BITS)
+#define D_MASK        ((1U << D_BITS) - 1)
+#define D_HIGH        ((D_MASK >> 1) + 1)
+
+#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
+    ( \
+        m_pos = ip - (unsigned)(ip - m_pos), \
+        ((uintptr_t)m_pos < (uintptr_t)in \
+	|| (m_off = (unsigned)(ip - m_pos)) <= 0 \
+	|| m_off > max_offset) \
+    )
+
+#define DENTRY(p,in)                      (p)
+#define UPDATE_I(dict,drun,index,p,in)    dict[index] = DENTRY(p,in)
+
+#define DMS(v,s)  ((unsigned) (((v) & (D_MASK >> (s))) << (s)))
+#define DM(v)     ((unsigned) ((v) & D_MASK))
+#define DMUL(a,b) ((unsigned) ((a) * (b)))
+
+/* lzo-2.03/src/lzo_ptr.h */
+#define pd(a,b)  ((unsigned)((a)-(b)))
+
+#    define TEST_IP             (ip < ip_end)
+#    define NEED_IP(x) \
+            if ((unsigned)(ip_end - ip) < (unsigned)(x))  goto input_overrun
+
+#    undef TEST_OP              /* don't need both of the tests here */
+#    define TEST_OP             1
+#    define NEED_OP(x) \
+            if ((unsigned)(op_end - op) < (unsigned)(x))  goto output_overrun
+
+#define HAVE_ANY_OP 1
+
+//#if defined(LZO_TEST_OVERRUN_LOOKBEHIND)
+#  define TEST_LB(m_pos)        if (m_pos < out || m_pos >= op) goto lookbehind_overrun
+//#  define TEST_LBO(m_pos,o)     if (m_pos < out || m_pos >= op - (o)) goto lookbehind_overrun
+//#else
+//#  define TEST_LB(m_pos)        ((void) 0)
+//#  define TEST_LBO(m_pos,o)     ((void) 0)
+//#endif
diff --git a/busybox-1.19.3/archival/libarchive/lzo1x_1.c b/busybox-1.19.3/archival/libarchive/lzo1x_1.c
new file mode 100644
index 0000000..a888398
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/lzo1x_1.c
@@ -0,0 +1,35 @@
+/* LZO1X-1 compression
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   Markus F.X.J. Oberhumer <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzo/
+
+   The LZO library 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.
+
+   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
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "libbb.h"
+#include "liblzo.h"
+
+#define D_BITS          14
+#define D_INDEX1(d,p)   d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5)
+#define D_INDEX2(d,p)   d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f)
+
+#define DO_COMPRESS     lzo1x_1_compress
+
+#include "lzo1x_c.c"
diff --git a/busybox-1.19.3/archival/libarchive/lzo1x_1o.c b/busybox-1.19.3/archival/libarchive/lzo1x_1o.c
new file mode 100644
index 0000000..3c61253
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/lzo1x_1o.c
@@ -0,0 +1,35 @@
+/* LZO1X-1(15) compression
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   Markus F.X.J. Oberhumer <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzo/
+
+   The LZO library 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.
+
+   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
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "libbb.h"
+#include "liblzo.h"
+
+#define D_BITS          15
+#define D_INDEX1(d,p)   d = DM(DMUL(0x21,DX3(p,5,5,6)) >> 5)
+#define D_INDEX2(d,p)   d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f)
+
+#define DO_COMPRESS     lzo1x_1_15_compress
+
+#include "lzo1x_c.c"
diff --git a/busybox-1.19.3/archival/libarchive/lzo1x_9x.c b/busybox-1.19.3/archival/libarchive/lzo1x_9x.c
new file mode 100644
index 0000000..4832051
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/lzo1x_9x.c
@@ -0,0 +1,921 @@
+/* lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 2008 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2007 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2006 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2005 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2004 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2003 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2002 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2001 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 2000 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1999 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1998 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1997 Markus Franz Xaver Johannes Oberhumer
+   Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   The LZO library 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.
+
+   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
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+   Markus F.X.J. Oberhumer
+   <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzo/
+*/
+#include "libbb.h"
+
+/* The following is probably only safe on Intel-compatible processors ... */
+#define LZO_UNALIGNED_OK_2
+#define LZO_UNALIGNED_OK_4
+
+#include "liblzo.h"
+
+#define LZO_MAX(a,b)        ((a) >= (b) ? (a) : (b))
+#define LZO_MIN(a,b)        ((a) <= (b) ? (a) : (b))
+#define LZO_MAX3(a,b,c)     ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c))
+
+/***********************************************************************
+//
+************************************************************************/
+#define SWD_N           M4_MAX_OFFSET   /* size of ring buffer */
+#define SWD_F           2048           /* upper limit for match length */
+
+#define SWD_BEST_OFF    (LZO_MAX3(M2_MAX_LEN, M3_MAX_LEN, M4_MAX_LEN) + 1)
+
+typedef struct {
+	int init;
+
+	unsigned look;          /* bytes in lookahead buffer */
+
+	unsigned m_len;
+	unsigned m_off;
+
+	const uint8_t *bp;
+	const uint8_t *ip;
+	const uint8_t *in;
+	const uint8_t *in_end;
+	uint8_t *out;
+
+	unsigned r1_lit;
+
+} lzo1x_999_t;
+
+#define getbyte(c)  ((c).ip < (c).in_end ? *((c).ip)++ : (-1))
+
+/* lzo_swd.c -- sliding window dictionary */
+
+/***********************************************************************
+//
+************************************************************************/
+#define SWD_UINT_MAX      USHRT_MAX
+
+#ifndef SWD_HSIZE
+#  define SWD_HSIZE         16384
+#endif
+#ifndef SWD_MAX_CHAIN
+#  define SWD_MAX_CHAIN     2048
+#endif
+
+#define HEAD3(b, p) \
+	( ((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]))
+#else
+#  define HEAD2(b,p)      (b[p] ^ ((unsigned)b[p+1]<<8))
+#endif
+#define NIL2              SWD_UINT_MAX
+
+typedef struct lzo_swd {
+	/* public - "built-in" */
+
+	/* public - configuration */
+	unsigned max_chain;
+	int use_best_off;
+
+	/* public - output */
+	unsigned m_len;
+	unsigned m_off;
+	unsigned look;
+	int b_char;
+#if defined(SWD_BEST_OFF)
+	unsigned best_off[SWD_BEST_OFF];
+#endif
+
+	/* semi public */
+	lzo1x_999_t *c;
+	unsigned m_pos;
+#if defined(SWD_BEST_OFF)
+	unsigned best_pos[SWD_BEST_OFF];
+#endif
+
+	/* private */
+	unsigned ip;                /* input pointer (lookahead) */
+	unsigned bp;                /* buffer pointer */
+	unsigned rp;                /* remove pointer */
+
+	unsigned node_count;
+	unsigned first_rp;
+
+	uint8_t b[SWD_N + SWD_F];
+	uint8_t b_wrap[SWD_F]; /* must follow b */
+	uint16_t head3[SWD_HSIZE];
+	uint16_t succ3[SWD_N + SWD_F];
+	uint16_t best3[SWD_N + SWD_F];
+	uint16_t llen3[SWD_HSIZE];
+#ifdef HEAD2
+	uint16_t head2[65536L];
+#endif
+} lzo_swd_t, *lzo_swd_p;
+
+#define SIZEOF_LZO_SWD_T    (sizeof(lzo_swd_t))
+
+
+/* Access macro for head3.
+ * head3[key] may be uninitialized, but then its value will never be used.
+ */
+#define s_get_head3(s,key)    s->head3[key]
+
+
+/***********************************************************************
+//
+************************************************************************/
+#define B_SIZE (SWD_N + SWD_F)
+
+static int swd_init(lzo_swd_p s)
+{
+	/* defaults */
+	s->node_count = SWD_N;
+
+	memset(s->llen3, 0, sizeof(s->llen3[0]) * (unsigned)SWD_HSIZE);
+#ifdef HEAD2
+	memset(s->head2, 0xff, sizeof(s->head2[0]) * 65536L);
+	assert(s->head2[0] == NIL2);
+#endif
+
+	s->ip = 0;
+	s->bp = s->ip;
+	s->first_rp = s->ip;
+
+	assert(s->ip + SWD_F <= B_SIZE);
+	s->look = (unsigned) (s->c->in_end - s->c->ip);
+	if (s->look > 0) {
+		if (s->look > SWD_F)
+			s->look = SWD_F;
+		memcpy(&s->b[s->ip], s->c->ip, s->look);
+		s->c->ip += s->look;
+		s->ip += s->look;
+	}
+	if (s->ip == B_SIZE)
+		s->ip = 0;
+
+	s->rp = s->first_rp;
+	if (s->rp >= s->node_count)
+		s->rp -= s->node_count;
+	else
+		s->rp += B_SIZE - s->node_count;
+
+	return LZO_E_OK;
+}
+
+#define swd_pos2off(s,pos) \
+	(s->bp > (pos) ? s->bp - (pos) : B_SIZE - ((pos) - s->bp))
+
+
+/***********************************************************************
+//
+************************************************************************/
+static void swd_getbyte(lzo_swd_p s)
+{
+	int c;
+
+	if ((c = getbyte(*(s->c))) < 0) {
+		if (s->look > 0)
+			--s->look;
+	} else {
+		s->b[s->ip] = c;
+		if (s->ip < SWD_F)
+			s->b_wrap[s->ip] = c;
+	}
+	if (++s->ip == B_SIZE)
+		s->ip = 0;
+	if (++s->bp == B_SIZE)
+		s->bp = 0;
+	if (++s->rp == B_SIZE)
+		s->rp = 0;
+}
+
+
+/***********************************************************************
+// remove node from lists
+************************************************************************/
+static void swd_remove_node(lzo_swd_p s, unsigned node)
+{
+	if (s->node_count == 0) {
+		unsigned key;
+
+		key = HEAD3(s->b,node);
+		assert(s->llen3[key] > 0);
+		--s->llen3[key];
+
+#ifdef HEAD2
+		key = HEAD2(s->b,node);
+		assert(s->head2[key] != NIL2);
+		if ((unsigned) s->head2[key] == node)
+			s->head2[key] = NIL2;
+#endif
+	} else
+		--s->node_count;
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+static void swd_accept(lzo_swd_p s, unsigned n)
+{
+	assert(n <= s->look);
+
+	while (n--) {
+		unsigned key;
+
+		swd_remove_node(s,s->rp);
+
+		/* add bp into HEAD3 */
+		key = HEAD3(s->b, s->bp);
+		s->succ3[s->bp] = s_get_head3(s, key);
+		s->head3[key] = s->bp;
+		s->best3[s->bp] = SWD_F + 1;
+		s->llen3[key]++;
+		assert(s->llen3[key] <= SWD_N);
+
+#ifdef HEAD2
+		/* add bp into HEAD2 */
+		key = HEAD2(s->b, s->bp);
+		s->head2[key] = s->bp;
+#endif
+
+		swd_getbyte(s);
+	}
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+static void swd_search(lzo_swd_p s, unsigned node, unsigned cnt)
+{
+	const uint8_t *p1;
+	const uint8_t *p2;
+	const uint8_t *px;
+	unsigned m_len = s->m_len;
+	const uint8_t *b  = s->b;
+	const uint8_t *bp = s->b + s->bp;
+	const uint8_t *bx = s->b + s->bp + s->look;
+	unsigned char scan_end1;
+
+	assert(s->m_len > 0);
+
+	scan_end1 = bp[m_len - 1];
+	for ( ; cnt-- > 0; node = s->succ3[node]) {
+		p1 = bp;
+		p2 = b + node;
+		px = bx;
+
+		assert(m_len < s->look);
+
+		if (p2[m_len - 1] == scan_end1
+		 && p2[m_len] == p1[m_len]
+		 && p2[0] == p1[0]
+		 && p2[1] == p1[1]
+		) {
+			unsigned i;
+			assert(lzo_memcmp(bp, &b[node], 3) == 0);
+
+			p1 += 2; p2 += 2;
+			do {} while (++p1 < px && *p1 == *++p2);
+			i = p1-bp;
+
+			assert(lzo_memcmp(bp, &b[node], i) == 0);
+
+#if defined(SWD_BEST_OFF)
+			if (i < SWD_BEST_OFF) {
+				if (s->best_pos[i] == 0)
+					s->best_pos[i] = node + 1;
+			}
+#endif
+			if (i > m_len) {
+				s->m_len = m_len = i;
+				s->m_pos = node;
+				if (m_len == s->look)
+					return;
+				if (m_len >= SWD_F)
+					return;
+				if (m_len > (unsigned) s->best3[node])
+					return;
+				scan_end1 = bp[m_len - 1];
+			}
+		}
+	}
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+#ifdef HEAD2
+
+static int swd_search2(lzo_swd_p s)
+{
+	unsigned key;
+
+	assert(s->look >= 2);
+	assert(s->m_len > 0);
+
+	key = s->head2[HEAD2(s->b, s->bp)];
+	if (key == NIL2)
+		return 0;
+	assert(lzo_memcmp(&s->b[s->bp], &s->b[key], 2) == 0);
+#if defined(SWD_BEST_OFF)
+	if (s->best_pos[2] == 0)
+		s->best_pos[2] = key + 1;
+#endif
+
+	if (s->m_len < 2) {
+		s->m_len = 2;
+		s->m_pos = key;
+	}
+	return 1;
+}
+
+#endif
+
+
+/***********************************************************************
+//
+************************************************************************/
+static void swd_findbest(lzo_swd_p s)
+{
+	unsigned key;
+	unsigned cnt, node;
+	unsigned len;
+
+	assert(s->m_len > 0);
+
+	/* get current head, add bp into HEAD3 */
+	key = HEAD3(s->b,s->bp);
+	node = s->succ3[s->bp] = s_get_head3(s, key);
+	cnt = s->llen3[key]++;
+	assert(s->llen3[key] <= SWD_N + SWD_F);
+	if (cnt > s->max_chain)
+		cnt = s->max_chain;
+	s->head3[key] = s->bp;
+
+	s->b_char = s->b[s->bp];
+	len = s->m_len;
+	if (s->m_len >= s->look) {
+		if (s->look == 0)
+			s->b_char = -1;
+		s->m_off = 0;
+		s->best3[s->bp] = SWD_F + 1;
+	} else {
+#ifdef HEAD2
+		if (swd_search2(s))
+#endif
+			if (s->look >= 3)
+				swd_search(s, node, cnt);
+		if (s->m_len > len)
+			s->m_off = swd_pos2off(s,s->m_pos);
+		s->best3[s->bp] = s->m_len;
+
+#if defined(SWD_BEST_OFF)
+		if (s->use_best_off) {
+			int i;
+			for (i = 2; i < SWD_BEST_OFF; i++) {
+				if (s->best_pos[i] > 0)
+					s->best_off[i] = swd_pos2off(s, s->best_pos[i]-1);
+				else
+					s->best_off[i] = 0;
+			}
+		}
+#endif
+	}
+
+	swd_remove_node(s,s->rp);
+
+#ifdef HEAD2
+	/* add bp into HEAD2 */
+	key = HEAD2(s->b, s->bp);
+	s->head2[key] = s->bp;
+#endif
+}
+
+#undef HEAD3
+#undef HEAD2
+#undef s_get_head3
+
+
+/***********************************************************************
+//
+************************************************************************/
+static int init_match(lzo1x_999_t *c, lzo_swd_p s, uint32_t use_best_off)
+{
+	int r;
+
+	assert(!c->init);
+	c->init = 1;
+
+	s->c = c;
+
+	r = swd_init(s);
+	if (r != 0)
+		return r;
+
+	s->use_best_off = use_best_off;
+	return r;
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+static int find_match(lzo1x_999_t *c, lzo_swd_p s,
+		unsigned this_len, unsigned skip)
+{
+	assert(c->init);
+
+	if (skip > 0) {
+		assert(this_len >= skip);
+		swd_accept(s, this_len - skip);
+	} else {
+		assert(this_len <= 1);
+	}
+
+	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));
+#endif
+	swd_findbest(s);
+	c->m_len = s->m_len;
+	c->m_off = s->m_off;
+
+	swd_getbyte(s);
+
+	if (s->b_char < 0) {
+		c->look = 0;
+		c->m_len = 0;
+	} else {
+		c->look = s->look + 1;
+	}
+	c->bp = c->ip - c->look;
+
+	return LZO_E_OK;
+}
+
+/* this is a public functions, but there is no prototype in a header file */
+static int lzo1x_999_compress_internal(const uint8_t *in , unsigned in_len,
+		uint8_t *out, unsigned *out_len,
+		void *wrkmem,
+		unsigned good_length,
+		unsigned max_lazy,
+		unsigned max_chain,
+		uint32_t use_best_off);
+
+
+/***********************************************************************
+//
+************************************************************************/
+static uint8_t *code_match(lzo1x_999_t *c,
+		uint8_t *op, unsigned m_len, unsigned m_off)
+{
+	assert(op > c->out);
+	if (m_len == 2) {
+		assert(m_off <= M1_MAX_OFFSET);
+		assert(c->r1_lit > 0);
+		assert(c->r1_lit < 4);
+		m_off -= 1;
+		*op++ = M1_MARKER | ((m_off & 3) << 2);
+		*op++ = m_off >> 2;
+	} else if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) {
+		assert(m_len >= 3);
+		m_off -= 1;
+		*op++ = ((m_len - 1) << 5) | ((m_off & 7) << 2);
+		*op++ = m_off >> 3;
+		assert(op[-2] >= M2_MARKER);
+	} else if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && c->r1_lit >= 4) {
+		assert(m_len == 3);
+		assert(m_off > M2_MAX_OFFSET);
+		m_off -= 1 + M2_MAX_OFFSET;
+		*op++ = M1_MARKER | ((m_off & 3) << 2);
+		*op++ = m_off >> 2;
+	} else if (m_off <= M3_MAX_OFFSET) {
+		assert(m_len >= 3);
+		m_off -= 1;
+		if (m_len <= M3_MAX_LEN)
+			*op++ = M3_MARKER | (m_len - 2);
+		else {
+			m_len -= M3_MAX_LEN;
+			*op++ = M3_MARKER | 0;
+			while (m_len > 255) {
+				m_len -= 255;
+				*op++ = 0;
+			}
+			assert(m_len > 0);
+			*op++ = m_len;
+		}
+		*op++ = m_off << 2;
+		*op++ = m_off >> 6;
+	} else {
+		unsigned k;
+
+		assert(m_len >= 3);
+		assert(m_off > 0x4000);
+		assert(m_off <= 0xbfff);
+		m_off -= 0x4000;
+		k = (m_off & 0x4000) >> 11;
+		if (m_len <= M4_MAX_LEN)
+			*op++ = M4_MARKER | k | (m_len - 2);
+		else {
+			m_len -= M4_MAX_LEN;
+			*op++ = M4_MARKER | k | 0;
+			while (m_len > 255) {
+				m_len -= 255;
+				*op++ = 0;
+			}
+			assert(m_len > 0);
+			*op++ = m_len;
+		}
+		*op++ = m_off << 2;
+		*op++ = m_off >> 6;
+	}
+
+	return op;
+}
+
+
+static uint8_t *STORE_RUN(lzo1x_999_t *c, uint8_t *op,
+		const uint8_t *ii, unsigned t)
+{
+	if (op == c->out && t <= 238) {
+		*op++ = 17 + t;
+	} else if (t <= 3) {
+		op[-2] |= t;
+	} else if (t <= 18) {
+		*op++ = t - 3;
+	} else {
+		unsigned tt = t - 18;
+
+		*op++ = 0;
+		while (tt > 255) {
+			tt -= 255;
+			*op++ = 0;
+		}
+		assert(tt > 0);
+		*op++ = tt;
+	}
+	do *op++ = *ii++; while (--t > 0);
+
+	return op;
+}
+
+
+static uint8_t *code_run(lzo1x_999_t *c, uint8_t *op, const uint8_t *ii,
+		unsigned lit)
+{
+	if (lit > 0) {
+		assert(m_len >= 2);
+		op = STORE_RUN(c, op, ii, lit);
+	} else {
+		assert(m_len >= 3);
+	}
+	c->r1_lit = lit;
+
+	return op;
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+static int len_of_coded_match(unsigned m_len, unsigned m_off, unsigned lit)
+{
+	int n = 4;
+
+	if (m_len < 2)
+		return -1;
+	if (m_len == 2)
+		return (m_off <= M1_MAX_OFFSET && lit > 0 && lit < 4) ? 2 : -1;
+	if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET)
+		return 2;
+	if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && lit >= 4)
+		return 2;
+	if (m_off <= M3_MAX_OFFSET) {
+		if (m_len <= M3_MAX_LEN)
+			return 3;
+		m_len -= M3_MAX_LEN;
+	} else if (m_off <= M4_MAX_OFFSET) {
+		if (m_len <= M4_MAX_LEN)
+			return 3;
+		m_len -= M4_MAX_LEN;
+	} else
+		return -1;
+	while (m_len > 255) {
+		m_len -= 255;
+		n++;
+	}
+	return n;
+}
+
+
+static int min_gain(unsigned ahead, unsigned lit1,
+		    unsigned lit2, int l1, int l2, int l3)
+{
+	int lazy_match_min_gain = 0;
+
+	assert (ahead >= 1);
+	lazy_match_min_gain += ahead;
+
+	if (lit1 <= 3)
+		lazy_match_min_gain += (lit2 <= 3) ? 0 : 2;
+	else if (lit1 <= 18)
+		lazy_match_min_gain += (lit2 <= 18) ? 0 : 1;
+
+	lazy_match_min_gain += (l2 - l1) * 2;
+	if (l3 > 0)
+		lazy_match_min_gain -= (ahead - l3) * 2;
+
+	if (lazy_match_min_gain < 0)
+		lazy_match_min_gain = 0;
+
+	return lazy_match_min_gain;
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+#if defined(SWD_BEST_OFF)
+
+static void better_match(const lzo_swd_p swd,
+			   unsigned *m_len, unsigned *m_off)
+{
+	if (*m_len <= M2_MIN_LEN)
+		return;
+
+	if (*m_off <= M2_MAX_OFFSET)
+		return;
+
+	/* M3/M4 -> M2 */
+	if (*m_off > M2_MAX_OFFSET
+	 && *m_len >= M2_MIN_LEN + 1 && *m_len <= M2_MAX_LEN + 1
+	 && swd->best_off[*m_len-1] && swd->best_off[*m_len-1] <= M2_MAX_OFFSET
+	) {
+		*m_len = *m_len - 1;
+		*m_off = swd->best_off[*m_len];
+		return;
+	}
+
+	/* M4 -> M2 */
+	if (*m_off > M3_MAX_OFFSET
+	 && *m_len >= M4_MAX_LEN + 1 && *m_len <= M2_MAX_LEN + 2
+	 && swd->best_off[*m_len-2] && swd->best_off[*m_len-2] <= M2_MAX_OFFSET
+	) {
+		*m_len = *m_len - 2;
+		*m_off = swd->best_off[*m_len];
+		return;
+	}
+	/* M4 -> M3 */
+	if (*m_off > M3_MAX_OFFSET
+	 && *m_len >= M4_MAX_LEN + 1 && *m_len <= M3_MAX_LEN + 1
+	 && swd->best_off[*m_len-1] && swd->best_off[*m_len-1] <= M3_MAX_OFFSET
+	) {
+		*m_len = *m_len - 1;
+		*m_off = swd->best_off[*m_len];
+	}
+}
+
+#endif
+
+
+/***********************************************************************
+//
+************************************************************************/
+static int lzo1x_999_compress_internal(const uint8_t *in, unsigned in_len,
+		uint8_t *out, unsigned *out_len,
+		void *wrkmem,
+		unsigned good_length,
+		unsigned max_lazy,
+		unsigned max_chain,
+		uint32_t use_best_off)
+{
+	uint8_t *op;
+	const uint8_t *ii;
+	unsigned lit;
+	unsigned m_len, m_off;
+	lzo1x_999_t cc;
+	lzo1x_999_t *const c = &cc;
+	const lzo_swd_p swd = (lzo_swd_p) wrkmem;
+	int r;
+
+	c->init = 0;
+	c->ip = c->in = in;
+	c->in_end = in + in_len;
+	c->out = out;
+
+	op = out;
+	ii = c->ip;             /* point to start of literal run */
+	lit = 0;
+	c->r1_lit = 0;
+
+	r = init_match(c, swd, use_best_off);
+	if (r != 0)
+		return r;
+	swd->max_chain = max_chain;
+
+	r = find_match(c, swd, 0, 0);
+	if (r != 0)
+		return r;
+
+	while (c->look > 0) {
+		unsigned ahead;
+		unsigned max_ahead;
+		int l1, l2, l3;
+
+		m_len = c->m_len;
+		m_off = c->m_off;
+
+		assert(c->bp == c->ip - c->look);
+		assert(c->bp >= in);
+		if (lit == 0)
+			ii = c->bp;
+		assert(ii + lit == c->bp);
+		assert(swd->b_char == *(c->bp));
+
+		if (m_len < 2
+		 || (m_len == 2 && (m_off > M1_MAX_OFFSET || lit == 0 || lit >= 4))
+		    /* Do not accept this match for compressed-data compatibility
+		     * with LZO v1.01 and before
+		     * [ might be a problem for decompress() and optimize() ]
+		     */
+		 || (m_len == 2 && op == out)
+		 || (op == out && lit == 0)
+		) {
+			/* a literal */
+			m_len = 0;
+		}
+		else if (m_len == M2_MIN_LEN) {
+			/* compression ratio improves if we code a literal in some cases */
+			if (m_off > MX_MAX_OFFSET && lit >= 4)
+				m_len = 0;
+		}
+
+		if (m_len == 0) {
+			/* a literal */
+			lit++;
+			swd->max_chain = max_chain;
+			r = find_match(c, swd, 1, 0);
+			assert(r == 0);
+			continue;
+		}
+
+		/* a match */
+#if defined(SWD_BEST_OFF)
+		if (swd->use_best_off)
+			better_match(swd, &m_len, &m_off);
+#endif
+
+		/* shall we try a lazy match ? */
+		ahead = 0;
+		if (m_len >= max_lazy) {
+			/* no */
+			l1 = 0;
+			max_ahead = 0;
+		} else {
+			/* yes, try a lazy match */
+			l1 = len_of_coded_match(m_len, m_off, lit);
+			assert(l1 > 0);
+			max_ahead = LZO_MIN(2, (unsigned)l1 - 1);
+		}
+
+
+		while (ahead < max_ahead && c->look > m_len) {
+			int lazy_match_min_gain;
+
+			if (m_len >= good_length)
+				swd->max_chain = max_chain >> 2;
+			else
+				swd->max_chain = max_chain;
+			r = find_match(c, swd, 1, 0);
+			ahead++;
+
+			assert(r == 0);
+			assert(c->look > 0);
+			assert(ii + lit + ahead == c->bp);
+
+			if (c->m_len < m_len)
+				continue;
+			if (c->m_len == m_len && c->m_off >= m_off)
+				continue;
+#if defined(SWD_BEST_OFF)
+			if (swd->use_best_off)
+				better_match(swd, &c->m_len, &c->m_off);
+#endif
+			l2 = len_of_coded_match(c->m_len, c->m_off, lit+ahead);
+			if (l2 < 0)
+				continue;
+
+			/* compressed-data compatibility [see above] */
+			l3 = (op == out) ? -1 : len_of_coded_match(ahead, m_off, lit);
+
+			lazy_match_min_gain = min_gain(ahead, lit, lit+ahead, l1, l2, l3);
+			if (c->m_len >= m_len + lazy_match_min_gain) {
+				if (l3 > 0) {
+					/* code previous run */
+					op = code_run(c, op, ii, lit);
+					lit = 0;
+					/* code shortened match */
+					op = code_match(c, op, ahead, m_off);
+				} else {
+					lit += ahead;
+					assert(ii + lit == c->bp);
+				}
+				goto lazy_match_done;
+			}
+		}
+
+		assert(ii + lit + ahead == c->bp);
+
+		/* 1 - code run */
+		op = code_run(c, op, ii, lit);
+		lit = 0;
+
+		/* 2 - code match */
+		op = code_match(c, op, m_len, m_off);
+		swd->max_chain = max_chain;
+		r = find_match(c, swd, m_len, 1+ahead);
+		assert(r == 0);
+
+ lazy_match_done: ;
+	}
+
+	/* store final run */
+	if (lit > 0)
+		op = STORE_RUN(c, op, ii, lit);
+
+#if defined(LZO_EOF_CODE)
+	*op++ = M4_MARKER | 1;
+	*op++ = 0;
+	*op++ = 0;
+#endif
+
+	*out_len = op - out;
+
+	return LZO_E_OK;
+}
+
+
+/***********************************************************************
+//
+************************************************************************/
+int lzo1x_999_compress_level(const uint8_t *in, unsigned in_len,
+		uint8_t *out, unsigned *out_len,
+		void *wrkmem,
+		int compression_level)
+{
+	static const struct {
+		uint16_t good_length;
+		uint16_t max_lazy;
+		uint16_t max_chain;
+		uint16_t use_best_off;
+	} c[3] = {
+		{     8,    32,  256,   0 },
+		{    32,   128, 2048,   1 },
+		{ SWD_F, SWD_F, 4096,   1 }       /* max. compression */
+	};
+
+	if (compression_level < 7 || compression_level > 9)
+		return LZO_E_ERROR;
+
+	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);
+}
diff --git a/busybox-1.19.3/archival/libarchive/lzo1x_c.c b/busybox-1.19.3/archival/libarchive/lzo1x_c.c
new file mode 100644
index 0000000..cc86f74
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/lzo1x_c.c
@@ -0,0 +1,296 @@
+/* implementation of the LZO1[XY]-1 compression algorithm
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   Markus F.X.J. Oberhumer <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzo/
+
+   The LZO library 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.
+
+   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
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/***********************************************************************
+// compress a block of data.
+************************************************************************/
+static NOINLINE unsigned
+do_compress(const uint8_t* in, unsigned in_len,
+		uint8_t* out, unsigned* out_len,
+		void* wrkmem)
+{
+	register const uint8_t* ip;
+	uint8_t* op;
+	const uint8_t* const in_end = in + in_len;
+	const uint8_t* const ip_end = in + in_len - M2_MAX_LEN - 5;
+	const uint8_t* ii;
+	const void* *const dict = (const void**) wrkmem;
+
+	op = out;
+	ip = in;
+	ii = ip;
+
+	ip += 4;
+	for (;;) {
+		register const uint8_t* m_pos;
+		unsigned m_off;
+		unsigned m_len;
+		unsigned dindex;
+
+		D_INDEX1(dindex,ip);
+		GINDEX(m_pos,m_off,dict,dindex,in);
+		if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET))
+			goto literal;
+#if 1
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		D_INDEX2(dindex,ip);
+#endif
+		GINDEX(m_pos,m_off,dict,dindex,in);
+		if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET))
+			goto literal;
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		goto literal;
+
+ try_match:
+#if 1 && defined(LZO_UNALIGNED_OK_2)
+		if (* (const lzo_ushortp) m_pos != * (const lzo_ushortp) ip)
+#else
+		if (m_pos[0] != ip[0] || m_pos[1] != ip[1])
+#endif
+		{
+		} else {
+			if (m_pos[2] == ip[2]) {
+#if 0
+				if (m_off <= M2_MAX_OFFSET)
+					goto match;
+				if (lit <= 3)
+					goto match;
+				if (lit == 3) {			  /* better compression, but slower */
+					assert(op - 2 > out); op[-2] |= (uint8_t)(3);
+					*op++ = *ii++; *op++ = *ii++; *op++ = *ii++;
+					goto code_match;
+				}
+				if (m_pos[3] == ip[3])
+#endif
+					goto match;
+			}
+			else {
+				/* still need a better way for finding M1 matches */
+#if 0
+				/* a M1 match */
+#if 0
+				if (m_off <= M1_MAX_OFFSET && lit > 0 && lit <= 3)
+#else
+				if (m_off <= M1_MAX_OFFSET && lit == 3)
+#endif
+				{
+					register unsigned t;
+
+					t = lit;
+					assert(op - 2 > out); op[-2] |= (uint8_t)(t);
+					do *op++ = *ii++; while (--t > 0);
+					assert(ii == ip);
+					m_off -= 1;
+					*op++ = (uint8_t)(M1_MARKER | ((m_off & 3) << 2));
+					*op++ = (uint8_t)(m_off >> 2);
+					ip += 2;
+					goto match_done;
+				}
+#endif
+			}
+		}
+
+		/* a literal */
+ literal:
+		UPDATE_I(dict, 0, dindex, ip, in);
+		++ip;
+		if (ip >= ip_end)
+			break;
+		continue;
+
+		/* a match */
+match:
+		UPDATE_I(dict, 0, dindex, ip, in);
+		/* store current literal run */
+		if (pd(ip, ii) > 0) {
+			register unsigned t = pd(ip, ii);
+
+			if (t <= 3) {
+				assert(op - 2 > out);
+				op[-2] |= (uint8_t)(t);
+			}
+			else if (t <= 18)
+				*op++ = (uint8_t)(t - 3);
+			else {
+				register unsigned tt = t - 18;
+
+				*op++ = 0;
+				while (tt > 255) {
+					tt -= 255;
+					*op++ = 0;
+				}
+				assert(tt > 0);
+				*op++ = (uint8_t)(tt);
+			}
+			do *op++ = *ii++; while (--t > 0);
+		}
+
+		/* code the match */
+		assert(ii == ip);
+		ip += 3;
+		if (m_pos[3] != *ip++ || m_pos[4] != *ip++ || m_pos[5] != *ip++
+		 || m_pos[6] != *ip++ || m_pos[7] != *ip++ || m_pos[8] != *ip++
+#ifdef LZO1Y
+		 || m_pos[ 9] != *ip++ || m_pos[10] != *ip++ || m_pos[11] != *ip++
+		 || m_pos[12] != *ip++ || m_pos[13] != *ip++ || m_pos[14] != *ip++
+#endif
+		) {
+			--ip;
+			m_len = pd(ip, ii);
+			assert(m_len >= 3);
+			assert(m_len <= M2_MAX_LEN);
+
+			if (m_off <= M2_MAX_OFFSET) {
+				m_off -= 1;
+#if defined(LZO1X)
+				*op++ = (uint8_t)(((m_len - 1) << 5) | ((m_off & 7) << 2));
+				*op++ = (uint8_t)(m_off >> 3);
+#elif defined(LZO1Y)
+				*op++ = (uint8_t)(((m_len + 1) << 4) | ((m_off & 3) << 2));
+				*op++ = (uint8_t)(m_off >> 2);
+#endif
+			}
+			else if (m_off <= M3_MAX_OFFSET) {
+				m_off -= 1;
+				*op++ = (uint8_t)(M3_MARKER | (m_len - 2));
+				goto m3_m4_offset;
+			} else {
+#if defined(LZO1X)
+				m_off -= 0x4000;
+				assert(m_off > 0);
+				assert(m_off <= 0x7fff);
+				*op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+				goto m3_m4_offset;
+#elif defined(LZO1Y)
+				goto m4_match;
+#endif
+			}
+		}
+		else {
+			{
+				const uint8_t* end = in_end;
+				const uint8_t* m = m_pos + M2_MAX_LEN + 1;
+				while (ip < end && *m == *ip)
+					m++, ip++;
+				m_len = pd(ip, ii);
+			}
+			assert(m_len > M2_MAX_LEN);
+
+			if (m_off <= M3_MAX_OFFSET) {
+				m_off -= 1;
+				if (m_len <= 33)
+					*op++ = (uint8_t)(M3_MARKER | (m_len - 2));
+				else {
+					m_len -= 33;
+					*op++ = M3_MARKER | 0;
+					goto m3_m4_len;
+				}
+			} else {
+#if defined(LZO1Y)
+ m4_match:
+#endif
+				m_off -= 0x4000;
+				assert(m_off > 0);
+				assert(m_off <= 0x7fff);
+				if (m_len <= M4_MAX_LEN)
+					*op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11) | (m_len - 2));
+				else {
+					m_len -= M4_MAX_LEN;
+					*op++ = (uint8_t)(M4_MARKER | ((m_off & 0x4000) >> 11));
+ m3_m4_len:
+					while (m_len > 255) {
+						m_len -= 255;
+						*op++ = 0;
+					}
+					assert(m_len > 0);
+					*op++ = (uint8_t)(m_len);
+				}
+			}
+ m3_m4_offset:
+			*op++ = (uint8_t)((m_off & 63) << 2);
+			*op++ = (uint8_t)(m_off >> 6);
+		}
+#if 0
+ match_done:
+#endif
+		ii = ip;
+		if (ip >= ip_end)
+			break;
+	}
+
+	*out_len = pd(op, out);
+	return pd(in_end, ii);
+}
+
+/***********************************************************************
+// public entry point
+************************************************************************/
+int DO_COMPRESS(const uint8_t* in, unsigned in_len,
+		uint8_t* out, unsigned* out_len,
+		void* wrkmem)
+{
+	uint8_t* op = out;
+	unsigned t;
+
+	if (in_len <= M2_MAX_LEN + 5)
+		t = in_len;
+	else {
+		t = do_compress(in,in_len,op,out_len,wrkmem);
+		op += *out_len;
+	}
+
+	if (t > 0) {
+		const uint8_t* ii = in + in_len - t;
+
+		if (op == out && t <= 238)
+			*op++ = (uint8_t)(17 + t);
+		else if (t <= 3)
+			op[-2] |= (uint8_t)(t);
+		else if (t <= 18)
+			*op++ = (uint8_t)(t - 3);
+		else {
+			unsigned tt = t - 18;
+
+			*op++ = 0;
+			while (tt > 255) {
+				tt -= 255;
+				*op++ = 0;
+			}
+			assert(tt > 0);
+			*op++ = (uint8_t)(tt);
+		}
+		do *op++ = *ii++; while (--t > 0);
+	}
+
+	*op++ = M4_MARKER | 1;
+	*op++ = 0;
+	*op++ = 0;
+
+	*out_len = pd(op, out);
+	return 0; /*LZO_E_OK*/
+}
diff --git a/busybox-1.19.3/archival/libarchive/lzo1x_d.c b/busybox-1.19.3/archival/libarchive/lzo1x_d.c
new file mode 100644
index 0000000..348a855
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/lzo1x_d.c
@@ -0,0 +1,420 @@
+/* implementation of the LZO1X decompression algorithm
+
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   Markus F.X.J. Oberhumer <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzo/
+
+   The LZO library 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.
+
+   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
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include "libbb.h"
+#include "liblzo.h"
+
+/***********************************************************************
+// decompress a block of data.
+************************************************************************/
+/* safe decompression with overrun testing */
+int lzo1x_decompress_safe(const uint8_t* in, unsigned in_len,
+		uint8_t* out, unsigned* out_len,
+		void* wrkmem UNUSED_PARAM)
+{
+	register uint8_t* op;
+	register const uint8_t* ip;
+	register unsigned t;
+#if defined(COPY_DICT)
+	unsigned m_off;
+	const uint8_t* dict_end;
+#else
+	register const uint8_t* m_pos = NULL; /* possibly not needed */
+#endif
+	const uint8_t* const ip_end = in + in_len;
+#if defined(HAVE_ANY_OP)
+	uint8_t* const op_end = out + *out_len;
+#endif
+#if defined(LZO1Z)
+	unsigned last_m_off = 0;
+#endif
+
+//	LZO_UNUSED(wrkmem);
+
+#if defined(COPY_DICT)
+	if (dict) {
+		if (dict_len > M4_MAX_OFFSET) {
+			dict += dict_len - M4_MAX_OFFSET;
+			dict_len = M4_MAX_OFFSET;
+		}
+		dict_end = dict + dict_len;
+	} else {
+		dict_len = 0;
+		dict_end = NULL;
+	}
+#endif /* COPY_DICT */
+
+	*out_len = 0;
+
+	op = out;
+	ip = in;
+
+	if (*ip > 17) {
+		t = *ip++ - 17;
+		if (t < 4)
+			goto match_next;
+		assert(t > 0); NEED_OP(t); NEED_IP(t+1);
+		do *op++ = *ip++; while (--t > 0);
+		goto first_literal_run;
+	}
+
+	while (TEST_IP && TEST_OP) {
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+		/* a literal run */
+		if (t == 0) {
+			NEED_IP(1);
+			while (*ip == 0) {
+				t += 255;
+				ip++;
+				NEED_IP(1);
+			}
+			t += 15 + *ip++;
+		}
+		/* copy literals */
+		assert(t > 0);
+		NEED_OP(t+3);
+		NEED_IP(t+4);
+#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+# if !defined(LZO_UNALIGNED_OK_4)
+		if (PTR_ALIGNED2_4(op, ip))
+# endif
+		{
+			COPY4(op, ip);
+			op += 4;
+			ip += 4;
+			if (--t > 0) {
+				if (t >= 4) {
+					do {
+						COPY4(op, ip);
+						op += 4;
+						ip += 4;
+						t -= 4;
+					} while (t >= 4);
+					if (t > 0)
+						do *op++ = *ip++; while (--t > 0);
+				} else {
+					do *op++ = *ip++; while (--t > 0);
+				}
+			}
+		}
+# if !defined(LZO_UNALIGNED_OK_4)
+		else
+# endif
+#endif
+#if !defined(LZO_UNALIGNED_OK_4)
+		{
+			*op++ = *ip++;
+			*op++ = *ip++;
+			*op++ = *ip++;
+			do *op++ = *ip++; while (--t > 0);
+		}
+#endif
+
+ first_literal_run:
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+		m_off = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+		last_m_off = m_off;
+#else
+		m_off = (1 + M2_MAX_OFFSET) + (t >> 2) + (*ip++ << 2);
+#endif
+		NEED_OP(3);
+		t = 3; COPY_DICT(t,m_off)
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+		t = (1 + M2_MAX_OFFSET) + (t << 6) + (*ip++ >> 2);
+		m_pos = op - t;
+		last_m_off = t;
+#else
+		m_pos = op - (1 + M2_MAX_OFFSET);
+		m_pos -= t >> 2;
+		m_pos -= *ip++ << 2;
+#endif
+		TEST_LB(m_pos); NEED_OP(3);
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		*op++ = *m_pos;
+#endif /* COPY_DICT */
+		goto match_done;
+
+		/* handle matches */
+		do {
+ match:
+			if (t >= 64) {		/* a M2 match */
+#if defined(COPY_DICT)
+#if defined(LZO1X)
+				m_off = 1 + ((t >> 2) & 7) + (*ip++ << 3);
+				t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+				m_off = 1 + ((t >> 2) & 3) + (*ip++ << 2);
+				t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+				m_off = t & 0x1f;
+				if (m_off >= 0x1c)
+					m_off = last_m_off;
+				else {
+					m_off = 1 + (m_off << 6) + (*ip++ >> 2);
+					last_m_off = m_off;
+				}
+				t = (t >> 5) - 1;
+#endif
+#else /* !COPY_DICT */
+#if defined(LZO1X)
+				m_pos = op - 1;
+				m_pos -= (t >> 2) & 7;
+				m_pos -= *ip++ << 3;
+				t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+				m_pos = op - 1;
+				m_pos -= (t >> 2) & 3;
+				m_pos -= *ip++ << 2;
+				t = (t >> 4) - 3;
+#elif defined(LZO1Z)
+				{
+					unsigned off = t & 0x1f;
+					m_pos = op;
+					if (off >= 0x1c) {
+						assert(last_m_off > 0);
+						m_pos -= last_m_off;
+					} else {
+						off = 1 + (off << 6) + (*ip++ >> 2);
+						m_pos -= off;
+						last_m_off = off;
+					}
+				}
+				t = (t >> 5) - 1;
+#endif
+				TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+				goto copy_match;
+#endif /* COPY_DICT */
+			}
+			else if (t >= 32) {			/* a M3 match */
+				t &= 31;
+				if (t == 0) {
+					NEED_IP(1);
+					while (*ip == 0) {
+						t += 255;
+						ip++;
+						NEED_IP(1);
+					}
+					t += 31 + *ip++;
+				}
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+				m_off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+				last_m_off = m_off;
+#else
+				m_off = 1 + (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+				{
+					unsigned off = 1 + (ip[0] << 6) + (ip[1] >> 2);
+					m_pos = op - off;
+					last_m_off = off;
+				}
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+				m_pos = op - 1;
+				m_pos -= (* (const lzo_ushortp) ip) >> 2;
+#else
+				m_pos = op - 1;
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+#endif /* COPY_DICT */
+				ip += 2;
+			}
+			else if (t >= 16) {			/* a M4 match */
+#if defined(COPY_DICT)
+				m_off = (t & 8) << 11;
+#else /* !COPY_DICT */
+				m_pos = op;
+				m_pos -= (t & 8) << 11;
+#endif /* COPY_DICT */
+				t &= 7;
+				if (t == 0) {
+					NEED_IP(1);
+					while (*ip == 0) {
+						t += 255;
+						ip++;
+						NEED_IP(1);
+					}
+					t += 7 + *ip++;
+				}
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+				m_off += (ip[0] << 6) + (ip[1] >> 2);
+#else
+				m_off += (ip[0] >> 2) + (ip[1] << 6);
+#endif
+				ip += 2;
+				if (m_off == 0)
+					goto eof_found;
+				m_off += 0x4000;
+#if defined(LZO1Z)
+				last_m_off = m_off;
+#endif
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+				m_pos -= (ip[0] << 6) + (ip[1] >> 2);
+#elif defined(LZO_UNALIGNED_OK_2) && defined(LZO_ABI_LITTLE_ENDIAN)
+				m_pos -= (* (const lzo_ushortp) ip) >> 2;
+#else
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+#endif
+				ip += 2;
+				if (m_pos == op)
+					goto eof_found;
+				m_pos -= 0x4000;
+#if defined(LZO1Z)
+				last_m_off = pd((const uint8_t*)op, m_pos);
+#endif
+#endif /* COPY_DICT */
+			}
+			else {				/* a M1 match */
+#if defined(COPY_DICT)
+#if defined(LZO1Z)
+				m_off = 1 + (t << 6) + (*ip++ >> 2);
+				last_m_off = m_off;
+#else
+				m_off = 1 + (t >> 2) + (*ip++ << 2);
+#endif
+				NEED_OP(2);
+				t = 2; COPY_DICT(t,m_off)
+#else /* !COPY_DICT */
+#if defined(LZO1Z)
+				t = 1 + (t << 6) + (*ip++ >> 2);
+				m_pos = op - t;
+				last_m_off = t;
+#else
+				m_pos = op - 1;
+				m_pos -= t >> 2;
+				m_pos -= *ip++ << 2;
+#endif
+				TEST_LB(m_pos); NEED_OP(2);
+				*op++ = *m_pos++;
+				*op++ = *m_pos;
+#endif /* COPY_DICT */
+				goto match_done;
+			}
+
+			/* copy match */
+#if defined(COPY_DICT)
+
+			NEED_OP(t+3-1);
+			t += 3-1; COPY_DICT(t,m_off)
+
+#else /* !COPY_DICT */
+
+			TEST_LB(m_pos); assert(t > 0); NEED_OP(t+3-1);
+#if defined(LZO_UNALIGNED_OK_4) || defined(LZO_ALIGNED_OK_4)
+# if !defined(LZO_UNALIGNED_OK_4)
+			if (t >= 2 * 4 - (3 - 1) && PTR_ALIGNED2_4(op,m_pos)) {
+				assert((op - m_pos) >= 4);	/* both pointers are aligned */
+# else
+			if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) {
+# endif
+				COPY4(op,m_pos);
+				op += 4; m_pos += 4; t -= 4 - (3 - 1);
+				do {
+					COPY4(op,m_pos);
+					op += 4; m_pos += 4; t -= 4;
+				} while (t >= 4);
+				if (t > 0)
+					do *op++ = *m_pos++; while (--t > 0);
+			}
+			else
+#endif
+			{
+ copy_match:
+				*op++ = *m_pos++; *op++ = *m_pos++;
+				do *op++ = *m_pos++; while (--t > 0);
+			}
+
+#endif /* COPY_DICT */
+
+ match_done:
+#if defined(LZO1Z)
+			t = ip[-1] & 3;
+#else
+			t = ip[-2] & 3;
+#endif
+			if (t == 0)
+				break;
+
+			/* copy literals */
+ match_next:
+			assert(t > 0);
+			assert(t < 4);
+			NEED_OP(t);
+			NEED_IP(t+1);
+#if 0
+			do *op++ = *ip++; while (--t > 0);
+#else
+			*op++ = *ip++;
+			if (t > 1) {
+				*op++ = *ip++;
+				if (t > 2)
+					*op++ = *ip++;
+			}
+#endif
+			t = *ip++;
+		} while (TEST_IP && TEST_OP);
+	}
+
+//#if defined(HAVE_TEST_IP) || defined(HAVE_TEST_OP)
+	/* no EOF code was found */
+	*out_len = pd(op, out);
+	return LZO_E_EOF_NOT_FOUND;
+//#endif
+
+ eof_found:
+	assert(t == 1);
+	*out_len = pd(op, out);
+	return (ip == ip_end ? LZO_E_OK :
+		   (ip < ip_end	 ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+//#if defined(HAVE_NEED_IP)
+ input_overrun:
+	*out_len = pd(op, out);
+	return LZO_E_INPUT_OVERRUN;
+//#endif
+
+//#if defined(HAVE_NEED_OP)
+ output_overrun:
+	*out_len = pd(op, out);
+	return LZO_E_OUTPUT_OVERRUN;
+//#endif
+
+//#if defined(LZO_TEST_OVERRUN_LOOKBEHIND)
+ lookbehind_overrun:
+	*out_len = pd(op, out);
+	return LZO_E_LOOKBEHIND_OVERRUN;
+//#endif
+}
diff --git a/busybox-1.19.3/archival/libarchive/open_transformer.c b/busybox-1.19.3/archival/libarchive/open_transformer.c
new file mode 100644
index 0000000..26ae565
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/open_transformer.c
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+/* transformer(), more than meets the eye */
+/*
+ * On MMU machine, the transform_prog is removed by macro magic
+ * in include/archive.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);
+}
diff --git a/busybox-1.19.3/archival/libarchive/seek_by_jump.c b/busybox-1.19.3/archival/libarchive/seek_by_jump.c
new file mode 100644
index 0000000..7c2c52a
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/seek_by_jump.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+
+void FAST_FUNC seek_by_jump(int fd, off_t amount)
+{
+	if (amount
+	 && lseek(fd, amount, SEEK_CUR) == (off_t) -1
+	) {
+		if (errno == ESPIPE)
+			seek_by_read(fd, amount);
+		else
+			bb_perror_msg_and_die("seek failure");
+	}
+}
diff --git a/busybox-1.19.3/archival/libarchive/seek_by_read.c b/busybox-1.19.3/archival/libarchive/seek_by_read.c
new file mode 100644
index 0000000..ad931a8
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/seek_by_read.c
@@ -0,0 +1,16 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "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.
+ */
+void FAST_FUNC seek_by_read(int fd, off_t amount)
+{
+	if (amount)
+		bb_copyfd_exact_size(fd, -1, amount);
+}
diff --git a/busybox-1.19.3/archival/libarchive/unpack_ar_archive.c b/busybox-1.19.3/archival/libarchive/unpack_ar_archive.c
new file mode 100644
index 0000000..18dbfd5
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unpack_ar_archive.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "archive.h"
+#include "ar.h"
+
+void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive)
+{
+	char magic[7];
+
+	xread(ar_archive->src_fd, magic, AR_MAGIC_LEN);
+	if (strncmp(magic, AR_MAGIC, AR_MAGIC_LEN) != 0) {
+		bb_error_msg_and_die("invalid ar magic");
+	}
+	ar_archive->offset += AR_MAGIC_LEN;
+
+	while (get_header_ar(ar_archive) == EXIT_SUCCESS)
+		continue;
+}
diff --git a/busybox-1.19.3/archival/libarchive/unxz/README b/busybox-1.19.3/archival/libarchive/unxz/README
new file mode 100644
index 0000000..c5972f6
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/README
@@ -0,0 +1,135 @@
+
+XZ Embedded
+===========
+
+    XZ Embedded is a relatively small, limited implementation of the .xz
+    file format. Currently only decoding is implemented.
+
+    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.
+
+    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
+    read linux/Documentation/xz.txt even if you aren't using XZ Embedded
+    as part of Linux; information in that file is not repeated in this
+    README.
+
+Compiling the Linux kernel module
+
+    The xz_dec module depends on crc32 module, so make sure that you have
+    it enabled (CONFIG_CRC32).
+
+    Building the xz_dec and xz_dec_test modules without support for BCJ
+    filters:
+
+        cd linux/lib/xz
+        make -C /path/to/kernel/source \
+                KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+                CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m
+
+    Building the xz_dec and xz_dec_test modules with support for BCJ
+    filters:
+
+        cd linux/lib/xz
+        make -C /path/to/kernel/source \
+                KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+                CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \
+                CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \
+                CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \
+                CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y
+
+    If you want only one or a few of the BCJ filters, omit the appropriate
+    variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support
+    code shared between all BCJ filters.
+
+    Most people don't need the xz_dec_test module. You can skip building
+    it by omitting CONFIG_XZ_DEC_TEST=m from the make command line.
+
+Compiler requirements
+
+    XZ Embedded should compile as either GNU-C89 (used in the Linux
+    kernel) or with any C99 compiler. Getting the code to compile with
+    non-GNU C89 compiler or a C++ compiler should be quite easy as
+    long as there is a data type for unsigned 64-bit integer (or the
+    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,
+    xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
+    compiled with GCC 4.3.3.
+
+Embedding into userspace applications
+
+    To embed the XZ decoder, copy the following files into a single
+    directory in your source code tree:
+
+        linux/include/linux/xz.h
+        linux/lib/xz/xz_crc32.c
+        linux/lib/xz/xz_dec_lzma2.c
+        linux/lib/xz/xz_dec_stream.c
+        linux/lib/xz/xz_lzma2.h
+        linux/lib/xz/xz_private.h
+        linux/lib/xz/xz_stream.h
+        userspace/xz_config.h
+
+    Alternatively, xz.h may be placed into a different directory but then
+    that directory must be in the compiler include path when compiling
+    the .c files.
+
+    Your code should use only the functions declared in xz.h. The rest of
+    the .h files are meant only for internal use in XZ Embedded.
+
+    You may want to modify xz_config.h to be more suitable for your build
+    environment. Probably you should at least skim through it even if the
+    default file works as is.
+
+BCJ filter support
+
+    If you want support for one or more BCJ filters, you need to copy also
+    linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate
+    #defines in xz_config.h or in compiler flags. You don't need these
+    #defines in the code that just uses XZ Embedded via xz.h, but having
+    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_POWERPC      PowerPC             Big endian only
+        XZ_DEC_IA64         Itanium (IA-64)     Big or little endian
+        XZ_DEC_ARM          ARM                 Little endian only
+        XZ_DEC_ARMTHUMB     ARM-Thumb           Little endian only
+        XZ_DEC_SPARC        SPARC               Big or little endian
+
+    While some architectures are (partially) bi-endian, the endianness
+    setting doesn't change the endianness of the instructions on all
+    architectures. That's why Itanium and SPARC filters work for both big
+    and little endian executables (Itanium has little endian instructions
+    and SPARC has big endian instructions).
+
+    There currently is no filter for little endian PowerPC or big endian
+    ARM or ARM-Thumb. Implementing filters for them can be considered if
+    there is a need for such filters in real-world applications.
+
+Notes about shared libraries
+
+    If you are including XZ Embedded into a shared library, you very
+    probably should rename the xz_* functions to prevent symbol
+    conflicts in case your library is linked against some other library
+    or application that also has XZ Embedded in it (which may even be
+    a different version of XZ Embedded). TODO: Provide an easy way
+    to do this.
+
+    Please don't create a shared library of XZ Embedded itself unless
+    it is fine to rebuild everything depending on that shared library
+    everytime you upgrade to a newer version of XZ Embedded. There are
+    no API or ABI stability guarantees between different versions of
+    XZ Embedded.
+
+Specifying the calling convention
+
+    XZ_FUNC macro was included to support declaring functions with __init
+    in Linux. Outside Linux, it can be used to specify the calling
+    convention on systems that support multiple calling conventions.
+    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.
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz.h b/busybox-1.19.3/archival/libarchive/unxz/xz.h
new file mode 100644
index 0000000..c6c071c
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz.h
@@ -0,0 +1,271 @@
+/*
+ * XZ decompressor
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_H
+#define XZ_H
+
+#ifdef __KERNEL__
+#	include <linux/stddef.h>
+#	include <linux/types.h>
+#else
+#	include <stddef.h>
+#	include <stdint.h>
+#endif
+
+/* In Linux, this is used to make extern functions static when needed. */
+#ifndef XZ_EXTERN
+#	define XZ_EXTERN extern
+#endif
+
+/* In Linux, this is used to mark the functions with __init when needed. */
+#ifndef XZ_FUNC
+#	define XZ_FUNC
+#endif
+
+/**
+ * enum xz_mode - Operation mode
+ *
+ * @XZ_SINGLE:              Single-call mode. This uses less RAM than
+ *                          than multi-call modes, because the LZMA2
+ *                          dictionary doesn't need to be allocated as
+ *                          part of the decoder state. All required data
+ *                          structures are allocated at initialization,
+ *                          so xz_dec_run() cannot return XZ_MEM_ERROR.
+ * @XZ_PREALLOC:            Multi-call mode with preallocated LZMA2
+ *                          dictionary buffer. All data structures are
+ *                          allocated at initialization, so xz_dec_run()
+ *                          cannot return XZ_MEM_ERROR.
+ * @XZ_DYNALLOC:            Multi-call mode. The LZMA2 dictionary is
+ *                          allocated once the required size has been
+ *                          parsed from the stream headers. If the
+ *                          allocation fails, xz_dec_run() will return
+ *                          XZ_MEM_ERROR.
+ *
+ * It is possible to enable support only for a subset of the above
+ * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
+ * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
+ * with support for all operation modes, but the preboot code may
+ * be built with fewer features to minimize code size.
+ */
+enum xz_mode {
+	XZ_SINGLE,
+	XZ_PREALLOC,
+	XZ_DYNALLOC
+};
+
+/**
+ * enum xz_ret - Return codes
+ * @XZ_OK:                  Everything is OK so far. More input or more
+ *                          output space is required to continue. This
+ *                          return code is possible only in multi-call mode
+ *                          (XZ_PREALLOC or XZ_DYNALLOC).
+ * @XZ_STREAM_END:          Operation finished successfully.
+ * @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
+ *                          XZ_DEC_ANY_CHECK was defined at build time,
+ *                          which is not used in the kernel. Unsupported
+ *                          check types return XZ_OPTIONS_ERROR if
+ *                          XZ_DEC_ANY_CHECK was not defined at build time.
+ * @XZ_MEM_ERROR:           Allocating memory failed. This return code is
+ *                          possible only if the decoder was initialized
+ *                          with XZ_DYNALLOC. The amount of memory that was
+ *                          tried to be allocated was no more than the
+ *                          dict_max argument given to xz_dec_init().
+ * @XZ_MEMLIMIT_ERROR:      A bigger LZMA2 dictionary would be needed than
+ *                          allowed by the dict_max argument given to
+ *                          xz_dec_init(). This return value is possible
+ *                          only in multi-call mode (XZ_PREALLOC or
+ *                          XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
+ *                          ignores the dict_max argument.
+ * @XZ_FORMAT_ERROR:        File format was not recognized (wrong magic
+ *                          bytes).
+ * @XZ_OPTIONS_ERROR:       This implementation doesn't support the requested
+ *                          compression options. In the decoder this means
+ *                          that the header CRC32 matches, but the header
+ *                          itself specifies something that we don't support.
+ * @XZ_DATA_ERROR:          Compressed data is corrupt.
+ * @XZ_BUF_ERROR:           Cannot make any progress. Details are slightly
+ *                          different between multi-call and single-call
+ *                          mode; more information below.
+ *
+ * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
+ * to XZ code cannot consume any input and cannot produce any new output.
+ * This happens when there is no new input available, or the output buffer
+ * is full while at least one output byte is still pending. Assuming your
+ * code is not buggy, you can get this error only when decoding a compressed
+ * 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
+ * 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.
+ */
+enum xz_ret {
+	XZ_OK,
+	XZ_STREAM_END,
+	XZ_UNSUPPORTED_CHECK,
+	XZ_MEM_ERROR,
+	XZ_MEMLIMIT_ERROR,
+	XZ_FORMAT_ERROR,
+	XZ_OPTIONS_ERROR,
+	XZ_DATA_ERROR,
+	XZ_BUF_ERROR
+};
+
+/**
+ * struct xz_buf - Passing input and output buffers to XZ code
+ * @in:         Beginning of the input buffer. This may be NULL if and only
+ *              if in_pos is equal to in_size.
+ * @in_pos:     Current position in the input buffer. This must not exceed
+ *              in_size.
+ * @in_size:    Size of the input buffer
+ * @out:        Beginning of the output buffer. This may be NULL if and only
+ *              if out_pos is equal to out_size.
+ * @out_pos:    Current position in the output buffer. This must not exceed
+ *              out_size.
+ * @out_size:   Size of the output buffer
+ *
+ * Only the contents of the output buffer from out[out_pos] onward, and
+ * the variables in_pos and out_pos are modified by the XZ code.
+ */
+struct xz_buf {
+	const uint8_t *in;
+	size_t in_pos;
+	size_t in_size;
+
+	uint8_t *out;
+	size_t out_pos;
+	size_t out_size;
+};
+
+/**
+ * struct xz_dec - Opaque type to hold the XZ decoder state
+ */
+struct xz_dec;
+
+/**
+ * xz_dec_init() - Allocate and initialize a XZ decoder state
+ * @mode:       Operation mode
+ * @dict_max:   Maximum size of the LZMA2 dictionary (history buffer) for
+ *              multi-call decoding. This is ignored in single-call mode
+ *              (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
+ *              or 2^n + 2^(n-1) bytes (the latter sizes are less common
+ *              in practice), so other values for dict_max don't make sense.
+ *              In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
+ *              512 KiB, and 1 MiB are probably the only reasonable values,
+ *              except for kernel and initramfs images where a bigger
+ *              dictionary can be fine and useful.
+ *
+ * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
+ * once. The caller must provide enough output space or the decoding will
+ * fail. The output space is used as the dictionary buffer, which is why
+ * there is no need to allocate the dictionary as part of the decoder's
+ * internal state.
+ *
+ * Because the output buffer is used as the workspace, streams encoded using
+ * a big dictionary are not a problem in single-call mode. It is enough that
+ * the output buffer is big enough to hold the actual uncompressed data; it
+ * can be smaller than the dictionary size stored in the stream headers.
+ *
+ * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
+ * of memory is preallocated for the LZMA2 dictionary. This way there is no
+ * risk that xz_dec_run() could run out of memory, since xz_dec_run() will
+ * never allocate any memory. Instead, if the preallocated dictionary is too
+ * small for decoding the given input stream, xz_dec_run() will return
+ * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
+ * decoded to avoid allocating excessive amount of memory for the dictionary.
+ *
+ * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
+ * dict_max specifies the maximum allowed dictionary size that xz_dec_run()
+ * may allocate once it has parsed the dictionary size from the stream
+ * headers. This way excessive allocations can be avoided while still
+ * limiting the maximum memory usage to a sane value to prevent running the
+ * system out of memory when decompressing streams from untrusted sources.
+ *
+ * On success, xz_dec_init() returns a pointer to struct xz_dec, which is
+ * ready to be used with xz_dec_run(). If memory allocation fails,
+ * xz_dec_init() returns NULL.
+ */
+XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init(
+		enum xz_mode mode, uint32_t dict_max);
+
+/**
+ * xz_dec_run() - Run the XZ decoder
+ * @s:          Decoder state allocated using xz_dec_init()
+ * @b:          Input and output buffers
+ *
+ * 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
+ * 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
+ * cannot be properly done if the output buffer is truncated. Thus, you
+ * cannot give the single-call decoder a too small buffer and then expect to
+ * get that amount valid data from the beginning of the stream. You must use
+ * the multi-call decoder if you don't want to uncompress the whole stream.
+ */
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_run(struct xz_dec *s, struct xz_buf *b);
+
+/**
+ * xz_dec_reset() - Reset an already allocated decoder state
+ * @s:          Decoder state allocated using xz_dec_init()
+ *
+ * This function can be used to reset the multi-call decoder state without
+ * freeing and reallocating memory with xz_dec_end() and xz_dec_init().
+ *
+ * In single-call mode, xz_dec_reset() is always called in the beginning of
+ * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
+ * multi-call mode.
+ */
+XZ_EXTERN void XZ_FUNC xz_dec_reset(struct xz_dec *s);
+
+/**
+ * xz_dec_end() - Free the memory allocated for the decoder state
+ * @s:          Decoder state allocated using xz_dec_init(). If s is NULL,
+ *              this function does nothing.
+ */
+XZ_EXTERN void XZ_FUNC xz_dec_end(struct xz_dec *s);
+
+/*
+ * Standalone build (userspace build or in-kernel build for boot time use)
+ * needs a CRC32 implementation. For normal in-kernel use, kernel's own
+ * CRC32 module is used instead, and users of this module don't need to
+ * care about the functions below.
+ */
+#ifndef XZ_INTERNAL_CRC32
+#	ifdef __KERNEL__
+#		define XZ_INTERNAL_CRC32 0
+#	else
+#		define XZ_INTERNAL_CRC32 1
+#	endif
+#endif
+
+#if XZ_INTERNAL_CRC32
+/*
+ * This must be called before any other xz_* function to initialize
+ * the CRC32 lookup table.
+ */
+XZ_EXTERN void XZ_FUNC xz_crc32_init(void);
+
+/*
+ * Update CRC32 value using the polynomial from IEEE-802.3. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint32_t XZ_FUNC xz_crc32(
+		const uint8_t *buf, size_t size, uint32_t crc);
+#endif
+#endif
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz_config.h b/busybox-1.19.3/archival/libarchive/unxz/xz_config.h
new file mode 100644
index 0000000..187e1cb
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz_config.h
@@ -0,0 +1,123 @@
+/*
+ * Private includes and definitions for userspace use of XZ Embedded
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_CONFIG_H
+#define XZ_CONFIG_H
+
+/* Uncomment as needed to enable BCJ filter decoders. */
+/* #define XZ_DEC_X86 */
+/* #define XZ_DEC_POWERPC */
+/* #define XZ_DEC_IA64 */
+/* #define XZ_DEC_ARM */
+/* #define XZ_DEC_ARMTHUMB */
+/* #define XZ_DEC_SPARC */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xz.h"
+
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) free(ptr)
+
+#define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#define memzero(buf, size) memset(buf, 0, size)
+
+#undef min
+#undef min_t
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#define min_t(type, x, y) min(x, y)
+
+/*
+ * Some functions have been marked with __always_inline to keep the
+ * performance reasonable even when the compiler is optimizing for
+ * small code size. You may be able to save a few bytes by #defining
+ * __always_inline to plain inline, but don't complain if the code
+ * becomes slow.
+ *
+ * NOTE: System headers on GNU/Linux may #define this macro already,
+ * so if you want to change it, you need to #undef it first.
+ */
+#ifndef __always_inline
+#	ifdef __GNUC__
+#		define __always_inline \
+			inline __attribute__((__always_inline__))
+#	else
+#		define __always_inline inline
+#	endif
+#endif
+
+/*
+ * Some functions are marked to never be inlined to reduce stack usage.
+ * If you don't care about stack usage, you may want to modify this so
+ * that noinline_for_stack is #defined to be empty even when using GCC.
+ * Doing so may save a few bytes in binary size.
+ */
+#ifndef noinline_for_stack
+#	ifdef __GNUC__
+#		define noinline_for_stack __attribute__((__noinline__))
+#	else
+#		define noinline_for_stack
+#	endif
+#endif
+
+/* Inline functions to access unaligned unsigned 32-bit integers */
+#ifndef get_unaligned_le32
+static inline uint32_t XZ_FUNC get_unaligned_le32(const uint8_t *buf)
+{
+	return (uint32_t)buf[0]
+			| ((uint32_t)buf[1] << 8)
+			| ((uint32_t)buf[2] << 16)
+			| ((uint32_t)buf[3] << 24);
+}
+#endif
+
+#ifndef get_unaligned_be32
+static inline uint32_t XZ_FUNC get_unaligned_be32(const uint8_t *buf)
+{
+	return (uint32_t)(buf[0] << 24)
+			| ((uint32_t)buf[1] << 16)
+			| ((uint32_t)buf[2] << 8)
+			| (uint32_t)buf[3];
+}
+#endif
+
+#ifndef put_unaligned_le32
+static inline void XZ_FUNC put_unaligned_le32(uint32_t val, uint8_t *buf)
+{
+	buf[0] = (uint8_t)val;
+	buf[1] = (uint8_t)(val >> 8);
+	buf[2] = (uint8_t)(val >> 16);
+	buf[3] = (uint8_t)(val >> 24);
+}
+#endif
+
+#ifndef put_unaligned_be32
+static inline void XZ_FUNC put_unaligned_be32(uint32_t val, uint8_t *buf)
+{
+	buf[0] = (uint8_t)(val >> 24);
+	buf[1] = (uint8_t)(val >> 16);
+	buf[2] = (uint8_t)(val >> 8);
+	buf[3] = (uint8_t)val;
+}
+#endif
+
+/*
+ * Use get_unaligned_le32() also for aligned access for simplicity. On
+ * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
+ * could save a few bytes in code size.
+ */
+#ifndef get_le32
+#	define get_le32 get_unaligned_le32
+#endif
+
+#endif
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz_dec_bcj.c b/busybox-1.19.3/archival/libarchive/unxz/xz_dec_bcj.c
new file mode 100644
index 0000000..09162b5
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz_dec_bcj.c
@@ -0,0 +1,564 @@
+/*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+/*
+ * The rest of the file is inside this ifdef. It makes things a little more
+ * convenient when building without support for any BCJ filters.
+ */
+#ifdef XZ_DEC_BCJ
+
+struct xz_dec_bcj {
+	/* Type of the BCJ filter being used */
+	enum {
+		BCJ_X86 = 4,        /* x86 or x86-64 */
+		BCJ_POWERPC = 5,    /* Big endian only */
+		BCJ_IA64 = 6,       /* Big or little endian */
+		BCJ_ARM = 7,        /* Little endian only */
+		BCJ_ARMTHUMB = 8,   /* Little endian only */
+		BCJ_SPARC = 9       /* Big or little endian */
+	} type;
+
+	/*
+	 * Return value of the next filter in the chain. We need to preserve
+	 * this information across calls, because we must not call the next
+	 * filter anymore once it has returned XZ_STREAM_END.
+	 */
+	enum xz_ret ret;
+
+	/* True if we are operating in single-call mode. */
+	bool single_call;
+
+	/*
+	 * Absolute position relative to the beginning of the uncompressed
+	 * data (in a single .xz Block). We care only about the lowest 32
+	 * bits so this doesn't need to be uint64_t even with big files.
+	 */
+	uint32_t pos;
+
+	/* x86 filter state */
+	uint32_t x86_prev_mask;
+
+	/* Temporary space to hold the variables from struct xz_buf */
+	uint8_t *out;
+	size_t out_pos;
+	size_t out_size;
+
+	struct {
+		/* Amount of already filtered data in the beginning of buf */
+		size_t filtered;
+
+		/* Total amount of data currently stored in buf  */
+		size_t size;
+
+		/*
+		 * Buffer to hold a mix of filtered and unfiltered data. This
+		 * needs to be big enough to hold Alignment + 2 * Look-ahead:
+		 *
+		 * Type         Alignment   Look-ahead
+		 * x86              1           4
+		 * PowerPC          4           0
+		 * IA-64           16           0
+		 * ARM              4           0
+		 * ARM-Thumb        2           2
+		 * SPARC            4           0
+		 */
+		uint8_t buf[16];
+	} temp;
+};
+
+#ifdef XZ_DEC_X86
+/*
+ * This is macro 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 noinline_for_stack size_t XZ_FUNC bcj_x86(
+		struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+	static const bool mask_to_allowed_status[8]
+		= { true, true, true, false, true, false, false, false };
+
+	static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+	size_t i;
+	size_t prev_pos = (size_t)-1;
+	uint32_t prev_mask = s->x86_prev_mask;
+	uint32_t src;
+	uint32_t dest;
+	uint32_t j;
+	uint8_t b;
+
+	if (size <= 4)
+		return 0;
+
+	size -= 4;
+	for (i = 0; i < size; ++i) {
+		if ((buf[i] & 0xFE) != 0xE8)
+			continue;
+
+		prev_pos = i - prev_pos;
+		if (prev_pos > 3) {
+			prev_mask = 0;
+		} else {
+			prev_mask = (prev_mask << (prev_pos - 1)) & 7;
+			if (prev_mask != 0) {
+				b = buf[i + 4 - mask_to_bit_num[prev_mask]];
+				if (!mask_to_allowed_status[prev_mask]
+						|| bcj_x86_test_msbyte(b)) {
+					prev_pos = i;
+					prev_mask = (prev_mask << 1) | 1;
+					continue;
+				}
+			}
+		}
+
+		prev_pos = i;
+
+		if (bcj_x86_test_msbyte(buf[i + 4])) {
+			src = get_unaligned_le32(buf + i + 1);
+			while (true) {
+				dest = src - (s->pos + (uint32_t)i + 5);
+				if (prev_mask == 0)
+					break;
+
+				j = mask_to_bit_num[prev_mask] * 8;
+				b = (uint8_t)(dest >> (24 - j));
+				if (!bcj_x86_test_msbyte(b))
+					break;
+
+				src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
+			}
+
+			dest &= 0x01FFFFFF;
+			dest |= (uint32_t)0 - (dest & 0x01000000);
+			put_unaligned_le32(dest, buf + i + 1);
+			i += 4;
+		} else {
+			prev_mask = (prev_mask << 1) | 1;
+		}
+	}
+
+	prev_pos = i - prev_pos;
+	s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
+	return i;
+}
+#endif
+
+#ifdef XZ_DEC_POWERPC
+static noinline_for_stack size_t XZ_FUNC bcj_powerpc(
+		struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+	size_t i;
+	uint32_t instr;
+
+	for (i = 0; i + 4 <= size; i += 4) {
+		instr = get_unaligned_be32(buf + i);
+		if ((instr & 0xFC000003) == 0x48000001) {
+			instr &= 0x03FFFFFC;
+			instr -= s->pos + (uint32_t)i;
+			instr &= 0x03FFFFFC;
+			instr |= 0x48000001;
+			put_unaligned_be32(instr, buf + i);
+		}
+	}
+
+	return i;
+}
+#endif
+
+#ifdef XZ_DEC_IA64
+static noinline_for_stack size_t XZ_FUNC bcj_ia64(
+		struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+	static const uint8_t branch_table[32] = {
+		0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0,
+		4, 4, 6, 6, 0, 0, 7, 7,
+		4, 4, 0, 0, 4, 4, 0, 0
+	};
+
+	/*
+	 * The local variables take a little bit stack space, but it's less
+	 * than what LZMA2 decoder takes, so it doesn't make sense to reduce
+	 * stack usage here without doing that for the LZMA2 decoder too.
+	 */
+
+	/* Loop counters */
+	size_t i;
+	size_t j;
+
+	/* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
+	uint32_t slot;
+
+	/* Bitwise offset of the instruction indicated by slot */
+	uint32_t bit_pos;
+
+	/* bit_pos split into byte and bit parts */
+	uint32_t byte_pos;
+	uint32_t bit_res;
+
+	/* Address part of an instruction */
+	uint32_t addr;
+
+	/* Mask used to detect which instructions to convert */
+	uint32_t mask;
+
+	/* 41-bit instruction stored somewhere in the lowest 48 bits */
+	uint64_t instr;
+
+	/* Instruction normalized with bit_res for easier manipulation */
+	uint64_t norm;
+
+	for (i = 0; i + 16 <= size; i += 16) {
+		mask = branch_table[buf[i] & 0x1F];
+		for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
+			if (((mask >> slot) & 1) == 0)
+				continue;
+
+			byte_pos = bit_pos >> 3;
+			bit_res = bit_pos & 7;
+			instr = 0;
+			for (j = 0; j < 6; ++j)
+				instr |= (uint64_t)(buf[i + j + byte_pos])
+						<< (8 * j);
+
+			norm = instr >> bit_res;
+
+			if (((norm >> 37) & 0x0F) == 0x05
+					&& ((norm >> 9) & 0x07) == 0) {
+				addr = (norm >> 13) & 0x0FFFFF;
+				addr |= ((uint32_t)(norm >> 36) & 1) << 20;
+				addr <<= 4;
+				addr -= s->pos + (uint32_t)i;
+				addr >>= 4;
+
+				norm &= ~((uint64_t)0x8FFFFF << 13);
+				norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
+				norm |= (uint64_t)(addr & 0x100000)
+						<< (36 - 20);
+
+				instr &= (1 << bit_res) - 1;
+				instr |= norm << bit_res;
+
+				for (j = 0; j < 6; j++)
+					buf[i + j + byte_pos]
+						= (uint8_t)(instr >> (8 * j));
+			}
+		}
+	}
+
+	return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARM
+static noinline_for_stack size_t XZ_FUNC bcj_arm(
+		struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+	size_t i;
+	uint32_t addr;
+
+	for (i = 0; i + 4 <= size; i += 4) {
+		if (buf[i + 3] == 0xEB) {
+			addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+					| ((uint32_t)buf[i + 2] << 16);
+			addr <<= 2;
+			addr -= s->pos + (uint32_t)i + 8;
+			addr >>= 2;
+			buf[i] = (uint8_t)addr;
+			buf[i + 1] = (uint8_t)(addr >> 8);
+			buf[i + 2] = (uint8_t)(addr >> 16);
+		}
+	}
+
+	return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARMTHUMB
+static noinline_for_stack size_t XZ_FUNC bcj_armthumb(
+		struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+	size_t i;
+	uint32_t addr;
+
+	for (i = 0; i + 4 <= size; i += 2) {
+		if ((buf[i + 1] & 0xF8) == 0xF0
+				&& (buf[i + 3] & 0xF8) == 0xF8) {
+			addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
+					| ((uint32_t)buf[i] << 11)
+					| (((uint32_t)buf[i + 3] & 0x07) << 8)
+					| (uint32_t)buf[i + 2];
+			addr <<= 1;
+			addr -= s->pos + (uint32_t)i + 4;
+			addr >>= 1;
+			buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
+			buf[i] = (uint8_t)(addr >> 11);
+			buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
+			buf[i + 2] = (uint8_t)addr;
+			i += 2;
+		}
+	}
+
+	return i;
+}
+#endif
+
+#ifdef XZ_DEC_SPARC
+static noinline_for_stack size_t XZ_FUNC bcj_sparc(
+		struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+	size_t i;
+	uint32_t instr;
+
+	for (i = 0; i + 4 <= size; i += 4) {
+		instr = get_unaligned_be32(buf + i);
+		if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
+			instr <<= 2;
+			instr -= s->pos + (uint32_t)i;
+			instr >>= 2;
+			instr = ((uint32_t)0x40000000 - (instr & 0x400000))
+					| 0x40000000 | (instr & 0x3FFFFF);
+			put_unaligned_be32(instr, buf + i);
+		}
+	}
+
+	return i;
+}
+#endif
+
+/*
+ * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
+ * of data that got filtered.
+ *
+ * NOTE: This is implemented as a switch statement to avoid using function
+ * pointers, which could be problematic in the kernel boot code, which must
+ * avoid pointers to static data (at least on x86).
+ */
+static void XZ_FUNC bcj_apply(struct xz_dec_bcj *s,
+		uint8_t *buf, size_t *pos, size_t size)
+{
+	size_t filtered;
+
+	buf += *pos;
+	size -= *pos;
+
+	switch (s->type) {
+#ifdef XZ_DEC_X86
+	case BCJ_X86:
+		filtered = bcj_x86(s, buf, size);
+		break;
+#endif
+#ifdef XZ_DEC_POWERPC
+	case BCJ_POWERPC:
+		filtered = bcj_powerpc(s, buf, size);
+		break;
+#endif
+#ifdef XZ_DEC_IA64
+	case BCJ_IA64:
+		filtered = bcj_ia64(s, buf, size);
+		break;
+#endif
+#ifdef XZ_DEC_ARM
+	case BCJ_ARM:
+		filtered = bcj_arm(s, buf, size);
+		break;
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+	case BCJ_ARMTHUMB:
+		filtered = bcj_armthumb(s, buf, size);
+		break;
+#endif
+#ifdef XZ_DEC_SPARC
+	case BCJ_SPARC:
+		filtered = bcj_sparc(s, buf, size);
+		break;
+#endif
+	default:
+		/* Never reached but silence compiler warnings. */
+		filtered = 0;
+		break;
+	}
+
+	*pos += filtered;
+	s->pos += filtered;
+}
+
+/*
+ * Flush pending filtered data from temp to the output buffer.
+ * Move the remaining mixture of possibly filtered and unfiltered
+ * data to the beginning of temp.
+ */
+static void XZ_FUNC bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
+{
+	size_t copy_size;
+
+	copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
+	memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
+	b->out_pos += copy_size;
+
+	s->temp.filtered -= copy_size;
+	s->temp.size -= copy_size;
+	memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
+}
+
+/*
+ * The BCJ filter functions are primitive in sense that they process the
+ * data in chunks of 1-16 bytes. To hide this issue, this function does
+ * some buffering.
+ */
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s,
+		struct xz_dec_lzma2 *lzma2, struct xz_buf *b)
+{
+	size_t out_start;
+
+	/*
+	 * Flush pending already filtered data to the output buffer. Return
+	 * immediatelly if we couldn't flush everything, or if the next
+	 * filter in the chain had already returned XZ_STREAM_END.
+	 */
+	if (s->temp.filtered > 0) {
+		bcj_flush(s, b);
+		if (s->temp.filtered > 0)
+			return XZ_OK;
+
+		if (s->ret == XZ_STREAM_END)
+			return XZ_STREAM_END;
+	}
+
+	/*
+	 * If we have more output space than what is currently pending in
+	 * temp, copy the unfiltered data from temp to the output buffer
+	 * and try to fill the output buffer by decoding more data from the
+	 * 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.
+	 */
+	if (s->temp.size < b->out_size - b->out_pos) {
+		out_start = b->out_pos;
+		memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
+		b->out_pos += s->temp.size;
+
+		s->ret = xz_dec_lzma2_run(lzma2, b);
+		if (s->ret != XZ_STREAM_END
+				&& (s->ret != XZ_OK || s->single_call))
+			return s->ret;
+
+		bcj_apply(s, b->out, &out_start, b->out_pos);
+
+		/*
+		 * As an exception, if the next filter returned XZ_STREAM_END,
+		 * we can do that too, since the last few bytes that remain
+		 * unfiltered are meant to remain unfiltered.
+		 */
+		if (s->ret == XZ_STREAM_END)
+			return XZ_STREAM_END;
+
+		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 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.
+	 */
+	if (s->temp.size > 0) {
+		/* Make b->out{,_pos,_size} temporarily point to s->temp. */
+		s->out = b->out;
+		s->out_pos = b->out_pos;
+		s->out_size = b->out_size;
+		b->out = s->temp.buf;
+		b->out_pos = s->temp.size;
+		b->out_size = sizeof(s->temp.buf);
+
+		s->ret = xz_dec_lzma2_run(lzma2, b);
+
+		s->temp.size = b->out_pos;
+		b->out = s->out;
+		b->out_pos = s->out_pos;
+		b->out_size = s->out_size;
+
+		if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
+			return s->ret;
+
+		bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
+
+		/*
+		 * If the next filter returned XZ_STREAM_END, we mark that
+		 * everything is filtered, since the last unfiltered bytes
+		 * of the stream are meant to be left as is.
+		 */
+		if (s->ret == XZ_STREAM_END)
+			s->temp.filtered = s->temp.size;
+
+		bcj_flush(s, b);
+		if (s->temp.filtered > 0)
+			return XZ_OK;
+	}
+
+	return s->ret;
+}
+
+XZ_EXTERN struct xz_dec_bcj * XZ_FUNC xz_dec_bcj_create(bool single_call)
+{
+	struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (s != NULL)
+		s->single_call = single_call;
+
+	return s;
+}
+
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_reset(
+		struct xz_dec_bcj *s, uint8_t id)
+{
+	switch (id) {
+#ifdef XZ_DEC_X86
+	case BCJ_X86:
+#endif
+#ifdef XZ_DEC_POWERPC
+	case BCJ_POWERPC:
+#endif
+#ifdef XZ_DEC_IA64
+	case BCJ_IA64:
+#endif
+#ifdef XZ_DEC_ARM
+	case BCJ_ARM:
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+	case BCJ_ARMTHUMB:
+#endif
+#ifdef XZ_DEC_SPARC
+	case BCJ_SPARC:
+#endif
+		break;
+
+	default:
+		/* Unsupported Filter ID */
+		return XZ_OPTIONS_ERROR;
+	}
+
+	s->type = id;
+	s->ret = XZ_OK;
+	s->pos = 0;
+	s->x86_prev_mask = 0;
+	s->temp.filtered = 0;
+	s->temp.size = 0;
+
+	return XZ_OK;
+}
+
+#endif
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz_dec_lzma2.c b/busybox-1.19.3/archival/libarchive/unxz/xz_dec_lzma2.c
new file mode 100644
index 0000000..da71cb4
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz_dec_lzma2.c
@@ -0,0 +1,1175 @@
+/*
+ * LZMA2 decoder
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_lzma2.h"
+
+/*
+ * Range decoder initialization eats the first five bytes of each LZMA chunk.
+ */
+#define RC_INIT_BYTES 5
+
+/*
+ * Minimum number of usable input buffer to safely decode one LZMA symbol.
+ * The worst case is that we decode 22 bits using probabilities and 26
+ * direct bits. This may decode at maximum of 20 bytes of input. However,
+ * lzma_main() does an extra normalization before returning, thus we
+ * need to put 21 here.
+ */
+#define LZMA_IN_REQUIRED 21
+
+/*
+ * Dictionary (history buffer)
+ *
+ * These are always true:
+ *    start <= pos <= full <= end
+ *    pos <= limit <= end
+ *
+ * In multi-call mode, also these are true:
+ *    end == size
+ *    size <= size_max
+ *    allocated <= size
+ *
+ * Most of these variables are size_t to support single-call mode,
+ * in which the dictionary variables address the actual output
+ * buffer directly.
+ */
+struct dictionary {
+	/* Beginning of the history buffer */
+	uint8_t *buf;
+
+	/* Old position in buf (before decoding more data) */
+	size_t start;
+
+	/* Position in buf */
+	size_t pos;
+
+	/*
+	 * How full dictionary is. This is used to detect corrupt input that
+	 * would read beyond the beginning of the uncompressed stream.
+	 */
+	size_t full;
+
+	/* Write limit; we don't write to buf[limit] or later bytes. */
+	size_t limit;
+
+	/*
+	 * End of the dictionary buffer. In multi-call mode, this is
+	 * the same as the dictionary size. In single-call mode, this
+	 * indicates the size of the output buffer.
+	 */
+	size_t end;
+
+	/*
+	 * Size of the dictionary as specified in Block Header. This is used
+	 * together with "full" to detect corrupt input that would make us
+	 * read beyond the beginning of the uncompressed stream.
+	 */
+	uint32_t size;
+
+	/*
+	 * Maximum allowed dictionary size in multi-call mode.
+	 * This is ignored in single-call mode.
+	 */
+	uint32_t size_max;
+
+	/*
+	 * Amount of memory currently allocated for the dictionary.
+	 * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,
+	 * size_max is always the same as the allocated size.)
+	 */
+	uint32_t allocated;
+
+	/* Operation mode */
+	enum xz_mode mode;
+};
+
+/* Range decoder */
+struct rc_dec {
+	uint32_t range;
+	uint32_t code;
+
+	/*
+	 * Number of initializing bytes remaining to be read
+	 * by rc_read_init().
+	 */
+	uint32_t init_bytes_left;
+
+	/*
+	 * Buffer from which we read our input. It can be either
+	 * temp.buf or the caller-provided input buffer.
+	 */
+	const uint8_t *in;
+	size_t in_pos;
+	size_t in_limit;
+};
+
+/* Probabilities for a length decoder. */
+struct lzma_len_dec {
+	/* Probability of match length being at least 10 */
+	uint16_t choice;
+
+	/* Probability of match length being at least 18 */
+	uint16_t choice2;
+
+	/* Probabilities for match lengths 2-9 */
+	uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];
+
+	/* Probabilities for match lengths 10-17 */
+	uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];
+
+	/* Probabilities for match lengths 18-273 */
+	uint16_t high[LEN_HIGH_SYMBOLS];
+};
+
+struct lzma_dec {
+	/* Distances of latest four matches */
+	uint32_t rep0;
+	uint32_t rep1;
+	uint32_t rep2;
+	uint32_t rep3;
+
+	/* Types of the most recently seen LZMA symbols */
+	enum lzma_state state;
+
+	/*
+	 * Length of a match. This is updated so that dict_repeat can
+	 * be called again to finish repeating the whole match.
+	 */
+	uint32_t len;
+
+	/*
+	 * LZMA properties or related bit masks (number of literal
+	 * context bits, a mask dervied from the number of literal
+	 * position bits, and a mask dervied from the number
+	 * position bits)
+	 */
+	uint32_t lc;
+	uint32_t literal_pos_mask; /* (1 << lp) - 1 */
+	uint32_t pos_mask;         /* (1 << pb) - 1 */
+
+	/* If 1, it's a match. Otherwise it's a single 8-bit literal. */
+	uint16_t is_match[STATES][POS_STATES_MAX];
+
+	/* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */
+	uint16_t is_rep[STATES];
+
+	/*
+	 * If 0, distance of a repeated match is rep0.
+	 * Otherwise check is_rep1.
+	 */
+	uint16_t is_rep0[STATES];
+
+	/*
+	 * If 0, distance of a repeated match is rep1.
+	 * Otherwise check is_rep2.
+	 */
+	uint16_t is_rep1[STATES];
+
+	/* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */
+	uint16_t is_rep2[STATES];
+
+	/*
+	 * If 1, the repeated match has length of one byte. Otherwise
+	 * the length is decoded from rep_len_decoder.
+	 */
+	uint16_t is_rep0_long[STATES][POS_STATES_MAX];
+
+	/*
+	 * Probability tree for the highest two bits of the match
+	 * distance. There is a separate probability tree for match
+	 * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].
+	 */
+	uint16_t dist_slot[DIST_STATES][DIST_SLOTS];
+
+	/*
+	 * Probility trees for additional bits for match distance
+	 * when the distance is in the range [4, 127].
+	 */
+	uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];
+
+	/*
+	 * Probability tree for the lowest four bits of a match
+	 * distance that is equal to or greater than 128.
+	 */
+	uint16_t dist_align[ALIGN_SIZE];
+
+	/* Length of a normal match */
+	struct lzma_len_dec match_len_dec;
+
+	/* Length of a repeated match */
+	struct lzma_len_dec rep_len_dec;
+
+	/* Probabilities of literals */
+	uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];
+};
+
+struct lzma2_dec {
+	/* Position in xz_dec_lzma2_run(). */
+	enum lzma2_seq {
+		SEQ_CONTROL,
+		SEQ_UNCOMPRESSED_1,
+		SEQ_UNCOMPRESSED_2,
+		SEQ_COMPRESSED_0,
+		SEQ_COMPRESSED_1,
+		SEQ_PROPERTIES,
+		SEQ_LZMA_PREPARE,
+		SEQ_LZMA_RUN,
+		SEQ_COPY
+	} sequence;
+
+	/* Next position after decoding the compressed size of the chunk. */
+	enum lzma2_seq next_sequence;
+
+	/* Uncompressed size of LZMA chunk (2 MiB at maximum) */
+	uint32_t uncompressed;
+
+	/*
+	 * Compressed size of LZMA chunk or compressed/uncompressed
+	 * size of uncompressed chunk (64 KiB at maximum)
+	 */
+	uint32_t compressed;
+
+	/*
+	 * True if dictionary reset is needed. This is false before
+	 * the first chunk (LZMA or uncompressed).
+	 */
+	bool need_dict_reset;
+
+	/*
+	 * True if new LZMA properties are needed. This is false
+	 * before the first LZMA chunk.
+	 */
+	bool need_props;
+};
+
+struct xz_dec_lzma2 {
+	/*
+	 * The order below is important on x86 to reduce code size and
+	 * it shouldn't hurt on other platforms. Everything up to and
+	 * including lzma.pos_mask are in the first 128 bytes on x86-32,
+	 * which allows using smaller instructions to access those
+	 * variables. On x86-64, fewer variables fit into the first 128
+	 * bytes, but this is still the best order without sacrificing
+	 * the readability by splitting the structures.
+	 */
+	struct rc_dec rc;
+	struct dictionary dict;
+	struct lzma2_dec lzma2;
+	struct lzma_dec lzma;
+
+	/*
+	 * Temporary buffer which holds small number of input bytes between
+	 * decoder calls. See lzma2_lzma() for details.
+	 */
+	struct {
+		uint32_t size;
+		uint8_t buf[3 * LZMA_IN_REQUIRED];
+	} temp;
+};
+
+/**************
+ * Dictionary *
+ **************/
+
+/*
+ * Reset the dictionary state. When in single-call mode, set up the beginning
+ * of the dictionary to point to the actual output buffer.
+ */
+static void XZ_FUNC dict_reset(struct dictionary *dict, struct xz_buf *b)
+{
+	if (DEC_IS_SINGLE(dict->mode)) {
+		dict->buf = b->out + b->out_pos;
+		dict->end = b->out_size - b->out_pos;
+	}
+
+	dict->start = 0;
+	dict->pos = 0;
+	dict->limit = 0;
+	dict->full = 0;
+}
+
+/* Set dictionary write limit */
+static void XZ_FUNC dict_limit(struct dictionary *dict, size_t out_max)
+{
+	if (dict->end - dict->pos <= out_max)
+		dict->limit = dict->end;
+	else
+		dict->limit = dict->pos + out_max;
+}
+
+/* Return true if at least one byte can be written into the dictionary. */
+static __always_inline bool XZ_FUNC dict_has_space(const struct dictionary *dict)
+{
+	return dict->pos < dict->limit;
+}
+
+/*
+ * Get a byte from the dictionary at the given distance. The distance is
+ * assumed to valid, or as a special case, zero when the dictionary is
+ * still empty. This special case is needed for single-call decoding to
+ * avoid writing a '\0' to the end of the destination buffer.
+ */
+static __always_inline uint32_t XZ_FUNC dict_get(
+		const struct dictionary *dict, uint32_t dist)
+{
+	size_t offset = dict->pos - dist - 1;
+
+	if (dist >= dict->pos)
+		offset += dict->end;
+
+	return dict->full > 0 ? dict->buf[offset] : 0;
+}
+
+/*
+ * Put one byte into the dictionary. It is assumed that there is space for it.
+ */
+static inline void XZ_FUNC dict_put(struct dictionary *dict, uint8_t byte)
+{
+	dict->buf[dict->pos++] = byte;
+
+	if (dict->full < dict->pos)
+		dict->full = dict->pos;
+}
+
+/*
+ * Repeat given number of bytes from the given distance. If the distance is
+ * invalid, false is returned. On success, true is returned and *len is
+ * updated to indicate how many bytes were left to be repeated.
+ */
+static bool XZ_FUNC dict_repeat(
+		struct dictionary *dict, uint32_t *len, uint32_t dist)
+{
+	size_t back;
+	uint32_t left;
+
+	if (dist >= dict->full || dist >= dict->size)
+		return false;
+
+	left = min_t(size_t, dict->limit - dict->pos, *len);
+	*len -= left;
+
+	back = dict->pos - dist - 1;
+	if (dist >= dict->pos)
+		back += dict->end;
+
+	do {
+		dict->buf[dict->pos++] = dict->buf[back++];
+		if (back == dict->end)
+			back = 0;
+	} while (--left > 0);
+
+	if (dict->full < dict->pos)
+		dict->full = dict->pos;
+
+	return true;
+}
+
+/* Copy uncompressed data as is from input to dictionary and output buffers. */
+static void XZ_FUNC dict_uncompressed(
+		struct dictionary *dict, struct xz_buf *b, uint32_t *left)
+{
+	size_t copy_size;
+
+	while (*left > 0 && b->in_pos < b->in_size
+			&& b->out_pos < b->out_size) {
+		copy_size = min(b->in_size - b->in_pos,
+				b->out_size - b->out_pos);
+		if (copy_size > dict->end - dict->pos)
+			copy_size = dict->end - dict->pos;
+		if (copy_size > *left)
+			copy_size = *left;
+
+		*left -= copy_size;
+
+		memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);
+		dict->pos += copy_size;
+
+		if (dict->full < dict->pos)
+			dict->full = dict->pos;
+
+		if (DEC_IS_MULTI(dict->mode)) {
+			if (dict->pos == dict->end)
+				dict->pos = 0;
+
+			memcpy(b->out + b->out_pos, b->in + b->in_pos,
+					copy_size);
+		}
+
+		dict->start = dict->pos;
+
+		b->out_pos += copy_size;
+		b->in_pos += copy_size;
+
+	}
+}
+
+/*
+ * Flush pending data from dictionary to b->out. It is assumed that there is
+ * enough space in b->out. This is guaranteed because caller uses dict_limit()
+ * before decoding data into the dictionary.
+ */
+static uint32_t XZ_FUNC dict_flush(struct dictionary *dict, struct xz_buf *b)
+{
+	size_t copy_size = dict->pos - dict->start;
+
+	if (DEC_IS_MULTI(dict->mode)) {
+		if (dict->pos == dict->end)
+			dict->pos = 0;
+
+		memcpy(b->out + b->out_pos, dict->buf + dict->start,
+				copy_size);
+	}
+
+	dict->start = dict->pos;
+	b->out_pos += copy_size;
+	return copy_size;
+}
+
+/*****************
+ * Range decoder *
+ *****************/
+
+/* Reset the range decoder. */
+static void XZ_FUNC rc_reset(struct rc_dec *rc)
+{
+	rc->range = (uint32_t)-1;
+	rc->code = 0;
+	rc->init_bytes_left = RC_INIT_BYTES;
+}
+
+/*
+ * Read the first five initial bytes into rc->code if they haven't been
+ * read already. (Yes, the first byte gets completely ignored.)
+ */
+static bool XZ_FUNC rc_read_init(struct rc_dec *rc, struct xz_buf *b)
+{
+	while (rc->init_bytes_left > 0) {
+		if (b->in_pos == b->in_size)
+			return false;
+
+		rc->code = (rc->code << 8) + b->in[b->in_pos++];
+		--rc->init_bytes_left;
+	}
+
+	return true;
+}
+
+/* Return true if there may not be enough input for the next decoding loop. */
+static inline bool XZ_FUNC rc_limit_exceeded(const struct rc_dec *rc)
+{
+	return rc->in_pos > rc->in_limit;
+}
+
+/*
+ * Return true if it is possible (from point of view of range decoder) that
+ * we have reached the end of the LZMA chunk.
+ */
+static inline bool XZ_FUNC rc_is_finished(const struct rc_dec *rc)
+{
+	return rc->code == 0;
+}
+
+/* Read the next input byte if needed. */
+static __always_inline void XZ_FUNC rc_normalize(struct rc_dec *rc)
+{
+	if (rc->range < RC_TOP_VALUE) {
+		rc->range <<= RC_SHIFT_BITS;
+		rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];
+	}
+}
+
+/*
+ * Decode one bit. In some versions, this function has been splitted in three
+ * functions so that the compiler is supposed to be able to more easily avoid
+ * an extra branch. In this particular version of the LZMA decoder, this
+ * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3
+ * on x86). Using a non-splitted version results in nicer looking code too.
+ *
+ * NOTE: This must return an int. Do not make it return a bool or the speed
+ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,
+ * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)
+ */
+static __always_inline int XZ_FUNC rc_bit(struct rc_dec *rc, uint16_t *prob)
+{
+	uint32_t bound;
+	int bit;
+
+	rc_normalize(rc);
+	bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;
+	if (rc->code < bound) {
+		rc->range = bound;
+		*prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;
+		bit = 0;
+	} else {
+		rc->range -= bound;
+		rc->code -= bound;
+		*prob -= *prob >> RC_MOVE_BITS;
+		bit = 1;
+	}
+
+	return bit;
+}
+
+/* Decode a bittree starting from the most significant bit. */
+static __always_inline uint32_t XZ_FUNC rc_bittree(
+		struct rc_dec *rc, uint16_t *probs, uint32_t limit)
+{
+	uint32_t symbol = 1;
+
+	do {
+		if (rc_bit(rc, &probs[symbol]))
+			symbol = (symbol << 1) + 1;
+		else
+			symbol <<= 1;
+	} while (symbol < limit);
+
+	return symbol;
+}
+
+/* Decode a bittree starting from the least significant bit. */
+static __always_inline void XZ_FUNC rc_bittree_reverse(struct rc_dec *rc,
+		uint16_t *probs, uint32_t *dest, uint32_t limit)
+{
+	uint32_t symbol = 1;
+	uint32_t i = 0;
+
+	do {
+		if (rc_bit(rc, &probs[symbol])) {
+			symbol = (symbol << 1) + 1;
+			*dest += 1 << i;
+		} else {
+			symbol <<= 1;
+		}
+	} while (++i < limit);
+}
+
+/* Decode direct bits (fixed fifty-fifty probability) */
+static inline void XZ_FUNC rc_direct(
+		struct rc_dec *rc, uint32_t *dest, uint32_t limit)
+{
+	uint32_t mask;
+
+	do {
+		rc_normalize(rc);
+		rc->range >>= 1;
+		rc->code -= rc->range;
+		mask = (uint32_t)0 - (rc->code >> 31);
+		rc->code += rc->range & mask;
+		*dest = (*dest << 1) + (mask + 1);
+	} while (--limit > 0);
+}
+
+/********
+ * LZMA *
+ ********/
+
+/* Get pointer to literal coder probability array. */
+static uint16_t * XZ_FUNC lzma_literal_probs(struct xz_dec_lzma2 *s)
+{
+	uint32_t prev_byte = dict_get(&s->dict, 0);
+	uint32_t low = prev_byte >> (8 - s->lzma.lc);
+	uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;
+	return s->lzma.literal[low + high];
+}
+
+/* Decode a literal (one 8-bit byte) */
+static void XZ_FUNC lzma_literal(struct xz_dec_lzma2 *s)
+{
+	uint16_t *probs;
+	uint32_t symbol;
+	uint32_t match_byte;
+	uint32_t match_bit;
+	uint32_t offset;
+	uint32_t i;
+
+	probs = lzma_literal_probs(s);
+
+	if (lzma_state_is_literal(s->lzma.state)) {
+		symbol = rc_bittree(&s->rc, probs, 0x100);
+	} else {
+		symbol = 1;
+		match_byte = dict_get(&s->dict, s->lzma.rep0) << 1;
+		offset = 0x100;
+
+		do {
+			match_bit = match_byte & offset;
+			match_byte <<= 1;
+			i = offset + match_bit + symbol;
+
+			if (rc_bit(&s->rc, &probs[i])) {
+				symbol = (symbol << 1) + 1;
+				offset &= match_bit;
+			} else {
+				symbol <<= 1;
+				offset &= ~match_bit;
+			}
+		} while (symbol < 0x100);
+	}
+
+	dict_put(&s->dict, (uint8_t)symbol);
+	lzma_state_literal(&s->lzma.state);
+}
+
+/* Decode the length of the match into s->lzma.len. */
+static void XZ_FUNC lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l,
+		uint32_t pos_state)
+{
+	uint16_t *probs;
+	uint32_t limit;
+
+	if (!rc_bit(&s->rc, &l->choice)) {
+		probs = l->low[pos_state];
+		limit = LEN_LOW_SYMBOLS;
+		s->lzma.len = MATCH_LEN_MIN;
+	} else {
+		if (!rc_bit(&s->rc, &l->choice2)) {
+			probs = l->mid[pos_state];
+			limit = LEN_MID_SYMBOLS;
+			s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;
+		} else {
+			probs = l->high;
+			limit = LEN_HIGH_SYMBOLS;
+			s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS
+					+ LEN_MID_SYMBOLS;
+		}
+	}
+
+	s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;
+}
+
+/* Decode a match. The distance will be stored in s->lzma.rep0. */
+static void XZ_FUNC lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+	uint16_t *probs;
+	uint32_t dist_slot;
+	uint32_t limit;
+
+	lzma_state_match(&s->lzma.state);
+
+	s->lzma.rep3 = s->lzma.rep2;
+	s->lzma.rep2 = s->lzma.rep1;
+	s->lzma.rep1 = s->lzma.rep0;
+
+	lzma_len(s, &s->lzma.match_len_dec, pos_state);
+
+	probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];
+	dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;
+
+	if (dist_slot < DIST_MODEL_START) {
+		s->lzma.rep0 = dist_slot;
+	} else {
+		limit = (dist_slot >> 1) - 1;
+		s->lzma.rep0 = 2 + (dist_slot & 1);
+
+		if (dist_slot < DIST_MODEL_END) {
+			s->lzma.rep0 <<= limit;
+			probs = s->lzma.dist_special + s->lzma.rep0
+					- dist_slot - 1;
+			rc_bittree_reverse(&s->rc, probs,
+					&s->lzma.rep0, limit);
+		} else {
+			rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);
+			s->lzma.rep0 <<= ALIGN_BITS;
+			rc_bittree_reverse(&s->rc, s->lzma.dist_align,
+					&s->lzma.rep0, ALIGN_BITS);
+		}
+	}
+}
+
+/*
+ * Decode a repeated match. The distance is one of the four most recently
+ * seen matches. The distance will be stored in s->lzma.rep0.
+ */
+static void XZ_FUNC lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+	uint32_t tmp;
+
+	if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) {
+		if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[
+				s->lzma.state][pos_state])) {
+			lzma_state_short_rep(&s->lzma.state);
+			s->lzma.len = 1;
+			return;
+		}
+	} else {
+		if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) {
+			tmp = s->lzma.rep1;
+		} else {
+			if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) {
+				tmp = s->lzma.rep2;
+			} else {
+				tmp = s->lzma.rep3;
+				s->lzma.rep3 = s->lzma.rep2;
+			}
+
+			s->lzma.rep2 = s->lzma.rep1;
+		}
+
+		s->lzma.rep1 = s->lzma.rep0;
+		s->lzma.rep0 = tmp;
+	}
+
+	lzma_state_long_rep(&s->lzma.state);
+	lzma_len(s, &s->lzma.rep_len_dec, pos_state);
+}
+
+/* LZMA decoder core */
+static bool XZ_FUNC lzma_main(struct xz_dec_lzma2 *s)
+{
+	uint32_t pos_state;
+
+	/*
+	 * If the dictionary was reached during the previous call, try to
+	 * finish the possibly pending repeat in the dictionary.
+	 */
+	if (dict_has_space(&s->dict) && s->lzma.len > 0)
+		dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);
+
+	/*
+	 * Decode more LZMA symbols. One iteration may consume up to
+	 * LZMA_IN_REQUIRED - 1 bytes.
+	 */
+	while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) {
+		pos_state = s->dict.pos & s->lzma.pos_mask;
+
+		if (!rc_bit(&s->rc, &s->lzma.is_match[
+				s->lzma.state][pos_state])) {
+			lzma_literal(s);
+		} else {
+			if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))
+				lzma_rep_match(s, pos_state);
+			else
+				lzma_match(s, pos_state);
+
+			if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))
+				return false;
+		}
+	}
+
+	/*
+	 * Having the range decoder always normalized when we are outside
+	 * this function makes it easier to correctly handle end of the chunk.
+	 */
+	rc_normalize(&s->rc);
+
+	return true;
+}
+
+/*
+ * Reset the LZMA decoder and range decoder state. Dictionary is nore reset
+ * here, because LZMA state may be reset without resetting the dictionary.
+ */
+static void XZ_FUNC lzma_reset(struct xz_dec_lzma2 *s)
+{
+	uint16_t *probs;
+	size_t i;
+
+	s->lzma.state = STATE_LIT_LIT;
+	s->lzma.rep0 = 0;
+	s->lzma.rep1 = 0;
+	s->lzma.rep2 = 0;
+	s->lzma.rep3 = 0;
+
+	/*
+	 * All probabilities are initialized to the same value. This hack
+	 * makes the code smaller by avoiding a separate loop for each
+	 * probability array.
+	 *
+	 * This could be optimized so that only that part of literal
+	 * probabilities that are actually required. In the common case
+	 * we would write 12 KiB less.
+	 */
+	probs = s->lzma.is_match[0];
+	for (i = 0; i < PROBS_TOTAL; ++i)
+		probs[i] = RC_BIT_MODEL_TOTAL / 2;
+
+	rc_reset(&s->rc);
+}
+
+/*
+ * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks
+ * from the decoded lp and pb values. On success, the LZMA decoder state is
+ * reset and true is returned.
+ */
+static bool XZ_FUNC lzma_props(struct xz_dec_lzma2 *s, uint8_t props)
+{
+	if (props > (4 * 5 + 4) * 9 + 8)
+		return false;
+
+	s->lzma.pos_mask = 0;
+	while (props >= 9 * 5) {
+		props -= 9 * 5;
+		++s->lzma.pos_mask;
+	}
+
+	s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;
+
+	s->lzma.literal_pos_mask = 0;
+	while (props >= 9) {
+		props -= 9;
+		++s->lzma.literal_pos_mask;
+	}
+
+	s->lzma.lc = props;
+
+	if (s->lzma.lc + s->lzma.literal_pos_mask > 4)
+		return false;
+
+	s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;
+
+	lzma_reset(s);
+
+	return true;
+}
+
+/*********
+ * LZMA2 *
+ *********/
+
+/*
+ * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't
+ * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This
+ * wrapper function takes care of making the LZMA decoder's assumption safe.
+ *
+ * As long as there is plenty of input left to be decoded in the current LZMA
+ * chunk, we decode directly from the caller-supplied input buffer until
+ * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into
+ * s->temp.buf, which (hopefully) gets filled on the next call to this
+ * function. We decode a few bytes from the temporary buffer so that we can
+ * continue decoding from the caller-supplied input buffer again.
+ */
+static bool XZ_FUNC lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+	size_t in_avail;
+	uint32_t tmp;
+
+	in_avail = b->in_size - b->in_pos;
+	if (s->temp.size > 0 || s->lzma2.compressed == 0) {
+		tmp = 2 * LZMA_IN_REQUIRED - s->temp.size;
+		if (tmp > s->lzma2.compressed - s->temp.size)
+			tmp = s->lzma2.compressed - s->temp.size;
+		if (tmp > in_avail)
+			tmp = in_avail;
+
+		memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);
+
+		if (s->temp.size + tmp == s->lzma2.compressed) {
+			memzero(s->temp.buf + s->temp.size + tmp,
+					sizeof(s->temp.buf)
+						- s->temp.size - tmp);
+			s->rc.in_limit = s->temp.size + tmp;
+		} else if (s->temp.size + tmp < LZMA_IN_REQUIRED) {
+			s->temp.size += tmp;
+			b->in_pos += tmp;
+			return true;
+		} else {
+			s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;
+		}
+
+		s->rc.in = s->temp.buf;
+		s->rc.in_pos = 0;
+
+		if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)
+			return false;
+
+		s->lzma2.compressed -= s->rc.in_pos;
+
+		if (s->rc.in_pos < s->temp.size) {
+			s->temp.size -= s->rc.in_pos;
+			memmove(s->temp.buf, s->temp.buf + s->rc.in_pos,
+					s->temp.size);
+			return true;
+		}
+
+		b->in_pos += s->rc.in_pos - s->temp.size;
+		s->temp.size = 0;
+	}
+
+	in_avail = b->in_size - b->in_pos;
+	if (in_avail >= LZMA_IN_REQUIRED) {
+		s->rc.in = b->in;
+		s->rc.in_pos = b->in_pos;
+
+		if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)
+			s->rc.in_limit = b->in_pos + s->lzma2.compressed;
+		else
+			s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;
+
+		if (!lzma_main(s))
+			return false;
+
+		in_avail = s->rc.in_pos - b->in_pos;
+		if (in_avail > s->lzma2.compressed)
+			return false;
+
+		s->lzma2.compressed -= in_avail;
+		b->in_pos = s->rc.in_pos;
+	}
+
+	in_avail = b->in_size - b->in_pos;
+	if (in_avail < LZMA_IN_REQUIRED) {
+		if (in_avail > s->lzma2.compressed)
+			in_avail = s->lzma2.compressed;
+
+		memcpy(s->temp.buf, b->in + b->in_pos, in_avail);
+		s->temp.size = in_avail;
+		b->in_pos += in_avail;
+	}
+
+	return true;
+}
+
+/*
+ * Take care of the LZMA2 control layer, and forward the job of actual LZMA
+ * decoding or copying of uncompressed chunks to other functions.
+ */
+XZ_EXTERN NOINLINE enum xz_ret XZ_FUNC xz_dec_lzma2_run(
+		struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+	uint32_t tmp;
+
+	while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) {
+		switch (s->lzma2.sequence) {
+		case SEQ_CONTROL:
+			/*
+			 * LZMA2 control byte
+			 *
+			 * Exact values:
+			 *   0x00   End marker
+			 *   0x01   Dictionary reset followed by
+			 *          an uncompressed chunk
+			 *   0x02   Uncompressed chunk (no dictionary reset)
+			 *
+			 * Highest three bits (s->control & 0xE0):
+			 *   0xE0   Dictionary reset, new properties and state
+			 *          reset, followed by LZMA compressed chunk
+			 *   0xC0   New properties and state reset, followed
+			 *          by LZMA compressed chunk (no dictionary
+			 *          reset)
+			 *   0xA0   State reset using old properties,
+			 *          followed by LZMA compressed chunk (no
+			 *          dictionary reset)
+			 *   0x80   LZMA chunk (no dictionary or state reset)
+			 *
+			 * For LZMA compressed chunks, the lowest five bits
+			 * (s->control & 1F) are the highest bits of the
+			 * uncompressed size (bits 16-20).
+			 *
+			 * A new LZMA2 stream must begin with a dictionary
+			 * reset. The first LZMA chunk must set new
+			 * properties and reset the LZMA state.
+			 *
+			 * Values that don't match anything described above
+			 * are invalid and we return XZ_DATA_ERROR.
+			 */
+			tmp = b->in[b->in_pos++];
+
+			if (tmp >= 0xE0 || tmp == 0x01) {
+				s->lzma2.need_props = true;
+				s->lzma2.need_dict_reset = false;
+				dict_reset(&s->dict, b);
+			} else if (s->lzma2.need_dict_reset) {
+				return XZ_DATA_ERROR;
+			}
+
+			if (tmp >= 0x80) {
+				s->lzma2.uncompressed = (tmp & 0x1F) << 16;
+				s->lzma2.sequence = SEQ_UNCOMPRESSED_1;
+
+				if (tmp >= 0xC0) {
+					/*
+					 * When there are new properties,
+					 * state reset is done at
+					 * SEQ_PROPERTIES.
+					 */
+					s->lzma2.need_props = false;
+					s->lzma2.next_sequence
+							= SEQ_PROPERTIES;
+
+				} else if (s->lzma2.need_props) {
+					return XZ_DATA_ERROR;
+
+				} else {
+					s->lzma2.next_sequence
+							= SEQ_LZMA_PREPARE;
+					if (tmp >= 0xA0)
+						lzma_reset(s);
+				}
+			} else {
+				if (tmp == 0x00)
+					return XZ_STREAM_END;
+
+				if (tmp > 0x02)
+					return XZ_DATA_ERROR;
+
+				s->lzma2.sequence = SEQ_COMPRESSED_0;
+				s->lzma2.next_sequence = SEQ_COPY;
+			}
+
+			break;
+
+		case SEQ_UNCOMPRESSED_1:
+			s->lzma2.uncompressed
+					+= (uint32_t)b->in[b->in_pos++] << 8;
+			s->lzma2.sequence = SEQ_UNCOMPRESSED_2;
+			break;
+
+		case SEQ_UNCOMPRESSED_2:
+			s->lzma2.uncompressed
+					+= (uint32_t)b->in[b->in_pos++] + 1;
+			s->lzma2.sequence = SEQ_COMPRESSED_0;
+			break;
+
+		case SEQ_COMPRESSED_0:
+			s->lzma2.compressed
+					= (uint32_t)b->in[b->in_pos++] << 8;
+			s->lzma2.sequence = SEQ_COMPRESSED_1;
+			break;
+
+		case SEQ_COMPRESSED_1:
+			s->lzma2.compressed
+					+= (uint32_t)b->in[b->in_pos++] + 1;
+			s->lzma2.sequence = s->lzma2.next_sequence;
+			break;
+
+		case SEQ_PROPERTIES:
+			if (!lzma_props(s, b->in[b->in_pos++]))
+				return XZ_DATA_ERROR;
+
+			s->lzma2.sequence = SEQ_LZMA_PREPARE;
+
+		case SEQ_LZMA_PREPARE:
+			if (s->lzma2.compressed < RC_INIT_BYTES)
+				return XZ_DATA_ERROR;
+
+			if (!rc_read_init(&s->rc, b))
+				return XZ_OK;
+
+			s->lzma2.compressed -= RC_INIT_BYTES;
+			s->lzma2.sequence = SEQ_LZMA_RUN;
+
+		case SEQ_LZMA_RUN:
+			/*
+			 * Set dictionary limit to indicate how much we want
+			 * to be encoded at maximum. Decode new data into the
+			 * dictionary. Flush the new data from dictionary to
+			 * b->out. Check if we finished decoding this chunk.
+			 * In case the dictionary got full but we didn't fill
+			 * the output buffer yet, we may run this loop
+			 * multiple times without changing s->lzma2.sequence.
+			 */
+			dict_limit(&s->dict, min_t(size_t,
+					b->out_size - b->out_pos,
+					s->lzma2.uncompressed));
+			if (!lzma2_lzma(s, b))
+				return XZ_DATA_ERROR;
+
+			s->lzma2.uncompressed -= dict_flush(&s->dict, b);
+
+			if (s->lzma2.uncompressed == 0) {
+				if (s->lzma2.compressed > 0 || s->lzma.len > 0
+						|| !rc_is_finished(&s->rc))
+					return XZ_DATA_ERROR;
+
+				rc_reset(&s->rc);
+				s->lzma2.sequence = SEQ_CONTROL;
+
+			} else if (b->out_pos == b->out_size
+					|| (b->in_pos == b->in_size
+						&& s->temp.size
+						< s->lzma2.compressed)) {
+				return XZ_OK;
+			}
+
+			break;
+
+		case SEQ_COPY:
+			dict_uncompressed(&s->dict, b, &s->lzma2.compressed);
+			if (s->lzma2.compressed > 0)
+				return XZ_OK;
+
+			s->lzma2.sequence = SEQ_CONTROL;
+			break;
+		}
+	}
+
+	return XZ_OK;
+}
+
+XZ_EXTERN struct xz_dec_lzma2 * XZ_FUNC xz_dec_lzma2_create(
+		enum xz_mode mode, uint32_t dict_max)
+{
+	struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (s == NULL)
+		return NULL;
+
+	s->dict.mode = mode;
+	s->dict.size_max = dict_max;
+
+	if (DEC_IS_PREALLOC(mode)) {
+		s->dict.buf = vmalloc(dict_max);
+		if (s->dict.buf == NULL) {
+			kfree(s);
+			return NULL;
+		}
+	} else if (DEC_IS_DYNALLOC(mode)) {
+		s->dict.buf = NULL;
+		s->dict.allocated = 0;
+	}
+
+	return s;
+}
+
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_reset(
+		struct xz_dec_lzma2 *s, uint8_t props)
+{
+	/* This limits dictionary size to 3 GiB to keep parsing simpler. */
+	if (props > 39)
+		return XZ_OPTIONS_ERROR;
+
+	s->dict.size = 2 + (props & 1);
+	s->dict.size <<= (props >> 1) + 11;
+
+	if (DEC_IS_MULTI(s->dict.mode)) {
+		if (s->dict.size > s->dict.size_max)
+			return XZ_MEMLIMIT_ERROR;
+
+		s->dict.end = s->dict.size;
+
+		if (DEC_IS_DYNALLOC(s->dict.mode)) {
+			if (s->dict.allocated < s->dict.size) {
+				vfree(s->dict.buf);
+				s->dict.buf = vmalloc(s->dict.size);
+				if (s->dict.buf == NULL) {
+					s->dict.allocated = 0;
+					return XZ_MEM_ERROR;
+				}
+			}
+		}
+	}
+
+	s->lzma.len = 0;
+
+	s->lzma2.sequence = SEQ_CONTROL;
+	s->lzma2.need_dict_reset = true;
+
+	s->temp.size = 0;
+
+	return XZ_OK;
+}
+
+XZ_EXTERN void XZ_FUNC xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
+{
+	if (DEC_IS_MULTI(s->dict.mode))
+		vfree(s->dict.buf);
+
+	kfree(s);
+}
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz_dec_stream.c b/busybox-1.19.3/archival/libarchive/unxz/xz_dec_stream.c
new file mode 100644
index 0000000..bdcbf1b
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz_dec_stream.c
@@ -0,0 +1,822 @@
+/*
+ * .xz Stream decoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_stream.h"
+
+/* Hash used to validate the Index field */
+struct xz_dec_hash {
+	vli_type unpadded;
+	vli_type uncompressed;
+	uint32_t crc32;
+};
+
+struct xz_dec {
+	/* Position in dec_main() */
+	enum {
+		SEQ_STREAM_HEADER,
+		SEQ_BLOCK_START,
+		SEQ_BLOCK_HEADER,
+		SEQ_BLOCK_UNCOMPRESS,
+		SEQ_BLOCK_PADDING,
+		SEQ_BLOCK_CHECK,
+		SEQ_INDEX,
+		SEQ_INDEX_PADDING,
+		SEQ_INDEX_CRC32,
+		SEQ_STREAM_FOOTER
+	} sequence;
+
+	/* Position in variable-length integers and Check fields */
+	uint32_t pos;
+
+	/* Variable-length integer decoded by dec_vli() */
+	vli_type vli;
+
+	/* Saved in_pos and out_pos */
+	size_t in_start;
+	size_t out_start;
+
+	/* CRC32 value in Block or Index */
+	uint32_t crc32;
+
+	/* Type of the integrity check calculated from uncompressed data */
+	enum xz_check check_type;
+
+	/* Operation mode */
+	enum xz_mode mode;
+
+	/*
+	 * True if the next call to xz_dec_run() is allowed to return
+	 * XZ_BUF_ERROR.
+	 */
+	bool allow_buf_error;
+
+	/* Information stored in Block Header */
+	struct {
+		/*
+		 * Value stored in the Compressed Size field, or
+		 * VLI_UNKNOWN if Compressed Size is not present.
+		 */
+		vli_type compressed;
+
+		/*
+		 * Value stored in the Uncompressed Size field, or
+		 * VLI_UNKNOWN if Uncompressed Size is not present.
+		 */
+		vli_type uncompressed;
+
+		/* Size of the Block Header field */
+		uint32_t size;
+	} block_header;
+
+	/* Information collected when decoding Blocks */
+	struct {
+		/* Observed compressed size of the current Block */
+		vli_type compressed;
+
+		/* Observed uncompressed size of the current Block */
+		vli_type uncompressed;
+
+		/* Number of Blocks decoded so far */
+		vli_type count;
+
+		/*
+		 * Hash calculated from the Block sizes. This is used to
+		 * validate the Index field.
+		 */
+		struct xz_dec_hash hash;
+	} block;
+
+	/* Variables needed when verifying the Index field */
+	struct {
+		/* Position in dec_index() */
+		enum {
+			SEQ_INDEX_COUNT,
+			SEQ_INDEX_UNPADDED,
+			SEQ_INDEX_UNCOMPRESSED
+		} sequence;
+
+		/* Size of the Index in bytes */
+		vli_type size;
+
+		/* Number of Records (matches block.count in valid files) */
+		vli_type count;
+
+		/*
+		 * Hash calculated from the Records (matches block.hash in
+		 * valid files).
+		 */
+		struct xz_dec_hash hash;
+	} index;
+
+	/*
+	 * Temporary buffer needed to hold Stream Header, Block Header,
+	 * and Stream Footer. The Block Header is the biggest (1 KiB)
+	 * so we reserve space according to that. buf[] has to be aligned
+	 * to a multiple of four bytes; the size_t variables before it
+	 * should guarantee this.
+	 */
+	struct {
+		size_t pos;
+		size_t size;
+		uint8_t buf[1024];
+	} temp;
+
+	struct xz_dec_lzma2 *lzma2;
+
+#ifdef XZ_DEC_BCJ
+	struct xz_dec_bcj *bcj;
+	bool bcj_active;
+#endif
+};
+
+#ifdef XZ_DEC_ANY_CHECK
+/* Sizes of the Check field with different Check IDs */
+static const uint8_t check_sizes[16] = {
+	0,
+	4, 4, 4,
+	8, 8, 8,
+	16, 16, 16,
+	32, 32, 32,
+	64, 64, 64
+};
+#endif
+
+/*
+ * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
+ * must have set s->temp.pos to indicate how much data we are supposed
+ * to copy into s->temp.buf. Return true once s->temp.pos has reached
+ * s->temp.size.
+ */
+static bool XZ_FUNC fill_temp(struct xz_dec *s, struct xz_buf *b)
+{
+	size_t copy_size = min_t(size_t,
+			b->in_size - b->in_pos, s->temp.size - s->temp.pos);
+
+	memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
+	b->in_pos += copy_size;
+	s->temp.pos += copy_size;
+
+	if (s->temp.pos == s->temp.size) {
+		s->temp.pos = 0;
+		return true;
+	}
+
+	return false;
+}
+
+/* Decode a variable-length integer (little-endian base-128 encoding) */
+static enum xz_ret XZ_FUNC dec_vli(struct xz_dec *s,
+		const uint8_t *in, size_t *in_pos, size_t in_size)
+{
+	uint8_t byte;
+
+	if (s->pos == 0)
+		s->vli = 0;
+
+	while (*in_pos < in_size) {
+		byte = in[*in_pos];
+		++*in_pos;
+
+		s->vli |= (vli_type)(byte & 0x7F) << s->pos;
+
+		if ((byte & 0x80) == 0) {
+			/* Don't allow non-minimal encodings. */
+			if (byte == 0 && s->pos != 0)
+				return XZ_DATA_ERROR;
+
+			s->pos = 0;
+			return XZ_STREAM_END;
+		}
+
+		s->pos += 7;
+		if (s->pos == 7 * VLI_BYTES_MAX)
+			return XZ_DATA_ERROR;
+	}
+
+	return XZ_OK;
+}
+
+/*
+ * Decode the Compressed Data field from a Block. Update and validate
+ * the observed compressed and uncompressed sizes of the Block so that
+ * they don't exceed the values possibly stored in the Block Header
+ * (validation assumes that no integer overflow occurs, since vli_type
+ * is normally uint64_t). Update the CRC32 if presence of the CRC32
+ * field was indicated in Stream Header.
+ *
+ * Once the decoding is finished, validate that the observed sizes match
+ * the sizes possibly stored in the Block Header. Update the hash and
+ * Block count, which are later used to validate the Index field.
+ */
+static enum xz_ret XZ_FUNC dec_block(struct xz_dec *s, struct xz_buf *b)
+{
+	enum xz_ret ret;
+
+	s->in_start = b->in_pos;
+	s->out_start = b->out_pos;
+
+#ifdef XZ_DEC_BCJ
+	if (s->bcj_active)
+		ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
+	else
+#endif
+		ret = xz_dec_lzma2_run(s->lzma2, b);
+
+	s->block.compressed += b->in_pos - s->in_start;
+	s->block.uncompressed += b->out_pos - s->out_start;
+
+	/*
+	 * There is no need to separately check for VLI_UNKNOWN, since
+	 * the observed sizes are always smaller than VLI_UNKNOWN.
+	 */
+	if (s->block.compressed > s->block_header.compressed
+			|| s->block.uncompressed
+				> s->block_header.uncompressed)
+		return XZ_DATA_ERROR;
+
+	if (s->check_type == XZ_CHECK_CRC32)
+		s->crc32 = xz_crc32(b->out + s->out_start,
+				b->out_pos - s->out_start, s->crc32);
+
+	if (ret == XZ_STREAM_END) {
+		if (s->block_header.compressed != VLI_UNKNOWN
+				&& s->block_header.compressed
+					!= s->block.compressed)
+			return XZ_DATA_ERROR;
+
+		if (s->block_header.uncompressed != VLI_UNKNOWN
+				&& s->block_header.uncompressed
+					!= s->block.uncompressed)
+			return XZ_DATA_ERROR;
+
+		s->block.hash.unpadded += s->block_header.size
+				+ s->block.compressed;
+
+#ifdef XZ_DEC_ANY_CHECK
+		s->block.hash.unpadded += check_sizes[s->check_type];
+#else
+		if (s->check_type == XZ_CHECK_CRC32)
+			s->block.hash.unpadded += 4;
+#endif
+
+		s->block.hash.uncompressed += s->block.uncompressed;
+		s->block.hash.crc32 = xz_crc32(
+				(const uint8_t *)&s->block.hash,
+				sizeof(s->block.hash), s->block.hash.crc32);
+
+		++s->block.count;
+	}
+
+	return ret;
+}
+
+/* Update the Index size and the CRC32 value. */
+static void XZ_FUNC index_update(struct xz_dec *s, const struct xz_buf *b)
+{
+	size_t in_used = b->in_pos - s->in_start;
+	s->index.size += in_used;
+	s->crc32 = xz_crc32(b->in + s->in_start, in_used, s->crc32);
+}
+
+/*
+ * Decode the Number of Records, Unpadded Size, and Uncompressed Size
+ * fields from the Index field. That is, Index Padding and CRC32 are not
+ * decoded by this function.
+ *
+ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything
+ * successfully decoded), or XZ_DATA_ERROR (input is corrupt).
+ */
+static enum xz_ret XZ_FUNC dec_index(struct xz_dec *s, struct xz_buf *b)
+{
+	enum xz_ret ret;
+
+	do {
+		ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
+		if (ret != XZ_STREAM_END) {
+			index_update(s, b);
+			return ret;
+		}
+
+		switch (s->index.sequence) {
+		case SEQ_INDEX_COUNT:
+			s->index.count = s->vli;
+
+			/*
+			 * Validate that the Number of Records field
+			 * indicates the same number of Records as
+			 * there were Blocks in the Stream.
+			 */
+			if (s->index.count != s->block.count)
+				return XZ_DATA_ERROR;
+
+			s->index.sequence = SEQ_INDEX_UNPADDED;
+			break;
+
+		case SEQ_INDEX_UNPADDED:
+			s->index.hash.unpadded += s->vli;
+			s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
+			break;
+
+		case SEQ_INDEX_UNCOMPRESSED:
+			s->index.hash.uncompressed += s->vli;
+			s->index.hash.crc32 = xz_crc32(
+					(const uint8_t *)&s->index.hash,
+					sizeof(s->index.hash),
+					s->index.hash.crc32);
+			--s->index.count;
+			s->index.sequence = SEQ_INDEX_UNPADDED;
+			break;
+		}
+	} while (s->index.count > 0);
+
+	return XZ_STREAM_END;
+}
+
+/*
+ * Validate that the next four input bytes match the value of s->crc32.
+ * s->pos must be zero when starting to validate the first byte.
+ */
+static enum xz_ret XZ_FUNC crc32_validate(struct xz_dec *s, struct xz_buf *b)
+{
+	do {
+		if (b->in_pos == b->in_size)
+			return XZ_OK;
+
+		if (((s->crc32 >> s->pos) & 0xFF) != b->in[b->in_pos++])
+			return XZ_DATA_ERROR;
+
+		s->pos += 8;
+
+	} while (s->pos < 32);
+
+	s->crc32 = 0;
+	s->pos = 0;
+
+	return XZ_STREAM_END;
+}
+
+#ifdef XZ_DEC_ANY_CHECK
+/*
+ * Skip over the Check field when the Check ID is not supported.
+ * Returns true once the whole Check field has been skipped over.
+ */
+static bool XZ_FUNC check_skip(struct xz_dec *s, struct xz_buf *b)
+{
+	while (s->pos < check_sizes[s->check_type]) {
+		if (b->in_pos == b->in_size)
+			return false;
+
+		++b->in_pos;
+		++s->pos;
+	}
+
+	s->pos = 0;
+
+	return true;
+}
+#endif
+
+/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
+static enum xz_ret XZ_FUNC dec_stream_header(struct xz_dec *s)
+{
+	if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
+		return XZ_FORMAT_ERROR;
+
+	if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
+			!= get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
+		return XZ_DATA_ERROR;
+
+	if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
+		return XZ_OPTIONS_ERROR;
+
+	/*
+	 * Of integrity checks, we support only none (Check ID = 0) and
+	 * CRC32 (Check ID = 1). However, if XZ_DEC_ANY_CHECK is defined,
+	 * we will accept other check types too, but then the check won't
+	 * be verified and a warning (XZ_UNSUPPORTED_CHECK) will be given.
+	 */
+	s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
+
+#ifdef XZ_DEC_ANY_CHECK
+	if (s->check_type > XZ_CHECK_MAX)
+		return XZ_OPTIONS_ERROR;
+
+	if (s->check_type > XZ_CHECK_CRC32)
+		return XZ_UNSUPPORTED_CHECK;
+#else
+	if (s->check_type > XZ_CHECK_CRC32)
+		return XZ_OPTIONS_ERROR;
+#endif
+
+	return XZ_OK;
+}
+
+/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
+static enum xz_ret XZ_FUNC dec_stream_footer(struct xz_dec *s)
+{
+	if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
+		return XZ_DATA_ERROR;
+
+	if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
+		return XZ_DATA_ERROR;
+
+	/*
+	 * Validate Backward Size. Note that we never added the size of the
+	 * Index CRC32 field to s->index.size, thus we use s->index.size / 4
+	 * instead of s->index.size / 4 - 1.
+	 */
+	if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
+		return XZ_DATA_ERROR;
+
+	if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
+		return XZ_DATA_ERROR;
+
+	/*
+	 * Use XZ_STREAM_END instead of XZ_OK to be more convenient
+	 * for the caller.
+	 */
+	return XZ_STREAM_END;
+}
+
+/* Decode the Block Header and initialize the filter chain. */
+static enum xz_ret XZ_FUNC dec_block_header(struct xz_dec *s)
+{
+	enum xz_ret ret;
+
+	/*
+	 * Validate the CRC32. We know that the temp buffer is at least
+	 * eight bytes so this is safe.
+	 */
+	s->temp.size -= 4;
+	if (xz_crc32(s->temp.buf, s->temp.size, 0)
+			!= get_le32(s->temp.buf + s->temp.size))
+		return XZ_DATA_ERROR;
+
+	s->temp.pos = 2;
+
+	/*
+	 * Catch unsupported Block Flags. We support only one or two filters
+	 * in the chain, so we catch that with the same test.
+	 */
+#ifdef XZ_DEC_BCJ
+	if (s->temp.buf[1] & 0x3E)
+#else
+	if (s->temp.buf[1] & 0x3F)
+#endif
+		return XZ_OPTIONS_ERROR;
+
+	/* Compressed Size */
+	if (s->temp.buf[1] & 0x40) {
+		if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+					!= XZ_STREAM_END)
+			return XZ_DATA_ERROR;
+
+		s->block_header.compressed = s->vli;
+	} else {
+		s->block_header.compressed = VLI_UNKNOWN;
+	}
+
+	/* Uncompressed Size */
+	if (s->temp.buf[1] & 0x80) {
+		if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+				!= XZ_STREAM_END)
+			return XZ_DATA_ERROR;
+
+		s->block_header.uncompressed = s->vli;
+	} else {
+		s->block_header.uncompressed = VLI_UNKNOWN;
+	}
+
+#ifdef XZ_DEC_BCJ
+	/* If there are two filters, the first one must be a BCJ filter. */
+	s->bcj_active = s->temp.buf[1] & 0x01;
+	if (s->bcj_active) {
+		if (s->temp.size - s->temp.pos < 2)
+			return XZ_OPTIONS_ERROR;
+
+		ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
+		if (ret != XZ_OK)
+			return ret;
+
+		/*
+		 * We don't support custom start offset,
+		 * so Size of Properties must be zero.
+		 */
+		if (s->temp.buf[s->temp.pos++] != 0x00)
+			return XZ_OPTIONS_ERROR;
+	}
+#endif
+
+	/* Valid Filter Flags always take at least two bytes. */
+	if (s->temp.size - s->temp.pos < 2)
+		return XZ_DATA_ERROR;
+
+	/* Filter ID = LZMA2 */
+	if (s->temp.buf[s->temp.pos++] != 0x21)
+		return XZ_OPTIONS_ERROR;
+
+	/* Size of Properties = 1-byte Filter Properties */
+	if (s->temp.buf[s->temp.pos++] != 0x01)
+		return XZ_OPTIONS_ERROR;
+
+	/* Filter Properties contains LZMA2 dictionary size. */
+	if (s->temp.size - s->temp.pos < 1)
+		return XZ_DATA_ERROR;
+
+	ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
+	if (ret != XZ_OK)
+		return ret;
+
+	/* The rest must be Header Padding. */
+	while (s->temp.pos < s->temp.size)
+		if (s->temp.buf[s->temp.pos++] != 0x00)
+			return XZ_OPTIONS_ERROR;
+
+	s->temp.pos = 0;
+	s->block.compressed = 0;
+	s->block.uncompressed = 0;
+
+	return XZ_OK;
+}
+
+static enum xz_ret XZ_FUNC dec_main(struct xz_dec *s, struct xz_buf *b)
+{
+	enum xz_ret ret;
+
+	/*
+	 * Store the start position for the case when we are in the middle
+	 * of the Index field.
+	 */
+	s->in_start = b->in_pos;
+
+	while (true) {
+		switch (s->sequence) {
+		case SEQ_STREAM_HEADER:
+			/*
+			 * Stream Header is copied to s->temp, and then
+			 * decoded from there. This way if the caller
+			 * gives us only little input at a time, we can
+			 * still keep the Stream Header decoding code
+			 * simple. Similar approach is used in many places
+			 * in this file.
+			 */
+			if (!fill_temp(s, b))
+				return XZ_OK;
+
+			/*
+			 * If dec_stream_header() returns
+			 * XZ_UNSUPPORTED_CHECK, it is still possible
+			 * to continue decoding if working in multi-call
+			 * mode. Thus, update s->sequence before calling
+			 * dec_stream_header().
+			 */
+			s->sequence = SEQ_BLOCK_START;
+
+			ret = dec_stream_header(s);
+			if (ret != XZ_OK)
+				return ret;
+
+		case SEQ_BLOCK_START:
+			/* We need one byte of input to continue. */
+			if (b->in_pos == b->in_size)
+				return XZ_OK;
+
+			/* See if this is the beginning of the Index field. */
+			if (b->in[b->in_pos] == 0) {
+				s->in_start = b->in_pos++;
+				s->sequence = SEQ_INDEX;
+				break;
+			}
+
+			/*
+			 * Calculate the size of the Block Header and
+			 * prepare to decode it.
+			 */
+			s->block_header.size
+				= ((uint32_t)b->in[b->in_pos] + 1) * 4;
+
+			s->temp.size = s->block_header.size;
+			s->temp.pos = 0;
+			s->sequence = SEQ_BLOCK_HEADER;
+
+		case SEQ_BLOCK_HEADER:
+			if (!fill_temp(s, b))
+				return XZ_OK;
+
+			ret = dec_block_header(s);
+			if (ret != XZ_OK)
+				return ret;
+
+			s->sequence = SEQ_BLOCK_UNCOMPRESS;
+
+		case SEQ_BLOCK_UNCOMPRESS:
+			ret = dec_block(s, b);
+			if (ret != XZ_STREAM_END)
+				return ret;
+
+			s->sequence = SEQ_BLOCK_PADDING;
+
+		case SEQ_BLOCK_PADDING:
+			/*
+			 * Size of Compressed Data + Block Padding
+			 * must be a multiple of four. We don't need
+			 * s->block.compressed for anything else
+			 * anymore, so we use it here to test the size
+			 * of the Block Padding field.
+			 */
+			while (s->block.compressed & 3) {
+				if (b->in_pos == b->in_size)
+					return XZ_OK;
+
+				if (b->in[b->in_pos++] != 0)
+					return XZ_DATA_ERROR;
+
+				++s->block.compressed;
+			}
+
+			s->sequence = SEQ_BLOCK_CHECK;
+
+		case SEQ_BLOCK_CHECK:
+			if (s->check_type == XZ_CHECK_CRC32) {
+				ret = crc32_validate(s, b);
+				if (ret != XZ_STREAM_END)
+					return ret;
+			}
+#ifdef XZ_DEC_ANY_CHECK
+			else if (!check_skip(s, b)) {
+				return XZ_OK;
+			}
+#endif
+
+			s->sequence = SEQ_BLOCK_START;
+			break;
+
+		case SEQ_INDEX:
+			ret = dec_index(s, b);
+			if (ret != XZ_STREAM_END)
+				return ret;
+
+			s->sequence = SEQ_INDEX_PADDING;
+
+		case SEQ_INDEX_PADDING:
+			while ((s->index.size + (b->in_pos - s->in_start))
+					& 3) {
+				if (b->in_pos == b->in_size) {
+					index_update(s, b);
+					return XZ_OK;
+				}
+
+				if (b->in[b->in_pos++] != 0)
+					return XZ_DATA_ERROR;
+			}
+
+			/* Finish the CRC32 value and Index size. */
+			index_update(s, b);
+
+			/* Compare the hashes to validate the Index field. */
+			if (!memeq(&s->block.hash, &s->index.hash,
+					sizeof(s->block.hash)))
+				return XZ_DATA_ERROR;
+
+			s->sequence = SEQ_INDEX_CRC32;
+
+		case SEQ_INDEX_CRC32:
+			ret = crc32_validate(s, b);
+			if (ret != XZ_STREAM_END)
+				return ret;
+
+			s->temp.size = STREAM_HEADER_SIZE;
+			s->sequence = SEQ_STREAM_FOOTER;
+
+		case SEQ_STREAM_FOOTER:
+			if (!fill_temp(s, b))
+				return XZ_OK;
+
+			return dec_stream_footer(s);
+		}
+	}
+
+	/* Never reached */
+}
+
+/*
+ * xz_dec_run() is a wrapper for dec_main() to handle some special cases in
+ * multi-call and single-call decoding.
+ *
+ * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
+ * are not going to make any progress anymore. This is to prevent the caller
+ * from calling us infinitely when the input file is truncated or otherwise
+ * corrupt. Since zlib-style API allows that the caller fills the input buffer
+ * only when the decoder doesn't produce any new output, we have to be careful
+ * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
+ * after the second consecutive call to xz_dec_run() that makes no progress.
+ *
+ * In single-call mode, if we couldn't decode everything and no error
+ * occurred, either the input is truncated or the output buffer is too small.
+ * Since we know that the last input byte never produces any output, we know
+ * that if all the input was consumed and decoding wasn't finished, the file
+ * must be corrupt. Otherwise the output buffer has to be too small or the
+ * file is corrupt in a way that decoding it produces too big output.
+ *
+ * If single-call decoding fails, we reset b->in_pos and b->out_pos back to
+ * their original values. This is because with some filter chains there won't
+ * be any valid uncompressed data in the output buffer unless the decoding
+ * actually succeeds (that's the price to pay of using the output buffer as
+ * the workspace).
+ */
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+{
+	size_t in_start;
+	size_t out_start;
+	enum xz_ret ret;
+
+	if (DEC_IS_SINGLE(s->mode))
+		xz_dec_reset(s);
+
+	in_start = b->in_pos;
+	out_start = b->out_pos;
+	ret = dec_main(s, b);
+
+	if (DEC_IS_SINGLE(s->mode)) {
+		if (ret == XZ_OK)
+			ret = b->in_pos == b->in_size
+					? XZ_DATA_ERROR : XZ_BUF_ERROR;
+
+		if (ret != XZ_STREAM_END) {
+			b->in_pos = in_start;
+			b->out_pos = out_start;
+		}
+
+	} else if (ret == XZ_OK && in_start == b->in_pos
+			&& out_start == b->out_pos) {
+		if (s->allow_buf_error)
+			ret = XZ_BUF_ERROR;
+
+		s->allow_buf_error = true;
+	} else {
+		s->allow_buf_error = false;
+	}
+
+	return ret;
+}
+
+XZ_EXTERN struct xz_dec * XZ_FUNC xz_dec_init(
+		enum xz_mode mode, uint32_t dict_max)
+{
+	struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (s == NULL)
+		return NULL;
+
+	s->mode = mode;
+
+#ifdef XZ_DEC_BCJ
+	s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
+	if (s->bcj == NULL)
+		goto error_bcj;
+#endif
+
+	s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
+	if (s->lzma2 == NULL)
+		goto error_lzma2;
+
+	xz_dec_reset(s);
+	return s;
+
+error_lzma2:
+#ifdef XZ_DEC_BCJ
+	xz_dec_bcj_end(s->bcj);
+error_bcj:
+#endif
+	kfree(s);
+	return NULL;
+}
+
+XZ_EXTERN void XZ_FUNC xz_dec_reset(struct xz_dec *s)
+{
+	s->sequence = SEQ_STREAM_HEADER;
+	s->allow_buf_error = false;
+	s->pos = 0;
+	s->crc32 = 0;
+	memzero(&s->block, sizeof(s->block));
+	memzero(&s->index, sizeof(s->index));
+	s->temp.pos = 0;
+	s->temp.size = STREAM_HEADER_SIZE;
+}
+
+XZ_EXTERN void XZ_FUNC xz_dec_end(struct xz_dec *s)
+{
+	if (s != NULL) {
+		xz_dec_lzma2_end(s->lzma2);
+#ifdef XZ_DEC_BCJ
+		xz_dec_bcj_end(s->bcj);
+#endif
+		kfree(s);
+	}
+}
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz_lzma2.h b/busybox-1.19.3/archival/libarchive/unxz/xz_lzma2.h
new file mode 100644
index 0000000..47f21af
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz_lzma2.h
@@ -0,0 +1,204 @@
+/*
+ * LZMA2 definitions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_LZMA2_H
+#define XZ_LZMA2_H
+
+/* Range coder constants */
+#define RC_SHIFT_BITS 8
+#define RC_TOP_BITS 24
+#define RC_TOP_VALUE (1 << RC_TOP_BITS)
+#define RC_BIT_MODEL_TOTAL_BITS 11
+#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
+#define RC_MOVE_BITS 5
+
+/*
+ * Maximum number of position states. A position state is the lowest pb
+ * number of bits of the current uncompressed offset. In some places there
+ * are different sets of probabilities for different position states.
+ */
+#define POS_STATES_MAX (1 << 4)
+
+/*
+ * This enum is used to track which LZMA symbols have occurred most recently
+ * and in which order. This information is used to predict the next symbol.
+ *
+ * Symbols:
+ *  - Literal: One 8-bit byte
+ *  - Match: Repeat a chunk of data at some distance
+ *  - Long repeat: Multi-byte match at a recently seen distance
+ *  - Short repeat: One-byte repeat at a recently seen distance
+ *
+ * The symbol names are in from STATE_oldest_older_previous. REP means
+ * either short or long repeated match, and NONLIT means any non-literal.
+ */
+enum lzma_state {
+	STATE_LIT_LIT,
+	STATE_MATCH_LIT_LIT,
+	STATE_REP_LIT_LIT,
+	STATE_SHORTREP_LIT_LIT,
+	STATE_MATCH_LIT,
+	STATE_REP_LIT,
+	STATE_SHORTREP_LIT,
+	STATE_LIT_MATCH,
+	STATE_LIT_LONGREP,
+	STATE_LIT_SHORTREP,
+	STATE_NONLIT_MATCH,
+	STATE_NONLIT_REP
+};
+
+/* Total number of states */
+#define STATES 12
+
+/* The lowest 7 states indicate that the previous state was a literal. */
+#define LIT_STATES 7
+
+/* Indicate that the latest symbol was a literal. */
+static inline void XZ_FUNC lzma_state_literal(enum lzma_state *state)
+{
+	if (*state <= STATE_SHORTREP_LIT_LIT)
+		*state = STATE_LIT_LIT;
+	else if (*state <= STATE_LIT_SHORTREP)
+		*state -= 3;
+	else
+		*state -= 6;
+}
+
+/* Indicate that the latest symbol was a match. */
+static inline void XZ_FUNC lzma_state_match(enum lzma_state *state)
+{
+	*state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
+}
+
+/* Indicate that the latest state was a long repeated match. */
+static inline void XZ_FUNC lzma_state_long_rep(enum lzma_state *state)
+{
+	*state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
+}
+
+/* Indicate that the latest symbol was a short match. */
+static inline void XZ_FUNC lzma_state_short_rep(enum lzma_state *state)
+{
+	*state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
+}
+
+/* Test if the previous symbol was a literal. */
+static inline bool XZ_FUNC lzma_state_is_literal(enum lzma_state state)
+{
+	return state < LIT_STATES;
+}
+
+/* Each literal coder is divided in three sections:
+ *   - 0x001-0x0FF: Without match byte
+ *   - 0x101-0x1FF: With match byte; match bit is 0
+ *   - 0x201-0x2FF: With match byte; match bit is 1
+ *
+ * Match byte is used when the previous LZMA symbol was something else than
+ * a literal (that is, it was some kind of match).
+ */
+#define LITERAL_CODER_SIZE 0x300
+
+/* Maximum number of literal coders */
+#define LITERAL_CODERS_MAX (1 << 4)
+
+/* Minimum length of a match is two bytes. */
+#define MATCH_LEN_MIN 2
+
+/* Match length is encoded with 4, 5, or 10 bits.
+ *
+ * Length   Bits
+ *  2-9      4 = Choice=0 + 3 bits
+ * 10-17     5 = Choice=1 + Choice2=0 + 3 bits
+ * 18-273   10 = Choice=1 + Choice2=1 + 8 bits
+ */
+#define LEN_LOW_BITS 3
+#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
+#define LEN_MID_BITS 3
+#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
+#define LEN_HIGH_BITS 8
+#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
+#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
+
+/*
+ * Maximum length of a match is 273 which is a result of the encoding
+ * described above.
+ */
+#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
+
+/*
+ * Different sets of probabilities are used for match distances that have
+ * very short match length: Lengths of 2, 3, and 4 bytes have a separate
+ * set of probabilities for each length. The matches with longer length
+ * use a shared set of probabilities.
+ */
+#define DIST_STATES 4
+
+/*
+ * Get the index of the appropriate probability array for decoding
+ * the distance slot.
+ */
+static inline uint32_t XZ_FUNC lzma_get_dist_state(uint32_t len)
+{
+	return len < DIST_STATES + MATCH_LEN_MIN
+			? len - MATCH_LEN_MIN : DIST_STATES - 1;
+}
+
+/*
+ * The highest two bits of a 32-bit match distance are encoded using six bits.
+ * This six-bit value is called a distance slot. This way encoding a 32-bit
+ * value takes 6-36 bits, larger values taking more bits.
+ */
+#define DIST_SLOT_BITS 6
+#define DIST_SLOTS (1 << DIST_SLOT_BITS)
+
+/* Match distances up to 127 are fully encoded using probabilities. Since
+ * the highest two bits (distance slot) are always encoded using six bits,
+ * the distances 0-3 don't need any additional bits to encode, since the
+ * distance slot itself is the same as the actual distance. DIST_MODEL_START
+ * indicates the first distance slot where at least one additional bit is
+ * needed.
+ */
+#define DIST_MODEL_START 4
+
+/*
+ * Match distances greater than 127 are encoded in three pieces:
+ *   - distance slot: the highest two bits
+ *   - direct bits: 2-26 bits below the highest two bits
+ *   - alignment bits: four lowest bits
+ *
+ * Direct bits don't use any probabilities.
+ *
+ * The distance slot value of 14 is for distances 128-191.
+ */
+#define DIST_MODEL_END 14
+
+/* Distance slots that indicate a distance <= 127. */
+#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
+#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
+
+/*
+ * For match distances greater than 127, only the highest two bits and the
+ * lowest four bits (alignment) is encoded using probabilities.
+ */
+#define ALIGN_BITS 4
+#define ALIGN_SIZE (1 << ALIGN_BITS)
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+/* Total number of all probability variables */
+#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
+
+/*
+ * LZMA remembers the four most recent match distances. Reusing these
+ * distances tends to take less space than re-encoding the actual
+ * distance value.
+ */
+#define REPS 4
+
+#endif
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz_private.h b/busybox-1.19.3/archival/libarchive/unxz/xz_private.h
new file mode 100644
index 0000000..145649a
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz_private.h
@@ -0,0 +1,159 @@
+/*
+ * Private includes and definitions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_PRIVATE_H
+#define XZ_PRIVATE_H
+
+#ifdef __KERNEL__
+	/* XZ_PREBOOT may be defined only via decompress_unxz.c. */
+#	ifndef XZ_PREBOOT
+#		include <linux/slab.h>
+#		include <linux/vmalloc.h>
+#		include <linux/string.h>
+#		define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#		define memzero(buf, size) memset(buf, 0, size)
+#	endif
+#	include <asm/byteorder.h>
+#	include <asm/unaligned.h>
+#	define get_le32(p) le32_to_cpup((const uint32_t *)(p))
+	/* XZ_IGNORE_KCONFIG may be defined only via decompress_unxz.c. */
+#	ifndef XZ_IGNORE_KCONFIG
+#		ifdef CONFIG_XZ_DEC_X86
+#			define XZ_DEC_X86
+#		endif
+#		ifdef CONFIG_XZ_DEC_POWERPC
+#			define XZ_DEC_POWERPC
+#		endif
+#		ifdef CONFIG_XZ_DEC_IA64
+#			define XZ_DEC_IA64
+#		endif
+#		ifdef CONFIG_XZ_DEC_ARM
+#			define XZ_DEC_ARM
+#		endif
+#		ifdef CONFIG_XZ_DEC_ARMTHUMB
+#			define XZ_DEC_ARMTHUMB
+#		endif
+#		ifdef CONFIG_XZ_DEC_SPARC
+#			define XZ_DEC_SPARC
+#		endif
+#	endif
+#	include <linux/xz.h>
+#else
+	/*
+	 * For userspace builds, use a separate header to define the required
+	 * macros and functions. This makes it easier to adapt the code into
+	 * different environments and avoids clutter in the Linux kernel tree.
+	 */
+#	include "xz_config.h"
+#endif
+
+/* If no specific decoding mode is requested, enable support for all modes. */
+#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
+		&& !defined(XZ_DEC_DYNALLOC)
+#	define XZ_DEC_SINGLE
+#	define XZ_DEC_PREALLOC
+#	define XZ_DEC_DYNALLOC
+#endif
+
+/*
+ * The DEC_IS_foo(mode) macros are used in "if" statements. If only some
+ * of the supported modes are enabled, these macros will evaluate to true or
+ * false at compile time and thus allow the compiler to omit unneeded code.
+ */
+#ifdef XZ_DEC_SINGLE
+#	define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
+#else
+#	define DEC_IS_SINGLE(mode) (false)
+#endif
+
+#ifdef XZ_DEC_PREALLOC
+#	define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
+#else
+#	define DEC_IS_PREALLOC(mode) (false)
+#endif
+
+#ifdef XZ_DEC_DYNALLOC
+#	define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
+#else
+#	define DEC_IS_DYNALLOC(mode) (false)
+#endif
+
+#if !defined(XZ_DEC_SINGLE)
+#	define DEC_IS_MULTI(mode) (true)
+#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
+#	define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
+#else
+#	define DEC_IS_MULTI(mode) (false)
+#endif
+
+/*
+ * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
+ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
+ */
+#ifndef XZ_DEC_BCJ
+#	if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
+			|| defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
+			|| defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
+			|| defined(XZ_DEC_SPARC)
+#		define XZ_DEC_BCJ
+#	endif
+#endif
+
+/*
+ * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
+ * before calling xz_dec_lzma2_run().
+ */
+XZ_EXTERN struct xz_dec_lzma2 * XZ_FUNC xz_dec_lzma2_create(
+		enum xz_mode mode, uint32_t dict_max);
+
+/*
+ * Decode the LZMA2 properties (one byte) and reset the decoder. Return
+ * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
+ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this
+ * decoder doesn't support.
+ */
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_reset(
+		struct xz_dec_lzma2 *s, uint8_t props);
+
+/* Decode raw LZMA2 stream from b->in to b->out. */
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_lzma2_run(
+		struct xz_dec_lzma2 *s, struct xz_buf *b);
+
+/* Free the memory allocated for the LZMA2 decoder. */
+XZ_EXTERN void XZ_FUNC xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
+
+#ifdef XZ_DEC_BCJ
+/*
+ * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
+ * calling xz_dec_bcj_run().
+ */
+XZ_EXTERN struct xz_dec_bcj * XZ_FUNC xz_dec_bcj_create(bool single_call);
+
+/*
+ * Decode the Filter ID of a BCJ filter. This implementation doesn't
+ * support custom start offsets, so no decoding of Filter Properties
+ * is needed. Returns XZ_OK if the given Filter ID is supported.
+ * Otherwise XZ_OPTIONS_ERROR is returned.
+ */
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_reset(
+		struct xz_dec_bcj *s, uint8_t id);
+
+/*
+ * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
+ * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
+ * must be called directly.
+ */
+XZ_EXTERN enum xz_ret XZ_FUNC xz_dec_bcj_run(struct xz_dec_bcj *s,
+		struct xz_dec_lzma2 *lzma2, struct xz_buf *b);
+
+/* Free the memory allocated for the BCJ filters. */
+#define xz_dec_bcj_end(s) kfree(s)
+#endif
+
+#endif
diff --git a/busybox-1.19.3/archival/libarchive/unxz/xz_stream.h b/busybox-1.19.3/archival/libarchive/unxz/xz_stream.h
new file mode 100644
index 0000000..36f2a7c
--- /dev/null
+++ b/busybox-1.19.3/archival/libarchive/unxz/xz_stream.h
@@ -0,0 +1,57 @@
+/*
+ * Definitions for handling the .xz file format
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_STREAM_H
+#define XZ_STREAM_H
+
+#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32
+#	include <linux/crc32.h>
+#	undef crc32
+#	define xz_crc32(buf, size, crc) \
+		(~crc32_le(~(uint32_t)(crc), buf, size))
+#endif
+
+/*
+ * See the .xz file format specification at
+ * http://tukaani.org/xz/xz-file-format.txt
+ * to understand the container format.
+ */
+
+#define STREAM_HEADER_SIZE 12
+
+#define HEADER_MAGIC "\3757zXZ\0"
+#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.
+ */
+typedef uint64_t vli_type;
+
+#define VLI_MAX ((vli_type)-1 / 2)
+#define VLI_UNKNOWN ((vli_type)-1)
+
+/* Maximum encoded size of a VLI */
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+/* Integrity Check types */
+enum xz_check {
+	XZ_CHECK_NONE = 0,
+	XZ_CHECK_CRC32 = 1,
+	XZ_CHECK_CRC64 = 4,
+	XZ_CHECK_SHA256 = 10
+};
+
+/* Maximum possible Check ID */
+#define XZ_CHECK_MAX 15
+
+#endif
diff --git a/busybox-1.19.3/archival/lzop.c b/busybox-1.19.3/archival/lzop.c
new file mode 100644
index 0000000..1326bd7
--- /dev/null
+++ b/busybox-1.19.3/archival/lzop.c
@@ -0,0 +1,1100 @@
+/*
+   This file is part of the lzop file compressor.
+
+   Copyright (C) 1996..2003 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   Markus F.X.J. Oberhumer <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzop/
+
+   lzop and the LZO library are free software; you can redistribute them
+   and/or modify them 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 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; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   "Minimalized" for busybox by Alain Knaff
+*/
+
+//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 "archive.h"
+#include "liblzo_interface.h"
+
+/* lzo-2.03/src/lzo_ptr.h */
+#define pd(a,b)	 ((unsigned)((a)-(b)))
+
+#define lzo_version()			LZO_VERSION
+#define lzo_sizeof_dict_t		(sizeof(uint8_t*))
+
+/* lzo-2.03/include/lzo/lzo1x.h */
+#define LZO1X_1_MEM_COMPRESS	(16384 * lzo_sizeof_dict_t)
+#define LZO1X_1_15_MEM_COMPRESS (32768 * lzo_sizeof_dict_t)
+#define LZO1X_999_MEM_COMPRESS	(14 * 16384 * sizeof(short))
+
+/* lzo-2.03/src/lzo1x_oo.c */
+#define NO_LIT UINT_MAX
+
+/**********************************************************************/
+static void copy2(uint8_t* ip, const uint8_t* m_pos, unsigned off)
+{
+	ip[0] = m_pos[0];
+	if (off == 1)
+		ip[1] = m_pos[0];
+	else
+		ip[1] = m_pos[1];
+}
+
+static void copy3(uint8_t* ip, const uint8_t* m_pos, unsigned off)
+{
+	ip[0] = m_pos[0];
+	if (off == 1) {
+		ip[2] = ip[1] = m_pos[0];
+	}
+	else if (off == 2) {
+		ip[1] = m_pos[1];
+		ip[2] = m_pos[0];
+	}
+	else {
+		ip[1] = m_pos[1];
+		ip[2] = m_pos[2];
+	}
+}
+
+/**********************************************************************/
+// optimize a block of data.
+/**********************************************************************/
+#define TEST_IP		(ip < ip_end)
+#define TEST_OP		(op <= op_end)
+
+static NOINLINE int lzo1x_optimize(uint8_t *in, unsigned in_len,
+		uint8_t *out, unsigned *out_len,
+		void* wrkmem UNUSED_PARAM)
+{
+	uint8_t* op;
+	uint8_t* ip;
+	unsigned t;
+	uint8_t* m_pos;
+	uint8_t* const ip_end = in + in_len;
+	uint8_t* const op_end = out + *out_len;
+	uint8_t* litp = NULL;
+	unsigned lit = 0;
+	unsigned next_lit = NO_LIT;
+	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);
+
+	*out_len = 0;
+
+	op = out;
+	ip = in;
+
+	if (*ip > 17) {
+		t = *ip++ - 17;
+		if (t < 4)
+			goto match_next;
+		goto first_literal_run;
+	}
+
+	while (TEST_IP && TEST_OP) {
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+		/* a literal run */
+		litp = ip - 1;
+		if (t == 0) {
+			t = 15;
+			while (*ip == 0)
+				t += 255, ip++;
+			t += *ip++;
+		}
+		lit = t + 3;
+		/* copy literals */
+ copy_literal_run:
+		*op++ = *ip++;
+		*op++ = *ip++;
+		*op++ = *ip++;
+ first_literal_run:
+		do *op++ = *ip++; while (--t > 0);
+
+		t = *ip++;
+
+		if (t >= 16)
+			goto match;
+#if defined(LZO1X)
+		m_pos = op - 1 - 0x800;
+#elif defined(LZO1Y)
+		m_pos = op - 1 - 0x400;
+#endif
+		m_pos -= t >> 2;
+		m_pos -= *ip++ << 2;
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		lit = 0;
+		goto match_done;
+
+
+		/* handle matches */
+		do {
+			if (t < 16) { /* a M1 match */
+				m_pos = op - 1;
+				m_pos -= t >> 2;
+				m_pos -= *ip++ << 2;
+
+				if (litp == NULL)
+					goto copy_m1;
+
+				nl = ip[-2] & 3;
+				/* test if a match follows */
+				if (nl == 0 && lit == 1 && ip[0] >= 16) {
+					next_lit = nl;
+					/* adjust length of previous short run */
+					lit += 2;
+					*litp = (unsigned char)((*litp & ~3) | lit);
+					/* copy over the 2 literals that replace the match */
+					copy2(ip-2, m_pos, pd(op, m_pos));
+					o_m1_a++;
+				}
+				/* test if a literal run follows */
+				else
+				if (nl == 0
+				 && ip[0] < 16
+				 && ip[0] != 0
+				 && (lit + 2 + ip[0] < 16)
+				) {
+					t = *ip++;
+					/* remove short run */
+					*litp &= ~3;
+					/* copy over the 2 literals that replace the match */
+					copy2(ip-3+1,m_pos,pd(op,m_pos));
+					/* move literals 1 byte ahead */
+					litp += 2;
+					if (lit > 0)
+						memmove(litp+1, litp, lit);
+					/* insert new length of long literal run */
+					lit += 2 + t + 3;
+					*litp = (unsigned char)(lit - 3);
+
+					o_m1_b++;
+					*op++ = *m_pos++; *op++ = *m_pos++;
+					goto copy_literal_run;
+				}
+ copy_m1:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+			} else {
+ match:
+				if (t >= 64) {				/* a M2 match */
+					m_pos = op - 1;
+#if defined(LZO1X)
+					m_pos -= (t >> 2) & 7;
+					m_pos -= *ip++ << 3;
+					t = (t >> 5) - 1;
+#elif defined(LZO1Y)
+					m_pos -= (t >> 2) & 3;
+					m_pos -= *ip++ << 2;
+					t = (t >> 4) - 3;
+#endif
+					if (litp == NULL)
+						goto copy_m;
+
+					nl = ip[-2] & 3;
+					/* test if in beetween two long literal runs */
+					if (t == 1 && lit > 3 && nl == 0
+					 && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16)
+					) {
+						t = *ip++;
+						/* copy over the 3 literals that replace the match */
+						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);
+						o_m2++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						goto copy_literal_run;
+					}
+				} else {
+					if (t >= 32) {			/* a M3 match */
+						t &= 31;
+						if (t == 0) {
+							t = 31;
+							while (*ip == 0)
+								t += 255, ip++;
+							t += *ip++;
+						}
+						m_pos = op - 1;
+						m_pos -= *ip++ >> 2;
+						m_pos -= *ip++ << 6;
+					} else {					/* a M4 match */
+						m_pos = op;
+						m_pos -= (t & 8) << 11;
+						t &= 7;
+						if (t == 0) {
+							t = 7;
+							while (*ip == 0)
+								t += 255, ip++;
+							t += *ip++;
+						}
+						m_pos -= *ip++ >> 2;
+						m_pos -= *ip++ << 6;
+						if (m_pos == op)
+							goto eof_found;
+						m_pos -= 0x4000;
+					}
+					if (litp == NULL)
+						goto copy_m;
+
+					nl = ip[-2] & 3;
+					/* test if in beetween two matches */
+					if (t == 1 && lit == 0 && nl == 0 && ip[0] >= 16) {
+						next_lit = nl;
+						/* make a previous short run */
+						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));
+						o_m3_a++;
+					}
+					/* test if a literal run follows */
+					else if (t == 1 && lit <= 3 && nl == 0
+					 && ip[0] < 16 && ip[0] != 0 && (lit + 3 + ip[0] < 16)
+					) {
+						t = *ip++;
+						/* remove short run */
+						*litp &= ~3;
+						/* copy over the 3 literals that replace the match */
+						copy3(ip-4+1,m_pos,pd(op,m_pos));
+						/* move literals 1 byte ahead */
+						litp += 2;
+						if (lit > 0)
+							memmove(litp+1,litp,lit);
+						/* insert new length of long literal run */
+						lit += 3 + t + 3;
+						*litp = (unsigned char)(lit - 3);
+
+						o_m3_b++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						goto copy_literal_run;
+					}
+				}
+ copy_m:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+				do *op++ = *m_pos++; while (--t > 0);
+			}
+
+ match_done:
+			if (next_lit == NO_LIT) {
+				t = ip[-2] & 3;
+				lit = t;
+				litp = ip - 2;
+			}
+			else
+				t = next_lit;
+			next_lit = NO_LIT;
+			if (t == 0)
+				break;
+			/* copy literals */
+ match_next:
+			do *op++ = *ip++; while (--t > 0);
+			t = *ip++;
+		} while (TEST_IP && TEST_OP);
+	}
+
+	/* no EOF code was found */
+	*out_len = pd(op, out);
+	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);
+	*out_len = pd(op, out);
+	return (ip == ip_end ? LZO_E_OK :
+		   (ip < ip_end	 ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+}
+
+/**********************************************************************/
+#define F_OS F_OS_UNIX
+#define F_CS F_CS_NATIVE
+
+/**********************************************************************/
+#define ADLER32_INIT_VALUE 1
+#define CRC32_INIT_VALUE   0
+
+/**********************************************************************/
+enum {
+	M_LZO1X_1    = 1,
+	M_LZO1X_1_15 = 2,
+	M_LZO1X_999  = 3,
+};
+
+/**********************************************************************/
+/* header flags */
+#define F_ADLER32_D     0x00000001L
+#define F_ADLER32_C     0x00000002L
+#define F_H_EXTRA_FIELD 0x00000040L
+#define F_H_GMTDIFF     0x00000080L
+#define F_CRC32_D       0x00000100L
+#define F_CRC32_C       0x00000200L
+#define F_H_FILTER      0x00000800L
+#define F_H_CRC32       0x00001000L
+#define F_MASK          0x00003FFFL
+
+/* operating system & file system that created the file [mostly unused] */
+#define F_OS_UNIX       0x03000000L
+#define F_OS_SHIFT      24
+#define F_OS_MASK       0xff000000L
+
+/* character set for file name encoding [mostly unused] */
+#define F_CS_NATIVE     0x00000000L
+#define F_CS_SHIFT      20
+#define F_CS_MASK       0x00f00000L
+
+/* these bits must be zero */
+#define F_RESERVED      ((F_MASK | F_OS_MASK | F_CS_MASK) ^ 0xffffffffL)
+
+typedef struct chksum_t {
+	uint32_t f_adler32;
+	uint32_t f_crc32;
+} chksum_t;
+
+typedef struct header_t {
+	unsigned version;
+	unsigned lib_version;
+	unsigned version_needed_to_extract;
+	uint32_t flags;
+	uint32_t mode;
+	uint32_t mtime;
+	uint32_t gmtdiff;
+	uint32_t header_checksum;
+
+	uint32_t extra_field_len;
+	uint32_t extra_field_checksum;
+
+	unsigned char method;
+	unsigned char level;
+
+	/* info */
+	char name[255+1];
+} header_t;
+
+struct globals {
+	/*const uint32_t *lzo_crc32_table;*/
+	chksum_t chksum_in;
+	chksum_t chksum_out;
+} 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)
+
+
+/**********************************************************************/
+#define LZOP_VERSION            0x1010
+//#define LZOP_VERSION_STRING     "1.01"
+//#define LZOP_VERSION_DATE       "Apr 27th 2003"
+
+#define OPTION_STRING "cfvdt123456789CF"
+
+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),
+};
+
+/**********************************************************************/
+// adler32 checksum
+// adapted from free code by Mark Adler <madler@alumni.caltech.edu>
+// see http://www.zlib.org/
+/**********************************************************************/
+static FAST_FUNC uint32_t
+lzo_adler32(uint32_t adler, const uint8_t* buf, unsigned len)
+{
+	enum {
+		LZO_BASE = 65521, /* largest prime smaller than 65536 */
+		/* NMAX is the largest n such that
+		 * 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+		LZO_NMAX = 5552,
+	};
+	uint32_t s1 = adler & 0xffff;
+	uint32_t s2 = (adler >> 16) & 0xffff;
+	unsigned k;
+
+	if (buf == NULL)
+		return 1;
+
+	while (len > 0) {
+		k = len < LZO_NMAX ? (unsigned) len : LZO_NMAX;
+		len -= k;
+		if (k != 0) do {
+			s1 += *buf++;
+			s2 += s1;
+		} while (--k > 0);
+		s1 %= LZO_BASE;
+		s2 %= LZO_BASE;
+	}
+	return (s2 << 16) | s1;
+}
+
+static FAST_FUNC uint32_t
+lzo_crc32(uint32_t c, const uint8_t* buf, unsigned len)
+{
+	//if (buf == NULL) - impossible
+	//	return 0;
+
+	return ~crc32_block_endian0(~c, buf, len, global_crc32_table);
+}
+
+/**********************************************************************/
+static void init_chksum(chksum_t *ct)
+{
+	ct->f_adler32 = ADLER32_INIT_VALUE;
+	ct->f_crc32 = CRC32_INIT_VALUE;
+}
+
+static void add_bytes_to_chksum(chksum_t *ct, const void* buf, int cnt)
+{
+	/* We need to handle the two checksums at once, because at the
+	 * beginning of the header, we don't know yet which one we'll
+	 * eventually need */
+	ct->f_adler32 = lzo_adler32(ct->f_adler32, (const uint8_t*)buf, cnt);
+	ct->f_crc32 = lzo_crc32(ct->f_crc32, (const uint8_t*)buf, cnt);
+}
+
+static uint32_t chksum_getresult(chksum_t *ct, const header_t *h)
+{
+	return (h->flags & F_H_CRC32) ? ct->f_crc32 : ct->f_adler32;
+}
+
+/**********************************************************************/
+static uint32_t read32(void)
+{
+	uint32_t v;
+	xread(0, &v, 4);
+	return ntohl(v);
+}
+
+static void write32(uint32_t v)
+{
+	v = htonl(v);
+	xwrite(1, &v, 4);
+}
+
+static void f_write(const void* buf, int cnt)
+{
+	xwrite(1, buf, cnt);
+	add_bytes_to_chksum(&G.chksum_out, buf, cnt);
+}
+
+static void f_read(void* buf, int cnt)
+{
+	xread(0, buf, cnt);
+	add_bytes_to_chksum(&G.chksum_in, buf, cnt);
+}
+
+static int f_read8(void)
+{
+	uint8_t v;
+	f_read(&v, 1);
+	return v;
+}
+
+static void f_write8(uint8_t v)
+{
+	f_write(&v, 1);
+}
+
+static unsigned f_read16(void)
+{
+	uint16_t v;
+	f_read(&v, 2);
+	return ntohs(v);
+}
+
+static void f_write16(uint16_t v)
+{
+	v = htons(v);
+	f_write(&v, 2);
+}
+
+static uint32_t f_read32(void)
+{
+	uint32_t v;
+	f_read(&v, 4);
+	return ntohl(v);
+}
+
+static void f_write32(uint32_t v)
+{
+	v = htonl(v);
+	f_write(&v, 4);
+}
+
+/**********************************************************************/
+static int lzo_get_method(header_t *h)
+{
+	/* check method */
+	if (h->method == M_LZO1X_1) {
+		if (h->level == 0)
+			h->level = 3;
+	} else if (h->method == M_LZO1X_1_15) {
+		if (h->level == 0)
+			h->level = 1;
+	} else if (h->method == M_LZO1X_999) {
+		if (h->level == 0)
+			h->level = 9;
+	} else
+		return -1;		/* not a LZO method */
+
+	/* check compression level */
+	if (h->level < 1 || h->level > 9)
+		return 15;
+
+	return 0;
+}
+
+/**********************************************************************/
+#define LZO_BLOCK_SIZE	(256 * 1024l)
+#define MAX_BLOCK_SIZE	(64 * 1024l * 1024l)	/* DO NOT CHANGE */
+
+/* LZO may expand uncompressible data by a small amount */
+#define MAX_COMPRESSED_SIZE(x)	((x) + (x) / 16 + 64 + 3)
+
+/**********************************************************************/
+// compress a file
+/**********************************************************************/
+static NOINLINE smallint lzo_compress(const header_t *h)
+{
+	unsigned block_size = LZO_BLOCK_SIZE;
+	int r = 0; /* LZO_E_OK */
+	uint8_t *const b1 = xzalloc(block_size);
+	uint8_t *const b2 = xzalloc(MAX_COMPRESSED_SIZE(block_size));
+	unsigned src_len = 0, dst_len = 0;
+	uint32_t d_adler32 = ADLER32_INIT_VALUE;
+	uint32_t d_crc32 = CRC32_INIT_VALUE;
+	int l;
+	smallint ok = 1;
+	uint8_t *wrk_mem = NULL;
+
+	if (h->method == M_LZO1X_1)
+		wrk_mem = xzalloc(LZO1X_1_MEM_COMPRESS);
+	else if (h->method == M_LZO1X_1_15)
+		wrk_mem = xzalloc(LZO1X_1_15_MEM_COMPRESS);
+	else if (h->method == M_LZO1X_999)
+		wrk_mem = xzalloc(LZO1X_999_MEM_COMPRESS);
+
+	for (;;) {
+		/* read a block */
+		l = full_read(0, b1, block_size);
+		src_len = (l > 0 ? l : 0);
+
+		/* write uncompressed block size */
+		write32(src_len);
+
+		/* exit if last block */
+		if (src_len == 0)
+			break;
+
+		/* compute checksum of uncompressed block */
+		if (h->flags & F_ADLER32_D)
+			d_adler32 = lzo_adler32(ADLER32_INIT_VALUE, b1, src_len);
+		if (h->flags & F_CRC32_D)
+			d_crc32 = lzo_crc32(CRC32_INIT_VALUE, b1, src_len);
+
+		/* compress */
+		if (h->method == M_LZO1X_1)
+			r = lzo1x_1_compress(b1, src_len, b2, &dst_len, wrk_mem);
+		else if (h->method == M_LZO1X_1_15)
+			r = lzo1x_1_15_compress(b1, src_len, b2, &dst_len, wrk_mem);
+#if ENABLE_LZOP_COMPR_HIGH
+		else if (h->method == M_LZO1X_999)
+			r = lzo1x_999_compress_level(b1, src_len, b2, &dst_len,
+						wrk_mem, h->level);
+#endif
+		else
+			bb_error_msg_and_die("internal error");
+
+		if (r != 0) /* not LZO_E_OK */
+			bb_error_msg_and_die("internal error - compression failed");
+
+		/* write compressed block size */
+		if (dst_len < src_len) {
+			/* optimize */
+			if (h->method == M_LZO1X_999) {
+				unsigned new_len = src_len;
+				r = lzo1x_optimize(b2, dst_len, b1, &new_len, NULL);
+				if (r != 0 /*LZO_E_OK*/ || new_len != src_len)
+					bb_error_msg_and_die("internal error - optimization failed");
+			}
+			write32(dst_len);
+		} else {
+			/* data actually expanded => store data uncompressed */
+			write32(src_len);
+		}
+
+		/* write checksum of uncompressed block */
+		if (h->flags & F_ADLER32_D)
+			write32(d_adler32);
+		if (h->flags & F_CRC32_D)
+			write32(d_crc32);
+
+		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));
+			if (h->flags & F_CRC32_C)
+				write32(lzo_crc32(CRC32_INIT_VALUE, b2, dst_len));
+			/* write compressed block data */
+			xwrite(1, b2, dst_len);
+		} else {
+			/* write uncompressed block data */
+			xwrite(1, b1, src_len);
+		}
+	}
+
+	free(wrk_mem);
+	free(b1);
+	free(b2);
+	return ok;
+}
+
+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");
+}
+
+/**********************************************************************/
+// decompress a file
+/**********************************************************************/
+static NOINLINE smallint lzo_decompress(const header_t *h)
+{
+	unsigned block_size = LZO_BLOCK_SIZE;
+	int r;
+	uint32_t src_len, dst_len;
+	uint32_t c_adler32 = ADLER32_INIT_VALUE;
+	uint32_t d_adler32 = ADLER32_INIT_VALUE;
+	uint32_t c_crc32 = CRC32_INIT_VALUE, d_crc32 = CRC32_INIT_VALUE;
+	smallint ok = 1;
+	uint8_t *b1;
+	uint32_t mcs_block_size = MAX_COMPRESSED_SIZE(block_size);
+	uint8_t *b2 = NULL;
+
+	for (;;) {
+		uint8_t *dst;
+
+		/* read uncompressed block size */
+		dst_len = read32();
+
+		/* exit if last block */
+		if (dst_len == 0)
+			break;
+
+		/* error if split file */
+		if (dst_len == 0xffffffffL)
+			/* should not happen - not yet implemented */
+			bb_error_msg_and_die("this file is a split lzop file");
+
+		if (dst_len > MAX_BLOCK_SIZE)
+			bb_error_msg_and_die("corrupted data");
+
+		/* read compressed block size */
+		src_len = read32();
+		if (src_len <= 0 || src_len > dst_len)
+			bb_error_msg_and_die("corrupted data");
+
+		if (dst_len > block_size) {
+			if (b2) {
+				free(b2);
+				b2 = NULL;
+			}
+			block_size = dst_len;
+			mcs_block_size = MAX_COMPRESSED_SIZE(block_size);
+		}
+
+		/* read checksum of uncompressed block */
+		if (h->flags & F_ADLER32_D)
+			d_adler32 = read32();
+		if (h->flags & F_CRC32_D)
+			d_crc32 = read32();
+
+		/* read checksum of compressed block */
+		if (src_len < dst_len) {
+			if (h->flags & F_ADLER32_C)
+				c_adler32 = read32();
+			if (h->flags & F_CRC32_C)
+				c_crc32 = read32();
+		}
+
+		if (b2 == NULL)
+			b2 = xzalloc(mcs_block_size);
+		/* read the block into the end of our buffer */
+		b1 = b2 + mcs_block_size - src_len;
+		xread(0, b1, src_len);
+
+		if (src_len < dst_len) {
+			unsigned d = dst_len;
+
+			if (!(option_mask32 & OPT_F)) {
+				/* verify checksum of compressed block */
+				if (h->flags & F_ADLER32_C)
+					lzo_check(ADLER32_INIT_VALUE,
+							b1, src_len,
+							lzo_adler32, c_adler32);
+				if (h->flags & F_CRC32_C)
+					lzo_check(CRC32_INIT_VALUE,
+							b1, src_len,
+							lzo_crc32, c_crc32);
+			}
+
+			/* decompress */
+//			if (option_mask32 & OPT_F)
+//				r = lzo1x_decompress(b1, src_len, b2, &d, NULL);
+//			else
+				r = lzo1x_decompress_safe(b1, src_len, b2, &d, NULL);
+
+			if (r != 0 /*LZO_E_OK*/ || dst_len != d) {
+				bb_error_msg_and_die("corrupted data");
+			}
+			dst = b2;
+		} else {
+			/* "stored" block => no decompression */
+			dst = b1;
+		}
+
+		if (!(option_mask32 & OPT_F)) {
+			/* verify checksum of uncompressed block */
+			if (h->flags & F_ADLER32_D)
+				lzo_check(ADLER32_INIT_VALUE,
+					dst, dst_len,
+					lzo_adler32, d_adler32);
+			if (h->flags & F_CRC32_D)
+				lzo_check(CRC32_INIT_VALUE,
+					dst, dst_len,
+					lzo_crc32, d_crc32);
+		}
+
+		/* write uncompressed block data */
+		xwrite(1, dst, dst_len);
+	}
+
+	free(b2);
+	return ok;
+}
+
+/**********************************************************************/
+// lzop file signature (shamelessly borrowed from PNG)
+/**********************************************************************/
+/*
+ * The first nine bytes of a lzop file always contain the following values:
+ *
+ *                                 0   1   2   3   4   5   6   7   8
+ *                               --- --- --- --- --- --- --- --- ---
+ * (hex)                          89  4c  5a  4f  00  0d  0a  1a  0a
+ * (decimal)                     137  76  90  79   0  13  10  26  10
+ * (C notation - ASCII)         \211   L   Z   O  \0  \r  \n \032 \n
+ */
+
+/* (vda) comparison with lzop v1.02rc1 ("lzop -1 <FILE" cmd):
+ * Only slight differences in header:
+ * -00000000  89 4c 5a 4f 00 0d 0a 1a 0a 10 20 20 20 09 40 02
+ * +00000000  89 4c 5a 4f 00 0d 0a 1a 0a 10 10 20 30 09 40 02
+ *                                       ^^^^^ ^^^^^
+ *                                     version lib_version
+ * -00000010  01 03 00 00 0d 00 00 81 a4 49 f7 a6 3f 00 00 00
+ * +00000010  01 03 00 00 01 00 00 00 00 00 00 00 00 00 00 00
+ *               ^^^^^^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^
+ *               flags       mode        mtime
+ * -00000020  00 00 2d 67 04 17 00 04 00 00 00 03 ed ec 9d 6d
+ * +00000020  00 00 10 5f 00 c1 00 04 00 00 00 03 ed ec 9d 6d
+ *                  ^^^^^^^^^^^
+ *                  chksum_out
+ * The rest is identical.
+*/
+static const unsigned char lzop_magic[9] = {
+	0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a
+};
+
+/* This coding is derived from Alexander Lehmann's pngcheck code. */
+static void check_magic(void)
+{
+	unsigned char magic[sizeof(lzop_magic)];
+	xread(0, magic, sizeof(magic));
+	if (memcmp(magic, lzop_magic, sizeof(lzop_magic)) != 0)
+		bb_error_msg_and_die("bad magic number");
+}
+
+/**********************************************************************/
+// lzop file header
+/**********************************************************************/
+static void write_header(const header_t *h)
+{
+	int l;
+
+	xwrite(1, lzop_magic, sizeof(lzop_magic));
+
+	init_chksum(&G.chksum_out);
+
+	f_write16(h->version);
+	f_write16(h->lib_version);
+	f_write16(h->version_needed_to_extract);
+	f_write8(h->method);
+	f_write8(h->level);
+	f_write32(h->flags);
+	f_write32(h->mode);
+	f_write32(h->mtime);
+	f_write32(h->gmtdiff);
+
+	l = (int) strlen(h->name);
+	f_write8(l);
+	if (l)
+		f_write(h->name, l);
+
+	f_write32(chksum_getresult(&G.chksum_out, h));
+}
+
+static int read_header(header_t *h)
+{
+	int r;
+	int l;
+	uint32_t checksum;
+
+	memset(h, 0, sizeof(*h));
+	h->version_needed_to_extract = 0x0900;	/* first lzop version */
+	h->level = 0;
+
+	init_chksum(&G.chksum_in);
+
+	h->version = f_read16();
+	if (h->version < 0x0900)
+		return 3;
+	h->lib_version = f_read16();
+	if (h->version >= 0x0940) {
+		h->version_needed_to_extract = f_read16();
+		if (h->version_needed_to_extract > LZOP_VERSION)
+			return 16;
+		if (h->version_needed_to_extract < 0x0900)
+			return 3;
+	}
+	h->method = f_read8();
+	if (h->version >= 0x0940)
+		h->level = f_read8();
+	h->flags = f_read32();
+	if (h->flags & F_H_FILTER)
+		return 16; /* filter not supported */
+	h->mode = f_read32();
+	h->mtime = f_read32();
+	if (h->version >= 0x0940)
+		h->gmtdiff = f_read32();
+
+	l = f_read8();
+	if (l > 0)
+		f_read(h->name, l);
+	h->name[l] = 0;
+
+	checksum = chksum_getresult(&G.chksum_in, h);
+	h->header_checksum = f_read32();
+	if (h->header_checksum != checksum)
+		return 2;
+
+	if (h->method <= 0)
+		return 14;
+	r = lzo_get_method(h);
+	if (r != 0)
+		return r;
+
+	/* check reserved flags */
+	if (h->flags & F_RESERVED)
+		return -13;
+
+	/* skip extra field [not used yet] */
+	if (h->flags & F_H_EXTRA_FIELD) {
+		uint32_t k;
+
+		/* note: the checksum also covers the length */
+		init_chksum(&G.chksum_in);
+		h->extra_field_len = f_read32();
+		for (k = 0; k < h->extra_field_len; k++)
+			f_read8();
+		checksum = chksum_getresult(&G.chksum_in, h);
+		h->extra_field_checksum = f_read32();
+		if (h->extra_field_checksum != checksum)
+			return 3;
+	}
+
+	return 0;
+}
+
+static void p_header(header_t *h)
+{
+	int r;
+
+	r = read_header(h);
+	if (r == 0)
+		return;
+	bb_error_msg_and_die("header_error %d", r);
+}
+
+/**********************************************************************/
+// compress
+/**********************************************************************/
+static void lzo_set_method(header_t *h)
+{
+	int level = 1;
+
+	if (option_mask32 & OPT_1) {
+		h->method = M_LZO1X_1_15;
+	} else if (option_mask32 & OPT_789) {
+#if ENABLE_LZOP_COMPR_HIGH
+		h->method = M_LZO1X_999;
+		if (option_mask32 & OPT_7)
+			level = 7;
+		else if (option_mask32 & OPT_8)
+			level = 8;
+		else
+			level = 9;
+#else
+		bb_error_msg_and_die("high compression not compiled in");
+#endif
+	} else { /* levels 2..6 or none (defaults to level 3) */
+		h->method = M_LZO1X_1;
+		level = 5; /* levels 2-6 are actually the same */
+	}
+
+	h->level = level;
+}
+
+static smallint do_lzo_compress(void)
+{
+	header_t header;
+
+#define h (&header)
+	memset(h, 0, sizeof(*h));
+
+	lzo_set_method(h);
+
+	h->version = (LZOP_VERSION & 0xffff);
+	h->version_needed_to_extract = 0x0940;
+	h->lib_version = lzo_version() & 0xffff;
+
+	h->flags = (F_OS & F_OS_MASK) | (F_CS & F_CS_MASK);
+
+	if (!(option_mask32 & OPT_F) || h->method == M_LZO1X_999) {
+		h->flags |= F_ADLER32_D;
+		if (option_mask32 & OPT_C)
+			h->flags |= F_ADLER32_C;
+	}
+	write_header(h);
+	return lzo_compress(h);
+#undef h
+}
+
+/**********************************************************************/
+// decompress
+/**********************************************************************/
+static smallint do_lzo_decompress(void)
+{
+	header_t header;
+
+	check_magic();
+	p_header(&header);
+	return lzo_decompress(&header);
+}
+
+static char* FAST_FUNC make_new_name_lzop(char *filename, const char *expected_ext UNUSED_PARAM)
+{
+	if (option_mask32 & OPT_DECOMPRESS) {
+		char *extension = strrchr(filename, '.');
+		if (!extension || strcmp(extension + 1, "lzo") != 0)
+			return xasprintf("%s.out", filename);
+		*extension = '\0';
+		return filename;
+	}
+	return xasprintf("%s.lzo", filename);
+}
+
+static IF_DESKTOP(long long) int FAST_FUNC pack_lzop(unpack_info_t *info UNUSED_PARAM)
+{
+	if (option_mask32 & OPT_DECOMPRESS)
+		return do_lzo_decompress();
+	return do_lzo_compress();
+}
+
+int lzop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lzop_main(int argc UNUSED_PARAM, char **argv)
+{
+	getopt32(argv, OPTION_STRING);
+	argv += optind;
+	/* lzopcat? */
+	if (applet_name[4] == 'c')
+		option_mask32 |= (OPT_STDOUT | OPT_DECOMPRESS);
+	/* unlzop? */
+	if (applet_name[0] == 'u')
+		option_mask32 |= OPT_DECOMPRESS;
+
+	global_crc32_table = crc32_filltable(NULL, 0);
+	return bbunpack(argv, pack_lzop, make_new_name_lzop, /*unused:*/ NULL);
+}
diff --git a/busybox-1.19.3/archival/rpm.c b/busybox-1.19.3/archival/rpm.c
new file mode 100644
index 0000000..8174f48
--- /dev/null
+++ b/busybox-1.19.3/archival/rpm.c
@@ -0,0 +1,394 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rpm applet for busybox
+ *
+ * Copyright (C) 2001,2002 by Laurence Anderson
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//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	-i	Show information"
+//usage:     "\n	-l	List contents"
+//usage:     "\n	-d	List documents"
+//usage:     "\n	-c	List config files"
+
+#include "libbb.h"
+#include "archive.h"
+#include "rpm.h"
+
+#define RPM_CHAR_TYPE           1
+#define RPM_INT8_TYPE           2
+#define RPM_INT16_TYPE          3
+#define RPM_INT32_TYPE          4
+/* #define RPM_INT64_TYPE       5   ---- These aren't supported (yet) */
+#define RPM_STRING_TYPE         6
+#define RPM_BIN_TYPE            7
+#define RPM_STRING_ARRAY_TYPE   8
+#define RPM_I18NSTRING_TYPE     9
+
+#define TAG_NAME                1000
+#define TAG_VERSION             1001
+#define TAG_RELEASE             1002
+#define TAG_SUMMARY             1004
+#define TAG_DESCRIPTION         1005
+#define TAG_BUILDTIME           1006
+#define TAG_BUILDHOST           1007
+#define TAG_SIZE                1009
+#define TAG_VENDOR              1011
+#define TAG_LICENSE             1014
+#define TAG_PACKAGER            1015
+#define TAG_GROUP               1016
+#define TAG_URL                 1020
+#define TAG_PREIN               1023
+#define TAG_POSTIN              1024
+#define TAG_FILEFLAGS           1037
+#define TAG_FILEUSERNAME        1039
+#define TAG_FILEGROUPNAME       1040
+#define TAG_SOURCERPM           1044
+#define TAG_PREINPROG           1085
+#define TAG_POSTINPROG          1086
+#define TAG_PREFIXS             1098
+#define TAG_DIRINDEXES          1116
+#define TAG_BASENAMES           1117
+#define TAG_DIRNAMES            1118
+
+#define RPMFILE_CONFIG          (1 << 0)
+#define RPMFILE_DOC             (1 << 1)
+
+enum rpm_functions_e {
+	rpm_query = 1,
+	rpm_install = 2,
+	rpm_query_info = 4,
+	rpm_query_package = 8,
+	rpm_query_list = 16,
+	rpm_query_list_doc = 32,
+	rpm_query_list_config = 64
+};
+
+typedef struct {
+	uint32_t tag; /* 4 byte tag */
+	uint32_t type; /* 4 byte type */
+	uint32_t offset; /* 4 byte offset */
+	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;
+}
+
+static void extract_cpio(int fd, const char *source_rpm)
+{
+	archive_handle_t *archive_handle;
+
+	if (source_rpm != NULL) {
+		/* Binary rpm (it was built from some SRPM), install to root */
+		xchdir("/");
+	} /* else: SRPM, install to current dir */
+
+	/* Initialize */
+	archive_handle = init_handle();
+	archive_handle->seek = seek_by_read;
+	archive_handle->action_data = data_extract_all;
+#if 0 /* For testing (rpm -i only lists the files in internal cpio): */
+	archive_handle->action_header = header_list;
+	archive_handle->action_data = data_skip;
+#endif
+	archive_handle->ah_flags = ARCHIVE_RESTORE_DATE | ARCHIVE_CREATE_LEADING_DIRS
+		/* 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_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*/);
+	while (get_header_cpio(archive_handle) == EXIT_SUCCESS)
+		continue;
+}
+
+static rpm_index **rpm_gettags(int fd, int *num_tags)
+{
+	/* We should never need more than 200 (shrink via realloc later) */
+	rpm_index **tags = xzalloc(200 * sizeof(tags[0]));
+	int pass, tagindex = 0;
+
+	xlseek(fd, 96, SEEK_CUR); /* Seek past the unused lead */
+
+	/* 1st pass is the signature headers, 2nd is the main stuff */
+	for (pass = 0; pass < 2; pass++) {
+		struct rpm_header header;
+		rpm_index *tmpindex;
+		int storepos;
+
+		xread(fd, &header, sizeof(header));
+		if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER))
+			return NULL; /* Invalid magic, or not version 1 */
+		header.size = ntohl(header.size);
+		header.entries = ntohl(header.entries);
+		storepos = xlseek(fd, 0, SEEK_CUR) + header.entries * 16;
+
+		while (header.entries--) {
+			tmpindex = tags[tagindex++] = xmalloc(sizeof(*tmpindex));
+			xread(fd, tmpindex, sizeof(*tmpindex));
+			tmpindex->tag = ntohl(tmpindex->tag);
+			tmpindex->type = ntohl(tmpindex->type);
+			tmpindex->count = ntohl(tmpindex->count);
+			tmpindex->offset = storepos + ntohl(tmpindex->offset);
+			if (pass == 0)
+				tmpindex->tag -= 743;
+		}
+		storepos = xlseek(fd, header.size, SEEK_CUR); /* Seek past store */
+		/* Skip padding to 8 byte boundary after reading signature headers */
+		if (pass == 0)
+			xlseek(fd, (-storepos) & 0x7, SEEK_CUR);
+	}
+	/* realloc tags to save space */
+	tags = xrealloc(tags, tagindex * sizeof(tags[0]));
+	*num_tags = tagindex;
+	/* All done, leave the file at the start of the gzipped cpio archive */
+	return tags;
+}
+
+static int bsearch_rpmtag(const void *key, const void *item)
+{
+	int *tag = (int *)key;
+	rpm_index **tmp = (rpm_index **) item;
+	return (*tag - tmp[0]->tag);
+}
+
+static int rpm_getcount(int tag)
+{
+	rpm_index **found;
+	found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+	if (!found)
+		return 0;
+	return found[0]->count;
+}
+
+static char *rpm_getstr(int tag, int itemindex)
+{
+	rpm_index **found;
+	found = bsearch(&tag, mytags, tagcount, sizeof(struct rpmtag *), bsearch_rpmtag);
+	if (!found || itemindex >= found[0]->count)
+		return NULL;
+	if (found[0]->type == RPM_STRING_TYPE
+	 || found[0]->type == RPM_I18NSTRING_TYPE
+	 || found[0]->type == RPM_STRING_ARRAY_TYPE
+	) {
+		int n;
+		char *tmpstr = (char *) map + found[0]->offset;
+		for (n = 0; n < itemindex; n++)
+			tmpstr = tmpstr + strlen(tmpstr) + 1;
+		return tmpstr;
+	}
+	return NULL;
+}
+
+static int rpm_getint(int tag, int itemindex)
+{
+	rpm_index **found;
+	int *tmpint; /* NB: using int8_t* would be easier to code */
+
+	/* 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);
+	if (!found || itemindex >= found[0]->count)
+		return -1;
+
+	tmpint = (int *) ((char *) 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 */
+		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);*/
+		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);*/
+		return *(int8_t*)tmpint;
+	}
+	return -1;
+}
+
+static void fileaction_dobackup(char *filename, int fileref)
+{
+	struct stat oldfile;
+	int stat_res;
+	char *newname;
+	if (rpm_getint(TAG_FILEFLAGS, fileref) & RPMFILE_CONFIG) {
+		/* Only need to backup config files */
+		stat_res = lstat(filename, &oldfile);
+		if (stat_res == 0 && S_ISREG(oldfile.st_mode)) {
+			/* File already exists  - really should check MD5's etc to see if different */
+			newname = xasprintf("%s.rpmorig", filename);
+			copy_file(filename, newname, FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS);
+			remove_file(filename, FILEUTILS_RECUR | FILEUTILS_FORCE);
+			free(newname);
+		}
+	}
+}
+
+static void fileaction_setowngrp(char *filename, int fileref)
+{
+	/* real rpm warns: "user foo does not exist - using <you>" */
+	struct passwd *pw = getpwnam(rpm_getstr(TAG_FILEUSERNAME, fileref));
+	int uid = pw ? pw->pw_uid : getuid(); /* or euid? */
+	struct group *gr = getgrnam(rpm_getstr(TAG_FILEGROUPNAME, fileref));
+	int gid = gr ? gr->gr_gid : getgid();
+	chown(filename, uid, gid);
+}
+
+static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
+{
+	int count = 0;
+	while (rpm_getstr(filetag, count)) {
+		char* filename = xasprintf("%s%s",
+			rpm_getstr(TAG_DIRNAMES, rpm_getint(TAG_DIRINDEXES, count)),
+			rpm_getstr(TAG_BASENAMES, count));
+		fileaction(filename, count++);
+		free(filename);
+	}
+}
diff --git a/busybox-1.19.3/archival/rpm.h b/busybox-1.19.3/archival/rpm.h
new file mode 100644
index 0000000..afe2b55
--- /dev/null
+++ b/busybox-1.19.3/archival/rpm.h
@@ -0,0 +1,38 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * RPM structs and consts
+ *
+ * Copyright (C) 2001 by Laurence Anderson
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* RPM file starts with this struct: */
+struct rpm_lead {
+	uint32_t magic;
+	uint8_t  major, minor;
+	uint16_t type;
+	uint16_t archnum;
+	char     name[66];
+	uint16_t osnum;
+	uint16_t signature_type;
+	char     reserved[16];
+};
+struct BUG_rpm_lead {
+	char bug[sizeof(struct rpm_lead) == 96 ? 1 : -1];
+};
+#define RPM_LEAD_MAGIC      0xedabeedb
+#define RPM_LEAD_MAGIC_STR  "\355\253\356\333"
+
+/* Then follows the header: */
+struct rpm_header {
+	uint32_t magic_and_ver; /* 3 byte magic: 0x8e 0xad 0xe8; 1 byte version: 0x01 */
+	uint32_t reserved;      /* 4 bytes reserved */
+	uint32_t entries;       /* Number of entries in header (4 bytes) */
+	uint32_t size;          /* Size of store (4 bytes) */
+};
+struct BUG_rpm_header {
+	char bug[sizeof(struct rpm_header) == 16 ? 1 : -1];
+};
+#define RPM_HEADER_MAGICnVER  0x8eade801
+#define RPM_HEADER_MAGIC_STR  "\216\255\350"
diff --git a/busybox-1.19.3/archival/rpm2cpio.c b/busybox-1.19.3/archival/rpm2cpio.c
new file mode 100644
index 0000000..ff4a0d1
--- /dev/null
+++ b/busybox-1.19.3/archival/rpm2cpio.c
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rpm2cpio implementation for busybox
+ *
+ * Copyright (C) 2001 by Laurence Anderson
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//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 "archive.h"
+#include "rpm.h"
+
+enum { rpm_fd = STDIN_FILENO };
+
+static unsigned skip_header(void)
+{
+	struct rpm_header header;
+	unsigned len;
+
+	xread(rpm_fd, &header, sizeof(header));
+//	if (strncmp((char *) &header.magic, RPM_HEADER_MAGIC_STR, 3) != 0) {
+//		bb_error_msg_and_die("invalid RPM header magic");
+//	}
+//	if (header.version != 1) {
+//		bb_error_msg_and_die("unsupported RPM header version");
+//	}
+	if (header.magic_and_ver != htonl(RPM_HEADER_MAGICnVER)) {
+		bb_error_msg_and_die("invalid RPM header magic or unsupported version");
+		// ": %x != %x", header.magic_and_ver, htonl(RPM_HEADER_MAGICnVER));
+	}
+
+	/* Seek past index entries, and past store */
+	len = 16 * ntohl(header.entries) + ntohl(header.size);
+	seek_by_jump(rpm_fd, len);
+
+	return sizeof(header) + len;
+}
+
+/* No getopt required */
+int rpm2cpio_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rpm2cpio_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct rpm_lead lead;
+	unsigned pos;
+
+	if (argv[1]) {
+		xmove_fd(xopen(argv[1], O_RDONLY), rpm_fd);
+	}
+	xread(rpm_fd, &lead, sizeof(lead));
+
+	/* Just check the magic, the rest is irrelevant */
+	if (lead.magic != htonl(RPM_LEAD_MAGIC)) {
+		bb_error_msg_and_die("invalid RPM magic");
+	}
+
+	/* Skip the signature header, align to 8 bytes */
+	pos = skip_header();
+	seek_by_jump(rpm_fd, (-(int)pos) & 7);
+
+	/* Skip the main header */
+	skip_header();
+
+#if 0
+	/* This works, but doesn't report uncompress errors (they happen in child) */
+	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;
+}
diff --git a/busybox-1.19.3/archival/tar.c b/busybox-1.19.3/archival/tar.c
new file mode 100644
index 0000000..375e838
--- /dev/null
+++ b/busybox-1.19.3/archival/tar.c
@@ -0,0 +1,1135 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tar implementation for busybox
+ *
+ * Modified to use common extraction code used by ar, cpio, dpkg-deb, dpkg
+ *  by Glenn McGrath
+ *
+ * Note, that as of BusyBox-0.43, tar has been completely rewritten from the
+ * ground up.  It still has remnants of the old code lying about, but it is
+ * very different now (i.e., cleaner, less global variables, etc.)
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Based in part in the tar implementation in sash
+ *  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 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 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.
+ */
+
+#include <fnmatch.h>
+#include "libbb.h"
+#include "archive.h"
+/* FIXME: Stop using this non-standard feature */
+#ifndef FNM_LEADING_DIR
+# define FNM_LEADING_DIR 0
+#endif
+
+
+//#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__)
+#define DBG(...) ((void)0)
+
+
+#define block_buf bb_common_bufsiz1
+
+
+#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)
+#endif
+
+
+#if ENABLE_FEATURE_TAR_CREATE
+
+/*
+** writeTarFile(), writeFileToTarball(), and writeTarHeader() are
+** the only functions that deal with the HardLinkInfo structure.
+** Even these functions use the xxxHardLinkInfo() functions.
+*/
+typedef struct HardLinkInfo {
+	struct HardLinkInfo *next; /* Next entry in list */
+	dev_t dev;                 /* Device number */
+	ino_t ino;                 /* Inode number */
+//	short linkCount;           /* (Hard) Link Count */
+	char name[1];              /* Start of filename (must be last) */
+} HardLinkInfo;
+
+/* Some info to be carried along when creating a new tarball */
+typedef struct TarBallInfo {
+	int tarFd;                      /* Open-for-write file descriptor
+	                                 * for the tarball */
+	int verboseFlag;                /* Whether to print extra stuff or not */
+	const llist_t *excludeList;     /* List of files to not include */
+	HardLinkInfo *hlInfoHead;       /* Hard Link Tracking Information */
+	HardLinkInfo *hlInfo;           /* Hard Link Info for the current file */
+//TODO: save only st_dev + st_ino
+	struct stat tarFileStatBuf;     /* Stat info for the tarball, letting
+	                                 * us know the inode and device that the
+	                                 * tarball lives, so we can avoid trying
+	                                 * to include the tarball into itself */
+} TarBallInfo;
+
+/* A nice enum with all the possible tar file content types */
+enum {
+	REGTYPE = '0',		/* regular file */
+	REGTYPE0 = '\0',	/* regular file (ancient bug compat) */
+	LNKTYPE = '1',		/* hard link */
+	SYMTYPE = '2',		/* symbolic link */
+	CHRTYPE = '3',		/* character special */
+	BLKTYPE = '4',		/* block special */
+	DIRTYPE = '5',		/* directory */
+	FIFOTYPE = '6',		/* FIFO special */
+	CONTTYPE = '7',		/* reserved */
+	GNULONGLINK = 'K',	/* GNU long (>100 chars) link name */
+	GNULONGNAME = 'L',	/* GNU long (>100 chars) file name */
+};
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static void addHardLinkInfo(HardLinkInfo **hlInfoHeadPtr,
+					struct stat *statbuf,
+					const char *fileName)
+{
+	/* Note: hlInfoHeadPtr can never be NULL! */
+	HardLinkInfo *hlInfo;
+
+	hlInfo = xmalloc(sizeof(HardLinkInfo) + strlen(fileName));
+	hlInfo->next = *hlInfoHeadPtr;
+	*hlInfoHeadPtr = hlInfo;
+	hlInfo->dev = statbuf->st_dev;
+	hlInfo->ino = statbuf->st_ino;
+//	hlInfo->linkCount = statbuf->st_nlink;
+	strcpy(hlInfo->name, fileName);
+}
+
+static void freeHardLinkInfo(HardLinkInfo **hlInfoHeadPtr)
+{
+	HardLinkInfo *hlInfo;
+	HardLinkInfo *hlInfoNext;
+
+	if (hlInfoHeadPtr) {
+		hlInfo = *hlInfoHeadPtr;
+		while (hlInfo) {
+			hlInfoNext = hlInfo->next;
+			free(hlInfo);
+			hlInfo = hlInfoNext;
+		}
+		*hlInfoHeadPtr = NULL;
+	}
+}
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order ;) */
+static HardLinkInfo *findHardLinkInfo(HardLinkInfo *hlInfo, struct stat *statbuf)
+{
+	while (hlInfo) {
+		if (statbuf->st_ino == hlInfo->ino
+		 && statbuf->st_dev == hlInfo->dev
+		) {
+			DBG("found hardlink:'%s'", hlInfo->name);
+			break;
+		}
+		hlInfo = hlInfo->next;
+	}
+	return hlInfo;
+}
+
+/* Put an octal string into the specified buffer.
+ * The number is zero padded and possibly null terminated.
+ * Stores low-order bits only if whole value does not fit. */
+static void putOctal(char *cp, int len, off_t value)
+{
+	char tempBuffer[sizeof(off_t)*3 + 1];
+	char *tempString = tempBuffer;
+	int width;
+
+	width = sprintf(tempBuffer, "%0*"OFF_FMT"o", len, value);
+	tempString += (width - len);
+
+	/* If string has leading zeroes, we can drop one */
+	/* and field will have trailing '\0' */
+	/* (increases chances of compat with other tars) */
+	if (tempString[0] == '0')
+		tempString++;
+
+	/* Copy the string to the field */
+	memcpy(cp, tempString, len);
+}
+#define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b))
+
+static void chksum_and_xwrite(int fd, struct tar_header_t* hp)
+{
+	/* POSIX says that checksum is done on unsigned bytes
+	 * (Sun and HP-UX gets it wrong... more details in
+	 * GNU tar source) */
+	const unsigned char *cp;
+	int chksum, size;
+
+	strcpy(hp->magic, "ustar  ");
+
+	/* Calculate and store the checksum (i.e., the sum of all of the bytes of
+	 * the header).  The checksum field must be filled with blanks for the
+	 * calculation.  The checksum field is formatted differently from the
+	 * other fields: it has 6 digits, a null, then a space -- rather than
+	 * digits, followed by a null like the other fields... */
+	memset(hp->chksum, ' ', sizeof(hp->chksum));
+	cp = (const unsigned char *) hp;
+	chksum = 0;
+	size = sizeof(*hp);
+	do { chksum += *cp++; } while (--size);
+	putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum);
+
+	/* Now write the header out to disk */
+	xwrite(fd, hp, sizeof(*hp));
+}
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+static void writeLongname(int fd, int type, const char *name, int dir)
+{
+	static const struct {
+		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 */
+	} prefilled = {
+		"0000000",
+		"0000000",
+		"0000000",
+		"00000000000",
+		"00000000000",
+	};
+	struct tar_header_t header;
+	int size;
+
+	dir = !!dir; /* normalize: 0/1 */
+	size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */
+	/* + dir: account for possible '/' */
+
+	memset(&header, 0, sizeof(header));
+	strcpy(header.name, "././@LongLink");
+	memcpy(header.mode, prefilled.mode, sizeof(prefilled));
+	PUT_OCTAL(header.size, size);
+	header.typeflag = type;
+	chksum_and_xwrite(fd, &header);
+
+	/* Write filename[/] and pad the block. */
+	/* dir=0: writes 'name<NUL>', pads */
+	/* dir=1: writes 'name', writes '/<NUL>', pads */
+	dir *= 2;
+	xwrite(fd, name, size - dir);
+	xwrite(fd, "/", dir);
+	size = (-size) & (TAR_BLOCK_SIZE-1);
+	memset(&header, 0, size);
+	xwrite(fd, &header, size);
+}
+#endif
+
+/* Write out a tar header for the specified file/directory/whatever */
+static int writeTarHeader(struct TarBallInfo *tbInfo,
+		const char *header_name, const char *fileName, struct stat *statbuf)
+{
+	struct tar_header_t header;
+
+	memset(&header, 0, sizeof(header));
+
+	strncpy(header.name, header_name, sizeof(header.name));
+
+	/* POSIX says to mask mode with 07777. */
+	PUT_OCTAL(header.mode, statbuf->st_mode & 07777);
+	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 */
+	/* 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));
+	safe_strncpy(header.gname, get_cached_groupname(statbuf->st_gid), sizeof(header.gname));
+
+	if (tbInfo->hlInfo) {
+		/* This is a hard link */
+		header.typeflag = LNKTYPE;
+		strncpy(header.linkname, tbInfo->hlInfo->name,
+				sizeof(header.linkname));
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+		/* Write out long linkname if needed */
+		if (header.linkname[sizeof(header.linkname)-1])
+			writeLongname(tbInfo->tarFd, GNULONGLINK,
+					tbInfo->hlInfo->name, 0);
+#endif
+	} else if (S_ISLNK(statbuf->st_mode)) {
+		char *lpath = xmalloc_readlink_or_warn(fileName);
+		if (!lpath)
+			return FALSE;
+		header.typeflag = SYMTYPE;
+		strncpy(header.linkname, lpath, sizeof(header.linkname));
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+		/* Write out long linkname if needed */
+		if (header.linkname[sizeof(header.linkname)-1])
+			writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0);
+#else
+		/* If it is larger than 100 bytes, bail out */
+		if (header.linkname[sizeof(header.linkname)-1]) {
+			free(lpath);
+			bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
+			return FALSE;
+		}
+#endif
+		free(lpath);
+	} else if (S_ISDIR(statbuf->st_mode)) {
+		header.typeflag = DIRTYPE;
+		/* Append '/' only if there is a space for it */
+		if (!header.name[sizeof(header.name)-1])
+			header.name[strlen(header.name)] = '/';
+	} else if (S_ISCHR(statbuf->st_mode)) {
+		header.typeflag = CHRTYPE;
+		PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
+		PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
+	} else if (S_ISBLK(statbuf->st_mode)) {
+		header.typeflag = BLKTYPE;
+		PUT_OCTAL(header.devmajor, major(statbuf->st_rdev));
+		PUT_OCTAL(header.devminor, minor(statbuf->st_rdev));
+	} else if (S_ISFIFO(statbuf->st_mode)) {
+		header.typeflag = FIFOTYPE;
+	} else if (S_ISREG(statbuf->st_mode)) {
+		/* 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;
+	} else {
+		bb_error_msg("%s: unknown file type", fileName);
+		return FALSE;
+	}
+
+#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+	/* Write out long name if needed */
+	/* (we, like GNU tar, output long linkname *before* long name) */
+	if (header.name[sizeof(header.name)-1])
+		writeLongname(tbInfo->tarFd, GNULONGNAME,
+				header_name, S_ISDIR(statbuf->st_mode));
+#endif
+
+	/* Now write the header out to disk */
+	chksum_and_xwrite(tbInfo->tarFd, &header);
+
+	/* Now do the verbose thing (or not) */
+	if (tbInfo->verboseFlag) {
+		FILE *vbFd = stdout;
+
+		/* If archive goes to stdout, verbose goes to stderr */
+		if (tbInfo->tarFd == STDOUT_FILENO)
+			vbFd = stderr;
+		/* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */
+		/* We don't have such excesses here: for us "v" == "vv" */
+		/* '/' is probably a GNUism */
+		fprintf(vbFd, "%s%s\n", header_name,
+				S_ISDIR(statbuf->st_mode) ? "/" : "");
+	}
+
+	return TRUE;
+}
+
+#if ENABLE_FEATURE_TAR_FROM
+static int exclude_file(const llist_t *excluded_files, const char *file)
+{
+	while (excluded_files) {
+		if (excluded_files->data[0] == '/') {
+			if (fnmatch(excluded_files->data, file,
+					FNM_PATHNAME | FNM_LEADING_DIR) == 0)
+				return 1;
+		} else {
+			const char *p;
+
+			for (p = file; p[0] != '\0'; p++) {
+				if ((p == file || p[-1] == '/')
+				 && p[0] != '/'
+				 && fnmatch(excluded_files->data, p,
+						FNM_PATHNAME | FNM_LEADING_DIR) == 0
+				) {
+					return 1;
+				}
+			}
+		}
+		excluded_files = excluded_files->link;
+	}
+
+	return 0;
+}
+#else
+# define exclude_file(excluded_files, file) 0
+#endif
+
+static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf,
+			void *userData, int depth UNUSED_PARAM)
+{
+	struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData;
+	const char *header_name;
+	int inputFileFd = -1;
+
+	DBG("writeFileToTarball('%s')", fileName);
+
+	/* Strip leading '/' and such (must be before memorizing hardlink's name) */
+	header_name = strip_unsafe_prefix(fileName);
+
+	if (header_name[0] == '\0')
+		return TRUE;
+
+	/* It is against the rules to archive a socket */
+	if (S_ISSOCK(statbuf->st_mode)) {
+		bb_error_msg("%s: socket ignored", fileName);
+		return TRUE;
+	}
+
+	/*
+	 * Check to see if we are dealing with a hard link.
+	 * If so -
+	 * Treat the first occurance of a given dev/inode as a file while
+	 * treating any additional occurances as hard links.  This is done
+	 * by adding the file information to the HardLinkInfo linked list.
+	 */
+	tbInfo->hlInfo = NULL;
+	if (!S_ISDIR(statbuf->st_mode) && statbuf->st_nlink > 1) {
+		DBG("'%s': st_nlink > 1", header_name);
+		tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf);
+		if (tbInfo->hlInfo == NULL) {
+			DBG("'%s': addHardLinkInfo", header_name);
+			addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name);
+		}
+	}
+
+	/* It is a bad idea to store the archive we are in the process of creating,
+	 * so check the device and inode to be sure that this particular file isn't
+	 * the new tarball */
+	if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev
+	 && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino
+	) {
+		bb_error_msg("%s: file is the archive; skipping", fileName);
+		return TRUE;
+	}
+
+	if (exclude_file(tbInfo->excludeList, header_name))
+		return SKIP;
+
+#if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
+	if (strlen(header_name) >= NAME_SIZE) {
+		bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported");
+		return TRUE;
+	}
+#endif
+
+	/* Is this a regular file? */
+	if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) {
+		/* open the file we want to archive, and make sure all is well */
+		inputFileFd = open_or_warn(fileName, O_RDONLY);
+		if (inputFileFd < 0) {
+			return FALSE;
+		}
+	}
+
+	/* Add an entry to the tarball */
+	if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) {
+		return FALSE;
+	}
+
+	/* If it was a regular file, write out the body */
+	if (inputFileFd >= 0) {
+		size_t readSize;
+		/* Write the file to the archive. */
+		/* We record size into header first, */
+		/* and then write out file. If file shrinks in between, */
+		/* tar will be corrupted. So we don't allow for that. */
+		/* NB: GNU tar 1.16 warns and pads with zeroes */
+		/* or even seeks back and updates header */
+		bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
+		////off_t readSize;
+		////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size);
+		////if (readSize != statbuf->st_size && readSize >= 0) {
+		////	bb_error_msg_and_die("short read from %s, aborting", fileName);
+		////}
+
+		/* Check that file did not grow in between? */
+		/* if (safe_read(inputFileFd, 1) == 1) warn but continue? */
+
+		close(inputFileFd);
+
+		/* Pad the file up to the tar block size */
+		/* (a few tricks here in the name of code size) */
+		readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1);
+		memset(block_buf, 0, readSize);
+		xwrite(tbInfo->tarFd, block_buf, readSize);
+	}
+
+	return TRUE;
+}
+
+#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
+# if !(ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2)
+#  define vfork_compressor(tar_fd, gzip) vfork_compressor(tar_fd)
+# endif
+/* Don't inline: vfork scares gcc and pessimizes code */
+static void NOINLINE vfork_compressor(int tar_fd, int gzip)
+{
+	pid_t gzipPid;
+# if ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2
+	const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2";
+# elif ENABLE_FEATURE_SEAMLESS_GZ
+	const char *zip_exec = "gzip";
+# else /* only ENABLE_FEATURE_SEAMLESS_BZ2 */
+	const char *zip_exec = "bzip2";
+# endif
+	// On Linux, vfork never unpauses parent early, although standard
+	// allows for that. Do we want to waste bytes checking for it?
+# define WAIT_FOR_CHILD 0
+	volatile int vfork_exec_errno = 0;
+	struct fd_pair gzipDataPipe;
+# if WAIT_FOR_CHILD
+	struct fd_pair gzipStatusPipe;
+	xpiped_pair(gzipStatusPipe);
+# endif
+	xpiped_pair(gzipDataPipe);
+
+	signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */
+
+# if defined(__GNUC__) && __GNUC__
+	/* Avoid vfork clobbering */
+	(void) &zip_exec;
+# endif
+
+	gzipPid = xvfork();
+
+	if (gzipPid == 0) {
+		/* child */
+		/* NB: close _first_, then move fds! */
+		close(gzipDataPipe.wr);
+# if WAIT_FOR_CHILD
+		close(gzipStatusPipe.rd);
+		/* gzipStatusPipe.wr will close only on exec -
+		 * parent waits for this close to happen */
+		fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC);
+# endif
+		xmove_fd(gzipDataPipe.rd, 0);
+		xmove_fd(tar_fd, 1);
+		/* exec gzip/bzip2 program/applet */
+		BB_EXECLP(zip_exec, zip_exec, "-f", NULL);
+		vfork_exec_errno = errno;
+		_exit(EXIT_FAILURE);
+	}
+
+	/* parent */
+	xmove_fd(gzipDataPipe.wr, tar_fd);
+	close(gzipDataPipe.rd);
+# if WAIT_FOR_CHILD
+	close(gzipStatusPipe.wr);
+	while (1) {
+		char buf;
+		int n;
+
+		/* Wait until child execs (or fails to) */
+		n = full_read(gzipStatusPipe.rd, &buf, 1);
+		if (n < 0 /* && errno == EAGAIN */)
+			continue;	/* try it again */
+	}
+	close(gzipStatusPipe.rd);
+# endif
+	if (vfork_exec_errno) {
+		errno = vfork_exec_errno;
+		bb_perror_msg_and_die("can't execute '%s'", zip_exec);
+	}
+}
+#endif /* ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2 */
+
+
+/* gcc 4.2.1 inlines it, making code bigger */
+static NOINLINE int writeTarFile(int tar_fd, int verboseFlag,
+	int dereferenceFlag, const llist_t *include,
+	const llist_t *exclude, int gzip)
+{
+	int errorFlag = FALSE;
+	struct TarBallInfo tbInfo;
+
+	tbInfo.hlInfoHead = NULL;
+	tbInfo.tarFd = tar_fd;
+	tbInfo.verboseFlag = verboseFlag;
+
+	/* Store the stat info for the tarball's file, so
+	 * can avoid including the tarball into itself....  */
+	xfstat(tbInfo.tarFd, &tbInfo.tarFileStatBuf, "can't stat tar file");
+
+#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
+	if (gzip)
+		vfork_compressor(tbInfo.tarFd, gzip);
+#endif
+
+	tbInfo.excludeList = exclude;
+
+	/* 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),
+				writeFileToTarball, writeFileToTarball, &tbInfo, 0)
+		) {
+			errorFlag = TRUE;
+		}
+		include = include->link;
+	}
+	/* Write two empty blocks to the end of the archive */
+	memset(block_buf, 0, 2*TAR_BLOCK_SIZE);
+	xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE);
+
+	/* To be pedantically correct, we would check if the tarball
+	 * is smaller than 20 tar blocks, and pad it if it was smaller,
+	 * but that isn't necessary for GNU tar interoperability, and
+	 * so is considered a waste of space */
+
+	/* Close so the child process (if any) will exit */
+	close(tbInfo.tarFd);
+
+	/* Hang up the tools, close up shop, head home */
+	if (ENABLE_FEATURE_CLEAN_UP)
+		freeHardLinkInfo(&tbInfo.hlInfoHead);
+
+	if (errorFlag)
+		bb_error_msg("error exit delayed from previous errors");
+
+#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
+	if (gzip) {
+		int status;
+		if (safe_waitpid(-1, &status, 0) == -1)
+			bb_perror_msg("waitpid");
+		else if (!WIFEXITED(status) || WEXITSTATUS(status))
+			/* gzip was killed or has exited with nonzero! */
+			errorFlag = TRUE;
+	}
+#endif
+	return errorFlag;
+}
+#else
+int writeTarFile(int tar_fd, int verboseFlag,
+	int dereferenceFlag, const llist_t *include,
+	const llist_t *exclude, int gzip);
+#endif /* FEATURE_TAR_CREATE */
+
+#if ENABLE_FEATURE_TAR_FROM
+static llist_t *append_file_list_to_list(llist_t *list)
+{
+	FILE *src_stream;
+	char *line;
+	llist_t *newlist = NULL;
+
+	while (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);
+		}
+		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"
+//usage:	IF_FEATURE_SEAMLESS_Z("Z")
+//usage:	IF_FEATURE_SEAMLESS_GZ("z")
+//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 ")
+//usage:	"or list files from a tar file\n"
+//usage:     "\nOperation:"
+//usage:	IF_FEATURE_TAR_CREATE(
+//usage:     "\n	c	Create"
+//usage:	)
+//usage:     "\n	x	Extract"
+//usage:     "\n	t	List"
+//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_BZ2(
+//usage:     "\n	j	(De)compress using bzip2"
+//usage:	)
+//usage:	IF_FEATURE_SEAMLESS_LZMA(
+//usage:     "\n	a	(De)compress using lzma"
+//usage:	)
+//usage:     "\n	O	Extract to stdout"
+//usage:	IF_FEATURE_TAR_CREATE(
+//usage:     "\n	h	Follow symlinks"
+//usage:	)
+//usage:	IF_FEATURE_TAR_NOPRESERVE_TIME(
+//usage:     "\n	m	Don't restore mtime"
+//usage:	)
+//usage:	IF_FEATURE_TAR_FROM(
+//usage:	IF_FEATURE_TAR_LONG_OPTIONS(
+//usage:     "\n	exclude	File to exclude"
+//usage:	)
+//usage:     "\n	X	File with names to exclude"
+//usage:     "\n	T	File with names to include"
+//usage:	)
+//usage:
+//usage:#define tar_example_usage
+//usage:       "$ zcat /tmp/tarball.tar.gz | tar -xf -\n"
+//usage:       "$ tar -cf /tmp/tarball.tar /usr/local\n"
+
+// Supported but aren't in --help:
+//	o	no-same-owner
+//	p	same-permissions
+//	k	keep-old
+//	numeric-owner
+//	no-same-permissions
+//	overwrite
+//IF_FEATURE_TAR_TO_COMMAND(
+//	to-command
+//)
+
+enum {
+	OPTBIT_KEEP_OLD = 8,
+	IF_FEATURE_TAR_CREATE(   OPTBIT_CREATE      ,)
+	IF_FEATURE_TAR_CREATE(   OPTBIT_DEREFERENCE ,)
+	IF_FEATURE_SEAMLESS_BZ2( OPTBIT_BZIP2       ,)
+	IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA        ,)
+	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_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,)
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+	IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND   ,)
+	OPTBIT_NUMERIC_OWNER,
+	OPTBIT_NOPRESERVE_PERM,
+	OPTBIT_OVERWRITE,
+#endif
+	OPT_TEST         = 1 << 0, // t
+	OPT_EXTRACT      = 1 << 1, // x
+	OPT_BASEDIR      = 1 << 2, // C
+	OPT_TARNAME      = 1 << 3, // f
+	OPT_2STDOUT      = 1 << 4, // O
+	OPT_NOPRESERVE_OWNER = 1 << 5, // o == no-same-owner
+	OPT_P            = 1 << 6, // p
+	OPT_VERBOSE      = 1 << 7, // v
+	OPT_KEEP_OLD     = 1 << 8, // k
+	OPT_CREATE       = IF_FEATURE_TAR_CREATE(   (1 << OPTBIT_CREATE      )) + 0, // c
+	OPT_DEREFERENCE  = IF_FEATURE_TAR_CREATE(   (1 << OPTBIT_DEREFERENCE )) + 0, // h
+	OPT_BZIP2        = IF_FEATURE_SEAMLESS_BZ2( (1 << OPTBIT_BZIP2       )) + 0, // j
+	OPT_LZMA         = IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA        )) + 0, // a
+	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_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_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
+};
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+static const char tar_longopts[] ALIGN1 =
+	"list\0"                No_argument       "t"
+	"extract\0"             No_argument       "x"
+	"directory\0"           Required_argument "C"
+	"file\0"                Required_argument "f"
+	"to-stdout\0"           No_argument       "O"
+	/* do not restore owner */
+	/* Note: GNU tar handles 'o' as no-same-owner only on extract,
+	 * on create, 'o' is --old-archive. We do not support --old-archive. */
+	"no-same-owner\0"       No_argument       "o"
+	"same-permissions\0"    No_argument       "p"
+	"verbose\0"             No_argument       "v"
+	"keep-old\0"            No_argument       "k"
+# if ENABLE_FEATURE_TAR_CREATE
+	"create\0"              No_argument       "c"
+	"dereference\0"         No_argument       "h"
+# endif
+# if ENABLE_FEATURE_SEAMLESS_BZ2
+	"bzip2\0"               No_argument       "j"
+# endif
+# if ENABLE_FEATURE_SEAMLESS_LZMA
+	"lzma\0"                No_argument       "a"
+# endif
+# if ENABLE_FEATURE_TAR_FROM
+	"files-from\0"          Required_argument "T"
+	"exclude-from\0"        Required_argument "X"
+# endif
+# if ENABLE_FEATURE_SEAMLESS_GZ
+	"gzip\0"                No_argument       "z"
+# endif
+# if ENABLE_FEATURE_SEAMLESS_Z
+	"compress\0"            No_argument       "Z"
+# endif
+# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME
+	"touch\0"               No_argument       "m"
+# endif
+# if ENABLE_FEATURE_TAR_TO_COMMAND
+	"to-command\0"		Required_argument "\xfb"
+# endif
+	/* use numeric uid/gid from tar header, not textual */
+	"numeric-owner\0"       No_argument       "\xfc"
+	/* do not restore mode */
+	"no-same-permissions\0" No_argument       "\xfd"
+	/* on unpack, open with O_TRUNC and !O_EXCL */
+	"overwrite\0"           No_argument       "\xfe"
+	/* --exclude takes next bit position in option mask, */
+	/* therefore we have to put it _after_ --no-same-permissions */
+# if ENABLE_FEATURE_TAR_FROM
+	"exclude\0"             Required_argument "\xff"
+# endif
+	;
+#endif
+
+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 = "-";
+	unsigned opt;
+	int verboseFlag = 0;
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
+	llist_t *excludes = NULL;
+#endif
+
+	/* Initialise default values */
+	tar_handle = init_handle();
+	tar_handle->ah_flags = ARCHIVE_CREATE_LEADING_DIRS
+	                     | ARCHIVE_RESTORE_DATE
+	                     | ARCHIVE_UNLINK_OLD;
+
+	/* Apparently only root's tar preserves perms (see bug 3844) */
+	if (getuid() != 0)
+		tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM;
+
+	/* Prepend '-' to the first argument if required */
+	opt_complementary = "--:" // first arg is options
+		"tt:vv:" // count -t,-v
+		IF_FEATURE_TAR_FROM("X::T::") // cumulative lists
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
+		"\xff::" // cumulative lists for --exclude
+#endif
+		IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd
+		IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
+		IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+	applet_long_options = tar_longopts;
+#endif
+#if ENABLE_DESKTOP
+	if (argv[1] && argv[1][0] != '-') {
+		/* Compat:
+		 * 1st argument without dash handles options with parameters
+		 * differently from dashed one: it takes *next argv[i]*
+		 * as paramenter even if there are more chars in 1st argument:
+		 *  "tar fx TARFILE" - "x" is not taken as f's param
+		 *  but is interpreted as -x option
+		 *  "tar -xf TARFILE" - dashed equivalent of the above
+		 *  "tar -fx ..." - "x" is taken as f's param
+		 * getopt32 wouldn't handle 1st command correctly.
+		 * Unfortunately, people do use such commands.
+		 * We massage argv[1] to work around it by moving 'f'
+		 * to the end of the string.
+		 * More contrived "tar fCx TARFILE DIR" still fails,
+		 * but such commands are much less likely to be used.
+		 */
+		char *f = strchr(argv[1], 'f');
+		if (f) {
+			while (f[1] != '\0') {
+				*f = f[1];
+				f++;
+			}
+			*f = 'f';
+		}
+	}
+#endif
+	opt = getopt32(argv,
+		"txC:f:Oopvk"
+		IF_FEATURE_TAR_CREATE(   "ch"  )
+		IF_FEATURE_SEAMLESS_BZ2( "j"   )
+		IF_FEATURE_SEAMLESS_LZMA("a"   )
+		IF_FEATURE_TAR_FROM(     "T:X:")
+		IF_FEATURE_SEAMLESS_GZ(  "z"   )
+		IF_FEATURE_SEAMLESS_Z(   "Z"   )
+		IF_FEATURE_TAR_NOPRESERVE_TIME("m")
+		, &base_dir // -C dir
+		, &tar_filename // -f filename
+		IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
+		IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
+		IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
+		, &excludes // --exclude
+#endif
+		, &verboseFlag // combined count for -t and -v
+		, &verboseFlag // combined count for -t and -v
+		);
+	//bb_error_msg("opt:%08x", opt);
+	argv += optind;
+
+	if (verboseFlag) tar_handle->action_header = header_verbose_list;
+	if (verboseFlag == 1) tar_handle->action_header = header_list;
+
+	if (opt & OPT_EXTRACT)
+		tar_handle->action_data = data_extract_all;
+
+	if (opt & OPT_2STDOUT)
+		tar_handle->action_data = data_extract_to_stdout;
+
+	if (opt & OPT_2COMMAND) {
+		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)
+		tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD;
+
+	if (opt & OPT_NUMERIC_OWNER)
+		tar_handle->ah_flags |= ARCHIVE_NUMERIC_OWNER;
+
+	if (opt & OPT_NOPRESERVE_OWNER)
+		tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_OWNER;
+
+	if (opt & OPT_NOPRESERVE_PERM)
+		tar_handle->ah_flags |= ARCHIVE_DONT_RESTORE_PERM;
+
+	if (opt & OPT_OVERWRITE) {
+		tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD;
+		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;
+
+#if ENABLE_FEATURE_TAR_FROM
+	tar_handle->reject = append_file_list_to_list(tar_handle->reject);
+# if ENABLE_FEATURE_TAR_LONG_OPTIONS
+	/* Append excludes to reject */
+	while (excludes) {
+		llist_t *next = excludes->link;
+		excludes->link = tar_handle->reject;
+		tar_handle->reject = excludes;
+		excludes = next;
+	}
+# endif
+	tar_handle->accept = append_file_list_to_list(tar_handle->accept);
+#endif
+
+	/* Setup an array of filenames to work with */
+	/* TODO: This is the same as in ar, make a separate function? */
+	while (*argv) {
+		/* kill trailing '/' unless the string is just "/" */
+		char *cp = last_char_is(*argv, '/');
+		if (cp > *argv)
+			*cp = '\0';
+		llist_add_to_end(&tar_handle->accept, *argv);
+		argv++;
+	}
+
+	if (tar_handle->accept || tar_handle->reject)
+		tar_handle->filter = filter_accept_reject_list;
+
+	/* Open the tar file */
+	{
+		int tar_fd = STDIN_FILENO;
+		int flags = O_RDONLY;
+
+		if (opt & OPT_CREATE) {
+			/* Make sure there is at least one file to tar up */
+			if (tar_handle->accept == NULL)
+				bb_error_msg_and_die("empty archive");
+
+			tar_fd = STDOUT_FILENO;
+			/* Mimicking GNU tar 1.15.1: */
+			flags = O_WRONLY | O_CREAT | O_TRUNC;
+		}
+
+		if (LONE_DASH(tar_filename)) {
+			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
+			) {
+				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);
+			} else {
+				tar_handle->src_fd = xopen(tar_filename, flags);
+			}
+		}
+	}
+
+	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
+
+	/* Create an archive */
+	if (opt & OPT_CREATE) {
+#if ENABLE_FEATURE_SEAMLESS_GZ || ENABLE_FEATURE_SEAMLESS_BZ2
+		int zipMode = 0;
+		if (ENABLE_FEATURE_SEAMLESS_GZ && (opt & OPT_GZIP))
+			zipMode = 1;
+		if (ENABLE_FEATURE_SEAMLESS_BZ2 && (opt & OPT_BZIP2))
+			zipMode = 2;
+#endif
+		/* NB: writeTarFile() closes tar_handle->src_fd */
+		return writeTarFile(tar_handle->src_fd, verboseFlag, opt & OPT_DEREFERENCE,
+				tar_handle->accept,
+				tar_handle->reject, zipMode);
+	}
+
+	while (get_header_ptr(tar_handle) == EXIT_SUCCESS)
+		continue;
+
+	/* Check that every file that should have been extracted was */
+	while (tar_handle->accept) {
+		if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
+		 && !find_list_entry(tar_handle->passed, tar_handle->accept->data)
+		) {
+			bb_error_msg_and_die("%s: not found in archive",
+				tar_handle->accept->data);
+		}
+		tar_handle->accept = tar_handle->accept->link;
+	}
+	if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
+		close(tar_handle->src_fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/archival/unzip.c b/busybox-1.19.3/archival/unzip.c
new file mode 100644
index 0000000..4fa7293
--- /dev/null
+++ b/busybox-1.19.3/archival/unzip.c
@@ -0,0 +1,703 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini unzip implementation for busybox
+ *
+ * Copyright (C) 2004 by Ed Clark
+ *
+ * Loosely based on original busybox unzip applet by Laurence Anderson.
+ * All options and features should work in this version.
+ *
+ * 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
+ * Zip64 + other methods
+ */
+
+//usage:#define unzip_trivial_usage
+//usage:       "[-opts[modifiers]] FILE[.zip] [LIST] [-x XLIST] [-d DIR]"
+//usage:#define unzip_full_usage "\n\n"
+//usage:       "Extract files from ZIP archives\n"
+//usage:     "\n	-l	List archive contents (with -q for short form)"
+//usage:     "\n	-n	Never overwrite files (default)"
+//usage:     "\n	-o	Overwrite"
+//usage:     "\n	-p	Send output to stdout"
+//usage:     "\n	-q	Quiet"
+//usage:     "\n	-x XLST	Exclude these files"
+//usage:     "\n	-d DIR	Extract files into DIR"
+
+#include "libbb.h"
+#include "archive.h"
+
+enum {
+#if BB_BIG_ENDIAN
+	ZIP_FILEHEADER_MAGIC = 0x504b0304,
+	ZIP_CDF_MAGIC        = 0x504b0102, /* central directory's file header */
+	ZIP_CDE_MAGIC        = 0x504b0506, /* "end of central directory" record */
+	ZIP_DD_MAGIC         = 0x504b0708,
+#else
+	ZIP_FILEHEADER_MAGIC = 0x04034b50,
+	ZIP_CDF_MAGIC        = 0x02014b50,
+	ZIP_CDE_MAGIC        = 0x06054b50,
+	ZIP_DD_MAGIC         = 0x08074b50,
+#endif
+};
+
+#define ZIP_HEADER_LEN 26
+
+typedef union {
+	uint8_t raw[ZIP_HEADER_LEN];
+	struct {
+		uint16_t version;               /* 0-1 */
+		uint16_t zip_flags;             /* 2-3 */
+		uint16_t method;                /* 4-5 */
+		uint16_t modtime;               /* 6-7 */
+		uint16_t moddate;               /* 8-9 */
+		uint32_t crc32 PACKED;          /* 10-13 */
+		uint32_t cmpsize PACKED;        /* 14-17 */
+		uint32_t ucmpsize PACKED;       /* 18-21 */
+		uint16_t filename_len;          /* 22-23 */
+		uint16_t extra_len;             /* 24-25 */
+	} formatted PACKED;
+} zip_header_t; /* PACKED - gcc 4.2.1 doesn't like it (spews warning) */
+
+/* Check the offset of the last element, not the length.  This leniency
+ * allows for poor packing, whereby the overall struct may be too long,
+ * even though the elements are all in the right place.
+ */
+struct BUG_zip_header_must_be_26_bytes {
+	char BUG_zip_header_must_be_26_bytes[
+		offsetof(zip_header_t, formatted.extra_len) + 2
+			== ZIP_HEADER_LEN ? 1 : -1];
+};
+
+#define FIX_ENDIANNESS_ZIP(zip_header) do { \
+	(zip_header).formatted.version      = SWAP_LE16((zip_header).formatted.version     ); \
+	(zip_header).formatted.method       = SWAP_LE16((zip_header).formatted.method      ); \
+	(zip_header).formatted.modtime      = SWAP_LE16((zip_header).formatted.modtime     ); \
+	(zip_header).formatted.moddate      = SWAP_LE16((zip_header).formatted.moddate     ); \
+	(zip_header).formatted.crc32        = SWAP_LE32((zip_header).formatted.crc32       ); \
+	(zip_header).formatted.cmpsize      = SWAP_LE32((zip_header).formatted.cmpsize     ); \
+	(zip_header).formatted.ucmpsize     = SWAP_LE32((zip_header).formatted.ucmpsize    ); \
+	(zip_header).formatted.filename_len = SWAP_LE16((zip_header).formatted.filename_len); \
+	(zip_header).formatted.extra_len    = SWAP_LE16((zip_header).formatted.extra_len   ); \
+} while (0)
+
+#define CDF_HEADER_LEN 42
+
+typedef union {
+	uint8_t raw[CDF_HEADER_LEN];
+	struct {
+		/* uint32_t signature; 50 4b 01 02 */
+		uint16_t version_made_by;       /* 0-1 */
+		uint16_t version_needed;        /* 2-3 */
+		uint16_t cdf_flags;             /* 4-5 */
+		uint16_t method;                /* 6-7 */
+		uint16_t mtime;                 /* 8-9 */
+		uint16_t mdate;                 /* 10-11 */
+		uint32_t crc32;                 /* 12-15 */
+		uint32_t cmpsize;               /* 16-19 */
+		uint32_t ucmpsize;              /* 20-23 */
+		uint16_t file_name_length;      /* 24-25 */
+		uint16_t extra_field_length;    /* 26-27 */
+		uint16_t file_comment_length;   /* 28-29 */
+		uint16_t disk_number_start;     /* 30-31 */
+		uint16_t internal_file_attributes; /* 32-33 */
+		uint32_t external_file_attributes PACKED; /* 34-37 */
+		uint32_t relative_offset_of_local_header PACKED; /* 38-41 */
+	} formatted PACKED;
+} cdf_header_t;
+
+struct BUG_cdf_header_must_be_42_bytes {
+	char BUG_cdf_header_must_be_42_bytes[
+		offsetof(cdf_header_t, formatted.relative_offset_of_local_header) + 4
+			== CDF_HEADER_LEN ? 1 : -1];
+};
+
+#define FIX_ENDIANNESS_CDF(cdf_header) do { \
+	(cdf_header).formatted.crc32        = SWAP_LE32((cdf_header).formatted.crc32       ); \
+	(cdf_header).formatted.cmpsize      = SWAP_LE32((cdf_header).formatted.cmpsize     ); \
+	(cdf_header).formatted.ucmpsize     = SWAP_LE32((cdf_header).formatted.ucmpsize    ); \
+	(cdf_header).formatted.file_name_length = SWAP_LE16((cdf_header).formatted.file_name_length); \
+	(cdf_header).formatted.extra_field_length = SWAP_LE16((cdf_header).formatted.extra_field_length); \
+	(cdf_header).formatted.file_comment_length = SWAP_LE16((cdf_header).formatted.file_comment_length); \
+	IF_DESKTOP( \
+	(cdf_header).formatted.version_made_by = SWAP_LE16((cdf_header).formatted.version_made_by); \
+	(cdf_header).formatted.external_file_attributes = SWAP_LE32((cdf_header).formatted.external_file_attributes); \
+	) \
+} while (0)
+
+#define CDE_HEADER_LEN 16
+
+typedef union {
+	uint8_t raw[CDE_HEADER_LEN];
+	struct {
+		/* uint32_t signature; 50 4b 05 06 */
+		uint16_t this_disk_no;
+		uint16_t disk_with_cdf_no;
+		uint16_t cdf_entries_on_this_disk;
+		uint16_t cdf_entries_total;
+		uint32_t cdf_size;
+		uint32_t cdf_offset;
+		/* uint16_t file_comment_length; */
+		/* .ZIP file comment (variable size) */
+	} formatted PACKED;
+} cde_header_t;
+
+struct BUG_cde_header_must_be_16_bytes {
+	char BUG_cde_header_must_be_16_bytes[
+		sizeof(cde_header_t) == CDE_HEADER_LEN ? 1 : -1];
+};
+
+#define FIX_ENDIANNESS_CDE(cde_header) do { \
+	(cde_header).formatted.cdf_offset = SWAP_LE32((cde_header).formatted.cdf_offset); \
+} while (0)
+
+enum { zip_fd = 3 };
+
+
+#if ENABLE_DESKTOP
+
+#define PEEK_FROM_END 16384
+
+/* NB: does not preserve file position! */
+static uint32_t find_cdf_offset(void)
+{
+	cde_header_t cde_header;
+	unsigned char *p;
+	off_t end;
+	unsigned char *buf = xzalloc(PEEK_FROM_END);
+
+	end = xlseek(zip_fd, 0, SEEK_END);
+	end -= PEEK_FROM_END;
+	if (end < 0)
+		end = 0;
+	xlseek(zip_fd, end, SEEK_SET);
+	full_read(zip_fd, buf, PEEK_FROM_END);
+
+	p = buf;
+	while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
+		if (*p != 'P') {
+			p++;
+			continue;
+		}
+		if (*++p != 'K')
+			continue;
+		if (*++p != 5)
+			continue;
+		if (*++p != 6)
+			continue;
+		/* 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;
+	}
+	//free(buf);
+	bb_error_msg_and_die("can't find file table");
+};
+
+static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
+{
+	off_t org;
+
+	org = xlseek(zip_fd, 0, SEEK_CUR);
+
+	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;
+
+	xlseek(zip_fd, org, SEEK_SET);
+	return cdf_offset;
+};
+#endif
+
+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);
+}
+
+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 */
+	}
+	free(name);
+}
+
+static void unzip_extract(zip_header_t *zip_header, int dst_fd)
+{
+	if (zip_header->formatted.method == 0) {
+		/* Method 0 - stored (not compressed) */
+		off_t size = zip_header->formatted.ucmpsize;
+		if (size)
+			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)
+			bb_error_msg_and_die("inflate error");
+		/* Validate decompression - crc */
+		if (zip_header->formatted.crc32 != (res.crc ^ 0xffffffffL)) {
+			bb_error_msg_and_die("crc error");
+		}
+		/* Validate decompression - size */
+		if (zip_header->formatted.ucmpsize != res.bytes_out) {
+			/* Don't die. Who knows, maybe len calculation
+			 * was botched somewhere. After all, crc matched! */
+			bb_error_msg("bad length");
+		}
+	}
+}
+
+int unzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int unzip_main(int argc, char **argv)
+{
+	enum { O_PROMPT, O_NEVER, O_ALWAYS };
+
+	zip_header_t zip_header;
+	smallint quiet = 0;
+	IF_NOT_DESKTOP(const) smallint verbose = 0;
+	smallint listing = 0;
+	smallint overwrite = O_PROMPT;
+#if ENABLE_DESKTOP
+	uint32_t cdf_offset;
+#endif
+	unsigned long total_usize;
+	unsigned long total_size;
+	unsigned total_entries;
+	int dst_fd = -1;
+	char *src_fn = NULL;
+	char *dst_fn = NULL;
+	llist_t *zaccept = NULL;
+	llist_t *zreject = NULL;
+	char *base_dir = NULL;
+	int i, opt;
+	int opt_range = 0;
+	char key_buf[80];
+	struct stat stat_buf;
+
+/* -q, -l and -v: UnZip 5.52 of 28 February 2005, by Info-ZIP:
+ *
+ * # /usr/bin/unzip -qq -v decompress_unlzma.i.zip
+ *   204372  Defl:N    35278  83%  09-06-09 14:23  0d056252  decompress_unlzma.i
+ * # /usr/bin/unzip -q -v decompress_unlzma.i.zip
+ *  Length   Method    Size  Ratio   Date   Time   CRC-32    Name
+ * --------  ------  ------- -----   ----   ----   ------    ----
+ *   204372  Defl:N    35278  83%  09-06-09 14:23  0d056252  decompress_unlzma.i
+ * --------          -------  ---                            -------
+ *   204372            35278  83%                            1 file
+ * # /usr/bin/unzip -v decompress_unlzma.i.zip
+ * Archive:  decompress_unlzma.i.zip
+ *  Length   Method    Size  Ratio   Date   Time   CRC-32    Name
+ * --------  ------  ------- -----   ----   ----   ------    ----
+ *   204372  Defl:N    35278  83%  09-06-09 14:23  0d056252  decompress_unlzma.i
+ * --------          -------  ---                            -------
+ *   204372            35278  83%                            1 file
+ * # unzip -v decompress_unlzma.i.zip
+ * Archive:  decompress_unlzma.i.zip
+ *   Length     Date   Time    Name
+ *  --------    ----   ----    ----
+ *    204372  09-06-09 14:23   decompress_unlzma.i
+ *  --------                   -------
+ *    204372                   1 files
+ * # /usr/bin/unzip -l -qq decompress_unlzma.i.zip
+ *    204372  09-06-09 14:23   decompress_unlzma.i
+ * # /usr/bin/unzip -l -q decompress_unlzma.i.zip
+ *   Length     Date   Time    Name
+ *  --------    ----   ----    ----
+ *    204372  09-06-09 14:23   decompress_unlzma.i
+ *  --------                   -------
+ *    204372                   1 file
+ * # /usr/bin/unzip -l decompress_unlzma.i.zip
+ * Archive:  decompress_unlzma.i.zip
+ *   Length     Date   Time    Name
+ *  --------    ----   ----    ----
+ *    204372  09-06-09 14:23   decompress_unlzma.i
+ *  --------                   -------
+ *    204372                   1 file
+ */
+
+	/* '-' 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;
+
+			case 'o': /* Always overwrite existing files */
+				overwrite = O_ALWAYS;
+				break;
+
+			case 'p': /* Extract files to stdout and fall through to set verbosity */
+				dst_fd = STDOUT_FILENO;
+
+			case 'q': /* Be quiet */
+				quiet++;
+				break;
+
+			case 'v': /* Verbose list */
+				IF_DESKTOP(verbose++;)
+				listing = 1;
+				break;
+
+			case 1: /* The zip file */
+				/* +5: space for ".zip" and NUL */
+				src_fn = xmalloc(strlen(optarg) + 5);
+				strcpy(src_fn, optarg);
+				opt_range++;
+				break;
+
+			default:
+				bb_show_usage();
+			}
+			break;
+
+		case 1: /* Include files */
+			if (opt == 1) {
+				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) {
+				llist_add_to(&zreject, optarg);
+				break;
+			}
+			if (opt == 'd') { /* Extract to base directory */
+				base_dir = optarg;
+				opt_range++;
+				break;
+			}
+			/* fall through */
+
+		default:
+			bb_show_usage();
+		}
+	}
+
+	if (src_fn == NULL) {
+		bb_show_usage();
+	}
+
+	/* Open input file */
+	if (LONE_DASH(src_fn)) {
+		xdup2(STDIN_FILENO, zip_fd);
+		/* Cannot use prompt mode since zip data is arriving on STDIN */
+		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;
+
+		for (i = 0; (i < 3) && (src_fd == -1); i++) {
+			strcpy(src_fn + orig_src_fn_len, extn[i]);
+			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);
+		}
+		xmove_fd(src_fd, zip_fd);
+	}
+
+	/* Change dir if necessary */
+	if (base_dir)
+		xchdir(base_dir);
+
+	if (quiet <= 1) { /* not -qq */
+		if (quiet == 0)
+			printf("Archive:  %s\n", src_fn);
+		if (listing) {
+			puts(verbose ?
+				" Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
+				"--------  ------  ------- -----   ----   ----   ------    ----"
+				:
+				"  Length     Date   Time    Name\n"
+				" --------    ----   ----    ----"
+				);
+		}
+	}
+
+/* Example of an archive with one 0-byte long file named 'z'
+ * created by Zip 2.31 on Unix:
+ * 0000 [50 4b]03 04 0a 00 00 00 00 00 42 1a b8 3c 00 00 |PK........B..<..|
+ *       sig........ vneed flags compr mtime mdate crc32>
+ * 0010  00 00 00 00 00 00 00 00 00 00 01 00 15 00 7a 55 |..............zU|
+ *      >..... csize...... usize...... fnlen exlen fn ex>
+ * 0020  54 09 00 03 cc d3 f9 4b cc d3 f9 4b 55 78 04 00 |T......K...KUx..|
+ *      >tra_field......................................
+ * 0030  00 00 00 00[50 4b]01 02 17 03 0a 00 00 00 00 00 |....PK..........|
+ *       ........... sig........ vmade vneed flags compr
+ * 0040  42 1a b8 3c 00 00 00 00 00 00 00 00 00 00 00 00 |B..<............|
+ *       mtime mdate crc32...... csize...... usize......
+ * 0050  01 00 0d 00 00 00 00 00 00 00 00 00 a4 81 00 00 |................|
+ *       fnlen exlen clen. dnum. iattr eattr...... relofs> (eattr = rw-r--r--)
+ * 0060  00 00 7a 55 54 05 00 03 cc d3 f9 4b 55 78 00 00 |..zUT......KUx..|
+ *      >..... fn extra_field...........................
+ * 0070 [50 4b]05 06 00 00 00 00 01 00 01 00 3c 00 00 00 |PK..........<...|
+ * 0080  34 00 00 00 00 00                               |4.....|
+ */
+	total_usize = 0;
+	total_size = 0;
+	total_entries = 0;
+#if ENABLE_DESKTOP
+	cdf_offset = 0;
+#endif
+	while (1) {
+		uint32_t magic;
+		mode_t dir_mode = 0777;
+#if ENABLE_DESKTOP
+		mode_t file_mode = 0666;
+#endif
+
+		/* Check magic number */
+		xread(zip_fd, &magic, 4);
+		/* Central directory? It's at the end, so exit */
+		if (magic == ZIP_CDF_MAGIC)
+			break;
+#if ENABLE_DESKTOP
+		/* Data descriptor? It was a streaming file, go on */
+		if (magic == ZIP_DD_MAGIC) {
+			/* skip over duplicate crc32, cmpsize and ucmpsize */
+			unzip_skip(3 * 4);
+			continue;
+		}
+#endif
+		if (magic != ZIP_FILEHEADER_MAGIC)
+			bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
+
+		/* Read the file header */
+		xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN);
+		FIX_ENDIANNESS_ZIP(zip_header);
+		if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) {
+			bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method);
+		}
+#if !ENABLE_DESKTOP
+		if (zip_header.formatted.zip_flags & SWAP_LE16(0x0009)) {
+			bb_error_msg_and_die("zip flags 1 and 8 are not supported");
+		}
+#else
+		if (zip_header.formatted.zip_flags & SWAP_LE16(0x0001)) {
+			/* 0x0001 - encrypted */
+			bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
+		}
+
+		{
+			cdf_header_t cdf_header;
+			cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
+			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 */
+				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 */
+				dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
+			}
+		}
+#endif
+
+		/* Read filename */
+		free(dst_fn);
+		dst_fn = xzalloc(zip_header.formatted.filename_len + 1);
+		xread(zip_fd, dst_fn, zip_header.formatted.filename_len);
+
+		/* Skip extra header bytes */
+		unzip_skip(zip_header.formatted.extra_len);
+
+		/* Filter zip entries */
+		if (find_list_entry(zreject, dst_fn)
+		 || (zaccept && !find_list_entry(zaccept, dst_fn))
+		) { /* Skip entry */
+			i = 'n';
+
+		} else { /* Extract entry */
+			if (listing) { /* List entry */
+				unsigned dostime = zip_header.formatted.modtime | (zip_header.formatted.moddate << 16);
+				if (!verbose) {
+					//      "  Length     Date   Time    Name\n"
+					//      " --------    ----   ----    ----"
+					printf(       "%9u  %02u-%02u-%02u %02u:%02u   %s\n",
+						(unsigned)zip_header.formatted.ucmpsize,
+						(dostime & 0x01e00000) >> 21,
+						(dostime & 0x001f0000) >> 16,
+						(((dostime & 0xfe000000) >> 25) + 1980) % 100,
+						(dostime & 0x0000f800) >> 11,
+						(dostime & 0x000007e0) >> 5,
+						dst_fn);
+					total_usize += zip_header.formatted.ucmpsize;
+				} else {
+					unsigned long percents = zip_header.formatted.ucmpsize - zip_header.formatted.cmpsize;
+					percents = percents * 100;
+					if (zip_header.formatted.ucmpsize)
+						percents /= zip_header.formatted.ucmpsize;
+					//      " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
+					//      "--------  ------  ------- -----   ----   ----   ------    ----"
+					printf(      "%8u  Defl:N"    "%9u%4u%%  %02u-%02u-%02u %02u:%02u  %08x  %s\n",
+						(unsigned)zip_header.formatted.ucmpsize,
+						(unsigned)zip_header.formatted.cmpsize,
+						(unsigned)percents,
+						(dostime & 0x01e00000) >> 21,
+						(dostime & 0x001f0000) >> 16,
+						(((dostime & 0xfe000000) >> 25) + 1980) % 100,
+						(dostime & 0x0000f800) >> 11,
+						(dostime & 0x000007e0) >> 5,
+						zip_header.formatted.crc32,
+						dst_fn);
+					total_usize += zip_header.formatted.ucmpsize;
+					total_size += zip_header.formatted.cmpsize;
+				}
+				i = 'n';
+			} else if (dst_fd == STDOUT_FILENO) { /* Extracting to STDOUT */
+				i = -1;
+			} else if (last_char_is(dst_fn, '/')) { /* Extract directory */
+				if (stat(dst_fn, &stat_buf) == -1) {
+					if (errno != ENOENT) {
+						bb_perror_msg_and_die("can't stat '%s'", dst_fn);
+					}
+					if (!quiet) {
+						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");
+					}
+				} else {
+					if (!S_ISDIR(stat_buf.st_mode)) {
+						bb_error_msg_and_die("'%s' exists but is not directory", dst_fn);
+					}
+				}
+				i = 'n';
+
+			} else {  /* Extract file */
+ check_file:
+				if (stat(dst_fn, &stat_buf) == -1) { /* File does not exist */
+					if (errno != ENOENT) {
+						bb_perror_msg_and_die("can't stat '%s'", dst_fn);
+					}
+					i = 'y';
+				} else { /* File already exists */
+					if (overwrite == O_NEVER) {
+						i = 'n';
+					} else if (S_ISREG(stat_buf.st_mode)) { /* File is regular file */
+						if (overwrite == O_ALWAYS) {
+							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");
+							}
+							i = key_buf[0];
+						}
+					} else { /* File is not regular file */
+						bb_error_msg_and_die("'%s' exists but is not regular file", dst_fn);
+					}
+				}
+			}
+		}
+
+		switch (i) {
+		case 'A':
+			overwrite = O_ALWAYS;
+		case 'y': /* Open file and fall into unzip */
+			unzip_create_leading_dirs(dst_fn);
+#if ENABLE_DESKTOP
+			dst_fd = xopen3(dst_fn, O_WRONLY | O_CREAT | O_TRUNC, file_mode);
+#else
+			dst_fd = xopen(dst_fn, O_WRONLY | O_CREAT | O_TRUNC);
+#endif
+		case -1: /* Unzip */
+			if (!quiet) {
+				printf("  inflating: %s\n", dst_fn);
+			}
+			unzip_extract(&zip_header, dst_fd);
+			if (dst_fd != STDOUT_FILENO) {
+				/* closing STDOUT is potentially bad for future business */
+				close(dst_fd);
+			}
+			break;
+
+		case 'N':
+			overwrite = O_NEVER;
+		case 'n':
+			/* Skip entry data */
+			unzip_skip(zip_header.formatted.cmpsize);
+			break;
+
+		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");
+			}
+			free(dst_fn);
+			dst_fn = xstrdup(key_buf);
+			chomp(dst_fn);
+			goto check_file;
+
+		default:
+			printf("error: invalid response [%c]\n", (char)i);
+			goto check_file;
+		}
+
+		total_entries++;
+	}
+
+	if (listing && quiet <= 1) {
+		if (!verbose) {
+			//      "  Length     Date   Time    Name\n"
+			//      " --------    ----   ----    ----"
+			printf( " --------                   -------\n"
+				"%9lu"   "                   %u files\n",
+				total_usize, total_entries);
+		} else {
+			unsigned long percents = total_usize - total_size;
+			percents = percents * 100;
+			if (total_usize)
+				percents /= total_usize;
+			//      " Length   Method    Size  Ratio   Date   Time   CRC-32    Name\n"
+			//      "--------  ------  ------- -----   ----   ----   ------    ----"
+			printf( "--------          -------  ---                            -------\n"
+				"%8lu"              "%17lu%4u%%                            %u files\n",
+				total_usize, total_size, (unsigned)percents,
+				total_entries);
+		}
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/configs/TEST_nommu_defconfig b/busybox-1.19.3/configs/TEST_nommu_defconfig
new file mode 100644
index 0000000..905f652
--- /dev/null
+++ b/busybox-1.19.3/configs/TEST_nommu_defconfig
@@ -0,0 +1,926 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.16.0
+# Wed Jan 27 21:01:26 2010
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+CONFIG_DESKTOP=y
+CONFIG_EXTRA_COMPAT=y
+CONFIG_INCLUDE_SUSv2=y
+# 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=y
+CONFIG_FEATURE_VERBOSE_USAGE=y
+CONFIG_FEATURE_COMPRESS_USAGE=y
+CONFIG_FEATURE_INSTALLER=y
+# CONFIG_LOCALE_SUPPORT is not set
+# CONFIG_UNICODE_SUPPORT is not set
+# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
+CONFIG_LONG_OPTS=y
+CONFIG_FEATURE_DEVPTS=y
+# CONFIG_FEATURE_CLEAN_UP is not set
+CONFIG_FEATURE_PIDFILE=y
+CONFIG_FEATURE_SUID=y
+CONFIG_FEATURE_SUID_CONFIG=y
+CONFIG_FEATURE_SUID_CONFIG_QUIET=y
+CONFIG_SELINUX=y
+CONFIG_FEATURE_PREFER_APPLETS=y
+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=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=""
+
+#
+# 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=y
+CONFIG_FEATURE_ETC_NETWORKS=y
+CONFIG_FEATURE_EDITING=y
+CONFIG_FEATURE_EDITING_MAX_LEN=1024
+CONFIG_FEATURE_EDITING_VI=y
+CONFIG_FEATURE_EDITING_HISTORY=15
+# 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_NON_POSIX_CP=y
+CONFIG_FEATURE_VERBOSE_CP_MESSAGE=y
+CONFIG_FEATURE_COPYBUF_KB=4
+CONFIG_MONOTONIC_SYSCALL=y
+CONFIG_IOCTL_HEX2STR_ERROR=y
+CONFIG_FEATURE_HWIB=y
+
+#
+# Applets
+#
+
+#
+# Archival Utilities
+#
+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_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=y
+CONFIG_GUNZIP=y
+CONFIG_GZIP=y
+CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
+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=y
+CONFIG_FEATURE_TAR_UNAME_GNAME=y
+CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
+CONFIG_UNCOMPRESS=y
+CONFIG_UNLZMA=y
+CONFIG_FEATURE_LZMA_FAST=y
+CONFIG_UNZIP=y
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAL=y
+CONFIG_CAT=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_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+CONFIG_FEATURE_DATE_COMPAT=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=y
+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_ID=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=y
+CONFIG_FEATURE_STAT_FORMAT=y
+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_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=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=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_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+CONFIG_KBD_MODE=y
+CONFIG_LOADFONT=y
+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=y
+CONFIG_SETFONT=y
+CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y
+CONFIG_DEFAULT_SETFONT_DIR=""
+CONFIG_SETKEYCODES=y
+CONFIG_SETLOGCONS=y
+CONFIG_SHOWKEY=y
+
+#
+# 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_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_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_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=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=y
+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_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
+CONFIG_FEATURE_KILL_DELAY=1
+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=y
+
+#
+# Login/Password Management Utilities
+#
+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=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_FIRST_SYSTEM_ID=100
+CONFIG_LAST_SYSTEM_ID=999
+CONFIG_DELUSER=y
+CONFIG_GETTY=y
+CONFIG_FEATURE_UTMP=y
+CONFIG_FEATURE_WTMP=y
+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=y
+CONFIG_LSATTR=y
+
+#
+# Linux Module Utilities
+#
+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=y
+# 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_ACPID is not set
+# CONFIG_FEATURE_ACPID_COMPAT is not set
+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_FDISK=y
+CONFIG_FDISK_SUPPORT_LARGE_DISKS=y
+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_FINDFS=y
+CONFIG_FREERAMDISK=y
+CONFIG_FSCK_MINIX=y
+CONFIG_MKFS_EXT2=y
+CONFIG_MKFS_MINIX=y
+
+#
+# Minix filesystem support
+#
+CONFIG_FEATURE_MINIX2=y
+CONFIG_MKFS_REISER=y
+CONFIG_MKFS_VFAT=y
+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_LOSETUP=y
+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_EXEC=y
+CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
+CONFIG_MKSWAP=y
+CONFIG_FEATURE_MKSWAP_UUID=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_VOLUMEID=y
+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_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_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_SWITCH_ROOT=y
+CONFIG_UMOUNT=y
+CONFIG_FEATURE_UMOUNT_ALL=y
+
+#
+# Common options for mount/umount
+#
+CONFIG_FEATURE_MOUNT_LOOP=y
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Miscellaneous Utilities
+#
+CONFIG_ADJTIMEX=y
+CONFIG_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=y
+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=y
+CONFIG_FEATURE_EJECT_SCSI=y
+CONFIG_FBSPLASH=y
+CONFIG_FLASHCP=y
+# 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=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=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
+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=y
+CONFIG_MOUNTPOINT=y
+CONFIG_MT=y
+CONFIG_RAIDAUTORUN=y
+CONFIG_READAHEAD=y
+CONFIG_RUNLEVEL=y
+CONFIG_RX=y
+CONFIG_SETSID=y
+CONFIG_STRINGS=y
+CONFIG_TASKSET=y
+CONFIG_FEATURE_TASKSET_FANCY=y
+CONFIG_TIME=y
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
+CONFIG_WALL=y
+CONFIG_WATCHDOG=y
+
+#
+# Networking Utilities
+#
+CONFIG_FEATURE_IPV6=y
+CONFIG_FEATURE_UNIX_LOCAL=y
+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_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_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_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=y
+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=y
+CONFIG_FEATURE_IFUPDOWN_MAPPING=y
+CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP=y
+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_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=y
+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=y
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+CONFIG_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+CONFIG_NSLOOKUP=y
+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_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
+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_TRACEROUTE=y
+CONFIG_TRACEROUTE6=y
+CONFIG_FEATURE_TRACEROUTE_VERBOSE=y
+CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE=y
+CONFIG_FEATURE_TRACEROUTE_USE_ICMP=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_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="-R -n"
+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_TCPSVD=y
+CONFIG_TUNCTL=y
+CONFIG_FEATURE_TUNCTL_UG=y
+CONFIG_UDPSVD=y
+
+#
+# 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_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+CONFIG_NMETER=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=y
+CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS=y
+CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS=y
+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=y
+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_SVLOGD=y
+CONFIG_CHPST=y
+CONFIG_SETUIDGID=y
+CONFIG_ENVUIDGID=y
+CONFIG_ENVDIR=y
+CONFIG_SOFTLIMIT=y
+
+#
+# SELinux Utilities
+#
+CONFIG_CHCON=y
+CONFIG_FEATURE_CHCON_LONG_OPTIONS=y
+CONFIG_GETENFORCE=y
+CONFIG_GETSEBOOL=y
+CONFIG_LOAD_POLICY=y
+CONFIG_MATCHPATHCON=y
+CONFIG_RESTORECON=y
+CONFIG_RUNCON=y
+CONFIG_FEATURE_RUNCON_LONG_OPTIONS=y
+CONFIG_SELINUXENABLED=y
+CONFIG_SETENFORCE=y
+CONFIG_SETFILES=y
+CONFIG_FEATURE_SETFILES_CHECK_OPTION=y
+CONFIG_SETSEBOOL=y
+CONFIG_SESTATUS=y
+
+#
+# Shells
+#
+# CONFIG_FEATURE_SH_IS_ASH is not set
+CONFIG_FEATURE_SH_IS_HUSH=y
+# CONFIG_FEATURE_SH_IS_NONE is not set
+# 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=y
+CONFIG_HUSH_BASH_COMPAT=y
+CONFIG_HUSH_HELP=y
+CONFIG_HUSH_INTERACTIVE=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_EXPORT_N=y
+CONFIG_HUSH_RANDOM_SUPPORT=y
+CONFIG_MSH=y
+CONFIG_SH_MATH_SUPPORT=y
+CONFIG_SH_MATH_SUPPORT_64=y
+CONFIG_FEATURE_SH_EXTRA_QUIET=y
+CONFIG_FEATURE_SH_STANDALONE=y
+CONFIG_FEATURE_SH_NOFORK=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_IPC_SYSLOG=y
+CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=16
+CONFIG_LOGREAD=y
+CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING=y
+CONFIG_KLOGD=y
+CONFIG_LOGGER=y
diff --git a/busybox-1.19.3/configs/TEST_noprintf_defconfig b/busybox-1.19.3/configs/TEST_noprintf_defconfig
new file mode 100644
index 0000000..b72e128
--- /dev/null
+++ b/busybox-1.19.3/configs/TEST_noprintf_defconfig
@@ -0,0 +1,927 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.17.0.git
+# Mon Jun  7 13:37:55 2010
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+CONFIG_DESKTOP=y
+CONFIG_EXTRA_COMPAT=y
+CONFIG_INCLUDE_SUSv2=y
+# 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=y
+CONFIG_FEATURE_VERBOSE_USAGE=y
+# CONFIG_FEATURE_COMPRESS_USAGE is not set
+# CONFIG_FEATURE_INSTALLER 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_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=65535
+CONFIG_UNICODE_COMBINING_WCHARS=y
+CONFIG_UNICODE_WIDE_WCHARS=y
+CONFIG_UNICODE_BIDI_SUPPORT=y
+# CONFIG_UNICODE_NEUTRAL_TABLE is not set
+CONFIG_UNICODE_PRESERVE_BROKEN=y
+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_PIDFILE=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
+# CONFIG_FEATURE_PREFER_APPLETS is not set
+CONFIG_BUSYBOX_EXEC_PATH="/proc/self/exe"
+# CONFIG_FEATURE_SYSLOG is not set
+# CONFIG_FEATURE_HAVE_RPC is not set
+
+#
+# Build Options
+#
+CONFIG_STATIC=y
+# 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="i486-linux-uclibc-"
+CONFIG_EXTRA_CFLAGS=""
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
+# CONFIG_DEBUG_PESSIMIZE is not set
+CONFIG_WERROR=y
+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=y
+# CONFIG_FEATURE_ETC_NETWORKS is not set
+CONFIG_FEATURE_EDITING=y
+CONFIG_FEATURE_EDITING_MAX_LEN=1024
+CONFIG_FEATURE_EDITING_VI=y
+CONFIG_FEATURE_EDITING_HISTORY=15
+# 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_NON_POSIX_CP=y
+# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
+CONFIG_FEATURE_COPYBUF_KB=64
+CONFIG_MONOTONIC_SYSCALL=y
+# CONFIG_IOCTL_HEX2STR_ERROR is not set
+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=y
+# 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_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=y
+# 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=y
+# 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_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=y
+# 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=y
+# 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 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_USE_SIGNALS is not set
+# CONFIG_FEATURE_VI_DOT_CMD is not set
+# 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 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
+
+#
+# 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_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
+# CONFIG_BOOTCHARTD 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_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=y
+# 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_VOLUMEID=y
+# 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_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 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 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
+
+#
+# Miscellaneous Utilities
+#
+# CONFIG_ADJTIMEX is not set
+CONFIG_BBCONFIG=y
+# 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_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_NC is not set
+# CONFIG_NC_SERVER is not set
+# CONFIG_NC_EXTRA 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 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=""
+# 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_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
+# 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_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_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 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=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 is not set
+# CONFIG_LOGGER is not set
diff --git a/busybox-1.19.3/configs/TEST_rh9_defconfig b/busybox-1.19.3/configs/TEST_rh9_defconfig
new file mode 100644
index 0000000..23094e3
--- /dev/null
+++ b/busybox-1.19.3/configs/TEST_rh9_defconfig
@@ -0,0 +1,941 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.17.0.git
+# Fri Apr 16 22:25:22 2010
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+# CONFIG_DESKTOP is not set
+# CONFIG_EXTRA_COMPAT is not set
+CONFIG_INCLUDE_SUSv2=y
+# 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=y
+CONFIG_FEATURE_VERBOSE_USAGE=y
+CONFIG_FEATURE_COMPRESS_USAGE=y
+CONFIG_FEATURE_INSTALLER=y
+CONFIG_LOCALE_SUPPORT=y
+CONFIG_UNICODE_SUPPORT=y
+# CONFIG_UNICODE_USING_LOCALE is not set
+# 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
+# CONFIG_UNICODE_WIDE_WCHARS is not set
+# CONFIG_UNICODE_BIDI_SUPPORT is not set
+# CONFIG_UNICODE_NEUTRAL_TABLE 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_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 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=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
+#
+# 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=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 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 is not set
+CONFIG_IOCTL_HEX2STR_ERROR=y
+# CONFIG_FEATURE_HWIB is not set
+
+#
+# Applets
+#
+
+#
+# Archival Utilities
+#
+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 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_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_UNZIP=y
+
+#
+# Coreutils
+#
+CONFIG_BASENAME=y
+CONFIG_CAL=y
+CONFIG_CAT=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_DATE=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+CONFIG_FEATURE_DATE_COMPAT=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=y
+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_ID=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=y
+CONFIG_FEATURE_STAT_FORMAT=y
+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_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TOUCH=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=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=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_CLEAR=y
+CONFIG_DEALLOCVT=y
+CONFIG_DUMPKMAP=y
+CONFIG_KBD_MODE=y
+CONFIG_LOADFONT=y
+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=y
+CONFIG_SETFONT=y
+CONFIG_FEATURE_SETFONT_TEXTUAL_MAP=y
+CONFIG_DEFAULT_SETFONT_DIR=""
+CONFIG_SETKEYCODES=y
+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
+
+#
+# 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_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 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_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=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_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_HALT=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_MESG=y
+
+#
+# Login/Password Management Utilities
+#
+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=y
+CONFIG_FEATURE_ADDUSER_TO_GROUP=y
+CONFIG_DELGROUP=y
+CONFIG_FEATURE_DEL_USER_FROM_GROUP=y
+# CONFIG_FEATURE_CHECK_NAMES is not set
+CONFIG_ADDUSER=y
+CONFIG_FEATURE_ADDUSER_LONG_OPTIONS=y
+CONFIG_FIRST_SYSTEM_ID=100
+CONFIG_LAST_SYSTEM_ID=999
+CONFIG_DELUSER=y
+CONFIG_GETTY=y
+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=y
+CONFIG_LSATTR=y
+
+#
+# Linux Module Utilities
+#
+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_ACPID is not set
+# CONFIG_FEATURE_ACPID_COMPAT is not set
+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_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_FDISK_ADVANCED=y
+CONFIG_FINDFS=y
+# CONFIG_FLOCK is not set
+CONFIG_FREERAMDISK=y
+CONFIG_FSCK_MINIX=y
+# CONFIG_MKFS_EXT2 is not set
+CONFIG_MKFS_MINIX=y
+
+#
+# Minix filesystem support
+#
+CONFIG_FEATURE_MINIX2=y
+# 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_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=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_EXEC=y
+CONFIG_FEATURE_MDEV_LOAD_FIRMWARE=y
+CONFIG_MKSWAP=y
+CONFIG_FEATURE_MKSWAP_UUID=y
+CONFIG_MORE=y
+CONFIG_FEATURE_USE_TERMIOS=y
+CONFIG_VOLUMEID=y
+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_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=y
+CONFIG_RDATE=y
+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_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 is not set
+# CONFIG_FEATURE_MTAB_SUPPORT is not set
+
+#
+# Miscellaneous Utilities
+#
+CONFIG_ADJTIMEX=y
+# CONFIG_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=y
+CONFIG_FEATURE_EJECT_SCSI=y
+CONFIG_FBSPLASH=y
+# 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=y
+# CONFIG_FEATURE_LAST_SMALL is not set
+CONFIG_FEATURE_LAST_FANCY=y
+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
+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=y
+CONFIG_MOUNTPOINT=y
+CONFIG_MT=y
+CONFIG_RAIDAUTORUN=y
+# CONFIG_READAHEAD 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_TIME=y
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
+CONFIG_WALL=y
+# CONFIG_WATCHDOG is not set
+
+#
+# Networking Utilities
+#
+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=y
+CONFIG_ARPING=y
+CONFIG_BRCTL=y
+CONFIG_FEATURE_BRCTL_FANCY=y
+CONFIG_FEATURE_BRCTL_SHOW=y
+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_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_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 is not set
+CONFIG_IFPLUGD=y
+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=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_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=y
+CONFIG_NAMEIF=y
+CONFIG_FEATURE_NAMEIF_EXTENDED=y
+CONFIG_NC=y
+CONFIG_NC_SERVER=y
+CONFIG_NC_EXTRA=y
+CONFIG_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+CONFIG_NSLOOKUP=y
+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_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
+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=y
+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_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_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="-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_FEATURE_WGET_TIMEOUT=y
+CONFIG_ZCIP=y
+
+#
+# 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_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+CONFIG_NMETER=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 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_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=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_FEATURE_SH_IS_ASH=y
+# CONFIG_FEATURE_SH_IS_HUSH is not set
+# CONFIG_FEATURE_SH_IS_NONE is not set
+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 is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+CONFIG_ASH_RANDOM_SUPPORT=y
+CONFIG_ASH_EXPAND_PRMT=y
+CONFIG_HUSH=y
+CONFIG_HUSH_BASH_COMPAT=y
+CONFIG_HUSH_HELP=y
+CONFIG_HUSH_INTERACTIVE=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_EXPORT_N=y
+CONFIG_HUSH_RANDOM_SUPPORT=y
+CONFIG_MSH=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_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_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=y
+CONFIG_LOGGER=y
diff --git a/busybox-1.19.3/configs/android_defconfig b/busybox-1.19.3/configs/android_defconfig
new file mode 100644
index 0000000..7e5232a
--- /dev/null
+++ b/busybox-1.19.3/configs/android_defconfig
@@ -0,0 +1,1014 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.19.0.git
+# Wed Jun 29 12:01:57 2011
+#
+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_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
+# 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
+# -frerun-cse-after-loop
+# -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/ -DANDROID -DSK_RELEASE -nostdlib -march=armv7-a -msoft-float -mfloat-abi=softfp -mfpu=neon -mthumb -mthumb-interwork -fpic -fno-short-enums -fgcse-after-reload -frerun-cse-after-loop -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_SIZE_VS_SPEED=2
+# 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_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_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_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=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 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 is not set
+# CONFIG_FEATURE_ENV_LONG_OPTIONS is not set
+CONFIG_EXPAND=y
+# CONFIG_FEATURE_EXPAND_LONG_OPTIONS is not set
+# CONFIG_EXPR is not set
+# CONFIG_EXPR_MATH_SUPPORT_64 is not set
+CONFIG_FALSE=y
+CONFIG_FOLD=y
+# CONFIG_FSYNC is not set
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+# CONFIG_HOSTID is not set
+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 is not set
+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=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 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
+# CONFIG_FEATURE_VI_SETOPTS is not set
+# 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 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=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=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_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_REV=y
+# CONFIG_ACPID is not set
+# CONFIG_FEATURE_ACPID_COMPAT is not set
+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_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 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=y
+CONFIG_LSUSB=y
+# 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=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 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=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_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 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=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_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=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 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=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 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=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 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=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_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=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 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=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_FREE is not set
+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 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=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=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
diff --git a/busybox-1.19.3/configs/cygwin_defconfig b/busybox-1.19.3/configs/cygwin_defconfig
new file mode 100644
index 0000000..cc2d643
--- /dev/null
+++ b/busybox-1.19.3/configs/cygwin_defconfig
@@ -0,0 +1,997 @@
+#
+# 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_SIZE_VS_SPEED=2
+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_FEATURE_VI_OPTIMIZE_CURSOR=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
diff --git a/busybox-1.19.3/configs/freebsd_defconfig b/busybox-1.19.3/configs/freebsd_defconfig
new file mode 100644
index 0000000..5f2985b
--- /dev/null
+++ b/busybox-1.19.3/configs/freebsd_defconfig
@@ -0,0 +1,970 @@
+#
+# Automatically generated make config: don't edit
+# Busybox version: 1.18.1
+# Tue Dec 21 19:47:40 2010
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Busybox Settings
+#
+
+#
+# General Configuration
+#
+# CONFIG_DESKTOP is not set
+# CONFIG_EXTRA_COMPAT is not set
+CONFIG_INCLUDE_SUSv2=y
+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
+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=y
+CONFIG_UNICODE_SUPPORT=y
+# CONFIG_UNICODE_USING_LOCALE is not set
+# 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
+# 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_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 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_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+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=30
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+CONFIG_FEATURE_TAB_COMPLETION=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_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=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 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=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 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_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_TEST=y
+CONFIG_FEATURE_TEST_64=y
+CONFIG_TR=y
+CONFIG_FEATURE_TR_CLASSES=y
+CONFIG_FEATURE_TR_EQUIV=y
+# CONFIG_BASE64 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_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 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=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_ID=y
+CONFIG_INSTALL=y
+CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
+CONFIG_LENGTH=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 is not set
+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_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 is not set
+CONFIG_SUM=y
+CONFIG_SYNC=y
+# CONFIG_TAC is not set
+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_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 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=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
+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=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=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 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=y
+# 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=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
+# 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 is not set
+# CONFIG_TUNE2FS is not set
+# 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
+# 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_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=y
+# 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 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 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 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
+# 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=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 is not set
+CONFIG_READPROFILE=y
+# CONFIG_RTCWAKE is not set
+# CONFIG_SCRIPT is not set
+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_UBIATTACH is not set
+# CONFIG_UBIDETACH 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 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=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="/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 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=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
+# 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=y
+# 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=y
+CONFIG_STRINGS=y
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+# CONFIG_TIME is not set
+CONFIG_TIMEOUT=y
+CONFIG_TTYSIZE=y
+CONFIG_VOLNAME=y
+# CONFIG_WALL is not set
+# 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 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=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 is not set
+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 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=y
+CONFIG_FEATURE_IPCALC_FANCY=y
+CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
+# 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=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=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_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_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
+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=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
+
+#
+# Process Utilities
+#
+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=y
+# CONFIG_NMETER is not set
+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_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_UNUSUAL_SYSTEMS is not set
+CONFIG_RENICE=y
+CONFIG_BB_SYSCTL=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
+# 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=y
+# CONFIG_UPTIME is not set
+CONFIG_WATCH=y
+
+#
+# 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=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
+# 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=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 is not set
+# CONFIG_FEATURE_SH_STANDALONE is not set
+# CONFIG_FEATURE_SH_NOFORK is not set
+
+#
+# System Logging Utilities
+#
+CONFIG_SYSLOGD=y
+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=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
diff --git a/busybox-1.19.3/console-tools/Config.src b/busybox-1.19.3/console-tools/Config.src
new file mode 100644
index 0000000..c657044
--- /dev/null
+++ b/busybox-1.19.3/console-tools/Config.src
@@ -0,0 +1,176 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Console Utilities"
+
+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)
+
+config FGCONSOLE
+	bool "fgconsole"
+	default y
+	select PLATFORM_LINUX
+	help
+	  This program prints active (foreground) console number.
+
+config CLEAR
+	bool "clear"
+	default y
+	help
+	  This program clears the terminal screen.
+
+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.
+
+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.
+
+config OPENVT
+	bool "openvt"
+	default y
+	select PLATFORM_LINUX
+	help
+	  This program is used to start a command on an unused
+	  virtual terminal.
+
+config RESET
+	bool "reset"
+	default y
+	help
+	  This program is used to reset the terminal screen, if it
+	  gets messed up.
+
+config RESIZE
+	bool "resize"
+	default y
+	help
+	  This program is used to (re)set the width and height of your current
+	  terminal.
+
+config FEATURE_RESIZE_PRINT
+	bool "Print environment variables"
+	default y
+	depends on RESIZE
+	help
+	  Prints the newly set size (number of columns and rows) of
+	  the terminal.
+	  E.g.:
+	  COLUMNS=80;LINES=44;export COLUMNS LINES;
+
+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.
+
+config FEATURE_SETCONSOLE_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on SETCONSOLE && LONG_OPTS
+	help
+	  Support long options for the setconsole applet.
+
+config SETFONT
+	bool "setfont"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Allows to load console screen map. Useful for i18n.
+
+config FEATURE_SETFONT_TEXTUAL_MAP
+	bool "Support reading textual screen maps"
+	default y
+	depends on SETFONT
+	help
+	  Support reading textual screen maps.
+
+config DEFAULT_SETFONT_DIR
+	string "Default directory for console-tools files"
+	default ""
+	depends on SETFONT
+	help
+	  Directory to use if setfont's params are simple filenames
+	  (not /path/to/file or ./file). Default is "" (no default directory).
+
+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.
+
+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.
+
+comment "Common options for loadfont and setfont"
+	depends on LOADFONT || SETFONT
+
+config FEATURE_LOADFONT_PSF2
+	bool "Support for PSF2 console fonts"
+	default y
+	depends on LOADFONT || SETFONT
+	help
+	  Support PSF2 console fonts.
+
+config FEATURE_LOADFONT_RAW
+	bool "Support for old (raw) console fonts"
+	default y
+	depends on LOADFONT || SETFONT
+	help
+	  Support old (raw) console fonts.
+
+endmenu
diff --git a/busybox-1.19.3/console-tools/Kbuild.src b/busybox-1.19.3/console-tools/Kbuild.src
new file mode 100644
index 0000000..94de9ad
--- /dev/null
+++ b/busybox-1.19.3/console-tools/Kbuild.src
@@ -0,0 +1,25 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_CHVT)		+= chvt.o
+lib-$(CONFIG_FGCONSOLE)		+= fgconsole.o
+lib-$(CONFIG_CLEAR)		+= clear.o
+lib-$(CONFIG_DEALLOCVT)		+= deallocvt.o
+lib-$(CONFIG_DUMPKMAP)		+= dumpkmap.o
+lib-$(CONFIG_SETCONSOLE)	+= setconsole.o
+lib-$(CONFIG_KBD_MODE)		+= kbd_mode.o
+lib-$(CONFIG_LOADFONT)		+= loadfont.o
+lib-$(CONFIG_LOADKMAP)		+= loadkmap.o
+lib-$(CONFIG_OPENVT)		+= openvt.o
+lib-$(CONFIG_RESET)		+= reset.o
+lib-$(CONFIG_RESIZE)		+= resize.o
+lib-$(CONFIG_SETFONT)		+= loadfont.o
+lib-$(CONFIG_SETKEYCODES)	+= setkeycodes.o
+lib-$(CONFIG_SETLOGCONS)	+= setlogcons.o
+lib-$(CONFIG_SHOWKEY)		+= showkey.o
diff --git a/busybox-1.19.3/console-tools/chvt.c b/busybox-1.19.3/console-tools/chvt.c
new file mode 100644
index 0000000..b9c974f
--- /dev/null
+++ b/busybox-1.19.3/console-tools/chvt.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chvt implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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;
+int chvt_main(int argc UNUSED_PARAM, char **argv)
+{
+	int num = xatou_range(single_argv(argv), 1, 63);
+	console_make_active(get_console_fd_or_die(), num);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/clear.c b/busybox-1.19.3/console-tools/clear.c
new file mode 100644
index 0000000..ac22b78
--- /dev/null
+++ b/busybox-1.19.3/console-tools/clear.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini clear implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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;
+int clear_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	/* home; clear to the end of screen */
+	return full_write1_str("\033[H""\033[J") != 6;
+}
diff --git a/busybox-1.19.3/console-tools/deallocvt.c b/busybox-1.19.3/console-tools/deallocvt.c
new file mode 100644
index 0000000..b131c0a
--- /dev/null
+++ b/busybox-1.19.3/console-tools/deallocvt.c
@@ -0,0 +1,38 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Disallocate virtual terminal(s)
+ *
+ * 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 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> */
+enum { VT_DISALLOCATE = 0x5608 }; /* free memory associated to vt */
+
+int deallocvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int deallocvt_main(int argc UNUSED_PARAM, char **argv)
+{
+	/* num = 0 deallocate all unused consoles */
+	int num = 0;
+
+	if (argv[1]) {
+		if (argv[2])
+			bb_show_usage();
+		num = xatou_range(argv[1], 1, 63);
+	}
+
+	/* double cast suppresses "cast to ptr from int of different size" */
+	xioctl(get_console_fd_or_die(), VT_DISALLOCATE, (void *)(ptrdiff_t)num);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/dumpkmap.c b/busybox-1.19.3/console-tools/dumpkmap.c
new file mode 100644
index 0000000..6b923d2
--- /dev/null
+++ b/busybox-1.19.3/console-tools/dumpkmap.c
@@ -0,0 +1,82 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dumpkmap implementation for busybox
+ *
+ * Copyright (C) Arne Bernin <arne@matrix.loopback.org>
+ *
+ * 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> */
+struct kbentry {
+	unsigned char kb_table;
+	unsigned char kb_index;
+	unsigned short kb_value;
+};
+#define KDGKBENT 0x4B46  /* gets one entry in translation table */
+
+/* From <linux/keyboard.h> */
+#define NR_KEYS 128
+#define MAX_NR_KEYMAPS 256
+
+int dumpkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dumpkmap_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct kbentry ke;
+	int i, j, fd;
+	RESERVE_CONFIG_BUFFER(flags, MAX_NR_KEYMAPS);
+
+	/* When user accidentally runs "dumpkmap FILE"
+	 * instead of "dumpkmap >FILE", we'd dump binary stuff to tty.
+	 * Let's prevent it: */
+	if (argv[1])
+		bb_show_usage();
+/*	bb_warn_ignoring_args(argv[1]);*/
+
+	fd = get_console_fd_or_die();
+
+	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);
+	memset(flags, 0x01, 13);
+	flags[3] = flags[7] = flags[11] = 0;
+
+	/* dump flags */
+	write(STDOUT_FILENO, flags, MAX_NR_KEYMAPS);
+
+	for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+		if (flags[i] == 1) {
+			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)
+				) {
+					write(STDOUT_FILENO, (void*)&ke.kb_value, 2);
+				}
+			}
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(fd);
+		RELEASE_CONFIG_BUFFER(flags);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/fgconsole.c b/busybox-1.19.3/console-tools/fgconsole.c
new file mode 100644
index 0000000..54355be
--- /dev/null
+++ b/busybox-1.19.3/console-tools/fgconsole.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini fgconsole implementation for busybox
+ *
+ * Copyright (C) 2010 by Grigory Batalov <bga@altlinux.org>
+ *
+ * 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> */
+struct vt_stat {
+	unsigned short v_active;        /* active vt */
+	unsigned short v_signal;        /* signal to send */
+	unsigned short v_state;         /* vt bitmask */
+};
+enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
+
+int fgconsole_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fgconsole_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	struct vt_stat vtstat;
+
+	vtstat.v_active = 0;
+	xioctl(get_console_fd_or_die(), VT_GETSTATE, &vtstat);
+	printf("%d\n", vtstat.v_active);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/kbd_mode.c b/busybox-1.19.3/console-tools/kbd_mode.c
new file mode 100644
index 0000000..1385367
--- /dev/null
+++ b/busybox-1.19.3/console-tools/kbd_mode.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini kbd_mode implementation for busybox
+ *
+ * Copyright (C) 2007 Loic Grenie <loic.grenie@gmail.com>
+ *   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 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>
+
+int kbd_mode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int kbd_mode_main(int argc UNUSED_PARAM, char **argv)
+{
+	enum {
+		SCANCODE  = (1 << 0),
+		ASCII     = (1 << 1),
+		MEDIUMRAW = (1 << 2),
+		UNICODE   = (1 << 3),
+	};
+	int fd;
+	unsigned opt;
+	const char *tty_name = CURRENT_TTY;
+
+	opt = getopt32(argv, "sakuC:", &tty_name);
+	fd = xopen_nonblocking(tty_name);
+	opt &= 0xf; /* clear -C bit, see (*) */
+
+	if (!opt) { /* print current setting */
+		const char *mode = "unknown";
+		int m;
+
+		xioctl(fd, KDGKBMODE, &m);
+		if (m == K_RAW)
+			mode = "raw (scancode)";
+		else if (m == K_XLATE)
+			mode = "default (ASCII)";
+		else if (m == K_MEDIUMRAW)
+			mode = "mediumraw (keycode)";
+		else if (m == K_UNICODE)
+			mode = "Unicode (UTF-8)";
+		printf("The keyboard is in %s mode\n", mode);
+	} else {
+		/* here we depend on specific bits assigned to options (*) */
+		opt = opt & UNICODE ? 3 : opt >> 1;
+		/* double cast prevents warnings about widening conversion */
+		xioctl(fd, KDSKBMODE, (void*)(ptrdiff_t)opt);
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/loadfont.c b/busybox-1.19.3/console-tools/loadfont.c
new file mode 100644
index 0000000..9e887f2
--- /dev/null
+++ b/busybox-1.19.3/console-tools/loadfont.c
@@ -0,0 +1,492 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * loadfont.c - Eugene Crosser & Andries Brouwer
+ *
+ * Version 0.96bb
+ *
+ * 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 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>
+
+#ifndef KDFONTOP
+# define KDFONTOP 0x4B72
+struct console_font_op {
+	unsigned op;            /* KD_FONT_OP_* */
+	unsigned flags;         /* KD_FONT_FLAG_* */
+	unsigned width, height;
+	unsigned charcount;
+	unsigned char *data;    /* font data with height fixed to 32 */
+};
+# define KD_FONT_OP_SET          0  /* Set font */
+# define KD_FONT_OP_GET          1  /* Get font */
+# define KD_FONT_OP_SET_DEFAULT  2  /* Set font to default, data points to name / NULL */
+# define KD_FONT_OP_COPY         3  /* Copy from another console */
+# define KD_FONT_FLAG_OLD        0x80000000 /* Invoked via old interface */
+# define KD_FONT_FLAG_DONT_RECALC 1 /* Don't call adjust_height() */
+                                   /* (Used internally for PIO_FONT support) */
+#endif /* KDFONTOP */
+
+
+enum {
+	PSF1_MAGIC0 = 0x36,
+	PSF1_MAGIC1 = 0x04,
+	PSF1_MODE512 = 0x01,
+	PSF1_MODEHASTAB = 0x02,
+	PSF1_MODEHASSEQ = 0x04,
+	PSF1_MAXMODE = 0x05,
+	PSF1_STARTSEQ = 0xfffe,
+	PSF1_SEPARATOR = 0xffff,
+};
+
+struct psf1_header {
+	unsigned char magic[2];         /* Magic number */
+	unsigned char mode;             /* PSF font mode */
+	unsigned char charsize;         /* Character size */
+};
+
+#define psf1h(x) ((struct psf1_header*)(x))
+
+#define PSF1_MAGIC_OK(x) ( \
+     (x)->magic[0] == PSF1_MAGIC0 \
+  && (x)->magic[1] == PSF1_MAGIC1 \
+)
+
+#if ENABLE_FEATURE_LOADFONT_PSF2
+enum {
+	PSF2_MAGIC0 = 0x72,
+	PSF2_MAGIC1 = 0xb5,
+	PSF2_MAGIC2 = 0x4a,
+	PSF2_MAGIC3 = 0x86,
+	PSF2_HAS_UNICODE_TABLE = 0x01,
+	PSF2_MAXVERSION = 0,
+	PSF2_STARTSEQ = 0xfe,
+	PSF2_SEPARATOR = 0xff
+};
+
+struct psf2_header {
+	unsigned char magic[4];
+	unsigned int version;
+	unsigned int headersize;    /* offset of bitmaps in file */
+	unsigned int flags;
+	unsigned int length;        /* number of glyphs */
+	unsigned int charsize;      /* number of bytes for each character */
+	unsigned int height;        /* max dimensions of glyphs */
+	unsigned int width;         /* charsize = height * ((width + 7) / 8) */
+};
+
+#define psf2h(x) ((struct psf2_header*)(x))
+
+#define PSF2_MAGIC_OK(x) ( \
+     (x)->magic[0] == PSF2_MAGIC0 \
+  && (x)->magic[1] == PSF2_MAGIC1 \
+  && (x)->magic[2] == PSF2_MAGIC2 \
+  && (x)->magic[3] == PSF2_MAGIC3 \
+)
+#endif /* ENABLE_FEATURE_LOADFONT_PSF2 */
+
+
+static void do_loadfont(int fd, unsigned char *inbuf, int height, int width, int charsize, int fontsize)
+{
+	unsigned char *buf;
+	int charwidth = 32 * ((width+7)/8);
+	int i;
+
+	if (height < 1 || height > 32 || width < 1 || width > 32)
+		bb_error_msg_and_die("bad character size %dx%d", height, width);
+
+	buf = xzalloc(charwidth * ((fontsize < 128) ? 128 : fontsize));
+	for (i = 0; i < fontsize; i++)
+		memcpy(buf + (i*charwidth), inbuf + (i*charsize), charsize);
+
+	{ /* KDFONTOP */
+		struct console_font_op cfo;
+		cfo.op = KD_FONT_OP_SET;
+		cfo.flags = 0;
+		cfo.width = width;
+		cfo.height = height;
+		cfo.charcount = fontsize;
+		cfo.data = buf;
+		xioctl(fd, KDFONTOP, &cfo);
+	}
+
+	free(buf);
+}
+
+/*
+ * Format of the Unicode information:
+ *
+ * For each font position <uc>*<seq>*<term>
+ * where <uc> is a 2-byte little endian Unicode value (PSF1)
+ * or an UTF-8 coded value (PSF2),
+ * <seq> = <ss><uc><uc>*, <ss> = psf1 ? 0xFFFE : 0xFE,
+ * <term> = psf1 ? 0xFFFF : 0xFF.
+ * and * denotes zero or more occurrences of the preceding item.
+ *
+ * Semantics:
+ * The leading <uc>* part gives Unicode symbols that are all
+ * represented by this font position. The following sequences
+ * are sequences of Unicode symbols - probably a symbol
+ * together with combining accents - also represented by
+ * this font position.
+ *
+ * Example:
+ * At the font position for a capital A-ring glyph, we
+ * may have:
+ *   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.
+ */
+#if !ENABLE_FEATURE_LOADFONT_PSF2
+#define do_loadtable(fd, inbuf, tailsz, fontsize, psf2) \
+	do_loadtable(fd, inbuf, tailsz, fontsize)
+#endif
+static void do_loadtable(int fd, unsigned char *inbuf, int tailsz, int fontsize, int psf2)
+{
+#if !ENABLE_FEATURE_LOADFONT_PSF2
+/* gcc 4.3.1 code size: */
+# define psf2 0 /* +0 bytes */
+//	const int psf2 = 0; /* +8 bytes */
+//	enum { psf2 = 0 }; /* +13 bytes */
+#endif
+	struct unimapinit advice;
+	struct unimapdesc ud;
+	struct unipair *up;
+	int ct = 0, maxct;
+	int glyph;
+	uint16_t unicode;
+
+	maxct = tailsz; /* more than enough */
+	up = xmalloc(maxct * sizeof(*up));
+
+	for (glyph = 0; glyph < fontsize; glyph++) {
+		while (tailsz > 0) {
+			if (!psf2) { /* PSF1 */
+				unicode = (((uint16_t) inbuf[1]) << 8) + inbuf[0];
+				tailsz -= 2;
+				inbuf += 2;
+				if (unicode == PSF1_SEPARATOR)
+					break;
+			} else { /* PSF2 */
+#if ENABLE_FEATURE_LOADFONT_PSF2
+				--tailsz;
+				unicode = *inbuf++;
+				if (unicode == PSF2_SEPARATOR) {
+					break;
+				} else if (unicode == PSF2_STARTSEQ) {
+					bb_error_msg_and_die("unicode sequences not implemented");
+				} else if (unicode >= 0xC0) {
+					if (unicode >= 0xFC)
+						unicode &= 0x01, maxct = 5;
+					else if (unicode >= 0xF8)
+						unicode &= 0x03, maxct = 4;
+					else if (unicode >= 0xF0)
+						unicode &= 0x07, maxct = 3;
+					else if (unicode >= 0xE0)
+						unicode &= 0x0F, maxct = 2;
+					else
+						unicode &= 0x1F, maxct = 1;
+					do {
+						if (tailsz <= 0 || *inbuf < 0x80 || *inbuf > 0xBF)
+							bb_error_msg_and_die("illegal UTF-8 character");
+						--tailsz;
+						unicode = (unicode << 6) + (*inbuf++ & 0x3F);
+					} while (--maxct > 0);
+				} else if (unicode >= 0x80) {
+					bb_error_msg_and_die("illegal UTF-8 character");
+				}
+#else
+				return;
+#endif
+			}
+			up[ct].unicode = unicode;
+			up[ct].fontpos = glyph;
+			ct++;
+		}
+	}
+
+	/* Note: after PIO_UNIMAPCLR and before PIO_UNIMAP
+	   this printf did not work on many kernels */
+
+	advice.advised_hashsize = 0;
+	advice.advised_hashstep = 0;
+	advice.advised_hashlevel = 0;
+	xioctl(fd, PIO_UNIMAPCLR, &advice);
+	ud.entry_ct = ct;
+	ud.entries = up;
+	xioctl(fd, PIO_UNIMAP, &ud);
+#undef psf2
+}
+
+static void do_load(int fd, unsigned char *buffer, size_t len)
+{
+	int height;
+	int width = 8;
+	int charsize;
+	int fontsize = 256;
+	int has_table = 0;
+	unsigned char *font = buffer;
+	unsigned char *table;
+
+	if (len >= sizeof(struct psf1_header) && PSF1_MAGIC_OK(psf1h(buffer))) {
+		if (psf1h(buffer)->mode > PSF1_MAXMODE)
+			bb_error_msg_and_die("unsupported psf file mode");
+		if (psf1h(buffer)->mode & PSF1_MODE512)
+			fontsize = 512;
+		if (psf1h(buffer)->mode & PSF1_MODEHASTAB)
+			has_table = 1;
+		height = charsize = psf1h(buffer)->charsize;
+		font += sizeof(struct psf1_header);
+	} else
+#if ENABLE_FEATURE_LOADFONT_PSF2
+	if (len >= sizeof(struct psf2_header) && PSF2_MAGIC_OK(psf2h(buffer))) {
+		if (psf2h(buffer)->version > PSF2_MAXVERSION)
+			bb_error_msg_and_die("unsupported psf file version");
+		fontsize = psf2h(buffer)->length;
+		if (psf2h(buffer)->flags & PSF2_HAS_UNICODE_TABLE)
+			has_table = 2;
+		charsize = psf2h(buffer)->charsize;
+		height = psf2h(buffer)->height;
+		width = psf2h(buffer)->width;
+		font += psf2h(buffer)->headersize;
+	} else
+#endif
+#if ENABLE_FEATURE_LOADFONT_RAW
+	if (len == 9780) {  /* file with three code pages? */
+		charsize = height = 16;
+		font += 40;
+	} else if ((len & 0377) == 0) {  /* bare font */
+		charsize = height = len / 256;
+	} else
+#endif
+	{
+		bb_error_msg_and_die("input file: bad length or unsupported font type");
+	}
+
+#if !defined(PIO_FONTX) || defined(__sparc__)
+	if (fontsize != 256)
+		bb_error_msg_and_die("only fontsize 256 supported");
+#endif
+
+	table = font + fontsize * charsize;
+	buffer += len;
+
+	if (table > buffer || (!has_table && table != buffer))
+		bb_error_msg_and_die("input file: bad length");
+
+	do_loadfont(fd, font, height, width, charsize, fontsize);
+
+	if (has_table)
+		do_loadtable(fd, table, buffer - table, fontsize, has_table - 1);
+}
+
+
+#if ENABLE_LOADFONT
+int loadfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int loadfont_main(int argc UNUSED_PARAM, char **argv)
+{
+	size_t len;
+	unsigned char *buffer;
+
+	// no arguments allowed!
+	opt_complementary = "=0";
+	getopt32(argv, "");
+
+	/*
+	 * We used to look at the length of the input file
+	 * with stat(); now that we accept compressed files,
+	 * just read the entire file.
+	 */
+	len = 32*1024; // can't be larger
+	buffer = xmalloc_read(STDIN_FILENO, &len);
+	// xmalloc_open_zipped_read_close(filename, &len);
+	if (!buffer)
+		bb_perror_msg_and_die("error reading input font");
+	do_load(get_console_fd_or_die(), buffer, len);
+
+	return EXIT_SUCCESS;
+}
+#endif
+
+#if ENABLE_SETFONT
+
+/*
+kbd-1.12:
+
+setfont [-O font+umap.orig] [-o font.orig] [-om cmap.orig]
+[-ou umap.orig] [-N] [font.new ...] [-m cmap] [-u umap] [-C console]
+[-hNN] [-v] [-V]
+
+-h NN  Override font height
+-o file
+       Save previous font in file
+-O file
+       Save previous font and Unicode map in file
+-om file
+       Store console map in file
+-ou file
+       Save previous Unicode map in file
+-m file
+       Load console map or Unicode console map from file
+-u file
+       Load Unicode table describing the font from file
+       Example:
+       # cp866
+       0x00-0x7f       idem
+       #
+       0x80    U+0410  # CYRILLIC CAPITAL LETTER A
+       0x81    U+0411  # CYRILLIC CAPITAL LETTER BE
+       0x82    U+0412  # CYRILLIC CAPITAL LETTER VE
+-C console
+       Set the font for the indicated console
+-v     Verbose
+-V     Version
+*/
+
+#if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
+static int ctoi(char *s)
+{
+	if (s[0] == '\'' && s[1] != '\0' && s[2] == '\'' && s[3] == '\0')
+		return s[1];
+	// U+ means 0x
+	if (s[0] == 'U' && s[1] == '+') {
+		s[0] = '0';
+		s[1] = 'x';
+	}
+	if (!isdigit(s[0]))
+		return -1;
+	return xstrtoul(s, 0);
+}
+#endif
+
+int setfont_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setfont_main(int argc UNUSED_PARAM, char **argv)
+{
+	size_t len;
+	unsigned opts;
+	int fd;
+	unsigned char *buffer;
+	char *mapfilename;
+	const char *tty_name = CURRENT_TTY;
+
+	opt_complementary = "=1";
+	opts = getopt32(argv, "m:C:", &mapfilename, &tty_name);
+	argv += optind;
+
+	fd = xopen_nonblocking(tty_name);
+
+	if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
+		if (*argv[0] != '/') {
+			// goto default fonts location. don't die if doesn't exist
+			chdir(CONFIG_DEFAULT_SETFONT_DIR "/consolefonts");
+		}
+	}
+	// load font
+	len = 32*1024; // can't be larger
+	buffer = xmalloc_open_zipped_read_close(*argv, &len);
+	if (!buffer)
+		bb_simple_perror_msg_and_die(*argv);
+	do_load(fd, buffer, len);
+
+	// load the screen map, if any
+	if (opts & 1) { // -m
+		unsigned mode = PIO_SCRNMAP;
+		void *map;
+
+		if (sizeof(CONFIG_DEFAULT_SETFONT_DIR) > 1) { // if not ""
+			if (mapfilename[0] != '/') {
+				// goto default keymaps location
+				chdir(CONFIG_DEFAULT_SETFONT_DIR "/consoletrans");
+			}
+		}
+		// fetch keymap
+		map = xmalloc_open_zipped_read_close(mapfilename, &len);
+		if (!map)
+			bb_simple_perror_msg_and_die(mapfilename);
+		// file size is 256 or 512 bytes? -> assume binary map
+		if (len == E_TABSZ || len == 2*E_TABSZ) {
+			if (len == 2*E_TABSZ)
+				mode = PIO_UNISCRNMAP;
+		}
+#if ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
+		// assume textual Unicode console maps:
+		// 0x00 U+0000  #  NULL (NUL)
+		// 0x01 U+0001  #  START OF HEADING (SOH)
+		// 0x02 U+0002  #  START OF TEXT (STX)
+		// 0x03 U+0003  #  END OF TEXT (ETX)
+		else {
+			int i;
+			char *token[2];
+			parser_t *parser;
+
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(map);
+			map = xmalloc(E_TABSZ * sizeof(unsigned short));
+
+#define unicodes ((unsigned short *)map)
+			// fill vanilla map
+			for (i = 0; i < E_TABSZ; i++)
+				unicodes[i] = 0xf000 + i;
+
+			parser = config_open(mapfilename);
+			while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL | PARSE_MIN_DIE)) {
+				// parse code/value pair
+				int a = ctoi(token[0]);
+				int b = ctoi(token[1]);
+				if (a < 0 || a >= E_TABSZ
+				 || b < 0 || b > 65535
+				) {
+					bb_error_msg_and_die("map format");
+				}
+				// patch map
+				unicodes[a] = b;
+				// unicode character is met?
+				if (b > 255)
+					mode = PIO_UNISCRNMAP;
+			}
+			if (ENABLE_FEATURE_CLEAN_UP)
+				config_close(parser);
+
+			if (mode != PIO_UNISCRNMAP) {
+#define asciis ((unsigned char *)map)
+				for (i = 0; i < E_TABSZ; i++)
+					asciis[i] = unicodes[i];
+#undef asciis
+			}
+#undef unicodes
+		}
+#endif // ENABLE_FEATURE_SETFONT_TEXTUAL_MAP
+
+		// do set screen map
+		xioctl(fd, mode, map);
+
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(map);
+	}
+
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/busybox-1.19.3/console-tools/loadkmap.c b/busybox-1.19.3/console-tools/loadkmap.c
new file mode 100644
index 0000000..bcffe16
--- /dev/null
+++ b/busybox-1.19.3/console-tools/loadkmap.c
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini loadkmap implementation for busybox
+ *
+ * Copyright (C) 1998 Enrique Zanardi <ezanardi@ull.es>
+ *
+ * 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"
+
+/* From <linux/kd.h> */
+struct kbentry {
+	unsigned char kb_table;
+	unsigned char kb_index;
+	unsigned short kb_value;
+};
+/* sets one entry in translation table */
+#define KDSKBENT        0x4B47
+
+/* From <linux/keyboard.h> */
+#define NR_KEYS         128
+#define MAX_NR_KEYMAPS  256
+
+int loadkmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int loadkmap_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct kbentry ke;
+	int i, j, fd;
+	uint16_t ibuff[NR_KEYS];
+/*	const char *tty_name = CURRENT_TTY; */
+	RESERVE_CONFIG_BUFFER(flags, MAX_NR_KEYMAPS);
+
+	/* When user accidentally runs "loadkmap FILE"
+	 * instead of "loadkmap <FILE", we end up waiting for input from tty.
+	 * Let's prevent it: */
+	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);
+	fd = xopen_nonblocking(tty_name);
+*/
+
+	xread(STDIN_FILENO, flags, 7);
+	if (strncmp(flags, BINARY_KEYMAP_MAGIC, 7))
+		bb_error_msg_and_die("not a valid binary keymap");
+
+	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 (ENABLE_FEATURE_CLEAN_UP) {
+		close(fd);
+		RELEASE_CONFIG_BUFFER(flags);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/openvt.c b/busybox-1.19.3/console-tools/openvt.c
new file mode 100644
index 0000000..e523566
--- /dev/null
+++ b/busybox-1.19.3/console-tools/openvt.c
@@ -0,0 +1,188 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  openvt.c - open a vt to run a command.
+ *
+ *  busyboxed by Quy Tonthat <quy@signal3.com>
+ *  hacked by Tito <farmatito@tiscali.it>
+ *
+ * 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"
+
+/* "Standard" openvt's man page (we do not support all of this):
+
+openvt [-c NUM] [-fsulv] [--] [command [args]]
+
+Find the first available VT, and run command on it. Stdio is directed
+to that VT. If no command is specified then $SHELL is used.
+
+-c NUM
+    Use the given VT number, not the first free one.
+-f
+    Force opening a VT: don't try to check if VT is already in use.
+-s
+    Switch to the new VT when starting the command.
+    The VT of the new command will be made the new current VT.
+-u
+    Figure out the owner of the current VT, and run login as that user.
+    Suitable to be called by init. Shouldn't be used with -c or -l.
+-l
+    Make the command a login shell: a "-" is prepended to the argv[0]
+    when command is executed.
+-v
+    Verbose.
+-w
+    Wait for command to complete. If -w and -s are used together,
+    switch back to the controlling terminal when the command completes.
+
+bbox:
+-u: not implemented
+-f: always in effect
+-l: not implemented, ignored
+-v: ignored
+-ws: does NOT switch back
+*/
+
+/* Helper: does this fd understand VT_xxx? */
+static int not_vt_fd(int fd)
+{
+	struct vt_stat vtstat;
+	return ioctl(fd, VT_GETSTATE, &vtstat); /* !0: error, it's not VT fd */
+}
+
+/* Helper: get a fd suitable for VT_xxx */
+static int get_vt_fd(void)
+{
+	int fd;
+
+	/* Do we, by chance, already have it? */
+	for (fd = 0; fd < 3; fd++)
+		if (!not_vt_fd(fd))
+			return fd;
+	fd = open(DEV_CONSOLE, O_RDONLY | O_NONBLOCK);
+	if (fd >= 0 && !not_vt_fd(fd))
+		return fd;
+	bb_error_msg_and_die("can't find open VT");
+}
+
+static int find_free_vtno(void)
+{
+	int vtno;
+	int fd = get_vt_fd();
+
+	errno = 0;
+	/*xfunc_error_retval = 3; - do we need compat? */
+	if (ioctl(fd, VT_OPENQRY, &vtno) != 0 || vtno <= 0)
+		bb_perror_msg_and_die("can't find open VT");
+// Not really needed, grep for DAEMON_ONLY_SANITIZE
+//	if (fd > 2)
+//		close(fd);
+	return vtno;
+}
+
+/* vfork scares gcc, it generates bigger code.
+ * Keep it away from main program.
+ * TODO: move to libbb; or adapt existing libbb's spawn().
+ */
+static NOINLINE void vfork_child(char **argv)
+{
+	if (vfork() == 0) {
+		/* CHILD */
+		/* Try to make this VT our controlling tty */
+		setsid(); /* lose old ctty */
+		ioctl(STDIN_FILENO, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
+		//bb_error_msg("our sid %d", getsid(0));
+		//bb_error_msg("our pgrp %d", getpgrp());
+		//bb_error_msg("VT's sid %d", tcgetsid(0));
+		//bb_error_msg("VT's pgrp %d", tcgetpgrp(0));
+		BB_EXECVP_or_die(argv);
+	}
+}
+
+int openvt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int openvt_main(int argc UNUSED_PARAM, char **argv)
+{
+	char vtname[sizeof(VC_FORMAT) + sizeof(int)*3];
+	struct vt_stat vtstat;
+	char *str_c;
+	int vtno;
+	int flags;
+	enum {
+		OPT_c = (1 << 0),
+		OPT_w = (1 << 1),
+		OPT_s = (1 << 2),
+		OPT_l = (1 << 3),
+		OPT_f = (1 << 4),
+		OPT_v = (1 << 5),
+	};
+
+	/* "+" - stop on first non-option */
+	flags = getopt32(argv, "+c:wslfv", &str_c);
+	argv += optind;
+
+	if (flags & OPT_c) {
+		/* Check for illegal vt number: < 1 or > 63 */
+		vtno = xatou_range(str_c, 1, 63);
+	} else {
+		vtno = find_free_vtno();
+	}
+
+	/* Grab new VT */
+	sprintf(vtname, VC_FORMAT, vtno);
+	/* (Try to) clean up stray open fds above fd 2 */
+	bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS | DAEMON_ONLY_SANITIZE, NULL);
+	close(STDIN_FILENO);
+	/*setsid(); - BAD IDEA: after we exit, child is SIGHUPed... */
+	xopen(vtname, O_RDWR);
+	xioctl(STDIN_FILENO, VT_GETSTATE, &vtstat);
+
+	if (flags & OPT_s) {
+		console_make_active(STDIN_FILENO, vtno);
+	}
+
+	if (!argv[0]) {
+		argv--;
+		argv[0] = (char *) get_shell_name();
+		/*argv[1] = NULL; - already is */
+	}
+
+	xdup2(STDIN_FILENO, STDOUT_FILENO);
+	xdup2(STDIN_FILENO, STDERR_FILENO);
+
+#ifdef BLOAT
+	{
+	/* Handle -l (login shell) option */
+	const char *prog = argv[0];
+	if (flags & OPT_l)
+		argv[0] = xasprintf("-%s", argv[0]);
+	}
+#endif
+
+	vfork_child(argv);
+	if (flags & OPT_w) {
+		/* We have only one child, wait for it */
+		safe_waitpid(-1, NULL, 0); /* loops on EINTR */
+		if (flags & OPT_s) {
+			console_make_active(STDIN_FILENO, vtstat.v_active);
+			// Compat: even with -c N (try to) disallocate:
+			// # /usr/app/kbd-1.12/bin/openvt -f -c 9 -ws sleep 5
+			// openvt: could not deallocate console 9
+			xioctl(STDIN_FILENO, VT_DISALLOCATE, (void*)(ptrdiff_t)vtno);
+		}
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/reset.c b/busybox-1.19.3/console-tools/reset.c
new file mode 100644
index 0000000..65940bd
--- /dev/null
+++ b/busybox-1.19.3/console-tools/reset.c
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini reset implementation for busybox
+ *
+ * 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 source tree.
+ */
+
+/* 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
+
+int reset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	static const char *const args[] = {
+		"stty", "sane", NULL
+	};
+
+	/* no options, no getopt */
+
+	if (/*isatty(STDIN_FILENO) &&*/ isatty(STDOUT_FILENO)) {
+		/* See 'man 4 console_codes' for details:
+		 * "ESC c"        -- Reset
+		 * "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(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
+		return stty_main(2, (char**)args);
+#else
+		execvp("stty", (char**)args);
+#endif
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/resize.c b/busybox-1.19.3/console-tools/resize.c
new file mode 100644
index 0000000..4b0d63a
--- /dev/null
+++ b/busybox-1.19.3/console-tools/resize.c
@@ -0,0 +1,78 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * resize - set terminal width and height.
+ *
+ * Copyright 2006 Bernhard Reutner-Fischer
+ *
+ * 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"
+
+#define old_termios_p ((struct termios*)&bb_common_bufsiz1)
+
+static void
+onintr(int sig UNUSED_PARAM)
+{
+	tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p);
+	_exit(EXIT_FAILURE);
+}
+
+int resize_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int resize_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	struct termios new;
+	struct winsize w = { 0, 0, 0, 0 };
+	int ret;
+
+	/* We use _stderr_ in order to make resize usable
+	 * in shell backticks (those redirect stdout away from tty).
+	 * NB: other versions of resize open "/dev/tty"
+	 * and operate on it - should we do the same?
+	 */
+
+	tcgetattr(STDERR_FILENO, old_termios_p); /* fiddle echo */
+	memcpy(&new, old_termios_p, sizeof(new));
+	new.c_cflag |= (CLOCAL | CREAD);
+	new.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+	bb_signals(0
+		+ (1 << SIGINT)
+		+ (1 << SIGQUIT)
+		+ (1 << SIGTERM)
+		+ (1 << SIGALRM)
+		, onintr);
+	tcsetattr(STDERR_FILENO, TCSANOW, &new);
+
+	/* save_cursor_pos 7
+	 * scroll_whole_screen [r
+	 * put_cursor_waaaay_off [$x;$yH
+	 * get_cursor_pos [6n
+	 * restore_cursor_pos 8
+	 */
+	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");
+
+	/* BTW, other versions of resize recalculate w.ws_xpixel, ws.ws_ypixel
+	 * by calculating character cell HxW from old values
+	 * (gotten via TIOCGWINSZ) and recomputing *pixel values */
+	ret = ioctl(STDERR_FILENO, TIOCSWINSZ, &w);
+
+	tcsetattr(STDERR_FILENO, TCSANOW, old_termios_p);
+
+	if (ENABLE_FEATURE_RESIZE_PRINT)
+		printf("COLUMNS=%d;LINES=%d;export COLUMNS LINES;\n",
+			w.ws_col, w.ws_row);
+
+	return ret;
+}
diff --git a/busybox-1.19.3/console-tools/setconsole.c b/busybox-1.19.3/console-tools/setconsole.c
new file mode 100644
index 0000000..c0051dc
--- /dev/null
+++ b/busybox-1.19.3/console-tools/setconsole.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  setconsole.c - redirect system console output
+ *
+ *  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 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;
+int setconsole_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *device = CURRENT_TTY;
+	bool reset;
+
+#if ENABLE_FEATURE_SETCONSOLE_LONG_OPTIONS
+	static const char setconsole_longopts[] ALIGN1 =
+		"reset\0" No_argument "r"
+		;
+	applet_long_options = setconsole_longopts;
+#endif
+	/* at most one non-option argument */
+	opt_complementary = "?1";
+	reset = getopt32(argv, "r");
+
+	argv += 1 + reset;
+	if (*argv) {
+		device = *argv;
+	} else {
+		if (reset)
+			device = DEV_CONSOLE;
+	}
+
+	xioctl(xopen(device, O_WRONLY), TIOCCONS, NULL);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/setkeycodes.c b/busybox-1.19.3/console-tools/setkeycodes.c
new file mode 100644
index 0000000..a6a7c23
--- /dev/null
+++ b/busybox-1.19.3/console-tools/setkeycodes.c
@@ -0,0 +1,59 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setkeycodes
+ *
+ * Copyright (C) 1994-1998 Andries E. Brouwer <aeb@cwi.nl>
+ *
+ * Adjusted for BusyBox by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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> */
+struct kbkeycode {
+	unsigned scancode, keycode;
+};
+enum {
+	KDSETKEYCODE = 0x4B4D  /* write kernel keycode table entry */
+};
+
+int setkeycodes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setkeycodes_main(int argc, char **argv)
+{
+	int fd;
+	struct kbkeycode a;
+
+	if (!(argc & 1) /* if even */ || argc < 2) {
+		bb_show_usage();
+	}
+
+	fd = get_console_fd_or_die();
+
+	while (argv[1]) {
+		int sc = xstrtoul_range(argv[1], 16, 0, 0xe07f);
+		if (sc >= 0xe000) {
+			sc -= 0xe000;
+			sc += 0x0080;
+		}
+		a.scancode = sc;
+		a.keycode = xatou_range(argv[2], 0, 255);
+		ioctl_or_perror_and_die(fd, KDSETKEYCODE, &a,
+			"can't set SCANCODE %x to KEYCODE %d",
+			sc, a.keycode);
+		argv += 2;
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/setlogcons.c b/busybox-1.19.3/console-tools/setlogcons.c
new file mode 100644
index 0000000..83a8954
--- /dev/null
+++ b/busybox-1.19.3/console-tools/setlogcons.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setlogcons: Send kernel messages to the current console or to console N
+ *
+ * Copyright (C) 2006 by Jan Kiszka <jan.kiszka@web.de>
+ *
+ * Based on setlogcons (kbd-1.12) by Andries E. Brouwer
+ *
+ * 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;
+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) */
+			};
+
+	if (argv[1])
+		arg.subarg = xatou_range(argv[1], 0, 63);
+
+	xioctl(xopen(VC_1, O_RDONLY), TIOCLINUX, &arg);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/console-tools/showkey.c b/busybox-1.19.3/console-tools/showkey.c
new file mode 100644
index 0000000..69b785e
--- /dev/null
+++ b/busybox-1.19.3/console-tools/showkey.c
@@ -0,0 +1,150 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * shows keys pressed. inspired by kbd package
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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>
+
+
+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 INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+// 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)
+{
+	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
+	};
+
+	INIT_G();
+
+	// FIXME: aks are all mutually exclusive
+	getopt32(argv, "aks");
+
+	// prepare for raw mode
+	xget1(&tio, &tio0);
+	// put stdin in raw mode
+	xset1(&tio);
+
+#define press_keys "Press any keys, program terminates %s:\r\n\n"
+
+	if (option_mask32 & OPT_a) {
+		// just read stdin char by char
+		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 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 (!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) {
+				if (option_mask32 & OPT_s) {
+					// show raw scancodes
+					printf("0x%02x ", buf[i++]);
+				} 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
+					) {
+						kc = ((buf[i+1] & 0x7f) << 7) | (buf[i+2] & 0x7f);
+						i += 3;
+					} else {
+						kc = (c & 0x7f);
+						i++;
+					}
+					printf("keycode %3u %s", kc, (c & 0x80) ? "release" : "press");
+				}
+			}
+			puts("\r");
+		}
+
+		// restore keyboard mode
+		xioctl(STDIN_FILENO, KDSKBMODE, (void *)(ptrdiff_t)kbmode);
+	}
+
+	// restore console settings
+	xset1(&tio0);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/Config.src b/busybox-1.19.3/coreutils/Config.src
new file mode 100644
index 0000000..65165d7
--- /dev/null
+++ b/busybox-1.19.3/coreutils/Config.src
@@ -0,0 +1,789 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Coreutils"
+
+INSERT
+
+config CAL
+	bool "cal"
+	default y
+	help
+	  cal is used to display a monthly calender.
+
+config CATV
+	bool "catv"
+	default y
+	help
+	  Display nonprinting characters as escape sequences (like some
+	  implementations' cat -v option).
+
+config CHGRP
+	bool "chgrp"
+	default y
+	help
+	  chgrp is used to change the group ownership of files.
+
+config CHMOD
+	bool "chmod"
+	default y
+	help
+	  chmod is used to change the access permission of files.
+
+config CHOWN
+	bool "chown"
+	default y
+	help
+	  chown is used to change the user and/or group ownership
+	  of files.
+
+config FEATURE_CHOWN_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on CHOWN && LONG_OPTS
+	help
+	  Enable use of long options
+
+config CHROOT
+	bool "chroot"
+	default y
+	help
+	  chroot is used to change the root directory and run a command.
+	  The default command is `/bin/sh'.
+
+config CKSUM
+	bool "cksum"
+	default y
+	help
+	  cksum is used to calculate the CRC32 checksum of a file.
+
+config COMM
+	bool "comm"
+	default y
+	help
+	  comm is used to compare two files line by line and return
+	  a three-column output.
+
+config CP
+	bool "cp"
+	default y
+	help
+	  cp is used to copy files and directories.
+
+config FEATURE_CP_LONG_OPTIONS
+	bool "Enable long options for cp"
+	default y
+	depends on CP && LONG_OPTS
+	help
+	  Enable long options for cp.
+	  Also add support for --parents option.
+
+config CUT
+	bool "cut"
+	default y
+	help
+	  cut is used to print selected parts of lines from
+	  each file to stdout.
+
+config DD
+	bool "dd"
+	default y
+	help
+	  dd copies a file (from standard input to standard output,
+	  by default) using specific input and output blocksizes,
+	  while optionally performing conversions on it.
+
+config FEATURE_DD_SIGNAL_HANDLING
+	bool "Enable DD signal handling for status reporting"
+	default y
+	depends on DD
+	help
+	  Sending a SIGUSR1 signal to a running `dd' process makes it
+	  print to standard error the number of records read and written
+	  so far, then to resume copying.
+
+	  $ dd if=/dev/zero of=/dev/null&
+	  $ pid=$! kill -USR1 $pid; sleep 1; kill $pid
+	  10899206+0 records in
+	  10899206+0 records out
+
+config FEATURE_DD_THIRD_STATUS_LINE
+	bool "Enable the third status line upon signal"
+	default y
+	depends on DD && FEATURE_DD_SIGNAL_HANDLING
+	help
+	  Displays a coreutils-like third status line with transferred bytes,
+	  elapsed time and speed.
+
+config FEATURE_DD_IBS_OBS
+	bool "Enable ibs, obs and conv options"
+	default y
+	depends on DD
+	help
+	  Enables support for writing a certain number of bytes in and out,
+	  at a time, and performing conversions on the data stream.
+
+config DF
+	bool "df"
+	default y
+	help
+	  df reports the amount of disk space used and available
+	  on filesystems.
+
+config FEATURE_DF_FANCY
+	bool "Enable -a, -i, -B"
+	default y
+	depends on DF
+	help
+	  This option enables -a, -i and -B.
+
+	    -a Show all filesystems
+	    -i Inodes
+	    -B <SIZE> Blocksize
+
+config DIRNAME
+	bool "dirname"
+	default y
+	help
+	  dirname is used to strip a non-directory suffix from
+	  a file name.
+
+config DOS2UNIX
+	bool "dos2unix/unix2dos"
+	default y
+	help
+	  dos2unix is used to convert a text file from DOS format to
+	  UNIX format, and vice versa.
+
+config UNIX2DOS
+	bool
+	default y
+	depends on DOS2UNIX
+	help
+	  unix2dos is used to convert a text file from UNIX format to
+	  DOS format, and vice versa.
+
+config DU
+	bool "du (default blocksize of 512 bytes)"
+	default y
+	help
+	  du is used to report the amount of disk space used
+	  for specified files.
+
+config FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+	bool "Use a default blocksize of 1024 bytes (1K)"
+	default y
+	depends on DU
+	help
+	  Use a blocksize of (1K) instead of the default 512b.
+
+config ECHO
+	bool "echo (basic SuSv3 version taking no options)"
+	default y
+	help
+	  echo is used to print a specified string to stdout.
+
+# this entry also appears in shell/Config.in, next to the echo builtin
+config FEATURE_FANCY_ECHO
+	bool "Enable echo options (-n and -e)"
+	default y
+	depends on ECHO || ASH_BUILTIN_ECHO || HUSH
+	help
+	  This adds options (-n and -e) to echo.
+
+config ENV
+	bool "env"
+	default y
+	help
+	  env is used to set an environment variable and run
+	  a command; without options it displays the current
+	  environment.
+
+config FEATURE_ENV_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on ENV && LONG_OPTS
+	help
+	  Support long options for the env applet.
+
+config EXPAND
+	bool "expand"
+	default y
+	help
+	  By default, convert all tabs to spaces.
+
+config FEATURE_EXPAND_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on EXPAND && LONG_OPTS
+	help
+	  Support long options for the expand applet.
+
+config EXPR
+	bool "expr"
+	default y
+	help
+	  expr is used to calculate numbers and print the result
+	  to standard output.
+
+config EXPR_MATH_SUPPORT_64
+	bool "Extend Posix numbers support to 64 bit"
+	default y
+	depends on EXPR
+	help
+	  Enable 64-bit math support in the expr applet. This will make
+	  the applet slightly larger, but will allow computation with very
+	  large numbers.
+
+config FALSE
+	bool "false"
+	default y
+	help
+	  false returns an exit code of FALSE (1).
+
+config FOLD
+	bool "fold"
+	default y
+	help
+	  Wrap text to fit a specific width.
+
+config FSYNC
+	bool "fsync"
+	default y
+	help
+	  fsync is used to flush file-related cached blocks to disk.
+
+config HEAD
+	bool "head"
+	default y
+	help
+	  head is used to print the first specified number of lines
+	  from files.
+
+config FEATURE_FANCY_HEAD
+	bool "Enable head options (-c, -q, and -v)"
+	default y
+	depends on 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 INSTALL
+	bool "install"
+	default y
+	help
+	  Copy files and set attributes.
+
+config FEATURE_INSTALL_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on INSTALL && LONG_OPTS
+	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 LN
+	bool "ln"
+	default y
+	help
+	  ln is used to create hard or soft links between files.
+
+config LOGNAME
+	bool "logname"
+	default y
+	help
+	  logname is used to print the current user's login name.
+
+config LS
+	bool "ls"
+	default y
+	help
+	  ls is used to list the contents of directories.
+
+config FEATURE_LS_FILETYPES
+	bool "Enable filetyping options (-p and -F)"
+	default y
+	depends on LS
+	help
+	  Enable the ls options (-p and -F).
+
+config FEATURE_LS_FOLLOWLINKS
+	bool "Enable symlinks dereferencing (-L)"
+	default y
+	depends on LS
+	help
+	  Enable the ls option (-L).
+
+config FEATURE_LS_RECURSIVE
+	bool "Enable recursion (-R)"
+	default y
+	depends on LS
+	help
+	  Enable the ls option (-R).
+
+config FEATURE_LS_SORTFILES
+	bool "Sort the file names"
+	default y
+	depends on LS
+	help
+	  Allow ls to sort file names alphabetically.
+
+config FEATURE_LS_TIMESTAMPS
+	bool "Show file timestamps"
+	default y
+	depends on LS
+	help
+	  Allow ls to display timestamps for files.
+
+config FEATURE_LS_USERNAME
+	bool "Show username/groupnames"
+	default y
+	depends on LS
+	help
+	  Allow ls to display username/groupname for files.
+
+config FEATURE_LS_COLOR
+	bool "Allow use of color to identify file types"
+	default y
+	depends on LS && LONG_OPTS
+	help
+	  This enables the --color option to ls.
+
+config FEATURE_LS_COLOR_IS_DEFAULT
+	bool "Produce colored ls output by default"
+	default y
+	depends on FEATURE_LS_COLOR
+	help
+	  Saying yes here will turn coloring on by default,
+	  even if no "--color" option is given to the ls command.
+	  This is not recommended, since the colors are not
+	  configurable, and the output may not be legible on
+	  many output screens.
+
+config MD5SUM
+	bool "md5sum"
+	default y
+	help
+	  md5sum is used to print or check MD5 checksums.
+
+config MKDIR
+	bool "mkdir"
+	default y
+	help
+	  mkdir is used to create directories with the specified names.
+
+config FEATURE_MKDIR_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on MKDIR && LONG_OPTS
+	help
+	  Support long options for the mkdir applet.
+
+config MKFIFO
+	bool "mkfifo"
+	default y
+	help
+	  mkfifo is used to create FIFOs (named pipes).
+	  The `mknod' program can also create FIFOs.
+
+config MKNOD
+	bool "mknod"
+	default y
+	help
+	  mknod is used to create FIFOs or block/character special
+	  files with the specified names.
+
+config MV
+	bool "mv"
+	default y
+	help
+	  mv is used to move or rename files or directories.
+
+config FEATURE_MV_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on MV && LONG_OPTS
+	help
+	  Support long options for the mv applet.
+
+config NICE
+	bool "nice"
+	default y
+	help
+	  nice runs a program with modified scheduling priority.
+
+config NOHUP
+	bool "nohup"
+	default y
+	help
+	  run a command immune to hangups, with output to a non-tty.
+
+config OD
+	bool "od"
+	default y
+	help
+	  od is used to dump binary files in octal and other formats.
+
+config PRINTENV
+	bool "printenv"
+	default y
+	help
+	  printenv is used to print all or part of environment.
+
+config PRINTF
+	bool "printf"
+	default y
+	help
+	  printf is used to format and print specified strings.
+	  It's similar to `echo' except it has more options.
+
+config PWD
+	bool "pwd"
+	default y
+	help
+	  pwd is used to print the current directory.
+
+config READLINK
+	bool "readlink"
+	default y
+	help
+	  This program reads a symbolic link and returns the name
+	  of the file it points to
+
+config FEATURE_READLINK_FOLLOW
+	bool "Enable canonicalization by following all symlinks (-f)"
+	default y
+	depends on READLINK
+	help
+	  Enable the readlink option (-f).
+
+config REALPATH
+	bool "realpath"
+	default y
+	help
+	  Return the canonicalized absolute pathname.
+	  This isn't provided by GNU shellutils, but where else does it belong.
+
+config RM
+	bool "rm"
+	default y
+	help
+	  rm is used to remove files or directories.
+
+config RMDIR
+	bool "rmdir"
+	default y
+	help
+	  rmdir is used to remove empty directories.
+
+config FEATURE_RMDIR_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on RMDIR && LONG_OPTS
+	help
+	  Support long options for the rmdir applet, including
+	  --ignore-fail-on-non-empty for compatibility with GNU rmdir.
+
+config SEQ
+	bool "seq"
+	default y
+	help
+	  print a sequence of numbers
+
+config SHA1SUM
+	bool "sha1sum"
+	default y
+	help
+	  Compute and check SHA1 message digest
+
+config SHA256SUM
+	bool "sha256sum"
+	default y
+	help
+	  Compute and check SHA256 message digest
+
+config SHA512SUM
+	bool "sha512sum"
+	default y
+	help
+	  Compute and check SHA512 message digest
+
+config SLEEP
+	bool "sleep"
+	default y
+	help
+	  sleep is used to pause for a specified number of seconds.
+	  It comes in 3 versions:
+	  - small: takes one integer parameter
+	  - fancy: takes multiple integer arguments with suffixes:
+	    sleep 1d 2h 3m 15s
+	  - fancy with fractional numbers:
+	    sleep 2.3s 4.5h sleeps for 16202.3 seconds
+	  Last one is "the most compatible" with coreutils sleep,
+	  but it adds around 1k of code.
+
+config FEATURE_FANCY_SLEEP
+	bool "Enable multiple arguments and s/m/h/d suffixes"
+	default y
+	depends on SLEEP
+	help
+	  Allow sleep to pause for specified minutes, hours, and days.
+
+config FEATURE_FLOAT_SLEEP
+	bool "Enable fractional arguments"
+	default y
+	depends on FEATURE_FANCY_SLEEP
+	help
+	  Allow for fractional numeric parameters.
+
+config SORT
+	bool "sort"
+	default y
+	help
+	  sort is used to sort lines of text in specified files.
+
+config FEATURE_SORT_BIG
+	bool "Full SuSv3 compliant sort (support -ktcsbdfiozgM)"
+	default y
+	depends on SORT
+	help
+	  Without this, sort only supports -r, -u, and an integer version
+	  of -n. Selecting this adds sort keys, floating point support, and
+	  more. This adds a little over 3k to a nonstatic build on x86.
+
+	  The SuSv3 sort standard is available at:
+	  http://www.opengroup.org/onlinepubs/007904975/utilities/sort.html
+
+config SPLIT
+	bool "split"
+	default y
+	help
+	  split a file into pieces.
+
+config FEATURE_SPLIT_FANCY
+	bool "Fancy extensions"
+	default y
+	depends on SPLIT
+	help
+	  Add support for features not required by SUSv3.
+	  Supports additional suffixes 'b' for 512 bytes,
+	  'g' for 1GiB for the -b option.
+
+config STAT
+	bool "stat"
+	default y
+	select PLATFORM_LINUX # statfs()
+	help
+	  display file or filesystem status.
+
+config FEATURE_STAT_FORMAT
+	bool "Enable custom formats (-c)"
+	default y
+	depends on STAT
+	help
+	  Without this, stat will not support the '-c format' option where
+	  users can pass a custom format string for output. This adds about
+	  7k to a nonstatic build on amd64.
+
+config STTY
+	bool "stty"
+	default y
+	help
+	  stty is used to change and print terminal line settings.
+
+config SUM
+	bool "sum"
+	default y
+	help
+	  checksum and count the blocks in a file
+
+config SYNC
+	bool "sync"
+	default y
+	help
+	  sync is used to flush filesystem buffers.
+
+config TAC
+	bool "tac"
+	default y
+	help
+	  tac is used to concatenate and print files in reverse.
+
+config TAIL
+	bool "tail"
+	default y
+	help
+	  tail is used to print the last specified number of lines
+	  from files.
+
+config FEATURE_FANCY_TAIL
+	bool "Enable extra tail options (-q, -s, -v, and -F)"
+	default y
+	depends on TAIL
+	help
+	  The options (-q, -s, and -v) are provided by GNU tail, but
+	  are not specific in the SUSv3 standard.
+
+	    -q      Never output headers giving file names
+	    -s SEC  Wait SEC seconds between reads with -f
+	    -v      Always output headers giving file names
+
+config TEE
+	bool "tee"
+	default y
+	help
+	  tee is used to read from standard input and write
+	  to standard output and files.
+
+config FEATURE_TEE_USE_BLOCK_IO
+	bool "Enable block I/O (larger/faster) instead of byte I/O"
+	default y
+	depends on TEE
+	help
+	  Enable this option for a faster tee, at expense of size.
+
+config TRUE
+	bool "true"
+	default y
+	help
+	  true returns an exit code of TRUE (0).
+
+config TTY
+	bool "tty"
+	default y
+	help
+	  tty is used to print the name of the current terminal to
+	  standard output.
+
+config UNAME
+	bool "uname"
+	default y
+	help
+	  uname is used to print system information.
+
+config UNEXPAND
+	bool "unexpand"
+	default y
+	help
+	  By default, convert only leading sequences of blanks to tabs.
+
+config FEATURE_UNEXPAND_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on UNEXPAND && LONG_OPTS
+	help
+	  Support long options for the unexpand applet.
+
+config UNIQ
+	bool "uniq"
+	default y
+	help
+	  uniq is used to remove duplicate lines from a sorted file.
+
+config USLEEP
+	bool "usleep"
+	default y
+	help
+	  usleep is used to pause for a specified number of microseconds.
+
+config UUDECODE
+	bool "uudecode"
+	default y
+	help
+	  uudecode is used to decode a uuencoded file.
+
+config UUENCODE
+	bool "uuencode"
+	default y
+	help
+	  uuencode is used to uuencode a file.
+
+config WC
+	bool "wc"
+	default y
+	help
+	  wc is used to print the number of bytes, words, and lines,
+	  in specified files.
+
+config FEATURE_WC_LARGE
+	bool "Support very large files in wc"
+	default y
+	depends on WC
+	help
+	  Use "unsigned long long" in wc for counter variables.
+
+config WHOAMI
+	bool "whoami"
+	default y
+	help
+	  whoami is used to print the username of the current
+	  user id (same as id -un).
+
+config YES
+	bool "yes"
+	default y
+	help
+	  yes is used to repeatedly output a specific string, or
+	  the default string `y'.
+
+comment "Common options for cp and mv"
+	depends on CP || MV
+
+config FEATURE_PRESERVE_HARDLINKS
+	bool "Preserve hard links"
+	default y
+	depends on CP || MV
+	help
+	  Allow cp and mv to preserve hard links.
+
+comment "Common options for ls, more and telnet"
+	depends on LS || MORE || TELNET
+
+config FEATURE_AUTOWIDTH
+	bool "Calculate terminal & column widths"
+	default y
+	depends on LS || MORE || TELNET
+	help
+	  This option allows utilities such as 'ls', 'more' and 'telnet'
+	  to determine the width of the screen, which can allow them to
+	  display additional text or avoid wrapping text onto the next line.
+	  If you leave this disabled, your utilities will be especially
+	  primitive and will be unable to determine the current screen width.
+
+comment "Common options for df, du, ls"
+	depends on DF || DU || LS
+
+config FEATURE_HUMAN_READABLE
+	bool "Support for human readable output (example 13k, 23M, 235G)"
+	default y
+	depends on DF || DU || LS
+	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
+
+config FEATURE_MD5_SHA1_SUM_CHECK
+	bool "Enable -c, -s and -w options"
+	default y
+	depends on MD5SUM || SHA1SUM || SHA256SUM || SHA512SUM
+	help
+	  Enabling the -c options allows files to be checked
+	  against pre-calculated hash values.
+
+	  -s and -w are useful options when verifying checksums.
+
+endmenu
diff --git a/busybox-1.19.3/coreutils/Kbuild.src b/busybox-1.19.3/coreutils/Kbuild.src
new file mode 100644
index 0000000..53d88b3
--- /dev/null
+++ b/busybox-1.19.3/coreutils/Kbuild.src
@@ -0,0 +1,86 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+libs-y			+= libcoreutils/
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_CAL)       += cal.o
+lib-$(CONFIG_CATV)      += catv.o
+lib-$(CONFIG_CHGRP)     += chgrp.o chown.o
+lib-$(CONFIG_CHMOD)     += chmod.o
+lib-$(CONFIG_CHOWN)     += chown.o
+lib-$(CONFIG_ADDUSER)   += chown.o # used by adduser
+lib-$(CONFIG_ADDGROUP)  += chown.o # used by adduser
+lib-$(CONFIG_CHROOT)    += chroot.o
+lib-$(CONFIG_CKSUM)     += cksum.o
+lib-$(CONFIG_COMM)      += comm.o
+lib-$(CONFIG_CP)        += cp.o
+lib-$(CONFIG_CUT)       += cut.o
+lib-$(CONFIG_DD)        += dd.o
+lib-$(CONFIG_DF)        += df.o
+lib-$(CONFIG_DIRNAME)   += dirname.o
+lib-$(CONFIG_DOS2UNIX)  += dos2unix.o
+lib-$(CONFIG_DU)        += du.o
+lib-$(CONFIG_ECHO)      += echo.o
+lib-$(CONFIG_ASH)       += echo.o # used by ash
+lib-$(CONFIG_HUSH)      += echo.o # used by hush
+lib-$(CONFIG_ENV)       += env.o
+lib-$(CONFIG_EXPR)      += expr.o
+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_INSTALL)   += install.o
+#lib-$(CONFIG_LENGTH)    += length.o
+lib-$(CONFIG_LN)        += ln.o
+lib-$(CONFIG_LOGNAME)   += logname.o
+lib-$(CONFIG_LS)        += ls.o
+lib-$(CONFIG_FTPD)      += ls.o
+lib-$(CONFIG_MD5SUM)    += md5_sha1_sum.o
+lib-$(CONFIG_MKDIR)     += mkdir.o
+lib-$(CONFIG_MKFIFO)    += mkfifo.o
+lib-$(CONFIG_MKNOD)     += mknod.o
+lib-$(CONFIG_MV)        += mv.o
+lib-$(CONFIG_NICE)      += nice.o
+lib-$(CONFIG_NOHUP)     += nohup.o
+lib-$(CONFIG_OD)        += od.o
+lib-$(CONFIG_PRINTENV)  += printenv.o
+lib-$(CONFIG_PRINTF)    += printf.o
+lib-$(CONFIG_ASH_BUILTIN_PRINTF) += printf.o
+lib-$(CONFIG_PWD)       += pwd.o
+lib-$(CONFIG_READLINK)  += readlink.o
+lib-$(CONFIG_REALPATH)  += realpath.o
+lib-$(CONFIG_RM)        += rm.o
+lib-$(CONFIG_RMDIR)     += rmdir.o
+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_SLEEP)     += sleep.o
+lib-$(CONFIG_SPLIT)     += split.o
+lib-$(CONFIG_SORT)      += sort.o
+lib-$(CONFIG_STAT)      += stat.o
+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_TRUE)      += true.o
+lib-$(CONFIG_TTY)       += tty.o
+lib-$(CONFIG_UNAME)     += uname.o
+lib-$(CONFIG_UNEXPAND)  += expand.o
+lib-$(CONFIG_UNIQ)      += uniq.o
+lib-$(CONFIG_USLEEP)    += usleep.o
+lib-$(CONFIG_UUDECODE)  += uudecode.o
+lib-$(CONFIG_UUENCODE)  += uuencode.o
+lib-$(CONFIG_WC)        += wc.o
+lib-$(CONFIG_WHOAMI)    += whoami.o
+lib-$(CONFIG_YES)       += yes.o
diff --git a/busybox-1.19.3/coreutils/basename.c b/busybox-1.19.3/coreutils/basename.c
new file mode 100644
index 0000000..1f7a137
--- /dev/null
+++ b/busybox-1.19.3/coreutils/basename.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini basename implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Changes:
+ * 1) Now checks for too many args.  Need at least one and at most two.
+ * 2) Don't check for options, as per SUSv3.
+ * 3) Save some space by using strcmp().  Calling strncmp() here was silly.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/basename.html */
+
+//kbuild:lib-$(CONFIG_BASENAME) += basename.o
+
+//config:config BASENAME
+//config:	bool "basename"
+//config:	default y
+//config:	help
+//config:	  basename is used to strip the directory and suffix from filenames,
+//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! */
+
+int basename_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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();
+	}
+
+	/* It should strip slash: /abc/def/ -> def */
+	s = bb_get_last_path_component_strip(*++argv);
+
+	m = strlen(s);
+	if (*++argv) {
+		n = strlen(*argv);
+		if ((m > n) && (strcmp(s+m-n, *argv) == 0)) {
+			m -= n;
+			/*s[m] = '\0'; - redundant */
+		}
+	}
+
+	/* puts(s) will do, but we can do without stdio this way: */
+	s[m++] = '\n';
+	/* NB: != is correct here: */
+	return full_write(STDOUT_FILENO, s, m) != (ssize_t)m;
+}
diff --git a/busybox-1.19.3/coreutils/cal.c b/busybox-1.19.3/coreutils/cal.c
new file mode 100644
index 0000000..b470ad9
--- /dev/null
+++ b/busybox-1.19.3/coreutils/cal.c
@@ -0,0 +1,381 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Calendar implementation for busybox
+ *
+ * See original copyright at the end of this file
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant with -j and -y extensions (from util-linux). */
+/* BB_AUDIT BUG: The output of 'cal -j 1752' is incorrect.  The upstream
+ * BB_AUDIT BUG: version in util-linux seems to be broken as well. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cal.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * 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"
+
+/* We often use "unsigned" intead of "int", it's easier to div on most CPUs */
+
+#define	THURSDAY		4		/* for reformation */
+#define	SATURDAY		6		/* 1 Jan 1 was a Saturday */
+
+#define	FIRST_MISSING_DAY	639787		/* 3 Sep 1752 */
+#define	NUMBER_MISSING_DAYS	11		/* 11 day correction */
+
+#define	MAXDAYS			42		/* max slots in a month array */
+#define	SPACE			-1		/* used in day array */
+
+static const unsigned char days_in_month[] ALIGN1 = {
+	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static const unsigned char sep1752[] ALIGN1 = {
+		 1,	2,	14,	15,	16,
+	17,	18,	19,	20,	21,	22,	23,
+	24,	25,	26,	27,	28,	29,	30
+};
+
+/* Set to 0 or 1 in main */
+#define julian ((unsigned)option_mask32)
+
+/* leap year -- account for Gregorian reformation in 1752 */
+static int leap_year(unsigned yr)
+{
+	if (yr <= 1752)
+		return !(yr % 4);
+	return (!(yr % 4) && (yr % 100)) || !(yr % 400);
+}
+
+/* number of centuries since 1700, not inclusive */
+#define	centuries_since_1700(yr) \
+	((yr) > 1700 ? (yr) / 100 - 17 : 0)
+
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define	quad_centuries_since_1700(yr) \
+	((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
+
+/* number of leap years between year 1 and this year, not inclusive */
+#define	leap_years_since_year_1(yr) \
+	((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
+
+static void center(char *, unsigned, unsigned);
+static void day_array(unsigned, unsigned, unsigned *);
+static void trim_trailing_spaces_and_print(char *);
+
+static void blank_string(char *buf, size_t buflen);
+static char *build_row(char *p, unsigned *dp);
+
+#define	DAY_LEN		3		/* 3 spaces per day */
+#define	J_DAY_LEN	(DAY_LEN + 1)
+#define	WEEK_LEN	20		/* 7 * 3 - one space at the end */
+#define	J_WEEK_LEN	(WEEK_LEN + 7)
+#define	HEAD_SEP	2		/* spaces between day headings */
+
+int cal_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cal_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct tm zero_tm;
+	time_t now;
+	unsigned month, year, flags, i;
+	char *month_names[12];
+	/* normal heading: */
+	/* "Su Mo Tu We Th Fr Sa" */
+	/* -j heading: */
+	/* " Su  Mo  Tu  We  Th  Fr  Sa" */
+	char day_headings[ENABLE_UNICODE_SUPPORT ? 28 * 6 : 28];
+	IF_UNICODE_SUPPORT(char *hp = day_headings;)
+	char buf[40];
+
+	init_unicode();
+
+	flags = getopt32(argv, "jy");
+	/* This sets julian = flags & 1: */
+	option_mask32 &= 1;
+	month = 0;
+	argv += optind;
+
+	if (!argv[0]) {
+		struct tm *ptm;
+
+		time(&now);
+		ptm = localtime(&now);
+		year = ptm->tm_year + 1900;
+		if (!(flags & 2)) { /* no -y */
+			month = ptm->tm_mon + 1;
+		}
+	} else {
+		if (argv[1]) {
+			if (argv[2]) {
+				bb_show_usage();
+			}
+			if (!(flags & 2)) { /* no -y */
+				month = xatou_range(*argv, 1, 12);
+			}
+			argv++;
+		}
+		year = xatou_range(*argv, 1, 9999);
+	}
+
+	blank_string(day_headings, sizeof(day_headings) - 7 + 7*julian);
+
+	i = 0;
+	do {
+		zero_tm.tm_mon = i;
+		/* full month name according to locale */
+		strftime(buf, sizeof(buf), "%B", &zero_tm);
+		month_names[i] = xstrdup(buf);
+
+		if (i < 7) {
+			zero_tm.tm_wday = i;
+			/* abbreviated weekday name according to locale */
+			strftime(buf, sizeof(buf), "%a", &zero_tm);
+#if ENABLE_UNICODE_SUPPORT
+			if (julian)
+				*hp++ = ' ';
+			{
+				char *two_wchars = unicode_conv_to_printable_fixedwidth(/*NULL,*/ buf, 2);
+				strcpy(hp, two_wchars);
+				free(two_wchars);
+			}
+			hp += strlen(hp);
+			*hp++ = ' ';
+#else
+			strncpy(day_headings + i * (3+julian) + julian, buf, 2);
+#endif
+		}
+	} while (++i < 12);
+	IF_UNICODE_SUPPORT(hp[-1] = '\0';)
+
+	if (month) {
+		unsigned row, len, days[MAXDAYS];
+		unsigned *dp = days;
+		char lineout[30];
+
+		day_array(month, year, dp);
+		len = sprintf(lineout, "%s %d", month_names[month - 1], year);
+		printf("%*s%s\n%s\n",
+			   ((7*julian + WEEK_LEN) - len) / 2, "",
+			   lineout, day_headings);
+		for (row = 0; row < 6; row++) {
+			build_row(lineout, dp)[0] = '\0';
+			dp += 7;
+			trim_trailing_spaces_and_print(lineout);
+		}
+	} else {
+		unsigned row, which_cal, week_len, days[12][MAXDAYS];
+		unsigned *dp;
+		char lineout[80];
+
+		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);
+		puts("\n");		/* two \n's */
+		for (i = 0; i < 12; i++) {
+			day_array(i + 1, year, days[i]);
+		}
+		blank_string(lineout, sizeof(lineout));
+		week_len = WEEK_LEN + julian * (J_WEEK_LEN - WEEK_LEN);
+		for (month = 0; month < 12; month += 3-julian) {
+			center(month_names[month], week_len, HEAD_SEP);
+			if (!julian) {
+				center(month_names[month + 1], week_len, HEAD_SEP);
+			}
+			center(month_names[month + 2 - julian], week_len, 0);
+			printf("\n%s%*s%s", day_headings, HEAD_SEP, "", day_headings);
+			if (!julian) {
+				printf("%*s%s", HEAD_SEP, "", day_headings);
+			}
+			bb_putchar('\n');
+			for (row = 0; row < (6*7); row += 7) {
+				for (which_cal = 0; which_cal < 3-julian; which_cal++) {
+					dp = days[month + which_cal] + row;
+					build_row(lineout + which_cal * (week_len + 2), dp);
+				}
+				/* blank_string took care of nul termination. */
+				trim_trailing_spaces_and_print(lineout);
+			}
+		}
+	}
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
+
+/*
+ * day_array --
+ *	Fill in an array of 42 integers with a calendar.  Assume for a moment
+ *	that you took the (maximum) 6 rows in a calendar and stretched them
+ *	out end to end.  You would have 42 numbers or spaces.  This routine
+ *	builds that array for any month from Jan. 1 through Dec. 9999.
+ */
+static void day_array(unsigned month, unsigned year, unsigned *days)
+{
+	unsigned long temp;
+	unsigned i;
+	unsigned day, dw, dm;
+
+	memset(days, SPACE, MAXDAYS * sizeof(int));
+
+	if ((month == 9) && (year == 1752)) {
+		/* Assumes the Gregorian reformation eliminates
+		 * 3 Sep. 1752 through 13 Sep. 1752.
+		 */
+		unsigned j_offset = julian * 244;
+		size_t oday = 0;
+
+		do {
+			days[oday+2] = sep1752[oday] + j_offset;
+		} while (++oday < sizeof(sep1752));
+
+		return;
+	}
+
+	/* day_in_year
+	 * return the 1 based day number within the year
+	 */
+	day = 1;
+	if ((month > 2) && leap_year(year)) {
+		++day;
+	}
+
+	i = month;
+	while (i) {
+		day += days_in_month[--i];
+	}
+
+	/* day_in_week
+	 * return the 0 based day number for any date from 1 Jan. 1 to
+	 * 31 Dec. 9999.  Assumes the Gregorian reformation eliminates
+	 * 3 Sep. 1752 through 13 Sep. 1752.  Returns Thursday for all
+	 * missing days.
+	 */
+	temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1) + day;
+	if (temp < FIRST_MISSING_DAY) {
+		dw = ((temp - 1 + SATURDAY) % 7);
+	} else {
+		dw = (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+	}
+
+	if (!julian) {
+		day = 1;
+	}
+
+	dm = days_in_month[month];
+	if ((month == 2) && leap_year(year)) {
+		++dm;
+	}
+
+	do {
+		days[dw++] = day++;
+	} while (--dm);
+}
+
+static void trim_trailing_spaces_and_print(char *s)
+{
+	char *p = s;
+
+	while (*p) {
+		++p;
+	}
+	while (p != s) {
+		--p;
+		if (!isspace(*p)) {
+			p[1] = '\0';
+			break;
+		}
+	}
+
+	puts(s);
+}
+
+static void center(char *str, unsigned len, unsigned separate)
+{
+	unsigned n = strlen(str);
+	len -= n;
+	printf("%*s%*s", (len/2) + n, str, (len/2) + (len % 2) + separate, "");
+}
+
+static void blank_string(char *buf, size_t buflen)
+{
+	memset(buf, ' ', buflen);
+	buf[buflen-1] = '\0';
+}
+
+static char *build_row(char *p, unsigned *dp)
+{
+	unsigned col, val, day;
+
+	memset(p, ' ', (julian + DAY_LEN) * 7);
+
+	col = 0;
+	do {
+		day = *dp++;
+		if (day != SPACE) {
+			if (julian) {
+				++p;
+				if (day >= 100) {
+					*p = '0';
+					p[-1] = (day / 100) + '0';
+					day %= 100;
+				}
+			}
+			val = day / 10;
+			if (val > 0) {
+				*p = val + '0';
+			}
+			*++p = day % 10 + '0';
+			p += 2;
+		} else {
+			p += DAY_LEN + julian;
+		}
+	} while (++col < 7);
+
+	return p;
+}
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kim Letkeman.
+ *
+ * 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. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/coreutils/cat.c b/busybox-1.19.3/coreutils/cat.c
new file mode 100644
index 0000000..00c38d4
--- /dev/null
+++ b/busybox-1.19.3/coreutils/cat.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cat implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cat.html */
+
+//kbuild:lib-$(CONFIG_CAT)     += cat.o
+//kbuild:lib-$(CONFIG_MORE)    += cat.o # more uses it if stdout isn't a tty
+//kbuild:lib-$(CONFIG_LESS)    += cat.o # less too
+//kbuild:lib-$(CONFIG_CRONTAB) += cat.o # crontab -l
+
+//config:config CAT
+//config:	bool "cat"
+//config:	default y
+//config:	help
+//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! */
+
+
+int bb_cat(char **argv)
+{
+	int fd;
+	int retval = EXIT_SUCCESS;
+
+	if (!*argv)
+		argv = (char**) &bb_argv_dash;
+
+	do {
+		fd = open_or_warn_stdin(*argv);
+		if (fd >= 0) {
+			/* This is not a xfunc - never exits */
+			off_t r = bb_copyfd_eof(fd, STDOUT_FILENO);
+			if (fd != STDIN_FILENO)
+				close(fd);
+			if (r >= 0)
+				continue;
+		}
+		retval = EXIT_FAILURE;
+	} while (*++argv);
+
+	return retval;
+}
+
+int cat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cat_main(int argc UNUSED_PARAM, char **argv)
+{
+	getopt32(argv, "u");
+	argv += optind;
+	return bb_cat(argv);
+}
diff --git a/busybox-1.19.3/coreutils/catv.c b/busybox-1.19.3/coreutils/catv.c
new file mode 100644
index 0000000..214b431
--- /dev/null
+++ b/busybox-1.19.3/coreutils/catv.c
@@ -0,0 +1,83 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cat -v implementation for busybox
+ *
+ * Copyright (C) 2006 Rob Landley <rob@landley.net>
+ *
+ * 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;
+int catv_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval = EXIT_SUCCESS;
+	int fd;
+	unsigned flags;
+
+	flags = getopt32(argv, "etv");
+#define CATV_OPT_e (1<<0)
+#define CATV_OPT_t (1<<1)
+#define CATV_OPT_v (1<<2)
+	flags ^= CATV_OPT_v;
+	argv += optind;
+
+	/* Read from stdin if there's nothing else to do. */
+	if (!argv[0])
+		*--argv = (char*)"-";
+	do {
+		fd = open_or_warn_stdin(*argv);
+		if (fd < 0) {
+			retval = EXIT_FAILURE;
+			continue;
+		}
+		for (;;) {
+			int i, res;
+
+#define read_buf bb_common_bufsiz1
+			res = read(fd, read_buf, COMMON_BUFSIZE);
+			if (res < 0)
+				retval = EXIT_FAILURE;
+			if (res < 1)
+				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;
+					}
+				}
+				bb_putchar(c);
+			}
+		}
+		if (ENABLE_FEATURE_CLEAN_UP && fd)
+			close(fd);
+	} while (*++argv);
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/busybox-1.19.3/coreutils/chgrp.c b/busybox-1.19.3/coreutils/chgrp.c
new file mode 100644
index 0000000..7076db6
--- /dev/null
+++ b/busybox-1.19.3/coreutils/chgrp.c
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chgrp implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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! */
+
+
+int chgrp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chgrp_main(int argc, char **argv)
+{
+	/* "chgrp [opts] abc file(s)" == "chown [opts] :abc file(s)" */
+	char **p = argv;
+	while (*++p) {
+		if (p[0][0] != '-') {
+			p[0] = xasprintf(":%s", p[0]);
+			break;
+		}
+	}
+	return chown_main(argc, argv);
+}
diff --git a/busybox-1.19.3/coreutils/chmod.c b/busybox-1.19.3/coreutils/chmod.c
new file mode 100644
index 0000000..5ee45b9
--- /dev/null
+++ b/busybox-1.19.3/coreutils/chmod.c
@@ -0,0 +1,182 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chmod implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
+ *  to correctly parse '-rwxgoa'
+ *
+ * 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! */
+
+
+#define OPT_RECURSE (option_mask32 & 1)
+#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0))
+#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0))
+#define OPT_QUIET   (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0))
+#define OPT_STR     "R" IF_DESKTOP("vcf")
+
+/* coreutils:
+ * chmod never changes the permissions of symbolic links; the chmod
+ * system call cannot change their permissions. This is not a problem
+ * since the permissions of symbolic links are never used.
+ * However, for each symbolic link listed on the command line, chmod changes
+ * the permissions of the pointed-to file. In contrast, chmod ignores
+ * symbolic links encountered during recursive directory traversals.
+ */
+
+static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth)
+{
+	mode_t newmode;
+
+	/* match coreutils behavior */
+	if (depth == 0) {
+		/* statbuf holds lstat result, but we need stat (follow link) */
+		if (stat(fileName, statbuf))
+			goto err;
+	} else { /* depth > 0: skip links */
+		if (S_ISLNK(statbuf->st_mode))
+			return TRUE;
+	}
+	newmode = statbuf->st_mode;
+
+	if (!bb_parse_mode((char *)param, &newmode))
+		bb_error_msg_and_die("invalid mode '%s'", (char *)param);
+
+	if (chmod(fileName, newmode) == 0) {
+		if (OPT_VERBOSE
+		 || (OPT_CHANGED && statbuf->st_mode != newmode)
+		) {
+			printf("mode of '%s' changed to %04o (%s)\n", fileName,
+				newmode & 07777, bb_mode_string(newmode)+1);
+		}
+		return TRUE;
+	}
+ err:
+	if (!OPT_QUIET)
+		bb_simple_perror_msg(fileName);
+	return FALSE;
+}
+
+int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chmod_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval = EXIT_SUCCESS;
+	char *arg, **argp;
+	char *smode;
+
+	/* Convert first encountered -r into ar, -w into aw etc
+	 * so that getopt would not eat it */
+	argp = argv;
+	while ((arg = *++argp)) {
+		/* Mode spec must be the first arg (sans -R etc) */
+		/* (protect against mishandling e.g. "chmod 644 -r") */
+		if (arg[0] != '-') {
+			arg = NULL;
+			break;
+		}
+		/* An option. Not a -- or valid option? */
+		if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
+			arg[0] = 'a';
+			break;
+		}
+	}
+
+	/* Parse options */
+	opt_complementary = "-2";
+	getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */
+	argv += optind;
+
+	/* Restore option-like mode if needed */
+	if (arg) arg[0] = '-';
+
+	/* Ok, ready to do the deed now */
+	smode = *argv++;
+	do {
+		if (!recursive_action(*argv,
+			OPT_RECURSE,    // recurse
+			fileAction,     // file action
+			fileAction,     // dir action
+			smode,          // user data
+			0)              // depth
+		) {
+			retval = EXIT_FAILURE;
+		}
+	} while (*++argv);
+
+	return retval;
+}
+
+/*
+Security: chmod is too important and too subtle.
+This is a test script (busybox chmod versus coreutils).
+Run it in empty directory.
+
+#!/bin/sh
+t1="/tmp/busybox chmod"
+t2="/usr/bin/chmod"
+create() {
+    rm -rf $1; mkdir $1
+    (
+    cd $1 || exit 1
+    mkdir dir
+    >up
+    >file
+    >dir/file
+    ln -s dir linkdir
+    ln -s file linkfile
+    ln -s ../up dir/up
+    )
+}
+tst() {
+    (cd test1; $t1 $1)
+    (cd test2; $t2 $1)
+    (cd test1; ls -lR) >out1
+    (cd test2; ls -lR) >out2
+    echo "chmod $1" >out.diff
+    if ! diff -u out1 out2 >>out.diff; then exit 1; fi
+    rm out.diff
+}
+echo "If script produced 'out.diff' file, then at least one testcase failed"
+create test1; create test2
+tst "a+w file"
+tst "a-w dir"
+tst "a+w linkfile"
+tst "a-w linkdir"
+tst "-R a+w file"
+tst "-R a-w dir"
+tst "-R a+w linkfile"
+tst "-R a-w linkdir"
+tst "a-r,a+x linkfile"
+*/
diff --git a/busybox-1.19.3/coreutils/chown.c b/busybox-1.19.3/coreutils/chown.c
new file mode 100644
index 0000000..bb166d8
--- /dev/null
+++ b/busybox-1.19.3/coreutils/chown.c
@@ -0,0 +1,221 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chown implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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! */
+
+
+#define OPT_STR     ("Rh" IF_DESKTOP("vcfLHP"))
+#define BIT_RECURSE 1
+#define OPT_RECURSE (opt & 1)
+#define OPT_NODEREF (opt & 2)
+#define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0))
+#define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0))
+#define OPT_QUIET   (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0))
+/* POSIX options
+ * -L traverse every symbolic link to a directory encountered
+ * -H if a command line argument is a symbolic link to a directory, traverse it
+ * -P do not traverse any symbolic links (default)
+ * We do not conform to the following:
+ * "Specifying more than one of -H, -L, and -P is not an error.
+ * The last option specified shall determine the behavior of the utility." */
+/* -L */
+#define BIT_TRAVERSE 0x20
+#define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0))
+/* -H or -L */
+#define BIT_TRAVERSE_TOP (0x20|0x40)
+#define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0))
+
+#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
+static const char chown_longopts[] ALIGN1 =
+	"recursive\0"        No_argument   "R"
+	"dereference\0"      No_argument   "\xff"
+	"no-dereference\0"   No_argument   "h"
+# if ENABLE_DESKTOP
+	"changes\0"          No_argument   "c"
+	"silent\0"           No_argument   "f"
+	"quiet\0"            No_argument   "f"
+	"verbose\0"          No_argument   "v"
+# endif
+	;
+#endif
+
+typedef int (*chown_fptr)(const char *, uid_t, gid_t);
+
+struct param_t {
+	struct bb_uidgid_t ugid;
+	chown_fptr chown_func;
+};
+
+static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf,
+		void *vparam, int depth UNUSED_PARAM)
+{
+#define param  (*(struct param_t*)vparam)
+#define opt option_mask32
+	uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid;
+	gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid;
+
+	if (param.chown_func(fileName, u, g) == 0) {
+		if (OPT_VERBOSE
+		 || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
+		) {
+			printf("changed ownership of '%s' to %u:%u\n",
+					fileName, (unsigned)u, (unsigned)g);
+		}
+		return TRUE;
+	}
+	if (!OPT_QUIET)
+		bb_simple_perror_msg(fileName);
+	return FALSE;
+#undef opt
+#undef param
+}
+
+int chown_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval = EXIT_SUCCESS;
+	int opt, flags;
+	struct param_t param;
+
+	/* Just -1 might not work: uid_t may be unsigned long */
+	param.ugid.uid = -1L;
+	param.ugid.gid = -1L;
+
+#if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
+	applet_long_options = chown_longopts;
+#endif
+	opt_complementary = "-2";
+	opt = getopt32(argv, OPT_STR);
+	argv += optind;
+
+	/* 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)
+	) {
+		param.chown_func = lchown;
+	}
+
+	flags = ACTION_DEPTHFIRST; /* match coreutils order */
+	if (OPT_RECURSE)
+		flags |= ACTION_RECURSE;
+	if (OPT_TRAVERSE_TOP)
+		flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */
+	if (OPT_TRAVERSE)
+		flags |= ACTION_FOLLOWLINKS; /* follow links if -L */
+
+	parse_chown_usergroup_or_die(&param.ugid, argv[0]);
+
+	/* Ok, ready to do the deed now */
+	while (*++argv) {
+		if (!recursive_action(*argv,
+				flags,          /* flags */
+				fileAction,     /* file action */
+				fileAction,     /* dir action */
+				&param,         /* user data */
+				0)              /* depth */
+		) {
+			retval = EXIT_FAILURE;
+		}
+	}
+
+	return retval;
+}
+
+/*
+Testcase. Run in empty directory.
+
+#!/bin/sh
+t1="/tmp/busybox chown"
+t2="/usr/bin/chown"
+create() {
+    rm -rf $1; mkdir $1
+    (
+    cd $1 || exit 1
+    mkdir dir dir2
+    >up
+    >file
+    >dir/file
+    >dir2/file
+    ln -s dir linkdir
+    ln -s file linkfile
+    ln -s ../up dir/linkup
+    ln -s ../dir2 dir/linkupdir2
+    )
+    chown -R 0:0 $1
+}
+tst() {
+    create test1
+    create test2
+    echo "[$1]" >>test1.out
+    echo "[$1]" >>test2.out
+    (cd test1; $t1 $1) >>test1.out 2>&1
+    (cd test2; $t2 $1) >>test2.out 2>&1
+    (cd test1; ls -lnR) >out1
+    (cd test2; ls -lnR) >out2
+    echo "chown $1" >out.diff
+    if ! diff -u out1 out2 >>out.diff; then exit 1; fi
+    rm out.diff
+}
+tst_for_each() {
+    tst "$1 1:1 file"
+    tst "$1 1:1 dir"
+    tst "$1 1:1 linkdir"
+    tst "$1 1:1 linkfile"
+}
+echo "If script produced 'out.diff' file, then at least one testcase failed"
+>test1.out
+>test2.out
+# These match coreutils 6.8:
+tst_for_each "-v"
+tst_for_each "-vR"
+tst_for_each "-vRP"
+tst_for_each "-vRL"
+tst_for_each "-vRH"
+tst_for_each "-vh"
+tst_for_each "-vhR"
+tst_for_each "-vhRP"
+tst_for_each "-vhRL"
+tst_for_each "-vhRH"
+# Fix `name' in coreutils output
+sed 's/`/'"'"'/g' -i test2.out
+# Compare us with coreutils output
+diff -u test1.out test2.out
+
+*/
diff --git a/busybox-1.19.3/coreutils/chroot.c b/busybox-1.19.3/coreutils/chroot.c
new file mode 100644
index 0000000..ab8beb0
--- /dev/null
+++ b/busybox-1.19.3/coreutils/chroot.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini chroot implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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;
+int chroot_main(int argc UNUSED_PARAM, char **argv)
+{
+	++argv;
+	if (!*argv)
+		bb_show_usage();
+	xchroot(*argv);
+	xchdir("/");
+
+	++argv;
+	if (!*argv) { /* no 2nd param (PROG), use shell */
+		argv -= 2;
+		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);
+}
diff --git a/busybox-1.19.3/coreutils/cksum.c b/busybox-1.19.3/coreutils/cksum.c
new file mode 100644
index 0000000..ac0b0c3
--- /dev/null
+++ b/busybox-1.19.3/coreutils/cksum.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cksum - calculate the CRC32 checksum of a file
+ *
+ * Copyright (C) 2006 by Rob Sullivan, with ideas from code by Walter Harms
+ *
+ * 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)
+{
+	uint32_t *crc32_table = crc32_filltable(NULL, 1);
+	uint32_t crc;
+	off_t length, filesize;
+	int bytes_read;
+	int exit_code = EXIT_SUCCESS;
+
+#if ENABLE_DESKTOP
+	getopt32(argv, ""); /* coreutils 6.9 compat */
+	argv += optind;
+#else
+	argv++;
+#endif
+
+	do {
+		int fd = open_or_warn_stdin(*argv ? *argv : bb_msg_standard_input);
+
+		if (fd < 0) {
+			exit_code = EXIT_FAILURE;
+			continue;
+		}
+		crc = 0;
+		length = 0;
+
+#define read_buf bb_common_bufsiz1
+		while ((bytes_read = safe_read(fd, read_buf, sizeof(read_buf))) > 0) {
+			length += bytes_read;
+			crc = crc32_block_endian1(crc, read_buf, bytes_read, crc32_table);
+		}
+		close(fd);
+
+		filesize = length;
+
+		while (length) {
+			crc = (crc << 8) ^ crc32_table[(uint8_t)(crc >> 24) ^ (uint8_t)length];
+			/* must ensure that shift is unsigned! */
+			if (sizeof(length) <= sizeof(unsigned))
+				length = (unsigned)length >> 8;
+			else if (sizeof(length) <= sizeof(unsigned long))
+				length = (unsigned long)length >> 8;
+			else
+				length = (unsigned long long)length >> 8;
+		}
+		crc = ~crc;
+
+		printf((*argv ? "%"PRIu32" %"OFF_FMT"i %s\n" : "%"PRIu32" %"OFF_FMT"i\n"),
+				crc, filesize, *argv);
+	} while (*argv && *++argv);
+
+	fflush_stdout_and_exit(exit_code);
+}
diff --git a/busybox-1.19.3/coreutils/comm.c b/busybox-1.19.3/coreutils/comm.c
new file mode 100644
index 0000000..cd45095
--- /dev/null
+++ b/busybox-1.19.3/coreutils/comm.c
@@ -0,0 +1,108 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini comm implementation for busybox
+ *
+ * Copyright (C) 2005 by Robert Sullivan <cogito.ergo.cogito@gmail.com>
+ *
+ * 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)
+#define COMM_OPT_2 (1 << 1)
+#define COMM_OPT_3 (1 << 2)
+
+/* writeline outputs the input given, appropriately aligned according to class */
+static void writeline(char *line, int class)
+{
+	int flags = option_mask32;
+	if (class == 0) {
+		if (flags & COMM_OPT_1)
+			return;
+	} else if (class == 1) {
+		if (flags & COMM_OPT_2)
+			return;
+		if (!(flags & COMM_OPT_1))
+			putchar('\t');
+	} else /*if (class == 2)*/ {
+		if (flags & COMM_OPT_3)
+			return;
+		if (!(flags & COMM_OPT_1))
+			putchar('\t');
+		if (!(flags & COMM_OPT_2))
+			putchar('\t');
+	}
+	puts(line);
+}
+
+int comm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int comm_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *thisline[2];
+	FILE *stream[2];
+	int i;
+	int order;
+
+	opt_complementary = "=2";
+	getopt32(argv, "123");
+	argv += optind;
+
+	for (i = 0; i < 2; ++i) {
+		stream[i] = xfopen_stdin(argv[i]);
+	}
+
+	order = 0;
+	thisline[1] = thisline[0] = NULL;
+	while (1) {
+		if (order <= 0) {
+			free(thisline[0]);
+			thisline[0] = xmalloc_fgetline(stream[0]);
+		}
+		if (order >= 0) {
+			free(thisline[1]);
+			thisline[1] = xmalloc_fgetline(stream[1]);
+		}
+
+		i = !thisline[0] + (!thisline[1] << 1);
+		if (i)
+			break;
+		order = strcmp(thisline[0], thisline[1]);
+
+		if (order >= 0)
+			writeline(thisline[1], order ? 1 : 2);
+		else
+			writeline(thisline[0], 0);
+	}
+
+	/* EOF at least on one of the streams */
+	i &= 1;
+	if (thisline[i]) {
+		/* stream[i] is not at EOF yet */
+		/* we did not print thisline[i] yet */
+		char *p = thisline[i];
+		writeline(p, i);
+		while (1) {
+			free(p);
+			p = xmalloc_fgetline(stream[i]);
+			if (!p)
+				break;
+			writeline(p, i);
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		fclose(stream[0]);
+		fclose(stream[1]);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/cp.c b/busybox-1.19.3/coreutils/cp.c
new file mode 100644
index 0000000..e48e21c
--- /dev/null
+++ b/busybox-1.19.3/coreutils/cp.c
@@ -0,0 +1,208 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cp implementation for busybox
+ *
+ * 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 source tree.
+ */
+
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/cp.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reduction.
+ */
+
+//usage:#define cp_trivial_usage
+//usage:       "[OPTIONS] SOURCE DEST"
+//usage:#define cp_full_usage "\n\n"
+//usage:       "Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY\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"
+
+/* This is a NOEXEC applet. Be very careful! */
+
+int cp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cp_main(int argc, char **argv)
+{
+	struct stat source_stat;
+	struct stat dest_stat;
+	const char *last;
+	const char *dest;
+	int s_flags;
+	int d_flags;
+	int flags;
+	int status;
+	enum {
+		OPT_a = 1 << (sizeof(FILEUTILS_CP_OPTSTR)-1),
+		OPT_r = 1 << (sizeof(FILEUTILS_CP_OPTSTR)),
+		OPT_P = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+1),
+		OPT_v = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+2),
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+		OPT_parents = 1 << (sizeof(FILEUTILS_CP_OPTSTR)+3),
+#endif
+	};
+
+	// Need at least two arguments
+	// Soft- and hardlinking doesn't mix
+	// -P and -d are the same (-P is POSIX, -d is GNU)
+	// -r and -R are the same
+	// -R (and therefore -r) turns on -d (coreutils does this)
+	// -a = -pdR
+	opt_complementary = "-2:l--s:s--l:Pd:rRd:Rd:apdR";
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+	applet_long_options =
+		"archive\0"        No_argument "a"
+		"force\0"          No_argument "f"
+		"interactive\0"    No_argument "i"
+		"link\0"           No_argument "l"
+		"dereference\0"    No_argument "L"
+		"no-dereference\0" No_argument "P"
+		"recursive\0"      No_argument "R"
+		"symbolic-link\0"  No_argument "s"
+		"verbose\0"        No_argument "v"
+		"parents\0"        No_argument "\xff"
+		;
+#endif
+	// -v (--verbose) is ignored
+	flags = getopt32(argv, FILEUTILS_CP_OPTSTR "arPv");
+	/* Options of cp from GNU coreutils 6.10:
+	 * -a, --archive
+	 * -f, --force
+	 * -i, --interactive
+	 * -l, --link
+	 * -L, --dereference
+	 * -P, --no-dereference
+	 * -R, -r, --recursive
+	 * -s, --symbolic-link
+	 * -v, --verbose
+	 * -H	follow command-line symbolic links in SOURCE
+	 * -d	same as --no-dereference --preserve=links
+	 * -p	same as --preserve=mode,ownership,timestamps
+	 * -c	same as --preserve=context
+	 * --parents
+	 *	use full source file name under DIRECTORY
+	 * NOT SUPPORTED IN BBOX:
+	 * --backup[=CONTROL]
+	 *	make a backup of each existing destination file
+	 * -b	like --backup but does not accept an argument
+	 * --copy-contents
+	 *	copy contents of special files when recursive
+	 * --preserve[=ATTR_LIST]
+	 *	preserve attributes (default: mode,ownership,timestamps),
+	 *	if possible additional attributes: security context,links,all
+	 * --no-preserve=ATTR_LIST
+	 * --remove-destination
+	 *	remove  each existing destination file before attempting to open
+	 * --sparse=WHEN
+	 *	control creation of sparse files
+	 * --strip-trailing-slashes
+	 *	remove any trailing slashes from each SOURCE argument
+	 * -S, --suffix=SUFFIX
+	 *	override the usual backup suffix
+	 * -t, --target-directory=DIRECTORY
+	 *	copy all SOURCE arguments into DIRECTORY
+	 * -T, --no-target-directory
+	 *	treat DEST as a normal file
+	 * -u, --update
+	 *	copy only when the SOURCE file is newer than the destination
+	 *	file or when the destination file is missing
+	 * -x, --one-file-system
+	 *	stay on this file system
+	 * -Z, --context=CONTEXT
+	 *	(SELinux) set SELinux security context of copy to CONTEXT
+	 */
+	argc -= optind;
+	argv += optind;
+	/* Reverse this bit. If there is -d, bit is not set: */
+	flags ^= FILEUTILS_DEREFERENCE;
+	/* coreutils 6.9 compat:
+	 * by default, "cp" derefs symlinks (creates regular dest files),
+	 * but "cp -R" does not. We switch off deref if -r or -R (see above).
+	 * However, "cp -RL" must still deref symlinks: */
+	if (flags & FILEUTILS_DEREF_SOFTLINK) /* -L */
+		flags |= FILEUTILS_DEREFERENCE;
+
+#if ENABLE_SELINUX
+	if (flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) {
+		selinux_or_die();
+	}
+#endif
+
+	status = EXIT_SUCCESS;
+	last = argv[argc - 1];
+	/* If there are only two arguments and...  */
+	if (argc == 2) {
+		s_flags = cp_mv_stat2(*argv, &source_stat,
+				(flags & FILEUTILS_DEREFERENCE) ? stat : lstat);
+		if (s_flags < 0)
+			return EXIT_FAILURE;
+		d_flags = cp_mv_stat(last, &dest_stat);
+		if (d_flags < 0)
+			return EXIT_FAILURE;
+
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+		if (flags & OPT_parents) {
+			if (!(d_flags & 2)) {
+				bb_error_msg_and_die("with --parents, the destination must be a directory");
+			}
+		}
+#endif
+
+		/* ...if neither is a directory...  */
+		if (!((s_flags | d_flags) & 2)
+		    /* ...or: recursing, the 1st is a directory, and the 2nd doesn't exist... */
+		 || ((flags & FILEUTILS_RECUR) && (s_flags & 2) && !d_flags)
+		) {
+			/* Do a simple copy */
+			dest = last;
+			goto DO_COPY; /* NB: argc==2 -> *++argv==last */
+		}
+	}
+
+	while (1) {
+#if ENABLE_FEATURE_CP_LONG_OPTIONS
+		if (flags & OPT_parents) {
+			char *dest_dup;
+			char *dest_dir;
+			dest = concat_path_file(last, *argv);
+			dest_dup = xstrdup(dest);
+			dest_dir = dirname(dest_dup);
+			if (bb_make_directory(dest_dir, -1, FILEUTILS_RECUR)) {
+				return EXIT_FAILURE;
+			}
+			free(dest_dup);
+			goto DO_COPY;
+		}
+#endif
+		dest = concat_path_file(last, bb_get_last_path_component_strip(*argv));
+ DO_COPY:
+		if (copy_file(*argv, dest, flags) < 0) {
+			status = EXIT_FAILURE;
+		}
+		if (*++argv == last) {
+			/* possibly leaking dest... */
+			break;
+		}
+		/* don't move up: dest may be == last and not malloced! */
+		free((void*)dest);
+	}
+
+	/* Exit. We are NOEXEC, not NOFORK. We do exit at the end of main() */
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/cut.c b/busybox-1.19.3/coreutils/cut.c
new file mode 100644
index 0000000..2c27b70
--- /dev/null
+++ b/busybox-1.19.3/coreutils/cut.c
@@ -0,0 +1,306 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cut.c - minimalist version of cut
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc.
+ * Written by Mark Whitley <markw@codepoet.org>
+ * debloated by Bernhard Reutner-Fischer
+ *
+ * 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! */
+
+
+/* option vars */
+static const char optstring[] ALIGN1 = "b:c:f:d:sn";
+#define CUT_OPT_BYTE_FLGS     (1 << 0)
+#define CUT_OPT_CHAR_FLGS     (1 << 1)
+#define CUT_OPT_FIELDS_FLGS   (1 << 2)
+#define CUT_OPT_DELIM_FLGS    (1 << 3)
+#define CUT_OPT_SUPPRESS_FLGS (1 << 4)
+
+struct cut_list {
+	int startpos;
+	int endpos;
+};
+
+enum {
+	BOL = 0,
+	EOL = INT_MAX,
+	NON_RANGE = -1
+};
+
+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)
+{
+	char *line;
+	unsigned linenum = 0;	/* keep these zero-based to be consistent */
+
+	/* go through every line in the file */
+	while ((line = xmalloc_fgetline(file)) != NULL) {
+
+		/* set up a list so we can keep track of what's been printed */
+		int linelen = strlen(line);
+		char *printed = xzalloc(linelen + 1);
+		char *orig_line = line;
+		unsigned cl_pos = 0;
+		int spos;
+
+		/* cut based on chars/bytes XXX: only works when sizeof(char) == byte */
+		if (option_mask32 & (CUT_OPT_CHAR_FLGS | CUT_OPT_BYTE_FLGS)) {
+			/* print the chars specified in each cut list */
+			for (; cl_pos < nlists; cl_pos++) {
+				spos = cut_lists[cl_pos].startpos;
+				while (spos < linelen) {
+					if (!printed[spos]) {
+						printed[spos] = 'X';
+						putchar(line[spos]);
+					}
+					spos++;
+					if (spos > cut_lists[cl_pos].endpos
+					/* NON_RANGE is -1, so if below is true,
+					 * the above was true too (spos is >= 0) */
+					/* || cut_lists[cl_pos].endpos == NON_RANGE */
+					) {
+						break;
+					}
+				}
+			}
+		} else if (delim == '\n') {	/* cut by lines */
+			spos = cut_lists[cl_pos].startpos;
+
+			/* get out if we have no more lists to process or if the lines
+			 * are lower than what we're interested in */
+			if (((int)linenum < spos) || (cl_pos >= nlists))
+				goto next_line;
+
+			/* if the line we're looking for is lower than the one we were
+			 * passed, it means we displayed it already, so move on */
+			while (spos < (int)linenum) {
+				spos++;
+				/* go to the next list if we're at the end of this one */
+				if (spos > cut_lists[cl_pos].endpos
+				 || cut_lists[cl_pos].endpos == NON_RANGE
+				) {
+					cl_pos++;
+					/* get out if there's no more lists to process */
+					if (cl_pos >= nlists)
+						goto next_line;
+					spos = cut_lists[cl_pos].startpos;
+					/* get out if the current line is lower than the one
+					 * we just became interested in */
+					if ((int)linenum < spos)
+						goto next_line;
+				}
+			}
+
+			/* If we made it here, it means we've found the line we're
+			 * looking for, so print it */
+			puts(line);
+			goto next_line;
+		} else {		/* cut by fields */
+			int ndelim = -1;	/* zero-based / one-based problem */
+			int nfields_printed = 0;
+			char *field = NULL;
+			char delimiter[2];
+
+			delimiter[0] = delim;
+			delimiter[1] = 0;
+
+			/* does this line contain any delimiters? */
+			if (strchr(line, delim) == NULL) {
+				if (!(option_mask32 & CUT_OPT_SUPPRESS_FLGS))
+					puts(line);
+				goto next_line;
+			}
+
+			/* process each list on this line, for as long as we've got
+			 * a line to process */
+			for (; cl_pos < nlists && line; cl_pos++) {
+				spos = cut_lists[cl_pos].startpos;
+				do {
+					/* find the field we're looking for */
+					while (line && ndelim < spos) {
+						field = strsep(&line, delimiter);
+						ndelim++;
+					}
+
+					/* we found it, and it hasn't been printed yet */
+					if (field && ndelim == spos && !printed[ndelim]) {
+						/* if this isn't our first time through, we need to
+						 * print the delimiter after the last field that was
+						 * printed */
+						if (nfields_printed > 0)
+							putchar(delim);
+						fputs(field, stdout);
+						printed[ndelim] = 'X';
+						nfields_printed++;	/* shouldn't overflow.. */
+					}
+
+					spos++;
+
+					/* keep going as long as we have a line to work with,
+					 * this is a list, and we're not at the end of that
+					 * list */
+				} while (spos <= cut_lists[cl_pos].endpos && line
+						&& cut_lists[cl_pos].endpos != NON_RANGE);
+			}
+		}
+		/* if we printed anything at all, we need to finish it with a
+		 * newline cuz we were handed a chomped line */
+		putchar('\n');
+ next_line:
+		linenum++;
+		free(printed);
+		free(orig_line);
+	}
+}
+
+int cut_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cut_main(int argc UNUSED_PARAM, char **argv)
+{
+	/* growable array holding a series of lists */
+	struct cut_list *cut_lists = NULL;
+	unsigned nlists = 0;	/* number of elements in above list */
+	char delim = '\t';	/* delimiter, default is tab */
+	char *sopt, *ltok;
+	unsigned opt;
+
+	opt_complementary = "b--bcf:c--bcf:f--bcf";
+	opt = getopt32(argv, optstring, &sopt, &sopt, &sopt, &ltok);
+//	argc -= optind;
+	argv += optind;
+	if (!(opt & (CUT_OPT_BYTE_FLGS | CUT_OPT_CHAR_FLGS | CUT_OPT_FIELDS_FLGS)))
+		bb_error_msg_and_die("expected a list of bytes, characters, or fields");
+
+	if (opt & CUT_OPT_DELIM_FLGS) {
+		if (ltok[0] && ltok[1]) { /* more than 1 char? */
+			bb_error_msg_and_die("the delimiter must be a single character");
+		}
+		delim = ltok[0];
+	}
+
+	/*  non-field (char or byte) cutting has some special handling */
+	if (!(opt & CUT_OPT_FIELDS_FLGS)) {
+		static const char _op_on_field[] ALIGN1 = " only when operating on fields";
+
+		if (opt & CUT_OPT_SUPPRESS_FLGS) {
+			bb_error_msg_and_die
+				("suppressing non-delimited lines makes sense%s",
+				 _op_on_field);
+		}
+		if (delim != '\t') {
+			bb_error_msg_and_die
+				("a delimiter may be specified%s", _op_on_field);
+		}
+	}
+
+	/*
+	 * parse list and put values into startpos and endpos.
+	 * valid list formats: N, N-, N-M, -M
+	 * more than one list can be separated by commas
+	 */
+	{
+		char *ntok;
+		int s = 0, e = 0;
+
+		/* take apart the lists, one by one (they are separated with commas) */
+		while ((ltok = strsep(&sopt, ",")) != NULL) {
+
+			/* it's actually legal to pass an empty list */
+			if (!ltok[0])
+				continue;
+
+			/* get the start pos */
+			ntok = strsep(&ltok, "-");
+			if (!ntok[0]) {
+				s = BOL;
+			} else {
+				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)
+					s--;
+			}
+
+			/* get the end pos */
+			if (ltok == NULL) {
+				e = NON_RANGE;
+			} else if (!ltok[0]) {
+				e = EOL;
+			} else {
+				e = xatoi_positive(ltok);
+				/* if the user specified and end position of 0,
+				 * that means "til the end of the line" */
+				if (e == 0)
+					e = EOL;
+				e--;	/* again, arrays are zero based, lines are 1 based */
+				if (e == s)
+					e = NON_RANGE;
+			}
+
+			/* add the new list */
+			cut_lists = xrealloc_vector(cut_lists, 4, nlists);
+			/* NB: startpos is always >= 0,
+			 * while endpos may be = NON_RANGE (-1) */
+			cut_lists[nlists].startpos = s;
+			cut_lists[nlists].endpos = e;
+			nlists++;
+		}
+
+		/* make sure we got some cut positions out of all that */
+		if (nlists == 0)
+			bb_error_msg_and_die("missing list of positions");
+
+		/* now that the lists are parsed, we need to sort them to make life
+		 * easier on us when it comes time to print the chars / fields / lines
+		 */
+		qsort(cut_lists, nlists, sizeof(cut_lists[0]), cmpfunc);
+	}
+
+	{
+		int retval = EXIT_SUCCESS;
+
+		if (!*argv)
+			*--argv = (char *)"-";
+
+		do {
+			FILE *file = fopen_or_warn_stdin(*argv);
+			if (!file) {
+				retval = EXIT_FAILURE;
+				continue;
+			}
+			cut_file(file, delim, cut_lists, nlists);
+			fclose_if_not_stdin(file);
+		} while (*++argv);
+
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(cut_lists);
+		fflush_stdout_and_exit(retval);
+	}
+}
diff --git a/busybox-1.19.3/coreutils/date.c b/busybox-1.19.3/coreutils/date.c
new file mode 100644
index 0000000..6a7d5fa
--- /dev/null
+++ b/busybox-1.19.3/coreutils/date.c
@@ -0,0 +1,382 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini date implementation for busybox
+ *
+ * by Matthew Grant <grantma@anathoth.gen.nz>
+ *
+ * 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 source tree.
+*/
+
+/* This 'date' command supports only 2 time setting formats,
+   all the GNU strftime stuff (its in libc, lets use it),
+   setting time using UTC and displaying it, as well as
+   an RFC 2822 compliant date output for shell scripting
+   mail commands */
+
+/* Input parsing code is always bulky - used heavy duty libc stuff as
+   much as possible, missed out a lot of bounds checking */
+
+//applet:IF_DATE(APPLET(date, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_DATE) += date.o
+
+//config:config DATE
+//config:	bool "date"
+//config:	default y
+//config:	help
+//config:	  date is used to set the system date or display the
+//config:	  current time in the given format.
+//config:
+//config:config FEATURE_DATE_ISOFMT
+//config:	bool "Enable ISO date format output (-I)"
+//config:	default y
+//config:	depends on DATE
+//config:	help
+//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  # syscall(__NR_clock_gettime)
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Support %[num]N format specifier. Adds ~250 bytes of code.
+//config:
+//config:config FEATURE_DATE_COMPAT
+//config:	bool "Support weird 'date MMDDhhmm[[YY]YY][.ss]' format"
+//config:	default y
+//config:	depends on DATE
+//config:	help
+//config:	  System time can be set by 'date -s DATE' and simply 'date DATE',
+//config:	  but formats of DATE string are different. 'date DATE' accepts
+//config:	  a rather weird MMDDhhmm[[YY]YY][.ss] format with completely
+//config:	  unnatural placement of year between minutes and seconds.
+//config:	  date -s (and other commands like touch -d) use more sensible
+//config:	  formats (for one, ISO format YYYY-MM-DD hh:mm:ss.ssssss).
+//config:
+//config:	  With this option off, 'date DATE' is 'date -s DATE' support
+//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:
+//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>
+#endif
+
+enum {
+	OPT_RFC2822   = (1 << 0), /* R */
+	OPT_SET       = (1 << 1), /* s */
+	OPT_UTC       = (1 << 2), /* u */
+	OPT_DATE      = (1 << 3), /* d */
+	OPT_REFERENCE = (1 << 4), /* r */
+	OPT_TIMESPEC  = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */
+	OPT_HINT      = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
+};
+
+static void maybe_set_utc(int opt)
+{
+	if (opt & OPT_UTC)
+		putenv((char*)"TZ=UTC0");
+}
+
+#if ENABLE_LONG_OPTS
+static const char date_longopts[] ALIGN1 =
+		"rfc-822\0"   No_argument       "R"
+		"rfc-2822\0"  No_argument       "R"
+		"set\0"       Required_argument "s"
+		"utc\0"       No_argument       "u"
+	/*	"universal\0" No_argument       "u" */
+		"date\0"      Required_argument "d"
+		"reference\0" Required_argument "r"
+		;
+#endif
+
+int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int date_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct timespec ts;
+	struct tm tm_time;
+	char buf_fmt_dt2str[64];
+	unsigned opt;
+	int ifmt = -1;
+	char *date_str;
+	char *fmt_dt2str;
+	char *fmt_str2dt;
+	char *filename;
+	char *isofmt_arg = NULL;
+
+	opt_complementary = "d--s:s--d"
+		IF_FEATURE_DATE_ISOFMT(":R--I:I--R");
+	IF_LONG_OPTS(applet_long_options = date_longopts;)
+	opt = getopt32(argv, "Rs:ud:r:"
+			IF_FEATURE_DATE_ISOFMT("I::D:"),
+			&date_str, &date_str, &filename
+			IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt));
+	argv += optind;
+	maybe_set_utc(opt);
+
+	if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_TIMESPEC)) {
+		ifmt = 0; /* default is date */
+		if (isofmt_arg) {
+			static const char isoformats[] ALIGN1 =
+				"date\0""hours\0""minutes\0""seconds\0"; /* ns? */
+			ifmt = index_in_substrings(isoformats, isofmt_arg);
+			if (ifmt < 0)
+				bb_show_usage();
+		}
+	}
+
+	fmt_dt2str = NULL;
+	if (argv[0] && argv[0][0] == '+') {
+		fmt_dt2str = &argv[0][1]; /* skip over the '+' */
+		argv++;
+	}
+	if (!(opt & (OPT_SET | OPT_DATE))) {
+		opt |= OPT_SET;
+		date_str = argv[0]; /* can be NULL */
+		if (date_str) {
+#if ENABLE_FEATURE_DATE_COMPAT
+			int len = strspn(date_str, "0123456789");
+			if (date_str[len] == '\0'
+			 || (date_str[len] == '.'
+			    && isdigit(date_str[len+1])
+			    && isdigit(date_str[len+2])
+			    && date_str[len+3] == '\0'
+			    )
+			) {
+				/* Dreaded MMDDhhmm[[CC]YY][.ss] format!
+				 * It does not match -d or -s format.
+				 * Some users actually do use it.
+				 */
+				len -= 8;
+				if (len < 0 || len > 4 || (len & 1))
+					bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+				if (len != 0) { /* move YY or CCYY to front */
+					char buf[4];
+					memcpy(buf, date_str + 8, len);
+					memmove(date_str + len, date_str, 8);
+					memcpy(date_str, buf, len);
+				}
+			}
+#endif
+			argv++;
+		}
+	}
+	if (*argv)
+		bb_show_usage();
+
+	/* Now we have parsed all the information except the date format
+	 * which depends on whether the clock is being set or read */
+
+	if (opt & OPT_REFERENCE) {
+		struct stat statbuf;
+		xstat(filename, &statbuf);
+		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
+		/* libc has incredibly messy way of doing this,
+		 * typically requiring -lrt. We just skip all this mess */
+		syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts);
+#else
+		time(&ts.tv_sec);
+#endif
+	}
+	localtime_r(&ts.tv_sec, &tm_time);
+
+	/* If date string is given, update tm_time, and maybe set date */
+	if (date_str != NULL) {
+		/* Zero out fields - take her back to midnight! */
+		tm_time.tm_sec = 0;
+		tm_time.tm_min = 0;
+		tm_time.tm_hour = 0;
+
+		/* Process any date input to UNIX time since 1 Jan 1970 */
+		if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_HINT)) {
+			if (strptime(date_str, fmt_str2dt, &tm_time) == NULL)
+				bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+		} else {
+			parse_datestr(date_str, &tm_time);
+		}
+
+		/* Correct any day of week and day of year etc. fields */
+		/* 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);
+
+		/* if setting time, set it */
+		if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) {
+			bb_perror_msg("can't set date");
+		}
+	}
+
+	/* Display output */
+
+	/* Deal with format string */
+	if (fmt_dt2str == NULL) {
+		int i;
+		fmt_dt2str = buf_fmt_dt2str;
+		if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) {
+			/* -I[SPEC]: 0:date 1:hours 2:minutes 3:seconds */
+			strcpy(fmt_dt2str, "%Y-%m-%dT%H:%M:%S");
+			i = 8 + 3 * ifmt;
+			if (ifmt != 0) {
+				/* TODO: if (ifmt==4) i += sprintf(&fmt_dt2str[i], ",%09u", nanoseconds); */
+ format_utc:
+				fmt_dt2str[i++] = '%';
+				fmt_dt2str[i++] = (opt & OPT_UTC) ? 'Z' : 'z';
+			}
+			fmt_dt2str[i] = '\0';
+		} else if (opt & OPT_RFC2822) {
+			/* -R. undo busybox.c setlocale */
+			if (ENABLE_LOCALE_SUPPORT)
+				setlocale(LC_TIME, "C");
+			strcpy(fmt_dt2str, "%a, %d %b %Y %H:%M:%S ");
+			i = sizeof("%a, %d %b %Y %H:%M:%S ")-1;
+			goto format_utc;
+		} else { /* default case */
+			fmt_dt2str = (char*)"%a %b %e %H:%M:%S %Z %Y";
+		}
+	}
+#if ENABLE_FEATURE_DATE_NANO
+	else {
+		/* User-specified fmt_dt2str */
+		/* Search for and process "%N" */
+		char *p = fmt_dt2str;
+		while ((p = strchr(p, '%')) != NULL) {
+			int n, m;
+			unsigned pres, scale;
+
+			p++;
+			if (*p == '%') {
+				p++;
+				continue;
+			}
+			n = strspn(p, "0123456789");
+			if (p[n] != 'N') {
+				p += n;
+				continue;
+			}
+			/* We have "%[nnn]N" */
+			p[-1] = '\0';
+			p[n] = '\0';
+			scale = 1;
+			pres = 9;
+			if (n) {
+				pres = xatoi_positive(p);
+				if (pres == 0)
+					pres = 9;
+				m = 9 - pres;
+				while (--m >= 0)
+					scale *= 10;
+			}
+
+			m = p - fmt_dt2str;
+			p += n + 1;
+			fmt_dt2str = xasprintf("%s%0*u%s", fmt_dt2str, pres, (unsigned)ts.tv_nsec / scale, p);
+			p = fmt_dt2str + m;
+		}
+	}
+#endif
+
+#define date_buf bb_common_bufsiz1
+	if (*fmt_dt2str == '\0') {
+		/* With no format string, just print a blank line */
+		date_buf[0] = '\0';
+	} else {
+		/* Handle special conversions */
+		if (strncmp(fmt_dt2str, "%f", 2) == 0) {
+			fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S";
+		}
+		/* Generate output string */
+		strftime(date_buf, sizeof(date_buf), fmt_dt2str, &tm_time);
+	}
+	puts(date_buf);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/dd.c b/busybox-1.19.3/coreutils/dd.c
new file mode 100644
index 0000000..96602eb
--- /dev/null
+++ b/busybox-1.19.3/coreutils/dd.c
@@ -0,0 +1,439 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dd implementation for busybox
+ *
+ *
+ * Copyright (C) 2000,2001  Matt Kraai
+ *
+ * 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:	)
+//usage:     "\n"
+//usage:     "\nNumbers may be suffixed by c (x1), w (x2), b (x512), kD (x1000), k (x1024),"
+//usage:     "\nMD (x1000000), M (x1048576), GD (x1000000000) or G (x1073741824)"
+//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! */
+
+
+enum {
+	ifd = STDIN_FILENO,
+	ofd = STDOUT_FILENO,
+};
+
+static const struct suffix_mult dd_suffixes[] = {
+	{ "c", 1 },
+	{ "w", 2 },
+	{ "b", 512 },
+	{ "kD", 1000 },
+	{ "k", 1024 },
+	{ "K", 1024 },  /* compat with coreutils dd */
+	{ "MD", 1000000 },
+	{ "M", 1048576 },
+	{ "GD", 1000000000 },
+	{ "G", 1073741824 },
+	{ "", 0 }
+};
+
+struct globals {
+	off_t out_full, out_part, in_full, in_part;
+#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
+	unsigned long long total_bytes;
+	unsigned long long begin_time_us;
+#endif
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	/* we have to zero it out because of NOEXEC */ \
+	memset(&G, 0, sizeof(G)); \
+} while (0)
+
+
+static void dd_output_status(int UNUSED_PARAM cur_signal)
+{
+#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
+	double seconds;
+	unsigned long long bytes_sec;
+	unsigned long long now_us = monotonic_us(); /* before fprintf */
+#endif
+
+	/* Deliberately using %u, not %d */
+	fprintf(stderr, "%"OFF_FMT"u+%"OFF_FMT"u records in\n"
+			"%"OFF_FMT"u+%"OFF_FMT"u records out\n",
+			G.in_full, G.in_part,
+			G.out_full, G.out_part);
+
+#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
+	fprintf(stderr, "%llu bytes (%sB) copied, ",
+			G.total_bytes,
+			/* show fractional digit, use suffixes */
+			make_human_readable_str(G.total_bytes, 1, 0)
+	);
+	/* Corner cases:
+	 * ./busybox dd </dev/null >/dev/null
+	 * ./busybox dd bs=1M count=2000 </dev/zero >/dev/null
+	 * (echo DONE) | ./busybox dd >/dev/null
+	 * (sleep 1; echo DONE) | ./busybox dd >/dev/null
+	 */
+	seconds = (now_us - G.begin_time_us) / 1000000.0;
+	bytes_sec = G.total_bytes / seconds;
+	fprintf(stderr, "%f seconds, %sB/s\n",
+			seconds,
+			/* show fractional digit, use suffixes */
+			make_human_readable_str(bytes_sec, 1, 0)
+	);
+#endif
+}
+
+static ssize_t full_write_or_warn(const void *buf, size_t len,
+	const char *const filename)
+{
+	ssize_t n = full_write(ofd, buf, len);
+	if (n < 0)
+		bb_perror_msg("writing '%s'", filename);
+	return n;
+}
+
+static bool write_and_stats(const void *buf, size_t len, size_t obs,
+	const char *filename)
+{
+	ssize_t n = full_write_or_warn(buf, len, filename);
+	if (n < 0)
+		return 1;
+	if ((size_t)n == obs)
+		G.out_full++;
+	else if (n) /* > 0 */
+		G.out_part++;
+#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
+	G.total_bytes += n;
+#endif
+	return 0;
+}
+
+#if ENABLE_LFS
+# define XATOU_SFX xatoull_sfx
+#else
+# define XATOU_SFX xatoul_sfx
+#endif
+
+int dd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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,
+		/* end of conv flags */
+		FLAG_TWOBUFS = 1 << 4,
+		FLAG_COUNT   = 1 << 5,
+	};
+	static const char keywords[] ALIGN1 =
+		"bs\0""count\0""seek\0""skip\0""if\0""of\0"
+#if ENABLE_FEATURE_DD_IBS_OBS
+		"ibs\0""obs\0""conv\0"
+#endif
+		;
+#if ENABLE_FEATURE_DD_IBS_OBS
+	static const char conv_words[] ALIGN1 =
+		"notrunc\0""sync\0""noerror\0""fsync\0";
+#endif
+	enum {
+		OP_bs = 0,
+		OP_count,
+		OP_seek,
+		OP_skip,
+		OP_if,
+		OP_of,
+#if ENABLE_FEATURE_DD_IBS_OBS
+		OP_ibs,
+		OP_obs,
+		OP_conv,
+		/* Must be in the same order as FLAG_XXX! */
+		OP_conv_notrunc = 0,
+		OP_conv_sync,
+		OP_conv_noerror,
+		OP_conv_fsync,
+	/* 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
+	//unblock       replace trailing spaces in cbs-size records with newline
+	//ascii         from EBCDIC to ASCII
+	//ebcdic        from ASCII to EBCDIC
+	//ibm           from ASCII to alternate EBCDIC
+#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! */
+	struct {
+		int flags;
+		size_t oc;
+		off_t count;
+		off_t seek, skip;
+		const char *infile, *outfile;
+	} Z;
+#define flags   (Z.flags  )
+#define oc      (Z.oc     )
+#define count   (Z.count  )
+#define seek    (Z.seek   )
+#define skip    (Z.skip   )
+#define infile  (Z.infile )
+#define outfile (Z.outfile)
+
+	memset(&Z, 0, sizeof(Z));
+	INIT_G();
+	//fflush_all(); - is this needed because of NOEXEC?
+
+	for (n = 1; argv[n]; n++) {
+		int what;
+		char *val;
+		char *arg = argv[n];
+
+#if ENABLE_DESKTOP
+		/* "dd --". NB: coreutils 6.9 will complain if they see
+		 * more than one of them. We wouldn't. */
+		if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0')
+			continue;
+#endif
+		val = strchr(arg, '=');
+		if (val == NULL)
+			bb_show_usage();
+		*val = '\0';
+		what = index_in_strings(keywords, arg);
+		if (what < 0)
+			bb_show_usage();
+		/* *val = '='; - to preserve ps listing? */
+		val++;
+#if ENABLE_FEATURE_DD_IBS_OBS
+		if (what == OP_ibs) {
+			/* Must fit into positive ssize_t */
+			ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+			/*continue;*/
+		}
+		if (what == OP_obs) {
+			obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+			/*continue;*/
+		}
+		if (what == OP_conv) {
+			while (1) {
+				/* 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.
+				 */
+				arg = strchr(val, ',');
+				if (arg)
+					*arg = '\0';
+				what = index_in_strings(conv_words, val);
+				if (what < 0)
+					bb_error_msg_and_die(bb_msg_invalid_arg, val, "conv");
+				flags |= (1 << what);
+				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 */
+		}
+#endif
+		if (what == OP_bs) {
+			ibs = obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, dd_suffixes);
+			/*continue;*/
+		}
+		/* These can be large: */
+		if (what == OP_count) {
+			flags |= FLAG_COUNT;
+			count = XATOU_SFX(val, dd_suffixes);
+			/*continue;*/
+		}
+		if (what == OP_seek) {
+			seek = XATOU_SFX(val, dd_suffixes);
+			/*continue;*/
+		}
+		if (what == OP_skip) {
+			skip = XATOU_SFX(val, dd_suffixes);
+			/*continue;*/
+		}
+		if (what == OP_if) {
+			infile = val;
+			/*continue;*/
+		}
+		if (what == OP_of) {
+			outfile = val;
+			/*continue;*/
+		}
+	} /* end of "for (argv[n])" */
+
+//XXX:FIXME for huge ibs or obs, malloc'ing them isn't the brightest idea ever
+	ibuf = obuf = xmalloc(ibs);
+	if (ibs != obs) {
+		flags |= FLAG_TWOBUFS;
+		obuf = xmalloc(obs);
+	}
+
+#if ENABLE_FEATURE_DD_SIGNAL_HANDLING
+	signal_SA_RESTART_empty_mask(SIGUSR1, dd_output_status);
+#endif
+#if ENABLE_FEATURE_DD_THIRD_STATUS_LINE
+	G.begin_time_us = monotonic_us();
+#endif
+
+	if (infile != NULL)
+		xmove_fd(xopen(infile, O_RDONLY), ifd);
+	else {
+		infile = bb_msg_standard_input;
+	}
+	if (outfile != NULL) {
+		int oflag = O_WRONLY | O_CREAT;
+
+		if (!seek && !(flags & FLAG_NOTRUNC))
+			oflag |= O_TRUNC;
+
+		xmove_fd(xopen(outfile, oflag), ofd);
+
+		if (seek && !(flags & FLAG_NOTRUNC)) {
+			if (ftruncate(ofd, seek * obs) < 0) {
+				struct stat st;
+
+				if (fstat(ofd, &st) < 0
+				 || S_ISREG(st.st_mode)
+				 || S_ISDIR(st.st_mode)
+				) {
+					goto die_outfile;
+				}
+			}
+		}
+	} else {
+		outfile = bb_msg_standard_output;
+	}
+	if (skip) {
+		if (lseek(ifd, skip * ibs, SEEK_CUR) < 0) {
+			while (skip-- > 0) {
+				n = safe_read(ifd, ibuf, ibs);
+				if (n < 0)
+					goto die_infile;
+				if (n == 0)
+					break;
+			}
+		}
+	}
+	if (seek) {
+		if (lseek(ofd, seek * obs, SEEK_CUR) < 0)
+			goto die_outfile;
+	}
+
+	while (!(flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
+		n = safe_read(ifd, ibuf, ibs);
+		if (n == 0)
+			break;
+		if (n < 0) {
+			/* "Bad block" */
+			if (!(flags & FLAG_NOERROR))
+				goto die_infile;
+			bb_simple_perror_msg(infile);
+			/* GNU dd with conv=noerror skips over bad blocks */
+			xlseek(ifd, ibs, SEEK_CUR);
+			/* conv=noerror,sync writes NULs,
+			 * conv=noerror just ignores input bad blocks */
+			n = 0;
+		}
+		if ((size_t)n == ibs)
+			G.in_full++;
+		else {
+			G.in_part++;
+			if (flags & FLAG_SYNC) {
+				memset(ibuf + n, 0, ibs - n);
+				n = ibs;
+			}
+		}
+		if (flags & FLAG_TWOBUFS) {
+			char *tmp = ibuf;
+			while (n) {
+				size_t d = obs - oc;
+
+				if (d > (size_t)n)
+					d = n;
+				memcpy(obuf + oc, tmp, d);
+				n -= d;
+				tmp += d;
+				oc += d;
+				if (oc == obs) {
+					if (write_and_stats(obuf, obs, obs, outfile))
+						goto out_status;
+					oc = 0;
+				}
+			}
+		} else if (write_and_stats(ibuf, n, obs, outfile))
+			goto out_status;
+
+		if (flags & FLAG_FSYNC) {
+			if (fsync(ofd) < 0)
+				goto die_outfile;
+		}
+	}
+
+	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 (close(ifd) < 0) {
+ die_infile:
+		bb_simple_perror_msg_and_die(infile);
+	}
+
+	if (close(ofd) < 0) {
+ die_outfile:
+		bb_simple_perror_msg_and_die(outfile);
+	}
+
+	exitcode = EXIT_SUCCESS;
+ out_status:
+	dd_output_status(0);
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(obuf);
+		if (flags & FLAG_TWOBUFS)
+			free(ibuf);
+	}
+
+	return exitcode;
+}
diff --git a/busybox-1.19.3/coreutils/df.c b/busybox-1.19.3/coreutils/df.c
new file mode 100644
index 0000000..63dbd61
--- /dev/null
+++ b/busybox-1.19.3/coreutils/df.c
@@ -0,0 +1,255 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini df implementation for busybox
+ *
+ * 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 source tree.
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- option -t missing. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/df.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reduction.  Removed floating point dependency.  Added error checking
+ * on output.  Output stats on 0-sized filesystems if specifically listed on
+ * the command line.  Properly round *-blocks, Used, and Available quantities.
+ *
+ * Aug 28, 2008      Bernhard Reutner-Fischer
+ *
+ * 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"
+#include "unicode.h"
+
+#if !ENABLE_FEATURE_HUMAN_READABLE
+static unsigned long kscale(unsigned long b, unsigned long bs)
+{
+	return (b * (unsigned long long) bs + 1024/2) / 1024;
+}
+#endif
+
+int df_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int df_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned long blocks_used;
+	unsigned blocks_percent_used;
+	unsigned long df_disp_hr = 1024;
+	int status = EXIT_SUCCESS;
+	unsigned opt;
+	FILE *mount_table;
+	struct mntent *mount_entry;
+	struct statfs s;
+
+	enum {
+		OPT_KILO  = (1 << 0),
+		OPT_POSIX = (1 << 1),
+		OPT_ALL   = (1 << 2) * ENABLE_FEATURE_DF_FANCY,
+		OPT_INODE = (1 << 3) * ENABLE_FEATURE_DF_FANCY,
+		OPT_BSIZE = (1 << 4) * ENABLE_FEATURE_DF_FANCY,
+		OPT_HUMAN = (1 << (2 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
+		OPT_MEGA  = (1 << (3 + 3*ENABLE_FEATURE_DF_FANCY)) * ENABLE_FEATURE_HUMAN_READABLE,
+	};
+	const char *disp_units_hdr = NULL;
+	char *chp;
+
+	init_unicode();
+
+#if ENABLE_FEATURE_HUMAN_READABLE && ENABLE_FEATURE_DF_FANCY
+	opt_complementary = "k-mB:m-Bk:B-km";
+#elif ENABLE_FEATURE_HUMAN_READABLE
+	opt_complementary = "k-m:m-k";
+#endif
+	opt = getopt32(argv, "kP"
+			IF_FEATURE_DF_FANCY("aiB:")
+			IF_FEATURE_HUMAN_READABLE("hm")
+			IF_FEATURE_DF_FANCY(, &chp));
+	if (opt & OPT_MEGA)
+		df_disp_hr = 1024*1024;
+
+	if (opt & OPT_BSIZE)
+		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.
+	*/
+	if (getenv("POSIXLY_CORRECT")) /* TODO - a new libbb function? */
+		df_disp_hr = 512;
+
+	if (opt & OPT_HUMAN) {
+		df_disp_hr = 0;
+		disp_units_hdr = "     Size";
+	}
+	if (opt & OPT_INODE)
+		disp_units_hdr = "   Inodes";
+
+	if (disp_units_hdr == NULL) {
+#if ENABLE_FEATURE_HUMAN_READABLE
+		disp_units_hdr = xasprintf("%s-blocks",
+			/* print df_disp_hr, show no fractionals,
+			 * use suffixes if OPT_POSIX is set in opt */
+			make_human_readable_str(df_disp_hr, 0, !!(opt & OPT_POSIX))
+		);
+#else
+		disp_units_hdr = xasprintf("%lu-blocks", df_disp_hr);
+#endif
+	}
+	printf("Filesystem           %-15sUsed Available %s Mounted on\n",
+			disp_units_hdr, (opt & OPT_POSIX) ? "Capacity" : "Use%");
+
+	mount_table = NULL;
+	argv += optind;
+	if (!argv[0]) {
+		mount_table = setmntent(bb_path_mtab_file, "r");
+		if (!mount_table)
+			bb_perror_msg_and_die(bb_path_mtab_file);
+	}
+
+	while (1) {
+		const char *device;
+		const char *mount_point;
+
+		if (mount_table) {
+			mount_entry = getmntent(mount_table);
+			if (!mount_entry) {
+				endmntent(mount_table);
+				break;
+			}
+		} else {
+			mount_point = *argv++;
+			if (!mount_point)
+				break;
+			mount_entry = find_mount_point(mount_point, 1);
+			if (!mount_entry) {
+				bb_error_msg("%s: can't find mount point", mount_point);
+ set_error:
+				status = EXIT_FAILURE;
+				continue;
+			}
+		}
+
+		device = mount_entry->mnt_fsname;
+		mount_point = mount_entry->mnt_dir;
+
+		if (statfs(mount_point, &s) != 0) {
+			bb_simple_perror_msg(mount_point);
+			goto set_error;
+		}
+
+		if ((s.f_blocks > 0) || !mount_table || (opt & OPT_ALL)) {
+			if (opt & OPT_INODE) {
+				s.f_blocks = s.f_files;
+				s.f_bavail = s.f_bfree = s.f_ffree;
+				s.f_bsize = 1;
+
+				if (df_disp_hr)
+					df_disp_hr = 1;
+			}
+			blocks_used = s.f_blocks - s.f_bfree;
+			blocks_percent_used = 0;
+			if (blocks_used + s.f_bavail) {
+				blocks_percent_used = (blocks_used * 100ULL
+						+ (blocks_used + s.f_bavail)/2
+						) / (blocks_used + s.f_bavail);
+			}
+
+			/* GNU coreutils 6.10 skips certain mounts, try to be compatible.  */
+			if (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(device, "rootfs") == 0)
+				continue;
+
+#ifdef WHY_WE_DO_IT_FOR_DEV_ROOT_ONLY
+			if (strcmp(device, "/dev/root") == 0) {
+				/* Adjusts device to be the real root device,
+				 * or leaves device alone if it can't find it */
+				device = find_block_device("/");
+				if (!device) {
+					goto set_error;
+				}
+			}
+#endif
+
+#if ENABLE_UNICODE_SUPPORT
+			{
+				uni_stat_t uni_stat;
+				char *uni_dev = unicode_conv_to_printable(&uni_stat, device);
+				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, "");
+				}
+				free(uni_dev);
+			}
+#else
+			if (printf("\n%-20s" + 1, device) > 20 && !(opt & OPT_POSIX))
+				    printf("\n%-20s", "");
+#endif
+
+#if ENABLE_FEATURE_HUMAN_READABLE
+			printf(" %9s ",
+				/* f_blocks x f_bsize / df_disp_hr, show one fractional,
+				 * use suffixes if df_disp_hr == 0 */
+				make_human_readable_str(s.f_blocks, s.f_bsize, df_disp_hr));
+
+			printf(" %9s " + 1,
+				/* EXPR x f_bsize / df_disp_hr, show one fractional,
+				 * use suffixes if df_disp_hr == 0 */
+				make_human_readable_str((s.f_blocks - s.f_bfree),
+						s.f_bsize, df_disp_hr));
+
+			printf("%9s %3u%% %s\n",
+				/* f_bavail x f_bsize / df_disp_hr, show one fractional,
+				 * use suffixes if df_disp_hr == 0 */
+				make_human_readable_str(s.f_bavail, s.f_bsize, df_disp_hr),
+				blocks_percent_used, mount_point);
+#else
+			printf(" %9lu %9lu %9lu %3u%% %s\n",
+				kscale(s.f_blocks, s.f_bsize),
+				kscale(s.f_blocks - s.f_bfree, s.f_bsize),
+				kscale(s.f_bavail, s.f_bsize),
+				blocks_percent_used, mount_point);
+#endif
+		}
+	}
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/dirname.c b/busybox-1.19.3/coreutils/dirname.c
new file mode 100644
index 0000000..101067c
--- /dev/null
+++ b/busybox-1.19.3/coreutils/dirname.c
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini dirname implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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! */
+
+int dirname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dirname_main(int argc UNUSED_PARAM, char **argv)
+{
+	puts(dirname(single_argv(argv)));
+	return fflush_all();
+}
diff --git a/busybox-1.19.3/coreutils/dos2unix.c b/busybox-1.19.3/coreutils/dos2unix.c
new file mode 100644
index 0000000..07398bd
--- /dev/null
+++ b/busybox-1.19.3/coreutils/dos2unix.c
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dos2unix for BusyBox
+ *
+ * dos2unix '\n' convertor 0.5.0
+ * based on Unix2Dos 0.9.0 by Peter Hanecak (made 19.2.1997)
+ * Copyright 1997,.. by Peter Hanecak <hanecak@megaloman.sk>.
+ * All rights reserved.
+ *
+ * dos2unix filters reading input from stdin and writing output to stdout.
+ *
+ * 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
+};
+
+/* if fn is NULL then input is stdin and output is stdout */
+static void convert(char *fn, int conv_type)
+{
+	FILE *in, *out;
+	int i;
+	char *temp_fn = temp_fn; /* for compiler */
+	char *resolved_fn = resolved_fn;
+
+	in = stdin;
+	out = stdout;
+	if (fn != NULL) {
+		struct stat st;
+
+		resolved_fn = xmalloc_follow_symlinks(fn);
+		if (resolved_fn == NULL)
+			bb_simple_perror_msg_and_die(fn);
+		in = xfopen_for_read(resolved_fn);
+		fstat(fileno(in), &st);
+
+		temp_fn = xasprintf("%sXXXXXX", resolved_fn);
+		i = xmkstemp(temp_fn);
+		if (fchmod(i, st.st_mode) == -1)
+			bb_simple_perror_msg_and_die(temp_fn);
+
+		out = xfdopen_for_write(i);
+	}
+
+	while ((i = fgetc(in)) != EOF) {
+		if (i == '\r')
+			continue;
+		if (i == '\n')
+			if (conv_type == CT_UNIX2DOS)
+				fputc('\r', out);
+		fputc(i, out);
+	}
+
+	if (fn != NULL) {
+		if (fclose(in) < 0 || fclose(out) < 0) {
+			unlink(temp_fn);
+			bb_perror_nomsg_and_die();
+		}
+		xrename(temp_fn, resolved_fn);
+		free(temp_fn);
+		free(resolved_fn);
+	}
+}
+
+int dos2unix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dos2unix_main(int argc UNUSED_PARAM, char **argv)
+{
+	int o, conv_type;
+
+	/* See if we are supposed to be doing dos2unix or unix2dos */
+	conv_type = CT_UNIX2DOS;
+	if (applet_name[0] == 'd') {
+		conv_type = CT_DOS2UNIX;
+	}
+
+	/* -u convert to unix, -d convert to dos */
+	opt_complementary = "u--d:d--u"; /* mutually exclusive */
+	o = getopt32(argv, "du");
+
+	/* Do the conversion requested by an argument else do the default
+	 * conversion depending on our name.  */
+	if (o)
+		conv_type = o;
+
+	argv += optind;
+	do {
+		/* might be convert(NULL) if there is no filename given */
+		convert(*argv, conv_type);
+	} while (*argv && *++argv);
+
+	return 0;
+}
diff --git a/busybox-1.19.3/coreutils/du.c b/busybox-1.19.3/coreutils/du.c
new file mode 100644
index 0000000..b8bbe3d
--- /dev/null
+++ b/busybox-1.19.3/coreutils/du.c
@@ -0,0 +1,267 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini du implementation for busybox
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and John Beppu
+ * 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 source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant (unless default blocksize set to 1k) */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/du.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Mostly rewritten for SUSv3 compliance and to fix bugs/defects.
+ * 1) Added support for SUSv3 -a, -H, -L, gnu -c, and (busybox) -d options.
+ *    The -d option allows setting of max depth (similar to gnu --max-depth).
+ * 2) Fixed incorrect size calculations for links and directories, especially
+ *    when errors occurred.  Calculates sizes should now match gnu du output.
+ * 3) Added error checking of output.
+ * 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:       "Disk space is printed in units of "
+//usage:	IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("1024")
+//usage:	IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K("512")
+//usage:       " bytes.\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"
+//usage:			IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(" (default)")
+//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 {
+	OPT_a_files_too    = (1 << 0),
+	OPT_H_follow_links = (1 << 1),
+	OPT_k_kbytes       = (1 << 2),
+	OPT_L_follow_links = (1 << 3),
+	OPT_s_total_norecurse = (1 << 4),
+	OPT_x_one_FS       = (1 << 5),
+	OPT_d_maxdepth     = (1 << 6),
+	OPT_l_hardlinks    = (1 << 7),
+	OPT_c_total        = (1 << 8),
+	OPT_h_for_humans   = (1 << 9),
+	OPT_m_mbytes       = (1 << 10),
+};
+
+struct globals {
+#if ENABLE_FEATURE_HUMAN_READABLE
+	unsigned long disp_hr;
+#else
+	unsigned disp_k;
+#endif
+	int max_print_depth;
+	bool status;
+	int slink_depth;
+	int du_depth;
+	dev_t dir_dev;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+
+
+static void print(unsigned long size, const char *filename)
+{
+	/* TODO - May not want to defer error checking here. */
+#if ENABLE_FEATURE_HUMAN_READABLE
+	printf("%s\t%s\n",
+			/* size x 512 / G.disp_hr, show one fractional,
+			 * use suffixes if G.disp_hr == 0 */
+			make_human_readable_str(size, 512, G.disp_hr),
+			filename);
+#else
+	if (G.disp_k) {
+		size++;
+		size >>= 1;
+	}
+	printf("%lu\t%s\n", size, filename);
+#endif
+}
+
+/* tiny recursive du */
+static unsigned long du(const char *filename)
+{
+	struct stat statbuf;
+	unsigned long sum;
+
+	if (lstat(filename, &statbuf) != 0) {
+		bb_simple_perror_msg(filename);
+		G.status = EXIT_FAILURE;
+		return 0;
+	}
+
+	if (option_mask32 & OPT_x_one_FS) {
+		if (G.du_depth == 0) {
+			G.dir_dev = statbuf.st_dev;
+		} else if (G.dir_dev != statbuf.st_dev) {
+			return 0;
+		}
+	}
+
+	sum = statbuf.st_blocks;
+
+	if (S_ISLNK(statbuf.st_mode)) {
+		if (G.slink_depth > G.du_depth) { /* -H or -L */
+			if (stat(filename, &statbuf) != 0) {
+				bb_simple_perror_msg(filename);
+				G.status = EXIT_FAILURE;
+				return 0;
+			}
+			sum = statbuf.st_blocks;
+			if (G.slink_depth == 1) {
+				/* Convert -H to -L */
+				G.slink_depth = INT_MAX;
+			}
+		}
+	}
+
+	if (!(option_mask32 & OPT_l_hardlinks)
+	 && statbuf.st_nlink > 1
+	) {
+		/* Add files/directories with links only once */
+		if (is_in_ino_dev_hashtable(&statbuf)) {
+			return 0;
+		}
+		add_to_ino_dev_hashtable(&statbuf, NULL);
+	}
+
+	if (S_ISDIR(statbuf.st_mode)) {
+		DIR *dir;
+		struct dirent *entry;
+		char *newfile;
+
+		dir = warn_opendir(filename);
+		if (!dir) {
+			G.status = EXIT_FAILURE;
+			return sum;
+		}
+
+		while ((entry = readdir(dir))) {
+			newfile = concat_subpath_file(filename, entry->d_name);
+			if (newfile == NULL)
+				continue;
+			++G.du_depth;
+			sum += du(newfile);
+			--G.du_depth;
+			free(newfile);
+		}
+		closedir(dir);
+	} else {
+		if (!(option_mask32 & OPT_a_files_too) && G.du_depth != 0)
+			return sum;
+	}
+	if (G.du_depth <= G.max_print_depth) {
+		print(sum, filename);
+	}
+	return sum;
+}
+
+int du_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int du_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned long total;
+	int slink_depth_save;
+	unsigned opt;
+
+#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;)
+	if (getenv("POSIXLY_CORRECT"))  /* TODO - a new libbb function? */
+		G.disp_hr = 512;
+#else
+	IF_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 1;)
+	/* IF_NOT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K(G.disp_k = 0;) - G is pre-zeroed */
+#endif
+	G.max_print_depth = INT_MAX;
+
+	/* Note: SUSv3 specifies that -a and -s options cannot be used together
+	 * in strictly conforming applications.  However, it also says that some
+	 * du implementations may produce output when -a and -s are used together.
+	 * gnu du exits with an error code in this case.  We choose to simply
+	 * ignore -a.  This is consistent with -s being equivalent to -d 0.
+	 */
+#if ENABLE_FEATURE_HUMAN_READABLE
+	opt_complementary = "h-km:k-hm:m-hk:H-L:L-H:s-d:d-s:d+";
+	opt = getopt32(argv, "aHkLsx" "d:" "lc" "hm", &G.max_print_depth);
+	argv += optind;
+	if (opt & OPT_h_for_humans) {
+		G.disp_hr = 0;
+	}
+	if (opt & OPT_m_mbytes) {
+		G.disp_hr = 1024*1024;
+	}
+	if (opt & OPT_k_kbytes) {
+		G.disp_hr = 1024;
+	}
+#else
+	opt_complementary = "H-L:L-H:s-d:d-s:d+";
+	opt = getopt32(argv, "aHkLsx" "d:" "lc", &G.max_print_depth);
+	argv += optind;
+#if !ENABLE_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+	if (opt & OPT_k_kbytes) {
+		G.disp_k = 1;
+	}
+#endif
+#endif
+	if (opt & OPT_H_follow_links) {
+		G.slink_depth = 1;
+	}
+	if (opt & OPT_L_follow_links) {
+		G.slink_depth = INT_MAX;
+	}
+	if (opt & OPT_s_total_norecurse) {
+		G.max_print_depth = 0;
+	}
+
+	/* go through remaining args (if any) */
+	if (!*argv) {
+		*--argv = (char*)".";
+		if (G.slink_depth == 1) {
+			G.slink_depth = 0;
+		}
+	}
+
+	slink_depth_save = G.slink_depth;
+	total = 0;
+	do {
+		total += du(*argv);
+		/* otherwise du /dir /dir won't show /dir twice: */
+		reset_ino_dev_hashtable();
+		G.slink_depth = slink_depth_save;
+	} while (*++argv);
+
+	if (opt & OPT_c_total)
+		print(total, "total");
+
+	fflush_stdout_and_exit(G.status);
+}
diff --git a/busybox-1.19.3/coreutils/echo.c b/busybox-1.19.3/coreutils/echo.c
new file mode 100644
index 0000000..9663894
--- /dev/null
+++ b/busybox-1.19.3/coreutils/echo.c
@@ -0,0 +1,332 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * echo implementation for busybox
+ *
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Because of behavioral differences, implemented configurable SUSv3
+ * or 'fancy' gnu-ish behaviors.  Also, reduced size and fixed bugs.
+ * 1) In handling '\c' escape, the previous version only suppressed the
+ *     trailing newline.  SUSv3 specifies _no_ output after '\c'.
+ * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
+ *    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' */
+	};
+
+	argv++;
+#else
+	char nflag = 1;
+	char eflag = 0;
+
+	while ((arg = *++argv) != NULL) {
+		char n, e;
+
+		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.
+		 */
+		arg++;
+		n = nflag;
+		e = eflag;
+		do {
+			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 (*++arg);
+		nflag = n;
+		eflag = e;
+	}
+ just_echo:
+#endif
+
+	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 */
+			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
+					 * ignore all subsequent chars. */
+					goto do_write;
+				}
+				/* 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). */
+				{
+					/* 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;
+				}
+			}
+			*out++ = c;
+		}
+
+		if (!*++argv)
+			break;
+		*out++ = ' ';
+	}
+
+	if (nflag) {
+		*out++ = '\n';
+	}
+
+ 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.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *		ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ *	California, Berkeley and its contributors.
+ * 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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ *	@(#)echo.c	8.1 (Berkeley) 5/31/93
+ */
+
+#ifdef VERSION_WITH_WRITEV
+/* We can't use stdio.
+ * 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.
+ *
+ * Using writev instead, with 'direct' conversion of argv vector.
+ */
+
+int echo_main(int argc, char **argv)
+{
+	struct iovec io[argc];
+	struct iovec *cur_io = io;
+	char *arg;
+	char *p;
+#if !ENABLE_FEATURE_FANCY_ECHO
+	enum {
+		eflag = '\\',
+		nflag = 1,  /* 1 -- print '\n' */
+	};
+	arg = *++argv;
+	if (!arg)
+		goto newline_ret;
+#else
+	char nflag = 1;
+	char eflag = 0;
+
+	while (1) {
+		arg = *++argv;
+		if (!arg)
+			goto newline_ret;
+		if (*arg != '-')
+			break;
+
+		/* 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;
+
+		do {
+			if (!strchr("neE", *p))
+				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);
+	}
+ just_echo:
+#endif
+
+	while (1) {
+		/* arg is already == *argv and isn't NULL */
+		int c;
+
+		cur_io->iov_base = p = arg;
+
+		if (!eflag) {
+			/* optimization for very common case */
+			p += strlen(arg);
+		} else while ((c = *arg++)) {
+			if (c == eflag) {
+				/* This is an "\x" sequence */
+
+				if (*arg == 'c') {
+					/* "\c" means cancel newline and
+					 * ignore all subsequent chars. */
+					cur_io->iov_len = p - (char*)cur_io->iov_base;
+					cur_io++;
+					goto ret;
+				}
+				/* 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;
+		}
+
+		arg = *++argv;
+		if (arg)
+			*p++ = ' ';
+		cur_io->iov_len = p - (char*)cur_io->iov_base;
+		cur_io++;
+		if (!arg)
+			break;
+	}
+
+ newline_ret:
+	if (nflag) {
+		cur_io->iov_base = (char*)"\n";
+		cur_io->iov_len = 1;
+		cur_io++;
+	}
+ ret:
+	/* TODO: implement and use full_writev? */
+	return writev(1, io, (cur_io - io)) >= 0;
+}
+#endif
diff --git a/busybox-1.19.3/coreutils/env.c b/busybox-1.19.3/coreutils/env.c
new file mode 100644
index 0000000..807ef13
--- /dev/null
+++ b/busybox-1.19.3/coreutils/env.c
@@ -0,0 +1,132 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * env implementation for busybox
+ *
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@codepoet.org>
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/env.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Fixed bug involving exit return codes if execvp fails.  Also added
+ * output error checking.
+ */
+
+/*
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
+ * - correct "-" option usage
+ * - multiple "-u unsetenv" support
+ * - GNU long option support
+ * - use xfunc_error_retval
+ */
+
+/* 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
+static const char env_longopts[] ALIGN1 =
+	"ignore-environment\0" No_argument       "i"
+	"unset\0"              Required_argument "u"
+	;
+#endif
+
+int env_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int env_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opts;
+	llist_t *unset_env = NULL;
+
+	opt_complementary = "u::";
+#if ENABLE_FEATURE_ENV_LONG_OPTIONS
+	applet_long_options = env_longopts;
+#endif
+	opts = getopt32(argv, "+iu:", &unset_env);
+	argv += optind;
+	if (argv[0] && LONE_DASH(argv[0])) {
+		opts |= 1;
+		++argv;
+	}
+	if (opts & 1) {
+		clearenv();
+	}
+	while (unset_env) {
+		char *var = llist_pop(&unset_env);
+		/* This does not handle -uVAR=VAL
+		 * (coreutils _sets_ the variable in that case): */
+		/*unsetenv(var);*/
+		/* This does, but uses somewhan undocumented feature that
+		 * putenv("name_without_equal_sign") unsets the variable: */
+		putenv(var);
+	}
+
+	while (*argv && (strchr(*argv, '=') != NULL)) {
+		if (putenv(*argv) < 0) {
+			bb_perror_msg_and_die("putenv");
+		}
+		++argv;
+	}
+
+	if (argv[0]) {
+		BB_EXECVP_or_die(argv);
+	}
+
+	if (environ) { /* clearenv() may set environ == NULL! */
+		char **ep;
+		for (ep = environ; *ep; ep++) {
+			puts(*ep);
+		}
+	}
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
+
+/*
+ * Copyright (c) 1988, 1993, 1994
+ *      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
+ * 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. 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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/coreutils/expand.c b/busybox-1.19.3/coreutils/expand.c
new file mode 100644
index 0000000..25bbffc
--- /dev/null
+++ b/busybox-1.19.3/coreutils/expand.c
@@ -0,0 +1,233 @@
+/* expand - convert tabs to spaces
+ * unexpand - convert spaces to tabs
+ *
+ * Copyright (C) 89, 91, 1995-2006 Free Software Foundation, Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * David MacKenzie <djm@gnu.ai.mit.edu>
+ *
+ * Options for expand:
+ * -t num  --tabs=NUM      Convert tabs to num spaces (default 8 spaces).
+ * -i      --initial       Only convert initial tabs on each line to spaces.
+ *
+ * Options for unexpand:
+ * -a      --all           Convert all blanks, instead of just initial blanks.
+ * -f      --first-only    Convert only leading sequences of blanks (default).
+ * -t num  --tabs=NUM      Have tabs num characters apart instead of 8.
+ *
+ *  Busybox version (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
+ *
+ *  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"
+
+enum {
+	OPT_INITIAL     = 1 << 0,
+	OPT_TABS        = 1 << 1,
+	OPT_ALL         = 1 << 2,
+};
+
+#if ENABLE_EXPAND
+static void expand(FILE *file, unsigned tab_size, unsigned opt)
+{
+	char *line;
+
+	while ((line = xmalloc_fgets(file)) != NULL) {
+		unsigned char c;
+		char *ptr;
+		char *ptr_strbeg;
+
+		ptr = ptr_strbeg = line;
+		while ((c = *ptr) != '\0') {
+			if ((opt & OPT_INITIAL) && !isblank(c)) {
+				/* not space or tab */
+				break;
+			}
+			if (c == '\t') {
+				unsigned len;
+				*ptr = '\0';
+# if ENABLE_UNICODE_SUPPORT
+				{
+					uni_stat_t uni_stat;
+					printable_string(&uni_stat, ptr_strbeg);
+					len = uni_stat.unicode_width;
+				}
+# else
+				len = ptr - ptr_strbeg;
+# endif
+				len = tab_size - (len % tab_size);
+				/*while (ptr[1] == '\t') { ptr++; len += tab_size; } - can handle many tabs at once */
+				printf("%s%*s", ptr_strbeg, len, "");
+				ptr_strbeg = ptr + 1;
+			}
+			ptr++;
+		}
+		fputs(ptr_strbeg, stdout);
+		free(line);
+	}
+}
+#endif
+
+#if ENABLE_UNEXPAND
+static void unexpand(FILE *file, unsigned tab_size, unsigned opt)
+{
+	char *line;
+
+	while ((line = xmalloc_fgets(file)) != NULL) {
+		char *ptr = line;
+		unsigned column = 0;
+
+		while (*ptr) {
+			unsigned n;
+			unsigned len = 0;
+
+			while (*ptr == ' ') {
+				ptr++;
+				len++;
+			}
+			column += len;
+			if (*ptr == '\t') {
+				column += tab_size - (column % tab_size);
+				ptr++;
+				continue;
+			}
+
+			n = column / tab_size;
+			if (n) {
+				len = column = column % tab_size;
+				while (n--)
+					putchar('\t');
+			}
+
+			if ((opt & OPT_INITIAL) && ptr != line) {
+				printf("%*s%s", len, "", ptr);
+				break;
+			}
+			n = strcspn(ptr, "\t ");
+			printf("%*s%.*s", len, "", n, ptr);
+# if ENABLE_UNICODE_SUPPORT
+			{
+				char c;
+				uni_stat_t uni_stat;
+				c = ptr[n];
+				ptr[n] = '\0';
+				printable_string(&uni_stat, ptr);
+				len = uni_stat.unicode_width;
+				ptr[n] = c;
+			}
+# else
+			len = n;
+# endif
+			ptr += n;
+			column = (column + len) % tab_size;
+		}
+		free(line);
+	}
+}
+#endif
+
+int expand_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int expand_main(int argc UNUSED_PARAM, char **argv)
+{
+	/* Default 8 spaces for 1 tab */
+	const char *opt_t = "8";
+	FILE *file;
+	unsigned tab_size;
+	unsigned opt;
+	int exit_status = EXIT_SUCCESS;
+
+#if ENABLE_FEATURE_EXPAND_LONG_OPTIONS
+	static const char expand_longopts[] ALIGN1 =
+		/* name, has_arg, val */
+		"initial\0"          No_argument       "i"
+		"tabs\0"             Required_argument "t"
+	;
+#endif
+#if ENABLE_FEATURE_UNEXPAND_LONG_OPTIONS
+	static const char unexpand_longopts[] ALIGN1 =
+		/* name, has_arg, val */
+		"first-only\0"       No_argument       "i"
+		"tabs\0"             Required_argument "t"
+		"all\0"              No_argument       "a"
+	;
+#endif
+	init_unicode();
+
+	if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e')) {
+		IF_FEATURE_EXPAND_LONG_OPTIONS(applet_long_options = expand_longopts);
+		opt = getopt32(argv, "it:", &opt_t);
+	} else {
+		IF_FEATURE_UNEXPAND_LONG_OPTIONS(applet_long_options = unexpand_longopts);
+		/* -t NUM sets also -a */
+		opt_complementary = "ta";
+		opt = getopt32(argv, "ft:a", &opt_t);
+		/* -f --first-only is the default */
+		if (!(opt & OPT_ALL)) opt |= OPT_INITIAL;
+	}
+	tab_size = xatou_range(opt_t, 1, UINT_MAX);
+
+	argv += optind;
+
+	if (!*argv) {
+		*--argv = (char*)bb_msg_standard_input;
+	}
+	do {
+		file = fopen_or_warn_stdin(*argv);
+		if (!file) {
+			exit_status = EXIT_FAILURE;
+			continue;
+		}
+
+		if (ENABLE_EXPAND && (!ENABLE_UNEXPAND || applet_name[0] == 'e'))
+			IF_EXPAND(expand(file, tab_size, opt));
+		else
+			IF_UNEXPAND(unexpand(file, tab_size, opt));
+
+		/* Check and close the file */
+		if (fclose_if_not_stdin(file)) {
+			bb_simple_perror_msg(*argv);
+			exit_status = EXIT_FAILURE;
+		}
+		/* If stdin also clear EOF */
+		if (file == stdin)
+			clearerr(file);
+	} while (*++argv);
+
+	/* Now close stdin also */
+	/* (if we didn't read from it, it's a no-op) */
+	if (fclose(stdin))
+		bb_perror_msg_and_die(bb_msg_standard_input);
+
+	fflush_stdout_and_exit(exit_status);
+}
diff --git a/busybox-1.19.3/coreutils/expr.c b/busybox-1.19.3/coreutils/expr.c
new file mode 100644
index 0000000..24e75b5
--- /dev/null
+++ b/busybox-1.19.3/coreutils/expr.c
@@ -0,0 +1,535 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini expr implementation for busybox
+ *
+ * based on GNU expr Mike Parker.
+ * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
+ *
+ * Busybox modifications
+ * Copyright (c) 2000  Edward Betts <edward@debian.org>.
+ * Copyright (C) 2003-2005  Vladimir Oleynik <dzo@simtreas.ru>
+ *  - reduced 464 bytes.
+ *  - 64 math support
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* This program evaluates expressions.  Each token (operator, operand,
+ * parenthesis) of the expression must be a separate argument.  The
+ * parser used is a reasonably general one, though any incarnation of
+ * it is language-specific.  It is especially nice for expressions.
+ *
+ * No parse tree is needed; a new node is evaluated immediately.
+ * One function can handle multiple operators all of equal precedence,
+ * provided they all associate ((x op x) op x). */
+
+/* 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"
+
+#if ENABLE_EXPR_MATH_SUPPORT_64
+typedef int64_t arith_t;
+
+#define PF_REZ      "ll"
+#define PF_REZ_TYPE (long long)
+#define STRTOL(s, e, b) strtoll(s, e, b)
+#else
+typedef long arith_t;
+
+#define PF_REZ      "l"
+#define PF_REZ_TYPE (long)
+#define STRTOL(s, e, b) strtol(s, e, b)
+#endif
+
+/* TODO: use bb_strtol[l]? It's easier to check for errors... */
+
+/* The kinds of value we can have.  */
+enum {
+	INTEGER,
+	STRING
+};
+
+/* A value is.... */
+struct valinfo {
+	smallint type;                  /* Which kind. */
+	union {                         /* The value itself. */
+		arith_t i;
+		char *s;
+	} u;
+};
+typedef struct valinfo VALUE;
+
+/* The arguments given to the program, minus the program name.  */
+struct globals {
+	char **args;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+
+/* forward declarations */
+static VALUE *eval(void);
+
+
+/* Return a VALUE for I.  */
+
+static VALUE *int_value(arith_t i)
+{
+	VALUE *v;
+
+	v = xzalloc(sizeof(VALUE));
+	if (INTEGER) /* otherwise xzaaloc did it already */
+		v->type = INTEGER;
+	v->u.i = i;
+	return v;
+}
+
+/* Return a VALUE for S.  */
+
+static VALUE *str_value(const char *s)
+{
+	VALUE *v;
+
+	v = xzalloc(sizeof(VALUE));
+	if (STRING) /* otherwise xzaaloc did it already */
+		v->type = STRING;
+	v->u.s = xstrdup(s);
+	return v;
+}
+
+/* Free VALUE V, including structure components.  */
+
+static void freev(VALUE *v)
+{
+	if (v->type == STRING)
+		free(v->u.s);
+	free(v);
+}
+
+/* Return nonzero if V is a null-string or zero-number.  */
+
+static int null(VALUE *v)
+{
+	if (v->type == INTEGER)
+		return v->u.i == 0;
+	/* STRING: */
+	return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0');
+}
+
+/* Coerce V to a STRING value (can't fail).  */
+
+static void tostring(VALUE *v)
+{
+	if (v->type == INTEGER) {
+		v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i);
+		v->type = STRING;
+	}
+}
+
+/* Coerce V to an INTEGER value.  Return 1 on success, 0 on failure.  */
+
+static bool toarith(VALUE *v)
+{
+	if (v->type == STRING) {
+		arith_t i;
+		char *e;
+
+		/* Don't interpret the empty string as an integer.  */
+		/* Currently does not worry about overflow or int/long differences. */
+		i = STRTOL(v->u.s, &e, 10);
+		if ((v->u.s == e) || *e)
+			return 0;
+		free(v->u.s);
+		v->u.i = i;
+		v->type = INTEGER;
+	}
+	return 1;
+}
+
+/* Return str[0]+str[1] if the next token matches STR exactly.
+   STR must not be NULL.  */
+
+static int nextarg(const char *str)
+{
+	if (*G.args == NULL || strcmp(*G.args, str) != 0)
+		return 0;
+	return (unsigned char)str[0] + (unsigned char)str[1];
+}
+
+/* The comparison operator handling functions.  */
+
+static int cmp_common(VALUE *l, VALUE *r, int op)
+{
+	arith_t ll, rr;
+
+	ll = l->u.i;
+	rr = r->u.i;
+	if (l->type == STRING || r->type == STRING) {
+		tostring(l);
+		tostring(r);
+		ll = strcmp(l->u.s, r->u.s);
+		rr = 0;
+	}
+	/* calculating ll - rr and checking the result is prone to overflows.
+	 * We'll do it differently: */
+	if (op == '<')
+		return ll < rr;
+	if (op == ('<' + '='))
+		return ll <= rr;
+	if (op == '=' || (op == '=' + '='))
+		return ll == rr;
+	if (op == '!' + '=')
+		return ll != rr;
+	if (op == '>')
+		return ll > rr;
+	/* >= */
+	return ll >= rr;
+}
+
+/* The arithmetic operator handling functions.  */
+
+static arith_t arithmetic_common(VALUE *l, VALUE *r, int op)
+{
+	arith_t li, ri;
+
+	if (!toarith(l) || !toarith(r))
+		bb_error_msg_and_die("non-numeric argument");
+	li = l->u.i;
+	ri = r->u.i;
+	if (op == '+')
+		return li + ri;
+	if (op == '-')
+		return li - ri;
+	if (op == '*')
+		return li * ri;
+	if (ri == 0)
+		bb_error_msg_and_die("division by zero");
+	if (op == '/')
+		return li / ri;
+	return li % ri;
+}
+
+/* Do the : operator.
+   SV is the VALUE for the lhs (the string),
+   PV is the VALUE for the rhs (the pattern).  */
+
+static VALUE *docolon(VALUE *sv, VALUE *pv)
+{
+	enum { NMATCH = 2 };
+	VALUE *v;
+	regex_t re_buffer;
+	regmatch_t re_regs[NMATCH];
+
+	tostring(sv);
+	tostring(pv);
+
+	if (pv->u.s[0] == '^') {
+		bb_error_msg(
+"warning: '%s': using '^' as the first character\n"
+"of a basic regular expression is not portable; it is ignored", pv->u.s);
+	}
+
+	memset(&re_buffer, 0, sizeof(re_buffer));
+	memset(re_regs, 0, sizeof(re_regs));
+	xregcomp(&re_buffer, pv->u.s, 0);
+
+	/* expr uses an anchored pattern match, so check that there was a
+	 * match and that the match starts at offset 0. */
+	if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH
+	 && re_regs[0].rm_so == 0
+	) {
+		/* Were \(...\) used? */
+		if (re_buffer.re_nsub > 0 && re_regs[1].rm_so >= 0) {
+			sv->u.s[re_regs[1].rm_eo] = '\0';
+			v = str_value(sv->u.s + re_regs[1].rm_so);
+		} else {
+			v = int_value(re_regs[0].rm_eo);
+		}
+	} else {
+		/* Match failed -- return the right kind of null.  */
+		if (re_buffer.re_nsub > 0)
+			v = str_value("");
+		else
+			v = int_value(0);
+	}
+	regfree(&re_buffer);
+	return v;
+}
+
+/* Handle bare operands and ( expr ) syntax.  */
+
+static VALUE *eval7(void)
+{
+	VALUE *v;
+
+	if (!*G.args)
+		bb_error_msg_and_die("syntax error");
+
+	if (nextarg("(")) {
+		G.args++;
+		v = eval();
+		if (!nextarg(")"))
+			bb_error_msg_and_die("syntax error");
+		G.args++;
+		return v;
+	}
+
+	if (nextarg(")"))
+		bb_error_msg_and_die("syntax error");
+
+	return str_value(*G.args++);
+}
+
+/* Handle match, substr, index, length, and quote keywords.  */
+
+static VALUE *eval6(void)
+{
+	static const char keywords[] ALIGN1 =
+		"quote\0""length\0""match\0""index\0""substr\0";
+
+	VALUE *r, *i1, *i2;
+	VALUE *l = l; /* silence gcc */
+	VALUE *v = v; /* silence gcc */
+	int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0;
+
+	if (key == 0) /* not a keyword */
+		return eval7();
+	G.args++; /* We have a valid token, so get the next argument.  */
+	if (key == 1) { /* quote */
+		if (!*G.args)
+			bb_error_msg_and_die("syntax error");
+		return str_value(*G.args++);
+	}
+	if (key == 2) { /* length */
+		r = eval6();
+		tostring(r);
+		v = int_value(strlen(r->u.s));
+		freev(r);
+	} else
+		l = eval6();
+
+	if (key == 3) { /* match */
+		r = eval6();
+		v = docolon(l, r);
+		freev(l);
+		freev(r);
+	}
+	if (key == 4) { /* index */
+		r = eval6();
+		tostring(l);
+		tostring(r);
+		v = int_value(strcspn(l->u.s, r->u.s) + 1);
+		if (v->u.i == (arith_t) strlen(l->u.s) + 1)
+			v->u.i = 0;
+		freev(l);
+		freev(r);
+	}
+	if (key == 5) { /* substr */
+		i1 = eval6();
+		i2 = eval6();
+		tostring(l);
+		if (!toarith(i1) || !toarith(i2)
+		 || i1->u.i > (arith_t) strlen(l->u.s)
+		 || i1->u.i <= 0 || i2->u.i <= 0)
+			v = str_value("");
+		else {
+			v = xmalloc(sizeof(VALUE));
+			v->type = STRING;
+			v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
+		}
+		freev(l);
+		freev(i1);
+		freev(i2);
+	}
+	return v;
+}
+
+/* Handle : operator (pattern matching).
+   Calls docolon to do the real work.  */
+
+static VALUE *eval5(void)
+{
+	VALUE *l, *r, *v;
+
+	l = eval6();
+	while (nextarg(":")) {
+		G.args++;
+		r = eval6();
+		v = docolon(l, r);
+		freev(l);
+		freev(r);
+		l = v;
+	}
+	return l;
+}
+
+/* Handle *, /, % operators.  */
+
+static VALUE *eval4(void)
+{
+	VALUE *l, *r;
+	int op;
+	arith_t val;
+
+	l = eval5();
+	while (1) {
+		op = nextarg("*");
+		if (!op) { op = nextarg("/");
+		 if (!op) { op = nextarg("%");
+		  if (!op) return l;
+		}}
+		G.args++;
+		r = eval5();
+		val = arithmetic_common(l, r, op);
+		freev(l);
+		freev(r);
+		l = int_value(val);
+	}
+}
+
+/* Handle +, - operators.  */
+
+static VALUE *eval3(void)
+{
+	VALUE *l, *r;
+	int op;
+	arith_t val;
+
+	l = eval4();
+	while (1) {
+		op = nextarg("+");
+		if (!op) {
+			op = nextarg("-");
+			if (!op) return l;
+		}
+		G.args++;
+		r = eval4();
+		val = arithmetic_common(l, r, op);
+		freev(l);
+		freev(r);
+		l = int_value(val);
+	}
+}
+
+/* Handle comparisons.  */
+
+static VALUE *eval2(void)
+{
+	VALUE *l, *r;
+	int op;
+	arith_t val;
+
+	l = eval3();
+	while (1) {
+		op = nextarg("<");
+		if (!op) { op = nextarg("<=");
+		 if (!op) { op = nextarg("=");
+		  if (!op) { op = nextarg("==");
+		   if (!op) { op = nextarg("!=");
+		    if (!op) { op = nextarg(">=");
+		     if (!op) { op = nextarg(">");
+		      if (!op) return l;
+		}}}}}}
+		G.args++;
+		r = eval3();
+		toarith(l);
+		toarith(r);
+		val = cmp_common(l, r, op);
+		freev(l);
+		freev(r);
+		l = int_value(val);
+	}
+}
+
+/* Handle &.  */
+
+static VALUE *eval1(void)
+{
+	VALUE *l, *r;
+
+	l = eval2();
+	while (nextarg("&")) {
+		G.args++;
+		r = eval2();
+		if (null(l) || null(r)) {
+			freev(l);
+			freev(r);
+			l = int_value(0);
+		} else
+			freev(r);
+	}
+	return l;
+}
+
+/* Handle |.  */
+
+static VALUE *eval(void)
+{
+	VALUE *l, *r;
+
+	l = eval1();
+	while (nextarg("|")) {
+		G.args++;
+		r = eval1();
+		if (null(l)) {
+			freev(l);
+			l = r;
+		} else
+			freev(r);
+	}
+	return l;
+}
+
+int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int expr_main(int argc UNUSED_PARAM, char **argv)
+{
+	VALUE *v;
+
+	xfunc_error_retval = 2; /* coreutils compat */
+	G.args = argv + 1;
+	if (*G.args == NULL) {
+		bb_error_msg_and_die("too few arguments");
+	}
+	v = eval();
+	if (*G.args)
+		bb_error_msg_and_die("syntax error");
+	if (v->type == INTEGER)
+		printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
+	else
+		puts(v->u.s);
+	fflush_stdout_and_exit(null(v));
+}
diff --git a/busybox-1.19.3/coreutils/false.c b/busybox-1.19.3/coreutils/false.c
new file mode 100644
index 0000000..59c2f32
--- /dev/null
+++ b/busybox-1.19.3/coreutils/false.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini false implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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! */
+
+int false_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int false_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/coreutils/fold.c b/busybox-1.19.3/coreutils/fold.c
new file mode 100644
index 0000000..0e73063
--- /dev/null
+++ b/busybox-1.19.3/coreutils/fold.c
@@ -0,0 +1,176 @@
+/* vi: set sw=4 ts=4: */
+/* fold -- wrap each input line to fit in specified width.
+
+   Written by David MacKenzie, djm@gnu.ai.mit.edu.
+   Copyright (C) 91, 1995-2002 Free Software Foundation, Inc.
+
+   Modified for busybox based on coreutils v 5.0
+   Copyright (C) 2003 Glenn McGrath
+
+   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
+#define FLAG_WIDTH              4
+
+/* Assuming the current column is COLUMN, return the column that
+   printing C will move the cursor to.
+   The first column is 0. */
+static int adjust_column(unsigned column, char c)
+{
+	if (option_mask32 & FLAG_COUNT_BYTES)
+		return ++column;
+
+	if (c == '\t')
+		return column + 8 - column % 8;
+
+	if (c == '\b') {
+		if ((int)--column < 0)
+			column = 0;
+	}
+	else if (c == '\r')
+		column = 0;
+	else { /* just a printable char */
+		if (unicode_status != UNICODE_ON /* every byte is a new char */
+		 || (c & 0xc0) != 0x80 /* it isn't a 2nd+ byte of a Unicode char */
+		) {
+			column++;
+		}
+	}
+	return column;
+}
+
+/* Note that this function can write NULs, unlike fputs etc. */
+static void write2stdout(const void *buf, unsigned size)
+{
+	fwrite(buf, 1, size, stdout);
+}
+
+int fold_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fold_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *line_out = NULL;
+	const char *w_opt = "80";
+	unsigned width;
+	smallint exitcode = EXIT_SUCCESS;
+
+	init_unicode();
+
+	if (ENABLE_INCLUDE_SUSv2) {
+		/* Turn any numeric options into -w options.  */
+		int i;
+		for (i = 1; argv[i]; i++) {
+			const char *a = argv[i];
+			if (*a == '-') {
+				a++;
+				if (*a == '-' && !a[1]) /* "--" */
+					break;
+				if (isdigit(*a))
+					argv[i] = xasprintf("-w%s", a);
+			}
+		}
+	}
+
+	getopt32(argv, "bsw:", &w_opt);
+	width = xatou_range(w_opt, 1, 10000);
+
+	argv += optind;
+	if (!*argv)
+		*--argv = (char*)"-";
+
+	do {
+		FILE *istream = fopen_or_warn_stdin(*argv);
+		int c;
+		unsigned column = 0;     /* Screen column where next char will go */
+		unsigned offset_out = 0; /* Index in 'line_out' for next char */
+
+		if (istream == NULL) {
+			exitcode = EXIT_FAILURE;
+			continue;
+		}
+
+		while ((c = getc(istream)) != EOF) {
+			/* We grow line_out in chunks of 0x1000 bytes */
+			if ((offset_out & 0xfff) == 0) {
+				line_out = xrealloc(line_out, offset_out + 0x1000);
+			}
+ rescan:
+			line_out[offset_out] = c;
+			if (c == '\n') {
+				write2stdout(line_out, offset_out + 1);
+				column = offset_out = 0;
+				continue;
+			}
+			column = adjust_column(column, c);
+			if (column <= width || offset_out == 0) {
+				/* offset_out == 0 case happens
+				 * with small width (say, 1) and tabs.
+				 * The very first tab already goes to column 8,
+				 * but we must not wrap it */
+				offset_out++;
+				continue;
+			}
+
+			/* This character would make the line too long.
+			 * Print the line plus a newline, and make this character
+			 * start the next line */
+			if (option_mask32 & FLAG_BREAK_SPACES) {
+				unsigned i;
+				unsigned logical_end;
+
+				/* Look for the last blank. */
+				for (logical_end = offset_out - 1; (int)logical_end >= 0; logical_end--) {
+					if (!isblank(line_out[logical_end]))
+						continue;
+
+					/* Found a space or tab.
+					 * Output up to and including it, and start a new line */
+					logical_end++;
+					/*line_out[logical_end] = '\n'; - NO! this nukes one buffered character */
+					write2stdout(line_out, logical_end);
+					putchar('\n');
+					/* Move the remainder to the beginning of the next line.
+					 * The areas being copied here might overlap. */
+					memmove(line_out, line_out + logical_end, offset_out - logical_end);
+					offset_out -= logical_end;
+					for (column = i = 0; i < offset_out; i++) {
+						column = adjust_column(column, line_out[i]);
+					}
+					goto rescan;
+				}
+				/* No blank found, wrap will split the overlong word */
+			}
+			/* Output what we accumulated up to now, and start a new line */
+			line_out[offset_out] = '\n';
+			write2stdout(line_out, offset_out + 1);
+			column = offset_out = 0;
+			goto rescan;
+		} /* while (not EOF) */
+
+		if (offset_out) {
+			write2stdout(line_out, offset_out);
+		}
+
+		if (fclose_if_not_stdin(istream)) {
+			bb_simple_perror_msg(*argv);
+			exitcode = EXIT_FAILURE;
+		}
+	} while (*++argv);
+
+	fflush_stdout_and_exit(exitcode);
+}
diff --git a/busybox-1.19.3/coreutils/fsync.c b/busybox-1.19.3/coreutils/fsync.c
new file mode 100644
index 0000000..652a41c
--- /dev/null
+++ b/busybox-1.19.3/coreutils/fsync.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini fsync implementation for busybox
+ *
+ * Copyright (C) 2008 Nokia Corporation. All rights reserved.
+ *
+ * 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
+#endif
+
+/* This is a NOFORK applet. Be very careful! */
+
+int fsync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fsync_main(int argc UNUSED_PARAM, char **argv)
+{
+	int status;
+	int opts;
+
+	opts = getopt32(argv, "d"); /* fdatasync */
+	argv += optind;
+	if (!*argv) {
+		bb_show_usage();
+	}
+
+	status = EXIT_SUCCESS;
+	do {
+		int fd = open_or_warn(*argv, O_NOATIME | O_NOCTTY | O_RDONLY);
+
+		if (fd == -1) {
+			status = EXIT_FAILURE;
+			continue;
+		}
+		if ((opts ? fdatasync(fd) : fsync(fd))) {
+			//status = EXIT_FAILURE; - do we want this?
+			bb_simple_perror_msg(*argv);
+		}
+		close(fd);
+	} while (*++argv);
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/head.c b/busybox-1.19.3/coreutils/head.c
new file mode 100644
index 0000000..ec45127
--- /dev/null
+++ b/busybox-1.19.3/coreutils/head.c
@@ -0,0 +1,155 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * head implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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 */
+
+//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	-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! */
+
+static const char head_opts[] ALIGN1 =
+	"n:"
+#if ENABLE_FEATURE_FANCY_HEAD
+	"c:qv"
+#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;
+#endif
+	FILE *fp;
+	const char *fmt;
+	char *p;
+	int opt;
+	int c;
+	int retval = EXIT_SUCCESS;
+
+#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
+	/* Allow legacy syntax of an initial numeric option without -n. */
+	if (argv[1] && argv[1][0] == '-'
+	 && isdigit(argv[1][1])
+	) {
+		--argc;
+		++argv;
+		p = (*argv) + 1;
+		goto GET_COUNT;
+	}
+#endif
+
+	/* No size benefit in converting this to getopt32 */
+	while ((opt = getopt(argc, argv, head_opts)) > 0) {
+		switch (opt) {
+#if ENABLE_FEATURE_FANCY_HEAD
+		case 'q':
+			header_threshhold = INT_MAX;
+			break;
+		case 'v':
+			header_threshhold = -1;
+			break;
+		case 'c':
+			count_bytes = 1;
+			/* fall through */
+#endif
+		case 'n':
+			p = optarg;
+#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_HEAD
+ GET_COUNT:
+#endif
+			count = xatoul_sfx(p, head_suffixes);
+			break;
+		default:
+			bb_show_usage();
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+	if (!*argv)
+		*--argv = (char*)"-";
+
+	fmt = header_fmt_str + 1;
+#if ENABLE_FEATURE_FANCY_HEAD
+	if (argc <= header_threshhold) {
+		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
+
+	do {
+		fp = fopen_or_warn_stdin(*argv);
+		if (fp) {
+			if (fp == stdin) {
+				*argv = (char *) bb_msg_standard_input;
+			}
+			if (header_threshhold) {
+				printf(fmt, *argv);
+			}
+			i = count;
+			while (i && ((c = getc(fp)) != EOF)) {
+				if (count_bytes || (c == '\n')) {
+					--i;
+				}
+				putchar(c);
+			}
+			if (fclose_if_not_stdin(fp)) {
+				bb_simple_perror_msg(*argv);
+				retval = EXIT_FAILURE;
+			}
+			die_if_ferror_stdout();
+		} else {
+			retval = EXIT_FAILURE;
+		}
+		fmt = header_fmt_str;
+	} while (*++argv);
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/busybox-1.19.3/coreutils/hostid.c b/busybox-1.19.3/coreutils/hostid.c
new file mode 100644
index 0000000..49409b9
--- /dev/null
+++ b/busybox-1.19.3/coreutils/hostid.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hostid implementation for busybox
+ *
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+//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! */
+
+int hostid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hostid_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	if (argv[1]) {
+		bb_show_usage();
+	}
+
+	printf("%lx\n", gethostid());
+
+	return fflush_all();
+}
diff --git a/busybox-1.19.3/coreutils/id.c b/busybox-1.19.3/coreutils/id.c
new file mode 100644
index 0000000..399d25e
--- /dev/null
+++ b/busybox-1.19.3/coreutils/id.c
@@ -0,0 +1,269 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini id implementation for busybox
+ *
+ * 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 source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant. */
+/* Hacked by Tito Ragusa (C) 2004 to handle usernames of whatever
+ * length and to be more similar to GNU id.
+ * -Z option support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
+ * 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)
+#error "Sorry, you need at least uClibc version 0.9.30 for id applet to build"
+#endif
+#endif
+#endif
+
+enum {
+	PRINT_REAL      = (1 << 0),
+	NAME_NOT_NUMBER = (1 << 1),
+	JUST_USER       = (1 << 2),
+	JUST_GROUP      = (1 << 3),
+	JUST_ALL_GROUPS = (1 << 4),
+#if ENABLE_SELINUX
+	JUST_CONTEXT    = (1 << 5),
+#endif
+};
+
+static int print_common(unsigned id, const char *name, const char *prefix)
+{
+	if (prefix) {
+		printf("%s", prefix);
+	}
+	if (!(option_mask32 & NAME_NOT_NUMBER) || !name) {
+		printf("%u", id);
+	}
+	if (!option_mask32 || (option_mask32 & NAME_NOT_NUMBER)) {
+		if (name) {
+			printf(option_mask32 ? "%s" : "(%s)", name);
+		} else {
+			/* Don't set error status flag in default mode */
+			if (option_mask32) {
+				if (ENABLE_DESKTOP)
+					bb_error_msg("unknown ID %u", id);
+				return EXIT_FAILURE;
+			}
+		}
+	}
+	return EXIT_SUCCESS;
+}
+
+static int print_group(gid_t id, const char *prefix)
+{
+	return print_common(id, gid2group(id), prefix);
+}
+
+static int print_user(uid_t id, const char *prefix)
+{
+	return print_common(id, uid2uname(id), 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)
+ * Otherwise fill in groups[] and return >= 0
+ */
+static int get_groups(const char *username, gid_t rgid, gid_t *groups, int *n)
+{
+	int m;
+
+	if (username) {
+		/* If the user is a member of more than
+		 * *n groups, then -1 is returned. Otherwise >= 0.
+		 * (and no defined way of detecting errors?!) */
+		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;
+	}
+
+	*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;
+int id_main(int argc UNUSED_PARAM, char **argv)
+{
+	uid_t ruid;
+	gid_t rgid;
+	uid_t euid;
+	gid_t egid;
+	unsigned opt;
+	int i;
+	int status = EXIT_SUCCESS;
+	const char *prefix;
+	const char *username;
+#if ENABLE_SELINUX
+	security_context_t scontext = NULL;
+#endif
+
+	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) {
+		struct passwd *p = xgetpwnam(username);
+		euid = ruid = p->pw_uid;
+		egid = rgid = p->pw_gid;
+	} else {
+		egid = getegid();
+		rgid = getgid();
+		euid = geteuid();
+		ruid = getuid();
+	}
+	/* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */
+	/* id says: print the real ID instead of the effective ID, with -ugG */
+	/* in fact in this case egid is always printed if egid != rgid */
+	if (!opt || (opt & JUST_ALL_GROUPS)) {
+		gid_t *groups;
+		int n;
+
+		if (!opt) {
+			/* Default Mode */
+			status |= print_user(ruid, "uid=");
+			status |= print_group(rgid, " gid=");
+			if (euid != ruid)
+				status |= print_user(euid, " euid=");
+			if (egid != rgid)
+				status |= print_group(egid, " egid=");
+		} else {
+			/* JUST_ALL_GROUPS */
+			status |= print_group(rgid, NULL);
+			if (egid != rgid)
+				status |= print_group(egid, " ");
+		}
+		/* 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(groups[0]));
+		n = 64;
+		if (get_groups(username, rgid, groups, &n) < 0) {
+			/* Need bigger buffer after all */
+			groups = xrealloc(groups, n * sizeof(groups[0]));
+			get_groups(username, rgid, groups, &n);
+		}
+		if (n > 0) {
+			/* Print the list */
+			prefix = " groups=";
+			for (i = 0; i < n; i++) {
+				if (opt && (groups[i] == rgid || groups[i] == egid))
+					continue;
+				status |= print_group(groups[i], opt ? " " : prefix);
+				prefix = ",";
+			}
+		} else if (n < 0) { /* error in get_groups() */
+			if (ENABLE_DESKTOP)
+				bb_error_msg_and_die("can't get groups");
+			return EXIT_FAILURE;
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(groups);
+#if ENABLE_SELINUX
+		if (is_selinux_enabled()) {
+			if (getcon(&scontext) == 0)
+				printf(" context=%s", scontext);
+		}
+#endif
+	} else if (opt & PRINT_REAL) {
+		euid = ruid;
+		egid = rgid;
+	}
+
+	if (opt & JUST_USER)
+		status |= print_user(euid, NULL);
+	else if (opt & JUST_GROUP)
+		status |= print_group(egid, NULL);
+#if ENABLE_SELINUX
+	else if (opt & JUST_CONTEXT) {
+		selinux_or_die();
+		if (username || getcon(&scontext)) {
+			bb_error_msg_and_die("can't get process context%s",
+				username ? " for a different user" : "");
+		}
+		fputs(scontext, stdout);
+	}
+	/* freecon(NULL) seems to be harmless */
+	if (ENABLE_FEATURE_CLEAN_UP)
+		freecon(scontext);
+#endif
+	bb_putchar('\n');
+	fflush_stdout_and_exit(status);
+}
diff --git a/busybox-1.19.3/coreutils/id_test.sh b/busybox-1.19.3/coreutils/id_test.sh
new file mode 100755
index 0000000..0d65f2a
--- /dev/null
+++ b/busybox-1.19.3/coreutils/id_test.sh
@@ -0,0 +1,244 @@
+#!/bin/bash
+# Test script for busybox id vs. coreutils id.
+# Needs root privileges for some tests.
+
+cp /usr/bin/id .
+BUSYBOX=./busybox
+ID=./id
+LIST=`awk -F: '{ printf "%s\n", $1 }' /etc/passwd`
+FLAG_USER_EXISTS="no"
+TEST_USER="f583ca884c1d93458fb61ed137ff44f6"
+
+echo "test 1: id [options] nousername"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	$BUSYBOX id $OPTIONS >foo 2>/dev/null
+	RET1=$?
+	$ID $OPTIONS >bar 2>/dev/null
+	RET2=$?
+	if test "$RET1" != "$RET2"; then
+		echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+	fi
+	diff foo bar
+done
+
+echo "test 2: id [options] username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	for i in $LIST ; do
+		if test "$i" = "$TEST_USER"; then
+			FLAG_USER_EXISTS="yes"
+		fi
+		$BUSYBOX id $OPTIONS $i >foo 2>/dev/null
+		RET1=$?
+		$ID $OPTIONS $i >bar 2>/dev/null
+		RET2=$?
+		if test "$RET1" != "$RET2"; then
+			echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+		fi
+		diff foo bar
+	done
+done
+
+if test $FLAG_USER_EXISTS = "yes"; then
+	echo "test 3,4,5,6,7,8,9,10,11,12 skipped because test user $TEST_USER already exists"
+	rm -f foo bar
+	exit 1
+fi
+
+adduser -s /bin/true -g "" -H -D "$TEST_USER" || exit 1
+
+chown $TEST_USER.$TEST_USER $BUSYBOX
+chmod u+s $BUSYBOX 2>&1 /dev/null
+chown $TEST_USER.$TEST_USER $ID
+chmod u+s $ID 2>&1 /dev/null
+
+echo "test 3 setuid, existing user: id [options] no username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	$BUSYBOX id $OPTIONS >foo 2>/dev/null
+	RET1=$?
+	$ID $OPTIONS >bar 2>/dev/null
+	RET2=$?
+	if test "$RET1" != "$RET2"; then
+		echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+	fi
+	diff foo bar
+	#done
+done
+
+echo "test 4 setuid, existing user: id [options] username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	for i in $LIST ; do
+		$BUSYBOX id $OPTIONS $i >foo 2>/dev/null
+		RET1=$?
+		$ID $OPTIONS $i >bar 2>/dev/null
+		RET2=$?
+		if test "$RET1" != "$RET2"; then
+			echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+		fi
+		diff foo bar
+	done
+done
+
+chown $TEST_USER.$TEST_USER $BUSYBOX
+chmod g+s $BUSYBOX 2>&1 /dev/null
+chown $TEST_USER.$TEST_USER $ID
+chmod g+s $ID 2>&1 /dev/null
+
+echo "test 5 setgid, existing user: id [options] no username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	$BUSYBOX id $OPTIONS >foo 2>/dev/null
+	RET1=$?
+	$ID $OPTIONS >bar 2>/dev/null
+	RET2=$?
+	if test "$RET1" != "$RET2"; then
+		echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+	fi
+	diff foo bar
+	#done
+done
+
+echo "test 6 setgid, existing user: id [options] username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	for i in $LIST ; do
+		$BUSYBOX id $OPTIONS $i >foo 2>/dev/null
+		RET1=$?
+		$ID $OPTIONS $i >bar 2>/dev/null
+		RET2=$?
+		if test "$RET1" != "$RET2"; then
+			echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+		fi
+		diff foo bar
+	done
+done
+
+chown $TEST_USER.$TEST_USER $BUSYBOX
+chmod u+s,g+s $BUSYBOX 2>&1 /dev/null
+chown $TEST_USER.$TEST_USER $ID
+chmod u+s,g+s $ID 2>&1 /dev/null
+
+echo "test 7 setuid, setgid, existing user: id [options] no username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	$BUSYBOX id $OPTIONS >foo 2>/dev/null
+	RET1=$?
+	$ID $OPTIONS >bar 2>/dev/null
+	RET2=$?
+	if test "$RET1" != "$RET2"; then
+		echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+	fi
+	diff foo bar
+	#done
+done
+
+echo "test 8 setuid, setgid, existing user: id [options] username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	for i in $LIST ; do
+		$BUSYBOX id $OPTIONS $i >foo 2>/dev/null
+		RET1=$?
+		$ID $OPTIONS $i >bar 2>/dev/null
+		RET2=$?
+		if test "$RET1" != "$RET2"; then
+			echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+		fi
+		diff foo bar
+	done
+done
+
+deluser $TEST_USER || exit 1
+
+echo "test 9 setuid, setgid, not existing user: id [options] no username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	$BUSYBOX id $OPTIONS >foo 2>/dev/null
+	RET1=$?
+	$ID $OPTIONS >bar 2>/dev/null
+	RET2=$?
+	if test "$RET1" != "$RET2"; then
+		echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+	fi
+	diff foo bar
+done
+
+echo "test 10 setuid, setgid, not existing user: id [options] username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	for i in $LIST ; do
+		$BUSYBOX id $OPTIONS $i >foo 2>/dev/null
+		RET1=$?
+		$ID $OPTIONS $i >bar 2>/dev/null
+		RET2=$?
+		if test "$RET1" != "$RET2"; then
+			echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+		fi
+		diff foo bar
+	done
+done
+
+chown .root $BUSYBOX 2>&1 /dev/null
+chown .root $ID 2>&1 /dev/null
+chmod g+s $BUSYBOX 2>&1 /dev/null
+chmod g+s $ID 2>&1 /dev/null
+
+echo "test 11 setgid, not existing group: id [options] no username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	$BUSYBOX id $OPTIONS >foo 2>/dev/null
+	RET1=$?
+	$ID $OPTIONS >bar 2>/dev/null
+	RET2=$?
+	if test "$RET1" != "$RET2"; then
+		echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+	fi
+	diff foo bar
+	#done
+done
+
+echo "test 12 setgid, not existing group: id [options] username"
+rm -f foo bar
+for OPTIONS in "" "-u" "-un" "-unr" "-g" "-gn" "-gnr" "-G" "-Gn" "-Gnr"
+do
+	#echo "$OPTIONS"
+	for i in $LIST ; do
+		$BUSYBOX id $OPTIONS $i >foo 2>/dev/null
+		RET1=$?
+		$ID $OPTIONS $i >bar 2>/dev/null
+		RET2=$?
+		if test "$RET1" != "$RET2"; then
+			echo "Return Values differ ($RET1 != $RET2): options $OPTIONS"
+		fi
+		diff foo bar
+	done
+done
+
+chown root.root $BUSYBOX 2>&1 /dev/null
+chown root.root $ID 2>&1 /dev/null
+rm -f $ID
+rm -f foo bar
diff --git a/busybox-1.19.3/coreutils/install.c b/busybox-1.19.3/coreutils/install.c
new file mode 100644
index 0000000..445497f
--- /dev/null
+++ b/busybox-1.19.3/coreutils/install.c
@@ -0,0 +1,229 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003 by Glenn McGrath
+ * SELinux support: by Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * 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"
+
+#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
+static const char install_longopts[] ALIGN1 =
+	"directory\0"           No_argument       "d"
+	"preserve-timestamps\0" No_argument       "p"
+	"strip\0"               No_argument       "s"
+	"group\0"               Required_argument "g"
+	"mode\0"                Required_argument "m"
+	"owner\0"               Required_argument "o"
+/* autofs build insists of using -b --suffix=.orig */
+/* TODO? (short option for --suffix is -S) */
+#if ENABLE_SELINUX
+	"context\0"             Required_argument "Z"
+	"preserve_context\0"    No_argument       "\xff"
+	"preserve-context\0"    No_argument       "\xff"
+#endif
+	;
+#endif
+
+
+#if ENABLE_SELINUX
+static void setdefaultfilecon(const char *path)
+{
+	struct stat s;
+	security_context_t scontext = NULL;
+
+	if (!is_selinux_enabled()) {
+		return;
+	}
+	if (lstat(path, &s) != 0) {
+		return;
+	}
+
+	if (matchpathcon(path, s.st_mode, &scontext) < 0) {
+		goto out;
+	}
+	if (strcmp(scontext, "<<none>>") == 0) {
+		goto out;
+	}
+
+	if (lsetfilecon(path, scontext) < 0) {
+		if (errno != ENOTSUP) {
+			bb_perror_msg("warning: can't change context"
+					" of %s to %s", path, scontext);
+		}
+	}
+
+ out:
+	freecon(scontext);
+}
+
+#endif
+
+int install_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int install_main(int argc, char **argv)
+{
+	struct stat statbuf;
+	mode_t mode;
+	uid_t uid;
+	gid_t gid;
+	char *arg, *last;
+	const char *gid_str;
+	const char *uid_str;
+	const char *mode_str;
+	int copy_flags = FILEUTILS_DEREFERENCE | FILEUTILS_FORCE;
+	int opts;
+	int min_args = 1;
+	int ret = EXIT_SUCCESS;
+	int isdir = 0;
+#if ENABLE_SELINUX
+	security_context_t scontext;
+	bool use_default_selinux_context = 1;
+#endif
+	enum {
+		OPT_c             = 1 << 0,
+		OPT_v             = 1 << 1,
+		OPT_b             = 1 << 2,
+		OPT_MKDIR_LEADING = 1 << 3,
+		OPT_DIRECTORY     = 1 << 4,
+		OPT_PRESERVE_TIME = 1 << 5,
+		OPT_STRIP         = 1 << 6,
+		OPT_GROUP         = 1 << 7,
+		OPT_MODE          = 1 << 8,
+		OPT_OWNER         = 1 << 9,
+#if ENABLE_SELINUX
+		OPT_SET_SECURITY_CONTEXT = 1 << 10,
+		OPT_PRESERVE_SECURITY_CONTEXT = 1 << 11,
+#endif
+	};
+
+#if ENABLE_FEATURE_INSTALL_LONG_OPTIONS
+	applet_long_options = install_longopts;
+#endif
+	opt_complementary = "s--d:d--s" IF_FEATURE_INSTALL_LONG_OPTIONS(IF_SELINUX(":Z--\xff:\xff--Z"));
+	/* -c exists for backwards compatibility, it's needed */
+	/* -v is ignored ("print name of each created directory") */
+	/* -b is ignored ("make a backup of each existing destination file") */
+	opts = getopt32(argv, "cvb" "Ddpsg:m:o:" IF_SELINUX("Z:"),
+			&gid_str, &mode_str, &uid_str IF_SELINUX(, &scontext));
+	argc -= optind;
+	argv += optind;
+
+#if ENABLE_SELINUX
+	if (opts & (OPT_PRESERVE_SECURITY_CONTEXT|OPT_SET_SECURITY_CONTEXT)) {
+		selinux_or_die();
+		use_default_selinux_context = 0;
+		if (opts & OPT_PRESERVE_SECURITY_CONTEXT) {
+			copy_flags |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
+		}
+		if (opts & OPT_SET_SECURITY_CONTEXT) {
+			setfscreatecon_or_die(scontext);
+			copy_flags |= FILEUTILS_SET_SECURITY_CONTEXT;
+		}
+	}
+#endif
+
+	/* preserve access and modification time, this is GNU behaviour,
+	 * BSD only preserves modification time */
+	if (opts & OPT_PRESERVE_TIME) {
+		copy_flags |= FILEUTILS_PRESERVE_STATUS;
+	}
+	mode = 0755; /* GNU coreutils 6.10 compat */
+	if (opts & OPT_MODE)
+		bb_parse_mode(mode_str, &mode);
+	uid = (opts & OPT_OWNER) ? get_ug_id(uid_str, xuname2uid) : getuid();
+	gid = (opts & OPT_GROUP) ? get_ug_id(gid_str, xgroup2gid) : getgid();
+
+	last = argv[argc - 1];
+	if (!(opts & OPT_DIRECTORY)) {
+		argv[argc - 1] = NULL;
+		min_args++;
+
+		/* coreutils install resolves link in this case, don't use lstat */
+		isdir = stat(last, &statbuf) < 0 ? 0 : S_ISDIR(statbuf.st_mode);
+	}
+
+	if (argc < min_args)
+		bb_show_usage();
+
+	while ((arg = *argv++) != NULL) {
+		char *dest = last;
+		if (opts & OPT_DIRECTORY) {
+			dest = arg;
+			/* GNU coreutils 6.9 does not set uid:gid
+			 * on intermediate created directories
+			 * (only on last one) */
+			if (bb_make_directory(dest, 0755, FILEUTILS_RECUR)) {
+				ret = EXIT_FAILURE;
+				goto next;
+			}
+		} else {
+			if (opts & OPT_MKDIR_LEADING) {
+				char *ddir = xstrdup(dest);
+				bb_make_directory(dirname(ddir), 0755, FILEUTILS_RECUR);
+				/* errors are not checked. copy_file
+				 * will fail if dir is not created. */
+				free(ddir);
+			}
+			if (isdir)
+				dest = concat_path_file(last, bb_basename(arg));
+			if (copy_file(arg, dest, copy_flags) != 0) {
+				/* copy is not made */
+				ret = EXIT_FAILURE;
+				goto next;
+			}
+			if (opts & OPT_STRIP) {
+				char *args[4];
+				args[0] = (char*)"strip";
+				args[1] = (char*)"-p"; /* -p --preserve-dates */
+				args[2] = dest;
+				args[3] = NULL;
+				if (spawn_and_wait(args)) {
+					bb_perror_msg("strip");
+					ret = EXIT_FAILURE;
+				}
+			}
+		}
+
+		/* Set the file mode (always, not only with -m).
+		 * GNU coreutils 6.10 is not affected by umask. */
+		if (chmod(dest, mode) == -1) {
+			bb_perror_msg("can't change %s of %s", "permissions", dest);
+			ret = EXIT_FAILURE;
+		}
+#if ENABLE_SELINUX
+		if (use_default_selinux_context)
+			setdefaultfilecon(dest);
+#endif
+		/* Set the user and group id */
+		if ((opts & (OPT_OWNER|OPT_GROUP))
+		 && lchown(dest, uid, gid) == -1
+		) {
+			bb_perror_msg("can't change %s of %s", "ownership", dest);
+			ret = EXIT_FAILURE;
+		}
+ next:
+		if (ENABLE_FEATURE_CLEAN_UP && isdir)
+			free(dest);
+	}
+
+	return ret;
+}
diff --git a/busybox-1.19.3/coreutils/length.c.disabled b/busybox-1.19.3/coreutils/length.c.disabled
new file mode 100644
index 0000000..aee898d
--- /dev/null
+++ b/busybox-1.19.3/coreutils/length.c.disabled
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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! */
+
+int length_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int length_main(int argc, char **argv)
+{
+	if ((argc != 2) || (**(++argv) == '-')) {
+		bb_show_usage();
+	}
+
+	printf("%u\n", (unsigned)strlen(*argv));
+
+	return fflush_all();
+}
diff --git a/busybox-1.19.3/coreutils/libcoreutils/Kbuild.src b/busybox-1.19.3/coreutils/libcoreutils/Kbuild.src
new file mode 100644
index 0000000..2042d5f
--- /dev/null
+++ b/busybox-1.19.3/coreutils/libcoreutils/Kbuild.src
@@ -0,0 +1,14 @@
+# 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:=
+
+INSERT
+lib-$(CONFIG_MKFIFO)	+= getopt_mk_fifo_nod.o
+lib-$(CONFIG_MKNOD)	+= getopt_mk_fifo_nod.o
+lib-$(CONFIG_INSTALL)	+= cp_mv_stat.o
+lib-$(CONFIG_CP)	+= cp_mv_stat.o
+lib-$(CONFIG_MV)	+= cp_mv_stat.o
diff --git a/busybox-1.19.3/coreutils/libcoreutils/coreutils.h b/busybox-1.19.3/coreutils/libcoreutils/coreutils.h
new file mode 100644
index 0000000..307d033
--- /dev/null
+++ b/busybox-1.19.3/coreutils/libcoreutils/coreutils.h
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#ifndef COREUTILS_H
+#define COREUTILS_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+typedef int (*stat_func)(const char *fn, struct stat *ps);
+
+int cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf) FAST_FUNC;
+int cp_mv_stat(const char *fn, struct stat *fn_stat) FAST_FUNC;
+
+mode_t getopt_mk_fifo_nod(char **argv) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/coreutils/libcoreutils/cp_mv_stat.c b/busybox-1.19.3/coreutils/libcoreutils/cp_mv_stat.c
new file mode 100644
index 0000000..5ba07ec
--- /dev/null
+++ b/busybox-1.19.3/coreutils/libcoreutils/cp_mv_stat.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * coreutils utility routine
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "libbb.h"
+#include "coreutils.h"
+
+int FAST_FUNC cp_mv_stat2(const char *fn, struct stat *fn_stat, stat_func sf)
+{
+	if (sf(fn, fn_stat) < 0) {
+		if (errno != ENOENT) {
+#if ENABLE_FEATURE_VERBOSE_CP_MESSAGE
+			if (errno == ENOTDIR) {
+				bb_error_msg("can't stat '%s': Path has non-directory component", fn);
+				return -1;
+			}
+#endif
+			bb_perror_msg("can't stat '%s'", fn);
+			return -1;
+		}
+		return 0;
+	}
+	if (S_ISDIR(fn_stat->st_mode)) {
+		return 3;
+	}
+	return 1;
+}
+
+int FAST_FUNC cp_mv_stat(const char *fn, struct stat *fn_stat)
+{
+	return cp_mv_stat2(fn, fn_stat, stat);
+}
diff --git a/busybox-1.19.3/coreutils/libcoreutils/getopt_mk_fifo_nod.c b/busybox-1.19.3/coreutils/libcoreutils/getopt_mk_fifo_nod.c
new file mode 100644
index 0000000..2227171
--- /dev/null
+++ b/busybox-1.19.3/coreutils/libcoreutils/getopt_mk_fifo_nod.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * coreutils utility routine
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "libbb.h"
+#include "coreutils.h"
+
+mode_t FAST_FUNC getopt_mk_fifo_nod(char **argv)
+{
+	mode_t mode = 0666;
+	char *smode = NULL;
+#if ENABLE_SELINUX
+	security_context_t scontext;
+#endif
+	int opt;
+	opt = getopt32(argv, "m:" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext));
+	if (opt & 1) {
+		if (bb_parse_mode(smode, &mode))
+			umask(0);
+	}
+
+#if ENABLE_SELINUX
+	if (opt & 2) {
+		selinux_or_die();
+		setfscreatecon_or_die(scontext);
+	}
+#endif
+
+	return mode;
+}
diff --git a/busybox-1.19.3/coreutils/ln.c b/busybox-1.19.3/coreutils/ln.c
new file mode 100644
index 0000000..88a9a8f
--- /dev/null
+++ b/busybox-1.19.3/coreutils/ln.c
@@ -0,0 +1,125 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ln implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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:
+//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
+
+int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ln_main(int argc, char **argv)
+{
+	int status = EXIT_SUCCESS;
+	int opts;
+	char *last;
+	char *src_name;
+	char *src;
+	char *suffix = (char*)"~";
+	struct stat statbuf;
+	int (*link_func)(const char *, const char *);
+
+	opt_complementary = "-1"; /* min one arg */
+	opts = getopt32(argv, "sfnbS:", &suffix);
+
+	last = argv[argc - 1];
+	argv += optind;
+
+	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));
+	}
+
+	do {
+		src_name = NULL;
+		src = last;
+
+		if (is_directory(src,
+		                (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE,
+		                NULL)
+		) {
+			src_name = xstrdup(*argv);
+			src = concat_path_file(src, bb_get_last_path_component_strip(src_name));
+			free(src_name);
+			src_name = src;
+		}
+		if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) {
+			// coreutils: "ln dangling_symlink new_hardlink" works
+			if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) {
+				bb_simple_perror_msg(*argv);
+				status = EXIT_FAILURE;
+				free(src_name);
+				continue;
+			}
+		}
+
+		if (opts & LN_BACKUP) {
+			char *backup;
+			backup = xasprintf("%s%s", src, suffix);
+			if (rename(src, backup) < 0 && errno != ENOENT) {
+				bb_simple_perror_msg(src);
+				status = EXIT_FAILURE;
+				free(backup);
+				continue;
+			}
+			free(backup);
+			/*
+			 * When the source and dest are both hard links to the same
+			 * inode, a rename may succeed even though nothing happened.
+			 * Therefore, always unlink().
+			 */
+			unlink(src);
+		} else if (opts & LN_FORCE) {
+			unlink(src);
+		}
+
+		link_func = link;
+		if (opts & LN_SYMLINK) {
+			link_func = symlink;
+		}
+
+		if (link_func(*argv, src) != 0) {
+			bb_simple_perror_msg(src);
+			status = EXIT_FAILURE;
+		}
+
+		free(src_name);
+
+	} while ((++argv)[1]);
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/logname.c b/busybox-1.19.3/coreutils/logname.c
new file mode 100644
index 0000000..10b9615
--- /dev/null
+++ b/busybox-1.19.3/coreutils/logname.c
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini logname implementation for busybox
+ *
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/logname.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * SUSv3 specifies the string used is that returned from getlogin().
+ * The previous implementation used getpwuid() for geteuid(), which
+ * is _not_ the same.  Erik apparently made this change almost 3 years
+ * ago to avoid failing when no utmp was available.  However, the
+ * correct course of action wrt SUSv3 for a failing getlogin() is
+ * 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! */
+
+int logname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int logname_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	char buf[64];
+
+	if (argv[1]) {
+		bb_show_usage();
+	}
+
+	/* Using _r function - avoid pulling in static buffer from libc */
+	if (getlogin_r(buf, sizeof(buf)) == 0) {
+		puts(buf);
+		return fflush_all();
+	}
+
+	bb_perror_msg_and_die("getlogin");
+}
diff --git a/busybox-1.19.3/coreutils/ls.c b/busybox-1.19.3/coreutils/ls.c
new file mode 100644
index 0000000..d5b25ee
--- /dev/null
+++ b/busybox-1.19.3/coreutils/ls.c
@@ -0,0 +1,1255 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 1996 Brian Candler <B.Candler@pobox.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* [date unknown. Perhaps before year 2000]
+ * To achieve a small memory footprint, this version of 'ls' doesn't do any
+ * file sorting, and only has the most essential command line switches
+ * (i.e., the ones I couldn't live without :-) All features which involve
+ * linking in substantial chunks of libc can be disabled.
+ *
+ * Although I don't really want to add new features to this program to
+ * keep it small, I *am* interested to receive bug fixes and ways to make
+ * it more portable.
+ *
+ * KNOWN BUGS:
+ * 1. hidden files can make column width too large
+ *
+ * NON-OPTIMAL BEHAVIOUR:
+ * 1. autowidth reads directories twice
+ * 2. if you do a short directory listing without filetype characters
+ *    appended, there's no need to stat each one
+ * PORTABILITY:
+ * 1. requires lstat (BSD) - how do you do it without?
+ *
+ * [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"
+
+
+/* This is a NOEXEC applet. Be very careful! */
+
+
+#if ENABLE_FTPD
+/* ftpd uses ls, and without timestamps Mozilla won't understand
+ * ftpd's LIST output.
+ */
+# undef CONFIG_FEATURE_LS_TIMESTAMPS
+# undef ENABLE_FEATURE_LS_TIMESTAMPS
+# undef IF_FEATURE_LS_TIMESTAMPS
+# undef IF_NOT_FEATURE_LS_TIMESTAMPS
+# define CONFIG_FEATURE_LS_TIMESTAMPS 1
+# define ENABLE_FEATURE_LS_TIMESTAMPS 1
+# define IF_FEATURE_LS_TIMESTAMPS(...) __VA_ARGS__
+# define IF_NOT_FEATURE_LS_TIMESTAMPS(...)
+#endif
+
+
+enum {
+TERMINAL_WIDTH  = 80,           /* use 79 if terminal has linefold bug */
+
+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 */
+LIST_INO        = 1 << 0,
+LIST_BLOCKS     = 1 << 1,
+LIST_MODEBITS   = 1 << 2,
+LIST_NLINKS     = 1 << 3,
+LIST_ID_NAME    = 1 << 4,
+LIST_ID_NUMERIC = 1 << 5,
+LIST_CONTEXT    = 1 << 6,
+LIST_SIZE       = 1 << 7,
+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 << 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),
+
+/* 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 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
+TIME_ACCESS     = (2 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
+TIME_MASK       = (3 << 21) * ENABLE_FEATURE_LS_TIMESTAMPS,
+
+/* how will the files be sorted (CONFIG_FEATURE_LS_SORTFILES) */
+SORT_REVERSE    = 1 << 23,
+
+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_LONG       = LIST_MODEBITS | LIST_NLINKS | LIST_ID_NAME | LIST_SIZE | \
+                  LIST_DATE_TIME | LIST_SYMLINK,
+};
+
+/* -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 */
+	IF_FEATURE_LS_TIMESTAMPS("cetu") /* 4, 17 */
+	IF_FEATURE_LS_SORTFILES("SXrv")  /* 4, 21 */
+	IF_FEATURE_LS_FILETYPES("Fp")    /* 2, 23 */
+	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),
+	//OPT_d = (1 << 2),
+	//OPT_i = (1 << 3),
+	//OPT_l = (1 << 4),
+	//OPT_1 = (1 << 5),
+	OPT_g = (1 << 6),
+	//OPT_n = (1 << 7),
+	//OPT_s = (1 << 8),
+	//OPT_x = (1 << 9),
+	OPT_Q = (1 << 10),
+	//OPT_A = (1 << 11),
+	//OPT_k = (1 << 12),
+
+	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 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 */
+#endif
+#if ENABLE_FEATURE_LS_SORTFILES
+	SORT_SIZE,                   /* S */
+	SORT_EXT,                    /* X */
+	SORT_REVERSE,                /* r */
+	SORT_VERSION,                /* v */
+#endif
+#if ENABLE_FEATURE_LS_FILETYPES
+	LIST_FILETYPE | LIST_CLASSIFY, /* F */
+	LIST_FILETYPE,               /* p */
+#endif
+#if ENABLE_FEATURE_LS_RECURSIVE
+	DISP_RECURSIVE,              /* R */
+#endif
+#if ENABLE_SELINUX
+	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 */
+};
+
+
+/*
+ * a directory entry and its stat info
+ */
+struct dnode {
+	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 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" */
+	time_t current_time_t;
+#endif
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	/* we have to zero it out because of NOEXEC */ \
+	memset(&G, 0, sizeof(G)); \
+	IF_FEATURE_AUTOWIDTH(G_terminal_width = TERMINAL_WIDTH;) \
+	IF_FEATURE_LS_TIMESTAMPS(time(&G.current_time_t);) \
+} while (0)
+
+
+/*** Output code ***/
+
+
+/* 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)
+/*                       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
+   041 brown foreground              053 brown background
+   042 blue foreground               054 blue background
+   043 magenta (purple) foreground   055 magenta background
+   044 cyan (light blue) foreground  056 cyan background
+   045 gray foreground               057 white background
+*/
+#define COLOR(mode) ( \
+	/*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)
+ * (other attributes are 2:dim 4:underline 5:blink 7:reverse,
+ *  let's use 7 for "impossible" types, just for fun)
+ * Note: coreutils 6.9 uses inverted red for setuid binaries.
+ */
+#define ATTR(mode) ( \
+	/*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)])
+
+#if ENABLE_FEATURE_LS_COLOR
+/* mode of zero is interpreted as "unknown" (stat failed) */
+static char fgcolor(mode_t mode)
+{
+	if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+		return COLOR(0xF000);	/* File is executable ... */
+	return COLOR(mode);
+}
+static char bold(mode_t mode)
+{
+	if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+		return ATTR(0xF000);	/* File is executable ... */
+	return ATTR(mode);
+}
+#endif
+
+#if ENABLE_FEATURE_LS_FILETYPES
+static char append_char(mode_t mode)
+{
+	if (!(G.all_fmt & LIST_FILETYPE))
+		return '\0';
+	if (S_ISDIR(mode))
+		return '/';
+	if (!(G.all_fmt & LIST_CLASSIFY))
+		return '\0';
+	if (S_ISREG(mode) && (mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
+		return '*';
+	return APPCHAR(mode);
+}
+#endif
+
+static unsigned calc_name_len(const char *name)
+{
+	unsigned len;
+	uni_stat_t uni_stat;
+
+	// TODO: quote tab as \t, etc, if -Q
+	name = printable_string(&uni_stat, name);
+
+	if (!(option_mask32 & OPT_Q)) {
+		return uni_stat.unicode_width;
+	}
+
+	len = 2 + uni_stat.unicode_width;
+	while (*name) {
+		if (*name == '"' || *name == '\\') {
+			len++;
+		}
+		name++;
+	}
+	return len;
+}
+
+/* Return the number of used columns.
+ * 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)
+ * ls -N (--literal) = not escape at all
+ */
+static unsigned print_name(const char *name)
+{
+	unsigned len;
+	uni_stat_t uni_stat;
+
+	// TODO: quote tab as \t, etc, if -Q
+	name = printable_string(&uni_stat, name);
+
+	if (!(option_mask32 & OPT_Q)) {
+		fputs(name, stdout);
+		return uni_stat.unicode_width;
+	}
+
+	len = 2 + uni_stat.unicode_width;
+	putchar('"');
+	while (*name) {
+		if (*name == '"' || *name == '\\') {
+			putchar('\\');
+			len++;
+		}
+		putchar(*name);
+		name++;
+	}
+	putchar('"');
+	return len;
+}
+
+/* Return the number of used columns.
+ * Note that only STYLE_COLUMNAR uses return value,
+ * STYLE_SINGLE and STYLE_LONG don't care.
+ */
+static NOINLINE unsigned display_single(const struct dnode *dn)
+{
+	unsigned column = 0;
+	char *lpath;
+#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
+	struct stat statbuf;
+	char append;
+#endif
+
+#if ENABLE_FEATURE_LS_FILETYPES
+	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 */
+	lpath = NULL;
+	if (G.all_fmt & LIST_SYMLINK)
+		if (S_ISLNK(dn->dn_mode))
+			lpath = xmalloc_readlink_or_warn(dn->fullname);
+
+	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
+	else if (G.all_fmt & LIST_ID_NAME) {
+		if (option_mask32 & OPT_g) {
+			column += printf("%-8.8s ",
+				get_cached_groupname(dn->dn_gid));
+		} else {
+			column += printf("%-8.8s %-8.8s ",
+				get_cached_username(dn->dn_uid),
+				get_cached_groupname(dn->dn_gid));
+		}
+	}
+#endif
+	if (G.all_fmt & LIST_SIZE) {
+		if (S_ISBLK(dn->dn_mode) || S_ISCHR(dn->dn_mode)) {
+			column += printf("%4u, %3u ",
+					dn->dn_rdev_maj,
+					dn->dn_rdev_min);
+		} else {
+			if (option_mask32 & OPT_h) {
+				column += printf("%"HUMAN_READABLE_MAX_WIDTH_STR"s ",
+					/* print size, show one fractional, use suffixes */
+					make_human_readable_str(dn->dn_size, 1, 0)
+				);
+			} else {
+				column += printf("%9"OFF_FMT"u ", dn->dn_size);
+			}
+		}
+	}
+#if ENABLE_FEATURE_LS_TIMESTAMPS
+	if (G.all_fmt & (LIST_FULLTIME|LIST_DATE_TIME)) {
+		char *filetime;
+		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 (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 */
+			/* 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 */
+				printf("%.5s ", filetime + 11);
+			} else { /* year. buggy if year > 9999 ;) */
+				printf(" %.4s ", filetime + 20);
+			}
+			column += 13;
+		}
+	}
+#endif
+#if ENABLE_SELINUX
+	if (G.all_fmt & LIST_CONTEXT) {
+		column += printf("%-32s ", dn->sid ? dn->sid : "unknown");
+		freecon(dn->sid);
+	}
+#endif
+
+#if ENABLE_FEATURE_LS_COLOR
+	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 (G_show_color) {
+		printf("\033[0m");
+	}
+
+	if (lpath) {
+		printf(" -> ");
+#if ENABLE_FEATURE_LS_FILETYPES || ENABLE_FEATURE_LS_COLOR
+		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;
+		free(lpath);
+		if (G_show_color) {
+			printf("\033[0m");
+		}
+	}
+#if ENABLE_FEATURE_LS_FILETYPES
+	if (G.all_fmt & LIST_FILETYPE) {
+		if (append) {
+			putchar(append);
+			column++;
+		}
+	}
+#endif
+
+	return column;
+}
+
+static void display_files(struct dnode **dn, unsigned nfiles)
+{
+	unsigned i, ncols, nrows, row, nc;
+	unsigned column;
+	unsigned nexttab;
+	unsigned column_width = 0; /* used only by STYLE_COLUMNAR */
+
+	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 */
+		for (i = 0; dn[i]; i++) {
+			int len = calc_name_len(dn[i]->name);
+			if (column_width < len)
+				column_width = len;
+		}
+		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) {
+		nrows = nfiles / ncols;
+		if (nrows * ncols < nfiles)
+			nrows++;                /* round up fractionals */
+	} else {
+		nrows = 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 (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 + 1;
+				}
+				nexttab = column + column_width;
+				column += display_single(dn[i]);
+			}
+		}
+		putchar('\n');
+		column = 0;
+	}
+}
+
+
+/*** Dir scanning code ***/
+
+static struct dnode *my_stat(const char *fullname, const char *name, int force_follow)
+{
+	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;
+	}
+
+	/* 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 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)->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++;
+		}
+	}
+	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)->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]))
+			) {
+				dnp[d++] = *dn;
+			}
+		} 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 **scan_one_dir(const char *path, unsigned *nfiles_p)
+{
+	struct dnode *dn, *cur, **dnp;
+	struct dirent *entry;
+	DIR *dir;
+	unsigned i, nfiles;
+
+	*nfiles_p = 0;
+	dir = warn_opendir(path);
+	if (dir == NULL) {
+		G.exit_code = EXIT_FAILURE;
+		return NULL;	/* could not open the dir */
+	}
+	dn = NULL;
+	nfiles = 0;
+	while ((entry = readdir(dir)) != NULL) {
+		char *fullname;
+
+		/* 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]))
+			 && !(G.all_fmt & DISP_DOT)
+			) {
+				continue;
+			}
+			if (!(G.all_fmt & DISP_HIDDEN))
+				continue;
+		}
+		fullname = concat_path_file(path, entry->d_name);
+		cur = my_stat(fullname, bb_basename(fullname), 0);
+		if (!cur) {
+			free(fullname);
+			continue;
+		}
+		cur->fname_allocated = 1;
+		cur->dn_next = dn;
+		dn = cur;
+		nfiles++;
+	}
+	closedir(dir);
+
+	if (dn == NULL)
+		return NULL;
+
+	/* now that we know how many files there are
+	 * allocate memory for an array to hold dnode pointers
+	 */
+	*nfiles_p = nfiles;
+	dnp = dnalloc(nfiles);
+	for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
+		dnp[i] = dn;	/* save pointer to node in array */
+		dn = dn->dn_next;
+		if (!dn)
+			break;
+	}
+
+	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)
+{
+	struct dnode **dnd;
+	struct dnode **dnf;
+	struct dnode **dnp;
+	struct dnode *dn;
+	struct dnode *cur;
+	unsigned opt;
+	unsigned nfiles;
+	unsigned dnfiles;
+	unsigned dndirs;
+	unsigned i;
+#if ENABLE_FEATURE_LS_COLOR
+	/* colored LS support by JaWi, janwillem.janssen@lxtreme.nl */
+	/* coreutils 6.10:
+	 * # ls --color=BOGUS
+	 * ls: invalid argument 'BOGUS' for '--color'
+	 * Valid arguments are:
+	 * 'always', 'yes', 'force'
+	 * 'never', 'no', 'none'
+	 * 'auto', 'tty', 'if-tty'
+	 * (and substrings: "--color=alwa" work too)
+	 */
+	static const char ls_longopts[] ALIGN1 =
+		"color\0" Optional_argument "\xff"; /* no short equivalent */
+	static const char color_str[] ALIGN1 =
+		"always\0""yes\0""force\0"
+		"auto\0""tty\0""if-tty\0";
+	/* need to initialize since --color has _an optional_ argument */
+	const char *color_opt = color_str; /* "always" */
+#endif
+
+	INIT_G();
+
+	init_unicode();
+
+	if (ENABLE_FEATURE_LS_SORTFILES)
+		G.all_fmt = SORT_NAME;
+
+#if ENABLE_FEATURE_AUTOWIDTH
+	/* obtain the terminal width */
+	get_terminal_width_height(STDIN_FILENO, &G_terminal_width, NULL);
+	/* go one less... */
+	G_terminal_width--;
+#endif
+
+	/* process options */
+	IF_FEATURE_LS_COLOR(applet_long_options = ls_longopts;)
+	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)) {
+			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)
+				G.all_fmt &= ~TIME_MASK;
+
+			G.all_fmt |= flags;
+		}
+	}
+
+#if ENABLE_FEATURE_LS_COLOR
+	/* 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))
+			G_show_color = 1;
+	}
+	if (opt & OPT_color) {
+		if (color_opt[0] == 'n')
+			G_show_color = 0;
+		else switch (index_in_substrings(color_str, color_opt)) {
+		case 3:
+		case 4:
+		case 5:
+			if (isatty(STDOUT_FILENO)) {
+		case 0:
+		case 1:
+		case 2:
+				G_show_color = 1;
+			}
+		}
+	}
+#endif
+
+	/* sort out which command line options take precedence */
+	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 (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 ((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 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])
+		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 {
+		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; - already is */
+		cur->dn_next = dn;
+		dn = cur;
+		nfiles++;
+	} while (*argv);
+
+	/* nfiles _may_ be 0 here - try "ls doesnt_exist" */
+	if (nfiles == 0)
+		return G.exit_code;
+
+	/* now that we know how many files there are
+	 * allocate memory for an array to hold dnode pointers
+	 */
+	dnp = dnalloc(nfiles);
+	for (i = 0; /* i < nfiles - detected via !dn below */; i++) {
+		dnp[i] = dn;	/* save pointer to node in array */
+		dn = dn->dn_next;
+		if (!dn)
+			break;
+	}
+
+	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) {
+			sort_and_display_files(dnf, dnfiles);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(dnf);
+		}
+		if (dndirs > 0) {
+			dnsort(dnd, dndirs);
+			scan_and_display_dirs_recur(dnd, dnfiles == 0);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(dnd);
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		dfree(dnp);
+	return G.exit_code;
+}
diff --git a/busybox-1.19.3/coreutils/md5_sha1_sum.c b/busybox-1.19.3/coreutils/md5_sha1_sum.c
new file mode 100644
index 0000000..2cb6dd4
--- /dev/null
+++ b/busybox-1.19.3/coreutils/md5_sha1_sum.c
@@ -0,0 +1,241 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  Copyright (C) 2003 Glenn L. McGrath
+ *  Copyright (C) 2003-2004 Erik Andersen
+ *
+ * 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:	)
+
+#include "libbb.h"
+
+/* 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_SHA512 = '5',
+};
+
+#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,
+				unsigned hash_length)
+{
+	/* xzalloc zero-terminates */
+	char *hex_value = xzalloc((hash_length * 2) + 1);
+	bin2hex(hex_value, (char*)hash_value, hash_length);
+	return (unsigned char *)hex_value;
+}
+
+static uint8_t *hash_file(const char *filename)
+{
+	int src_fd, hash_len, count;
+	union _ctx_ {
+		sha512_ctx_t sha512;
+		sha256_ctx_t sha256;
+		sha1_ctx_t sha1;
+		md5_ctx_t md5;
+	} context;
+	uint8_t *hash_value;
+	void FAST_FUNC (*update)(void*, const void*, size_t);
+	void FAST_FUNC (*final)(void*, void*);
+	char hash_algo;
+
+	src_fd = open_or_warn_stdin(filename);
+	if (src_fd < 0) {
+		return NULL;
+	}
+
+	hash_algo = applet_name[3];
+
+	/* figure specific hash algorithms */
+	if (ENABLE_MD5SUM && hash_algo == HASH_MD5) {
+		md5_begin(&context.md5);
+		update = (void*)md5_hash;
+		final = (void*)md5_end;
+		hash_len = 16;
+	} else if (ENABLE_SHA1SUM && hash_algo == HASH_SHA1) {
+		sha1_begin(&context.sha1);
+		update = (void*)sha1_hash;
+		final = (void*)sha1_end;
+		hash_len = 20;
+	} else if (ENABLE_SHA256SUM && hash_algo == HASH_SHA256) {
+		sha256_begin(&context.sha256);
+		update = (void*)sha256_hash;
+		final = (void*)sha256_end;
+		hash_len = 32;
+	} else if (ENABLE_SHA512SUM && hash_algo == HASH_SHA512) {
+		sha512_begin(&context.sha512);
+		update = (void*)sha512_hash;
+		final = (void*)sha512_end;
+		hash_len = 64;
+	} else {
+		xfunc_die(); /* can't reach this */
+	}
+
+	{
+		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) {
+			final(&context, in_buf);
+			hash_value = hash_bin_to_hex(in_buf, hash_len);
+		}
+		RELEASE_CONFIG_BUFFER(in_buf);
+	}
+
+	if (src_fd != STDIN_FILENO) {
+		close(src_fd);
+	}
+
+	return hash_value;
+}
+
+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;
+	unsigned flags;
+
+	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;
+	}
+	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 with -c", 's');
+		}
+		if (flags & FLAG_WARN) {
+			bb_error_msg_and_die("-%c is meaningful only with -c", 'w');
+		}
+	}
+
+	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;
+
+			pre_computed_stream = xfopen_stdin(*argv);
+
+			while ((line = xmalloc_fgetline(pre_computed_stream)) != NULL) {
+				uint8_t *hash_value;
+				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;
+
+				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;
+				}
+				/* 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);
+			}
+			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);
+
+	return return_value;
+}
diff --git a/busybox-1.19.3/coreutils/mkdir.c b/busybox-1.19.3/coreutils/mkdir.c
new file mode 100644
index 0000000..a4429b1
--- /dev/null
+++ b/busybox-1.19.3/coreutils/mkdir.c
@@ -0,0 +1,96 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mkdir implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/mkdir.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Fixed broken permission setting when -p was used; especially in
+ * conjunction with -m.
+ */
+
+/* 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! */
+
+#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS
+static const char mkdir_longopts[] ALIGN1 =
+	"mode\0"    Required_argument "m"
+	"parents\0" No_argument       "p"
+#if ENABLE_SELINUX
+	"context\0" Required_argument "Z"
+#endif
+	;
+#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);
+	int status = EXIT_SUCCESS;
+	int flags = 0;
+	unsigned opt;
+	char *smode;
+#if ENABLE_SELINUX
+	security_context_t scontext;
+#endif
+
+#if ENABLE_FEATURE_MKDIR_LONG_OPTIONS
+	applet_long_options = mkdir_longopts;
+#endif
+	opt = getopt32(argv, "m:p" IF_SELINUX("Z:"), &smode IF_SELINUX(,&scontext));
+	if (opt & 1) {
+		mode = 0777;
+		if (!bb_parse_mode(smode, &mode)) {
+			bb_error_msg_and_die("invalid mode '%s'", smode);
+		}
+	}
+	if (opt & 2)
+		flags |= FILEUTILS_RECUR;
+#if ENABLE_SELINUX
+	if (opt & 4) {
+		selinux_or_die();
+		setfscreatecon_or_die(scontext);
+	}
+#endif
+
+	argv += optind;
+	if (!argv[0])
+		bb_show_usage();
+
+	do {
+		if (bb_make_directory(*argv, mode, flags)) {
+			status = EXIT_FAILURE;
+		}
+	} while (*++argv);
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/mkfifo.c b/busybox-1.19.3/coreutils/mkfifo.c
new file mode 100644
index 0000000..ef58325
--- /dev/null
+++ b/busybox-1.19.3/coreutils/mkfifo.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfifo implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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)
+{
+	mode_t mode;
+	int retval = EXIT_SUCCESS;
+
+	mode = getopt_mk_fifo_nod(argv);
+
+	argv += optind;
+	if (!*argv) {
+		bb_show_usage();
+	}
+
+	do {
+		if (mkfifo(*argv, mode) < 0) {
+			bb_simple_perror_msg(*argv);  /* Avoid multibyte problems. */
+			retval = EXIT_FAILURE;
+		}
+	} while (*++argv);
+
+	return retval;
+}
diff --git a/busybox-1.19.3/coreutils/mknod.c b/busybox-1.19.3/coreutils/mknod.c
new file mode 100644
index 0000000..32d3659
--- /dev/null
+++ b/busybox-1.19.3/coreutils/mknod.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mknod implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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 };
+
+int mknod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mknod_main(int argc, char **argv)
+{
+	mode_t mode;
+	dev_t dev;
+	const char *name;
+
+	mode = getopt_mk_fifo_nod(argv);
+	argv += optind;
+	argc -= optind;
+
+	if (argc >= 2) {
+		name = strchr(modes_chars, argv[1][0]);
+		if (name != NULL) {
+			mode |= modes_cubp[(int)(name[4])];
+
+			dev = 0;
+			if (*name != 'p') {
+				argc -= 2;
+				if (argc == 2) {
+					/* 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)));
+				}
+			}
+
+			if (argc == 2) {
+				name = *argv;
+				if (mknod(name, mode, dev) == 0) {
+					return EXIT_SUCCESS;
+				}
+				bb_simple_perror_msg_and_die(name);
+			}
+		}
+	}
+	bb_show_usage();
+}
diff --git a/busybox-1.19.3/coreutils/mv.c b/busybox-1.19.3/coreutils/mv.c
new file mode 100644
index 0000000..87f4cd5
--- /dev/null
+++ b/busybox-1.19.3/coreutils/mv.c
@@ -0,0 +1,154 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mv implementation for busybox
+ *
+ * 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 source tree.
+ */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reduction and improved error checking.
+ */
+
+#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"
+	;
+#endif
+
+#define OPT_FILEUTILS_FORCE       1
+#define OPT_FILEUTILS_INTERACTIVE 2
+#define OPT_FILEUTILS_NOCLOBBER   4
+
+int mv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mv_main(int argc, char **argv)
+{
+	struct stat dest_stat;
+	const char *last;
+	const char *dest;
+	unsigned flags;
+	int dest_exists;
+	int status = 0;
+	int copy_flag = 0;
+
+#if ENABLE_FEATURE_MV_LONG_OPTIONS
+	applet_long_options = mv_longopts;
+#endif
+	/* 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). */
+	opt_complementary = "-2:f-in:i-fn:n-fi";
+	flags = getopt32(argv, "fin");
+	argc -= optind;
+	argv += optind;
+	last = argv[argc - 1];
+
+	if (argc == 2) {
+		dest_exists = cp_mv_stat(last, &dest_stat);
+		if (dest_exists < 0) {
+			return EXIT_FAILURE;
+		}
+
+		if (!(dest_exists & 2)) { /* last is not a directory */
+			dest = last;
+			goto DO_MOVE;
+		}
+	}
+
+	do {
+		dest = concat_path_file(last, bb_get_last_path_component_strip(*argv));
+		dest_exists = cp_mv_stat(dest, &dest_stat);
+		if (dest_exists < 0) {
+			goto RET_1;
+		}
+
+ DO_MOVE:
+		if (dest_exists) {
+			if (flags & OPT_FILEUTILS_NOCLOBBER)
+				goto RET_0;
+			if (!(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()) {
+					goto RET_0;
+				}
+			}
+		}
+
+		if (rename(*argv, dest) < 0) {
+			struct stat source_stat;
+			int source_exists;
+
+			if (errno != EXDEV
+			 || (source_exists = cp_mv_stat2(*argv, &source_stat, lstat)) < 1
+			) {
+				bb_perror_msg("can't rename '%s'", *argv);
+			} else {
+				static const char fmt[] ALIGN1 =
+					"can't overwrite %sdirectory with %sdirectory";
+
+				if (dest_exists) {
+					if (dest_exists == 3) {
+						if (source_exists != 3) {
+							bb_error_msg(fmt, "", "non-");
+							goto RET_1;
+						}
+					} else {
+						if (source_exists == 3) {
+							bb_error_msg(fmt, "non-", "");
+							goto RET_1;
+						}
+					}
+					if (unlink(dest) < 0) {
+						bb_perror_msg("can't remove '%s'", dest);
+						goto RET_1;
+					}
+				}
+				/* FILEUTILS_RECUR also prevents nasties like
+				 * "read from device and write contents to dst"
+				 * instead of "create same device node" */
+				copy_flag = FILEUTILS_RECUR | FILEUTILS_PRESERVE_STATUS;
+#if ENABLE_SELINUX
+				copy_flag |= FILEUTILS_PRESERVE_SECURITY_CONTEXT;
+#endif
+				if ((copy_file(*argv, dest, copy_flag) >= 0)
+				 && (remove_file(*argv, FILEUTILS_RECUR | FILEUTILS_FORCE) >= 0)
+				) {
+					goto RET_0;
+				}
+			}
+ RET_1:
+			status = 1;
+		}
+ RET_0:
+		if (dest != last) {
+			free((void *) dest);
+		}
+	} while (*++argv != last);
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/nice.c b/busybox-1.19.3/coreutils/nice.c
new file mode 100644
index 0000000..ce75991
--- /dev/null
+++ b/busybox-1.19.3/coreutils/nice.c
@@ -0,0 +1,57 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * nice implementation for busybox
+ *
+ * Copyright (C) 2005  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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"
+
+int nice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nice_main(int argc, char **argv)
+{
+	int old_priority, adjustment;
+
+	old_priority = getpriority(PRIO_PROCESS, 0);
+
+	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. */
+
+	if (argv[0][0] == '-') {
+		if (argv[0][1] == 'n') { /* -n */
+			if (argv[0][2]) { /* -nNNNN (w/o space) */
+				argv[0] += 2; argv--; argc++;
+			}
+		} else { /* -NNN (NNN may be negative) == -n NNN */
+			argv[0] += 1; argv--; argc++;
+		}
+		if (argc < 4) {  /* Missing priority and/or utility! */
+			bb_show_usage();
+		}
+		adjustment = xatoi_range(argv[1], INT_MIN/2, INT_MAX/2);
+		argv += 2;
+	}
+
+	{  /* Set our priority. */
+		int prio = old_priority + adjustment;
+
+		if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
+			bb_perror_msg_and_die("setpriority(%d)", prio);
+		}
+	}
+
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/coreutils/nohup.c b/busybox-1.19.3/coreutils/nohup.c
new file mode 100644
index 0000000..63853fd
--- /dev/null
+++ b/busybox-1.19.3/coreutils/nohup.c
@@ -0,0 +1,88 @@
+/* vi: set sw=4 ts=4: */
+/* nohup - invoke a utility immune to hangups.
+ *
+ * Busybox version based on nohup specification at
+ * http://www.opengroup.org/onlinepubs/007904975/utilities/nohup.html
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ * Copyright 2006 Bernhard Reutner-Fischer
+ *
+ * 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:
+# nohup true
+nohup: ignoring input and appending output to `nohup.out'
+# nohup true 1>/dev/null
+nohup: ignoring input and redirecting stderr to stdout
+# nohup true 2>zz
+# cat zz
+nohup: ignoring input and appending output to `nohup.out'
+# nohup true 2>zz 1>/dev/null
+# cat zz
+nohup: ignoring input
+# nohup true </dev/null 1>/dev/null
+nohup: redirecting stderr to stdout
+# nohup true </dev/null 2>zz 1>/dev/null
+# cat zz
+  (nothing)
+#
+*/
+
+int nohup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nohup_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *nohupout;
+	char *home;
+
+	xfunc_error_retval = 127;
+
+	if (!argv[1]) {
+		bb_show_usage();
+	}
+
+	/* If stdin is a tty, detach from it. */
+	if (isatty(STDIN_FILENO)) {
+		/* bb_error_msg("ignoring input"); */
+		close(STDIN_FILENO);
+		xopen(bb_dev_null, O_RDONLY); /* will be fd 0 (STDIN_FILENO) */
+	}
+
+	nohupout = "nohup.out";
+	/* Redirect stdout to nohup.out, either in "." or in "$HOME". */
+	if (isatty(STDOUT_FILENO)) {
+		close(STDOUT_FILENO);
+		if (open(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR) < 0) {
+			home = getenv("HOME");
+			if (home) {
+				nohupout = concat_path_file(home, nohupout);
+				xopen3(nohupout, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR);
+			} else {
+				xopen(bb_dev_null, O_RDONLY); /* will be fd 1 */
+			}
+		}
+		bb_error_msg("appending output to %s", nohupout);
+	}
+
+	/* If we have a tty on stderr, redirect to stdout. */
+	if (isatty(STDERR_FILENO)) {
+		/* if (stdout_wasnt_a_tty)
+			bb_error_msg("redirecting stderr to stdout"); */
+		dup2(STDOUT_FILENO, STDERR_FILENO);
+	}
+
+	signal(SIGHUP, SIG_IGN);
+
+	argv++;
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/coreutils/od.c b/busybox-1.19.3/coreutils/od.c
new file mode 100644
index 0000000..fb11fcf
--- /dev/null
+++ b/busybox-1.19.3/coreutils/od.c
@@ -0,0 +1,228 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * od implementation for busybox
+ * Based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1990
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * 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
+/* This one provides -t (busybox's own build script needs it) */
+#include "od_bloaty.c"
+#else
+
+#include "dump.h"
+
+static void
+odoffset(dumper_t *dumper, int argc, char ***argvp)
+{
+	char *num, *p;
+	int base;
+	char *end;
+
+	/*
+	 * The offset syntax of od(1) was genuinely bizarre.  First, if
+	 * it started with a plus it had to be an offset.  Otherwise, if
+	 * there were at least two arguments, a number or lower-case 'x'
+	 * followed by a number makes it an offset.  By default it was
+	 * octal; if it started with 'x' or '0x' it was hex.  If it ended
+	 * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
+	 * multiplied the number by 512 or 1024 byte units.  There was
+	 * no way to assign a block count to a hex offset.
+	 *
+	 * We assumes it's a file if the offset is bad.
+	 */
+	p = **argvp;
+
+	if (!p) {
+		/* hey someone is probably piping to us ... */
+		return;
+	}
+
+	if ((*p != '+')
+		&& (argc < 2
+			|| (!isdigit(p[0])
+				&& ((p[0] != 'x') || !isxdigit(p[1])))))
+		return;
+
+	base = 0;
+	/*
+	 * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
+	 * set base.
+	 */
+	if (p[0] == '+')
+		++p;
+	if (p[0] == 'x' && isxdigit(p[1])) {
+		++p;
+		base = 16;
+	} else if (p[0] == '0' && p[1] == 'x') {
+		p += 2;
+		base = 16;
+	}
+
+	/* skip over the number */
+	if (base == 16)
+		for (num = p; isxdigit(*p); ++p)
+			continue;
+	else
+		for (num = p; isdigit(*p); ++p)
+			continue;
+
+	/* check for no number */
+	if (num == p)
+		return;
+
+	/* if terminates with a '.', base is decimal */
+	if (*p == '.') {
+		if (base)
+			return;
+		base = 10;
+	}
+
+	dumper->dump_skip = strtol(num, &end, base ? base : 8);
+
+	/* if end isn't the same as p, we got a non-octal digit */
+	if (end != p)
+		dumper->dump_skip = 0;
+	else {
+		if (*p) {
+			if (*p == 'b') {
+				dumper->dump_skip *= 512;
+				++p;
+			} else if (*p == 'B') {
+				dumper->dump_skip *= 1024;
+				++p;
+			}
+		}
+		if (*p)
+			dumper->dump_skip = 0;
+		else {
+			++*argvp;
+			/*
+			 * If the offset uses a non-octal base, the base of
+			 * the offset is changed as well.  This isn't pretty,
+			 * but it's easy.
+			 */
+#define TYPE_OFFSET 7
+			{
+				char x_or_d;
+				if (base == 16) {
+					x_or_d = 'x';
+					goto DO_X_OR_D;
+				}
+				if (base == 10) {
+					x_or_d = 'd';
+ DO_X_OR_D:
+					dumper->fshead->nextfu->fmt[TYPE_OFFSET]
+						= dumper->fshead->nextfs->nextfu->fmt[TYPE_OFFSET]
+						= x_or_d;
+				}
+			}
+		}
+	}
+}
+
+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 */
+};
+
+static const char od_opts[] ALIGN1 = "aBbcDdeFfHhIiLlOoXxv";
+
+static const char od_o2si[] ALIGN1 = {
+	0, 1, 2, 3, 5,
+	4, 6, 6, 7, 8,
+	9, 0xa, 0xb, 0xa, 0xa,
+	0xb, 1, 8, 9,
+};
+
+int od_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int od_main(int argc, char **argv)
+{
+	int ch;
+	int first = 1;
+	char *p;
+	dumper_t *dumper = alloc_dumper();
+
+	while ((ch = getopt(argc, argv, od_opts)) > 0) {
+		if (ch == 'v') {
+			dumper->dump_vflag = ALL;
+		} else if (((p = strchr(od_opts, ch)) != NULL) && (*p != '\0')) {
+			if (first) {
+				first = 0;
+				bb_dump_add(dumper, "\"%07.7_Ao\n\"");
+				bb_dump_add(dumper, "\"%07.7_ao  \"");
+			} else {
+				bb_dump_add(dumper, "\"         \"");
+			}
+			bb_dump_add(dumper, add_strings[(int)od_o2si[(p - od_opts)]]);
+		} else {  /* P, p, s, w, or other unhandled */
+			bb_show_usage();
+		}
+	}
+	if (!dumper->fshead) {
+		bb_dump_add(dumper, "\"%07.7_Ao\n\"");
+		bb_dump_add(dumper, "\"%07.7_ao  \" 8/2 \"%06o \" \"\\n\"");
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	odoffset(dumper, argc, &argv);
+
+	return bb_dump_dump(dumper, argv);
+}
+#endif /* ENABLE_DESKTOP */
+
+/*-
+ * Copyright (c) 1990 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
+ * 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. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/coreutils/od_bloaty.c b/busybox-1.19.3/coreutils/od_bloaty.c
new file mode 100644
index 0000000..347f879
--- /dev/null
+++ b/busybox-1.19.3/coreutils/od_bloaty.c
@@ -0,0 +1,1384 @@
+/* od -- dump files in octal and other formats
+   Copyright (C) 92, 1995-2004 Free Software Foundation, Inc.
+
+   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, or (at your option)
+   any later version.
+
+   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., 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 */
+
+
+/* #include "libbb.h" - done in od.c */
+#define assert(a) ((void)0)
+
+
+//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,
+};
+
+#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)
+
+
+/* Check for 0x7f is a coreutils 6.3 addition */
+#define ISPRINT(c) (((c) >= ' ') && (c) < 0x7f)
+
+typedef long double longdouble_t;
+typedef unsigned long long ulonglong_t;
+typedef long long llong;
+
+#if ENABLE_LFS
+# define xstrtooff_sfx xstrtoull_sfx
+#else
+# define xstrtooff_sfx xstrtoul_sfx
+#endif
+
+/* The default number of input bytes per output line.  */
+#define DEFAULT_BYTES_PER_BLOCK 16
+
+/* The number of decimal digits of precision in a float.  */
+#ifndef FLT_DIG
+# define FLT_DIG 7
+#endif
+
+/* The number of decimal digits of precision in a double.  */
+#ifndef DBL_DIG
+# define DBL_DIG 15
+#endif
+
+/* The number of decimal digits of precision in a long double.  */
+#ifndef LDBL_DIG
+# define LDBL_DIG DBL_DIG
+#endif
+
+enum size_spec {
+	NO_SIZE,
+	CHAR,
+	SHORT,
+	INT,
+	LONG,
+	LONG_LONG,
+	FLOAT_SINGLE,
+	FLOAT_DOUBLE,
+	FLOAT_LONG_DOUBLE,
+	N_SIZE_SPECS
+};
+
+enum output_format {
+	SIGNED_DECIMAL,
+	UNSIGNED_DECIMAL,
+	OCTAL,
+	HEXADECIMAL,
+	FLOATING_POINT,
+	NAMED_CHARACTER,
+	CHARACTER
+};
+
+/* Each output format specification (from '-t spec' or from
+   old-style options) is represented by one of these structures.  */
+struct tspec {
+	enum output_format fmt;
+	enum size_spec size;
+	void (*print_function) (size_t, const char *, const char *);
+	char *fmt_string;
+	int hexl_mode_trailer;
+	int field_width;
+};
+
+/* Convert the number of 8-bit bytes of a binary representation to
+   the number of characters (digits + sign if the type is signed)
+   required to represent the same quantity in the specified base/type.
+   For example, a 32-bit (4-byte) quantity may require a field width
+   as wide as the following for these types:
+   11	unsigned octal
+   11	signed decimal
+   10	unsigned decimal
+   8	unsigned hexadecimal  */
+
+static const uint8_t bytes_to_oct_digits[] ALIGN1 =
+{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};
+
+static const uint8_t bytes_to_signed_dec_digits[] ALIGN1 =
+{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};
+
+static const uint8_t bytes_to_unsigned_dec_digits[] ALIGN1 =
+{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};
+
+static const uint8_t bytes_to_hex_digits[] ALIGN1 =
+{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};
+
+/* Convert enum size_spec to the size of the named type.  */
+static const signed char width_bytes[] ALIGN1 = {
+	-1,
+	sizeof(char),
+	sizeof(short),
+	sizeof(int),
+	sizeof(long),
+	sizeof(ulonglong_t),
+	sizeof(float),
+	sizeof(double),
+	sizeof(longdouble_t)
+};
+/* Ensure that for each member of 'enum size_spec' there is an
+   initializer in the width_bytes array.  */
+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 exit_code;
+
+static unsigned string_min;
+
+/* An array of specs describing how to format each input block.  */
+static size_t n_specs;
+static struct tspec *spec;
+
+/* Function that accepts an address and an optional following char,
+   and prints the address and char to stdout.  */
+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.  */
+
+/* The number of input bytes formatted per output line.  It must be
+   a multiple of the least common multiple of the sizes associated with
+   the specified output types.  It should be as large as possible, but
+   no larger than 16 -- unless specified with the -w option.  */
+static unsigned bytes_per_block = 32; /* have to use unsigned, not size_t */
+
+/* A NULL-terminated list of the file-arguments from the command line.  */
+static const char *const *file_list;
+
+/* The input stream associated with the current file.  */
+static FILE *in_stream;
+
+#define MAX_INTEGRAL_TYPE_SIZE sizeof(ulonglong_t)
+static const unsigned char integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1] ALIGN1 = {
+	[sizeof(char)] = CHAR,
+#if USHRT_MAX != UCHAR_MAX
+	[sizeof(short)] = SHORT,
+#endif
+#if UINT_MAX != USHRT_MAX
+	[sizeof(int)] = INT,
+#endif
+#if ULONG_MAX != UINT_MAX
+	[sizeof(long)] = LONG,
+#endif
+#if ULLONG_MAX != ULONG_MAX
+	[sizeof(ulonglong_t)] = LONG_LONG,
+#endif
+};
+
+#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 wins */
+	[sizeof(longdouble_t)] = FLOAT_LONG_DOUBLE,
+	[sizeof(double)] = FLOAT_DOUBLE,
+	[sizeof(float)] = FLOAT_SINGLE
+};
+
+
+static unsigned
+gcd(unsigned u, unsigned v)
+{
+	unsigned t;
+	while (v != 0) {
+		t = u % v;
+		u = v;
+		v = t;
+	}
+	return u;
+}
+
+/* Compute the least common multiple of U and V.  */
+static unsigned
+lcm(unsigned u, unsigned v) {
+	unsigned t = gcd(u, v);
+	if (t == 0)
+		return 0;
+	return u * v / t;
+}
+
+static void
+print_s_char(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	while (n_bytes--) {
+		int tmp = *(signed char *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(unsigned char);
+	}
+}
+
+static void
+print_char(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	while (n_bytes--) {
+		unsigned tmp = *(unsigned char *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(unsigned char);
+	}
+}
+
+static void
+print_s_short(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(signed short);
+	while (n_bytes--) {
+		int tmp = *(signed short *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(unsigned short);
+	}
+}
+
+static void
+print_short(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(unsigned short);
+	while (n_bytes--) {
+		unsigned tmp = *(unsigned short *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(unsigned short);
+	}
+}
+
+static void
+print_int(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(unsigned);
+	while (n_bytes--) {
+		unsigned tmp = *(unsigned *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(unsigned);
+	}
+}
+
+#if UINT_MAX == ULONG_MAX
+# define print_long print_int
+#else
+static void
+print_long(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(unsigned long);
+	while (n_bytes--) {
+		unsigned long tmp = *(unsigned long *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(unsigned long);
+	}
+}
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+# define print_long_long print_long
+#else
+static void
+print_long_long(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(ulonglong_t);
+	while (n_bytes--) {
+		ulonglong_t tmp = *(ulonglong_t *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(ulonglong_t);
+	}
+}
+#endif
+
+static void
+print_float(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(float);
+	while (n_bytes--) {
+		float tmp = *(float *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(float);
+	}
+}
+
+static void
+print_double(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(double);
+	while (n_bytes--) {
+		double tmp = *(double *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(double);
+	}
+}
+
+static void
+print_long_double(size_t n_bytes, const char *block, const char *fmt_string)
+{
+	n_bytes /= sizeof(longdouble_t);
+	while (n_bytes--) {
+		longdouble_t tmp = *(longdouble_t *) block;
+		printf(fmt_string, tmp);
+		block += sizeof(longdouble_t);
+	}
+}
+
+/* print_[named]_ascii are optimized for speed.
+ * Remember, someday you may want to pump gigabytes through this thing.
+ * Saving a dozen of .text bytes here is counter-productive */
+
+static void
+print_named_ascii(size_t n_bytes, const char *block,
+		const char *unused_fmt_string UNUSED_PARAM)
+{
+	/* Names for some non-printing characters.  */
+	static const char charname[33][3] ALIGN1 = {
+		"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+		" bs", " ht", " nl", " vt", " ff", " cr", " so", " si",
+		"dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+		"can", " em", "sub", "esc", " fs", " gs", " rs", " us",
+		" sp"
+	};
+	// buf[N] pos:  01234 56789
+	char buf[12] = "   x\0 0xx\0";
+	// 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.
+
+	while (n_bytes--) {
+		unsigned masked_c = *(unsigned char *) block++;
+
+		masked_c &= 0x7f;
+		if (masked_c == 0x7f) {
+			fputs(" del", stdout);
+			continue;
+		}
+		if (masked_c > ' ') {
+			buf[3] = masked_c;
+			fputs(buf, stdout);
+			continue;
+		}
+		/* Why? Because printf(" %3.3s") is much slower... */
+		buf[6] = charname[masked_c][0];
+		buf[7] = charname[masked_c][1];
+		buf[8] = charname[masked_c][2];
+		fputs(buf+5, stdout);
+	}
+}
+
+static void
+print_ascii(size_t n_bytes, const char *block,
+		const char *unused_fmt_string UNUSED_PARAM)
+{
+	// buf[N] pos:  01234 56789
+	char buf[12] = "   x\0 0xx\0";
+
+	while (n_bytes--) {
+		const char *s;
+		unsigned c = *(unsigned char *) block++;
+
+		if (ISPRINT(c)) {
+			buf[3] = c;
+			fputs(buf, stdout);
+			continue;
+		}
+		switch (c) {
+		case '\0':
+			s = "  \\0";
+			break;
+		case '\007':
+			s = "  \\a";
+			break;
+		case '\b':
+			s = "  \\b";
+			break;
+		case '\f':
+			s = "  \\f";
+			break;
+		case '\n':
+			s = "  \\n";
+			break;
+		case '\r':
+			s = "  \\r";
+			break;
+		case '\t':
+			s = "  \\t";
+			break;
+		case '\v':
+			s = "  \\v";
+			break;
+		case '\x7f':
+			s = " 177";
+			break;
+		default: /* c is never larger than 040 */
+			buf[7] = (c >> 3) + '0';
+			buf[8] = (c & 7) + '0';
+			s = buf + 5;
+		}
+		fputs(s, stdout);
+	}
+}
+
+/* Given a list of one or more input filenames FILE_LIST, set the global
+   file pointer IN_STREAM and the global string INPUT_FILENAME to the
+   first one that can be successfully opened. Modify FILE_LIST to
+   reference the next filename in the list.  A file name of "-" is
+   interpreted as standard input.  If any file open fails, give an error
+   message and return nonzero.  */
+
+static void
+open_next_file(void)
+{
+	while (1) {
+		if (!*file_list)
+			return;
+		in_stream = fopen_or_warn_stdin(*file_list++);
+		if (in_stream) {
+			break;
+		}
+		exit_code = 1;
+	}
+
+	if ((option_mask32 & (OPT_N|OPT_S)) == OPT_N)
+		setbuf(in_stream, NULL);
+}
+
+/* Test whether there have been errors on in_stream, and close it if
+   it is not standard input.  Return nonzero if there has been an error
+   on in_stream or stdout; return zero otherwise.  This function will
+   report more than one error only if both a read and a write error
+   have occurred.  IN_ERRNO, if nonzero, is the error number
+   corresponding to the most recent action for IN_STREAM.  */
+
+static void
+check_and_close(void)
+{
+	if (in_stream) {
+		if (ferror(in_stream))	{
+			bb_error_msg("%s: read error", (in_stream == stdin)
+					? bb_msg_standard_input
+					: file_list[-1]
+			);
+			exit_code = 1;
+		}
+		fclose_if_not_stdin(in_stream);
+		in_stream = NULL;
+	}
+
+	if (ferror(stdout)) {
+		bb_error_msg_and_die(bb_msg_write_error);
+	}
+}
+
+/* If S points to a single valid modern od format string, put
+   a description of that format in *TSPEC, return pointer to
+   character following the just-decoded format.
+   For example, if S were "d4afL", we will return a rtp to "afL"
+   and *TSPEC would be
+	{
+		fmt = SIGNED_DECIMAL;
+		size = INT or LONG; (whichever integral_type_size[4] resolves to)
+		print_function = print_int; (assuming size == INT)
+		fmt_string = "%011d%c";
+	}
+   S_ORIG is solely for reporting errors.  It should be the full format
+   string argument. */
+
+static NOINLINE const char *
+decode_one_format(const char *s_orig, const char *s, struct tspec *tspec)
+{
+	enum size_spec size_spec;
+	unsigned size;
+	enum output_format fmt;
+	const char *p;
+	char *end;
+	char *fmt_string = NULL;
+	void (*print_function) (size_t, const char *, const char *);
+	unsigned c;
+	unsigned field_width = 0;
+	int pos;
+
+	switch (*s) {
+	case 'd':
+	case 'o':
+	case 'u':
+	case 'x': {
+		static const char CSIL[] ALIGN1 = "CSIL";
+
+		c = *s++;
+		p = strchr(CSIL, *s);
+		/* if *s == NUL, p != NULL! Testcase: "od -tx" */
+		if (!p || *p == '\0') {
+			size = sizeof(int);
+			if (isdigit(s[0])) {
+				size = bb_strtou(s, &end, 0);
+				if (errno == ERANGE
+				 || MAX_INTEGRAL_TYPE_SIZE < size
+				 || integral_type_size[size] == NO_SIZE
+				) {
+					bb_error_msg_and_die("invalid type string '%s'; "
+						"%u-byte %s type is not supported",
+						s_orig, size, "integral");
+				}
+				s = end;
+			}
+		} else {
+			static const uint8_t CSIL_sizeof[4] = {
+				sizeof(char),
+				sizeof(short),
+				sizeof(int),
+				sizeof(long),
+			};
+			size = CSIL_sizeof[p - CSIL];
+			s++; /* skip C/S/I/L */
+		}
+
+#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format) \
+	((Spec) == LONG_LONG ? (Max_format) \
+	: ((Spec) == LONG ? (Long_format) : (Min_format)))
+
+#define FMT_BYTES_ALLOCATED 9
+		size_spec = integral_type_size[size];
+
+		{
+			static const char doux[] ALIGN1 = "doux";
+			static const char doux_fmt_letter[][4] = {
+				"lld", "llo", "llu", "llx"
+			};
+			static const enum output_format doux_fmt[] = {
+				SIGNED_DECIMAL,
+				OCTAL,
+				UNSIGNED_DECIMAL,
+				HEXADECIMAL,
+			};
+			static const uint8_t *const doux_bytes_to_XXX[] = {
+				bytes_to_signed_dec_digits,
+				bytes_to_oct_digits,
+				bytes_to_unsigned_dec_digits,
+				bytes_to_hex_digits,
+			};
+			static const char doux_fmtstring[][sizeof(" %%0%u%s")] = {
+				" %%%u%s",
+				" %%0%u%s",
+				" %%%u%s",
+				" %%0%u%s",
+			};
+
+			pos = strchr(doux, c) - doux;
+			fmt = doux_fmt[pos];
+			field_width = doux_bytes_to_XXX[pos][size];
+			p = doux_fmt_letter[pos] + 2;
+			if (size_spec == LONG) p--;
+			if (size_spec == LONG_LONG) p -= 2;
+			fmt_string = xasprintf(doux_fmtstring[pos], field_width, p);
+		}
+
+		switch (size_spec) {
+		case CHAR:
+			print_function = (fmt == SIGNED_DECIMAL
+				    ? print_s_char
+				    : print_char);
+			break;
+		case SHORT:
+			print_function = (fmt == SIGNED_DECIMAL
+				    ? print_s_short
+				    : print_short);
+			break;
+		case INT:
+			print_function = print_int;
+			break;
+		case LONG:
+			print_function = print_long;
+			break;
+		default: /* case LONG_LONG: */
+			print_function = print_long_long;
+			break;
+		}
+		break;
+	}
+
+	case 'f': {
+		static const char FDL[] ALIGN1 = "FDL";
+
+		fmt = FLOATING_POINT;
+		++s;
+		p = strchr(FDL, *s);
+		if (!p) {
+			size = sizeof(double);
+			if (isdigit(s[0])) {
+				size = bb_strtou(s, &end, 0);
+				if (errno == ERANGE || size > MAX_FP_TYPE_SIZE
+				 || fp_type_size[size] == NO_SIZE
+				) {
+					bb_error_msg_and_die("invalid type string '%s'; "
+						"%u-byte %s type is not supported",
+						s_orig, size, "floating point");
+				}
+				s = end;
+			}
+		} else {
+			static const uint8_t FDL_sizeof[] = {
+				sizeof(float),
+				sizeof(double),
+				sizeof(longdouble_t),
+			};
+
+			size = FDL_sizeof[p - FDL];
+		}
+
+		size_spec = fp_type_size[size];
+
+		switch (size_spec) {
+		case FLOAT_SINGLE:
+			print_function = print_float;
+			field_width = FLT_DIG + 8;
+			/* Don't use %#e; not all systems support it.  */
+			fmt_string = xasprintf(" %%%d.%de", field_width, FLT_DIG);
+			break;
+		case FLOAT_DOUBLE:
+			print_function = print_double;
+			field_width = DBL_DIG + 8;
+			fmt_string = xasprintf(" %%%d.%de", field_width, DBL_DIG);
+			break;
+		default: /* case FLOAT_LONG_DOUBLE: */
+			print_function = print_long_double;
+			field_width = LDBL_DIG + 8;
+			fmt_string = xasprintf(" %%%d.%dLe", field_width, LDBL_DIG);
+			break;
+		}
+		break;
+	}
+
+	case 'a':
+		++s;
+		fmt = NAMED_CHARACTER;
+		size_spec = CHAR;
+		print_function = print_named_ascii;
+		field_width = 3;
+		break;
+	case 'c':
+		++s;
+		fmt = CHARACTER;
+		size_spec = CHAR;
+		print_function = print_ascii;
+		field_width = 3;
+		break;
+	default:
+		bb_error_msg_and_die("invalid character '%c' "
+				"in type string '%s'", *s, s_orig);
+	}
+
+	tspec->size = size_spec;
+	tspec->fmt = fmt;
+	tspec->print_function = print_function;
+	tspec->fmt_string = fmt_string;
+
+	tspec->field_width = field_width;
+	tspec->hexl_mode_trailer = (*s == 'z');
+	if (tspec->hexl_mode_trailer)
+		s++;
+
+	return s;
+}
+
+/* Decode the modern od format string S.  Append the decoded
+   representation to the global array SPEC, reallocating SPEC if
+   necessary.  */
+
+static void
+decode_format_string(const char *s)
+{
+	const char *s_orig = s;
+
+	while (*s != '\0') {
+		struct tspec tspec;
+		const char *next;
+
+		next = decode_one_format(s_orig, s, &tspec);
+
+		assert(s != next);
+		s = next;
+		spec = xrealloc_vector(spec, 4, n_specs);
+		memcpy(&spec[n_specs], &tspec, sizeof(spec[0]));
+		n_specs++;
+	}
+}
+
+/* Given a list of one or more input filenames FILE_LIST, set the global
+   file pointer IN_STREAM to position N_SKIP in the concatenation of
+   those files.  If any file operation fails or if there are fewer than
+   N_SKIP bytes in the combined input, give an error message and return
+   nonzero.  When possible, use seek rather than read operations to
+   advance IN_STREAM.  */
+
+static void
+skip(off_t n_skip)
+{
+	if (n_skip == 0)
+		return;
+
+	while (in_stream) { /* !EOF */
+		struct stat file_stats;
+
+		/* First try seeking.  For large offsets, this extra work is
+		   worthwhile.  If the offset is below some threshold it may be
+		   more efficient to move the pointer by reading.  There are two
+		   issues when trying to seek:
+			- the file must be seekable.
+			- before seeking to the specified position, make sure
+			  that the new position is in the current file.
+			  Try to do that by getting file's size using fstat.
+			  But that will work only for regular files.  */
+
+			/* The st_size field is valid only for regular files
+			   (and for symbolic links, which cannot occur here).
+			   If the number of bytes left to skip is at least
+			   as large as the size of the current file, we can
+			   decrement n_skip and go on to the next file.  */
+		if (fstat(fileno(in_stream), &file_stats) == 0
+		 && S_ISREG(file_stats.st_mode) && file_stats.st_size > 0
+		) {
+			if (file_stats.st_size < n_skip) {
+				n_skip -= file_stats.st_size;
+				/* take "check & close / open_next" route */
+			} else {
+				if (fseeko(in_stream, n_skip, SEEK_CUR) != 0)
+					exit_code = 1;
+				return;
+			}
+		} else {
+			/* If it's not a regular file with positive size,
+			   position the file pointer by reading.  */
+			char buf[1024];
+			size_t n_bytes_to_read = 1024;
+			size_t n_bytes_read;
+
+			while (n_skip > 0) {
+				if (n_skip < n_bytes_to_read)
+					n_bytes_to_read = n_skip;
+				n_bytes_read = fread(buf, 1, n_bytes_to_read, in_stream);
+				n_skip -= n_bytes_read;
+				if (n_bytes_read != n_bytes_to_read)
+					break; /* EOF on this file or error */
+			}
+		}
+		if (n_skip == 0)
+			return;
+
+		check_and_close();
+		open_next_file();
+	}
+
+	if (n_skip)
+		bb_error_msg_and_die("can't skip past end of combined input");
+}
+
+
+typedef void FN_format_address(off_t address, char c);
+
+static void
+format_address_none(off_t address UNUSED_PARAM, char c UNUSED_PARAM)
+{
+}
+
+static char address_fmt[] ALIGN1 = "%0n"OFF_FMT"xc";
+/* Corresponds to 'x' above */
+#define address_base_char address_fmt[sizeof(address_fmt)-3]
+/* Corresponds to 'n' above */
+#define address_pad_len_char address_fmt[2]
+
+static void
+format_address_std(off_t address, char c)
+{
+	/* Corresponds to 'c' */
+	address_fmt[sizeof(address_fmt)-2] = c;
+	printf(address_fmt, address);
+}
+
+#if ENABLE_LONG_OPTS
+/* only used with --traditional */
+static void
+format_address_paren(off_t address, char c)
+{
+	putchar('(');
+	format_address_std(address, ')');
+	if (c) putchar(c);
+}
+
+static void
+format_address_label(off_t address, char c)
+{
+	format_address_std(address, ' ');
+	format_address_paren(address + pseudo_offset, c);
+}
+#endif
+
+static void
+dump_hexl_mode_trailer(size_t n_bytes, const char *block)
+{
+	fputs("  >", stdout);
+	while (n_bytes--) {
+		unsigned c = *(unsigned char *) block++;
+		c = (ISPRINT(c) ? c : '.');
+		putchar(c);
+	}
+	putchar('<');
+}
+
+/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
+   of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
+   CURR_BLOCK in the concatenation of input files, and it is printed
+   (optionally) only before the output line associated with the first
+   format spec.  When duplicate blocks are being abbreviated, the output
+   for a sequence of identical input blocks is the output for the first
+   block followed by an asterisk alone on a line.  It is valid to compare
+   the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
+   That condition may be false only for the last input block -- and then
+   only when it has not been padded to length BYTES_PER_BLOCK.  */
+
+static void
+write_block(off_t current_offset, size_t n_bytes,
+		const char *prev_block, const char *curr_block)
+{
+	static char first = 1;
+	static char prev_pair_equal = 0;
+	size_t i;
+
+	if (!(option_mask32 & OPT_v)
+	 && !first
+	 && n_bytes == bytes_per_block
+	 && memcmp(prev_block, curr_block, bytes_per_block) == 0
+	) {
+		if (prev_pair_equal) {
+			/* The two preceding blocks were equal, and the current
+			   block is the same as the last one, so print nothing.  */
+		} else {
+			puts("*");
+			prev_pair_equal = 1;
+		}
+	} else {
+		first = 0;
+		prev_pair_equal = 0;
+		for (i = 0; i < n_specs; i++) {
+			if (i == 0)
+				format_address(current_offset, '\0');
+			else
+				printf("%*s", address_pad_len_char - '0', "");
+			(*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 */
+				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);
+			}
+			putchar('\n');
+		}
+	}
+}
+
+static void
+read_block(size_t n, char *block, size_t *n_bytes_in_buffer)
+{
+	assert(0 < n && n <= bytes_per_block);
+
+	*n_bytes_in_buffer = 0;
+
+	if (n == 0)
+		return;
+
+	while (in_stream != NULL) { /* EOF.  */
+		size_t n_needed;
+		size_t n_read;
+
+		n_needed = n - *n_bytes_in_buffer;
+		n_read = fread(block + *n_bytes_in_buffer, 1, n_needed, in_stream);
+		*n_bytes_in_buffer += n_read;
+		if (n_read == n_needed)
+			break;
+		/* error check is done in check_and_close */
+		check_and_close();
+		open_next_file();
+	}
+}
+
+/* Return the least common multiple of the sizes associated
+   with the format specs.  */
+
+static int
+get_lcm(void)
+{
+	size_t i;
+	int l_c_m = 1;
+
+	for (i = 0; i < n_specs; i++)
+		l_c_m = lcm(l_c_m, width_bytes[(int) spec[i].size]);
+	return l_c_m;
+}
+
+/* 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
+   processed.  If the last block read is smaller than BYTES_PER_BLOCK
+   and its size is not a multiple of the size associated with a format
+   spec, extend the input block with zero bytes until its length is a
+   multiple of all format spec sizes.  Write the final block.  Finally,
+   write on a line by itself the offset of the byte after the last byte
+   read.  */
+
+static void
+dump(off_t current_offset, off_t end_offset)
+{
+	char *block[2];
+	int idx;
+	size_t n_bytes_read;
+
+	block[0] = xmalloc(2 * bytes_per_block);
+	block[1] = block[0] + bytes_per_block;
+
+	idx = 0;
+	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);
+			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 ^ 1], block[idx]);
+			current_offset += n_bytes_read;
+			idx ^= 1;
+		}
+	} else {
+		while (1) {
+			read_block(bytes_per_block, 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 ^ 1], block[idx]);
+			current_offset += n_bytes_read;
+			idx ^= 1;
+		}
+	}
+
+	if (n_bytes_read > 0) {
+		int l_c_m;
+		size_t bytes_to_write;
+
+		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.  */
+		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 ^ 1], block[idx]);
+		current_offset += n_bytes_read;
+	}
+
+	format_address(current_offset, '\n');
+
+	if ((option_mask32 & OPT_N) && current_offset >= end_offset)
+		check_and_close();
+
+	free(block[0]);
+}
+
+/* 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
+   stream associated with the input file INPUT_FILENAME.  If all N
+   bytes cannot be read from IN_STREAM, close IN_STREAM and update
+   the global variables IN_STREAM and INPUT_FILENAME.  Then try to
+   read the remaining bytes from the newly opened file.  Repeat if
+   necessary until EOF is reached for the last file in FILE_LIST.
+   On subsequent calls, don't modify BLOCK and return zero.  Set
+   *N_BYTES_IN_BUFFER to the number of bytes read.  If an error occurs,
+   it will be detected through ferror when the stream is about to be
+   closed.  If there is an error, give a message but continue reading
+   as usual and return nonzero.  Otherwise return zero.  */
+
+/* STRINGS mode.  Find each "string constant" in the input.
+   A string constant is a run of at least 'string_min' ASCII
+   graphic (or formatting) characters terminated by a null.
+   Based on a function written by Richard Stallman for a
+   traditional version of od.  */
+
+static void
+dump_strings(off_t address, off_t end_offset)
+{
+	unsigned bufsize = MAX(100, string_min);
+	unsigned char *buf = xmalloc(bufsize);
+
+	while (1) {
+		size_t i;
+		int c;
+
+		/* See if the next 'string_min' chars are all printing chars.  */
+ tryline:
+		if ((option_mask32 & OPT_N) && (end_offset - string_min <= address))
+			break;
+		i = 0;
+		while (!(option_mask32 & OPT_N) || address < end_offset) {
+			if (i == bufsize) {
+				bufsize += bufsize/8;
+				buf = xrealloc(buf, bufsize);
+			}
+
+			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;
+			if (!ISPRINT(c))
+				goto tryline;	/* It isn't; give up on this string.  */
+			buf[i++] = c;		/* String continues; store it all.  */
+		}
+
+		if (i < string_min)		/* Too short! */
+			goto tryline;
+
+		/* If we get here, the string is all printable and NUL-terminated */
+		buf[i] = 0;
+		format_address(address - i - 1, ' ');
+
+		for (i = 0; (c = buf[i]); i++) {
+			switch (c) {
+			case '\007': fputs("\\a", stdout); break;
+			case '\b': fputs("\\b", stdout); break;
+			case '\f': fputs("\\f", stdout); break;
+			case '\n': fputs("\\n", stdout); break;
+			case '\r': fputs("\\r", stdout); break;
+			case '\t': fputs("\\t", stdout); break;
+			case '\v': fputs("\\v", stdout); break;
+			default: putchar(c);
+			}
+		}
+		putchar('\n');
+	}
+
+	/* We reach this point only if we search through
+	   (max_bytes_to_format - string_min) bytes before reaching EOF.  */
+	check_and_close();
+ ret:
+	free(buf);
+}
+
+#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;
+	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)
+{
+	static const struct suffix_mult bkm[] = {
+		{ "b", 512 },
+		{ "k", 1024 },
+		{ "m", 1024*1024 },
+		{ "", 0 }
+	};
+#if ENABLE_LONG_OPTS
+	static const char od_longopts[] ALIGN1 =
+		"skip-bytes\0"        Required_argument "j"
+		"address-radix\0"     Required_argument "A"
+		"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
+	const char *str_A, *str_N, *str_j, *str_S = "3";
+	llist_t *lst_t = NULL;
+	unsigned opt;
+	int l_c_m;
+	/* 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.  */
+	off_t end_offset = 0;
+	/* The maximum number of bytes that will be formatted.  */
+	off_t max_bytes_to_format = 0;
+
+	spec = NULL;
+	format_address = format_address_std;
+	address_base_char = 'o';
+	address_pad_len_char = '7';
+
+	/* Parse command line */
+	opt_complementary = "w+:t::"; /* -w N, -t is a list */
+#if ENABLE_LONG_OPTS
+	applet_long_options = od_longopts;
+#endif
+	opt = OD_GETOPT32();
+	argv += optind;
+	if (opt & OPT_A) {
+		static const char doxn[] ALIGN1 = "doxn";
+		static const char doxn_address_base_char[] ALIGN1 = {
+			'u', 'o', 'x', /* '?' fourth one is not important */
+		};
+		static const uint8_t doxn_address_pad_len_char[] ALIGN1 = {
+			'7', '7', '6', /* '?' */
+		};
+		char *p;
+		int pos;
+		p = strchr(doxn, str_A[0]);
+		if (!p)
+			bb_error_msg_and_die("bad output address radix "
+				"'%c' (must be [doxn])", str_A[0]);
+		pos = p - doxn;
+		if (pos == 3) format_address = format_address_none;
+		address_base_char = doxn_address_base_char[pos];
+		address_pad_len_char = doxn_address_pad_len_char[pos];
+	}
+	if (opt & OPT_N) {
+		max_bytes_to_format = xstrtooff_sfx(str_N, 0, bkm);
+	}
+	if (opt & OPT_a) decode_format_string("a");
+	if (opt & OPT_b) decode_format_string("oC");
+	if (opt & OPT_c) decode_format_string("c");
+	if (opt & OPT_d) decode_format_string("u2");
+	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_l) decode_format_string("d4");
+	if (opt & OPT_o) decode_format_string("o2");
+	while (lst_t) {
+		decode_format_string(llist_pop(&lst_t));
+	}
+	if (opt & OPT_x) decode_format_string("x2");
+	if (opt & OPT_s) decode_format_string("d2");
+	if (opt & OPT_S) {
+		string_min = xstrtou_sfx(str_S, 0, bkm);
+	}
+
+	// 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]]]
+	 * The offset and pseudo_start have the same syntax.
+	 *
+	 * FIXME: POSIX 1003.1-2001 with XSI requires support for the
+	 * traditional syntax even if --traditional is not given.  */
+
+#if ENABLE_LONG_OPTS
+	if (opt & OPT_traditional) {
+		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");
+			}
+
+			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 (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 + SIZE is too large");
+	}
+
+	if (n_specs == 0) {
+		decode_format_string("o2");
+		/*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 (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_next_file();
+	/* Skip over any unwanted header bytes */
+	skip(n_bytes_to_skip);
+	if (!in_stream)
+		return EXIT_FAILURE;
+
+	/* Compute output block length */
+	l_c_m = get_lcm();
+
+	if (opt & OPT_w) { /* -w: width */
+		if (!bytes_per_block || bytes_per_block % l_c_m != 0) {
+			bb_error_msg("warning: invalid width %u; using %d instead",
+					(unsigned)bytes_per_block, l_c_m);
+			bytes_per_block = l_c_m;
+		}
+	} else {
+		bytes_per_block = l_c_m;
+		if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
+			bytes_per_block *= DEFAULT_BYTES_PER_BLOCK / l_c_m;
+	}
+
+#ifdef DEBUG
+	for (i = 0; i < n_specs; i++) {
+		printf("%d: fmt=\"%s\" width=%d\n",
+			i, spec[i].fmt_string, width_bytes[spec[i].size]);
+	}
+#endif
+
+	if (option_mask32 & OPT_S)
+		dump_strings(n_bytes_to_skip, end_offset);
+	else
+		dump(n_bytes_to_skip, end_offset);
+
+	if (fclose(stdin))
+		bb_perror_msg_and_die(bb_msg_standard_input);
+
+	return exit_code;
+}
diff --git a/busybox-1.19.3/coreutils/printenv.c b/busybox-1.19.3/coreutils/printenv.c
new file mode 100644
index 0000000..bd5db70
--- /dev/null
+++ b/busybox-1.19.3/coreutils/printenv.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * printenv implementation for busybox
+ *
+ * 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 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)
+{
+	int exit_code = EXIT_SUCCESS;
+
+	/* no variables specified, show whole env */
+	if (!argv[1]) {
+		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;
+
+		while ((arg = *++argv) != NULL) {
+			env = getenv(arg);
+			if (env)
+				puts(env);
+			else
+				exit_code = EXIT_FAILURE;
+		}
+	}
+
+	fflush_stdout_and_exit(exit_code);
+}
diff --git a/busybox-1.19.3/coreutils/printf.c b/busybox-1.19.3/coreutils/printf.c
new file mode 100644
index 0000000..f53aa47
--- /dev/null
+++ b/busybox-1.19.3/coreutils/printf.c
@@ -0,0 +1,414 @@
+/* vi: set sw=4 ts=4: */
+/* printf - format and print data
+
+   Copyright 1999 Dave Cinege
+   Portions copyright (C) 1990-1996 Free Software Foundation, Inc.
+
+   Licensed under GPLv2 or later, see file LICENSE in this source tree.
+*/
+
+/* Usage: printf format [argument...]
+
+   A front end to the printf function that lets it be used from the shell.
+
+   Backslash escapes:
+
+   \" = double quote
+   \\ = backslash
+   \a = alert (bell)
+   \b = backspace
+   \c = produce no further output
+   \f = form feed
+   \n = new line
+   \r = carriage return
+   \t = horizontal tab
+   \v = vertical tab
+   \0ooo = octal number (ooo is 0 to 3 digits)
+   \xhhh = hexadecimal number (hhh is 1 to 3 digits)
+
+   Additional directive:
+
+   %b = print an argument string, interpreting backslash escapes
+
+   The 'format' argument is re-used as many times as necessary
+   to convert all of the given arguments.
+
+   David MacKenzie <djm@gnu.ai.mit.edu>
+*/
+
+//   19990508 Busy Boxed! Dave Cinege
+
+//usage:#define printf_trivial_usage
+//usage:       "FORMAT [ARGUMENT]..."
+//usage:#define printf_full_usage "\n\n"
+//usage:       "Format and print ARGUMENT(s) according to FORMAT,\n"
+//usage:       "where FORMAT controls the output exactly as in C printf"
+//usage:
+//usage:#define printf_example_usage
+//usage:       "$ printf \"Val=%d\\n\" 5\n"
+//usage:       "Val=5\n"
+
+#include "libbb.h"
+
+/* A note on bad input: neither bash 3.2 nor coreutils 6.10 stop on it.
+ * They report it:
+ *  bash: printf: XXX: invalid number
+ *  printf: XXX: expected a numeric value
+ *  bash: printf: 123XXX: invalid number
+ *  printf: 123XXX: value not completely converted
+ * but then they use 0 (or partially converted numeric prefix) as a value
+ * and continue. They exit with 1 in this case.
+ * Both accept insane field width/precision (e.g. %9999999999.9999999999d).
+ * Both print error message and assume 0 if %*.*f width/precision is "bad"
+ *  (but negative numbers are not "bad").
+ * Both accept negative numbers for %u specifier.
+ *
+ * We try to be compatible.
+ */
+
+typedef void FAST_FUNC (*converter)(const char *arg, void *result);
+
+static int multiconvert(const char *arg, void *result, converter convert)
+{
+	if (*arg == '"' || *arg == '\'') {
+		arg = utoa((unsigned char)arg[1]);
+	}
+	errno = 0;
+	convert(arg, result);
+	if (errno) {
+		bb_error_msg("invalid number '%s'", arg);
+		return 1;
+	}
+	return 0;
+}
+
+static void FAST_FUNC conv_strtoull(const char *arg, void *result)
+{
+	*(unsigned long long*)result = bb_strtoull(arg, NULL, 0);
+	/* both coreutils 6.10 and bash 3.2:
+	 * $ printf '%x\n' -2
+	 * fffffffffffffffe
+	 * Mimic that:
+	 */
+	if (errno) {
+		*(unsigned long long*)result = bb_strtoll(arg, NULL, 0);
+	}
+}
+static void FAST_FUNC conv_strtoll(const char *arg, void *result)
+{
+	*(long long*)result = bb_strtoll(arg, NULL, 0);
+}
+static void FAST_FUNC conv_strtod(const char *arg, void *result)
+{
+	char *end;
+	/* Well, this one allows leading whitespace... so what? */
+	/* What I like much less is that "-" accepted too! :( */
+	*(double*)result = strtod(arg, &end);
+	if (end[0]) {
+		errno = ERANGE;
+		*(double*)result = 0;
+	}
+}
+
+/* Callers should check errno to detect errors */
+static unsigned long long my_xstrtoull(const char *arg)
+{
+	unsigned long long result;
+	if (multiconvert(arg, &result, conv_strtoull))
+		result = 0;
+	return result;
+}
+static long long my_xstrtoll(const char *arg)
+{
+	long long result;
+	if (multiconvert(arg, &result, conv_strtoll))
+		result = 0;
+	return result;
+}
+static double my_xstrtod(const char *arg)
+{
+	double result;
+	multiconvert(arg, &result, conv_strtod);
+	return result;
+}
+
+static void print_esc_string(const char *str)
+{
+	char c;
+	while ((c = *str) != '\0') {
+		str++;
+		if (c == '\\')
+			c = bb_process_escape_sequence(&str);
+		putchar(c);
+	}
+}
+
+static void print_direc(char *format, unsigned fmt_length,
+		int field_width, int precision,
+		const char *argument)
+{
+	long long llv;
+	double dv;
+	char saved;
+	char *have_prec, *have_width;
+
+	saved = format[fmt_length];
+	format[fmt_length] = '\0';
+
+	have_prec = strstr(format, ".*");
+	have_width = strchr(format, '*');
+	if (have_width - 1 == have_prec)
+		have_width = NULL;
+
+	errno = 0;
+
+	switch (format[fmt_length - 1]) {
+	case 'c':
+		printf(format, *argument);
+		break;
+	case 'd':
+	case 'i':
+		llv = my_xstrtoll(argument);
+ print_long:
+		if (!have_width) {
+			if (!have_prec)
+				printf(format, llv);
+			else
+				printf(format, precision, llv);
+		} else {
+			if (!have_prec)
+				printf(format, field_width, llv);
+			else
+				printf(format, field_width, precision, llv);
+		}
+		break;
+	case 'o':
+	case 'u':
+	case 'x':
+	case 'X':
+		llv = my_xstrtoull(argument);
+		/* cheat: unsigned long and long have same width, so... */
+		goto print_long;
+	case 's':
+		/* Are char* and long long the same? */
+		if (sizeof(argument) == sizeof(llv)) {
+			llv = (long long)(ptrdiff_t)argument;
+			goto print_long;
+		} else {
+			/* Hope compiler will optimize it out by moving call
+			 * instruction after the ifs... */
+			if (!have_width) {
+				if (!have_prec)
+					printf(format, argument, /*unused:*/ argument, argument);
+				else
+					printf(format, precision, argument, /*unused:*/ argument);
+			} else {
+				if (!have_prec)
+					printf(format, field_width, argument, /*unused:*/ argument);
+				else
+					printf(format, field_width, precision, argument);
+			}
+			break;
+		}
+	case 'f':
+	case 'e':
+	case 'E':
+	case 'g':
+	case 'G':
+		dv = my_xstrtod(argument);
+		if (!have_width) {
+			if (!have_prec)
+				printf(format, dv);
+			else
+				printf(format, precision, dv);
+		} else {
+			if (!have_prec)
+				printf(format, field_width, dv);
+			else
+				printf(format, field_width, precision, dv);
+		}
+		break;
+	} /* switch */
+
+	format[fmt_length] = saved;
+}
+
+/* Handle params for "%*.*f". Negative numbers are ok (compat). */
+static int get_width_prec(const char *str)
+{
+	int v = bb_strtoi(str, NULL, 10);
+	if (errno) {
+		bb_error_msg("invalid number '%s'", str);
+		v = 0;
+	}
+	return v;
+}
+
+/* Print the text in FORMAT, using ARGV for arguments to any '%' directives.
+   Return advanced ARGV.  */
+static char **print_formatted(char *f, char **argv, int *conv_err)
+{
+	char *direc_start;      /* Start of % directive.  */
+	unsigned direc_length;  /* Length of % directive.  */
+	int field_width;        /* Arg to first '*' */
+	int precision;          /* Arg to second '*' */
+	char **saved_argv = argv;
+
+	for (; *f; ++f) {
+		switch (*f) {
+		case '%':
+			direc_start = f++;
+			direc_length = 1;
+			field_width = precision = 0;
+			if (*f == '%') {
+				bb_putchar('%');
+				break;
+			}
+			if (*f == 'b') {
+				if (*argv) {
+					print_esc_string(*argv);
+					++argv;
+				}
+				break;
+			}
+			if (strchr("-+ #", *f)) {
+				++f;
+				++direc_length;
+			}
+			if (*f == '*') {
+				++f;
+				++direc_length;
+				if (*argv)
+					field_width = get_width_prec(*argv++);
+			} else {
+				while (isdigit(*f)) {
+					++f;
+					++direc_length;
+				}
+			}
+			if (*f == '.') {
+				++f;
+				++direc_length;
+				if (*f == '*') {
+					++f;
+					++direc_length;
+					if (*argv)
+						precision = get_width_prec(*argv++);
+				} else {
+					while (isdigit(*f)) {
+						++f;
+						++direc_length;
+					}
+				}
+			}
+
+			/* Remove "lLhz" size modifiers, repeatedly.
+			 * bash does not like "%lld", but coreutils
+			 * happily takes even "%Llllhhzhhzd"!
+			 * We are permissive like coreutils */
+			while ((*f | 0x20) == 'l' || *f == 'h' || *f == 'z') {
+				overlapping_strcpy(f, f + 1);
+			}
+			/* Add "ll" if integer modifier, then print */
+			{
+				static const char format_chars[] ALIGN1 = "diouxXfeEgGcs";
+				char *p = strchr(format_chars, *f);
+				/* needed - try "printf %" without it */
+				if (p == NULL) {
+					bb_error_msg("%s: invalid format", direc_start);
+					/* causes main() to exit with error */
+					return saved_argv - 1;
+				}
+				++direc_length;
+				if (p - format_chars <= 5) {
+					/* it is one of "diouxX" */
+					p = xmalloc(direc_length + 3);
+					memcpy(p, direc_start, direc_length);
+					p[direc_length + 1] = p[direc_length - 1];
+					p[direc_length - 1] = 'l';
+					p[direc_length] = 'l';
+					//bb_error_msg("<%s>", p);
+					direc_length += 2;
+					direc_start = p;
+				} else {
+					p = NULL;
+				}
+				if (*argv) {
+					print_direc(direc_start, direc_length, field_width,
+								precision, *argv++);
+				} else {
+					print_direc(direc_start, direc_length, field_width,
+								precision, "");
+				}
+				*conv_err |= errno;
+				free(p);
+			}
+			break;
+		case '\\':
+			if (*++f == 'c') {
+				return saved_argv; /* causes main() to exit */
+			}
+			bb_putchar(bb_process_escape_sequence((const char **)&f));
+			f--;
+			break;
+		default:
+			putchar(*f);
+		}
+	}
+
+	return argv;
+}
+
+int printf_main(int argc UNUSED_PARAM, char **argv)
+{
+	int conv_err;
+	char *format;
+	char **argv2;
+
+	/* We must check that stdout is not closed.
+	 * The reason for this is highly non-obvious.
+	 * printf_main is used from shell.
+	 * Shell must correctly handle 'printf "%s" 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;
+
+	/* bash builtin errors out on "printf '-%s-\n' foo",
+	 * coreutils-6.9 works. Both work with "printf -- '-%s-\n' foo".
+	 * We will mimic coreutils. */
+	if (argv[1] && argv[1][0] == '-' && argv[1][1] == '-' && !argv[1][2])
+		argv++;
+	if (!argv[1]) {
+		if (ENABLE_ASH_BUILTIN_PRINTF
+		 && applet_name[0] != 'p'
+		) {
+			bb_error_msg("usage: printf FORMAT [ARGUMENT...]");
+			return 2; /* bash compat */
+		}
+		bb_show_usage();
+	}
+
+	format = argv[1];
+	argv2 = argv + 2;
+
+	conv_err = 0;
+	do {
+		argv = argv2;
+		argv2 = print_formatted(format, argv, &conv_err);
+	} while (argv2 > argv && *argv2);
+
+	/* coreutils compat (bash doesn't do this):
+	if (*argv)
+		fprintf(stderr, "excess args ignored");
+	*/
+
+	return (argv2 < argv) /* if true, print_formatted errored out */
+		|| conv_err; /* print_formatted saw invalid number */
+}
diff --git a/busybox-1.19.3/coreutils/pwd.c b/busybox-1.19.3/coreutils/pwd.c
new file mode 100644
index 0000000..739b835
--- /dev/null
+++ b/busybox-1.19.3/coreutils/pwd.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini pwd implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ *
+ * 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! */
+
+int pwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pwd_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	char *buf;
+
+	buf = xrealloc_getcwd_or_warn(NULL);
+	if (buf != NULL) {
+		puts(buf);
+		free(buf);
+		return fflush_all();
+	}
+
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/coreutils/readlink.c b/busybox-1.19.3/coreutils/readlink.c
new file mode 100644
index 0000000..f7ad791
--- /dev/null
+++ b/busybox-1.19.3/coreutils/readlink.c
@@ -0,0 +1,82 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini readlink implementation for busybox
+ *
+ * Copyright (C) 2000,2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * 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"
+
+/*
+ * # readlink --version
+ * readlink (GNU coreutils) 6.10
+ * # readlink --help
+ *   -f, --canonicalize
+ *      canonicalize by following every symlink in
+ *      every component of the given name recursively;
+ *      all but the last component must exist
+ *   -e, --canonicalize-existing
+ *      canonicalize by following every symlink in
+ *      every component of the given name recursively,
+ *      all components must exist
+ *   -m, --canonicalize-missing
+ *      canonicalize by following every symlink in
+ *      every component of the given name recursively,
+ *      without requirements on components existence
+ *   -n, --no-newline              do not output the trailing newline
+ *   -q, --quiet, -s, --silent     suppress most error messages
+ *   -v, --verbose                 report error messages
+ *
+ * bbox supports: -f -n -v (fully), -q -s (accepts but ignores)
+ */
+
+int readlink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int readlink_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *buf;
+	char *fname;
+
+	IF_FEATURE_READLINK_FOLLOW(
+		unsigned opt;
+		/* We need exactly one non-option argument.  */
+		opt_complementary = "=1";
+		opt = getopt32(argv, "fnvsq");
+		fname = argv[optind];
+	)
+	IF_NOT_FEATURE_READLINK_FOLLOW(
+		const unsigned opt = 0;
+		if (argc != 2) bb_show_usage();
+		fname = argv[1];
+	)
+
+	/* compat: coreutils readlink reports errors silently via exit code */
+	if (!(opt & 4)) /* not -v */
+		logmode = LOGMODE_NONE;
+
+	if (opt & 1) { /* -f */
+		buf = xmalloc_realpath(fname);
+	} else {
+		buf = xmalloc_readlink_or_warn(fname);
+	}
+
+	if (!buf)
+		return EXIT_FAILURE;
+	printf((opt & 2) ? "%s" : "%s\n", buf);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(buf);
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/coreutils/realpath.c b/busybox-1.19.3/coreutils/realpath.c
new file mode 100644
index 0000000..c513b55
--- /dev/null
+++ b/busybox-1.19.3/coreutils/realpath.c
@@ -0,0 +1,41 @@
+/* vi: set sw=4 ts=4: */
+
+/* BB_AUDIT SUSv3 N/A -- Apparently a busybox extension. */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * 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 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;
+int realpath_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval = EXIT_SUCCESS;
+
+	if (!*++argv) {
+		bb_show_usage();
+	}
+
+	do {
+		char *resolved_path = xmalloc_realpath(*argv);
+		if (resolved_path != NULL) {
+			puts(resolved_path);
+			free(resolved_path);
+		} else {
+			retval = EXIT_FAILURE;
+			bb_simple_perror_msg(*argv);
+		}
+	} while (*++argv);
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/busybox-1.19.3/coreutils/rm.c b/busybox-1.19.3/coreutils/rm.c
new file mode 100644
index 0000000..042fba1
--- /dev/null
+++ b/busybox-1.19.3/coreutils/rm.c
@@ -0,0 +1,67 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rm implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/rm.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * 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! */
+
+int rm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rm_main(int argc UNUSED_PARAM, char **argv)
+{
+	int status = 0;
+	int flags = 0;
+	unsigned opt;
+
+	opt_complementary = "f-i:i-f";
+	/* -v (verbose) is ignored */
+	opt = getopt32(argv, "fiRrv");
+	argv += optind;
+	if (opt & 1)
+		flags |= FILEUTILS_FORCE;
+	if (opt & 2)
+		flags |= FILEUTILS_INTERACTIVE;
+	if (opt & (8|4))
+		flags |= FILEUTILS_RECUR;
+
+	if (*argv != NULL) {
+		do {
+			const char *base = bb_get_last_path_component_strip(*argv);
+
+			if (DOT_OR_DOTDOT(base)) {
+				bb_error_msg("can't remove '.' or '..'");
+			} else if (remove_file(*argv, flags) >= 0) {
+				continue;
+			}
+			status = 1;
+		} while (*++argv);
+	} else if (!(flags & FILEUTILS_FORCE)) {
+		bb_show_usage();
+	}
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/rmdir.c b/busybox-1.19.3/coreutils/rmdir.c
new file mode 100644
index 0000000..2840d1c
--- /dev/null
+++ b/busybox-1.19.3/coreutils/rmdir.c
@@ -0,0 +1,85 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rmdir implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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
+
+int rmdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rmdir_main(int argc UNUSED_PARAM, char **argv)
+{
+	int status = EXIT_SUCCESS;
+	int flags;
+	char *path;
+
+#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS
+	static const char rmdir_longopts[] ALIGN1 =
+		"parents\0"                  No_argument "p"
+		/* 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");
+	argv += optind;
+
+	if (!*argv) {
+		bb_show_usage();
+	}
+
+	do {
+		path = *argv;
+
+		while (1) {
+			if (rmdir(path) < 0) {
+#if ENABLE_FEATURE_RMDIR_LONG_OPTIONS
+				if ((flags & IGNORE_NON_EMPTY) && errno == ENOTEMPTY)
+					break;
+#endif
+				bb_perror_msg("'%s'", path);  /* Match gnu rmdir msg. */
+				status = EXIT_FAILURE;
+			} else if (flags & PARENTS) {
+				/* Note: path was not "" since rmdir succeeded. */
+				path = dirname(path);
+				/* Path is now just the parent component.  Dirname
+				 * returns "." if there are no parents.
+				 */
+				if (NOT_LONE_CHAR(path, '.')) {
+					continue;
+				}
+			}
+			break;
+		}
+	} while (*++argv);
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/seq.c b/busybox-1.19.3/coreutils/seq.c
new file mode 100644
index 0000000..8986192
--- /dev/null
+++ b/busybox-1.19.3/coreutils/seq.c
@@ -0,0 +1,109 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * seq implementation for busybox
+ *
+ * Copyright (C) 2004, Glenn McGrath
+ *
+ * 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! */
+
+int seq_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int seq_main(int argc, char **argv)
+{
+	enum {
+		OPT_w = (1 << 0),
+		OPT_s = (1 << 1),
+	};
+	double first, last, increment, v;
+	unsigned n;
+	unsigned width;
+	unsigned frac_part;
+	const char *sep, *opt_s = "\n";
+	unsigned opt;
+
+#if ENABLE_LOCALE_SUPPORT
+	/* Undo busybox.c: on input, we want to use dot
+	 * as fractional separator, regardless of current locale */
+	setlocale(LC_NUMERIC, "C");
+#endif
+
+	opt = getopt32(argv, "+ws:", &opt_s);
+	argc -= optind;
+	argv += optind;
+	first = increment = 1;
+	errno = 0;
+	switch (argc) {
+			char *pp;
+		case 3:
+			increment = strtod(argv[1], &pp);
+			errno |= *pp;
+		case 2:
+			first = strtod(argv[0], &pp);
+			errno |= *pp;
+		case 1:
+			last = strtod(argv[argc-1], &pp);
+			if (!errno && *pp == '\0')
+				break;
+		default:
+			bb_show_usage();
+	}
+
+#if ENABLE_LOCALE_SUPPORT
+	setlocale(LC_NUMERIC, "");
+#endif
+
+	/* Last checked to be compatible with: coreutils-6.10 */
+	width = 0;
+	frac_part = 0;
+	while (1) {
+		char *dot = strchrnul(*argv, '.');
+		int w = (dot - *argv);
+		int f = strlen(dot);
+		if (width < w)
+			width = w;
+		argv++;
+		if (!*argv)
+			break;
+		/* Why do the above _before_ frac check below?
+		 * Try "seq 1 2.0" and "seq 1.0 2.0":
+		 * coreutils never pay attention to the number
+		 * of fractional digits in last arg. */
+		if (frac_part < f)
+			frac_part = f;
+	}
+	if (frac_part) {
+		frac_part--;
+		if (frac_part)
+			width += frac_part + 1;
+	}
+	if (!(opt & OPT_w))
+		width = 0;
+
+	sep = "";
+	v = first;
+	n = 0;
+	while (increment >= 0 ? v <= last : v >= last) {
+		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++;
+		v = first + n * increment;
+	}
+	if (n) /* if while loop executed at least once */
+		bb_putchar('\n');
+
+	return fflush_all();
+}
diff --git a/busybox-1.19.3/coreutils/sleep.c b/busybox-1.19.3/coreutils/sleep.c
new file mode 100644
index 0000000..0ffbd16
--- /dev/null
+++ b/busybox-1.19.3/coreutils/sleep.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sleep implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/sleep.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Rewritten to do proper arg and error checking.
+ * Also, added a 'fancy' configuration to accept multiple args with
+ * 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"
+
+/* Do not make this applet NOFORK. It breaks ^C-ing of pauses in shells */
+
+
+#if ENABLE_FEATURE_FANCY_SLEEP || ENABLE_FEATURE_FLOAT_SLEEP
+static const struct suffix_mult sfx[] = {
+	{ "s", 1 },
+	{ "m", 60 },
+	{ "h", 60*60 },
+	{ "d", 24*60*60 },
+	{ "", 0 }
+};
+#endif
+
+int sleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sleep_main(int argc UNUSED_PARAM, char **argv)
+{
+#if ENABLE_FEATURE_FLOAT_SLEEP
+	double duration;
+	struct timespec ts;
+#else
+	unsigned duration;
+#endif
+
+	++argv;
+	if (!*argv)
+		bb_show_usage();
+
+#if ENABLE_FEATURE_FLOAT_SLEEP
+
+# if ENABLE_LOCALE_SUPPORT
+	/* undo busybox.c setlocale */
+	setlocale(LC_NUMERIC, "C");
+# endif
+	duration = 0;
+	do {
+		char *arg = *argv;
+		if (strchr(arg, '.')) {
+			double d;
+			char *pp;
+			int len = strspn(arg, "0123456789.");
+			char sv = arg[len];
+			arg[len] = '\0';
+			errno = 0;
+			d = strtod(arg, &pp);
+			if (errno || *pp)
+				bb_show_usage();
+			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));
+	ts.tv_nsec = 0;
+	if (duration >= 0 && duration < ts.tv_sec) {
+		ts.tv_sec = duration;
+		ts.tv_nsec = (duration - ts.tv_sec) * 1000000000;
+	}
+	do {
+		errno = 0;
+		nanosleep(&ts, &ts);
+	} while (errno == EINTR);
+
+#elif ENABLE_FEATURE_FANCY_SLEEP
+
+	duration = 0;
+	do {
+		duration += xatou_range_sfx(*argv, 0, UINT_MAX - duration, sfx);
+	} while (*++argv);
+	sleep(duration);
+
+#else /* simple */
+
+	duration = xatou(*argv);
+	sleep(duration);
+	// Off. If it's really needed, provide example why
+	//if (sleep(duration)) {
+	//	bb_perror_nomsg_and_die();
+	//}
+
+#endif
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/sort.c b/busybox-1.19.3/coreutils/sort.c
new file mode 100644
index 0000000..1df0728
--- /dev/null
+++ b/busybox-1.19.3/coreutils/sort.c
@@ -0,0 +1,469 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * SuS3 compliant sort implementation for busybox
+ *
+ * Copyright (C) 2004 by Rob Landley <rob@landley.net>
+ *
+ * MAINTAINER: Rob Landley <rob@landley.net>
+ *
+ * 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! */
+
+
+/*
+	sort [-m][-o output][-bdfinru][-t char][-k keydef]... [file...]
+	sort -c [-bdfinru][-t char][-k keydef][file]
+*/
+
+/* These are sort types */
+static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:t:";
+enum {
+	FLAG_n  = 1,            /* Numeric sort */
+	FLAG_g  = 2,            /* Sort using strtod() */
+	FLAG_M  = 4,            /* Sort date */
+/* ucsz apply to root level only, not keys.  b at root level implies bb */
+	FLAG_u  = 8,            /* Unique */
+	FLAG_c  = 0x10,         /* Check: no output, exit(!ordered) */
+	FLAG_s  = 0x20,         /* Stable sort, no ascii fallback at end */
+	FLAG_z  = 0x40,         /* Input and output is NUL terminated, not \n */
+/* These can be applied to search keys, the previous four can't */
+	FLAG_b  = 0x80,         /* Ignore leading blanks */
+	FLAG_r  = 0x100,        /* Reverse */
+	FLAG_d  = 0x200,        /* Ignore !(isalnum()|isspace()) */
+	FLAG_f  = 0x400,        /* Force uppercase */
+	FLAG_i  = 0x800,        /* Ignore !isprint() */
+	FLAG_m  = 0x1000,       /* ignored: merge already sorted files; do not sort */
+	FLAG_S  = 0x2000,       /* ignored: -S, --buffer-size=SIZE */
+	FLAG_T  = 0x4000,       /* ignored: -T, --temporary-directory=DIR */
+	FLAG_o  = 0x8000,
+	FLAG_k  = 0x10000,
+	FLAG_t  = 0x20000,
+	FLAG_bb = 0x80000000,   /* Ignore trailing blanks  */
+};
+
+#if ENABLE_FEATURE_SORT_BIG
+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 */
+	unsigned flags;
+} *key_list;
+
+static char *get_key(char *str, struct sort_key *key, int flags)
+{
+	int start = 0, end = 0, len, j;
+	unsigned i;
+
+	/* Special case whole string, so we don't have to make a copy */
+	if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3]
+	 && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb))
+	) {
+		return str;
+	}
+
+	/* Find start of key on first pass, end on second pass */
+	len = strlen(str);
+	for (j = 0; j < 2; j++) {
+		if (!key->range[2*j])
+			end = len;
+		/* Loop through fields */
+		else {
+			end = 0;
+			for (i = 1; i < key->range[2*j] + j; i++) {
+				if (key_separator) {
+					/* Skip body of key and separator */
+					while (str[end]) {
+						if (str[end++] == key_separator)
+							break;
+					}
+				} else {
+					/* Skip leading blanks */
+					while (isspace(str[end]))
+						end++;
+					/* Skip body of key */
+					while (str[end]) {
+						if (isspace(str[end]))
+							break;
+						end++;
+					}
+				}
+			}
+		}
+		if (!j) start = end;
+	}
+	/* Strip leading whitespace if necessary */
+//XXX: skip_whitespace()
+	if (flags & FLAG_b)
+		while (isspace(str[start])) start++;
+	/* Strip trailing whitespace if necessary */
+	if (flags & FLAG_bb)
+		while (end > start && isspace(str[end-1])) end--;
+	/* Handle offsets on start and end */
+	if (key->range[3]) {
+		end += key->range[3] - 1;
+		if (end > len) end = len;
+	}
+	if (key->range[1]) {
+		start += key->range[1] - 1;
+		if (start > len) start = len;
+	}
+	/* Make the copy */
+	if (end < start) end = start;
+	str = xstrndup(str+start, end-start);
+	/* Handle -d */
+	if (flags & FLAG_d) {
+		for (start = end = 0; str[end]; end++)
+			if (isspace(str[end]) || isalnum(str[end]))
+				str[start++] = str[end];
+		str[start] = '\0';
+	}
+	/* Handle -i */
+	if (flags & FLAG_i) {
+		for (start = end = 0; str[end]; end++)
+			if (isprint_asciionly(str[end]))
+				str[start++] = str[end];
+		str[start] = '\0';
+	}
+	/* Handle -f */
+	if (flags & FLAG_f)
+		for (i = 0; str[i]; i++)
+			str[i] = toupper(str[i]);
+
+	return str;
+}
+
+static struct sort_key *add_key(void)
+{
+	struct sort_key **pkey = &key_list;
+	while (*pkey)
+		pkey = &((*pkey)->next_key);
+	return *pkey = xzalloc(sizeof(struct sort_key));
+}
+
+#define GET_LINE(fp) \
+	((option_mask32 & FLAG_z) \
+	? bb_get_chunk_from_file(fp, NULL) \
+	: xmalloc_fgetline(fp))
+#else
+#define GET_LINE(fp) xmalloc_fgetline(fp)
+#endif
+
+/* Iterate through keys list and perform comparisons */
+static int compare_keys(const void *xarg, const void *yarg)
+{
+	int flags = option_mask32, retval = 0;
+	char *x, *y;
+
+#if ENABLE_FEATURE_SORT_BIG
+	struct sort_key *key;
+
+	for (key = key_list; !retval && key; key = key->next_key) {
+		flags = key->flags ? key->flags : option_mask32;
+		/* Chop out and modify key chunks, handling -dfib */
+		x = get_key(*(char **)xarg, key, flags);
+		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 */
+	{
+		x = *(char **)xarg;
+		y = *(char **)yarg;
+#endif
+		/* Perform actual comparison */
+		switch (flags & 7) {
+		default:
+			bb_error_msg_and_die("unknown sort type");
+			break;
+		/* Ascii sort */
+		case 0:
+#if ENABLE_LOCALE_SUPPORT
+			retval = strcoll(x, y);
+#else
+			retval = strcmp(x, y);
+#endif
+			break;
+#if ENABLE_FEATURE_SORT_BIG
+		case FLAG_g: {
+			char *xx, *yy;
+			double dx = strtod(x, &xx);
+			double dy = strtod(y, &yy);
+			/* not numbers < NaN < -infinity < numbers < +infinity) */
+			if (x == xx)
+				retval = (y == yy ? 0 : -1);
+			else if (y == yy)
+				retval = 1;
+			/* Check for isnan */
+			else if (dx != dx)
+				retval = (dy != dy) ? 0 : -1;
+			else if (dy != dy)
+				retval = 1;
+			/* Check for infinity.  Could underflow, but it avoids libm. */
+			else if (1.0 / dx == 0.0) {
+				if (dx < 0)
+					retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1;
+				else
+					retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1;
+			} else if (1.0 / dy == 0.0)
+				retval = (dy < 0) ? 1 : -1;
+			else
+				retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0);
+			break;
+		}
+		case FLAG_M: {
+			struct tm thyme;
+			int dx;
+			char *xx, *yy;
+
+			xx = strptime(x, "%b", &thyme);
+			dx = thyme.tm_mon;
+			yy = strptime(y, "%b", &thyme);
+			if (!xx)
+				retval = (!yy) ? 0 : -1;
+			else if (!yy)
+				retval = 1;
+			else
+				retval = (dx == thyme.tm_mon) ? 0 : dx - thyme.tm_mon;
+			break;
+		}
+		/* Full floating point version of -n */
+		case FLAG_n: {
+			double dx = atof(x);
+			double dy = atof(y);
+			retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0);
+			break;
+		}
+		} /* switch */
+		/* Free key copies. */
+		if (x != *(char **)xarg) free(x);
+		if (y != *(char **)yarg) free(y);
+		/* if (retval) break; - done by for () anyway */
+#else
+		/* Integer version of -n for tiny systems */
+		case FLAG_n:
+			retval = atoi(x) - atoi(y);
+			break;
+		} /* switch */
+#endif
+	} /* for */
+
+	/* Perform fallback sort if necessary */
+	if (!retval && !(option_mask32 & FLAG_s))
+		retval = strcmp(*(char **)xarg, *(char **)yarg);
+
+	if (flags & FLAG_r) return -retval;
+	return retval;
+}
+
+#if ENABLE_FEATURE_SORT_BIG
+static unsigned str2u(char **str)
+{
+	unsigned long lu;
+	if (!isdigit((*str)[0]))
+		bb_error_msg_and_die("bad field specification");
+	lu = strtoul(*str, str, 10);
+	if ((sizeof(long) > sizeof(int) && lu > INT_MAX) || !lu)
+		bb_error_msg_and_die("bad field specification");
+	return lu;
+}
+#endif
+
+int sort_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sort_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *line, **lines;
+	char *str_ignored, *str_o, *str_t;
+	llist_t *lst_k = NULL;
+	int i, flag;
+	int linecount;
+	unsigned opts;
+
+	xfunc_error_retval = 2;
+
+	/* Parse command line options */
+	/* -o and -t can be given at most once */
+	opt_complementary = "o--o:t--t:" /* -t, -o: at most one of each */
+			"k::"; /* -k takes list */
+	opts = getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t);
+	/* global b strips leading and trailing spaces */
+	if (opts & FLAG_b)
+		option_mask32 |= FLAG_bb;
+#if ENABLE_FEATURE_SORT_BIG
+	if (opts & FLAG_t) {
+		if (!str_t[0] || str_t[1])
+			bb_error_msg_and_die("bad -t parameter");
+		key_separator = str_t[0];
+	}
+	/* note: below this point we use option_mask32, not opts,
+	 * since that reduces register pressure and makes code smaller */
+
+	/* parse sort key */
+	while (lst_k) {
+		enum {
+			FLAG_allowed_for_k =
+				FLAG_n | /* Numeric sort */
+				FLAG_g | /* Sort using strtod() */
+				FLAG_M | /* Sort date */
+				FLAG_b | /* Ignore leading blanks */
+				FLAG_r | /* Reverse */
+				FLAG_d | /* Ignore !(isalnum()|isspace()) */
+				FLAG_f | /* Force uppercase */
+				FLAG_i | /* Ignore !isprint() */
+			0
+		};
+		struct sort_key *key = add_key();
+		char *str_k = llist_pop(&lst_k);
+
+		i = 0; /* i==0 before comma, 1 after (-k3,6) */
+		while (*str_k) {
+			/* Start of range */
+			/* Cannot use bb_strtou - suffix can be a letter */
+			key->range[2*i] = str2u(&str_k);
+			if (*str_k == '.') {
+				str_k++;
+				key->range[2*i+1] = str2u(&str_k);
+			}
+			while (*str_k) {
+				const char *temp2;
+
+				if (*str_k == ',' && !i++) {
+					str_k++;
+					break;
+				} /* no else needed: fall through to syntax error
+					because comma isn't in OPT_STR */
+				temp2 = strchr(OPT_STR, *str_k);
+				if (!temp2)
+					bb_error_msg_and_die("unknown key option");
+				flag = 1 << (temp2 - OPT_STR);
+				if (flag & ~FLAG_allowed_for_k)
+					bb_error_msg_and_die("unknown sort type");
+				/* b after ',' means strip _trailing_ space */
+				if (i && flag == FLAG_b)
+					flag = FLAG_bb;
+				key->flags |= flag;
+				str_k++;
+			}
+		}
+	}
+#endif
+
+	/* Open input files and read data */
+	argv += optind;
+	if (!*argv)
+		*--argv = (char*)"-";
+	linecount = 0;
+	lines = NULL;
+	do {
+		/* coreutils 6.9 compat: abort on first open error,
+		 * do not continue to next file: */
+		FILE *fp = xfopen_stdin(*argv);
+		for (;;) {
+			line = GET_LINE(fp);
+			if (!line)
+				break;
+			lines = xrealloc_vector(lines, 6, linecount);
+			lines[linecount++] = line;
+		}
+		fclose_if_not_stdin(fp);
+	} while (*++argv);
+
+#if ENABLE_FEATURE_SORT_BIG
+	/* if no key, perform alphabetic sort */
+	if (!key_list)
+		add_key()->range[0] = 1;
+	/* handle -c */
+	if (option_mask32 & FLAG_c) {
+		int j = (option_mask32 & FLAG_u) ? -1 : 0;
+		for (i = 1; i < linecount; i++) {
+			if (compare_keys(&lines[i-1], &lines[i]) > j) {
+				fprintf(stderr, "Check line %u\n", i);
+				return EXIT_FAILURE;
+			}
+		}
+		return EXIT_SUCCESS;
+	}
+#endif
+	/* Perform the actual sort */
+	qsort(lines, linecount, sizeof(lines[0]), compare_keys);
+	/* handle -u */
+	if (option_mask32 & FLAG_u) {
+		flag = 0;
+		/* coreutils 6.3 drop lines for which only key is the same */
+		/* -- disabling last-resort compare... */
+		option_mask32 |= FLAG_s;
+		for (i = 1; i < linecount; i++) {
+			if (compare_keys(&lines[flag], &lines[i]) == 0)
+				free(lines[i]);
+			else
+				lines[++flag] = lines[i];
+		}
+		if (linecount)
+			linecount = flag+1;
+	}
+
+	/* Print it */
+#if ENABLE_FEATURE_SORT_BIG
+	/* Open output file _after_ we read all input ones */
+	if (option_mask32 & FLAG_o)
+		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++)
+		printf("%s%c", lines[i], flag);
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/coreutils/split.c b/busybox-1.19.3/coreutils/split.c
new file mode 100644
index 0000000..11e6404
--- /dev/null
+++ b/busybox-1.19.3/coreutils/split.c
@@ -0,0 +1,153 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * split - split a file into pieces
+ * Copyright (c) 2007 Bernhard Reutner-Fischer
+ *
+ * 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
+	{ "b", 512 },
+#endif
+	{ "k", 1024 },
+	{ "m", 1024*1024 },
+#if ENABLE_FEATURE_SPLIT_FANCY
+	{ "g", 1024*1024*1024 },
+#endif
+	{ "", 0 }
+};
+
+/* Increment the suffix part of the filename.
+ * Returns NULL if we are out of filenames.
+ */
+static char *next_file(char *old, unsigned suffix_len)
+{
+	size_t end = strlen(old);
+	unsigned i = 1;
+	char *curr;
+
+	while (1) {
+		curr = old + end - i;
+		if (*curr < 'z') {
+			*curr += 1;
+			break;
+		}
+		i++;
+		if (i > suffix_len) {
+			return NULL;
+		}
+		*curr = 'a';
+	}
+
+	return old;
+}
+
+#define read_buffer bb_common_bufsiz1
+enum { READ_BUFFER_SIZE = COMMON_BUFSIZE - 1 };
+
+#define SPLIT_OPT_l (1<<0)
+#define SPLIT_OPT_b (1<<1)
+#define SPLIT_OPT_a (1<<2)
+
+int split_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int split_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned suffix_len = 2;
+	char *pfx;
+	char *count_p;
+	const char *sfx;
+	off_t cnt = 1000;
+	off_t remaining = 0;
+	unsigned opt;
+	ssize_t bytes_read, to_write;
+	char *src;
+
+	opt_complementary = "?2:a+"; /* max 2 args; -a N */
+	opt = getopt32(argv, "l:b:a:", &count_p, &count_p, &suffix_len);
+
+	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);
+	sfx = "x";
+
+	argv += optind;
+	if (argv[0]) {
+		int fd;
+		if (argv[1])
+			sfx = argv[1];
+		fd = xopen_stdin(argv[0]);
+		xmove_fd(fd, STDIN_FILENO);
+	} else {
+		argv[0] = (char *) bb_msg_standard_input;
+	}
+
+	if (NAME_MAX < strlen(sfx) + suffix_len)
+		bb_error_msg_and_die("suffix too long");
+
+	{
+		char *char_p = xzalloc(suffix_len + 1);
+		memset(char_p, 'a', suffix_len);
+		pfx = xasprintf("%s%s", sfx, char_p);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(char_p);
+	}
+
+	while (1) {
+		bytes_read = safe_read(STDIN_FILENO, read_buffer, READ_BUFFER_SIZE);
+		if (!bytes_read)
+			break;
+		if (bytes_read < 0)
+			bb_simple_perror_msg_and_die(argv[0]);
+		src = read_buffer;
+		do {
+			if (!remaining) {
+				if (!pfx)
+					bb_error_msg_and_die("suffixes exhausted");
+				xmove_fd(xopen(pfx, O_WRONLY | O_CREAT | O_TRUNC), 1);
+				pfx = next_file(pfx, suffix_len);
+				remaining = cnt;
+			}
+
+			if (opt & SPLIT_OPT_b) {
+				/* split by bytes */
+				to_write = (bytes_read < remaining) ? bytes_read : remaining;
+				remaining -= to_write;
+			} else {
+				/* split by lines */
+				/* can be sped up by using _memrchr_
+				 * and writing many lines at once... */
+				char *end = memchr(src, '\n', bytes_read);
+				if (end) {
+					--remaining;
+					to_write = end - src + 1;
+				} else {
+					to_write = bytes_read;
+				}
+			}
+
+			xwrite(STDOUT_FILENO, src, to_write);
+			bytes_read -= to_write;
+			src += to_write;
+		} while (bytes_read);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/stat.c b/busybox-1.19.3/coreutils/stat.c
new file mode 100644
index 0000000..2797719
--- /dev/null
+++ b/busybox-1.19.3/coreutils/stat.c
@@ -0,0 +1,728 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stat -- display file or file system status
+ *
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation.
+ * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
+ * Copyright (C) 2006 by Yoshinori Sato <ysato@users.sourceforge.jp>
+ *
+ * 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 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)
+#define OPT_TERSE       (1 << 1)
+#define OPT_DEREFERENCE (1 << 2)
+#define OPT_SELINUX     (1 << 3)
+
+#if ENABLE_FEATURE_STAT_FORMAT
+typedef bool (*statfunc_ptr)(const char *, const char *);
+#else
+typedef bool (*statfunc_ptr)(const char *);
+#endif
+
+static const char *file_type(const struct stat *st)
+{
+	/* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107
+	 * for some of these formats.
+	 * To keep diagnostics grammatical in English, the
+	 * returned string must start with a consonant.
+	 */
+	if (S_ISREG(st->st_mode))  return st->st_size == 0 ? "regular empty file" : "regular file";
+	if (S_ISDIR(st->st_mode))  return "directory";
+	if (S_ISBLK(st->st_mode))  return "block special file";
+	if (S_ISCHR(st->st_mode))  return "character special file";
+	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";
+	if (S_TYPEISMQ(st))        return "message queue";
+	if (S_TYPEISSEM(st))       return "semaphore";
+	if (S_TYPEISSHM(st))       return "shared memory object";
+#ifdef S_TYPEISTMO
+	if (S_TYPEISTMO(st))       return "typed memory object";
+#endif
+	return "weird file";
+}
+
+static const char *human_time(time_t t)
+{
+	/* Old
+	static char *str;
+	str = ctime(&t);
+	str[strlen(str)-1] = '\0';
+	return str;
+	*/
+	/* coreutils 6.3 compat: */
+
+	/*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));
+	return buf;
+#undef buf
+}
+
+/* Return the type of the specified file system.
+ * Some systems have statfvs.f_basetype[FSTYPSZ]. (AIX, HP-UX, and Solaris)
+ * Others have statfs.f_fstypename[MFSNAMELEN]. (NetBSD 1.5.2)
+ * Still others have neither and have to get by with f_type (Linux).
+ */
+static const char *human_fstype(uint32_t f_type)
+{
+	static const struct types {
+		uint32_t type;
+		const char *const fs;
+	} humantypes[] = {
+		{ 0xADFF,     "affs" },
+		{ 0x1Cd1,     "devpts" },
+		{ 0x137D,     "ext" },
+		{ 0xEF51,     "ext2" },
+		{ 0xEF53,     "ext2/ext3" },
+		{ 0x3153464a, "jfs" },
+		{ 0x58465342, "xfs" },
+		{ 0xF995E849, "hpfs" },
+		{ 0x9660,     "isofs" },
+		{ 0x4000,     "isofs" },
+		{ 0x4004,     "isofs" },
+		{ 0x137F,     "minix" },
+		{ 0x138F,     "minix (30 char.)" },
+		{ 0x2468,     "minix v2" },
+		{ 0x2478,     "minix v2 (30 char.)" },
+		{ 0x4d44,     "msdos" },
+		{ 0x4006,     "fat" },
+		{ 0x564c,     "novell" },
+		{ 0x6969,     "nfs" },
+		{ 0x9fa0,     "proc" },
+		{ 0x517B,     "smb" },
+		{ 0x012FF7B4, "xenix" },
+		{ 0x012FF7B5, "sysv4" },
+		{ 0x012FF7B6, "sysv2" },
+		{ 0x012FF7B7, "coh" },
+		{ 0x00011954, "ufs" },
+		{ 0x012FD16D, "xia" },
+		{ 0x5346544e, "ntfs" },
+		{ 0x1021994,  "tmpfs" },
+		{ 0x52654973, "reiserfs" },
+		{ 0x28cd3d45, "cramfs" },
+		{ 0x7275,     "romfs" },
+		{ 0x858458f6, "romfs" },
+		{ 0x73717368, "squashfs" },
+		{ 0x62656572, "sysfs" },
+		{ 0, "UNKNOWN" }
+	};
+
+	int i;
+
+	for (i = 0; humantypes[i].type; ++i)
+		if (humantypes[i].type == f_type)
+			break;
+	return humantypes[i].fs;
+}
+
+/* "man statfs" says that statfsbuf->f_fsid is a mess */
+/* coreutils treats it as an array of ints, most significant first */
+static unsigned long long get_f_fsid(const struct statfs *statfsbuf)
+{
+	const unsigned *p = (const void*) &statfsbuf->f_fsid;
+	unsigned sz = sizeof(statfsbuf->f_fsid) / sizeof(unsigned);
+	unsigned long long r = 0;
+
+	do
+		r = (r << (sizeof(unsigned)*8)) | *p++;
+	while (--sz > 0);
+	return r;
+}
+
+#if ENABLE_FEATURE_STAT_FORMAT
+static void strcatc(char *str, char c)
+{
+	int len = strlen(str);
+	str[len++] = c;
+	str[len] = '\0';
+}
+
+static void printfs(char *pformat, const char *msg)
+{
+	strcatc(pformat, 's');
+	printf(pformat, msg);
+}
+
+/* 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))
+{
+	const struct statfs *statfsbuf = data;
+	if (m == 'n') {
+		printfs(pformat, filename);
+	} else if (m == 'i') {
+		strcat(pformat, "llx");
+		printf(pformat, get_f_fsid(statfsbuf));
+	} else if (m == 'l') {
+		strcat(pformat, "lu");
+		printf(pformat, (unsigned long) statfsbuf->f_namelen);
+	} else if (m == 't') {
+		strcat(pformat, "lx");
+		printf(pformat, (unsigned long) statfsbuf->f_type); /* no equiv */
+	} else if (m == 'T') {
+		printfs(pformat, human_fstype(statfsbuf->f_type));
+	} else if (m == 'b') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statfsbuf->f_blocks);
+	} else if (m == 'f') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statfsbuf->f_bfree);
+	} else if (m == 'a') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statfsbuf->f_bavail);
+	} else if (m == 's' || m == 'S') {
+		strcat(pformat, "lu");
+		printf(pformat, (unsigned long) statfsbuf->f_bsize);
+	} else if (m == 'c') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statfsbuf->f_files);
+	} else if (m == 'd') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statfsbuf->f_ffree);
+# if ENABLE_SELINUX
+	} else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
+		printfs(pformat, scontext);
+# endif
+	} else {
+		strcatc(pformat, 'c');
+		printf(pformat, m);
+	}
+}
+
+/* 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))
+{
+#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+	struct stat *statbuf = (struct stat *) data;
+	struct passwd *pw_ent;
+	struct group *gw_ent;
+
+	if (m == 'n') {
+		printfs(pformat, filename);
+	} else if (m == 'N') {
+		strcatc(pformat, 's');
+		if (S_ISLNK(statbuf->st_mode)) {
+			char *linkname = xmalloc_readlink_or_warn(filename);
+			if (linkname == NULL)
+				return;
+			printf("'%s' -> '%s'", filename, linkname);
+			free(linkname);
+		} else {
+			printf(pformat, filename);
+		}
+	} else if (m == 'd') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statbuf->st_dev);
+	} else if (m == 'D') {
+		strcat(pformat, "llx");
+		printf(pformat, (unsigned long long) statbuf->st_dev);
+	} else if (m == 'i') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statbuf->st_ino);
+	} else if (m == 'a') {
+		strcat(pformat, "lo");
+		printf(pformat, (unsigned long) (statbuf->st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)));
+	} else if (m == 'A') {
+		printfs(pformat, bb_mode_string(statbuf->st_mode));
+	} else if (m == 'f') {
+		strcat(pformat, "lx");
+		printf(pformat, (unsigned long) statbuf->st_mode);
+	} else if (m == 'F') {
+		printfs(pformat, file_type(statbuf));
+	} else if (m == 'h') {
+		strcat(pformat, "lu");
+		printf(pformat, (unsigned long) statbuf->st_nlink);
+	} else if (m == 'u') {
+		strcat(pformat, "lu");
+		printf(pformat, (unsigned long) statbuf->st_uid);
+	} else if (m == 'U') {
+		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') {
+		gw_ent = getgrgid(statbuf->st_gid);
+		printfs(pformat, (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
+	} else if (m == 't') {
+		strcat(pformat, "lx");
+		printf(pformat, (unsigned long) major(statbuf->st_rdev));
+	} else if (m == 'T') {
+		strcat(pformat, "lx");
+		printf(pformat, (unsigned long) minor(statbuf->st_rdev));
+	} else if (m == 's') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statbuf->st_size);
+	} else if (m == 'B') {
+		strcat(pformat, "lu");
+		printf(pformat, (unsigned long) 512); //ST_NBLOCKSIZE
+	} else if (m == 'b') {
+		strcat(pformat, "llu");
+		printf(pformat, (unsigned long long) statbuf->st_blocks);
+	} else if (m == 'o') {
+		strcat(pformat, "lu");
+		printf(pformat, (unsigned long) statbuf->st_blksize);
+	} else if (m == 'x') {
+		printfs(pformat, human_time(statbuf->st_atime));
+	} else if (m == 'X') {
+		strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
+		/* note: (unsigned long) would be wrong:
+		 * imagine (unsigned long64)int32 */
+		printf(pformat, (long) statbuf->st_atime);
+	} else if (m == 'y') {
+		printfs(pformat, human_time(statbuf->st_mtime));
+	} else if (m == 'Y') {
+		strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
+		printf(pformat, (long) statbuf->st_mtime);
+	} else if (m == 'z') {
+		printfs(pformat, human_time(statbuf->st_ctime));
+	} else if (m == 'Z') {
+		strcat(pformat, TYPE_SIGNED(time_t) ? "ld" : "lu");
+		printf(pformat, (long) statbuf->st_ctime);
+# if ENABLE_SELINUX
+	} else if (m == 'C' && (option_mask32 & OPT_SELINUX)) {
+		printfs(pformat, scontext);
+# endif
+	} else {
+		strcatc(pformat, 'c');
+		printf(pformat, m);
+	}
+}
+
+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)),
+		const void *data
+		IF_SELINUX(, security_context_t scontext))
+{
+	/* Create a working copy of the format string */
+	char *format = xstrdup(masterformat);
+	/* Add 2 to accomodate our conversion of the stat '%s' format string
+	 * to the printf '%llu' one.  */
+	char *dest = xmalloc(strlen(format) + 2 + 1);
+	char *b;
+
+	b = format;
+	while (b) {
+		/* Each iteration finds next %spec,
+		 * prints preceding string and handles found %spec
+		 */
+		size_t len;
+		char *p = strchr(b, '%');
+		if (!p) {
+			/* coreutils 6.3 always prints newline at the end */
+			/*fputs(b, stdout);*/
+			puts(b);
+			break;
+		}
+
+		/* dest = "%<modifiers>" */
+		len = 1 + strspn(p + 1, "#-+.I 0123456789");
+		memcpy(dest, p, len);
+		dest[len] = '\0';
+
+		/* print preceding string */
+		*p = '\0';
+		fputs(b, stdout);
+
+		p += len;
+		b = p + 1;
+		switch (*p) {
+		case '\0':
+			b = NULL;
+			/* fall through */
+		case '%':
+			bb_putchar('%');
+			break;
+		default:
+			/* Completes "%<modifiers>" with specifier and printfs */
+			print_func(dest, *p, filename, data IF_SELINUX(,scontext));
+			break;
+		}
+	}
+
+	free(format);
+	free(dest);
+}
+#endif  /* FEATURE_STAT_FORMAT */
+
+/* Stat the file system and print what we find.  */
+#if !ENABLE_FEATURE_STAT_FORMAT
+#define do_statfs(filename, format) do_statfs(filename)
+#endif
+static bool do_statfs(const char *filename, const char *format)
+{
+	struct statfs statfsbuf;
+#if !ENABLE_FEATURE_STAT_FORMAT
+	const char *format;
+#endif
+#if ENABLE_SELINUX
+	security_context_t scontext = NULL;
+
+	if (option_mask32 & OPT_SELINUX) {
+		if ((option_mask32 & OPT_DEREFERENCE
+		     ? lgetfilecon(filename, &scontext)
+		     : getfilecon(filename, &scontext)
+		    ) < 0
+		) {
+			bb_perror_msg(filename);
+			return 0;
+		}
+	}
+#endif
+	if (statfs(filename, &statfsbuf) != 0) {
+		bb_perror_msg("can't read file system information for '%s'", filename);
+		return 0;
+	}
+
+#if ENABLE_FEATURE_STAT_FORMAT
+	if (format == NULL) {
+# if !ENABLE_SELINUX
+		format = (option_mask32 & OPT_TERSE
+			? "%n %i %l %t %s %b %f %a %c %d\n"
+			: "  File: \"%n\"\n"
+			  "    ID: %-8i Namelen: %-7l Type: %T\n"
+			  "Block size: %-10s\n"
+			  "Blocks: Total: %-10b Free: %-10f Available: %a\n"
+			  "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":
+			"%n %i %l %t %s %b %f %a %c %d\n")
+			: (option_mask32 & OPT_SELINUX ?
+			"  File: \"%n\"\n"
+			"    ID: %-8i Namelen: %-7l Type: %T\n"
+			"Block size: %-10s\n"
+			"Blocks: Total: %-10b Free: %-10f Available: %a\n"
+			"Inodes: Total: %-10c Free: %d"
+			"  S_context: %C\n":
+			"  File: \"%n\"\n"
+			"    ID: %-8i Namelen: %-7l Type: %T\n"
+			"Block size: %-10s\n"
+			"Blocks: Total: %-10b Free: %-10f Available: %a\n"
+			"Inodes: Total: %-10c Free: %d\n")
+			);
+# endif /* SELINUX */
+	}
+	print_it(format, filename, print_statfs, &statfsbuf IF_SELINUX(, scontext));
+#else /* FEATURE_STAT_FORMAT */
+	format = (option_mask32 & OPT_TERSE
+		? "%s %llx %lu "
+		: "  File: \"%s\"\n"
+		  "    ID: %-8llx Namelen: %-7lu ");
+	printf(format,
+	       filename,
+	       get_f_fsid(&statfsbuf),
+	       statfsbuf.f_namelen);
+
+	if (option_mask32 & OPT_TERSE)
+		printf("%lx ", (unsigned long) statfsbuf.f_type);
+	else
+		printf("Type: %s\n", human_fstype(statfsbuf.f_type));
+
+# if !ENABLE_SELINUX
+	format = (option_mask32 & OPT_TERSE
+		? "%lu %llu %llu %llu %llu %llu\n"
+		: "Block size: %-10lu\n"
+		  "Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
+		  "Inodes: Total: %-10llu Free: %llu\n");
+	printf(format,
+	       (unsigned long) statfsbuf.f_bsize,
+	       (unsigned long long) statfsbuf.f_blocks,
+	       (unsigned long long) statfsbuf.f_bfree,
+	       (unsigned long long) statfsbuf.f_bavail,
+	       (unsigned long long) statfsbuf.f_files,
+	       (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
+			?	"Block size: %-10lu\n"
+				"Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
+				"Inodes: Total: %-10llu Free: %llu"
+				"S_context: %C\n"
+			:	"Block size: %-10lu\n"
+				"Blocks: Total: %-10llu Free: %-10llu Available: %llu\n"
+				"Inodes: Total: %-10llu Free: %llu\n"
+			)
+		);
+	printf(format,
+		(unsigned long) statfsbuf.f_bsize,
+		(unsigned long long) statfsbuf.f_blocks,
+		(unsigned long long) statfsbuf.f_bfree,
+		(unsigned long long) statfsbuf.f_bavail,
+		(unsigned long long) statfsbuf.f_files,
+		(unsigned long long) statfsbuf.f_ffree,
+		scontext);
+
+	if (scontext)
+		freecon(scontext);
+# endif
+#endif  /* FEATURE_STAT_FORMAT */
+	return 1;
+}
+
+/* stat the file and print what we find */
+#if !ENABLE_FEATURE_STAT_FORMAT
+#define do_stat(filename, format) do_stat(filename)
+#endif
+static bool do_stat(const char *filename, const char *format)
+{
+	struct stat statbuf;
+#if ENABLE_SELINUX
+	security_context_t scontext = NULL;
+
+	if (option_mask32 & OPT_SELINUX) {
+		if ((option_mask32 & OPT_DEREFERENCE
+		     ? lgetfilecon(filename, &scontext)
+		     : getfilecon(filename, &scontext)
+		    ) < 0
+		) {
+			bb_perror_msg(filename);
+			return 0;
+		}
+	}
+#endif
+	if ((option_mask32 & OPT_DEREFERENCE ? stat : lstat) (filename, &statbuf) != 0) {
+		bb_perror_msg("can't stat '%s'", filename);
+		return 0;
+	}
+
+#if ENABLE_FEATURE_STAT_FORMAT
+	if (format == NULL) {
+# if !ENABLE_SELINUX
+		if (option_mask32 & OPT_TERSE) {
+			format = "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o";
+		} else {
+			if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode)) {
+				format =
+					"  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 =
+					"  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";
+			}
+		}
+# 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");
+		} 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");
+			} 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");
+			}
+		}
+# endif
+	}
+	print_it(format, filename, print_stat, &statbuf IF_SELINUX(, scontext));
+#else	/* FEATURE_STAT_FORMAT */
+	if (option_mask32 & OPT_TERSE) {
+		printf("%s %llu %llu %lx %lu %lu %llx %llu %lu %lx %lx %lu %lu %lu %lu"
+		       IF_NOT_SELINUX("\n"),
+		       filename,
+		       (unsigned long long) statbuf.st_size,
+		       (unsigned long long) statbuf.st_blocks,
+		       (unsigned long) statbuf.st_mode,
+		       (unsigned long) statbuf.st_uid,
+		       (unsigned long) statbuf.st_gid,
+		       (unsigned long long) statbuf.st_dev,
+		       (unsigned long long) statbuf.st_ino,
+		       (unsigned long) statbuf.st_nlink,
+		       (unsigned long) major(statbuf.st_rdev),
+		       (unsigned long) minor(statbuf.st_rdev),
+		       (unsigned long) statbuf.st_atime,
+		       (unsigned long) statbuf.st_mtime,
+		       (unsigned long) statbuf.st_ctime,
+		       (unsigned long) statbuf.st_blksize
+		);
+# if ENABLE_SELINUX
+		if (option_mask32 & OPT_SELINUX)
+			printf(" %lc\n", *scontext);
+		else
+			bb_putchar('\n');
+# endif
+	} else {
+		char *linkname = NULL;
+		struct passwd *pw_ent;
+		struct group *gw_ent;
+
+		gw_ent = getgrgid(statbuf.st_gid);
+		pw_ent = getpwuid(statbuf.st_uid);
+
+		if (S_ISLNK(statbuf.st_mode))
+			linkname = xmalloc_readlink_or_warn(filename);
+		if (linkname) {
+			printf("  File: '%s' -> '%s'\n", filename, linkname);
+			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",
+		       (unsigned long long) statbuf.st_size,
+		       (unsigned long long) statbuf.st_blocks,
+		       (unsigned long) statbuf.st_blksize,
+		       file_type(&statbuf),
+		       (unsigned long long) statbuf.st_dev,
+		       (unsigned long long) statbuf.st_dev,
+		       (unsigned long long) statbuf.st_ino,
+		       (unsigned long) statbuf.st_nlink);
+		if (S_ISBLK(statbuf.st_mode) || S_ISCHR(statbuf.st_mode))
+			printf(" Device type: %lx,%lx\n",
+			       (unsigned long) major(statbuf.st_rdev),
+			       (unsigned long) minor(statbuf.st_rdev));
+		else
+			bb_putchar('\n');
+		printf("Access: (%04lo/%10.10s)  Uid: (%5lu/%8s)   Gid: (%5lu/%8s)\n",
+		       (unsigned long) (statbuf.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)),
+		       bb_mode_string(statbuf.st_mode),
+		       (unsigned long) statbuf.st_uid,
+		       (pw_ent != NULL) ? pw_ent->pw_name : "UNKNOWN",
+		       (unsigned long) statbuf.st_gid,
+		       (gw_ent != NULL) ? gw_ent->gr_name : "UNKNOWN");
+# if ENABLE_SELINUX
+		printf("   S_Context: %lc\n", *scontext);
+# endif
+		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 */
+	return 1;
+}
+
+int stat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int stat_main(int argc UNUSED_PARAM, char **argv)
+{
+	IF_FEATURE_STAT_FORMAT(char *format = NULL;)
+	int i;
+	int ok;
+	unsigned opts;
+	statfunc_ptr statfunc = do_stat;
+
+	opt_complementary = "-1"; /* min one arg */
+	opts = getopt32(argv, "ftL"
+		IF_SELINUX("Z")
+		IF_FEATURE_STAT_FORMAT("c:", &format)
+	);
+	if (opts & OPT_FILESYS) /* -f */
+		statfunc = do_statfs;
+#if ENABLE_SELINUX
+	if (opts & OPT_SELINUX) {
+		selinux_or_die();
+	}
+#endif
+	ok = 1;
+	argv += optind;
+	for (i = 0; argv[i]; ++i)
+		ok &= statfunc(argv[i] IF_FEATURE_STAT_FORMAT(, format));
+
+	return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+}
diff --git a/busybox-1.19.3/coreutils/stty.c b/busybox-1.19.3/coreutils/stty.c
new file mode 100644
index 0000000..7f057ea
--- /dev/null
+++ b/busybox-1.19.3/coreutils/stty.c
@@ -0,0 +1,1554 @@
+/* vi: set sw=4 ts=4: */
+/* stty -- change and print terminal line settings
+   Copyright (C) 1990-1999 Free Software Foundation, Inc.
+
+   Licensed under GPLv2 or later, see file LICENSE in this source tree.
+*/
+/* Usage: stty [-ag] [-F device] [setting...]
+
+   Options:
+   -a Write all current settings to stdout in human-readable form.
+   -g Write all current settings to stdout in stty-readable form.
+   -F Open and use the specified device instead of stdin
+
+   If no args are given, write to stdout the baud rate and settings that
+   have been changed from their defaults.  Mode reading and changes
+   are done on the specified device, or stdin if none was specified.
+
+   David MacKenzie <djm@gnu.ai.mit.edu>
+
+   Special for busybox ported by Vladimir Oleynik <dzo@simtreas.ru> 2001
+
+   */
+
+//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
+# define _POSIX_VDISABLE ((unsigned char) 0)
+#endif
+
+#define Control(c) ((c) & 0x1f)
+/* Canonical values for control characters */
+#ifndef CINTR
+# define CINTR Control('c')
+#endif
+#ifndef CQUIT
+# define CQUIT 28
+#endif
+#ifndef CERASE
+# define CERASE 127
+#endif
+#ifndef CKILL
+# define CKILL Control('u')
+#endif
+#ifndef CEOF
+# define CEOF Control('d')
+#endif
+#ifndef CEOL
+# define CEOL _POSIX_VDISABLE
+#endif
+#ifndef CSTART
+# define CSTART Control('q')
+#endif
+#ifndef CSTOP
+# define CSTOP Control('s')
+#endif
+#ifndef CSUSP
+# define CSUSP Control('z')
+#endif
+#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 CSWTCH CSUSP
+#endif
+#if defined(VSWTCH) && !defined(CSWTCH)
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+/* SunOS 5.3 loses (^Z doesn't work) if 'swtch' is the same as 'susp'.
+   So the default is to disable 'swtch.'  */
+#if defined(__sparc__) && defined(__svr4__)
+# undef CSWTCH
+# define CSWTCH _POSIX_VDISABLE
+#endif
+
+#if defined(VWERSE) && !defined(VWERASE)       /* AIX-3.2.5 */
+# define VWERASE VWERSE
+#endif
+#if defined(VDSUSP) && !defined(CDSUSP)
+# define CDSUSP Control('y')
+#endif
+#if !defined(VREPRINT) && defined(VRPRNT)       /* Irix 4.0.5 */
+# define VREPRINT VRPRNT
+#endif
+#if defined(VREPRINT) && !defined(CRPRNT)
+# define CRPRNT Control('r')
+#endif
+#if defined(VWERASE) && !defined(CWERASE)
+# define CWERASE Control('w')
+#endif
+#if defined(VLNEXT) && !defined(CLNEXT)
+# define CLNEXT Control('v')
+#endif
+#if defined(VDISCARD) && !defined(VFLUSHO)
+# define VFLUSHO VDISCARD
+#endif
+#if defined(VFLUSH) && !defined(VFLUSHO)        /* Ultrix 4.2 */
+# define VFLUSHO VFLUSH
+#endif
+#if defined(CTLECH) && !defined(ECHOCTL)        /* Ultrix 4.3 */
+# define ECHOCTL CTLECH
+#endif
+#if defined(TCTLECH) && !defined(ECHOCTL)       /* Ultrix 4.2 */
+# define ECHOCTL TCTLECH
+#endif
+#if defined(CRTKIL) && !defined(ECHOKE)         /* Ultrix 4.2 and 4.3 */
+# define ECHOKE CRTKIL
+#endif
+#if defined(VFLUSHO) && !defined(CFLUSHO)
+# define CFLUSHO Control('o')
+#endif
+#if defined(VSTATUS) && !defined(CSTATUS)
+# 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
+};
+
+/* 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
+};
+
+/* Flags for 'struct mode_info' */
+#define SANE_SET 1              /* Set in 'sane' mode                  */
+#define SANE_UNSET 2            /* Unset in 'sane' mode                */
+#define REV 4                   /* Can be turned off by prepending '-' */
+#define OMIT 8                  /* Don't display value                 */
+
+
+/* Each mode.
+ * This structure should be kept as small as humanly possible.
+ */
+struct mode_info {
+	const uint8_t type;           /* Which structure element to change    */
+	const uint8_t flags;          /* Setting and display options          */
+	/* only these values are ever used, so... */
+#if   (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x100
+	const uint8_t mask;
+#elif (CSIZE | NLDLY | CRDLY | TABDLY | BSDLY | VTDLY | FFDLY) < 0x10000
+	const uint16_t mask;
+#else
+	const tcflag_t mask;          /* Other bits to turn off for this mode */
+#endif
+	/* was using short here, but ppc32 was unhappy */
+	const tcflag_t bits;          /* Bits to set for this mode            */
+};
+
+enum {
+	/* Must match mode_name[] and mode_info[] order! */
+	IDX_evenp = 0,
+	IDX_parity,
+	IDX_oddp,
+	IDX_nl,
+	IDX_ek,
+	IDX_sane,
+	IDX_cooked,
+	IDX_raw,
+	IDX_pass8,
+	IDX_litout,
+	IDX_cbreak,
+	IDX_crt,
+	IDX_dec,
+#if IXANY
+	IDX_decctlq,
+#endif
+#if TABDLY || OXTABS
+	IDX_tabs,
+#endif
+#if XCASE && IUCLC && OLCUC
+	IDX_lcase,
+	IDX_LCASE,
+#endif
+};
+
+#define MI_ENTRY(N,T,F,B,M) N "\0"
+
+/* Mode names given on command line */
+static const char mode_name[] =
+	MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("ek",       combination, OMIT,              0,          0 )
+	MI_ENTRY("sane",     combination, OMIT,              0,          0 )
+	MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("crt",      combination, OMIT,              0,          0 )
+	MI_ENTRY("dec",      combination, OMIT,              0,          0 )
+#if IXANY
+	MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
+#endif
+#if TABDLY || OXTABS
+	MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
+#endif
+#if XCASE && IUCLC && OLCUC
+	MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
+#endif
+	MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
+	MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
+	MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
+	MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
+	MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
+	MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
+	MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
+	MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
+	MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
+	MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
+	MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
+#if CRTSCTS
+	MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
+#endif
+	MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
+	MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
+	MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
+	MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
+	MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
+	MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
+	MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
+	MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
+	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,       OMIT       | REV,  IXOFF,      0 )
+#if IUCLC
+	MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
+#endif
+#if IXANY
+	MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
+#endif
+#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 )
+#if OLCUC
+	MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
+#endif
+#if OCRNL
+	MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
+#endif
+#if ONLCR
+	MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
+#endif
+#if ONOCR
+	MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
+#endif
+#if ONLRET
+	MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
+#endif
+#if OFILL
+	MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
+#endif
+#if OFDEL
+	MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
+#endif
+#if NLDLY
+	MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
+	MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
+#endif
+#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
+
+#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
+# if OXTABS
+	MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
+# endif
+#endif
+
+#if BSDLY
+	MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
+	MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
+#endif
+#if VTDLY
+	MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
+	MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
+#endif
+#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 )
+#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,       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 )
+#if XCASE
+	MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
+#endif
+#if TOSTOP
+	MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
+#endif
+#if ECHOPRT
+	MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
+	MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
+#endif
+#if ECHOCTL
+	MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
+	MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
+#endif
+#if ECHOKE
+	MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
+	MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
+#endif
+	;
+
+#undef MI_ENTRY
+#define MI_ENTRY(N,T,F,B,M) { T, F, M, B },
+
+static const struct mode_info mode_info[] = {
+	/* This should be verbatim cut-n-paste copy of the above MI_ENTRYs */
+	MI_ENTRY("evenp",    combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("parity",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("oddp",     combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("nl",       combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("ek",       combination, OMIT,              0,          0 )
+	MI_ENTRY("sane",     combination, OMIT,              0,          0 )
+	MI_ENTRY("cooked",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("raw",      combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("pass8",    combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("litout",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("cbreak",   combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("crt",      combination, OMIT,              0,          0 )
+	MI_ENTRY("dec",      combination, OMIT,              0,          0 )
+#if IXANY
+	MI_ENTRY("decctlq",  combination, REV        | OMIT, 0,          0 )
+#endif
+#if TABDLY || OXTABS
+	MI_ENTRY("tabs",     combination, REV        | OMIT, 0,          0 )
+#endif
+#if XCASE && IUCLC && OLCUC
+	MI_ENTRY("lcase",    combination, REV        | OMIT, 0,          0 )
+	MI_ENTRY("LCASE",    combination, REV        | OMIT, 0,          0 )
+#endif
+	MI_ENTRY("parenb",   control,     REV,               PARENB,     0 )
+	MI_ENTRY("parodd",   control,     REV,               PARODD,     0 )
+	MI_ENTRY("cs5",      control,     0,                 CS5,     CSIZE)
+	MI_ENTRY("cs6",      control,     0,                 CS6,     CSIZE)
+	MI_ENTRY("cs7",      control,     0,                 CS7,     CSIZE)
+	MI_ENTRY("cs8",      control,     0,                 CS8,     CSIZE)
+	MI_ENTRY("hupcl",    control,     REV,               HUPCL,      0 )
+	MI_ENTRY("hup",      control,     REV        | OMIT, HUPCL,      0 )
+	MI_ENTRY("cstopb",   control,     REV,               CSTOPB,     0 )
+	MI_ENTRY("cread",    control,     SANE_SET   | REV,  CREAD,      0 )
+	MI_ENTRY("clocal",   control,     REV,               CLOCAL,     0 )
+#if CRTSCTS
+	MI_ENTRY("crtscts",  control,     REV,               CRTSCTS,    0 )
+#endif
+	MI_ENTRY("ignbrk",   input,       SANE_UNSET | REV,  IGNBRK,     0 )
+	MI_ENTRY("brkint",   input,       SANE_SET   | REV,  BRKINT,     0 )
+	MI_ENTRY("ignpar",   input,       REV,               IGNPAR,     0 )
+	MI_ENTRY("parmrk",   input,       REV,               PARMRK,     0 )
+	MI_ENTRY("inpck",    input,       REV,               INPCK,      0 )
+	MI_ENTRY("istrip",   input,       REV,               ISTRIP,     0 )
+	MI_ENTRY("inlcr",    input,       SANE_UNSET | REV,  INLCR,      0 )
+	MI_ENTRY("igncr",    input,       SANE_UNSET | REV,  IGNCR,      0 )
+	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,       OMIT       | REV,  IXOFF,      0 )
+#if IUCLC
+	MI_ENTRY("iuclc",    input,       SANE_UNSET | REV,  IUCLC,      0 )
+#endif
+#if IXANY
+	MI_ENTRY("ixany",    input,       SANE_UNSET | REV,  IXANY,      0 )
+#endif
+#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 )
+#if OLCUC
+	MI_ENTRY("olcuc",    output,      SANE_UNSET | REV,  OLCUC,      0 )
+#endif
+#if OCRNL
+	MI_ENTRY("ocrnl",    output,      SANE_UNSET | REV,  OCRNL,      0 )
+#endif
+#if ONLCR
+	MI_ENTRY("onlcr",    output,      SANE_SET   | REV,  ONLCR,      0 )
+#endif
+#if ONOCR
+	MI_ENTRY("onocr",    output,      SANE_UNSET | REV,  ONOCR,      0 )
+#endif
+#if ONLRET
+	MI_ENTRY("onlret",   output,      SANE_UNSET | REV,  ONLRET,     0 )
+#endif
+#if OFILL
+	MI_ENTRY("ofill",    output,      SANE_UNSET | REV,  OFILL,      0 )
+#endif
+#if OFDEL
+	MI_ENTRY("ofdel",    output,      SANE_UNSET | REV,  OFDEL,      0 )
+#endif
+#if NLDLY
+	MI_ENTRY("nl1",      output,      SANE_UNSET,        NL1,     NLDLY)
+	MI_ENTRY("nl0",      output,      SANE_SET,          NL0,     NLDLY)
+#endif
+#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
+
+#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
+# if OXTABS
+	MI_ENTRY("tab3",     output,      SANE_UNSET,        OXTABS,     0 )
+# endif
+#endif
+
+#if BSDLY
+	MI_ENTRY("bs1",      output,      SANE_UNSET,        BS1,     BSDLY)
+	MI_ENTRY("bs0",      output,      SANE_SET,          BS0,     BSDLY)
+#endif
+#if VTDLY
+	MI_ENTRY("vt1",      output,      SANE_UNSET,        VT1,     VTDLY)
+	MI_ENTRY("vt0",      output,      SANE_SET,          VT0,     VTDLY)
+#endif
+#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 )
+#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,       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 )
+#if XCASE
+	MI_ENTRY("xcase",    local,       SANE_UNSET | REV,  XCASE,      0 )
+#endif
+#if TOSTOP
+	MI_ENTRY("tostop",   local,       SANE_UNSET | REV,  TOSTOP,     0 )
+#endif
+#if ECHOPRT
+	MI_ENTRY("echoprt",  local,       SANE_UNSET | REV,  ECHOPRT,    0 )
+	MI_ENTRY("prterase", local,       OMIT       | REV,  ECHOPRT,    0 )
+#endif
+#if ECHOCTL
+	MI_ENTRY("echoctl",  local,       SANE_SET   | REV,  ECHOCTL,    0 )
+	MI_ENTRY("ctlecho",  local,       OMIT       | REV,  ECHOCTL,    0 )
+#endif
+#if ECHOKE
+	MI_ENTRY("echoke",   local,       SANE_SET   | REV,  ECHOKE,     0 )
+	MI_ENTRY("crtkill",  local,       OMIT       | REV,  ECHOKE,     0 )
+#endif
+};
+
+enum {
+	NUM_mode_info = ARRAY_SIZE(mode_info)
+};
+
+
+/* Control characters */
+struct control_info {
+	const uint8_t saneval;  /* Value to set for 'stty sane' */
+	const uint8_t offset;   /* Offset in c_cc */
+};
+
+enum {
+	/* Must match control_name[] and control_info[] order! */
+	CIDX_intr = 0,
+	CIDX_quit,
+	CIDX_erase,
+	CIDX_kill,
+	CIDX_eof,
+	CIDX_eol,
+#if VEOL2
+	CIDX_eol2,
+#endif
+#if VSWTCH
+	CIDX_swtch,
+#endif
+	CIDX_start,
+	CIDX_stop,
+	CIDX_susp,
+#if VDSUSP
+	CIDX_dsusp,
+#endif
+#if VREPRINT
+	CIDX_rprnt,
+#endif
+#if VWERASE
+	CIDX_werase,
+#endif
+#if VLNEXT
+	CIDX_lnext,
+#endif
+#if VFLUSHO
+	CIDX_flush,
+#endif
+#if VSTATUS
+	CIDX_status,
+#endif
+	CIDX_min,
+	CIDX_time,
+};
+
+#define CI_ENTRY(n,s,o) n "\0"
+
+/* Name given on command line */
+static const char control_name[] =
+	CI_ENTRY("intr",     CINTR,   VINTR   )
+	CI_ENTRY("quit",     CQUIT,   VQUIT   )
+	CI_ENTRY("erase",    CERASE,  VERASE  )
+	CI_ENTRY("kill",     CKILL,   VKILL   )
+	CI_ENTRY("eof",      CEOF,    VEOF    )
+	CI_ENTRY("eol",      CEOL,    VEOL    )
+#if VEOL2
+	CI_ENTRY("eol2",     CEOL2,   VEOL2   )
+#endif
+#if VSWTCH
+	CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
+#endif
+	CI_ENTRY("start",    CSTART,  VSTART  )
+	CI_ENTRY("stop",     CSTOP,   VSTOP   )
+	CI_ENTRY("susp",     CSUSP,   VSUSP   )
+#if VDSUSP
+	CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
+#endif
+#if VREPRINT
+	CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
+#endif
+#if VWERASE
+	CI_ENTRY("werase",   CWERASE, VWERASE )
+#endif
+#if VLNEXT
+	CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
+#endif
+#if VFLUSHO
+	CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
+#endif
+#if VSTATUS
+	CI_ENTRY("status",   CSTATUS, VSTATUS )
+#endif
+	/* These must be last because of the display routines */
+	CI_ENTRY("min",      1,       VMIN    )
+	CI_ENTRY("time",     0,       VTIME   )
+	;
+
+#undef CI_ENTRY
+#define CI_ENTRY(n,s,o) { s, o },
+
+static const struct control_info control_info[] = {
+	/* This should be verbatim cut-n-paste copy of the above CI_ENTRYs */
+	CI_ENTRY("intr",     CINTR,   VINTR   )
+	CI_ENTRY("quit",     CQUIT,   VQUIT   )
+	CI_ENTRY("erase",    CERASE,  VERASE  )
+	CI_ENTRY("kill",     CKILL,   VKILL   )
+	CI_ENTRY("eof",      CEOF,    VEOF    )
+	CI_ENTRY("eol",      CEOL,    VEOL    )
+#if VEOL2
+	CI_ENTRY("eol2",     CEOL2,   VEOL2   )
+#endif
+#if VSWTCH
+	CI_ENTRY("swtch",    CSWTCH,  VSWTCH  )
+#endif
+	CI_ENTRY("start",    CSTART,  VSTART  )
+	CI_ENTRY("stop",     CSTOP,   VSTOP   )
+	CI_ENTRY("susp",     CSUSP,   VSUSP   )
+#if VDSUSP
+	CI_ENTRY("dsusp",    CDSUSP,  VDSUSP  )
+#endif
+#if VREPRINT
+	CI_ENTRY("rprnt",    CRPRNT,  VREPRINT)
+#endif
+#if VWERASE
+	CI_ENTRY("werase",   CWERASE, VWERASE )
+#endif
+#if VLNEXT
+	CI_ENTRY("lnext",    CLNEXT,  VLNEXT  )
+#endif
+#if VFLUSHO
+	CI_ENTRY("flush",    CFLUSHO, VFLUSHO )
+#endif
+#if VSTATUS
+	CI_ENTRY("status",   CSTATUS, VSTATUS )
+#endif
+	/* These must be last because of the display routines */
+	CI_ENTRY("min",      1,       VMIN    )
+	CI_ENTRY("time",     0,       VTIME   )
+};
+
+enum {
+	NUM_control_info = ARRAY_SIZE(control_info)
+};
+
+
+struct globals {
+	const char *device_name;
+	/* The width of the screen, for output wrapping */
+	unsigned max_col;
+	/* Current position, to know when to wrap */
+	unsigned current_col;
+	char buf[10];
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	G.device_name = bb_msg_standard_input; \
+	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)
+{
+	speed_t baud;
+
+	baud = tty_value_to_baud(xatou(arg));
+
+	if (type != output_speed) {     /* either input or both */
+		cfsetispeed(mode, baud);
+	}
+	if (type != input_speed) {      /* either output or both */
+		cfsetospeed(mode, baud);
+	}
+}
+
+static NORETURN void perror_on_device_and_die(const char *fmt)
+{
+	bb_perror_msg_and_die(fmt, G.device_name);
+}
+
+static void perror_on_device(const char *fmt)
+{
+	bb_perror_msg(fmt, G.device_name);
+}
+
+/* Print format string MESSAGE and optional args.
+   Wrap to next line first if it won't fit.
+   Print a space first unless MESSAGE will start a new line */
+static void wrapf(const char *message, ...)
+{
+	char buf[128];
+	va_list args;
+	unsigned buflen;
+
+	va_start(args, message);
+	buflen = vsnprintf(buf, sizeof(buf), message, args);
+	va_end(args);
+	/* We seem to be called only with suitable lengths, but check if
+	   somebody failed to adhere to this assumption just to be sure.  */
+	if (!buflen || buflen >= sizeof(buf)) return;
+
+	if (G.current_col > 0) {
+		G.current_col++;
+		if (buf[0] != '\n') {
+			if (G.current_col + buflen >= G.max_col) {
+				bb_putchar('\n');
+				G.current_col = 0;
+			} else
+				bb_putchar(' ');
+		}
+	}
+	fputs(buf, stdout);
+	G.current_col += buflen;
+	if (buf[buflen-1] == '\n')
+		G.current_col = 0;
+}
+
+static void newline(void)
+{
+	if (G.current_col != 0)
+		wrapf("\n");
+}
+
+#ifdef TIOCGWINSZ
+static void set_window_size(int rows, int cols)
+{
+	struct winsize win = { 0, 0, 0, 0 };
+
+	if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win)) {
+		if (errno != EINVAL) {
+			goto bail;
+		}
+		memset(&win, 0, sizeof(win));
+	}
+
+	if (rows >= 0)
+		win.ws_row = rows;
+	if (cols >= 0)
+		win.ws_col = cols;
+
+	if (ioctl(STDIN_FILENO, TIOCSWINSZ, (char *) &win))
+bail:
+		perror_on_device("%s");
+}
+#endif
+
+static void display_window_size(int fancy)
+{
+	const char *fmt_str = "%s\0%s: no size information for this device";
+	unsigned width, height;
+
+	if (get_terminal_width_height(STDIN_FILENO, &width, &height)) {
+		if ((errno != EINVAL) || ((fmt_str += 2), !fancy)) {
+			perror_on_device(fmt_str);
+		}
+	} else {
+		wrapf(fancy ? "rows %u; columns %u;" : "%u %u\n",
+				height, width);
+	}
+}
+
+static const struct suffix_mult stty_suffixes[] = {
+	{ "b",  512 },
+	{ "k", 1024 },
+	{ "B", 1024 },
+	{ "", 0 }
+};
+
+static const struct mode_info *find_mode(const char *name)
+{
+	int i = index_in_strings(mode_name, name);
+	return i >= 0 ? &mode_info[i] : NULL;
+}
+
+static const struct control_info *find_control(const char *name)
+{
+	int i = index_in_strings(control_name, name);
+	return i >= 0 ? &control_info[i] : NULL;
+}
+
+enum {
+	param_need_arg = 0x80,
+	param_line    = 1 | 0x80,
+	param_rows    = 2 | 0x80,
+	param_cols    = 3 | 0x80,
+	param_columns = 4 | 0x80,
+	param_size    = 5,
+	param_speed   = 6,
+	param_ispeed  = 7 | 0x80,
+	param_ospeed  = 8 | 0x80,
+};
+
+static int find_param(const char *name)
+{
+	static const char params[] ALIGN1 =
+		"line\0"    /* 1 */
+		"rows\0"    /* 2 */
+		"cols\0"    /* 3 */
+		"columns\0" /* 4 */
+		"size\0"    /* 5 */
+		"speed\0"   /* 6 */
+		"ispeed\0"
+		"ospeed\0";
+	int i = index_in_strings(params, name) + 1;
+	if (i == 0)
+		return 0;
+	if (i != 5 && i != 6)
+		i |= 0x80;
+	return i;
+}
+
+static int recover_mode(const char *arg, struct termios *mode)
+{
+	int i, n;
+	unsigned chr;
+	unsigned long iflag, oflag, cflag, lflag;
+
+	/* Scan into temporaries since it is too much trouble to figure out
+	   the right format for 'tcflag_t' */
+	if (sscanf(arg, "%lx:%lx:%lx:%lx%n",
+			   &iflag, &oflag, &cflag, &lflag, &n) != 4)
+		return 0;
+	mode->c_iflag = iflag;
+	mode->c_oflag = oflag;
+	mode->c_cflag = cflag;
+	mode->c_lflag = lflag;
+	arg += n;
+	for (i = 0; i < NCCS; ++i) {
+		if (sscanf(arg, ":%x%n", &chr, &n) != 1)
+			return 0;
+		mode->c_cc[i] = chr;
+		arg += n;
+	}
+
+	/* Fail if there are too many fields */
+	if (*arg != '\0')
+		return 0;
+
+	return 1;
+}
+
+static void display_recoverable(const struct termios *mode,
+				int UNUSED_PARAM dummy)
+{
+	int i;
+	printf("%lx:%lx:%lx:%lx",
+		   (unsigned long) mode->c_iflag, (unsigned long) mode->c_oflag,
+		   (unsigned long) mode->c_cflag, (unsigned long) mode->c_lflag);
+	for (i = 0; i < NCCS; ++i)
+		printf(":%x", (unsigned int) mode->c_cc[i]);
+	bb_putchar('\n');
+}
+
+static void display_speed(const struct termios *mode, int fancy)
+{
+	//____________________ 01234567 8 9
+	const char *fmt_str = "%lu %lu\n\0ispeed %lu baud; ospeed %lu baud;";
+	unsigned long ispeed, ospeed;
+
+	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;";
+	}
+	if (fancy) fmt_str += 9;
+	wrapf(fmt_str, tty_baud_to_value(ispeed), tty_baud_to_value(ospeed));
+}
+
+static void do_display(const struct termios *mode, int all)
+{
+	int i;
+	tcflag_t *bitsp;
+	unsigned long mask;
+	int prev_type = control;
+
+	display_speed(mode, 1);
+	if (all)
+		display_window_size(1);
+#ifdef __linux__
+	wrapf("line = %u;\n", mode->c_line);
+#else
+	newline();
+#endif
+
+	for (i = 0; i != CIDX_min; ++i) {
+		/* If swtch is the same as susp, don't print both */
+#if VSWTCH == VSUSP
+		if (i == CIDX_swtch)
+			continue;
+#endif
+		/* If eof uses the same slot as min, only print whichever applies */
+#if VEOF == VMIN
+		if (!(mode->c_lflag & ICANON)
+		 && (i == CIDX_eof || i == CIDX_eol)
+		) {
+			continue;
+		}
+#endif
+		wrapf("%s = %s;", nth_string(control_name, i),
+			  visible(mode->c_cc[control_info[i].offset]));
+	}
+#if VEOF == VMIN
+	if ((mode->c_lflag & ICANON) == 0)
+#endif
+		wrapf("min = %u; time = %u;", mode->c_cc[VMIN], mode->c_cc[VTIME]);
+	newline();
+
+	for (i = 0; i < NUM_mode_info; ++i) {
+		if (mode_info[i].flags & OMIT)
+			continue;
+		if (mode_info[i].type != prev_type) {
+			newline();
+			prev_type = mode_info[i].type;
+		}
+
+		bitsp = mode_type_flag(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))
+				wrapf("-%s"+1, nth_string(mode_name, i));
+		} else {
+			if ((all && mode_info[i].flags & REV)
+			 || (!all && (mode_info[i].flags & (SANE_SET | REV)) == (SANE_SET | REV))
+			) {
+				wrapf("-%s", nth_string(mode_name, i));
+			}
+		}
+	}
+	newline();
+}
+
+static void sane_mode(struct termios *mode)
+{
+	int i;
+	tcflag_t *bitsp;
+
+	for (i = 0; i < NUM_control_info; ++i) {
+#if VMIN == VEOF
+		if (i == CIDX_min)
+			break;
+#endif
+		mode->c_cc[control_info[i].offset] = control_info[i].saneval;
+	}
+
+	for (i = 0; i < NUM_mode_info; ++i) {
+		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;
+		}
+	}
+}
+
+static void set_mode(const struct mode_info *info, int reversed,
+					struct termios *mode)
+{
+	tcflag_t *bitsp;
+
+	bitsp = mode_type_flag(info->type, mode);
+
+	if (bitsp) {
+		if (reversed)
+			*bitsp = *bitsp & ~info->mask & ~info->bits;
+		else
+			*bitsp = (*bitsp & ~info->mask) | info->bits;
+		return;
+	}
+
+	/* 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;
+	} 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;
+	} 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;
+		} else {
+			mode->c_iflag = mode->c_iflag & ~ICRNL;
+			if (ONLCR) mode->c_oflag = mode->c_oflag & ~ONLCR;
+		}
+	} else if (info == &mode_info[IDX_ek]) {
+		mode->c_cc[VERASE] = CERASE;
+		mode->c_cc[VKILL] = CKILL;
+	} else if (info == &mode_info[IDX_sane]) {
+		sane_mode(mode);
+	} else if (info == &mode_info[IDX_cbreak]) {
+		if (reversed)
+			mode->c_lflag |= ICANON;
+		else
+			mode->c_lflag &= ~ICANON;
+	} else if (info == &mode_info[IDX_pass8]) {
+		if (reversed) {
+			mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+			mode->c_iflag |= ISTRIP;
+		} else {
+			mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+			mode->c_iflag &= ~ISTRIP;
+		}
+	} else if (info == &mode_info[IDX_litout]) {
+		if (reversed) {
+			mode->c_cflag = (mode->c_cflag & ~CSIZE) | CS7 | PARENB;
+			mode->c_iflag |= ISTRIP;
+			mode->c_oflag |= OPOST;
+		} else {
+			mode->c_cflag = (mode->c_cflag & ~PARENB & ~CSIZE) | CS8;
+			mode->c_iflag &= ~ISTRIP;
+			mode->c_oflag &= ~OPOST;
+		}
+	} else if (info == &mode_info[IDX_raw] || info == &mode_info[IDX_cooked]) {
+		if ((info == &mode_info[IDX_raw] && reversed)
+		 || (info == &mode_info[IDX_cooked] && !reversed)
+		) {
+			/* Cooked mode */
+			mode->c_iflag |= BRKINT | IGNPAR | ISTRIP | ICRNL | IXON;
+			mode->c_oflag |= OPOST;
+			mode->c_lflag |= ISIG | ICANON;
+#if VMIN == VEOF
+			mode->c_cc[VEOF] = CEOF;
+#endif
+#if VTIME == VEOL
+			mode->c_cc[VEOL] = CEOL;
+#endif
+		} else {
+			/* Raw mode */
+			mode->c_iflag = 0;
+			mode->c_oflag &= ~OPOST;
+			mode->c_lflag &= ~(ISIG | ICANON | XCASE);
+			mode->c_cc[VMIN] = 1;
+			mode->c_cc[VTIME] = 0;
+		}
+	}
+#if IXANY
+	else if (info == &mode_info[IDX_decctlq]) {
+		if (reversed)
+			mode->c_iflag |= IXANY;
+		else
+			mode->c_iflag &= ~IXANY;
+	}
+#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;
+	}
+#endif
+#if OXTABS
+	else if (info == &mode_info[IDX_tabs]) {
+		if (reversed)
+			mode->c_oflag |= OXTABS;
+		else
+			mode->c_oflag &= ~OXTABS;
+	}
+#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;
+			mode->c_oflag &= ~OLCUC;
+		} else {
+			mode->c_lflag |= XCASE;
+			mode->c_iflag |= IUCLC;
+			mode->c_oflag |= OLCUC;
+		}
+	}
+#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 */
+		mode->c_cc[VERASE] = 127; /* DEL */
+		mode->c_cc[VKILL] = 21; /* ^U */
+		mode->c_lflag |= ECHOE | ECHOCTL | ECHOKE;
+		if (IXANY) mode->c_iflag &= ~IXANY;
+	}
+}
+
+static void set_control_char_or_die(const struct control_info *info,
+			const char *arg, struct termios *mode)
+{
+	unsigned char value;
+
+	if (info == &control_info[CIDX_min] || info == &control_info[CIDX_time])
+		value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+	else if (arg[0] == '\0' || arg[1] == '\0')
+		value = arg[0];
+	else if (strcmp(arg, "^-") == 0 || strcmp(arg, "undef") == 0)
+		value = _POSIX_VDISABLE;
+	else if (arg[0] == '^') { /* Ignore any trailing junk (^Cjunk) */
+		value = arg[1] & 0x1f; /* Non-letters get weird results */
+		if (arg[1] == '?')
+			value = 127;
+	} else
+		value = xatoul_range_sfx(arg, 0, 0xff, stty_suffixes);
+	mode->c_cc[info->offset] = value;
+}
+
+#define STTY_require_set_attr   (1 << 0)
+#define STTY_speed_was_set      (1 << 1)
+#define STTY_verbose_output     (1 << 2)
+#define STTY_recoverable_output (1 << 3)
+#define STTY_noargs             (1 << 4)
+
+int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int stty_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct termios mode;
+	void (*output_func)(const struct termios *, int);
+	const char *file_name = NULL;
+	int display_all = 0;
+	int stty_state;
+	int k;
+
+	INIT_G();
+
+	stty_state = STTY_noargs;
+	output_func = do_display;
+
+	/* First pass: only parse/verify command line params */
+	k = 0;
+	while (argv[++k]) {
+		const struct mode_info *mp;
+		const struct control_info *cp;
+		const char *arg = argv[k];
+		const char *argnext = argv[k+1];
+		int param;
+
+		if (arg[0] == '-') {
+			int i;
+			mp = find_mode(arg+1);
+			if (mp) {
+				if (!(mp->flags & REV))
+					goto invalid_argument;
+				stty_state &= ~STTY_noargs;
+				continue;
+			}
+			/* It is an option - parse it */
+			i = 0;
+			while (arg[++i]) {
+				switch (arg[i]) {
+				case 'a':
+					stty_state |= STTY_verbose_output;
+					output_func = do_display;
+					display_all = 1;
+					break;
+				case 'g':
+					stty_state |= STTY_recoverable_output;
+					output_func = display_recoverable;
+					break;
+				case 'F':
+					if (file_name)
+						bb_error_msg_and_die("only one device may be specified");
+					file_name = &arg[i+1]; /* "-Fdevice" ? */
+					if (!file_name[0]) { /* nope, "-F device" */
+						int p = k+1; /* argv[p] is argnext */
+						file_name = argnext;
+						if (!file_name)
+							bb_error_msg_and_die(bb_msg_requires_arg, "-F");
+						/* remove -F param from arg[vc] */
+						while (argv[p]) {
+							argv[p] = argv[p+1];
+							++p;
+						}
+					}
+					goto end_option;
+				default:
+					goto invalid_argument;
+				}
+			}
+ end_option:
+			continue;
+		}
+
+		mp = find_mode(arg);
+		if (mp) {
+			stty_state &= ~STTY_noargs;
+			continue;
+		}
+
+		cp = find_control(arg);
+		if (cp) {
+			if (!argnext)
+				bb_error_msg_and_die(bb_msg_requires_arg, arg);
+			/* called for the side effect of xfunc death only */
+			set_control_char_or_die(cp, argnext, &mode);
+			stty_state &= ~STTY_noargs;
+			++k;
+			continue;
+		}
+
+		param = find_param(arg);
+		if (param & param_need_arg) {
+			if (!argnext)
+				bb_error_msg_and_die(bb_msg_requires_arg, arg);
+			++k;
+		}
+
+		switch (param) {
+#ifdef __linux__
+		case param_line:
+# ifndef TIOCGWINSZ
+			xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
+			break;
+# endif /* else fall-through */
+#endif
+#ifdef TIOCGWINSZ
+		case param_rows:
+		case param_cols:
+		case param_columns:
+			xatoul_range_sfx(argnext, 1, INT_MAX, stty_suffixes);
+			break;
+		case param_size:
+#endif
+		case param_speed:
+			break;
+		case param_ispeed:
+			/* called for the side effect of xfunc death only */
+			set_speed_or_die(input_speed, argnext, &mode);
+			break;
+		case param_ospeed:
+			/* called for the side effect of xfunc death only */
+			set_speed_or_die(output_speed, argnext, &mode);
+			break;
+		default:
+			if (recover_mode(arg, &mode) == 1) break;
+			if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) break;
+ invalid_argument:
+			bb_error_msg_and_die("invalid argument '%s'", arg);
+		}
+		stty_state &= ~STTY_noargs;
+	}
+
+	/* 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");
+	/* Specifying -a or -g with non-options is an error */
+	if (!(stty_state & STTY_noargs)
+	 && (stty_state & (STTY_verbose_output | STTY_recoverable_output))
+	) {
+		bb_error_msg_and_die("modes may not be set when specifying an output style");
+	}
+
+	/* Now it is safe to start doing things */
+	if (file_name) {
+		G.device_name = file_name;
+		xmove_fd(xopen_nonblocking(G.device_name), STDIN_FILENO);
+		ndelay_off(STDIN_FILENO);
+	}
+
+	/* Initialize to all zeroes so there is no risk memcmp will report a
+	   spurious difference in an uninitialized portion of the structure */
+	memset(&mode, 0, sizeof(mode));
+	if (tcgetattr(STDIN_FILENO, &mode))
+		perror_on_device_and_die("%s");
+
+	if (stty_state & (STTY_verbose_output | STTY_recoverable_output | STTY_noargs)) {
+		get_terminal_width_height(STDOUT_FILENO, &G.max_col, NULL);
+		output_func(&mode, display_all);
+		return EXIT_SUCCESS;
+	}
+
+	/* Second pass: perform actions */
+	k = 0;
+	while (argv[++k]) {
+		const struct mode_info *mp;
+		const struct control_info *cp;
+		const char *arg = argv[k];
+		const char *argnext = argv[k+1];
+		int param;
+
+		if (arg[0] == '-') {
+			mp = find_mode(arg+1);
+			if (mp) {
+				set_mode(mp, 1 /* reversed */, &mode);
+				stty_state |= STTY_require_set_attr;
+			}
+			/* It is an option - already parsed. Skip it */
+			continue;
+		}
+
+		mp = find_mode(arg);
+		if (mp) {
+			set_mode(mp, 0 /* non-reversed */, &mode);
+			stty_state |= STTY_require_set_attr;
+			continue;
+		}
+
+		cp = find_control(arg);
+		if (cp) {
+			++k;
+			set_control_char_or_die(cp, argnext, &mode);
+			stty_state |= STTY_require_set_attr;
+			continue;
+		}
+
+		param = find_param(arg);
+		if (param & param_need_arg) {
+			++k;
+		}
+
+		switch (param) {
+#ifdef __linux__
+		case param_line:
+			mode.c_line = xatoul_sfx(argnext, stty_suffixes);
+			stty_state |= STTY_require_set_attr;
+			break;
+#endif
+#ifdef TIOCGWINSZ
+		case param_cols:
+		case param_columns:
+			set_window_size(-1, xatoul_sfx(argnext, stty_suffixes));
+			break;
+		case param_size:
+			display_window_size(0);
+			break;
+		case param_rows:
+			set_window_size(xatoul_sfx(argnext, stty_suffixes), -1);
+			break;
+#endif
+		case param_speed:
+			display_speed(&mode, 0);
+			break;
+		case param_ispeed:
+			set_speed_or_die(input_speed, argnext, &mode);
+			stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
+			break;
+		case param_ospeed:
+			set_speed_or_die(output_speed, argnext, &mode);
+			stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
+			break;
+		default:
+			if (recover_mode(arg, &mode) == 1)
+				stty_state |= STTY_require_set_attr;
+			else /* true: if (tty_value_to_baud(xatou(arg)) != (speed_t) -1) */{
+				set_speed_or_die(both_speeds, arg, &mode);
+				stty_state |= (STTY_require_set_attr | STTY_speed_was_set);
+			} /* else - impossible (caught in the first pass):
+				bb_error_msg_and_die("invalid argument '%s'", arg); */
+		}
+	}
+
+	if (stty_state & STTY_require_set_attr) {
+		struct termios new_mode;
+
+		if (tcsetattr(STDIN_FILENO, TCSADRAIN, &mode))
+			perror_on_device_and_die("%s");
+
+		/* POSIX (according to Zlotnick's book) tcsetattr returns zero if
+		   it performs *any* of the requested operations.  This means it
+		   can report 'success' when it has actually failed to perform
+		   some proper subset of the requested operations.  To detect
+		   this partial failure, get the current terminal attributes and
+		   compare them to the requested ones */
+
+		/* Initialize to all zeroes so there is no risk memcmp will report a
+		   spurious difference in an uninitialized portion of the structure */
+		memset(&new_mode, 0, sizeof(new_mode));
+		if (tcgetattr(STDIN_FILENO, &new_mode))
+			perror_on_device_and_die("%s");
+
+		if (memcmp(&mode, &new_mode, sizeof(mode)) != 0) {
+#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
+			   of the c_cflag field corresponding to the baud rate.  To save
+			   Sun users a little confusion, don't report an error if this
+			   happens.  But suppress the error only if we haven't tried to
+			   set the baud rate explicitly -- otherwise we'd never give an
+			   error for a true failure to set the baud rate */
+
+			new_mode.c_cflag &= (~CIBAUD);
+			if ((stty_state & STTY_speed_was_set)
+			 || memcmp(&mode, &new_mode, sizeof(mode)) != 0)
+#endif
+				perror_on_device_and_die("%s: cannot perform all requested operations");
+		}
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/sum.c b/busybox-1.19.3/coreutils/sum.c
new file mode 100644
index 0000000..95110a6
--- /dev/null
+++ b/busybox-1.19.3/coreutils/sum.c
@@ -0,0 +1,106 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sum -- checksum and count the blocks in a file
+ *     Like BSD sum or SysV sum -r, except like SysV sum if -s option is given.
+ *
+ * Copyright (C) 86, 89, 91, 1995-2002, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2005 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Mike Frysinger <vapier@gentoo.org>
+ *
+ * Written by Kayvan Aghaiepour and David MacKenzie
+ * Taken from coreutils and turned into a busybox applet by Mike Frysinger
+ *
+ * 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 };
+
+/* BSD: calculate and print the rotated checksum and the size in 1K blocks
+   The checksum varies depending on sizeof (int). */
+/* SYSV: calculate and print the checksum and the size in 512-byte blocks */
+/* Return 1 if successful.  */
+static unsigned sum_file(const char *file, unsigned type)
+{
+#define buf bb_common_bufsiz1
+	unsigned long long total_bytes = 0;
+	int fd, r;
+	/* The sum of all the input bytes, modulo (UINT_MAX + 1).  */
+	unsigned s = 0;
+
+	fd = open_or_warn_stdin(file);
+	if (fd == -1)
+		return 0;
+
+	while (1) {
+		size_t bytes_read = safe_read(fd, buf, BUFSIZ);
+
+		if ((ssize_t)bytes_read <= 0) {
+			r = (fd && close(fd) != 0);
+			if (!bytes_read && !r)
+				/* no error */
+				break;
+			bb_simple_perror_msg(file);
+			return 0;
+		}
+
+		total_bytes += bytes_read;
+		if (type >= SUM_SYSV) {
+			do s += buf[--bytes_read]; while (bytes_read);
+		} else {
+			r = 0;
+			do {
+				s = (s >> 1) + ((s & 1) << 15);
+				s += buf[r++];
+				s &= 0xffff; /* Keep it within bounds. */
+			} while (--bytes_read);
+		}
+	}
+
+	if (type < PRINT_NAME)
+		file = "";
+	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);
+	} else
+		printf("%05d %5llu %s\n", s, (total_bytes + 1023) / 1024, file);
+	return 1;
+#undef buf
+}
+
+int sum_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sum_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned n;
+	unsigned type = SUM_BSD;
+
+	n = getopt32(argv, "sr");
+	argv += optind;
+	if (n & 1) type = SUM_SYSV;
+	/* give the bsd priority over sysv func */
+	if (n & 2) type = SUM_BSD;
+
+	if (!argv[0]) {
+		/* Do not print the name */
+		n = sum_file("-", type);
+	} else {
+		/* Need to print the name if either
+		   - more than one file given
+		   - doing sysv */
+		type += (argv[1] || type == SUM_SYSV);
+		n = 1;
+		do {
+			n &= sum_file(*argv, type);
+		} while (*++argv);
+	}
+	return !n;
+}
diff --git a/busybox-1.19.3/coreutils/sync.c b/busybox-1.19.3/coreutils/sync.c
new file mode 100644
index 0000000..7d98a1e
--- /dev/null
+++ b/busybox-1.19.3/coreutils/sync.c
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini sync implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ *
+ * 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! */
+
+int sync_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sync_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
+{
+	/* coreutils-6.9 compat */
+	bb_warn_ignoring_args(argv[1]);
+
+	sync();
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/tac.c b/busybox-1.19.3/coreutils/tac.c
new file mode 100644
index 0000000..94d669d
--- /dev/null
+++ b/busybox-1.19.3/coreutils/tac.c
@@ -0,0 +1,111 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tac implementation for busybox
+ *
+ * Copyright (C) 2003  Yang Xiaopeng  <yxp at hanwang.com.cn>
+ * 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 source tree.
+ *
+ */
+
+/* tac - concatenate and print files in reverse */
+
+/* Based on Yang Xiaopeng's (yxp at hanwang.com.cn) patch
+ * 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! */
+
+struct lstring {
+	int size;
+	char buf[1];
+};
+
+int tac_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tac_main(int argc UNUSED_PARAM, char **argv)
+{
+	char **name;
+	FILE *f;
+	struct lstring *line = NULL;
+	llist_t *list = NULL;
+	int retval = EXIT_SUCCESS;
+
+#if ENABLE_DESKTOP
+/* tac from coreutils 6.9 supports:
+       -b, --before
+              attach the separator before instead of after
+       -r, --regex
+              interpret the separator as a regular expression
+       -s, --separator=STRING
+              use STRING as the separator instead of newline
+We support none, but at least we will complain or handle "--":
+*/
+	getopt32(argv, "");
+	argv += optind;
+#else
+	argv++;
+#endif
+	if (!*argv)
+		*--argv = (char *)"-";
+	/* We will read from last file to first */
+	name = argv;
+	while (*name)
+		name++;
+
+	do {
+		int ch, i;
+
+		name--;
+		f = fopen_or_warn_stdin(*name);
+		if (f == NULL) {
+			/* error message is printed by fopen_or_warn_stdin */
+			retval = EXIT_FAILURE;
+			continue;
+		}
+
+		errno = i = 0;
+		do {
+			ch = fgetc(f);
+			if (ch != EOF) {
+				if (!(i & 0x7f))
+					/* Grow on every 128th char */
+					line = xrealloc(line, i + 0x7f + sizeof(int) + 1);
+				line->buf[i++] = ch;
+			}
+			if (ch == '\n' || (ch == EOF && i != 0)) {
+				line = xrealloc(line, i + sizeof(int));
+				line->size = i;
+				llist_add_to(&list, line);
+				line = NULL;
+				i = 0;
+			}
+		} while (ch != EOF);
+		/* fgetc sets errno to ENOENT on EOF, we don't want
+		 * to warn on this non-error! */
+		if (errno && errno != ENOENT) {
+			bb_simple_perror_msg(*name);
+			retval = EXIT_FAILURE;
+		}
+	} while (name != argv);
+
+	while (list) {
+		line = (struct lstring *)list->data;
+		xwrite(STDOUT_FILENO, line->buf, line->size);
+		if (ENABLE_FEATURE_CLEAN_UP) {
+			free(llist_pop(&list));
+		} else {
+			list = list->link;
+		}
+	}
+
+	return retval;
+}
diff --git a/busybox-1.19.3/coreutils/tail.c b/busybox-1.19.3/coreutils/tail.c
new file mode 100644
index 0000000..43cecbd
--- /dev/null
+++ b/busybox-1.19.3/coreutils/tail.c
@@ -0,0 +1,382 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tail implementation for busybox
+ *
+ * Copyright (C) 2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant (need fancy for -c) */
+/* BB_AUDIT GNU compatible -c, -q, and -v options in 'fancy' configuration. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/tail.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Pretty much rewritten to fix numerous bugs and reduce realloc() calls.
+ * Bugs fixed (although I may have forgotten one or two... it was pretty bad)
+ * 1) mixing printf/write without fflush()ing stdout
+ * 2) no check that any open files are present
+ * 3) optstring had -q taking an arg
+ * 4) no error checking on write in some cases, and a warning even then
+ * 5) q and s interaction bug
+ * 6) no check for lseek error
+ * 7) lseek attempted when count==0 even if arg was +0 (from top)
+ */
+
+//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:	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:     "\nIf N starts with a '+', output begins with the Nth item from the start"
+//usage:     "\nof each file, not from the end."
+//usage:	)
+//usage:
+//usage:#define tail_example_usage
+//usage:       "$ tail -n 1 /etc/resolv.conf\n"
+//usage:       "nameserver 10.0.0.1\n"
+
+#include "libbb.h"
+
+static const struct suffix_mult tail_suffixes[] = {
+	{ "b", 512 },
+	{ "k", 1024 },
+	{ "m", 1024*1024 },
+	{ "", 0 }
+};
+
+struct globals {
+	bool from_top;
+	bool exitcode;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+
+static void tail_xprint_header(const char *fmt, const char *filename)
+{
+	if (fdprintf(STDOUT_FILENO, fmt, filename) < 0)
+		bb_perror_nomsg_and_die();
+}
+
+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.exitcode = EXIT_FAILURE;
+	}
+
+	return r;
+}
+
+#define header_fmt_str "\n==> %s <==\n"
+
+static unsigned eat_num(const char *p)
+{
+	if (*p == '-')
+		p++;
+	else if (*p == '+') {
+		p++;
+		G.from_top = 1;
+	}
+	return xatou_sfx(p, tail_suffixes);
+}
+
+int tail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tail_main(int argc, char **argv)
+{
+	unsigned count = 10;
+	unsigned sleep_period = 1;
+	const char *str_c, *str_n;
+
+	char *tailbuf;
+	size_t tailbufsize;
+	unsigned header_threshhold = 1;
+	unsigned nfiles;
+	int i, opt;
+
+	int *fds;
+	const char *fmt;
+
+#if ENABLE_INCLUDE_SUSv2 || ENABLE_FEATURE_FANCY_TAIL
+	/* Allow legacy syntax of an initial numeric option without -n. */
+	if (argv[1] && (argv[1][0] == '+' || argv[1][0] == '-')
+	 && isdigit(argv[1][1])
+	) {
+		count = eat_num(argv[1]);
+		argv++;
+		argc--;
+	}
+#endif
+
+	/* -s NUM, -F imlies -f */
+	IF_FEATURE_FANCY_TAIL(opt_complementary = "s+:Ff";)
+	opt = getopt32(argv, "fc:n:" IF_FEATURE_FANCY_TAIL("qs:vF"),
+			&str_c, &str_n IF_FEATURE_FANCY_TAIL(,&sleep_period));
+#define FOLLOW (opt & 0x1)
+#define COUNT_BYTES (opt & 0x2)
+	//if (opt & 0x1) // -f
+	if (opt & 0x2) count = eat_num(str_c); // -c
+	if (opt & 0x4) count = eat_num(str_n); // -n
+#if ENABLE_FEATURE_FANCY_TAIL
+	/* q: make it impossible for nfiles to be > header_threshhold */
+	if (opt & 0x8) header_threshhold = UINT_MAX; // -q
+	//if (opt & 0x10) // -s
+	if (opt & 0x20) header_threshhold = 0; // -v
+# define FOLLOW_RETRY (opt & 0x40)
+#else
+# define FOLLOW_RETRY 0
+#endif
+	argc -= optind;
+	argv += optind;
+
+	/* open all the files */
+	fds = xmalloc(sizeof(fds[0]) * (argc + 1));
+	if (!argv[0]) {
+		struct stat statbuf;
+
+		if (fstat(STDIN_FILENO, &statbuf) == 0
+		 && S_ISFIFO(statbuf.st_mode)
+		) {
+			opt &= ~1; /* clear FOLLOW */
+		}
+		argv[0] = (char *) bb_msg_standard_input;
+	}
+	nfiles = i = 0;
+	do {
+		int fd = open_or_warn_stdin(argv[i]);
+		if (fd < 0 && !FOLLOW_RETRY) {
+			G.exitcode = EXIT_FAILURE;
+			continue;
+		}
+		fds[nfiles] = fd;
+		argv[nfiles++] = argv[i];
+	} while (++i < argc);
+
+	if (!nfiles)
+		bb_error_msg_and_die("no files");
+
+	/* prepare the buffer */
+	tailbufsize = BUFSIZ;
+	if (!G.from_top && COUNT_BYTES) {
+		if (tailbufsize < count + BUFSIZ) {
+			tailbufsize = count + BUFSIZ;
+		}
+	}
+	/* 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 leading newline in the header on the first output */
+	i = 0;
+	do {
+		char *buf;
+		int taillen;
+		int newlines_seen;
+		unsigned seen;
+		int nread;
+		int fd = fds[i];
+
+		if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
+			continue; /* may happen with -F */
+
+		if (nfiles > header_threshhold) {
+			tail_xprint_header(fmt, argv[i]);
+			fmt = header_fmt_str;
+		}
+
+		if (!G.from_top) {
+			off_t current = lseek(fd, 0, SEEK_END);
+			if (current > 0) {
+				unsigned off;
+				if (COUNT_BYTES) {
+				/* Optimizing count-bytes case if the file is seekable.
+				 * Beware of backing up too far.
+				 * Also we exclude files with size 0 (because of /proc/xxx) */
+					if (count == 0)
+						continue; /* showing zero bytes is easy :) */
+					current -= count;
+					if (current < 0)
+						current = 0;
+					xlseek(fd, current, SEEK_SET);
+					bb_copyfd_size(fd, STDOUT_FILENO, count);
+					continue;
+				}
+#if 1 /* This is technically incorrect for *LONG* strings, but very useful */
+				/* Optimizing count-lines case if the file is seekable.
+				 * We assume the lines are <64k.
+				 * (Users complain that tail takes too long
+				 * on multi-gigabyte files) */
+				off = (count | 0xf); /* for small counts, be more paranoid */
+				if (off > (INT_MAX / (64*1024)))
+					off = (INT_MAX / (64*1024));
+				current -= off * (64*1024);
+				if (current < 0)
+					current = 0;
+				xlseek(fd, current, SEEK_SET);
+#endif
+			}
+		}
+
+		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 (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 += nread;
+					} else {
+						char *s = buf;
+						do {
+							--nwrite;
+							if (*s++ == '\n' && ++seen == count) {
+								break;
+							}
+						} while (nwrite);
+					}
+				}
+				if (nwrite > 0)
+					xwrite(STDOUT_FILENO, buf + nread - nwrite, nwrite);
+			} else if (count) {
+				if (COUNT_BYTES) {
+					taillen += nread;
+					if (taillen > (int)count) {
+						memmove(tailbuf, tailbuf + taillen - count, count);
+						taillen = count;
+					}
+				} else {
+					int k = nread;
+					int newlines_in_buf = 0;
+
+					do { /* count '\n' in last read */
+						k--;
+						if (buf[k] == '\n') {
+							newlines_in_buf++;
+						}
+					} while (k);
+
+					if (newlines_seen + newlines_in_buf < (int)count) {
+						newlines_seen += newlines_in_buf;
+						taillen += nread;
+					} else {
+						int extra = (buf[nread-1] != '\n');
+						char *s;
+
+						k = newlines_seen + newlines_in_buf + extra - count;
+						s = tailbuf;
+						while (k) {
+							if (*s == '\n') {
+								k--;
+							}
+							s++;
+						}
+						taillen += nread - (s - tailbuf);
+						memmove(tailbuf, s, taillen);
+						newlines_seen = count - extra;
+					}
+					if (tailbufsize < (size_t)taillen + BUFSIZ) {
+						tailbufsize = taillen + BUFSIZ;
+						tailbuf = xrealloc(tailbuf, tailbufsize);
+					}
+				}
+				buf = tailbuf + taillen;
+			}
+		} /* while (tail_read() > 0) */
+		if (!G.from_top) {
+			xwrite(STDOUT_FILENO, tailbuf, taillen);
+		}
+	} while (++i < nfiles);
+
+	tailbuf = xrealloc(tailbuf, BUFSIZ);
+
+	fmt = NULL;
+
+	if (FOLLOW) while (1) {
+		sleep(sleep_period);
+
+		i = 0;
+		do {
+			int nread;
+			const char *filename = argv[i];
+			int fd = fds[i];
+
+			if (FOLLOW_RETRY) {
+				struct stat sbuf, fsbuf;
+
+				if (fd < 0
+				 || fstat(fd, &fsbuf) < 0
+				 || stat(filename, &sbuf) < 0
+				 || fsbuf.st_dev != sbuf.st_dev
+				 || fsbuf.st_ino != sbuf.st_ino
+				) {
+					int new_fd;
+
+					if (fd >= 0)
+						close(fd);
+					new_fd = open(filename, O_RDONLY);
+					if (new_fd >= 0) {
+						bb_error_msg("%s has %s; following end of new file",
+							filename, (fd < 0) ? "appeared" : "been replaced"
+						);
+					} else if (fd >= 0) {
+						bb_perror_msg("%s has become inaccessible", filename);
+					}
+					fds[i] = fd = new_fd;
+				}
+			}
+			if (ENABLE_FEATURE_FANCY_TAIL && fd < 0)
+				continue;
+			if (nfiles > header_threshhold) {
+				fmt = header_fmt_str;
+			}
+			while ((nread = tail_read(fd, tailbuf, BUFSIZ)) > 0) {
+				if (fmt) {
+					tail_xprint_header(fmt, filename);
+					fmt = NULL;
+				}
+				xwrite(STDOUT_FILENO, tailbuf, nread);
+			}
+		} while (++i < nfiles);
+	} /* while (1) */
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(fds);
+		free(tailbuf);
+	}
+	return G.exitcode;
+}
diff --git a/busybox-1.19.3/coreutils/tee.c b/busybox-1.19.3/coreutils/tee.c
new file mode 100644
index 0000000..48cc050
--- /dev/null
+++ b/busybox-1.19.3/coreutils/tee.c
@@ -0,0 +1,118 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tee implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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;
+int tee_main(int argc, char **argv)
+{
+	const char *mode = "w\0a";
+	FILE **files;
+	FILE **fp;
+	char **names;
+	char **np;
+	char retval;
+//TODO: make unconditional
+#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
+	ssize_t c;
+# define buf bb_common_bufsiz1
+#else
+	int c;
+#endif
+	retval = getopt32(argv, "ia");	/* 'a' must be 2nd */
+	argc -= optind;
+	argv += optind;
+
+	mode += (retval & 2);	/* Since 'a' is the 2nd option... */
+
+	if (retval & 1) {
+		signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. (why?) */
+	}
+	retval = EXIT_SUCCESS;
+	/* gnu tee ignores SIGPIPE in case one of the output files is a pipe
+	 * that doesn't consume all its input.  Good idea... */
+	signal(SIGPIPE, SIG_IGN);
+
+	/* Allocate an array of FILE *'s, with one extra for a sentinel. */
+	fp = files = xzalloc(sizeof(FILE *) * (argc + 2));
+	np = names = argv - 1;
+
+	files[0] = stdout;
+	goto GOT_NEW_FILE;
+	do {
+		*fp = stdout;
+		if (NOT_LONE_DASH(*argv)) {
+			*fp = fopen_or_warn(*argv, mode);
+			if (*fp == NULL) {
+				retval = EXIT_FAILURE;
+				argv++;
+				continue;
+			}
+		}
+		*np = *argv++;
+ GOT_NEW_FILE:
+		setbuf(*fp, NULL);	/* tee must not buffer output. */
+		fp++;
+		np++;
+	} while (*argv);
+	/* names[0] will be filled later */
+
+#if ENABLE_FEATURE_TEE_USE_BLOCK_IO
+	while ((c = safe_read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
+		fp = files;
+		do
+			fwrite(buf, 1, c, *fp);
+		while (*++fp);
+	}
+	if (c < 0) {		/* Make sure read errors are signaled. */
+		retval = EXIT_FAILURE;
+	}
+#else
+	setvbuf(stdout, NULL, _IONBF, 0);
+	while ((c = getchar()) != EOF) {
+		fp = files;
+		do
+			putc(c, *fp);
+		while (*++fp);
+	}
+#endif
+
+	/* Now we need to check for i/o errors on stdin and the various
+	 * output files.  Since we know that the first entry in the output
+	 * file table is stdout, we can save one "if ferror" test by
+	 * setting the first entry to stdin and checking stdout error
+	 * status with fflush_stdout_and_exit()... although fflush()ing
+	 * is unnecessary here. */
+	np = names;
+	fp = files;
+	names[0] = (char *) bb_msg_standard_input;
+	files[0] = stdin;
+	do {	/* Now check for input and output errors. */
+		/* Checking ferror should be sufficient, but we may want to fclose.
+		 * If we do, remember not to close stdin! */
+		die_if_ferror(*fp++, *np++);
+	} while (*fp);
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/busybox-1.19.3/coreutils/test.c b/busybox-1.19.3/coreutils/test.c
new file mode 100644
index 0000000..1f5398a
--- /dev/null
+++ b/busybox-1.19.3/coreutils/test.c
@@ -0,0 +1,915 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * test implementation for busybox
+ *
+ * Copyright (c) by a whole pile of folks:
+ *
+ *     test(1); version 7-like  --  author Erik Baalbergen
+ *     modified by Eric Gisin to be used as built-in.
+ *     modified by Arnold Robbins to add SVR3 compatibility
+ *     (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket).
+ *     modified by J.T. Conklin for NetBSD.
+ *     modified by Herbert Xu to be used as built-in in ash.
+ *     modified by Erik Andersen <andersen@codepoet.org> to be used
+ *     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 source tree.
+ *
+ * Original copyright notice states:
+ *     "This program is in the Public Domain."
+ */
+
+//kbuild:lib-$(CONFIG_TEST)      += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_ASH)       += test.o test_ptr_hack.o
+//kbuild:lib-$(CONFIG_HUSH)      += test.o test_ptr_hack.o
+
+//config:config TEST
+//config:	bool "test"
+//config:	default y
+//config:	help
+//config:	  test is used to check file types and compare values,
+//config:	  returning an appropriate exit code. The bash shell
+//config:	  has test built in, ash can build it in optionally.
+//config:
+//config:config FEATURE_TEST_64
+//config:	bool "Extend test to 64 bit"
+//config:	default y
+//config:	depends on TEST || ASH_BUILTIN_TEST || HUSH
+//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 SH_STANDALONE
+ * state. */
+
+/* test(1) accepts the following grammar:
+	oexpr   ::= aexpr | aexpr "-o" oexpr ;
+	aexpr   ::= nexpr | nexpr "-a" aexpr ;
+	nexpr   ::= primary | "!" primary
+	primary ::= unary-operator operand
+		| operand binary-operator operand
+		| operand
+		| "(" oexpr ")"
+		;
+	unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"|
+		"-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S";
+
+	binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
+			"-nt"|"-ot"|"-ef";
+	operand ::= <any legal UNIX file name>
+*/
+
+/* TODO: handle [[ expr ]] bashism bash-compatibly.
+ * [[ ]] is meant to be a "better [ ]", with less weird syntax
+ * and without the risk of variables and quoted strings misinterpreted
+ * as operators.
+ * This will require support from shells - we need to know quote status
+ * of each parameter (see below).
+ *
+ * Word splitting and pathname expansion should NOT be performed:
+ *      # a="a b"; [[ $a = "a b" ]] && echo YES
+ *      YES
+ *      # [[ /bin/m* ]] && echo YES
+ *      YES
+ *
+ * =~ should do regexp match
+ * = and == should do pattern match against right side:
+ *      # [[ *a* == bab ]] && echo YES
+ *      # [[ bab == *a* ]] && echo YES
+ *      YES
+ * != does the negated == (i.e., also with pattern matching).
+ * Pattern matching is quotation-sensitive:
+ *      # [[ bab == "b"a* ]] && echo YES
+ *      YES
+ *      # [[ bab == b"a*" ]] && echo YES
+ *
+ * Conditional operators such as -f must be unquoted literals to be recognized:
+ *      # [[ -e /bin ]] && echo YES
+ *      YES
+ *      # [[ '-e' /bin ]] && echo YES
+ *      bash: conditional binary operator expected...
+ *      # A='-e'; [[ $A /bin ]] && echo YES
+ *      bash: conditional binary operator expected...
+ *
+ * || and && should work as -o and -a work in [ ]
+ * -a and -o aren't recognized (&& and || are to be used instead)
+ * ( and ) do not need to be quoted unlike in [ ]:
+ *      # [[ ( abc ) && '' ]] && echo YES
+ *      # [[ ( abc ) || '' ]] && echo YES
+ *      YES
+ *      # [[ ( abc ) -o '' ]] && echo YES
+ *      bash: syntax error in conditional expression...
+ *
+ * Apart from the above, [[ expr ]] should work as [ expr ]
+ */
+
+#define TEST_DEBUG 0
+
+enum token {
+	EOI,
+
+	FILRD, /* file access */
+	FILWR,
+	FILEX,
+
+	FILEXIST,
+
+	FILREG, /* file type */
+	FILDIR,
+	FILCDEV,
+	FILBDEV,
+	FILFIFO,
+	FILSOCK,
+
+	FILSYM,
+	FILGZ,
+	FILTT,
+
+	FILSUID, /* file bit */
+	FILSGID,
+	FILSTCK,
+
+	FILNT, /* file ops */
+	FILOT,
+	FILEQ,
+
+	FILUID,
+	FILGID,
+
+	STREZ, /* str ops */
+	STRNZ,
+	STREQ,
+	STRNE,
+	STRLT,
+	STRGT,
+
+	INTEQ, /* int ops */
+	INTNE,
+	INTGE,
+	INTGT,
+	INTLE,
+	INTLT,
+
+	UNOT,
+	BAND,
+	BOR,
+	LPAREN,
+	RPAREN,
+	OPERAND
+};
+#define is_int_op(a)      (((unsigned char)((a) - INTEQ)) <= 5)
+#define is_str_op(a)      (((unsigned char)((a) - STREZ)) <= 5)
+#define is_file_op(a)     (((unsigned char)((a) - FILNT)) <= 2)
+#define is_file_access(a) (((unsigned char)((a) - FILRD)) <= 2)
+#define is_file_type(a)   (((unsigned char)((a) - FILREG)) <= 5)
+#define is_file_bit(a)    (((unsigned char)((a) - FILSUID)) <= 2)
+
+#if TEST_DEBUG
+int depth;
+#define nest_msg(...) do { \
+	depth++; \
+	fprintf(stderr, "%*s", depth*2, ""); \
+	fprintf(stderr, __VA_ARGS__); \
+} while (0)
+#define unnest_msg(...) do { \
+	fprintf(stderr, "%*s", depth*2, ""); \
+	fprintf(stderr, __VA_ARGS__); \
+	depth--; \
+} while (0)
+#define dbg_msg(...) do { \
+	fprintf(stderr, "%*s", depth*2, ""); \
+	fprintf(stderr, __VA_ARGS__); \
+} while (0)
+#define unnest_msg_and_return(expr, ...) do { \
+	number_t __res = (expr); \
+	fprintf(stderr, "%*s", depth*2, ""); \
+	fprintf(stderr, __VA_ARGS__, res); \
+	depth--; \
+	return __res; \
+} while (0)
+static const char *const TOKSTR[] = {
+	"EOI",
+	"FILRD",
+	"FILWR",
+	"FILEX",
+	"FILEXIST",
+	"FILREG",
+	"FILDIR",
+	"FILCDEV",
+	"FILBDEV",
+	"FILFIFO",
+	"FILSOCK",
+	"FILSYM",
+	"FILGZ",
+	"FILTT",
+	"FILSUID",
+	"FILSGID",
+	"FILSTCK",
+	"FILNT",
+	"FILOT",
+	"FILEQ",
+	"FILUID",
+	"FILGID",
+	"STREZ",
+	"STRNZ",
+	"STREQ",
+	"STRNE",
+	"STRLT",
+	"STRGT",
+	"INTEQ",
+	"INTNE",
+	"INTGE",
+	"INTGT",
+	"INTLE",
+	"INTLT",
+	"UNOT",
+	"BAND",
+	"BOR",
+	"LPAREN",
+	"RPAREN",
+	"OPERAND"
+};
+#else
+#define nest_msg(...)   ((void)0)
+#define unnest_msg(...) ((void)0)
+#define dbg_msg(...)    ((void)0)
+#define unnest_msg_and_return(expr, ...) return expr
+#endif
+
+enum {
+	UNOP,
+	BINOP,
+	BUNOP,
+	BBINOP,
+	PAREN
+};
+
+struct operator_t {
+	unsigned char op_num, op_type;
+};
+
+static const struct operator_t ops_table[] = {
+	{ /* "-r" */ FILRD   , UNOP   },
+	{ /* "-w" */ FILWR   , UNOP   },
+	{ /* "-x" */ FILEX   , UNOP   },
+	{ /* "-e" */ FILEXIST, UNOP   },
+	{ /* "-f" */ FILREG  , UNOP   },
+	{ /* "-d" */ FILDIR  , UNOP   },
+	{ /* "-c" */ FILCDEV , UNOP   },
+	{ /* "-b" */ FILBDEV , UNOP   },
+	{ /* "-p" */ FILFIFO , UNOP   },
+	{ /* "-u" */ FILSUID , UNOP   },
+	{ /* "-g" */ FILSGID , UNOP   },
+	{ /* "-k" */ FILSTCK , UNOP   },
+	{ /* "-s" */ FILGZ   , UNOP   },
+	{ /* "-t" */ FILTT   , UNOP   },
+	{ /* "-z" */ STREZ   , UNOP   },
+	{ /* "-n" */ STRNZ   , UNOP   },
+	{ /* "-h" */ FILSYM  , UNOP   },    /* for backwards compat */
+
+	{ /* "-O" */ FILUID  , UNOP   },
+	{ /* "-G" */ FILGID  , UNOP   },
+	{ /* "-L" */ FILSYM  , UNOP   },
+	{ /* "-S" */ FILSOCK , UNOP   },
+	{ /* "="  */ STREQ   , BINOP  },
+	{ /* "==" */ STREQ   , BINOP  },
+	{ /* "!=" */ STRNE   , BINOP  },
+	{ /* "<"  */ STRLT   , BINOP  },
+	{ /* ">"  */ STRGT   , BINOP  },
+	{ /* "-eq"*/ INTEQ   , BINOP  },
+	{ /* "-ne"*/ INTNE   , BINOP  },
+	{ /* "-ge"*/ INTGE   , BINOP  },
+	{ /* "-gt"*/ INTGT   , BINOP  },
+	{ /* "-le"*/ INTLE   , BINOP  },
+	{ /* "-lt"*/ INTLT   , BINOP  },
+	{ /* "-nt"*/ FILNT   , BINOP  },
+	{ /* "-ot"*/ FILOT   , BINOP  },
+	{ /* "-ef"*/ FILEQ   , BINOP  },
+	{ /* "!"  */ UNOT    , BUNOP  },
+	{ /* "-a" */ BAND    , BBINOP },
+	{ /* "-o" */ BOR     , BBINOP },
+	{ /* "("  */ LPAREN  , PAREN  },
+	{ /* ")"  */ RPAREN  , PAREN  },
+};
+/* Please keep these two tables in sync */
+static const char ops_texts[] ALIGN1 =
+	"-r"  "\0"
+	"-w"  "\0"
+	"-x"  "\0"
+	"-e"  "\0"
+	"-f"  "\0"
+	"-d"  "\0"
+	"-c"  "\0"
+	"-b"  "\0"
+	"-p"  "\0"
+	"-u"  "\0"
+	"-g"  "\0"
+	"-k"  "\0"
+	"-s"  "\0"
+	"-t"  "\0"
+	"-z"  "\0"
+	"-n"  "\0"
+	"-h"  "\0"
+
+	"-O"  "\0"
+	"-G"  "\0"
+	"-L"  "\0"
+	"-S"  "\0"
+	"="   "\0"
+	"=="  "\0"
+	"!="  "\0"
+	"<"   "\0"
+	">"   "\0"
+	"-eq" "\0"
+	"-ne" "\0"
+	"-ge" "\0"
+	"-gt" "\0"
+	"-le" "\0"
+	"-lt" "\0"
+	"-nt" "\0"
+	"-ot" "\0"
+	"-ef" "\0"
+	"!"   "\0"
+	"-a"  "\0"
+	"-o"  "\0"
+	"("   "\0"
+	")"   "\0"
+;
+
+
+#if ENABLE_FEATURE_TEST_64
+typedef int64_t number_t;
+#else
+typedef int number_t;
+#endif
+
+
+/* We try to minimize both static and stack usage. */
+struct test_statics {
+	char **args;
+	/* set only by check_operator(), either to bogus struct
+	 * or points to matching operator_t struct. Never NULL. */
+	const struct operator_t *last_operator;
+	gid_t *group_array;
+	int ngroups;
+	jmp_buf leaving;
+};
+
+/* See test_ptr_hack.c */
+extern struct test_statics *const test_ptr_to_statics;
+
+#define S (*test_ptr_to_statics)
+#define args            (S.args         )
+#define last_operator   (S.last_operator)
+#define group_array     (S.group_array  )
+#define ngroups         (S.ngroups      )
+#define leaving         (S.leaving      )
+
+#define INIT_S() do { \
+	(*(struct test_statics**)&test_ptr_to_statics) = xzalloc(sizeof(S)); \
+	barrier(); \
+} while (0)
+#define DEINIT_S() do { \
+	free(test_ptr_to_statics); \
+} while (0)
+
+static number_t primary(enum token n);
+
+static void syntax(const char *op, const char *msg) NORETURN;
+static void syntax(const char *op, const char *msg)
+{
+	if (op && *op) {
+		bb_error_msg("%s: %s", op, msg);
+	} else {
+		bb_error_msg("%s: %s"+4, msg);
+	}
+	longjmp(leaving, 2);
+}
+
+/* atoi with error detection */
+//XXX: FIXME: duplicate of existing libbb function?
+static number_t getn(const char *s)
+{
+	char *p;
+#if ENABLE_FEATURE_TEST_64
+	long long r;
+#else
+	long r;
+#endif
+
+	errno = 0;
+#if ENABLE_FEATURE_TEST_64
+	r = strtoll(s, &p, 10);
+#else
+	r = strtol(s, &p, 10);
+#endif
+
+	if (errno != 0)
+		syntax(s, "out of range");
+
+	if (p == s || *(skip_whitespace(p)) != '\0')
+		syntax(s, "bad number");
+
+	return r;
+}
+
+/* UNUSED
+static int newerf(const char *f1, const char *f2)
+{
+	struct stat b1, b2;
+
+	return (stat(f1, &b1) == 0 &&
+			stat(f2, &b2) == 0 && b1.st_mtime > b2.st_mtime);
+}
+
+static int olderf(const char *f1, const char *f2)
+{
+	struct stat b1, b2;
+
+	return (stat(f1, &b1) == 0 &&
+			stat(f2, &b2) == 0 && b1.st_mtime < b2.st_mtime);
+}
+
+static int equalf(const char *f1, const char *f2)
+{
+	struct stat b1, b2;
+
+	return (stat(f1, &b1) == 0 &&
+			stat(f2, &b2) == 0 &&
+			b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
+}
+*/
+
+
+static enum token check_operator(const char *s)
+{
+	static const struct operator_t no_op = {
+		.op_num = -1,
+		.op_type = -1
+	};
+	int n;
+
+	last_operator = &no_op;
+	if (s == NULL)
+		return EOI;
+	n = index_in_strings(ops_texts, s);
+	if (n < 0)
+		return OPERAND;
+	last_operator = &ops_table[n];
+	return ops_table[n].op_num;
+}
+
+
+static int binop(void)
+{
+	const char *opnd1, *opnd2;
+	const struct operator_t *op;
+	number_t val1, val2;
+
+	opnd1 = *args;
+	check_operator(*++args);
+	op = last_operator;
+
+	opnd2 = *++args;
+	if (opnd2 == NULL)
+		syntax(args[-1], "argument expected");
+
+	if (is_int_op(op->op_num)) {
+		val1 = getn(opnd1);
+		val2 = getn(opnd2);
+		if (op->op_num == INTEQ)
+			return val1 == val2;
+		if (op->op_num == INTNE)
+			return val1 != val2;
+		if (op->op_num == INTGE)
+			return val1 >= val2;
+		if (op->op_num == INTGT)
+			return val1 >  val2;
+		if (op->op_num == INTLE)
+			return val1 <= val2;
+		/*if (op->op_num == INTLT)*/
+		return val1 <  val2;
+	}
+	if (is_str_op(op->op_num)) {
+		val1 = strcmp(opnd1, opnd2);
+		if (op->op_num == STREQ)
+			return val1 == 0;
+		if (op->op_num == STRNE)
+			return val1 != 0;
+		if (op->op_num == STRLT)
+			return val1 < 0;
+		/*if (op->op_num == STRGT)*/
+		return val1 > 0;
+	}
+	/* We are sure that these three are by now the only binops we didn't check
+	 * yet, so we do not check if the class is correct:
+	 */
+/*	if (is_file_op(op->op_num)) */
+	{
+		struct stat b1, b2;
+
+		if (stat(opnd1, &b1) || stat(opnd2, &b2))
+			return 0; /* false, since at least one stat failed */
+		if (op->op_num == FILNT)
+			return b1.st_mtime > b2.st_mtime;
+		if (op->op_num == FILOT)
+			return b1.st_mtime < b2.st_mtime;
+		/*if (op->op_num == FILEQ)*/
+		return b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino;
+	}
+	/*return 1; - NOTREACHED */
+}
+
+
+static void initialize_group_array(void)
+{
+	int n;
+
+	/* getgroups may be expensive, try to use it only once */
+	ngroups = 32;
+	do {
+		/* FIXME: ash tries so hard to not die on OOM,
+		 * and we spoil it with just one xrealloc here */
+		/* We realloc, because test_main can be entered repeatedly by shell.
+		 * Testcase (ash): 'while true; do test -x some_file; done'
+		 * and watch top. (some_file must have owner != you) */
+		n = ngroups;
+		group_array = xrealloc(group_array, n * sizeof(gid_t));
+		ngroups = getgroups(n, group_array);
+	} while (ngroups > n);
+}
+
+
+/* Return non-zero if GID is one that we have in our groups list. */
+//XXX: FIXME: duplicate of existing libbb function?
+// see toplevel TODO file:
+// possible code duplication ingroup() and is_a_group_member()
+static int is_a_group_member(gid_t gid)
+{
+	int i;
+
+	/* Short-circuit if possible, maybe saving a call to getgroups(). */
+	if (gid == getgid() || gid == getegid())
+		return 1;
+
+	if (ngroups == 0)
+		initialize_group_array();
+
+	/* Search through the list looking for GID. */
+	for (i = 0; i < ngroups; i++)
+		if (gid == group_array[i])
+			return 1;
+
+	return 0;
+}
+
+
+/* Do the same thing access(2) does, but use the effective uid and gid,
+   and don't make the mistake of telling root that any file is
+   executable. */
+static int test_eaccess(char *path, int mode)
+{
+	struct stat st;
+	unsigned int euid = geteuid();
+
+	if (stat(path, &st) < 0)
+		return -1;
+
+	if (euid == 0) {
+		/* Root can read or write any file. */
+		if (mode != X_OK)
+			return 0;
+
+		/* Root can execute any file that has any one of the execute
+		   bits set. */
+		if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+			return 0;
+	}
+
+	if (st.st_uid == euid)  /* owner */
+		mode <<= 6;
+	else if (is_a_group_member(st.st_gid))
+		mode <<= 3;
+
+	if (st.st_mode & mode)
+		return 0;
+
+	return -1;
+}
+
+
+static int filstat(char *nm, enum token mode)
+{
+	struct stat s;
+	unsigned i = i; /* gcc 3.x thinks it can be used uninitialized */
+
+	if (mode == FILSYM) {
+#ifdef S_IFLNK
+		if (lstat(nm, &s) == 0) {
+			i = S_IFLNK;
+			goto filetype;
+		}
+#endif
+		return 0;
+	}
+
+	if (stat(nm, &s) != 0)
+		return 0;
+	if (mode == FILEXIST)
+		return 1;
+	if (is_file_access(mode)) {
+		if (mode == FILRD)
+			i = R_OK;
+		if (mode == FILWR)
+			i = W_OK;
+		if (mode == FILEX)
+			i = X_OK;
+		return test_eaccess(nm, i) == 0;
+	}
+	if (is_file_type(mode)) {
+		if (mode == FILREG)
+			i = S_IFREG;
+		if (mode == FILDIR)
+			i = S_IFDIR;
+		if (mode == FILCDEV)
+			i = S_IFCHR;
+		if (mode == FILBDEV)
+			i = S_IFBLK;
+		if (mode == FILFIFO) {
+#ifdef S_IFIFO
+			i = S_IFIFO;
+#else
+			return 0;
+#endif
+		}
+		if (mode == FILSOCK) {
+#ifdef S_IFSOCK
+			i = S_IFSOCK;
+#else
+			return 0;
+#endif
+		}
+ filetype:
+		return ((s.st_mode & S_IFMT) == i);
+	}
+	if (is_file_bit(mode)) {
+		if (mode == FILSUID)
+			i = S_ISUID;
+		if (mode == FILSGID)
+			i = S_ISGID;
+		if (mode == FILSTCK)
+			i = S_ISVTX;
+		return ((s.st_mode & i) != 0);
+	}
+	if (mode == FILGZ)
+		return s.st_size > 0L;
+	if (mode == FILUID)
+		return s.st_uid == geteuid();
+	if (mode == FILGID)
+		return s.st_gid == getegid();
+	return 1; /* NOTREACHED */
+}
+
+
+static number_t nexpr(enum token n)
+{
+	number_t res;
+
+	nest_msg(">nexpr(%s)\n", TOKSTR[n]);
+	if (n == UNOT) {
+		n = check_operator(*++args);
+		if (n == EOI) {
+			/* special case: [ ! ], [ a -a ! ] are valid */
+			/* IOW, "! ARG" may miss ARG */
+			unnest_msg("<nexpr:1 (!EOI)\n");
+			return 1;
+		}
+		res = !nexpr(n);
+		unnest_msg("<nexpr:%lld\n", res);
+		return res;
+	}
+	res = primary(n);
+	unnest_msg("<nexpr:%lld\n", res);
+	return res;
+}
+
+
+static number_t aexpr(enum token n)
+{
+	number_t res;
+
+	nest_msg(">aexpr(%s)\n", TOKSTR[n]);
+	res = nexpr(n);
+	dbg_msg("aexpr: nexpr:%lld, next args:%s\n", res, args[1]);
+	if (check_operator(*++args) == BAND) {
+		dbg_msg("aexpr: arg is AND, next args:%s\n", 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]);
+	return res;
+}
+
+
+static number_t oexpr(enum token n)
+{
+	number_t res;
+
+	nest_msg(">oexpr(%s)\n", TOKSTR[n]);
+	res = aexpr(n);
+	dbg_msg("oexpr: aexpr:%lld, next args:%s\n", res, args[1]);
+	if (check_operator(*++args) == BOR) {
+		dbg_msg("oexpr: next arg is OR, next args:%s\n", 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]);
+	return res;
+}
+
+
+static number_t primary(enum token n)
+{
+#if TEST_DEBUG
+	number_t res = res; /* for compiler */
+#else
+	number_t res;
+#endif
+	const struct operator_t *args0_op;
+
+	nest_msg(">primary(%s)\n", TOKSTR[n]);
+	if (n == EOI) {
+		syntax(NULL, "argument expected");
+	}
+	if (n == LPAREN) {
+		res = oexpr(check_operator(*++args));
+		if (check_operator(*++args) != RPAREN)
+			syntax(NULL, "closing paren expected");
+		unnest_msg("<primary:%lld\n", res);
+		return res;
+	}
+
+	/* coreutils 6.9 checks "is args[1] binop and args[2] exist?" first,
+	 * do the same */
+	args0_op = last_operator;
+	/* last_operator = operator at args[1] */
+	if (check_operator(args[1]) != EOI) { /* if args[1] != NULL */
+		if (args[2]) {
+			// coreutils also does this:
+			// if (args[3] && args[0]="-l" && args[2] is BINOP)
+			//	return binop(1 /* prepended by -l */);
+			if (last_operator->op_type == BINOP)
+				unnest_msg_and_return(binop(), "<primary: binop:%lld\n");
+		}
+	}
+	/* check "is args[0] unop?" second */
+	if (args0_op->op_type == UNOP) {
+		/* unary expression */
+		if (args[1] == NULL)
+//			syntax(args0_op->op_text, "argument expected");
+			goto check_emptiness;
+		args++;
+		if (n == STREZ)
+			unnest_msg_and_return(args[0][0] == '\0', "<primary:%lld\n");
+		if (n == STRNZ)
+			unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
+		if (n == FILTT)
+			unnest_msg_and_return(isatty(getn(*args)), "<primary: isatty(%s)%lld\n", *args);
+		unnest_msg_and_return(filstat(*args, n), "<primary: filstat(%s):%lld\n", *args);
+	}
+
+	/*check_operator(args[1]); - already done */
+	if (last_operator->op_type == BINOP) {
+		/* args[2] is known to be NULL, isn't it bound to fail? */
+		unnest_msg_and_return(binop(), "<primary:%lld\n");
+	}
+ check_emptiness:
+	unnest_msg_and_return(args[0][0] != '\0', "<primary:%lld\n");
+}
+
+
+int test_main(int argc, char **argv)
+{
+	int res;
+	const char *arg0;
+//	bool negate = 0;
+
+	arg0 = bb_basename(argv[0]);
+	if (arg0[0] == '[') {
+		--argc;
+		if (!arg0[1]) { /* "[" ? */
+			if (NOT_LONE_CHAR(argv[argc], ']')) {
+				bb_error_msg("missing ]");
+				return 2;
+			}
+		} else { /* assuming "[[" */
+			if (strcmp(argv[argc], "]]") != 0) {
+				bb_error_msg("missing ]]");
+				return 2;
+			}
+		}
+		argv[argc] = NULL;
+	}
+
+	/* We must do DEINIT_S() prior to returning */
+	INIT_S();
+
+	res = setjmp(leaving);
+	if (res)
+		goto ret;
+
+	/* resetting ngroups is probably unnecessary.  it will
+	 * force a new call to getgroups(), which prevents using
+	 * group data fetched during a previous call.  but the
+	 * only way the group data could be stale is if there's
+	 * been an intervening call to setgroups(), and this
+	 * isn't likely in the case of a shell.  paranoia
+	 * prevails...
+	 */
+	/*ngroups = 0; - done by INIT_S() */
+
+	//argc--;
+	argv++;
+
+	/* Implement special cases from POSIX.2, section 4.62.4 */
+	if (!argv[0]) { /* "test" */
+		res = 1;
+		goto ret;
+	}
+#if 0
+// Now it's fixed in the parser and should not be needed
+	if (LONE_CHAR(argv[0], '!') && argv[1]) {
+		negate = 1;
+		//argc--;
+		argv++;
+	}
+	if (!argv[1]) { /* "test [!] arg" */
+		res = (*argv[0] == '\0');
+		goto ret;
+	}
+	if (argv[2] && !argv[3]) {
+		check_operator(argv[1]);
+		if (last_operator->op_type == BINOP) {
+			/* "test [!] arg1 <binary_op> arg2" */
+			args = argv;
+			res = (binop() == 0);
+			goto ret;
+		}
+	}
+
+	/* Some complex expression. Undo '!' removal */
+	if (negate) {
+		negate = 0;
+		//argc++;
+		argv--;
+	}
+#endif
+	args = argv;
+	res = !oexpr(check_operator(*args));
+
+	if (*args != NULL && *++args != NULL) {
+		/* Examples:
+		 * test 3 -lt 5 6
+		 * test -t 1 2
+		 */
+		bb_error_msg("%s: unknown operand", *args);
+		res = 2;
+	}
+ ret:
+	DEINIT_S();
+//	return negate ? !res : res;
+	return res;
+}
diff --git a/busybox-1.19.3/coreutils/test_ptr_hack.c b/busybox-1.19.3/coreutils/test_ptr_hack.c
new file mode 100644
index 0000000..5ba9dcc
--- /dev/null
+++ b/busybox-1.19.3/coreutils/test_ptr_hack.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+struct test_statics;
+
+#ifndef GCC_COMBINE
+
+/* We cheat here. It is declared as const ptr in libbb.h,
+ * but here we make it live in R/W memory */
+struct test_statics *test_ptr_to_statics;
+
+#else
+
+/* gcc -combine will see through and complain */
+/* Using alternative method which is more likely to break
+ * on weird architectures, compilers, linkers and so on */
+struct test_statics *const test_ptr_to_statics __attribute__ ((section (".data")));
+
+#endif
diff --git a/busybox-1.19.3/coreutils/touch.c b/busybox-1.19.3/coreutils/touch.c
new file mode 100644
index 0000000..0f980fd
--- /dev/null
+++ b/busybox-1.19.3/coreutils/touch.c
@@ -0,0 +1,156 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini touch implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 _NOT_ compliant -- options -a, -m, -r, -t not supported. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/touch.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Previous version called open() and then utime().  While this will be
+ * be necessary to implement -r and -t, it currently only makes things bigger.
+ * Also, exiting on a failure was a bug.  All args should be processed.
+ */
+
+#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.
+
+//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_DESKTOP(" [-d DATE] [-r FILE]") " 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_DESKTOP(
+//usage:     "\n	-d 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:
+ * -a   change only the access time
+ * -c, --no-create
+ *      do not create any files
+ * -d, --date=STRING
+ *      parse STRING and use it instead of current time
+ * -f   (ignored, BSD compat)
+ * -m   change only the modification time
+ * -r, --reference=FILE
+ *      use this file's times instead of current time
+ * -t STAMP
+ *      use [[CC]YY]MMDDhhmm[.ss] instead of current time
+ * --time=WORD
+ *      change the specified time: WORD is access, atime, or use
+ */
+
+int touch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int touch_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+	int status = EXIT_SUCCESS;
+	int opts;
+#if ENABLE_DESKTOP
+# 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"
+	;
+# endif
+	char *reference_file = NULL;
+	char *date_str = NULL;
+	struct timeval timebuf[2];
+	timebuf[1].tv_usec = timebuf[0].tv_usec = 0;
+#else
+# define reference_file NULL
+# define date_str       NULL
+# define timebuf        ((struct timeval*)NULL)
+#endif
+
+#if ENABLE_DESKTOP && 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:")
+				/*ignored:*/ "fma"
+				IF_DESKTOP(, &reference_file)
+				IF_DESKTOP(, &date_str)
+				IF_DESKTOP(, &date_str)
+	);
+
+	opts &= 1; /* only -c bit is left */
+	argv += optind;
+	if (!*argv) {
+		bb_show_usage();
+	}
+
+	if (reference_file) {
+		struct stat stbuf;
+		xstat(reference_file, &stbuf);
+		timebuf[1].tv_sec = timebuf[0].tv_sec = stbuf.st_mtime;
+	}
+
+	if (date_str) {
+		struct tm tm_time;
+		time_t t;
+
+		//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 */
+		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 */
+					continue;
+				}
+				/* Try to create the file */
+				fd = open(*argv, O_RDWR | O_CREAT, 0666);
+				if (fd >= 0) {
+					xclose(fd);
+					if (reference_file || date_str)
+						utimes(*argv, timebuf);
+					continue;
+				}
+			}
+			status = EXIT_FAILURE;
+			bb_simple_perror_msg(*argv);
+		}
+	} while (*++argv);
+
+	return status;
+}
diff --git a/busybox-1.19.3/coreutils/tr.c b/busybox-1.19.3/coreutils/tr.c
new file mode 100644
index 0000000..e67948a
--- /dev/null
+++ b/busybox-1.19.3/coreutils/tr.c
@@ -0,0 +1,346 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini tr implementation for busybox
+ *
+ ** Copyright (c) 1987,1997, Prentice Hall   All rights reserved.
+ *
+ * The name of Prentice Hall may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * Copyright (c) Michiel Huisjes
+ *
+ * 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 source tree.
+ */
+/* http://www.opengroup.org/onlinepubs/009695399/utilities/tr.html
+ * TODO: graph, print
+ */
+
+//kbuild:lib-$(CONFIG_TR) += tr.o
+
+//config:config TR
+//config:	bool "tr"
+//config:	default y
+//config:	help
+//config:	  tr is used to squeeze, and/or delete characters from standard
+//config:	  input, writing to standard output.
+//config:
+//config:config FEATURE_TR_CLASSES
+//config:	bool "Enable character classes (such as [:upper:])"
+//config:	default y
+//config:	depends on TR
+//config:	help
+//config:	  Enable character classes, enabling commands such as:
+//config:	  tr [:upper:] [:lower:] to convert input into lowercase.
+//config:
+//config:config FEATURE_TR_EQUIV
+//config:	bool "Enable equivalence classes"
+//config:	default y
+//config:	depends on TR
+//config:	help
+//config:	  Enable equivalence classes, which essentially add the enclosed
+//config:	  character to the current set. For instance, tr [=a=] xyz would
+//config:	  replace all instances of 'a' with 'xyz'. This option is mainly
+//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 {
+	ASCII = 256,
+	/* string buffer needs to be at least as big as the whole "alphabet".
+	 * BUFSIZ == ASCII is ok, but we will realloc in expand
+	 * even for smallest patterns, let's avoid that by using *2:
+	 */
+	TR_BUFSIZ = (BUFSIZ > ASCII*2) ? BUFSIZ : ASCII*2,
+};
+
+static void map(char *pvector,
+		char *string1, unsigned string1_len,
+		char *string2, unsigned string2_len)
+{
+	char last = '0';
+	unsigned i, j;
+
+	for (j = 0, i = 0; i < string1_len; i++) {
+		if (string2_len <= j)
+			pvector[(unsigned char)(string1[i])] = last;
+		else
+			pvector[(unsigned char)(string1[i])] = last = string2[j++];
+	}
+}
+
+/* supported constructs:
+ *   Ranges,  e.g.,  0-9   ==>  0123456789
+ *   Escapes, e.g.,  \a    ==>  Control-G
+ *   Character classes, e.g. [:upper:] ==> A...Z
+ *   Equiv classess, e.g. [=A=] ==> A   (hmmmmmmm?)
+ * not supported:
+ *   \ooo-\ooo - octal ranges
+ *   [x*N] - repeat char x N times
+ *   [x*] - repeat char x until it fills STRING2:
+ * # echo qwe123 | /usr/bin/tr 123456789 '[d]'
+ * qwe[d]
+ * # echo qwe123 | /usr/bin/tr 123456789 '[d*]'
+ * qweddd
+ */
+static unsigned expand(const char *arg, char **buffer_p)
+{
+	char *buffer = *buffer_p;
+	unsigned pos = 0;
+	unsigned size = TR_BUFSIZ;
+	unsigned i; /* can't be unsigned char: must be able to hold 256 */
+	unsigned char ac;
+
+	while (*arg) {
+		if (pos + ASCII > size) {
+			size += ASCII;
+			*buffer_p = buffer = xrealloc(buffer, size);
+		}
+		if (*arg == '\\') {
+			arg++;
+			buffer[pos++] = bb_process_escape_sequence(&arg);
+			continue;
+		}
+		if (arg[1] == '-') { /* "0-9..." */
+			ac = arg[2];
+			if (ac == '\0') { /* "0-": copy verbatim */
+				buffer[pos++] = *arg++; /* copy '0' */
+				continue; /* next iter will copy '-' and stop */
+			}
+			i = (unsigned char) *arg;
+			while (i <= ac) /* ok: i is unsigned _int_ */
+				buffer[pos++] = i++;
+			arg += 3; /* skip 0-9 */
+			continue;
+		}
+		if ((ENABLE_FEATURE_TR_CLASSES || ENABLE_FEATURE_TR_EQUIV)
+		 && *arg == '['
+		) {
+			arg++;
+			i = (unsigned char) *arg++;
+			/* "[xyz...". i=x, arg points to y */
+			if (ENABLE_FEATURE_TR_CLASSES && i == ':') { /* [:class:] */
+#define CLO ":]\0"
+				static const char classes[] ALIGN1 =
+					"alpha"CLO "alnum"CLO "digit"CLO
+					"lower"CLO "upper"CLO "space"CLO
+					"blank"CLO "punct"CLO "cntrl"CLO
+					"xdigit"CLO;
+				enum {
+					CLASS_invalid = 0, /* we increment the retval */
+					CLASS_alpha = 1,
+					CLASS_alnum = 2,
+					CLASS_digit = 3,
+					CLASS_lower = 4,
+					CLASS_upper = 5,
+					CLASS_space = 6,
+					CLASS_blank = 7,
+					CLASS_punct = 8,
+					CLASS_cntrl = 9,
+					CLASS_xdigit = 10,
+					//CLASS_graph = 11,
+					//CLASS_print = 12,
+				};
+				smalluint j;
+				char *tmp;
+
+				/* xdigit needs 8, not 7 */
+				i = 7 + (arg[0] == 'x');
+				tmp = xstrndup(arg, i);
+				j = index_in_strings(classes, tmp) + 1;
+				free(tmp);
+
+				if (j == CLASS_invalid)
+					goto skip_bracket;
+
+				arg += i;
+				if (j == CLASS_alnum || j == CLASS_digit || j == CLASS_xdigit) {
+					for (i = '0'; i <= '9'; i++)
+						buffer[pos++] = i;
+				}
+				if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_upper) {
+					for (i = 'A'; i <= 'Z'; i++)
+						buffer[pos++] = i;
+				}
+				if (j == CLASS_alpha || j == CLASS_alnum || j == CLASS_lower) {
+					for (i = 'a'; i <= 'z'; i++)
+						buffer[pos++] = i;
+				}
+				if (j == CLASS_space || j == CLASS_blank) {
+					buffer[pos++] = '\t';
+					if (j == CLASS_space) {
+						buffer[pos++] = '\n';
+						buffer[pos++] = '\v';
+						buffer[pos++] = '\f';
+						buffer[pos++] = '\r';
+					}
+					buffer[pos++] = ' ';
+				}
+				if (j == CLASS_punct || j == CLASS_cntrl) {
+					for (i = '\0'; i < ASCII; i++) {
+						if ((j == CLASS_punct && isprint_asciionly(i) && !isalnum(i) && !isspace(i))
+						 || (j == CLASS_cntrl && iscntrl(i))
+						) {
+							buffer[pos++] = i;
+						}
+					}
+				}
+				if (j == CLASS_xdigit) {
+					for (i = 'A'; i <= 'F'; i++) {
+						buffer[pos + 6] = i | 0x20;
+						buffer[pos++] = i;
+					}
+					pos += 6;
+				}
+				continue;
+			}
+			/* "[xyz...", i=x, arg points to y */
+			if (ENABLE_FEATURE_TR_EQUIV && i == '=') { /* [=CHAR=] */
+				buffer[pos++] = *arg; /* copy CHAR */
+				if (!arg[0] || arg[1] != '=' || arg[2] != ']')
+					bb_show_usage();
+				arg += 3;  /* skip CHAR=] */
+				continue;
+			}
+			/* The rest of "[xyz..." cases is treated as normal
+			 * string, "[" has no special meaning here:
+			 * tr "[a-z]" "[A-Z]" can be written as tr "a-z" "A-Z",
+			 * also try tr "[a-z]" "_A-Z+" and you'll see that
+			 * [] is not special here.
+			 */
+ skip_bracket:
+			arg -= 2; /* points to "[" in "[xyz..." */
+		}
+		buffer[pos++] = *arg++;
+	}
+	return pos;
+}
+
+/* NB: buffer is guaranteed to be at least TR_BUFSIZE
+ * (which is >= ASCII) big.
+ */
+static int complement(char *buffer, int buffer_len)
+{
+	int len;
+	char conv[ASCII];
+	unsigned char ch;
+
+	len = 0;
+	ch = '\0';
+	while (1) {
+		if (memchr(buffer, ch, buffer_len) == NULL)
+			conv[len++] = ch;
+		if (++ch == '\0')
+			break;
+	}
+	memcpy(buffer, conv, len);
+	return len;
+}
+
+int tr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tr_main(int argc UNUSED_PARAM, char **argv)
+{
+	int i;
+	smalluint opts;
+	ssize_t read_chars;
+	size_t in_index, out_index;
+	unsigned last = UCHAR_MAX + 1; /* not equal to any char */
+	unsigned char coded, c;
+	char *str1 = xmalloc(TR_BUFSIZ);
+	char *str2 = xmalloc(TR_BUFSIZ);
+	int str2_length;
+	int str1_length;
+	char *vector = xzalloc(ASCII * 3);
+	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)
+
+	for (i = 0; i < ASCII; i++) {
+		vector[i] = i;
+		/*invec[i] = outvec[i] = FALSE; - done by xzalloc */
+	}
+
+	/* -C/-c difference is that -C complements "characters",
+	 * and -c complements "values" (binary bytes I guess).
+	 * In POSIX locale, these are the same.
+	 */
+
+	opt_complementary = "-1";
+	opts = getopt32(argv, "+Ccds"); /* '+': stop at first non-option */
+	argv += optind;
+
+	str1_length = expand(*argv++, &str1);
+	str2_length = 0;
+	if (opts & TR_OPT_complement)
+		str1_length = complement(str1, str1_length);
+	if (*argv) {
+		if (argv[0][0] == '\0')
+			bb_error_msg_and_die("STRING2 cannot be empty");
+		str2_length = expand(*argv, &str2);
+		map(vector, str1, str1_length,
+				str2, str2_length);
+	}
+	for (i = 0; i < str1_length; i++)
+		invec[(unsigned char)(str1[i])] = TRUE;
+	for (i = 0; i < str2_length; i++)
+		outvec[(unsigned char)(str2[i])] = TRUE;
+
+	goto start_from;
+
+	/* In this loop, str1 space is reused as input buffer,
+	 * str2 - as output one. */
+	for (;;) {
+		/* If we're out of input, flush output and read more input. */
+		if ((ssize_t)in_index == read_chars) {
+			if (out_index) {
+				xwrite(STDOUT_FILENO, str2, out_index);
+ start_from:
+				out_index = 0;
+			}
+			read_chars = safe_read(STDIN_FILENO, str1, TR_BUFSIZ);
+			if (read_chars <= 0) {
+				if (read_chars < 0)
+					bb_perror_msg_and_die(bb_msg_read_error);
+				break;
+			}
+			in_index = 0;
+		}
+		c = str1[in_index++];
+		if ((opts & TR_OPT_delete) && invec[c])
+			continue;
+		coded = vector[c];
+		if ((opts & TR_OPT_squeeze_reps) && last == coded
+		 && (invec[c] || outvec[coded])
+		) {
+			continue;
+		}
+		str2[out_index++] = last = coded;
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(vector);
+		free(str2);
+		free(str1);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/true.c b/busybox-1.19.3/coreutils/true.c
new file mode 100644
index 0000000..382e476
--- /dev/null
+++ b/busybox-1.19.3/coreutils/true.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini true implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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! */
+
+int true_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int true_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/tty.c b/busybox-1.19.3/coreutils/tty.c
new file mode 100644
index 0000000..4517505
--- /dev/null
+++ b/busybox-1.19.3/coreutils/tty.c
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tty implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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. */
+	int retval;
+
+	xfunc_error_retval = 2;  /* SUSv3 requires > 1 for error. */
+
+	IF_INCLUDE_SUSv2(silent = getopt32(argv, "s");)
+	IF_INCLUDE_SUSv2(argv += optind;)
+	IF_NOT_INCLUDE_SUSv2(argv += 1;)
+
+	/* gnu tty outputs a warning that it is ignoring all args. */
+	bb_warn_ignoring_args(argv[0]);
+
+	retval = EXIT_SUCCESS;
+
+	s = xmalloc_ttyname(STDIN_FILENO);
+	if (s == NULL) {
+	/* According to SUSv3, ttyname can fail with EBADF or ENOTTY.
+	 * We know the file descriptor is good, so failure means not a tty. */
+		s = "not a tty";
+		retval = EXIT_FAILURE;
+	}
+	IF_INCLUDE_SUSv2(if (!silent) puts(s);)
+	IF_NOT_INCLUDE_SUSv2(puts(s);)
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/busybox-1.19.3/coreutils/uname.c b/busybox-1.19.3/coreutils/uname.c
new file mode 100644
index 0000000..b96d76b
--- /dev/null
+++ b/busybox-1.19.3/coreutils/uname.c
@@ -0,0 +1,171 @@
+/* vi: set sw=4 ts=4: */
+/* uname -- print system information
+ * Copyright (C) 1989-1999 Free Software Foundation, Inc.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/uname.html */
+
+/* Option		Example
+ * -s, --sysname	SunOS
+ * -n, --nodename	rocky8
+ * -r, --release	4.0
+ * -v, --version
+ * -m, --machine	sun
+ * -a, --all		SunOS rocky8 4.0  sun
+ *
+ * The default behavior is equivalent to '-s'.
+ *
+ * David MacKenzie <djm@gnu.ai.mit.edu>
+ *
+ * GNU coreutils 6.10:
+ * Option:                      struct   Example(s):
+ *                              utsname
+ *                              field:
+ * -s, --kernel-name            sysname  Linux
+ * -n, --nodename               nodename localhost.localdomain
+ * -r, --kernel-release         release  2.6.29
+ * -v, --kernel-version         version  #1 SMP Sun Jan 11 20:52:37 EST 2009
+ * -m, --machine                machine  x86_64   i686
+ * -p, --processor              (none)   x86_64   i686
+ * -i, --hardware-platform      (none)   x86_64   i386
+ *      NB: vanilla coreutils reports "unknown" -p and -i,
+ *      x86_64 and i686/i386 shown above are Fedora's inventions.
+ * -o, --operating-system       (none)   GNU/Linux
+ * -a, --all: all of the above, in the order shown.
+ *      If -p or -i is not known, don't show them
+ */
+
+/* Busyboxed by Erik Andersen
+ *
+ * Before 2003: Glenn McGrath and Manuel Novoa III
+ *  Further size reductions.
+ * Mar 16, 2003: Manuel Novoa III (mjn3@codepoet.org)
+ *  Now does proper error checking on i/o.  Plus some further space savings.
+ * Jan 2009:
+ *  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>
+
+typedef struct {
+	struct utsname name;
+	char processor[sizeof(((struct utsname*)NULL)->machine)];
+	char platform[sizeof(((struct utsname*)NULL)->machine)];
+	char os[sizeof("GNU/Linux")];
+} uname_info_t;
+
+static const char options[] ALIGN1 = "snrvmpioa";
+static const unsigned short utsname_offset[] = {
+	offsetof(uname_info_t, name.sysname), /* -s */
+	offsetof(uname_info_t, name.nodename), /* -n */
+	offsetof(uname_info_t, name.release), /* -r */
+	offsetof(uname_info_t, name.version), /* -v */
+	offsetof(uname_info_t, name.machine), /* -m */
+	offsetof(uname_info_t, processor), /* -p */
+	offsetof(uname_info_t, platform), /* -i */
+	offsetof(uname_info_t, os), /* -o */
+};
+
+int uname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int uname_main(int argc UNUSED_PARAM, char **argv)
+{
+#if ENABLE_LONG_OPTS
+	static const char uname_longopts[] ALIGN1 =
+		/* name, has_arg, val */
+		"all\0"               No_argument       "a"
+		"kernel-name\0"       No_argument       "s"
+		"nodename\0"          No_argument       "n"
+		"kernel-release\0"    No_argument       "r"
+		"release\0"           No_argument       "r"
+		"kernel-version\0"    No_argument       "v"
+		"machine\0"           No_argument       "m"
+		"processor\0"         No_argument       "p"
+		"hardware-platform\0" No_argument       "i"
+		"operating-system\0"  No_argument       "o"
+	;
+#endif
+	uname_info_t uname_info;
+#if defined(__sparc__) && defined(__linux__)
+	char *fake_sparc = getenv("FAKE_SPARC");
+#endif
+	const char *unknown_str = "unknown";
+	const char *fmt;
+	const unsigned short *delta;
+	unsigned toprint;
+
+	IF_LONG_OPTS(applet_long_options = uname_longopts);
+	toprint = getopt32(argv, options);
+
+	if (argv[optind]) { /* coreutils-6.9 compat */
+		bb_show_usage();
+	}
+
+	if (toprint & (1 << 8)) { /* -a => all opts on */
+		toprint = (1 << 8) - 1;
+		unknown_str = ""; /* -a does not print unknown fields */
+	}
+
+	if (toprint == 0) { /* no opts => -s (sysname) */
+		toprint = 1;
+	}
+
+	uname(&uname_info.name); /* never fails */
+
+#if defined(__sparc__) && defined(__linux__)
+	if (fake_sparc && (fake_sparc[0] | 0x20) == 'y') {
+		strcpy(uname_info.name.machine, "sparc");
+	}
+#endif
+	strcpy(uname_info.processor, unknown_str);
+	strcpy(uname_info.platform, unknown_str);
+	strcpy(uname_info.os, "GNU/Linux");
+#if 0
+	/* Fedora does something like this */
+	strcpy(uname_info.processor, uname_info.name.machine);
+	strcpy(uname_info.platform, uname_info.name.machine);
+	if (uname_info.platform[0] == 'i'
+	 && uname_info.platform[1]
+	 && uname_info.platform[2] == '8'
+	 && uname_info.platform[3] == '6'
+	) {
+		uname_info.platform[1] = '3';
+	}
+#endif
+
+	delta = utsname_offset;
+	fmt = " %s" + 1;
+	do {
+		if (toprint & 1) {
+			const char *p = (char *)(&uname_info) + *delta;
+			if (p[0]) {
+				printf(fmt, p);
+				fmt = " %s";
+			}
+		}
+		++delta;
+	} while (toprint >>= 1);
+	bb_putchar('\n');
+
+	fflush_stdout_and_exit(EXIT_SUCCESS); /* coreutils-6.9 compat */
+}
diff --git a/busybox-1.19.3/coreutils/uniq.c b/busybox-1.19.3/coreutils/uniq.c
new file mode 100644
index 0000000..9208d34
--- /dev/null
+++ b/busybox-1.19.3/coreutils/uniq.c
@@ -0,0 +1,124 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uniq implementation for busybox
+ *
+ * Copyright (C) 2005  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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;
+int uniq_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *input_filename;
+	unsigned skip_fields, skip_chars, max_chars;
+	unsigned opt;
+	char *cur_line;
+	const char *cur_compare;
+
+	enum {
+		OPT_c = 0x1,
+		OPT_d = 0x2, /* print only dups */
+		OPT_u = 0x4, /* print only uniq */
+		OPT_f = 0x8,
+		OPT_s = 0x10,
+		OPT_w = 0x20,
+	};
+
+	skip_fields = skip_chars = 0;
+	max_chars = INT_MAX;
+
+	opt_complementary = "f+:s+:w+";
+	opt = getopt32(argv, "cduf:s:w:", &skip_fields, &skip_chars, &max_chars);
+	argv += optind;
+
+	input_filename = argv[0];
+	if (input_filename) {
+		const char *output;
+
+		if (input_filename[0] != '-' || input_filename[1]) {
+			close(STDIN_FILENO); /* == 0 */
+			xopen(input_filename, O_RDONLY); /* fd will be 0 */
+		}
+		output = argv[1];
+		if (output) {
+			if (argv[2])
+				bb_show_usage();
+			if (output[0] != '-' || output[1]) {
+				// Won't work with "uniq - FILE" and closed stdin:
+				//close(STDOUT_FILENO);
+				//xopen(output, O_WRONLY | O_CREAT | O_TRUNC);
+				xmove_fd(xopen(output, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
+			}
+		}
+	}
+
+	cur_compare = cur_line = NULL; /* prime the pump */
+
+	do {
+		unsigned i;
+		unsigned long dups;
+		char *old_line;
+		const char *old_compare;
+
+		old_line = cur_line;
+		old_compare = cur_compare;
+		dups = 0;
+
+		/* gnu uniq ignores newlines */
+		while ((cur_line = xmalloc_fgetline(stdin)) != NULL) {
+			cur_compare = cur_line;
+			for (i = skip_fields; i; i--) {
+				cur_compare = skip_whitespace(cur_compare);
+				cur_compare = skip_non_whitespace(cur_compare);
+			}
+			for (i = skip_chars; *cur_compare && i; i--) {
+				++cur_compare;
+			}
+
+			if (!old_line || strncmp(old_compare, cur_compare, max_chars)) {
+				break;
+			}
+
+			free(cur_line);
+			++dups;  /* testing for overflow seems excessive */
+		}
+
+		if (old_line) {
+			if (!(opt & (OPT_d << !!dups))) { /* (if dups, opt & OPT_u) */
+				if (opt & OPT_c) {
+					/* %7lu matches GNU coreutils 6.9 */
+					printf("%7lu ", dups + 1);
+				}
+				printf("%s\n", old_line);
+			}
+			free(old_line);
+		}
+	} while (cur_line);
+
+	die_if_ferror(stdin, input_filename);
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/coreutils/usleep.c b/busybox-1.19.3/coreutils/usleep.c
new file mode 100644
index 0000000..2e4eb57
--- /dev/null
+++ b/busybox-1.19.3/coreutils/usleep.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * usleep implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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! */
+
+int usleep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int usleep_main(int argc UNUSED_PARAM, char **argv)
+{
+	if (!argv[1]) {
+		bb_show_usage();
+	}
+
+	usleep(xatou(argv[1]));
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/uudecode.c b/busybox-1.19.3/coreutils/uudecode.c
new file mode 100644
index 0000000..6ecfe6c
--- /dev/null
+++ b/busybox-1.19.3/coreutils/uudecode.c
@@ -0,0 +1,238 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 2003, Glenn McGrath
+ *
+ * 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
+ *
+ * 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 name 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"
+
+#if ENABLE_UUDECODE
+static void FAST_FUNC read_stduu(FILE *src_stream, FILE *dst_stream, int flags UNUSED_PARAM)
+{
+	char *line;
+
+	while ((line = xmalloc_fgetline(src_stream)) != NULL) {
+		int encoded_len, str_len;
+		char *line_ptr, *dst;
+
+		if (strcmp(line, "end") == 0) {
+			return; /* the only non-error exit */
+		}
+
+		line_ptr = line;
+		while (*line_ptr) {
+			*line_ptr = (*line_ptr - 0x20) & 0x3f;
+			line_ptr++;
+		}
+		str_len = line_ptr - line;
+
+		encoded_len = line[0] * 4 / 3;
+		/* Check that line is not too short. (we tolerate
+		 * overly _long_ line to accomodate possible extra '`').
+		 * Empty line case is also caught here. */
+		if (str_len <= encoded_len) {
+			break; /* go to bb_error_msg_and_die("short file"); */
+		}
+		if (encoded_len <= 0) {
+			/* Ignore the "`\n" line, why is it even in the encode file ? */
+			free(line);
+			continue;
+		}
+		if (encoded_len > 60) {
+			bb_error_msg_and_die("line too long");
+		}
+
+		dst = line;
+		line_ptr = line + 1;
+		do {
+			/* Merge four 6 bit chars to three 8 bit chars */
+			*dst++ = line_ptr[0] << 2 | line_ptr[1] >> 4;
+			encoded_len--;
+			if (encoded_len == 0) {
+				break;
+			}
+
+			*dst++ = line_ptr[1] << 4 | line_ptr[2] >> 2;
+			encoded_len--;
+			if (encoded_len == 0) {
+				break;
+			}
+
+			*dst++ = line_ptr[2] << 6 | line_ptr[3];
+			line_ptr += 4;
+			encoded_len -= 2;
+		} while (encoded_len > 0);
+		fwrite(line, 1, dst - line, dst_stream);
+		free(line);
+	}
+	bb_error_msg_and_die("short file");
+}
+#endif
+
+#if ENABLE_UUDECODE
+int uudecode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int uudecode_main(int argc UNUSED_PARAM, char **argv)
+{
+	FILE *src_stream;
+	char *outname = NULL;
+	char *line;
+
+	opt_complementary = "?1"; /* 1 argument max */
+	getopt32(argv, "o:", &outname);
+	argv += optind;
+
+	if (!argv[0])
+		*--argv = (char*)"-";
+	src_stream = xfopen_stdin(argv[0]);
+
+	/* Search for the start of the encoding */
+	while ((line = xmalloc_fgetline(src_stream)) != NULL) {
+		void FAST_FUNC (*decode_fn_ptr)(FILE *src, FILE *dst, int flags);
+		char *line_ptr;
+		FILE *dst_stream;
+		int mode;
+
+		if (strncmp(line, "begin-base64 ", 13) == 0) {
+			line_ptr = line + 13;
+			decode_fn_ptr = read_base64;
+		} else if (strncmp(line, "begin ", 6) == 0) {
+			line_ptr = line + 6;
+			decode_fn_ptr = read_stduu;
+		} else {
+			free(line);
+			continue;
+		}
+
+		/* begin line found. decode and exit */
+		mode = bb_strtou(line_ptr, NULL, 8);
+		if (outname == NULL) {
+			outname = strchr(line_ptr, ' ');
+			if ((outname == NULL) || (*outname == '\0')) {
+				break;
+			}
+			outname++;
+		}
+		dst_stream = stdout;
+		if (NOT_LONE_DASH(outname)) {
+			dst_stream = xfopen_for_write(outname);
+			fchmod(fileno(dst_stream), mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+		}
+		free(line);
+		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.
+
+#!/bin/sh
+test -x busybox || { echo "No ./busybox?"; exit; }
+ln -sf busybox uudecode
+ln -sf busybox uuencode
+>A_null
+echo -n A >A
+echo -n AB >AB
+echo -n ABC >ABC
+echo -n ABCD >ABCD
+echo -n ABCDE >ABCDE
+echo -n ABCDEF >ABCDEF
+cat busybox >A_bbox
+for f in A*; do
+    echo uuencode $f
+    ./uuencode    $f <$f >u_$f
+    ./uuencode -m $f <$f >m_$f
+done
+mkdir unpk_u unpk_m 2>/dev/null
+for f in u_*; do
+    ./uudecode <$f -o unpk_u/${f:2}
+    diff -a ${f:2} unpk_u/${f:2} >/dev/null 2>&1
+    echo uudecode $f: $?
+done
+for f in m_*; do
+    ./uudecode <$f -o unpk_m/${f:2}
+    diff -a ${f:2} unpk_m/${f:2} >/dev/null 2>&1
+    echo uudecode $f: $?
+done
+*/
diff --git a/busybox-1.19.3/coreutils/uuencode.c b/busybox-1.19.3/coreutils/uuencode.c
new file mode 100644
index 0000000..84a489a
--- /dev/null
+++ b/busybox-1.19.3/coreutils/uuencode.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  Copyright (C) 2000 by Glenn McGrath
+ *
+ *  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 source tree.
+ */
+
+//usage:#define uuencode_trivial_usage
+//usage:       "[-m] [INFILE] STORED_FILENAME"
+//usage:#define uuencode_full_usage "\n\n"
+//usage:       "Uuencode a file 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 = 15*3,  /* This *MUST* be a multiple of 3 */
+	DST_BUF_SIZE = 4 * ((SRC_BUF_SIZE + 2) / 3),
+};
+
+int uuencode_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int uuencode_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat stat_buf;
+	int src_fd = STDIN_FILENO;
+	const char *tbl;
+	mode_t mode;
+	char src_buf[SRC_BUF_SIZE];
+	char dst_buf[DST_BUF_SIZE + 1];
+
+	tbl = bb_uuenc_tbl_std;
+	mode = 0666 & ~umask(0666);
+	opt_complementary = "-1:?2"; /* must have 1 or 2 args */
+	if (getopt32(argv, "m")) {
+		tbl = bb_uuenc_tbl_base64;
+	}
+	argv += optind;
+	if (argv[1]) {
+		src_fd = xopen(argv[0], O_RDONLY);
+		fstat(src_fd, &stat_buf);
+		mode = stat_buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+		argv++;
+	}
+
+	printf("begin%s %o %s", tbl == bb_uuenc_tbl_std ? "" : "-base64", mode, *argv);
+	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, tbl);
+		bb_putchar('\n');
+		if (tbl == bb_uuenc_tbl_std) {
+			bb_putchar(tbl[size]);
+		}
+		fflush(stdout);
+		xwrite(STDOUT_FILENO, dst_buf, 4 * ((size + 2) / 3));
+	}
+	printf(tbl == bb_uuenc_tbl_std ? "\n`\nend\n" : "\n====\n");
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/coreutils/wc.c b/busybox-1.19.3/coreutils/wc.c
new file mode 100644
index 0000000..a410e40
--- /dev/null
+++ b/busybox-1.19.3/coreutils/wc.c
@@ -0,0 +1,240 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wc implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 compliant. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/wc.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Rewritten to fix a number of problems and do some size optimizations.
+ * Problems in the previous busybox implementation (besides bloat) included:
+ *  1) broken 'wc -c' optimization (read note below)
+ *  2) broken handling of '-' args
+ *  3) no checking of ferror on EOF returns
+ *  4) isprint() wasn't considered when word counting.
+ *
+ * NOTES:
+ *
+ * The previous busybox wc attempted an optimization using stat for the
+ * case of counting chars only.  I omitted that because it was broken.
+ * It didn't take into account the possibility of input coming from a
+ * pipe, or input from a file with file pointer not at the beginning.
+ *
+ * To implement such a speed optimization correctly, not only do you
+ * need the size, but also the file position.  Note also that the
+ * file position may be past the end of file.  Consider the example
+ * (adapted from example in gnu wc.c)
+ *
+ *      echo hello > /tmp/testfile &&
+ *      (dd ibs=1k skip=1 count=0 &> /dev/null; wc -c) < /tmp/testfile
+ *
+ * for which 'wc -c' should output '0'.
+ */
+#include "libbb.h"
+#include "unicode.h"
+
+#if !ENABLE_LOCALE_SUPPORT
+# undef isprint
+# undef isspace
+# define isprint(c) ((unsigned)((c) - 0x20) <= (0x7e - 0x20))
+# define isspace(c) ((c) == ' ')
+#endif
+
+#if ENABLE_FEATURE_WC_LARGE
+# define COUNT_T unsigned long long
+# define COUNT_FMT "llu"
+#else
+# define COUNT_T unsigned
+# 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, /* -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;
+int wc_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *arg;
+	const char *start_fmt = " %9"COUNT_FMT + 1;
+	const char *fname_fmt = " %s\n";
+	COUNT_T *pcounts;
+	COUNT_T counts[NUM_WCS];
+	COUNT_T totals[NUM_WCS];
+	int num_files;
+	smallint status = EXIT_SUCCESS;
+	unsigned print_type;
+
+	init_unicode();
+
+	print_type = getopt32(argv, "lwmcL");
+
+	if (print_type == 0) {
+		print_type = (1 << WC_LINES) | (1 << WC_WORDS) | (1 << WC_BYTES);
+	}
+
+	argv += optind;
+	if (!argv[0]) {
+		*--argv = (char *) bb_msg_standard_input;
+		fname_fmt = "\n";
+	}
+	if (!argv[1]) { /* zero or one filename? */
+		if (!((print_type-1) & print_type)) /* exactly one option? */
+			start_fmt = "%"COUNT_FMT;
+	}
+
+	memset(totals, 0, sizeof(totals));
+
+	pcounts = counts;
+
+	num_files = 0;
+	while ((arg = *argv++) != NULL) {
+		FILE *fp;
+		const char *s;
+		unsigned u;
+		unsigned linepos;
+		smallint in_word;
+
+		++num_files;
+		fp = fopen_or_warn_stdin(arg);
+		if (!fp) {
+			status = EXIT_FAILURE;
+			continue;
+		}
+
+		memset(counts, 0, sizeof(counts));
+		linepos = 0;
+		in_word = 0;
+
+		while (1) {
+			int c;
+			/* Our -w doesn't match GNU wc exactly... oh well */
+
+			c = getc(fp);
+			if (c == EOF) {
+				if (ferror(fp)) {
+					bb_simple_perror_msg(arg);
+					status = EXIT_FAILURE;
+				}
+				goto DO_EOF;  /* Treat an EOF as '\r'. */
+			}
+
+			/* 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;
+					continue;
+				}
+			} else if ((unsigned)(c - 9) <= 4) {
+				/* \t  9
+				 * \n 10
+				 * \v 11
+				 * \f 12
+				 * \r 13
+				 */
+				if (c == '\t') {
+					linepos = (linepos | 7) + 1;
+				} else {  /* '\n', '\r', '\f', or '\v' */
+ DO_EOF:
+					if (linepos > counts[WC_LENGTH]) {
+						counts[WC_LENGTH] = linepos;
+					}
+					if (c == '\n') {
+						++counts[WC_LINES];
+					}
+					if (c != '\v') {
+						linepos = 0;
+					}
+				}
+			} else {
+				continue;
+			}
+
+			counts[WC_WORDS] += in_word;
+			in_word = 0;
+			if (c == EOF) {
+				break;
+			}
+		}
+
+		fclose_if_not_stdin(fp);
+
+		if (totals[WC_LENGTH] < counts[WC_LENGTH]) {
+			totals[WC_LENGTH] = counts[WC_LENGTH];
+		}
+		totals[WC_LENGTH] -= counts[WC_LENGTH];
+
+ OUTPUT:
+		/* coreutils wc tries hard to print pretty columns
+		 * (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;
+		do {
+			if (print_type & (1 << u)) {
+				printf(s, pcounts[u]);
+				s = " %9"COUNT_FMT; /* Ok... restore the leading space. */
+			}
+			totals[u] += pcounts[u];
+		} while (++u < NUM_WCS);
+		printf(fname_fmt, arg);
+	}
+
+	/* If more than one file was processed, we want the totals.  To save some
+	 * space, we set the pcounts ptr to the totals array.  This has the side
+	 * 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. */
+		arg = "total";
+		pcounts = totals;
+		--argv;
+		goto OUTPUT;
+	}
+
+	fflush_stdout_and_exit(status);
+}
diff --git a/busybox-1.19.3/coreutils/who.c b/busybox-1.19.3/coreutils/who.c
new file mode 100644
index 0000000..c6c9252
--- /dev/null
+++ b/busybox-1.19.3/coreutils/who.c
@@ -0,0 +1,130 @@
+/* vi: set sw=4 ts=4: */
+/*----------------------------------------------------------------------
+ * Mini who is used to display user name, login time,
+ * idle time and host name.
+ *
+ * Author: Da Chen  <dchen@ayrnetworks.com>
+ *
+ * This is a free document; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation:
+ *    http://www.gnu.org/copyleft/gpl.html
+ *
+ * Copyright (c) 2002 AYR Networks, Inc.
+ *
+ * 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"
+
+#include "libbb.h"
+
+static void idle_string(char *str6, time_t t)
+{
+	t = time(NULL) - t;
+
+	/*if (t < 60) {
+		str6[0] = '.';
+		str6[1] = '\0';
+		return;
+	}*/
+	if (t >= 0 && t < (24 * 60 * 60)) {
+		sprintf(str6, "%02d:%02d",
+				(int) (t / (60 * 60)),
+				(int) ((t % (60 * 60)) / 60));
+		return;
+	}
+	strcpy(str6, "old");
+}
+
+int who_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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, do_users ? "" : "aH");
+	if (opt & 2) // -H
+		printf("USER\t\tTTY\t\tIDLE\tTIME\t\t HOST\n");
+
+	setutent();
+	while ((ut = getutent()) != NULL) {
+		if (ut->ut_user[0]
+		 && ((opt & 1) || ut->ut_type == USER_PROCESS)
+		) {
+			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
+				);
+			} else {
+				printf(fmt, ut->ut_user);
+				fmt = " %s";
+			}
+		}
+	}
+	if (do_users)
+		bb_putchar('\n');
+	if (ENABLE_FEATURE_CLEAN_UP)
+		endutent();
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/coreutils/whoami.c b/busybox-1.19.3/coreutils/whoami.c
new file mode 100644
index 0000000..30b17ca
--- /dev/null
+++ b/busybox-1.19.3/coreutils/whoami.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini whoami implementation for busybox
+ *
+ * Copyright (C) 2000  Edward Betts <edward@debian.org>.
+ *
+ * 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! */
+
+int whoami_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int whoami_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	if (argv[1])
+		bb_show_usage();
+
+	/* Will complain and die if username not found */
+	puts(xuid2uname(geteuid()));
+
+	return fflush_all();
+}
diff --git a/busybox-1.19.3/coreutils/yes.c b/busybox-1.19.3/coreutils/yes.c
new file mode 100644
index 0000000..5d799f0
--- /dev/null
+++ b/busybox-1.19.3/coreutils/yes.c
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * yes implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* BB_AUDIT SUSv3 N/A -- Matches GNU behavior. */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Size reductions and removed redundant applet name prefix from error messages.
+ */
+
+#include "libbb.h"
+
+/* This is a NOFORK applet. Be very careful! */
+
+//usage:#define yes_trivial_usage
+//usage:       "[STRING]"
+//usage:#define yes_full_usage "\n\n"
+//usage:       "Repeatedly output a line with STRING, or 'y'"
+
+int yes_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int yes_main(int argc UNUSED_PARAM, char **argv)
+{
+	char **pp;
+
+	argv[0] = (char*)"y";
+	if (argv[1])
+		++argv;
+
+	do {
+		pp = argv;
+		while (1) {
+			fputs(*pp, stdout);
+			if (!*++pp)
+				break;
+			putchar(' ');
+		}
+	} while (putchar('\n') != EOF);
+
+	bb_perror_nomsg_and_die();
+}
diff --git a/busybox-1.19.3/debianutils/Config.src b/busybox-1.19.3/debianutils/Config.src
new file mode 100644
index 0000000..cbc09b5
--- /dev/null
+++ b/busybox-1.19.3/debianutils/Config.src
@@ -0,0 +1,85 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Debian Utilities"
+
+INSERT
+
+config MKTEMP
+	bool "mktemp"
+	default y
+	help
+	  mktemp is used to create unique temporary files
+
+config PIPE_PROGRESS
+	bool "pipe_progress"
+	default y
+	help
+	  Display a dot to indicate pipe activity.
+
+config RUN_PARTS
+	bool "run-parts"
+	default y
+	help
+	  run-parts is a utility designed to run all the scripts in a directory.
+
+	  It is useful to set up a directory like cron.daily, where you need to
+	  execute all the scripts in that directory.
+
+	  In this implementation of run-parts some features (such as report
+	  mode) are not implemented.
+
+	  Unless you know that run-parts is used in some of your scripts
+	  you can safely say N here.
+
+config FEATURE_RUN_PARTS_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on RUN_PARTS && LONG_OPTS
+	help
+	  Support long options for the run-parts applet.
+
+config FEATURE_RUN_PARTS_FANCY
+	bool "Support additional arguments"
+	default y
+	depends on RUN_PARTS
+	help
+	  Support additional options:
+	  -l --list print the names of the all matching files (not
+	            limited to executables), but don't actually run them.
+
+config START_STOP_DAEMON
+	bool "start-stop-daemon"
+	default y
+	help
+	  start-stop-daemon is used to control the creation and
+	  termination of system-level processes, usually the ones
+	  started during the startup of the system.
+
+config FEATURE_START_STOP_DAEMON_FANCY
+	bool "Support additional arguments"
+	default y
+	depends on START_STOP_DAEMON
+	help
+	  Support additional arguments.
+	  -o|--oknodo ignored since we exit with 0 anyway
+	  -v|--verbose
+	  -N|--nicelevel N
+
+config FEATURE_START_STOP_DAEMON_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on START_STOP_DAEMON && LONG_OPTS
+	help
+	  Support long options for the start-stop-daemon applet.
+
+config WHICH
+	bool "which"
+	default y
+	help
+	  which is used to find programs in your PATH and
+	  print out their pathnames.
+
+endmenu
diff --git a/busybox-1.19.3/debianutils/Kbuild.src b/busybox-1.19.3/debianutils/Kbuild.src
new file mode 100644
index 0000000..d41b5c8
--- /dev/null
+++ b/busybox-1.19.3/debianutils/Kbuild.src
@@ -0,0 +1,14 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_MKTEMP)            += mktemp.o
+lib-$(CONFIG_PIPE_PROGRESS)     += pipe_progress.o
+lib-$(CONFIG_RUN_PARTS)         += run_parts.o
+lib-$(CONFIG_START_STOP_DAEMON) += start_stop_daemon.o
+lib-$(CONFIG_WHICH)             += which.o
diff --git a/busybox-1.19.3/debianutils/mktemp.c b/busybox-1.19.3/debianutils/mktemp.c
new file mode 100644
index 0000000..007cb1c
--- /dev/null
+++ b/busybox-1.19.3/debianutils/mktemp.c
@@ -0,0 +1,98 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mktemp implementation for busybox
+ *
+ *
+ * Copyright (C) 2000 by Daniel Jacobowitz
+ * Written by Daniel Jacobowitz <dan@debian.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Coreutils 6.12 man page says:
+ *        mktemp [OPTION]... [TEMPLATE]
+ * Create a temporary file or directory, safely, and print its name. If
+ * TEMPLATE is not specified, use tmp.XXXXXXXXXX.
+ * -d, --directory
+ *        create a directory, not a file
+ * -q, --quiet
+ *        suppress diagnostics about file/dir-creation failure
+ * -u, --dry-run
+ *        do not create anything; merely print a name (unsafe)
+ * --tmpdir[=DIR]
+ *        interpret TEMPLATE relative to DIR. If DIR is not specified,
+ *        use  $TMPDIR if set, else /tmp.  With this option, TEMPLATE must
+ *        not be an absolute name. Unlike with -t, TEMPLATE may contain
+ *        slashes, but even here, mktemp still creates only the final com-
+ *        ponent.
+ * -p DIR use DIR as a prefix; implies -t [deprecated]
+ * -t     interpret TEMPLATE as a single file name component, relative  to
+ *        a  directory:  $TMPDIR, if set; else the directory specified via
+ *        -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" - we ignore this opt
+//usage:     "\n	-t	Prepend base directory name to TEMPLATE"
+//usage:     "\n	-p DIR	Use DIR as a base directory (implies -t)"
+//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"
+
+int mktemp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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,
+	};
+
+	path = getenv("TMPDIR");
+	if (!path || path[0] == '\0')
+		path = "/tmp";
+
+	/* -q is ignored */
+	opt_complementary = "?1"; /* 1 argument max */
+	opts = getopt32(argv, "dqtp:", &path);
+
+	chp = argv[optind];
+	if (!chp) {
+		/* GNU coreutils 8.4:
+		 * bare "mktemp" -> "mktemp -t tmp.XXXXXX"
+		 */
+		chp = xstrdup("tmp.XXXXXX");
+		opts |= OPT_t;
+	}
+	if (opts & (OPT_t|OPT_p))
+		chp = concat_path_file(path, chp);
+
+	if (opts & OPT_d) {
+		if (mkdtemp(chp) == NULL)
+			return EXIT_FAILURE;
+	} else {
+		if (mkstemp(chp) < 0)
+			return EXIT_FAILURE;
+	}
+
+	puts(chp);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/debianutils/pipe_progress.c b/busybox-1.19.3/debianutils/pipe_progress.c
new file mode 100644
index 0000000..2c7444f
--- /dev/null
+++ b/busybox-1.19.3/debianutils/pipe_progress.c
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Monitor a pipe with a simple progress display.
+ *
+ * Copyright (C) 2003 by Rob Landley <rob@landley.net>, Joey Hess
+ *
+ * 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
+
+/* Read a block of data from stdin, write it to stdout.
+ * Activity is indicated by a '.' to stderr
+ */
+int pipe_progress_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pipe_progress_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	char buf[PIPE_PROGRESS_SIZE];
+	time_t t = time(NULL);
+	int len;
+
+	while ((len = safe_read(STDIN_FILENO, buf, PIPE_PROGRESS_SIZE)) > 0) {
+		time_t new_time = time(NULL);
+		if (new_time != t) {
+			t = new_time;
+			bb_putchar_stderr('.');
+		}
+		full_write(STDOUT_FILENO, buf, len);
+	}
+
+	bb_putchar_stderr('\n');
+
+	return 0;
+}
diff --git a/busybox-1.19.3/debianutils/run_parts.c b/busybox-1.19.3/debianutils/run_parts.c
new file mode 100644
index 0000000..65cbfc3
--- /dev/null
+++ b/busybox-1.19.3/debianutils/run_parts.c
@@ -0,0 +1,197 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini run-parts implementation for busybox
+ *
+ * Copyright (C) 2007 Bernhard Reutner-Fischer
+ *
+ * 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 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 a 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:       "[-t] "IF_FEATURE_RUN_PARTS_FANCY("[-l] ")"[-a ARG] [-u MASK] DIRECTORY"
+//usage:#define run_parts_full_usage "\n\n"
+//usage:       "Run a bunch of scripts in DIRECTORY\n"
+//usage:     "\n	-t	Print what would be run, but don't actually run anything"
+//usage:     "\n	-a ARG	Pass ARG as argument for every program"
+//usage:     "\n	-u MASK	Set the umask to MASK before running every program"
+//usage:	IF_FEATURE_RUN_PARTS_FANCY(
+//usage:     "\n	-l	Print names of all matching files even if they are not executable"
+//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];
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define names (G.names)
+#define cur   (G.cur  )
+#define cmd   (G.cmd  )
+
+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,
+};
+
+#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?)
+ */
+static bool invalid_name(const char *c)
+{
+	c = bb_basename(c);
+
+	while (*c && (isalnum(*c) || *c == '_' || *c == '-'))
+		c++;
+
+	return *c; /* TRUE (!0) if terminating NUL is not reached */
+}
+
+static int bb_alphasort(const void *p1, const void *p2)
+{
+	int r = strcmp(*(char **) p1, *(char **) p2);
+	return (option_mask32 & OPT_r) ? -r : r;
+}
+
+static int FAST_FUNC act(const char *file, struct stat *statbuf, void *args UNUSED_PARAM, int depth)
+{
+	if (depth == 1)
+		return TRUE;
+
+	if (depth == 2
+	 && (  !(statbuf->st_mode & (S_IFREG | S_IFLNK))
+	    || invalid_name(file)
+	    || (!list_mode && access(file, X_OK) != 0))
+	) {
+		return SKIP;
+	}
+
+	names = xrealloc_vector(names, 4, cur);
+	names[cur++] = xstrdup(file);
+	/*names[cur] = NULL; - xrealloc_vector did it */
+
+	return TRUE;
+}
+
+#if ENABLE_FEATURE_RUN_PARTS_LONG_OPTIONS
+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"
+#endif
+	;
+#endif
+
+int run_parts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int run_parts_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *umask_p = "22";
+	llist_t *arg_list = NULL;
+	unsigned n;
+	int ret;
+
+#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);
+
+	umask(xstrtou_range(umask_p, 8, 0, 07777));
+
+	n = 1;
+	while (arg_list && n < NUM_CMD) {
+		cmd[n++] = llist_pop(&arg_list);
+	}
+	/* cmd[n] = NULL; - is already zeroed out */
+
+	/* run-parts has to sort executables by name before running them */
+
+	recursive_action(argv[optind],
+			ACTION_RECURSE|ACTION_FOLLOWLINKS,
+			act,            /* file action */
+			act,            /* dir action */
+			NULL,           /* user data */
+			1               /* depth */
+		);
+
+	if (!names)
+		return 0;
+
+	qsort(names, cur, sizeof(char *), bb_alphasort);
+
+	n = 0;
+	while (1) {
+		char *name = *names++;
+		if (!name)
+			break;
+		if (option_mask32 & (OPT_t | OPT_l)) {
+			puts(name);
+			continue;
+		}
+		cmd[0] = name;
+		ret = spawn_and_wait(cmd);
+		if (ret == 0)
+			continue;
+		n = 1;
+		if (ret < 0)
+			bb_perror_msg("can't execute '%s'", name);
+		else /* ret > 0 */
+			bb_error_msg("%s exited with code %d", name, ret & 0xff);
+	}
+
+	return n;
+}
diff --git a/busybox-1.19.3/debianutils/start_stop_daemon.c b/busybox-1.19.3/debianutils/start_stop_daemon.c
new file mode 100644
index 0000000..bc61959
--- /dev/null
+++ b/busybox-1.19.3/debianutils/start_stop_daemon.c
@@ -0,0 +1,519 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini start-stop-daemon implementation(s) for busybox
+ *
+ * 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 source tree.
+ */
+
+/*
+This is how it is supposed to work:
+
+start-stop-daemon [OPTIONS] [--start|--stop] [[--] arguments...]
+
+One (only) of these must be given:
+        -S,--start              Start
+        -K,--stop               Stop
+
+Search for matching processes.
+If --stop is given, stop all matching processes (by sending a signal).
+If --start is given, start a new process unless a matching process was found.
+
+Options controlling process matching
+(if multiple conditions are specified, all must match):
+        -u,--user USERNAME|UID  Only consider this user's processes
+        -n,--name PROCESS_NAME  Look for processes by matching PROCESS_NAME
+                                with comm field in /proc/$PID/stat.
+                                Only basename is compared:
+                                "ntpd" == "./ntpd" == "/path/to/ntpd".
+[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.
+                                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
+
+Options which are valid for --start only:
+        -x,--exec EXECUTABLE    Program to run (1st arg of execvp). Mandatory.
+        -a,--startas NAME       argv[0] (defaults to EXECUTABLE)
+        -b,--background         Put process into background
+        -N,--nicelevel N        Add N to process' nice level
+        -c,--chuid USER[:[GRP]] Change to specified user [and group]
+        -m,--make-pidfile       Write PID to the pidfile
+                                (both -m and -p must be given!)
+
+Options which are valid for --stop only:
+        -s,--signal SIG         Signal to send (default:TERM)
+        -t,--test               Exit with status 0 if process is found
+                                (we don't actually start or stop daemons)
+
+Misc options:
+        -o,--oknodo             Exit with status 0 if nothing is done
+        -q,--quiet              Quiet
+        -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/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 */
+#define WANT_PIDFILE 1
+#include "libbb.h"
+
+struct pid_list {
+	struct pid_list *next;
+	pid_t pid;
+};
+
+enum {
+	CTX_STOP       = (1 <<  0),
+	CTX_START      = (1 <<  1),
+	OPT_BACKGROUND = (1 <<  2), // -b
+	OPT_QUIET      = (1 <<  3), // -q
+	OPT_TEST       = (1 <<  4), // -t
+	OPT_MAKEPID    = (1 <<  5), // -m
+	OPT_a          = (1 <<  6), // -a
+	OPT_n          = (1 <<  7), // -n
+	OPT_s          = (1 <<  8), // -s
+	OPT_u          = (1 <<  9), // -u
+	OPT_c          = (1 << 10), // -c
+	OPT_x          = (1 << 11), // -x
+	OPT_p          = (1 << 12), // -p
+	OPT_OKNODO     = (1 << 13) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -o
+	OPT_VERBOSE    = (1 << 14) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -v
+	OPT_NICELEVEL  = (1 << 15) * ENABLE_FEATURE_START_STOP_DAEMON_FANCY, // -N
+};
+#define QUIET (option_mask32 & OPT_QUIET)
+#define TEST  (option_mask32 & OPT_TEST)
+
+struct globals {
+	struct pid_list *found_procs;
+	char *userspec;
+	char *cmdname;
+	char *execname;
+	char *pidfile;
+	char *execname_cmpbuf;
+	unsigned execname_sizeof;
+	int user_id;
+	smallint signal_nr;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define userspec          (G.userspec            )
+#define cmdname           (G.cmdname             )
+#define execname          (G.execname            )
+#define pidfile           (G.pidfile             )
+#define user_id           (G.user_id             )
+#define signal_nr         (G.signal_nr           )
+#define INIT_G() do { \
+	user_id = -1; \
+	signal_nr = 15; \
+} while (0)
+
+#ifdef OLDER_VERSION_OF_X
+/* -x,--exec EXECUTABLE
+ * Look for processes with matching /proc/$PID/exe.
+ * Match is performed using device+inode.
+ */
+static int pid_is_exec(pid_t pid)
+{
+	struct stat st;
+	char buf[sizeof("/proc/%u/exe") + sizeof(int)*3];
+
+	sprintf(buf, "/proc/%u/exe", (unsigned)pid);
+	if (stat(buf, &st) < 0)
+		return 0;
+	if (st.st_dev == execstat.st_dev
+	 && st.st_ino == execstat.st_ino)
+		return 1;
+	return 0;
+}
+#endif
+
+static int pid_is_exec(pid_t pid)
+{
+	ssize_t bytes;
+	char buf[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
+
+	sprintf(buf, "/proc/%u/cmdline", (unsigned)pid);
+	bytes = open_read_close(buf, G.execname_cmpbuf, G.execname_sizeof);
+	if (bytes > 0) {
+		G.execname_cmpbuf[bytes] = '\0';
+		return strcmp(execname, G.execname_cmpbuf) == 0;
+	}
+	return 0;
+}
+
+static int pid_is_name(pid_t pid)
+{
+	/* /proc/PID/stat is "PID (comm_15_bytes_max) ..." */
+	char buf[32]; /* should be enough */
+	char *p, *pe;
+
+	sprintf(buf, "/proc/%u/stat", (unsigned)pid);
+	if (open_read_close(buf, buf, sizeof(buf) - 1) < 0)
+		return 0;
+	buf[sizeof(buf) - 1] = '\0'; /* paranoia */
+	p = strchr(buf, '(');
+	if (!p)
+		return 0;
+	pe = strrchr(++p, ')');
+	if (!pe)
+		return 0;
+	*pe = '\0';
+	/* we require comm to match and to not be truncated */
+	/* in Linux, if comm is 15 chars, it may be a truncated
+	 * name, so we don't allow that to match */
+	if (strlen(p) >= COMM_LEN - 1) /* COMM_LEN is 16 */
+		return 0;
+	return strcmp(p, cmdname) == 0;
+}
+
+static int pid_is_user(int pid)
+{
+	struct stat sb;
+	char buf[sizeof("/proc/") + sizeof(int)*3];
+
+	sprintf(buf, "/proc/%u", (unsigned)pid);
+	if (stat(buf, &sb) != 0)
+		return 0;
+	return (sb.st_uid == (uid_t)user_id);
+}
+
+static void check(int pid)
+{
+	struct pid_list *p;
+
+	if (execname && !pid_is_exec(pid)) {
+		return;
+	}
+	if (cmdname && !pid_is_name(pid)) {
+		return;
+	}
+	if (userspec && !pid_is_user(pid)) {
+		return;
+	}
+	p = xmalloc(sizeof(*p));
+	p->next = G.found_procs;
+	p->pid = pid;
+	G.found_procs = p;
+}
+
+static void do_pidfile(void)
+{
+	FILE *f;
+	unsigned pid;
+
+	f = fopen_for_read(pidfile);
+	if (f) {
+		if (fscanf(f, "%u", &pid) == 1)
+			check(pid);
+		fclose(f);
+	} else if (errno != ENOENT)
+		bb_perror_msg_and_die("open pidfile %s", pidfile);
+}
+
+static void do_procinit(void)
+{
+	DIR *procdir;
+	struct dirent *entry;
+	int pid;
+
+	if (pidfile) {
+		do_pidfile();
+		return;
+	}
+
+	procdir = xopendir("/proc");
+
+	pid = 0;
+	while (1) {
+		errno = 0; /* clear any previous error */
+		entry = readdir(procdir);
+// TODO: this check is too generic, it's better
+// to check for exact errno(s) which mean that we got stale entry
+		if (errno) /* Stale entry, process has died after opendir */
+			continue;
+		if (!entry) /* EOF, no more entries */
+			break;
+		pid = bb_strtou(entry->d_name, NULL, 10);
+		if (errno) /* NaN */
+			continue;
+		check(pid);
+	}
+	closedir(procdir);
+	if (!pid)
+		bb_error_msg_and_die("nothing in /proc - not mounted?");
+}
+
+static int do_stop(void)
+{
+	char *what;
+	struct pid_list *p;
+	int killed = 0;
+
+	if (cmdname) {
+		if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(cmdname);
+		if (!ENABLE_FEATURE_CLEAN_UP) what = cmdname;
+	} else if (execname) {
+		if (ENABLE_FEATURE_CLEAN_UP) what = xstrdup(execname);
+		if (!ENABLE_FEATURE_CLEAN_UP) what = execname;
+	} else if (pidfile) {
+		what = xasprintf("process in pidfile '%s'", pidfile);
+	} else if (userspec) {
+		what = xasprintf("process(es) owned by '%s'", userspec);
+	} else {
+		bb_error_msg_and_die("internal error, please report");
+	}
+
+	if (!G.found_procs) {
+		if (!QUIET)
+			printf("no %s found; none killed\n", what);
+		killed = -1;
+		goto ret;
+	}
+	for (p = G.found_procs; p; p = p->next) {
+		if (kill(p->pid, TEST ? 0 : signal_nr) == 0) {
+			killed++;
+		} else {
+			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) {
+		printf("stopped %s (pid", what);
+		for (p = G.found_procs; p; p = p->next)
+			if (p->pid)
+				printf(" %u", (unsigned)p->pid);
+		puts(")");
+	}
+ ret:
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(what);
+	return killed;
+}
+
+#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
+static const char start_stop_daemon_longopts[] ALIGN1 =
+	"stop\0"         No_argument       "K"
+	"start\0"        No_argument       "S"
+	"background\0"   No_argument       "b"
+	"quiet\0"        No_argument       "q"
+	"test\0"         No_argument       "t"
+	"make-pidfile\0" No_argument       "m"
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+	"oknodo\0"       No_argument       "o"
+	"verbose\0"      No_argument       "v"
+	"nicelevel\0"    Required_argument "N"
+#endif
+	"startas\0"      Required_argument "a"
+	"name\0"         Required_argument "n"
+	"signal\0"       Required_argument "s"
+	"user\0"         Required_argument "u"
+	"chuid\0"        Required_argument "c"
+	"exec\0"         Required_argument "x"
+	"pidfile\0"      Required_argument "p"
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+	"retry\0"        Required_argument "R"
+#endif
+	;
+#endif
+
+int start_stop_daemon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int start_stop_daemon_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	char *signame;
+	char *startas;
+	char *chuid;
+#ifdef OLDER_VERSION_OF_X
+	struct stat execstat;
+#endif
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+//	char *retry_arg = NULL;
+//	int retries = -1;
+	char *opt_N;
+#endif
+
+	INIT_G();
+
+#if ENABLE_FEATURE_START_STOP_DAEMON_LONG_OPTIONS
+	applet_long_options = start_stop_daemon_longopts;
+#endif
+
+	/* -K or -S is required; they are mutually exclusive */
+	/* -p is required if -m is given */
+	/* -xpun (at least one) is required if -K is given */
+	/* -xa (at least one) is required if -S is given */
+	/* -q turns off -v */
+	opt_complementary = "K:S:K--S:S--K:m?p:K?xpun:S?xa"
+		IF_FEATURE_START_STOP_DAEMON_FANCY("q-v");
+	opt = getopt32(argv, "KSbqtma:n:s:u:c:x:p:"
+		IF_FEATURE_START_STOP_DAEMON_FANCY("ovN:R:"),
+		&startas, &cmdname, &signame, &userspec, &chuid, &execname, &pidfile
+		IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
+		/* We accept and ignore -R <param> / --retry <param> */
+		IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL)
+	);
+
+	if (opt & OPT_s) {
+		signal_nr = get_signum(signame);
+		if (signal_nr < 0) bb_show_usage();
+	}
+
+	if (!(opt & OPT_a))
+		startas = execname;
+	if (!execname) /* in case -a is given and -x is not */
+		execname = startas;
+	if (execname) {
+		G.execname_sizeof = strlen(execname) + 1;
+		G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1);
+	}
+
+//	IF_FEATURE_START_STOP_DAEMON_FANCY(
+//		if (retry_arg)
+//			retries = xatoi_positive(retry_arg);
+//	)
+	//argc -= optind;
+	argv += optind;
+
+	if (userspec) {
+		user_id = bb_strtou(userspec, NULL, 10);
+		if (errno)
+			user_id = xuname2uid(userspec);
+	}
+	/* Both start and stop need to know current processes */
+	do_procinit();
+
+	if (opt & CTX_STOP) {
+		int i = do_stop();
+		return (opt & OPT_OKNODO) ? 0 : (i <= 0);
+	}
+
+	if (G.found_procs) {
+		if (!QUIET)
+			printf("%s is already running\n%u\n", execname, (unsigned)G.found_procs->pid);
+		return !(opt & OPT_OKNODO);
+	}
+
+#ifdef OLDER_VERSION_OF_X
+	if (execname)
+		xstat(execname, &execstat);
+#endif
+
+	*--argv = startas;
+	if (opt & OPT_BACKGROUND) {
+#if BB_MMU
+		bb_daemonize(DAEMON_DEVNULL_STDIO + DAEMON_CLOSE_EXTRA_FDS);
+		/* DAEMON_DEVNULL_STDIO is superfluous -
+		 * it's always done by bb_daemonize() */
+#else
+		pid_t pid = xvfork();
+		if (pid != 0) {
+			/* parent */
+			/* why _exit? the child may have changed the stack,
+			 * so "return 0" may do bad things */
+			_exit(EXIT_SUCCESS);
+		}
+		/* Child */
+		setsid(); /* detach from controlling tty */
+		/* Redirect stdio to /dev/null, close extra FDs.
+		 * We do not actually daemonize because of DAEMON_ONLY_SANITIZE */
+		bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO
+			+ DAEMON_CLOSE_EXTRA_FDS
+			+ DAEMON_ONLY_SANITIZE,
+			NULL /* argv, unused */ );
+#endif
+	}
+	if (opt & OPT_MAKEPID) {
+		/* User wants _us_ to make the pidfile */
+		write_pidfile(pidfile);
+	}
+	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 ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+	if (opt & OPT_NICELEVEL) {
+		/* Set process priority */
+		int prio = getpriority(PRIO_PROCESS, 0) + xatoi_range(opt_N, INT_MIN/2, INT_MAX/2);
+		if (setpriority(PRIO_PROCESS, 0, prio) < 0) {
+			bb_perror_msg_and_die("setpriority(%d)", prio);
+		}
+	}
+#endif
+	execvp(startas, argv);
+	bb_perror_msg_and_die("can't execute '%s'", startas);
+}
diff --git a/busybox-1.19.3/debianutils/which.c b/busybox-1.19.3/debianutils/which.c
new file mode 100644
index 0000000..15fd598
--- /dev/null
+++ b/busybox-1.19.3/debianutils/which.c
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Which implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * 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;
+int which_main(int argc UNUSED_PARAM, char **argv)
+{
+	IF_DESKTOP(int opt;)
+	int status = EXIT_SUCCESS;
+	char *path;
+	char *p;
+
+	opt_complementary = "-1"; /* at least one argument */
+	IF_DESKTOP(opt =) getopt32(argv, "a");
+	argv += optind;
+
+	/* This matches what is seen on e.g. ubuntu.
+	 * "which" there is a shell script. */
+	path = getenv("PATH");
+	if (!path) {
+		path = (char*)bb_PATH_root_path;
+		putenv(path);
+		path += 5; /* skip "PATH=" */
+	}
+
+	do {
+#if ENABLE_DESKTOP
+/* Much bloat just to support -a */
+		if (strchr(*argv, '/')) {
+			if (execable_file(*argv)) {
+				puts(*argv);
+				continue;
+			}
+			status = EXIT_FAILURE;
+		} else {
+			char *path2 = xstrdup(path);
+			char *tmp = path2;
+
+			p = find_execable(*argv, &tmp);
+			if (!p)
+				status = EXIT_FAILURE;
+			else {
+ print:
+				puts(p);
+				free(p);
+				if (opt) {
+					/* -a: show matches in all PATH components */
+					if (tmp) {
+						p = find_execable(*argv, &tmp);
+						if (p)
+							goto print;
+					}
+				}
+			}
+			free(path2);
+		}
+#else
+/* Just ignoring -a */
+		if (strchr(*argv, '/')) {
+			if (execable_file(*argv)) {
+				puts(*argv);
+				continue;
+			}
+		} else {
+			char *path2 = xstrdup(path);
+			char *tmp = path2;
+			p = find_execable(*argv, &tmp);
+			free(path2);
+			if (p) {
+				puts(p);
+				free(p);
+				continue;
+			}
+		}
+		status = EXIT_FAILURE;
+#endif
+	} while (*(++argv) != NULL);
+
+	fflush_stdout_and_exit(status);
+}
diff --git a/busybox-1.19.3/docs/Serial-Programming-HOWTO.txt b/busybox-1.19.3/docs/Serial-Programming-HOWTO.txt
new file mode 100644
index 0000000..8a3954b
--- /dev/null
+++ b/busybox-1.19.3/docs/Serial-Programming-HOWTO.txt
@@ -0,0 +1,424 @@
+Downloaded from http://www.lafn.org/~dave/linux/Serial-Programming-HOWTO.txt
+Seems to be somewhat old, but contains useful bits for getty.c hacking
+============================================================================
+
+  The Linux Serial Programming HOWTO, Part 1 of 2
+  By Vernon C. Hoxie
+  v2.0 10 September 1999
+
+  This document describes how to program communications with devices
+  over a serial port on a Linux box.
+  ______________________________________________________________________
+
+  Table of Contents
+
+  1. Copyright
+
+  2. Introduction
+
+  3. Opening
+
+  4. Commands
+
+  5. Changing Baud Rates
+
+  6. Additional Control Calls
+
+     6.1 Sending a "break".
+     6.2 Hardware flow control.
+     6.3 Flushing I/O buffers.
+
+  7. Modem control
+
+  8. Process Groups
+
+     8.1 Sessions
+     8.2 Process Groups
+     8.3 Controlling Terminal
+        8.3.1 Get the foreground group process id.
+        8.3.2 Set the foreground process group id of a terminal.
+        8.3.3 Get process group id.
+
+  9. Lockfiles
+
+  10. Additional Information
+
+  11. Feedback
+
+  ______________________________________________________________________
+
+  1.  Copyright
+
+  The Linux Serial-Programming-HOWTO is copyright (C) 1997 by Vernon
+  Hoxie.  Linux HOWTO documents may be reproduced and distributed in
+  whole or in part, in any medium physical or electronic, as long as
+  this copyright notice is retained on all copies. Commercial
+  redistribution is allowed and encouraged; however, the author would
+  like to be notified of any such distributions.
+
+  All translations, derivative works, or aggregate works incorporating
+  this Linux HOWTO document must be covered under this copyright notice.
+  That is, you may not produce a derivative work from this HOWTO and
+  impose additional restrictions on its distribution.
+
+  This version is a complete rewrite of the previous Serial-Programming-
+  HOWTO  by Peter H. Baumann,  <mailto:Peter.Baumann@dlr.de>
+
+  2.  Introduction
+
+  This HOWTO will attempt to give hints about how to write a program
+  which needs to access a serial port.  Its principal focus will be on
+  the Linux implementation and what the meaning of the various library
+  functions available.
+
+  Someone asked about which of several sequences of operations was
+  right.  There is no absolute right way to accomplish an outcome.  The
+  options available are too numerous.  If your sequences produces the
+  desired results, then that is the right way for you.  Another
+  programmer may select another set of options and get the same results.
+  His method is right for him.
+
+  Neither of these methods may operate properly with some other
+  implementation of UNIX.  It is strange that many of the concepts which
+  were implemented in the SYSV version have been dumped.  Because UNIX
+  was developed by AT&T and much code has been generated on those
+  concepts, the AT&T version should be the standard to which others
+  should emulate.
+
+  Now the standard is POSIX.
+
+  It was once stated that the popularity of UNIX and C was that they
+  were created by programmers for programmers.  Not by scholars who
+  insist on purity of style in deference to results and simplicity of
+  use.  Not by committees with people who have diverse personal or
+  proprietary agenda.  Now ANSI and POSIX have strayed from those
+  original clear and simply concepts.
+
+  3.  Opening
+
+  The various serial devices are opened just as any other file.
+  Although, the fopen(3) command may be used, the plain open(2) is
+  preferred.  This call returns the file descriptor which is required
+  for the various commands that configure the interface.
+
+  Open(2) has the format:
+
+       #include <fcntl.h>
+       int open(char *path, int flags, [int mode]);
+
+  In addition to the obvious O_RDWR, O_WRONLY and O_RDONLY, two
+  additional flags are available.  These are O_NONBLOCK and O_NOCTTY.
+  Other flags listed in the open(2) manual page are not applicable to
+  serial devices.
+
+  Normally, a serial device opens in "blocking" mode.  This means that
+  the open() will not return until the Carrier Detect line from the port
+  is active, e.g. modem, is active.  When opened with the O_NONBLOCK
+  flag set, the open() will return immediately regardless of the status
+  of the DCD line.  The "blocking" mode also affects the read() call.
+
+  The fcntl(2) command can be used to change the O_NONBLOCK flag anytime
+  after the device has been opened.
+
+  The device driver and the data passing through it are controlled
+  according to settings in the struct termios.  This structure is
+  defined in "/usr/include/termios.h".  In the Linux tree, further
+  reference is made to "/usr/include/asm/termbits.h".
+  In blocking mode, a read(2) will block until data is available or a
+  signal is received.  It is still subject to state of the ICANON flag.
+
+  When the termios.c_lflag ICANON bit is set, input data is collected
+  into strings until a NL, EOF or EOL character is received.  You can
+  define these in the termios.c_cc[] array.  Also, ERASE and KILL
+  characters will operate on the incoming data before it is delivered to
+  the user.
+
+  In non-canonical mode, incoming data is quantified by use of the
+  c_cc[VMIN and c_cc[VTIME] values in termios.c_cc[].
+
+  Some programmers use the select() call to detect the completion of a
+  read().  This is not the best way of checking for incoming data.
+  Select() is part of the SOCKETS scheme and too complex for most
+  applications.
+
+  A full explanation of the fields of the termios structure is contained
+  in termios(7) of the Users Manual.  A version is included in Part 2 of
+  this HOWTO document.
+
+  4.  Commands
+
+  Changes to the struct termios are made by retrieving the current
+  settings, making the desired changes and transmitting the modified
+  structure back to the kernel.
+
+  The historic means of communicating with the kernel was by use of the
+  ioctl(fd, COMMAND, arg) system call.  Then the purists in the
+  computer industry decided that this was not genetically consistent.
+  Their argument was that the argument changed its stripes.  Sometimes
+  it was an int, sometimes it was a pointer to int and other times it
+  was a pointer to struct termios.  Then there were those times it was
+  empty or NULL.  These variations are dependent upon the COMMAND.
+
+  As a alternative, the tc* series of functions were concocted.
+
+  These are:
+
+       int tcgetattr(int filedes, struct termios *termios_p);
+       int tcsetattr(int filedes, int optional_actions,
+                     const struct termios *termios_p);
+
+  instead of:
+
+       int ioctl(int filedes, int command,
+                 struct termios *termios_p);
+
+  where command is TCGETS or one of TCSETS, TCSETSW or TCSETSF.
+
+  The TCSETS command is comparable to the TCSANOW optional_action for
+  the tc* version.  These direct the kernel to adopt the changes
+  immediately.  Other pairs are:
+
+    command   optional_action   Meaning
+    TCSETSW   TCSADRAIN         Change after all output has drained.
+    TCSETSF   TCSAFLUSH         Change after all output has drained
+                                then discard any input characters
+                                not read.
+
+  Since the return code from either the ioctl(2) or the tcsetattr(2)
+  commands only indicate that the command was processed by the kernel.
+  These do not indicate whether or not the changes were actually
+  accomplished.  Either of these commands should be followed by a call
+  to:
+
+       ioctl(fd, TCGETS, &new_termios);
+
+  or:
+
+       tcgetattr(fd, &new_termios);
+
+  A user function which makes changes to the termios structure should
+  define two struct termios variables.  One of these variables should
+  contain the desired configuration.  The other should contain a copy of
+  the kernels version.  Then after the desired configuration has been
+  sent to the kernel, another call should be made to retrieve the
+  kernels version.  Then the two compared.
+
+  Here is an example of how to add RTS/CTS flow control:
+
+       struct termios my_termios;
+       struct termios new_termios;
+
+       tcgetattr(fd, &my_termios);
+       my_termios.c_flag |= CRTSCTS;
+       tcsetattr(fd, TCSANOW, &my_termios);
+       tcgetattr(fd, &new_termios);
+       if (memcmp(my_termios, new_termios,
+            sizeof(my_termios)) != 0) {
+           /* do some error handling */
+       }
+
+  5.  Changing Baud Rates
+
+  With Linux, the baud rate can be changed using a technique similar to
+  add/delete RTS/CTS.
+
+  struct termios my_termios;
+  struct termios new_termios;
+
+  tcgetattr(fd, &my_termios);
+  my_termios.c_flag &= ~CBAUD;
+  my_termios.c_flag |= B19200;
+  tcsetattr(fd, TCSANOW, &my_termios);
+  tcgetattr(fd, &new_termios);
+  if (memcmp(my_termios, new_termios,
+       sizeof(my_termios)) != 0) {
+      /* do some error handling */
+  }
+
+  POSIX adds another method.  They define:
+
+       speed_t cfgetispeed(const struct termios *termios_p);
+       speed_t cfgetospeed(const struct termios *termios_p);
+
+  library calls to extract the current input or output speed from the
+  struct termios pointed to with *termio_p.  This is a variable defined
+  in the calling process.  In practice, the data contained in this
+  termios, should be obtained by the tcgetattr() call or an ioctl() call
+  using the TCGETS command.
+
+  The companion library calls are:
+
+       int cfsetispeed(struct termios *termios_p, speed_t speed);
+       int cfsetospeed(struct termios *termios_p, speed_t speed);
+
+  which are used to change the value of the baud rate in the locally
+  defined *termios_p.  Following either of these calls, either a call to
+  tcsetattr() or ioctl() with one of TCSETS, TCSETSW or TCSETSF as the
+  command to transmit the change to the kernel.
+
+  The cf* commands are preferred for portability.  Some weird Unices use
+  a considerably different format of termios.
+
+  Most implementations of Linux use only the input speed for both input
+  and output.  These functions are defined in the application program by
+  reference to <termios.h>.  In reality, they are in
+  /usr/include/asm/termbits.h.
+
+  6.  Additional Control Calls
+
+  6.1.  Sending a "break".
+
+       int ioctl(fd, TCSBRK, int arg);
+       int tcsendbreak(fd, int arg);
+
+  Send a break:  Here the action differs between the conventional
+  ioctl() call and the POSIX call.  For the conventional call, an arg of
+  '0' sets the break control line of the UART for 0.25 seconds.  For the
+  POSIX command, the break line is set for arg times 0.1 seconds.
+
+  6.2.  Hardware flow control.
+
+       int ioctl(fd, TCXONC, int action);
+       int tcflow(fd, int action);
+
+  The action flags are:
+
+  o  TCOOFF  0  suspend output
+
+  o  TCOON   1  restart output
+
+  o  TCIOFF  2  transmit STOP character to suspend input
+
+  o  TCION   3  transmit START character to restart input
+
+  6.3.  Flushing I/O buffers.
+
+       int ioctl(fd, TCFLSH, queue_selector);
+       int tcflush(fd, queue_selector);
+
+  The queue_selector flags are:
+
+  o  TCIFLUSH  0  flush any data not yet read from the input buffer
+
+  o  TCOFLUSH  1  flush any data written to the output buffer but not
+     yet transmitted
+
+  o  TCIOFLUSH 2  flush both buffers
+
+  7.  Modem control
+
+  The hardware modem control lines can be monitored or modified by the
+  ioctl(2) system call.  A set of comparable tc* calls apparently do not
+  exist.  The form of this call is:
+
+       int ioctl(fd, COMMAND, (int *)flags);
+
+  The COMMANDS and their action are:
+
+  o  TIOCMBIS  turn on control lines depending upon which bits are set
+     in flags.
+
+  o  TIOCMBIC  turn off control lines depending upon which bits are
+     unset in flags.
+  o  TIOCMGET  the appropriate bits are set in flags according to the
+     current status
+
+  o  TIOCMSET  the state of the UART is changed according to which bits
+     are set/unset in 'flags'
+
+     The bit pattern of flags refer to the following control lines:
+
+  o  TIOCM_LE      Line enable
+
+  o  TIOCM_DTR     Data Terminal Ready
+
+  o  TIOCM_RTS     Request to send
+
+  o  TIOCM_ST      Secondary transmit
+
+  o  TIOCM_SR      Secondary receive
+
+  o  TIOCM_CTS     Clear to send
+
+  o  TIOCM_CAR     Carrier detect
+
+  o  TIOCM_RNG     Ring
+
+  o  TIOCM_DSR     Data set ready
+
+  It should be noted that some of these bits are controlled by the modem
+  and the UART cannot change them but their status can be sensed by
+  TIOCMGET.  Also, most Personal Computers do not provide hardware for
+  secondary transmit and receive.
+
+  There are also a pair of ioctl() to monitor these lines.  They are
+  undocumented as far as I have learned.  The commands are TIOCMIWAIT
+  and TCIOGICOUNT.  They also differ between versions of the Linux
+  kernel.
+
+  See the lines.c file in my "serial_suite" for an example of how these
+  can be used see  <ftp://scicom.alphacd.com/pub/linux/serial_suite>
+
+  8.  Process Groups
+
+  8.1.  Sessions
+
+  8.2.  Process Groups
+
+  Any newly created process inherits the Process Group of its creator.
+  The Process Group leader has the same PID as PGID.
+
+  8.3.  Controlling Terminal
+
+  There are a series of ioctl(2) and tc*(2) calls which can be used to
+  monitor or to change the process group to which the device is
+  attached.
+
+  8.3.1.  Get the foreground group process id.
+
+  If there is no foreground group, a number not representing an existing
+  process group is returned.  On error, a -1 is returned and errno is
+  set.
+
+       int ioctl(fd, TIOCGPGRP, (pid_t *)pid);
+       int tcgetpgrp(fd, (pid_t *)pid);
+
+  8.3.2.  Set the foreground process group id of a terminal.
+
+  The fd must be the controlling terminal and be associated with the
+  session of the calling process.
+
+       int ioctl(fd, TIOCSPGRP, (pid_t *)pid);
+       int tcsetpgrp(fd, (pid_t *)pid);
+
+  8.3.3.  Get process group id.
+
+       int ioctl(fd, TIOCGPGRP, &(pid_t)pid);
+       int tcgetpgrp(fd, &(pid_t)pid);
+
+  9.  Lockfiles
+
+  Any process which accesses a serial device should first check for the
+  existence of lock file for the desired device.  If such a lock lock
+  file exists, this means that the device may be in use by another
+  process.
+
+  Check my "libdevlocks-x.x.tgz" at
+  <ftp://scicom.alphacdc.com/pub/linux> for an example of how these lock
+  files should be utilized.
+
+  10.  Additional Information
+
+  Check out my "serial_suite.tgz" for more information about programming
+  the serial ports at   <mailto:vern@zebra.alphacdc.com>.  There some
+  examples and some blurbs about setting up modems and comments about
+  some general considerations.
+
+  11.  Feedback
+
+  Please send me any corrections, questions, comments, suggestions, or
+  additional material. I would like to improve this HOWTO!  Tell me
+  exactly what you don't understand, or what could be clearer.  You can
+  reach me at  <mailto:vern@zebra.alphacdc.com> via email.  Please
+  include the version number of the Serial-Programming-HOWTO when
+  writing.
diff --git a/busybox-1.19.3/docs/busybox_footer.pod b/busybox-1.19.3/docs/busybox_footer.pod
new file mode 100644
index 0000000..c346c73
--- /dev/null
+++ b/busybox-1.19.3/docs/busybox_footer.pod
@@ -0,0 +1,285 @@
+=back
+
+=head1 LIBC NSS
+
+GNU Libc (glibc) uses the Name Service Switch (NSS) to configure the behavior
+of the C library for the local environment, and to configure how it reads
+system data, such as passwords and group information.  This is implemented
+using an /etc/nsswitch.conf configuration file, and using one or more of the
+/lib/libnss_* libraries.  BusyBox tries to avoid using any libc calls that make
+use of NSS.  Some applets however, such as login and su, will use libc functions
+that require NSS.
+
+If you enable CONFIG_USE_BB_PWD_GRP, BusyBox will use internal functions to
+directly access the /etc/passwd, /etc/group, and /etc/shadow files without
+using NSS.  This may allow you to run your system without the need for
+installing any of the NSS configuration files and libraries.
+
+When used with glibc, the BusyBox 'networking' applets will similarly require
+that you install at least some of the glibc NSS stuff (in particular,
+/etc/nsswitch.conf, /lib/libnss_dns*, /lib/libnss_files*, and /lib/libresolv*).
+
+Shameless Plug: As an alternative, one could use a C library such as uClibc.  In
+addition to making your system significantly smaller, uClibc does not require the
+use of any NSS support files or libraries.
+
+=head1 MAINTAINER
+
+Denis Vlasenko <vda.linux@googlemail.com>
+
+=head1 AUTHORS
+
+The following people have contributed code to BusyBox whether they know it or
+not.  If you have written code included in BusyBox, you should probably be
+listed here so you can obtain your bit of eternal glory.  If you should be
+listed here, or the description of what you have done needs more detail, or is
+incorrect, please send in an update.
+
+
+=for html <br>
+
+Emanuele Aina <emanuele.aina@tiscali.it>
+    run-parts
+
+=for html <br>
+
+Erik Andersen <andersen@codepoet.org>
+
+    Tons of new stuff, major rewrite of most of the
+    core apps, tons of new apps as noted in header files.
+    Lots of tedious effort writing these boring docs that
+    nobody is going to actually read.
+
+=for html <br>
+
+Laurence Anderson <l.d.anderson@warwick.ac.uk>
+
+    rpm2cpio, unzip, get_header_cpio, read_gz interface, rpm
+
+=for html <br>
+
+Jeff Angielski <jeff@theptrgroup.com>
+
+    ftpput, ftpget
+
+=for html <br>
+
+Edward Betts <edward@debian.org>
+
+    expr, hostid, logname, whoami
+
+=for html <br>
+
+John Beppu <beppu@codepoet.org>
+
+    du, nslookup, sort
+
+=for html <br>
+
+Brian Candler <B.Candler@pobox.com>
+
+    tiny-ls(ls)
+
+=for html <br>
+
+Randolph Chung <tausq@debian.org>
+
+    fbset, ping, hostname
+
+=for html <br>
+
+Dave Cinege <dcinege@psychosis.com>
+
+    more(v2), makedevs, dutmp, modularization, auto links file,
+    various fixes, Linux Router Project maintenance
+
+=for html <br>
+
+Jordan Crouse <jordan@cosmicpenguin.net>
+
+    ipcalc
+
+=for html <br>
+
+Magnus Damm <damm@opensource.se>
+
+    tftp client insmod powerpc support
+
+=for html <br>
+
+Larry Doolittle <ldoolitt@recycle.lbl.gov>
+
+    pristine source directory compilation, lots of patches and fixes.
+
+=for html <br>
+
+Glenn Engel <glenne@engel.org>
+
+    httpd
+
+=for html <br>
+
+Gennady Feldman <gfeldman@gena01.com>
+
+    Sysklogd (single threaded syslogd, IPC Circular buffer support,
+    logread), various fixes.
+
+=for html <br>
+
+Karl M. Hegbloom <karlheg@debian.org>
+
+    cp_mv.c, the test suite, various fixes to utility.c, &c.
+
+=for html <br>
+
+Daniel Jacobowitz <dan@debian.org>
+
+    mktemp.c
+
+=for html <br>
+
+Matt Kraai <kraai@alumni.cmu.edu>
+
+    documentation, bugfixes, test suite
+
+=for html <br>
+
+Stephan Linz <linz@li-pro.net>
+
+    ipcalc, Red Hat equivalence
+
+=for html <br>
+
+John Lombardo <john@deltanet.com>
+
+    tr
+
+=for html <br>
+
+Glenn McGrath <bug1@iinet.net.au>
+
+    Common unarchiving code and unarchiving applets, ifupdown, ftpgetput,
+    nameif, sed, patch, fold, install, uudecode.
+    Various bugfixes, review and apply numerous patches.
+
+=for html <br>
+
+Manuel Novoa III <mjn3@codepoet.org>
+
+    cat, head, mkfifo, mknod, rmdir, sleep, tee, tty, uniq, usleep, wc, yes,
+    mesg, vconfig, make_directory, parse_mode, dirname, mode_string,
+    get_last_path_component, simplify_path, and a number trivial libbb routines
+
+    also bug fixes, partial rewrites, and size optimizations in
+    ash, basename, cal, cmp, cp, df, du, echo, env, ln, logname, md5sum, mkdir,
+    mv, realpath, rm, sort, tail, touch, uname, watch, arith, human_readable,
+    interface, dutmp, ifconfig, route
+
+=for html <br>
+
+Vladimir Oleynik <dzo@simtreas.ru>
+
+    cmdedit; xargs(current), httpd(current);
+    ports: ash, crond, fdisk, inetd, stty, traceroute, top;
+    locale, various fixes
+    and irreconcilable critic of everything not perfect.
+
+=for html <br>
+
+Bruce Perens <bruce@pixar.com>
+
+    Original author of BusyBox in 1995, 1996. Some of his code can
+    still be found hiding here and there...
+
+=for html <br>
+
+Tim Riker <Tim@Rikers.org>
+
+    bug fixes, member of fan club
+
+=for html <br>
+
+Kent Robotti <robotti@metconnect.com>
+
+    reset, tons and tons of bug reports and patches.
+
+=for html <br>
+
+Chip Rosenthal <chip@unicom.com>, <crosenth@covad.com>
+
+    wget - Contributed by permission of Covad Communications
+
+=for html <br>
+
+Pavel Roskin <proski@gnu.org>
+
+    Lots of bugs fixes and patches.
+
+=for html <br>
+
+Gyepi Sam <gyepi@praxis-sw.com>
+
+    Remote logging feature for syslogd
+
+=for html <br>
+
+Linus Torvalds <torvalds@transmeta.com>
+
+    mkswap, fsck.minix, mkfs.minix
+
+=for html <br>
+
+Mark Whitley <markw@codepoet.org>
+
+    grep, sed, cut, xargs(previous),
+    style-guide, new-applet-HOWTO, bug fixes, etc.
+
+=for html <br>
+
+Charles P. Wright <cpwright@villagenet.com>
+
+    gzip, mini-netcat(nc)
+
+=for html <br>
+
+Enrique Zanardi <ezanardi@ull.es>
+
+    tarcat (since removed), loadkmap, various fixes, Debian maintenance
+
+=for html <br>
+
+Tito Ragusa <farmatito@tiscali.it>
+
+    devfsd and size optimizations in strings, openvt and deallocvt.
+
+=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
diff --git a/busybox-1.19.3/docs/busybox_header.pod b/busybox-1.19.3/docs/busybox_header.pod
new file mode 100644
index 0000000..85a173e
--- /dev/null
+++ b/busybox-1.19.3/docs/busybox_header.pod
@@ -0,0 +1,82 @@
+# vi: set sw=4 ts=4:
+
+=head1 NAME
+
+BusyBox - The Swiss Army Knife of Embedded Linux
+
+=head1 SYNTAX
+
+ busybox <applet> [arguments...]  # or
+
+ <applet> [arguments...]	  # if symlinked
+
+=head1 DESCRIPTION
+
+BusyBox combines tiny versions of many common UNIX utilities into a single
+small executable. It provides minimalist replacements for most of the utilities
+you usually find in GNU coreutils, util-linux, 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.
+
+BusyBox has been written with size-optimization and limited resources in mind.
+It is also extremely modular so you can easily include or exclude commands (or
+features) at compile time. This makes it easy to customize your embedded
+systems. To create a working system, just add /dev, /etc, and a Linux kernel.
+BusyBox provides a fairly complete POSIX environment for any small or embedded
+system.
+
+BusyBox is extremely configurable.  This allows you to include only the
+components you need, thereby reducing binary size. Run 'make config' or 'make
+menuconfig' to select the functionality that you wish to enable.  Then run
+'make' to compile BusyBox using your configuration.
+
+After the compile has finished, you should use 'make install' to install
+BusyBox. This will install the 'bin/busybox' binary, in the target directory
+specified by CONFIG_PREFIX. CONFIG_PREFIX can be set when configuring BusyBox,
+or you can specify an alternative location at install time (i.e., with a
+command line like 'make CONFIG_PREFIX=/tmp/foo install'). If you enabled
+any applet installation scheme (either as symlinks or hardlinks), these will
+also be installed in the location pointed to by CONFIG_PREFIX.
+
+=head1 USAGE
+
+BusyBox is a multi-call binary.  A multi-call binary is an executable program
+that performs the same job as more than one utility program.  That means there
+is just a single BusyBox binary, but that single binary acts like a large
+number of utilities.  This allows BusyBox to be smaller since all the built-in
+utility programs (we call them applets) can share code for many common
+operations.
+
+You can also invoke BusyBox by issuing a command as an argument on the
+command line.  For example, entering
+
+	/bin/busybox ls
+
+will also cause BusyBox to behave as 'ls'.
+
+Of course, adding '/bin/busybox' into every command would be painful.  So most
+people will invoke BusyBox using links to the BusyBox binary.
+
+For example, entering
+
+	ln -s /bin/busybox ls
+	./ls
+
+will cause BusyBox to behave as 'ls' (if the 'ls' command has been compiled
+into BusyBox).  Generally speaking, you should never need to make all these
+links yourself, as the BusyBox build system will do this for you when you run
+the 'make install' command.
+
+If you invoke BusyBox with no arguments, it will provide you with a list of the
+applets that have been compiled into your BusyBox binary.
+
+=head1 COMMON OPTIONS
+
+Most BusyBox applets support the B<--help> argument to provide a terse runtime
+description of their behavior.  If the CONFIG_FEATURE_VERBOSE_USAGE option has
+been enabled, more detailed usage information will also be available.
+
+=head1 COMMANDS
+
+Currently available applets include:
diff --git a/busybox-1.19.3/docs/cgi/cl.html b/busybox-1.19.3/docs/cgi/cl.html
new file mode 100644
index 0000000..4f8faae
--- /dev/null
+++ b/busybox-1.19.3/docs/cgi/cl.html
@@ -0,0 +1,46 @@
+<html><head><title>CGI Command line options</title></head><body><h1><img alt="" src="cl_files/CGIlogo.gif"> CGI Command line options</h1>
+<hr> <p>
+
+</p><h2>Specification</h2>
+
+The command line is only used in the case of an ISINDEX query. It is
+not used in the case of an HTML form or any as yet undefined query
+type. The server should search the query information (the <code>QUERY_STRING</code> environment variable) for a non-encoded
+= character to determine if the command line is to be used, if it
+finds one, the command line is not to be used. This trusts the clients
+to encode the = sign in ISINDEX queries, a practice which was
+considered safe at the time of the design of this specification. <p>
+
+For example, use the <a href="http://hoohoo.ncsa.uiuc.edu/cgi-bin/finger">finger script</a> and the ISINDEX interface to look up "httpd".  You will see that the script will call itself with <code>/cgi-bin/finger?httpd</code> and will actually execute "finger httpd" on the command line and output the results to you.
+</p><p>
+If the server does find a "=" in the <code>QUERY_STRING</code>,
+then the command line will not be used, and no decoding will be
+performed. The query then remains intact for processing by an
+appropriate FORM submission decoder.
+Again, as an example, use <a href="http://hoohoo.ncsa.uiuc.edu/cgi-bin/finger?httpd=name">this hyperlink</a> to submit <code>"httpd=name"</code> to the finger script.  Since this <code>QUERY_STRING</code>
+contained an unencoded "=", nothing was decoded, the script didn't know
+it was being submitted a valid query, and just gave you the default
+finger form.
+</p><p>
+If the server finds that it cannot send the string due to internal
+limitations (such as exec() or /bin/sh command line restrictions) the
+server should include NO command line information and provide the
+non-decoded query information in the environment
+variable <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#query"><code>QUERY_STRING</code></a>. </p><p>
+</p><hr>
+<h2>Examples</h2>
+
+Examples of the command line usage are much better <a href="http://hoohoo.ncsa.uiuc.edu/cgi/examples.html">demonstrated</a> than explained. For these
+examples, pay close attention to the script output which says what
+argc and argv are. <p>
+
+</p><hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="cl_files/back.gif">Return to the
+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>
diff --git a/busybox-1.19.3/docs/cgi/env.html b/busybox-1.19.3/docs/cgi/env.html
new file mode 100644
index 0000000..b83c750
--- /dev/null
+++ b/busybox-1.19.3/docs/cgi/env.html
@@ -0,0 +1,149 @@
+<html><head><title>CGI Environment Variables</title></head><body><h1><img alt="" src="env_files/CGIlogo.gif"> CGI Environment Variables</h1>
+<hr>
+
+<p>
+
+In order to pass data about the information request from the server to
+the script, the server uses command line arguments as well as
+environment variables. These environment variables are set when the
+server executes the gateway program. </p><p>
+
+</p><hr>
+<h2>Specification</h2>
+
+ <p>
+The following environment variables are not request-specific and are
+set for all requests: </p><p>
+
+</p><ul>
+<li> <code>SERVER_SOFTWARE</code> <p>
+
+    The name and version of the information server software answering
+    the request (and running the gateway). Format: name/version </p><p>
+
+</p></li><li> <code>SERVER_NAME</code> <p>
+    The server's hostname, DNS alias, or IP address as it would appear
+    in self-referencing URLs. </p><p>
+
+</p></li><li> <code>GATEWAY_INTERFACE</code> <p>
+    The revision of the CGI specification to which this server
+    complies. Format: CGI/revision</p><p>
+
+</p></li></ul>
+
+<hr>
+
+The following environment variables are specific to the request being
+fulfilled by the gateway program: <p>
+
+</p><ul>
+<li> <a name="protocol"><code>SERVER_PROTOCOL</code></a> <p>
+    The name and revision of the information protcol this request came
+    in with. Format: protocol/revision </p><p>
+
+</p></li><li> <code>SERVER_PORT</code>  <p>
+    The port number to which the request was sent. </p><p>
+
+</p></li><li> <code>REQUEST_METHOD</code> <p>
+    The method with which the request was made. For HTTP, this is
+    "GET", "HEAD", "POST", etc. </p><p>
+
+</p></li><li> <code>PATH_INFO</code> <p>
+    The extra path information, as given by the client. In other
+    words, scripts can be accessed by their virtual pathname, followed
+    by extra information at the end of this path. The extra
+    information is sent as PATH_INFO. This information should be
+    decoded by the server if it comes from a URL before it is passed
+    to the CGI script.</p><p>
+
+</p></li><li> <code>PATH_TRANSLATED</code> <p>
+    The server provides a translated version of PATH_INFO, which takes
+    the path and does any virtual-to-physical mapping to it. </p><p>
+
+</p></li><li> <code>SCRIPT_NAME</code> <p>
+    A virtual path to the script being executed, used for
+    self-referencing URLs. </p><p>
+
+</p></li><li> <a name="query"><code>QUERY_STRING</code></a> <p>
+    The information which follows the ? in the <a href="http://www.ncsa.uiuc.edu/demoweb/url-primer.html">URL</a>
+    which referenced this script. This is the query information. It
+    should not be decoded in any fashion. This variable should always
+    be set when there is query information, regardless of <a href="http://hoohoo.ncsa.uiuc.edu/cgi/cl.html">command line decoding</a>. </p><p>
+
+</p></li><li> <code>REMOTE_HOST</code> <p>
+    The hostname making the request. If the server does not have this
+    information, it should set REMOTE_ADDR and leave this unset.</p><p>
+
+</p></li><li> <code>REMOTE_ADDR</code> <p>
+    The IP address of the remote host making the request. </p><p>
+
+</p></li><li> <code>AUTH_TYPE</code> <p>
+    If the server supports user authentication, and the script is
+    protects, this is the protocol-specific authentication method used
+    to validate the user. </p><p>
+
+</p></li><li> <code>REMOTE_USER</code> <p>
+    If the server supports user authentication, and the script is
+    protected, this is the username they have authenticated as. </p><p>
+</p></li><li> <code>REMOTE_IDENT</code> <p>
+    If the HTTP server supports RFC 931 identification, then this
+    variable will be set to the remote user name retrieved from the
+    server. Usage of this variable should be limited to logging only.
+    </p><p>
+
+</p></li><li> <a name="ct"><code>CONTENT_TYPE</code></a> <p>
+    For queries which have attached information, such as HTTP POST and
+    PUT, this is the content type of the data. </p><p>
+
+</p></li><li> <a name="cl"><code>CONTENT_LENGTH</code></a> <p>
+    The length of the said content as given by the client. </p><p>
+
+</p></li></ul>
+
+
+<a name="headers"><hr></a>
+
+In addition to these, the header lines received from the client, if
+any, are placed into the environment with the prefix HTTP_ followed by
+the header name. Any - characters in the header name are changed to _
+characters. The server may exclude any headers which it has already
+processed, such as Authorization, Content-type, and Content-length. If
+necessary, the server may choose to exclude any or all of these
+headers if including them would exceed any system environment
+limits. <p>
+
+An example of this is the HTTP_ACCEPT variable which was defined in
+CGI/1.0. Another example is the header User-Agent.</p><p>
+
+</p><ul>
+<li> <code>HTTP_ACCEPT</code> <p>
+    The MIME types which the client will accept, as given by HTTP
+    headers. Other protocols may need to get this information from
+    elsewhere. Each item in this list should be separated by commas as
+    per the HTTP spec. </p><p>
+
+    Format: type/subtype, type/subtype </p><p>
+
+
+</p></li><li> <code>HTTP_USER_AGENT</code><p>
+
+    The browser the client is using to send the request. General
+format: <code>software/version library/version</code>.</p><p>
+
+</p></li></ul>
+
+<hr>
+<h2>Examples</h2>
+
+Examples of the setting of environment variables are really much better
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/examples.html">demonstrated</a> than explained. <p>
+
+</p><hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="env_files/back.gif">Return to the
+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>
diff --git a/busybox-1.19.3/docs/cgi/in.html b/busybox-1.19.3/docs/cgi/in.html
new file mode 100644
index 0000000..7ee5fe6
--- /dev/null
+++ b/busybox-1.19.3/docs/cgi/in.html
@@ -0,0 +1,33 @@
+<html><head><title>CGI Script input</title></head><body><h1><img alt="" src="in_files/CGIlogo.gif"> CGI Script Input</h1>
+<hr>
+
+<h2>Specification</h2>
+
+For requests which have information attached after the header, such as
+HTTP POST or PUT, the information will be sent to the script on stdin.
+<p>
+
+The server will send <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#cl">CONTENT_LENGTH</a> bytes on
+this file descriptor. Remember that it will give the <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#ct">CONTENT_TYPE</a> of the data as well. The server is
+in no way obligated to send end-of-file after the script reads
+<code>CONTENT_LENGTH</code> bytes. </p><p>
+</p><hr>
+<h2>Example</h2>
+
+Let's take a form with METHOD="POST" as an example. Let's say the form
+results are 7 bytes encoded, and look like <code>a=b&amp;b=c</code>.
+<p>
+
+In this case, the server will set CONTENT_LENGTH to 7 and CONTENT_TYPE
+to application/x-www-form-urlencoded. The first byte on the script's
+standard input will be "a", followed by the rest of the encoded string.</p><p>
+
+</p><hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="in_files/back.gif">Return to the
+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>
diff --git a/busybox-1.19.3/docs/cgi/interface.html b/busybox-1.19.3/docs/cgi/interface.html
new file mode 100644
index 0000000..0be016b
--- /dev/null
+++ b/busybox-1.19.3/docs/cgi/interface.html
@@ -0,0 +1,29 @@
+<html><head><title>The Common Gateway Interface Specification
+[http://hoohoo.ncsa.uiuc.edu/cgi/interface.html]
+</title></head><body><h1><img alt="" src="interface_files/CGIlogo.gif"> The CGI Specification</h1>
+
+<hr>
+
+This is the specification for CGI version 1.1, or CGI/1.1. Further
+revisions of this protocol are guaranteed to be backward compatible.
+<p>
+
+The server and the CGI script communicate in four major ways. Each of
+the following is a hotlink to graphic detail.</p><p>
+
+</p><ul>
+<li> <a href="env.html">Environment variables</a>
+</li><li> <a href="cl.html">The command line</a>
+</li><li> <a href="in.html">Standard input</a>
+</li><li> <a href="out.html">Standard output</a>
+</li></ul>
+<hr>
+
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/overview.html"><img alt="[Back]" src="interface_files/back.gif">Return to the overview</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>
diff --git a/busybox-1.19.3/docs/cgi/out.html b/busybox-1.19.3/docs/cgi/out.html
new file mode 100644
index 0000000..5266985
--- /dev/null
+++ b/busybox-1.19.3/docs/cgi/out.html
@@ -0,0 +1,126 @@
+<html><head><title>CGI Script output</title></head><body><h1><img alt="" src="out_files/CGIlogo.gif"> CGI Script Output</h1>
+<hr>
+
+<h2>Script output</h2>
+
+The script sends its output to stdout. This output can either be a
+document generated by the script, or instructions to the server for
+retrieving the desired output. <p>
+</p><hr>
+
+<h2>Script naming conventions</h2>
+
+Normally, scripts produce output which is interpreted and sent back to
+the client. An advantage of this is that the scripts do not need to
+send a full HTTP/1.0 header for every request.  <p>
+<a name="nph">
+Some scripts may want to avoid the extra overhead of the server
+parsing their output, and talk directly to the client. In order to
+distinguish these scripts from the other scripts, CGI requires that
+the script name begins with nph- if a script does not want the server
+to parse its header. In this case, it is the script's responsibility
+to return a valid HTTP/1.0 (or HTTP/0.9) response to the client.  </a></p><p>
+
+</p><hr>
+<h2><a name="nph">Parsed headers</a></h2>
+
+<a name="nph">The output of scripts begins with a small header. This header consists
+of text lines, in the same format as an </a><a href="http://www.w3.org/hypertext/WWW/Protocols/HTTP/Object_Headers.html">
+HTTP header</a>, terminated by a blank line (a line with only a
+linefeed or CR/LF). <p>
+
+Any headers which are not server directives are sent directly back to
+the client. Currently, this specification defines three server
+directives:</p><p>
+
+</p><ul>
+<li> <code>Content-type</code> <p>
+
+    This is the MIME type of the document you are returning.  </p><p>
+
+</p></li><li> <code>Location</code> <p>
+
+    This is used to specify to the server that you are returning a
+    reference to a document rather than an actual document. </p><p>
+
+    If the argument to this is a URL, the server will issue a redirect
+    to the client. </p><p>
+
+    If the argument to this is a virtual path, the server will
+    retrieve the document specified as if the client had requested
+    that document originally. ? directives will work in here, but #
+    directives must be redirected back to the client.</p><p>
+
+
+</p></li><li> <a name="status"><code>Status</code></a><p>
+
+    This is used to give the server an HTTP/1.0 <a href="http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html">status
+line</a> to send to the client. The format is <code>nnn xxxxx</code>,
+where <code>nnn</code> is the 3-digit status code, and
+<code>xxxxx</code> is the reason string, such as "Forbidden".</p><p>
+
+</p></li></ul>
+
+<hr>
+<h2>Examples</h2>
+
+Let's say I have a fromgratz to HTML converter. When my converter is
+finished with its work, it will output the following on stdout (note
+that the lines beginning and ending with --- are just for illustration
+and would not be output): <p>
+
+</p><pre>--- start of output ---
+Content-type: text/html
+
+--- end of output ---
+</pre>
+
+Note the blank line after Content-type. <p>
+
+Now, let's say I have a script which, in certain instances, wants to
+return the document <code>/path/doc.txt</code> from this server just
+as if the user had actually requested
+<code>http://server:port/path/doc.txt</code> to begin with. In this
+case, the script would output: </p><p>
+</p><pre>--- start of output ---
+Location: /path/doc.txt
+
+--- end of output ---
+</pre>
+
+The server would then perform the request and send it to the client.
+<p>
+
+Let's say that I have a script which wants to reference our gopher
+server. In this case, if the script wanted to refer the user to
+<code>gopher://gopher.ncsa.uiuc.edu/</code>, it would output: </p><p>
+
+</p><pre>--- start of output ---
+Location: gopher://gopher.ncsa.uiuc.edu/
+
+--- end of output ---
+</pre>
+
+Finally, I have a script which wants to talk to the client directly.
+In this case, if the script is referenced with <a href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html#protocol"><code>SERVER_PROTOCOL</code></a> of HTTP/1.0,
+the script would output the following HTTP/1.0 response: <p>
+
+</p><pre>--- start of output ---
+HTTP/1.0 200 OK
+Server: NCSA/1.0a6
+Content-type: text/plain
+
+This is a plaintext document generated on the fly just for you.
+
+--- end of output ---
+</pre>
+
+
+<hr>
+
+<a href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html"><img alt="[Back]" src="out_files/back.gif">Return to the
+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>
diff --git a/busybox-1.19.3/docs/contributing.txt b/busybox-1.19.3/docs/contributing.txt
new file mode 100644
index 0000000..e3289fd
--- /dev/null
+++ b/busybox-1.19.3/docs/contributing.txt
@@ -0,0 +1,439 @@
+Contributing To Busybox
+=======================
+
+This document describes what you need to do to contribute to Busybox, where
+you can help, guidelines on testing, and how to submit a well-formed patch
+that is more likely to be accepted.
+
+The Busybox home page is at: http://busybox.net/
+
+
+
+Pre-Contribution Checklist
+--------------------------
+
+So you want to contribute to Busybox, eh? Great, wonderful, glad you want to
+help. However, before you dive in, headlong and hotfoot, there are some things
+you need to do:
+
+
+Checkout the Latest Code
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is a necessary first step. Please do not try to work with the last
+released version, as there is a good chance that somebody has already fixed
+the bug you found. Somebody might have even added the feature you had in mind.
+Don't make your work obsolete before you start!
+
+For information on how to check out Busybox development tree, please look at the
+following links:
+
+	http://busybox.net/source.html
+
+
+Read the Mailing List
+~~~~~~~~~~~~~~~~~~~~~
+
+No one is required to read the entire archives of the mailing list, but you
+should at least read up on what people have been talking about lately. If
+you've recently discovered a problem, chances are somebody else has too. If
+you're the first to discover a problem, post a message and let the rest of us
+know.
+
+Archives can be found here:
+
+	http://busybox.net/lists/busybox/
+
+If you have a serious interest in Busybox, i.e., you are using it day-to-day or
+as part of an embedded project, it would be a good idea to join the mailing
+list.
+
+A web-based sign-up form can be found here:
+
+	http://busybox.net/mailman/listinfo/busybox
+
+
+Coordinate with the Applet Maintainer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some (not all) of the applets in Busybox are "owned" by a maintainer who has
+put significant effort into it and is probably more familiar with it than
+others. To find the maintainer of an applet, look at the top of the .c file
+for a name following the word 'Copyright' or 'Written by' or 'Maintainer'.
+
+Before plunging ahead, it's a good idea to send a message to the mailing list
+that says: "Hey, I was thinking about adding the 'transmogrify' feature to the
+'foo' applet.  Would this be useful? Is anyone else working on it?" You might
+want to CC the maintainer (if any) with your question.
+
+
+
+Areas Where You Can Help
+------------------------
+
+Busybox can always use improvement! If you're looking for ways to help, there
+are a variety of areas where you could help.
+
+
+What Busybox Doesn't Need
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Before listing the areas where you _can_ help, it's worthwhile to mention the
+areas where you shouldn't bother. While Busybox strives to be the "Swiss Army
+Knife" of embedded Linux, there are some applets that will not be accepted:
+
+ - Any filesystem manipulation tools: Busybox is filesystem independent and
+   we do not want to start adding mkfs/fsck tools for every (or any)
+   filesystem under the sun. (fsck_minix.c and mkfs_minix.c are living on
+   borrowed time.) There are far too many of these tools out there.  Use
+   the upstream version.  Rationale: bugs in these tools can destroy
+   vast amounts of data.  Keeping up with filesystem format development
+   is impractical (especially in the area of keeping fsck tool safe
+   and up-to-date).
+
+ - Any disk, device, or media-specific tools: Use the -utils or -tools package
+   that was designed for your device; don't try to shoehorn them into Busybox.
+
+ - Any architecture specific tools: Busybox is (or should be) architecture
+   independent. Do not send us tools that cannot be used across multiple
+   platforms / arches.
+
+
+Bug Reporting
+~~~~~~~~~~~~~
+
+If you find bugs, please submit a detailed bug report to the busybox mailing
+list at busybox@busybox.net.  A well-written bug report should include a
+transcript of a shell session that demonstrates the bad behavior and enables
+anyone else to duplicate the bug on their own machine. The following is such
+an example:
+
+    To: busybox@busybox.net
+    From: diligent@testing.linux.org
+    Subject: /bin/date doesn't work
+
+    Package: busybox
+    Version: 1.00
+
+    When I execute Busybox 'date' it produces unexpected results.
+    With GNU date I get the following output:
+
+	$ date
+	Wed Mar 21 14:19:41 MST 2001
+
+    But when I use BusyBox date I get this instead:
+
+	$ date
+	Illegal instruction
+
+    I am using Debian unstable, kernel version 2.4.19-rmk1 on an Netwinder,
+    and the latest uClibc from CVS.
+
+	-Diligent
+
+Note the careful description and use of examples showing not only what BusyBox
+does, but also a counter example showing what an equivalent GNU app does.  Bug
+reports lacking such detail may never be fixed...  Thanks for understanding.
+
+
+
+Write Documentation
+~~~~~~~~~~~~~~~~~~~
+
+Chances are, documentation in Busybox is either missing or needs improvement.
+Either way, help is welcome.
+
+Work is being done to automatically generate documentation from sources,
+especially from the usage.h file. If you want to correct the documentation,
+please make changes to the pre-generation parts, rather than the generated
+documentation. [More to come on this later...]
+
+It is preferred that modifications to documentation be submitted in patch
+format (more on this below), but we're a little more lenient when it comes to
+docs. You could, for example, just say "after the listing of the mount
+options, the following example would be helpful..."
+
+
+Consult Existing Sources
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+For a quick listing of "needs work" spots in the sources, cd into the Busybox
+directory and run the following:
+
+	for i in TODO FIXME XXX; do find -name '*.[ch]'|xargs grep $i; done
+
+This will show all of the trouble spots or 'questionable' code. Pick a spot,
+any spot, these are all invitations for you to contribute.
+
+
+Add a New Applet
+~~~~~~~~~~~~~~~~
+
+If you want to add a new applet to Busybox, we'd love to see it. However,
+before you write any code, please ask beforehand on the mailing list something
+like "Do you think applet 'foo' would be useful in Busybox?" or "Would you
+guys accept applet 'foo' into Busybox if I were to write it?" If the answer is
+"no" by the folks on the mailing list, then you've saved yourself some time.
+Conversely, you could get some positive responses from folks who might be
+interested in helping you implement it, or can recommend the best approach.
+Perhaps most importantly, this is your way of calling "dibs" on something and
+avoiding duplication of effort.
+
+Also, before you write a line of code, please read the 'new-applet-HOWTO.txt'
+file in the docs/ directory.
+
+
+Janitorial Work
+~~~~~~~~~~~~~~~
+
+These are dirty jobs, but somebody's gotta do 'em.
+
+ - Security audits:
+   http://www.securityfocus.com/popups/forums/secprog/intro.shtml
+
+ - Synthetic code removal: http://www.perl.com/pub/2000/06/commify.html - This
+   is very Perl-specific, but the advice given in here applies equally well to
+   C.
+
+ - C library function use audits: Verifying that functions are being used
+   properly (called with the right args), replacing unsafe library functions
+   with safer versions, making sure return codes are being checked, etc.
+
+ - Where appropriate, replace preprocessor defined macros and values with
+   compile-time equivalents.
+
+ - Style guide compliance. See: docs/style-guide.txt
+
+ - Add testcases to tests/testcases.
+
+ - Makefile improvements:
+   http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html
+   (I think the recursive problems are pretty much taken care of at this point, non?)
+
+ - "Ten Commandments" compliance: (this is a "maybe", certainly not as
+   important as any of the previous items.)
+    http://www.lysator.liu.se/c/ten-commandments.html
+
+Other useful links:
+
+ - the comp.lang.c FAQ: http://home.datacomm.ch/t_wolf/tw/c/index.html#Sources
+
+
+
+Submitting Patches To Busybox
+-----------------------------
+
+Here are some guidelines on how to submit a patch to Busybox.
+
+
+Making A 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
+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 -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,
+discreet changes on a per-patch basis. Sometimes you need to make a patch that
+touches code in many places, but these kind of patches are rare and should be
+coordinated with a maintainer.
+
+
+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 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
+   behavior / output is identical between the two.
+
+ - Try several different permutations and combinations of the features you're
+   adding (i.e., different combinations of command-line switches) and make sure
+   they all work; make sure one feature does not interfere with another.
+
+ - Make sure you test compiling against the source both with the feature
+   turned on and turned off in Config.h and make sure Busybox compiles cleanly
+   both ways.
+
+ - Run the multibuild.pl script in the tests directory and make sure
+   everything checks out OK. (Do this from within the busybox/ directory by
+   typing: 'tests/multibuild.pl'.)
+
+
+Making Sure Your Patch Doesn't Get Lost
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you don't want your patch to be lost or forgotten, send it to the busybox
+mailing list with a subject line something like this:
+
+	[PATCH] - Adds "transmogrify" feature to "foo"
+
+In the body, you should have a pseudo-header that looks like the following:
+
+    Package: busybox
+    Version: v1.01pre (or whatever the current version is)
+    Severity: wishlist
+
+The remainder of the body should read along these lines:
+
+	This patch adds the "transmogrify" feature to the "foo" applet. I have
+	tested this on [arch] system(s) and it works. I have tested it against the
+	GNU counterparts and the outputs are identical. I have run the scripts in
+	the 'tests' directory and nothing breaks.
+
+
+
+Improving Your Chances of Patch Acceptance
+------------------------------------------
+
+Even after you send a brilliant patch to the mailing list, sometimes it can go
+unnoticed, un-replied-to, and sometimes (sigh) even lost. This is an
+unfortunate fact of life, but there are steps you can take to help your patch
+get noticed and convince a maintainer that it should be added:
+
+
+Be Succinct
+~~~~~~~~~~~
+
+A patch that includes small, isolated, obvious changes is more likely to be
+accepted than a patch that touches code in lots of different places or makes
+sweeping, dubious changes.
+
+
+Back It Up
+~~~~~~~~~~
+
+Hard facts on why your patch is better than the existing code will go a long
+way toward convincing maintainers that your patch should be included.
+Specifically, patches are more likely to be accepted if they are provably more
+correct, smaller, faster, simpler, or more maintainable than the existing
+code.
+
+Conversely, any patch that is supported with nothing more than "I think this
+would be cool" or "this patch is good because I say it is and I've got a Phd
+in Computer Science" will likely be ignored.
+
+
+Follow The Style Guide
+~~~~~~~~~~~~~~~~~~~~~~
+
+It's considered good form to abide by the established coding style used in a
+project; Busybox is no exception. We have gone so far as to delineate the
+"elements of Busybox style" in the file docs/style-guide.txt. Please follow
+them.
+
+
+Work With Someone Else
+~~~~~~~~~~~~~~~~~~~~~~
+
+Working on a patch in isolation is less effective than working with someone
+else for a variety of reasons. If another Busybox user is interested in what
+you're doing, then it's two (or more) voices instead of one that can petition
+for inclusion of the patch. You'll also have more people that can test your
+changes, or even offer suggestions on better approaches you could take.
+
+Getting other folks interested follows as a natural course if you've received
+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 Git. Please
+use these resources.
+
+
+Send Patches to the Bug Tracking System
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This was mentioned above in the "Making Sure Your Patch Doesn't Get Lost"
+section, but it is worth mentioning again. A patch sent to the mailing list
+might be unnoticed and forgotten. A patch sent to the bug tracking system will
+be stored and closely connected to the bug it fixes.
+
+
+Be Polite
+~~~~~~~~~
+
+The old saying "You'll catch more flies with honey than you will with vinegar"
+applies when submitting patches to the mailing list for approval. The way you
+present your patch is sometimes just as important as the actual patch itself
+(if not more so). Being rude to the maintainers is not an effective way to
+convince them that your patch should be included; it will likely have the
+opposite effect.
+
+
+
+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 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 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 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 push a change
+directly to Git.
+
+
+When Not To Push
+~~~~~~~~~~~~~~~~
+
+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
+ - You are adding a feature
+ - Your changes are speculative or experimental (i.e., trying a new algorithm)
+ - 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 pushing.
+
+
+
+Final Words
+-----------
+
+If all of this seems complicated, don't panic, it's really not that tough. If
+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.
diff --git a/busybox-1.19.3/docs/ctty.htm b/busybox-1.19.3/docs/ctty.htm
new file mode 100644
index 0000000..8f466cd
--- /dev/null
+++ b/busybox-1.19.3/docs/ctty.htm
@@ -0,0 +1,476 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html><head>
+ <!-- saved from http://www.win.tue.nl/~aeb/linux/lk/lk-10.html -->
+ <meta name="GENERATOR" content="SGML-Tools 1.0.9"><title>The Linux kernel: Processes</title>
+</head>
+<body>
+<hr>
+<h2><a name="s10">10. Processes</a></h2>
+
+<p>Before looking at the Linux implementation, first a general Unix
+description of threads, processes, process groups and sessions.
+</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.
+</p><p>A session can have a controlling tty.
+At most one process group in a session can be a foreground process group.
+An interrupt character typed on a tty ("Teletype", i.e., terminal)
+causes a signal to be sent to all members of the foreground process group
+in the session (if any) that has that tty as controlling tty.
+</p><p>All these objects have numbers, and we have thread IDs, process IDs,
+process group IDs and session IDs.
+</p><p>
+</p><h2><a name="ss10.1">10.1 Processes</a>
+</h2>
+
+<p>
+</p><h3>Creation</h3>
+
+<p>A new process is traditionally started using the <code>fork()</code>
+system call:
+</p><blockquote>
+<pre>pid_t p;
+
+p = fork();
+if (p == (pid_t) -1)
+        /* ERROR */
+else if (p == 0)
+        /* CHILD */
+else
+        /* PARENT */
+</pre>
+</blockquote>
+<p>This creates a child as a duplicate of its parent.
+Parent and child are identical in almost all respects.
+In the code they are distinguished by the fact that the parent
+learns the process ID of its child, while <code>fork()</code>
+returns 0 in the child. (It can find the process ID of its
+parent using the <code>getppid()</code> system call.)
+</p><p>
+</p><h3>Termination</h3>
+
+<p>Normal termination is when the process does
+</p><blockquote>
+<pre>exit(n);
+</pre>
+</blockquote>
+
+or
+<blockquote>
+<pre>return n;
+</pre>
+</blockquote>
+
+from its <code>main()</code> procedure. It returns the single byte <code>n</code>
+to its parent.
+<p>Abnormal termination is usually caused by a signal.
+</p><p>
+</p><h3>Collecting the exit code. Zombies</h3>
+
+<p>The parent does
+</p><blockquote>
+<pre>pid_t p;
+int status;
+
+p = wait(&amp;status);
+</pre>
+</blockquote>
+
+and collects two bytes:
+<p>
+<figure>
+<eps file="absent">
+<img src="ctty_files/exit_status.png">
+</eps>
+</figure></p><p>A process that has terminated but has not yet been waited for
+is a <i>zombie</i>. It need only store these two bytes:
+exit code and reason for termination.
+</p><p>On the other hand, if the parent dies first, <code>init</code> (process 1)
+inherits the child and becomes its parent.
+</p><p>
+</p><h3>Signals</h3>
+
+<p>
+</p><h3>Stopping</h3>
+
+<p>Some signals cause a process to stop:
+<code>SIGSTOP</code> (stop!),
+<code>SIGTSTP</code> (stop from tty: probably ^Z was typed),
+<code>SIGTTIN</code> (tty input asked by background process),
+<code>SIGTTOU</code> (tty output sent by background process, and this was
+disallowed by <code>stty tostop</code>).
+</p><p>Apart from ^Z there also is ^Y. The former stops the process
+when it is typed, the latter stops it when it is read.
+</p><p>Signals generated by typing the corresponding character on some tty
+are sent to all processes that are in the foreground process group
+of the session that has that tty as controlling tty. (Details below.)
+</p><p>If a process is being traced, every signal will stop it.
+</p><p>
+</p><h3>Continuing</h3>
+
+<p><code>SIGCONT</code>: continue a stopped process.
+</p><p>
+</p><h3>Terminating</h3>
+
+<p><code>SIGKILL</code> (die! now!),
+<code>SIGTERM</code> (please, go away),
+<code>SIGHUP</code> (modem hangup),
+<code>SIGINT</code> (^C),
+<code>SIGQUIT</code> (^\), etc.
+Many signals have as default action to kill the target.
+(Sometimes with an additional core dump, when such is
+allowed by rlimit.)
+The signals <code>SIGCHLD</code> and <code>SIGWINCH</code>
+are ignored by default.
+All except <code>SIGKILL</code> and <code>SIGSTOP</code> can be
+caught or ignored or blocked.
+For details, see <code>signal(7)</code>.
+</p><p>
+</p><h2><a name="ss10.2">10.2 Process groups</a>
+</h2>
+
+<p>Every process is member of a unique <i>process group</i>,
+identified by its <i>process group ID</i>.
+(When the process is created, it becomes a member of the process group
+of its parent.)
+By convention, the process group ID of a process group
+equals the process ID of the first member of the process group,
+called the <i>process group leader</i>.
+A process finds the ID of its process group using the system call
+<code>getpgrp()</code>, or, equivalently, <code>getpgid(0)</code>.
+One finds the process group ID of process <code>p</code> using
+<code>getpgid(p)</code>.
+</p><p>One may use the command <code>ps j</code> to see PPID (parent process ID),
+PID (process ID), PGID (process group ID) and SID (session ID)
+of processes. With a shell that does not know about job control,
+like <code>ash</code>, each of its children will be in the same session
+and have the same process group as the shell. With a shell that knows
+about job control, like <code>bash</code>, the processes of one pipeline, like
+</p><blockquote>
+<pre>% cat paper | ideal | pic | tbl | eqn | ditroff &gt; out
+</pre>
+</blockquote>
+
+form a single process group.
+<p>
+</p><h3>Creation</h3>
+
+<p>A process <code>pid</code> is put into the process group <code>pgid</code> by
+</p><blockquote>
+<pre>setpgid(pid, pgid);
+</pre>
+</blockquote>
+
+If <code>pgid == pid</code> or <code>pgid == 0</code> then this creates
+a new process group with process group leader <code>pid</code>.
+Otherwise, this puts <code>pid</code> into the already existing
+process group <code>pgid</code>.
+A zero <code>pid</code> refers to the current process.
+The call <code>setpgrp()</code> is equivalent to <code>setpgid(0,0)</code>.
+<p>
+</p><h3>Restrictions on setpgid()</h3>
+
+<p>The calling process must be <code>pid</code> itself, or its parent,
+and the parent can only do this before <code>pid</code> has done
+<code>exec()</code>, and only when both belong to the same session.
+It is an error if process <code>pid</code> is a session leader
+(and this call would change its <code>pgid</code>).
+</p><p>
+</p><h3>Typical sequence</h3>
+
+<p>
+</p><blockquote>
+<pre>p = fork();
+if (p == (pid_t) -1) {
+        /* ERROR */
+} else if (p == 0) {    /* CHILD */
+        setpgid(0, pgid);
+        ...
+} else {                /* PARENT */
+        setpgid(p, pgid);
+        ...
+}
+</pre>
+</blockquote>
+
+This ensures that regardless of whether parent or child is scheduled
+first, the process group setting is as expected by both.
+<p>
+</p><h3>Signalling and waiting</h3>
+
+<p>One can signal all members of a process group:
+</p><blockquote>
+<pre>killpg(pgrp, sig);
+</pre>
+</blockquote>
+<p>One can wait for children in ones own process group:
+</p><blockquote>
+<pre>waitpid(0, &amp;status, ...);
+</pre>
+</blockquote>
+
+or in a specified process group:
+<blockquote>
+<pre>waitpid(-pgrp, &amp;status, ...);
+</pre>
+</blockquote>
+<p>
+</p><h3>Foreground process group</h3>
+
+<p>Among the process groups in a session at most one can be
+the <i>foreground process group</i> of that session.
+The tty input and tty signals (signals generated by ^C, ^Z, etc.)
+go to processes in this foreground process group.
+</p><p>A process can determine the foreground process group in its session
+using <code>tcgetpgrp(fd)</code>, where <code>fd</code> refers to its
+controlling tty. If there is none, this returns a random value
+larger than 1 that is not a process group ID.
+</p><p>A process can set the foreground process group in its session
+using <code>tcsetpgrp(fd,pgrp)</code>, where <code>fd</code> refers to its
+controlling tty, and <code>pgrp</code> is a process group in
+its session, and this session still is associated to the controlling
+tty of the calling process.
+</p><p>How does one get <code>fd</code>? By definition, <code>/dev/tty</code>
+refers to the controlling tty, entirely independent of redirects
+of standard input and output. (There is also the function
+<code>ctermid()</code> to get the name of the controlling terminal.
+On a POSIX standard system it will return <code>/dev/tty</code>.)
+Opening the name of the
+controlling tty gives a file descriptor <code>fd</code>.
+</p><p>
+</p><h3>Background process groups</h3>
+
+<p>All process groups in a session that are not foreground
+process group are <i>background process groups</i>.
+Since the user at the keyboard is interacting with foreground
+processes, background processes should stay away from it.
+When a background process reads from the terminal it gets
+a SIGTTIN signal. Normally, that will stop it, the job control shell
+notices and tells the user, who can say <code>fg</code> to continue
+this background process as a foreground process, and then this
+process can read from the terminal. But if the background process
+ignores or blocks the SIGTTIN signal, or if its process group
+is orphaned (see below), then the read() returns an EIO error,
+and no signal is sent. (Indeed, the idea is to tell the process
+that reading from the terminal is not allowed right now.
+If it wouldn't see the signal, then it will see the error return.)
+</p><p>When a background process writes to the terminal, it may get
+a SIGTTOU signal. May: namely, when the flag that this must happen
+is set (it is off by default). One can set the flag by
+</p><blockquote>
+<pre>% stty tostop
+</pre>
+</blockquote>
+
+and clear it again by
+<blockquote>
+<pre>% stty -tostop
+</pre>
+</blockquote>
+
+and inspect it by
+<blockquote>
+<pre>% stty -a
+</pre>
+</blockquote>
+
+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.
+<p>
+</p><h3>Orphaned process groups</h3>
+
+<p>The process group leader is the first member of the process group.
+It may terminate before the others, and then the process group is
+without leader.
+</p><p>A process group is called <i>orphaned</i> when <i>the
+parent of every member is either in the process group
+or outside the session</i>.
+In particular, the process group of the session leader
+is always orphaned.
+</p><p>If termination of a process causes a process group to become
+orphaned, and some member is stopped, then all are sent first SIGHUP
+and then SIGCONT.
+</p><p>The idea is that perhaps the parent of the process group leader
+is a job control shell. (In the same session but a different
+process group.) As long as this parent is alive, it can
+handle the stopping and starting of members in the process group.
+When it dies, there may be nobody to continue stopped processes.
+Therefore, these stopped processes are sent SIGHUP, so that they
+die unless they catch or ignore it, and then SIGCONT to continue them.
+</p><p>Note that the process group of the session leader is already
+orphaned, so no signals are sent when the session leader dies.
+</p><p>Note also that a process group can become orphaned in two ways
+by termination of a process: either it was a parent and not itself
+in the process group, or it was the last element of the process group
+with a parent outside but in the same session.
+Furthermore, that a process group can become orphaned
+other than by termination of a process, namely when some
+member is moved to a different process group.
+</p><p>
+</p><h2><a name="ss10.3">10.3 Sessions</a>
+</h2>
+
+<p>Every process group is in a unique <i>session</i>.
+(When the process is created, it becomes a member of the session
+of its parent.)
+By convention, the session ID of a session
+equals the process ID of the first member of the session,
+called the <i>session leader</i>.
+A process finds the ID of its session using the system call
+<code>getsid()</code>.
+</p><p>Every session may have a <i>controlling tty</i>,
+that then also is called the controlling tty of each of
+its member processes.
+A file descriptor for the controlling tty is obtained by
+opening <code>/dev/tty</code>. (And when that fails, there was no
+controlling tty.) Given a file descriptor for the controlling tty,
+one may obtain the SID using <code>tcgetsid(fd)</code>.
+</p><p>A session is often set up by a login process. The terminal
+on which one is logged in then becomes the controlling tty
+of the session. All processes that are descendants of the
+login process will in general be members of the session.
+</p><p>
+</p><h3>Creation</h3>
+
+<p>A new session is created by
+</p><blockquote>
+<pre>pid = setsid();
+</pre>
+</blockquote>
+
+This is allowed only when the current process is not a process group leader.
+In order to be sure of that we fork first:
+<blockquote>
+<pre>p = fork();
+if (p) exit(0);
+pid = setsid();
+</pre>
+</blockquote>
+
+The result is that the current process (with process ID <code>pid</code>)
+becomes session leader of a new session with session ID <code>pid</code>.
+Moreover, it becomes process group leader of a new process group.
+Both session and process group contain only the single process <code>pid</code>.
+Furthermore, this process has no controlling tty.
+<p>The restriction that the current process must not be a process group leader
+is needed: otherwise its PID serves as PGID of some existing process group
+and cannot be used as the PGID of a new process group.
+</p><p>
+</p><h3>Getting a controlling tty</h3>
+
+<p>How does one get a controlling terminal? Nobody knows,
+this is a great mystery.
+</p><p>The System V approach is that the first tty opened by the process
+becomes its controlling tty.
+</p><p>The BSD approach is that one has to explicitly call
+</p><blockquote>
+<pre>ioctl(fd, TIOCSCTTY, 0/1);
+</pre>
+</blockquote>
+
+to get a controlling tty.
+<p>Linux tries to be compatible with both, as always, and this
+results in a very obscure complex of conditions. Roughly:
+</p><p>The <code>TIOCSCTTY</code> ioctl will give us a controlling tty,
+provided that (i) the current process is a session leader,
+and (ii) it does not yet have a controlling tty, and
+(iii) maybe the tty should not already control some other session;
+if it does it is an error if we aren't root, or we steal the tty
+if we are all-powerful.
+[vda: correction: third parameter controls this: if 1, we steal tty from
+any such session, if 0, we don't steal]
+</p><p>Opening some terminal will give us a controlling tty,
+provided that (i) the current process is a session leader, and
+(ii) it does not yet have a controlling tty, and
+(iii) the tty does not already control some other session, and
+(iv) the open did not have the <code>O_NOCTTY</code> flag, and
+(v) the tty is not the foreground VT, and
+(vi) the tty is not the console, and
+(vii) maybe the tty should not be master or slave pty.
+</p><p>
+</p><h3>Getting rid of a controlling tty</h3>
+
+<p>If a process wants to continue as a daemon, it must detach itself
+from its controlling tty. Above we saw that <code>setsid()</code>
+will remove the controlling tty. Also the ioctl TIOCNOTTY does this.
+Moreover, in order not to get a controlling tty again as soon as it
+opens a tty, the process has to fork once more, to assure that it
+is not a session leader. Typical code fragment:
+</p><p>
+</p><pre>        if ((fork()) != 0)
+                exit(0);
+        setsid();
+        if ((fork()) != 0)
+                exit(0);
+</pre>
+<p>See also <code>daemon(3)</code>.
+</p><p>
+</p><h3>Disconnect</h3>
+
+<p>If the terminal goes away by modem hangup, and the line was not local,
+then a SIGHUP is sent to the session leader.
+Any further reads from the gone terminal return EOF.
+(Or possibly -1 with <code>errno</code> set to EIO.)
+</p><p>If the terminal is the slave side of a pseudotty, and the master side
+is closed (for the last time), then a SIGHUP is sent to the foreground
+process group of the slave side.
+</p><p>When the session leader dies, a SIGHUP is sent to all processes
+in the foreground process group. Moreover, the terminal stops being
+the controlling terminal of this session (so that it can become
+the controlling terminal of another session).
+</p><p>Thus, if the terminal goes away and the session leader is
+a job control shell, then it can handle things for its descendants,
+e.g. by sending them again a SIGHUP.
+If on the other hand the session leader is an innocent process
+that does not catch SIGHUP, it will die, and all foreground processes
+get a SIGHUP.
+</p><p>
+</p><h2><a name="ss10.4">10.4 Threads</a>
+</h2>
+
+<p>A process can have several threads. New threads (with the same PID
+as the parent thread) are started using the <code>clone</code> system
+call using the <code>CLONE_THREAD</code> flag. Threads are distinguished
+by a <i>thread ID</i> (TID). An ordinary process has a single thread
+with TID equal to PID. The system call <code>gettid()</code> returns the
+TID. The system call <code>tkill()</code> sends a signal to a single thread.
+</p><p>Example: a process with two threads. Both only print PID and TID and exit.
+(Linux 2.4.19 or later.)
+</p><pre>% cat &lt;&lt; EOF &gt; gettid-demo.c
+#include &lt;unistd.h&gt;
+#include &lt;sys/types.h&gt;
+#define CLONE_SIGHAND   0x00000800
+#define CLONE_THREAD    0x00010000
+#include &lt;linux/unistd.h&gt;
+#include &lt;errno.h&gt;
+_syscall0(pid_t,gettid)
+
+int thread(void *p) {
+        printf("thread: %d %d\n", gettid(), getpid());
+}
+
+main() {
+        unsigned char stack[4096];
+        int i;
+
+        i = clone(thread, stack+2048, CLONE_THREAD | CLONE_SIGHAND, NULL);
+        if (i == -1)
+                perror("clone");
+        else
+                printf("clone returns %d\n", i);
+        printf("parent: %d %d\n", gettid(), getpid());
+}
+EOF
+% cc -o gettid-demo gettid-demo.c
+% ./gettid-demo
+clone returns 21826
+parent: 21825 21825
+thread: 21826 21825
+%
+</pre>
+<p>
+</p><p>
+</p><hr>
+
+</body></html>
diff --git a/busybox-1.19.3/docs/draft-coar-cgi-v11-03-clean.html b/busybox-1.19.3/docs/draft-coar-cgi-v11-03-clean.html
new file mode 100644
index 0000000..d52c9b8
--- /dev/null
+++ b/busybox-1.19.3/docs/draft-coar-cgi-v11-03-clean.html
@@ -0,0 +1,2674 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
+ "http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+ <HEAD>
+  <TITLE>Common Gateway Interface - 1.1 *Draft 03* [http://cgi-spec.golux.com/draft-coar-cgi-v11-03-clean.html]
+  </TITLE>
+<!--#if expr="$HTTP_USER_AGENT != /Lynx/" -->
+ <!--#set var="GUI" value="1" -->
+<!--#endif -->
+  <LINK HREF="mailto:Ken.Coar@Golux.Com" rev="revised">
+  <LINK REL="STYLESHEET" HREF="cgip-style-rfc.css" TYPE="text/css">
+  <META name="latexstyle" content="rfc">
+  <META name="author" content="Ken A L Coar">
+  <META name="institute" content="IBM Corporation">
+  <META name="date" content="25 June 1999">
+  <META name="expires" content="Expires 31 December 1999">
+  <META name="document" content="INTERNET-DRAFT">
+  <META name="file" content="&lt;draft-coar-cgi-v11-03.txt&gt;">
+  <META name="group" content="INTERNET-DRAFT">
+<!--
+    There are a lot of BNF fragments in this document.  To make it work
+    in all possible browsers (including Lynx, which is used to turn it
+    into text/plain), we handle these by using PREformatted blocks with
+    a universal internal margin of 2, inside one-level DL blocks.
+ -->
+ </HEAD>
+ <BODY>
+  <!--
+    HTML doesn't do paper pagination, so we need to fake it out.  Basing
+    our formatting upon RFC2068, there are four (4) lines of header and
+    four (4) lines of footer for each page.
+
+<DIV ALIGN="CENTER">
+ <PRE>
+
+
+
+
+Coar, et al.               CGI/1.1 Specification                     May, 1998
+INTERNET-DRAFT             Expires 1 December 1998                    [Page 2]
+
+
+ </PRE>
+</DIV>
+  -->
+  <!--
+    The following weirdness wrt non-breaking spaces is to get Lynx
+    (which is barely TABLE-aware) to line the left/right justified
+    text up properly.
+  -->
+  <DIV ALIGN="CENTER">
+   <TABLE WIDTH="100%" CELLPADDING=0 CELLSPACING=0>
+    <TR VALIGN="TOP">
+     <TD ALIGN="LEFT">
+      INTERNET-DRAFT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+     </TD>
+     <TD ALIGN="RIGHT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Ken A L Coar
+     </TD>
+    </TR>
+    <TR VALIGN="TOP">
+     <TD ALIGN="LEFT">
+      draft-coar-cgi-v11-03.{html,txt}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+     </TD>
+     <TD ALIGN="RIGHT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IBM Corporation
+     </TD>
+    </TR>
+    <TR VALIGN="TOP">
+     <TD ALIGN="LEFT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+     </TD>
+     <TD ALIGN="RIGHT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D.R.T. Robinson
+     </TD>
+    </TR>
+    <TR VALIGN="TOP">
+     <TD ALIGN="LEFT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+     </TD>
+     <TD ALIGN="RIGHT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E*TRADE&nbsp;UK&nbsp;Ltd.
+     </TD>
+    </TR>
+    <TR VALIGN="TOP">
+     <TD ALIGN="LEFT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+     </TD>
+     <TD ALIGN="RIGHT">
+      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;25 June 1999
+     </TD>
+    </TR>
+   </TABLE>
+  </DIV>
+
+  <H1 ALIGN="CENTER">
+   The WWW Common Gateway Interface
+   <BR>
+   Version 1.1
+  </H1>
+
+<!--#include virtual="I-D-statement" -->
+
+  <H2>
+   <A NAME="Abstract">
+    Abstract
+   </A>
+  </H2>
+  <P>
+  The Common Gateway Interface (CGI) is a simple interface for running
+  external programs, software or gateways under an information server
+  in a platform-independent manner. Currently, the supported information
+  servers are HTTP servers.
+  </P>
+  <P>
+  The interface has been in use by the World-Wide Web since 1993. This
+  specification defines the
+  "current practice" parameters of the
+  'CGI/1.1' interface developed and documented at the U.S. National
+  Centre for Supercomputing Applications [NCSA-CGI].
+  This document also defines the use of the CGI/1.1 interface
+  on the Unix and AmigaDOS(tm) systems.
+  </P>
+  <P>
+  Discussion of this draft occurs on the CGI-WG mailing list; see the
+  project Web page at
+  <SAMP>&lt;URL:<A HREF="http://CGI-Spec.Golux.Com/"
+                >http://CGI-Spec.Golux.Com/</A>&gt;</SAMP>
+  for details on the mailing list and the status of the project.
+  </P>
+
+<!--#if expr="$GUI" -->
+  <H2>
+   Revision History
+  </H2>
+  <P>
+  The revision history of this draft is being maintained using Web-based
+  GUI notation, such as struck-through characters and colour-coded
+  sections.  The following legend describes how to determine the origin
+  of a particular revision according to the colour of the text:
+  </P>
+  <DL COMPACT>
+   <DT>Black
+   </DT>
+   <DD>Revision 00, released 28 May 1998
+   </DD>
+   <DT>Green
+   </DT>
+   <DD>Revision 01, released 28 December 1998
+    <BR>
+    Major structure change: Section 4, "Request Metadata (Meta-Variables)"
+    was moved entirely under <A HREF="#7.0">Section 7</A>, "Data Input to the
+    CGI Script."
+    Due to the size of this change, it is noted here and the text in its
+    former location does <EM>not</EM> appear as struckthrough.  This has
+    caused major <A HREF="#6.0">sections 5</A> and following to decrement
+    by one.  Other
+    large text movements are likewise not marked up.  References to RFC
+    1738 were changed to 2396 (1738's replacement).
+   </DD>
+   <DT>Red
+   </DT>
+   <DD>Revision 02, released 2 April, 1999
+    <BR>
+    Added text to <A HREF="#8.3">section 8.3</A> defining correct handling
+    of HTTP/1.1
+    requests using "chunked" Transfer-Encoding.  Labelled metavariable
+    names in <A HREF="#8.0">section 8</A> with the appropriate detail section
+    numbers.
+    Clarified allowed usage of <SAMP>Status</SAMP> and
+    <SAMP>Location</SAMP> response header fields.  Included new
+    Internet-Draft language.
+   </DD>
+   <DT>Fuchsia
+   </DT>
+   <DD>Revision 03, released 25 June 1999
+    <BR>
+    Changed references from "HTTP" to "Protocol-Specific" for the listing of
+    things like HTTP_ACCEPT.  Changed 'entity-body' and 'content-body' to
+    'message-body.'  Added a note that response headers must comply with
+    requirements of the protocol level in use.  Added a lot of stuff about
+    security (section 11).  Clarified a bunch of productions.  Pointed out
+    that zero-length and omitted values are indistinguishable in this
+    specification.  Clarified production describing order of fields in
+    script response header.  Clarified issues surrounding encoding of
+    data.  Acknowledged additional contributors, and changed one of
+    the authors' addresses.
+   </DD>
+  </DL>
+<!--#endif -->
+
+  <H2>
+   <A NAME="Contents">
+    Table of Contents
+   </A>
+  </H2>
+  <DIV ALIGN="CENTER">
+   <PRE>
+  1 Introduction..............................................<A
+                                                               HREF="#1.0"
+                                                              >TBD</A>
+   1.1 Purpose................................................<A
+                                                               HREF="#1.1"
+                                                              >TBD</A>
+   1.2 Requirements...........................................<A
+                                                               HREF="#1.2"
+                                                              >TBD</A>
+   1.3 Specifications.........................................<A
+                                                               HREF="#1.3"
+                                                              >TBD</A>
+   1.4 Terminology............................................<A
+                                                               HREF="#1.4"
+                                                              >TBD</A>
+  2 Notational Conventions and Generic Grammar................<A
+                                                               HREF="#2.0"
+                                                              >TBD</A>
+   2.1 Augmented BNF..........................................<A
+                                                               HREF="#2.1"
+                                                              >TBD</A>
+   2.2 Basic Rules............................................<A
+                                                               HREF="#2.2"
+                                                              >TBD</A>
+  3 Protocol Parameters.......................................<A
+                                                               HREF="#3.0"
+                                                              >TBD</A>
+   3.1 URL Encoding...........................................<A
+                                                               HREF="#3.1"
+                                                              >TBD</A>
+   3.2 The Script-URI.........................................<A
+                                                               HREF="#3.2"
+                                                              >TBD</A>
+  4 Invoking the Script.......................................<A
+                                                               HREF="#4.0"
+                                                              >TBD</A>
+  5 The CGI Script Command Line...............................<A
+                                                               HREF="#5.0"
+                                                              >TBD</A>
+  6 Data Input to the CGI Script..............................<A
+                                                               HREF="#6.0"
+                                                              >TBD</A>
+   6.1 Request Metadata (Metavariables).......................<A
+                                                               HREF="#6.1"
+                                                              >TBD</A>
+    6.1.1 AUTH_TYPE...........................................<A
+                                                               HREF="#6.1.1"
+                                                              >TBD</A>
+    6.1.2 CONTENT_LENGTH......................................<A
+                                                               HREF="#6.1.2"
+                                                              >TBD</A>
+    6.1.3 CONTENT_TYPE........................................<A
+                                                               HREF="#6.1.3"
+                                                              >TBD</A>
+    6.1.4 GATEWAY_INTERFACE...................................<A
+                                                               HREF="#6.1.4"
+                                                              >TBD</A>
+    6.1.5 Protocol-Specific Metavariables.....................<A
+                                                               HREF="#6.1.5"
+                                                              >TBD</A>
+    6.1.6 PATH_INFO...........................................<A
+                                                               HREF="#6.1.6"
+                                                              >TBD</A>
+    6.1.7 PATH_TRANSLATED.....................................<A
+                                                               HREF="#6.1.7"
+                                                              >TBD</A>
+    6.1.8 QUERY_STRING........................................<A
+                                                               HREF="#6.1.8"
+                                                              >TBD</A>
+    6.1.9 REMOTE_ADDR.........................................<A
+                                                               HREF="#6.1.9"
+                                                              >TBD</A>
+    6.1.10 REMOTE_HOST........................................<A
+                                                               HREF="#6.1.10"
+                                                              >TBD</A>
+    6.1.11 REMOTE_IDENT.......................................<A
+                                                               HREF="#6.1.11"
+                                                              >TBD</A>
+    6.1.12 REMOTE_USER........................................<A
+                                                               HREF="#6.1.12"
+                                                              >TBD</A>
+    6.1.13 REQUEST_METHOD.....................................<A
+                                                               HREF="#6.1.13"
+                                                              >TBD</A>
+    6.1.14 SCRIPT_NAME........................................<A
+                                                               HREF="#6.1.14"
+                                                              >TBD</A>
+    6.1.15 SERVER_NAME........................................<A
+                                                               HREF="#6.1.15"
+                                                              >TBD</A>
+    6.1.16 SERVER_PORT........................................<A
+                                                               HREF="#6.1.16"
+                                                              >TBD</A>
+    6.1.17 SERVER_PROTOCOL....................................<A
+                                                               HREF="#6.1.17"
+                                                              >TBD</A>
+    6.1.18 SERVER_SOFTWARE....................................<A
+                                                               HREF="#6.1.18"
+                                                              >TBD</A>
+    6.2 Request Message-Bodies................................<A
+                                                               HREF="#6.2"
+                                                              >TBD</A>
+  7 Data Output from the CGI Script...........................<A
+                                                               HREF="#7.0"
+                                                              >TBD</A>
+   7.1 Non-Parsed Header Output...............................<A
+                                                               HREF="#7.1"
+                                                              >TBD</A>
+   7.2 Parsed Header Output...................................<A
+                                                               HREF="#7.2"
+                                                              >TBD</A>
+    7.2.1 CGI header fields...................................<A
+                                                               HREF="#7.2.1"
+                                                              >TBD</A>
+     7.2.1.1 Content-Type.....................................<A
+                                                               HREF="#7.2.1.1"
+                                                              >TBD</A>
+     7.2.1.2 Location.........................................<A
+                                                               HREF="#7.2.1.2"
+                                                              >TBD</A>
+     7.2.1.3 Status...........................................<A
+                                                               HREF="#7.2.1.3"
+                                                              >TBD</A>
+     7.2.1.4 Extension header fields..........................<A
+                                                               HREF="#7.2.1.3"
+                                                              >TBD</A>
+    7.2.2 HTTP header fields..................................<A
+                                                               HREF="#7.2.2"
+                                                              >TBD</A>
+  8 Server Implementation.....................................<A
+                                                               HREF="#8.0"
+                                                              >TBD</A>
+   8.1 Requirements for Servers...............................<A
+                                                               HREF="#8.1"
+                                                              >TBD</A>
+    8.1.1 Script-URI..........................................<A
+                                                               HREF="#8.1"
+                                                              >TBD</A>
+    8.1.2 Request Message-body Handling.......................<A
+                                                               HREF="#8.1.2"
+                                                              >TBD</A>
+    8.1.3 Required Metavariables..............................<A
+                                                               HREF="#8.1.3"
+                                                              >TBD</A>
+    8.1.4 Response Compliance.................................<A
+                                                               HREF="#8.1.4"
+                                                              >TBD</A>
+   8.2 Recommendations for Servers............................<A
+                                                               HREF="#8.2"
+                                                              >TBD</A>
+   8.3 Summary of Metavariables...............................<A
+                                                               HREF="#8.3"
+                                                              >TBD</A>
+  9 Script Implementation.....................................<A
+                                                               HREF="#9.0"
+                                                              >TBD</A>
+   9.1 Requirements for Scripts...............................<A
+                                                               HREF="#9.1"
+                                                              >TBD</A>
+   9.2 Recommendations for Scripts............................<A
+                                                               HREF="#9.2"
+                                                              >TBD</A>
+  10 System Specifications....................................<A
+                                                               HREF="#10.0"
+                                                              >TBD</A>
+   10.1 AmigaDOS..............................................<A
+                                                               HREF="#10.1"
+                                                              >TBD</A>
+   10.2 Unix..................................................<A
+                                                               HREF="#10.2"
+                                                              >TBD</A>
+  11 Security Considerations..................................<A
+                                                               HREF="#11.0"
+                                                              >TBD</A>
+   11.1 Safe Methods..........................................<A
+                                                               HREF="#11.1"
+                                                              >TBD</A>
+   11.2 HTTP Header Fields Containing Sensitive Information...<A
+                                                               HREF="#11.2"
+                                                              >TBD</A>
+   11.3 Script Interference with the Server...................<A
+                                                               HREF="#11.3"
+                                                              >TBD</A>
+   11.4 Data Length and Buffering Considerations..............<A
+                                                               HREF="#11.4"
+                                                              >TBD</A>
+   11.5 Stateless Processing..................................<A
+                                                               HREF="#11.5"
+                                                              >TBD</A>
+  12 Acknowledgments..........................................<A
+                                                               HREF="#12.0"
+                                                              >TBD</A>
+  13 References...............................................<A
+                                                               HREF="#13.0"
+                                                              >TBD</A>
+  14 Authors' Addresses.......................................<A
+                                                               HREF="#14.0"
+                                                              >TBD</A>
+     </PRE>
+  </DIV>
+
+  <H2>
+   <A NAME="1.0">
+    1. Introduction
+   </A>
+  </H2>
+
+  <H3>
+   <A NAME="1.1">
+    1.1. Purpose
+   </A>
+  </H3>
+  <P>
+  Together the HTTP [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>] server
+  and the CGI script are responsible
+  for servicing a client
+  request by sending back responses. The client
+  request comprises a Universal Resource Identifier (URI)
+  [<A HREF="#[1]">1</A>], a
+  request method, and various ancillary
+  information about the request
+  provided by the transport mechanism.
+  </P>
+  <P>
+  The CGI defines the abstract parameters, known as
+  metavariables,
+  which describe the client's
+  request. Together with a
+  concrete programmer interface this specifies a platform-independent
+  interface between the script and the HTTP server.
+  </P>
+
+  <H3>
+   <A NAME="1.2">
+    1.2. Requirements
+   </A>
+  </H3>
+  <P>
+  This specification uses the same words as RFC 1123
+  [<A HREF="#[5]">5</A>] to define the
+  significance of each particular requirement. These are:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <DL>
+   <DT><EM>MUST</EM>
+   </DT>
+   <DD>
+    <P>
+    This word or the adjective 'required' means that the item is an
+    absolute requirement of the specification.
+    </P>
+   </DD>
+   <DT><EM>SHOULD</EM>
+   </DT>
+   <DD>
+    <P>
+    This word or the adjective 'recommended' means that there may
+    exist valid reasons in particular circumstances to ignore this
+    item, but the full implications should be understood and the case
+    carefully weighed before choosing a different course.
+    </P>
+   </DD>
+   <DT><EM>MAY</EM>
+   </DT>
+   <DD>
+    <P>
+    This word or the adjective 'optional' means that this item is
+    truly optional. One vendor may choose to include the item because
+    a particular marketplace requires it or because it enhances the
+    product, for example; another vendor may omit the same item.
+    </P>
+   </DD>
+  </DL>
+  <P>
+  An implementation is not compliant if it fails to satisfy one or more
+  of the 'must' requirements for the protocols it implements. An
+  implementation that satisfies all of the 'must' and all of the
+  'should' requirements for its features is said to be 'unconditionally
+  compliant'; one that satisfies all of the 'must' requirements but not
+  all of the 'should' requirements for its features is said to be
+  'conditionally compliant.'
+  </P>
+
+  <H3>
+   <A NAME="1.3">
+    1.3. Specifications
+   </A>
+  </H3>
+  <P>
+  Not all of the functions and features of the CGI are defined in the
+  main part of this specification. The following phrases are used to
+  describe the features which are not specified:
+  </P>
+  <DL>
+   <DT><EM>system defined</EM>
+   </DT>
+   <DD>
+    <P>
+    The feature may differ between systems, but must be the same for
+    different implementations using the same system. A system will
+    usually identify a class of operating-systems. Some systems are
+    defined in
+    <A HREF="#10.0"
+    >section 10</A> of this document.
+    New systems may be defined
+    by new specifications without revision of this document.
+    </P>
+   </DD>
+   <DT><EM>implementation defined</EM>
+   </DT>
+   <DD>
+    <P>
+    The behaviour of the feature may vary from implementation to
+    implementation, but a particular implementation must document its
+    behaviour.
+    </P>
+   </DD>
+  </DL>
+
+  <H3>
+   <A NAME="1.4">
+    1.4. Terminology
+   </A>
+  </H3>
+  <P>
+  This specification uses many terms defined in the HTTP/1.1
+  specification [<A HREF="#[8]">8</A>]; however, the following terms are
+  used here in a
+  sense which may not accord with their definitions in that document,
+  or with their common meaning.
+  </P>
+
+  <DL>
+   <DT><EM>metavariable</EM>
+   </DT>
+   <DD>
+    <P>
+    A named parameter that carries information from the server to the
+    script. It is not necessarily a variable in the operating-system's
+    environment, although that is the most common implementation.
+    </P>
+   </DD>
+
+   <DT><EM>script</EM>
+   </DT>
+   <DD>
+    <P>
+    The software which is invoked by the server <EM>via</EM> this
+    interface. It
+    need not be a standalone program, but could be a
+    dynamically-loaded or shared library, or even a subroutine in the
+    server.  It <EM>may</EM> be a set of statements
+    interpreted at run-time, as the term 'script' is frequently
+    understood, but that is not a requirement and within the context
+    of this specification the term has the broader definition stated.
+    </P>
+   </DD>
+   <DT><EM>server</EM>
+   </DT>
+   <DD>
+    <P>
+    The application program which invokes the script in order to service
+    requests.
+    </P>
+   </DD>
+  </DL>
+
+  <H2>
+   <A NAME="2.0">
+    2. Notational Conventions and Generic Grammar
+   </A>
+  </H2>
+
+  <H3>
+   <A NAME="2.1">
+    2.1. Augmented BNF
+   </A>
+  </H3>
+  <P>
+  All of the mechanisms specified in this document are described in
+  both prose and an augmented Backus-Naur Form (BNF) similar to that
+  used by RFC 822 [<A HREF="#[6]">6</A>]. This augmented BNF contains
+  the following constructs:
+  </P>
+  <DL>
+   <DT>name = definition
+   </DT>
+   <DD>
+    <P>
+    The
+    definition by the equal character ("="). Whitespace is only
+    significant in that continuation lines of a definition are
+    indented.
+    </P>
+   </DD>
+   <DT>"literal"
+   </DT>
+   <DD>
+    <P>
+    Quotation marks (") surround literal text, except for a literal
+    quotation mark, which is surrounded by angle-brackets ("&lt;" and "&gt;").
+    Unless stated otherwise, the text is case-sensitive.
+    </P>
+   </DD>
+   <DT>rule1 | rule2
+   </DT>
+   <DD>
+    <P>
+    Alternative rules are separated by a vertical bar ("|").
+    </P>
+   </DD>
+   <DT>(rule1 rule2 rule3)
+   </DT>
+   <DD>
+    <P>
+    Elements enclosed in parentheses are treated as a single element.
+    </P>
+   </DD>
+   <DT>*rule
+   </DT>
+   <DD>
+    <P>
+    A rule preceded by an asterisk ("*") may have zero or more
+    occurrences. A rule preceded by an integer followed by an asterisk
+    must occur at least the specified number of times.
+    </P>
+   </DD>
+   <DT>[rule]
+   </DT>
+   <DD>
+    <P>
+    An element enclosed in square
+    brackets ("[" and "]") is optional.
+    </P>
+   </DD>
+  </DL>
+
+  <H3>
+   <A NAME="2.2">
+    2.2. Basic Rules
+   </A>
+  </H3>
+  <P>
+  The following rules are used throughout this specification to
+  describe basic parsing constructs.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    alpha         = lowalpha | hialpha
+    alphanum      = alpha | digit
+    lowalpha      = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h"
+                    | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p"
+                    | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x"
+                    | "y" | "z"
+    hialpha       = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H"
+                    | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P"
+                    | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X"
+                    | "Y" | "Z"
+    digit         = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7"
+                    | "8" | "9"
+    hex           = digit | "A" | "B" | "C" | "D" | "E" | "F" | "a"
+                    | "b" | "c" | "d" | "e" | "f"
+    escaped       = "%" hex hex
+    OCTET         = &lt;any 8-bit sequence of data&gt;
+    CHAR          = &lt;any US-ASCII character (octets 0 - 127)&gt;
+    CTL           = &lt;any US-ASCII control character
+                    (octets 0 - 31) and DEL (127)&gt;
+    CR            = &lt;US-ASCII CR, carriage return (13)&gt;
+    LF            = &lt;US-ASCII LF, linefeed (10)&gt;
+    SP            = &lt;US-ASCII SP, space (32)&gt;
+    HT            = &lt;US-ASCII HT, horizontal tab (9)&gt;
+    NL            = CR | LF
+    LWSP          = SP | HT | NL
+    tspecial      = "(" | ")" | "@" | "," | ";" | ":" | "\" | &lt;"&gt;
+                    | "/" | "[" | "]" | "?" | "&lt;" | "&gt;" | "{" | "}"
+                    | SP | HT | NL
+    token         = 1*&lt;any CHAR except CTLs or tspecials&gt;
+    quoted-string = ( &lt;"&gt; *qdtext &lt;"&gt; ) | ( "&lt;" *qatext "&gt;")
+    qdtext        = &lt;any CHAR except &lt;"&gt; and CTLs but including LWSP&gt;
+    qatext        = &lt;any CHAR except "&lt;", "&gt;" and CTLs but
+                    including LWSP&gt;
+    mark          = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+    unreserved    = alphanum | mark
+    reserved      = ";" | "/" | "?" | ":" | "@" | "&amp;" | "=" |
+                    "$" | ","
+    uric          = reserved | unreserved | escaped
+  </PRE>
+  <P>
+  Note that newline (NL) need not be a single character, but can be a
+  character sequence.
+  </P>
+
+  <H2>
+   <A NAME="3.0">
+    3. Protocol Parameters
+   </A>
+  </H2>
+
+  <H3>
+   <A NAME="3.1">
+    3.1. URL Encoding
+   </A>
+  </H3>
+  <P>
+  Some variables and constructs used here are described as being
+  'URL-encoded'. This encoding is described in section
+  2 of RFC
+  2396
+  [<A HREF="#[4]">4</A>].
+  </P>
+  <P>
+  An alternate "shortcut" encoding for representing the space
+  character exists and is in common use.  Scripts MUST be prepared to
+  recognise both '+' and '%20' as an encoded space in a
+  URL-encoded value.
+  </P>
+  <P>
+  Note that some unsafe characters may have different semantics if
+  they are encoded. The definition of which characters are unsafe
+  depends on the context.
+  For example, the following two URLs do not
+  necessarily refer to the same resource:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    http://somehost.com/somedir%2Fvalue
+    http://somehost.com/somedir/value
+  </PRE>
+  <P>
+  See section
+  2 of RFC
+  2396 [<A HREF="#[4]">4</A>]
+  for authoritative treatment of this issue.
+  </P>
+
+  <H3>
+   <A NAME="3.2">
+    3.2. The Script-URI
+   </A>
+  </H3>
+  <P>
+  The 'Script-URI' is defined as the URI of the resource identified
+  by the metavariables.   Often,
+  this URI will be the same as
+  the URI requested by the client (the 'Client-URI'); however, it need
+  not be. Instead, it could be a URI invented by the server, and so it
+  can only be used in the context of the server and its CGI interface.
+  </P>
+  <P>
+  The Script-URI has the syntax of generic-RL as defined in section 2.1
+  of RFC 1808 [<A HREF="#[7]">7</A>], with the exception that object
+  parameters and
+  fragment identifiers are not permitted:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    &lt;scheme&gt;://&lt;host&gt;&lt;port&gt;/&lt;path&gt;?&lt;query&gt;
+  </PRE>
+  <P>
+  The various components of the
+  Script-URI
+  are defined by some of the
+  metavariables (see
+  <A HREF="#4.0">section 4</A>
+  below);
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    script-uri = protocol "://" SERVER_NAME ":" SERVER_PORT enc-script
+                 enc-path-info "?" QUERY_STRING
+  </PRE>
+  <P>
+  where 'protocol' is obtained
+  from SERVER_PROTOCOL, 'enc-script' is a
+  URL-encoded version of SCRIPT_NAME and 'enc-path-info' is a
+  URL-encoded version of PATH_INFO.  See
+  <A HREF="#4.6">section 4.6</A> for more information about the PATH_INFO
+  metavariable.
+  </P>
+  <P>
+  Note that the scheme and the protocol are <EM>not</EM> identical;
+  for instance, a resource accessed <EM>via</EM> an SSL mechanism
+  may have a Client-URI with a scheme of "<SAMP>https</SAMP>"
+  rather than "<SAMP>http</SAMP>".   CGI/1.1 provides no means
+  for the script to reconstruct this, and therefore
+  the Script-URI includes the base protocol used.
+  </P>
+
+  <H2>
+   <A NAME="4.0">
+    4. Invoking the Script
+   </A>
+  </H2>
+  <P>
+  The
+  script is invoked in a system defined manner. Unless specified
+  otherwise, the file containing the script will be invoked as an
+  executable program.
+  </P>
+
+  <H2>
+   <A NAME="5.0">
+    5. The CGI Script Command Line
+   </A>
+  </H2>
+  <P>
+  Some systems support a method for supplying an array of strings to
+  the CGI script. This is only used in the case of an 'indexed' query.
+  This is identified by a "GET" or "HEAD" HTTP request with a URL
+  query
+  string not containing any unencoded "=" characters. For such a
+  request,
+  servers SHOULD parse the search string
+  into words, using the following rules:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    search-string = search-word *( "+" search-word )
+    search-word   = 1*schar
+    schar         = xunreserved | escaped | xreserved
+    xunreserved   = alpha | digit | xsafe | extra
+    xsafe         = "$" | "-" | "_" | "."
+    xreserved     = ";" | "/" | "?" | ":" | "@" | "&"
+  </PRE>
+  <P>
+  After parsing, each word is URL-decoded, optionally encoded in a
+  system defined manner,
+  and then the argument list is set to the list
+  of words.
+  </P>
+  <P>
+  If the server cannot create any part of the argument list, then the
+  server SHOULD NOT generate any command line information. For example, the
+  number of arguments may be greater than operating system or server
+  limitations permit, or one of the words may not be representable as an
+  argument.
+  </P>
+  <P>
+  Scripts SHOULD check to see if the QUERY_STRING value contains an
+  unencoded "=" character, and SHOULD NOT use the command line arguments
+  if it does.
+  </P>
+
+  <H2>
+   <A NAME="6.0">
+    6. Data Input to the CGI Script
+   </A>
+  </H2>
+  <P>
+  Information about a request comes from two different sources: the
+  request header, and any associated
+  message-body.
+  Servers MUST
+  make portions of this information available to
+   scripts.
+  </P>
+
+  <H3>
+   <A NAME="6.1">
+    6.1. Request Metadata
+    (Metavariables)
+   </A>
+  </H3>
+  <P>
+  Each CGI server
+  implementation MUST define a mechanism
+  to pass data about the request from
+  the server to the script.
+  The metavariables containing these
+  data
+  are accessed by the script in a system
+  defined manner.
+  The
+  representation of the characters in the
+  metavariables is
+  system defined.
+  </P>
+  <P>
+  This specification does not distinguish between the representation of
+  null values and missing ones.  Whether null or missing values
+  (such as a query component of "?" or "", respectively) are represented
+  by undefined metavariables or by metavariables with values of "" is
+  implementation-defined.
+  </P>
+  <P>
+  Case is not significant in the
+  metavariable
+  names, in that there cannot be two
+  different variables
+  whose names differ in case only. Here they are
+  shown using a canonical representation of capitals plus underscore
+  ("_"). The actual representation of the names is system defined; for
+  a particular system the representation MAY be defined differently
+  than this.
+  </P>
+  <P>
+  Metavariable
+  values MUST be
+  considered case-sensitive except as noted
+  otherwise.
+  </P>
+  <P>
+  The canonical
+  metavariables
+  defined by this specification are:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    AUTH_TYPE
+    CONTENT_LENGTH
+    CONTENT_TYPE
+    GATEWAY_INTERFACE
+    PATH_INFO
+    PATH_TRANSLATED
+    QUERY_STRING
+    REMOTE_ADDR
+    REMOTE_HOST
+    REMOTE_IDENT
+    REMOTE_USER
+    REQUEST_METHOD
+    SCRIPT_NAME
+    SERVER_NAME
+    SERVER_PORT
+    SERVER_PROTOCOL
+    SERVER_SOFTWARE
+  </PRE>
+  <P>
+  Metavariables with names beginning with the protocol name (<EM>e.g.</EM>,
+  "HTTP_ACCEPT") are also canonical in their description of request header
+  fields.  The number and meaning of these fields may change independently
+  of this specification.  (See also <A HREF="#6.1.5">section 6.1.5</A>.)
+  </P>
+
+  <H4>
+   <A NAME="6.1.1">
+    6.1.1. AUTH_TYPE
+   </A>
+  </H4>
+  <P>
+  This variable is specific to requests made
+  <EM>via</EM> the
+  "<CODE>http</CODE>"
+  scheme.
+  </P>
+  <P>
+  If the Script-URI
+  required access authentication for external
+  access, then the server
+  MUST set
+  the value of
+  this variable
+  from the '<SAMP>auth-scheme</SAMP>' token in
+  the request's "<SAMP>Authorization</SAMP>" header
+  field.
+  Otherwise
+  it is
+  set to NULL.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    AUTH_TYPE   = "" | auth-scheme
+    auth-scheme = "Basic" | "Digest" | token
+  </PRE>
+  <P>
+  HTTP access authentication schemes are described in section 11 of the
+  HTTP/1.1 specification [<A HREF="#[8]">8</A>]. The auth-scheme is
+  not case-sensitive.
+  </P>
+  <P>
+  Servers
+  MUST
+  provide this metavariable
+  to scripts if the request
+  header included an "<SAMP>Authorization</SAMP>" field
+  that was authenticated.
+  </P>
+
+  <H4>
+   <A NAME="6.1.2">
+    6.1.2. CONTENT_LENGTH
+   </A>
+  </H4>
+  <P>
+  This
+  metavariable
+  is set to the
+  size of the message-body
+  entity attached to the request, if any, in decimal
+  number of octets. If no data are attached, then this
+  metavariable
+  is either NULL or not
+  defined. The syntax is
+  the same as for
+  the HTTP "<SAMP>Content-Length</SAMP>" header field (section 14.14, HTTP/1.1
+  specification [<A HREF="#[8]">8</A>]).
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    CONTENT_LENGTH = "" | 1*digit
+  </PRE>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts if the request
+  was accompanied by a
+  message-body entity.
+  </P>
+
+  <H4>
+   <A NAME="6.1.3">
+    6.1.3. CONTENT_TYPE
+   </A>
+  </H4>
+  <P>
+  If the request includes a
+  message-body,
+  CONTENT_TYPE is set
+  to
+  the Internet Media Type
+  [<A HREF="#[9]">9</A>] of the attached
+  entity if the type was provided <EM>via</EM>
+  a "<SAMP>Content-type</SAMP>" field in the
+  request header, or if the server can determine it in the absence
+  of a supplied "<SAMP>Content-type</SAMP>" field. The syntax is the
+  same as for the HTTP
+  "<SAMP>Content-Type</SAMP>" header field.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    CONTENT_TYPE = "" | media-type
+    media-type   = type "/" subtype *( ";" parameter)
+    type         = token
+    subtype      = token
+    parameter    = attribute "=" value
+    attribute    = token
+    value        = token | quoted-string
+  </PRE>
+  <P>
+  The type, subtype,
+  and parameter attribute names are not
+  case-sensitive. Parameter values MAY be case sensitive.
+  Media types and their use in HTTP are described
+  in section 3.7 of the
+  HTTP/1.1 specification [<A HREF="#[8]">8</A>].
+  </P>
+  <P>
+  Example:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    application/x-www-form-urlencoded
+  </PRE>
+  <P>
+  There is no default value for this variable. If and only if it is
+  unset, then the script MAY attempt to determine the media type from
+  the data received. If the type remains unknown, then
+  the script MAY choose to either assume a
+  content-type of
+  <SAMP>application/octet-stream</SAMP>
+  or reject the request with  a 415 ("Unsupported Media Type")
+  error.  See <A HREF="#7.2.1.3">section 7.2.1.3</A>
+  for more information about returning error status values.
+  </P>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts if
+  a "<SAMP>Content-Type</SAMP>" field was present
+  in the original request header.  If the server receives a request
+  with an attached entity but no "<SAMP>Content-Type</SAMP>"
+  header field, it MAY attempt to
+  determine the correct datatype, or it MAY omit this
+  metavariable when
+  communicating the request information to the script.
+  </P>
+
+  <H4>
+   <A NAME="6.1.4">
+    6.1.4. GATEWAY_INTERFACE
+   </A>
+  </H4>
+  <P>
+  This
+  metavariable
+  is set to
+  the dialect of CGI being used
+  by the server to communicate with the script.
+  Syntax:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    GATEWAY_INTERFACE = "CGI" "/" major "." minor
+    major             = 1*digit
+    minor             = 1*digit
+  </PRE>
+  <P>
+  Note that the major and minor numbers are treated as separate
+  integers and hence each may be
+  more than a single
+  digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in turn
+  is lower than CGI/12.3. Leading zeros in either
+  the major or the minor number MUST be ignored by scripts and
+  SHOULD NOT be generated by servers.
+  </P>
+  <P>
+  This document defines the 1.1 version of the CGI interface
+  ("CGI/1.1").
+  </P>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.5">
+    6.1.5. Protocol-Specific Metavariables
+   </A>
+  </H4>
+  <P>
+  These metavariables are specific to
+  the protocol
+  <EM>via</EM> which the request is made.
+  Interpretation of these variables depends on the value of
+  the
+  SERVER_PROTOCOL
+  metavariable
+  (see
+  <A HREF="#6.1.17">section 6.1.17</A>).
+  </P>
+  <P>
+  Metavariables
+  with names beginning with "HTTP_" contain
+  values from the request header, if the
+  scheme used was HTTP.
+  Each
+  HTTP header field name is converted to upper case, has all occurrences of
+  "-" replaced with "_",
+  and has "HTTP_" prepended to  form
+  the metavariable name.
+  Similar transformations are applied for other
+  protocols.
+  The header data MAY be presented as sent
+  by the client, or MAY be rewritten in ways which do not change its
+  semantics. If multiple header fields with the same field-name are received
+  then  the server
+  MUST  rewrite them as though they
+  had been received as a single header field having the same
+  semantics before being represented in a
+  metavariable.
+  Similarly, a header field that is received on more than one line
+  MUST be merged into a single line. The server MUST, if necessary,
+  change the representation of the data (for example, the character
+  set) to be appropriate for a CGI
+  metavariable.
+  <!-- ###NOTE: See if 2068 describes this thoroughly, and
+  point there if so. -->
+  </P>
+  <P>
+  Servers are
+  not required to create
+  metavariables for all
+  the request
+  header fields that they
+  receive. In particular,
+  they MAY
+  decline to make available any
+  header fields carrying authentication information, such as
+  "<SAMP>Authorization</SAMP>", or
+  which are available to the script
+  <EM>via</EM> other metavariables,
+  such as "<SAMP>Content-Length</SAMP>" and "<SAMP>Content-Type</SAMP>".
+  </P>
+
+  <H4>
+   <A NAME="6.1.6">
+    6.1.6. PATH_INFO
+   </A>
+  </H4>
+  <P>
+  The PATH_INFO
+  metavariable
+  specifies
+  a path to be interpreted by the CGI script. It identifies the
+  resource or sub-resource to be returned
+  by the CGI
+  script, and it is derived from the portion
+  of the URI path following the script name but preceding
+  any query data.
+  The syntax
+  and semantics are similar to a decoded HTTP URL
+  'path' token
+  (defined in
+  RFC 2396
+  [<A HREF="#[4]">4</A>]), with the exception
+  that a PATH_INFO of "/"
+  represents a single void path segment.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    PATH_INFO = "" | ( "/" path )
+    path      = segment *( "/" segment )
+    segment   = *pchar
+    pchar     = &lt;any CHAR except "/"&gt;
+  </PRE>
+  <P>
+  The PATH_INFO string is the trailing part of the &lt;path&gt; component of
+  the Script-URI
+  (see <A HREF="#3.2">section 3.2</A>)
+  that follows the SCRIPT_NAME
+  portion of the path.
+  </P>
+  <P>
+  Servers MAY impose their own restrictions and
+  limitations on what values they will accept for PATH_INFO, and MAY
+  reject or edit any values they
+  consider objectionable before passing
+  them to the script.
+  </P>
+  <P>
+  Servers MUST make this URI component available
+  to CGI scripts.  The PATH_INFO
+  value is case-sensitive, and the
+  server MUST preserve the case of the PATH_INFO element of the URI
+  when making it available to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.7">
+    6.1.7. PATH_TRANSLATED
+   </A>
+  </H4>
+  <P>
+  PATH_TRANSLATED is derived by taking any path-info component of the
+  request URI (see
+  <A HREF="#6.1.6">section 6.1.6</A>), decoding it
+  (see <A HREF="#3.1">section 3.1</A>), parsing it as a URI in its own
+  right, and performing any virtual-to-physical
+  translation appropriate to map it onto the
+  server's document repository structure.
+  If the request URI includes no path-info
+  component, the PATH_TRANSLATED metavariable SHOULD NOT be defined.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    PATH_TRANSLATED = *CHAR
+  </PRE>
+  <P>
+  For a request such as the following:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo
+  </PRE>
+  <P>
+  the PATH_INFO component would be decoded, and the result
+  parsed as though it were a request for the following:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    http://somehost.com/this.is.the.path.info
+  </PRE>
+  <P>
+  This would then be translated to a
+  location in the server's document repository,
+  perhaps a filesystem path something
+  like this:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    /usr/local/www/htdocs/this.is.the.path.info
+  </PRE>
+  <P>
+  The result of the translation is the value of PATH_TRANSLATED.
+  </P>
+  <P>
+  The value of PATH_TRANSLATED may or may not map to a valid
+  repository
+  location.
+  Servers MUST preserve the case of the path-info
+  segment if and only if the underlying
+  repository
+  supports case-sensitive
+  names.  If the
+  repository
+  is only case-aware, case-preserving, or case-blind
+  with regard to
+  document names,
+  servers are not required to preserve the
+  case of the original segment through the translation.
+  </P>
+  <P>
+  The
+  translation
+  algorithm the server uses to derive PATH_TRANSLATED is
+  implementation defined; CGI scripts which use this variable may
+  suffer limited portability.
+  </P>
+  <P>
+  Servers SHOULD provide this metavariable
+  to scripts if and only if the request URI includes a
+  path-info component.
+  </P>
+
+  <H4>
+   <A NAME="6.1.8">
+    6.1.8. QUERY_STRING
+   </A>
+  </H4>
+  <P>
+  A URL-encoded
+  string; the &lt;query&gt; part of the
+  Script-URI.
+  (See
+  <A HREF="#3.2">section 3.2</A>.)
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    QUERY_STRING = query-string
+    query-string = *uric
+  </PRE>
+  <P>
+  The URL syntax for a  query
+  string is described in
+  section 3 of
+  RFC 2396
+  [<A HREF="#[4]">4</A>].
+  </P>
+  <P>
+  Servers MUST supply this value to scripts.
+  The QUERY_STRING value is case-sensitive.
+  If the Script-URI does not include a query component,
+  the QUERY_STRING metavariable MUST be defined as an empty string ("").
+  </P>
+
+  <H4>
+   <A NAME="6.1.9">
+    6.1.9. REMOTE_ADDR
+   </A>
+  </H4>
+  <P>
+  The IP address of the client
+  sending the request to the server. This
+  is not necessarily that of the user
+  agent
+  (such as if the request came through a proxy).
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    REMOTE_ADDR  = hostnumber
+    hostnumber   = ipv4-address | ipv6-address
+  </PRE>
+  <P>
+  The definitions of <SAMP>ipv4-address</SAMP> and <SAMP>ipv6-address</SAMP>
+  are provided in Appendix B of RFC 2373 [<A HREF="#[13]">13</A>].
+  </P>
+  <P>
+  Servers MUST supply this value to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.10">
+    6.1.10. REMOTE_HOST
+   </A>
+  </H4>
+  <P>
+  The fully qualified domain name of the
+  client sending the request to
+  the server, if available, otherwise NULL.
+  (See <A HREF="#6.1.9">section 6.1.9</A>.)
+  Fully qualified domain names take the form as described in
+  section 3.5 of RFC 1034 [<A HREF="#[10]">10</A>] and section 2.1 of
+  RFC 1123 [<A HREF="#[5]">5</A>].  Domain names are not case sensitive.
+  </P>
+  <P>
+  Servers SHOULD provide this information to
+  scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.11">
+    6.1.11. REMOTE_IDENT
+   </A>
+  </H4>
+  <P>
+  The identity information reported about the connection by a
+  RFC 1413 [<A HREF="#[11]">11</A>] request to the remote agent, if
+  available. Servers
+  MAY choose not
+  to support this feature, or not to request the data
+  for efficiency reasons.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    REMOTE_IDENT = *CHAR
+  </PRE>
+  <P>
+  The data returned
+  may be used for authentication purposes, but the level
+  of trust reposed in them should be minimal.
+  </P>
+  <P>
+  Servers MAY supply this information to scripts if the
+  RFC1413 [<A HREF="#[11]">11</A>] lookup is performed.
+  </P>
+
+  <H4>
+   <A NAME="6.1.12">
+    6.1.12. REMOTE_USER
+   </A>
+  </H4>
+  <P>
+  If the request required authentication using the "Basic"
+  mechanism (<EM>i.e.</EM>, the AUTH_TYPE
+  metavariable is set
+  to "Basic"), then the value of the REMOTE_USER
+  metavariable is set to the
+  user-ID supplied.  In all other cases
+  the value of this metavariable
+  is undefined.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    REMOTE_USER = *OCTET
+  </PRE>
+  <P>
+  This variable is specific to requests made <EM>via</EM> the
+  HTTP protocol.
+  </P>
+  <P>
+  Servers SHOULD provide this metavariable
+  to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.13">
+    6.1.13. REQUEST_METHOD
+   </A>
+  </H4>
+  <P>
+  The REQUEST_METHOD
+  metavariable
+  is set to the
+  method with which the request was made, as described in section
+  5.1.1 of the HTTP/1.0 specification [<A HREF="#[3]">3</A>] and
+  section 5.1.1 of the
+  HTTP/1.1 specification [<A HREF="#[8]">8</A>].
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    REQUEST_METHOD   = http-method
+    http-method      = "GET" | "HEAD" | "POST" | "PUT" | "DELETE"
+                       | "OPTIONS" | "TRACE" | extension-method
+    extension-method = token
+  </PRE>
+  <P>
+  The method is case sensitive.
+  CGI/1.1 servers MAY choose to process some methods
+  directly rather than passing them to scripts.
+  </P>
+  <P>
+  This variable is specific to requests made with HTTP.
+  </P>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.14">
+    6.1.14. SCRIPT_NAME
+   </A>
+  </H4>
+  <P>
+  The SCRIPT_NAME
+  metavariable
+  is
+  set to a URL path that could identify the CGI script (rather than the
+  script's
+  output). The syntax and semantics are identical to a
+  decoded HTTP URL 'path' token
+  (see RFC 2396
+  [<A HREF="#[4]">4</A>]).
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    SCRIPT_NAME = "" | ( "/" [ path ] )
+  </PRE>
+  <P>
+  The SCRIPT_NAME string is some leading part of the &lt;path&gt; component
+  of the Script-URI derived in some
+  implementation defined manner.
+  No PATH_INFO or QUERY_STRING segments
+  (see sections <A HREF="#6.1.6">6.1.6</A> and
+  <A HREF="#6.1.8">6.1.8</A>) are included
+  in the SCRIPT_NAME value.
+  </P>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.15">
+    6.1.15. SERVER_NAME
+   </A>
+  </H4>
+  <P>
+  The SERVER_NAME
+  metavariable
+  is set to the
+  name  of the
+  server, as
+  derived from the &lt;host&gt; part of the
+  Script-URI
+  (see <A HREF="#3.2">section 3.2</A>).
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    SERVER_NAME = hostname | hostnumber
+  </PRE>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.16">
+    6.1.16. SERVER_PORT
+   </A>
+  </H4>
+  <P>
+  The SERVER_PORT
+  metavariable
+  is set to the
+  port on which the
+  request was received, as used in the &lt;port&gt;
+  part of the Script-URI.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    SERVER_PORT = 1*digit
+  </PRE>
+  <P>
+  If the &lt;port&gt; portion of the script-URI is blank, the actual
+  port number upon which the request was received MUST be supplied.
+  </P>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.17">
+    6.1.17. SERVER_PROTOCOL
+   </A>
+  </H4>
+  <P>
+  The SERVER_PROTOCOL
+  metavariable
+  is set to
+  the
+  name and revision of the information protocol with which
+  the
+  request
+  arrived.  This is not necessarily the same as the protocol version used by
+  the server in its response to the client.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    SERVER_PROTOCOL   = HTTP-Version | extension-version
+                        | extension-token
+    HTTP-Version      = "HTTP" "/" 1*digit "." 1*digit
+    extension-version = protocol "/" 1*digit "." 1*digit
+    protocol          = 1*( alpha | digit | "+" | "-" | "." )
+    extension-token   = token
+  </PRE>
+  <P>
+  'protocol' is a version of the &lt;scheme&gt; part of the
+  Script-URI, but is
+  not identical to it.  For example, the scheme of a request may be
+  "<SAMP>https</SAMP>" while the protocol remains "<SAMP>http</SAMP>".
+  The protocol is not case sensitive, but
+  by convention, 'protocol' is in
+  upper case.
+  </P>
+  <P>
+  A well-known extension token value is "INCLUDED",
+  which signals that the current document is being included as part of
+  a composite document, rather than being the direct target of the
+  client request.
+  </P>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts.
+  </P>
+
+  <H4>
+   <A NAME="6.1.18">
+    6.1.18. SERVER_SOFTWARE
+   </A>
+  </H4>
+  <P>
+  The SERVER_SOFTWARE
+  metavariable
+  is set to the
+  name and version of the information server software answering the
+  request (and running the gateway).
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    SERVER_SOFTWARE = 1*product
+    product         = token [ "/" product-version ]
+    product-version = token
+  </PRE>
+  <P>
+  Servers MUST provide this metavariable
+  to scripts.
+  </P>
+
+  <H3>
+   <A NAME="6.2">
+    6.2. Request Message-Bodies
+   </A>
+  </H3>
+  <P>
+  As there may be a data entity attached to the request, there MUST be
+  a system defined method for the script to read
+  these data. Unless
+  defined otherwise, this will be <EM>via</EM> the 'standard input' file
+  descriptor.
+  </P>
+  <P>
+  If the CONTENT_LENGTH value (see <A HREF="#6.1.2">section 6.1.2</A>)
+  is non-NULL, the server MUST supply at least that many bytes to
+  scripts on the standard input stream.
+  Scripts are
+  not obliged to read the data.
+  Servers MAY signal an EOF condition after CONTENT_LENGTH bytes have been
+  read, but are
+  not obligated to do so.  Therefore, scripts
+  MUST NOT
+  attempt to read more than CONTENT_LENGTH bytes, even if more data
+  are available.
+  </P>
+  <P>
+  For non-parsed header (NPH) scripts (see
+  <A HREF="#7.1">section 7.1</A>
+  below),
+  servers SHOULD
+  attempt to ensure that the data
+  supplied to the script are precisely
+  as supplied by the client and unaltered by
+  the server.
+  </P>
+  <P>
+  <A HREF="#8.1.2">Section 8.1.2</A> describes the requirements of
+  servers with regard to requests that include
+  message-bodies.
+  </P>
+
+  <H2>
+   <A NAME="7.0">
+    7. Data Output from the CGI Script
+   </A>
+  </H2>
+  <P>
+  There MUST be a system defined method for the script to send data
+  back to the server or client; a script MUST always return some data.
+  Unless defined otherwise, this will be <EM>via</EM> the 'standard
+  output' file descriptor.
+  </P>
+  <P>
+  There are two forms of output that  scripts can supply to servers: non-parsed
+  header (NPH) output, and parsed header output.
+  Servers MUST support parsed header
+  output and MAY support NPH output.  The method of
+  distinguishing between the two
+  types of output (or scripts) is implementation defined.
+  </P>
+  <P>
+  Servers MAY implement a timeout period within which data must be
+  received from scripts.  If a server implementation defines such
+  a timeout and receives no data from a script within the timeout
+  period, the server MAY terminate the script process and SHOULD
+  abort the client request with
+  either a
+  '504 Gateway Timed Out' or a
+  '500 Internal Server Error' response.
+  </P>
+
+  <H3>
+   <A NAME="7.1">
+    7.1. Non-Parsed Header Output
+   </A>
+  </H3>
+  <P>
+  Scripts using the NPH output form
+  MUST return a complete HTTP response message, as described
+  in Section 6 of the HTTP specifications
+  [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>].
+   NPH scripts
+  MUST use the SERVER_PROTOCOL variable to determine the appropriate format
+  for a response.
+  </P>
+  <P>
+  Servers
+  SHOULD attempt to ensure that the script output is sent
+  directly to the client, with minimal
+  internal and no transport-visible
+  buffering.
+  </P>
+
+  <H3>
+   <A NAME="7.2">
+    7.2. Parsed Header Output
+   </A>
+  </H3>
+  <P>
+  Scripts using the parsed header output form MUST supply
+  a CGI response message to the server
+  as follows:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    CGI-Response   = *optional-field CGI-Field *optional-field NL [ Message-Body ]
+    optional-field = ( CGI-Field | HTTP-Field )
+    CGI-Field      = Content-type
+                   | Location
+                   | Status
+                   | extension-header
+  </PRE>
+  <P><!-- ##### If HTTP defines x-headers, remove ours except x-cgi- -->
+  The response comprises a header and a body, separated by a blank line.
+  The body may be NULL.
+  The header fields are either CGI header fields to be interpreted by
+  the server, or HTTP header fields
+  to be included in the response returned
+  to the client
+  if the request method is HTTP. At least one
+  CGI-Field MUST be
+  supplied, but no CGI  field name may be used more than once
+  in a response.
+  If a body is supplied, then a "<SAMP>Content-type</SAMP>"
+  header field MUST be
+  supplied by the script,
+  otherwise the script MUST send a "<SAMP>Location</SAMP>"
+  or "<SAMP>Status</SAMP>" header field. If a
+  <SAMP>Location</SAMP> CGI-Field
+  is returned, then the script MUST NOT supply
+  any HTTP-Fields.
+  </P>
+  <P>
+  Each header field in a CGI-Response MUST be specified on a single line;
+  CGI/1.1 does not support continuation lines.
+  </P>
+
+  <H4>
+   <A NAME="7.2.1">
+    7.2.1. CGI header fields
+   </A>
+  </H4>
+  <P>
+  The CGI header fields have the generic syntax:
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    generic-field  = field-name ":" [ field-value ] NL
+    field-name     = token
+    field-value    = *( field-content | LWSP )
+    field-content  = *( token | tspecial | quoted-string )
+  </PRE>
+  <P>
+  The field-name is not case sensitive; a NULL field value is
+  equivalent to the header field not being sent.
+  </P>
+
+  <H4>
+   <A NAME="7.2.1.1">
+    7.2.1.1. Content-Type
+   </A>
+  </H4>
+  <P>
+  The Internet Media Type [<A HREF="#[9]">9</A>] of the entity
+  body, which is to be sent unmodified to the client.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    Content-Type = "Content-Type" ":" media-type NL
+  </PRE>
+  <P>
+  This is actually an HTTP-Field
+  rather than a CGI-Field, but
+  it is listed here because of its importance in the CGI dialogue as
+  a member of the "one of these is required" set of header
+  fields.
+  </P>
+
+  <H4>
+   <A NAME="7.2.1.2">
+    7.2.1.2. Location
+   </A>
+  </H4>
+  <P>
+  This is used to specify to the server that the script is returning a
+  reference to a document rather than an actual document.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    Location         = "Location" ":"
+                       ( fragment-URI | rel-URL-abs-path ) NL
+    fragment-URI     = URI [ # fragmentid ]
+    URI              = scheme ":" *qchar
+    fragmentid       = *qchar
+    rel-URL-abs-path = "/" [ hpath ] [ "?" query-string ]
+    hpath            = fpsegment *( "/" psegment )
+    fpsegment        = 1*hchar
+    psegment         = *hchar
+    hchar            = alpha | digit | safe | extra
+                       | ":" | "@" | "& | "="
+  </PRE>
+  <P>
+  The Location
+  value is either an absolute URI with optional fragment,
+  as defined in RFC 1630 [<A HREF="#[1]">1</A>], or an absolute path
+  within the server's URI space (<EM>i.e.</EM>,
+  omitting the scheme and network-related fields) and optional
+  query-string. If an absolute URI is returned by the script,
+  then the
+  server MUST generate a
+  '302 redirect' HTTP response
+  message unless the script has supplied an
+  explicit Status response header field.
+  Scripts returning an absolute URI MAY choose to
+  provide a message-body.  Servers MUST make any appropriate modifications
+  to the script's output to ensure the response to the user-agent complies
+  with the response protocol version.
+  If the Location value is a path, then the server
+  MUST generate
+  the response that it would have produced in response to a request
+  containing the URL
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    scheme "://" SERVER_NAME ":" SERVER_PORT rel-URL-abs-path
+  </PRE>
+  <P>
+  Note: If the request was accompanied by a
+  message-body
+  (such as for a POST request), and the script
+  redirects the request with a Location field, the
+  message-body
+  may not be
+  available to the resource that is the target of the redirect.
+  </P>
+
+  <H4>
+   <A NAME="7.2.1.3">
+    7.2.1.3. Status
+   </A>
+  </H4>
+  <P>
+  The "<SAMP>Status</SAMP>" header field is used to indicate to the server what
+  status code the server MUST use in the response message.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    Status        = "Status" ":" digit digit digit SP reason-phrase NL
+    reason-phrase = *&lt;CHAR, excluding CTLs, NL&gt;
+  </PRE>
+  <P>
+  The valid status codes are listed in section 6.1.1 of the HTTP/1.0
+  specifications [<A HREF="#[3]">3</A>]. If the SERVER_PROTOCOL is
+  "HTTP/1.1", then the status codes defined in the HTTP/1.1
+  specification [<A HREF="#[8]">8</A>] may
+  be used. If the script does not return a "<SAMP>Status</SAMP>" header
+  field, then "200 OK" SHOULD be assumed by the server.
+  </P>
+  <P>
+  If a script is being used to handle a particular error or condition
+  encountered by the server, such as a '404 Not Found' error, the script
+  SHOULD use the "<SAMP>Status</SAMP>" CGI header field to propagate the error
+  condition back to the client.  <EM>E.g.</EM>, in the example mentioned it
+  SHOULD include a "Status:&nbsp;404&nbsp;Not&nbsp;Found" in the
+  header data returned to the server.
+  </P>
+
+  <H4>
+   <A NAME="7.2.1.4">
+    7.2.1.4. Extension header fields
+   </A>
+  </H4>
+  <P>
+  Scripts MAY include in their CGI response header additional fields
+  not defined in this or the HTTP specification.
+  These are called "extension" fields,
+  and have the syntax of a <SAMP>generic-field</SAMP> as defined in
+  <A HREF="#7.2.1">section 7.2.1</A>.  The name of an extension field
+  MUST NOT conflict with a field name defined in this or any other
+  specification; extension field names SHOULD begin with "X-CGI-"
+  to ensure uniqueness.
+  </P>
+
+  <H4>
+   <A NAME="7.2.2">
+    7.2.2. HTTP header fields
+   </A>
+  </H4>
+  <P>
+  The script MAY return any other header fields defined by the
+  specification
+  for the SERVER_PROTOCOL (HTTP/1.0 [<A HREF="#[3]">3</A>] or HTTP/1.1
+  [<A HREF="#[8]">8</A>]).
+  Servers MUST resolve conflicts beteen CGI header
+  and HTTP header formats or names (see <A HREF="#8.0">section 8</A>).
+  </P>
+
+  <H2>
+   <A NAME="8.0">
+    8. Server Implementation
+   </A>
+  </H2>
+  <P>
+  This section defines the requirements that must be met by HTTP
+  servers in order to provide a coherent and correct CGI/1.1
+  environment in which scripts may function.  It is intended
+  primarily for server implementors, but it is useful for
+  script authors to be familiar with the information as well.
+  </P>
+
+  <H3>
+   <A NAME="8.1">
+    8.1. Requirements for Servers
+   </A>
+  </H3>
+  <P>
+  In order to be considered CGI/1.1-compliant, a server must meet
+  certain basic criteria and provide certain minimal functionality.
+  The details of these requirements are described in the following sections.
+  </P>
+
+  <H3>
+   <A NAME="8.1.1">
+    8.1.1. Script-URI
+   </A>
+  </H3>
+  <P>
+  Servers MUST support the standard mechanism (described below) which
+  allows
+  script authors to determine
+  what URL to use in documents
+  which reference the script;
+  specifically, what URL to use in order to
+  achieve particular settings of the
+  metavariables. This
+  mechanism is as follows:
+  </P>
+  <P>
+  The server
+  MUST translate the header data from the CGI header field syntax to
+  the HTTP
+  header field syntax if these differ. For example, the character
+  sequence for
+  newline (such as Unix's ASCII NL) used by CGI scripts may not be the
+  same as that used by HTTP (ASCII CR followed by LF). The server MUST
+  also resolve any conflicts between header fields returned by the script
+  and header fields that it would otherwise send itself.
+  </P>
+
+  <H3>
+   <A NAME="8.1.2">
+    8.1.2. Request Message-body Handling
+   </A>
+  </H3>
+  <P>
+  These are the requirements for server handling of message-bodies directed
+  to CGI/1.1 resources:
+  </P>
+  <OL>
+   <LI>The message-body the server provides to the CGI script MUST
+    have any transfer encodings removed.
+   </LI>
+   <LI>The server MUST derive and provide a value for the CONTENT_LENGTH
+    metavariable that reflects the length of the message-body after any
+    transfer decoding.
+   </LI>
+   <LI>The server MUST leave intact any content-encodings of the message-body.
+   </LI>
+  </OL>
+
+  <H3>
+   <A NAME="8.1.3">
+    8.1.3. Required Metavariables
+   </A>
+  </H3>
+  <P>
+  Servers MUST provide scripts with certain information and
+  metavariables
+  as described in <A HREF="#8.3">section 8.3</A>.
+  </P>
+
+  <H3>
+   <A NAME="8.1.4">
+    8.1.4. Response Compliance
+   </A>
+  </H3>
+  <P>
+  Servers MUST ensure that responses sent to the user-agent meet all
+  requirements of the protocol level in effect.  This may involve
+  modifying, deleting, or augmenting any header
+  fields and/or message-body supplied by the script.
+  </P>
+
+  <H3>
+   <A NAME="8.2">
+    8.2. Recommendations for Servers
+   </A>
+  </H3>
+  <P>
+  Servers SHOULD provide the "<SAMP>query</SAMP>" component of the script-URI
+  as command-line arguments to scripts if it does not
+  contain any unencoded '=' characters and the command-line arguments can
+  be generated in an unambiguous manner.
+  (See <A HREF="#5.0">section 5</A>.)
+  </P>
+  <P>
+  Servers SHOULD set the AUTH_TYPE
+  metavariable to the value of the
+  '<SAMP>auth-scheme</SAMP>' token of the "<SAMP>Authorization</SAMP>"
+  field if it was supplied as part of the request header.
+  (See <A HREF="#6.1.1">section 6.1.1</A>.)
+  </P>
+  <P>
+  Where applicable, servers SHOULD set the current working directory
+  to the directory in which the script is located before invoking
+  it.
+  </P>
+  <P>
+  Servers MAY reject with error '404 Not Found'
+  any requests that would result in
+  an encoded "/" being decoded into PATH_INFO or SCRIPT_NAME, as this
+  might represent a loss of information to the script.
+  </P>
+  <P>
+  Although the server and the CGI script need not be consistent in
+  their handling of URL paths (client URLs and the PATH_INFO data,
+  respectively), server authors may wish to impose consistency.
+  So the server implementation SHOULD define its behaviour for the
+  following cases:
+  </P>
+  <OL>
+   <LI>define any restrictions on allowed characters, in particular
+    whether ASCII NUL is permitted;
+   </LI>
+   <LI>define any restrictions on allowed path segments, in particular
+    whether non-terminal NULL segments are permitted;
+   </LI>
+   <LI>define the behaviour for <SAMP>"."</SAMP> or <SAMP>".."</SAMP> path
+    segments; <EM>i.e.</EM>, whether they are prohibited, treated as
+    ordinary path
+    segments or interpreted in accordance with the relative URL
+    specification [<A HREF="#[7]">7</A>];
+   </LI>
+   <LI>define any limits of the implementation, including limits on path or
+    search string lengths, and limits on the volume of header data the server
+    will parse.
+   </LI><!-- ##### Move the field resolution/translation para below here -->
+  </OL>
+  <P>
+  Servers MAY generate the
+  Script-URI in
+  any way from the client URI,
+  or from any other data (but the behaviour SHOULD be documented).
+  </P>
+  <P>
+  For non-parsed header (NPH) scripts (see
+  <A HREF="#7.1">section 7.1</A>), servers SHOULD
+  attempt to ensure that the script input comes directly from the
+  client, with minimal buffering. For all scripts the data will be
+  as supplied by the client.
+  </P>
+
+  <H3>
+   <A NAME="8.3">
+    8.3. Summary of
+    MetaVariables
+   </A>
+  </H3>
+  <P>
+  Servers MUST provide the following
+  metavariables to
+  scripts.  See the individual descriptions for exceptions and semantics.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    CONTENT_LENGTH (section <A HREF="#6.1.2">6.1.2</A>)
+    CONTENT_TYPE (section <A HREF="#6.1.3">6.1.3</A>)
+    GATEWAY_INTERFACE (section <A HREF="#6.1.4">6.1.4</A>)
+    PATH_INFO (section <A HREF="#6.1.6">6.1.6</A>)
+    QUERY_STRING (section <A HREF="#6.1.8">6.1.8</A>)
+    REMOTE_ADDR (section <A HREF="#6.1.9">6.1.9</A>)
+    REQUEST_METHOD (section <A HREF="#6.1.13">6.1.13</A>)
+    SCRIPT_NAME (section <A HREF="#6.1.14">6.1.14</A>)
+    SERVER_NAME (section <A HREF="#6.1.15">6.1.15</A>)
+    SERVER_PORT (section <A HREF="#6.1.16">6.1.16</A>)
+    SERVER_PROTOCOL (section <A HREF="#6.1.17">6.1.17</A>)
+    SERVER_SOFTWARE (section <A HREF="#6.1.18">6.1.18</A>)
+  </PRE>
+  <P>
+  Servers SHOULD define the following
+  metavariables for scripts.
+  See the individual descriptions for exceptions and semantics.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    AUTH_TYPE (section <A HREF="#6.1.1">6.1.1</A>)
+    REMOTE_HOST (section <A HREF="#6.1.10">6.1.10</A>)
+  </PRE>
+  <P>
+  In addition, servers SHOULD provide
+  metavariables for all fields present
+  in the HTTP request header, with the exception of those involved with
+  access control.  Servers MAY at their discretion provide
+  metavariables
+  for access control fields.
+  </P>
+  <P>
+  Servers MAY define the following
+  metavariables.  See the individual
+  descriptions for exceptions and semantics.
+  </P><!--#if expr="! $GUI" -->
+  <P></P><!--#endif -->
+  <PRE>
+    PATH_TRANSLATED (section <A HREF="#6.1.7">6.1.7</A>)
+    REMOTE_IDENT (section <A HREF="#6.1.11">6.1.11</A>)
+    REMOTE_USER (section <A HREF="#6.1.12">6.1.12</A>)
+  </PRE>
+  <P>
+  Servers MAY
+  at their discretion define additional implementation-specific
+  extension metavariables
+  provided their names do not
+  conflict with defined header field names.  Implementation-specific
+  metavariable names SHOULD
+  be prefixed with "X_" (<EM>e.g.</EM>,
+  "X_DBA") to avoid the potential for such conflicts.
+  </P>
+
+  <H2>
+   <A NAME="9.0">
+    9.
+    Script Implementation
+   </A>
+  </H2>
+  <P>
+  This section defines the requirements and recommendations for scripts
+  that are intended to function in a CGI/1.1 environment.  It is intended
+  primarily as a reference for script authors, but server implementors
+  should be familiar with these issues as well.
+  </P>
+
+  <H3>
+   <A NAME="9.1">
+    9.1. Requirements for Scripts
+   </A>
+  </H3>
+  <P>
+  Scripts using the parsed-header method to communicate with servers
+  MUST supply a response header to the server.
+  (See <A HREF="#7.0">section 7</A>.)
+  </P>
+  <P>
+  Scripts using the NPH method to communicate with servers MUST
+  provide complete HTTP responses, and MUST use the value of the
+  SERVER_PROTOCOL metavariable
+  to determine the appropriate format.
+  (See <A HREF="#7.1">section 7.1</A>.)
+  </P>
+  <P>
+  Scripts MUST check the value of the REQUEST_METHOD
+  metavariable in order
+  to provide an appropriate response.
+  (See <A HREF="#6.1.13">section 6.1.13</A>.)
+  </P>
+  <P>
+  Scripts MUST be prepared to handled URL-encoded values in
+  metavariables.
+  In addition, they MUST recognise both "+" and "%20" in URL-encoded
+  quantities as representing the space character.
+  (See <A HREF="#3.1">section 3.1</A>.)
+  </P>
+  <P>
+  Scripts MUST ignore leading zeros in the major and minor version numbers
+  in the GATEWAY_INTERFACE
+  metavariable value. (See
+  <A HREF="#6.1.4">section 6.1.4</A>.)
+  </P>
+  <P>
+  When processing requests that include a
+  message-body, scripts
+  MUST NOT read more than CONTENT_LENGTH bytes from the input stream.
+  (See sections <A HREF="#6.1.2">6.1.2</A> and <A HREF="#6.2">6.2</A>.)
+  </P>
+
+  <H3>
+   <A NAME="9.2">
+    9.2. Recommendations for Scripts
+   </A>
+  </H3>
+  <P>
+  Servers may interrupt or terminate script execution at any time
+  and without warning, so scripts SHOULD be prepared to deal with
+  abnormal termination.
+  </P>
+  <P>
+  Scripts MUST
+  reject with
+  error '405 Method Not
+  Allowed' requests
+  made using methods that they do not support. If the script does
+  not intend
+  processing the PATH_INFO data, then it SHOULD reject the request with
+  '404 Not
+  Found' if PATH_INFO is not NULL.
+  </P>
+  <P>
+  If a script is processing the output of a form, it SHOULD
+  verify that the CONTENT_TYPE
+  is "<SAMP>application/x-www-form-urlencoded</SAMP>" [<A HREF="#[2]">2</A>]
+  or whatever other media type is expected.
+  </P>
+  <P>
+  Scripts parsing PATH_INFO,
+  PATH_TRANSLATED, or SCRIPT_NAME
+  SHOULD be careful
+  of void path segments ("<SAMP>//</SAMP>") and special path segments
+  (<SAMP>"."</SAMP> and
+  <SAMP>".."</SAMP>). They SHOULD either be removed from the path before
+  use in OS
+  system calls, or the request SHOULD be rejected with
+  '404 Not Found'.
+  </P>
+  <P>
+  As it is impossible for
+  scripts to determine the client URI that
+  initiated  a
+  request without knowledge of the specific server in
+  use, the script SHOULD NOT return "<SAMP>text/html</SAMP>"
+  documents containing
+  relative URL links without including a "<SAMP>&lt;BASE&gt;</SAMP>"
+  tag in the document.
+  </P>
+  <P>
+  When returning header fields,
+  scripts SHOULD try to send the CGI
+  header fields (see section
+  <A HREF="#7.2">7.2</A>) as soon as possible, and
+  SHOULD send them
+  before any HTTP header fields. This may
+  help reduce the server's memory requirements.
+  </P>
+
+  <H2>
+   <A NAME="10.0">
+    10. System Specifications
+   </A>
+  </H2>
+
+  <H3>
+   <A NAME="10.1">
+    10.1. AmigaDOS
+   </A>
+  </H3>
+  <P>
+  The implementation of the CGI on an AmigaDOS operating system platform
+  SHOULD use environment variables as the mechanism of providing
+  request metadata to CGI scripts.
+  </P>
+  <DL>
+   <DT><STRONG>Environment variables</STRONG>
+   </DT>
+   <DD>
+    <P>
+    These are accessed by the DOS library routine <SAMP>GetVar</SAMP>. The
+    flags argument SHOULD be 0. Case is ignored, but upper case is
+    recommended for compatibility with case-sensitive systems.
+    </P>
+   </DD>
+   <DT><STRONG>The current working directory</STRONG>
+   </DT>
+   <DD>
+    <P>
+    The current working directory for the script is set to the directory
+    containing the script.
+    </P>
+   </DD>
+   <DT><STRONG>Character set</STRONG>
+   </DT>
+   <DD>
+    <P>
+    The US-ASCII character set is used for the definition of environment
+    variable names and header
+    field names; the newline (NL) sequence is LF;
+    servers SHOULD also accept CR LF as a newline.
+    </P>
+   </DD>
+  </DL>
+
+  <H3>
+   <A NAME="10.2">
+    10.2. Unix
+   </A>
+  </H3>
+  <P>
+  The implementation of the CGI on a UNIX operating system platform
+  SHOULD use environment variables as the mechanism of providing
+  request metadata to CGI scripts.
+  </P>
+  <P>
+  For Unix compatible operating systems, the following are defined:
+  </P>
+  <DL>
+   <DT><STRONG>Environment variables</STRONG>
+   </DT>
+   <DD>
+    <P>
+    These are accessed by the C library routine <SAMP>getenv</SAMP>.
+    </P>
+   </DD>
+   <DT><STRONG>The command line</STRONG>
+   </DT>
+   <DD>
+    <P>
+    This is accessed using the
+    <SAMP>argc</SAMP> and <SAMP>argv</SAMP>
+    arguments to <SAMP>main()</SAMP>. The words have any characters
+    that
+    are 'active' in the Bourne shell escaped with a backslash.
+    If the value of the QUERY_STRING
+    metavariable
+    contains an unencoded equals-sign '=', then the command line
+    SHOULD NOT be used by the script.
+    </P>
+   </DD>
+   <DT><STRONG>The current working directory</STRONG>
+   </DT>
+   <DD>
+    <P>
+    The current working directory for the script
+    SHOULD be set to the directory
+    containing the script.
+    </P>
+   </DD>
+   <DT><STRONG>Character set</STRONG>
+   </DT>
+   <DD>
+    <P>
+    The US-ASCII character set is used for the definition of environment
+    variable names and header field names; the newline (NL) sequence is LF;
+    servers SHOULD also accept CR LF as a newline.
+    </P>
+   </DD>
+  </DL>
+
+  <H2>
+   <A NAME="11.0">
+    11. Security Considerations
+   </A>
+  </H2>
+
+  <H3>
+   <A NAME="11.1">
+    11.1. Safe Methods
+   </A>
+  </H3>
+  <P>
+  As discussed in the security considerations of the HTTP
+  specifications [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>], the
+  convention has been established that the
+  GET and HEAD methods should be 'safe'; they should cause no
+  side-effects and only have the significance of resource retrieval.
+  </P>
+  <P>
+  CGI scripts are responsible for enforcing any HTTP security considerations
+  [<A HREF="#[3]">3</A>,<A HREF="#[8]">8</A>]
+  with respect to the protocol version level of the request and
+  any side effects generated by the scripts on behalf of
+  the server.  Primary
+  among these
+  are the considerations of safe and idempotent methods.  Idempotent
+  requests are those that may be repeated an arbitrary number of times
+  and produce side effects identical to a single request.
+  </P>
+
+  <H3>
+   <A NAME="11.2">
+    11.2. HTTP Header
+    Fields Containing Sensitive Information
+   </A>
+  </H3>
+  <P>
+  Some HTTP header fields may carry sensitive information which the server
+  SHOULD NOT pass on to the script unless explicitly configured to do
+  so. For example, if the server protects the script using the
+  "<SAMP>Basic</SAMP>"
+  authentication scheme, then the client will send an
+  "<SAMP>Authorization</SAMP>"
+  header field containing a username and password. If the server, rather
+  than the script, validates this information then the password SHOULD
+  NOT be passed on to the script <EM>via</EM> the HTTP_AUTHORIZATION
+  metavariable
+  without careful consideration.
+  This also applies to the
+  Proxy-Authorization header field and the corresponding
+  HTTP_PROXY_AUTHORIZATION
+  metavariable.
+  </P>
+
+  <H3>
+   <A NAME="11.3">
+    11.3. Script
+    Interference with the Server
+   </A>
+  </H3>
+  <P>
+  The most common implementation of CGI invokes the script as a child
+  process using the same user and group as the server process. It
+  SHOULD therefore be ensured that the script cannot interfere with the
+  server process, its configuration, or documents.
+  </P>
+  <P>
+  If the script is executed by calling a function linked in to the
+  server software (either at compile-time or run-time) then precautions
+  SHOULD be taken to protect the core memory of the server, or to
+  ensure that untrusted code cannot be executed.
+  </P>
+
+  <H3>
+   <A NAME="11.4">
+    11.4. Data Length and Buffering Considerations
+   </A>
+  </H3>
+  <P>
+  This specification places no limits on the length of message-bodies
+  presented to the script.  Scripts should not assume that statically
+  allocated buffers of any size are sufficient to contain the entire
+  submission at one time.  Use of a fixed length buffer without careful
+  overflow checking may result in an attacker exploiting 'stack-smashing'
+  or 'stack-overflow' vulnerabilities of the operating system.
+  Scripts may spool large submissions to disk or other buffering media,
+  but a rapid succession of large submissions may result in denial of
+  service conditions.  If the CONTENT_LENGTH of a message-body is larger
+  than resource considerations allow, scripts should respond with an
+  error status appropriate for the protocol version; potentially applicable
+  status codes include '503 Service Unavailable' (HTTP/1.0 and HTTP/1.1),
+  '413 Request Entity Too Large' (HTTP/1.1), and
+  '414 Request-URI Too Long' (HTTP/1.1).
+  </P>
+
+  <H3>
+   <A NAME="11.5">
+    11.5. Stateless Processing
+   </A>
+  </H3>
+  <P>
+  The stateless nature of the Web makes each script execution and resource
+  retrieval independent of all others even when multiple requests constitute a
+  single conceptual Web transaction.  Because of this, a script should not
+  make any assumptions about the context of the user-agent submitting a
+  request.  In particular, scripts should examine data obtained from the client
+  and verify that they are valid, both in form and content, before allowing
+  them to be used for sensitive purposes such as input to other
+  applications, commands, or operating system services.  These uses
+  include, but are not
+  limited to: system call arguments, database writes, dynamically evaluated
+  source code, and input to billing or other secure processes.  It is important
+  that applications be protected from invalid input regardless of whether
+  the invalidity is the result of user error, logic error, or malicious action.
+  </P>
+  <P>
+  Authors of scripts involved in multi-request transactions should be
+  particularly cautios about validating the state information;
+  undesirable effects may result from the substitution of dangerous
+  values for portions of the submission which might otherwise be
+  presumed safe.  Subversion of this type occurs when alterations
+  are made to data from a prior stage of the transaction that were
+  not meant to be controlled by the client (<EM>e.g.</EM>, hidden
+  HTML form elements, cookies, embedded URLs, <EM>etc.</EM>).
+  </P>
+
+  <H2>
+   <A NAME="12.0">
+    12. Acknowledgements
+   </A>
+  </H2>
+  <P>
+  This work is based on a draft published in 1997 by David R. Robinson,
+  which in turn was based on the original CGI interface that arose out of
+  discussions on the <EM>www-talk</EM> mailing list. In particular,
+  Rob McCool, John Franks, Ari Luotonen,
+  George Phillips and
+  Tony Sanders deserve special recognition for their efforts in
+  defining and implementing the early versions of this interface.
+  </P>
+  <P>
+  This document has also greatly benefited from the comments and
+  suggestions made by  Chris Adie, Dave Kristol,
+  Mike Meyer, David Morris, Jeremy Madea,
+  Patrick M<SUP>c</SUP>Manus, Adam Donahue,
+  Ross Patterson, and Harald Alvestrand.
+  </P>
+
+  <H2>
+   <A NAME="13.0">
+    13. References
+   </A>
+  </H2>
+  <DL COMPACT>
+   <DT><A NAME="[1]">[1]</A>
+   </DT>
+   <DD>Berners-Lee, T., 'Universal Resource Identifiers in WWW: A
+       Unifying Syntax for the Expression of Names and Addresses of
+       Objects on the Network as used in the World-Wide Web', RFC 1630,
+       CERN, June 1994.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[2]">[2]</A>
+   </DT>
+   <DD>Berners-Lee, T. and Connolly, D., 'Hypertext Markup Language -
+        2.0', RFC 1866, MIT/W3C, November 1995.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[3]">[3]</A>
+   </DT>
+   <DD>Berners-Lee, T., Fielding, R. T. and Frystyk, H.,
+          'Hypertext Transfer Protocol -- HTTP/1.0', RFC 1945, MIT/LCS,
+          UC Irvine, May 1996.
+       <P>
+       </P>
+   </DD>
+
+  <DT><A NAME="[4]">[4]</A>
+  </DT>
+  <DD>Berners-Lee, T., Fielding, R., and Masinter, L., Editors,
+   'Uniform Resource Identifiers (URI): Generic Syntax', RFC 2396,
+   MIT, U.C. Irvine, Xerox Corporation, August 1996.
+   <P>
+   </P>
+  </DD>
+
+  <DT><A NAME="[5]">[5]</A>
+  </DT>
+  <DD>Braden, R., Editor, 'Requirements for Internet Hosts --
+          Application and Support', STD 3, RFC 1123, IETF, October 1989.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[6]">[6]</A>
+   </DT>
+   <DD>Crocker, D.H., 'Standard for the Format of ARPA Internet Text
+          Messages', STD 11, RFC 822, University of Delaware, August 1982.
+       <P>
+       </P>
+   </DD>
+  <DT><A NAME="[7]">[7]</A>
+  </DT>
+  <DD>Fielding, R., 'Relative Uniform Resource Locators', RFC 1808,
+          UC Irvine, June 1995.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[8]">[8]</A>
+   </DT>
+   <DD>Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and
+          Berners-Lee, T., 'Hypertext Transfer Protocol -- HTTP/1.1',
+          RFC 2068, UC Irvine, DEC,
+	  MIT/LCS, January 1997.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[9]">[9]</A>
+   </DT>
+   <DD>Freed, N. and Borenstein N., 'Multipurpose Internet Mail
+          Extensions (MIME) Part Two: Media Types', RFC 2046, Innosoft,
+          First Virtual, November 1996.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[10]">[10]</A>
+   </DT>
+   <DD>Mockapetris, P., 'Domain Names - Concepts and Facilities',
+          STD 13, RFC 1034, ISI, November 1987.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[11]">[11]</A>
+   </DT>
+   <DD>St. Johns, M., 'Identification Protocol', RFC 1431, US
+          Department of Defense, February 1993.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[12]">[12]</A>
+   </DT>
+   <DD>'Coded Character Set -- 7-bit American Standard Code for
+          Information Interchange', ANSI X3.4-1986.
+       <P>
+       </P>
+   </DD>
+   <DT><A NAME="[13]">[13]</A>
+   </DT>
+   <DD>Hinden, R. and Deering, S.,
+          'IP Version 6 Addressing Architecture', RFC 2373,
+	  Nokia, Cisco Systems,
+          July 1998.
+       <P>
+       </P>
+   </DD>
+  </DL>
+
+  <H2>
+   <A NAME="14.0">
+    14. Authors' Addresses
+   </A>
+  </H2>
+  <ADDRESS>
+   <P>
+   Ken A L Coar
+   <BR>
+   MeepZor Consulting
+   <BR>
+   7824 Mayfaire Crest Lane, Suite 202
+   <BR>
+   Raleigh, NC   27615-4875
+   <BR>
+   U.S.A.
+   </P>
+   <P>
+   Tel: +1 (919) 254.4237
+   <BR>
+   Fax: +1 (919) 254.5250
+   <BR>
+   Email:
+   <A
+    HREF="mailto:Ken.Coar@Golux.Com"
+   ><SAMP>Ken.Coar@Golux.Com</SAMP></A>
+   </P>
+  </ADDRESS>
+  <ADDRESS>
+   <P>
+   David Robinson
+   <BR>
+   E*TRADE UK Ltd
+   <BR>
+   Mount Pleasant House
+   <BR>
+   2 Mount Pleasant
+   <BR>
+   Huntingdon Road
+   <BR>
+   Cambridge CB3 0RN
+   <BR>
+   UK
+   </P>
+   <P>
+   Tel: +44 (1223) 566926
+   <BR>
+   Fax: +44 (1223) 506288
+   <BR>
+   Email:
+   <A
+    HREF="mailto:drtr@etrade.co.uk"
+   ><SAMP>drtr@etrade.co.uk</SAMP></A>
+  </ADDRESS>
+
+ </BODY>
+</HTML>
diff --git a/busybox-1.19.3/docs/ifupdown_design.txt b/busybox-1.19.3/docs/ifupdown_design.txt
new file mode 100644
index 0000000..8ab4e51
--- /dev/null
+++ b/busybox-1.19.3/docs/ifupdown_design.txt
@@ -0,0 +1,44 @@
+This document is meant to convince you to not use ifup/ifdown.
+
+
+The general problem with ifupdown is that it is "copulated in vertical
+fashion" by design. It tries to do the job of shell script in C,
+and this is invariably doomed to fail. You need ifup/ifdown
+to be adaptable by local admins, and C is an extremely poor choice
+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", ........ },
+};
+
+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);
+#elif ENABLE_UDHCPC
+	return execute("kill "
+			"`cat /var/run/udhcpc.%iface%.pid` 2>/dev/null", ifd, exec);
+#else
+	return 0; /* no dhcp support */
+#endif
+}
+
+How the hell it is supposed to work reliably this way? Just imagine that
+admin is using pump and ifup/ifdown. It works. Then, for whatever reason,
+admin installs dhclient, but does NOT use it. ifdown will STOP WORKING,
+just because it will see installed dhclient binary in e.g. /usr/bin/dhclient!
+This is stupid.
+
+I seriously urge people to not use ifup/ifdown.
+Use something less brain damaged.
diff --git a/busybox-1.19.3/docs/keep_data_small.txt b/busybox-1.19.3/docs/keep_data_small.txt
new file mode 100644
index 0000000..21d7326
--- /dev/null
+++ b/busybox-1.19.3/docs/keep_data_small.txt
@@ -0,0 +1,256 @@
+		Keeping data small
+
+When many applets are compiled into busybox, all rw data and
+bss for each applet are concatenated. Including those from libc,
+if static busybox is built. When busybox is started, _all_ this data
+is allocated, not just that one part for selected applet.
+
+What "allocated" exactly means, depends on arch.
+On NOMMU it's probably bites the most, actually using real
+RAM for rwdata and bss. On i386, bss is lazily allocated
+by COWed zero pages. Not sure about rwdata - also COW?
+
+In order to keep busybox NOMMU and small-mem systems friendly
+we should avoid large global data in our applets, and should
+minimize usage of libc functions which implicitly use
+such structures.
+
+Small experiment to measure "parasitic" bbox memory consumption:
+here we start 1000 "busybox sleep 10" in parallel.
+busybox binary is practically allyesconfig static one,
+built against uclibc. Run on x86-64 machine with 64-bit kernel:
+
+bash-3.2# nmeter '%t %c %m %p %[pn]'
+23:17:28 .......... 168M    0  147
+23:17:29 .......... 168M    0  147
+23:17:30 U......... 168M    1  147
+23:17:31 SU........ 181M  244  391
+23:17:32 SSSSUUU... 223M  757 1147
+23:17:33 UUU....... 223M    0 1147
+23:17:34 U......... 223M    1 1147
+23:17:35 .......... 223M    0 1147
+23:17:36 .......... 223M    0 1147
+23:17:37 S......... 223M    0 1147
+23:17:38 .......... 223M    1 1147
+23:17:39 .......... 223M    0 1147
+23:17:40 .......... 223M    0 1147
+23:17:41 .......... 210M    0  906
+23:17:42 .......... 168M    1  147
+23:17:43 .......... 168M    0  147
+
+This requires 55M of memory. Thus 1 trivial busybox applet
+takes 55k of memory on 64-bit x86 kernel.
+
+On 32-bit kernel we need ~26k per applet.
+
+Script:
+
+i=1000; while test $i != 0; do
+        echo -n .
+        busybox sleep 30 &
+        i=$((i - 1))
+done
+echo
+wait
+
+(Data from NOMMU arches are sought. Provide 'size busybox' output too)
+
+
+		Example 1
+
+One example how to reduce global data usage is in
+archival/libarchive/decompress_unzip.c:
+
+/* This is somewhat complex-looking arrangement, but it allows
+ * to place decompressor state either in bss or in
+ * malloc'ed space simply by changing #defines below.
+ * Sizes on i386:
+ * text    data     bss     dec     hex
+ * 5256       0     108    5364    14f4 - bss
+ * 4915       0       0    4915    1333 - malloc
+ */
+#define STATE_IN_BSS 0
+#define STATE_IN_MALLOC 1
+
+(see the rest of the file to get the idea)
+
+This example completely eliminates globals in that module.
+Required memory is allocated in unpack_gz_stream() [its main module]
+and then passed down to all subroutines which need to access 'globals'
+as a parameter.
+
+
+		Example 2
+
+In case you don't want to pass this additional parameter everywhere,
+take a look at archival/gzip.c. Here all global data is replaced by
+single global pointer (ptr_to_globals) to allocated storage.
+
+In order to not duplicate ptr_to_globals in every applet, you can
+reuse single common one. It is defined in libbb/messages.c
+as struct globals *const ptr_to_globals, but the struct globals is
+NOT defined in libbb.h. You first define your own struct:
+
+struct globals { int a; char buf[1000]; };
+
+and then declare that ptr_to_globals is a pointer to it:
+
+#define G (*ptr_to_globals)
+
+ptr_to_globals is declared as constant pointer.
+This helps gcc understand that it won't change, resulting in noticeably
+smaller code. In order to assign it, use SET_PTR_TO_GLOBALS macro:
+
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G)));
+
+Typically it is done in <applet>_main().
+
+Now you can reference "globals" by G.a, G.buf and so on, in any function.
+
+
+		bb_common_bufsiz1
+
+There is one big common buffer in bss - bb_common_bufsiz1. It is a much
+earlier mechanism to reduce bss usage. Each applet can use it for
+its needs. Library functions are prohibited from using it.
+
+'G.' trick can be done using bb_common_bufsiz1 instead of malloced buffer:
+
+#define G (*(struct globals*)&bb_common_bufsiz1)
+
+Be careful, though, and use it only if globals fit into bb_common_bufsiz1.
+Since bb_common_bufsiz1 is BUFSIZ + 1 bytes long and BUFSIZ can change
+from one libc to another, you have to add compile-time check for it:
+
+if (sizeof(struct globals) > sizeof(bb_common_bufsiz1))
+	BUG_<applet>_globals_too_big();
+
+
+		Drawbacks
+
+You have to initialize it by hand. xzalloc() can be helpful in clearing
+allocated storage to 0, but anything more must be done by hand.
+
+All global variables are prefixed by 'G.' now. If this makes code
+less readable, use #defines:
+
+#define dev_fd (G.dev_fd)
+#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
+
+
+		gcc's data alignment problem
+
+The following attribute added in vi.c:
+
+static int tabstop;
+static struct termios term_orig __attribute__ ((aligned (4)));
+static struct termios term_vi __attribute__ ((aligned (4)));
+
+reduces bss size by 32 bytes, because gcc sometimes aligns structures to
+ridiculously large values. asm output diff for above example:
+
+ tabstop:
+        .zero   4
+        .section        .bss.term_orig,"aw",@nobits
+-       .align 32
++       .align 4
+        .type   term_orig, @object
+        .size   term_orig, 60
+ term_orig:
+        .zero   60
+        .section        .bss.term_vi,"aw",@nobits
+-       .align 32
++       .align 4
+        .type   term_vi, @object
+        .size   term_vi, 60
+
+gcc doesn't seem to have options for altering this behaviour.
+
+gcc 3.4.3 and 4.1.1 tested:
+char c = 1;
+// gcc aligns to 32 bytes if sizeof(struct) >= 32
+struct {
+    int a,b,c,d;
+    int i1,i2,i3;
+} s28 = { 1 };    // struct will be aligned to 4 bytes
+struct {
+    int a,b,c,d;
+    int i1,i2,i3,i4;
+} s32 = { 1 };    // struct will be aligned to 32 bytes
+// same for arrays
+char vc31[31] = { 1 }; // unaligned
+char vc32[32] = { 1 }; // aligned to 32 bytes
+
+-fpack-struct=1 reduces alignment of s28 to 1 (but probably
+will break layout of many libc structs) but s32 and vc32
+are still aligned to 32 bytes.
+
+I will try to cook up a patch to add a gcc option for disabling it.
+Meanwhile, this is where it can be disabled in gcc source:
+
+gcc/config/i386/i386.c
+int
+ix86_data_alignment (tree type, int align)
+{
+#if 0
+  if (AGGREGATE_TYPE_P (type)
+       && TYPE_SIZE (type)
+       && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+       && (TREE_INT_CST_LOW (TYPE_SIZE (type)) >= 256
+           || TREE_INT_CST_HIGH (TYPE_SIZE (type))) && align < 256)
+    return 256;
+#endif
+
+Result (non-static busybox built against glibc):
+
+# size /usr/srcdevel/bbox/fix/busybox.t0/busybox busybox
+   text    data     bss     dec     hex filename
+ 634416    2736   23856  661008   a1610 busybox
+ 632580    2672   22944  658196   a0b14 busybox_noalign
+
+
+
+		Keeping code small
+
+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
+to some of these functions. In 1.16.x timeframe, the results were
+(annotated "make bloatcheck" output):
+
+function             old     new   delta
+expand_vars_to_list    -    1712   +1712 win
+lzo1x_optimize         -    1429   +1429 win
+arith_apply            -    1326   +1326 win
+read_interfaces        -    1163   +1163 loss, leave w/o NOINLINE
+logdir_open            -    1148   +1148 win
+check_deps             -    1148   +1148 loss
+rewrite                -    1039   +1039 win
+run_pipe             358    1396   +1038 win
+write_status_file      -    1029   +1029 almost the same, leave w/o NOINLINE
+dump_identity          -     987    +987 win
+mainQSort3             -     921    +921 win
+parse_one_line         -     916    +916 loss
+summarize              -     897    +897 almost the same
+do_shm                 -     884    +884 win
+cpio_o                 -     863    +863 win
+subCommand             -     841    +841 loss
+receive                -     834    +834 loss
+
+855 bytes saved in total.
+
+scripts/mkdiff_obj_bloat may be useful to automate this process: run
+"scripts/mkdiff_obj_bloat NORMALLY_BUILT_TREE FORCED_NOINLINE_TREE"
+and select modules which shrank.
diff --git a/busybox-1.19.3/docs/logging_and_backgrounding.txt b/busybox-1.19.3/docs/logging_and_backgrounding.txt
new file mode 100644
index 0000000..7e68855
--- /dev/null
+++ b/busybox-1.19.3/docs/logging_and_backgrounding.txt
@@ -0,0 +1,96 @@
+	Logging and backgrounding
+
+By default, bb_[p]error_msg[_and_die] messages go to stderr,
+and of course, usually applets do not auto-background. :)
+
+Historically, daemons and inetd services are different.
+
+Busybox is trying to provide compatible behavior, thus if an applet
+is emulating an existing utility, it should mimic it. If utility
+auto-backgrounds itself, busybox applet should do the same.
+If utility normally logs to syslog, busybox applet should do
+the same too.
+
+However, busybox should not needlessly restrict the freedom
+of the users. And users have different needs and different preferences.
+Some might like logging everything from daemons to syslog.
+Others prefer running stuff under runsv/svlogd and thus would like
+logging to stderr and no daemonization.
+
+To help with that, busybox applets should have options to override
+default behavior, whatever that is for a given applet.
+
+
+Current situation is a bit of a mess:
+
+acpid - auto-backgrounds unless -d
+crond - auto-backgrounds unless -f, logs to syslog unless -d or -L.
+    option -d logs to stderr, -L FILE logs to FILE
+devfsd - (obsolete)
+dnsd - option -d makes it background and log to syslog
+fakeidentd - inetd service. Auto-backgrounds and logs to syslog
+    if no -f and no -i and no -w (-i is "inetd service" flag,
+    -w is "inetd-wait service" flag)
+ftpd - inetd service. Logs to syslog with -S, with -v logs to strerr too
+httpd - auto-backgrounds unless -f or -i (-i is "inetd service" flag)
+inetd - auto-backgrounds unless -f, logs to syslog unless -e
+klogd - auto-backgrounds unless -n
+syslogd - auto-backgrounds unless -n
+telnetd - auto-backgrounds unless -f or -i (-i is "inetd service" flag)
+udhcpc - auto-backgrounds unless -f after lease is obtained,
+    option -b makes it background sooner (when lease attempt
+    fails and retries start),
+    after backgrounding it stops logging to stderr;
+    logs to stderr, but option -S makes it log *also* to syslog
+udhcpd - auto-backgrounds and do not log to stderr unless -f,
+    otherwise logs to stderr, but option -S makes it log *also* to syslog
+zcip - auto-backgrounds and logs *also* to syslog unless -f
+
+Total: 13 applets (+1 obsolete),
+ 4 log to syslog by default (crond fakeidentd inetd zcip),
+ 5 never log to syslog (acpid httpd telnetd klogd syslogd, last two
+ - for obviously correct reasons),
+ there are no daemons which always log to syslog,
+ 12 auto-background if not run as inetd services (all except dnsd.
+ Note that there is no "standard" dnsd AFAIKS). But see below
+ for daemons (tcpsvd etc) which don't auto-background.
+
+miscutils/crond.c:            logmode = LOGMODE_SYSLOG;
+networking/dnsd.c:            logmode = LOGMODE_SYSLOG;
+networking/ftpd.c:            logmode = LOGMODE_NONE;
+networking/ftpd.c:            logmode |= LOGMODE_SYSLOG;
+networking/inetd.c:           logmode = LOGMODE_SYSLOG;
+networking/isrv_identd.c:     logmode = LOGMODE_SYSLOG;
+networking/telnetd.c:         logmode = LOGMODE_SYSLOG;
+networking/udhcp/dhcpc.c:     logmode = LOGMODE_NONE;
+networking/udhcp/dhcpc.c:     logmode |= LOGMODE_SYSLOG;
+networking/udhcp/dhcpc.c:     logmode &= ~LOGMODE_STDIO;
+networking/udhcp/dhcpd.c:     logmode = LOGMODE_NONE;
+networking/udhcp/dhcpd.c:     logmode |= LOGMODE_SYSLOG;
+networking/zcip.c:            logmode |= LOGMODE_SYSLOG;
+
+
+These daemons never auto-background and never log to syslog:
+
+lpd - inetd service. Has nothing to log so far, though
+dhcprelay - standard behavior
+inotifyd - standard behavior
+runsv - standard behavior
+runsvdir - standard behavior
+svlogd - standard behavior
+tcpsvd, udpsvd - standard behavior
+tftpd - standard behavior
+
+
+Non-daemons (seems to be use syslog for a good reason):
+
+networking/nameif.c:          logmode |= LOGMODE_SYSLOG;
+loginutils/chpasswd.c:        logmode = LOGMODE_BOTH;
+loginutils/chpasswd.c:        logmode = LOGMODE_STDIO;
+loginutils/getty.c:           logmode = LOGMODE_BOTH;
+loginutils/getty.c:           logmode = LOGMODE_NONE;
+loginutils/passwd.c:          logmode = LOGMODE_STDIO;
+loginutils/passwd.c:          logmode = LOGMODE_BOTH;
+loginutils/sulogin.c:         logmode = LOGMODE_SYSLOG; (used if stdio isn't a tty)
+loginutils/sulogin.c:         logmode = LOGMODE_BOTH;
+util-linux/mount.c:           logmode = LOGMODE_SYSLOG; (used in a backgrounded NFS mount helper)
diff --git a/busybox-1.19.3/docs/mdev.txt b/busybox-1.19.3/docs/mdev.txt
new file mode 100644
index 0000000..2d03bd8
--- /dev/null
+++ b/busybox-1.19.3/docs/mdev.txt
@@ -0,0 +1,145 @@
+-------------
+ MDEV Primer
+-------------
+
+For those of us who know how to use mdev, a primer might seem lame.  For
+everyone else, mdev is a weird black box that they hear is awesome, but can't
+seem to get their head around how it works.  Thus, a primer.
+
+-----------
+ Basic Use
+-----------
+
+Mdev has two primary uses: initial population and dynamic updates.  Both
+require sysfs support in the kernel and have it mounted at /sys.  For dynamic
+updates, you also need to have hotplugging enabled in your kernel.
+
+Here's a typical code snippet from the init script:
+[0] mount -t proc proc /proc
+[1] mount -t sysfs sysfs /sys
+[2] echo /sbin/mdev > /proc/sys/kernel/hotplug
+[3] mdev -s
+
+Alternatively, without procfs the above becomes:
+[1] mount -t sysfs sysfs /sys
+[2] sysctl -w kernel.hotplug=/sbin/mdev
+[3] mdev -s
+
+
+Of course, a more "full" setup would entail executing this before the previous
+code snippet:
+[4] mount -t tmpfs -o size=64k,mode=0755 tmpfs /dev
+[5] mkdir /dev/pts
+[6] mount -t devpts devpts /dev/pts
+
+The simple explanation here is that [1] you need to have /sys mounted before
+executing mdev.  Then you [2] instruct the kernel to execute /sbin/mdev whenever
+a device is added or removed so that the device node can be created or
+destroyed.  Then you [3] seed /dev with all the device nodes that were created
+while the system was booting.
+
+For the "full" setup, you want to [4] make sure /dev is a tmpfs filesystem
+(assuming you're running out of flash).  Then you want to [5] create the
+/dev/pts mount point and finally [6] mount the devpts filesystem on it.
+
+-------------
+ MDEV Config   (/etc/mdev.conf)
+-------------
+
+Mdev has an optional config file for controlling ownership/permissions of
+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>
+
+For example:
+    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/
+This will move "hda" into the drives/ subdirectory.
+	hdb 0:3 660 =cdrom
+This will rename "hdb" to "cdrom".
+
+Similarly, ">path" renames/moves the device but it also creates
+a direct symlink /dev/DEVNAME to the renamed/moved device.
+
+You can also prevent creation of device nodes with the 4th field as "!":
+	tty[a-z]. 0:0 660 !
+	pty[a-z]. 0:0 660 !
+
+If you also enable support for executing your own commands, then the file has
+the format:
+	<device regex> <uid>:<gid> <permissions> [=path] [@|$|*<command>]
+    or
+	<device regex> <uid>:<gid> <permissions> [>path] [@|$|*<command>]
+    or
+	<device regex> <uid>:<gid> <permissions> [!] [@|$|*<command>]
+
+For example:
+---8<---
+# block devices
+([hs]d[a-z])		root:disk	660	>disk/%1/0
+([hs]d[a-z])([0-9]+)	root:disk	660	>disk/%1/%2
+mmcblk([0-9]+)		root:disk	660	>disk/mmc/%1/0
+mmcblk([0-9]+)p([0-9]+)	root:disk	660	>disk/mmc/%1/%2
+# network devices
+(tun|tap)		root:network	660	>net/%1
+---8<---
+
+The special characters have the meaning:
+	@ Run after creating the device.
+	$ Run before removing the device.
+	* Run both after creating and before removing the device.
+
+The command is executed via the system() function (which means you're giving a
+command to the shell), so make sure you have a shell installed at /bin/sh.  You
+should also keep in mind that the kernel executes hotplug helpers with stdin,
+stdout, and stderr connected to /dev/null.
+
+For your convenience, the shell env var $MDEV is set to the device name.  So if
+the device "hdc" was matched, MDEV would be set to "hdc".
+
+----------
+ FIRMWARE
+----------
+
+Some kernel device drivers need to request firmware at runtime in order to
+properly initialize a device.  Place all such firmware files into the
+/lib/firmware/ directory.  At runtime, the kernel will invoke mdev with the
+filename of the firmware which mdev will load out of /lib/firmware/ and into
+the kernel via the sysfs interface.  The exact filename is hardcoded in the
+kernel, so look there if you need to know how to name the file in userspace.
+
+------------
+ SEQUENCING
+------------
+
+Kernel does not serialize hotplug events. It increments SEQNUM environmental
+variable for each successive hotplug invocation. Normally, mdev doesn't care.
+This may reorder hotplug and hot-unplug events, with typical symptoms of
+device nodes sometimes not created as expected.
+
+However, if /dev/mdev.seq file is found, mdev will compare its
+contents with SEQNUM. It will retry up to two seconds, waiting for them
+to match. If they match exactly (not even trailing '\n' is allowed),
+or if two seconds pass, mdev runs as usual, then it rewrites /dev/mdev.seq
+with SEQNUM+1.
+
+IOW: this will serialize concurrent mdev invocations.
+
+If you want to activate this feature, execute "echo >/dev/mdev.seq" prior to
+setting mdev to be the hotplug handler. This writes single '\n' to the file.
+NB: mdev recognizes /dev/mdev.seq consisting of single '\n' character
+as a special case. IOW: this will not make your first hotplug event
+to stall for two seconds.
diff --git a/busybox-1.19.3/docs/new-applet-HOWTO.txt b/busybox-1.19.3/docs/new-applet-HOWTO.txt
new file mode 100644
index 0000000..6a8054d
--- /dev/null
+++ b/busybox-1.19.3/docs/new-applet-HOWTO.txt
@@ -0,0 +1,182 @@
+How to Add a New Applet to BusyBox
+==================================
+
+This document details the steps you must take to add a new applet to BusyBox.
+
+Credits:
+Matt Kraai - initial writeup
+Mark Whitley - the remix
+Thomas Lundquist - Trying to keep it updated.
+
+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
+-------------
+
+First, write your applet.  Be sure to include copyright information at the top,
+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.
+
+For a new applet mu, here is the code that would go in mu.c:
+
+(busybox.h already includes most usual header files. You do not need
+#include <stdio.h> etc...)
+
+
+----begin example code------
+
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mu implementation for busybox
+ *
+ * Copyright (C) [YEAR] by [YOUR NAME] <YOUR EMAIL>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "other.h"
+
+int mu_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mu_main(int argc, char **argv)
+{
+	int fd;
+	ssize_t n;
+	char mu;
+
+	fd = xopen("/dev/random", O_RDONLY);
+
+	if ((n = safe_read(fd, &mu, 1)) < 1)
+		bb_perror_msg_and_die("/dev/random");
+
+	return mu;
+}
+
+----end example code------
+
+
+Coding Style
+------------
+
+Before you submit your applet for inclusion in BusyBox, (or better yet, before
+you _write_ your applet) please read through the style guide in the docs
+directory and make your program compliant.
+
+
+Some Words on libbb
+-------------------
+
+As you are writing your applet, please be aware of the body of pre-existing
+useful functions in libbb. Use these instead of reinventing the wheel.
+
+Additionally, if you have any useful, general-purpose functions in your
+applet that could be useful in other applets, consider putting them in libbb.
+
+And it may be possible that some of the other applets uses functions you
+could use. If so, you have to rip the function out of the applet and make
+a libbb function out of it.
+
+Adding a libbb function:
+------------------------
+
+Make a new file named <function_name>.c
+
+----start example code------
+
+#include "libbb.h"
+#include "other.h"
+
+int function(char *a)
+{
+	return *a;
+}
+
+----end example code------
+
+Add <function_name>.o in the right alphabetically sorted place
+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.src and try to find out if the function is
+tunable and add it there if it is.
+
+
+Placement / Directory
+---------------------
+
+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.src in the chosen directory:
+
+lib-$(CONFIG_MU)               += mu.o
+
+Add the applet to Config.src in the chosen directory:
+
+config MU
+	bool "MU"
+	default n
+	help
+	  Returns an indeterminate value.
+
+
+Usage String(s)
+---------------
+
+Next, add usage information for you applet to include/usage.src.h.
+This should look like the following:
+
+	#define mu_trivial_usage \
+		"-[abcde] FILES"
+	#define mu_full_usage \
+		"Returns an indeterminate value.\n\n" \
+		"Options:\n" \
+		"\t-a\t\tfirst function\n" \
+		"\t-b\t\tsecond function\n" \
+		...
+
+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.src.h.)
+
+
+Header Files
+------------
+
+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.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))
+	/* all programs below here are alphabetically "greater than" 'mu' */
+
+
+The Grand Announcement
+----------------------
+
+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:
+	git add libbb/function.c
+then
+	git commit
+	git format-patch HEAD^
+and send it to the mailing list:
+	busybox@busybox.net
+	http://busybox.net/mailman/listinfo/busybox
+
+Sending patches as attachments is preferred, but not required.
diff --git a/busybox-1.19.3/docs/nofork_noexec.txt b/busybox-1.19.3/docs/nofork_noexec.txt
new file mode 100644
index 0000000..c58f5a8
--- /dev/null
+++ b/busybox-1.19.3/docs/nofork_noexec.txt
@@ -0,0 +1,98 @@
+	NOEXEC and NOFORK applets.
+
+Unix shells traditionally execute some commands internally in the attempt
+to dramatically speed up execution. It will be slow as hell if for every
+"echo blah" shell will fork and exec /bin/echo. To this end, shells
+have to _reimplement_ these commands internally.
+
+Busybox is unique in this regard because it already is a collection
+of reimplemented Unix commands, and we can do the same trick
+for speeding up busybox shells, and more. NOEXEC and NOFORK applets
+are exactly those applets which are eligible for these tricks.
+
+Applet will be subject to NOFORK/NOEXEC tricks if it is marked as such
+in applets.h. FEATURE_PREFER_APPLETS is a config option which
+globally enables usage of NOFORK/NOEXEC tricks.
+If it is enabled, FEATURE_SH_STANDALONE can be enabled too,
+and then shells will use NOFORK/NOEXEC tricks for ordinary commands.
+NB: shell builtins use these tricks regardless of FEATURE_SH_STANDALONE
+or FEATURE_PREFER_APPLETS.
+
+In C, if you want to call a program and wait for it, use
+spawn_and_wait(argv), BB_EXECVP(prog,argv) or BB_EXECLP(prog,argv0,...).
+They check whether program name is an applet name and optionally
+do NOFORK/NOEXEC thing depending on configuration.
+
+
+	NOEXEC
+
+NOEXEC applet should work correctly if another applet forks and then
+executes exit(<applet>_main(argc,argv)) in the child. The rules
+roughly are:
+
+* do not expect shared global variables/buffers to be in their
+  "initialized" state. Examples: xfunc_error_retval can be != 1,
+  bb_common_bufsiz1 can be scribbled over, ...
+* do not expect that stdio wasn't used before. Calling set[v]buf()
+  can be disastrous.
+* ...
+
+NOEXEC applets save only one half of fork+exec overhead.
+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 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().
+  - xfuncs are okay. They are using special trick to return
+    to the caller applet instead of dying when they detect "x" condition.
+  - you may "exit" to caller applet by calling xfunc_die(). Return value
+    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
+  (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
+  allocation. All other allocations should use malloc[_or_warn]().
+  After first allocation, you cannot use any xfuncs.
+  Otherwise, failing xfunc will return to caller applet
+  without freeing malloced data!
+* All allocated data, opened files, signal handlers, termios settings,
+  O_NONBLOCK flags etc should be freed/closed/restored prior to return.
+* ...
+
+NOFORK applets give the most of speed advantage, but are trickiest
+to implement. In order to minimize amount of bugs and maintenance,
+prime candidates for NOFORK-ification are those applets which
+are small and easy to audit, and those which are more likely to be
+frequently executed from shell/find/xargs, particularly in shell
+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
diff --git a/busybox-1.19.3/docs/posix_conformance.txt b/busybox-1.19.3/docs/posix_conformance.txt
new file mode 100644
index 0000000..5b616d7
--- /dev/null
+++ b/busybox-1.19.3/docs/posix_conformance.txt
@@ -0,0 +1,741 @@
+
+Busybox POSIX conformance table
+
+See POSIX documentation (1003.1-2008) here:
+http://www.opengroup.org/onlinepubs/9699919799/
+And the complete list of all utilities that POSIX covers:
+http://www.opengroup.org/onlinepubs/9699919799/idx/utilities.html
+
+This listing is a work in progress, and currently only covers
+tool options (not operands, environment variables, return codes, etc..).
+For each option it is set if it (a) exists and (b) compliant to POSIX 2008.
+Some options exist but there is no value in the 'compliant' column: that
+means no one has yet bothered to make sure that the option does what it is
+required to do.
+
+-----------------------------------------------
+
+POSIX Tools supported only as shell built-ins (ash shell):
+  alias, bg, cd, fg, getopts, hash, jobs, read, type, umask, ulimit,
+  unalias, wait, write
+
+POSIX Tools not supported:
+  asa, at, batch, bc, c99, command, compress, csplit, ex, fc, file,
+  gencat, getconf, iconv, join, link, locale, localedef, lp, m4,
+  mailx, newgrp, nl, paste, pathchk, pax, pr, qalter, qdel, qhold, qmove,
+  qmsg, qrerun, qrls, qselect, qsig, qstat, qsub, tabs, talk, tput,
+  tsort, unlink, uucp, uustat, uux
+
+POSIX Tools not supported (DEVELOPMENT):
+  admin, cflow, ctags, cxref, delta, fort77, get, lex, make, nm, prs, rmdel,
+  sact, sccs, strip, unget, val, what, yacc
+
+
+POSIX Tools supported:
+
+Note: echo, printf, kill, pwd documented here as stand-alone applets,
+      not as ash built-ins.
+
+
+ar POSIX options ********************* Failed to recognize zip & tar (did not compare to regular ar)
+ option           | exists | compliant | remarks
+  -C              |  no    | no        |
+  -T              |  no    | no        |
+  -a              |  no    | no        |
+  -b              |  no    | no        |
+  -c              |  no    | no        |
+  -d              |  no    | no        |
+  -i              |  no    | no        |
+  -m              |  no    | no        |
+  -p              |  yes   |           |
+  -q              |  no    | no        |
+  -r              |  no    | no        |
+  -s              |  no    | no        |
+  -t              |  yes   |           |
+  -u              |  no    | no        |
+  -v              |  yes   |           |
+  -x              |  yes   |           |
+ar Busybox specific options:
+  -o
+
+awk POSIX options
+ option           | exists | compliant | remarks
+  -F ERE          |  yes   |           |
+  -f progfile     |  yes   |           |
+  -v assignment   |  yes   |           |
+awk Busybox specific options: None
+
+basename POSIX options: None
+basename Busybox specific options: None
+
+cal POSIX options: None
+cal Busybox specific options:
+  -y, -j
+
+cat POSIX options
+ option           | exists | compliant | remarks
+  -u              |  yes   | no        | option is ignored
+cat Busybox specific options: None
+
+chgrp POSIX options
+ option           | exists | compliant | remarks
+  -H              |  yes   |           |
+  -L              |  yes   |           |
+  -P              |  yes   |           |
+  -R              |  yes   |           |
+  -h              |  yes   |           |
+chgrp Busybox specific options:
+  -f, -c, -v
+
+chmod POSIX options
+ option           | exists | compliant | remarks
+  -R              |  yes   | yes       |
+chmod Busybox specific options:
+  -f, -v, -c
+
+chown POSIX options *********************************************
+ option           | exists | compliant | remarks
+  -H              |  yes   |           | It seems like all flags are supported (according to printout), but
+  -L              |  yes   |           |  it fails to work on my machine
+  -P              |  yes   |           |
+  -R              |  yes   |           |
+  -h              |  yes   |           |
+chown Busybox specific options:
+  -f, -c, -v
+
+cksum POSIX options: None
+cksum Busybox specific options: None
+
+cmp POSIX options
+ option           | exists | compliant | remarks
+  -l              |  yes   | yes       |
+  -s              |  yes   | yes       |
+cmp Busybox specific options:
+
+
+comm POSIX options
+ option           | exists | compliant | remarks
+  -1              |  yes   | yes       |
+  -2              |  yes   | yes       |
+  -3              |  yes   | yes       |
+comm Busybox specific options: None
+
+cp POSIX options
+ option           | exists | compliant | remarks
+  -H              |  yes   | yes       |
+  -L              |  yes   | yes       |
+  -P              |  yes   | yes       |
+  -R              |  yes   | yes       |
+  -f              |  yes   | yes       |
+  -i              |  yes   | yes       |
+  -p              |  yes   | yes       |
+cp Busybox specific options:
+  -d, -a, -s, -c, -r, -l
+
+crontab POSIX options
+ option           | exists | compliant | remarks
+  -e              |  yes   |           |
+  -l              |  yes   |           |
+  -r              |  yes   |           |
+crontab Busybox specific options:
+  -u, -c
+
+cut POSIX options
+ option           | exists | compliant | remarks
+  -b list         |  yes   | yes       |
+  -c list         |  yes   | yes       |
+  -d delim        |  yes   | yes       |
+  -f list         |  yes   | yes       |
+  -n              |  yes   | yes       |
+  -s              |  yes   | yes       |
+cut Busybox specific options: None
+
+date POSIX options
+ option           | exists | compliant | remarks
+  -u              |  yes   | yes       |
+date Busybox specific options:
+  -I[SPEC], -d TIME, -r FILE, -R, -D FMT
+
+dd POSIX options:
+ option           | exists | compliant | remarks
+  if              |  yes   |           |
+  of              |  yes   |           |
+  ibs             |  yes   |           |
+  obs             |  yes   |           |
+  bs              |  yes   |           |
+  cbs             |  no    | no        |
+  skip            |  yes   |           |
+  seek            |  yes   |           |
+  count           |  yes   |           |
+  conv=ascii      |  no    | no        |
+  conv=ebcdic     |  no    | no        |
+  conv=ibm        |  no    | no        |
+  conv=block      |  no    | no        |
+  conv=unblock    |  no    | no        |
+  conv=lcase      |  no    | no        |
+  conv=ucase      |  no    | no        |
+  conv=swap       |  no    | no        |
+  conv=noerror    |  yes   |           |
+  conv=notrunc    |  yes   |           |
+  conv=sync       |  yes   |           |
+dd Busybox specific options:
+ conv=fsync
+
+df POSIX options
+ option           | exists | compliant | remarks
+  -P              |  yes   | yes       |
+  -k              |  yes   | yes       |
+  -t              |  no    | no        |
+df Busybox specific options:
+  -a, -m, -B SIZE, -i, -h
+Remark:
+- It seems that GNU df does not round percents up in its output (thus its results are a bit different)
+
+diff POSIX options
+ option           | exists | compliant | remarks
+  -C n            |  no    | no        |
+  -U n            |  yes   |           |
+  -b              |  yes   |           |
+  -c              |  no    | no        |
+  -e              |  no    | no        |
+  -f              |  no    | no        |
+  -r              |  yes   |           |
+  -u              |  no    | no        |
+diff Busybox specific options:
+  -d, -a, -s, -t, -L, -N, -i, -T, -w, -q, -S
+
+dirname POSIX options: None
+dirname Busybox specific options: None
+
+du POSIX options
+ option           | exists | compliant | remarks
+  -H              |  yes   |           |
+  -L              |  yes   |           |
+  -a              |  yes   |           |
+  -k              |  yes   |           |
+  -s              |  yes   |           |
+  -x              |  yes   |           |
+du Busybox specific options:
+  -c, -m, -h, -d N, -l
+
+
+echo POSIX options: None
+ option           | exists | compliant | remarks
+  -n              |  yes   | yes       | The result of -n is "implementation-defined"
+echo Busybox specific options:
+  -e, -E
+
+ed POSIX options
+ option           | exists | compliant | remarks
+  -p string       |  no    | no        |
+  -s              |  no    | no        |
+ed Busybox specific options: None
+
+env POSIX options
+ option           | exists | compliant | remarks
+  -i              |  no    | no        |
+env Busybox specific options:
+  -u, -,  -i
+
+expand POSIX options
+ option           | exists | compliant | remarks
+  -t tablist      |  yes   | yes       |
+expand Busybox specific options:
+  --tabs=N, -i, --initial
+
+expr POSIX operations:
+ option           | exists | compliant | remarks
+  |               |  yes   |  yes      |
+  &               |  yes   |  yes      |
+  =               |  yes   |  yes      |
+  >               |  yes   |  yes      |
+  >=              |  yes   |  yes      |
+  <=              |  yes   |  yes      |
+  <               |  yes   |  yes      |
+  !=              |  yes   |  yes      |
+  +               |  yes   |  yes      |
+  -               |  yes   |  yes      |
+  *               |  yes   |  yes      |
+  /               |  yes   |  yes      |
+  %               |  yes   |  yes      |
+  :               |  yes   |  yes      |
+  (expr)          |  yes   |  yes      |
+  integer         |  yes   |  yes      |
+  string          |  yes   |  yes      |
+expr Busybox specific operations:
+  match, substr, index, length, quote
+
+false POSIX options: None
+false Busybox specific options: None
+
+find POSIX options
+ option           | exists | compliant | remarks
+  -H              |  no    | no        |
+  -L              |  no    | no        |
+find Busybox specific options:
+  -group NAME, -mtime DAYS, -print, -maxdepth N, -exec CMD ARG ;, -newer FILE, -context, -iname PATTERN, -follow, -depth, -xdev, -inum N, -type X, -print0, -mindepth N, -mmin MINS, -regex PATTERN, -prune, -path PATTERN, -user NAME, -delete, -perm NNN, -name PATTERN, -size N[bck]
+
+fold POSIX options
+ option           | exists | compliant | remarks
+  -b              |  yes   | yes       |
+  -s              |  yes   | yes       |
+  -w width        |  yes   | yes       |
+fold Busybox specific options: None
+
+fuser POSIX options
+ option           | exists | compliant | remarks
+  -c              |  no    | no        |
+  -f              |  no    | no        |
+  -u              |  no    | no        |
+fuser Busybox specific options:
+  -m, -k, -4, -SIGNAL, -6, -s
+
+grep POSIX options
+ option           | exists | compliant | remarks
+  -E              |  yes   |           |
+  -F              |  yes   |           |
+  -c              |  yes   |           |
+  -e pattern_list |  yes   |           |
+  -f pattern_file |  yes   |           |
+  -i              |  yes   |           |
+  -l              |  yes   |           |
+  -n              |  yes   |           |
+  -q              |  yes   |           |
+  -s              |  yes   |           |
+  -v              |  yes   |           |
+  -x              |  no    | no        |
+grep Busybox specific options:
+  -A, -C, -B, -L, -H, -o, -h, -w, -r, -z, -m MAX
+
+head POSIX options
+ option           | exists | compliant | remarks
+  -n number       |  yes   | yes       |
+head Busybox specific options:
+  -v, -c NUM, -q
+
+id POSIX options
+ option           | exists | compliant | remarks
+  -G              |  yes   | yes       |
+  -g              |  yes   | yes       |
+  -n              |  yes   | yes       |
+  -r              |  yes   | yes       |
+  -u              |  yes   | yes       |
+id Busybox specific options:
+  -Z
+
+ipcrm POSIX options
+ option           | exists | compliant | remarks
+  -M shmkey       |  no    | no        |
+  -Q msgkey       |  no    | no        |
+  -S semkey       |  no    | no        |
+  -m shmid        |  no    | no        |
+  -q msgid        |  no    | no        |
+  -s semid        |  no    | no        |
+ipcrm Busybox specific options:
+  -mM, -qQ, -sS
+
+ipcs POSIX options
+ option           | exists | compliant | remarks
+  -a              |  yes   |           |
+  -b              |  no    | no        |
+  -c              |  yes   |           |
+  -m              |  yes   |           |
+  -o              |  no    | no        |
+  -p              |  yes   |           |
+  -q              |  yes   |           |
+  -s              |  yes   |           |
+  -t              |  yes   |           |
+ipcs Busybox specific options:
+  -l, -i, -u
+
+kill POSIX options
+ option           | exists | compliant | remarks
+  -l              |  yes   | yes       |
+  -s signal_name  |  yes   | yes       |
+  -signal_name    |  yes   | yes       |
+  -signal_number  |  yes   | yes       |
+kill Busybox specific options:
+   -q, -o
+
+ln POSIX options
+ option           | exists | compliant | remarks
+  -L              |  no    | no        |
+  -P              |  no    | no        |
+  -f              |  yes   | yes       |
+  -s              |  yes   | yes       |
+ln Busybox specific options:
+  -S suf, -n, -b
+
+logger POSIX options: None
+logger Busybox specific options:
+  -p PRIO, -t TAG, -s
+
+logname POSIX options: None
+logname Busybox specific options: None
+
+ls POSIX options
+ option           | exists | compliant | remarks
+  -1              |  yes   | yes       |
+  -A              |  yes   | yes       |
+  -C              |  yes   | yes       |
+  -F              |  yes   | yes       | And more: '=' for sockets (not defined by POSIX)
+  -H              |  no    | no        |
+  -L              |  yes   | yes       | But coloring may be wrong (at least POSIX does not require correct colors :) )
+  -R              |  yes   | yes       |
+  -S              |  yes   | yes       |
+  -a              |  yes   | yes       |
+  -c              |  yes   | no        | Sorts output with '-l' (should only show ctime with '-l', and sort only with '-t')
+  -d              |  yes   | no        | When invoked together with '-L' should read symbolic links, and doesn't
+  -f              |  no    | no        |
+  -g              |  no    | no        |
+  -i              |  yes   | yes       |
+  -k              |  yes   | no        | Does something completely unrelated! (Lists security context instead of specifying block size)
+  -l              |  yes   | yes       |
+  -m              |  no    | no        |
+  -n              |  yes   | no        | Works correctly only together with '-l' (but POSIX requires '-l' to be implicitly assumed)
+  -o              |  no    | no        |
+  -p              |  yes   | yes       |
+  -q              |  no    | no        |
+  -r              |  yes   | yes       |
+  -s              |  yes   | yes       |
+  -t              |  yes   | yes       |
+  -u              |  yes   | yes       |
+  -x              |  yes   | yes       |
+ls Busybox specific options:
+  --color, -T NUM, -K, -X, -Z, -e, -h, -v, -w NUM
+
+man POSIX options
+ option           | exists | compliant | remarks
+  -k              |  no    | no        |
+man Busybox specific options:
+  -a      Display all pages
+
+
+mesg POSIX options: None
+mesg Busybox specific options: None
+
+mkdir POSIX options
+ option           | exists | compliant | remarks
+  -m mode         |  yes   | yes       |
+  -p              |  yes   | yes       |
+mkdir Busybox specific options:
+  -Z
+
+mkfifo POSIX options
+ option           | exists | compliant | remarks
+  -m mode         |  yes   | yes       |
+mkfifo Busybox specific options:
+  -Z
+
+more POSIX options
+ option           | exists | compliant | remarks
+  -c              |  no    | no        |
+  -e              |  no    | no        |
+  -i              |  no    | no        |
+  -n number       |  no    | no        |
+  -p command      |  no    | no        |
+  -s              |  no    | no        |
+  -t tagstring    |  no    | no        |
+  -u              |  no    | no        |
+more Busybox specific options: None
+
+mv POSIX options
+ option           | exists | compliant | remarks
+  -f              |  yes   | yes       |
+  -i              |  yes   | yes       |
+mv Busybox specific options: None
+
+nice POSIX options
+ option           | exists | compliant | remarks
+  -n increment    |  yes   | yes       |
+nice Busybox specific options: None
+
+nohup POSIX options: None
+nohup Busybox specific options: None
+
+od POSIX options
+ option           | exists | compliant | remarks
+  -A address_base |  no    | no        |
+  -N count        |  no    | no        |
+  -b              |  no    | no        |
+  -c              |  no    | no        |
+  -d              |  no    | no        |
+  -j skip         |  no    | no        |
+  -o              |  no    | no        |
+  -s              |  no    | no        |
+  -t type_string  |  no    | no        |
+  -v              |  no    | no        |
+  -x              |  no    | no        |
+od Busybox specific options: None
+
+patch POSIX options
+ option           | exists | compliant | remarks
+  -D define       |  no    | no        |
+  -N              |  no    | no        |
+  -R              |  yes   | yes       |
+  -b              |  no    | no        |
+  -c              |  no    | no        |
+  -d dir          |  no    | no        |
+  -e              |  no    | no        |
+  -i patchfile    |  yes   | yes       |
+  -l              |  no    | no        |
+  -n              |  no    | no        |
+  -o outfile      |  no    | no        |
+  -p num          |  yes   | yes       |
+  -r rejectfile   |  no    | no        |
+  -u              |  no    | no        |
+patch Busybox specific options: None
+
+printf POSIX options: None
+printf Busybox specific options: None
+
+ps POSIX options
+ option           | exists | compliant | remarks
+  -A              |  no    | no        |
+  -G grouplist    |  no    | no        |
+  -U userlist     |  no    | no        |
+  -a              |  no    | no        |
+  -d              |  no    | no        |
+  -e              |  no    | no        |
+  -f              |  no    | no        |
+  -g grouplist    |  no    | no        |
+  -l              |  no    | no        |
+  -n namelist     |  no    | no        |
+  -o format       |  yes   | no        | not supported: ruser, group, rgroup, pcpu
+  -p proclist     |  no    | no        |
+  -t termlist     |  no    | no        |
+  -u userlist     |  no    | no        |
+ps Busybox specific options: None
+
+pwd POSIX options
+ option           | exists | compliant | remarks
+  -L              |  no    | no        |
+  -P              |  no    | no        |
+pwd Busybox specific options: None
+
+renice POSIX options
+ option           | exists | compliant | remarks
+  -g              |  yes   | yes       |
+  -n increment    |  yes   | yes       | Note POSIX allows only to run with this option (busybox also allows to run without '-n' and set niceness directly)
+  -p              |  yes   | yes       |
+  -u              |  yes   | yes       |
+renice Busybox specific options: None
+
+rm POSIX options
+ option           | exists | compliant | remarks
+  -R              |  yes   | yes       |
+  -f              |  yes   | yes       |
+  -i              |  yes   | yes       |
+  -r              |  yes   | yes       |
+rm Busybox specific options: None
+
+rmdir POSIX options
+ option           | exists | compliant | remarks
+  -p              |  yes   | yes       |
+rmdir Busybox specific options:
+  --parents
+
+sed POSIX options
+ option           | exists | compliant | remarks
+  -e script       |  yes   |           |
+  -f script_file  |  yes   |           |
+  -n              |  yes   |           |
+sed Busybox specific options:
+  -i, -r
+
+sh POSIX options
+ option           | exists | compliant | remarks
+  -c              |  no    | no        |
+  -i              |  no    | no        |
+  -s              |  no    | no        |
+sh Busybox specific options: None
+
+sleep POSIX options: None
+sleep Busybox specific options: None
+
+sort POSIX options
+ option           | exists | compliant | remarks
+  -C              |  no    | no        |
+  -b              |  yes   | yes       |
+  -c              |  yes   | yes       |
+  -d              |  yes   | yes       |
+  -f              |  yes   | yes       |
+  -i              |  yes   | yes       | But is not like GNU sort, which isn't! (try to sort 'a\nA\nB\nb' with and without -f)
+  -k keydef       |  yes   |           |
+  -m              |  no    | no        |
+  -n              |  yes   | yes       |
+  -o output       |  yes   | yes       |
+  -r              |  yes   | yes       |
+  -t char         |  yes   |           |
+  -u              |  yes   | yes       |
+sort Busybox specific options:
+  -mST, -g, -M, -s, -z
+
+split POSIX options
+ option           | exists | compliant | remarks
+  -a suffix_length |  yes   | yes       |
+  -b n            |  yes   | yes       |
+  -b nk           |  yes   | yes       |
+  -b nm           |  yes   | yes       |
+  -l line_count   |  yes   | yes       |
+split Busybox specific options: None
+
+strings POSIX options
+ option           | exists | compliant | remarks
+  -a              |  yes   | yes       |
+  -n number       |  yes   | yes       |
+  -t format       |  no    | no        |
+strings Busybox specific options:
+  -o, -f
+
+stty POSIX options
+ option           | exists | compliant | remarks
+  -a              |  yes   | yes       |
+  -g              |  yes   | yes       |
+stty Busybox specific options:
+  -F DEVICE
+
+tail POSIX options
+ option           | exists | compliant | remarks
+  -c number       |  yes   | yes       |
+  -f              |  yes   | yes       |
+  -n number       |  yes   | yes       |
+tail Busybox specific options:
+  -v, -q, -s SEC
+
+tee POSIX options
+ option           | exists | compliant | remarks
+  -a              |  yes   | yes       |
+  -i              |  yes   | yes       |
+tee Busybox specific options: None
+
+test POSIX options: None
+test Busybox specific options: None
+
+time POSIX options
+ option           | exists | compliant | remarks
+  -p              |  no    | no        |
+time Busybox specific options:
+  -v
+
+touch POSIX options
+ option           | exists | compliant | remarks
+  -a              |  no    | no        |
+  -c              |  yes   | yes       |
+  -d date_time    |  no    | no        |
+  -m              |  no    | no        |
+  -r ref_file     |  no    | no        |
+  -t time         |  no    | no        |
+touch Busybox specific options: None
+
+tr POSIX options
+ option           | exists | compliant | remarks
+  -C              |  no    | no        |
+  -c              |  yes   | yes       |
+  -d              |  yes   | yes       |
+  -s              |  yes   | yes       |
+tr Busybox specific options: None
+
+true POSIX options: None
+true Busybox specific options: None
+
+tty POSIX options: None
+tty Busybox specific options:
+  -s
+
+uname POSIX options
+ option           | exists | compliant | remarks
+  -a              |  yes   | yes       |
+  -m              |  yes   | yes       |
+  -n              |  yes   | yes       |
+  -r              |  yes   | yes       |
+  -s              |  yes   | yes       |
+  -v              |  yes   | yes       |
+uname Busybox specific options:
+  -p
+
+uncompress POSIX options
+ option           | exists | compliant | remarks
+  -c              |  yes   | yes       |
+  -f              |  yes   | yes       |
+  -v              |  no    | no        |
+uncompress Busybox specific options: None
+
+unexpand POSIX options
+ option           | exists | compliant | remarks
+  -a              |  yes   | no        | POSIX requires converting two or more spaces to tabs, busybox converts one or more spaces
+  -t tablist      |  yes   | yes       |
+unexpand Busybox specific options:
+  --tabs=N, -f, --first-only, --all
+
+uniq POSIX options
+ option           | exists | compliant | remarks
+  -c              |  yes   | yes       |
+  -d              |  yes   | yes       |
+  -f fields       |  yes   | yes       |
+  -s chars        |  yes   | yes       |
+  -u              |  yes   | yes       |
+uniq Busybox specific options:
+  -w N
+
+uudecode POSIX options
+ option           | exists | compliant | remarks
+  -o outfile      |  no    | no        |
+uudecode Busybox specific options: None
+
+uuencode POSIX options
+ option           | exists | compliant | remarks
+  -m              |  yes   | yes       |
+uuencode Busybox specific options: None
+
+vi POSIX options
+ option           | exists | compliant | remarks
+  -R              |  yes   |           |
+  -c command      |  yes   |           |
+  -r              |  no    | no        |
+  -t tagstring    |  no    | no        |
+  -w size         |  no    | no        |
+vi Busybox specific options:
+  -H
+
+wc POSIX options
+ option           | exists | compliant | remarks
+  -c              |  yes   | yes       |
+  -l              |  yes   | yes       |
+  -m              |  no    | no        |
+  -w              |  yes   | yes       |
+wc Busybox specific options:
+  -L
+
+who POSIX options
+ option           | exists | compliant | remarks
+  -H              |  no    | no        |
+  -T              |  no    | no        |
+  -a              |  yes   | no        | just shows all
+  -b              |  no    | no        |
+  -d              |  no    | no        |
+  -l              |  no    | no        |
+  -m              |  no    | no        |
+  -p              |  no    | no        |
+  -q              |  no    | no        |
+  -r              |  no    | no        |
+  -s              |  no    | no        |
+  -t              |  no    | no        |
+  -u              |  no    | no        |
+who Busybox specific options: None
+
+xargs POSIX options
+ option           | exists | compliant | remarks
+  -E eofstr       |  no    | no        |
+  -I replstr      |  no    | no        |
+  -L number       |  no    | no        |
+  -n number       |  yes   | yes       |
+  -p              |  yes   | yes       |
+  -s size         |  yes   | yes       |
+  -t              |  yes   | yes       |
+  -x              |  yes   | yes       |
+xargs Busybox specific options:
+  -e[STR], -0, -r
+
+zcat POSIX options: None
+zcat Busybox specific options: None
diff --git a/busybox-1.19.3/docs/sigint.htm b/busybox-1.19.3/docs/sigint.htm
new file mode 100644
index 0000000..e230f4d
--- /dev/null
+++ b/busybox-1.19.3/docs/sigint.htm
@@ -0,0 +1,627 @@
+<HTML>
+<HEAD>
+<link rel="SHORTCUT ICON" href="http://www.cons.org/favicon.ico">
+<TITLE>Proper handling of SIGINT/SIGQUIT [http://www.cons.org/cracauer/sigint.html]</TITLE>
+<!-- Created by: GNU m4 using $Revision: 1.20 $ of crawww.m4lib on 11-Feb-2005 -->
+<BODY BGCOLOR="#fff8e1">
+<CENTER><H2>Proper handling of SIGINT/SIGQUIT</H2></CENTER>
+<img src=linie.png width="100%" alt=" ">
+<P>
+
+<table border=1 cellpadding=4>
+<tr><th valign=top align=left>Abstract: </th>
+<td valign=top align=left>
+In UNIX terminal sessions, you usually have a key like
+<code>C-c</code> (Control-C) to immediately end whatever program you
+have running in the foreground. This should work even when the program
+you called has called other programs in turn. Everything should be
+aborted, giving you your command prompt back, no matter how deep the
+call stack is.
+
+<p>Basically, it's trivial. But the existence of interactive
+applications that use SIGINT and/or SIGQUIT for other purposes than a
+complete immediate abort make matters complicated, and - as was to
+expect - left us with several ways to solve the problems. Of course,
+existing shells and applications follow different ways.
+
+<P>This Web pages outlines different ways to solve the problem and
+argues that only one of them can do everything right, although it
+means that we have to fix some existing software.
+
+
+
+</td></tr><tr><th valign=top align=left>Intended audience: </th>
+<td valign=top align=left>Programmers who implement programs that catch SIGINT/SIGQUIT.
+<BR>Programmers who implements shells or shell-like programs that
+execute batches of programs.
+
+<p>Users who have problems problems getting rid of runaway shell
+scripts using <code>Control-C</code>. Or have interactive applications
+that don't behave right when sending SIGINT. Examples are emacs'es
+that die on Control-g or shellscript statements that sometimes are
+executed and sometimes not, apparently not determined by the user's
+intention.
+
+
+</td></tr><tr><th valign=top align=left>Required knowledge: </th>
+<td valign=top align=left>You have to know what it means to catch SIGINT or SIGQUIT and how
+processes are waiting for other processes (childs) they spawned.
+
+
+</td></tr></table>
+<img src=linie.png width="100%" alt=" ">
+
+
+<H3>Basic concepts</H3>
+
+What technically happens when you press Control-C is that all programs
+running in the foreground in your current terminal (or virtual
+terminal) get the signal SIGINT sent.
+
+<p>You may change the key that triggers the signal using
+<code>stty</code> and running programs may remap the SIGINT-sending
+key at any time they like, without your intervention and without
+asking you first.
+
+<p>The usual reaction of a running program to SIGINT is to exit.
+However, not all program do an exit on SIGINT, programs are free to
+use the signal for other actions or to ignore it at all.
+
+<p>All programs running in the foreground receive the signal. This may
+be a nested "stack" of programs: You started a program that started
+another and the outer is waiting for the inner to exit. This nesting
+may be arbitrarily deep.
+
+<p>The innermost program is the one that decides what to do on SIGINT.
+It may exit, do something else or do nothing. Still, when the user hit
+SIGINT, all the outer programs are awaken, get the signal and may
+react on it.
+
+<H3>What we try to achieve</H3>
+
+The problem is with shell scripts (or similar programs that call
+several subprograms one after another).
+
+<p>Let us consider the most basic script:
+<PRE>
+#! /bin/sh
+program1
+program2
+</PRE>
+and the usual run looks like this:
+<PRE>
+$ sh myscript
+[output of program1]
+[output of program2]
+$
+</PRE>
+
+<p>Let us assume that both programs do nothing special on SIGINT, they
+just exit.
+
+<p>Now imagine the user hits C-c while a shellscript is executing its
+first program. The following programs receive SIGINT: program1 and
+also the shell executing the script. program1 exits.
+
+<p>But what should the shell do? If we say that it is only the
+innermost's programs business to react on SIGINT, the shell will do
+nothing special (not exit) and it will continue the execution of the
+script and run program2. But this is wrong: The user's intention in
+hitting C-c is to abort the whole script, to get his prompt back. If
+he hits C-c while the first program is running, he does not want
+program2 to be even started.
+
+<p>here is what would happen if the shell doesn't do anything:
+<PRE>
+$ sh myscript
+[first half of program1's output]
+C-c   [users presses C-c]
+[second half of program1's output will not be displayed]
+[output of program2 will appear]
+</PRE>
+
+
+<p>Consider a more annoying example:
+<pre>
+#! /bin/sh
+# let's assume there are 300 *.dat files
+for file in *.dat ; do
+	dat2ascii $dat
+done
+</pre>
+
+If your shell wouldn't end if the user hits <code>C-c</code>,
+<code>C-c</code> would just end <strong>one</strong> dat2ascii run and
+the script would continue. Thus, you had to hit <code>C-c</code> up to
+300 times to end this script.
+
+<H3>Alternatives to do so</H3>
+
+<p>There are several ways to handle abortion of shell scripts when
+SIGINT is received while a foreground child runs:
+
+<menu>
+
+<li>As just outlined, the shellscript may just continue, ignoring the
+fact that the user hit <code>C-c</code>. That way, your shellscript -
+including any loops - would continue and you had no chance of aborting
+it except using the kill command after finding out the outermost
+shell's PID. This "solution" will not be discussed further, as it is
+obviously not desirable.
+
+<p><li>The shell itself exits immediately when it receives SIGINT. Not
+only the program called will exit, but the calling (the
+script-executing) shell. The first variant is to exit the shell (and
+therefore discontinuing execution of the script) immediately, while
+the background program may still be executing (remember that although
+the shell is just waiting for the called program to exit, it is woken
+up and may act). I will call the way of doing things the "IUE" (for
+"immediate unconditional exit") for the rest of this document.
+
+<p><li>As a variant of the former, when the shell receives SIGINT
+while it is waiting for a child to exit, the shell does not exit
+immediately. but it remembers the fact that a SIGINT happened. After
+the called program exits and the shell's wait ends, the shell will
+exit itself and hence discontinue the script. I will call the way of
+doing things the "WUE" (for "wait and unconditional exit") for the
+rest of this document.
+
+<p><li>There is also a way that the calling shell can tell whether the
+called program exited on SIGINT and if it ignored SIGINT (or used it
+for other purposes). As in the <sl>WUE</sl> way, the shell waits for
+the child to complete. It figures whether the program was ended on
+SIGINT and if so, it discontinue the script. If the program did any
+other exit, the script will be continued. I will call the way of doing
+things the "WCE" (for "wait and cooperative exit") for the rest of
+this document.
+
+</menu>
+
+<H3>The problem</H3>
+
+On first sight, all three solutions (IUE, WUE and WCE) all seem to do
+what we want: If C-c is hit while the first program of the shell
+script runs, the script is discontinued. The user gets his prompt back
+immediately. So what are the difference between these way of handling
+SIGINT?
+
+<p>There are programs that use the signal SIGINT for other purposes
+than exiting. They use it as a normal keystroke. The user is expected
+to use the key that sends SIGINT during a perfectly normal program
+run. As a result, the user sends SIGINT in situations where he/she
+does not want the program or the script to end.
+
+<p>The primary example is the emacs editor: C-g does what ESC does in
+other applications: It cancels a partially executed or prepared
+operation. Technically, emacs remaps the key that sends SIGINT from
+C-c to C-g and catches SIGINT.
+
+<p>Remember that the SIGINT is sent to all programs running in the
+foreground. If emacs is executing from a shell script, both emacs and
+the shell get SIGINT. emacs is the program that decides what to do:
+Exit on SIGINT or not. emacs decides not to exit. The problem arises
+when the shell draws its own conclusions from receiving SIGINT without
+consulting emacs for its opinion.
+
+<p>Consider this script:
+<PRE>
+#! /bin/sh
+emacs /tmp/foo
+cp /tmp/foo /home/user/mail/sent
+</PRE>
+
+<p>If C-g is used in emacs, both the shell and emacs will received
+SIGINT. Emacs will not exit, the user used C-g as a normal editing
+keystroke, he/she does not want the script to be aborted on C-g.
+
+<p>The central problem is that the second command (cp) may
+unintentionally be killed when the shell draws its own conclusion
+about the user's intention. The innermost program is the only one to
+judge.
+
+<H3>One more example</H3>
+
+<p>Imagine a mail session using a curses mailer in a tty. You called
+your mailer and started to compose a message. Your mailer calls emacs.
+<code>C-g</code> is a normal editing key in emacs. Technically it
+sends SIGINT (it was <code>C-c</code>, but emacs remapped the key) to
+<menu>
+<li>emacs
+<li>the shell between your mailer and emacs, the one from your mailers
+    system("emacs /tmp/bla.44") command
+<li>the mailer itself
+<li>possibly another shell if your mailer was called by a shell script
+or from another application using system(3)
+<li>your interactive shell (which ignores it since it is interactive
+and hence is not relevant to this discussion)
+</menu>
+
+<p>If everyone just exits on SIGINT, you will be left with nothing but
+your login shell, without asking.
+
+<p>But for sure you don't want to be dropped out of your editor and
+out of your mailer back to the commandline, having your edited data
+and mailer status deleted.
+
+<p>Understand the difference: While <code>C-g</code> is used an a kind
+of abort key in emacs, it isn't the major "abort everything" key. When
+you use <code>C-g</code> in emacs, you want to end some internal emacs
+command. You don't want your whole emacs and mailer session to end.
+
+<p>So, if the shell exits immediately if the user sends SIGINT (the
+second of the four ways shown above), the parent of emacs would die,
+leaving emacs without the controlling tty. The user will lose it's
+editing session immediately and unrecoverable. If the "main" shell of
+the operating system defaults to this behavior, every editor session
+that is spawned from a mailer or such will break (because it is
+usually executed by system(3), which calls /bin/sh). This was the case
+in FreeBSD before I and Bruce Evans changed it in 1998.
+
+<p>If the shell recognized that SIGINT was sent and exits after the
+current foreground process exited (the third way of the four), the
+editor session will not be disturbed, but things will still not work
+right.
+
+<H3>A further look at the alternatives</H3>
+
+<p>Still considering this script to examine the shell's actions in the
+IUE, WUE and ICE way of handling SIGINT:
+<PRE>
+#! /bin/sh
+emacs /tmp/foo
+cp /tmp/foo /home/user/mail/sent
+</PRE>
+
+<p>The IUE ("immediate unconditional exit") way does not work at all:
+emacs wants to survive the SIGINT (it's a normal editing key for
+emacs), but its parent shell unconditionally thinks "We received
+SIGINT. Abort everything. Now.". The shell will exit even before emacs
+exits. But this will leave emacs in an unusable state, since the death
+of its calling shell will leave it without required resources (file
+descriptors). This way does not work at all for shellscripts that call
+programs that use SIGINT for other purposes than immediate exit. Even
+for programs that exit on SIGINT, but want to do some cleanup between
+the signal and the exit, may fail before they complete their cleanup.
+
+<p>It should be noted that this way has one advantage: If a child
+blocks SIGINT and does not exit at all, this way will get control back
+to the user's terminal. Since such programs should be banned from your
+system anyway, I don't think that weighs against the disadvantages.
+
+<p>WUE ("wait and unconditional exit") is a little more clever: If C-g
+was used in emacs, the shell will get SIGINT. It will not immediately
+exit, but remember the fact that a SIGINT happened. When emacs ends
+(maybe a long time after the SIGINT), it will say "Ok, a SIGINT
+happened sometime while the child was executing, the user wants the
+script to be discontinued". It will then exit. The cp will not be
+executed. But that's bad. The "cp" will be executed when the emacs
+session ended without the C-g key ever used, but it will not be
+executed when the user used C-g at least one time. That is clearly not
+desired. Since C-g is a normal editing key in emacs, the user expects
+the rest of the script to behave identically no matter what keys he
+used.
+
+<p>As a result, the "WUE" way is better than the "IUE" way in that it
+does not break SIGINT-using programs completely. The emacs session
+will end undisturbed. But it still does not support scripts where
+other actions should be performed after a program that use SIGINT for
+non-exit purposes. Since the behavior is basically undeterminable for
+the user, this can lead to nasty surprises.
+
+<p>The "WCE" way fixes this by "asking" the called program whether it
+exited on SIGINT or not. While emacs receives SIGINT, it does not exit
+on it and a calling shell waiting for its exit will not be told that
+it exited on SIGINT. (Although it receives SIGINT at some point in
+time, the system does not enforce that emacs will exit with
+"I-exited-on-SIGINT" status. This is under emacs' control, see below).
+
+<p>this still work for the normal script without SIGINT-using
+programs:</p>
+<PRE>
+#! /bin/sh
+program1
+program2
+</PRE>
+
+Unless program1 and program2 mess around with signal handling, the
+system will tell the calling shell whether the programs exited
+normally or as a result of SIGINT.
+
+<p>The "WCE" way then has an easy way to things right: When one called
+program exited with "I-exited-on-SIGINT" status, it will discontinue
+the script after this program. If the program ends without this
+status, the next command in the script is started.
+
+<p>It is important to understand that a shell in "WCE" modus does not
+need to listen to the SIGINT signal at all. Both in the
+"emacs-then-cp" script and in the "several-normal-programs" script, it
+will be woken up and receive SIGINT when the user hits the
+corresponding key. But the shell does not need to react on this event
+and it doesn't need to remember the event of any SIGINT, either.
+Telling whether the user wants to end a script is done by asking that
+program that has to decide, that program that interprets keystrokes
+from the user, the innermost program.
+
+<H3>So everything is well with WCE?</H3>
+
+Well, almost.
+
+<p>The problem with the "WCE" modus is that there are broken programs
+that do not properly communicate the required information up to the
+calling program.
+
+<p>Unless a program messes with signal handling, the system does this
+automatically.
+
+<p>There are programs that want to exit on SIGINT, but they don't let
+the system do the automatic exit, because they want to do some
+cleanup. To do so, they catch SIGINT, do the cleanup and then exit by
+themselves.
+
+<p>And here is where the problem arises: Once they catch the signal,
+the system will no longer communicate the "I-exited-on-SIGINT" status
+to the calling program automatically. Even if the program exit
+immediately in the signal handler of SIGINT. Once it catches the
+signal, it has to take care of communicating the signal status
+itself.
+
+<p>Some programs don't do this. On SIGINT, they do cleanup and exit
+immediatly, but the calling shell isn't told about the non-normal exit
+and it will call the next program in the script.
+
+<p>As a result, the user hits SIGINT and while one program exits, the
+shellscript continues. To him/her it looks like the shell fails to
+obey to his abortion command.
+
+<p>Both IUE or WUE shell would not have this problem, since they
+discontinue the script on their own. But as I said, they don't support
+programs using SIGINT for non-exiting purposes, no matter whether
+these programs properly communicate their signal status to the calling
+shell or not.
+
+<p>Since some shell in wide use implement the WUE way (and some even
+IUE), there is a considerable number of broken programs out there that
+break WCE shells. The programmers just don't recognize it if their
+shell isn't WCE.
+
+<H3>How to be a proper program</H3>
+
+<p>(Short note in advance: What you need to achieve is that
+WIFSIGNALED(status) is true in the calling program and that
+WTERMSIG(status) returns SIGINT.)
+
+<p>If you don't catch SIGINT, the system automatically does the right
+thing for you: Your program exits and the calling program gets the
+right "I-exited-on-SIGINT" status after waiting for your exit.
+
+<p>But once you catch SIGINT, you have to act.
+
+<p>Decide whether the SIGINT is used for exit/abort purposes and hence
+a shellscript calling this program should discontinue. This is
+hopefully obvious. If you just need to do some cleanup on SIGINT, but
+then exit immediately, the answer is "yes".
+
+<p>If so, you have to tell the calling program about it by exiting
+with the "I-exited-on-SIGINT" status.
+
+<p>There is no other way of doing this than to kill yourself with a
+SIGINT signal. Do it by resetting the SIGINT handler to SIG_DFL, then
+send yourself the signal.
+
+<PRE>
+void sigint_handler(int sig)
+{
+	<do some cleanup>
+	signal(SIGINT, SIG_DFL);
+	kill(getpid(), SIGINT);
+}
+</PRE>
+
+Notes:
+
+<MENU>
+
+<LI>You cannot "fake" the proper exit status by an exit(3) with a
+special numeric value. People often assume this since the manuals for
+shells often list some return value for exactly this. But this is just
+a convention for your shell script. It does not work from one UNIX API
+program to another.
+
+<P>All that happens is that the shell sets the "$?" variable to a
+special numeric value for the convenience of your script, because your
+script does not have access to the lower-lever UNIX status evaluation
+functions. This is just an agreement between your script and the
+executing shell, it does not have any meaning in other contexts.
+
+<P><LI>Do not use kill(0, SIGINT) without consulting the manul for
+your OS implementation. I.e. on BSD, this would not send the signal to
+the current process, but to all processes in the group.
+
+<P><LI>POSIX 1003.1 allows all these calls to appear in signal
+handlers, so it is portable.
+
+</MENU>
+
+<p>In a bourne shell script, you can catch signals using the
+<code>trap</code> command. Here, the same as for C programs apply.  If
+the intention of SIGINT is to end your program, you have to exit in a
+way that the calling programs "sees" that you have been killed.  If
+you don't catch SIGINT, this happend automatically, but of you catch
+SIGINT, i.e. to do cleanup work, you have to end the program by
+killing yourself, not by calling exit.
+
+<p>Consider this example from FreeBSD's <code>mkdep</code>, which is a
+bourne shell script.
+
+<pre>
+TMP=_mkdep$$
+trap 'rm -f $TMP ; trap 2 ; kill -2 $$' 1 2 3 13 15
+</pre>
+
+Yes, you have to do it the hard way. It's even more annoying in shell
+scripts than in C programs since you can't "pre-delete" temporary
+files (which isn't really portable in C, though).
+
+<P>All this applies to programs in all languages, not only C and
+bourne shell. Every language implementation that lets you catch SIGINT
+should also give you the option to reset the signal and kill yourself.
+
+<P>It is always desireable to exit the right way, even if you don't
+expect your usual callers to depend on it, some unusual one will come
+along. This proper exit status will be needed for WCE and will not
+hurt when the calling shell uses IUE or WUE.
+
+<H3>How to be a proper shell</H3>
+
+All this applies only for the script-executing case. Most shells will
+also have interactive modes where things are different.
+
+<MENU>
+
+<LI>Do nothing special when SIGINT appears while you wait for a child.
+You don't even have to remember that one happened.
+
+<P><LI>Wait for child to exit, get the exit status. Do not truncate it
+to type char.
+
+<P><LI>Look at WIFSIGNALED(status) and WTERMSIG(status) to tell
+whether the child says "I exited on SIGINT: in my opinion the user
+wants the shellscript to be discontinued".
+
+<P><LI>If the latter applies, discontinue the script.
+
+<P><LI>Exit. But since a shellscript may in turn be called by a
+shellscript, you need to make sure that you properly communicate the
+discontinue intention to the calling program. As in any other program
+(see above), do
+
+<PRE>
+	signal(SIGINT, SIG_DFL);
+	kill(getpid(), SIGINT);
+</PRE>
+
+</MENU>
+
+<H3>Other remarks</H3>
+
+Although this web page talks about SIGINT only, almost the same issues
+apply to SIGQUIT, including proper exiting by killing yourself after
+catching the signal and proper reaction on the WIFSIGNALED(status)
+value. One notable difference for SIGQUIT is that you have to make
+sure that not the whole call tree dumps core.
+
+<H3>What to fight</H3>
+
+Make sure all programs <em>really</em> kill themselves if they react
+to SIGINT or SIGQUIT and intend to abort their operation as a result
+of this signal. Programs that don't use SIGINT/SIGQUIT as a
+termination trigger - but as part of normal operation - don't kill
+themselves, but do a normal exit instead.
+
+<p>Make sure people understand why you can't fake an exit-on-signal by
+doing exit(...) using any numerical status.
+
+<p>Make sure you use a shell that behaves right. Especially if you
+develop programs, since it will help seeing problems.
+
+<H3>Concrete examples how to fix programs:</H3>
+<ul>
+
+<li>The fix for FreeBSD's
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/time/time.c.diff?r1=1.10&r2=1.11">time(1)</A>. This fix is the best example, it's quite short and clear and
+it fixes a case where someone tried to fake signal exit status by a
+numerical value. And the complete program is small.
+
+<p><li>Fix for FreeBSD's
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/truss/main.c.diff?r1=1.9&r2=1.10">truss(1)</A>.
+
+<p><li>The fix for FreeBSD's
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/mkdep/mkdep.gcc.sh.diff?r1=1.8.2.1&r2=1.8.2.2">mkdep(1)</A>, a shell script.
+
+
+<p><li>Fix for FreeBSD's make(1), <A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/make/job.c.diff?r1=1.9&r2=1.10">part 1</A>,
+<A HREF="http://www.freebsd.org/cgi/cvsweb.cgi/src/usr.bin/make/compat.c.diff?r1=1.10&r2=1.11">part 2</A>.
+
+</ul>
+
+<H3>Testsuite for shells</H3>
+
+I have a collection of shellscripts that test shells for the
+behavior. See my <A HREF="download/">download dir</A> to get the newest
+"sh-interrupt" files, either as a tarfile or as individual file for
+online browsing. This isn't really documented, besides from the
+comments the scripts echo.
+
+<H3>Appendix 1 - table of implementation choices</H3>
+
+<table border cellpadding=2>
+
+<tr valign=top>
+<th>Method sign</th>
+<th>Does what?</th>
+<th>Example shells that implement it:</th>
+<th>What happens when a shellscript called emacs, the user used
+<code>C-g</code> and the script has additional commands in it?</th>
+<th>What happens when a shellscript called emacs, the user did not use
+<code>C-c</code> and the script has additional commands in it?</th>
+<th>What happens if a non-interactive child catches SIGINT?</th>
+<th>To behave properly, childs must do what?</th>
+</tr>
+
+<tr valign=top align=left>
+<td>IUE</td>
+<td>The shell executing a script exits immediately if it receives
+SIGINT.</td>
+<td>4.4BSD ash (ash), NetBSD, FreeBSD prior to 3.0/22.8</td>
+<td>The editor session is lost and subsequent commands are not
+executed.</td>
+<td>The editor continues as normal and the subsequent commands are
+executed. </td>
+<td>The scripts ends immediately, returning to the caller even before
+the current foreground child of the shell exits. </td>
+<td>It doesn't matter what the child does or how it exits, even if the
+child continues to operate, the shell returns. </td>
+</tr>
+
+<tr valign=top align=left>
+<td>WUE</td>
+<td>If the shell executing a script received SIGINT while a foreground
+process was running, it will exit after that child's exit.</td>
+<td>pdksh (OpenBSD /bin/sh)</td>
+<td>The editor continues as normal, but subsequent commands from the
+script are not executed.</td>
+<td>The editor continues as normal and subsequent commands are
+executed. </td>
+<td>The scripts returns to its caller after the current foreground
+child exits, no matter how the child exited. </td>
+<td>It doesn't matter how the child exits (signal status or not), but
+if it doesn't return at all, the shell will not return. In no case
+will further commands from the script be executed. </td>
+</tr>
+
+<tr valign=top align=left>
+<td>WCE</td>
+<td>The shell exits if a child signaled that it was killed on a
+signal (either it had the default handler for SIGINT or it killed
+itself).  </td>
+<td>bash (Linux /bin/sh), most commercial /bin/sh, FreeBSD /bin/sh
+from 3.0/2.2.8.</td>
+<td>The editor continues as normal and subsequent commands are
+executed. </td>
+<td>The editor continues as normal and subsequent commands are
+executed. </td>
+<td>The scripts returns to its caller after the current foreground
+child exits, but only if the child exited with signal status. If
+the child did a normal exit (even if it received SIGINT, but catches
+it), the script will continue. </td>
+<td>The child must be implemented right, or the user will not be able
+to break shell scripts reliably.</td>
+</tr>
+
+</table>
+
+<P><img src=linie.png width="100%" alt=" ">
+<BR>&copy;2005 Martin Cracauer &lt;cracauer @ cons.org&gt;
+<A HREF="http://www.cons.org/cracauer/">http://www.cons.org/cracauer/</A>
+<BR>Last changed: $Date: 2005/02/11 21:44:43 $
+</BODY></HTML>
diff --git a/busybox-1.19.3/docs/smallint.txt b/busybox-1.19.3/docs/smallint.txt
new file mode 100644
index 0000000..b57dfd7
--- /dev/null
+++ b/busybox-1.19.3/docs/smallint.txt
@@ -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.
diff --git a/busybox-1.19.3/docs/style-guide.txt b/busybox-1.19.3/docs/style-guide.txt
new file mode 100644
index 0000000..10ed893
--- /dev/null
+++ b/busybox-1.19.3/docs/style-guide.txt
@@ -0,0 +1,713 @@
+Busybox Style Guide
+===================
+
+This document describes the coding style conventions used in Busybox. If you
+add a new file to Busybox or are editing an existing file, please format your
+code according to this style. If you are the maintainer of a file that does
+not follow these guidelines, please -- at your own convenience -- modify the
+file(s) you maintain to bring them into conformance with this style guide.
+Please note that this is a low priority task.
+
+To help you format the whitespace of your programs, an ".indent.pro" file is
+included in the main Busybox source directory that contains option flags to
+format code as per this style guide. This way you can run GNU indent on your
+files by typing 'indent myfile.c myfile.h' and it will magically apply all the
+right formatting rules to your file. Please _do_not_ run this on all the files
+in the directory, just your own.
+
+
+
+Declaration Order
+-----------------
+
+Here is the preferred order in which code should be laid out in a file:
+
+ - commented program name and one-line description
+ - commented author name and email address(es)
+ - commented GPL boilerplate
+ - commented longer description / notes for the program (if needed)
+ - #includes of .h files with angle brackets (<>) around them
+ - #includes of .h files with quotes ("") around them
+ - #defines (if any, note the section below titled "Avoid the Preprocessor")
+ - const and global variables
+ - function declarations (if necessary)
+ - function implementations
+
+
+
+Whitespace and Formatting
+-------------------------
+
+This is everybody's favorite flame topic so let's get it out of the way right
+up front.
+
+
+Tabs vs. Spaces in Line Indentation
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The preference in Busybox is to indent lines with tabs. Do not indent lines
+with spaces and do not indents lines using a mixture of tabs and spaces. (The
+indentation style in the Apache and Postfix source does this sort of thing:
+\s\s\s\sif (expr) {\n\tstmt; --ick.) The only exception to this rule is
+multi-line comments that use an asterisk at the beginning of each line, i.e.:
+
+	\t/*
+	\t * This is a block comment.
+	\t * Note that it has multiple lines
+	\t * and that the beginning of each line has a tab plus a space
+	\t * except for the opening '/*' line where the slash
+	\t * is used instead of a space.
+	\t */
+
+Furthermore, The preference is that tabs be set to display at four spaces
+wide, but the beauty of using only tabs (and not spaces) at the beginning of
+lines is that you can set your editor to display tabs at *whatever* number of
+spaces is desired and the code will still look fine.
+
+
+Operator Spacing
+~~~~~~~~~~~~~~~~
+
+Put spaces between terms and operators. Example:
+
+	Don't do this:
+
+		for(i=0;i<num_items;i++){
+
+	Do this instead:
+
+		for (i = 0; i < num_items; i++) {
+
+	While it extends the line a bit longer, the spaced version is more
+	readable. An allowable exception to this rule is the situation where
+	excluding the spacing makes it more obvious that we are dealing with a
+	single term (even if it is a compound term) such as:
+
+		if (str[idx] == '/' && str[idx-1] != '\\')
+
+	or
+
+		if ((argc-1) - (optind+1) > 0)
+
+
+Bracket Spacing
+~~~~~~~~~~~~~~~
+
+If an opening bracket starts a function, it should be on the
+next line with no spacing before it. However, if a bracket follows an opening
+control block, it should be on the same line with a single space (not a tab)
+between it and the opening control block statement. Examples:
+
+	Don't do this:
+
+		while (!done)
+		{
+
+		do
+		{
+
+	Don't do this either:
+
+		while (!done){
+
+		do{
+
+	And for heaven's sake, don't do this:
+
+		while (!done)
+		  {
+
+		do
+		  {
+
+	Do this instead:
+
+		while (!done) {
+
+		do {
+
+If you have long logic statements that need to be wrapped, then uncuddling
+the bracket to improve readability is allowed. Generally, this style makes
+it easier for reader to notice that 2nd and following lines are still
+inside 'if':
+
+		if (some_really_long_checks && some_other_really_long_checks
+		 && some_more_really_long_checks
+		 && even_more_of_long_checks
+		) {
+			do_foo_now;
+
+Spacing around Parentheses
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Put a space between C keywords and left parens, but not between function names
+and the left paren that starts it's parameter list (whether it is being
+declared or called). Examples:
+
+	Don't do this:
+
+		while(foo) {
+		for(i = 0; i < n; i++) {
+
+	Do this instead:
+
+		while (foo) {
+		for (i = 0; i < n; i++) {
+
+	But do functions like this:
+
+		static int my_func(int foo, char bar)
+		...
+		baz = my_func(1, 2);
+
+Also, don't put a space between the left paren and the first term, nor between
+the last arg and the right paren.
+
+	Don't do this:
+
+		if ( x < 1 )
+		strcmp( thisstr, thatstr )
+
+	Do this instead:
+
+		if (x < 1)
+		strcmp(thisstr, thatstr)
+
+
+Cuddled Elses
+~~~~~~~~~~~~~
+
+Also, please "cuddle" your else statements by putting the else keyword on the
+same line after the right bracket that closes an 'if' statement.
+
+	Don't do this:
+
+	if (foo) {
+		stmt;
+	}
+	else {
+		stmt;
+	}
+
+	Do this instead:
+
+	if (foo) {
+		stmt;
+	} else {
+		stmt;
+	}
+
+The exception to this rule is if you want to include a comment before the else
+block. Example:
+
+	if (foo) {
+		stmts...
+	}
+	/* otherwise, we're just kidding ourselves, so re-frob the input */
+	else {
+		other_stmts...
+	}
+
+
+Labels
+~~~~~~
+
+Labels should start at the beginning of the line, not indented to the block
+level (because they do not "belong" to block scope, only to whole function).
+
+	if (foo) {
+		stmt;
+ label:
+		stmt2;
+		stmt;
+	}
+
+(Putting label at position 1 prevents diff -p from confusing label for function
+name, but it's not a policy of busybox project to enforce such a minor detail).
+
+
+
+Variable and Function Names
+---------------------------
+
+Use the K&R style with names in all lower-case and underscores occasionally
+used to separate words (e.g., "variable_name" and "numchars" are both
+acceptable). Using underscores makes variable and function names more readable
+because it looks like whitespace; using lower-case is easy on the eyes.
+
+	Frowned upon:
+
+		hitList
+		TotalChars
+		szFileName
+		pf_Nfol_TriState
+
+	Preferred:
+
+		hit_list
+		total_chars
+		file_name
+		sensible_name
+
+Exceptions:
+
+ - Enums, macros, and constant variables are occasionally written in all
+   upper-case with words optionally separated by underscores (i.e. FIFO_TYPE,
+   ISBLKDEV()).
+
+ - Nobody is going to get mad at you for using 'pvar' as the name of a
+   variable that is a pointer to 'var'.
+
+
+Converting to K&R
+~~~~~~~~~~~~~~~~~
+
+The Busybox codebase is very much a mixture of code gathered from a variety of
+sources. This explains why the current codebase contains such a hodge-podge of
+different naming styles (Java, Pascal, K&R, just-plain-weird, etc.). The K&R
+guideline explained above should therefore be used on new files that are added
+to the repository. Furthermore, the maintainer of an existing file that uses
+alternate naming conventions should, at his own convenience, convert those
+names over to K&R style. Converting variable names is a very low priority
+task.
+
+If you want to do a search-and-replace of a single variable name in different
+files, you can do the following in the busybox directory:
+
+	$ perl -pi -e 's/\bOldVar\b/new_var/g' *.[ch]
+
+If you want to convert all the non-K&R vars in your file all at once, follow
+these steps:
+
+ - In the busybox directory type 'examples/mk2knr.pl files-to-convert'. This
+   does not do the actual conversion, rather, it generates a script called
+   'convertme.pl' that shows what will be converted, giving you a chance to
+   review the changes beforehand.
+
+ - Review the 'convertme.pl' script that gets generated in the busybox
+   directory and remove / edit any of the substitutions in there. Please
+   especially check for false positives (strings that should not be
+   converted).
+
+ - Type './convertme.pl same-files-as-before' to perform the actual
+   conversion.
+
+ - Compile and see if everything still works.
+
+Please be aware of changes that have cascading effects into other files. For
+example, if you're changing the name of something in, say utility.c, you
+should probably run 'examples/mk2knr.pl utility.c' at first, but when you run
+the 'convertme.pl' script you should run it on _all_ files like so:
+'./convertme.pl *.[ch]'.
+
+
+
+Avoid The Preprocessor
+----------------------
+
+At best, the preprocessor is a necessary evil, helping us account for platform
+and architecture differences. Using the preprocessor unnecessarily is just
+plain evil.
+
+
+The Folly of #define
+~~~~~~~~~~~~~~~~~~~~
+
+Use 'const <type> var' for declaring constants.
+
+	Don't do this:
+
+		#define CONST 80
+
+	Do this instead, when the variable is in a header file and will be used in
+	several source files:
+
+		enum { CONST = 80 };
+
+Although enum may look ugly to some people, it is better for code size.
+With "const int" compiler may fail to optimize it out and will reserve
+a real storage in rodata for it! (Hopefully, newer gcc will get better
+at it...).  With "define", you have slight risk of polluting namespace
+(#define doesn't allow you to redefine the name in the inner scopes),
+and complex "define" are evaluated each time they uesd, not once
+at declarations like enums. Also, the preprocessor does _no_ type checking
+whatsoever, making it much more error prone.
+
+
+The Folly of Macros
+~~~~~~~~~~~~~~~~~~~
+
+Use 'static inline' instead of a macro.
+
+	Don't do this:
+
+		#define mini_func(param1, param2) (param1 << param2)
+
+	Do this instead:
+
+		static inline int mini_func(int param1, param2)
+		{
+			return (param1 << param2);
+		}
+
+Static inline functions are greatly preferred over macros. They provide type
+safety, have no length limitations, no formatting limitations, have an actual
+return value, and under gcc they are as cheap as macros. Besides, really long
+macros with backslashes at the end of each line are ugly as sin.
+
+
+The Folly of #ifdef
+~~~~~~~~~~~~~~~~~~~
+
+Code cluttered with ifdefs is difficult to read and maintain. Don't do it.
+Instead, put your ifdefs at the top of your .c file (or in a header), and
+conditionally define 'static inline' functions, (or *maybe* macros), which are
+used in the code.
+
+	Don't do this:
+
+		ret = my_func(bar, baz);
+		if (!ret)
+			return -1;
+		#ifdef CONFIG_FEATURE_FUNKY
+			maybe_do_funky_stuff(bar, baz);
+		#endif
+
+	Do this instead:
+
+	(in .h header file)
+
+		#if ENABLE_FEATURE_FUNKY
+		static inline void maybe_do_funky_stuff(int bar, int baz)
+		{
+			/* lotsa code in here */
+		}
+		#else
+		static inline void maybe_do_funky_stuff(int bar, int baz) {}
+		#endif
+
+	(in the .c source file)
+
+		ret = my_func(bar, baz);
+		if (!ret)
+			return -1;
+		maybe_do_funky_stuff(bar, baz);
+
+The great thing about this approach is that the compiler will optimize away
+the "no-op" case (the empty function) when the feature is turned off.
+
+Note also the use of the word 'maybe' in the function name to indicate
+conditional execution.
+
+
+
+Notes on Strings
+----------------
+
+Strings in C can get a little thorny. Here's some guidelines for dealing with
+strings in Busybox. (There is surely more that could be added to this
+section.)
+
+
+String Files
+~~~~~~~~~~~~
+
+Put all help/usage messages in usage.c. Put other strings in messages.c.
+Putting these strings into their own file is a calculated decision designed to
+confine spelling errors to a single place and aid internationalization
+efforts, if needed. (Side Note: we might want to use a single file - maybe
+called 'strings.c' - instead of two, food for thought).
+
+
+Testing String Equivalence
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There's a right way and a wrong way to test for string equivalence with
+strcmp():
+
+	The wrong way:
+
+		if (!strcmp(string, "foo")) {
+			...
+
+	The right way:
+
+		if (strcmp(string, "foo") == 0){
+			...
+
+The use of the "equals" (==) operator in the latter example makes it much more
+obvious that you are testing for equivalence. The former example with the
+"not" (!) operator makes it look like you are testing for an error. In a more
+perfect world, we would have a streq() function in the string library, but
+that ain't the world we're living in.
+
+
+Avoid Dangerous String Functions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Unfortunately, the way C handles strings makes them prone to overruns when
+certain library functions are (mis)used. The following table  offers a summary
+of some of the more notorious troublemakers:
+
+function     overflows                  preferred
+-------------------------------------------------
+strcpy       dest string                safe_strncpy
+strncpy      may fail to 0-terminate dst safe_strncpy
+strcat       dest string                strncat
+gets         string it gets             fgets
+getwd        buf string                 getcwd
+[v]sprintf   str buffer                 [v]snprintf
+realpath     path buffer                use with pathconf
+[vf]scanf    its arguments              just avoid it
+
+
+The above is by no means a complete list. Be careful out there.
+
+
+
+Avoid Big Static Buffers
+------------------------
+
+First, some background to put this discussion in context: static buffers look
+like this in code:
+
+	/* in a .c file outside any functions */
+	static char buffer[BUFSIZ]; /* happily used by any function in this file,
+	                                but ick! big! */
+
+The problem with these is that any time any busybox app is run, you pay a
+memory penalty for this buffer, even if the applet that uses said buffer is
+not run. This can be fixed, thusly:
+
+	static char *buffer;
+	...
+	other_func()
+	{
+		strcpy(buffer, lotsa_chars); /* happily uses global *buffer */
+	...
+	foo_main()
+	{
+		buffer = xmalloc(sizeof(char)*BUFSIZ);
+	...
+
+However, this approach trades bss segment for text segment. Rather than
+mallocing the buffers (and thus growing the text size), buffers can be
+declared on the stack in the *_main() function and made available globally by
+assigning them to a global pointer thusly:
+
+	static char *pbuffer;
+	...
+	other_func()
+	{
+		strcpy(pbuffer, lotsa_chars); /* happily uses global *pbuffer */
+	...
+	foo_main()
+	{
+		char *buffer[BUFSIZ]; /* declared locally, on stack */
+		pbuffer = buffer;     /* but available globally */
+	...
+
+This last approach has some advantages (low code size, space not used until
+it's needed), but can be a problem in some low resource machines that have
+very limited stack space (e.g., uCLinux).
+
+A macro is declared in busybox.h that implements compile-time selection
+between xmalloc() and stack creation, so you can code the line in question as
+
+		RESERVE_CONFIG_BUFFER(buffer, BUFSIZ);
+
+and the right thing will happen, based on your configuration.
+
+Another relatively new trick of similar nature is explained
+in keep_data_small.txt.
+
+
+
+Miscellaneous Coding Guidelines
+-------------------------------
+
+The following are important items that don't fit into any of the above
+sections.
+
+
+Model Busybox Applets After GNU Counterparts
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When in doubt about the proper behavior of a Busybox program (output,
+formatting, options, etc.), model it after the equivalent GNU program.
+Doesn't matter how that program behaves on some other flavor of *NIX; doesn't
+matter what the POSIX standard says or doesn't say, just model Busybox
+programs after their GNU counterparts and it will make life easier on (nearly)
+everyone.
+
+The only time we deviate from emulating the GNU behavior is when:
+
+	- We are deliberately not supporting a feature (such as a command line
+	  switch)
+	- Emulating the GNU behavior is prohibitively expensive (lots more code
+	  would be required, lots more memory would be used, etc.)
+	- The difference is minor or cosmetic
+
+A note on the 'cosmetic' case: output differences might be considered
+cosmetic, but if the output is significant enough to break other scripts that
+use the output, it should really be fixed.
+
+
+Scope
+~~~~~
+
+If a const variable is used only in a single source file, put it in the source
+file and not in a header file. Likewise, if a const variable is used in only
+one function, do not make it global to the file. Instead, declare it inside
+the function body. Bottom line: Make a conscious effort to limit declarations
+to the smallest scope possible.
+
+Inside applet files, all functions should be declared static so as to keep the
+global name space clean. The only exception to this rule is the "applet_main"
+function which must be declared extern.
+
+If you write a function that performs a task that could be useful outside the
+immediate file, turn it into a general-purpose function with no ties to any
+applet and put it in the utility.c file instead.
+
+
+Brackets Are Your Friends
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Please use brackets on all if and else statements, even if it is only one
+line. Example:
+
+	Don't do this:
+
+		if (foo)
+			stmt1;
+		stmt2
+		stmt3;
+
+	Do this instead:
+
+		if (foo) {
+			stmt1;
+		}
+		stmt2
+		stmt3;
+
+The "bracketless" approach is error prone because someday you might add a line
+like this:
+
+		if (foo)
+			stmt1;
+			new_line();
+		stmt2;
+		stmt3;
+
+And the resulting behavior of your program would totally bewilder you. (Don't
+laugh, it happens to us all.) Remember folks, this is C, not Python.
+
+
+Function Declarations
+~~~~~~~~~~~~~~~~~~~~~
+
+Do not use old-style function declarations that declare variable types between
+the parameter list and opening bracket. Example:
+
+	Don't do this:
+
+		int foo(parm1, parm2)
+			char parm1;
+			float parm2;
+		{
+			....
+
+	Do this instead:
+
+		int foo(char parm1, float parm2)
+		{
+			....
+
+The only time you would ever need to use the old declaration syntax is to
+support ancient, antediluvian compilers. To our good fortune, we have access
+to more modern compilers and the old declaration syntax is neither necessary
+nor desired.
+
+
+Emphasizing Logical Blocks
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Organization and readability are improved by putting extra newlines around
+blocks of code that perform a single task. These are typically blocks that
+begin with a C keyword, but not always.
+
+Furthermore, you should put a single comment (not necessarily one line, just
+one comment) before the block, rather than commenting each and every line.
+There is an optimal amount of commenting that a program can have; you can
+comment too much as well as too little.
+
+A picture is really worth a thousand words here, the following example
+illustrates how to emphasize logical blocks:
+
+	while (line = xmalloc_fgets(fp)) {
+
+		/* eat the newline, if any */
+		chomp(line);
+
+		/* ignore blank lines */
+		if (strlen(file_to_act_on) == 0) {
+			continue;
+		}
+
+		/* if the search string is in this line, print it,
+		 * unless we were told to be quiet */
+		if (strstr(line, search) && !be_quiet) {
+			puts(line);
+		}
+
+		/* clean up */
+		free(line);
+	}
+
+
+Processing Options with getopt
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If your applet needs to process command-line switches, please use getopt32() to
+do so. Numerous examples can be seen in many of the existing applets, but
+basically it boils down to two things: at the top of the .c file, have this
+line in the midst of your #includes, if you need to parse long options:
+
+	#include <getopt.h>
+
+Then have long options defined:
+
+	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:
+
+	char *str_b;
+
+	opt_complementary = "cryptic_string";
+	applet_long_options = <applet>_longopts; /* if you have them */
+	opt = getopt32(argc, argv, "ab:c", &str_b);
+	if (opt & 1) {
+		handle_option_a();
+	}
+	if (opt & 2) {
+		handle_option_b(str_b);
+	}
+	if (opt & 4) {
+		handle_option_c();
+	}
+
+If your applet takes no options (such as 'init'), there should be a line
+somewhere in the file reads:
+
+	/* no options, no getopt */
+
+That way, when people go grepping to see which applets need to be converted to
+use getopt, they won't get false positives.
+
+For more info and examples, examine getopt32.c, tar.c, wget.c etc.
diff --git a/busybox-1.19.3/docs/syslog.conf.txt b/busybox-1.19.3/docs/syslog.conf.txt
new file mode 100644
index 0000000..6d9c4a1
--- /dev/null
+++ b/busybox-1.19.3/docs/syslog.conf.txt
@@ -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/busybox-1.19.3/docs/tar_pax.txt b/busybox-1.19.3/docs/tar_pax.txt
new file mode 100644
index 0000000..e56c27b
--- /dev/null
+++ b/busybox-1.19.3/docs/tar_pax.txt
@@ -0,0 +1,239 @@
+'pax headers' is POSIX 2003 (iirc) addition designed to fix
+tar format limitations - older tar format has fixed fields
+for everything (filename, uid, filesize etc) which can overflow.
+
+pax Header Block
+
+The pax header block shall be identical to the ustar header block
+described in ustar Interchange Format, except that two additional
+typeflag values are defined:
+
+x
+    Represents extended header records for the following file in
+the archive (which shall have its own ustar header block).
+
+g
+    Represents global extended header records for the following
+files in the archive. Each value shall affect all subsequent files
+that do not override that value in their own extended header
+record and until another global extended header record is reached
+that provides another value for the same field. The typeflag g
+global headers should not be used with interchange media that
+could suffer partial data loss in transporting the archive.
+
+For both of these types, the size field shall be the size of the
+extended header records in octets. The other fields in the header
+block are not meaningful to this version of the pax utility.
+However, if this archive is read by a pax utility conforming to
+the ISO POSIX-2:1993 standard, the header block fields are used to
+create a regular file that contains the extended header records as
+data. Therefore, header block field values should be selected to
+provide reasonable file access to this regular file.
+
+A further difference from the ustar header block is that data
+blocks for files of typeflag 1 (the digit one) (hard link) may be
+included, which means that the size field may be greater than
+zero.
+
+pax Extended Header
+
+An extended header shall consist of one or more records, each
+constructed as follows:
+
+"%d %s=%s\n", <length>, <keyword>, <value>
+
+The <length> field shall be the decimal length of the extended
+header record in octets, including length string itself and the
+trailing <newline>.
+
+[skip]
+
+atime
+    The file access time for the following file(s), equivalent to
+the value of the st_atime member of the stat structure for a file,
+as described by the stat() function. The access time shall be
+restored if the process has the appropriate privilege required to
+do so. The format of the <value> shall be as described in pax
+Extended Header File Times.
+
+charset
+    The name of the character set used to encode the data in the
+following file(s).
+
+    The encoding is included in an extended header for information
+only; when pax is used as described in IEEE Std 1003.1-2001, it
+shall not translate the file data into any other encoding. The
+BINARY entry indicates unencoded binary data.
+
+    When used in write or copy mode, it is implementation-defined
+whether pax includes a charset extended header record for a file.
+
+comment
+    A series of characters used as a comment. All characters in
+the <value> field shall be ignored by pax.
+
+gid
+    The group ID of the group that owns the file, expressed as a
+decimal number using digits from the ISO/IEC 646:1991 standard.
+This record shall override the gid field in the following header
+block(s). When used in write or copy mode, pax shall include a gid
+extended header record for each file whose group ID is greater
+than 2097151 (octal 7777777).
+
+gname
+    The group of the file(s), formatted as a group name in the
+group database. This record shall override the gid and gname
+fields in the following header block(s), and any gid extended
+header record. When used in read, copy, or list mode, pax shall
+translate the name from the UTF-8 encoding in the header record to
+the character set appropriate for the group database on the
+receiving system. If any of the UTF-8 characters cannot be
+translated, and if the -o invalid= UTF-8 option is not specified,
+the results are implementation-defined. When used in write or copy
+mode, pax shall include a gname extended header record for each
+file whose group name cannot be represented entirely with the
+letters and digits of the portable character set.
+
+linkpath
+    The pathname of a link being created to another file, of any
+type, previously archived. This record shall override the linkname
+field in the following ustar header block(s). The following ustar
+header block shall determine the type of link created. If typeflag
+of the following header block is 1, it shall be a hard link. If
+typeflag is 2, it shall be a symbolic link and the linkpath value
+shall be the contents of the symbolic link. The pax utility shall
+translate the name of the link (contents of the symbolic link)
+from the UTF-8 encoding to the character set appropriate for the
+local file system. When used in write or copy mode, pax shall
+include a linkpath extended header record for each link whose
+pathname cannot be represented entirely with the members of the
+portable character set other than NUL.
+
+mtime
+    The file modification time of the following file(s),
+equivalent to the value of the st_mtime member of the stat
+structure for a file, as described in the stat() function. This
+record shall override the mtime field in the following header
+block(s). The modification time shall be restored if the process
+has the appropriate privilege required to do so. The format of the
+<value> shall be as described in pax Extended Header File Times.
+
+path
+    The pathname of the following file(s). This record shall
+override the name and prefix fields in the following header
+block(s). The pax utility shall translate the pathname of the file
+from the UTF-8 encoding to the character set appropriate for the
+local file system.
+
+    When used in write or copy mode, pax shall include a path
+extended header record for each file whose pathname cannot be
+represented entirely with the members of the portable character
+set other than NUL.
+
+realtime.any
+    The keywords prefixed by "realtime." are reserved for future
+standardization.
+
+security.any
+    The keywords prefixed by "security." are reserved for future
+standardization.
+
+size
+    The size of the file in octets, expressed as a decimal number
+using digits from the ISO/IEC 646:1991 standard. This record shall
+override the size field in the following header block(s). When
+used in write or copy mode, pax shall include a size extended
+header record for each file with a size value greater than
+8589934591 (octal 77777777777).
+
+uid
+    The user ID of the file owner, expressed as a decimal number
+using digits from the ISO/IEC 646:1991 standard. This record shall
+override the uid field in the following header block(s). When used
+in write or copy mode, pax shall include a uid extended header
+record for each file whose owner ID is greater than 2097151 (octal
+7777777).
+
+uname
+    The owner of the following file(s), formatted as a user name
+in the user database. This record shall override the uid and uname
+fields in the following header block(s), and any uid extended
+header record. When used in read, copy, or list mode, pax shall
+translate the name from the UTF-8 encoding in the header record to
+the character set appropriate for the user database on the
+receiving system. If any of the UTF-8 characters cannot be
+translated, and if the -o invalid= UTF-8 option is not specified,
+the results are implementation-defined. When used in write or copy
+mode, pax shall include a uname extended header record for each
+file whose user name cannot be represented entirely with the
+letters and digits of the portable character set.
+
+If the <value> field is zero length, it shall delete any header
+block field, previously entered extended header value, or global
+extended header value of the same name.
+
+If a keyword in an extended header record (or in a -o
+option-argument) overrides or deletes a corresponding field in the
+ustar header block, pax shall ignore the contents of that header
+block field.
+
+Unlike the ustar header block fields, NULs shall not delimit
+<value>s; all characters within the <value> field shall be
+considered data for the field. None of the length limitations of
+the ustar header block fields in ustar Header Block shall apply to
+the extended header records.
+
+pax Extended Header File Times
+
+Time records shall be formatted as a decimal representation of the
+time in seconds since the Epoch. If a period ( '.' ) decimal point
+character is present, the digits to the right of the point shall
+represent the units of a subsecond timing granularity. In read or
+copy mode, the pax utility shall truncate the time of a file to
+the greatest value that is not greater than the input header
+file time. In write or copy mode, the pax utility shall output a
+time exactly if it can be represented exactly as a decimal number,
+and otherwise shall generate only enough digits so that the same
+time shall be recovered if the file is extracted on a system whose
+underlying implementation supports the same time granularity.
+
+Example from Linux kernel archive tarball:
+
+00000000  70 61 78 5f 67 6c 6f 62  61 6c 5f 68 65 61 64 65  |pax_global_heade|
+00000010  72 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |r...............|
+00000020  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000030  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000060  00 00 00 00 30 30 30 30  36 36 36 00 30 30 30 30  |....0000666.0000|
+00000070  30 30 30 00 30 30 30 30  30 30 30 00 30 30 30 30  |000.0000000.0000|
+00000080  30 30 30 30 30 36 34 00  30 30 30 30 30 30 30 30  |0000064.00000000|
+00000090  30 30 30 00 30 30 31 34  30 35 33 00 67 00 00 00  |000.0014053.g...|
+000000a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000000b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000000d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000100  00 75 73 74 61 72 00 30  30 67 69 74 00 00 00 00  |.ustar.00git....|
+00000110  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000120  00 00 00 00 00 00 00 00  00 67 69 74 00 00 00 00  |.........git....|
+00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000140  00 00 00 00 00 00 00 00  00 30 30 30 30 30 30 30  |.........0000000|
+00000150  00 30 30 30 30 30 30 30  00 00 00 00 00 00 00 00  |.0000000........|
+00000160  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000170  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000190  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000001a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000001c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000001d0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+000001f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+00000200  35 32 20 63 6f 6d 6d 65  6e 74 3d 62 31 30 35 30  |52 comment=b1050|
+00000210  32 62 32 32 61 31 32 30  39 64 36 62 34 37 36 33  |2b22a1209d6b4763|
+00000220  39 64 38 38 62 38 31 32  62 32 31 66 62 35 39 34  |9d88b812b21fb594|
+00000230  39 65 34 0a 00 00 00 00  00 00 00 00 00 00 00 00  |9e4.............|
+00000240  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+...
diff --git a/busybox-1.19.3/docs/unicode.txt b/busybox-1.19.3/docs/unicode.txt
new file mode 100644
index 0000000..9c159ce
--- /dev/null
+++ b/busybox-1.19.3/docs/unicode.txt
@@ -0,0 +1,71 @@
+	Unicode support in busybox
+
+There are several scenarios where we need to handle unicode
+correctly.
+
+	Shell input
+
+We want to correctly handle input of unicode characters.
+There are several problems with it. Just handling input
+as sequence of bytes would break any editing. This was fixed
+and now lineedit operates on the array of wchar_t's.
+But we also need to handle the following problematic moments:
+
+* It is unreasonable to expect that output device supports
+  _any_ unicode chars. Perhaps we need to avoid printing
+  those chars which are not supported by output device.
+  Examples: chars which are not present in the font,
+  chars which are not assigned in unicode,
+  combining chars (especially trying to combine bad pairs:
+  a_chinese_symbol + "combining grave accent" = ??!)
+
+* We need to account for the fact that unicode chars have
+  different widths: 0 for combining chars, 1 for usual,
+  2 for ideograms (are there 3+ wide chars?).
+
+* Bidirectional handling. If user wants to echo a phrase
+  in Hebrew, he types: echo "srettel werbeH"
+
+	Editors (vi, ed)
+
+This case is a bit similar to "shell input", but unlike shell,
+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.
+
+	more, less
+
+Need to correctly display any input file. Ideally, with
+ASCII/unicode/filtered_unicode option or keyboard switch.
+Note: need to handle tabs and backspaces specially
+(bksp is for manpage compat).
+
+	cut, fold, watch
+
+May need ability to cut unicode string to specified number of wchars
+and/or to specified screen width. Need to handle tabs specially.
+
+	sed, awk, grep
+
+Handle unicode-aware regexp match
+
+	ls (multi-column display)
+
+ls will fail to line up columnar output if it will not account
+for character widths (and maybe filter out some of them, see
+above). OTOH, non-columnar views (ls -1, ls -l, ls | car)
+should NOT filter out bad unicode (but need to filter out
+control chars (coreutils does that). Note that unlike more/less,
+tabs and backspaces need not special handling.
+
+	top, ps
+
+Need to perform filtering similar to ls.
+
+	Filename display (in error messages and elsewhere)
+
+Need to perform filtering similar to ls.
+
+
+TODO: write an email to Asmus Freytag (asmus@unicode.org),
+author of http://unicode.org/reports/tr11/
diff --git a/busybox-1.19.3/docs/unicode_UTF-8-test.txt b/busybox-1.19.3/docs/unicode_UTF-8-test.txt
new file mode 100644
index 0000000..abd16f7
--- /dev/null
+++ b/busybox-1.19.3/docs/unicode_UTF-8-test.txt
Binary files differ
diff --git a/busybox-1.19.3/docs/unicode_full-bmp.txt b/busybox-1.19.3/docs/unicode_full-bmp.txt
new file mode 100644
index 0000000..2aeaa1e
--- /dev/null
+++ b/busybox-1.19.3/docs/unicode_full-bmp.txt
@@ -0,0 +1,2079 @@
+
+Full BMP Test File
+------------------
+
+Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2003-04-22
+
+This file contains the UTF-8 sequences of all code positions in the
+ISO 10646-1 Basic Multilingual Plane, except for the C0 and C1 control
+character areas. This corresponds to all codes in the range U+0020 -
+U+007E and U+00A0 - U+FFFF. [uniset +0000..ffff utf8-list]
+
+
+Basic Latin (U+0000-U+007F):
+
+ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+`abcdefghijklmnopqrstuvwxyz{|}~
+
+Latin-1 Supplement (U+0080-U+00FF):
+
+ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞß
+àáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
+
+Latin Extended-A (U+0100-U+017F):
+
+ĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĸĹĺĻļĽľĿ
+ŀŁłŃńŅņŇňʼnŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſ
+
+Latin Extended-B (U+0180-U+024F):
+
+ƀƁƂƃƄƅƆƇƈƉƊƋƌƍƎƏƐƑƒƓƔƕƖƗƘƙƚƛƜƝƞƟƠơƢƣƤƥƦƧƨƩƪƫƬƭƮƯưƱƲƳƴƵƶƷƸƹƺƻƼƽƾƿ
+ǀǁǂǃDŽDždžLJLjljNJNjnjǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǝǞǟǠǡǢǣǤǥǦǧǨǩǪǫǬǭǮǯǰDZDzdzǴǵǶǷǸǹǺǻǼǽǾǿ
+ȀȁȂȃȄȅȆȇȈȉȊȋȌȍȎȏȐȑȒȓȔȕȖȗȘșȚțȜȝȞȟȠȡȢȣȤȥȦȧȨȩȪȫȬȭȮȯȰȱȲȳȴȵȶȷȸȹȺȻȼȽȾȿ
+ɀɁɂɃɄɅɆɇɈɉɊɋɌɍɎɏ
+
+IPA Extensions (U+0250-U+02AF):
+
+ɐɑɒɓɔɕɖɗɘəɚɛɜɝɞɟɠɡɢɣɤɥɦɧɨɩɪɫɬɭɮɯɰɱɲɳɴɵɶɷɸɹɺɻɼɽɾɿʀʁʂʃʄʅʆʇʈʉʊʋʌʍʎʏ
+ʐʑʒʓʔʕʖʗʘʙʚʛʜʝʞʟʠʡʢʣʤʥʦʧʨʩʪʫʬʭʮʯ
+
+Spacing Modifier Letters (U+02B0-U+02FF):
+
+ʰʱʲʳʴʵʶʷʸʹʺʻʼʽʾʿˀˁ˂˃˄˅ˆˇˈˉˊˋˌˍˎˏːˑ˒˓˔˕˖˗˘˙˚˛˜˝˞˟ˠˡˢˣˤ˥˦˧˨˩˪˫ˬ˭ˮ˯
+˰˱˲˳˴˵˶˷˸˹˺˻˼˽˾˿
+
+Combining Diacritical Marks (U+0300-U+036F):
+
+◌̀◌́◌̂◌̃◌̄◌̅◌̆◌̇◌̈◌̉◌̊◌̋◌̌◌̍◌̎◌̏◌̐◌̑◌̒◌̓◌̔◌̕◌̖◌̗◌̘◌̙◌̚◌̛◌̜◌̝◌̞◌̟◌̠◌̡◌̢◌̣◌̤◌̥◌̦◌̧◌̨◌̩◌̪◌̫◌̬◌̭◌̮◌̯◌̰◌̱◌̲◌̳◌̴◌̵◌̶◌̷◌̸◌̹◌̺◌̻◌̼◌̽◌̾◌̿
+◌̀◌́◌͂◌̓◌̈́◌ͅ◌͆◌͇◌͈◌͉◌͊◌͋◌͌◌͍◌͎◌͏͓͔͕͖͙͚͐͑͒͗͛͘͜͟͝͞◌͠◌͡◌͢◌ͣ◌ͤ◌ͥ◌ͦ◌ͧ◌ͨ◌ͩ◌ͪ◌ͫ◌ͬ◌ͭ◌ͮ◌ͯ
+
+Greek and Coptic (U+0370-U+03FF):
+
+ͰͱͲͳʹ͵Ͷͷ͸͹ͺͻͼͽ;Ϳ΀΁΂΃΄΅Ά·ΈΉΊ΋Ό΍ΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩΪΫάέήί
+ΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯ
+ϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿ
+
+Cyrillic (U+0400-U+04FF):
+
+ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп
+рстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿ
+Ҁҁ҂◌҃◌҄◌҅◌҆҇ ҈ ҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿ
+ӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿ
+
+Cyrillic Supplementary (U+0500-U+052F):
+
+ԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԨԩԪԫԬԭԮԯ
+
+Armenian (U+0530-U+058F):
+
+԰ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖ՗՘ՙ՚՛՜՝՞՟ՠաբգդեզէըթժիլխծկ
+հձղճմյնշոչպջռսվտրցւփքօֆևֈ։֊֋֌֍֎֏
+
+Hebrew (U+0590-U+05FF):
+
+֐◌֑◌֒◌֓◌֔◌֕◌֖◌֗◌֘◌֙◌֚◌֛◌֜◌֝◌֞◌֟◌֠◌֢֡◌֣◌֤◌֥◌֦◌֧◌֨◌֩◌֪◌֫◌֬◌֭◌֮◌֯◌ְ◌ֱ◌ֲ◌ֳ◌ִ◌ֵ◌ֶ◌ַ◌ָ◌ֹֺ◌ֻ◌ּ◌ֽ־◌ֿ׀◌ׁ◌ׂ׃◌ׅׄ׆ׇ׈׉׊׋׌׍׎׏
+אבגדהוזחטיךכלםמןנסעףפץצקרשת׫׬׭׮ׯװױײ׳״׵׶׷׸׹׺׻׼׽׾׿
+
+Arabic (U+0600-U+06FF):
+
+؀؁؂؃؄؅؆؇؈؉؊؋،؍؎؏ؘؙؚؐؑؒؓؔؕؖؗ؛؜؝؞؟ؠءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿ
+ـفقكلمنهوىي◌ً◌ٌ◌ٍ◌َ◌ُ◌ِ◌ّ◌ْ◌ٓ◌ٔ◌ٕٖٜٟٗ٘ٙٚٛٝٞ٠١٢٣٤٥٦٧٨٩٪٫٬٭ٮٯ◌ٰٱٲٳٴٵٶٷٸٹٺٻټٽپٿ
+ڀځڂڃڄڅچڇڈډڊڋڌڍڎڏڐڑڒړڔڕږڗژڙښڛڜڝڞڟڠڡڢڣڤڥڦڧڨکڪګڬڭڮگڰڱڲڳڴڵڶڷڸڹںڻڼڽھڿ
+ۀہۂۃۄۅۆۇۈۉۊۋیۍێۏېۑےۓ۔ە◌ۖ◌ۗ◌ۘ◌ۙ◌ۚ◌ۛ◌ۜ۝ ۞◌۟◌۠◌ۡ◌ۢ◌ۣ◌ۤۥۦ◌ۧ◌ۨ۩◌۪◌۫◌۬◌ۭۮۯ۰۱۲۳۴۵۶۷۸۹ۺۻۼ۽۾ۿ
+
+Syriac (U+0700-U+074F):
+
+܀܁܂܃܄܅܆܇܈܉܊܋܌܍܎܏ܐ◌ܑܒܓܔܕܖܗܘܙܚܛܜܝܞܟܠܡܢܣܤܥܦܧܨܩܪܫܬܭܮܯ◌ܰ◌ܱ◌ܲ◌ܳ◌ܴ◌ܵ◌ܶ◌ܷ◌ܸ◌ܹ◌ܺ◌ܻ◌ܼ◌ܽ◌ܾ◌ܿ
+◌݀◌݁◌݂◌݃◌݄◌݅◌݆◌݇◌݈◌݉◌݊݋݌ݍݎݏ
+
+Free block (U+0750-U+077F):
+
+ݐݑݒݓݔݕݖݗݘݙݚݛݜݝݞݟݠݡݢݣݤݥݦݧݨݩݪݫݬݭݮݯݰݱݲݳݴݵݶݷݸݹݺݻݼݽݾݿ
+
+Thaana (U+0780-U+07BF):
+
+ހށނރބޅކއވމފދތލގޏސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥ◌ަ◌ާ◌ި◌ީ◌ު◌ޫ◌ެ◌ޭ◌ޮ◌ޯ◌ްޱ޲޳޴޵޶޷޸޹޺޻޼޽޾޿
+
+Free block (U+07C0-U+08FF):
+
+߀߁߂߃߄߅߆߇߈߉ߊߋߌߍߎߏߐߑߒߓߔߕߖߗߘߙߚߛߜߝߞߟߠߡߢߣߤߥߦߧߨߩߪ߲߫߬߭߮߯߰߱߳ߴߵ߶߷߸߹ߺ߻߼߽߾߿
+ࠀࠁࠂࠃࠄࠅࠆࠇࠈࠉࠊࠋࠌࠍࠎࠏࠐࠑࠒࠓࠔࠕࠖࠗ࠘࠙ࠚࠛࠜࠝࠞࠟࠠࠡࠢࠣࠤࠥࠦࠧࠨࠩࠪࠫࠬ࠭࠮࠯࠰࠱࠲࠳࠴࠵࠶࠷࠸࠹࠺࠻࠼࠽࠾࠿
+ࡀࡁࡂࡃࡄࡅࡆࡇࡈࡉࡊࡋࡌࡍࡎࡏࡐࡑࡒࡓࡔࡕࡖࡗࡘ࡙࡚࡛࡜࡝࡞࡟ࡠࡡࡢࡣࡤࡥࡦࡧࡨࡩࡪ࡫࡬࡭࡮࡯ࡰࡱࡲࡳࡴࡵࡶࡷࡸࡹࡺࡻࡼࡽࡾࡿ
+ࢀࢁࢂࢃࢄࢅࢆࢇ࢈ࢉࢊࢋࢌࢍࢎ࢏࢐࢑࢒࢓࢔࢕࢖࢙࢚࢛ࢗ࢘࢜࢝࢞࢟ࢠࢡࢢࢣࢤࢥࢦࢧࢨࢩࢪࢫࢬࢭࢮࢯࢰࢱࢲࢳࢴࢵࢶࢷࢸࢹࢺࢻࢼࢽࢾࢿ
+ࣀࣁࣂࣃࣄࣅࣆࣇࣈࣉ࣏࣐࣑࣒࣓࣊࣋࣌࣍࣎ࣔࣕࣖࣗࣘࣙࣚࣛࣜࣝࣞࣟ࣠࣡࣢ࣰࣱࣲࣣࣦࣩ࣭࣮࣯ࣶࣹࣺࣤࣥࣧࣨ࣪࣫࣬ࣳࣴࣵࣷࣸࣻࣼࣽࣾࣿ
+
+Devanagari (U+0900-U+097F):
+
+ऀ◌ँ◌ंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयरऱलळऴवशषसहऺऻ◌़ऽाि
+ी◌ु◌ू◌ृ◌ॄ◌ॅ◌ॆ◌े◌ैॉॊोौ◌्ॎॏॐ◌॑◌॒◌॓◌॔ॕॖॗक़ख़ग़ज़ड़ढ़फ़य़ॠॡ◌ॢ◌ॣ।॥०१२३४५६७८९॰ॱॲॳॴॵॶॷॸॹॺॻॼॽॾॿ
+
+Bengali (U+0980-U+09FF):
+
+ঀ◌ঁংঃ঄অআইঈউঊঋঌ঍঎এঐ঑঒ওঔকখগঘঙচছজঝঞটঠডঢণতথদধন঩পফবভমযর঱ল঳঴঵শষসহ঺঻◌়ঽাি
+ী◌ু◌ূ◌ৃ◌ৄ৅৆েৈ৉৊োৌ◌্ৎ৏৐৑৒৓৔৕৖ৗ৘৙৚৛ড়ঢ়৞য়ৠৡ◌ৢ◌ৣ৤৥০১২৩৪৫৬৭৮৯ৰৱ৲৳৴৵৶৷৸৹৺৻ৼ৽৾৿
+
+Gurmukhi (U+0A00-U+0A7F):
+
+਀ਁ◌ਂਃ਄ਅਆਇਈਉਊ਋਌਍਎ਏਐ਑਒ਓਔਕਖਗਘਙਚਛਜਝਞਟਠਡਢਣਤਥਦਧਨ਩ਪਫਬਭਮਯਰ਱ਲਲ਼਴ਵਸ਼਷ਸਹ਺਻◌਼਽ਾਿ
+ੀ◌ੁ◌ੂ੃੄੅੆◌ੇ◌ੈ੉੊◌ੋ◌ੌ◌੍੎੏੐ੑ੒੓੔੕੖੗੘ਖ਼ਗ਼ਜ਼ੜ੝ਫ਼੟੠੡੢੣੤੥੦੧੨੩੪੫੬੭੮੯◌ੰ◌ੱੲੳੴੵ੶੷੸੹੺੻੼੽੾੿
+
+Gujarati (U+0A80-U+0AFF):
+
+઀◌ઁ◌ંઃ઄અઆઇઈઉઊઋઌઍ઎એઐઑ઒ઓઔકખગઘઙચછજઝઞટઠડઢણતથદધન઩પફબભમયર઱લળ઴વશષસહ઺઻◌઼ઽાિ
+ી◌ુ◌ૂ◌ૃ◌ૄ◌ૅ૆◌ે◌ૈૉ૊ોૌ◌્૎૏ૐ૑૒૓૔૕૖૗૘૙૚૛૜૝૞૟ૠૡૢૣ૤૥૦૧૨૩૪૫૬૭૮૯૰૱૲૳૴૵૶૷૸ૹૺૻૼ૽૾૿
+
+Oriya (U+0B00-U+0B7F):
+
+଀◌ଁଂଃ଄ଅଆଇଈଉଊଋଌ଍଎ଏଐ଑଒ଓଔକଖଗଘଙଚଛଜଝଞଟଠଡଢଣତଥଦଧନ଩ପଫବଭମଯର଱ଲଳ଴ଵଶଷସହ଺଻◌଼ଽା◌ି
+ୀ◌ୁ◌ୂ◌ୃୄ୅୆େୈ୉୊ୋୌ◌୍୎୏୐୑୒୓୔୕◌ୖୗ୘୙୚୛ଡ଼ଢ଼୞ୟୠୡୢୣ୤୥୦୧୨୩୪୫୬୭୮୯୰ୱ୲୳୴୵୶୷୸୹୺୻୼୽୾୿
+
+Tamil (U+0B80-U+0BFF):
+
+஀஁◌ஂஃ஄அஆஇஈஉஊ஋஌஍எஏஐ஑ஒஓஔக஖஗஘ஙச஛ஜ஝ஞட஠஡஢ணத஥஦஧நனப஫஬஭மயரறலளழவஶஷஸஹ஺஻஼஽ாி
+◌ீுூ௃௄௅ெேை௉ொோௌ◌்௎௏ௐ௑௒௓௔௕௖ௗ௘௙௚௛௜௝௞௟௠௡௢௣௤௥௦௧௨௩௪௫௬௭௮௯௰௱௲௳௴௵௶௷௸௹௺௻௼௽௾௿
+
+Telugu (U+0C00-U+0C7F):
+
+ఀఁంఃఄఅఆఇఈఉఊఋఌ఍ఎఏఐ఑ఒఓఔకఖగఘఙచఛజఝఞటఠడఢణతథదధన఩పఫబభమయరఱలళఴవశషసహ఺఻఼ఽ◌ా◌ి
+◌ీుూృౄ౅◌ె◌ే◌ై౉◌ొ◌ో◌ౌ◌్౎౏౐౑౒౓౔◌ౕ◌ౖ౗ౘౙౚ౛౜ౝ౞౟ౠౡౢౣ౤౥౦౧౨౩౪౫౬౭౮౯౰౱౲౳౴౵౶౷౸౹౺౻౼౽౾౿
+
+Kannada (U+0C80-U+0CFF):
+
+ಀಁಂಃ಄ಅಆಇಈಉಊಋಌ಍ಎಏಐ಑ಒಓಔಕಖಗಘಙಚಛಜಝಞಟಠಡಢಣತಥದಧನ಩ಪಫಬಭಮಯರಱಲಳ಴ವಶಷಸಹ಺಻಼ಽಾ◌ಿ
+ೀುೂೃೄ೅◌ೆೇೈ೉ೊೋ◌ೌ◌್೎೏೐೑೒೓೔ೕೖ೗೘೙೚೛೜ೝೞ೟ೠೡೢೣ೤೥೦೧೨೩೪೫೬೭೮೯೰ೱೲೳ೴೵೶೷೸೹೺೻೼೽೾೿
+
+Malayalam (U+0D00-U+0D7F):
+
+ഀഁംഃഄഅആഇഈഉഊഋഌ഍എഏഐ഑ഒഓഔകഖഗഘങചഛജഝഞടഠഡഢണതഥദധനഩപഫബഭമയരറലളഴവശഷസഹഺ഻഼ഽാി
+ീ◌ു◌ൂ◌ൃൄ൅െേൈ൉ൊോൌ◌്ൎ൏൐൑൒൓ൔൕൖൗ൘൙൚൛൜൝൞ൟൠൡൢൣ൤൥൦൧൨൩൪൫൬൭൮൯൰൱൲൳൴൵൶൷൸൹ൺൻർൽൾൿ
+
+Sinhala (U+0D80-U+0DFF):
+
+඀ඁංඃ඄අආඇඈඉඊඋඌඍඎඏඐඑඒඓඔඕඖ඗඘඙කඛගඝඞඟචඡජඣඤඥඦටඨඩඪණඬතථදධන඲ඳපඵබභමඹයර඼ල඾඿
+වශෂසහළෆ෇෈෉◌්෋෌෍෎ාැෑ◌ි◌ී◌ු෕◌ූ෗ෘෙේෛොෝෞෟ෠෡෢෣෤෥෦෧෨෩෪෫෬෭෮෯෰෱ෲෳ෴෵෶෷෸෹෺෻෼෽෾෿
+
+Thai (U+0E00-U+0E7F):
+
+฀กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะ◌ัาำ◌ิ◌ี◌ึ◌ื◌ุ◌ู◌ฺ฻฼฽฾฿
+เแโใไๅๆ◌็◌่◌้◌๊◌๋◌์◌ํ◌๎๏๐๑๒๓๔๕๖๗๘๙๚๛๜๝๞๟๠๡๢๣๤๥๦๧๨๩๪๫๬๭๮๯๰๱๲๳๴๵๶๷๸๹๺๻๼๽๾๿
+
+Lao (U+0E80-U+0EFF):
+
+຀ກຂ຃ຄ຅ຆງຈຉຊ຋ຌຍຎຏຐຑຒຓດຕຖທຘນບປຜຝພຟຠມຢຣ຤ລ຦ວຨຩສຫຬອຮຯະ◌ັາຳ◌ິ◌ີ◌ຶ◌ື◌ຸ◌຺ູ◌ົ◌ຼຽ຾຿
+ເແໂໃໄ໅ໆ໇◌່◌້◌໊◌໋◌໌◌ໍ໎໏໐໑໒໓໔໕໖໗໘໙໚໛ໜໝໞໟ໠໡໢໣໤໥໦໧໨໩໪໫໬໭໮໯໰໱໲໳໴໵໶໷໸໹໺໻໼໽໾໿
+
+Tibetan (U+0F00-U+0FFF):
+
+ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗◌༘◌༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴◌༵༶◌༷༸◌༹༺༻༼༽༾༿
+ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰◌ཱ◌ི◌ཱི◌ུ◌ཱུ◌ྲྀ◌ཷ◌ླྀ◌ཹ◌ེ◌ཻ◌ོ◌ཽ◌ཾཿ
+◌ྀ◌ཱྀ◌ྂ◌ྃ◌྄྅◌྆◌྇ྈྉྊྋྌྍྎྏ◌ྐ◌ྑ◌ྒ◌ྒྷ◌ྔ◌ྕ◌ྖ◌ྗ྘◌ྙ◌ྚ◌ྛ◌ྜ◌ྜྷ◌ྞ◌ྟ◌ྠ◌ྡ◌ྡྷ◌ྣ◌ྤ◌ྥ◌ྦ◌ྦྷ◌ྨ◌ྩ◌ྪ◌ྫ◌ྫྷ◌ྭ◌ྮ◌ྯ◌ྰ◌ྱ◌ྲ◌ླ◌ྴ◌ྵ◌ྶ◌ྷ◌ྸ◌ྐྵ◌ྺ◌ྻ◌ྼ྽྾྿
+࿀࿁࿂࿃࿄࿅◌࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚࿛࿜࿝࿞࿟࿠࿡࿢࿣࿤࿥࿦࿧࿨࿩࿪࿫࿬࿭࿮࿯࿰࿱࿲࿳࿴࿵࿶࿷࿸࿹࿺࿻࿼࿽࿾࿿
+
+Myanmar (U+1000-U+109F):
+
+ကခဂဃငစဆဇဈဉညဋဌဍဎဏတထဒဓနပဖဗဘမယရလဝသဟဠအဢဣဤဥဦဧဨဩဪါာ◌ိ◌ီ◌ု◌ူေ◌ဲဳဴဵ◌ံ◌့း◌္်ျြွှဿ
+၀၁၂၃၄၅၆၇၈၉၊။၌၍၎၏ၐၑၒၓၔၕၖၗ◌ၘ◌ၙၚၛၜၝၞၟၠၡၢၣၤၥၦၧၨၩၪၫၬၭၮၯၰၱၲၳၴၵၶၷၸၹၺၻၼၽၾၿ
+ႀႁႂႃႄႅႆႇႈႉႊႋႌႍႎႏ႐႑႒႓႔႕႖႗႘႙ႚႛႜႝ႞႟
+
+Georgian (U+10A0-U+10FF):
+
+ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ჆Ⴧ჈჉჊჋჌Ⴭ჎჏აბგდევზთიკლმნოპჟ
+რსტუფქღყშჩცძწჭხჯჰჱჲჳჴჵჶჷჸჹჺ჻ჼჽჾჿ
+
+Hangul Jamo (U+1100-U+11FF):
+
+ᄀᄁᄂᄃᄄᄅᄆᄇᄈᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒᄓᄔᄕᄖᄗᄘᄙᄚᄛᄜᄝᄞᄟ
+ᄠᄡᄢᄣᄤᄥᄦᄧᄨᄩᄪᄫᄬᄭᄮᄯᄰᄱᄲᄳᄴᄵᄶᄷᄸᄹᄺᄻᄼᄽᄾᄿ
+ᅀᅁᅂᅃᅄᅅᅆᅇᅈᅉᅊᅋᅌᅍᅎᅏᅐᅑᅒᅓᅔᅕᅖᅗᅘᅙᅚᅛᅜᅝᅞᅟ
+ᅠᅡᅢᅣᅤᅥᅦᅧᅨᅩᅪᅫᅬᅭᅮᅯᅰᅱᅲᅳᅴᅵᅶᅷᅸᅹᅺᅻᅼᅽᅾᅿᆀᆁᆂᆃᆄᆅᆆᆇᆈᆉᆊᆋᆌᆍᆎᆏᆐᆑᆒᆓᆔᆕᆖᆗᆘᆙᆚᆛᆜᆝᆞᆟ
+ᆠᆡᆢᆣᆤᆥᆦᆧᆨᆩᆪᆫᆬᆭᆮᆯᆰᆱᆲᆳᆴᆵᆶᆷᆸᆹᆺᆻᆼᆽᆾᆿᇀᇁᇂᇃᇄᇅᇆᇇᇈᇉᇊᇋᇌᇍᇎᇏᇐᇑᇒᇓᇔᇕᇖᇗᇘᇙᇚᇛᇜᇝᇞᇟ
+ᇠᇡᇢᇣᇤᇥᇦᇧᇨᇩᇪᇫᇬᇭᇮᇯᇰᇱᇲᇳᇴᇵᇶᇷᇸᇹᇺᇻᇼᇽᇾᇿ
+
+Ethiopic (U+1200-U+137F):
+
+ሀሁሂሃሄህሆሇለሉሊላሌልሎሏሐሑሒሓሔሕሖሗመሙሚማሜምሞሟሠሡሢሣሤሥሦሧረሩሪራሬርሮሯሰሱሲሳሴስሶሷሸሹሺሻሼሽሾሿ
+ቀቁቂቃቄቅቆቇቈ቉ቊቋቌቍ቎቏ቐቑቒቓቔቕቖ቗ቘ቙ቚቛቜቝ቞቟በቡቢባቤብቦቧቨቩቪቫቬቭቮቯተቱቲታቴትቶቷቸቹቺቻቼችቾቿ
+ኀኁኂኃኄኅኆኇኈ኉ኊኋኌኍ኎኏ነኑኒናኔንኖኗኘኙኚኛኜኝኞኟአኡኢኣኤእኦኧከኩኪካኬክኮኯኰ኱ኲኳኴኵ኶኷ኸኹኺኻኼኽኾ኿
+ዀ዁ዂዃዄዅ዆዇ወዉዊዋዌውዎዏዐዑዒዓዔዕዖ዗ዘዙዚዛዜዝዞዟዠዡዢዣዤዥዦዧየዩዪያዬይዮዯደዱዲዳዴድዶዷዸዹዺዻዼዽዾዿ
+ጀጁጂጃጄጅጆጇገጉጊጋጌግጎጏጐ጑ጒጓጔጕ጖጗ጘጙጚጛጜጝጞጟጠጡጢጣጤጥጦጧጨጩጪጫጬጭጮጯጰጱጲጳጴጵጶጷጸጹጺጻጼጽጾጿ
+ፀፁፂፃፄፅፆፇፈፉፊፋፌፍፎፏፐፑፒፓፔፕፖፗፘፙፚ፛፜፝፞፟፠፡።፣፤፥፦፧፨፩፪፫፬፭፮፯፰፱፲፳፴፵፶፷፸፹፺፻፼፽፾፿
+
+Free block (U+1380-U+139F):
+
+ᎀᎁᎂᎃᎄᎅᎆᎇᎈᎉᎊᎋᎌᎍᎎᎏ᎐᎑᎒᎓᎔᎕᎖᎗᎘᎙᎚᎛᎜᎝᎞᎟
+
+Cherokee (U+13A0-U+13FF):
+
+ᎠᎡᎢᎣᎤᎥᎦᎧᎨᎩᎪᎫᎬᎭᎮᎯᎰᎱᎲᎳᎴᎵᎶᎷᎸᎹᎺᎻᎼᎽᎾᎿᏀᏁᏂᏃᏄᏅᏆᏇᏈᏉᏊᏋᏌᏍᏎᏏᏐᏑᏒᏓᏔᏕᏖᏗᏘᏙᏚᏛᏜᏝᏞᏟ
+ᏠᏡᏢᏣᏤᏥᏦᏧᏨᏩᏪᏫᏬᏭᏮᏯᏰᏱᏲᏳᏴᏵ᏶᏷ᏸᏹᏺᏻᏼᏽ᏾᏿
+
+Unified Canadian Aboriginal Syllabics (U+1400-U+167F):
+
+᐀ᐁᐂᐃᐄᐅᐆᐇᐈᐉᐊᐋᐌᐍᐎᐏᐐᐑᐒᐓᐔᐕᐖᐗᐘᐙᐚᐛᐜᐝᐞᐟᐠᐡᐢᐣᐤᐥᐦᐧᐨᐩᐪᐫᐬᐭᐮᐯᐰᐱᐲᐳᐴᐵᐶᐷᐸᐹᐺᐻᐼᐽᐾᐿ
+ᑀᑁᑂᑃᑄᑅᑆᑇᑈᑉᑊᑋᑌᑍᑎᑏᑐᑑᑒᑓᑔᑕᑖᑗᑘᑙᑚᑛᑜᑝᑞᑟᑠᑡᑢᑣᑤᑥᑦᑧᑨᑩᑪᑫᑬᑭᑮᑯᑰᑱᑲᑳᑴᑵᑶᑷᑸᑹᑺᑻᑼᑽᑾᑿ
+ᒀᒁᒂᒃᒄᒅᒆᒇᒈᒉᒊᒋᒌᒍᒎᒏᒐᒑᒒᒓᒔᒕᒖᒗᒘᒙᒚᒛᒜᒝᒞᒟᒠᒡᒢᒣᒤᒥᒦᒧᒨᒩᒪᒫᒬᒭᒮᒯᒰᒱᒲᒳᒴᒵᒶᒷᒸᒹᒺᒻᒼᒽᒾᒿ
+ᓀᓁᓂᓃᓄᓅᓆᓇᓈᓉᓊᓋᓌᓍᓎᓏᓐᓑᓒᓓᓔᓕᓖᓗᓘᓙᓚᓛᓜᓝᓞᓟᓠᓡᓢᓣᓤᓥᓦᓧᓨᓩᓪᓫᓬᓭᓮᓯᓰᓱᓲᓳᓴᓵᓶᓷᓸᓹᓺᓻᓼᓽᓾᓿ
+ᔀᔁᔂᔃᔄᔅᔆᔇᔈᔉᔊᔋᔌᔍᔎᔏᔐᔑᔒᔓᔔᔕᔖᔗᔘᔙᔚᔛᔜᔝᔞᔟᔠᔡᔢᔣᔤᔥᔦᔧᔨᔩᔪᔫᔬᔭᔮᔯᔰᔱᔲᔳᔴᔵᔶᔷᔸᔹᔺᔻᔼᔽᔾᔿ
+ᕀᕁᕂᕃᕄᕅᕆᕇᕈᕉᕊᕋᕌᕍᕎᕏᕐᕑᕒᕓᕔᕕᕖᕗᕘᕙᕚᕛᕜᕝᕞᕟᕠᕡᕢᕣᕤᕥᕦᕧᕨᕩᕪᕫᕬᕭᕮᕯᕰᕱᕲᕳᕴᕵᕶᕷᕸᕹᕺᕻᕼᕽᕾᕿ
+ᖀᖁᖂᖃᖄᖅᖆᖇᖈᖉᖊᖋᖌᖍᖎᖏᖐᖑᖒᖓᖔᖕᖖᖗᖘᖙᖚᖛᖜᖝᖞᖟᖠᖡᖢᖣᖤᖥᖦᖧᖨᖩᖪᖫᖬᖭᖮᖯᖰᖱᖲᖳᖴᖵᖶᖷᖸᖹᖺᖻᖼᖽᖾᖿ
+ᗀᗁᗂᗃᗄᗅᗆᗇᗈᗉᗊᗋᗌᗍᗎᗏᗐᗑᗒᗓᗔᗕᗖᗗᗘᗙᗚᗛᗜᗝᗞᗟᗠᗡᗢᗣᗤᗥᗦᗧᗨᗩᗪᗫᗬᗭᗮᗯᗰᗱᗲᗳᗴᗵᗶᗷᗸᗹᗺᗻᗼᗽᗾᗿ
+ᘀᘁᘂᘃᘄᘅᘆᘇᘈᘉᘊᘋᘌᘍᘎᘏᘐᘑᘒᘓᘔᘕᘖᘗᘘᘙᘚᘛᘜᘝᘞᘟᘠᘡᘢᘣᘤᘥᘦᘧᘨᘩᘪᘫᘬᘭᘮᘯᘰᘱᘲᘳᘴᘵᘶᘷᘸᘹᘺᘻᘼᘽᘾᘿ
+ᙀᙁᙂᙃᙄᙅᙆᙇᙈᙉᙊᙋᙌᙍᙎᙏᙐᙑᙒᙓᙔᙕᙖᙗᙘᙙᙚᙛᙜᙝᙞᙟᙠᙡᙢᙣᙤᙥᙦᙧᙨᙩᙪᙫᙬ᙭᙮ᙯᙰᙱᙲᙳᙴᙵᙶᙷᙸᙹᙺᙻᙼᙽᙾᙿ
+
+Ogham (U+1680-U+169F):
+
+ ᚁᚂᚃᚄᚅᚆᚇᚈᚉᚊᚋᚌᚍᚎᚏᚐᚑᚒᚓᚔᚕᚖᚗᚘᚙᚚ᚛᚜᚝᚞᚟
+
+Runic (U+16A0-U+16FF):
+
+ᚠᚡᚢᚣᚤᚥᚦᚧᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟ
+ᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪ᛫᛬᛭ᛮᛯᛰᛱᛲᛳᛴᛵᛶᛷᛸ᛹᛺᛻᛼᛽᛾᛿
+
+Tagalog (U+1700-U+171F):
+
+ᜀᜁᜂᜃᜄᜅᜆᜇᜈᜉᜊᜋᜌᜍᜎᜏᜐᜑ◌ᜒ◌ᜓ◌᜔᜕᜖᜗᜘᜙᜚᜛᜜᜝᜞ᜟ
+
+Hanunoo (U+1720-U+173F):
+
+ᜠᜡᜢᜣᜤᜥᜦᜧᜨᜩᜪᜫᜬᜭᜮᜯᜰᜱ◌ᜲ◌ᜳ◌᜴᜵᜶᜷᜸᜹᜺᜻᜼᜽᜾᜿
+
+Buhid (U+1740-U+175F):
+
+ᝀᝁᝂᝃᝄᝅᝆᝇᝈᝉᝊᝋᝌᝍᝎᝏᝐᝑ◌ᝒ◌ᝓ᝔᝕᝖᝗᝘᝙᝚᝛᝜᝝᝞᝟
+
+Tagbanwa (U+1760-U+177F):
+
+ᝠᝡᝢᝣᝤᝥᝦᝧᝨᝩᝪᝫᝬ᝭ᝮᝯᝰ᝱◌ᝲ◌ᝳ᝴᝵᝶᝷᝸᝹᝺᝻᝼᝽᝾᝿
+
+Khmer (U+1780-U+17FF):
+
+កខគឃងចឆជឈញដឋឌឍណតថទធនបផពភមយរលវឝឞសហឡអឣឤឥឦឧឨឩឪឫឬឭឮឯឰឱឲឳ឴឵ា◌ិ◌ី◌ឹ◌ឺ◌ុ◌ូ◌ួើឿ
+ៀេែៃោៅ◌ំះៈ◌៉◌៊◌់◌៌◌៍◌៎◌៏◌័◌៑◌្◌៓។៕៖ៗ៘៙៚៛ៜ៝៞៟០១២៣៤៥៦៧៨៩៪៫៬៭៮៯៰៱៲៳៴៵៶៷៸៹៺៻៼៽៾៿
+
+Mongolian (U+1800-U+18AF):
+
+᠀᠁᠂᠃᠄᠅᠆᠇᠈᠉᠊◌᠋◌᠌◌᠍᠎᠏᠐᠑᠒᠓᠔᠕᠖᠗᠘᠙᠚᠛᠜᠝᠞᠟ᠠᠡᠢᠣᠤᠥᠦᠧᠨᠩᠪᠫᠬᠭᠮᠯᠰᠱᠲᠳᠴᠵᠶᠷᠸᠹᠺᠻᠼᠽᠾᠿ
+ᡀᡁᡂᡃᡄᡅᡆᡇᡈᡉᡊᡋᡌᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡘᡙᡚᡛᡜᡝᡞᡟᡠᡡᡢᡣᡤᡥᡦᡧᡨᡩᡪᡫᡬᡭᡮᡯᡰᡱᡲᡳᡴᡵᡶᡷᡸ᡹᡺᡻᡼᡽᡾᡿
+ᢀᢁᢂᢃᢄᢅᢆᢇᢈᢉᢊᢋᢌᢍᢎᢏᢐᢑᢒᢓᢔᢕᢖᢗᢘᢙᢚᢛᢜᢝᢞᢟᢠᢡᢢᢣᢤᢥᢦᢧᢨ◌ᢩᢪ᢫᢬᢭᢮᢯
+
+Free block (U+18B0-U+18FF):
+
+ᢰᢱᢲᢳᢴᢵᢶᢷᢸᢹᢺᢻᢼᢽᢾᢿᣀᣁᣂᣃᣄᣅᣆᣇᣈᣉᣊᣋᣌᣍᣎᣏᣐᣑᣒᣓᣔᣕᣖᣗᣘᣙᣚᣛᣜᣝᣞᣟᣠᣡᣢᣣᣤᣥᣦᣧᣨᣩᣪᣫᣬᣭᣮᣯ
+ᣰᣱᣲᣳᣴᣵ᣶᣷᣸᣹᣺᣻᣼᣽᣾᣿
+
+Limbu (U+1900-U+194F):
+
+ᤀᤁᤂᤃᤄᤅᤆᤇᤈᤉᤊᤋᤌᤍᤎᤏᤐᤑᤒᤓᤔᤕᤖᤗᤘᤙᤚᤛᤜᤝᤞ᤟ᤠᤡᤢᤣᤤᤥᤦᤧᤨᤩᤪᤫ᤬᤭᤮᤯ᤰᤱᤲᤳᤴᤵᤶᤷᤸ᤻᤹᤺᤼᤽᤾᤿
+᥀᥁᥂᥃᥄᥅᥆᥇᥈᥉᥊᥋᥌᥍᥎᥏
+
+Tai Le (U+1950-U+197F):
+
+ᥐᥑᥒᥓᥔᥕᥖᥗᥘᥙᥚᥛᥜᥝᥞᥟᥠᥡᥢᥣᥤᥥᥦᥧᥨᥩᥪᥫᥬᥭ᥮᥯ᥰᥱᥲᥳᥴ᥵᥶᥷᥸᥹᥺᥻᥼᥽᥾᥿
+
+Free block (U+1980-U+19DF):
+
+ᦀᦁᦂᦃᦄᦅᦆᦇᦈᦉᦊᦋᦌᦍᦎᦏᦐᦑᦒᦓᦔᦕᦖᦗᦘᦙᦚᦛᦜᦝᦞᦟᦠᦡᦢᦣᦤᦥᦦᦧᦨᦩᦪᦫ᦬᦭᦮᦯ᦰᦱᦲᦳᦴᦵᦶᦷᦸᦹᦺᦻᦼᦽᦾᦿ
+ᧀᧁᧂᧃᧄᧅᧆᧇᧈᧉ᧊᧋᧌᧍᧎᧏᧐᧑᧒᧓᧔᧕᧖᧗᧘᧙᧚᧛᧜᧝᧞᧟
+
+Khmer Symbols (U+19E0-U+19FF):
+
+᧠᧡᧢᧣᧤᧥᧦᧧᧨᧩᧪᧫᧬᧭᧮᧯᧰᧱᧲᧳᧴᧵᧶᧷᧸᧹᧺᧻᧼᧽᧾᧿
+
+Free block (U+1A00-U+1CFF):
+
+ᨀᨁᨂᨃᨄᨅᨆᨇᨈᨉᨊᨋᨌᨍᨎᨏᨐᨑᨒᨓᨔᨕᨖᨘᨗᨙᨚᨛ᨜᨝᨞᨟ᨠᨡᨢᨣᨤᨥᨦᨧᨨᨩᨪᨫᨬᨭᨮᨯᨰᨱᨲᨳᨴᨵᨶᨷᨸᨹᨺᨻᨼᨽᨾᨿ
+ᩀᩁᩂᩃᩄᩅᩆᩇᩈᩉᩊᩋᩌᩍᩎᩏᩐᩑᩒᩓᩔᩕᩖᩗᩘᩙᩚᩛᩜᩝᩞ᩟᩠ᩡᩢᩣᩤᩥᩦᩧᩨᩩᩪᩫᩬᩭᩮᩯᩰᩱᩲᩳᩴ᩵᩶᩷᩸᩹᩺᩻᩼᩽᩾᩿
+᪀᪁᪂᪃᪄᪅᪆᪇᪈᪉᪊᪋᪌᪍᪎᪏᪐᪑᪒᪓᪔᪕᪖᪗᪘᪙᪚᪛᪜᪝᪞᪟᪠᪡᪢᪣᪤᪥᪦ᪧ᪨᪩᪪᪫᪬᪭᪮᪯᪵᪶᪷᪸᪹᪺᪽᪰᪱᪲᪳᪴᪻᪼᪾ᪿ
+ᫀ᫃᫄᫊᫁᫂᫅᫆᫇᫈᫉᫋ᫌᫍᫎ᫏᫐᫑᫒᫓᫔᫕᫖᫗᫘᫙᫚᫛᫜᫝᫞᫟᫠᫡᫢᫣᫤᫥᫦᫧᫨᫩᫪᫫᫬᫭᫮᫯᫰᫱᫲᫳᫴᫵᫶᫷᫸᫹᫺᫻᫼᫽᫾᫿
+ᬀᬁᬂᬃᬄᬅᬆᬇᬈᬉᬊᬋᬌᬍᬎᬏᬐᬑᬒᬓᬔᬕᬖᬗᬘᬙᬚᬛᬜᬝᬞᬟᬠᬡᬢᬣᬤᬥᬦᬧᬨᬩᬪᬫᬬᬭᬮᬯᬰᬱᬲᬳ᬴ᬵᬶᬷᬸᬹᬺᬻᬼᬽᬾᬿ
+ᭀᭁᭂᭃ᭄ᭅᭆᭇᭈᭉᭊᭋᭌ᭍᭎᭏᭐᭑᭒᭓᭔᭕᭖᭗᭘᭙᭚᭛᭜᭝᭞᭟᭠᭡᭢᭣᭤᭥᭦᭧᭨᭩᭪᭬᭫᭭᭮᭯᭰᭱᭲᭳᭴᭵᭶᭷᭸᭹᭺᭻᭼᭽᭾᭿
+ᮀᮁᮂᮃᮄᮅᮆᮇᮈᮉᮊᮋᮌᮍᮎᮏᮐᮑᮒᮓᮔᮕᮖᮗᮘᮙᮚᮛᮜᮝᮞᮟᮠᮡᮢᮣᮤᮥᮦᮧᮨᮩ᮪᮫ᮬᮭᮮᮯ᮰᮱᮲᮳᮴᮵᮶᮷᮸᮹ᮺᮻᮼᮽᮾᮿ
+ᯀᯁᯂᯃᯄᯅᯆᯇᯈᯉᯊᯋᯌᯍᯎᯏᯐᯑᯒᯓᯔᯕᯖᯗᯘᯙᯚᯛᯜᯝᯞᯟᯠᯡᯢᯣᯤᯥ᯦ᯧᯨᯩᯪᯫᯬᯭᯮᯯᯰᯱ᯲᯳᯴᯵᯶᯷᯸᯹᯺᯻᯼᯽᯾᯿
+ᰀᰁᰂᰃᰄᰅᰆᰇᰈᰉᰊᰋᰌᰍᰎᰏᰐᰑᰒᰓᰔᰕᰖᰗᰘᰙᰚᰛᰜᰝᰞᰟᰠᰡᰢᰣᰤᰥᰦᰧᰨᰩᰪᰫᰬᰭᰮᰯᰰᰱᰲᰳᰴᰵᰶ᰷᰸᰹᰺᰻᰼᰽᰾᰿
+᱀᱁᱂᱃᱄᱅᱆᱇᱈᱉᱊᱋᱌ᱍᱎᱏ᱐᱑᱒᱓᱔᱕᱖᱗᱘᱙ᱚᱛᱜᱝᱞᱟᱠᱡᱢᱣᱤᱥᱦᱧᱨᱩᱪᱫᱬᱭᱮᱯᱰᱱᱲᱳᱴᱵᱶᱷᱸᱹᱺᱻᱼᱽ᱾᱿
+ᲀᲁᲂᲃᲄᲅᲆᲇᲈᲉᲊ᲋᲌᲍᲎᲏ᲐᲑᲒᲓᲔᲕᲖᲗᲘᲙᲚᲛᲜᲝᲞᲟᲠᲡᲢᲣᲤᲥᲦᲧᲨᲩᲪᲫᲬᲭᲮᲯᲰᲱᲲᲳᲴᲵᲶᲷᲸᲹᲺ᲻᲼ᲽᲾᲿ
+᳀᳁᳂᳃᳄᳅᳆᳇᳈᳉᳊᳋᳌᳍᳎᳏᳐᳑᳒᳓᳔᳕᳖᳗᳘᳙᳜᳝᳞᳟᳚᳛᳠᳡᳢᳣᳤᳥᳦᳧᳨ᳩᳪᳫᳬ᳭ᳮᳯᳰᳱᳲᳳ᳴ᳵᳶ᳷᳸᳹ᳺ᳻᳼᳽᳾᳿
+
+Phonetic Extensions (U+1D00-U+1D7F):
+
+ᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩᴪᴫᴬᴭᴮᴯᴰᴱᴲᴳᴴᴵᴶᴷᴸᴹᴺᴻᴼᴽᴾᴿ
+ᵀᵁᵂᵃᵄᵅᵆᵇᵈᵉᵊᵋᵌᵍᵎᵏᵐᵑᵒᵓᵔᵕᵖᵗᵘᵙᵚᵛᵜᵝᵞᵟᵠᵡᵢᵣᵤᵥᵦᵧᵨᵩᵪᵫᵬᵭᵮᵯᵰᵱᵲᵳᵴᵵᵶᵷᵸᵹᵺᵻᵼᵽᵾᵿ
+
+Free block (U+1D80-U+1DFF):
+
+ᶀᶁᶂᶃᶄᶅᶆᶇᶈᶉᶊᶋᶌᶍᶎᶏᶐᶑᶒᶓᶔᶕᶖᶗᶘᶙᶚᶛᶜᶝᶞᶟᶠᶡᶢᶣᶤᶥᶦᶧᶨᶩᶪᶫᶬᶭᶮᶯᶰᶱᶲᶳᶴᶵᶶᶷᶸᶹᶺᶻᶼᶽᶾᶿ
+᷐᷎᷺᷂᷊᷏᷹᷽᷿᷷᷸᷀᷁᷃᷄᷅᷆᷇᷈᷉᷋᷌᷑᷒ᷓᷔᷕᷖᷗᷘᷙᷚᷛᷜᷝᷞᷟᷠᷡᷢᷣᷤᷥᷦᷧᷨᷩᷪᷫᷬᷭᷮᷯᷰᷱᷲᷳᷴ᷵᷻᷾᷶᷼᷍
+
+Latin Extended Additional (U+1E00-U+1EFF):
+
+ḀḁḂḃḄḅḆḇḈḉḊḋḌḍḎḏḐḑḒḓḔḕḖḗḘḙḚḛḜḝḞḟḠḡḢḣḤḥḦḧḨḩḪḫḬḭḮḯḰḱḲḳḴḵḶḷḸḹḺḻḼḽḾḿ
+ṀṁṂṃṄṅṆṇṈṉṊṋṌṍṎṏṐṑṒṓṔṕṖṗṘṙṚṛṜṝṞṟṠṡṢṣṤṥṦṧṨṩṪṫṬṭṮṯṰṱṲṳṴṵṶṷṸṹṺṻṼṽṾṿ
+ẀẁẂẃẄẅẆẇẈẉẊẋẌẍẎẏẐẑẒẓẔẕẖẗẘẙẚẛẜẝẞẟẠạẢảẤấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾế
+ỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹỺỻỼỽỾỿ
+
+Greek Extended (U+1F00-U+1FFF):
+
+ἀἁἂἃἄἅἆἇἈἉἊἋἌἍἎἏἐἑἒἓἔἕ἖἗ἘἙἚἛἜἝ἞἟ἠἡἢἣἤἥἦἧἨἩἪἫἬἭἮἯἰἱἲἳἴἵἶἷἸἹἺἻἼἽἾἿ
+ὀὁὂὃὄὅ὆὇ὈὉὊὋὌὍ὎὏ὐὑὒὓὔὕὖὗ὘Ὑ὚Ὓ὜Ὕ὞ὟὠὡὢὣὤὥὦὧὨὩὪὫὬὭὮὯὰάὲέὴήὶίὸόὺύὼώ὾὿
+ᾀᾁᾂᾃᾄᾅᾆᾇᾈᾉᾊᾋᾌᾍᾎᾏᾐᾑᾒᾓᾔᾕᾖᾗᾘᾙᾚᾛᾜᾝᾞᾟᾠᾡᾢᾣᾤᾥᾦᾧᾨᾩᾪᾫᾬᾭᾮᾯᾰᾱᾲᾳᾴ᾵ᾶᾷᾸᾹᾺΆᾼ᾽ι᾿
+῀῁ῂῃῄ῅ῆῇῈΈῊΉῌ῍῎῏ῐῑῒΐ῔῕ῖῗῘῙῚΊ῜῝῞῟ῠῡῢΰῤῥῦῧῨῩῪΎῬ῭΅`῰῱ῲῳῴ῵ῶῷῸΌῺΏῼ´῾῿
+
+General Punctuation (U+2000-U+206F):
+
+           ​‌‍‎‏‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧

‪‫‬‭‮ ‰‱′″‴‵‶‷‸‹›※‼‽‾‿
+⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁖⁗⁘⁙⁚⁛⁜⁝⁞ ⁠⁡⁢⁣⁤⁥⁦⁧⁨⁩
+
+Superscripts and Subscripts (U+2070-U+209F):
+
+⁰ⁱ⁲⁳⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎₏ₐₑₒₓₔₕₖₗₘₙₚₛₜ₝₞₟
+
+Currency Symbols (U+20A0-U+20CF):
+
+₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃀⃁⃂⃃⃄⃅⃆⃇⃈⃉⃊⃋⃌⃍⃎⃏
+
+Combining Diacritical Marks for Symbols (U+20D0-U+20FF):
+
+◌⃐◌⃑◌⃒◌⃓◌⃔◌⃕◌⃖◌⃗◌⃘◌⃙◌⃚◌⃛◌⃜ ⃝ ⃞ ⃟ ⃠◌⃡ ⃢ ⃣ ⃤◌⃥◌⃦◌⃧◌⃨◌⃩◌⃪⃫⃬⃭⃮⃯⃰⃱⃲⃳⃴⃵⃶⃷⃸⃹⃺⃻⃼⃽⃾⃿
+
+Letterlike Symbols (U+2100-U+214F):
+
+℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠℡™℣ℤ℥Ω℧ℨ℩KÅℬℭ℮ℯℰℱℲℳℴℵℶℷℸℹ℺℻ℼℽℾℿ
+⅀⅁⅂⅃⅄ⅅⅆⅇⅈⅉ⅊⅋⅌⅍ⅎ⅏
+
+Number Forms (U+2150-U+218F):
+
+⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↀↁↂↃↄↅↆↇↈ↉↊↋↌↍↎↏
+
+Arrows (U+2190-U+21FF):
+
+←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏
+⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿
+
+Mathematical Operators (U+2200-U+22FF):
+
+∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿
+≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿
+⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿
+⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⋲⋳⋴⋵⋶⋷⋸⋹⋺⋻⋼⋽⋾⋿
+
+Miscellaneous Technical (U+2300-U+23FF):
+
+⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌌⌍⌎⌏⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌜⌝⌞⌟⌠⌡⌢⌣⌤⌥⌦⌧⌨⟨⟩⌫⌬⌭⌮⌯⌰⌱⌲⌳⌴⌵⌶⌷⌸⌹⌺⌻⌼⌽
+⌾⌿⍀⍁⍂⍃⍄⍅⍆⍇⍈⍉⍊⍋⍌⍍⍎⍏⍐⍑⍒⍓⍔⍕⍖⍗⍘⍙⍚⍛⍜⍝⍞⍟⍠⍡⍢⍣⍤⍥⍦⍧⍨⍩⍪⍫⍬⍭⍮⍯⍰⍱⍲⍳⍴⍵⍶⍷⍸⍹⍺⍻⍼⍽
+⍾⍿⎀⎁⎂⎃⎄⎅⎆⎇⎈⎉⎊⎋⎌⎍⎎⎏⎐⎑⎒⎓⎔⎕⎖⎗⎘⎙⎚⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⎮⎯⎰⎱⎲⎳⎴⎵⎶⎷⎸⎹⎺⎻⎼⎽
+⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌⏍⏎⏏⏐⏑⏒⏓⏔⏕⏖⏗⏘⏙⏚⏛⏜⏝⏞⏟⏠⏡⏢⏣⏤⏥⏦⏧⏨⏩⏪⏫⏬⏭⏮⏯⏰⏱⏲⏳⏴⏵⏶⏷⏸⏹⏺⏻⏼⏽
+⏾⏿
+
+Control Pictures (U+2400-U+243F):
+
+␀␁␂␃␄␅␆␇␈␉␊␋␌␍␎␏␐␑␒␓␔␕␖␗␘␙␚␛␜␝␞␟␠␡␢␣␤␥␦␧␨␩␪␫␬␭␮␯␰␱␲␳␴␵␶␷␸␹␺␻␼␽␾␿
+
+Optical Character Recognition (U+2440-U+245F):
+
+⑀⑁⑂⑃⑄⑅⑆⑇⑈⑉⑊⑋⑌⑍⑎⑏⑐⑑⑒⑓⑔⑕⑖⑗⑘⑙⑚⑛⑜⑝⑞⑟
+
+Enclosed Alphanumerics (U+2460-U+24FF):
+
+①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛⒜⒝⒞⒟
+⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟ
+ⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾⓿
+
+Box Drawing (U+2500-U+257F):
+
+─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿
+╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿
+
+Block Elements (U+2580-U+259F):
+
+▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟
+
+Geometric Shapes (U+25A0-U+25FF):
+
+■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟
+◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿
+
+Miscellaneous Symbols (U+2600-U+26FF):
+
+☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿
+♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♰♱♲♳♴♵♶♷♸♹♺♻♼♽♾♿
+⚀⚁⚂⚃⚄⚅⚆⚇⚈⚉⚊⚋⚌⚍⚎⚏⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⚽⚾⚿
+⛀⛁⛂⛃⛄⛅⛆⛇⛈⛉⛊⛋⛌⛍⛎⛏⛐⛑⛒⛓⛔⛕⛖⛗⛘⛙⛚⛛⛜⛝⛞⛟⛠⛡⛢⛣⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿
+
+Dingbats (U+2700-U+27BF):
+
+✀✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿
+❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞❟❠❡❢❣❤❥❦❧❨❩❪❫❬❭❮❯❰❱❲❳❴❵❶❷❸❹❺❻❼❽❾❿
+➀➁➂➃➄➅➆➇➈➉➊➋➌➍➎➏➐➑➒➓➔➕➖➗➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➰➱➲➳➴➵➶➷➸➹➺➻➼➽➾➿
+
+Miscellaneous Mathematical Symbols-A (U+27C0-U+27EF):
+
+⟀⟁⟂⟃⟄⟅⟆⟇⟈⟉⟊⟋⟌⟍⟎⟏⟐⟑⟒⟓⟔⟕⟖⟗⟘⟙⟚⟛⟜⟝⟞⟟⟠⟡⟢⟣⟤⟥⟦⟧⟨⟩⟪⟫⟬⟭⟮⟯
+
+Supplemental Arrows-A (U+27F0-U+27FF):
+
+⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿
+
+Braille Patterns (U+2800-U+28FF):
+
+⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿
+⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿
+⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿
+⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿
+
+Supplemental Arrows-B (U+2900-U+297F):
+
+⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿
+⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⥒⥓⥔⥕⥖⥗⥘⥙⥚⥛⥜⥝⥞⥟⥠⥡⥢⥣⥤⥥⥦⥧⥨⥩⥪⥫⥬⥭⥮⥯⥰⥱⥲⥳⥴⥵⥶⥷⥸⥹⥺⥻⥼⥽⥾⥿
+
+Miscellaneous Mathematical Symbols-B (U+2980-U+29FF):
+
+⦀⦁⦂⦃⦄⦅⦆⦇⦈⦉⦊⦋⦌⦍⦎⦏⦐⦑⦒⦓⦔⦕⦖⦗⦘⦙⦚⦛⦜⦝⦞⦟⦠⦡⦢⦣⦤⦥⦦⦧⦨⦩⦪⦫⦬⦭⦮⦯⦰⦱⦲⦳⦴⦵⦶⦷⦸⦹⦺⦻⦼⦽⦾⦿
+⧀⧁⧂⧃⧄⧅⧆⧇⧈⧉⧊⧋⧌⧍⧎⧏⧐⧑⧒⧓⧔⧕⧖⧗⧘⧙⧚⧛⧜⧝⧞⧟⧠⧡⧢⧣⧤⧥⧦⧧⧨⧩⧪⧫⧬⧭⧮⧯⧰⧱⧲⧳⧴⧵⧶⧷⧸⧹⧺⧻⧼⧽⧾⧿
+
+Supplemental Mathematical Operators (U+2A00-U+2AFF):
+
+⨀⨁⨂⨃⨄⨅⨆⨇⨈⨉⨊⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜⨝⨞⨟⨠⨡⨢⨣⨤⨥⨦⨧⨨⨩⨪⨫⨬⨭⨮⨯⨰⨱⨲⨳⨴⨵⨶⨷⨸⨹⨺⨻⨼⨽⨾⨿
+⩀⩁⩂⩃⩄⩅⩆⩇⩈⩉⩊⩋⩌⩍⩎⩏⩐⩑⩒⩓⩔⩕⩖⩗⩘⩙⩚⩛⩜⩝⩞⩟⩠⩡⩢⩣⩤⩥⩦⩧⩨⩩⩪⩫⩬⩭⩮⩯⩰⩱⩲⩳⩴⩵⩶⩷⩸⩹⩺⩻⩼⩽⩾⩿
+⪀⪁⪂⪃⪄⪅⪆⪇⪈⪉⪊⪋⪌⪍⪎⪏⪐⪑⪒⪓⪔⪕⪖⪗⪘⪙⪚⪛⪜⪝⪞⪟⪠⪡⪢⪣⪤⪥⪦⪧⪨⪩⪪⪫⪬⪭⪮⪯⪰⪱⪲⪳⪴⪵⪶⪷⪸⪹⪺⪻⪼⪽⪾⪿
+⫀⫁⫂⫃⫄⫅⫆⫇⫈⫉⫊⫋⫌⫍⫎⫏⫐⫑⫒⫓⫔⫕⫖⫗⫘⫙⫚⫛⫝̸⫝⫞⫟⫠⫡⫢⫣⫤⫥⫦⫧⫨⫩⫪⫫⫬⫭⫮⫯⫰⫱⫲⫳⫴⫵⫶⫷⫸⫹⫺⫻⫼⫽⫾⫿
+
+Miscellaneous Symbols and Arrows (U+2B00-U+2BFF):
+
+⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚⬛⬜⬝⬞⬟⬠⬡⬢⬣⬤⬥⬦⬧⬨⬩⬪⬫⬬⬭⬮⬯⬰⬱⬲⬳⬴⬵⬶⬷⬸⬹⬺⬻⬼⬽⬾⬿
+⭀⭁⭂⭃⭄⭅⭆⭇⭈⭉⭊⭋⭌⭍⭎⭏⭐⭑⭒⭓⭔⭕⭖⭗⭘⭙⭚⭛⭜⭝⭞⭟⭠⭡⭢⭣⭤⭥⭦⭧⭨⭩⭪⭫⭬⭭⭮⭯⭰⭱⭲⭳⭴⭵⭶⭷⭸⭹⭺⭻⭼⭽⭾⭿
+⮀⮁⮂⮃⮄⮅⮆⮇⮈⮉⮊⮋⮌⮍⮎⮏⮐⮑⮒⮓⮔⮕⮖⮗⮘⮙⮚⮛⮜⮝⮞⮟⮠⮡⮢⮣⮤⮥⮦⮧⮨⮩⮪⮫⮬⮭⮮⮯⮰⮱⮲⮳⮴⮵⮶⮷⮸⮹⮺⮻⮼⮽⮾⮿
+⯀⯁⯂⯃⯄⯅⯆⯇⯈⯉⯊⯋⯌⯍⯎⯏⯐⯑⯒⯓⯔⯕⯖⯗⯘⯙⯚⯛⯜⯝⯞⯟⯠⯡⯢⯣⯤⯥⯦⯧⯨⯩⯪⯫⯬⯭⯮⯯⯰⯱⯲⯳⯴⯵⯶⯷⯸⯹⯺⯻⯼⯽⯾⯿
+
+Free block (U+2C00-U+2E7F):
+
+ⰀⰁⰂⰃⰄⰅⰆⰇⰈⰉⰊⰋⰌⰍⰎⰏⰐⰑⰒⰓⰔⰕⰖⰗⰘⰙⰚⰛⰜⰝⰞⰟⰠⰡⰢⰣⰤⰥⰦⰧⰨⰩⰪⰫⰬⰭⰮⰯⰰⰱⰲⰳⰴⰵⰶⰷⰸⰹⰺⰻⰼⰽⰾⰿ
+ⱀⱁⱂⱃⱄⱅⱆⱇⱈⱉⱊⱋⱌⱍⱎⱏⱐⱑⱒⱓⱔⱕⱖⱗⱘⱙⱚⱛⱜⱝⱞⱟⱠⱡⱢⱣⱤⱥⱦⱧⱨⱩⱪⱫⱬⱭⱮⱯⱰⱱⱲⱳⱴⱵⱶⱷⱸⱹⱺⱻⱼⱽⱾⱿ
+ⲀⲁⲂⲃⲄⲅⲆⲇⲈⲉⲊⲋⲌⲍⲎⲏⲐⲑⲒⲓⲔⲕⲖⲗⲘⲙⲚⲛⲜⲝⲞⲟⲠⲡⲢⲣⲤⲥⲦⲧⲨⲩⲪⲫⲬⲭⲮⲯⲰⲱⲲⲳⲴⲵⲶⲷⲸⲹⲺⲻⲼⲽⲾⲿ
+ⳀⳁⳂⳃⳄⳅⳆⳇⳈⳉⳊⳋⳌⳍⳎⳏⳐⳑⳒⳓⳔⳕⳖⳗⳘⳙⳚⳛⳜⳝⳞⳟⳠⳡⳢⳣⳤ⳥⳦⳧⳨⳩⳪ⳫⳬⳭⳮ⳯⳰⳱Ⳳⳳ⳴⳵⳶⳷⳸⳹⳺⳻⳼⳽⳾⳿
+ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥ⴦ⴧ⴨⴩⴪⴫⴬ⴭ⴮⴯ⴰⴱⴲⴳⴴⴵⴶⴷⴸⴹⴺⴻⴼⴽⴾⴿ
+ⵀⵁⵂⵃⵄⵅⵆⵇⵈⵉⵊⵋⵌⵍⵎⵏⵐⵑⵒⵓⵔⵕⵖⵗⵘⵙⵚⵛⵜⵝⵞⵟⵠⵡⵢⵣⵤⵥⵦⵧ⵨⵩⵪⵫⵬⵭⵮ⵯ⵰⵱⵲⵳⵴⵵⵶⵷⵸⵹⵺⵻⵼⵽⵾⵿
+ⶀⶁⶂⶃⶄⶅⶆⶇⶈⶉⶊⶋⶌⶍⶎⶏⶐⶑⶒⶓⶔⶕⶖ⶗⶘⶙⶚⶛⶜⶝⶞⶟ⶠⶡⶢⶣⶤⶥⶦ⶧ⶨⶩⶪⶫⶬⶭⶮ⶯ⶰⶱⶲⶳⶴⶵⶶ⶷ⶸⶹⶺⶻⶼⶽⶾ⶿
+ⷀⷁⷂⷃⷄⷅⷆ⷇ⷈⷉⷊⷋⷌⷍⷎ⷏ⷐⷑⷒⷓⷔⷕⷖ⷗ⷘⷙⷚⷛⷜⷝⷞ⷟ⷠⷡⷢⷣⷤⷥⷦⷧⷨⷩⷪⷫⷬⷭⷮⷯⷰⷱⷲⷳⷴⷵⷶⷷⷸⷹⷺⷻⷼⷽⷾⷿ
+⸀⸁⸂⸃⸄⸅⸆⸇⸈⸉⸊⸋⸌⸍⸎⸏⸐⸑⸒⸓⸔⸕⸖⸗⸘⸙⸚⸛⸜⸝⸞⸟⸠⸡⸢⸣⸤⸥⸦⸧⸨⸩⸪⸫⸬⸭⸮ⸯ⸰⸱⸲⸳⸴⸵⸶⸷⸸⸹⸺⸻⸼⸽⸾⸿
+⹀⹁⹂⹃⹄⹅⹆⹇⹈⹉⹊⹋⹌⹍⹎⹏⹐⹑⹒⹓⹔⹕⹖⹗⹘⹙⹚⹛⹜⹝⹞⹟⹠⹡⹢⹣⹤⹥⹦⹧⹨⹩⹪⹫⹬⹭⹮⹯⹰⹱⹲⹳⹴⹵⹶⹷⹸⹹⹺⹻⹼⹽⹾⹿
+
+CJK Radicals Supplement (U+2E80-U+2EFF):
+
+⺀⺁⺂⺃⺄⺅⺆⺇⺈⺉⺊⺋⺌⺍⺎⺏⺐⺑⺒⺓⺔⺕⺖⺗⺘⺙⺚⺛⺜⺝⺞⺟
+⺠⺡⺢⺣⺤⺥⺦⺧⺨⺩⺪⺫⺬⺭⺮⺯⺰⺱⺲⺳⺴⺵⺶⺷⺸⺹⺺⺻⺼⺽⺾⺿
+⻀⻁⻂⻃⻄⻅⻆⻇⻈⻉⻊⻋⻌⻍⻎⻏⻐⻑⻒⻓⻔⻕⻖⻗⻘⻙⻚⻛⻜⻝⻞⻟
+⻠⻡⻢⻣⻤⻥⻦⻧⻨⻩⻪⻫⻬⻭⻮⻯⻰⻱⻲⻳⻴⻵⻶⻷⻸⻹⻺⻻⻼⻽⻾⻿
+
+Kangxi Radicals (U+2F00-U+2FDF):
+
+⼀⼁⼂⼃⼄⼅⼆⼇⼈⼉⼊⼋⼌⼍⼎⼏⼐⼑⼒⼓⼔⼕⼖⼗⼘⼙⼚⼛⼜⼝⼞⼟
+⼠⼡⼢⼣⼤⼥⼦⼧⼨⼩⼪⼫⼬⼭⼮⼯⼰⼱⼲⼳⼴⼵⼶⼷⼸⼹⼺⼻⼼⼽⼾⼿
+⽀⽁⽂⽃⽄⽅⽆⽇⽈⽉⽊⽋⽌⽍⽎⽏⽐⽑⽒⽓⽔⽕⽖⽗⽘⽙⽚⽛⽜⽝⽞⽟
+⽠⽡⽢⽣⽤⽥⽦⽧⽨⽩⽪⽫⽬⽭⽮⽯⽰⽱⽲⽳⽴⽵⽶⽷⽸⽹⽺⽻⽼⽽⽾⽿
+⾀⾁⾂⾃⾄⾅⾆⾇⾈⾉⾊⾋⾌⾍⾎⾏⾐⾑⾒⾓⾔⾕⾖⾗⾘⾙⾚⾛⾜⾝⾞⾟
+⾠⾡⾢⾣⾤⾥⾦⾧⾨⾩⾪⾫⾬⾭⾮⾯⾰⾱⾲⾳⾴⾵⾶⾷⾸⾹⾺⾻⾼⾽⾾⾿
+⿀⿁⿂⿃⿄⿅⿆⿇⿈⿉⿊⿋⿌⿍⿎⿏⿐⿑⿒⿓⿔⿕⿖⿗⿘⿙⿚⿛⿜⿝⿞⿟
+
+Free block (U+2FE0-U+2FEF):
+
+⿠⿡⿢⿣⿤⿥⿦⿧⿨⿩⿪⿫⿬⿭⿮⿯
+
+Ideographic Description Characters (U+2FF0-U+2FFF):
+
+⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻⿼⿽⿾⿿
+
+CJK Symbols and Punctuation (U+3000-U+303F):
+
+ 、。〃〄々〆〇〈〉《》「」『』【】〒〓〔〕〖〗〘〙〚〛〜〝〞〟
+〠〡〢〣〤〥〦〧〨〩◌〪◌〫◌〬◌〭◌〮◌〯〰〱〲〳〴〵〶〷〸〹〺〻〼〽〾〿
+
+Hiragana (U+3040-U+309F):
+
+぀ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞた
+だちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみ
+むめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ゗゘◌゙◌゚゛゜ゝゞゟ
+
+Katakana (U+30A0-U+30FF):
+
+゠ァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタ
+ダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミ
+ムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶヷヸヹヺ・ーヽヾヿ
+
+Bopomofo (U+3100-U+312F):
+
+㄀㄁㄂㄃㄄ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄚㄛㄜㄝㄞㄟ
+ㄠㄡㄢㄣㄤㄥㄦㄧㄨㄩㄪㄫㄬㄭㄮㄯ
+
+Hangul Compatibility Jamo (U+3130-U+318F):
+
+㄰ㄱㄲㄳㄴㄵㄶㄷㄸㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅃㅄㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎㅏ
+ㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣㅤㅥㅦㅧㅨㅩㅪㅫㅬㅭㅮㅯ
+ㅰㅱㅲㅳㅴㅵㅶㅷㅸㅹㅺㅻㅼㅽㅾㅿㆀㆁㆂㆃㆄㆅㆆㆇㆈㆉㆊㆋㆌㆍㆎ㆏
+
+Kanbun (U+3190-U+319F):
+
+㆐㆑㆒㆓㆔㆕㆖㆗㆘㆙㆚㆛㆜㆝㆞㆟
+
+Bopomofo Extended (U+31A0-U+31BF):
+
+ㆠㆡㆢㆣㆤㆥㆦㆧㆨㆩㆪㆫㆬㆭㆮㆯㆰㆱㆲㆳㆴㆵㆶㆷㆸㆹㆺㆻㆼㆽㆾㆿ
+
+Free block (U+31C0-U+31EF):
+
+㇀㇁㇂㇃㇄㇅㇆㇇㇈㇉㇊㇋㇌㇍㇎㇏㇐㇑㇒㇓㇔㇕㇖㇗㇘㇙㇚㇛㇜㇝㇞㇟
+㇠㇡㇢㇣㇤㇥㇦㇧㇨㇩㇪㇫㇬㇭㇮㇯
+
+Katakana Phonetic Extensions (U+31F0-U+31FF):
+
+ㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ
+
+Enclosed CJK Letters and Months (U+3200-U+32FF):
+
+㈀㈁㈂㈃㈄㈅㈆㈇㈈㈉㈊㈋㈌㈍㈎㈏㈐㈑㈒㈓㈔㈕㈖㈗㈘㈙㈚㈛㈜㈝㈞㈟
+㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩㈪㈫㈬㈭㈮㈯㈰㈱㈲㈳㈴㈵㈶㈷㈸㈹㈺㈻㈼㈽㈾㈿
+㉀㉁㉂㉃㉄㉅㉆㉇㉈㉉㉊㉋㉌㉍㉎㉏㉐㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟
+㉠㉡㉢㉣㉤㉥㉦㉧㉨㉩㉪㉫㉬㉭㉮㉯㉰㉱㉲㉳㉴㉵㉶㉷㉸㉹㉺㉻㉼㉽㉾㉿
+㊀㊁㊂㊃㊄㊅㊆㊇㊈㊉㊊㊋㊌㊍㊎㊏㊐㊑㊒㊓㊔㊕㊖㊗㊘㊙㊚㊛㊜㊝㊞㊟
+㊠㊡㊢㊣㊤㊥㊦㊧㊨㊩㊪㊫㊬㊭㊮㊯㊰㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿
+㋀㋁㋂㋃㋄㋅㋆㋇㋈㋉㋊㋋㋌㋍㋎㋏㋐㋑㋒㋓㋔㋕㋖㋗㋘㋙㋚㋛㋜㋝㋞㋟
+㋠㋡㋢㋣㋤㋥㋦㋧㋨㋩㋪㋫㋬㋭㋮㋯㋰㋱㋲㋳㋴㋵㋶㋷㋸㋹㋺㋻㋼㋽㋾㋿
+
+CJK Compatibility (U+3300-U+33FF):
+
+㌀㌁㌂㌃㌄㌅㌆㌇㌈㌉㌊㌋㌌㌍㌎㌏㌐㌑㌒㌓㌔㌕㌖㌗㌘㌙㌚㌛㌜㌝㌞㌟
+㌠㌡㌢㌣㌤㌥㌦㌧㌨㌩㌪㌫㌬㌭㌮㌯㌰㌱㌲㌳㌴㌵㌶㌷㌸㌹㌺㌻㌼㌽㌾㌿
+㍀㍁㍂㍃㍄㍅㍆㍇㍈㍉㍊㍋㍌㍍㍎㍏㍐㍑㍒㍓㍔㍕㍖㍗㍘㍙㍚㍛㍜㍝㍞㍟
+㍠㍡㍢㍣㍤㍥㍦㍧㍨㍩㍪㍫㍬㍭㍮㍯㍰㍱㍲㍳㍴㍵㍶㍷㍸㍹㍺㍻㍼㍽㍾㍿
+㎀㎁㎂㎃㎄㎅㎆㎇㎈㎉㎊㎋㎌㎍㎎㎏㎐㎑㎒㎓㎔㎕㎖㎗㎘㎙㎚㎛㎜㎝㎞㎟
+㎠㎡㎢㎣㎤㎥㎦㎧㎨㎩㎪㎫㎬㎭㎮㎯㎰㎱㎲㎳㎴㎵㎶㎷㎸㎹㎺㎻㎼㎽㎾㎿
+㏀㏁㏂㏃㏄㏅㏆㏇㏈㏉㏊㏋㏌㏍㏎㏏㏐㏑㏒㏓㏔㏕㏖㏗㏘㏙㏚㏛㏜㏝㏞㏟
+㏠㏡㏢㏣㏤㏥㏦㏧㏨㏩㏪㏫㏬㏭㏮㏯㏰㏱㏲㏳㏴㏵㏶㏷㏸㏹㏺㏻㏼㏽㏾㏿
+
+CJK Unified Ideographs Extension A (U+3400-U+4DBF):
+
+㐀㐁㐂㐃㐄㐅㐆㐇㐈㐉㐊㐋㐌㐍㐎㐏㐐㐑㐒㐓㐔㐕㐖㐗㐘㐙㐚㐛㐜㐝㐞㐟
+㐠㐡㐢㐣㐤㐥㐦㐧㐨㐩㐪㐫㐬㐭㐮㐯㐰㐱㐲㐳㐴㐵㐶㐷㐸㐹㐺㐻㐼㐽㐾㐿
+㑀㑁㑂㑃㑄㑅㑆㑇㑈㑉㑊㑋㑌㑍㑎㑏㑐㑑㑒㑓㑔㑕㑖㑗㑘㑙㑚㑛㑜㑝㑞㑟
+㑠㑡㑢㑣㑤㑥㑦㑧㑨㑩㑪㑫㑬㑭㑮㑯㑰㑱㑲㑳㑴㑵㑶㑷㑸㑹㑺㑻㑼㑽㑾㑿
+㒀㒁㒂㒃㒄㒅㒆㒇㒈㒉㒊㒋㒌㒍㒎㒏㒐㒑㒒㒓㒔㒕㒖㒗㒘㒙㒚㒛㒜㒝㒞㒟
+㒠㒡㒢㒣㒤㒥㒦㒧㒨㒩㒪㒫㒬㒭㒮㒯㒰㒱㒲㒳㒴㒵㒶㒷㒸㒹㒺㒻㒼㒽㒾㒿
+㓀㓁㓂㓃㓄㓅㓆㓇㓈㓉㓊㓋㓌㓍㓎㓏㓐㓑㓒㓓㓔㓕㓖㓗㓘㓙㓚㓛㓜㓝㓞㓟
+㓠㓡㓢㓣㓤㓥㓦㓧㓨㓩㓪㓫㓬㓭㓮㓯㓰㓱㓲㓳㓴㓵㓶㓷㓸㓹㓺㓻㓼㓽㓾㓿
+㔀㔁㔂㔃㔄㔅㔆㔇㔈㔉㔊㔋㔌㔍㔎㔏㔐㔑㔒㔓㔔㔕㔖㔗㔘㔙㔚㔛㔜㔝㔞㔟
+㔠㔡㔢㔣㔤㔥㔦㔧㔨㔩㔪㔫㔬㔭㔮㔯㔰㔱㔲㔳㔴㔵㔶㔷㔸㔹㔺㔻㔼㔽㔾㔿
+㕀㕁㕂㕃㕄㕅㕆㕇㕈㕉㕊㕋㕌㕍㕎㕏㕐㕑㕒㕓㕔㕕㕖㕗㕘㕙㕚㕛㕜㕝㕞㕟
+㕠㕡㕢㕣㕤㕥㕦㕧㕨㕩㕪㕫㕬㕭㕮㕯㕰㕱㕲㕳㕴㕵㕶㕷㕸㕹㕺㕻㕼㕽㕾㕿
+㖀㖁㖂㖃㖄㖅㖆㖇㖈㖉㖊㖋㖌㖍㖎㖏㖐㖑㖒㖓㖔㖕㖖㖗㖘㖙㖚㖛㖜㖝㖞㖟
+㖠㖡㖢㖣㖤㖥㖦㖧㖨㖩㖪㖫㖬㖭㖮㖯㖰㖱㖲㖳㖴㖵㖶㖷㖸㖹㖺㖻㖼㖽㖾㖿
+㗀㗁㗂㗃㗄㗅㗆㗇㗈㗉㗊㗋㗌㗍㗎㗏㗐㗑㗒㗓㗔㗕㗖㗗㗘㗙㗚㗛㗜㗝㗞㗟
+㗠㗡㗢㗣㗤㗥㗦㗧㗨㗩㗪㗫㗬㗭㗮㗯㗰㗱㗲㗳㗴㗵㗶㗷㗸㗹㗺㗻㗼㗽㗾㗿
+㘀㘁㘂㘃㘄㘅㘆㘇㘈㘉㘊㘋㘌㘍㘎㘏㘐㘑㘒㘓㘔㘕㘖㘗㘘㘙㘚㘛㘜㘝㘞㘟
+㘠㘡㘢㘣㘤㘥㘦㘧㘨㘩㘪㘫㘬㘭㘮㘯㘰㘱㘲㘳㘴㘵㘶㘷㘸㘹㘺㘻㘼㘽㘾㘿
+㙀㙁㙂㙃㙄㙅㙆㙇㙈㙉㙊㙋㙌㙍㙎㙏㙐㙑㙒㙓㙔㙕㙖㙗㙘㙙㙚㙛㙜㙝㙞㙟
+㙠㙡㙢㙣㙤㙥㙦㙧㙨㙩㙪㙫㙬㙭㙮㙯㙰㙱㙲㙳㙴㙵㙶㙷㙸㙹㙺㙻㙼㙽㙾㙿
+㚀㚁㚂㚃㚄㚅㚆㚇㚈㚉㚊㚋㚌㚍㚎㚏㚐㚑㚒㚓㚔㚕㚖㚗㚘㚙㚚㚛㚜㚝㚞㚟
+㚠㚡㚢㚣㚤㚥㚦㚧㚨㚩㚪㚫㚬㚭㚮㚯㚰㚱㚲㚳㚴㚵㚶㚷㚸㚹㚺㚻㚼㚽㚾㚿
+㛀㛁㛂㛃㛄㛅㛆㛇㛈㛉㛊㛋㛌㛍㛎㛏㛐㛑㛒㛓㛔㛕㛖㛗㛘㛙㛚㛛㛜㛝㛞㛟
+㛠㛡㛢㛣㛤㛥㛦㛧㛨㛩㛪㛫㛬㛭㛮㛯㛰㛱㛲㛳㛴㛵㛶㛷㛸㛹㛺㛻㛼㛽㛾㛿
+㜀㜁㜂㜃㜄㜅㜆㜇㜈㜉㜊㜋㜌㜍㜎㜏㜐㜑㜒㜓㜔㜕㜖㜗㜘㜙㜚㜛㜜㜝㜞㜟
+㜠㜡㜢㜣㜤㜥㜦㜧㜨㜩㜪㜫㜬㜭㜮㜯㜰㜱㜲㜳㜴㜵㜶㜷㜸㜹㜺㜻㜼㜽㜾㜿
+㝀㝁㝂㝃㝄㝅㝆㝇㝈㝉㝊㝋㝌㝍㝎㝏㝐㝑㝒㝓㝔㝕㝖㝗㝘㝙㝚㝛㝜㝝㝞㝟
+㝠㝡㝢㝣㝤㝥㝦㝧㝨㝩㝪㝫㝬㝭㝮㝯㝰㝱㝲㝳㝴㝵㝶㝷㝸㝹㝺㝻㝼㝽㝾㝿
+㞀㞁㞂㞃㞄㞅㞆㞇㞈㞉㞊㞋㞌㞍㞎㞏㞐㞑㞒㞓㞔㞕㞖㞗㞘㞙㞚㞛㞜㞝㞞㞟
+㞠㞡㞢㞣㞤㞥㞦㞧㞨㞩㞪㞫㞬㞭㞮㞯㞰㞱㞲㞳㞴㞵㞶㞷㞸㞹㞺㞻㞼㞽㞾㞿
+㟀㟁㟂㟃㟄㟅㟆㟇㟈㟉㟊㟋㟌㟍㟎㟏㟐㟑㟒㟓㟔㟕㟖㟗㟘㟙㟚㟛㟜㟝㟞㟟
+㟠㟡㟢㟣㟤㟥㟦㟧㟨㟩㟪㟫㟬㟭㟮㟯㟰㟱㟲㟳㟴㟵㟶㟷㟸㟹㟺㟻㟼㟽㟾㟿
+㠀㠁㠂㠃㠄㠅㠆㠇㠈㠉㠊㠋㠌㠍㠎㠏㠐㠑㠒㠓㠔㠕㠖㠗㠘㠙㠚㠛㠜㠝㠞㠟
+㠠㠡㠢㠣㠤㠥㠦㠧㠨㠩㠪㠫㠬㠭㠮㠯㠰㠱㠲㠳㠴㠵㠶㠷㠸㠹㠺㠻㠼㠽㠾㠿
+㡀㡁㡂㡃㡄㡅㡆㡇㡈㡉㡊㡋㡌㡍㡎㡏㡐㡑㡒㡓㡔㡕㡖㡗㡘㡙㡚㡛㡜㡝㡞㡟
+㡠㡡㡢㡣㡤㡥㡦㡧㡨㡩㡪㡫㡬㡭㡮㡯㡰㡱㡲㡳㡴㡵㡶㡷㡸㡹㡺㡻㡼㡽㡾㡿
+㢀㢁㢂㢃㢄㢅㢆㢇㢈㢉㢊㢋㢌㢍㢎㢏㢐㢑㢒㢓㢔㢕㢖㢗㢘㢙㢚㢛㢜㢝㢞㢟
+㢠㢡㢢㢣㢤㢥㢦㢧㢨㢩㢪㢫㢬㢭㢮㢯㢰㢱㢲㢳㢴㢵㢶㢷㢸㢹㢺㢻㢼㢽㢾㢿
+㣀㣁㣂㣃㣄㣅㣆㣇㣈㣉㣊㣋㣌㣍㣎㣏㣐㣑㣒㣓㣔㣕㣖㣗㣘㣙㣚㣛㣜㣝㣞㣟
+㣠㣡㣢㣣㣤㣥㣦㣧㣨㣩㣪㣫㣬㣭㣮㣯㣰㣱㣲㣳㣴㣵㣶㣷㣸㣹㣺㣻㣼㣽㣾㣿
+㤀㤁㤂㤃㤄㤅㤆㤇㤈㤉㤊㤋㤌㤍㤎㤏㤐㤑㤒㤓㤔㤕㤖㤗㤘㤙㤚㤛㤜㤝㤞㤟
+㤠㤡㤢㤣㤤㤥㤦㤧㤨㤩㤪㤫㤬㤭㤮㤯㤰㤱㤲㤳㤴㤵㤶㤷㤸㤹㤺㤻㤼㤽㤾㤿
+㥀㥁㥂㥃㥄㥅㥆㥇㥈㥉㥊㥋㥌㥍㥎㥏㥐㥑㥒㥓㥔㥕㥖㥗㥘㥙㥚㥛㥜㥝㥞㥟
+㥠㥡㥢㥣㥤㥥㥦㥧㥨㥩㥪㥫㥬㥭㥮㥯㥰㥱㥲㥳㥴㥵㥶㥷㥸㥹㥺㥻㥼㥽㥾㥿
+㦀㦁㦂㦃㦄㦅㦆㦇㦈㦉㦊㦋㦌㦍㦎㦏㦐㦑㦒㦓㦔㦕㦖㦗㦘㦙㦚㦛㦜㦝㦞㦟
+㦠㦡㦢㦣㦤㦥㦦㦧㦨㦩㦪㦫㦬㦭㦮㦯㦰㦱㦲㦳㦴㦵㦶㦷㦸㦹㦺㦻㦼㦽㦾㦿
+㧀㧁㧂㧃㧄㧅㧆㧇㧈㧉㧊㧋㧌㧍㧎㧏㧐㧑㧒㧓㧔㧕㧖㧗㧘㧙㧚㧛㧜㧝㧞㧟
+㧠㧡㧢㧣㧤㧥㧦㧧㧨㧩㧪㧫㧬㧭㧮㧯㧰㧱㧲㧳㧴㧵㧶㧷㧸㧹㧺㧻㧼㧽㧾㧿
+㨀㨁㨂㨃㨄㨅㨆㨇㨈㨉㨊㨋㨌㨍㨎㨏㨐㨑㨒㨓㨔㨕㨖㨗㨘㨙㨚㨛㨜㨝㨞㨟
+㨠㨡㨢㨣㨤㨥㨦㨧㨨㨩㨪㨫㨬㨭㨮㨯㨰㨱㨲㨳㨴㨵㨶㨷㨸㨹㨺㨻㨼㨽㨾㨿
+㩀㩁㩂㩃㩄㩅㩆㩇㩈㩉㩊㩋㩌㩍㩎㩏㩐㩑㩒㩓㩔㩕㩖㩗㩘㩙㩚㩛㩜㩝㩞㩟
+㩠㩡㩢㩣㩤㩥㩦㩧㩨㩩㩪㩫㩬㩭㩮㩯㩰㩱㩲㩳㩴㩵㩶㩷㩸㩹㩺㩻㩼㩽㩾㩿
+㪀㪁㪂㪃㪄㪅㪆㪇㪈㪉㪊㪋㪌㪍㪎㪏㪐㪑㪒㪓㪔㪕㪖㪗㪘㪙㪚㪛㪜㪝㪞㪟
+㪠㪡㪢㪣㪤㪥㪦㪧㪨㪩㪪㪫㪬㪭㪮㪯㪰㪱㪲㪳㪴㪵㪶㪷㪸㪹㪺㪻㪼㪽㪾㪿
+㫀㫁㫂㫃㫄㫅㫆㫇㫈㫉㫊㫋㫌㫍㫎㫏㫐㫑㫒㫓㫔㫕㫖㫗㫘㫙㫚㫛㫜㫝㫞㫟
+㫠㫡㫢㫣㫤㫥㫦㫧㫨㫩㫪㫫㫬㫭㫮㫯㫰㫱㫲㫳㫴㫵㫶㫷㫸㫹㫺㫻㫼㫽㫾㫿
+㬀㬁㬂㬃㬄㬅㬆㬇㬈㬉㬊㬋㬌㬍㬎㬏㬐㬑㬒㬓㬔㬕㬖㬗㬘㬙㬚㬛㬜㬝㬞㬟
+㬠㬡㬢㬣㬤㬥㬦㬧㬨㬩㬪㬫㬬㬭㬮㬯㬰㬱㬲㬳㬴㬵㬶㬷㬸㬹㬺㬻㬼㬽㬾㬿
+㭀㭁㭂㭃㭄㭅㭆㭇㭈㭉㭊㭋㭌㭍㭎㭏㭐㭑㭒㭓㭔㭕㭖㭗㭘㭙㭚㭛㭜㭝㭞㭟
+㭠㭡㭢㭣㭤㭥㭦㭧㭨㭩㭪㭫㭬㭭㭮㭯㭰㭱㭲㭳㭴㭵㭶㭷㭸㭹㭺㭻㭼㭽㭾㭿
+㮀㮁㮂㮃㮄㮅㮆㮇㮈㮉㮊㮋㮌㮍㮎㮏㮐㮑㮒㮓㮔㮕㮖㮗㮘㮙㮚㮛㮜㮝㮞㮟
+㮠㮡㮢㮣㮤㮥㮦㮧㮨㮩㮪㮫㮬㮭㮮㮯㮰㮱㮲㮳㮴㮵㮶㮷㮸㮹㮺㮻㮼㮽㮾㮿
+㯀㯁㯂㯃㯄㯅㯆㯇㯈㯉㯊㯋㯌㯍㯎㯏㯐㯑㯒㯓㯔㯕㯖㯗㯘㯙㯚㯛㯜㯝㯞㯟
+㯠㯡㯢㯣㯤㯥㯦㯧㯨㯩㯪㯫㯬㯭㯮㯯㯰㯱㯲㯳㯴㯵㯶㯷㯸㯹㯺㯻㯼㯽㯾㯿
+㰀㰁㰂㰃㰄㰅㰆㰇㰈㰉㰊㰋㰌㰍㰎㰏㰐㰑㰒㰓㰔㰕㰖㰗㰘㰙㰚㰛㰜㰝㰞㰟
+㰠㰡㰢㰣㰤㰥㰦㰧㰨㰩㰪㰫㰬㰭㰮㰯㰰㰱㰲㰳㰴㰵㰶㰷㰸㰹㰺㰻㰼㰽㰾㰿
+㱀㱁㱂㱃㱄㱅㱆㱇㱈㱉㱊㱋㱌㱍㱎㱏㱐㱑㱒㱓㱔㱕㱖㱗㱘㱙㱚㱛㱜㱝㱞㱟
+㱠㱡㱢㱣㱤㱥㱦㱧㱨㱩㱪㱫㱬㱭㱮㱯㱰㱱㱲㱳㱴㱵㱶㱷㱸㱹㱺㱻㱼㱽㱾㱿
+㲀㲁㲂㲃㲄㲅㲆㲇㲈㲉㲊㲋㲌㲍㲎㲏㲐㲑㲒㲓㲔㲕㲖㲗㲘㲙㲚㲛㲜㲝㲞㲟
+㲠㲡㲢㲣㲤㲥㲦㲧㲨㲩㲪㲫㲬㲭㲮㲯㲰㲱㲲㲳㲴㲵㲶㲷㲸㲹㲺㲻㲼㲽㲾㲿
+㳀㳁㳂㳃㳄㳅㳆㳇㳈㳉㳊㳋㳌㳍㳎㳏㳐㳑㳒㳓㳔㳕㳖㳗㳘㳙㳚㳛㳜㳝㳞㳟
+㳠㳡㳢㳣㳤㳥㳦㳧㳨㳩㳪㳫㳬㳭㳮㳯㳰㳱㳲㳳㳴㳵㳶㳷㳸㳹㳺㳻㳼㳽㳾㳿
+㴀㴁㴂㴃㴄㴅㴆㴇㴈㴉㴊㴋㴌㴍㴎㴏㴐㴑㴒㴓㴔㴕㴖㴗㴘㴙㴚㴛㴜㴝㴞㴟
+㴠㴡㴢㴣㴤㴥㴦㴧㴨㴩㴪㴫㴬㴭㴮㴯㴰㴱㴲㴳㴴㴵㴶㴷㴸㴹㴺㴻㴼㴽㴾㴿
+㵀㵁㵂㵃㵄㵅㵆㵇㵈㵉㵊㵋㵌㵍㵎㵏㵐㵑㵒㵓㵔㵕㵖㵗㵘㵙㵚㵛㵜㵝㵞㵟
+㵠㵡㵢㵣㵤㵥㵦㵧㵨㵩㵪㵫㵬㵭㵮㵯㵰㵱㵲㵳㵴㵵㵶㵷㵸㵹㵺㵻㵼㵽㵾㵿
+㶀㶁㶂㶃㶄㶅㶆㶇㶈㶉㶊㶋㶌㶍㶎㶏㶐㶑㶒㶓㶔㶕㶖㶗㶘㶙㶚㶛㶜㶝㶞㶟
+㶠㶡㶢㶣㶤㶥㶦㶧㶨㶩㶪㶫㶬㶭㶮㶯㶰㶱㶲㶳㶴㶵㶶㶷㶸㶹㶺㶻㶼㶽㶾㶿
+㷀㷁㷂㷃㷄㷅㷆㷇㷈㷉㷊㷋㷌㷍㷎㷏㷐㷑㷒㷓㷔㷕㷖㷗㷘㷙㷚㷛㷜㷝㷞㷟
+㷠㷡㷢㷣㷤㷥㷦㷧㷨㷩㷪㷫㷬㷭㷮㷯㷰㷱㷲㷳㷴㷵㷶㷷㷸㷹㷺㷻㷼㷽㷾㷿
+㸀㸁㸂㸃㸄㸅㸆㸇㸈㸉㸊㸋㸌㸍㸎㸏㸐㸑㸒㸓㸔㸕㸖㸗㸘㸙㸚㸛㸜㸝㸞㸟
+㸠㸡㸢㸣㸤㸥㸦㸧㸨㸩㸪㸫㸬㸭㸮㸯㸰㸱㸲㸳㸴㸵㸶㸷㸸㸹㸺㸻㸼㸽㸾㸿
+㹀㹁㹂㹃㹄㹅㹆㹇㹈㹉㹊㹋㹌㹍㹎㹏㹐㹑㹒㹓㹔㹕㹖㹗㹘㹙㹚㹛㹜㹝㹞㹟
+㹠㹡㹢㹣㹤㹥㹦㹧㹨㹩㹪㹫㹬㹭㹮㹯㹰㹱㹲㹳㹴㹵㹶㹷㹸㹹㹺㹻㹼㹽㹾㹿
+㺀㺁㺂㺃㺄㺅㺆㺇㺈㺉㺊㺋㺌㺍㺎㺏㺐㺑㺒㺓㺔㺕㺖㺗㺘㺙㺚㺛㺜㺝㺞㺟
+㺠㺡㺢㺣㺤㺥㺦㺧㺨㺩㺪㺫㺬㺭㺮㺯㺰㺱㺲㺳㺴㺵㺶㺷㺸㺹㺺㺻㺼㺽㺾㺿
+㻀㻁㻂㻃㻄㻅㻆㻇㻈㻉㻊㻋㻌㻍㻎㻏㻐㻑㻒㻓㻔㻕㻖㻗㻘㻙㻚㻛㻜㻝㻞㻟
+㻠㻡㻢㻣㻤㻥㻦㻧㻨㻩㻪㻫㻬㻭㻮㻯㻰㻱㻲㻳㻴㻵㻶㻷㻸㻹㻺㻻㻼㻽㻾㻿
+㼀㼁㼂㼃㼄㼅㼆㼇㼈㼉㼊㼋㼌㼍㼎㼏㼐㼑㼒㼓㼔㼕㼖㼗㼘㼙㼚㼛㼜㼝㼞㼟
+㼠㼡㼢㼣㼤㼥㼦㼧㼨㼩㼪㼫㼬㼭㼮㼯㼰㼱㼲㼳㼴㼵㼶㼷㼸㼹㼺㼻㼼㼽㼾㼿
+㽀㽁㽂㽃㽄㽅㽆㽇㽈㽉㽊㽋㽌㽍㽎㽏㽐㽑㽒㽓㽔㽕㽖㽗㽘㽙㽚㽛㽜㽝㽞㽟
+㽠㽡㽢㽣㽤㽥㽦㽧㽨㽩㽪㽫㽬㽭㽮㽯㽰㽱㽲㽳㽴㽵㽶㽷㽸㽹㽺㽻㽼㽽㽾㽿
+㾀㾁㾂㾃㾄㾅㾆㾇㾈㾉㾊㾋㾌㾍㾎㾏㾐㾑㾒㾓㾔㾕㾖㾗㾘㾙㾚㾛㾜㾝㾞㾟
+㾠㾡㾢㾣㾤㾥㾦㾧㾨㾩㾪㾫㾬㾭㾮㾯㾰㾱㾲㾳㾴㾵㾶㾷㾸㾹㾺㾻㾼㾽㾾㾿
+㿀㿁㿂㿃㿄㿅㿆㿇㿈㿉㿊㿋㿌㿍㿎㿏㿐㿑㿒㿓㿔㿕㿖㿗㿘㿙㿚㿛㿜㿝㿞㿟
+㿠㿡㿢㿣㿤㿥㿦㿧㿨㿩㿪㿫㿬㿭㿮㿯㿰㿱㿲㿳㿴㿵㿶㿷㿸㿹㿺㿻㿼㿽㿾㿿
+䀀䀁䀂䀃䀄䀅䀆䀇䀈䀉䀊䀋䀌䀍䀎䀏䀐䀑䀒䀓䀔䀕䀖䀗䀘䀙䀚䀛䀜䀝䀞䀟
+䀠䀡䀢䀣䀤䀥䀦䀧䀨䀩䀪䀫䀬䀭䀮䀯䀰䀱䀲䀳䀴䀵䀶䀷䀸䀹䀺䀻䀼䀽䀾䀿
+䁀䁁䁂䁃䁄䁅䁆䁇䁈䁉䁊䁋䁌䁍䁎䁏䁐䁑䁒䁓䁔䁕䁖䁗䁘䁙䁚䁛䁜䁝䁞䁟
+䁠䁡䁢䁣䁤䁥䁦䁧䁨䁩䁪䁫䁬䁭䁮䁯䁰䁱䁲䁳䁴䁵䁶䁷䁸䁹䁺䁻䁼䁽䁾䁿
+䂀䂁䂂䂃䂄䂅䂆䂇䂈䂉䂊䂋䂌䂍䂎䂏䂐䂑䂒䂓䂔䂕䂖䂗䂘䂙䂚䂛䂜䂝䂞䂟
+䂠䂡䂢䂣䂤䂥䂦䂧䂨䂩䂪䂫䂬䂭䂮䂯䂰䂱䂲䂳䂴䂵䂶䂷䂸䂹䂺䂻䂼䂽䂾䂿
+䃀䃁䃂䃃䃄䃅䃆䃇䃈䃉䃊䃋䃌䃍䃎䃏䃐䃑䃒䃓䃔䃕䃖䃗䃘䃙䃚䃛䃜䃝䃞䃟
+䃠䃡䃢䃣䃤䃥䃦䃧䃨䃩䃪䃫䃬䃭䃮䃯䃰䃱䃲䃳䃴䃵䃶䃷䃸䃹䃺䃻䃼䃽䃾䃿
+䄀䄁䄂䄃䄄䄅䄆䄇䄈䄉䄊䄋䄌䄍䄎䄏䄐䄑䄒䄓䄔䄕䄖䄗䄘䄙䄚䄛䄜䄝䄞䄟
+䄠䄡䄢䄣䄤䄥䄦䄧䄨䄩䄪䄫䄬䄭䄮䄯䄰䄱䄲䄳䄴䄵䄶䄷䄸䄹䄺䄻䄼䄽䄾䄿
+䅀䅁䅂䅃䅄䅅䅆䅇䅈䅉䅊䅋䅌䅍䅎䅏䅐䅑䅒䅓䅔䅕䅖䅗䅘䅙䅚䅛䅜䅝䅞䅟
+䅠䅡䅢䅣䅤䅥䅦䅧䅨䅩䅪䅫䅬䅭䅮䅯䅰䅱䅲䅳䅴䅵䅶䅷䅸䅹䅺䅻䅼䅽䅾䅿
+䆀䆁䆂䆃䆄䆅䆆䆇䆈䆉䆊䆋䆌䆍䆎䆏䆐䆑䆒䆓䆔䆕䆖䆗䆘䆙䆚䆛䆜䆝䆞䆟
+䆠䆡䆢䆣䆤䆥䆦䆧䆨䆩䆪䆫䆬䆭䆮䆯䆰䆱䆲䆳䆴䆵䆶䆷䆸䆹䆺䆻䆼䆽䆾䆿
+䇀䇁䇂䇃䇄䇅䇆䇇䇈䇉䇊䇋䇌䇍䇎䇏䇐䇑䇒䇓䇔䇕䇖䇗䇘䇙䇚䇛䇜䇝䇞䇟
+䇠䇡䇢䇣䇤䇥䇦䇧䇨䇩䇪䇫䇬䇭䇮䇯䇰䇱䇲䇳䇴䇵䇶䇷䇸䇹䇺䇻䇼䇽䇾䇿
+䈀䈁䈂䈃䈄䈅䈆䈇䈈䈉䈊䈋䈌䈍䈎䈏䈐䈑䈒䈓䈔䈕䈖䈗䈘䈙䈚䈛䈜䈝䈞䈟
+䈠䈡䈢䈣䈤䈥䈦䈧䈨䈩䈪䈫䈬䈭䈮䈯䈰䈱䈲䈳䈴䈵䈶䈷䈸䈹䈺䈻䈼䈽䈾䈿
+䉀䉁䉂䉃䉄䉅䉆䉇䉈䉉䉊䉋䉌䉍䉎䉏䉐䉑䉒䉓䉔䉕䉖䉗䉘䉙䉚䉛䉜䉝䉞䉟
+䉠䉡䉢䉣䉤䉥䉦䉧䉨䉩䉪䉫䉬䉭䉮䉯䉰䉱䉲䉳䉴䉵䉶䉷䉸䉹䉺䉻䉼䉽䉾䉿
+䊀䊁䊂䊃䊄䊅䊆䊇䊈䊉䊊䊋䊌䊍䊎䊏䊐䊑䊒䊓䊔䊕䊖䊗䊘䊙䊚䊛䊜䊝䊞䊟
+䊠䊡䊢䊣䊤䊥䊦䊧䊨䊩䊪䊫䊬䊭䊮䊯䊰䊱䊲䊳䊴䊵䊶䊷䊸䊹䊺䊻䊼䊽䊾䊿
+䋀䋁䋂䋃䋄䋅䋆䋇䋈䋉䋊䋋䋌䋍䋎䋏䋐䋑䋒䋓䋔䋕䋖䋗䋘䋙䋚䋛䋜䋝䋞䋟
+䋠䋡䋢䋣䋤䋥䋦䋧䋨䋩䋪䋫䋬䋭䋮䋯䋰䋱䋲䋳䋴䋵䋶䋷䋸䋹䋺䋻䋼䋽䋾䋿
+䌀䌁䌂䌃䌄䌅䌆䌇䌈䌉䌊䌋䌌䌍䌎䌏䌐䌑䌒䌓䌔䌕䌖䌗䌘䌙䌚䌛䌜䌝䌞䌟
+䌠䌡䌢䌣䌤䌥䌦䌧䌨䌩䌪䌫䌬䌭䌮䌯䌰䌱䌲䌳䌴䌵䌶䌷䌸䌹䌺䌻䌼䌽䌾䌿
+䍀䍁䍂䍃䍄䍅䍆䍇䍈䍉䍊䍋䍌䍍䍎䍏䍐䍑䍒䍓䍔䍕䍖䍗䍘䍙䍚䍛䍜䍝䍞䍟
+䍠䍡䍢䍣䍤䍥䍦䍧䍨䍩䍪䍫䍬䍭䍮䍯䍰䍱䍲䍳䍴䍵䍶䍷䍸䍹䍺䍻䍼䍽䍾䍿
+䎀䎁䎂䎃䎄䎅䎆䎇䎈䎉䎊䎋䎌䎍䎎䎏䎐䎑䎒䎓䎔䎕䎖䎗䎘䎙䎚䎛䎜䎝䎞䎟
+䎠䎡䎢䎣䎤䎥䎦䎧䎨䎩䎪䎫䎬䎭䎮䎯䎰䎱䎲䎳䎴䎵䎶䎷䎸䎹䎺䎻䎼䎽䎾䎿
+䏀䏁䏂䏃䏄䏅䏆䏇䏈䏉䏊䏋䏌䏍䏎䏏䏐䏑䏒䏓䏔䏕䏖䏗䏘䏙䏚䏛䏜䏝䏞䏟
+䏠䏡䏢䏣䏤䏥䏦䏧䏨䏩䏪䏫䏬䏭䏮䏯䏰䏱䏲䏳䏴䏵䏶䏷䏸䏹䏺䏻䏼䏽䏾䏿
+䐀䐁䐂䐃䐄䐅䐆䐇䐈䐉䐊䐋䐌䐍䐎䐏䐐䐑䐒䐓䐔䐕䐖䐗䐘䐙䐚䐛䐜䐝䐞䐟
+䐠䐡䐢䐣䐤䐥䐦䐧䐨䐩䐪䐫䐬䐭䐮䐯䐰䐱䐲䐳䐴䐵䐶䐷䐸䐹䐺䐻䐼䐽䐾䐿
+䑀䑁䑂䑃䑄䑅䑆䑇䑈䑉䑊䑋䑌䑍䑎䑏䑐䑑䑒䑓䑔䑕䑖䑗䑘䑙䑚䑛䑜䑝䑞䑟
+䑠䑡䑢䑣䑤䑥䑦䑧䑨䑩䑪䑫䑬䑭䑮䑯䑰䑱䑲䑳䑴䑵䑶䑷䑸䑹䑺䑻䑼䑽䑾䑿
+䒀䒁䒂䒃䒄䒅䒆䒇䒈䒉䒊䒋䒌䒍䒎䒏䒐䒑䒒䒓䒔䒕䒖䒗䒘䒙䒚䒛䒜䒝䒞䒟
+䒠䒡䒢䒣䒤䒥䒦䒧䒨䒩䒪䒫䒬䒭䒮䒯䒰䒱䒲䒳䒴䒵䒶䒷䒸䒹䒺䒻䒼䒽䒾䒿
+䓀䓁䓂䓃䓄䓅䓆䓇䓈䓉䓊䓋䓌䓍䓎䓏䓐䓑䓒䓓䓔䓕䓖䓗䓘䓙䓚䓛䓜䓝䓞䓟
+䓠䓡䓢䓣䓤䓥䓦䓧䓨䓩䓪䓫䓬䓭䓮䓯䓰䓱䓲䓳䓴䓵䓶䓷䓸䓹䓺䓻䓼䓽䓾䓿
+䔀䔁䔂䔃䔄䔅䔆䔇䔈䔉䔊䔋䔌䔍䔎䔏䔐䔑䔒䔓䔔䔕䔖䔗䔘䔙䔚䔛䔜䔝䔞䔟
+䔠䔡䔢䔣䔤䔥䔦䔧䔨䔩䔪䔫䔬䔭䔮䔯䔰䔱䔲䔳䔴䔵䔶䔷䔸䔹䔺䔻䔼䔽䔾䔿
+䕀䕁䕂䕃䕄䕅䕆䕇䕈䕉䕊䕋䕌䕍䕎䕏䕐䕑䕒䕓䕔䕕䕖䕗䕘䕙䕚䕛䕜䕝䕞䕟
+䕠䕡䕢䕣䕤䕥䕦䕧䕨䕩䕪䕫䕬䕭䕮䕯䕰䕱䕲䕳䕴䕵䕶䕷䕸䕹䕺䕻䕼䕽䕾䕿
+䖀䖁䖂䖃䖄䖅䖆䖇䖈䖉䖊䖋䖌䖍䖎䖏䖐䖑䖒䖓䖔䖕䖖䖗䖘䖙䖚䖛䖜䖝䖞䖟
+䖠䖡䖢䖣䖤䖥䖦䖧䖨䖩䖪䖫䖬䖭䖮䖯䖰䖱䖲䖳䖴䖵䖶䖷䖸䖹䖺䖻䖼䖽䖾䖿
+䗀䗁䗂䗃䗄䗅䗆䗇䗈䗉䗊䗋䗌䗍䗎䗏䗐䗑䗒䗓䗔䗕䗖䗗䗘䗙䗚䗛䗜䗝䗞䗟
+䗠䗡䗢䗣䗤䗥䗦䗧䗨䗩䗪䗫䗬䗭䗮䗯䗰䗱䗲䗳䗴䗵䗶䗷䗸䗹䗺䗻䗼䗽䗾䗿
+䘀䘁䘂䘃䘄䘅䘆䘇䘈䘉䘊䘋䘌䘍䘎䘏䘐䘑䘒䘓䘔䘕䘖䘗䘘䘙䘚䘛䘜䘝䘞䘟
+䘠䘡䘢䘣䘤䘥䘦䘧䘨䘩䘪䘫䘬䘭䘮䘯䘰䘱䘲䘳䘴䘵䘶䘷䘸䘹䘺䘻䘼䘽䘾䘿
+䙀䙁䙂䙃䙄䙅䙆䙇䙈䙉䙊䙋䙌䙍䙎䙏䙐䙑䙒䙓䙔䙕䙖䙗䙘䙙䙚䙛䙜䙝䙞䙟
+䙠䙡䙢䙣䙤䙥䙦䙧䙨䙩䙪䙫䙬䙭䙮䙯䙰䙱䙲䙳䙴䙵䙶䙷䙸䙹䙺䙻䙼䙽䙾䙿
+䚀䚁䚂䚃䚄䚅䚆䚇䚈䚉䚊䚋䚌䚍䚎䚏䚐䚑䚒䚓䚔䚕䚖䚗䚘䚙䚚䚛䚜䚝䚞䚟
+䚠䚡䚢䚣䚤䚥䚦䚧䚨䚩䚪䚫䚬䚭䚮䚯䚰䚱䚲䚳䚴䚵䚶䚷䚸䚹䚺䚻䚼䚽䚾䚿
+䛀䛁䛂䛃䛄䛅䛆䛇䛈䛉䛊䛋䛌䛍䛎䛏䛐䛑䛒䛓䛔䛕䛖䛗䛘䛙䛚䛛䛜䛝䛞䛟
+䛠䛡䛢䛣䛤䛥䛦䛧䛨䛩䛪䛫䛬䛭䛮䛯䛰䛱䛲䛳䛴䛵䛶䛷䛸䛹䛺䛻䛼䛽䛾䛿
+䜀䜁䜂䜃䜄䜅䜆䜇䜈䜉䜊䜋䜌䜍䜎䜏䜐䜑䜒䜓䜔䜕䜖䜗䜘䜙䜚䜛䜜䜝䜞䜟
+䜠䜡䜢䜣䜤䜥䜦䜧䜨䜩䜪䜫䜬䜭䜮䜯䜰䜱䜲䜳䜴䜵䜶䜷䜸䜹䜺䜻䜼䜽䜾䜿
+䝀䝁䝂䝃䝄䝅䝆䝇䝈䝉䝊䝋䝌䝍䝎䝏䝐䝑䝒䝓䝔䝕䝖䝗䝘䝙䝚䝛䝜䝝䝞䝟
+䝠䝡䝢䝣䝤䝥䝦䝧䝨䝩䝪䝫䝬䝭䝮䝯䝰䝱䝲䝳䝴䝵䝶䝷䝸䝹䝺䝻䝼䝽䝾䝿
+䞀䞁䞂䞃䞄䞅䞆䞇䞈䞉䞊䞋䞌䞍䞎䞏䞐䞑䞒䞓䞔䞕䞖䞗䞘䞙䞚䞛䞜䞝䞞䞟
+䞠䞡䞢䞣䞤䞥䞦䞧䞨䞩䞪䞫䞬䞭䞮䞯䞰䞱䞲䞳䞴䞵䞶䞷䞸䞹䞺䞻䞼䞽䞾䞿
+䟀䟁䟂䟃䟄䟅䟆䟇䟈䟉䟊䟋䟌䟍䟎䟏䟐䟑䟒䟓䟔䟕䟖䟗䟘䟙䟚䟛䟜䟝䟞䟟
+䟠䟡䟢䟣䟤䟥䟦䟧䟨䟩䟪䟫䟬䟭䟮䟯䟰䟱䟲䟳䟴䟵䟶䟷䟸䟹䟺䟻䟼䟽䟾䟿
+䠀䠁䠂䠃䠄䠅䠆䠇䠈䠉䠊䠋䠌䠍䠎䠏䠐䠑䠒䠓䠔䠕䠖䠗䠘䠙䠚䠛䠜䠝䠞䠟
+䠠䠡䠢䠣䠤䠥䠦䠧䠨䠩䠪䠫䠬䠭䠮䠯䠰䠱䠲䠳䠴䠵䠶䠷䠸䠹䠺䠻䠼䠽䠾䠿
+䡀䡁䡂䡃䡄䡅䡆䡇䡈䡉䡊䡋䡌䡍䡎䡏䡐䡑䡒䡓䡔䡕䡖䡗䡘䡙䡚䡛䡜䡝䡞䡟
+䡠䡡䡢䡣䡤䡥䡦䡧䡨䡩䡪䡫䡬䡭䡮䡯䡰䡱䡲䡳䡴䡵䡶䡷䡸䡹䡺䡻䡼䡽䡾䡿
+䢀䢁䢂䢃䢄䢅䢆䢇䢈䢉䢊䢋䢌䢍䢎䢏䢐䢑䢒䢓䢔䢕䢖䢗䢘䢙䢚䢛䢜䢝䢞䢟
+䢠䢡䢢䢣䢤䢥䢦䢧䢨䢩䢪䢫䢬䢭䢮䢯䢰䢱䢲䢳䢴䢵䢶䢷䢸䢹䢺䢻䢼䢽䢾䢿
+䣀䣁䣂䣃䣄䣅䣆䣇䣈䣉䣊䣋䣌䣍䣎䣏䣐䣑䣒䣓䣔䣕䣖䣗䣘䣙䣚䣛䣜䣝䣞䣟
+䣠䣡䣢䣣䣤䣥䣦䣧䣨䣩䣪䣫䣬䣭䣮䣯䣰䣱䣲䣳䣴䣵䣶䣷䣸䣹䣺䣻䣼䣽䣾䣿
+䤀䤁䤂䤃䤄䤅䤆䤇䤈䤉䤊䤋䤌䤍䤎䤏䤐䤑䤒䤓䤔䤕䤖䤗䤘䤙䤚䤛䤜䤝䤞䤟
+䤠䤡䤢䤣䤤䤥䤦䤧䤨䤩䤪䤫䤬䤭䤮䤯䤰䤱䤲䤳䤴䤵䤶䤷䤸䤹䤺䤻䤼䤽䤾䤿
+䥀䥁䥂䥃䥄䥅䥆䥇䥈䥉䥊䥋䥌䥍䥎䥏䥐䥑䥒䥓䥔䥕䥖䥗䥘䥙䥚䥛䥜䥝䥞䥟
+䥠䥡䥢䥣䥤䥥䥦䥧䥨䥩䥪䥫䥬䥭䥮䥯䥰䥱䥲䥳䥴䥵䥶䥷䥸䥹䥺䥻䥼䥽䥾䥿
+䦀䦁䦂䦃䦄䦅䦆䦇䦈䦉䦊䦋䦌䦍䦎䦏䦐䦑䦒䦓䦔䦕䦖䦗䦘䦙䦚䦛䦜䦝䦞䦟
+䦠䦡䦢䦣䦤䦥䦦䦧䦨䦩䦪䦫䦬䦭䦮䦯䦰䦱䦲䦳䦴䦵䦶䦷䦸䦹䦺䦻䦼䦽䦾䦿
+䧀䧁䧂䧃䧄䧅䧆䧇䧈䧉䧊䧋䧌䧍䧎䧏䧐䧑䧒䧓䧔䧕䧖䧗䧘䧙䧚䧛䧜䧝䧞䧟
+䧠䧡䧢䧣䧤䧥䧦䧧䧨䧩䧪䧫䧬䧭䧮䧯䧰䧱䧲䧳䧴䧵䧶䧷䧸䧹䧺䧻䧼䧽䧾䧿
+䨀䨁䨂䨃䨄䨅䨆䨇䨈䨉䨊䨋䨌䨍䨎䨏䨐䨑䨒䨓䨔䨕䨖䨗䨘䨙䨚䨛䨜䨝䨞䨟
+䨠䨡䨢䨣䨤䨥䨦䨧䨨䨩䨪䨫䨬䨭䨮䨯䨰䨱䨲䨳䨴䨵䨶䨷䨸䨹䨺䨻䨼䨽䨾䨿
+䩀䩁䩂䩃䩄䩅䩆䩇䩈䩉䩊䩋䩌䩍䩎䩏䩐䩑䩒䩓䩔䩕䩖䩗䩘䩙䩚䩛䩜䩝䩞䩟
+䩠䩡䩢䩣䩤䩥䩦䩧䩨䩩䩪䩫䩬䩭䩮䩯䩰䩱䩲䩳䩴䩵䩶䩷䩸䩹䩺䩻䩼䩽䩾䩿
+䪀䪁䪂䪃䪄䪅䪆䪇䪈䪉䪊䪋䪌䪍䪎䪏䪐䪑䪒䪓䪔䪕䪖䪗䪘䪙䪚䪛䪜䪝䪞䪟
+䪠䪡䪢䪣䪤䪥䪦䪧䪨䪩䪪䪫䪬䪭䪮䪯䪰䪱䪲䪳䪴䪵䪶䪷䪸䪹䪺䪻䪼䪽䪾䪿
+䫀䫁䫂䫃䫄䫅䫆䫇䫈䫉䫊䫋䫌䫍䫎䫏䫐䫑䫒䫓䫔䫕䫖䫗䫘䫙䫚䫛䫜䫝䫞䫟
+䫠䫡䫢䫣䫤䫥䫦䫧䫨䫩䫪䫫䫬䫭䫮䫯䫰䫱䫲䫳䫴䫵䫶䫷䫸䫹䫺䫻䫼䫽䫾䫿
+䬀䬁䬂䬃䬄䬅䬆䬇䬈䬉䬊䬋䬌䬍䬎䬏䬐䬑䬒䬓䬔䬕䬖䬗䬘䬙䬚䬛䬜䬝䬞䬟
+䬠䬡䬢䬣䬤䬥䬦䬧䬨䬩䬪䬫䬬䬭䬮䬯䬰䬱䬲䬳䬴䬵䬶䬷䬸䬹䬺䬻䬼䬽䬾䬿
+䭀䭁䭂䭃䭄䭅䭆䭇䭈䭉䭊䭋䭌䭍䭎䭏䭐䭑䭒䭓䭔䭕䭖䭗䭘䭙䭚䭛䭜䭝䭞䭟
+䭠䭡䭢䭣䭤䭥䭦䭧䭨䭩䭪䭫䭬䭭䭮䭯䭰䭱䭲䭳䭴䭵䭶䭷䭸䭹䭺䭻䭼䭽䭾䭿
+䮀䮁䮂䮃䮄䮅䮆䮇䮈䮉䮊䮋䮌䮍䮎䮏䮐䮑䮒䮓䮔䮕䮖䮗䮘䮙䮚䮛䮜䮝䮞䮟
+䮠䮡䮢䮣䮤䮥䮦䮧䮨䮩䮪䮫䮬䮭䮮䮯䮰䮱䮲䮳䮴䮵䮶䮷䮸䮹䮺䮻䮼䮽䮾䮿
+䯀䯁䯂䯃䯄䯅䯆䯇䯈䯉䯊䯋䯌䯍䯎䯏䯐䯑䯒䯓䯔䯕䯖䯗䯘䯙䯚䯛䯜䯝䯞䯟
+䯠䯡䯢䯣䯤䯥䯦䯧䯨䯩䯪䯫䯬䯭䯮䯯䯰䯱䯲䯳䯴䯵䯶䯷䯸䯹䯺䯻䯼䯽䯾䯿
+䰀䰁䰂䰃䰄䰅䰆䰇䰈䰉䰊䰋䰌䰍䰎䰏䰐䰑䰒䰓䰔䰕䰖䰗䰘䰙䰚䰛䰜䰝䰞䰟
+䰠䰡䰢䰣䰤䰥䰦䰧䰨䰩䰪䰫䰬䰭䰮䰯䰰䰱䰲䰳䰴䰵䰶䰷䰸䰹䰺䰻䰼䰽䰾䰿
+䱀䱁䱂䱃䱄䱅䱆䱇䱈䱉䱊䱋䱌䱍䱎䱏䱐䱑䱒䱓䱔䱕䱖䱗䱘䱙䱚䱛䱜䱝䱞䱟
+䱠䱡䱢䱣䱤䱥䱦䱧䱨䱩䱪䱫䱬䱭䱮䱯䱰䱱䱲䱳䱴䱵䱶䱷䱸䱹䱺䱻䱼䱽䱾䱿
+䲀䲁䲂䲃䲄䲅䲆䲇䲈䲉䲊䲋䲌䲍䲎䲏䲐䲑䲒䲓䲔䲕䲖䲗䲘䲙䲚䲛䲜䲝䲞䲟
+䲠䲡䲢䲣䲤䲥䲦䲧䲨䲩䲪䲫䲬䲭䲮䲯䲰䲱䲲䲳䲴䲵䲶䲷䲸䲹䲺䲻䲼䲽䲾䲿
+䳀䳁䳂䳃䳄䳅䳆䳇䳈䳉䳊䳋䳌䳍䳎䳏䳐䳑䳒䳓䳔䳕䳖䳗䳘䳙䳚䳛䳜䳝䳞䳟
+䳠䳡䳢䳣䳤䳥䳦䳧䳨䳩䳪䳫䳬䳭䳮䳯䳰䳱䳲䳳䳴䳵䳶䳷䳸䳹䳺䳻䳼䳽䳾䳿
+䴀䴁䴂䴃䴄䴅䴆䴇䴈䴉䴊䴋䴌䴍䴎䴏䴐䴑䴒䴓䴔䴕䴖䴗䴘䴙䴚䴛䴜䴝䴞䴟
+䴠䴡䴢䴣䴤䴥䴦䴧䴨䴩䴪䴫䴬䴭䴮䴯䴰䴱䴲䴳䴴䴵䴶䴷䴸䴹䴺䴻䴼䴽䴾䴿
+䵀䵁䵂䵃䵄䵅䵆䵇䵈䵉䵊䵋䵌䵍䵎䵏䵐䵑䵒䵓䵔䵕䵖䵗䵘䵙䵚䵛䵜䵝䵞䵟
+䵠䵡䵢䵣䵤䵥䵦䵧䵨䵩䵪䵫䵬䵭䵮䵯䵰䵱䵲䵳䵴䵵䵶䵷䵸䵹䵺䵻䵼䵽䵾䵿
+䶀䶁䶂䶃䶄䶅䶆䶇䶈䶉䶊䶋䶌䶍䶎䶏䶐䶑䶒䶓䶔䶕䶖䶗䶘䶙䶚䶛䶜䶝䶞䶟
+䶠䶡䶢䶣䶤䶥䶦䶧䶨䶩䶪䶫䶬䶭䶮䶯䶰䶱䶲䶳䶴䶵䶶䶷䶸䶹䶺䶻䶼䶽䶾䶿
+
+Yijing Hexagram Symbols (U+4DC0-U+4DFF):
+
+䷀䷁䷂䷃䷄䷅䷆䷇䷈䷉䷊䷋䷌䷍䷎䷏䷐䷑䷒䷓䷔䷕䷖䷗䷘䷙䷚䷛䷜䷝䷞䷟
+䷠䷡䷢䷣䷤䷥䷦䷧䷨䷩䷪䷫䷬䷭䷮䷯䷰䷱䷲䷳䷴䷵䷶䷷䷸䷹䷺䷻䷼䷽䷾䷿
+
+CJK Unified Ideographs (U+4E00-U+9FFF):
+
+一丁丂七丄丅丆万丈三上下丌不与丏丐丑丒专且丕世丗丘丙业丛东丝丞丟
+丠両丢丣两严並丧丨丩个丫丬中丮丯丰丱串丳临丵丶丷丸丹为主丼丽举丿
+乀乁乂乃乄久乆乇么义乊之乌乍乎乏乐乑乒乓乔乕乖乗乘乙乚乛乜九乞也
+习乡乢乣乤乥书乧乨乩乪乫乬乭乮乯买乱乲乳乴乵乶乷乸乹乺乻乼乽乾乿
+亀亁亂亃亄亅了亇予争亊事二亍于亏亐云互亓五井亖亗亘亙亚些亜亝亞亟
+亠亡亢亣交亥亦产亨亩亪享京亭亮亯亰亱亲亳亴亵亶亷亸亹人亻亼亽亾亿
+什仁仂仃仄仅仆仇仈仉今介仌仍从仏仐仑仒仓仔仕他仗付仙仚仛仜仝仞仟
+仠仡仢代令以仦仧仨仩仪仫们仭仮仯仰仱仲仳仴仵件价仸仹仺任仼份仾仿
+伀企伂伃伄伅伆伇伈伉伊伋伌伍伎伏伐休伒伓伔伕伖众优伙会伛伜伝伞伟
+传伡伢伣伤伥伦伧伨伩伪伫伬伭伮伯估伱伲伳伴伵伶伷伸伹伺伻似伽伾伿
+佀佁佂佃佄佅但佇佈佉佊佋佌位低住佐佑佒体佔何佖佗佘余佚佛作佝佞佟
+你佡佢佣佤佥佦佧佨佩佪佫佬佭佮佯佰佱佲佳佴併佶佷佸佹佺佻佼佽佾使
+侀侁侂侃侄侅來侇侈侉侊例侌侍侎侏侐侑侒侓侔侕侖侗侘侙侚供侜依侞侟
+侠価侢侣侤侥侦侧侨侩侪侫侬侭侮侯侰侱侲侳侴侵侶侷侸侹侺侻侼侽侾便
+俀俁係促俄俅俆俇俈俉俊俋俌俍俎俏俐俑俒俓俔俕俖俗俘俙俚俛俜保俞俟
+俠信俢俣俤俥俦俧俨俩俪俫俬俭修俯俰俱俲俳俴俵俶俷俸俹俺俻俼俽俾俿
+倀倁倂倃倄倅倆倇倈倉倊個倌倍倎倏倐們倒倓倔倕倖倗倘候倚倛倜倝倞借
+倠倡倢倣値倥倦倧倨倩倪倫倬倭倮倯倰倱倲倳倴倵倶倷倸倹债倻值倽倾倿
+偀偁偂偃偄偅偆假偈偉偊偋偌偍偎偏偐偑偒偓偔偕偖偗偘偙做偛停偝偞偟
+偠偡偢偣偤健偦偧偨偩偪偫偬偭偮偯偰偱偲偳側偵偶偷偸偹偺偻偼偽偾偿
+傀傁傂傃傄傅傆傇傈傉傊傋傌傍傎傏傐傑傒傓傔傕傖傗傘備傚傛傜傝傞傟
+傠傡傢傣傤傥傦傧储傩傪傫催傭傮傯傰傱傲傳傴債傶傷傸傹傺傻傼傽傾傿
+僀僁僂僃僄僅僆僇僈僉僊僋僌働僎像僐僑僒僓僔僕僖僗僘僙僚僛僜僝僞僟
+僠僡僢僣僤僥僦僧僨僩僪僫僬僭僮僯僰僱僲僳僴僵僶僷僸價僺僻僼僽僾僿
+儀儁儂儃億儅儆儇儈儉儊儋儌儍儎儏儐儑儒儓儔儕儖儗儘儙儚儛儜儝儞償
+儠儡儢儣儤儥儦儧儨儩優儫儬儭儮儯儰儱儲儳儴儵儶儷儸儹儺儻儼儽儾儿
+兀允兂元兄充兆兇先光兊克兌免兎兏児兑兒兓兔兕兖兗兘兙党兛兜兝兞兟
+兠兡兢兣兤入兦內全兩兪八公六兮兯兰共兲关兴兵其具典兹兺养兼兽兾兿
+冀冁冂冃冄内円冇冈冉冊冋册再冎冏冐冑冒冓冔冕冖冗冘写冚军农冝冞冟
+冠冡冢冣冤冥冦冧冨冩冪冫冬冭冮冯冰冱冲决冴况冶冷冸冹冺冻冼冽冾冿
+净凁凂凃凄凅准凇凈凉凊凋凌凍凎减凐凑凒凓凔凕凖凗凘凙凚凛凜凝凞凟
+几凡凢凣凤凥処凧凨凩凪凫凬凭凮凯凰凱凲凳凴凵凶凷凸凹出击凼函凾凿
+刀刁刂刃刄刅分切刈刉刊刋刌刍刎刏刐刑划刓刔刕刖列刘则刚创刜初刞刟
+删刡刢刣判別刦刧刨利刪别刬刭刮刯到刱刲刳刴刵制刷券刹刺刻刼刽刾刿
+剀剁剂剃剄剅剆則剈剉削剋剌前剎剏剐剑剒剓剔剕剖剗剘剙剚剛剜剝剞剟
+剠剡剢剣剤剥剦剧剨剩剪剫剬剭剮副剰剱割剳剴創剶剷剸剹剺剻剼剽剾剿
+劀劁劂劃劄劅劆劇劈劉劊劋劌劍劎劏劐劑劒劓劔劕劖劗劘劙劚力劜劝办功
+加务劢劣劤劥劦劧动助努劫劬劭劮劯劰励劲劳労劵劶劷劸効劺劻劼劽劾势
+勀勁勂勃勄勅勆勇勈勉勊勋勌勍勎勏勐勑勒勓勔動勖勗勘務勚勛勜勝勞募
+勠勡勢勣勤勥勦勧勨勩勪勫勬勭勮勯勰勱勲勳勴勵勶勷勸勹勺勻勼勽勾勿
+匀匁匂匃匄包匆匇匈匉匊匋匌匍匎匏匐匑匒匓匔匕化北匘匙匚匛匜匝匞匟
+匠匡匢匣匤匥匦匧匨匩匪匫匬匭匮匯匰匱匲匳匴匵匶匷匸匹区医匼匽匾匿
+區十卂千卄卅卆升午卉半卋卌卍华协卐卑卒卓協单卖南単卙博卛卜卝卞卟
+占卡卢卣卤卥卦卧卨卩卪卫卬卭卮卯印危卲即却卵卶卷卸卹卺卻卼卽卾卿
+厀厁厂厃厄厅历厇厈厉厊压厌厍厎厏厐厑厒厓厔厕厖厗厘厙厚厛厜厝厞原
+厠厡厢厣厤厥厦厧厨厩厪厫厬厭厮厯厰厱厲厳厴厵厶厷厸厹厺去厼厽厾县
+叀叁参參叄叅叆叇又叉及友双反収叏叐发叒叓叔叕取受变叙叚叛叜叝叞叟
+叠叡叢口古句另叧叨叩只叫召叭叮可台叱史右叴叵叶号司叹叺叻叼叽叾叿
+吀吁吂吃各吅吆吇合吉吊吋同名后吏吐向吒吓吔吕吖吗吘吙吚君吜吝吞吟
+吠吡吢吣吤吥否吧吨吩吪含听吭吮启吰吱吲吳吴吵吶吷吸吹吺吻吼吽吾吿
+呀呁呂呃呄呅呆呇呈呉告呋呌呍呎呏呐呑呒呓呔呕呖呗员呙呚呛呜呝呞呟
+呠呡呢呣呤呥呦呧周呩呪呫呬呭呮呯呰呱呲味呴呵呶呷呸呹呺呻呼命呾呿
+咀咁咂咃咄咅咆咇咈咉咊咋和咍咎咏咐咑咒咓咔咕咖咗咘咙咚咛咜咝咞咟
+咠咡咢咣咤咥咦咧咨咩咪咫咬咭咮咯咰咱咲咳咴咵咶咷咸咹咺咻咼咽咾咿
+哀品哂哃哄哅哆哇哈哉哊哋哌响哎哏哐哑哒哓哔哕哖哗哘哙哚哛哜哝哞哟
+哠員哢哣哤哥哦哧哨哩哪哫哬哭哮哯哰哱哲哳哴哵哶哷哸哹哺哻哼哽哾哿
+唀唁唂唃唄唅唆唇唈唉唊唋唌唍唎唏唐唑唒唓唔唕唖唗唘唙唚唛唜唝唞唟
+唠唡唢唣唤唥唦唧唨唩唪唫唬唭售唯唰唱唲唳唴唵唶唷唸唹唺唻唼唽唾唿
+啀啁啂啃啄啅商啇啈啉啊啋啌啍啎問啐啑啒啓啔啕啖啗啘啙啚啛啜啝啞啟
+啠啡啢啣啤啥啦啧啨啩啪啫啬啭啮啯啰啱啲啳啴啵啶啷啸啹啺啻啼啽啾啿
+喀喁喂喃善喅喆喇喈喉喊喋喌喍喎喏喐喑喒喓喔喕喖喗喘喙喚喛喜喝喞喟
+喠喡喢喣喤喥喦喧喨喩喪喫喬喭單喯喰喱喲喳喴喵営喷喸喹喺喻喼喽喾喿
+嗀嗁嗂嗃嗄嗅嗆嗇嗈嗉嗊嗋嗌嗍嗎嗏嗐嗑嗒嗓嗔嗕嗖嗗嗘嗙嗚嗛嗜嗝嗞嗟
+嗠嗡嗢嗣嗤嗥嗦嗧嗨嗩嗪嗫嗬嗭嗮嗯嗰嗱嗲嗳嗴嗵嗶嗷嗸嗹嗺嗻嗼嗽嗾嗿
+嘀嘁嘂嘃嘄嘅嘆嘇嘈嘉嘊嘋嘌嘍嘎嘏嘐嘑嘒嘓嘔嘕嘖嘗嘘嘙嘚嘛嘜嘝嘞嘟
+嘠嘡嘢嘣嘤嘥嘦嘧嘨嘩嘪嘫嘬嘭嘮嘯嘰嘱嘲嘳嘴嘵嘶嘷嘸嘹嘺嘻嘼嘽嘾嘿
+噀噁噂噃噄噅噆噇噈噉噊噋噌噍噎噏噐噑噒噓噔噕噖噗噘噙噚噛噜噝噞噟
+噠噡噢噣噤噥噦噧器噩噪噫噬噭噮噯噰噱噲噳噴噵噶噷噸噹噺噻噼噽噾噿
+嚀嚁嚂嚃嚄嚅嚆嚇嚈嚉嚊嚋嚌嚍嚎嚏嚐嚑嚒嚓嚔嚕嚖嚗嚘嚙嚚嚛嚜嚝嚞嚟
+嚠嚡嚢嚣嚤嚥嚦嚧嚨嚩嚪嚫嚬嚭嚮嚯嚰嚱嚲嚳嚴嚵嚶嚷嚸嚹嚺嚻嚼嚽嚾嚿
+囀囁囂囃囄囅囆囇囈囉囊囋囌囍囎囏囐囑囒囓囔囕囖囗囘囙囚四囜囝回囟
+因囡团団囤囥囦囧囨囩囪囫囬园囮囯困囱囲図围囵囶囷囸囹固囻囼国图囿
+圀圁圂圃圄圅圆圇圈圉圊國圌圍圎圏圐圑園圓圔圕圖圗團圙圚圛圜圝圞土
+圠圡圢圣圤圥圦圧在圩圪圫圬圭圮圯地圱圲圳圴圵圶圷圸圹场圻圼圽圾圿
+址坁坂坃坄坅坆均坈坉坊坋坌坍坎坏坐坑坒坓坔坕坖块坘坙坚坛坜坝坞坟
+坠坡坢坣坤坥坦坧坨坩坪坫坬坭坮坯坰坱坲坳坴坵坶坷坸坹坺坻坼坽坾坿
+垀垁垂垃垄垅垆垇垈垉垊型垌垍垎垏垐垑垒垓垔垕垖垗垘垙垚垛垜垝垞垟
+垠垡垢垣垤垥垦垧垨垩垪垫垬垭垮垯垰垱垲垳垴垵垶垷垸垹垺垻垼垽垾垿
+埀埁埂埃埄埅埆埇埈埉埊埋埌埍城埏埐埑埒埓埔埕埖埗埘埙埚埛埜埝埞域
+埠埡埢埣埤埥埦埧埨埩埪埫埬埭埮埯埰埱埲埳埴埵埶執埸培基埻埼埽埾埿
+堀堁堂堃堄堅堆堇堈堉堊堋堌堍堎堏堐堑堒堓堔堕堖堗堘堙堚堛堜堝堞堟
+堠堡堢堣堤堥堦堧堨堩堪堫堬堭堮堯堰報堲堳場堵堶堷堸堹堺堻堼堽堾堿
+塀塁塂塃塄塅塆塇塈塉塊塋塌塍塎塏塐塑塒塓塔塕塖塗塘塙塚塛塜塝塞塟
+塠塡塢塣塤塥塦塧塨塩塪填塬塭塮塯塰塱塲塳塴塵塶塷塸塹塺塻塼塽塾塿
+墀墁墂境墄墅墆墇墈墉墊墋墌墍墎墏墐墑墒墓墔墕墖増墘墙墚墛墜墝增墟
+墠墡墢墣墤墥墦墧墨墩墪墫墬墭墮墯墰墱墲墳墴墵墶墷墸墹墺墻墼墽墾墿
+壀壁壂壃壄壅壆壇壈壉壊壋壌壍壎壏壐壑壒壓壔壕壖壗壘壙壚壛壜壝壞壟
+壠壡壢壣壤壥壦壧壨壩壪士壬壭壮壯声壱売壳壴壵壶壷壸壹壺壻壼壽壾壿
+夀夁夂夃处夅夆备夈変夊夋夌复夎夏夐夑夒夓夔夕外夗夘夙多夛夜夝夞够
+夠夡夢夣夤夥夦大夨天太夫夬夭央夯夰失夲夳头夵夶夷夸夹夺夻夼夽夾夿
+奀奁奂奃奄奅奆奇奈奉奊奋奌奍奎奏奐契奒奓奔奕奖套奘奙奚奛奜奝奞奟
+奠奡奢奣奤奥奦奧奨奩奪奫奬奭奮奯奰奱奲女奴奵奶奷奸她奺奻奼好奾奿
+妀妁如妃妄妅妆妇妈妉妊妋妌妍妎妏妐妑妒妓妔妕妖妗妘妙妚妛妜妝妞妟
+妠妡妢妣妤妥妦妧妨妩妪妫妬妭妮妯妰妱妲妳妴妵妶妷妸妹妺妻妼妽妾妿
+姀姁姂姃姄姅姆姇姈姉姊始姌姍姎姏姐姑姒姓委姕姖姗姘姙姚姛姜姝姞姟
+姠姡姢姣姤姥姦姧姨姩姪姫姬姭姮姯姰姱姲姳姴姵姶姷姸姹姺姻姼姽姾姿
+娀威娂娃娄娅娆娇娈娉娊娋娌娍娎娏娐娑娒娓娔娕娖娗娘娙娚娛娜娝娞娟
+娠娡娢娣娤娥娦娧娨娩娪娫娬娭娮娯娰娱娲娳娴娵娶娷娸娹娺娻娼娽娾娿
+婀婁婂婃婄婅婆婇婈婉婊婋婌婍婎婏婐婑婒婓婔婕婖婗婘婙婚婛婜婝婞婟
+婠婡婢婣婤婥婦婧婨婩婪婫婬婭婮婯婰婱婲婳婴婵婶婷婸婹婺婻婼婽婾婿
+媀媁媂媃媄媅媆媇媈媉媊媋媌媍媎媏媐媑媒媓媔媕媖媗媘媙媚媛媜媝媞媟
+媠媡媢媣媤媥媦媧媨媩媪媫媬媭媮媯媰媱媲媳媴媵媶媷媸媹媺媻媼媽媾媿
+嫀嫁嫂嫃嫄嫅嫆嫇嫈嫉嫊嫋嫌嫍嫎嫏嫐嫑嫒嫓嫔嫕嫖嫗嫘嫙嫚嫛嫜嫝嫞嫟
+嫠嫡嫢嫣嫤嫥嫦嫧嫨嫩嫪嫫嫬嫭嫮嫯嫰嫱嫲嫳嫴嫵嫶嫷嫸嫹嫺嫻嫼嫽嫾嫿
+嬀嬁嬂嬃嬄嬅嬆嬇嬈嬉嬊嬋嬌嬍嬎嬏嬐嬑嬒嬓嬔嬕嬖嬗嬘嬙嬚嬛嬜嬝嬞嬟
+嬠嬡嬢嬣嬤嬥嬦嬧嬨嬩嬪嬫嬬嬭嬮嬯嬰嬱嬲嬳嬴嬵嬶嬷嬸嬹嬺嬻嬼嬽嬾嬿
+孀孁孂孃孄孅孆孇孈孉孊孋孌孍孎孏子孑孒孓孔孕孖字存孙孚孛孜孝孞孟
+孠孡孢季孤孥学孧孨孩孪孫孬孭孮孯孰孱孲孳孴孵孶孷學孹孺孻孼孽孾孿
+宀宁宂它宄宅宆宇守安宊宋完宍宎宏宐宑宒宓宔宕宖宗官宙定宛宜宝实実
+宠审客宣室宥宦宧宨宩宪宫宬宭宮宯宰宱宲害宴宵家宷宸容宺宻宼宽宾宿
+寀寁寂寃寄寅密寇寈寉寊寋富寍寎寏寐寑寒寓寔寕寖寗寘寙寚寛寜寝寞察
+寠寡寢寣寤寥實寧寨審寪寫寬寭寮寯寰寱寲寳寴寵寶寷寸对寺寻导寽対寿
+尀封専尃射尅将將專尉尊尋尌對導小尐少尒尓尔尕尖尗尘尙尚尛尜尝尞尟
+尠尡尢尣尤尥尦尧尨尩尪尫尬尭尮尯尰就尲尳尴尵尶尷尸尹尺尻尼尽尾尿
+局屁层屃屄居屆屇屈屉届屋屌屍屎屏屐屑屒屓屔展屖屗屘屙屚屛屜屝属屟
+屠屡屢屣層履屦屧屨屩屪屫屬屭屮屯屰山屲屳屴屵屶屷屸屹屺屻屼屽屾屿
+岀岁岂岃岄岅岆岇岈岉岊岋岌岍岎岏岐岑岒岓岔岕岖岗岘岙岚岛岜岝岞岟
+岠岡岢岣岤岥岦岧岨岩岪岫岬岭岮岯岰岱岲岳岴岵岶岷岸岹岺岻岼岽岾岿
+峀峁峂峃峄峅峆峇峈峉峊峋峌峍峎峏峐峑峒峓峔峕峖峗峘峙峚峛峜峝峞峟
+峠峡峢峣峤峥峦峧峨峩峪峫峬峭峮峯峰峱峲峳峴峵島峷峸峹峺峻峼峽峾峿
+崀崁崂崃崄崅崆崇崈崉崊崋崌崍崎崏崐崑崒崓崔崕崖崗崘崙崚崛崜崝崞崟
+崠崡崢崣崤崥崦崧崨崩崪崫崬崭崮崯崰崱崲崳崴崵崶崷崸崹崺崻崼崽崾崿
+嵀嵁嵂嵃嵄嵅嵆嵇嵈嵉嵊嵋嵌嵍嵎嵏嵐嵑嵒嵓嵔嵕嵖嵗嵘嵙嵚嵛嵜嵝嵞嵟
+嵠嵡嵢嵣嵤嵥嵦嵧嵨嵩嵪嵫嵬嵭嵮嵯嵰嵱嵲嵳嵴嵵嵶嵷嵸嵹嵺嵻嵼嵽嵾嵿
+嶀嶁嶂嶃嶄嶅嶆嶇嶈嶉嶊嶋嶌嶍嶎嶏嶐嶑嶒嶓嶔嶕嶖嶗嶘嶙嶚嶛嶜嶝嶞嶟
+嶠嶡嶢嶣嶤嶥嶦嶧嶨嶩嶪嶫嶬嶭嶮嶯嶰嶱嶲嶳嶴嶵嶶嶷嶸嶹嶺嶻嶼嶽嶾嶿
+巀巁巂巃巄巅巆巇巈巉巊巋巌巍巎巏巐巑巒巓巔巕巖巗巘巙巚巛巜川州巟
+巠巡巢巣巤工左巧巨巩巪巫巬巭差巯巰己已巳巴巵巶巷巸巹巺巻巼巽巾巿
+帀币市布帄帅帆帇师帉帊帋希帍帎帏帐帑帒帓帔帕帖帗帘帙帚帛帜帝帞帟
+帠帡帢帣帤帥带帧帨帩帪師帬席帮帯帰帱帲帳帴帵帶帷常帹帺帻帼帽帾帿
+幀幁幂幃幄幅幆幇幈幉幊幋幌幍幎幏幐幑幒幓幔幕幖幗幘幙幚幛幜幝幞幟
+幠幡幢幣幤幥幦幧幨幩幪幫幬幭幮幯幰幱干平年幵并幷幸幹幺幻幼幽幾广
+庀庁庂広庄庅庆庇庈庉床庋庌庍庎序庐庑庒库应底庖店庘庙庚庛府庝庞废
+庠庡庢庣庤庥度座庨庩庪庫庬庭庮庯庰庱庲庳庴庵庶康庸庹庺庻庼庽庾庿
+廀廁廂廃廄廅廆廇廈廉廊廋廌廍廎廏廐廑廒廓廔廕廖廗廘廙廚廛廜廝廞廟
+廠廡廢廣廤廥廦廧廨廩廪廫廬廭廮廯廰廱廲廳廴廵延廷廸廹建廻廼廽廾廿
+开弁异弃弄弅弆弇弈弉弊弋弌弍弎式弐弑弒弓弔引弖弗弘弙弚弛弜弝弞弟
+张弡弢弣弤弥弦弧弨弩弪弫弬弭弮弯弰弱弲弳弴張弶強弸弹强弻弼弽弾弿
+彀彁彂彃彄彅彆彇彈彉彊彋彌彍彎彏彐彑归当彔录彖彗彘彙彚彛彜彝彞彟
+彠彡形彣彤彥彦彧彨彩彪彫彬彭彮彯彰影彲彳彴彵彶彷彸役彺彻彼彽彾彿
+往征徂徃径待徆徇很徉徊律後徍徎徏徐徑徒従徔徕徖得徘徙徚徛徜徝從徟
+徠御徢徣徤徥徦徧徨復循徫徬徭微徯徰徱徲徳徴徵徶德徸徹徺徻徼徽徾徿
+忀忁忂心忄必忆忇忈忉忊忋忌忍忎忏忐忑忒忓忔忕忖志忘忙忚忛応忝忞忟
+忠忡忢忣忤忥忦忧忨忩忪快忬忭忮忯忰忱忲忳忴念忶忷忸忹忺忻忼忽忾忿
+怀态怂怃怄怅怆怇怈怉怊怋怌怍怎怏怐怑怒怓怔怕怖怗怘怙怚怛怜思怞怟
+怠怡怢怣怤急怦性怨怩怪怫怬怭怮怯怰怱怲怳怴怵怶怷怸怹怺总怼怽怾怿
+恀恁恂恃恄恅恆恇恈恉恊恋恌恍恎恏恐恑恒恓恔恕恖恗恘恙恚恛恜恝恞恟
+恠恡恢恣恤恥恦恧恨恩恪恫恬恭恮息恰恱恲恳恴恵恶恷恸恹恺恻恼恽恾恿
+悀悁悂悃悄悅悆悇悈悉悊悋悌悍悎悏悐悑悒悓悔悕悖悗悘悙悚悛悜悝悞悟
+悠悡悢患悤悥悦悧您悩悪悫悬悭悮悯悰悱悲悳悴悵悶悷悸悹悺悻悼悽悾悿
+惀惁惂惃惄情惆惇惈惉惊惋惌惍惎惏惐惑惒惓惔惕惖惗惘惙惚惛惜惝惞惟
+惠惡惢惣惤惥惦惧惨惩惪惫惬惭惮惯惰惱惲想惴惵惶惷惸惹惺惻惼惽惾惿
+愀愁愂愃愄愅愆愇愈愉愊愋愌愍愎意愐愑愒愓愔愕愖愗愘愙愚愛愜愝愞感
+愠愡愢愣愤愥愦愧愨愩愪愫愬愭愮愯愰愱愲愳愴愵愶愷愸愹愺愻愼愽愾愿
+慀慁慂慃慄慅慆慇慈慉慊態慌慍慎慏慐慑慒慓慔慕慖慗慘慙慚慛慜慝慞慟
+慠慡慢慣慤慥慦慧慨慩慪慫慬慭慮慯慰慱慲慳慴慵慶慷慸慹慺慻慼慽慾慿
+憀憁憂憃憄憅憆憇憈憉憊憋憌憍憎憏憐憑憒憓憔憕憖憗憘憙憚憛憜憝憞憟
+憠憡憢憣憤憥憦憧憨憩憪憫憬憭憮憯憰憱憲憳憴憵憶憷憸憹憺憻憼憽憾憿
+懀懁懂懃懄懅懆懇懈應懊懋懌懍懎懏懐懑懒懓懔懕懖懗懘懙懚懛懜懝懞懟
+懠懡懢懣懤懥懦懧懨懩懪懫懬懭懮懯懰懱懲懳懴懵懶懷懸懹懺懻懼懽懾懿
+戀戁戂戃戄戅戆戇戈戉戊戋戌戍戎戏成我戒戓戔戕或戗战戙戚戛戜戝戞戟
+戠戡戢戣戤戥戦戧戨戩截戫戬戭戮戯戰戱戲戳戴戵戶户戸戹戺戻戼戽戾房
+所扁扂扃扄扅扆扇扈扉扊手扌才扎扏扐扑扒打扔払扖扗托扙扚扛扜扝扞扟
+扠扡扢扣扤扥扦执扨扩扪扫扬扭扮扯扰扱扲扳扴扵扶扷扸批扺扻扼扽找承
+技抁抂抃抄抅抆抇抈抉把抋抌抍抎抏抐抑抒抓抔投抖抗折抙抚抛抜抝択抟
+抠抡抢抣护报抦抧抨抩抪披抬抭抮抯抰抱抲抳抴抵抶抷抸抹抺抻押抽抾抿
+拀拁拂拃拄担拆拇拈拉拊拋拌拍拎拏拐拑拒拓拔拕拖拗拘拙拚招拜拝拞拟
+拠拡拢拣拤拥拦拧拨择拪拫括拭拮拯拰拱拲拳拴拵拶拷拸拹拺拻拼拽拾拿
+挀持挂挃挄挅挆指挈按挊挋挌挍挎挏挐挑挒挓挔挕挖挗挘挙挚挛挜挝挞挟
+挠挡挢挣挤挥挦挧挨挩挪挫挬挭挮振挰挱挲挳挴挵挶挷挸挹挺挻挼挽挾挿
+捀捁捂捃捄捅捆捇捈捉捊捋捌捍捎捏捐捑捒捓捔捕捖捗捘捙捚捛捜捝捞损
+捠捡换捣捤捥捦捧捨捩捪捫捬捭据捯捰捱捲捳捴捵捶捷捸捹捺捻捼捽捾捿
+掀掁掂掃掄掅掆掇授掉掊掋掌掍掎掏掐掑排掓掔掕掖掗掘掙掚掛掜掝掞掟
+掠採探掣掤接掦控推掩措掫掬掭掮掯掰掱掲掳掴掵掶掷掸掹掺掻掼掽掾掿
+揀揁揂揃揄揅揆揇揈揉揊揋揌揍揎描提揑插揓揔揕揖揗揘揙揚換揜揝揞揟
+揠握揢揣揤揥揦揧揨揩揪揫揬揭揮揯揰揱揲揳援揵揶揷揸揹揺揻揼揽揾揿
+搀搁搂搃搄搅搆搇搈搉搊搋搌損搎搏搐搑搒搓搔搕搖搗搘搙搚搛搜搝搞搟
+搠搡搢搣搤搥搦搧搨搩搪搫搬搭搮搯搰搱搲搳搴搵搶搷搸搹携搻搼搽搾搿
+摀摁摂摃摄摅摆摇摈摉摊摋摌摍摎摏摐摑摒摓摔摕摖摗摘摙摚摛摜摝摞摟
+摠摡摢摣摤摥摦摧摨摩摪摫摬摭摮摯摰摱摲摳摴摵摶摷摸摹摺摻摼摽摾摿
+撀撁撂撃撄撅撆撇撈撉撊撋撌撍撎撏撐撑撒撓撔撕撖撗撘撙撚撛撜撝撞撟
+撠撡撢撣撤撥撦撧撨撩撪撫撬播撮撯撰撱撲撳撴撵撶撷撸撹撺撻撼撽撾撿
+擀擁擂擃擄擅擆擇擈擉擊擋擌操擎擏擐擑擒擓擔擕擖擗擘擙據擛擜擝擞擟
+擠擡擢擣擤擥擦擧擨擩擪擫擬擭擮擯擰擱擲擳擴擵擶擷擸擹擺擻擼擽擾擿
+攀攁攂攃攄攅攆攇攈攉攊攋攌攍攎攏攐攑攒攓攔攕攖攗攘攙攚攛攜攝攞攟
+攠攡攢攣攤攥攦攧攨攩攪攫攬攭攮支攰攱攲攳攴攵收攷攸改攺攻攼攽放政
+敀敁敂敃敄故敆敇效敉敊敋敌敍敎敏敐救敒敓敔敕敖敗敘教敚敛敜敝敞敟
+敠敡敢散敤敥敦敧敨敩敪敫敬敭敮敯数敱敲敳整敵敶敷數敹敺敻敼敽敾敿
+斀斁斂斃斄斅斆文斈斉斊斋斌斍斎斏斐斑斒斓斔斕斖斗斘料斚斛斜斝斞斟
+斠斡斢斣斤斥斦斧斨斩斪斫斬断斮斯新斱斲斳斴斵斶斷斸方斺斻於施斾斿
+旀旁旂旃旄旅旆旇旈旉旊旋旌旍旎族旐旑旒旓旔旕旖旗旘旙旚旛旜旝旞旟
+无旡既旣旤日旦旧旨早旪旫旬旭旮旯旰旱旲旳旴旵时旷旸旹旺旻旼旽旾旿
+昀昁昂昃昄昅昆昇昈昉昊昋昌昍明昏昐昑昒易昔昕昖昗昘昙昚昛昜昝昞星
+映昡昢昣昤春昦昧昨昩昪昫昬昭昮是昰昱昲昳昴昵昶昷昸昹昺昻昼昽显昿
+晀晁時晃晄晅晆晇晈晉晊晋晌晍晎晏晐晑晒晓晔晕晖晗晘晙晚晛晜晝晞晟
+晠晡晢晣晤晥晦晧晨晩晪晫晬晭普景晰晱晲晳晴晵晶晷晸晹智晻晼晽晾晿
+暀暁暂暃暄暅暆暇暈暉暊暋暌暍暎暏暐暑暒暓暔暕暖暗暘暙暚暛暜暝暞暟
+暠暡暢暣暤暥暦暧暨暩暪暫暬暭暮暯暰暱暲暳暴暵暶暷暸暹暺暻暼暽暾暿
+曀曁曂曃曄曅曆曇曈曉曊曋曌曍曎曏曐曑曒曓曔曕曖曗曘曙曚曛曜曝曞曟
+曠曡曢曣曤曥曦曧曨曩曪曫曬曭曮曯曰曱曲曳更曵曶曷書曹曺曻曼曽曾替
+最朁朂會朄朅朆朇月有朊朋朌服朎朏朐朑朒朓朔朕朖朗朘朙朚望朜朝朞期
+朠朡朢朣朤朥朦朧木朩未末本札朮术朰朱朲朳朴朵朶朷朸朹机朻朼朽朾朿
+杀杁杂权杄杅杆杇杈杉杊杋杌杍李杏材村杒杓杔杕杖杗杘杙杚杛杜杝杞束
+杠条杢杣杤来杦杧杨杩杪杫杬杭杮杯杰東杲杳杴杵杶杷杸杹杺杻杼杽松板
+枀极枂枃构枅枆枇枈枉枊枋枌枍枎枏析枑枒枓枔枕枖林枘枙枚枛果枝枞枟
+枠枡枢枣枤枥枦枧枨枩枪枫枬枭枮枯枰枱枲枳枴枵架枷枸枹枺枻枼枽枾枿
+柀柁柂柃柄柅柆柇柈柉柊柋柌柍柎柏某柑柒染柔柕柖柗柘柙柚柛柜柝柞柟
+柠柡柢柣柤查柦柧柨柩柪柫柬柭柮柯柰柱柲柳柴柵柶柷柸柹柺査柼柽柾柿
+栀栁栂栃栄栅栆标栈栉栊栋栌栍栎栏栐树栒栓栔栕栖栗栘栙栚栛栜栝栞栟
+栠校栢栣栤栥栦栧栨栩株栫栬栭栮栯栰栱栲栳栴栵栶样核根栺栻格栽栾栿
+桀桁桂桃桄桅框桇案桉桊桋桌桍桎桏桐桑桒桓桔桕桖桗桘桙桚桛桜桝桞桟
+桠桡桢档桤桥桦桧桨桩桪桫桬桭桮桯桰桱桲桳桴桵桶桷桸桹桺桻桼桽桾桿
+梀梁梂梃梄梅梆梇梈梉梊梋梌梍梎梏梐梑梒梓梔梕梖梗梘梙梚梛梜條梞梟
+梠梡梢梣梤梥梦梧梨梩梪梫梬梭梮梯械梱梲梳梴梵梶梷梸梹梺梻梼梽梾梿
+检棁棂棃棄棅棆棇棈棉棊棋棌棍棎棏棐棑棒棓棔棕棖棗棘棙棚棛棜棝棞棟
+棠棡棢棣棤棥棦棧棨棩棪棫棬棭森棯棰棱棲棳棴棵棶棷棸棹棺棻棼棽棾棿
+椀椁椂椃椄椅椆椇椈椉椊椋椌植椎椏椐椑椒椓椔椕椖椗椘椙椚椛検椝椞椟
+椠椡椢椣椤椥椦椧椨椩椪椫椬椭椮椯椰椱椲椳椴椵椶椷椸椹椺椻椼椽椾椿
+楀楁楂楃楄楅楆楇楈楉楊楋楌楍楎楏楐楑楒楓楔楕楖楗楘楙楚楛楜楝楞楟
+楠楡楢楣楤楥楦楧楨楩楪楫楬業楮楯楰楱楲楳楴極楶楷楸楹楺楻楼楽楾楿
+榀榁概榃榄榅榆榇榈榉榊榋榌榍榎榏榐榑榒榓榔榕榖榗榘榙榚榛榜榝榞榟
+榠榡榢榣榤榥榦榧榨榩榪榫榬榭榮榯榰榱榲榳榴榵榶榷榸榹榺榻榼榽榾榿
+槀槁槂槃槄槅槆槇槈槉槊構槌槍槎槏槐槑槒槓槔槕槖槗様槙槚槛槜槝槞槟
+槠槡槢槣槤槥槦槧槨槩槪槫槬槭槮槯槰槱槲槳槴槵槶槷槸槹槺槻槼槽槾槿
+樀樁樂樃樄樅樆樇樈樉樊樋樌樍樎樏樐樑樒樓樔樕樖樗樘標樚樛樜樝樞樟
+樠模樢樣樤樥樦樧樨権横樫樬樭樮樯樰樱樲樳樴樵樶樷樸樹樺樻樼樽樾樿
+橀橁橂橃橄橅橆橇橈橉橊橋橌橍橎橏橐橑橒橓橔橕橖橗橘橙橚橛橜橝橞機
+橠橡橢橣橤橥橦橧橨橩橪橫橬橭橮橯橰橱橲橳橴橵橶橷橸橹橺橻橼橽橾橿
+檀檁檂檃檄檅檆檇檈檉檊檋檌檍檎檏檐檑檒檓檔檕檖檗檘檙檚檛檜檝檞檟
+檠檡檢檣檤檥檦檧檨檩檪檫檬檭檮檯檰檱檲檳檴檵檶檷檸檹檺檻檼檽檾檿
+櫀櫁櫂櫃櫄櫅櫆櫇櫈櫉櫊櫋櫌櫍櫎櫏櫐櫑櫒櫓櫔櫕櫖櫗櫘櫙櫚櫛櫜櫝櫞櫟
+櫠櫡櫢櫣櫤櫥櫦櫧櫨櫩櫪櫫櫬櫭櫮櫯櫰櫱櫲櫳櫴櫵櫶櫷櫸櫹櫺櫻櫼櫽櫾櫿
+欀欁欂欃欄欅欆欇欈欉權欋欌欍欎欏欐欑欒欓欔欕欖欗欘欙欚欛欜欝欞欟
+欠次欢欣欤欥欦欧欨欩欪欫欬欭欮欯欰欱欲欳欴欵欶欷欸欹欺欻欼欽款欿
+歀歁歂歃歄歅歆歇歈歉歊歋歌歍歎歏歐歑歒歓歔歕歖歗歘歙歚歛歜歝歞歟
+歠歡止正此步武歧歨歩歪歫歬歭歮歯歰歱歲歳歴歵歶歷歸歹歺死歼歽歾歿
+殀殁殂殃殄殅殆殇殈殉殊残殌殍殎殏殐殑殒殓殔殕殖殗殘殙殚殛殜殝殞殟
+殠殡殢殣殤殥殦殧殨殩殪殫殬殭殮殯殰殱殲殳殴段殶殷殸殹殺殻殼殽殾殿
+毀毁毂毃毄毅毆毇毈毉毊毋毌母毎每毐毑毒毓比毕毖毗毘毙毚毛毜毝毞毟
+毠毡毢毣毤毥毦毧毨毩毪毫毬毭毮毯毰毱毲毳毴毵毶毷毸毹毺毻毼毽毾毿
+氀氁氂氃氄氅氆氇氈氉氊氋氌氍氎氏氐民氒氓气氕氖気氘氙氚氛氜氝氞氟
+氠氡氢氣氤氥氦氧氨氩氪氫氬氭氮氯氰氱氲氳水氵氶氷永氹氺氻氼氽氾氿
+汀汁求汃汄汅汆汇汈汉汊汋汌汍汎汏汐汑汒汓汔汕汖汗汘汙汚汛汜汝汞江
+池污汢汣汤汥汦汧汨汩汪汫汬汭汮汯汰汱汲汳汴汵汶汷汸汹決汻汼汽汾汿
+沀沁沂沃沄沅沆沇沈沉沊沋沌沍沎沏沐沑沒沓沔沕沖沗沘沙沚沛沜沝沞沟
+沠没沢沣沤沥沦沧沨沩沪沫沬沭沮沯沰沱沲河沴沵沶沷沸油沺治沼沽沾沿
+泀況泂泃泄泅泆泇泈泉泊泋泌泍泎泏泐泑泒泓泔法泖泗泘泙泚泛泜泝泞泟
+泠泡波泣泤泥泦泧注泩泪泫泬泭泮泯泰泱泲泳泴泵泶泷泸泹泺泻泼泽泾泿
+洀洁洂洃洄洅洆洇洈洉洊洋洌洍洎洏洐洑洒洓洔洕洖洗洘洙洚洛洜洝洞洟
+洠洡洢洣洤津洦洧洨洩洪洫洬洭洮洯洰洱洲洳洴洵洶洷洸洹洺活洼洽派洿
+浀流浂浃浄浅浆浇浈浉浊测浌浍济浏浐浑浒浓浔浕浖浗浘浙浚浛浜浝浞浟
+浠浡浢浣浤浥浦浧浨浩浪浫浬浭浮浯浰浱浲浳浴浵浶海浸浹浺浻浼浽浾浿
+涀涁涂涃涄涅涆涇消涉涊涋涌涍涎涏涐涑涒涓涔涕涖涗涘涙涚涛涜涝涞涟
+涠涡涢涣涤涥润涧涨涩涪涫涬涭涮涯涰涱液涳涴涵涶涷涸涹涺涻涼涽涾涿
+淀淁淂淃淄淅淆淇淈淉淊淋淌淍淎淏淐淑淒淓淔淕淖淗淘淙淚淛淜淝淞淟
+淠淡淢淣淤淥淦淧淨淩淪淫淬淭淮淯淰深淲淳淴淵淶混淸淹淺添淼淽淾淿
+渀渁渂渃渄清渆渇済渉渊渋渌渍渎渏渐渑渒渓渔渕渖渗渘渙渚減渜渝渞渟
+渠渡渢渣渤渥渦渧渨温渪渫測渭渮港渰渱渲渳渴渵渶渷游渹渺渻渼渽渾渿
+湀湁湂湃湄湅湆湇湈湉湊湋湌湍湎湏湐湑湒湓湔湕湖湗湘湙湚湛湜湝湞湟
+湠湡湢湣湤湥湦湧湨湩湪湫湬湭湮湯湰湱湲湳湴湵湶湷湸湹湺湻湼湽湾湿
+満溁溂溃溄溅溆溇溈溉溊溋溌溍溎溏源溑溒溓溔溕準溗溘溙溚溛溜溝溞溟
+溠溡溢溣溤溥溦溧溨溩溪溫溬溭溮溯溰溱溲溳溴溵溶溷溸溹溺溻溼溽溾溿
+滀滁滂滃滄滅滆滇滈滉滊滋滌滍滎滏滐滑滒滓滔滕滖滗滘滙滚滛滜滝滞滟
+滠满滢滣滤滥滦滧滨滩滪滫滬滭滮滯滰滱滲滳滴滵滶滷滸滹滺滻滼滽滾滿
+漀漁漂漃漄漅漆漇漈漉漊漋漌漍漎漏漐漑漒漓演漕漖漗漘漙漚漛漜漝漞漟
+漠漡漢漣漤漥漦漧漨漩漪漫漬漭漮漯漰漱漲漳漴漵漶漷漸漹漺漻漼漽漾漿
+潀潁潂潃潄潅潆潇潈潉潊潋潌潍潎潏潐潑潒潓潔潕潖潗潘潙潚潛潜潝潞潟
+潠潡潢潣潤潥潦潧潨潩潪潫潬潭潮潯潰潱潲潳潴潵潶潷潸潹潺潻潼潽潾潿
+澀澁澂澃澄澅澆澇澈澉澊澋澌澍澎澏澐澑澒澓澔澕澖澗澘澙澚澛澜澝澞澟
+澠澡澢澣澤澥澦澧澨澩澪澫澬澭澮澯澰澱澲澳澴澵澶澷澸澹澺澻澼澽澾澿
+激濁濂濃濄濅濆濇濈濉濊濋濌濍濎濏濐濑濒濓濔濕濖濗濘濙濚濛濜濝濞濟
+濠濡濢濣濤濥濦濧濨濩濪濫濬濭濮濯濰濱濲濳濴濵濶濷濸濹濺濻濼濽濾濿
+瀀瀁瀂瀃瀄瀅瀆瀇瀈瀉瀊瀋瀌瀍瀎瀏瀐瀑瀒瀓瀔瀕瀖瀗瀘瀙瀚瀛瀜瀝瀞瀟
+瀠瀡瀢瀣瀤瀥瀦瀧瀨瀩瀪瀫瀬瀭瀮瀯瀰瀱瀲瀳瀴瀵瀶瀷瀸瀹瀺瀻瀼瀽瀾瀿
+灀灁灂灃灄灅灆灇灈灉灊灋灌灍灎灏灐灑灒灓灔灕灖灗灘灙灚灛灜灝灞灟
+灠灡灢灣灤灥灦灧灨灩灪火灬灭灮灯灰灱灲灳灴灵灶灷灸灹灺灻灼災灾灿
+炀炁炂炃炄炅炆炇炈炉炊炋炌炍炎炏炐炑炒炓炔炕炖炗炘炙炚炛炜炝炞炟
+炠炡炢炣炤炥炦炧炨炩炪炫炬炭炮炯炰炱炲炳炴炵炶炷炸点為炻炼炽炾炿
+烀烁烂烃烄烅烆烇烈烉烊烋烌烍烎烏烐烑烒烓烔烕烖烗烘烙烚烛烜烝烞烟
+烠烡烢烣烤烥烦烧烨烩烪烫烬热烮烯烰烱烲烳烴烵烶烷烸烹烺烻烼烽烾烿
+焀焁焂焃焄焅焆焇焈焉焊焋焌焍焎焏焐焑焒焓焔焕焖焗焘焙焚焛焜焝焞焟
+焠無焢焣焤焥焦焧焨焩焪焫焬焭焮焯焰焱焲焳焴焵然焷焸焹焺焻焼焽焾焿
+煀煁煂煃煄煅煆煇煈煉煊煋煌煍煎煏煐煑煒煓煔煕煖煗煘煙煚煛煜煝煞煟
+煠煡煢煣煤煥煦照煨煩煪煫煬煭煮煯煰煱煲煳煴煵煶煷煸煹煺煻煼煽煾煿
+熀熁熂熃熄熅熆熇熈熉熊熋熌熍熎熏熐熑熒熓熔熕熖熗熘熙熚熛熜熝熞熟
+熠熡熢熣熤熥熦熧熨熩熪熫熬熭熮熯熰熱熲熳熴熵熶熷熸熹熺熻熼熽熾熿
+燀燁燂燃燄燅燆燇燈燉燊燋燌燍燎燏燐燑燒燓燔燕燖燗燘燙燚燛燜燝燞營
+燠燡燢燣燤燥燦燧燨燩燪燫燬燭燮燯燰燱燲燳燴燵燶燷燸燹燺燻燼燽燾燿
+爀爁爂爃爄爅爆爇爈爉爊爋爌爍爎爏爐爑爒爓爔爕爖爗爘爙爚爛爜爝爞爟
+爠爡爢爣爤爥爦爧爨爩爪爫爬爭爮爯爰爱爲爳爴爵父爷爸爹爺爻爼爽爾爿
+牀牁牂牃牄牅牆片版牉牊牋牌牍牎牏牐牑牒牓牔牕牖牗牘牙牚牛牜牝牞牟
+牠牡牢牣牤牥牦牧牨物牪牫牬牭牮牯牰牱牲牳牴牵牶牷牸特牺牻牼牽牾牿
+犀犁犂犃犄犅犆犇犈犉犊犋犌犍犎犏犐犑犒犓犔犕犖犗犘犙犚犛犜犝犞犟
+犠犡犢犣犤犥犦犧犨犩犪犫犬犭犮犯犰犱犲犳犴犵状犷犸犹犺犻犼犽犾犿
+狀狁狂狃狄狅狆狇狈狉狊狋狌狍狎狏狐狑狒狓狔狕狖狗狘狙狚狛狜狝狞狟
+狠狡狢狣狤狥狦狧狨狩狪狫独狭狮狯狰狱狲狳狴狵狶狷狸狹狺狻狼狽狾狿
+猀猁猂猃猄猅猆猇猈猉猊猋猌猍猎猏猐猑猒猓猔猕猖猗猘猙猚猛猜猝猞猟
+猠猡猢猣猤猥猦猧猨猩猪猫猬猭献猯猰猱猲猳猴猵猶猷猸猹猺猻猼猽猾猿
+獀獁獂獃獄獅獆獇獈獉獊獋獌獍獎獏獐獑獒獓獔獕獖獗獘獙獚獛獜獝獞獟
+獠獡獢獣獤獥獦獧獨獩獪獫獬獭獮獯獰獱獲獳獴獵獶獷獸獹獺獻獼獽獾獿
+玀玁玂玃玄玅玆率玈玉玊王玌玍玎玏玐玑玒玓玔玕玖玗玘玙玚玛玜玝玞玟
+玠玡玢玣玤玥玦玧玨玩玪玫玬玭玮环现玱玲玳玴玵玶玷玸玹玺玻玼玽玾玿
+珀珁珂珃珄珅珆珇珈珉珊珋珌珍珎珏珐珑珒珓珔珕珖珗珘珙珚珛珜珝珞珟
+珠珡珢珣珤珥珦珧珨珩珪珫珬班珮珯珰珱珲珳珴珵珶珷珸珹珺珻珼珽現珿
+琀琁琂球琄琅理琇琈琉琊琋琌琍琎琏琐琑琒琓琔琕琖琗琘琙琚琛琜琝琞琟
+琠琡琢琣琤琥琦琧琨琩琪琫琬琭琮琯琰琱琲琳琴琵琶琷琸琹琺琻琼琽琾琿
+瑀瑁瑂瑃瑄瑅瑆瑇瑈瑉瑊瑋瑌瑍瑎瑏瑐瑑瑒瑓瑔瑕瑖瑗瑘瑙瑚瑛瑜瑝瑞瑟
+瑠瑡瑢瑣瑤瑥瑦瑧瑨瑩瑪瑫瑬瑭瑮瑯瑰瑱瑲瑳瑴瑵瑶瑷瑸瑹瑺瑻瑼瑽瑾瑿
+璀璁璂璃璄璅璆璇璈璉璊璋璌璍璎璏璐璑璒璓璔璕璖璗璘璙璚璛璜璝璞璟
+璠璡璢璣璤璥璦璧璨璩璪璫璬璭璮璯環璱璲璳璴璵璶璷璸璹璺璻璼璽璾璿
+瓀瓁瓂瓃瓄瓅瓆瓇瓈瓉瓊瓋瓌瓍瓎瓏瓐瓑瓒瓓瓔瓕瓖瓗瓘瓙瓚瓛瓜瓝瓞瓟
+瓠瓡瓢瓣瓤瓥瓦瓧瓨瓩瓪瓫瓬瓭瓮瓯瓰瓱瓲瓳瓴瓵瓶瓷瓸瓹瓺瓻瓼瓽瓾瓿
+甀甁甂甃甄甅甆甇甈甉甊甋甌甍甎甏甐甑甒甓甔甕甖甗甘甙甚甛甜甝甞生
+甠甡產産甤甥甦甧用甩甪甫甬甭甮甯田由甲申甴电甶男甸甹町画甼甽甾甿
+畀畁畂畃畄畅畆畇畈畉畊畋界畍畎畏畐畑畒畓畔畕畖畗畘留畚畛畜畝畞畟
+畠畡畢畣畤略畦畧畨畩番畫畬畭畮畯異畱畲畳畴畵當畷畸畹畺畻畼畽畾畿
+疀疁疂疃疄疅疆疇疈疉疊疋疌疍疎疏疐疑疒疓疔疕疖疗疘疙疚疛疜疝疞疟
+疠疡疢疣疤疥疦疧疨疩疪疫疬疭疮疯疰疱疲疳疴疵疶疷疸疹疺疻疼疽疾疿
+痀痁痂痃痄病痆症痈痉痊痋痌痍痎痏痐痑痒痓痔痕痖痗痘痙痚痛痜痝痞痟
+痠痡痢痣痤痥痦痧痨痩痪痫痬痭痮痯痰痱痲痳痴痵痶痷痸痹痺痻痼痽痾痿
+瘀瘁瘂瘃瘄瘅瘆瘇瘈瘉瘊瘋瘌瘍瘎瘏瘐瘑瘒瘓瘔瘕瘖瘗瘘瘙瘚瘛瘜瘝瘞瘟
+瘠瘡瘢瘣瘤瘥瘦瘧瘨瘩瘪瘫瘬瘭瘮瘯瘰瘱瘲瘳瘴瘵瘶瘷瘸瘹瘺瘻瘼瘽瘾瘿
+癀癁療癃癄癅癆癇癈癉癊癋癌癍癎癏癐癑癒癓癔癕癖癗癘癙癚癛癜癝癞癟
+癠癡癢癣癤癥癦癧癨癩癪癫癬癭癮癯癰癱癲癳癴癵癶癷癸癹発登發白百癿
+皀皁皂皃的皅皆皇皈皉皊皋皌皍皎皏皐皑皒皓皔皕皖皗皘皙皚皛皜皝皞皟
+皠皡皢皣皤皥皦皧皨皩皪皫皬皭皮皯皰皱皲皳皴皵皶皷皸皹皺皻皼皽皾皿
+盀盁盂盃盄盅盆盇盈盉益盋盌盍盎盏盐监盒盓盔盕盖盗盘盙盚盛盜盝盞盟
+盠盡盢監盤盥盦盧盨盩盪盫盬盭目盯盰盱盲盳直盵盶盷相盹盺盻盼盽盾盿
+眀省眂眃眄眅眆眇眈眉眊看県眍眎眏眐眑眒眓眔眕眖眗眘眙眚眛眜眝眞真
+眠眡眢眣眤眥眦眧眨眩眪眫眬眭眮眯眰眱眲眳眴眵眶眷眸眹眺眻眼眽眾眿
+着睁睂睃睄睅睆睇睈睉睊睋睌睍睎睏睐睑睒睓睔睕睖睗睘睙睚睛睜睝睞睟
+睠睡睢督睤睥睦睧睨睩睪睫睬睭睮睯睰睱睲睳睴睵睶睷睸睹睺睻睼睽睾睿
+瞀瞁瞂瞃瞄瞅瞆瞇瞈瞉瞊瞋瞌瞍瞎瞏瞐瞑瞒瞓瞔瞕瞖瞗瞘瞙瞚瞛瞜瞝瞞瞟
+瞠瞡瞢瞣瞤瞥瞦瞧瞨瞩瞪瞫瞬瞭瞮瞯瞰瞱瞲瞳瞴瞵瞶瞷瞸瞹瞺瞻瞼瞽瞾瞿
+矀矁矂矃矄矅矆矇矈矉矊矋矌矍矎矏矐矑矒矓矔矕矖矗矘矙矚矛矜矝矞矟
+矠矡矢矣矤知矦矧矨矩矪矫矬短矮矯矰矱矲石矴矵矶矷矸矹矺矻矼矽矾矿
+砀码砂砃砄砅砆砇砈砉砊砋砌砍砎砏砐砑砒砓研砕砖砗砘砙砚砛砜砝砞砟
+砠砡砢砣砤砥砦砧砨砩砪砫砬砭砮砯砰砱砲砳破砵砶砷砸砹砺砻砼砽砾砿
+础硁硂硃硄硅硆硇硈硉硊硋硌硍硎硏硐硑硒硓硔硕硖硗硘硙硚硛硜硝硞硟
+硠硡硢硣硤硥硦硧硨硩硪硫硬硭确硯硰硱硲硳硴硵硶硷硸硹硺硻硼硽硾硿
+碀碁碂碃碄碅碆碇碈碉碊碋碌碍碎碏碐碑碒碓碔碕碖碗碘碙碚碛碜碝碞碟
+碠碡碢碣碤碥碦碧碨碩碪碫碬碭碮碯碰碱碲碳碴碵碶碷碸碹確碻碼碽碾碿
+磀磁磂磃磄磅磆磇磈磉磊磋磌磍磎磏磐磑磒磓磔磕磖磗磘磙磚磛磜磝磞磟
+磠磡磢磣磤磥磦磧磨磩磪磫磬磭磮磯磰磱磲磳磴磵磶磷磸磹磺磻磼磽磾磿
+礀礁礂礃礄礅礆礇礈礉礊礋礌礍礎礏礐礑礒礓礔礕礖礗礘礙礚礛礜礝礞礟
+礠礡礢礣礤礥礦礧礨礩礪礫礬礭礮礯礰礱礲礳礴礵礶礷礸礹示礻礼礽社礿
+祀祁祂祃祄祅祆祇祈祉祊祋祌祍祎祏祐祑祒祓祔祕祖祗祘祙祚祛祜祝神祟
+祠祡祢祣祤祥祦祧票祩祪祫祬祭祮祯祰祱祲祳祴祵祶祷祸祹祺祻祼祽祾祿
+禀禁禂禃禄禅禆禇禈禉禊禋禌禍禎福禐禑禒禓禔禕禖禗禘禙禚禛禜禝禞禟
+禠禡禢禣禤禥禦禧禨禩禪禫禬禭禮禯禰禱禲禳禴禵禶禷禸禹禺离禼禽禾禿
+秀私秂秃秄秅秆秇秈秉秊秋秌种秎秏秐科秒秓秔秕秖秗秘秙秚秛秜秝秞租
+秠秡秢秣秤秥秦秧秨秩秪秫秬秭秮积称秱秲秳秴秵秶秷秸秹秺移秼秽秾秿
+稀稁稂稃稄稅稆稇稈稉稊程稌稍税稏稐稑稒稓稔稕稖稗稘稙稚稛稜稝稞稟
+稠稡稢稣稤稥稦稧稨稩稪稫稬稭種稯稰稱稲稳稴稵稶稷稸稹稺稻稼稽稾稿
+穀穁穂穃穄穅穆穇穈穉穊穋穌積穎穏穐穑穒穓穔穕穖穗穘穙穚穛穜穝穞穟
+穠穡穢穣穤穥穦穧穨穩穪穫穬穭穮穯穰穱穲穳穴穵究穷穸穹空穻穼穽穾穿
+窀突窂窃窄窅窆窇窈窉窊窋窌窍窎窏窐窑窒窓窔窕窖窗窘窙窚窛窜窝窞窟
+窠窡窢窣窤窥窦窧窨窩窪窫窬窭窮窯窰窱窲窳窴窵窶窷窸窹窺窻窼窽窾窿
+竀竁竂竃竄竅竆竇竈竉竊立竌竍竎竏竐竑竒竓竔竕竖竗竘站竚竛竜竝竞竟
+章竡竢竣竤童竦竧竨竩竪竫竬竭竮端竰竱竲竳竴竵競竷竸竹竺竻竼竽竾竿
+笀笁笂笃笄笅笆笇笈笉笊笋笌笍笎笏笐笑笒笓笔笕笖笗笘笙笚笛笜笝笞笟
+笠笡笢笣笤笥符笧笨笩笪笫第笭笮笯笰笱笲笳笴笵笶笷笸笹笺笻笼笽笾笿
+筀筁筂筃筄筅筆筇筈等筊筋筌筍筎筏筐筑筒筓答筕策筗筘筙筚筛筜筝筞筟
+筠筡筢筣筤筥筦筧筨筩筪筫筬筭筮筯筰筱筲筳筴筵筶筷筸筹筺筻筼筽签筿
+简箁箂箃箄箅箆箇箈箉箊箋箌箍箎箏箐箑箒箓箔箕箖算箘箙箚箛箜箝箞箟
+箠管箢箣箤箥箦箧箨箩箪箫箬箭箮箯箰箱箲箳箴箵箶箷箸箹箺箻箼箽箾箿
+節篁篂篃範篅篆篇篈築篊篋篌篍篎篏篐篑篒篓篔篕篖篗篘篙篚篛篜篝篞篟
+篠篡篢篣篤篥篦篧篨篩篪篫篬篭篮篯篰篱篲篳篴篵篶篷篸篹篺篻篼篽篾篿
+簀簁簂簃簄簅簆簇簈簉簊簋簌簍簎簏簐簑簒簓簔簕簖簗簘簙簚簛簜簝簞簟
+簠簡簢簣簤簥簦簧簨簩簪簫簬簭簮簯簰簱簲簳簴簵簶簷簸簹簺簻簼簽簾簿
+籀籁籂籃籄籅籆籇籈籉籊籋籌籍籎籏籐籑籒籓籔籕籖籗籘籙籚籛籜籝籞籟
+籠籡籢籣籤籥籦籧籨籩籪籫籬籭籮籯籰籱籲米籴籵籶籷籸籹籺类籼籽籾籿
+粀粁粂粃粄粅粆粇粈粉粊粋粌粍粎粏粐粑粒粓粔粕粖粗粘粙粚粛粜粝粞粟
+粠粡粢粣粤粥粦粧粨粩粪粫粬粭粮粯粰粱粲粳粴粵粶粷粸粹粺粻粼粽精粿
+糀糁糂糃糄糅糆糇糈糉糊糋糌糍糎糏糐糑糒糓糔糕糖糗糘糙糚糛糜糝糞糟
+糠糡糢糣糤糥糦糧糨糩糪糫糬糭糮糯糰糱糲糳糴糵糶糷糸糹糺系糼糽糾糿
+紀紁紂紃約紅紆紇紈紉紊紋紌納紎紏紐紑紒紓純紕紖紗紘紙級紛紜紝紞紟
+素紡索紣紤紥紦紧紨紩紪紫紬紭紮累細紱紲紳紴紵紶紷紸紹紺紻紼紽紾紿
+絀絁終絃組絅絆絇絈絉絊絋経絍絎絏結絑絒絓絔絕絖絗絘絙絚絛絜絝絞絟
+絠絡絢絣絤絥給絧絨絩絪絫絬絭絮絯絰統絲絳絴絵絶絷絸絹絺絻絼絽絾絿
+綀綁綂綃綄綅綆綇綈綉綊綋綌綍綎綏綐綑綒經綔綕綖綗綘継続綛綜綝綞綟
+綠綡綢綣綤綥綦綧綨綩綪綫綬維綮綯綰綱網綳綴綵綶綷綸綹綺綻綼綽綾綿
+緀緁緂緃緄緅緆緇緈緉緊緋緌緍緎総緐緑緒緓緔緕緖緗緘緙線緛緜緝緞緟
+締緡緢緣緤緥緦緧編緩緪緫緬緭緮緯緰緱緲緳練緵緶緷緸緹緺緻緼緽緾緿
+縀縁縂縃縄縅縆縇縈縉縊縋縌縍縎縏縐縑縒縓縔縕縖縗縘縙縚縛縜縝縞縟
+縠縡縢縣縤縥縦縧縨縩縪縫縬縭縮縯縰縱縲縳縴縵縶縷縸縹縺縻縼總績縿
+繀繁繂繃繄繅繆繇繈繉繊繋繌繍繎繏繐繑繒繓織繕繖繗繘繙繚繛繜繝繞繟
+繠繡繢繣繤繥繦繧繨繩繪繫繬繭繮繯繰繱繲繳繴繵繶繷繸繹繺繻繼繽繾繿
+纀纁纂纃纄纅纆纇纈纉纊纋續纍纎纏纐纑纒纓纔纕纖纗纘纙纚纛纜纝纞纟
+纠纡红纣纤纥约级纨纩纪纫纬纭纮纯纰纱纲纳纴纵纶纷纸纹纺纻纼纽纾线
+绀绁绂练组绅细织终绉绊绋绌绍绎经绐绑绒结绔绕绖绗绘给绚绛络绝绞统
+绠绡绢绣绤绥绦继绨绩绪绫绬续绮绯绰绱绲绳维绵绶绷绸绹绺绻综绽绾绿
+缀缁缂缃缄缅缆缇缈缉缊缋缌缍缎缏缐缑缒缓缔缕编缗缘缙缚缛缜缝缞缟
+缠缡缢缣缤缥缦缧缨缩缪缫缬缭缮缯缰缱缲缳缴缵缶缷缸缹缺缻缼缽缾缿
+罀罁罂罃罄罅罆罇罈罉罊罋罌罍罎罏罐网罒罓罔罕罖罗罘罙罚罛罜罝罞罟
+罠罡罢罣罤罥罦罧罨罩罪罫罬罭置罯罰罱署罳罴罵罶罷罸罹罺罻罼罽罾罿
+羀羁羂羃羄羅羆羇羈羉羊羋羌羍美羏羐羑羒羓羔羕羖羗羘羙羚羛羜羝羞羟
+羠羡羢羣群羥羦羧羨義羪羫羬羭羮羯羰羱羲羳羴羵羶羷羸羹羺羻羼羽羾羿
+翀翁翂翃翄翅翆翇翈翉翊翋翌翍翎翏翐翑習翓翔翕翖翗翘翙翚翛翜翝翞翟
+翠翡翢翣翤翥翦翧翨翩翪翫翬翭翮翯翰翱翲翳翴翵翶翷翸翹翺翻翼翽翾翿
+耀老耂考耄者耆耇耈耉耊耋而耍耎耏耐耑耒耓耔耕耖耗耘耙耚耛耜耝耞耟
+耠耡耢耣耤耥耦耧耨耩耪耫耬耭耮耯耰耱耲耳耴耵耶耷耸耹耺耻耼耽耾耿
+聀聁聂聃聄聅聆聇聈聉聊聋职聍聎聏聐聑聒聓联聕聖聗聘聙聚聛聜聝聞聟
+聠聡聢聣聤聥聦聧聨聩聪聫聬聭聮聯聰聱聲聳聴聵聶職聸聹聺聻聼聽聾聿
+肀肁肂肃肄肅肆肇肈肉肊肋肌肍肎肏肐肑肒肓肔肕肖肗肘肙肚肛肜肝肞肟
+肠股肢肣肤肥肦肧肨肩肪肫肬肭肮肯肰肱育肳肴肵肶肷肸肹肺肻肼肽肾肿
+胀胁胂胃胄胅胆胇胈胉胊胋背胍胎胏胐胑胒胓胔胕胖胗胘胙胚胛胜胝胞胟
+胠胡胢胣胤胥胦胧胨胩胪胫胬胭胮胯胰胱胲胳胴胵胶胷胸胹胺胻胼能胾胿
+脀脁脂脃脄脅脆脇脈脉脊脋脌脍脎脏脐脑脒脓脔脕脖脗脘脙脚脛脜脝脞脟
+脠脡脢脣脤脥脦脧脨脩脪脫脬脭脮脯脰脱脲脳脴脵脶脷脸脹脺脻脼脽脾脿
+腀腁腂腃腄腅腆腇腈腉腊腋腌腍腎腏腐腑腒腓腔腕腖腗腘腙腚腛腜腝腞腟
+腠腡腢腣腤腥腦腧腨腩腪腫腬腭腮腯腰腱腲腳腴腵腶腷腸腹腺腻腼腽腾腿
+膀膁膂膃膄膅膆膇膈膉膊膋膌膍膎膏膐膑膒膓膔膕膖膗膘膙膚膛膜膝膞膟
+膠膡膢膣膤膥膦膧膨膩膪膫膬膭膮膯膰膱膲膳膴膵膶膷膸膹膺膻膼膽膾膿
+臀臁臂臃臄臅臆臇臈臉臊臋臌臍臎臏臐臑臒臓臔臕臖臗臘臙臚臛臜臝臞臟
+臠臡臢臣臤臥臦臧臨臩自臫臬臭臮臯臰臱臲至致臵臶臷臸臹臺臻臼臽臾臿
+舀舁舂舃舄舅舆與興舉舊舋舌舍舎舏舐舑舒舓舔舕舖舗舘舙舚舛舜舝舞舟
+舠舡舢舣舤舥舦舧舨舩航舫般舭舮舯舰舱舲舳舴舵舶舷舸船舺舻舼舽舾舿
+艀艁艂艃艄艅艆艇艈艉艊艋艌艍艎艏艐艑艒艓艔艕艖艗艘艙艚艛艜艝艞艟
+艠艡艢艣艤艥艦艧艨艩艪艫艬艭艮良艰艱色艳艴艵艶艷艸艹艺艻艼艽艾艿
+芀芁节芃芄芅芆芇芈芉芊芋芌芍芎芏芐芑芒芓芔芕芖芗芘芙芚芛芜芝芞芟
+芠芡芢芣芤芥芦芧芨芩芪芫芬芭芮芯芰花芲芳芴芵芶芷芸芹芺芻芼芽芾芿
+苀苁苂苃苄苅苆苇苈苉苊苋苌苍苎苏苐苑苒苓苔苕苖苗苘苙苚苛苜苝苞苟
+苠苡苢苣苤若苦苧苨苩苪苫苬苭苮苯苰英苲苳苴苵苶苷苸苹苺苻苼苽苾苿
+茀茁茂范茄茅茆茇茈茉茊茋茌茍茎茏茐茑茒茓茔茕茖茗茘茙茚茛茜茝茞茟
+茠茡茢茣茤茥茦茧茨茩茪茫茬茭茮茯茰茱茲茳茴茵茶茷茸茹茺茻茼茽茾茿
+荀荁荂荃荄荅荆荇荈草荊荋荌荍荎荏荐荑荒荓荔荕荖荗荘荙荚荛荜荝荞荟
+荠荡荢荣荤荥荦荧荨荩荪荫荬荭荮药荰荱荲荳荴荵荶荷荸荹荺荻荼荽荾荿
+莀莁莂莃莄莅莆莇莈莉莊莋莌莍莎莏莐莑莒莓莔莕莖莗莘莙莚莛莜莝莞莟
+莠莡莢莣莤莥莦莧莨莩莪莫莬莭莮莯莰莱莲莳莴莵莶获莸莹莺莻莼莽莾莿
+菀菁菂菃菄菅菆菇菈菉菊菋菌菍菎菏菐菑菒菓菔菕菖菗菘菙菚菛菜菝菞菟
+菠菡菢菣菤菥菦菧菨菩菪菫菬菭菮華菰菱菲菳菴菵菶菷菸菹菺菻菼菽菾菿
+萀萁萂萃萄萅萆萇萈萉萊萋萌萍萎萏萐萑萒萓萔萕萖萗萘萙萚萛萜萝萞萟
+萠萡萢萣萤营萦萧萨萩萪萫萬萭萮萯萰萱萲萳萴萵萶萷萸萹萺萻萼落萾萿
+葀葁葂葃葄葅葆葇葈葉葊葋葌葍葎葏葐葑葒葓葔葕葖著葘葙葚葛葜葝葞葟
+葠葡葢董葤葥葦葧葨葩葪葫葬葭葮葯葰葱葲葳葴葵葶葷葸葹葺葻葼葽葾葿
+蒀蒁蒂蒃蒄蒅蒆蒇蒈蒉蒊蒋蒌蒍蒎蒏蒐蒑蒒蒓蒔蒕蒖蒗蒘蒙蒚蒛蒜蒝蒞蒟
+蒠蒡蒢蒣蒤蒥蒦蒧蒨蒩蒪蒫蒬蒭蒮蒯蒰蒱蒲蒳蒴蒵蒶蒷蒸蒹蒺蒻蒼蒽蒾蒿
+蓀蓁蓂蓃蓄蓅蓆蓇蓈蓉蓊蓋蓌蓍蓎蓏蓐蓑蓒蓓蓔蓕蓖蓗蓘蓙蓚蓛蓜蓝蓞蓟
+蓠蓡蓢蓣蓤蓥蓦蓧蓨蓩蓪蓫蓬蓭蓮蓯蓰蓱蓲蓳蓴蓵蓶蓷蓸蓹蓺蓻蓼蓽蓾蓿
+蔀蔁蔂蔃蔄蔅蔆蔇蔈蔉蔊蔋蔌蔍蔎蔏蔐蔑蔒蔓蔔蔕蔖蔗蔘蔙蔚蔛蔜蔝蔞蔟
+蔠蔡蔢蔣蔤蔥蔦蔧蔨蔩蔪蔫蔬蔭蔮蔯蔰蔱蔲蔳蔴蔵蔶蔷蔸蔹蔺蔻蔼蔽蔾蔿
+蕀蕁蕂蕃蕄蕅蕆蕇蕈蕉蕊蕋蕌蕍蕎蕏蕐蕑蕒蕓蕔蕕蕖蕗蕘蕙蕚蕛蕜蕝蕞蕟
+蕠蕡蕢蕣蕤蕥蕦蕧蕨蕩蕪蕫蕬蕭蕮蕯蕰蕱蕲蕳蕴蕵蕶蕷蕸蕹蕺蕻蕼蕽蕾蕿
+薀薁薂薃薄薅薆薇薈薉薊薋薌薍薎薏薐薑薒薓薔薕薖薗薘薙薚薛薜薝薞薟
+薠薡薢薣薤薥薦薧薨薩薪薫薬薭薮薯薰薱薲薳薴薵薶薷薸薹薺薻薼薽薾薿
+藀藁藂藃藄藅藆藇藈藉藊藋藌藍藎藏藐藑藒藓藔藕藖藗藘藙藚藛藜藝藞藟
+藠藡藢藣藤藥藦藧藨藩藪藫藬藭藮藯藰藱藲藳藴藵藶藷藸藹藺藻藼藽藾藿
+蘀蘁蘂蘃蘄蘅蘆蘇蘈蘉蘊蘋蘌蘍蘎蘏蘐蘑蘒蘓蘔蘕蘖蘗蘘蘙蘚蘛蘜蘝蘞蘟
+蘠蘡蘢蘣蘤蘥蘦蘧蘨蘩蘪蘫蘬蘭蘮蘯蘰蘱蘲蘳蘴蘵蘶蘷蘸蘹蘺蘻蘼蘽蘾蘿
+虀虁虂虃虄虅虆虇虈虉虊虋虌虍虎虏虐虑虒虓虔處虖虗虘虙虚虛虜虝虞號
+虠虡虢虣虤虥虦虧虨虩虪虫虬虭虮虯虰虱虲虳虴虵虶虷虸虹虺虻虼虽虾虿
+蚀蚁蚂蚃蚄蚅蚆蚇蚈蚉蚊蚋蚌蚍蚎蚏蚐蚑蚒蚓蚔蚕蚖蚗蚘蚙蚚蚛蚜蚝蚞蚟
+蚠蚡蚢蚣蚤蚥蚦蚧蚨蚩蚪蚫蚬蚭蚮蚯蚰蚱蚲蚳蚴蚵蚶蚷蚸蚹蚺蚻蚼蚽蚾蚿
+蛀蛁蛂蛃蛄蛅蛆蛇蛈蛉蛊蛋蛌蛍蛎蛏蛐蛑蛒蛓蛔蛕蛖蛗蛘蛙蛚蛛蛜蛝蛞蛟
+蛠蛡蛢蛣蛤蛥蛦蛧蛨蛩蛪蛫蛬蛭蛮蛯蛰蛱蛲蛳蛴蛵蛶蛷蛸蛹蛺蛻蛼蛽蛾蛿
+蜀蜁蜂蜃蜄蜅蜆蜇蜈蜉蜊蜋蜌蜍蜎蜏蜐蜑蜒蜓蜔蜕蜖蜗蜘蜙蜚蜛蜜蜝蜞蜟
+蜠蜡蜢蜣蜤蜥蜦蜧蜨蜩蜪蜫蜬蜭蜮蜯蜰蜱蜲蜳蜴蜵蜶蜷蜸蜹蜺蜻蜼蜽蜾蜿
+蝀蝁蝂蝃蝄蝅蝆蝇蝈蝉蝊蝋蝌蝍蝎蝏蝐蝑蝒蝓蝔蝕蝖蝗蝘蝙蝚蝛蝜蝝蝞蝟
+蝠蝡蝢蝣蝤蝥蝦蝧蝨蝩蝪蝫蝬蝭蝮蝯蝰蝱蝲蝳蝴蝵蝶蝷蝸蝹蝺蝻蝼蝽蝾蝿
+螀螁螂螃螄螅螆螇螈螉螊螋螌融螎螏螐螑螒螓螔螕螖螗螘螙螚螛螜螝螞螟
+螠螡螢螣螤螥螦螧螨螩螪螫螬螭螮螯螰螱螲螳螴螵螶螷螸螹螺螻螼螽螾螿
+蟀蟁蟂蟃蟄蟅蟆蟇蟈蟉蟊蟋蟌蟍蟎蟏蟐蟑蟒蟓蟔蟕蟖蟗蟘蟙蟚蟛蟜蟝蟞蟟
+蟠蟡蟢蟣蟤蟥蟦蟧蟨蟩蟪蟫蟬蟭蟮蟯蟰蟱蟲蟳蟴蟵蟶蟷蟸蟹蟺蟻蟼蟽蟾蟿
+蠀蠁蠂蠃蠄蠅蠆蠇蠈蠉蠊蠋蠌蠍蠎蠏蠐蠑蠒蠓蠔蠕蠖蠗蠘蠙蠚蠛蠜蠝蠞蠟
+蠠蠡蠢蠣蠤蠥蠦蠧蠨蠩蠪蠫蠬蠭蠮蠯蠰蠱蠲蠳蠴蠵蠶蠷蠸蠹蠺蠻蠼蠽蠾蠿
+血衁衂衃衄衅衆衇衈衉衊衋行衍衎衏衐衑衒術衔衕衖街衘衙衚衛衜衝衞衟
+衠衡衢衣衤补衦衧表衩衪衫衬衭衮衯衰衱衲衳衴衵衶衷衸衹衺衻衼衽衾衿
+袀袁袂袃袄袅袆袇袈袉袊袋袌袍袎袏袐袑袒袓袔袕袖袗袘袙袚袛袜袝袞袟
+袠袡袢袣袤袥袦袧袨袩袪被袬袭袮袯袰袱袲袳袴袵袶袷袸袹袺袻袼袽袾袿
+裀裁裂裃裄装裆裇裈裉裊裋裌裍裎裏裐裑裒裓裔裕裖裗裘裙裚裛補裝裞裟
+裠裡裢裣裤裥裦裧裨裩裪裫裬裭裮裯裰裱裲裳裴裵裶裷裸裹裺裻裼製裾裿
+褀褁褂褃褄褅褆複褈褉褊褋褌褍褎褏褐褑褒褓褔褕褖褗褘褙褚褛褜褝褞褟
+褠褡褢褣褤褥褦褧褨褩褪褫褬褭褮褯褰褱褲褳褴褵褶褷褸褹褺褻褼褽褾褿
+襀襁襂襃襄襅襆襇襈襉襊襋襌襍襎襏襐襑襒襓襔襕襖襗襘襙襚襛襜襝襞襟
+襠襡襢襣襤襥襦襧襨襩襪襫襬襭襮襯襰襱襲襳襴襵襶襷襸襹襺襻襼襽襾西
+覀要覂覃覄覅覆覇覈覉覊見覌覍覎規覐覑覒覓覔覕視覗覘覙覚覛覜覝覞覟
+覠覡覢覣覤覥覦覧覨覩親覫覬覭覮覯覰覱覲観覴覵覶覷覸覹覺覻覼覽覾覿
+觀见观觃规觅视觇览觉觊觋觌觍觎觏觐觑角觓觔觕觖觗觘觙觚觛觜觝觞觟
+觠觡觢解觤觥触觧觨觩觪觫觬觭觮觯觰觱觲觳觴觵觶觷觸觹觺觻觼觽觾觿
+言訁訂訃訄訅訆訇計訉訊訋訌訍討訏訐訑訒訓訔訕訖託記訙訚訛訜訝訞訟
+訠訡訢訣訤訥訦訧訨訩訪訫訬設訮訯訰許訲訳訴訵訶訷訸訹診註証訽訾訿
+詀詁詂詃詄詅詆詇詈詉詊詋詌詍詎詏詐詑詒詓詔評詖詗詘詙詚詛詜詝詞詟
+詠詡詢詣詤詥試詧詨詩詪詫詬詭詮詯詰話該詳詴詵詶詷詸詹詺詻詼詽詾詿
+誀誁誂誃誄誅誆誇誈誉誊誋誌認誎誏誐誑誒誓誔誕誖誗誘誙誚誛誜誝語誟
+誠誡誢誣誤誥誦誧誨誩說誫説読誮誯誰誱課誳誴誵誶誷誸誹誺誻誼誽誾調
+諀諁諂諃諄諅諆談諈諉諊請諌諍諎諏諐諑諒諓諔諕論諗諘諙諚諛諜諝諞諟
+諠諡諢諣諤諥諦諧諨諩諪諫諬諭諮諯諰諱諲諳諴諵諶諷諸諹諺諻諼諽諾諿
+謀謁謂謃謄謅謆謇謈謉謊謋謌謍謎謏謐謑謒謓謔謕謖謗謘謙謚講謜謝謞謟
+謠謡謢謣謤謥謦謧謨謩謪謫謬謭謮謯謰謱謲謳謴謵謶謷謸謹謺謻謼謽謾謿
+譀譁譂譃譄譅譆譇譈證譊譋譌譍譎譏譐譑譒譓譔譕譖譗識譙譚譛譜譝譞譟
+譠譡譢譣譤譥警譧譨譩譪譫譬譭譮譯議譱譲譳譴譵譶護譸譹譺譻譼譽譾譿
+讀讁讂讃讄讅讆讇讈讉變讋讌讍讎讏讐讑讒讓讔讕讖讗讘讙讚讛讜讝讞讟
+讠计订讣认讥讦讧讨让讪讫讬训议讯记讱讲讳讴讵讶讷许讹论讻讼讽设访
+诀证诂诃评诅识诇诈诉诊诋诌词诎诏诐译诒诓诔试诖诗诘诙诚诛诜话诞诟
+诠诡询诣诤该详诧诨诩诪诫诬语诮误诰诱诲诳说诵诶请诸诹诺读诼诽课诿
+谀谁谂调谄谅谆谇谈谉谊谋谌谍谎谏谐谑谒谓谔谕谖谗谘谙谚谛谜谝谞谟
+谠谡谢谣谤谥谦谧谨谩谪谫谬谭谮谯谰谱谲谳谴谵谶谷谸谹谺谻谼谽谾谿
+豀豁豂豃豄豅豆豇豈豉豊豋豌豍豎豏豐豑豒豓豔豕豖豗豘豙豚豛豜豝豞豟
+豠象豢豣豤豥豦豧豨豩豪豫豬豭豮豯豰豱豲豳豴豵豶豷豸豹豺豻豼豽豾豿
+貀貁貂貃貄貅貆貇貈貉貊貋貌貍貎貏貐貑貒貓貔貕貖貗貘貙貚貛貜貝貞貟
+負財貢貣貤貥貦貧貨販貪貫責貭貮貯貰貱貲貳貴貵貶買貸貹貺費貼貽貾貿
+賀賁賂賃賄賅賆資賈賉賊賋賌賍賎賏賐賑賒賓賔賕賖賗賘賙賚賛賜賝賞賟
+賠賡賢賣賤賥賦賧賨賩質賫賬賭賮賯賰賱賲賳賴賵賶賷賸賹賺賻購賽賾賿
+贀贁贂贃贄贅贆贇贈贉贊贋贌贍贎贏贐贑贒贓贔贕贖贗贘贙贚贛贜贝贞负
+贠贡财责贤败账货质贩贪贫贬购贮贯贰贱贲贳贴贵贶贷贸费贺贻贼贽贾贿
+赀赁赂赃资赅赆赇赈赉赊赋赌赍赎赏赐赑赒赓赔赕赖赗赘赙赚赛赜赝赞赟
+赠赡赢赣赤赥赦赧赨赩赪赫赬赭赮赯走赱赲赳赴赵赶起赸赹赺赻赼赽赾赿
+趀趁趂趃趄超趆趇趈趉越趋趌趍趎趏趐趑趒趓趔趕趖趗趘趙趚趛趜趝趞趟
+趠趡趢趣趤趥趦趧趨趩趪趫趬趭趮趯趰趱趲足趴趵趶趷趸趹趺趻趼趽趾趿
+跀跁跂跃跄跅跆跇跈跉跊跋跌跍跎跏跐跑跒跓跔跕跖跗跘跙跚跛跜距跞跟
+跠跡跢跣跤跥跦跧跨跩跪跫跬跭跮路跰跱跲跳跴践跶跷跸跹跺跻跼跽跾跿
+踀踁踂踃踄踅踆踇踈踉踊踋踌踍踎踏踐踑踒踓踔踕踖踗踘踙踚踛踜踝踞踟
+踠踡踢踣踤踥踦踧踨踩踪踫踬踭踮踯踰踱踲踳踴踵踶踷踸踹踺踻踼踽踾踿
+蹀蹁蹂蹃蹄蹅蹆蹇蹈蹉蹊蹋蹌蹍蹎蹏蹐蹑蹒蹓蹔蹕蹖蹗蹘蹙蹚蹛蹜蹝蹞蹟
+蹠蹡蹢蹣蹤蹥蹦蹧蹨蹩蹪蹫蹬蹭蹮蹯蹰蹱蹲蹳蹴蹵蹶蹷蹸蹹蹺蹻蹼蹽蹾蹿
+躀躁躂躃躄躅躆躇躈躉躊躋躌躍躎躏躐躑躒躓躔躕躖躗躘躙躚躛躜躝躞躟
+躠躡躢躣躤躥躦躧躨躩躪身躬躭躮躯躰躱躲躳躴躵躶躷躸躹躺躻躼躽躾躿
+軀軁軂軃軄軅軆軇軈軉車軋軌軍軎軏軐軑軒軓軔軕軖軗軘軙軚軛軜軝軞軟
+軠軡転軣軤軥軦軧軨軩軪軫軬軭軮軯軰軱軲軳軴軵軶軷軸軹軺軻軼軽軾軿
+輀輁輂較輄輅輆輇輈載輊輋輌輍輎輏輐輑輒輓輔輕輖輗輘輙輚輛輜輝輞輟
+輠輡輢輣輤輥輦輧輨輩輪輫輬輭輮輯輰輱輲輳輴輵輶輷輸輹輺輻輼輽輾輿
+轀轁轂轃轄轅轆轇轈轉轊轋轌轍轎轏轐轑轒轓轔轕轖轗轘轙轚轛轜轝轞轟
+轠轡轢轣轤轥车轧轨轩轪轫转轭轮软轰轱轲轳轴轵轶轷轸轹轺轻轼载轾轿
+辀辁辂较辄辅辆辇辈辉辊辋辌辍辎辏辐辑辒输辔辕辖辗辘辙辚辛辜辝辞辟
+辠辡辢辣辤辥辦辧辨辩辪辫辬辭辮辯辰辱農辳辴辵辶辷辸边辺辻込辽达辿
+迀迁迂迃迄迅迆过迈迉迊迋迌迍迎迏运近迒迓返迕迖迗还这迚进远违连迟
+迠迡迢迣迤迥迦迧迨迩迪迫迬迭迮迯述迱迲迳迴迵迶迷迸迹迺迻迼追迾迿
+退送适逃逄逅逆逇逈选逊逋逌逍逎透逐逑递逓途逕逖逗逘這通逛逜逝逞速
+造逡逢連逤逥逦逧逨逩逪逫逬逭逮逯逰週進逳逴逵逶逷逸逹逺逻逼逽逾逿
+遀遁遂遃遄遅遆遇遈遉遊運遌遍過遏遐遑遒道達違遖遗遘遙遚遛遜遝遞遟
+遠遡遢遣遤遥遦遧遨適遪遫遬遭遮遯遰遱遲遳遴遵遶遷選遹遺遻遼遽遾避
+邀邁邂邃還邅邆邇邈邉邊邋邌邍邎邏邐邑邒邓邔邕邖邗邘邙邚邛邜邝邞邟
+邠邡邢那邤邥邦邧邨邩邪邫邬邭邮邯邰邱邲邳邴邵邶邷邸邹邺邻邼邽邾邿
+郀郁郂郃郄郅郆郇郈郉郊郋郌郍郎郏郐郑郒郓郔郕郖郗郘郙郚郛郜郝郞郟
+郠郡郢郣郤郥郦郧部郩郪郫郬郭郮郯郰郱郲郳郴郵郶郷郸郹郺郻郼都郾郿
+鄀鄁鄂鄃鄄鄅鄆鄇鄈鄉鄊鄋鄌鄍鄎鄏鄐鄑鄒鄓鄔鄕鄖鄗鄘鄙鄚鄛鄜鄝鄞鄟
+鄠鄡鄢鄣鄤鄥鄦鄧鄨鄩鄪鄫鄬鄭鄮鄯鄰鄱鄲鄳鄴鄵鄶鄷鄸鄹鄺鄻鄼鄽鄾鄿
+酀酁酂酃酄酅酆酇酈酉酊酋酌配酎酏酐酑酒酓酔酕酖酗酘酙酚酛酜酝酞酟
+酠酡酢酣酤酥酦酧酨酩酪酫酬酭酮酯酰酱酲酳酴酵酶酷酸酹酺酻酼酽酾酿
+醀醁醂醃醄醅醆醇醈醉醊醋醌醍醎醏醐醑醒醓醔醕醖醗醘醙醚醛醜醝醞醟
+醠醡醢醣醤醥醦醧醨醩醪醫醬醭醮醯醰醱醲醳醴醵醶醷醸醹醺醻醼醽醾醿
+釀釁釂釃釄釅釆采釈釉释釋里重野量釐金釒釓釔釕釖釗釘釙釚釛釜針釞釟
+釠釡釢釣釤釥釦釧釨釩釪釫釬釭釮釯釰釱釲釳釴釵釶釷釸釹釺釻釼釽釾釿
+鈀鈁鈂鈃鈄鈅鈆鈇鈈鈉鈊鈋鈌鈍鈎鈏鈐鈑鈒鈓鈔鈕鈖鈗鈘鈙鈚鈛鈜鈝鈞鈟
+鈠鈡鈢鈣鈤鈥鈦鈧鈨鈩鈪鈫鈬鈭鈮鈯鈰鈱鈲鈳鈴鈵鈶鈷鈸鈹鈺鈻鈼鈽鈾鈿
+鉀鉁鉂鉃鉄鉅鉆鉇鉈鉉鉊鉋鉌鉍鉎鉏鉐鉑鉒鉓鉔鉕鉖鉗鉘鉙鉚鉛鉜鉝鉞鉟
+鉠鉡鉢鉣鉤鉥鉦鉧鉨鉩鉪鉫鉬鉭鉮鉯鉰鉱鉲鉳鉴鉵鉶鉷鉸鉹鉺鉻鉼鉽鉾鉿
+銀銁銂銃銄銅銆銇銈銉銊銋銌銍銎銏銐銑銒銓銔銕銖銗銘銙銚銛銜銝銞銟
+銠銡銢銣銤銥銦銧銨銩銪銫銬銭銮銯銰銱銲銳銴銵銶銷銸銹銺銻銼銽銾銿
+鋀鋁鋂鋃鋄鋅鋆鋇鋈鋉鋊鋋鋌鋍鋎鋏鋐鋑鋒鋓鋔鋕鋖鋗鋘鋙鋚鋛鋜鋝鋞鋟
+鋠鋡鋢鋣鋤鋥鋦鋧鋨鋩鋪鋫鋬鋭鋮鋯鋰鋱鋲鋳鋴鋵鋶鋷鋸鋹鋺鋻鋼鋽鋾鋿
+錀錁錂錃錄錅錆錇錈錉錊錋錌錍錎錏錐錑錒錓錔錕錖錗錘錙錚錛錜錝錞錟
+錠錡錢錣錤錥錦錧錨錩錪錫錬錭錮錯錰錱録錳錴錵錶錷錸錹錺錻錼錽錾錿
+鍀鍁鍂鍃鍄鍅鍆鍇鍈鍉鍊鍋鍌鍍鍎鍏鍐鍑鍒鍓鍔鍕鍖鍗鍘鍙鍚鍛鍜鍝鍞鍟
+鍠鍡鍢鍣鍤鍥鍦鍧鍨鍩鍪鍫鍬鍭鍮鍯鍰鍱鍲鍳鍴鍵鍶鍷鍸鍹鍺鍻鍼鍽鍾鍿
+鎀鎁鎂鎃鎄鎅鎆鎇鎈鎉鎊鎋鎌鎍鎎鎏鎐鎑鎒鎓鎔鎕鎖鎗鎘鎙鎚鎛鎜鎝鎞鎟
+鎠鎡鎢鎣鎤鎥鎦鎧鎨鎩鎪鎫鎬鎭鎮鎯鎰鎱鎲鎳鎴鎵鎶鎷鎸鎹鎺鎻鎼鎽鎾鎿
+鏀鏁鏂鏃鏄鏅鏆鏇鏈鏉鏊鏋鏌鏍鏎鏏鏐鏑鏒鏓鏔鏕鏖鏗鏘鏙鏚鏛鏜鏝鏞鏟
+鏠鏡鏢鏣鏤鏥鏦鏧鏨鏩鏪鏫鏬鏭鏮鏯鏰鏱鏲鏳鏴鏵鏶鏷鏸鏹鏺鏻鏼鏽鏾鏿
+鐀鐁鐂鐃鐄鐅鐆鐇鐈鐉鐊鐋鐌鐍鐎鐏鐐鐑鐒鐓鐔鐕鐖鐗鐘鐙鐚鐛鐜鐝鐞鐟
+鐠鐡鐢鐣鐤鐥鐦鐧鐨鐩鐪鐫鐬鐭鐮鐯鐰鐱鐲鐳鐴鐵鐶鐷鐸鐹鐺鐻鐼鐽鐾鐿
+鑀鑁鑂鑃鑄鑅鑆鑇鑈鑉鑊鑋鑌鑍鑎鑏鑐鑑鑒鑓鑔鑕鑖鑗鑘鑙鑚鑛鑜鑝鑞鑟
+鑠鑡鑢鑣鑤鑥鑦鑧鑨鑩鑪鑫鑬鑭鑮鑯鑰鑱鑲鑳鑴鑵鑶鑷鑸鑹鑺鑻鑼鑽鑾鑿
+钀钁钂钃钄钅钆钇针钉钊钋钌钍钎钏钐钑钒钓钔钕钖钗钘钙钚钛钜钝钞钟
+钠钡钢钣钤钥钦钧钨钩钪钫钬钭钮钯钰钱钲钳钴钵钶钷钸钹钺钻钼钽钾钿
+铀铁铂铃铄铅铆铇铈铉铊铋铌铍铎铏铐铑铒铓铔铕铖铗铘铙铚铛铜铝铞铟
+铠铡铢铣铤铥铦铧铨铩铪铫铬铭铮铯铰铱铲铳铴铵银铷铸铹铺铻铼铽链铿
+销锁锂锃锄锅锆锇锈锉锊锋锌锍锎锏锐锑锒锓锔锕锖锗锘错锚锛锜锝锞锟
+锠锡锢锣锤锥锦锧锨锩锪锫锬锭键锯锰锱锲锳锴锵锶锷锸锹锺锻锼锽锾锿
+镀镁镂镃镄镅镆镇镈镉镊镋镌镍镎镏镐镑镒镓镔镕镖镗镘镙镚镛镜镝镞镟
+镠镡镢镣镤镥镦镧镨镩镪镫镬镭镮镯镰镱镲镳镴镵镶長镸镹镺镻镼镽镾长
+門閁閂閃閄閅閆閇閈閉閊開閌閍閎閏閐閑閒間閔閕閖閗閘閙閚閛閜閝閞閟
+閠閡関閣閤閥閦閧閨閩閪閫閬閭閮閯閰閱閲閳閴閵閶閷閸閹閺閻閼閽閾閿
+闀闁闂闃闄闅闆闇闈闉闊闋闌闍闎闏闐闑闒闓闔闕闖闗闘闙闚闛關闝闞闟
+闠闡闢闣闤闥闦闧门闩闪闫闬闭问闯闰闱闲闳间闵闶闷闸闹闺闻闼闽闾闿
+阀阁阂阃阄阅阆阇阈阉阊阋阌阍阎阏阐阑阒阓阔阕阖阗阘阙阚阛阜阝阞队
+阠阡阢阣阤阥阦阧阨阩阪阫阬阭阮阯阰阱防阳阴阵阶阷阸阹阺阻阼阽阾阿
+陀陁陂陃附际陆陇陈陉陊陋陌降陎陏限陑陒陓陔陕陖陗陘陙陚陛陜陝陞陟
+陠陡院陣除陥陦陧陨险陪陫陬陭陮陯陰陱陲陳陴陵陶陷陸陹険陻陼陽陾陿
+隀隁隂隃隄隅隆隇隈隉隊隋隌隍階随隐隑隒隓隔隕隖隗隘隙隚際障隝隞隟
+隠隡隢隣隤隥隦隧隨隩險隫隬隭隮隯隰隱隲隳隴隵隶隷隸隹隺隻隼隽难隿
+雀雁雂雃雄雅集雇雈雉雊雋雌雍雎雏雐雑雒雓雔雕雖雗雘雙雚雛雜雝雞雟
+雠雡離難雤雥雦雧雨雩雪雫雬雭雮雯雰雱雲雳雴雵零雷雸雹雺電雼雽雾雿
+需霁霂霃霄霅霆震霈霉霊霋霌霍霎霏霐霑霒霓霔霕霖霗霘霙霚霛霜霝霞霟
+霠霡霢霣霤霥霦霧霨霩霪霫霬霭霮霯霰霱露霳霴霵霶霷霸霹霺霻霼霽霾霿
+靀靁靂靃靄靅靆靇靈靉靊靋靌靍靎靏靐靑青靓靔靕靖靗靘静靚靛靜靝非靟
+靠靡面靣靤靥靦靧靨革靪靫靬靭靮靯靰靱靲靳靴靵靶靷靸靹靺靻靼靽靾靿
+鞀鞁鞂鞃鞄鞅鞆鞇鞈鞉鞊鞋鞌鞍鞎鞏鞐鞑鞒鞓鞔鞕鞖鞗鞘鞙鞚鞛鞜鞝鞞鞟
+鞠鞡鞢鞣鞤鞥鞦鞧鞨鞩鞪鞫鞬鞭鞮鞯鞰鞱鞲鞳鞴鞵鞶鞷鞸鞹鞺鞻鞼鞽鞾鞿
+韀韁韂韃韄韅韆韇韈韉韊韋韌韍韎韏韐韑韒韓韔韕韖韗韘韙韚韛韜韝韞韟
+韠韡韢韣韤韥韦韧韨韩韪韫韬韭韮韯韰韱韲音韴韵韶韷韸韹韺韻韼韽韾響
+頀頁頂頃頄項順頇須頉頊頋頌頍頎頏預頑頒頓頔頕頖頗領頙頚頛頜頝頞頟
+頠頡頢頣頤頥頦頧頨頩頪頫頬頭頮頯頰頱頲頳頴頵頶頷頸頹頺頻頼頽頾頿
+顀顁顂顃顄顅顆顇顈顉顊顋題額顎顏顐顑顒顓顔顕顖顗願顙顚顛顜顝類顟
+顠顡顢顣顤顥顦顧顨顩顪顫顬顭顮顯顰顱顲顳顴页顶顷顸项顺须顼顽顾顿
+颀颁颂颃预颅领颇颈颉颊颋颌颍颎颏颐频颒颓颔颕颖颗题颙颚颛颜额颞颟
+颠颡颢颣颤颥颦颧風颩颪颫颬颭颮颯颰颱颲颳颴颵颶颷颸颹颺颻颼颽颾颿
+飀飁飂飃飄飅飆飇飈飉飊飋飌飍风飏飐飑飒飓飔飕飖飗飘飙飚飛飜飝飞食
+飠飡飢飣飤飥飦飧飨飩飪飫飬飭飮飯飰飱飲飳飴飵飶飷飸飹飺飻飼飽飾飿
+餀餁餂餃餄餅餆餇餈餉養餋餌餍餎餏餐餑餒餓餔餕餖餗餘餙餚餛餜餝餞餟
+餠餡餢餣餤餥餦餧館餩餪餫餬餭餮餯餰餱餲餳餴餵餶餷餸餹餺餻餼餽餾餿
+饀饁饂饃饄饅饆饇饈饉饊饋饌饍饎饏饐饑饒饓饔饕饖饗饘饙饚饛饜饝饞饟
+饠饡饢饣饤饥饦饧饨饩饪饫饬饭饮饯饰饱饲饳饴饵饶饷饸饹饺饻饼饽饾饿
+馀馁馂馃馄馅馆馇馈馉馊馋馌馍馎馏馐馑馒馓馔馕首馗馘香馚馛馜馝馞馟
+馠馡馢馣馤馥馦馧馨馩馪馫馬馭馮馯馰馱馲馳馴馵馶馷馸馹馺馻馼馽馾馿
+駀駁駂駃駄駅駆駇駈駉駊駋駌駍駎駏駐駑駒駓駔駕駖駗駘駙駚駛駜駝駞駟
+駠駡駢駣駤駥駦駧駨駩駪駫駬駭駮駯駰駱駲駳駴駵駶駷駸駹駺駻駼駽駾駿
+騀騁騂騃騄騅騆騇騈騉騊騋騌騍騎騏騐騑騒験騔騕騖騗騘騙騚騛騜騝騞騟
+騠騡騢騣騤騥騦騧騨騩騪騫騬騭騮騯騰騱騲騳騴騵騶騷騸騹騺騻騼騽騾騿
+驀驁驂驃驄驅驆驇驈驉驊驋驌驍驎驏驐驑驒驓驔驕驖驗驘驙驚驛驜驝驞驟
+驠驡驢驣驤驥驦驧驨驩驪驫马驭驮驯驰驱驲驳驴驵驶驷驸驹驺驻驼驽驾驿
+骀骁骂骃骄骅骆骇骈骉骊骋验骍骎骏骐骑骒骓骔骕骖骗骘骙骚骛骜骝骞骟
+骠骡骢骣骤骥骦骧骨骩骪骫骬骭骮骯骰骱骲骳骴骵骶骷骸骹骺骻骼骽骾骿
+髀髁髂髃髄髅髆髇髈髉髊髋髌髍髎髏髐髑髒髓體髕髖髗高髙髚髛髜髝髞髟
+髠髡髢髣髤髥髦髧髨髩髪髫髬髭髮髯髰髱髲髳髴髵髶髷髸髹髺髻髼髽髾髿
+鬀鬁鬂鬃鬄鬅鬆鬇鬈鬉鬊鬋鬌鬍鬎鬏鬐鬑鬒鬓鬔鬕鬖鬗鬘鬙鬚鬛鬜鬝鬞鬟
+鬠鬡鬢鬣鬤鬥鬦鬧鬨鬩鬪鬫鬬鬭鬮鬯鬰鬱鬲鬳鬴鬵鬶鬷鬸鬹鬺鬻鬼鬽鬾鬿
+魀魁魂魃魄魅魆魇魈魉魊魋魌魍魎魏魐魑魒魓魔魕魖魗魘魙魚魛魜魝魞魟
+魠魡魢魣魤魥魦魧魨魩魪魫魬魭魮魯魰魱魲魳魴魵魶魷魸魹魺魻魼魽魾魿
+鮀鮁鮂鮃鮄鮅鮆鮇鮈鮉鮊鮋鮌鮍鮎鮏鮐鮑鮒鮓鮔鮕鮖鮗鮘鮙鮚鮛鮜鮝鮞鮟
+鮠鮡鮢鮣鮤鮥鮦鮧鮨鮩鮪鮫鮬鮭鮮鮯鮰鮱鮲鮳鮴鮵鮶鮷鮸鮹鮺鮻鮼鮽鮾鮿
+鯀鯁鯂鯃鯄鯅鯆鯇鯈鯉鯊鯋鯌鯍鯎鯏鯐鯑鯒鯓鯔鯕鯖鯗鯘鯙鯚鯛鯜鯝鯞鯟
+鯠鯡鯢鯣鯤鯥鯦鯧鯨鯩鯪鯫鯬鯭鯮鯯鯰鯱鯲鯳鯴鯵鯶鯷鯸鯹鯺鯻鯼鯽鯾鯿
+鰀鰁鰂鰃鰄鰅鰆鰇鰈鰉鰊鰋鰌鰍鰎鰏鰐鰑鰒鰓鰔鰕鰖鰗鰘鰙鰚鰛鰜鰝鰞鰟
+鰠鰡鰢鰣鰤鰥鰦鰧鰨鰩鰪鰫鰬鰭鰮鰯鰰鰱鰲鰳鰴鰵鰶鰷鰸鰹鰺鰻鰼鰽鰾鰿
+鱀鱁鱂鱃鱄鱅鱆鱇鱈鱉鱊鱋鱌鱍鱎鱏鱐鱑鱒鱓鱔鱕鱖鱗鱘鱙鱚鱛鱜鱝鱞鱟
+鱠鱡鱢鱣鱤鱥鱦鱧鱨鱩鱪鱫鱬鱭鱮鱯鱰鱱鱲鱳鱴鱵鱶鱷鱸鱹鱺鱻鱼鱽鱾鱿
+鲀鲁鲂鲃鲄鲅鲆鲇鲈鲉鲊鲋鲌鲍鲎鲏鲐鲑鲒鲓鲔鲕鲖鲗鲘鲙鲚鲛鲜鲝鲞鲟
+鲠鲡鲢鲣鲤鲥鲦鲧鲨鲩鲪鲫鲬鲭鲮鲯鲰鲱鲲鲳鲴鲵鲶鲷鲸鲹鲺鲻鲼鲽鲾鲿
+鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊鳋鳌鳍鳎鳏鳐鳑鳒鳓鳔鳕鳖鳗鳘鳙鳚鳛鳜鳝鳞鳟
+鳠鳡鳢鳣鳤鳥鳦鳧鳨鳩鳪鳫鳬鳭鳮鳯鳰鳱鳲鳳鳴鳵鳶鳷鳸鳹鳺鳻鳼鳽鳾鳿
+鴀鴁鴂鴃鴄鴅鴆鴇鴈鴉鴊鴋鴌鴍鴎鴏鴐鴑鴒鴓鴔鴕鴖鴗鴘鴙鴚鴛鴜鴝鴞鴟
+鴠鴡鴢鴣鴤鴥鴦鴧鴨鴩鴪鴫鴬鴭鴮鴯鴰鴱鴲鴳鴴鴵鴶鴷鴸鴹鴺鴻鴼鴽鴾鴿
+鵀鵁鵂鵃鵄鵅鵆鵇鵈鵉鵊鵋鵌鵍鵎鵏鵐鵑鵒鵓鵔鵕鵖鵗鵘鵙鵚鵛鵜鵝鵞鵟
+鵠鵡鵢鵣鵤鵥鵦鵧鵨鵩鵪鵫鵬鵭鵮鵯鵰鵱鵲鵳鵴鵵鵶鵷鵸鵹鵺鵻鵼鵽鵾鵿
+鶀鶁鶂鶃鶄鶅鶆鶇鶈鶉鶊鶋鶌鶍鶎鶏鶐鶑鶒鶓鶔鶕鶖鶗鶘鶙鶚鶛鶜鶝鶞鶟
+鶠鶡鶢鶣鶤鶥鶦鶧鶨鶩鶪鶫鶬鶭鶮鶯鶰鶱鶲鶳鶴鶵鶶鶷鶸鶹鶺鶻鶼鶽鶾鶿
+鷀鷁鷂鷃鷄鷅鷆鷇鷈鷉鷊鷋鷌鷍鷎鷏鷐鷑鷒鷓鷔鷕鷖鷗鷘鷙鷚鷛鷜鷝鷞鷟
+鷠鷡鷢鷣鷤鷥鷦鷧鷨鷩鷪鷫鷬鷭鷮鷯鷰鷱鷲鷳鷴鷵鷶鷷鷸鷹鷺鷻鷼鷽鷾鷿
+鸀鸁鸂鸃鸄鸅鸆鸇鸈鸉鸊鸋鸌鸍鸎鸏鸐鸑鸒鸓鸔鸕鸖鸗鸘鸙鸚鸛鸜鸝鸞鸟
+鸠鸡鸢鸣鸤鸥鸦鸧鸨鸩鸪鸫鸬鸭鸮鸯鸰鸱鸲鸳鸴鸵鸶鸷鸸鸹鸺鸻鸼鸽鸾鸿
+鹀鹁鹂鹃鹄鹅鹆鹇鹈鹉鹊鹋鹌鹍鹎鹏鹐鹑鹒鹓鹔鹕鹖鹗鹘鹙鹚鹛鹜鹝鹞鹟
+鹠鹡鹢鹣鹤鹥鹦鹧鹨鹩鹪鹫鹬鹭鹮鹯鹰鹱鹲鹳鹴鹵鹶鹷鹸鹹鹺鹻鹼鹽鹾鹿
+麀麁麂麃麄麅麆麇麈麉麊麋麌麍麎麏麐麑麒麓麔麕麖麗麘麙麚麛麜麝麞麟
+麠麡麢麣麤麥麦麧麨麩麪麫麬麭麮麯麰麱麲麳麴麵麶麷麸麹麺麻麼麽麾麿
+黀黁黂黃黄黅黆黇黈黉黊黋黌黍黎黏黐黑黒黓黔黕黖黗默黙黚黛黜黝點黟
+黠黡黢黣黤黥黦黧黨黩黪黫黬黭黮黯黰黱黲黳黴黵黶黷黸黹黺黻黼黽黾黿
+鼀鼁鼂鼃鼄鼅鼆鼇鼈鼉鼊鼋鼌鼍鼎鼏鼐鼑鼒鼓鼔鼕鼖鼗鼘鼙鼚鼛鼜鼝鼞鼟
+鼠鼡鼢鼣鼤鼥鼦鼧鼨鼩鼪鼫鼬鼭鼮鼯鼰鼱鼲鼳鼴鼵鼶鼷鼸鼹鼺鼻鼼鼽鼾鼿
+齀齁齂齃齄齅齆齇齈齉齊齋齌齍齎齏齐齑齒齓齔齕齖齗齘齙齚齛齜齝齞齟
+齠齡齢齣齤齥齦齧齨齩齪齫齬齭齮齯齰齱齲齳齴齵齶齷齸齹齺齻齼齽齾齿
+龀龁龂龃龄龅龆龇龈龉龊龋龌龍龎龏龐龑龒龓龔龕龖龗龘龙龚龛龜龝龞龟
+龠龡龢龣龤龥龦龧龨龩龪龫龬龭龮龯龰龱龲龳龴龵龶龷龸龹龺龻龼龽龾龿
+鿀鿁鿂鿃鿄鿅鿆鿇鿈鿉鿊鿋鿌鿍鿎鿏鿐鿑鿒鿓鿔鿕鿖鿗鿘鿙鿚鿛鿜鿝鿞鿟
+鿠鿡鿢鿣鿤鿥鿦鿧鿨鿩鿪鿫鿬鿭鿮鿯鿰鿱鿲鿳鿴鿵鿶鿷鿸鿹鿺鿻鿼鿽鿾鿿
+
+Yi Syllables (U+A000-U+A48F):
+
+ꀀꀁꀂꀃꀄꀅꀆꀇꀈꀉꀊꀋꀌꀍꀎꀏꀐꀑꀒꀓꀔꀕꀖꀗꀘꀙꀚꀛꀜꀝꀞꀟ
+ꀠꀡꀢꀣꀤꀥꀦꀧꀨꀩꀪꀫꀬꀭꀮꀯꀰꀱꀲꀳꀴꀵꀶꀷꀸꀹꀺꀻꀼꀽꀾꀿ
+ꁀꁁꁂꁃꁄꁅꁆꁇꁈꁉꁊꁋꁌꁍꁎꁏꁐꁑꁒꁓꁔꁕꁖꁗꁘꁙꁚꁛꁜꁝꁞꁟ
+ꁠꁡꁢꁣꁤꁥꁦꁧꁨꁩꁪꁫꁬꁭꁮꁯꁰꁱꁲꁳꁴꁵꁶꁷꁸꁹꁺꁻꁼꁽꁾꁿ
+ꂀꂁꂂꂃꂄꂅꂆꂇꂈꂉꂊꂋꂌꂍꂎꂏꂐꂑꂒꂓꂔꂕꂖꂗꂘꂙꂚꂛꂜꂝꂞꂟ
+ꂠꂡꂢꂣꂤꂥꂦꂧꂨꂩꂪꂫꂬꂭꂮꂯꂰꂱꂲꂳꂴꂵꂶꂷꂸꂹꂺꂻꂼꂽꂾꂿ
+ꃀꃁꃂꃃꃄꃅꃆꃇꃈꃉꃊꃋꃌꃍꃎꃏꃐꃑꃒꃓꃔꃕꃖꃗꃘꃙꃚꃛꃜꃝꃞꃟ
+ꃠꃡꃢꃣꃤꃥꃦꃧꃨꃩꃪꃫꃬꃭꃮꃯꃰꃱꃲꃳꃴꃵꃶꃷꃸꃹꃺꃻꃼꃽꃾꃿ
+ꄀꄁꄂꄃꄄꄅꄆꄇꄈꄉꄊꄋꄌꄍꄎꄏꄐꄑꄒꄓꄔꄕꄖꄗꄘꄙꄚꄛꄜꄝꄞꄟ
+ꄠꄡꄢꄣꄤꄥꄦꄧꄨꄩꄪꄫꄬꄭꄮꄯꄰꄱꄲꄳꄴꄵꄶꄷꄸꄹꄺꄻꄼꄽꄾꄿ
+ꅀꅁꅂꅃꅄꅅꅆꅇꅈꅉꅊꅋꅌꅍꅎꅏꅐꅑꅒꅓꅔꅕꅖꅗꅘꅙꅚꅛꅜꅝꅞꅟ
+ꅠꅡꅢꅣꅤꅥꅦꅧꅨꅩꅪꅫꅬꅭꅮꅯꅰꅱꅲꅳꅴꅵꅶꅷꅸꅹꅺꅻꅼꅽꅾꅿ
+ꆀꆁꆂꆃꆄꆅꆆꆇꆈꆉꆊꆋꆌꆍꆎꆏꆐꆑꆒꆓꆔꆕꆖꆗꆘꆙꆚꆛꆜꆝꆞꆟ
+ꆠꆡꆢꆣꆤꆥꆦꆧꆨꆩꆪꆫꆬꆭꆮꆯꆰꆱꆲꆳꆴꆵꆶꆷꆸꆹꆺꆻꆼꆽꆾꆿ
+ꇀꇁꇂꇃꇄꇅꇆꇇꇈꇉꇊꇋꇌꇍꇎꇏꇐꇑꇒꇓꇔꇕꇖꇗꇘꇙꇚꇛꇜꇝꇞꇟ
+ꇠꇡꇢꇣꇤꇥꇦꇧꇨꇩꇪꇫꇬꇭꇮꇯꇰꇱꇲꇳꇴꇵꇶꇷꇸꇹꇺꇻꇼꇽꇾꇿ
+ꈀꈁꈂꈃꈄꈅꈆꈇꈈꈉꈊꈋꈌꈍꈎꈏꈐꈑꈒꈓꈔꈕꈖꈗꈘꈙꈚꈛꈜꈝꈞꈟ
+ꈠꈡꈢꈣꈤꈥꈦꈧꈨꈩꈪꈫꈬꈭꈮꈯꈰꈱꈲꈳꈴꈵꈶꈷꈸꈹꈺꈻꈼꈽꈾꈿ
+ꉀꉁꉂꉃꉄꉅꉆꉇꉈꉉꉊꉋꉌꉍꉎꉏꉐꉑꉒꉓꉔꉕꉖꉗꉘꉙꉚꉛꉜꉝꉞꉟ
+ꉠꉡꉢꉣꉤꉥꉦꉧꉨꉩꉪꉫꉬꉭꉮꉯꉰꉱꉲꉳꉴꉵꉶꉷꉸꉹꉺꉻꉼꉽꉾꉿ
+ꊀꊁꊂꊃꊄꊅꊆꊇꊈꊉꊊꊋꊌꊍꊎꊏꊐꊑꊒꊓꊔꊕꊖꊗꊘꊙꊚꊛꊜꊝꊞꊟ
+ꊠꊡꊢꊣꊤꊥꊦꊧꊨꊩꊪꊫꊬꊭꊮꊯꊰꊱꊲꊳꊴꊵꊶꊷꊸꊹꊺꊻꊼꊽꊾꊿ
+ꋀꋁꋂꋃꋄꋅꋆꋇꋈꋉꋊꋋꋌꋍꋎꋏꋐꋑꋒꋓꋔꋕꋖꋗꋘꋙꋚꋛꋜꋝꋞꋟ
+ꋠꋡꋢꋣꋤꋥꋦꋧꋨꋩꋪꋫꋬꋭꋮꋯꋰꋱꋲꋳꋴꋵꋶꋷꋸꋹꋺꋻꋼꋽꋾꋿ
+ꌀꌁꌂꌃꌄꌅꌆꌇꌈꌉꌊꌋꌌꌍꌎꌏꌐꌑꌒꌓꌔꌕꌖꌗꌘꌙꌚꌛꌜꌝꌞꌟ
+ꌠꌡꌢꌣꌤꌥꌦꌧꌨꌩꌪꌫꌬꌭꌮꌯꌰꌱꌲꌳꌴꌵꌶꌷꌸꌹꌺꌻꌼꌽꌾꌿ
+ꍀꍁꍂꍃꍄꍅꍆꍇꍈꍉꍊꍋꍌꍍꍎꍏꍐꍑꍒꍓꍔꍕꍖꍗꍘꍙꍚꍛꍜꍝꍞꍟ
+ꍠꍡꍢꍣꍤꍥꍦꍧꍨꍩꍪꍫꍬꍭꍮꍯꍰꍱꍲꍳꍴꍵꍶꍷꍸꍹꍺꍻꍼꍽꍾꍿ
+ꎀꎁꎂꎃꎄꎅꎆꎇꎈꎉꎊꎋꎌꎍꎎꎏꎐꎑꎒꎓꎔꎕꎖꎗꎘꎙꎚꎛꎜꎝꎞꎟ
+ꎠꎡꎢꎣꎤꎥꎦꎧꎨꎩꎪꎫꎬꎭꎮꎯꎰꎱꎲꎳꎴꎵꎶꎷꎸꎹꎺꎻꎼꎽꎾꎿ
+ꏀꏁꏂꏃꏄꏅꏆꏇꏈꏉꏊꏋꏌꏍꏎꏏꏐꏑꏒꏓꏔꏕꏖꏗꏘꏙꏚꏛꏜꏝꏞꏟ
+ꏠꏡꏢꏣꏤꏥꏦꏧꏨꏩꏪꏫꏬꏭꏮꏯꏰꏱꏲꏳꏴꏵꏶꏷꏸꏹꏺꏻꏼꏽꏾꏿ
+ꐀꐁꐂꐃꐄꐅꐆꐇꐈꐉꐊꐋꐌꐍꐎꐏꐐꐑꐒꐓꐔꐕꐖꐗꐘꐙꐚꐛꐜꐝꐞꐟ
+ꐠꐡꐢꐣꐤꐥꐦꐧꐨꐩꐪꐫꐬꐭꐮꐯꐰꐱꐲꐳꐴꐵꐶꐷꐸꐹꐺꐻꐼꐽꐾꐿ
+ꑀꑁꑂꑃꑄꑅꑆꑇꑈꑉꑊꑋꑌꑍꑎꑏꑐꑑꑒꑓꑔꑕꑖꑗꑘꑙꑚꑛꑜꑝꑞꑟ
+ꑠꑡꑢꑣꑤꑥꑦꑧꑨꑩꑪꑫꑬꑭꑮꑯꑰꑱꑲꑳꑴꑵꑶꑷꑸꑹꑺꑻꑼꑽꑾꑿ
+ꒀꒁꒂꒃꒄꒅꒆꒇꒈꒉꒊꒋꒌ꒍꒎꒏
+
+Yi Radicals (U+A490-U+A4CF):
+
+꒐꒑꒒꒓꒔꒕꒖꒗꒘꒙꒚꒛꒜꒝꒞꒟꒠꒡꒢꒣꒤꒥꒦꒧꒨꒩꒪꒫꒬꒭꒮꒯
+꒰꒱꒲꒳꒴꒵꒶꒷꒸꒹꒺꒻꒼꒽꒾꒿꓀꓁꓂꓃꓄꓅꓆꓇꓈꓉꓊꓋꓌꓍꓎꓏
+
+Free block (U+A4D0-U+ABFF):
+
+ꓐꓑꓒꓓꓔꓕꓖꓗꓘꓙꓚꓛꓜꓝꓞꓟꓠꓡꓢꓣꓤꓥꓦꓧꓨꓩꓪꓫꓬꓭꓮꓯꓰꓱꓲꓳꓴꓵꓶꓷꓸꓹꓺꓻꓼꓽ꓾꓿ꔀꔁꔂꔃꔄꔅꔆꔇꔈꔉꔊꔋꔌꔍꔎꔏ
+ꔐꔑꔒꔓꔔꔕꔖꔗꔘꔙꔚꔛꔜꔝꔞꔟꔠꔡꔢꔣꔤꔥꔦꔧꔨꔩꔪꔫꔬꔭꔮꔯꔰꔱꔲꔳꔴꔵꔶꔷꔸꔹꔺꔻꔼꔽꔾꔿꕀꕁꕂꕃꕄꕅꕆꕇꕈꕉꕊꕋꕌꕍꕎꕏ
+ꕐꕑꕒꕓꕔꕕꕖꕗꕘꕙꕚꕛꕜꕝꕞꕟꕠꕡꕢꕣꕤꕥꕦꕧꕨꕩꕪꕫꕬꕭꕮꕯꕰꕱꕲꕳꕴꕵꕶꕷꕸꕹꕺꕻꕼꕽꕾꕿꖀꖁꖂꖃꖄꖅꖆꖇꖈꖉꖊꖋꖌꖍꖎꖏ
+ꖐꖑꖒꖓꖔꖕꖖꖗꖘꖙꖚꖛꖜꖝꖞꖟꖠꖡꖢꖣꖤꖥꖦꖧꖨꖩꖪꖫꖬꖭꖮꖯꖰꖱꖲꖳꖴꖵꖶꖷꖸꖹꖺꖻꖼꖽꖾꖿꗀꗁꗂꗃꗄꗅꗆꗇꗈꗉꗊꗋꗌꗍꗎꗏ
+ꗐꗑꗒꗓꗔꗕꗖꗗꗘꗙꗚꗛꗜꗝꗞꗟꗠꗡꗢꗣꗤꗥꗦꗧꗨꗩꗪꗫꗬꗭꗮꗯꗰꗱꗲꗳꗴꗵꗶꗷꗸꗹꗺꗻꗼꗽꗾꗿꘀꘁꘂꘃꘄꘅꘆꘇꘈꘉꘊꘋꘌ꘍꘎꘏
+ꘐꘑꘒꘓꘔꘕꘖꘗꘘꘙꘚꘛꘜꘝꘞꘟ꘠꘡꘢꘣꘤꘥꘦꘧꘨꘩ꘪꘫ꘬꘭꘮꘯꘰꘱꘲꘳꘴꘵꘶꘷꘸꘹꘺꘻꘼꘽꘾꘿ꙀꙁꙂꙃꙄꙅꙆꙇꙈꙉꙊꙋꙌꙍꙎꙏ
+ꙐꙑꙒꙓꙔꙕꙖꙗꙘꙙꙚꙛꙜꙝꙞꙟꙠꙡꙢꙣꙤꙥꙦꙧꙨꙩꙪꙫꙬꙭꙮ꙯꙰꙱꙲꙳ꙴꙵꙶꙷꙸꙹꙺꙻ꙼꙽꙾ꙿꚀꚁꚂꚃꚄꚅꚆꚇꚈꚉꚊꚋꚌꚍꚎꚏ
+ꚐꚑꚒꚓꚔꚕꚖꚗꚘꚙꚚꚛꚜꚝꚞꚟꚠꚡꚢꚣꚤꚥꚦꚧꚨꚩꚪꚫꚬꚭꚮꚯꚰꚱꚲꚳꚴꚵꚶꚷꚸꚹꚺꚻꚼꚽꚾꚿꛀꛁꛂꛃꛄꛅꛆꛇꛈꛉꛊꛋꛌꛍꛎꛏ
+ꛐꛑꛒꛓꛔꛕꛖꛗꛘꛙꛚꛛꛜꛝꛞꛟꛠꛡꛢꛣꛤꛥꛦꛧꛨꛩꛪꛫꛬꛭꛮꛯ꛰꛱꛲꛳꛴꛵꛶꛷꛸꛹꛺꛻꛼꛽꛾꛿꜀꜁꜂꜃꜄꜅꜆꜇꜈꜉꜊꜋꜌꜍꜎꜏
+꜐꜑꜒꜓꜔꜕꜖ꜗꜘꜙꜚꜛꜜꜝꜞꜟ꜠꜡ꜢꜣꜤꜥꜦꜧꜨꜩꜪꜫꜬꜭꜮꜯꜰꜱꜲꜳꜴꜵꜶꜷꜸꜹꜺꜻꜼꜽꜾꜿꝀꝁꝂꝃꝄꝅꝆꝇꝈꝉꝊꝋꝌꝍꝎꝏ
+ꝐꝑꝒꝓꝔꝕꝖꝗꝘꝙꝚꝛꝜꝝꝞꝟꝠꝡꝢꝣꝤꝥꝦꝧꝨꝩꝪꝫꝬꝭꝮꝯꝰꝱꝲꝳꝴꝵꝶꝷꝸꝹꝺꝻꝼꝽꝾꝿꞀꞁꞂꞃꞄꞅꞆꞇꞈ꞉꞊ꞋꞌꞍꞎꞏ
+ꞐꞑꞒꞓꞔꞕꞖꞗꞘꞙꞚꞛꞜꞝꞞꞟꞠꞡꞢꞣꞤꞥꞦꞧꞨꞩꞪꞫꞬꞭꞮꞯꞰꞱꞲꞳꞴꞵꞶꞷꞸꞹꞺꞻꞼꞽꞾꞿꟀꟁꟂꟃꟄꟅꟆꟇꟈꟉꟊꟋꟌꟍ꟎꟏
+Ꟑꟑ꟒ꟓ꟔ꟕꟖꟗꟘꟙꟚꟛꟜ꟝꟞꟟꟠꟡꟢꟣꟤꟥꟦꟧꟨꟩꟪꟫꟬꟭꟮꟯꟰꟱ꟲꟳꟴꟵꟶꟷꟸꟹꟺꟻꟼꟽꟾꟿꠀꠁꠂꠃꠄꠅ꠆ꠇꠈꠉꠊꠋꠌꠍꠎꠏ
+ꠐꠑꠒꠓꠔꠕꠖꠗꠘꠙꠚꠛꠜꠝꠞꠟꠠꠡꠢꠣꠤꠥꠦꠧ꠨꠩꠪꠫꠬꠭꠮꠯꠰꠱꠲꠳꠴꠵꠶꠷꠸꠹꠺꠻꠼꠽꠾꠿ꡀꡁꡂꡃꡄꡅꡆꡇꡈꡉꡊꡋꡌꡍꡎꡏ
+ꡐꡑꡒꡓꡔꡕꡖꡗꡘꡙꡚꡛꡜꡝꡞꡟꡠꡡꡢꡣꡤꡥꡦꡧꡨꡩꡪꡫꡬꡭꡮꡯꡰꡱꡲꡳ꡴꡵꡶꡷꡸꡹꡺꡻꡼꡽꡾꡿ꢀꢁꢂꢃꢄꢅꢆꢇꢈꢉꢊꢋꢌꢍꢎꢏ
+ꢐꢑꢒꢓꢔꢕꢖꢗꢘꢙꢚꢛꢜꢝꢞꢟꢠꢡꢢꢣꢤꢥꢦꢧꢨꢩꢪꢫꢬꢭꢮꢯꢰꢱꢲꢳꢴꢵꢶꢷꢸꢹꢺꢻꢼꢽꢾꢿꣀꣁꣂꣃ꣄ꣅ꣆꣇꣈꣉꣊꣋꣌꣍꣎꣏
+꣐꣑꣒꣓꣔꣕꣖꣗꣘꣙꣚꣛꣜꣝꣞꣟꣠꣡꣢꣣꣤꣥꣦꣧꣨꣩꣪꣫꣬꣭꣮꣯꣰꣱ꣲꣳꣴꣵꣶꣷ꣸꣹꣺ꣻ꣼ꣽꣾꣿ꤀꤁꤂꤃꤄꤅꤆꤇꤈꤉ꤊꤋꤌꤍꤎꤏ
+ꤐꤑꤒꤓꤔꤕꤖꤗꤘꤙꤚꤛꤜꤝꤞꤟꤠꤡꤢꤣꤤꤥꤦꤧꤨꤩꤪ꤫꤬꤭꤮꤯ꤰꤱꤲꤳꤴꤵꤶꤷꤸꤹꤺꤻꤼꤽꤾꤿꥀꥁꥂꥃꥄꥅꥆꥇꥈꥉꥊꥋꥌꥍꥎꥏ
+ꥐꥑꥒ꥓꥔꥕꥖꥗꥘꥙꥚꥛꥜꥝꥞꥟ꥠꥡꥢꥣꥤꥥꥦꥧꥨꥩꥪꥫꥬꥭꥮꥯꥰꥱꥲꥳꥴꥵꥶꥷꥸꥹꥺꥻꥼ꥽꥾꥿ꦀꦁꦂꦃꦄꦅꦆꦇꦈꦉꦊꦋꦌꦍꦎꦏ
+ꦐꦑꦒꦓꦔꦕꦖꦗꦘꦙꦚꦛꦜꦝꦞꦟꦠꦡꦢꦣꦤꦥꦦꦧꦨꦩꦪꦫꦬꦭꦮꦯꦰꦱꦲ꦳ꦴꦵꦶꦷꦸꦹꦺꦻꦼꦽꦾꦿ꧀꧁꧂꧃꧄꧅꧆꧇꧈꧉꧊꧋꧌꧍꧎ꧏ
+꧐꧑꧒꧓꧔꧕꧖꧗꧘꧙꧚꧛꧜꧝꧞꧟ꧠꧡꧢꧣꧤꧥꧦꧧꧨꧩꧪꧫꧬꧭꧮꧯ꧰꧱꧲꧳꧴꧵꧶꧷꧸꧹ꧺꧻꧼꧽꧾ꧿ꨀꨁꨂꨃꨄꨅꨆꨇꨈꨉꨊꨋꨌꨍꨎꨏ
+ꨐꨑꨒꨓꨔꨕꨖꨗꨘꨙꨚꨛꨜꨝꨞꨟꨠꨡꨢꨣꨤꨥꨦꨧꨨꨩꨪꨫꨬꨭꨮꨯꨰꨱꨲꨳꨴꨵꨶ꨷꨸꨹꨺꨻꨼꨽꨾꨿ꩀꩁꩂꩃꩄꩅꩆꩇꩈꩉꩊꩋꩌꩍ꩎꩏
+꩐꩑꩒꩓꩔꩕꩖꩗꩘꩙꩚꩛꩜꩝꩞꩟ꩠꩡꩢꩣꩤꩥꩦꩧꩨꩩꩪꩫꩬꩭꩮꩯꩰꩱꩲꩳꩴꩵꩶ꩷꩸꩹ꩺꩻꩼꩽꩾꩿꪀꪁꪂꪃꪄꪅꪆꪇꪈꪉꪊꪋꪌꪍꪎꪏ
+ꪐꪑꪒꪓꪔꪕꪖꪗꪘꪙꪚꪛꪜꪝꪞꪟꪠꪡꪢꪣꪤꪥꪦꪧꪨꪩꪪꪫꪬꪭꪮꪯꪰꪱꪴꪲꪳꪵꪶꪷꪸꪹꪺꪻꪼꪽꪾ꪿ꫀ꫁ꫂ꫃꫄꫅꫆꫇꫈꫉꫊꫋꫌꫍꫎꫏
+꫐꫑꫒꫓꫔꫕꫖꫗꫘꫙꫚ꫛꫜꫝ꫞꫟ꫠꫡꫢꫣꫤꫥꫦꫧꫨꫩꫪꫫꫬꫭꫮꫯ꫰꫱ꫲꫳꫴꫵ꫶꫷꫸꫹꫺꫻꫼꫽꫾꫿꬀ꬁꬂꬃꬄꬅꬆ꬇꬈ꬉꬊꬋꬌꬍꬎ꬏
+꬐ꬑꬒꬓꬔꬕꬖ꬗꬘꬙꬚꬛꬜꬝꬞꬟ꬠꬡꬢꬣꬤꬥꬦ꬧ꬨꬩꬪꬫꬬꬭꬮ꬯ꬰꬱꬲꬳꬴꬵꬶꬷꬸꬹꬺꬻꬼꬽꬾꬿꭀꭁꭂꭃꭄꭅꭆꭇꭈꭉꭊꭋꭌꭍꭎꭏ
+ꭐꭑꭒꭓꭔꭕꭖꭗꭘꭙꭚ꭛ꭜꭝꭞꭟꭠꭡꭢꭣꭤꭥꭦꭧꭨꭩ꭪꭫꭬꭭꭮꭯ꭰꭱꭲꭳꭴꭵꭶꭷꭸꭹꭺꭻꭼꭽꭾꭿꮀꮁꮂꮃꮄꮅꮆꮇꮈꮉꮊꮋꮌꮍꮎꮏ
+ꮐꮑꮒꮓꮔꮕꮖꮗꮘꮙꮚꮛꮜꮝꮞꮟꮠꮡꮢꮣꮤꮥꮦꮧꮨꮩꮪꮫꮬꮭꮮꮯꮰꮱꮲꮳꮴꮵꮶꮷꮸꮹꮺꮻꮼꮽꮾꮿꯀꯁꯂꯃꯄꯅꯆꯇꯈꯉꯊꯋꯌꯍꯎꯏ
+ꯐꯑꯒꯓꯔꯕꯖꯗꯘꯙꯚꯛꯜꯝꯞꯟꯠꯡꯢꯣꯤꯥꯦꯧꯨꯩꯪ꯫꯬꯭꯮꯯꯰꯱꯲꯳꯴꯵꯶꯷꯸꯹꯺꯻꯼꯽꯾꯿
+
+Hangul Syllables (U+AC00-U+D7AF):
+
+가각갂갃간갅갆갇갈갉갊갋갌갍갎갏감갑값갓갔강갖갗갘같갚갛개객갞갟
+갠갡갢갣갤갥갦갧갨갩갪갫갬갭갮갯갰갱갲갳갴갵갶갷갸갹갺갻갼갽갾갿
+걀걁걂걃걄걅걆걇걈걉걊걋걌걍걎걏걐걑걒걓걔걕걖걗걘걙걚걛걜걝걞걟
+걠걡걢걣걤걥걦걧걨걩걪걫걬걭걮걯거걱걲걳건걵걶걷걸걹걺걻걼걽걾걿
+검겁겂것겄겅겆겇겈겉겊겋게겍겎겏겐겑겒겓겔겕겖겗겘겙겚겛겜겝겞겟
+겠겡겢겣겤겥겦겧겨격겪겫견겭겮겯결겱겲겳겴겵겶겷겸겹겺겻겼경겾겿
+곀곁곂곃계곅곆곇곈곉곊곋곌곍곎곏곐곑곒곓곔곕곖곗곘곙곚곛곜곝곞곟
+고곡곢곣곤곥곦곧골곩곪곫곬곭곮곯곰곱곲곳곴공곶곷곸곹곺곻과곽곾곿
+관괁괂괃괄괅괆괇괈괉괊괋괌괍괎괏괐광괒괓괔괕괖괗괘괙괚괛괜괝괞괟
+괠괡괢괣괤괥괦괧괨괩괪괫괬괭괮괯괰괱괲괳괴괵괶괷괸괹괺괻괼괽괾괿
+굀굁굂굃굄굅굆굇굈굉굊굋굌굍굎굏교굑굒굓굔굕굖굗굘굙굚굛굜굝굞굟
+굠굡굢굣굤굥굦굧굨굩굪굫구국굮굯군굱굲굳굴굵굶굷굸굹굺굻굼굽굾굿
+궀궁궂궃궄궅궆궇궈궉궊궋권궍궎궏궐궑궒궓궔궕궖궗궘궙궚궛궜궝궞궟
+궠궡궢궣궤궥궦궧궨궩궪궫궬궭궮궯궰궱궲궳궴궵궶궷궸궹궺궻궼궽궾궿
+귀귁귂귃귄귅귆귇귈귉귊귋귌귍귎귏귐귑귒귓귔귕귖귗귘귙귚귛규귝귞귟
+균귡귢귣귤귥귦귧귨귩귪귫귬귭귮귯귰귱귲귳귴귵귶귷그극귺귻근귽귾귿
+글긁긂긃긄긅긆긇금급긊긋긌긍긎긏긐긑긒긓긔긕긖긗긘긙긚긛긜긝긞긟
+긠긡긢긣긤긥긦긧긨긩긪긫긬긭긮긯기긱긲긳긴긵긶긷길긹긺긻긼긽긾긿
+김깁깂깃깄깅깆깇깈깉깊깋까깍깎깏깐깑깒깓깔깕깖깗깘깙깚깛깜깝깞깟
+깠깡깢깣깤깥깦깧깨깩깪깫깬깭깮깯깰깱깲깳깴깵깶깷깸깹깺깻깼깽깾깿
+꺀꺁꺂꺃꺄꺅꺆꺇꺈꺉꺊꺋꺌꺍꺎꺏꺐꺑꺒꺓꺔꺕꺖꺗꺘꺙꺚꺛꺜꺝꺞꺟
+꺠꺡꺢꺣꺤꺥꺦꺧꺨꺩꺪꺫꺬꺭꺮꺯꺰꺱꺲꺳꺴꺵꺶꺷꺸꺹꺺꺻꺼꺽꺾꺿
+껀껁껂껃껄껅껆껇껈껉껊껋껌껍껎껏껐껑껒껓껔껕껖껗께껙껚껛껜껝껞껟
+껠껡껢껣껤껥껦껧껨껩껪껫껬껭껮껯껰껱껲껳껴껵껶껷껸껹껺껻껼껽껾껿
+꼀꼁꼂꼃꼄꼅꼆꼇꼈꼉꼊꼋꼌꼍꼎꼏꼐꼑꼒꼓꼔꼕꼖꼗꼘꼙꼚꼛꼜꼝꼞꼟
+꼠꼡꼢꼣꼤꼥꼦꼧꼨꼩꼪꼫꼬꼭꼮꼯꼰꼱꼲꼳꼴꼵꼶꼷꼸꼹꼺꼻꼼꼽꼾꼿
+꽀꽁꽂꽃꽄꽅꽆꽇꽈꽉꽊꽋꽌꽍꽎꽏꽐꽑꽒꽓꽔꽕꽖꽗꽘꽙꽚꽛꽜꽝꽞꽟
+꽠꽡꽢꽣꽤꽥꽦꽧꽨꽩꽪꽫꽬꽭꽮꽯꽰꽱꽲꽳꽴꽵꽶꽷꽸꽹꽺꽻꽼꽽꽾꽿
+꾀꾁꾂꾃꾄꾅꾆꾇꾈꾉꾊꾋꾌꾍꾎꾏꾐꾑꾒꾓꾔꾕꾖꾗꾘꾙꾚꾛꾜꾝꾞꾟
+꾠꾡꾢꾣꾤꾥꾦꾧꾨꾩꾪꾫꾬꾭꾮꾯꾰꾱꾲꾳꾴꾵꾶꾷꾸꾹꾺꾻꾼꾽꾾꾿
+꿀꿁꿂꿃꿄꿅꿆꿇꿈꿉꿊꿋꿌꿍꿎꿏꿐꿑꿒꿓꿔꿕꿖꿗꿘꿙꿚꿛꿜꿝꿞꿟
+꿠꿡꿢꿣꿤꿥꿦꿧꿨꿩꿪꿫꿬꿭꿮꿯꿰꿱꿲꿳꿴꿵꿶꿷꿸꿹꿺꿻꿼꿽꿾꿿
+뀀뀁뀂뀃뀄뀅뀆뀇뀈뀉뀊뀋뀌뀍뀎뀏뀐뀑뀒뀓뀔뀕뀖뀗뀘뀙뀚뀛뀜뀝뀞뀟
+뀠뀡뀢뀣뀤뀥뀦뀧뀨뀩뀪뀫뀬뀭뀮뀯뀰뀱뀲뀳뀴뀵뀶뀷뀸뀹뀺뀻뀼뀽뀾뀿
+끀끁끂끃끄끅끆끇끈끉끊끋끌끍끎끏끐끑끒끓끔끕끖끗끘끙끚끛끜끝끞끟
+끠끡끢끣끤끥끦끧끨끩끪끫끬끭끮끯끰끱끲끳끴끵끶끷끸끹끺끻끼끽끾끿
+낀낁낂낃낄낅낆낇낈낉낊낋낌낍낎낏낐낑낒낓낔낕낖낗나낙낚낛난낝낞낟
+날낡낢낣낤낥낦낧남납낪낫났낭낮낯낰낱낲낳내낵낶낷낸낹낺낻낼낽낾낿
+냀냁냂냃냄냅냆냇냈냉냊냋냌냍냎냏냐냑냒냓냔냕냖냗냘냙냚냛냜냝냞냟
+냠냡냢냣냤냥냦냧냨냩냪냫냬냭냮냯냰냱냲냳냴냵냶냷냸냹냺냻냼냽냾냿
+넀넁넂넃넄넅넆넇너넉넊넋넌넍넎넏널넑넒넓넔넕넖넗넘넙넚넛넜넝넞넟
+넠넡넢넣네넥넦넧넨넩넪넫넬넭넮넯넰넱넲넳넴넵넶넷넸넹넺넻넼넽넾넿
+녀녁녂녃년녅녆녇녈녉녊녋녌녍녎녏념녑녒녓녔녕녖녗녘녙녚녛녜녝녞녟
+녠녡녢녣녤녥녦녧녨녩녪녫녬녭녮녯녰녱녲녳녴녵녶녷노녹녺녻논녽녾녿
+놀놁놂놃놄놅놆놇놈놉놊놋놌농놎놏놐놑높놓놔놕놖놗놘놙놚놛놜놝놞놟
+놠놡놢놣놤놥놦놧놨놩놪놫놬놭놮놯놰놱놲놳놴놵놶놷놸놹놺놻놼놽놾놿
+뇀뇁뇂뇃뇄뇅뇆뇇뇈뇉뇊뇋뇌뇍뇎뇏뇐뇑뇒뇓뇔뇕뇖뇗뇘뇙뇚뇛뇜뇝뇞뇟
+뇠뇡뇢뇣뇤뇥뇦뇧뇨뇩뇪뇫뇬뇭뇮뇯뇰뇱뇲뇳뇴뇵뇶뇷뇸뇹뇺뇻뇼뇽뇾뇿
+눀눁눂눃누눅눆눇눈눉눊눋눌눍눎눏눐눑눒눓눔눕눖눗눘눙눚눛눜눝눞눟
+눠눡눢눣눤눥눦눧눨눩눪눫눬눭눮눯눰눱눲눳눴눵눶눷눸눹눺눻눼눽눾눿
+뉀뉁뉂뉃뉄뉅뉆뉇뉈뉉뉊뉋뉌뉍뉎뉏뉐뉑뉒뉓뉔뉕뉖뉗뉘뉙뉚뉛뉜뉝뉞뉟
+뉠뉡뉢뉣뉤뉥뉦뉧뉨뉩뉪뉫뉬뉭뉮뉯뉰뉱뉲뉳뉴뉵뉶뉷뉸뉹뉺뉻뉼뉽뉾뉿
+늀늁늂늃늄늅늆늇늈늉늊늋늌늍늎늏느늑늒늓는늕늖늗늘늙늚늛늜늝늞늟
+늠늡늢늣늤능늦늧늨늩늪늫늬늭늮늯늰늱늲늳늴늵늶늷늸늹늺늻늼늽늾늿
+닀닁닂닃닄닅닆닇니닉닊닋닌닍닎닏닐닑닒닓닔닕닖닗님닙닚닛닜닝닞닟
+닠닡닢닣다닥닦닧단닩닪닫달닭닮닯닰닱닲닳담답닶닷닸당닺닻닼닽닾닿
+대댁댂댃댄댅댆댇댈댉댊댋댌댍댎댏댐댑댒댓댔댕댖댗댘댙댚댛댜댝댞댟
+댠댡댢댣댤댥댦댧댨댩댪댫댬댭댮댯댰댱댲댳댴댵댶댷댸댹댺댻댼댽댾댿
+덀덁덂덃덄덅덆덇덈덉덊덋덌덍덎덏덐덑덒덓더덕덖덗던덙덚덛덜덝덞덟
+덠덡덢덣덤덥덦덧덨덩덪덫덬덭덮덯데덱덲덳덴덵덶덷델덹덺덻덼덽덾덿
+뎀뎁뎂뎃뎄뎅뎆뎇뎈뎉뎊뎋뎌뎍뎎뎏뎐뎑뎒뎓뎔뎕뎖뎗뎘뎙뎚뎛뎜뎝뎞뎟
+뎠뎡뎢뎣뎤뎥뎦뎧뎨뎩뎪뎫뎬뎭뎮뎯뎰뎱뎲뎳뎴뎵뎶뎷뎸뎹뎺뎻뎼뎽뎾뎿
+돀돁돂돃도독돆돇돈돉돊돋돌돍돎돏돐돑돒돓돔돕돖돗돘동돚돛돜돝돞돟
+돠돡돢돣돤돥돦돧돨돩돪돫돬돭돮돯돰돱돲돳돴돵돶돷돸돹돺돻돼돽돾돿
+됀됁됂됃됄됅됆됇됈됉됊됋됌됍됎됏됐됑됒됓됔됕됖됗되됙됚됛된됝됞됟
+될됡됢됣됤됥됦됧됨됩됪됫됬됭됮됯됰됱됲됳됴됵됶됷됸됹됺됻됼됽됾됿
+둀둁둂둃둄둅둆둇둈둉둊둋둌둍둎둏두둑둒둓둔둕둖둗둘둙둚둛둜둝둞둟
+둠둡둢둣둤둥둦둧둨둩둪둫둬둭둮둯둰둱둲둳둴둵둶둷둸둹둺둻둼둽둾둿
+뒀뒁뒂뒃뒄뒅뒆뒇뒈뒉뒊뒋뒌뒍뒎뒏뒐뒑뒒뒓뒔뒕뒖뒗뒘뒙뒚뒛뒜뒝뒞뒟
+뒠뒡뒢뒣뒤뒥뒦뒧뒨뒩뒪뒫뒬뒭뒮뒯뒰뒱뒲뒳뒴뒵뒶뒷뒸뒹뒺뒻뒼뒽뒾뒿
+듀듁듂듃듄듅듆듇듈듉듊듋듌듍듎듏듐듑듒듓듔듕듖듗듘듙듚듛드득듞듟
+든듡듢듣들듥듦듧듨듩듪듫듬듭듮듯듰등듲듳듴듵듶듷듸듹듺듻듼듽듾듿
+딀딁딂딃딄딅딆딇딈딉딊딋딌딍딎딏딐딑딒딓디딕딖딗딘딙딚딛딜딝딞딟
+딠딡딢딣딤딥딦딧딨딩딪딫딬딭딮딯따딱딲딳딴딵딶딷딸딹딺딻딼딽딾딿
+땀땁땂땃땄땅땆땇땈땉땊땋때땍땎땏땐땑땒땓땔땕땖땗땘땙땚땛땜땝땞땟
+땠땡땢땣땤땥땦땧땨땩땪땫땬땭땮땯땰땱땲땳땴땵땶땷땸땹땺땻땼땽땾땿
+떀떁떂떃떄떅떆떇떈떉떊떋떌떍떎떏떐떑떒떓떔떕떖떗떘떙떚떛떜떝떞떟
+떠떡떢떣떤떥떦떧떨떩떪떫떬떭떮떯떰떱떲떳떴떵떶떷떸떹떺떻떼떽떾떿
+뗀뗁뗂뗃뗄뗅뗆뗇뗈뗉뗊뗋뗌뗍뗎뗏뗐뗑뗒뗓뗔뗕뗖뗗뗘뗙뗚뗛뗜뗝뗞뗟
+뗠뗡뗢뗣뗤뗥뗦뗧뗨뗩뗪뗫뗬뗭뗮뗯뗰뗱뗲뗳뗴뗵뗶뗷뗸뗹뗺뗻뗼뗽뗾뗿
+똀똁똂똃똄똅똆똇똈똉똊똋똌똍똎똏또똑똒똓똔똕똖똗똘똙똚똛똜똝똞똟
+똠똡똢똣똤똥똦똧똨똩똪똫똬똭똮똯똰똱똲똳똴똵똶똷똸똹똺똻똼똽똾똿
+뙀뙁뙂뙃뙄뙅뙆뙇뙈뙉뙊뙋뙌뙍뙎뙏뙐뙑뙒뙓뙔뙕뙖뙗뙘뙙뙚뙛뙜뙝뙞뙟
+뙠뙡뙢뙣뙤뙥뙦뙧뙨뙩뙪뙫뙬뙭뙮뙯뙰뙱뙲뙳뙴뙵뙶뙷뙸뙹뙺뙻뙼뙽뙾뙿
+뚀뚁뚂뚃뚄뚅뚆뚇뚈뚉뚊뚋뚌뚍뚎뚏뚐뚑뚒뚓뚔뚕뚖뚗뚘뚙뚚뚛뚜뚝뚞뚟
+뚠뚡뚢뚣뚤뚥뚦뚧뚨뚩뚪뚫뚬뚭뚮뚯뚰뚱뚲뚳뚴뚵뚶뚷뚸뚹뚺뚻뚼뚽뚾뚿
+뛀뛁뛂뛃뛄뛅뛆뛇뛈뛉뛊뛋뛌뛍뛎뛏뛐뛑뛒뛓뛔뛕뛖뛗뛘뛙뛚뛛뛜뛝뛞뛟
+뛠뛡뛢뛣뛤뛥뛦뛧뛨뛩뛪뛫뛬뛭뛮뛯뛰뛱뛲뛳뛴뛵뛶뛷뛸뛹뛺뛻뛼뛽뛾뛿
+뜀뜁뜂뜃뜄뜅뜆뜇뜈뜉뜊뜋뜌뜍뜎뜏뜐뜑뜒뜓뜔뜕뜖뜗뜘뜙뜚뜛뜜뜝뜞뜟
+뜠뜡뜢뜣뜤뜥뜦뜧뜨뜩뜪뜫뜬뜭뜮뜯뜰뜱뜲뜳뜴뜵뜶뜷뜸뜹뜺뜻뜼뜽뜾뜿
+띀띁띂띃띄띅띆띇띈띉띊띋띌띍띎띏띐띑띒띓띔띕띖띗띘띙띚띛띜띝띞띟
+띠띡띢띣띤띥띦띧띨띩띪띫띬띭띮띯띰띱띲띳띴띵띶띷띸띹띺띻라락띾띿
+란랁랂랃랄랅랆랇랈랉랊랋람랍랎랏랐랑랒랓랔랕랖랗래랙랚랛랜랝랞랟
+랠랡랢랣랤랥랦랧램랩랪랫랬랭랮랯랰랱랲랳랴략랶랷랸랹랺랻랼랽랾랿
+럀럁럂럃럄럅럆럇럈량럊럋럌럍럎럏럐럑럒럓럔럕럖럗럘럙럚럛럜럝럞럟
+럠럡럢럣럤럥럦럧럨럩럪럫러럭럮럯런럱럲럳럴럵럶럷럸럹럺럻럼럽럾럿
+렀렁렂렃렄렅렆렇레렉렊렋렌렍렎렏렐렑렒렓렔렕렖렗렘렙렚렛렜렝렞렟
+렠렡렢렣려력렦렧련렩렪렫렬렭렮렯렰렱렲렳렴렵렶렷렸령렺렻렼렽렾렿
+례롁롂롃롄롅롆롇롈롉롊롋롌롍롎롏롐롑롒롓롔롕롖롗롘롙롚롛로록롞롟
+론롡롢롣롤롥롦롧롨롩롪롫롬롭롮롯롰롱롲롳롴롵롶롷롸롹롺롻롼롽롾롿
+뢀뢁뢂뢃뢄뢅뢆뢇뢈뢉뢊뢋뢌뢍뢎뢏뢐뢑뢒뢓뢔뢕뢖뢗뢘뢙뢚뢛뢜뢝뢞뢟
+뢠뢡뢢뢣뢤뢥뢦뢧뢨뢩뢪뢫뢬뢭뢮뢯뢰뢱뢲뢳뢴뢵뢶뢷뢸뢹뢺뢻뢼뢽뢾뢿
+룀룁룂룃룄룅룆룇룈룉룊룋료룍룎룏룐룑룒룓룔룕룖룗룘룙룚룛룜룝룞룟
+룠룡룢룣룤룥룦룧루룩룪룫룬룭룮룯룰룱룲룳룴룵룶룷룸룹룺룻룼룽룾룿
+뤀뤁뤂뤃뤄뤅뤆뤇뤈뤉뤊뤋뤌뤍뤎뤏뤐뤑뤒뤓뤔뤕뤖뤗뤘뤙뤚뤛뤜뤝뤞뤟
+뤠뤡뤢뤣뤤뤥뤦뤧뤨뤩뤪뤫뤬뤭뤮뤯뤰뤱뤲뤳뤴뤵뤶뤷뤸뤹뤺뤻뤼뤽뤾뤿
+륀륁륂륃륄륅륆륇륈륉륊륋륌륍륎륏륐륑륒륓륔륕륖륗류륙륚륛륜륝륞륟
+률륡륢륣륤륥륦륧륨륩륪륫륬륭륮륯륰륱륲륳르륵륶륷른륹륺륻를륽륾륿
+릀릁릂릃름릅릆릇릈릉릊릋릌릍릎릏릐릑릒릓릔릕릖릗릘릙릚릛릜릝릞릟
+릠릡릢릣릤릥릦릧릨릩릪릫리릭릮릯린릱릲릳릴릵릶릷릸릹릺릻림립릾릿
+맀링맂맃맄맅맆맇마막맊맋만맍많맏말맑맒맓맔맕맖맗맘맙맚맛맜망맞맟
+맠맡맢맣매맥맦맧맨맩맪맫맬맭맮맯맰맱맲맳맴맵맶맷맸맹맺맻맼맽맾맿
+먀먁먂먃먄먅먆먇먈먉먊먋먌먍먎먏먐먑먒먓먔먕먖먗먘먙먚먛먜먝먞먟
+먠먡먢먣먤먥먦먧먨먩먪먫먬먭먮먯먰먱먲먳먴먵먶먷머먹먺먻먼먽먾먿
+멀멁멂멃멄멅멆멇멈멉멊멋멌멍멎멏멐멑멒멓메멕멖멗멘멙멚멛멜멝멞멟
+멠멡멢멣멤멥멦멧멨멩멪멫멬멭멮멯며멱멲멳면멵멶멷멸멹멺멻멼멽멾멿
+몀몁몂몃몄명몆몇몈몉몊몋몌몍몎몏몐몑몒몓몔몕몖몗몘몙몚몛몜몝몞몟
+몠몡몢몣몤몥몦몧모목몪몫몬몭몮몯몰몱몲몳몴몵몶몷몸몹몺못몼몽몾몿
+뫀뫁뫂뫃뫄뫅뫆뫇뫈뫉뫊뫋뫌뫍뫎뫏뫐뫑뫒뫓뫔뫕뫖뫗뫘뫙뫚뫛뫜뫝뫞뫟
+뫠뫡뫢뫣뫤뫥뫦뫧뫨뫩뫪뫫뫬뫭뫮뫯뫰뫱뫲뫳뫴뫵뫶뫷뫸뫹뫺뫻뫼뫽뫾뫿
+묀묁묂묃묄묅묆묇묈묉묊묋묌묍묎묏묐묑묒묓묔묕묖묗묘묙묚묛묜묝묞묟
+묠묡묢묣묤묥묦묧묨묩묪묫묬묭묮묯묰묱묲묳무묵묶묷문묹묺묻물묽묾묿
+뭀뭁뭂뭃뭄뭅뭆뭇뭈뭉뭊뭋뭌뭍뭎뭏뭐뭑뭒뭓뭔뭕뭖뭗뭘뭙뭚뭛뭜뭝뭞뭟
+뭠뭡뭢뭣뭤뭥뭦뭧뭨뭩뭪뭫뭬뭭뭮뭯뭰뭱뭲뭳뭴뭵뭶뭷뭸뭹뭺뭻뭼뭽뭾뭿
+뮀뮁뮂뮃뮄뮅뮆뮇뮈뮉뮊뮋뮌뮍뮎뮏뮐뮑뮒뮓뮔뮕뮖뮗뮘뮙뮚뮛뮜뮝뮞뮟
+뮠뮡뮢뮣뮤뮥뮦뮧뮨뮩뮪뮫뮬뮭뮮뮯뮰뮱뮲뮳뮴뮵뮶뮷뮸뮹뮺뮻뮼뮽뮾뮿
+므믁믂믃믄믅믆믇믈믉믊믋믌믍믎믏믐믑믒믓믔믕믖믗믘믙믚믛믜믝믞믟
+믠믡믢믣믤믥믦믧믨믩믪믫믬믭믮믯믰믱믲믳믴믵믶믷미믹믺믻민믽믾믿
+밀밁밂밃밄밅밆밇밈밉밊밋밌밍밎및밐밑밒밓바박밖밗반밙밚받발밝밞밟
+밠밡밢밣밤밥밦밧밨방밪밫밬밭밮밯배백밲밳밴밵밶밷밸밹밺밻밼밽밾밿
+뱀뱁뱂뱃뱄뱅뱆뱇뱈뱉뱊뱋뱌뱍뱎뱏뱐뱑뱒뱓뱔뱕뱖뱗뱘뱙뱚뱛뱜뱝뱞뱟
+뱠뱡뱢뱣뱤뱥뱦뱧뱨뱩뱪뱫뱬뱭뱮뱯뱰뱱뱲뱳뱴뱵뱶뱷뱸뱹뱺뱻뱼뱽뱾뱿
+벀벁벂벃버벅벆벇번벉벊벋벌벍벎벏벐벑벒벓범법벖벗벘벙벚벛벜벝벞벟
+베벡벢벣벤벥벦벧벨벩벪벫벬벭벮벯벰벱벲벳벴벵벶벷벸벹벺벻벼벽벾벿
+변볁볂볃별볅볆볇볈볉볊볋볌볍볎볏볐병볒볓볔볕볖볗볘볙볚볛볜볝볞볟
+볠볡볢볣볤볥볦볧볨볩볪볫볬볭볮볯볰볱볲볳보복볶볷본볹볺볻볼볽볾볿
+봀봁봂봃봄봅봆봇봈봉봊봋봌봍봎봏봐봑봒봓봔봕봖봗봘봙봚봛봜봝봞봟
+봠봡봢봣봤봥봦봧봨봩봪봫봬봭봮봯봰봱봲봳봴봵봶봷봸봹봺봻봼봽봾봿
+뵀뵁뵂뵃뵄뵅뵆뵇뵈뵉뵊뵋뵌뵍뵎뵏뵐뵑뵒뵓뵔뵕뵖뵗뵘뵙뵚뵛뵜뵝뵞뵟
+뵠뵡뵢뵣뵤뵥뵦뵧뵨뵩뵪뵫뵬뵭뵮뵯뵰뵱뵲뵳뵴뵵뵶뵷뵸뵹뵺뵻뵼뵽뵾뵿
+부북붂붃분붅붆붇불붉붊붋붌붍붎붏붐붑붒붓붔붕붖붗붘붙붚붛붜붝붞붟
+붠붡붢붣붤붥붦붧붨붩붪붫붬붭붮붯붰붱붲붳붴붵붶붷붸붹붺붻붼붽붾붿
+뷀뷁뷂뷃뷄뷅뷆뷇뷈뷉뷊뷋뷌뷍뷎뷏뷐뷑뷒뷓뷔뷕뷖뷗뷘뷙뷚뷛뷜뷝뷞뷟
+뷠뷡뷢뷣뷤뷥뷦뷧뷨뷩뷪뷫뷬뷭뷮뷯뷰뷱뷲뷳뷴뷵뷶뷷뷸뷹뷺뷻뷼뷽뷾뷿
+븀븁븂븃븄븅븆븇븈븉븊븋브븍븎븏븐븑븒븓블븕븖븗븘븙븚븛븜븝븞븟
+븠븡븢븣븤븥븦븧븨븩븪븫븬븭븮븯븰븱븲븳븴븵븶븷븸븹븺븻븼븽븾븿
+빀빁빂빃비빅빆빇빈빉빊빋빌빍빎빏빐빑빒빓빔빕빖빗빘빙빚빛빜빝빞빟
+빠빡빢빣빤빥빦빧빨빩빪빫빬빭빮빯빰빱빲빳빴빵빶빷빸빹빺빻빼빽빾빿
+뺀뺁뺂뺃뺄뺅뺆뺇뺈뺉뺊뺋뺌뺍뺎뺏뺐뺑뺒뺓뺔뺕뺖뺗뺘뺙뺚뺛뺜뺝뺞뺟
+뺠뺡뺢뺣뺤뺥뺦뺧뺨뺩뺪뺫뺬뺭뺮뺯뺰뺱뺲뺳뺴뺵뺶뺷뺸뺹뺺뺻뺼뺽뺾뺿
+뻀뻁뻂뻃뻄뻅뻆뻇뻈뻉뻊뻋뻌뻍뻎뻏뻐뻑뻒뻓뻔뻕뻖뻗뻘뻙뻚뻛뻜뻝뻞뻟
+뻠뻡뻢뻣뻤뻥뻦뻧뻨뻩뻪뻫뻬뻭뻮뻯뻰뻱뻲뻳뻴뻵뻶뻷뻸뻹뻺뻻뻼뻽뻾뻿
+뼀뼁뼂뼃뼄뼅뼆뼇뼈뼉뼊뼋뼌뼍뼎뼏뼐뼑뼒뼓뼔뼕뼖뼗뼘뼙뼚뼛뼜뼝뼞뼟
+뼠뼡뼢뼣뼤뼥뼦뼧뼨뼩뼪뼫뼬뼭뼮뼯뼰뼱뼲뼳뼴뼵뼶뼷뼸뼹뼺뼻뼼뼽뼾뼿
+뽀뽁뽂뽃뽄뽅뽆뽇뽈뽉뽊뽋뽌뽍뽎뽏뽐뽑뽒뽓뽔뽕뽖뽗뽘뽙뽚뽛뽜뽝뽞뽟
+뽠뽡뽢뽣뽤뽥뽦뽧뽨뽩뽪뽫뽬뽭뽮뽯뽰뽱뽲뽳뽴뽵뽶뽷뽸뽹뽺뽻뽼뽽뽾뽿
+뾀뾁뾂뾃뾄뾅뾆뾇뾈뾉뾊뾋뾌뾍뾎뾏뾐뾑뾒뾓뾔뾕뾖뾗뾘뾙뾚뾛뾜뾝뾞뾟
+뾠뾡뾢뾣뾤뾥뾦뾧뾨뾩뾪뾫뾬뾭뾮뾯뾰뾱뾲뾳뾴뾵뾶뾷뾸뾹뾺뾻뾼뾽뾾뾿
+뿀뿁뿂뿃뿄뿅뿆뿇뿈뿉뿊뿋뿌뿍뿎뿏뿐뿑뿒뿓뿔뿕뿖뿗뿘뿙뿚뿛뿜뿝뿞뿟
+뿠뿡뿢뿣뿤뿥뿦뿧뿨뿩뿪뿫뿬뿭뿮뿯뿰뿱뿲뿳뿴뿵뿶뿷뿸뿹뿺뿻뿼뿽뿾뿿
+쀀쀁쀂쀃쀄쀅쀆쀇쀈쀉쀊쀋쀌쀍쀎쀏쀐쀑쀒쀓쀔쀕쀖쀗쀘쀙쀚쀛쀜쀝쀞쀟
+쀠쀡쀢쀣쀤쀥쀦쀧쀨쀩쀪쀫쀬쀭쀮쀯쀰쀱쀲쀳쀴쀵쀶쀷쀸쀹쀺쀻쀼쀽쀾쀿
+쁀쁁쁂쁃쁄쁅쁆쁇쁈쁉쁊쁋쁌쁍쁎쁏쁐쁑쁒쁓쁔쁕쁖쁗쁘쁙쁚쁛쁜쁝쁞쁟
+쁠쁡쁢쁣쁤쁥쁦쁧쁨쁩쁪쁫쁬쁭쁮쁯쁰쁱쁲쁳쁴쁵쁶쁷쁸쁹쁺쁻쁼쁽쁾쁿
+삀삁삂삃삄삅삆삇삈삉삊삋삌삍삎삏삐삑삒삓삔삕삖삗삘삙삚삛삜삝삞삟
+삠삡삢삣삤삥삦삧삨삩삪삫사삭삮삯산삱삲삳살삵삶삷삸삹삺삻삼삽삾삿
+샀상샂샃샄샅샆샇새색샊샋샌샍샎샏샐샑샒샓샔샕샖샗샘샙샚샛샜생샞샟
+샠샡샢샣샤샥샦샧샨샩샪샫샬샭샮샯샰샱샲샳샴샵샶샷샸샹샺샻샼샽샾샿
+섀섁섂섃섄섅섆섇섈섉섊섋섌섍섎섏섐섑섒섓섔섕섖섗섘섙섚섛서석섞섟
+선섡섢섣설섥섦섧섨섩섪섫섬섭섮섯섰성섲섳섴섵섶섷세섹섺섻센섽섾섿
+셀셁셂셃셄셅셆셇셈셉셊셋셌셍셎셏셐셑셒셓셔셕셖셗션셙셚셛셜셝셞셟
+셠셡셢셣셤셥셦셧셨셩셪셫셬셭셮셯셰셱셲셳셴셵셶셷셸셹셺셻셼셽셾셿
+솀솁솂솃솄솅솆솇솈솉솊솋소속솎솏손솑솒솓솔솕솖솗솘솙솚솛솜솝솞솟
+솠송솢솣솤솥솦솧솨솩솪솫솬솭솮솯솰솱솲솳솴솵솶솷솸솹솺솻솼솽솾솿
+쇀쇁쇂쇃쇄쇅쇆쇇쇈쇉쇊쇋쇌쇍쇎쇏쇐쇑쇒쇓쇔쇕쇖쇗쇘쇙쇚쇛쇜쇝쇞쇟
+쇠쇡쇢쇣쇤쇥쇦쇧쇨쇩쇪쇫쇬쇭쇮쇯쇰쇱쇲쇳쇴쇵쇶쇷쇸쇹쇺쇻쇼쇽쇾쇿
+숀숁숂숃숄숅숆숇숈숉숊숋숌숍숎숏숐숑숒숓숔숕숖숗수숙숚숛순숝숞숟
+술숡숢숣숤숥숦숧숨숩숪숫숬숭숮숯숰숱숲숳숴숵숶숷숸숹숺숻숼숽숾숿
+쉀쉁쉂쉃쉄쉅쉆쉇쉈쉉쉊쉋쉌쉍쉎쉏쉐쉑쉒쉓쉔쉕쉖쉗쉘쉙쉚쉛쉜쉝쉞쉟
+쉠쉡쉢쉣쉤쉥쉦쉧쉨쉩쉪쉫쉬쉭쉮쉯쉰쉱쉲쉳쉴쉵쉶쉷쉸쉹쉺쉻쉼쉽쉾쉿
+슀슁슂슃슄슅슆슇슈슉슊슋슌슍슎슏슐슑슒슓슔슕슖슗슘슙슚슛슜슝슞슟
+슠슡슢슣스슥슦슧슨슩슪슫슬슭슮슯슰슱슲슳슴습슶슷슸승슺슻슼슽슾슿
+싀싁싂싃싄싅싆싇싈싉싊싋싌싍싎싏싐싑싒싓싔싕싖싗싘싙싚싛시식싞싟
+신싡싢싣실싥싦싧싨싩싪싫심십싮싯싰싱싲싳싴싵싶싷싸싹싺싻싼싽싾싿
+쌀쌁쌂쌃쌄쌅쌆쌇쌈쌉쌊쌋쌌쌍쌎쌏쌐쌑쌒쌓쌔쌕쌖쌗쌘쌙쌚쌛쌜쌝쌞쌟
+쌠쌡쌢쌣쌤쌥쌦쌧쌨쌩쌪쌫쌬쌭쌮쌯쌰쌱쌲쌳쌴쌵쌶쌷쌸쌹쌺쌻쌼쌽쌾쌿
+썀썁썂썃썄썅썆썇썈썉썊썋썌썍썎썏썐썑썒썓썔썕썖썗썘썙썚썛썜썝썞썟
+썠썡썢썣썤썥썦썧써썩썪썫썬썭썮썯썰썱썲썳썴썵썶썷썸썹썺썻썼썽썾썿
+쎀쎁쎂쎃쎄쎅쎆쎇쎈쎉쎊쎋쎌쎍쎎쎏쎐쎑쎒쎓쎔쎕쎖쎗쎘쎙쎚쎛쎜쎝쎞쎟
+쎠쎡쎢쎣쎤쎥쎦쎧쎨쎩쎪쎫쎬쎭쎮쎯쎰쎱쎲쎳쎴쎵쎶쎷쎸쎹쎺쎻쎼쎽쎾쎿
+쏀쏁쏂쏃쏄쏅쏆쏇쏈쏉쏊쏋쏌쏍쏎쏏쏐쏑쏒쏓쏔쏕쏖쏗쏘쏙쏚쏛쏜쏝쏞쏟
+쏠쏡쏢쏣쏤쏥쏦쏧쏨쏩쏪쏫쏬쏭쏮쏯쏰쏱쏲쏳쏴쏵쏶쏷쏸쏹쏺쏻쏼쏽쏾쏿
+쐀쐁쐂쐃쐄쐅쐆쐇쐈쐉쐊쐋쐌쐍쐎쐏쐐쐑쐒쐓쐔쐕쐖쐗쐘쐙쐚쐛쐜쐝쐞쐟
+쐠쐡쐢쐣쐤쐥쐦쐧쐨쐩쐪쐫쐬쐭쐮쐯쐰쐱쐲쐳쐴쐵쐶쐷쐸쐹쐺쐻쐼쐽쐾쐿
+쑀쑁쑂쑃쑄쑅쑆쑇쑈쑉쑊쑋쑌쑍쑎쑏쑐쑑쑒쑓쑔쑕쑖쑗쑘쑙쑚쑛쑜쑝쑞쑟
+쑠쑡쑢쑣쑤쑥쑦쑧쑨쑩쑪쑫쑬쑭쑮쑯쑰쑱쑲쑳쑴쑵쑶쑷쑸쑹쑺쑻쑼쑽쑾쑿
+쒀쒁쒂쒃쒄쒅쒆쒇쒈쒉쒊쒋쒌쒍쒎쒏쒐쒑쒒쒓쒔쒕쒖쒗쒘쒙쒚쒛쒜쒝쒞쒟
+쒠쒡쒢쒣쒤쒥쒦쒧쒨쒩쒪쒫쒬쒭쒮쒯쒰쒱쒲쒳쒴쒵쒶쒷쒸쒹쒺쒻쒼쒽쒾쒿
+쓀쓁쓂쓃쓄쓅쓆쓇쓈쓉쓊쓋쓌쓍쓎쓏쓐쓑쓒쓓쓔쓕쓖쓗쓘쓙쓚쓛쓜쓝쓞쓟
+쓠쓡쓢쓣쓤쓥쓦쓧쓨쓩쓪쓫쓬쓭쓮쓯쓰쓱쓲쓳쓴쓵쓶쓷쓸쓹쓺쓻쓼쓽쓾쓿
+씀씁씂씃씄씅씆씇씈씉씊씋씌씍씎씏씐씑씒씓씔씕씖씗씘씙씚씛씜씝씞씟
+씠씡씢씣씤씥씦씧씨씩씪씫씬씭씮씯씰씱씲씳씴씵씶씷씸씹씺씻씼씽씾씿
+앀앁앂앃아악앆앇안앉않앋알앍앎앏앐앑앒앓암압앖앗았앙앚앛앜앝앞앟
+애액앢앣앤앥앦앧앨앩앪앫앬앭앮앯앰앱앲앳앴앵앶앷앸앹앺앻야약앾앿
+얀얁얂얃얄얅얆얇얈얉얊얋얌얍얎얏얐양얒얓얔얕얖얗얘얙얚얛얜얝얞얟
+얠얡얢얣얤얥얦얧얨얩얪얫얬얭얮얯얰얱얲얳어억얶얷언얹얺얻얼얽얾얿
+엀엁엂엃엄업없엇었엉엊엋엌엍엎엏에엑엒엓엔엕엖엗엘엙엚엛엜엝엞엟
+엠엡엢엣엤엥엦엧엨엩엪엫여역엮엯연엱엲엳열엵엶엷엸엹엺엻염엽엾엿
+였영옂옃옄옅옆옇예옉옊옋옌옍옎옏옐옑옒옓옔옕옖옗옘옙옚옛옜옝옞옟
+옠옡옢옣오옥옦옧온옩옪옫올옭옮옯옰옱옲옳옴옵옶옷옸옹옺옻옼옽옾옿
+와왁왂왃완왅왆왇왈왉왊왋왌왍왎왏왐왑왒왓왔왕왖왗왘왙왚왛왜왝왞왟
+왠왡왢왣왤왥왦왧왨왩왪왫왬왭왮왯왰왱왲왳왴왵왶왷외왹왺왻왼왽왾왿
+욀욁욂욃욄욅욆욇욈욉욊욋욌욍욎욏욐욑욒욓요욕욖욗욘욙욚욛욜욝욞욟
+욠욡욢욣욤욥욦욧욨용욪욫욬욭욮욯우욱욲욳운욵욶욷울욹욺욻욼욽욾욿
+움웁웂웃웄웅웆웇웈웉웊웋워웍웎웏원웑웒웓월웕웖웗웘웙웚웛웜웝웞웟
+웠웡웢웣웤웥웦웧웨웩웪웫웬웭웮웯웰웱웲웳웴웵웶웷웸웹웺웻웼웽웾웿
+윀윁윂윃위윅윆윇윈윉윊윋윌윍윎윏윐윑윒윓윔윕윖윗윘윙윚윛윜윝윞윟
+유육윢윣윤윥윦윧율윩윪윫윬윭윮윯윰윱윲윳윴융윶윷윸윹윺윻으윽윾윿
+은읁읂읃을읅읆읇읈읉읊읋음읍읎읏읐응읒읓읔읕읖읗의읙읚읛읜읝읞읟
+읠읡읢읣읤읥읦읧읨읩읪읫읬읭읮읯읰읱읲읳이익읶읷인읹읺읻일읽읾읿
+잀잁잂잃임입잆잇있잉잊잋잌잍잎잏자작잒잓잔잕잖잗잘잙잚잛잜잝잞잟
+잠잡잢잣잤장잦잧잨잩잪잫재잭잮잯잰잱잲잳잴잵잶잷잸잹잺잻잼잽잾잿
+쟀쟁쟂쟃쟄쟅쟆쟇쟈쟉쟊쟋쟌쟍쟎쟏쟐쟑쟒쟓쟔쟕쟖쟗쟘쟙쟚쟛쟜쟝쟞쟟
+쟠쟡쟢쟣쟤쟥쟦쟧쟨쟩쟪쟫쟬쟭쟮쟯쟰쟱쟲쟳쟴쟵쟶쟷쟸쟹쟺쟻쟼쟽쟾쟿
+저적젂젃전젅젆젇절젉젊젋젌젍젎젏점접젒젓젔정젖젗젘젙젚젛제젝젞젟
+젠젡젢젣젤젥젦젧젨젩젪젫젬젭젮젯젰젱젲젳젴젵젶젷져젹젺젻젼젽젾젿
+졀졁졂졃졄졅졆졇졈졉졊졋졌졍졎졏졐졑졒졓졔졕졖졗졘졙졚졛졜졝졞졟
+졠졡졢졣졤졥졦졧졨졩졪졫졬졭졮졯조족졲졳존졵졶졷졸졹졺졻졼졽졾졿
+좀좁좂좃좄종좆좇좈좉좊좋좌좍좎좏좐좑좒좓좔좕좖좗좘좙좚좛좜좝좞좟
+좠좡좢좣좤좥좦좧좨좩좪좫좬좭좮좯좰좱좲좳좴좵좶좷좸좹좺좻좼좽좾좿
+죀죁죂죃죄죅죆죇죈죉죊죋죌죍죎죏죐죑죒죓죔죕죖죗죘죙죚죛죜죝죞죟
+죠죡죢죣죤죥죦죧죨죩죪죫죬죭죮죯죰죱죲죳죴죵죶죷죸죹죺죻주죽죾죿
+준줁줂줃줄줅줆줇줈줉줊줋줌줍줎줏줐중줒줓줔줕줖줗줘줙줚줛줜줝줞줟
+줠줡줢줣줤줥줦줧줨줩줪줫줬줭줮줯줰줱줲줳줴줵줶줷줸줹줺줻줼줽줾줿
+쥀쥁쥂쥃쥄쥅쥆쥇쥈쥉쥊쥋쥌쥍쥎쥏쥐쥑쥒쥓쥔쥕쥖쥗쥘쥙쥚쥛쥜쥝쥞쥟
+쥠쥡쥢쥣쥤쥥쥦쥧쥨쥩쥪쥫쥬쥭쥮쥯쥰쥱쥲쥳쥴쥵쥶쥷쥸쥹쥺쥻쥼쥽쥾쥿
+즀즁즂즃즄즅즆즇즈즉즊즋즌즍즎즏즐즑즒즓즔즕즖즗즘즙즚즛즜증즞즟
+즠즡즢즣즤즥즦즧즨즩즪즫즬즭즮즯즰즱즲즳즴즵즶즷즸즹즺즻즼즽즾즿
+지직짂짃진짅짆짇질짉짊짋짌짍짎짏짐집짒짓짔징짖짗짘짙짚짛짜짝짞짟
+짠짡짢짣짤짥짦짧짨짩짪짫짬짭짮짯짰짱짲짳짴짵짶짷째짹짺짻짼짽짾짿
+쨀쨁쨂쨃쨄쨅쨆쨇쨈쨉쨊쨋쨌쨍쨎쨏쨐쨑쨒쨓쨔쨕쨖쨗쨘쨙쨚쨛쨜쨝쨞쨟
+쨠쨡쨢쨣쨤쨥쨦쨧쨨쨩쨪쨫쨬쨭쨮쨯쨰쨱쨲쨳쨴쨵쨶쨷쨸쨹쨺쨻쨼쨽쨾쨿
+쩀쩁쩂쩃쩄쩅쩆쩇쩈쩉쩊쩋쩌쩍쩎쩏쩐쩑쩒쩓쩔쩕쩖쩗쩘쩙쩚쩛쩜쩝쩞쩟
+쩠쩡쩢쩣쩤쩥쩦쩧쩨쩩쩪쩫쩬쩭쩮쩯쩰쩱쩲쩳쩴쩵쩶쩷쩸쩹쩺쩻쩼쩽쩾쩿
+쪀쪁쪂쪃쪄쪅쪆쪇쪈쪉쪊쪋쪌쪍쪎쪏쪐쪑쪒쪓쪔쪕쪖쪗쪘쪙쪚쪛쪜쪝쪞쪟
+쪠쪡쪢쪣쪤쪥쪦쪧쪨쪩쪪쪫쪬쪭쪮쪯쪰쪱쪲쪳쪴쪵쪶쪷쪸쪹쪺쪻쪼쪽쪾쪿
+쫀쫁쫂쫃쫄쫅쫆쫇쫈쫉쫊쫋쫌쫍쫎쫏쫐쫑쫒쫓쫔쫕쫖쫗쫘쫙쫚쫛쫜쫝쫞쫟
+쫠쫡쫢쫣쫤쫥쫦쫧쫨쫩쫪쫫쫬쫭쫮쫯쫰쫱쫲쫳쫴쫵쫶쫷쫸쫹쫺쫻쫼쫽쫾쫿
+쬀쬁쬂쬃쬄쬅쬆쬇쬈쬉쬊쬋쬌쬍쬎쬏쬐쬑쬒쬓쬔쬕쬖쬗쬘쬙쬚쬛쬜쬝쬞쬟
+쬠쬡쬢쬣쬤쬥쬦쬧쬨쬩쬪쬫쬬쬭쬮쬯쬰쬱쬲쬳쬴쬵쬶쬷쬸쬹쬺쬻쬼쬽쬾쬿
+쭀쭁쭂쭃쭄쭅쭆쭇쭈쭉쭊쭋쭌쭍쭎쭏쭐쭑쭒쭓쭔쭕쭖쭗쭘쭙쭚쭛쭜쭝쭞쭟
+쭠쭡쭢쭣쭤쭥쭦쭧쭨쭩쭪쭫쭬쭭쭮쭯쭰쭱쭲쭳쭴쭵쭶쭷쭸쭹쭺쭻쭼쭽쭾쭿
+쮀쮁쮂쮃쮄쮅쮆쮇쮈쮉쮊쮋쮌쮍쮎쮏쮐쮑쮒쮓쮔쮕쮖쮗쮘쮙쮚쮛쮜쮝쮞쮟
+쮠쮡쮢쮣쮤쮥쮦쮧쮨쮩쮪쮫쮬쮭쮮쮯쮰쮱쮲쮳쮴쮵쮶쮷쮸쮹쮺쮻쮼쮽쮾쮿
+쯀쯁쯂쯃쯄쯅쯆쯇쯈쯉쯊쯋쯌쯍쯎쯏쯐쯑쯒쯓쯔쯕쯖쯗쯘쯙쯚쯛쯜쯝쯞쯟
+쯠쯡쯢쯣쯤쯥쯦쯧쯨쯩쯪쯫쯬쯭쯮쯯쯰쯱쯲쯳쯴쯵쯶쯷쯸쯹쯺쯻쯼쯽쯾쯿
+찀찁찂찃찄찅찆찇찈찉찊찋찌찍찎찏찐찑찒찓찔찕찖찗찘찙찚찛찜찝찞찟
+찠찡찢찣찤찥찦찧차착찪찫찬찭찮찯찰찱찲찳찴찵찶찷참찹찺찻찼창찾찿
+챀챁챂챃채책챆챇챈챉챊챋챌챍챎챏챐챑챒챓챔챕챖챗챘챙챚챛챜챝챞챟
+챠챡챢챣챤챥챦챧챨챩챪챫챬챭챮챯챰챱챲챳챴챵챶챷챸챹챺챻챼챽챾챿
+첀첁첂첃첄첅첆첇첈첉첊첋첌첍첎첏첐첑첒첓첔첕첖첗처척첚첛천첝첞첟
+철첡첢첣첤첥첦첧첨첩첪첫첬청첮첯첰첱첲첳체첵첶첷첸첹첺첻첼첽첾첿
+쳀쳁쳂쳃쳄쳅쳆쳇쳈쳉쳊쳋쳌쳍쳎쳏쳐쳑쳒쳓쳔쳕쳖쳗쳘쳙쳚쳛쳜쳝쳞쳟
+쳠쳡쳢쳣쳤쳥쳦쳧쳨쳩쳪쳫쳬쳭쳮쳯쳰쳱쳲쳳쳴쳵쳶쳷쳸쳹쳺쳻쳼쳽쳾쳿
+촀촁촂촃촄촅촆촇초촉촊촋촌촍촎촏촐촑촒촓촔촕촖촗촘촙촚촛촜총촞촟
+촠촡촢촣촤촥촦촧촨촩촪촫촬촭촮촯촰촱촲촳촴촵촶촷촸촹촺촻촼촽촾촿
+쵀쵁쵂쵃쵄쵅쵆쵇쵈쵉쵊쵋쵌쵍쵎쵏쵐쵑쵒쵓쵔쵕쵖쵗쵘쵙쵚쵛최쵝쵞쵟
+쵠쵡쵢쵣쵤쵥쵦쵧쵨쵩쵪쵫쵬쵭쵮쵯쵰쵱쵲쵳쵴쵵쵶쵷쵸쵹쵺쵻쵼쵽쵾쵿
+춀춁춂춃춄춅춆춇춈춉춊춋춌춍춎춏춐춑춒춓추축춖춗춘춙춚춛출춝춞춟
+춠춡춢춣춤춥춦춧춨충춪춫춬춭춮춯춰춱춲춳춴춵춶춷춸춹춺춻춼춽춾춿
+췀췁췂췃췄췅췆췇췈췉췊췋췌췍췎췏췐췑췒췓췔췕췖췗췘췙췚췛췜췝췞췟
+췠췡췢췣췤췥췦췧취췩췪췫췬췭췮췯췰췱췲췳췴췵췶췷췸췹췺췻췼췽췾췿
+츀츁츂츃츄츅츆츇츈츉츊츋츌츍츎츏츐츑츒츓츔츕츖츗츘츙츚츛츜츝츞츟
+츠측츢츣츤츥츦츧츨츩츪츫츬츭츮츯츰츱츲츳츴층츶츷츸츹츺츻츼츽츾츿
+칀칁칂칃칄칅칆칇칈칉칊칋칌칍칎칏칐칑칒칓칔칕칖칗치칙칚칛친칝칞칟
+칠칡칢칣칤칥칦칧침칩칪칫칬칭칮칯칰칱칲칳카칵칶칷칸칹칺칻칼칽칾칿
+캀캁캂캃캄캅캆캇캈캉캊캋캌캍캎캏캐캑캒캓캔캕캖캗캘캙캚캛캜캝캞캟
+캠캡캢캣캤캥캦캧캨캩캪캫캬캭캮캯캰캱캲캳캴캵캶캷캸캹캺캻캼캽캾캿
+컀컁컂컃컄컅컆컇컈컉컊컋컌컍컎컏컐컑컒컓컔컕컖컗컘컙컚컛컜컝컞컟
+컠컡컢컣커컥컦컧컨컩컪컫컬컭컮컯컰컱컲컳컴컵컶컷컸컹컺컻컼컽컾컿
+케켁켂켃켄켅켆켇켈켉켊켋켌켍켎켏켐켑켒켓켔켕켖켗켘켙켚켛켜켝켞켟
+켠켡켢켣켤켥켦켧켨켩켪켫켬켭켮켯켰켱켲켳켴켵켶켷켸켹켺켻켼켽켾켿
+콀콁콂콃콄콅콆콇콈콉콊콋콌콍콎콏콐콑콒콓코콕콖콗콘콙콚콛콜콝콞콟
+콠콡콢콣콤콥콦콧콨콩콪콫콬콭콮콯콰콱콲콳콴콵콶콷콸콹콺콻콼콽콾콿
+쾀쾁쾂쾃쾄쾅쾆쾇쾈쾉쾊쾋쾌쾍쾎쾏쾐쾑쾒쾓쾔쾕쾖쾗쾘쾙쾚쾛쾜쾝쾞쾟
+쾠쾡쾢쾣쾤쾥쾦쾧쾨쾩쾪쾫쾬쾭쾮쾯쾰쾱쾲쾳쾴쾵쾶쾷쾸쾹쾺쾻쾼쾽쾾쾿
+쿀쿁쿂쿃쿄쿅쿆쿇쿈쿉쿊쿋쿌쿍쿎쿏쿐쿑쿒쿓쿔쿕쿖쿗쿘쿙쿚쿛쿜쿝쿞쿟
+쿠쿡쿢쿣쿤쿥쿦쿧쿨쿩쿪쿫쿬쿭쿮쿯쿰쿱쿲쿳쿴쿵쿶쿷쿸쿹쿺쿻쿼쿽쿾쿿
+퀀퀁퀂퀃퀄퀅퀆퀇퀈퀉퀊퀋퀌퀍퀎퀏퀐퀑퀒퀓퀔퀕퀖퀗퀘퀙퀚퀛퀜퀝퀞퀟
+퀠퀡퀢퀣퀤퀥퀦퀧퀨퀩퀪퀫퀬퀭퀮퀯퀰퀱퀲퀳퀴퀵퀶퀷퀸퀹퀺퀻퀼퀽퀾퀿
+큀큁큂큃큄큅큆큇큈큉큊큋큌큍큎큏큐큑큒큓큔큕큖큗큘큙큚큛큜큝큞큟
+큠큡큢큣큤큥큦큧큨큩큪큫크큭큮큯큰큱큲큳클큵큶큷큸큹큺큻큼큽큾큿
+킀킁킂킃킄킅킆킇킈킉킊킋킌킍킎킏킐킑킒킓킔킕킖킗킘킙킚킛킜킝킞킟
+킠킡킢킣키킥킦킧킨킩킪킫킬킭킮킯킰킱킲킳킴킵킶킷킸킹킺킻킼킽킾킿
+타탁탂탃탄탅탆탇탈탉탊탋탌탍탎탏탐탑탒탓탔탕탖탗탘탙탚탛태택탞탟
+탠탡탢탣탤탥탦탧탨탩탪탫탬탭탮탯탰탱탲탳탴탵탶탷탸탹탺탻탼탽탾탿
+턀턁턂턃턄턅턆턇턈턉턊턋턌턍턎턏턐턑턒턓턔턕턖턗턘턙턚턛턜턝턞턟
+턠턡턢턣턤턥턦턧턨턩턪턫턬턭턮턯터턱턲턳턴턵턶턷털턹턺턻턼턽턾턿
+텀텁텂텃텄텅텆텇텈텉텊텋테텍텎텏텐텑텒텓텔텕텖텗텘텙텚텛템텝텞텟
+텠텡텢텣텤텥텦텧텨텩텪텫텬텭텮텯텰텱텲텳텴텵텶텷텸텹텺텻텼텽텾텿
+톀톁톂톃톄톅톆톇톈톉톊톋톌톍톎톏톐톑톒톓톔톕톖톗톘톙톚톛톜톝톞톟
+토톡톢톣톤톥톦톧톨톩톪톫톬톭톮톯톰톱톲톳톴통톶톷톸톹톺톻톼톽톾톿
+퇀퇁퇂퇃퇄퇅퇆퇇퇈퇉퇊퇋퇌퇍퇎퇏퇐퇑퇒퇓퇔퇕퇖퇗퇘퇙퇚퇛퇜퇝퇞퇟
+퇠퇡퇢퇣퇤퇥퇦퇧퇨퇩퇪퇫퇬퇭퇮퇯퇰퇱퇲퇳퇴퇵퇶퇷퇸퇹퇺퇻퇼퇽퇾퇿
+툀툁툂툃툄툅툆툇툈툉툊툋툌툍툎툏툐툑툒툓툔툕툖툗툘툙툚툛툜툝툞툟
+툠툡툢툣툤툥툦툧툨툩툪툫투툭툮툯툰툱툲툳툴툵툶툷툸툹툺툻툼툽툾툿
+퉀퉁퉂퉃퉄퉅퉆퉇퉈퉉퉊퉋퉌퉍퉎퉏퉐퉑퉒퉓퉔퉕퉖퉗퉘퉙퉚퉛퉜퉝퉞퉟
+퉠퉡퉢퉣퉤퉥퉦퉧퉨퉩퉪퉫퉬퉭퉮퉯퉰퉱퉲퉳퉴퉵퉶퉷퉸퉹퉺퉻퉼퉽퉾퉿
+튀튁튂튃튄튅튆튇튈튉튊튋튌튍튎튏튐튑튒튓튔튕튖튗튘튙튚튛튜튝튞튟
+튠튡튢튣튤튥튦튧튨튩튪튫튬튭튮튯튰튱튲튳튴튵튶튷트특튺튻튼튽튾튿
+틀틁틂틃틄틅틆틇틈틉틊틋틌틍틎틏틐틑틒틓틔틕틖틗틘틙틚틛틜틝틞틟
+틠틡틢틣틤틥틦틧틨틩틪틫틬틭틮틯티틱틲틳틴틵틶틷틸틹틺틻틼틽틾틿
+팀팁팂팃팄팅팆팇팈팉팊팋파팍팎팏판팑팒팓팔팕팖팗팘팙팚팛팜팝팞팟
+팠팡팢팣팤팥팦팧패팩팪팫팬팭팮팯팰팱팲팳팴팵팶팷팸팹팺팻팼팽팾팿
+퍀퍁퍂퍃퍄퍅퍆퍇퍈퍉퍊퍋퍌퍍퍎퍏퍐퍑퍒퍓퍔퍕퍖퍗퍘퍙퍚퍛퍜퍝퍞퍟
+퍠퍡퍢퍣퍤퍥퍦퍧퍨퍩퍪퍫퍬퍭퍮퍯퍰퍱퍲퍳퍴퍵퍶퍷퍸퍹퍺퍻퍼퍽퍾퍿
+펀펁펂펃펄펅펆펇펈펉펊펋펌펍펎펏펐펑펒펓펔펕펖펗페펙펚펛펜펝펞펟
+펠펡펢펣펤펥펦펧펨펩펪펫펬펭펮펯펰펱펲펳펴펵펶펷편펹펺펻펼펽펾펿
+폀폁폂폃폄폅폆폇폈평폊폋폌폍폎폏폐폑폒폓폔폕폖폗폘폙폚폛폜폝폞폟
+폠폡폢폣폤폥폦폧폨폩폪폫포폭폮폯폰폱폲폳폴폵폶폷폸폹폺폻폼폽폾폿
+퐀퐁퐂퐃퐄퐅퐆퐇퐈퐉퐊퐋퐌퐍퐎퐏퐐퐑퐒퐓퐔퐕퐖퐗퐘퐙퐚퐛퐜퐝퐞퐟
+퐠퐡퐢퐣퐤퐥퐦퐧퐨퐩퐪퐫퐬퐭퐮퐯퐰퐱퐲퐳퐴퐵퐶퐷퐸퐹퐺퐻퐼퐽퐾퐿
+푀푁푂푃푄푅푆푇푈푉푊푋푌푍푎푏푐푑푒푓푔푕푖푗푘푙푚푛표푝푞푟
+푠푡푢푣푤푥푦푧푨푩푪푫푬푭푮푯푰푱푲푳푴푵푶푷푸푹푺푻푼푽푾푿
+풀풁풂풃풄풅풆풇품풉풊풋풌풍풎풏풐풑풒풓풔풕풖풗풘풙풚풛풜풝풞풟
+풠풡풢풣풤풥풦풧풨풩풪풫풬풭풮풯풰풱풲풳풴풵풶풷풸풹풺풻풼풽풾풿
+퓀퓁퓂퓃퓄퓅퓆퓇퓈퓉퓊퓋퓌퓍퓎퓏퓐퓑퓒퓓퓔퓕퓖퓗퓘퓙퓚퓛퓜퓝퓞퓟
+퓠퓡퓢퓣퓤퓥퓦퓧퓨퓩퓪퓫퓬퓭퓮퓯퓰퓱퓲퓳퓴퓵퓶퓷퓸퓹퓺퓻퓼퓽퓾퓿
+픀픁픂픃프픅픆픇픈픉픊픋플픍픎픏픐픑픒픓픔픕픖픗픘픙픚픛픜픝픞픟
+픠픡픢픣픤픥픦픧픨픩픪픫픬픭픮픯픰픱픲픳픴픵픶픷픸픹픺픻피픽픾픿
+핀핁핂핃필핅핆핇핈핉핊핋핌핍핎핏핐핑핒핓핔핕핖핗하학핚핛한핝핞핟
+할핡핢핣핤핥핦핧함합핪핫핬항핮핯핰핱핲핳해핵핶핷핸핹핺핻핼핽핾핿
+햀햁햂햃햄햅햆햇했행햊햋햌햍햎햏햐햑햒햓햔햕햖햗햘햙햚햛햜햝햞햟
+햠햡햢햣햤향햦햧햨햩햪햫햬햭햮햯햰햱햲햳햴햵햶햷햸햹햺햻햼햽햾햿
+헀헁헂헃헄헅헆헇허헉헊헋헌헍헎헏헐헑헒헓헔헕헖헗험헙헚헛헜헝헞헟
+헠헡헢헣헤헥헦헧헨헩헪헫헬헭헮헯헰헱헲헳헴헵헶헷헸헹헺헻헼헽헾헿
+혀혁혂혃현혅혆혇혈혉혊혋혌혍혎혏혐협혒혓혔형혖혗혘혙혚혛혜혝혞혟
+혠혡혢혣혤혥혦혧혨혩혪혫혬혭혮혯혰혱혲혳혴혵혶혷호혹혺혻혼혽혾혿
+홀홁홂홃홄홅홆홇홈홉홊홋홌홍홎홏홐홑홒홓화확홖홗환홙홚홛활홝홞홟
+홠홡홢홣홤홥홦홧홨황홪홫홬홭홮홯홰홱홲홳홴홵홶홷홸홹홺홻홼홽홾홿
+횀횁횂횃횄횅횆횇횈횉횊횋회획횎횏횐횑횒횓횔횕횖횗횘횙횚횛횜횝횞횟
+횠횡횢횣횤횥횦횧효횩횪횫횬횭횮횯횰횱횲횳횴횵횶횷횸횹횺횻횼횽횾횿
+훀훁훂훃후훅훆훇훈훉훊훋훌훍훎훏훐훑훒훓훔훕훖훗훘훙훚훛훜훝훞훟
+훠훡훢훣훤훥훦훧훨훩훪훫훬훭훮훯훰훱훲훳훴훵훶훷훸훹훺훻훼훽훾훿
+휀휁휂휃휄휅휆휇휈휉휊휋휌휍휎휏휐휑휒휓휔휕휖휗휘휙휚휛휜휝휞휟
+휠휡휢휣휤휥휦휧휨휩휪휫휬휭휮휯휰휱휲휳휴휵휶휷휸휹휺휻휼휽휾휿
+흀흁흂흃흄흅흆흇흈흉흊흋흌흍흎흏흐흑흒흓흔흕흖흗흘흙흚흛흜흝흞흟
+흠흡흢흣흤흥흦흧흨흩흪흫희흭흮흯흰흱흲흳흴흵흶흷흸흹흺흻흼흽흾흿
+힀힁힂힃힄힅힆힇히힉힊힋힌힍힎힏힐힑힒힓힔힕힖힗힘힙힚힛힜힝힞힟
+힠힡힢힣힤힥힦힧힨힩힪힫힬힭힮힯
+
+Free block (U+D7B0-U+D7FF):
+
+ힰힱힲힳힴힵힶힷힸힹힺힻힼힽힾힿퟀퟁퟂퟃퟄퟅퟆ퟇퟈퟉퟊ퟋퟌퟍퟎퟏퟐퟑퟒퟓퟔퟕퟖퟗퟘퟙퟚퟛퟜퟝퟞퟟퟠퟡퟢퟣퟤퟥퟦퟧퟨퟩퟪퟫퟬퟭퟮퟯ
+ퟰퟱퟲퟳퟴퟵퟶퟷퟸퟹퟺퟻ퟼퟽퟾퟿
+
+High Surrogates (U+D800-U+DB7F):
+
+í €í í ‚í ƒí „í …í †í ‡í ˆí ‰í Ší ‹í Œí í Ží í í ‘í ’í “í ”í •í –í —í ˜í ™í ší ›í œí í ží Ÿí  í ¡í ¢í £í ¤í ¥í ¦í §í ¨í ©í ªí «í ¬í ­í ®í ¯í °í ±í ²í ³í ´í µí ¶í ·í ¸í ¹í ºí »í ¼í ½í ¾í ¿
+í¡€í¡í¡‚í¡ƒí¡„í¡…í¡†í¡‡í¡ˆí¡‰í¡Ší¡‹í¡Œí¡í¡Ží¡í¡í¡‘í¡’í¡“í¡”í¡•í¡–í¡—í¡˜í¡™í¡ší¡›í¡œí¡í¡ží¡Ÿí¡ í¡¡í¡¢í¡£í¡¤í¡¥í¡¦í¡§í¡¨í¡©í¡ªí¡«í¡¬í¡­í¡®í¡¯í¡°í¡±í¡²í¡³í¡´í¡µí¡¶í¡·í¡¸í¡¹í¡ºí¡»í¡¼í¡½í¡¾í¡¿
+í¢€í¢í¢‚í¢ƒí¢„í¢…í¢†í¢‡í¢ˆí¢‰í¢Ší¢‹í¢Œí¢í¢Ží¢í¢í¢‘í¢’í¢“í¢”í¢•í¢–í¢—í¢˜í¢™í¢ší¢›í¢œí¢í¢ží¢Ÿí¢ í¢¡í¢¢í¢£í¢¤í¢¥í¢¦í¢§í¢¨í¢©í¢ªí¢«í¢¬í¢­í¢®í¢¯í¢°í¢±í¢²í¢³í¢´í¢µí¢¶í¢·í¢¸í¢¹í¢ºí¢»í¢¼í¢½í¢¾í¢¿
+í£€í£í£‚í£ƒí£„í£…í£†í£‡í£ˆí£‰í£Ší£‹í£Œí£í£Ží£í£í£‘í£’í£“í£”í£•í£–í£—í£˜í£™í£ší£›í£œí£í£ží£Ÿí£ í£¡í£¢í££í£¤í£¥í£¦í£§í£¨í£©í£ªí£«í£¬í£­í£®í£¯í£°í£±í£²í£³í£´í£µí£¶í£·í£¸í£¹í£ºí£»í£¼í£½í£¾í£¿
+í¤€í¤í¤‚í¤ƒí¤„í¤…í¤†í¤‡í¤ˆí¤‰í¤Ší¤‹í¤Œí¤í¤Ží¤í¤í¤‘í¤’í¤“í¤”í¤•í¤–í¤—í¤˜í¤™í¤ší¤›í¤œí¤í¤ží¤Ÿí¤ í¤¡í¤¢í¤£í¤¤í¤¥í¤¦í¤§í¤¨í¤©í¤ªí¤«í¤¬í¤­í¤®í¤¯í¤°í¤±í¤²í¤³í¤´í¤µí¤¶í¤·í¤¸í¤¹í¤ºí¤»í¤¼í¤½í¤¾í¤¿
+í¥€í¥í¥‚í¥ƒí¥„í¥…í¥†í¥‡í¥ˆí¥‰í¥Ší¥‹í¥Œí¥í¥Ží¥í¥í¥‘í¥’í¥“í¥”í¥•í¥–í¥—í¥˜í¥™í¥ší¥›í¥œí¥í¥ží¥Ÿí¥ í¥¡í¥¢í¥£í¥¤í¥¥í¥¦í¥§í¥¨í¥©í¥ªí¥«í¥¬í¥­í¥®í¥¯í¥°í¥±í¥²í¥³í¥´í¥µí¥¶í¥·í¥¸í¥¹í¥ºí¥»í¥¼í¥½í¥¾í¥¿
+í¦€í¦í¦‚í¦ƒí¦„í¦…í¦†í¦‡í¦ˆí¦‰í¦Ší¦‹í¦Œí¦í¦Ží¦í¦í¦‘í¦’í¦“í¦”í¦•í¦–í¦—í¦˜í¦™í¦ší¦›í¦œí¦í¦ží¦Ÿí¦ í¦¡í¦¢í¦£í¦¤í¦¥í¦¦í¦§í¦¨í¦©í¦ªí¦«í¦¬í¦­í¦®í¦¯í¦°í¦±í¦²í¦³í¦´í¦µí¦¶í¦·í¦¸í¦¹í¦ºí¦»í¦¼í¦½í¦¾í¦¿
+í§€í§í§‚í§ƒí§„í§…í§†í§‡í§ˆí§‰í§Ší§‹í§Œí§í§Ží§í§í§‘í§’í§“í§”í§•í§–í§—í§˜í§™í§ší§›í§œí§í§ží§Ÿí§ í§¡í§¢í§£í§¤í§¥í§¦í§§í§¨í§©í§ªí§«í§¬í§­í§®í§¯í§°í§±í§²í§³í§´í§µí§¶í§·í§¸í§¹í§ºí§»í§¼í§½í§¾í§¿
+í¨€í¨í¨‚í¨ƒí¨„í¨…í¨†í¨‡í¨ˆí¨‰í¨Ší¨‹í¨Œí¨í¨Ží¨í¨í¨‘í¨’í¨“í¨”í¨•í¨–í¨—í¨˜í¨™í¨ší¨›í¨œí¨í¨ží¨Ÿí¨ í¨¡í¨¢í¨£í¨¤í¨¥í¨¦í¨§í¨¨í¨©í¨ªí¨«í¨¬í¨­í¨®í¨¯í¨°í¨±í¨²í¨³í¨´í¨µí¨¶í¨·í¨¸í¨¹í¨ºí¨»í¨¼í¨½í¨¾í¨¿
+í©€í©í©‚í©ƒí©„í©…í©†í©‡í©ˆí©‰í©Ší©‹í©Œí©í©Ží©í©í©‘í©’í©“í©”í©•í©–í©—í©˜í©™í©ší©›í©œí©í©ží©Ÿí© í©¡í©¢í©£í©¤í©¥í©¦í©§í©¨í©©í©ªí©«í©¬í©­í©®í©¯í©°í©±í©²í©³í©´í©µí©¶í©·í©¸í©¹í©ºí©»í©¼í©½í©¾í©¿
+íª€íªíª‚íªƒíª„íª…íª†íª‡íªˆíª‰íªŠíª‹íªŒíªíªŽíªíªíª‘íª’íª“íª”íª•íª–íª—íª˜íª™íªšíª›íªœíªíªžíªŸíª íª¡íª¢íª£íª¤íª¥íª¦íª§íª¨íª©íªªíª«íª¬íª­íª®íª¯íª°íª±íª²íª³íª´íªµíª¶íª·íª¸íª¹íªºíª»íª¼íª½íª¾íª¿
+í«€í«í«‚í«ƒí«„í«…í«†í«‡í«ˆí«‰í«Ší«‹í«Œí«í«Ží«í«í«‘í«’í«“í«”í«•í«–í«—í«˜í«™í«ší«›í«œí«í«ží«Ÿí« í«¡í«¢í«£í«¤í«¥í«¦í«§í«¨í«©í«ªí««í«¬í«­í«®í«¯í«°í«±í«²í«³í«´í«µí«¶í«·í«¸í«¹í«ºí«»í«¼í«½í«¾í«¿
+í¬€í¬í¬‚í¬ƒí¬„í¬…í¬†í¬‡í¬ˆí¬‰í¬Ší¬‹í¬Œí¬í¬Ží¬í¬í¬‘í¬’í¬“í¬”í¬•í¬–í¬—í¬˜í¬™í¬ší¬›í¬œí¬í¬ží¬Ÿí¬ í¬¡í¬¢í¬£í¬¤í¬¥í¬¦í¬§í¬¨í¬©í¬ªí¬«í¬¬í¬­í¬®í¬¯í¬°í¬±í¬²í¬³í¬´í¬µí¬¶í¬·í¬¸í¬¹í¬ºí¬»í¬¼í¬½í¬¾í¬¿
+í­€í­í­‚í­ƒí­„í­…í­†í­‡í­ˆí­‰í­Ší­‹í­Œí­í­Ží­í­í­‘í­’í­“í­”í­•í­–í­—í­˜í­™í­ší­›í­œí­í­ží­Ÿí­ í­¡í­¢í­£í­¤í­¥í­¦í­§í­¨í­©í­ªí­«í­¬í­­í­®í­¯í­°í­±í­²í­³í­´í­µí­¶í­·í­¸í­¹í­ºí­»í­¼í­½í­¾í­¿
+
+High Private Use Surrogates (U+DB80-U+DBFF):
+
+í®€í®í®‚í®ƒí®„í®…í®†í®‡í®ˆí®‰í®Ší®‹í®Œí®í®Ží®í®í®‘í®’í®“í®”í®•í®–í®—í®˜í®™í®ší®›í®œí®í®ží®Ÿí® í®¡í®¢í®£í®¤í®¥í®¦í®§í®¨í®©í®ªí®«í®¬í®­í®®í®¯í®°í®±í®²í®³í®´í®µí®¶í®·í®¸í®¹í®ºí®»í®¼í®½í®¾í®¿
+í¯€í¯í¯‚í¯ƒí¯„í¯…í¯†í¯‡í¯ˆí¯‰í¯Ší¯‹í¯Œí¯í¯Ží¯í¯í¯‘í¯’í¯“í¯”í¯•í¯–í¯—í¯˜í¯™í¯ší¯›í¯œí¯í¯ží¯Ÿí¯ í¯¡í¯¢í¯£í¯¤í¯¥í¯¦í¯§í¯¨í¯©í¯ªí¯«í¯¬í¯­í¯®í¯¯í¯°í¯±í¯²í¯³í¯´í¯µí¯¶í¯·í¯¸í¯¹í¯ºí¯»í¯¼í¯½í¯¾í¯¿
+
+Low Surrogates (U+DC00-U+DFFF):
+
+í°€í°í°‚í°ƒí°„í°…í°†í°‡í°ˆí°‰í°Ší°‹í°Œí°í°Ží°í°í°‘í°’í°“í°”í°•í°–í°—í°˜í°™í°ší°›í°œí°í°ží°Ÿí° í°¡í°¢í°£í°¤í°¥í°¦í°§í°¨í°©í°ªí°«í°¬í°­í°®í°¯í°°í°±í°²í°³í°´í°µí°¶í°·í°¸í°¹í°ºí°»í°¼í°½í°¾í°¿
+í±€í±í±‚í±ƒí±„í±…í±†í±‡í±ˆí±‰í±Ší±‹í±Œí±í±Ží±í±í±‘í±’í±“í±”í±•í±–í±—í±˜í±™í±ší±›í±œí±í±ží±Ÿí± í±¡í±¢í±£í±¤í±¥í±¦í±§í±¨í±©í±ªí±«í±¬í±­í±®í±¯í±°í±±í±²í±³í±´í±µí±¶í±·í±¸í±¹í±ºí±»í±¼í±½í±¾í±¿
+í²€í²í²‚í²ƒí²„í²…í²†í²‡í²ˆí²‰í²Ší²‹í²Œí²í²Ží²í²í²‘í²’í²“í²”í²•í²–í²—í²˜í²™í²ší²›í²œí²í²ží²Ÿí² í²¡í²¢í²£í²¤í²¥í²¦í²§í²¨í²©í²ªí²«í²¬í²­í²®í²¯í²°í²±í²²í²³í²´í²µí²¶í²·í²¸í²¹í²ºí²»í²¼í²½í²¾í²¿
+í³€í³í³‚í³ƒí³„í³…í³†í³‡í³ˆí³‰í³Ší³‹í³Œí³í³Ží³í³í³‘í³’í³“í³”í³•í³–í³—í³˜í³™í³ší³›í³œí³í³ží³Ÿí³ í³¡í³¢í³£í³¤í³¥í³¦í³§í³¨í³©í³ªí³«í³¬í³­í³®í³¯í³°í³±í³²í³³í³´í³µí³¶í³·í³¸í³¹í³ºí³»í³¼í³½í³¾í³¿
+í´€í´í´‚í´ƒí´„í´…í´†í´‡í´ˆí´‰í´Ší´‹í´Œí´í´Ží´í´í´‘í´’í´“í´”í´•í´–í´—í´˜í´™í´ší´›í´œí´í´ží´Ÿí´ í´¡í´¢í´£í´¤í´¥í´¦í´§í´¨í´©í´ªí´«í´¬í´­í´®í´¯í´°í´±í´²í´³í´´í´µí´¶í´·í´¸í´¹í´ºí´»í´¼í´½í´¾í´¿
+íµ€íµíµ‚íµƒíµ„íµ…íµ†íµ‡íµˆíµ‰íµŠíµ‹íµŒíµíµŽíµíµíµ‘íµ’íµ“íµ”íµ•íµ–íµ—íµ˜íµ™íµšíµ›íµœíµíµžíµŸíµ íµ¡íµ¢íµ£íµ¤íµ¥íµ¦íµ§íµ¨íµ©íµªíµ«íµ¬íµ­íµ®íµ¯íµ°íµ±íµ²íµ³íµ´íµµíµ¶íµ·íµ¸íµ¹íµºíµ»íµ¼íµ½íµ¾íµ¿
+í¶€í¶í¶‚í¶ƒí¶„í¶…í¶†í¶‡í¶ˆí¶‰í¶Ší¶‹í¶Œí¶í¶Ží¶í¶í¶‘í¶’í¶“í¶”í¶•í¶–í¶—í¶˜í¶™í¶ší¶›í¶œí¶í¶ží¶Ÿí¶ í¶¡í¶¢í¶£í¶¤í¶¥í¶¦í¶§í¶¨í¶©í¶ªí¶«í¶¬í¶­í¶®í¶¯í¶°í¶±í¶²í¶³í¶´í¶µí¶¶í¶·í¶¸í¶¹í¶ºí¶»í¶¼í¶½í¶¾í¶¿
+í·€í·í·‚í·ƒí·„í·…í·†í·‡í·ˆí·‰í·Ší·‹í·Œí·í·Ží·í·í·‘í·’í·“í·”í·•í·–í·—í·˜í·™í·ší·›í·œí·í·ží·Ÿí· í·¡í·¢í·£í·¤í·¥í·¦í·§í·¨í·©í·ªí·«í·¬í·­í·®í·¯í·°í·±í·²í·³í·´í·µí·¶í··í·¸í·¹í·ºí·»í·¼í·½í·¾í·¿
+í¸€í¸í¸‚í¸ƒí¸„í¸…í¸†í¸‡í¸ˆí¸‰í¸Ší¸‹í¸Œí¸í¸Ží¸í¸í¸‘í¸’í¸“í¸”í¸•í¸–í¸—í¸˜í¸™í¸ší¸›í¸œí¸í¸ží¸Ÿí¸ í¸¡í¸¢í¸£í¸¤í¸¥í¸¦í¸§í¸¨í¸©í¸ªí¸«í¸¬í¸­í¸®í¸¯í¸°í¸±í¸²í¸³í¸´í¸µí¸¶í¸·í¸¸í¸¹í¸ºí¸»í¸¼í¸½í¸¾í¸¿
+í¹€í¹í¹‚í¹ƒí¹„í¹…í¹†í¹‡í¹ˆí¹‰í¹Ší¹‹í¹Œí¹í¹Ží¹í¹í¹‘í¹’í¹“í¹”í¹•í¹–í¹—í¹˜í¹™í¹ší¹›í¹œí¹í¹ží¹Ÿí¹ í¹¡í¹¢í¹£í¹¤í¹¥í¹¦í¹§í¹¨í¹©í¹ªí¹«í¹¬í¹­í¹®í¹¯í¹°í¹±í¹²í¹³í¹´í¹µí¹¶í¹·í¹¸í¹¹í¹ºí¹»í¹¼í¹½í¹¾í¹¿
+íº€íºíº‚íºƒíº„íº…íº†íº‡íºˆíº‰íºŠíº‹íºŒíºíºŽíºíºíº‘íº’íº“íº”íº•íº–íº—íº˜íº™íºšíº›íºœíºíºžíºŸíº íº¡íº¢íº£íº¤íº¥íº¦íº§íº¨íº©íºªíº«íº¬íº­íº®íº¯íº°íº±íº²íº³íº´íºµíº¶íº·íº¸íº¹íººíº»íº¼íº½íº¾íº¿
+í»€í»í»‚í»ƒí»„í»…í»†í»‡í»ˆí»‰í»Ší»‹í»Œí»í»Ží»í»í»‘í»’í»“í»”í»•í»–í»—í»˜í»™í»ší»›í»œí»í»ží»Ÿí» í»¡í»¢í»£í»¤í»¥í»¦í»§í»¨í»©í»ªí»«í»¬í»­í»®í»¯í»°í»±í»²í»³í»´í»µí»¶í»·í»¸í»¹í»ºí»»í»¼í»½í»¾í»¿
+í¼€í¼í¼‚í¼ƒí¼„í¼…í¼†í¼‡í¼ˆí¼‰í¼Ší¼‹í¼Œí¼í¼Ží¼í¼í¼‘í¼’í¼“í¼”í¼•í¼–í¼—í¼˜í¼™í¼ší¼›í¼œí¼í¼ží¼Ÿí¼ í¼¡í¼¢í¼£í¼¤í¼¥í¼¦í¼§í¼¨í¼©í¼ªí¼«í¼¬í¼­í¼®í¼¯í¼°í¼±í¼²í¼³í¼´í¼µí¼¶í¼·í¼¸í¼¹í¼ºí¼»í¼¼í¼½í¼¾í¼¿
+í½€í½í½‚í½ƒí½„í½…í½†í½‡í½ˆí½‰í½Ší½‹í½Œí½í½Ží½í½í½‘í½’í½“í½”í½•í½–í½—í½˜í½™í½ší½›í½œí½í½ží½Ÿí½ í½¡í½¢í½£í½¤í½¥í½¦í½§í½¨í½©í½ªí½«í½¬í½­í½®í½¯í½°í½±í½²í½³í½´í½µí½¶í½·í½¸í½¹í½ºí½»í½¼í½½í½¾í½¿
+í¾€í¾í¾‚í¾ƒí¾„í¾…í¾†í¾‡í¾ˆí¾‰í¾Ší¾‹í¾Œí¾í¾Ží¾í¾í¾‘í¾’í¾“í¾”í¾•í¾–í¾—í¾˜í¾™í¾ší¾›í¾œí¾í¾ží¾Ÿí¾ í¾¡í¾¢í¾£í¾¤í¾¥í¾¦í¾§í¾¨í¾©í¾ªí¾«í¾¬í¾­í¾®í¾¯í¾°í¾±í¾²í¾³í¾´í¾µí¾¶í¾·í¾¸í¾¹í¾ºí¾»í¾¼í¾½í¾¾í¾¿
+í¿€í¿í¿‚í¿ƒí¿„í¿…í¿†í¿‡í¿ˆí¿‰í¿Ší¿‹í¿Œí¿í¿Ží¿í¿í¿‘í¿’í¿“í¿”í¿•í¿–í¿—í¿˜í¿™í¿ší¿›í¿œí¿í¿ží¿Ÿí¿ í¿¡í¿¢í¿£í¿¤í¿¥í¿¦í¿§í¿¨í¿©í¿ªí¿«í¿¬í¿­í¿®í¿¯í¿°í¿±í¿²í¿³í¿´í¿µí¿¶í¿·í¿¸í¿¹í¿ºí¿»í¿¼í¿½í¿¾í¿¿
+
+Private Use Area (U+E000-U+F8FF):
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+CJK Compatibility Ideographs (U+F900-U+FAFF):
+
+豈更車賈滑串句龜龜契金喇奈懶癩羅蘿螺裸邏樂洛烙珞落酪駱亂卵欄爛蘭
+鸞嵐濫藍襤拉臘蠟廊朗浪狼郎來冷勞擄櫓爐盧老蘆虜路露魯鷺碌祿綠菉錄
+鹿論壟弄籠聾牢磊賂雷壘屢樓淚漏累縷陋勒肋凜凌稜綾菱陵讀拏樂諾丹寧
+怒率異北磻便復不泌數索參塞省葉說殺辰沈拾若掠略亮兩凉梁糧良諒量勵
+呂女廬旅濾礪閭驪麗黎力曆歷轢年憐戀撚漣煉璉秊練聯輦蓮連鍊列劣咽烈
+裂說廉念捻殮簾獵令囹寧嶺怜玲瑩羚聆鈴零靈領例禮醴隸惡了僚寮尿料樂
+燎療蓼遼龍暈阮劉杻柳流溜琉留硫紐類六戮陸倫崙淪輪律慄栗率隆利吏履
+易李梨泥理痢罹裏裡里離匿溺吝燐璘藺隣鱗麟林淋臨立笠粒狀炙識什茶刺
+切度拓糖宅洞暴輻行降見廓兀嗀﨎﨏塚﨑晴﨓﨔凞猪益礼神祥福靖精羽﨟
+蘒﨡諸﨣﨤逸都﨧﨨﨩飯飼館鶴郞隷侮僧免勉勤卑喝嘆器塀墨層屮悔慨憎
+懲敏既暑梅海渚漢煮爫琢碑社祉祈祐祖祝禍禎穀突節練縉繁署者臭艹艹著
+褐視謁謹賓贈辶逸難響頻恵𤋮舘﩮﩯並况全侀充冀勇勺喝啕喙嗢塚墳奄奔
+婢嬨廒廙彩徭惘慎愈憎慠懲戴揄搜摒敖晴朗望杖歹殺流滛滋漢瀞煮瞧爵犯
+猪瑱甆画瘝瘟益盛直睊着磌窱節类絛練缾者荒華蝹襁覆視調諸請謁諾諭謹
+變贈輸遲醙鉶陼難靖韛響頋頻鬒龜𢡊𢡄𣏕㮝䀘䀹𥉉𥳐𧻓齃龎﫚﫛﫜﫝﫞﫟
+﫠﫡﫢﫣﫤﫥﫦﫧﫨﫩﫪﫫﫬﫭﫮﫯﫰﫱﫲﫳﫴﫵﫶﫷﫸﫹﫺﫻﫼﫽﫾﫿
+
+Alphabetic Presentation Forms (U+FB00-U+FB4F):
+
+fffiflffifflſtst﬇﬈﬉﬊﬋﬌﬍﬎﬏﬐﬑﬒ﬓﬔﬕﬖﬗ﬘﬙﬚﬛﬜יִ◌ﬞײַﬠﬡﬢﬣﬤﬥﬦﬧﬨ﬩שׁשׂשּׁשּׂאַאָאּבּגּדּהּוּזּ﬷טּיּךּכּלּ﬽מּ﬿
+נּסּ﭂ףּפּ﭅צּקּרּשּתּוֹבֿכֿפֿﭏ
+
+Arabic Presentation Forms-A (U+FB50-U+FDFF):
+
+ﭐﭑﭒﭓﭔﭕﭖﭗﭘﭙﭚﭛﭜﭝﭞﭟﭠﭡﭢﭣﭤﭥﭦﭧﭨﭩﭪﭫﭬﭭﭮﭯﭰﭱﭲﭳﭴﭵﭶﭷﭸﭹﭺﭻﭼﭽﭾﭿﮀﮁﮂﮃﮄﮅﮆﮇﮈﮉﮊﮋﮌﮍﮎﮏ
+ﮐﮑﮒﮓﮔﮕﮖﮗﮘﮙﮚﮛﮜﮝﮞﮟﮠﮡﮢﮣﮤﮥﮦﮧﮨﮩﮪﮫﮬﮭﮮﮯﮰﮱ﮲﮳﮴﮵﮶﮷﮸﮹﮺﮻﮼﮽﮾﮿﯀﯁﯂﯃﯄﯅﯆﯇﯈﯉﯊﯋﯌﯍﯎﯏
+﯐﯑﯒ﯓﯔﯕﯖﯗﯘﯙﯚﯛﯜﯝﯞﯟﯠﯡﯢﯣﯤﯥﯦﯧﯨﯩﯪﯫﯬﯭﯮﯯﯰﯱﯲﯳﯴﯵﯶﯷﯸﯹﯺﯻﯼﯽﯾﯿﰀﰁﰂﰃﰄﰅﰆﰇﰈﰉﰊﰋﰌﰍﰎﰏ
+ﰐﰑﰒﰓﰔﰕﰖﰗﰘﰙﰚﰛﰜﰝﰞﰟﰠﰡﰢﰣﰤﰥﰦﰧﰨﰩﰪﰫﰬﰭﰮﰯﰰﰱﰲﰳﰴﰵﰶﰷﰸﰹﰺﰻﰼﰽﰾﰿﱀﱁﱂﱃﱄﱅﱆﱇﱈﱉﱊﱋﱌﱍﱎﱏ
+ﱐﱑﱒﱓﱔﱕﱖﱗﱘﱙﱚﱛﱜﱝﱞﱟﱠﱡﱢﱣﱤﱥﱦﱧﱨﱩﱪﱫﱬﱭﱮﱯﱰﱱﱲﱳﱴﱵﱶﱷﱸﱹﱺﱻﱼﱽﱾﱿﲀﲁﲂﲃﲄﲅﲆﲇﲈﲉﲊﲋﲌﲍﲎﲏ
+ﲐﲑﲒﲓﲔﲕﲖﲗﲘﲙﲚﲛﲜﲝﲞﲟﲠﲡﲢﲣﲤﲥﲦﲧﲨﲩﲪﲫﲬﲭﲮﲯﲰﲱﲲﲳﲴﲵﲶﲷﲸﲹﲺﲻﲼﲽﲾﲿﳀﳁﳂﳃﳄﳅﳆﳇﳈﳉﳊﳋﳌﳍﳎﳏ
+ﳐﳑﳒﳓﳔﳕﳖﳗﳘﳙﳚﳛﳜﳝﳞﳟﳠﳡﳢﳣﳤﳥﳦﳧﳨﳩﳪﳫﳬﳭﳮﳯﳰﳱﳲﳳﳴﳵﳶﳷﳸﳹﳺﳻﳼﳽﳾﳿﴀﴁﴂﴃﴄﴅﴆﴇﴈﴉﴊﴋﴌﴍﴎﴏ
+ﴐﴑﴒﴓﴔﴕﴖﴗﴘﴙﴚﴛﴜﴝﴞﴟﴠﴡﴢﴣﴤﴥﴦﴧﴨﴩﴪﴫﴬﴭﴮﴯﴰﴱﴲﴳﴴﴵﴶﴷﴸﴹﴺﴻﴼﴽ﴾﴿﵀﵁﵂﵃﵄﵅﵆﵇﵈﵉﵊﵋﵌﵍﵎﵏
+ﵐﵑﵒﵓﵔﵕﵖﵗﵘﵙﵚﵛﵜﵝﵞﵟﵠﵡﵢﵣﵤﵥﵦﵧﵨﵩﵪﵫﵬﵭﵮﵯﵰﵱﵲﵳﵴﵵﵶﵷﵸﵹﵺﵻﵼﵽﵾﵿﶀﶁﶂﶃﶄﶅﶆﶇﶈﶉﶊﶋﶌﶍﶎﶏ
+﶐﶑ﶒﶓﶔﶕﶖﶗﶘﶙﶚﶛﶜﶝﶞﶟﶠﶡﶢﶣﶤﶥﶦﶧﶨﶩﶪﶫﶬﶭﶮﶯﶰﶱﶲﶳﶴﶵﶶﶷﶸﶹﶺﶻﶼﶽﶾﶿﷀﷁﷂﷃﷄﷅﷆﷇ﷈﷉﷊﷋﷌﷍﷎﷏
+﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯ﷰﷱﷲﷳﷴﷵﷶﷷﷸﷹﷺﷻ﷼﷽﷾﷿
+
+Variation Selectors (U+FE00-U+FE0F):
+
+◌︀◌︁◌︂◌︃◌︄◌︅◌︆◌︇◌︈◌︉◌︊◌︋◌︌◌︍◌︎◌️
+
+Free block (U+FE10-U+FE1F):
+
+︐︑︒︓︔︕︖︗︘︙︚︛︜︝︞︟
+
+Combining Half Marks (U+FE20-U+FE2F):
+
+◌︠◌︡◌︢◌︧︨︩︪︫︬︭︣︤︥︦︮︯
+
+CJK Compatibility Forms (U+FE30-U+FE4F):
+
+︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹇﹈﹉﹊﹋﹌﹍﹎﹏
+
+Small Form Variants (U+FE50-U+FE6F):
+
+﹐﹑﹒﹓﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹧﹨﹩﹪﹫﹬﹭﹮﹯
+
+Arabic Presentation Forms-B (U+FE70-U+FEFF):
+
+ﹰﹱﹲﹳﹴ﹵ﹶﹷﹸﹹﹺﹻﹼﹽﹾﹿﺀﺁﺂﺃﺄﺅﺆﺇﺈﺉﺊﺋﺌﺍﺎﺏﺐﺑﺒﺓﺔﺕﺖﺗﺘﺙﺚﺛﺜﺝﺞﺟﺠﺡﺢﺣﺤﺥﺦﺧﺨﺩﺪﺫﺬﺭﺮﺯ
+ﺰﺱﺲﺳﺴﺵﺶﺷﺸﺹﺺﺻﺼﺽﺾﺿﻀﻁﻂﻃﻄﻅﻆﻇﻈﻉﻊﻋﻌﻍﻎﻏﻐﻑﻒﻓﻔﻕﻖﻗﻘﻙﻚﻛﻜﻝﻞﻟﻠﻡﻢﻣﻤﻥﻦﻧﻨﻩﻪﻫﻬﻭﻮﻯ
+ﻰﻱﻲﻳﻴﻵﻶﻷﻸﻹﻺﻻﻼ﻽﻾
+
+Halfwidth and Fullwidth Forms (U+FF00-U+FFEF):
+
+＀!"#$%&'()*+,-./0123456789:;<=>?
+@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
+`abcdefghijklmnopqrstuvwxyz{|}~⦅
+⦆。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙
+゚ᅠᄀᄁᆪᄂᆬᆭᄃᄄᄅᆰᆱᆲᆳᆴᆵᄚᄆᄇᄈᄡᄉᄊᄋᄌᄍᄎᄏᄐᄑᄒ﾿￀￁ᅡᅢᅣᅤᅥᅦ￈￉ᅧᅨᅩᅪᅫᅬ￐￑ᅭᅮᅯᅰᅱᅲ￘￙ᅳᅴᅵ￝￞
+￟¢£¬ ̄¦¥₩￧│←↑→↓■○￯
+
+Specials (U+FFF0-U+FFFF):
+
+￰￱￲￳￴￵￶￷￸�￾￿
diff --git a/busybox-1.19.3/e2fsprogs/Config.src b/busybox-1.19.3/e2fsprogs/Config.src
new file mode 100644
index 0000000..743e1e1
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/Config.src
@@ -0,0 +1,71 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux Ext2 FS Progs"
+
+INSERT
+
+config CHATTR
+	bool "chattr"
+	default y
+	help
+	  chattr changes the file attributes on a second extended file system.
+
+### config E2FSCK
+###	bool "e2fsck"
+###	default y
+###	help
+###	  e2fsck is used to check Linux second extended file systems (ext2fs).
+###	  e2fsck also supports ext2 filesystems countaining a journal (ext3).
+###	  The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
+###	  provided.
+
+config FSCK
+	bool "fsck"
+	default y
+	help
+	  fsck is used to check and optionally repair one or more filesystems.
+	  In actuality, fsck is simply a front-end for the various file system
+	  checkers (fsck.fstype) available under Linux.
+
+config LSATTR
+	bool "lsattr"
+	default y
+	select PLATFORM_LINUX
+	help
+	  lsattr lists the file attributes on a second extended file system.
+
+### config MKE2FS
+###	bool "mke2fs"
+###	default y
+###	help
+###	  mke2fs is used to create an ext2/ext3 filesystem. The normal compat
+###	  symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided.
+
+config TUNE2FS
+	bool "tune2fs"
+	default n  # off: it is too limited compared to upstream version
+	help
+	  tune2fs allows the system administrator to adjust various tunable
+	  filesystem parameters on Linux ext2/ext3 filesystems.
+
+### config E2LABEL
+###	bool "e2label"
+###	default y
+###	depends on TUNE2FS
+###	help
+###	  e2label will display or change the filesystem label on the ext2
+###	  filesystem located on device.
+
+### NB: this one is now provided by util-linux/volume_id/*
+### config FINDFS
+###	bool "findfs"
+###	default y
+###	depends on TUNE2FS
+###	help
+###	  findfs will search the disks in the system looking for a filesystem
+###	  which has a label matching label or a UUID equal to uuid.
+
+endmenu
diff --git a/busybox-1.19.3/e2fsprogs/Kbuild.src b/busybox-1.19.3/e2fsprogs/Kbuild.src
new file mode 100644
index 0000000..b7a14c3
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/Kbuild.src
@@ -0,0 +1,15 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+
+lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
+lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o
+
+lib-$(CONFIG_FSCK)    += fsck.o
+lib-$(CONFIG_TUNE2FS) += tune2fs.o
diff --git a/busybox-1.19.3/e2fsprogs/README b/busybox-1.19.3/e2fsprogs/README
new file mode 100644
index 0000000..eb158e5
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/README
@@ -0,0 +1,12 @@
+Authors and contributors of original e2fsprogs:
+
+Remy Card <card@masi.ibp.fr>
+Theodore Ts'o <tytso@mit.edu>
+Stephen C. Tweedie <sct@redhat.com>
+Andreas Gruenbacher, <a.gruenbacher@computer.org>
+Kaz Kylheku <kaz@ashi.footprints.net>
+F.W. ten Wolde <franky@duteca.et.tudelft.nl>
+Jeremy Fitzhardinge <jeremy@zip.com.au>
+M.J.E. Mol <marcel@duteca.et.tudelft.nl>
+Miquel van Smoorenburg <miquels@drinkel.ow.org>
+Uwe Ohse <uwe@tirka.gun.de>
diff --git a/busybox-1.19.3/e2fsprogs/chattr.c b/busybox-1.19.3/e2fsprogs/chattr.c
new file mode 100644
index 0000000..f1cc838
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/chattr.c
@@ -0,0 +1,195 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chattr.c		- Change file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * 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	- Ignore symlinks when working recursively (G M Sipe)
+ * 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"
+
+#define OPT_ADD 1
+#define OPT_REM 2
+#define OPT_SET 4
+#define OPT_SET_VER 8
+
+struct globals {
+	unsigned long version;
+	unsigned long af;
+	unsigned long rf;
+	smallint flags;
+	smallint recursive;
+};
+
+static unsigned long get_flag(char c)
+{
+	const char *fp = strchr(e2attr_flags_sname_chattr, c);
+	if (fp)
+		return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr];
+	bb_show_usage();
+}
+
+static int decode_arg(const char *arg, struct globals *gp)
+{
+	unsigned long *fl;
+	char opt = *arg++;
+
+	fl = &gp->af;
+	if (opt == '-') {
+		gp->flags |= OPT_REM;
+		fl = &gp->rf;
+	} else if (opt == '+') {
+		gp->flags |= OPT_ADD;
+	} else if (opt == '=') {
+		gp->flags |= OPT_SET;
+	} else
+		return 0;
+
+	while (*arg)
+		*fl |= get_flag(*arg++);
+
+	return 1;
+}
+
+static void change_attributes(const char *name, struct globals *gp);
+
+static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
+{
+	char *path = concat_subpath_file(dir_name, de->d_name);
+	/* path is NULL if de->d_name is "." or "..", else... */
+	if (path) {
+		change_attributes(path, gp);
+		free(path);
+	}
+	return 0;
+}
+
+static void change_attributes(const char *name, struct globals *gp)
+{
+	unsigned long fsflags;
+	struct stat st;
+
+	if (lstat(name, &st) != 0) {
+		bb_perror_msg("stat %s", name);
+		return;
+	}
+	if (S_ISLNK(st.st_mode) && gp->recursive)
+		return;
+
+	/* Don't try to open device files, fifos etc.  We probably
+	 * ought to display an error if the file was explicitly given
+	 * on the command line (whether or not recursive was
+	 * requested).  */
+	if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+		return;
+
+	if (gp->flags & OPT_SET_VER)
+		if (fsetversion(name, gp->version) != 0)
+			bb_perror_msg("setting version on %s", name);
+
+	if (gp->flags & OPT_SET) {
+		fsflags = gp->af;
+	} else {
+		if (fgetflags(name, &fsflags) != 0) {
+			bb_perror_msg("reading flags on %s", name);
+			goto skip_setflags;
+		}
+		/*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
+			fsflags &= ~gp->rf;
+		/*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
+			fsflags |= gp->af;
+		/* What is this? And why it's not done for SET case? */
+		if (!S_ISDIR(st.st_mode))
+			fsflags &= ~EXT2_DIRSYNC_FL;
+	}
+	if (fsetflags(name, fsflags) != 0)
+		bb_perror_msg("setting flags on %s", name);
+
+ skip_setflags:
+	if (gp->recursive && S_ISDIR(st.st_mode))
+		iterate_on_dir(name, chattr_dir_proc, gp);
+}
+
+int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chattr_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct globals g;
+	char *arg;
+
+	memset(&g, 0, sizeof(g));
+
+	/* parse the args */
+	while ((arg = *++argv)) {
+		/* take care of -R and -v <version> */
+		if (arg[0] == '-'
+		 && (arg[1] == 'R' || arg[1] == 'v')
+		 && !arg[2]
+		) {
+			if (arg[1] == 'R') {
+				g.recursive = 1;
+				continue;
+			}
+			/* arg[1] == 'v' */
+			if (!*++argv)
+				bb_show_usage();
+			g.version = xatoul(*argv);
+			g.flags |= OPT_SET_VER;
+			continue;
+		}
+
+		if (!decode_arg(arg, &g))
+			break;
+	}
+
+	/* run sanity checks on all the arguments given us */
+	if (!*argv)
+		bb_show_usage();
+	if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
+		bb_error_msg_and_die("= is incompatible with - and +");
+	if (g.rf & g.af)
+		bb_error_msg_and_die("can't set and unset a flag");
+	if (!g.flags)
+		bb_error_msg_and_die("must use '-v', =, - or +");
+
+	/* now run chattr on all the files passed to us */
+	do change_attributes(*argv, &g); while (*++argv);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/e2fsprogs/e2fs_defs.h b/busybox-1.19.3/e2fsprogs/e2fs_defs.h
new file mode 100644
index 0000000..379640e
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/e2fs_defs.h
@@ -0,0 +1,561 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef LINUX_EXT2_FS_H
+#define LINUX_EXT2_FS_H 1
+
+/*
+ * Special inode numbers
+ */
+#define EXT2_BAD_INO		 1	/* Bad blocks inode */
+#define EXT2_ROOT_INO		 2	/* Root inode */
+#define EXT2_ACL_IDX_INO	 3	/* ACL inode */
+#define EXT2_ACL_DATA_INO	 4	/* ACL inode */
+#define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */
+#define EXT2_RESIZE_INO		 7	/* Reserved group descriptors inode */
+#define EXT2_JOURNAL_INO	 8	/* Journal inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO	11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC	0xEF53
+
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block.  This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb)	(sb)
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX		32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_LOG_SIZE		10	/* 1024 */
+#define EXT2_MAX_BLOCK_LOG_SIZE		16	/* 65536 */
+#define EXT2_MIN_BLOCK_SIZE	(1 << EXT2_MIN_BLOCK_LOG_SIZE)
+#define EXT2_MAX_BLOCK_SIZE	(1 << EXT2_MAX_BLOCK_LOG_SIZE)
+#define EXT2_BLOCK_SIZE(s)	(EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#define EXT2_BLOCK_SIZE_BITS(s)	((s)->s_log_block_size + 10)
+#define EXT2_INODE_SIZE(s)	(((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+				 EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
+#define EXT2_FIRST_INO(s)	(((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+				 EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
+#define EXT2_ADDR_PER_BLOCK(s)	(EXT2_BLOCK_SIZE(s) / sizeof(uint32_t))
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE		EXT2_MIN_BLOCK_SIZE
+#define EXT2_MAX_FRAG_SIZE		EXT2_MAX_BLOCK_SIZE
+#define EXT2_MIN_FRAG_LOG_SIZE		EXT2_MIN_BLOCK_LOG_SIZE
+#define EXT2_FRAG_SIZE(s)		(EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+#define EXT2_FRAGS_PER_BLOCK(s)		(EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header {	/* Header of Access Control Lists */
+	uint32_t	aclh_size;
+	uint32_t	aclh_file_count;
+	uint32_t	aclh_acle_count;
+	uint32_t	aclh_first_acle;
+};
+
+struct ext2_acl_entry {	/* Access Control List Entry */
+	uint32_t	acle_size;
+	uint16_t	acle_perms;	/* Access permissions */
+	uint16_t	acle_type;	/* Type of entry */
+	uint16_t	acle_tag;	/* User or group identity */
+	uint16_t	acle_pad1;
+	uint32_t	acle_next;	/* Pointer on next entry for the */
+					/* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc {
+	uint32_t	bg_block_bitmap;	/* Blocks bitmap block */
+	uint32_t	bg_inode_bitmap;	/* Inodes bitmap block */
+	uint32_t	bg_inode_table;		/* Inodes table block */
+	uint16_t	bg_free_blocks_count;	/* Free blocks count */
+	uint16_t	bg_free_inodes_count;	/* Free inodes count */
+	uint16_t	bg_used_dirs_count;	/* Directories count */
+	uint16_t	bg_pad;
+	uint32_t	bg_reserved[3];
+};
+
+/*
+ * Data structures used by the directory indexing feature
+ *
+ * Note: all of the multibyte integer fields are little endian.
+ */
+
+/*
+ * Note: dx_root_info is laid out so that if it should somehow get
+ * overlaid by a dirent the two low bits of the hash version will be
+ * zero.  Therefore, the hash version mod 4 should never be 0.
+ * Sincerely, the paranoia department.
+ */
+struct ext2_dx_root_info {
+	uint32_t	reserved_zero;
+	uint8_t		hash_version; /* 0 now, 1 at release */
+	uint8_t		info_length; /* 8 */
+	uint8_t		indirect_levels;
+	uint8_t		unused_flags;
+};
+
+#define EXT2_HASH_LEGACY	0
+#define EXT2_HASH_HALF_MD4	1
+#define EXT2_HASH_TEA		2
+
+#define EXT2_HASH_FLAG_INCOMPAT	0x1
+
+struct ext2_dx_entry {
+	uint32_t hash;
+	uint32_t block;
+};
+
+struct ext2_dx_countlimit {
+	uint16_t limit;
+	uint16_t count;
+};
+
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#define EXT2_BLOCKS_PER_GROUP(s)	(EXT2_SB(s)->s_blocks_per_group)
+#define EXT2_INODES_PER_GROUP(s)	(EXT2_SB(s)->s_inodes_per_group)
+#define EXT2_INODES_PER_BLOCK(s)	(EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
+/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
+#define EXT2_MAX_BLOCKS_PER_GROUP(s)	((1 << 16) - 8)
+#define EXT2_MAX_INODES_PER_GROUP(s)	((1 << 16) - EXT2_INODES_PER_BLOCK(s))
+#define EXT2_DESC_PER_BLOCK(s)		(EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS		12
+#define EXT2_IND_BLOCK			EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK			(EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK			(EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS			(EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL			0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL			0x00000002 /* Undelete */
+#define EXT2_COMPR_FL			0x00000004 /* Compress file */
+#define EXT2_SYNC_FL			0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL		0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL			0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL			0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL			0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL			0x00000100
+#define EXT2_COMPRBLK_FL		0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMPR_FL			0x00000400 /* Access raw compressed data */
+#define EXT2_ECOMPR_FL			0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */
+#define EXT2_BTREE_FL			0x00001000 /* btree format dir */
+#define EXT2_INDEX_FL			0x00001000 /* hash-indexed directory */
+#define EXT2_IMAGIC_FL			0x00002000
+#define EXT3_JOURNAL_DATA_FL		0x00004000 /* file data should be journaled */
+#define EXT2_NOTAIL_FL			0x00008000 /* file tail should not be merged */
+#define EXT2_DIRSYNC_FL			0x00010000 /* Synchronous directory modifications */
+#define EXT2_TOPDIR_FL			0x00020000 /* Top of directory hierarchies*/
+#define EXT3_EXTENTS_FL			0x00080000 /* Inode uses extents */
+#define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE		0x0003DFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE		0x000080FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define EXT2_IOC_GETFLAGS		_IOR('f', 1, long)
+#define EXT2_IOC_SETFLAGS		_IOW('f', 2, long)
+#define EXT2_IOC_GETVERSION		_IOR('v', 1, long)
+#define EXT2_IOC_SETVERSION		_IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+	uint16_t	i_mode;		/* File mode */
+	uint16_t	i_uid;		/* Low 16 bits of Owner Uid */
+	uint32_t	i_size;		/* Size in bytes */
+	uint32_t	i_atime;	/* Access time */
+	uint32_t	i_ctime;	/* Creation time */
+	uint32_t	i_mtime;	/* Modification time */
+	uint32_t	i_dtime;	/* Deletion Time */
+	uint16_t	i_gid;		/* Low 16 bits of Group Id */
+	uint16_t	i_links_count;	/* Links count */
+	uint32_t	i_blocks;	/* Blocks count */
+	uint32_t	i_flags;	/* File flags */
+	union {
+		struct {
+			uint32_t  l_i_reserved1;
+		} linux1;
+		struct {
+			uint32_t  h_i_translator;
+		} hurd1;
+		struct {
+			uint32_t  m_i_reserved1;
+		} masix1;
+	} osd1;				/* OS dependent 1 */
+	uint32_t	i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+	uint32_t	i_generation;	/* File version (for NFS) */
+	uint32_t	i_file_acl;	/* File ACL */
+	uint32_t	i_dir_acl;	/* Directory ACL */
+	uint32_t	i_faddr;	/* Fragment address */
+	union {
+		struct {
+			uint8_t		l_i_frag;	/* Fragment number */
+			uint8_t		l_i_fsize;	/* Fragment size */
+			uint16_t	i_pad1;
+			uint16_t	l_i_uid_high;	/* these 2 fields    */
+			uint16_t	l_i_gid_high;	/* were reserved2[0] */
+			uint32_t	l_i_reserved2;
+		} linux2;
+		struct {
+			uint8_t		h_i_frag;	/* Fragment number */
+			uint8_t		h_i_fsize;	/* Fragment size */
+			uint16_t	h_i_mode_high;
+			uint16_t	h_i_uid_high;
+			uint16_t	h_i_gid_high;
+			uint32_t	h_i_author;
+		} hurd2;
+		struct {
+			uint8_t		m_i_frag;	/* Fragment number */
+			uint8_t		m_i_fsize;	/* Fragment size */
+			uint16_t	m_pad1;
+			uint32_t	m_i_reserved2[2];
+		} masix2;
+	} osd2;				/* OS dependent 2 */
+};
+
+/*
+ * Permanent part of an large inode on the disk
+ */
+struct ext2_inode_large {
+	uint16_t	i_mode;		/* File mode */
+	uint16_t	i_uid;		/* Low 16 bits of Owner Uid */
+	uint32_t	i_size;		/* Size in bytes */
+	uint32_t	i_atime;	/* Access time */
+	uint32_t	i_ctime;	/* Creation time */
+	uint32_t	i_mtime;	/* Modification time */
+	uint32_t	i_dtime;	/* Deletion Time */
+	uint16_t	i_gid;		/* Low 16 bits of Group Id */
+	uint16_t	i_links_count;	/* Links count */
+	uint32_t	i_blocks;	/* Blocks count */
+	uint32_t	i_flags;	/* File flags */
+	union {
+		struct {
+			uint32_t  l_i_reserved1;
+		} linux1;
+		struct {
+			uint32_t  h_i_translator;
+		} hurd1;
+		struct {
+			uint32_t  m_i_reserved1;
+		} masix1;
+	} osd1;				/* OS dependent 1 */
+	uint32_t	i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+	uint32_t	i_generation;	/* File version (for NFS) */
+	uint32_t	i_file_acl;	/* File ACL */
+	uint32_t	i_dir_acl;	/* Directory ACL */
+	uint32_t	i_faddr;	/* Fragment address */
+	union {
+		struct {
+			uint8_t		l_i_frag;	/* Fragment number */
+			uint8_t		l_i_fsize;	/* Fragment size */
+			uint16_t	i_pad1;
+			uint16_t	l_i_uid_high;	/* these 2 fields    */
+			uint16_t	l_i_gid_high;	/* were reserved2[0] */
+			uint32_t	l_i_reserved2;
+		} linux2;
+		struct {
+			uint8_t		h_i_frag;	/* Fragment number */
+			uint8_t		h_i_fsize;	/* Fragment size */
+			uint16_t	h_i_mode_high;
+			uint16_t	h_i_uid_high;
+			uint16_t	h_i_gid_high;
+			uint32_t	h_i_author;
+		} hurd2;
+		struct {
+			uint8_t		m_i_frag;	/* Fragment number */
+			uint8_t		m_i_fsize;	/* Fragment size */
+			uint16_t	m_pad1;
+			uint32_t	m_i_reserved2[2];
+		} masix2;
+	} osd2;				/* OS dependent 2 */
+	uint16_t	i_extra_isize;
+	uint16_t	i_pad1;
+};
+
+#define i_size_high	i_dir_acl
+
+/*
+ * File system states
+ */
+#define EXT2_VALID_FS			0x0001	/* Unmounted cleanly */
+#define EXT2_ERROR_FS			0x0002	/* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK		0x0001	/* Do mount-time checks */
+#define EXT2_MOUNT_GRPID		0x0004	/* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG		0x0008	/* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT		0x0010	/* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO		0x0020	/* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC		0x0040	/* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF		0x0080	/* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_UID32		0x0200  /* Disable 32-bit UIDs */
+
+#define clear_opt(o, opt)		o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt)			o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt)		(EXT2_SB(sb)->s_mount_opt & \
+					 EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT		20	/* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL		0	/* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE		1	/* Continue execution */
+#define EXT2_ERRORS_RO			2	/* Remount fs read-only */
+#define EXT2_ERRORS_PANIC		3	/* Panic */
+#define EXT2_ERRORS_DEFAULT		EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+	uint32_t	s_inodes_count;		/* Inodes count */
+	uint32_t	s_blocks_count;		/* Blocks count */
+	uint32_t	s_r_blocks_count;	/* Reserved blocks count */
+	uint32_t	s_free_blocks_count;	/* Free blocks count */
+	uint32_t	s_free_inodes_count;	/* Free inodes count */
+	uint32_t	s_first_data_block;	/* First Data Block */
+	uint32_t	s_log_block_size;	/* Block size */
+	int32_t		s_log_frag_size;	/* Fragment size */
+	uint32_t	s_blocks_per_group;	/* # Blocks per group */
+	uint32_t	s_frags_per_group;	/* # Fragments per group */
+	uint32_t	s_inodes_per_group;	/* # Inodes per group */
+	uint32_t	s_mtime;		/* Mount time */
+	uint32_t	s_wtime;		/* Write time */
+	uint16_t	s_mnt_count;		/* Mount count */
+	int16_t		s_max_mnt_count;	/* Maximal mount count */
+	uint16_t	s_magic;		/* Magic signature */
+	uint16_t	s_state;		/* File system state */
+	uint16_t	s_errors;		/* Behaviour when detecting errors */
+	uint16_t	s_minor_rev_level;	/* minor revision level */
+	uint32_t	s_lastcheck;		/* time of last check */
+	uint32_t	s_checkinterval;	/* max. time between checks */
+	uint32_t	s_creator_os;		/* OS */
+	uint32_t	s_rev_level;		/* Revision level */
+	uint16_t	s_def_resuid;		/* Default uid for reserved blocks */
+	uint16_t	s_def_resgid;		/* Default gid for reserved blocks */
+	/*
+	 * These fields are for EXT2_DYNAMIC_REV superblocks only.
+	 *
+	 * Note: the difference between the compatible feature set and
+	 * the incompatible feature set is that if there is a bit set
+	 * in the incompatible feature set that the kernel doesn't
+	 * know about, it should refuse to mount the filesystem.
+	 *
+	 * e2fsck's requirements are more strict; if it doesn't know
+	 * about a feature in either the compatible or incompatible
+	 * feature set, it must abort and not try to meddle with
+	 * things it doesn't understand...
+	 */
+	uint32_t	s_first_ino;		/* First non-reserved inode */
+	uint16_t	s_inode_size;		/* size of inode structure */
+	uint16_t	s_block_group_nr;	/* block group # of this superblock */
+	uint32_t	s_feature_compat;	/* compatible feature set */
+	uint32_t	s_feature_incompat;	/* incompatible feature set */
+	uint32_t	s_feature_ro_compat;	/* readonly-compatible feature set */
+	uint8_t		s_uuid[16];		/* 128-bit uuid for volume */
+	char		s_volume_name[16];	/* volume name */
+	char		s_last_mounted[64];	/* directory where last mounted */
+	uint32_t	s_algorithm_usage_bitmap; /* For compression */
+	/*
+	 * 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 */
+	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 */
+	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;
+	uint32_t	s_first_meta_bg;	/* First metablock group */
+	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 */
+};
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX		0
+#define EXT2_OS_HURD		1
+#define EXT2_OS_MASIX		2
+#define EXT2_OS_FREEBSD		3
+#define EXT2_OS_LITES		4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV	0	/* The good old (original) format */
+#define EXT2_DYNAMIC_REV	1	/* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV	EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV	EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Journal inode backup types
+ */
+#define EXT3_JNL_BACKUP_BLOCKS	1
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask)			\
+	( EXT2_SB(sb)->s_feature_compat & (mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask)			\
+	( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask)			\
+	( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+#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_DIR_INDEX		0x0020
+
+#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_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 EXT2_FEATURE_INCOMPAT_META_BG		0x0010
+#define EXT3_FEATURE_INCOMPAT_EXTENTS		0x0040
+
+
+#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)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define EXT2_DEF_RESUID		0
+#define EXT2_DEF_RESGID		0
+
+/*
+ * Default mount options
+ */
+#define EXT2_DEFM_DEBUG		0x0001
+#define EXT2_DEFM_BSDGROUPS	0x0002
+#define EXT2_DEFM_XATTR_USER	0x0004
+#define EXT2_DEFM_ACL		0x0008
+#define EXT2_DEFM_UID16		0x0010
+#define EXT3_DEFM_JMODE		0x0060
+#define EXT3_DEFM_JMODE_DATA	0x0020
+#define EXT3_DEFM_JMODE_ORDERED	0x0040
+#define EXT3_DEFM_JMODE_WBACK	0x0060
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+	uint32_t	inode;			/* Inode number */
+	uint16_t	rec_len;		/* Directory entry length */
+	uint16_t	name_len;		/* Name length */
+	char		name[EXT2_NAME_LEN];	/* File name */
+};
+
+/*
+ * The new version of the directory entry.  Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+	uint32_t	inode;			/* Inode number */
+	uint16_t	rec_len;		/* Directory entry length */
+	uint8_t		name_len;		/* Name length */
+	uint8_t		file_type;
+	char		name[EXT2_NAME_LEN];	/* File name */
+};
+
+/*
+ * Ext2 directory file types.  Only the low 3 bits are used.  The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN		0
+#define EXT2_FT_REG_FILE	1
+#define EXT2_FT_DIR		2
+#define EXT2_FT_CHRDEV		3
+#define EXT2_FT_BLKDEV		4
+#define EXT2_FT_FIFO		5
+#define EXT2_FT_SOCK		6
+#define EXT2_FT_SYMLINK		7
+
+#define EXT2_FT_MAX		8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD			4
+#define EXT2_DIR_ROUND			(EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len)	(((name_len) + 8 + EXT2_DIR_ROUND) & \
+					 ~EXT2_DIR_ROUND)
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/e2fs_lib.c b/busybox-1.19.3/e2fsprogs/e2fs_lib.c
new file mode 100644
index 0000000..a6aec94
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/e2fs_lib.c
@@ -0,0 +1,213 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * See README for additional information
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "e2fs_lib.h"
+
+#define HAVE_EXT2_IOCTLS 1
+
+#if INT_MAX == LONG_MAX
+#define IF_LONG_IS_SAME(...) __VA_ARGS__
+#define IF_LONG_IS_WIDER(...)
+#else
+#define IF_LONG_IS_SAME(...)
+#define IF_LONG_IS_WIDER(...) __VA_ARGS__
+#endif
+
+static void close_silently(int fd)
+{
+	int e = errno;
+	close(fd);
+	errno = e;
+}
+
+
+/* Iterate a function on each entry of a directory */
+int iterate_on_dir(const char *dir_name,
+		int FAST_FUNC (*func)(const char *, struct dirent *, void *),
+		void *private)
+{
+	DIR *dir;
+	struct dirent *de;
+
+	dir = opendir(dir_name);
+	if (dir == NULL) {
+		return -1;
+	}
+	while ((de = readdir(dir)) != NULL) {
+		func(dir_name, de, private);
+	}
+	closedir(dir);
+	return 0;
+}
+
+
+/* Get/set a file version on an ext2 file system */
+int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version)
+{
+#if HAVE_EXT2_IOCTLS
+	int fd, r;
+	IF_LONG_IS_WIDER(int ver;)
+
+	fd = open(name, O_RDONLY | O_NONBLOCK);
+	if (fd == -1)
+		return -1;
+	if (!get_version) {
+		IF_LONG_IS_WIDER(
+			ver = (int) set_version;
+			r = ioctl(fd, EXT2_IOC_SETVERSION, &ver);
+		)
+		IF_LONG_IS_SAME(
+			r = ioctl(fd, EXT2_IOC_SETVERSION, (void*)&set_version);
+		)
+	} else {
+		IF_LONG_IS_WIDER(
+			r = ioctl(fd, EXT2_IOC_GETVERSION, &ver);
+			*get_version = ver;
+		)
+		IF_LONG_IS_SAME(
+			r = ioctl(fd, EXT2_IOC_GETVERSION, (void*)get_version);
+		)
+	}
+	close_silently(fd);
+	return r;
+#else /* ! HAVE_EXT2_IOCTLS */
+	errno = EOPNOTSUPP;
+	return -1;
+#endif /* ! HAVE_EXT2_IOCTLS */
+}
+
+
+/* Get/set a file flags on an ext2 file system */
+int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags)
+{
+#if HAVE_EXT2_IOCTLS
+	struct stat buf;
+	int fd, r;
+	IF_LONG_IS_WIDER(int f;)
+
+	if (stat(name, &buf) == 0 /* stat is ok */
+	 && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)
+	) {
+		goto notsupp;
+	}
+	fd = open(name, O_RDONLY | O_NONBLOCK); /* neither read nor write asked for */
+	if (fd == -1)
+		return -1;
+
+	if (!get_flags) {
+		IF_LONG_IS_WIDER(
+			f = (int) set_flags;
+			r = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+		)
+		IF_LONG_IS_SAME(
+			r = ioctl(fd, EXT2_IOC_SETFLAGS, (void*)&set_flags);
+		)
+	} else {
+		IF_LONG_IS_WIDER(
+			r = ioctl(fd, EXT2_IOC_GETFLAGS, &f);
+			*get_flags = f;
+		)
+		IF_LONG_IS_SAME(
+			r = ioctl(fd, EXT2_IOC_GETFLAGS, (void*)get_flags);
+		)
+	}
+
+	close_silently(fd);
+	return r;
+ notsupp:
+#endif /* HAVE_EXT2_IOCTLS */
+	errno = EOPNOTSUPP;
+	return -1;
+}
+
+
+/* Print file attributes on an ext2 file system */
+const uint32_t e2attr_flags_value[] = {
+#ifdef ENABLE_COMPRESSION
+	EXT2_COMPRBLK_FL,
+	EXT2_DIRTY_FL,
+	EXT2_NOCOMPR_FL,
+	EXT2_ECOMPR_FL,
+#endif
+	EXT2_INDEX_FL,
+	EXT2_SECRM_FL,
+	EXT2_UNRM_FL,
+	EXT2_SYNC_FL,
+	EXT2_DIRSYNC_FL,
+	EXT2_IMMUTABLE_FL,
+	EXT2_APPEND_FL,
+	EXT2_NODUMP_FL,
+	EXT2_NOATIME_FL,
+	EXT2_COMPR_FL,
+	EXT3_JOURNAL_DATA_FL,
+	EXT2_NOTAIL_FL,
+	EXT2_TOPDIR_FL
+};
+
+const char e2attr_flags_sname[] =
+#ifdef ENABLE_COMPRESSION
+	"BZXE"
+#endif
+	"I"
+	"suSDiadAcjtT";
+
+static const char e2attr_flags_lname[] =
+#ifdef ENABLE_COMPRESSION
+	"Compressed_File" "\0"
+	"Compressed_Dirty_File" "\0"
+	"Compression_Raw_Access" "\0"
+	"Compression_Error" "\0"
+#endif
+	"Indexed_directory" "\0"
+	"Secure_Deletion" "\0"
+	"Undelete" "\0"
+	"Synchronous_Updates" "\0"
+	"Synchronous_Directory_Updates" "\0"
+	"Immutable" "\0"
+	"Append_Only" "\0"
+	"No_Dump" "\0"
+	"No_Atime" "\0"
+	"Compression_Requested" "\0"
+	"Journaled_Data" "\0"
+	"No_Tailmerging" "\0"
+	"Top_of_Directory_Hierarchies" "\0"
+	/* Another trailing NUL is added by compiler */;
+
+void print_e2flags(FILE *f, unsigned long flags, unsigned options)
+{
+	const uint32_t *fv;
+	const char *fn;
+
+	fv = e2attr_flags_value;
+	if (options & PFOPT_LONG) {
+		int first = 1;
+		fn = e2attr_flags_lname;
+		do {
+			if (flags & *fv) {
+				if (!first)
+					fputs(", ", f);
+				fputs(fn, f);
+				first = 0;
+			}
+			fv++;
+			fn += strlen(fn) + 1;
+		} while (*fn);
+		if (first)
+			fputs("---", f);
+	} else {
+		fn = e2attr_flags_sname;
+		do  {
+			char c = '-';
+			if (flags & *fv)
+				c = *fn;
+			fputc(c, f);
+			fv++;
+			fn++;
+		} while (*fn);
+	}
+}
diff --git a/busybox-1.19.3/e2fsprogs/e2fs_lib.h b/busybox-1.19.3/e2fsprogs/e2fs_lib.h
new file mode 100644
index 0000000..3905ee7
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/e2fs_lib.h
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * See README for additional information
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/* Constants and structures */
+#include "e2fs_defs.h"
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* Iterate a function on each entry of a directory */
+int iterate_on_dir(const char *dir_name,
+		int FAST_FUNC (*func)(const char *, struct dirent *, void *),
+		void *private);
+
+/* Get/set a file version on an ext2 file system */
+int fgetsetversion(const char *name, unsigned long *get_version, unsigned long set_version);
+#define fgetversion(name, version) fgetsetversion(name, version, 0)
+#define fsetversion(name, version) fgetsetversion(name, NULL, version)
+
+/* Get/set a file flags on an ext2 file system */
+int fgetsetflags(const char *name, unsigned long *get_flags, unsigned long set_flags);
+#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
+#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
+
+/* Must be 1 for compatibility with `int long_format'. */
+#define PFOPT_LONG  1
+/* Print file attributes on an ext2 file system */
+void print_e2flags(FILE *f, unsigned long flags, unsigned options);
+
+extern const uint32_t e2attr_flags_value[];
+extern const char e2attr_flags_sname[];
+
+/* If you plan to ENABLE_COMPRESSION, see e2fs_lib.c and chattr.c - */
+/* make sure that chattr doesn't accept bad options! */
+#ifdef ENABLE_COMPRESSION
+#define e2attr_flags_value_chattr (&e2attr_flags_value[5])
+#define e2attr_flags_sname_chattr (&e2attr_flags_sname[5])
+#else
+#define e2attr_flags_value_chattr (&e2attr_flags_value[1])
+#define e2attr_flags_sname_chattr (&e2attr_flags_sname[1])
+#endif
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/busybox-1.19.3/e2fsprogs/fsck.c b/busybox-1.19.3/e2fsprogs/fsck.c
new file mode 100644
index 0000000..4b2f774
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/fsck.c
@@ -0,0 +1,1096 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck --- A generic, parallelizing front-end for the fsck program.
+ * It will automatically try to run fsck programs in parallel if the
+ * devices are on separate spindles.  It is based on the same ideas as
+ * the generic front end for fsck by David Engel and Fred van Kempen,
+ * but it has been completely rewritten from scratch to support
+ * parallel execution.
+ *
+ * Written by Theodore Ts'o, <tytso@mit.edu>
+ *
+ * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
+ *   o Changed -t fstype to behave like with mount when -A (all file
+ *     systems) or -M (like mount) is specified.
+ *   o fsck looks if it can find the fsck.type program to decide
+ *     if it should ignore the fs type. This way more fsck programs
+ *     can be added without changing this front-end.
+ *   o -R flag skip root file system.
+ *
+ * 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 source tree.
+ */
+
+/* All filesystem specific hooks have been removed.
+ * If filesystem cannot be determined, we will execute
+ * "fsck.auto". Currently this also happens if you specify
+ * UUID=xxx or LABEL=xxx as an object to check.
+ * Detection code for that is also probably has to be in fsck.auto.
+ *
+ * In other words, this is _really_ is just a driver program which
+ * spawns actual fsck.something for each filesystem to check.
+ * 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.
+ * We should be filesystem agnostic. IOW: there should be a well-defined
+ * API for fsck.something, NOT ad-hoc hacks in generic fsck. */
+#define DO_PROGRESS_INDICATOR 0
+
+/* fsck 1.41.4 (27-Jan-2009) manpage says:
+ * 0   - No errors
+ * 1   - File system errors corrected
+ * 2   - System should be rebooted
+ * 4   - File system errors left uncorrected
+ * 8   - Operational error
+ * 16  - Usage or syntax error
+ * 32  - Fsck canceled by user request
+ * 128 - Shared library error
+ */
+#define EXIT_OK          0
+#define EXIT_NONDESTRUCT 1
+#define EXIT_DESTRUCT    2
+#define EXIT_UNCORRECTED 4
+#define EXIT_ERROR       8
+#define EXIT_USAGE       16
+#define FSCK_CANCELED    32     /* Aborted with a signal or ^C */
+
+/*
+ * Internal structure for mount table entries.
+ */
+struct fs_info {
+	struct fs_info *next;
+	char	*device;
+	char	*mountpt;
+	char	*type;
+	char	*opts;
+	int	passno;
+	int	flags;
+};
+
+#define FLAG_DONE 1
+#define FLAG_PROGRESS 2
+/*
+ * Structure to allow exit codes to be stored
+ */
+struct fsck_instance {
+	struct fsck_instance *next;
+	int	pid;
+	int	flags;
+#if DO_PROGRESS_INDICATOR
+	time_t	start_time;
+#endif
+	char	*prog;
+	char	*device;
+	char	*base_device; /* /dev/hda for /dev/hdaN etc */
+};
+
+static const char ignored_types[] ALIGN1 =
+	"ignore\0"
+	"iso9660\0"
+	"nfs\0"
+	"proc\0"
+	"sw\0"
+	"swap\0"
+	"tmpfs\0"
+	"devpts\0";
+
+#if 0
+static const char really_wanted[] ALIGN1 =
+	"minix\0"
+	"ext2\0"
+	"ext3\0"
+	"jfs\0"
+	"reiserfs\0"
+	"xiafs\0"
+	"xfs\0";
+#endif
+
+#define BASE_MD "/dev/md"
+
+static char **args;
+static int num_args;
+static int verbose;
+
+#define FS_TYPE_FLAG_NORMAL 0
+#define FS_TYPE_FLAG_OPT    1
+#define FS_TYPE_FLAG_NEGOPT 2
+static char **fs_type_list;
+static uint8_t *fs_type_flag;
+static smallint fs_type_negated;
+
+static smallint noexecute;
+static smallint serialize;
+static smallint skip_root;
+/* static smallint like_mount; */
+static smallint parallel_root;
+static smallint force_all_parallel;
+
+#if DO_PROGRESS_INDICATOR
+static smallint progress;
+static int progress_fd;
+#endif
+
+static int num_running;
+static int max_running;
+static char *fstype;
+static struct fs_info *filesys_info;
+static struct fs_info *filesys_last;
+static struct fsck_instance *instance_list;
+
+/*
+ * Return the "base device" given a particular device; this is used to
+ * assure that we only fsck one partition on a particular drive at any
+ * one time.  Otherwise, the disk heads will be seeking all over the
+ * place.  If the base device cannot be determined, return NULL.
+ *
+ * The base_device() function returns an allocated string which must
+ * be freed.
+ */
+#if ENABLE_FEATURE_DEVFS
+/*
+ * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
+ * pathames.
+ */
+static const char *const devfs_hier[] = {
+	"host", "bus", "target", "lun", NULL
+};
+#endif
+
+static char *base_device(const char *device)
+{
+	char *str, *cp;
+#if ENABLE_FEATURE_DEVFS
+	const char *const *hier;
+	const char *disk;
+	int len;
+#endif
+	str = xstrdup(device);
+
+	/* Skip over "/dev/"; if it's not present, give up */
+	cp = skip_dev_pfx(str);
+	if (cp == str)
+		goto errout;
+
+	/*
+	 * For md devices, we treat them all as if they were all
+	 * on one disk, since we don't know how to parallelize them.
+	 */
+	if (cp[0] == 'm' && cp[1] == 'd') {
+		cp[2] = 0;
+		return str;
+	}
+
+	/* Handle DAC 960 devices */
+	if (strncmp(cp, "rd/", 3) == 0) {
+		cp += 3;
+		if (cp[0] != 'c' || !isdigit(cp[1])
+		 || cp[2] != 'd' || !isdigit(cp[3]))
+			goto errout;
+		cp[4] = 0;
+		return str;
+	}
+
+	/* Now let's handle /dev/hd* and /dev/sd* devices.... */
+	if ((cp[0] == 'h' || cp[0] == 's') && cp[1] == 'd') {
+		cp += 2;
+		/* If there's a single number after /dev/hd, skip it */
+		if (isdigit(*cp))
+			cp++;
+		/* What follows must be an alpha char, or give up */
+		if (!isalpha(*cp))
+			goto errout;
+		cp[1] = 0;
+		return str;
+	}
+
+#if ENABLE_FEATURE_DEVFS
+	/* Now let's handle devfs (ugh) names */
+	len = 0;
+	if (strncmp(cp, "ide/", 4) == 0)
+		len = 4;
+	if (strncmp(cp, "scsi/", 5) == 0)
+		len = 5;
+	if (len) {
+		cp += len;
+		/*
+		 * Now we proceed down the expected devfs hierarchy.
+		 * i.e., .../host1/bus2/target3/lun4/...
+		 * If we don't find the expected token, followed by
+		 * some number of digits at each level, abort.
+		 */
+		for (hier = devfs_hier; *hier; hier++) {
+			len = strlen(*hier);
+			if (strncmp(cp, *hier, len) != 0)
+				goto errout;
+			cp += len;
+			while (*cp != '/' && *cp != 0) {
+				if (!isdigit(*cp))
+					goto errout;
+				cp++;
+			}
+			cp++;
+		}
+		cp[-1] = 0;
+		return str;
+	}
+
+	/* Now handle devfs /dev/disc or /dev/disk names */
+	disk = 0;
+	if (strncmp(cp, "discs/", 6) == 0)
+		disk = "disc";
+	else if (strncmp(cp, "disks/", 6) == 0)
+		disk = "disk";
+	if (disk) {
+		cp += 6;
+		if (strncmp(cp, disk, 4) != 0)
+			goto errout;
+		cp += 4;
+		while (*cp != '/' && *cp != 0) {
+			if (!isdigit(*cp))
+				goto errout;
+			cp++;
+		}
+		*cp = 0;
+		return str;
+	}
+#endif
+ errout:
+	free(str);
+	return NULL;
+}
+
+static void free_instance(struct fsck_instance *p)
+{
+	free(p->prog);
+	free(p->device);
+	free(p->base_device);
+	free(p);
+}
+
+static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
+					const char *type, const char *opts,
+					int passno)
+{
+	struct fs_info *fs;
+
+	fs = xzalloc(sizeof(*fs));
+	fs->device = xstrdup(device);
+	fs->mountpt = xstrdup(mntpnt);
+	if (strchr(type, ','))
+		type = (char *)"auto";
+	fs->type = xstrdup(type);
+	fs->opts = xstrdup(opts ? opts : "");
+	fs->passno = passno < 0 ? 1 : passno;
+	/*fs->flags = 0; */
+	/*fs->next = NULL; */
+
+	if (!filesys_info)
+		filesys_info = fs;
+	else
+		filesys_last->next = fs;
+	filesys_last = fs;
+
+	return fs;
+}
+
+/* Load the filesystem database from /etc/fstab */
+static void load_fs_info(const char *filename)
+{
+	FILE *fstab;
+	struct mntent mte;
+
+	fstab = setmntent(filename, "r");
+	if (!fstab) {
+		bb_perror_msg("can't read '%s'", filename);
+		return;
+	}
+
+	// Loop through entries
+	while (getmntent_r(fstab, &mte, bb_common_bufsiz1, COMMON_BUFSIZE)) {
+		//bb_info_msg("CREATE[%s][%s][%s][%s][%d]", mte.mnt_fsname, mte.mnt_dir,
+		//	mte.mnt_type, mte.mnt_opts,
+		//	mte.mnt_passno);
+		create_fs_device(mte.mnt_fsname, mte.mnt_dir,
+			mte.mnt_type, mte.mnt_opts,
+			mte.mnt_passno);
+	}
+	endmntent(fstab);
+}
+
+/* Lookup filesys in /etc/fstab and return the corresponding entry. */
+static struct fs_info *lookup(char *filesys)
+{
+	struct fs_info *fs;
+
+	for (fs = filesys_info; fs; fs = fs->next) {
+		if (strcmp(filesys, fs->device) == 0
+		 || (fs->mountpt && strcmp(filesys, fs->mountpt) == 0)
+		)
+			break;
+	}
+
+	return fs;
+}
+
+#if DO_PROGRESS_INDICATOR
+static int progress_active(void)
+{
+	struct fsck_instance *inst;
+
+	for (inst = instance_list; inst; inst = inst->next) {
+		if (inst->flags & FLAG_DONE)
+			continue;
+		if (inst->flags & FLAG_PROGRESS)
+			return 1;
+	}
+	return 0;
+}
+#endif
+
+
+/*
+ * Send a signal to all outstanding fsck child processes
+ */
+static void kill_all_if_got_signal(void)
+{
+	static smallint kill_sent;
+
+	struct fsck_instance *inst;
+
+	if (!bb_got_signal || kill_sent)
+		return;
+
+	for (inst = instance_list; inst; inst = inst->next) {
+		if (inst->flags & FLAG_DONE)
+			continue;
+		kill(inst->pid, SIGTERM);
+	}
+	kill_sent = 1;
+}
+
+/*
+ * Wait for one child process to exit; when it does, unlink it from
+ * the list of executing child processes, free, and return its exit status.
+ * If there is no exited child, return -1.
+ */
+static int wait_one(int flags)
+{
+	int status;
+	int sig;
+	struct fsck_instance *inst, *prev;
+	pid_t pid;
+
+	if (!instance_list)
+		return -1;
+	/* if (noexecute) { already returned -1; } */
+
+	while (1) {
+		pid = waitpid(-1, &status, flags);
+		kill_all_if_got_signal();
+		if (pid == 0) /* flags == WNOHANG and no children exited */
+			return -1;
+		if (pid < 0) {
+			if (errno == EINTR)
+				continue;
+			if (errno == ECHILD) { /* paranoia */
+				bb_error_msg("wait: no more children");
+				return -1;
+			}
+			bb_perror_msg("wait");
+			continue;
+		}
+		prev = NULL;
+		inst = instance_list;
+		do {
+			if (inst->pid == pid)
+				goto child_died;
+			prev = inst;
+			inst = inst->next;
+		} while (inst);
+	}
+ child_died:
+
+	if (WIFEXITED(status))
+		status = WEXITSTATUS(status);
+	else if (WIFSIGNALED(status)) {
+		sig = WTERMSIG(status);
+		status = EXIT_UNCORRECTED;
+		if (sig != SIGINT) {
+			printf("Warning: %s %s terminated "
+				"by signal %d\n",
+				inst->prog, inst->device, sig);
+			status = EXIT_ERROR;
+		}
+	} else {
+		printf("%s %s: status is %x, should never happen\n",
+			inst->prog, inst->device, status);
+		status = EXIT_ERROR;
+	}
+
+#if DO_PROGRESS_INDICATOR
+	if (progress && (inst->flags & FLAG_PROGRESS) && !progress_active()) {
+		struct fsck_instance *inst2;
+		for (inst2 = instance_list; inst2; inst2 = inst2->next) {
+			if (inst2->flags & FLAG_DONE)
+				continue;
+			if (strcmp(inst2->type, "ext2") != 0
+			 && strcmp(inst2->type, "ext3") != 0
+			) {
+				continue;
+			}
+			/* ext[23], we will send USR1
+			 * (request to start displaying progress bar)
+			 *
+			 * If we've just started the fsck, wait a tiny
+			 * bit before sending the kill, to give it
+			 * time to set up the signal handler
+			 */
+			if (inst2->start_time >= time(NULL) - 1)
+				sleep(1);
+			kill(inst2->pid, SIGUSR1);
+			inst2->flags |= FLAG_PROGRESS;
+			break;
+		}
+	}
+#endif
+
+	if (prev)
+		prev->next = inst->next;
+	else
+		instance_list = inst->next;
+	if (verbose > 1)
+		printf("Finished with %s (exit status %d)\n",
+		       inst->device, status);
+	num_running--;
+	free_instance(inst);
+
+	return status;
+}
+
+/*
+ * Wait until all executing child processes have exited; return the
+ * logical OR of all of their exit code values.
+ */
+#define FLAG_WAIT_ALL           0
+#define FLAG_WAIT_ATLEAST_ONE   WNOHANG
+static int wait_many(int flags)
+{
+	int exit_status;
+	int global_status = 0;
+	int wait_flags = 0;
+
+	while ((exit_status = wait_one(wait_flags)) != -1) {
+		global_status |= exit_status;
+		wait_flags |= flags;
+	}
+	return global_status;
+}
+
+/*
+ * Execute a particular fsck program, and link it into the list of
+ * child processes we are waiting for.
+ */
+static void execute(const char *type, const char *device,
+		const char *mntpt /*, int interactive */)
+{
+	int i;
+	struct fsck_instance *inst;
+	pid_t pid;
+
+	args[0] = xasprintf("fsck.%s", type);
+
+#if DO_PROGRESS_INDICATOR
+	if (progress && !progress_active()) {
+		if (strcmp(type, "ext2") == 0
+		 || strcmp(type, "ext3") == 0
+		) {
+			args[XXX] = xasprintf("-C%d", progress_fd); /* 1 */
+			inst->flags |= FLAG_PROGRESS;
+		}
+	}
+#endif
+
+	args[num_args - 2] = (char*)device;
+	/* args[num_args - 1] = NULL; - already is */
+
+	if (verbose || noexecute) {
+		printf("[%s (%d) -- %s]", args[0], num_running,
+					mntpt ? mntpt : device);
+		for (i = 0; args[i]; i++)
+			printf(" %s", args[i]);
+		bb_putchar('\n');
+	}
+
+	/* Fork and execute the correct program. */
+	pid = -1;
+	if (!noexecute) {
+		pid = spawn(args);
+		if (pid < 0)
+			bb_simple_perror_msg(args[0]);
+	}
+
+#if DO_PROGRESS_INDICATOR
+	free(args[XXX]);
+#endif
+
+	/* No child, so don't record an instance */
+	if (pid <= 0) {
+		free(args[0]);
+		return;
+	}
+
+	inst = xzalloc(sizeof(*inst));
+	inst->pid = pid;
+	inst->prog = args[0];
+	inst->device = xstrdup(device);
+	inst->base_device = base_device(device);
+#if DO_PROGRESS_INDICATOR
+	inst->start_time = time(NULL);
+#endif
+
+	/* Add to the list of running fsck's.
+	 * (was adding to the end, but adding to the front is simpler...) */
+	inst->next = instance_list;
+	instance_list = inst;
+}
+
+/*
+ * Run the fsck program on a particular device
+ *
+ * If the type is specified using -t, and it isn't prefixed with "no"
+ * (as in "noext2") and only one filesystem type is specified, then
+ * use that type regardless of what is specified in /etc/fstab.
+ *
+ * If the type isn't specified by the user, then use either the type
+ * specified in /etc/fstab, or "auto".
+ */
+static void fsck_device(struct fs_info *fs /*, int interactive */)
+{
+	const char *type;
+
+	if (strcmp(fs->type, "auto") != 0) {
+		type = fs->type;
+		if (verbose > 2)
+			bb_info_msg("using filesystem type '%s' %s",
+					type, "from fstab");
+	} else if (fstype
+	 && (fstype[0] != 'n' || fstype[1] != 'o') /* != "no" */
+	 && strncmp(fstype, "opts=", 5) != 0
+	 && strncmp(fstype, "loop", 4) != 0
+	 && !strchr(fstype, ',')
+	) {
+		type = fstype;
+		if (verbose > 2)
+			bb_info_msg("using filesystem type '%s' %s",
+					type, "from -t");
+	} else {
+		type = "auto";
+		if (verbose > 2)
+			bb_info_msg("using filesystem type '%s' %s",
+					type, "(default)");
+	}
+
+	num_running++;
+	execute(type, fs->device, fs->mountpt /*, interactive */);
+}
+
+/*
+ * Returns TRUE if a partition on the same disk is already being
+ * checked.
+ */
+static int device_already_active(char *device)
+{
+	struct fsck_instance *inst;
+	char *base;
+
+	if (force_all_parallel)
+		return 0;
+
+#ifdef BASE_MD
+	/* Don't check a soft raid disk with any other disk */
+	if (instance_list
+	 && (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1)
+	     || !strncmp(device, BASE_MD, sizeof(BASE_MD)-1))
+	) {
+		return 1;
+	}
+#endif
+
+	base = base_device(device);
+	/*
+	 * If we don't know the base device, assume that the device is
+	 * already active if there are any fsck instances running.
+	 */
+	if (!base)
+		return (instance_list != NULL);
+
+	for (inst = instance_list; inst; inst = inst->next) {
+		if (!inst->base_device || !strcmp(base, inst->base_device)) {
+			free(base);
+			return 1;
+		}
+	}
+
+	free(base);
+	return 0;
+}
+
+/*
+ * This function returns true if a particular option appears in a
+ * comma-delimited options list
+ */
+static int opt_in_list(char *opt, char *optlist)
+{
+	char *s;
+	int len;
+
+	if (!optlist)
+		return 0;
+
+	len = strlen(opt);
+	s = optlist - 1;
+	while (1) {
+		s = strstr(s + 1, opt);
+		if (!s)
+			return 0;
+		/* neither "opt.." nor "xxx,opt.."? */
+		if (s != optlist && s[-1] != ',')
+			continue;
+		/* neither "..opt" nor "..opt,xxx"? */
+		if (s[len] != '\0' && s[len] != ',')
+			continue;
+		return 1;
+	}
+}
+
+/* See if the filesystem matches the criteria given by the -t option */
+static int fs_match(struct fs_info *fs)
+{
+	int n, ret, checked_type;
+	char *cp;
+
+	if (!fs_type_list)
+		return 1;
+
+	ret = 0;
+	checked_type = 0;
+	n = 0;
+	while (1) {
+		cp = fs_type_list[n];
+		if (!cp)
+			break;
+		switch (fs_type_flag[n]) {
+		case FS_TYPE_FLAG_NORMAL:
+			checked_type++;
+			if (strcmp(cp, fs->type) == 0)
+				ret = 1;
+			break;
+		case FS_TYPE_FLAG_NEGOPT:
+			if (opt_in_list(cp, fs->opts))
+				return 0;
+			break;
+		case FS_TYPE_FLAG_OPT:
+			if (!opt_in_list(cp, fs->opts))
+				return 0;
+			break;
+		}
+		n++;
+	}
+	if (checked_type == 0)
+		return 1;
+
+	return (fs_type_negated ? !ret : ret);
+}
+
+/* Check if we should ignore this filesystem. */
+static int ignore(struct fs_info *fs)
+{
+	/*
+	 * If the pass number is 0, ignore it.
+	 */
+	if (fs->passno == 0)
+		return 1;
+
+	/*
+	 * If a specific fstype is specified, and it doesn't match,
+	 * ignore it.
+	 */
+	if (!fs_match(fs))
+		return 1;
+
+	/* Are we ignoring this type? */
+	if (index_in_strings(ignored_types, fs->type) >= 0)
+		return 1;
+
+	/* We can and want to check this file system type. */
+	return 0;
+}
+
+/* Check all file systems, using the /etc/fstab table. */
+static int check_all(void)
+{
+	struct fs_info *fs;
+	int status = EXIT_OK;
+	smallint not_done_yet;
+	smallint pass_done;
+	int passno;
+
+	if (verbose)
+		puts("Checking all filesystems");
+
+	/*
+	 * Do an initial scan over the filesystem; mark filesystems
+	 * which should be ignored as done, and resolve any "auto"
+	 * filesystem types (done as a side-effect of calling ignore()).
+	 */
+	for (fs = filesys_info; fs; fs = fs->next)
+		if (ignore(fs))
+			fs->flags |= FLAG_DONE;
+
+	/*
+	 * Find and check the root filesystem.
+	 */
+	if (!parallel_root) {
+		for (fs = filesys_info; fs; fs = fs->next) {
+			if (LONE_CHAR(fs->mountpt, '/')) {
+				if (!skip_root && !ignore(fs)) {
+					fsck_device(fs /*, 1*/);
+					status |= wait_many(FLAG_WAIT_ALL);
+					if (status > EXIT_NONDESTRUCT)
+						return status;
+				}
+				fs->flags |= FLAG_DONE;
+				break;
+			}
+		}
+	}
+	/*
+	 * This is for the bone-headed user who has root
+	 * filesystem listed twice.
+	 * "Skip root" will skip _all_ root entries.
+	 */
+	if (skip_root)
+		for (fs = filesys_info; fs; fs = fs->next)
+			if (LONE_CHAR(fs->mountpt, '/'))
+				fs->flags |= FLAG_DONE;
+
+	not_done_yet = 1;
+	passno = 1;
+	while (not_done_yet) {
+		not_done_yet = 0;
+		pass_done = 1;
+
+		for (fs = filesys_info; fs; fs = fs->next) {
+			if (bb_got_signal)
+				break;
+			if (fs->flags & FLAG_DONE)
+				continue;
+			/*
+			 * If the filesystem's pass number is higher
+			 * than the current pass number, then we didn't
+			 * do it yet.
+			 */
+			if (fs->passno > passno) {
+				not_done_yet = 1;
+				continue;
+			}
+			/*
+			 * If a filesystem on a particular device has
+			 * already been spawned, then we need to defer
+			 * this to another pass.
+			 */
+			if (device_already_active(fs->device)) {
+				pass_done = 0;
+				continue;
+			}
+			/*
+			 * Spawn off the fsck process
+			 */
+			fsck_device(fs /*, serialize*/);
+			fs->flags |= FLAG_DONE;
+
+			/*
+			 * Only do one filesystem at a time, or if we
+			 * have a limit on the number of fsck's extant
+			 * at one time, apply that limit.
+			 */
+			if (serialize
+			 || (max_running && (num_running >= max_running))
+			) {
+				pass_done = 0;
+				break;
+			}
+		}
+		if (bb_got_signal)
+			break;
+		if (verbose > 1)
+			printf("--waiting-- (pass %d)\n", passno);
+		status |= wait_many(pass_done ? FLAG_WAIT_ALL :
+				    FLAG_WAIT_ATLEAST_ONE);
+		if (pass_done) {
+			if (verbose > 1)
+				puts("----------------------------------");
+			passno++;
+		} else
+			not_done_yet = 1;
+	}
+	kill_all_if_got_signal();
+	status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
+	return status;
+}
+
+/*
+ * Deal with the fsck -t argument.
+ * Huh, for mount "-t novfat,nfs" means "neither vfat nor nfs"!
+ * Why here we require "-t novfat,nonfs" ??
+ */
+static void compile_fs_type(char *fs_type)
+{
+	char *s;
+	int num = 2;
+	smallint negate;
+
+	s = fs_type;
+	while ((s = strchr(s, ','))) {
+		num++;
+		s++;
+	}
+
+	fs_type_list = xzalloc(num * sizeof(fs_type_list[0]));
+	fs_type_flag = xzalloc(num * sizeof(fs_type_flag[0]));
+	fs_type_negated = -1; /* not yet known is it negated or not */
+
+	num = 0;
+	s = fs_type;
+	while (1) {
+		char *comma;
+
+		negate = 0;
+		if (s[0] == 'n' && s[1] == 'o') { /* "no.." */
+			s += 2;
+			negate = 1;
+		} else if (s[0] == '!') {
+			s++;
+			negate = 1;
+		}
+
+		if (strcmp(s, "loop") == 0)
+			/* loop is really short-hand for opts=loop */
+			goto loop_special_case;
+		if (strncmp(s, "opts=", 5) == 0) {
+			s += 5;
+ loop_special_case:
+			fs_type_flag[num] = negate ? FS_TYPE_FLAG_NEGOPT : FS_TYPE_FLAG_OPT;
+		} else {
+			if (fs_type_negated == -1)
+				fs_type_negated = negate;
+			if (fs_type_negated != negate)
+				bb_error_msg_and_die(
+"either all or none of the filesystem types passed to -t must be prefixed "
+"with 'no' or '!'");
+		}
+		comma = strchr(s, ',');
+		fs_type_list[num++] = comma ? xstrndup(s, comma-s) : xstrdup(s);
+		if (!comma)
+			break;
+		s = comma + 1;
+	}
+}
+
+static char **new_args(void)
+{
+	args = xrealloc_vector(args, 2, num_args);
+	return &args[num_args++];
+}
+
+int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fsck_main(int argc UNUSED_PARAM, char **argv)
+{
+	int i, status;
+	/*int interactive;*/
+	struct fs_info *fs;
+	const char *fstab;
+	char *tmp;
+	char **devices;
+	int num_devices;
+	smallint opts_for_fsck;
+	smallint doall;
+	smallint notitle;
+
+	/* we want wait() to be interruptible */
+	signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
+	signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
+
+	setbuf(stdout, NULL);
+
+	opts_for_fsck = doall = notitle = 0;
+	devices = NULL;
+	num_devices = 0;
+	new_args(); /* args[0] = NULL, will be replaced by fsck.<type> */
+	/* instance_list = NULL; - in bss, so already zeroed */
+
+	while (*++argv) {
+		int j;
+		int optpos;
+		char *options;
+		char *arg = *argv;
+
+		/* "/dev/blk" or "/path" or "UUID=xxx" or "LABEL=xxx" */
+		if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
+// FIXME: must check that arg is a blkdev, or resolve
+// "/path", "UUID=xxx" or "LABEL=xxx" into block device name
+// ("UUID=xxx"/"LABEL=xxx" can probably shifted to fsck.auto duties)
+			devices = xrealloc_vector(devices, 2, num_devices);
+			devices[num_devices++] = arg;
+			continue;
+		}
+
+		if (arg[0] != '-' || opts_for_fsck) {
+			*new_args() = arg;
+			continue;
+		}
+
+		if (LONE_CHAR(arg + 1, '-')) { /* "--" ? */
+			opts_for_fsck = 1;
+			continue;
+		}
+
+		optpos = 0;
+		options = NULL;
+		for (j = 1; arg[j]; j++) {
+			switch (arg[j]) {
+			case 'A':
+				doall = 1;
+				break;
+#if DO_PROGRESS_INDICATOR
+			case 'C':
+				progress = 1;
+				if (arg[++j]) { /* -Cn */
+					progress_fd = xatoi_positive(&arg[j]);
+					goto next_arg;
+				}
+				/* -C n */
+				if (!*++argv)
+					bb_show_usage();
+				progress_fd = xatoi_positive(*argv);
+				goto next_arg;
+#endif
+			case 'V':
+				verbose++;
+				break;
+			case 'N':
+				noexecute = 1;
+				break;
+			case 'R':
+				skip_root = 1;
+				break;
+			case 'T':
+				notitle = 1;
+				break;
+/*			case 'M':
+				like_mount = 1;
+				break; */
+			case 'P':
+				parallel_root = 1;
+				break;
+			case 's':
+				serialize = 1;
+				break;
+			case 't':
+				if (fstype)
+					bb_show_usage();
+				if (arg[++j])
+					tmp = &arg[j];
+				else if (*++argv)
+					tmp = *argv;
+				else
+					bb_show_usage();
+				fstype = xstrdup(tmp);
+				compile_fs_type(fstype);
+				goto next_arg;
+			case '?':
+				bb_show_usage();
+				break;
+			default:
+				optpos++;
+				/* one extra for '\0' */
+				options = xrealloc(options, optpos + 2);
+				options[optpos] = arg[j];
+				break;
+			}
+		}
+ next_arg:
+		if (optpos) {
+			options[0] = '-';
+			options[optpos + 1] = '\0';
+			*new_args() = options;
+		}
+	}
+	if (getenv("FSCK_FORCE_ALL_PARALLEL"))
+		force_all_parallel = 1;
+	tmp = getenv("FSCK_MAX_INST");
+	if (tmp)
+		max_running = xatoi(tmp);
+	new_args(); /* args[num_args - 2] will be replaced by <device> */
+	new_args(); /* args[num_args - 1] is the last, NULL element */
+
+	if (!notitle)
+		puts("fsck (busybox "BB_VER", "BB_BT")");
+
+	/* Even plain "fsck /dev/hda1" needs fstab to get fs type,
+	 * so we are scanning it anyway */
+	fstab = getenv("FSTAB_FILE");
+	if (!fstab)
+		fstab = "/etc/fstab";
+	load_fs_info(fstab);
+
+	/*interactive = (num_devices == 1) | serialize;*/
+
+	if (num_devices == 0)
+		/*interactive =*/ serialize = doall = 1;
+	if (doall)
+		return check_all();
+
+	status = 0;
+	for (i = 0; i < num_devices; i++) {
+		if (bb_got_signal) {
+			kill_all_if_got_signal();
+			break;
+		}
+
+		fs = lookup(devices[i]);
+		if (!fs)
+			fs = create_fs_device(devices[i], "", "auto", NULL, -1);
+		fsck_device(fs /*, interactive */);
+
+		if (serialize
+		 || (max_running && (num_running >= max_running))
+		) {
+			int exit_status = wait_one(0);
+			if (exit_status >= 0)
+				status |= exit_status;
+			if (verbose > 1)
+				puts("----------------------------------");
+		}
+	}
+	status |= wait_many(FLAG_WAIT_ALL);
+	return status;
+}
diff --git a/busybox-1.19.3/e2fsprogs/lsattr.c b/busybox-1.19.3/e2fsprogs/lsattr.c
new file mode 100644
index 0000000..1312fe7
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/lsattr.c
@@ -0,0 +1,120 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lsattr.c		- List file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * 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)
+ */
+
+//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"
+
+enum {
+	OPT_RECUR      = 0x1,
+	OPT_ALL        = 0x2,
+	OPT_DIRS_OPT   = 0x4,
+	OPT_PF_LONG    = 0x8,
+	OPT_GENERATION = 0x10,
+};
+
+static void list_attributes(const char *name)
+{
+	unsigned long fsflags;
+	unsigned long generation;
+
+	if (fgetflags(name, &fsflags) != 0)
+		goto read_err;
+
+	if (option_mask32 & OPT_GENERATION) {
+		if (fgetversion(name, &generation) != 0)
+			goto read_err;
+		printf("%5lu ", generation);
+	}
+
+	if (option_mask32 & OPT_PF_LONG) {
+		printf("%-28s ", name);
+		print_e2flags(stdout, fsflags, PFOPT_LONG);
+		bb_putchar('\n');
+	} else {
+		print_e2flags(stdout, fsflags, 0);
+		printf(" %s\n", name);
+	}
+
+	return;
+ read_err:
+	bb_perror_msg("reading %s", name);
+}
+
+static int FAST_FUNC lsattr_dir_proc(const char *dir_name,
+		struct dirent *de,
+		void *private UNUSED_PARAM)
+{
+	struct stat st;
+	char *path;
+
+	path = concat_path_file(dir_name, de->d_name);
+
+	if (lstat(path, &st) != 0)
+		bb_perror_msg("stat %s", path);
+	else if (de->d_name[0] != '.' || (option_mask32 & OPT_ALL)) {
+		list_attributes(path);
+		if (S_ISDIR(st.st_mode) && (option_mask32 & OPT_RECUR)
+		 && !DOT_OR_DOTDOT(de->d_name)
+		) {
+			printf("\n%s:\n", path);
+			iterate_on_dir(path, lsattr_dir_proc, NULL);
+			bb_putchar('\n');
+		}
+	}
+
+	free(path);
+	return 0;
+}
+
+static void lsattr_args(const char *name)
+{
+	struct stat st;
+
+	if (lstat(name, &st) == -1) {
+		bb_perror_msg("stat %s", name);
+	} else if (S_ISDIR(st.st_mode) && !(option_mask32 & OPT_DIRS_OPT)) {
+		iterate_on_dir(name, lsattr_dir_proc, NULL);
+	} else {
+		list_attributes(name);
+	}
+}
+
+int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lsattr_main(int argc UNUSED_PARAM, char **argv)
+{
+	getopt32(argv, "Radlv");
+	argv += optind;
+
+	if (!*argv)
+		*--argv = (char*)".";
+	do lsattr_args(*argv++); while (*argv);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/Config.src b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/Config.src
new file mode 100644
index 0000000..bbec08e
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/Config.src
@@ -0,0 +1,69 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux Ext2 FS Progs"
+
+INSERT
+
+config CHATTR
+	bool "chattr"
+	default n
+	help
+	  chattr changes the file attributes on a second extended file system.
+
+config E2FSCK
+	bool "e2fsck"
+	default n
+	help
+	  e2fsck is used to check Linux second extended file systems (ext2fs).
+	  e2fsck also supports ext2 filesystems countaining a journal (ext3).
+	  The normal compat symlinks 'fsck.ext2' and 'fsck.ext3' are also
+	  provided.
+
+config FSCK
+	bool "fsck"
+	default n
+	help
+	  fsck is used to check and optionally repair one or more filesystems.
+	  In actuality, fsck is simply a front-end for the various file system
+	  checkers (fsck.fstype) available under Linux.
+
+config LSATTR
+	bool "lsattr"
+	default n
+	help
+	  lsattr lists the file attributes on a second extended file system.
+
+config MKE2FS
+	bool "mke2fs"
+	default n
+	help
+	  mke2fs is used to create an ext2/ext3 filesystem. The normal compat
+	  symlinks 'mkfs.ext2' and 'mkfs.ext3' are also provided.
+
+config TUNE2FS
+	bool "tune2fs"
+	default n
+	help
+	  tune2fs allows the system administrator to adjust various tunable
+	  filesystem parameters on Linux ext2/ext3 filesystems.
+
+config E2LABEL
+	bool "e2label"
+	default n
+	depends on TUNE2FS
+	help
+	  e2label will display or change the filesystem label on the ext2
+	  filesystem located on device.
+
+config FINDFS
+	bool "findfs"
+	default n
+	depends on TUNE2FS
+	help
+	  findfs will search the disks in the system looking for a filesystem
+	  which has a label matching label or a UUID equal to uuid.
+
+endmenu
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/Kbuild.src b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/Kbuild.src
new file mode 100644
index 0000000..fff1a0d
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/Kbuild.src
@@ -0,0 +1,18 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+
+lib-$(CONFIG_CHATTR)     += chattr.o
+lib-$(CONFIG_E2FSCK)     += e2fsck.o util.o
+lib-$(CONFIG_FSCK)       += fsck.o util.o
+lib-$(CONFIG_LSATTR)     += lsattr.o
+lib-$(CONFIG_MKE2FS)     += mke2fs.o util.o
+lib-$(CONFIG_TUNE2FS)    += tune2fs.o util.o
+
+CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/README b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/README
new file mode 100644
index 0000000..fac0901
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/README
@@ -0,0 +1,3 @@
+This is a pretty straight rip from the e2fsprogs pkg.
+
+See README's in subdirs for specific info.
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src
new file mode 100644
index 0000000..02b4d24
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/Kbuild.src
@@ -0,0 +1,26 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+NEEDED-$(CONFIG_E2FSCK) = y
+NEEDED-$(CONFIG_FSCK) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+
+INSERT
+
+lib-$(NEEDED-y) += cache.o dev.o devname.o devno.o blkid_getsize.o \
+                   probe.o read.o resolve.o save.o tag.o list.o
+
+CFLAGS_dev.o     := -include $(srctree)/include/busybox.h
+CFLAGS_devname.o := -include $(srctree)/include/busybox.h
+CFLAGS_devno.o   := -include $(srctree)/include/busybox.h
+CFLAGS_blkid_getsize.o := -include $(srctree)/include/busybox.h
+CFLAGS_probe.o   := -include $(srctree)/include/busybox.h
+CFLAGS_save.o    := -include $(srctree)/include/busybox.h
+CFLAGS_tag.o     := -include $(srctree)/include/busybox.h
+CFLAGS_list.o    := -include $(srctree)/include/busybox.h
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkid.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkid.h
new file mode 100644
index 0000000..9a3c2af
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkid.h
@@ -0,0 +1,104 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * blkid.h - Interface for libblkid, a library to identify block devices
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+#ifndef BLKID_BLKID_H
+#define BLKID_BLKID_H 1
+
+#include <sys/types.h>
+#include <linux/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLKID_VERSION	"1.0.0"
+#define BLKID_DATE	"12-Feb-2003"
+
+typedef struct blkid_struct_dev *blkid_dev;
+typedef struct blkid_struct_cache *blkid_cache;
+typedef __s64 blkid_loff_t;
+
+typedef struct blkid_struct_tag_iterate *blkid_tag_iterate;
+typedef struct blkid_struct_dev_iterate *blkid_dev_iterate;
+
+/*
+ * Flags for blkid_get_dev
+ *
+ * BLKID_DEV_CREATE	Create an empty device structure if not found
+ *			in the cache.
+ * BLKID_DEV_VERIFY	Make sure the device structure corresponds
+ *			with reality.
+ * BLKID_DEV_FIND	Just look up a device entry, and return NULL
+ *			if it is not found.
+ * BLKID_DEV_NORMAL	Get a valid device structure, either from the
+ *			cache or by probing the device.
+ */
+#define BLKID_DEV_FIND		0x0000
+#define BLKID_DEV_CREATE	0x0001
+#define BLKID_DEV_VERIFY	0x0002
+#define BLKID_DEV_NORMAL	(BLKID_DEV_CREATE | BLKID_DEV_VERIFY)
+
+/* cache.c */
+extern void blkid_put_cache(blkid_cache cache);
+extern int blkid_get_cache(blkid_cache *cache, const char *filename);
+
+/* dev.c */
+extern const char *blkid_dev_devname(blkid_dev dev);
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache);
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+				char *search_type, char *search_value);
+extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev);
+extern void blkid_dev_iterate_end(blkid_dev_iterate iterate);
+
+/* devno.c */
+extern char *blkid_devno_to_devname(dev_t devno);
+
+/* devname.c */
+extern int blkid_probe_all(blkid_cache cache);
+extern int blkid_probe_all_new(blkid_cache cache);
+extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname,
+			       int flags);
+
+/* getsize.c */
+extern blkid_loff_t blkid_get_dev_size(int fd);
+
+/* probe.c */
+int blkid_known_fstype(const char *fstype);
+extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev);
+
+/* read.c */
+
+/* resolve.c */
+extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+				       const char *devname);
+extern char *blkid_get_devname(blkid_cache cache, const char *token,
+			       const char *value);
+
+/* tag.c */
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev);
+extern int blkid_tag_next(blkid_tag_iterate iterate,
+			      const char **type, const char **value);
+extern void blkid_tag_iterate_end(blkid_tag_iterate iterate);
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
+			      const char *value);
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value);
+extern int blkid_parse_tag_string(const char *token, char **ret_type,
+				  char **ret_val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkidP.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkidP.h
new file mode 100644
index 0000000..d6b2b42
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkidP.h
@@ -0,0 +1,186 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * blkidP.h - Internal interfaces for libblkid
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+#ifndef BLKID_BLKIDP_H
+#define BLKID_BLKIDP_H 1
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "blkid.h"
+#include "list.h"
+
+#ifdef __GNUC__
+#define __BLKID_ATTR(x) __attribute__(x)
+#else
+#define __BLKID_ATTR(x)
+#endif
+
+
+/*
+ * This describes the attributes of a specific device.
+ * We can traverse all of the tags by bid_tags (linking to the tag bit_names).
+ * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag
+ * values, if they exist.
+ */
+struct blkid_struct_dev
+{
+	struct list_head	bid_devs;	/* All devices in the cache */
+	struct list_head	bid_tags;	/* All tags for this device */
+	blkid_cache		bid_cache;	/* Dev belongs to this cache */
+	char			*bid_name;	/* Device inode pathname */
+	char			*bid_type;	/* Preferred device TYPE */
+	int			bid_pri;	/* Device priority */
+	dev_t			bid_devno;	/* Device major/minor number */
+	time_t			bid_time;	/* Last update time of device */
+	unsigned int		bid_flags;	/* Device status bitflags */
+	char			*bid_label;	/* Shortcut to device LABEL */
+	char			*bid_uuid;	/* Shortcut to binary UUID */
+};
+
+#define BLKID_BID_FL_VERIFIED	0x0001	/* Device data validated from disk */
+#define BLKID_BID_FL_INVALID	0x0004	/* Device is invalid */
+
+/*
+ * Each tag defines a NAME=value pair for a particular device.  The tags
+ * are linked via bit_names for a single device, so that traversing the
+ * names list will get you a list of all tags associated with a device.
+ * They are also linked via bit_values for all devices, so one can easily
+ * search all tags with a given NAME for a specific value.
+ */
+struct blkid_struct_tag
+{
+	struct list_head	bit_tags;	/* All tags for this device */
+	struct list_head	bit_names;	/* All tags with given NAME */
+	char			*bit_name;	/* NAME of tag (shared) */
+	char			*bit_val;	/* value of tag */
+	blkid_dev		bit_dev;	/* pointer to device */
+};
+typedef struct blkid_struct_tag *blkid_tag;
+
+/*
+ * Minimum number of seconds between device probes, even when reading
+ * from the cache.  This is to avoid re-probing all devices which were
+ * just probed by another program that does not share the cache.
+ */
+#define BLKID_PROBE_MIN		2
+
+/*
+ * Time in seconds an entry remains verified in the in-memory cache
+ * before being reverified (in case of long-running processes that
+ * keep a cache in memory and continue to use it for a long time).
+ */
+#define BLKID_PROBE_INTERVAL	200
+
+/* This describes an entire blkid cache file and probed devices.
+ * We can traverse all of the found devices via bic_list.
+ * We can traverse all of the tag types by bic_tags, which hold empty tags
+ * for each tag type.  Those tags can be used as list_heads for iterating
+ * through all devices with a specific tag type (e.g. LABEL).
+ */
+struct blkid_struct_cache
+{
+	struct list_head	bic_devs;	/* List head of all devices */
+	struct list_head	bic_tags;	/* List head of all tag types */
+	time_t			bic_time;	/* Last probe time */
+	time_t			bic_ftime;	/* Mod time of the cachefile */
+	unsigned int		bic_flags;	/* Status flags of the cache */
+	char			*bic_filename;	/* filename of cache */
+};
+
+#define BLKID_BIC_FL_PROBED	0x0002	/* We probed /proc/partition devices */
+#define BLKID_BIC_FL_CHANGED	0x0004	/* Cache has changed from disk */
+
+extern char *blkid_strdup(const char *s);
+extern char *blkid_strndup(const char *s, const int length);
+
+#define BLKID_CACHE_FILE "/etc/blkid.tab"
+extern const char *blkid_devdirs[];
+
+#define BLKID_ERR_IO	 5
+#define BLKID_ERR_PROC	 9
+#define BLKID_ERR_MEM	12
+#define BLKID_ERR_CACHE	14
+#define BLKID_ERR_DEV	19
+#define BLKID_ERR_PARAM	22
+#define BLKID_ERR_BIG	27
+
+/*
+ * Priority settings for different types of devices
+ */
+#define BLKID_PRI_EVMS	30
+#define BLKID_PRI_LVM	20
+#define BLKID_PRI_MD	10
+
+#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG)
+#define CONFIG_BLKID_DEBUG
+#endif
+
+#define DEBUG_CACHE	0x0001
+#define DEBUG_DUMP	0x0002
+#define DEBUG_DEV	0x0004
+#define DEBUG_DEVNAME	0x0008
+#define DEBUG_DEVNO	0x0010
+#define DEBUG_PROBE	0x0020
+#define DEBUG_READ	0x0040
+#define DEBUG_RESOLVE	0x0080
+#define DEBUG_SAVE	0x0100
+#define DEBUG_TAG	0x0200
+#define DEBUG_INIT	0x8000
+#define DEBUG_ALL	0xFFFF
+
+#ifdef CONFIG_BLKID_DEBUG
+#include <stdio.h>
+extern int      blkid_debug_mask;
+#define DBG(m,x)	if ((m) & blkid_debug_mask) x;
+#else
+#define DBG(m,x)
+#endif
+
+#ifdef CONFIG_BLKID_DEBUG
+extern void blkid_debug_dump_dev(blkid_dev dev);
+extern void blkid_debug_dump_tag(blkid_tag tag);
+#endif
+
+/* lseek.c */
+/* extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); */
+#ifdef CONFIG_LFS
+# define blkid_llseek lseek64
+#else
+# define blkid_llseek lseek
+#endif
+
+/* read.c */
+extern void blkid_read_cache(blkid_cache cache);
+
+/* save.c */
+extern int blkid_flush_cache(blkid_cache cache);
+
+/*
+ * Functions to create and find a specific tag type: tag.c
+ */
+extern void blkid_free_tag(blkid_tag tag);
+extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type);
+extern int blkid_set_tag(blkid_dev dev, const char *name,
+			 const char *value, const int vlength);
+
+/*
+ * Functions to create and find a specific tag type: dev.c
+ */
+extern blkid_dev blkid_new_dev(void);
+extern void blkid_free_dev(blkid_dev dev);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
new file mode 100644
index 0000000..e1f6ba6
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/blkid_getsize.c
@@ -0,0 +1,179 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+/* include this before sys/queues.h! */
+#include "blkidP.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96)	/* return device size */
+#endif
+
+#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)	/* return device size in bytes (u64 *arg) */
+#endif
+
+#ifdef APPLE_DARWIN
+#define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif /* APPLE_DARWIN */
+
+static int valid_offset(int fd, blkid_loff_t offset)
+{
+	char ch;
+
+	if (blkid_llseek(fd, offset, 0) < 0)
+		return 0;
+	if (read(fd, &ch, 1) < 1)
+		return 0;
+	return 1;
+}
+
+/*
+ * Returns the number of blocks in a partition
+ */
+blkid_loff_t blkid_get_dev_size(int fd)
+{
+	int valid_blkgetsize64 = 1;
+#ifdef __linux__
+	struct		utsname ut;
+#endif
+	unsigned long long size64;
+	unsigned long size;
+	blkid_loff_t high, low;
+#ifdef FDGETPRM
+	struct floppy_struct this_floppy;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+	int part = -1;
+	struct disklabel lab;
+	struct partition *pp;
+	char ch;
+	struct stat st;
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+#ifdef DKIOCGETBLOCKCOUNT	/* For Apple Darwin */
+	if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
+		if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
+		    && (size64 << 9 > 0xFFFFFFFF))
+			return 0; /* EFBIG */
+		return (blkid_loff_t) size64 << 9;
+	}
+#endif
+
+#ifdef BLKGETSIZE64
+#ifdef __linux__
+	uname(&ut);
+	if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+	     (ut.release[2] < '6') && (ut.release[3] == '.'))
+		valid_blkgetsize64 = 0;
+#endif
+	if (valid_blkgetsize64 &&
+	    ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
+		if ((sizeof(blkid_loff_t) < sizeof(unsigned long long))
+		    && ((size64) > 0xFFFFFFFF))
+			return 0; /* EFBIG */
+		return size64;
+	}
+#endif
+
+#ifdef BLKGETSIZE
+	if (ioctl(fd, BLKGETSIZE, &size) >= 0)
+		return (blkid_loff_t)size << 9;
+#endif
+
+#ifdef FDGETPRM
+	if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
+		return (blkid_loff_t)this_floppy.size << 9;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#if 0
+	/*
+	 * This should work in theory but I haven't tested it.  Anyone
+	 * on a BSD system want to test this for me?  In the meantime,
+	 * binary search mechanism should work just fine.
+	 */
+	if ((fstat(fd, &st) >= 0) && S_ISBLK(st.st_mode))
+		part = st.st_rdev & 7;
+	if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+		pp = &lab.d_partitions[part];
+		if (pp->p_size)
+			return pp->p_size << 9;
+	}
+#endif
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+	/*
+	 * OK, we couldn't figure it out by using a specialized ioctl,
+	 * which is generally the best way.  So do binary search to
+	 * find the size of the partition.
+	 */
+	low = 0;
+	for (high = 1024; valid_offset(fd, high); high *= 2)
+		low = high;
+	while (low < high - 1)
+	{
+		const blkid_loff_t mid = (low + high) / 2;
+
+		if (valid_offset(fd, mid))
+			low = mid;
+		else
+			high = mid;
+	}
+	return low + 1;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_loff_t bytes;
+	int	fd;
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s device\n"
+			"Determine the size of a device\n", argv[0]);
+		return 1;
+	}
+
+	if ((fd = open(argv[1], O_RDONLY)) < 0)
+		perror(argv[0]);
+
+	bytes = blkid_get_dev_size(fd);
+	printf("Device %s has %lld 1k blocks.\n", argv[1], bytes >> 10);
+
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/cache.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/cache.c
new file mode 100644
index 0000000..d1d2914
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/cache.c
@@ -0,0 +1,125 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cache.c - allocation/initialization/free routines for cache
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "blkidP.h"
+
+int blkid_debug_mask = 0;
+
+int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
+{
+	blkid_cache cache;
+
+#ifdef CONFIG_BLKID_DEBUG
+	if (!(blkid_debug_mask & DEBUG_INIT)) {
+		char *dstr = getenv("BLKID_DEBUG");
+
+		if (dstr)
+			blkid_debug_mask = strtoul(dstr, 0, 0);
+		blkid_debug_mask |= DEBUG_INIT;
+	}
+#endif
+
+	DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n",
+				filename ? filename : "default cache"));
+
+	cache = xzalloc(sizeof(struct blkid_struct_cache));
+
+	INIT_LIST_HEAD(&cache->bic_devs);
+	INIT_LIST_HEAD(&cache->bic_tags);
+
+	if (filename && !strlen(filename))
+		filename = 0;
+	if (!filename && (getuid() == geteuid()))
+		filename = getenv("BLKID_FILE");
+	if (!filename)
+		filename = BLKID_CACHE_FILE;
+	cache->bic_filename = blkid_strdup(filename);
+
+	blkid_read_cache(cache);
+
+	*ret_cache = cache;
+	return 0;
+}
+
+void blkid_put_cache(blkid_cache cache)
+{
+	if (!cache)
+		return;
+
+	(void) blkid_flush_cache(cache);
+
+	DBG(DEBUG_CACHE, printf("freeing cache struct\n"));
+
+	/* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */
+
+	while (!list_empty(&cache->bic_devs)) {
+		blkid_dev dev = list_entry(cache->bic_devs.next,
+					   struct blkid_struct_dev,
+					    bid_devs);
+		blkid_free_dev(dev);
+	}
+
+	while (!list_empty(&cache->bic_tags)) {
+		blkid_tag tag = list_entry(cache->bic_tags.next,
+					   struct blkid_struct_tag,
+					   bit_tags);
+
+		while (!list_empty(&tag->bit_names)) {
+			blkid_tag bad = list_entry(tag->bit_names.next,
+						   struct blkid_struct_tag,
+						   bit_names);
+
+			DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n",
+						bad->bit_name, bad->bit_val));
+			blkid_free_tag(bad);
+		}
+		blkid_free_tag(tag);
+	}
+	free(cache->bic_filename);
+
+	free(cache);
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_debug_mask = DEBUG_ALL;
+	if ((argc > 2)) {
+		fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
+		exit(1);
+	}
+
+	if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
+		fprintf(stderr, "error %d parsing cache file %s\n", ret,
+			argv[1] ? argv[1] : BLKID_CACHE_FILE);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if ((ret = blkid_probe_all(cache) < 0))
+		fprintf(stderr, "error probing devices\n");
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/dev.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/dev.c
new file mode 100644
index 0000000..bb0cc91
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/dev.c
@@ -0,0 +1,213 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dev.c - allocation/initialization/free routines for dev
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "blkidP.h"
+
+blkid_dev blkid_new_dev(void)
+{
+	blkid_dev dev;
+
+	dev = xzalloc(sizeof(struct blkid_struct_dev));
+
+	INIT_LIST_HEAD(&dev->bid_devs);
+	INIT_LIST_HEAD(&dev->bid_tags);
+
+	return dev;
+}
+
+void blkid_free_dev(blkid_dev dev)
+{
+	if (!dev)
+		return;
+
+	DBG(DEBUG_DEV,
+	    printf("  freeing dev %s (%s)\n", dev->bid_name, dev->bid_type));
+	DBG(DEBUG_DEV, blkid_debug_dump_dev(dev));
+
+	list_del(&dev->bid_devs);
+	while (!list_empty(&dev->bid_tags)) {
+		blkid_tag tag = list_entry(dev->bid_tags.next,
+					   struct blkid_struct_tag,
+					   bit_tags);
+		blkid_free_tag(tag);
+	}
+	if (dev->bid_name)
+		free(dev->bid_name);
+	free(dev);
+}
+
+/*
+ * Given a blkid device, return its name
+ */
+const char *blkid_dev_devname(blkid_dev dev)
+{
+	return dev->bid_name;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_dev(blkid_dev dev)
+{
+	struct list_head *p;
+
+	if (!dev) {
+		printf("  dev: NULL\n");
+		return;
+	}
+
+	printf("  dev: name = %s\n", dev->bid_name);
+	printf("  dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno);
+	printf("  dev: TIME=\"%lu\"\n", dev->bid_time);
+	printf("  dev: PRI=\"%d\"\n", dev->bid_pri);
+	printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (tag)
+			printf("    tag: %s=\"%s\"\n", tag->bit_name,
+			       tag->bit_val);
+		else
+			printf("    tag: NULL\n");
+	}
+	bb_putchar('\n');
+}
+#endif
+
+/*
+ * dev iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation.  I'm not convinced I want
+ * to keep list.h in the long term, anyway.  It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application.  [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all devices in a blkid cache
+ */
+#define DEV_ITERATE_MAGIC	0x01a5284c
+
+struct blkid_struct_dev_iterate {
+	int			magic;
+	blkid_cache		cache;
+	struct list_head	*p;
+};
+
+blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+{
+	blkid_dev_iterate	iter;
+
+	iter = xmalloc(sizeof(struct blkid_struct_dev_iterate));
+	iter->magic = DEV_ITERATE_MAGIC;
+	iter->cache = cache;
+	iter->p	= cache->bic_devs.next;
+	return iter;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_dev_next(blkid_dev_iterate iter,
+			  blkid_dev *dev)
+{
+	*dev = 0;
+	if (!iter || iter->magic != DEV_ITERATE_MAGIC ||
+	    iter->p == &iter->cache->bic_devs)
+		return -1;
+	*dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs);
+	iter->p = iter->p->next;
+	return 0;
+}
+
+void blkid_dev_iterate_end(blkid_dev_iterate iter)
+{
+	if (!iter || iter->magic != DEV_ITERATE_MAGIC)
+		return;
+	iter->magic = 0;
+	free(iter);
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+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);
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	blkid_dev_iterate	iter;
+	blkid_cache		cache = NULL;
+	blkid_dev		dev;
+	int			c, ret;
+	char			*tmp;
+	char			*file = NULL;
+	char			*search_type = NULL;
+	char			*search_value = NULL;
+
+	while ((c = getopt (argc, argv, "m:f:")) != EOF)
+		switch (c) {
+		case 'f':
+			file = optarg;
+			break;
+		case 'm':
+			blkid_debug_mask = strtoul (optarg, &tmp, 0);
+			if (*tmp) {
+				fprintf(stderr, "Invalid debug mask: %d\n",
+					optarg);
+				exit(1);
+			}
+			break;
+		case '?':
+			usage(argv[0]);
+		}
+	if (argc >= optind+2) {
+		search_type = argv[optind];
+		search_value = argv[optind+1];
+		optind += 2;
+	}
+	if (argc != optind)
+		usage(argv[0]);
+
+	if ((ret = blkid_get_cache(&cache, file)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+
+	iter = blkid_dev_iterate_begin(cache);
+	if (search_type)
+		blkid_dev_set_search(iter, search_type, search_value);
+	while (blkid_dev_next(iter, &dev) == 0) {
+		printf("Device: %s\n", blkid_dev_devname(dev));
+	}
+	blkid_dev_iterate_end(iter);
+
+
+	blkid_put_cache(cache);
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/devname.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/devname.c
new file mode 100644
index 0000000..fad92cb
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/devname.c
@@ -0,0 +1,367 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * devname.c - get a dev by its device inode name
+ *
+ * Copyright (C) Andries Brouwer
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#include <time.h>
+
+#include "blkidP.h"
+
+/*
+ * Find a dev struct in the cache by device name, if available.
+ *
+ * If there is no entry with the specified device name, and the create
+ * flag is set, then create an empty device entry.
+ */
+blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+{
+	blkid_dev dev = NULL, tmp;
+	struct list_head *p;
+
+	if (!cache || !devname)
+		return NULL;
+
+	list_for_each(p, &cache->bic_devs) {
+		tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (strcmp(tmp->bid_name, devname))
+			continue;
+
+		DBG(DEBUG_DEVNAME,
+		    printf("found devname %s in cache\n", tmp->bid_name));
+		dev = tmp;
+		break;
+	}
+
+	if (!dev && (flags & BLKID_DEV_CREATE)) {
+		dev = blkid_new_dev();
+		if (!dev)
+			return NULL;
+		dev->bid_name = blkid_strdup(devname);
+		dev->bid_cache = cache;
+		list_add_tail(&dev->bid_devs, &cache->bic_devs);
+		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+	}
+
+	if (flags & BLKID_DEV_VERIFY)
+		dev = blkid_verify(cache, dev);
+	return dev;
+}
+
+/*
+ * Probe a single block device to add to the device cache.
+ */
+static void probe_one(blkid_cache cache, const char *ptname,
+		      dev_t devno, int pri)
+{
+	blkid_dev dev = NULL;
+	struct list_head *p;
+	const char **dir;
+	char *devname = NULL;
+
+	/* See if we already have this device number in the cache. */
+	list_for_each(p, &cache->bic_devs) {
+		blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
+					   bid_devs);
+		if (tmp->bid_devno == devno) {
+			dev = blkid_verify(cache, tmp);
+			break;
+		}
+	}
+	if (dev && dev->bid_devno == devno)
+		goto set_pri;
+
+	/*
+	 * Take a quick look at /dev/ptname for the device number.  We check
+	 * all of the likely device directories.  If we don't find it, or if
+	 * the stat information doesn't check out, use blkid_devno_to_devname()
+	 * to find it via an exhaustive search for the device major/minor.
+	 */
+	for (dir = blkid_devdirs; *dir; dir++) {
+		struct stat st;
+		char device[256];
+
+		sprintf(device, "%s/%s", *dir, ptname);
+		if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
+		    dev->bid_devno == devno)
+			goto set_pri;
+
+		if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) &&
+		    st.st_rdev == devno) {
+			devname = blkid_strdup(device);
+			break;
+		}
+	}
+	if (!devname) {
+		devname = blkid_devno_to_devname(devno);
+		if (!devname)
+			return;
+	}
+	dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
+	free(devname);
+
+set_pri:
+	if (!pri && !strncmp(ptname, "md", 2))
+		pri = BLKID_PRI_MD;
+	if (dev)
+		dev->bid_pri = pri;
+}
+
+#define PROC_PARTITIONS "/proc/partitions"
+#define VG_DIR		"/proc/lvm/VGs"
+
+/*
+ * This function initializes the UUID cache with devices from the LVM
+ * proc hierarchy.  We currently depend on the names of the LVM
+ * hierarchy giving us the device structure in /dev.  (XXX is this a
+ * safe thing to do?)
+ */
+#ifdef VG_DIR
+#include <dirent.h>
+static dev_t lvm_get_devno(const char *lvm_device)
+{
+	FILE *lvf;
+	char buf[1024];
+	int ma, mi;
+	dev_t ret = 0;
+
+	DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
+	if ((lvf = fopen_for_read(lvm_device)) == NULL) {
+		DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
+					  strerror(errno)));
+		return 0;
+	}
+
+	while (fgets(buf, sizeof(buf), lvf)) {
+		if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
+			ret = makedev(ma, mi);
+			break;
+		}
+	}
+	fclose(lvf);
+
+	return ret;
+}
+
+static void lvm_probe_all(blkid_cache cache)
+{
+	DIR		*vg_list;
+	struct dirent	*vg_iter;
+	int		vg_len = strlen(VG_DIR);
+	dev_t		dev;
+
+	if ((vg_list = opendir(VG_DIR)) == NULL)
+		return;
+
+	DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
+
+	while ((vg_iter = readdir(vg_list)) != NULL) {
+		DIR		*lv_list;
+		char		*vdirname;
+		char		*vg_name;
+		struct dirent	*lv_iter;
+
+		vg_name = vg_iter->d_name;
+		if (LONE_CHAR(vg_name, '.') || !strcmp(vg_name, ".."))
+			continue;
+		vdirname = xmalloc(vg_len + strlen(vg_name) + 8);
+		sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
+
+		lv_list = opendir(vdirname);
+		free(vdirname);
+		if (lv_list == NULL)
+			continue;
+
+		while ((lv_iter = readdir(lv_list)) != NULL) {
+			char		*lv_name, *lvm_device;
+
+			lv_name = lv_iter->d_name;
+			if (LONE_CHAR(lv_name, '.') || !strcmp(lv_name, ".."))
+				continue;
+
+			lvm_device = xmalloc(vg_len + strlen(vg_name) +
+					    strlen(lv_name) + 8);
+			sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
+				lv_name);
+			dev = lvm_get_devno(lvm_device);
+			sprintf(lvm_device, "%s/%s", vg_name, lv_name);
+			DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
+						  lvm_device,
+						  (unsigned int) dev));
+			probe_one(cache, lvm_device, dev, BLKID_PRI_LVM);
+			free(lvm_device);
+		}
+		closedir(lv_list);
+	}
+	closedir(vg_list);
+}
+#endif
+
+#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
+
+static int
+evms_probe_all(blkid_cache cache)
+{
+	char line[100];
+	int ma, mi, sz, num = 0;
+	FILE *procpt;
+	char device[110];
+
+	procpt = fopen_for_read(PROC_EVMS_VOLUMES);
+	if (!procpt)
+		return 0;
+	while (fgets(line, sizeof(line), procpt)) {
+		if (sscanf(line, " %d %d %d %*s %*s %[^\n ]",
+			    &ma, &mi, &sz, device) != 4)
+			continue;
+
+		DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
+					  device, ma, mi));
+
+		probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS);
+		num++;
+	}
+	fclose(procpt);
+	return num;
+}
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+int blkid_probe_all(blkid_cache cache)
+{
+	FILE *proc;
+	char line[1024];
+	char ptname0[128], ptname1[128], *ptname = NULL;
+	char *ptnames[2];
+	dev_t devs[2];
+	int ma, mi;
+	unsigned long long sz;
+	int lens[2] = { 0, 0 };
+	int which = 0, last = 0;
+
+	ptnames[0] = ptname0;
+	ptnames[1] = ptname1;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+	    time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL)
+		return 0;
+
+	blkid_read_cache(cache);
+	evms_probe_all(cache);
+#ifdef VG_DIR
+	lvm_probe_all(cache);
+#endif
+
+	proc = fopen_for_read(PROC_PARTITIONS);
+	if (!proc)
+		return -BLKID_ERR_PROC;
+
+	while (fgets(line, sizeof(line), proc)) {
+		last = which;
+		which ^= 1;
+		ptname = ptnames[which];
+
+		if (sscanf(line, " %d %d %llu %128[^\n ]",
+			   &ma, &mi, &sz, ptname) != 4)
+			continue;
+		devs[which] = makedev(ma, mi);
+
+		DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
+
+		/* Skip whole disk devs unless they have no partitions
+		 * If we don't have a partition on this dev, also
+		 * check previous dev to see if it didn't have a partn.
+		 * heuristic: partition name ends in a digit.
+		 *
+		 * Skip extended partitions.
+		 * heuristic: size is 1
+		 *
+		 * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
+		 */
+
+		lens[which] = strlen(ptname);
+		if (isdigit(ptname[lens[which] - 1])) {
+			DBG(DEBUG_DEVNAME,
+			    printf("partition dev %s, devno 0x%04X\n",
+				   ptname, (unsigned int) devs[which]));
+
+			if (sz > 1)
+				probe_one(cache, ptname, devs[which], 0);
+			lens[which] = 0;
+			lens[last] = 0;
+		} else if (lens[last] && strncmp(ptnames[last], ptname,
+						 lens[last])) {
+			DBG(DEBUG_DEVNAME,
+			    printf("whole dev %s, devno 0x%04X\n",
+				   ptnames[last], (unsigned int) devs[last]));
+			probe_one(cache, ptnames[last], devs[last], 0);
+			lens[last] = 0;
+		}
+	}
+
+	/* Handle the last device if it wasn't partitioned */
+	if (lens[which])
+		probe_one(cache, ptname, devs[which], 0);
+
+	fclose(proc);
+
+	cache->bic_time = time(NULL);
+	cache->bic_flags |= BLKID_BIC_FL_PROBED;
+	blkid_flush_cache(cache);
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_debug_mask = DEBUG_ALL;
+	if (argc != 1) {
+		fprintf(stderr, "Usage: %s\n"
+			"Probe all devices and exit\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if (blkid_probe_all(cache) < 0)
+		printf("%s: error probing devices\n", argv[0]);
+
+	blkid_put_cache(cache);
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/devno.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/devno.c
new file mode 100644
index 0000000..ae326f8
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/devno.c
@@ -0,0 +1,222 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * devno.c - find a particular device by its device number (major/minor)
+ *
+ * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/stat.h>
+#include <dirent.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#include "blkidP.h"
+
+struct dir_list {
+	char	*name;
+	struct dir_list *next;
+};
+
+char *blkid_strndup(const char *s, int length)
+{
+	char *ret;
+
+	if (!s)
+		return NULL;
+
+	if (!length)
+		length = strlen(s);
+
+	ret = xmalloc(length + 1);
+	strncpy(ret, s, length);
+	ret[length] = '\0';
+	return ret;
+}
+
+char *blkid_strdup(const char *s)
+{
+	return blkid_strndup(s, 0);
+}
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *name, struct dir_list **list)
+{
+	struct dir_list *dp;
+
+	dp = xmalloc(sizeof(struct dir_list));
+	dp->name = blkid_strdup(name);
+	dp->next = *list;
+	*list = dp;
+}
+
+/*
+ * This function frees a directory list
+ */
+static void free_dirlist(struct dir_list **list)
+{
+	struct dir_list *dp, *next;
+
+	for (dp = *list; dp; dp = next) {
+		next = dp->next;
+		free(dp->name);
+		free(dp);
+	}
+	*list = NULL;
+}
+
+static void scan_dir(char *dir_name, dev_t devno, struct dir_list **list,
+			    char **devname)
+{
+	DIR	*dir;
+	struct dirent *dp;
+	char	path[1024];
+	int	dirlen;
+	struct stat st;
+
+	if ((dir = opendir(dir_name)) == NULL)
+		return;
+	dirlen = strlen(dir_name) + 2;
+	while ((dp = readdir(dir)) != 0) {
+		if (dirlen + strlen(dp->d_name) >= sizeof(path))
+			continue;
+
+		if (dp->d_name[0] == '.' &&
+		    ((dp->d_name[1] == 0) ||
+		     ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+			continue;
+
+		sprintf(path, "%s/%s", dir_name, dp->d_name);
+		if (stat(path, &st) < 0)
+			continue;
+
+		if (S_ISDIR(st.st_mode))
+			add_to_dirlist(path, list);
+		else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
+			*devname = blkid_strdup(path);
+			DBG(DEBUG_DEVNO,
+			    printf("found 0x%llx at %s (%p)\n", devno,
+				   path, *devname));
+			break;
+		}
+	}
+	closedir(dir);
+}
+
+/* Directories where we will try to search for device numbers */
+const char *blkid_devdirs[] = { "/devices", "/devfs", "/dev", NULL };
+
+/*
+ * This function finds the pathname to a block device with a given
+ * device number.  It returns a pointer to allocated memory to the
+ * pathname on success, and NULL on failure.
+ */
+char *blkid_devno_to_devname(dev_t devno)
+{
+	struct dir_list *list = NULL, *new_list = NULL;
+	char *devname = NULL;
+	const char **dir;
+
+	/*
+	 * Add the starting directories to search in reverse order of
+	 * importance, since we are using a stack...
+	 */
+	for (dir = blkid_devdirs; *dir; dir++)
+		add_to_dirlist(*dir, &list);
+
+	while (list) {
+		struct dir_list *current = list;
+
+		list = list->next;
+		DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
+		scan_dir(current->name, devno, &new_list, &devname);
+		free(current->name);
+		free(current);
+		if (devname)
+			break;
+		/*
+		 * If we're done checking at this level, descend to
+		 * the next level of subdirectories. (breadth-first)
+		 */
+		if (list == NULL) {
+			list = new_list;
+			new_list = NULL;
+		}
+	}
+	free_dirlist(&list);
+	free_dirlist(&new_list);
+
+	if (!devname) {
+		DBG(DEBUG_DEVNO,
+		    printf("blkid: cannot find devno 0x%04lx\n",
+			   (unsigned long) devno));
+	} else {
+		DBG(DEBUG_DEVNO,
+		    printf("found devno 0x%04llx as %s\n", devno, devname));
+	}
+
+
+	return devname;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+	char	*devname, *tmp;
+	int	major, minor;
+	dev_t	devno;
+	const char *errmsg = "Cannot parse %s: %s\n";
+
+	blkid_debug_mask = DEBUG_ALL;
+	if ((argc != 2) && (argc != 3)) {
+		fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
+			"Resolve a device number to a device name\n",
+			argv[0], argv[0]);
+		exit(1);
+	}
+	if (argc == 2) {
+		devno = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "device number", argv[1]);
+			exit(1);
+		}
+	} else {
+		major = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "major number", argv[1]);
+			exit(1);
+		}
+		minor = strtoul(argv[2], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "minor number", argv[2]);
+			exit(1);
+		}
+		devno = makedev(major, minor);
+	}
+	printf("Looking for device 0x%04Lx\n", devno);
+	devname = blkid_devno_to_devname(devno);
+	free(devname);
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/list.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/list.c
new file mode 100644
index 0000000..04d61a1
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/list.c
@@ -0,0 +1,110 @@
+/* vi: set sw=4 ts=4: */
+
+#include "list.h"
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+void __list_add(struct list_head * add,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = add;
+	add->next = next;
+	add->prev = prev;
+	prev->next = add;
+}
+
+/*
+ * list_add - add a new entry
+ * @add:	new entry to be added
+ * @head:	list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+void list_add(struct list_head *add, struct list_head *head)
+{
+	__list_add(add, head, head->next);
+}
+
+/*
+ * list_add_tail - add a new entry
+ * @add:	new entry to be added
+ * @head:	list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+void list_add_tail(struct list_head *add, struct list_head *head)
+{
+	__list_add(add, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/*
+ * list_del - deletes entry from list.
+ * @entry:	the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+/*
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry:	the element to delete from the list.
+ */
+void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/*
+ * list_empty - tests whether a list is empty
+ * @head:	the list to test.
+ */
+int list_empty(struct list_head *head)
+{
+	return head->next == head;
+}
+
+/*
+ * list_splice - join two lists
+ * @list:	the new list to add.
+ * @head:	the place to add it in the first list.
+ */
+void list_splice(struct list_head *list, struct list_head *head)
+{
+	struct list_head *first = list->next;
+
+	if (first != list) {
+		struct list_head *last = list->prev;
+		struct list_head *at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/list.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/list.h
new file mode 100644
index 0000000..a24baaa
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/list.h
@@ -0,0 +1,73 @@
+/* vi: set sw=4 ts=4: */
+#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD)
+#define BLKID_LIST_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+void __list_add(struct list_head * add, struct list_head * prev,	struct list_head * next);
+void list_add(struct list_head *add, struct list_head *head);
+void list_add_tail(struct list_head *add, struct list_head *head);
+void __list_del(struct list_head * prev, struct list_head * next);
+void list_del(struct list_head *entry);
+void list_del_init(struct list_head *entry);
+int list_empty(struct list_head *head);
+void list_splice(struct list_head *list, struct list_head *head);
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ *                      pos after the body is done (in case it is freed)
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @pnext:	the &struct list_head to use as a pointer to the next item.
+ * @head:	the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+	for (pos = (head)->next, pnext = pos->next; pos != (head); \
+	     pos = pnext, pnext = pos->next)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/probe.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/probe.c
new file mode 100644
index 0000000..77bfc73
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/probe.c
@@ -0,0 +1,721 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * probe.c - identify a block device by its contents, and return a dev
+ *           struct with the details
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+#include "../uuid/uuid.h"
+#include "probe.h"
+
+/*
+ * This is a special case code to check for an MDRAID device.  We do
+ * this special since it requires checking for a superblock at the end
+ * of the device.
+ */
+static int check_mdraid(int fd, unsigned char *ret_uuid)
+{
+	struct mdp_superblock_s *md;
+	blkid_loff_t		offset;
+	char			buf[4096];
+
+	if (fd < 0)
+		return -BLKID_ERR_PARAM;
+
+	offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536;
+
+	if (blkid_llseek(fd, offset, 0) < 0 ||
+	    read(fd, buf, 4096) != 4096)
+		return -BLKID_ERR_IO;
+
+	/* Check for magic number */
+	if (memcmp("\251+N\374", buf, 4))
+		return -BLKID_ERR_PARAM;
+
+	if (!ret_uuid)
+		return 0;
+	*ret_uuid = 0;
+
+	/* The MD UUID is not contiguous in the superblock, make it so */
+	md = (struct mdp_superblock_s *)buf;
+	if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) {
+		memcpy(ret_uuid, &md->set_uuid0, 4);
+		memcpy(ret_uuid, &md->set_uuid1, 12);
+	}
+	return 0;
+}
+
+static void set_uuid(blkid_dev dev, uuid_t uuid)
+{
+	char	str[37];
+
+	if (!uuid_is_null(uuid)) {
+		uuid_unparse(uuid, str);
+		blkid_set_tag(dev, "UUID", str, sizeof(str));
+	}
+}
+
+static void get_ext2_info(blkid_dev dev, unsigned char *buf)
+{
+	struct ext2_super_block *es = (struct ext2_super_block *) buf;
+	const char *label = NULL;
+
+	DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
+		   blkid_le32(es->s_feature_compat),
+		   blkid_le32(es->s_feature_incompat),
+		   blkid_le32(es->s_feature_ro_compat)));
+
+	if (strlen(es->s_volume_name))
+		label = es->s_volume_name;
+	blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name));
+
+	set_uuid(dev, es->s_uuid);
+}
+
+static int probe_ext3(int fd __BLKID_ATTR((unused)),
+		      blkid_cache cache __BLKID_ATTR((unused)),
+		      blkid_dev dev,
+		      const struct blkid_magic *id __BLKID_ATTR((unused)),
+		      unsigned char *buf)
+{
+	struct ext2_super_block *es;
+
+	es = (struct ext2_super_block *)buf;
+
+	/* Distinguish between jbd and ext2/3 fs */
+	if (blkid_le32(es->s_feature_incompat) &
+	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return -BLKID_ERR_PARAM;
+
+	/* Distinguish between ext3 and ext2 */
+	if (!(blkid_le32(es->s_feature_compat) &
+	      EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+		return -BLKID_ERR_PARAM;
+
+	get_ext2_info(dev, buf);
+
+	blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2"));
+
+	return 0;
+}
+
+static int probe_ext2(int fd __BLKID_ATTR((unused)),
+		      blkid_cache cache __BLKID_ATTR((unused)),
+		      blkid_dev dev,
+		      const struct blkid_magic *id __BLKID_ATTR((unused)),
+		      unsigned char *buf)
+{
+	struct ext2_super_block *es;
+
+	es = (struct ext2_super_block *)buf;
+
+	/* Distinguish between jbd and ext2/3 fs */
+	if (blkid_le32(es->s_feature_incompat) &
+	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return -BLKID_ERR_PARAM;
+
+	get_ext2_info(dev, buf);
+
+	return 0;
+}
+
+static int probe_jbd(int fd __BLKID_ATTR((unused)),
+		     blkid_cache cache __BLKID_ATTR((unused)),
+		     blkid_dev dev,
+		     const struct blkid_magic *id __BLKID_ATTR((unused)),
+		     unsigned char *buf)
+{
+	struct ext2_super_block *es = (struct ext2_super_block *) buf;
+
+	if (!(blkid_le32(es->s_feature_incompat) &
+	      EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
+		return -BLKID_ERR_PARAM;
+
+	get_ext2_info(dev, buf);
+
+	return 0;
+}
+
+static int probe_vfat(int fd __BLKID_ATTR((unused)),
+		      blkid_cache cache __BLKID_ATTR((unused)),
+		      blkid_dev dev,
+		      const struct blkid_magic *id __BLKID_ATTR((unused)),
+		      unsigned char *buf)
+{
+	struct vfat_super_block *vs;
+	char serno[10];
+	const char *label = NULL;
+	int label_len = 0;
+
+	vs = (struct vfat_super_block *)buf;
+
+	if (strncmp(vs->vs_label, "NO NAME", 7)) {
+		char *end = vs->vs_label + sizeof(vs->vs_label) - 1;
+
+		while (*end == ' ' && end >= vs->vs_label)
+			--end;
+		if (end >= vs->vs_label) {
+			label = vs->vs_label;
+			label_len = end - vs->vs_label + 1;
+		}
+	}
+
+	/* We can't just print them as %04X, because they are unaligned */
+	sprintf(serno, "%02X%02X-%02X%02X", vs->vs_serno[3], vs->vs_serno[2],
+		vs->vs_serno[1], vs->vs_serno[0]);
+	blkid_set_tag(dev, "LABEL", label, label_len);
+	blkid_set_tag(dev, "UUID", serno, sizeof(serno));
+
+	return 0;
+}
+
+static int probe_msdos(int fd __BLKID_ATTR((unused)),
+		       blkid_cache cache __BLKID_ATTR((unused)),
+		       blkid_dev dev,
+		       const struct blkid_magic *id __BLKID_ATTR((unused)),
+		       unsigned char *buf)
+{
+	struct msdos_super_block *ms = (struct msdos_super_block *) buf;
+	char serno[10];
+	const char *label = NULL;
+	int label_len = 0;
+
+	if (strncmp(ms->ms_label, "NO NAME", 7)) {
+		char *end = ms->ms_label + sizeof(ms->ms_label) - 1;
+
+		while (*end == ' ' && end >= ms->ms_label)
+			--end;
+		if (end >= ms->ms_label) {
+			label = ms->ms_label;
+			label_len = end - ms->ms_label + 1;
+		}
+	}
+
+	/* We can't just print them as %04X, because they are unaligned */
+	sprintf(serno, "%02X%02X-%02X%02X", ms->ms_serno[3], ms->ms_serno[2],
+		ms->ms_serno[1], ms->ms_serno[0]);
+	blkid_set_tag(dev, "UUID", serno, 0);
+	blkid_set_tag(dev, "LABEL", label, label_len);
+	blkid_set_tag(dev, "SEC_TYPE", "msdos", sizeof("msdos"));
+
+	return 0;
+}
+
+static int probe_xfs(int fd __BLKID_ATTR((unused)),
+		     blkid_cache cache __BLKID_ATTR((unused)),
+		     blkid_dev dev,
+		     const struct blkid_magic *id __BLKID_ATTR((unused)),
+		     unsigned char *buf)
+{
+	struct xfs_super_block *xs;
+	const char *label = NULL;
+
+	xs = (struct xfs_super_block *)buf;
+
+	if (strlen(xs->xs_fname))
+		label = xs->xs_fname;
+	blkid_set_tag(dev, "LABEL", label, sizeof(xs->xs_fname));
+	set_uuid(dev, xs->xs_uuid);
+	return 0;
+}
+
+static int probe_reiserfs(int fd __BLKID_ATTR((unused)),
+			  blkid_cache cache __BLKID_ATTR((unused)),
+			  blkid_dev dev,
+			  const struct blkid_magic *id, unsigned char *buf)
+{
+	struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf;
+	unsigned int blocksize;
+	const char *label = NULL;
+
+	blocksize = blkid_le16(rs->rs_blocksize);
+
+	/* If the superblock is inside the journal, we have the wrong one */
+	if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block))
+		return -BLKID_ERR_BIG;
+
+	/* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */
+	if (!strcmp(id->bim_magic, "ReIsEr2Fs") ||
+	    !strcmp(id->bim_magic, "ReIsEr3Fs")) {
+		if (strlen(rs->rs_label))
+			label = rs->rs_label;
+		set_uuid(dev, rs->rs_uuid);
+	}
+	blkid_set_tag(dev, "LABEL", label, sizeof(rs->rs_label));
+
+	return 0;
+}
+
+static int probe_jfs(int fd __BLKID_ATTR((unused)),
+		     blkid_cache cache __BLKID_ATTR((unused)),
+		     blkid_dev dev,
+		     const struct blkid_magic *id __BLKID_ATTR((unused)),
+		     unsigned char *buf)
+{
+	struct jfs_super_block *js;
+	const char *label = NULL;
+
+	js = (struct jfs_super_block *)buf;
+
+	if (strlen((char *) js->js_label))
+		label = (char *) js->js_label;
+	blkid_set_tag(dev, "LABEL", label, sizeof(js->js_label));
+	set_uuid(dev, js->js_uuid);
+	return 0;
+}
+
+static int probe_romfs(int fd __BLKID_ATTR((unused)),
+		       blkid_cache cache __BLKID_ATTR((unused)),
+		       blkid_dev dev,
+		       const struct blkid_magic *id __BLKID_ATTR((unused)),
+		       unsigned char *buf)
+{
+	struct romfs_super_block *ros;
+	const char *label = NULL;
+
+	ros = (struct romfs_super_block *)buf;
+
+	if (strlen((char *) ros->ros_volume))
+		label = (char *) ros->ros_volume;
+	blkid_set_tag(dev, "LABEL", label, 0);
+	return 0;
+}
+
+static int probe_cramfs(int fd __BLKID_ATTR((unused)),
+		       blkid_cache cache __BLKID_ATTR((unused)),
+		       blkid_dev dev,
+		       const struct blkid_magic *id __BLKID_ATTR((unused)),
+		       unsigned char *buf)
+{
+	struct cramfs_super_block *csb;
+	const char *label = NULL;
+
+	csb = (struct cramfs_super_block *)buf;
+
+	if (strlen((char *) csb->name))
+		label = (char *) csb->name;
+	blkid_set_tag(dev, "LABEL", label, 0);
+	return 0;
+}
+
+static int probe_swap0(int fd __BLKID_ATTR((unused)),
+		       blkid_cache cache __BLKID_ATTR((unused)),
+		       blkid_dev dev,
+		       const struct blkid_magic *id __BLKID_ATTR((unused)),
+		       unsigned char *buf __BLKID_ATTR((unused)))
+{
+	blkid_set_tag(dev, "UUID", 0, 0);
+	blkid_set_tag(dev, "LABEL", 0, 0);
+	return 0;
+}
+
+static int probe_swap1(int fd,
+		       blkid_cache cache __BLKID_ATTR((unused)),
+		       blkid_dev dev,
+		       const struct blkid_magic *id __BLKID_ATTR((unused)),
+		       unsigned char *buf __BLKID_ATTR((unused)))
+{
+	struct swap_id_block *sws;
+
+	probe_swap0(fd, cache, dev, id, buf);
+	/*
+	 * Version 1 swap headers are always located at offset of 1024
+	 * bytes, although the swap signature itself is located at the
+	 * end of the page (which may vary depending on hardware
+	 * pagesize).
+	 */
+	if (lseek(fd, 1024, SEEK_SET) < 0) return 1;
+	sws = xmalloc(1024);
+	if (read(fd, sws, 1024) != 1024) {
+		free(sws);
+		return 1;
+	}
+
+	/* arbitrary sanity check.. is there any garbage down there? */
+	if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0)  {
+		if (sws->sws_volume[0])
+			blkid_set_tag(dev, "LABEL", (const char*)sws->sws_volume,
+				      sizeof(sws->sws_volume));
+		if (sws->sws_uuid[0])
+			set_uuid(dev, sws->sws_uuid);
+	}
+	free(sws);
+
+	return 0;
+}
+
+static const char
+* const udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02",
+		 "NSR03", "TEA01", 0 };
+
+static int probe_udf(int fd, blkid_cache cache __BLKID_ATTR((unused)),
+		     blkid_dev dev __BLKID_ATTR((unused)),
+		     const struct blkid_magic *id __BLKID_ATTR((unused)),
+		     unsigned char *buf __BLKID_ATTR((unused)))
+{
+	int j, bs;
+	struct iso_volume_descriptor isosb;
+	const char *const *m;
+
+	/* determine the block size by scanning in 2K increments
+	   (block sizes larger than 2K will be null padded) */
+	for (bs = 1; bs < 16; bs++) {
+		lseek(fd, bs*2048+32768, SEEK_SET);
+		if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb))
+			return 1;
+		if (isosb.id[0])
+			break;
+	}
+
+	/* Scan up to another 64 blocks looking for additional VSD's */
+	for (j = 1; j < 64; j++) {
+		if (j > 1) {
+			lseek(fd, j*bs*2048+32768, SEEK_SET);
+			if (read(fd, (char *)&isosb, sizeof(isosb))
+			    != sizeof(isosb))
+				return 1;
+		}
+		/* If we find NSR0x then call it udf:
+		   NSR01 for UDF 1.00
+		   NSR02 for UDF 1.50
+		   NSR03 for UDF 2.00 */
+		if (!strncmp(isosb.id, "NSR0", 4))
+			return 0;
+		for (m = udf_magic; *m; m++)
+			if (!strncmp(*m, isosb.id, 5))
+				break;
+		if (*m == 0)
+			return 1;
+	}
+	return 1;
+}
+
+static int probe_ocfs(int fd __BLKID_ATTR((unused)),
+		      blkid_cache cache __BLKID_ATTR((unused)),
+		      blkid_dev dev,
+		      const struct blkid_magic *id __BLKID_ATTR((unused)),
+		      unsigned char *buf)
+{
+	struct ocfs_volume_header ovh;
+	struct ocfs_volume_label ovl;
+	__u32 major;
+
+	memcpy(&ovh, buf, sizeof(ovh));
+	memcpy(&ovl, buf+512, sizeof(ovl));
+
+	major = ocfsmajor(ovh);
+	if (major == 1)
+		blkid_set_tag(dev, "SEC_TYPE", "ocfs1", sizeof("ocfs1"));
+	else if (major >= 9)
+		blkid_set_tag(dev, "SEC_TYPE", "ntocfs", sizeof("ntocfs"));
+
+	blkid_set_tag(dev, "LABEL", (const char*)ovl.label, ocfslabellen(ovl));
+	blkid_set_tag(dev, "MOUNT", (const char*)ovh.mount, ocfsmountlen(ovh));
+	set_uuid(dev, ovl.vol_id);
+	return 0;
+}
+
+static int probe_ocfs2(int fd __BLKID_ATTR((unused)),
+		       blkid_cache cache __BLKID_ATTR((unused)),
+		       blkid_dev dev,
+		       const struct blkid_magic *id __BLKID_ATTR((unused)),
+		       unsigned char *buf)
+{
+	struct ocfs2_super_block *osb;
+
+	osb = (struct ocfs2_super_block *)buf;
+
+	blkid_set_tag(dev, "LABEL", (const char*)osb->s_label, sizeof(osb->s_label));
+	set_uuid(dev, osb->s_uuid);
+	return 0;
+}
+
+static int probe_oracleasm(int fd __BLKID_ATTR((unused)),
+			   blkid_cache cache __BLKID_ATTR((unused)),
+			   blkid_dev dev,
+			   const struct blkid_magic *id __BLKID_ATTR((unused)),
+			   unsigned char *buf)
+{
+	struct oracle_asm_disk_label *dl;
+
+	dl = (struct oracle_asm_disk_label *)buf;
+
+	blkid_set_tag(dev, "LABEL", dl->dl_id, sizeof(dl->dl_id));
+	return 0;
+}
+
+/*
+ * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined
+ * in the type_array table below + bim_kbalign.
+ *
+ * When probing for a lot of magics, we handle everything in 1kB buffers so
+ * that we don't have to worry about reading each combination of block sizes.
+ */
+#define BLKID_BLK_OFFS	64	/* currently reiserfs */
+
+/*
+ * Various filesystem magics that we can check for.  Note that kboff and
+ * sboff are in kilobytes and bytes respectively.  All magics are in
+ * byte strings so we don't worry about endian issues.
+ */
+static const struct blkid_magic type_array[] = {
+/*  type     kboff   sboff len  magic			probe */
+  { "oracleasm", 0,	32,  8, "ORCLDISK",		probe_oracleasm },
+  { "ntfs",      0,      3,  8, "NTFS    ",             0 },
+  { "jbd",	 1,   0x38,  2, "\123\357",		probe_jbd },
+  { "ext3",	 1,   0x38,  2, "\123\357",		probe_ext3 },
+  { "ext2",	 1,   0x38,  2, "\123\357",		probe_ext2 },
+  { "reiserfs",	 8,   0x34,  8, "ReIsErFs",		probe_reiserfs },
+  { "reiserfs", 64,   0x34,  9, "ReIsEr2Fs",		probe_reiserfs },
+  { "reiserfs", 64,   0x34,  9, "ReIsEr3Fs",		probe_reiserfs },
+  { "reiserfs", 64,   0x34,  8, "ReIsErFs",		probe_reiserfs },
+  { "reiserfs",	 8,	20,  8, "ReIsErFs",		probe_reiserfs },
+  { "vfat",      0,   0x52,  5, "MSWIN",                probe_vfat },
+  { "vfat",      0,   0x52,  8, "FAT32   ",             probe_vfat },
+  { "vfat",      0,   0x36,  5, "MSDOS",                probe_msdos },
+  { "vfat",      0,   0x36,  8, "FAT16   ",             probe_msdos },
+  { "vfat",      0,   0x36,  8, "FAT12   ",             probe_msdos },
+  { "minix",     1,   0x10,  2, "\177\023",             0 },
+  { "minix",     1,   0x10,  2, "\217\023",             0 },
+  { "minix",	 1,   0x10,  2, "\150\044",		0 },
+  { "minix",	 1,   0x10,  2, "\170\044",		0 },
+  { "vxfs",	 1,	 0,  4, "\365\374\001\245",	0 },
+  { "xfs",	 0,	 0,  4, "XFSB",			probe_xfs },
+  { "romfs",	 0,	 0,  8, "-rom1fs-",		probe_romfs },
+  { "bfs",	 0,	 0,  4, "\316\372\173\033",	0 },
+  { "cramfs",	 0,	 0,  4, "E=\315\050",		probe_cramfs },
+  { "qnx4",	 0,	 4,  6, "QNX4FS",		0 },
+  { "udf",	32,	 1,  5, "BEA01",		probe_udf },
+  { "udf",	32,	 1,  5, "BOOT2",		probe_udf },
+  { "udf",	32,	 1,  5, "CD001",		probe_udf },
+  { "udf",	32,	 1,  5, "CDW02",		probe_udf },
+  { "udf",	32,	 1,  5, "NSR02",		probe_udf },
+  { "udf",	32,	 1,  5, "NSR03",		probe_udf },
+  { "udf",	32,	 1,  5, "TEA01",		probe_udf },
+  { "iso9660",	32,	 1,  5, "CD001",		0 },
+  { "iso9660",	32,	 9,  5, "CDROM",		0 },
+  { "jfs",	32,	 0,  4, "JFS1",			probe_jfs },
+  { "hfs",	 1,	 0,  2, "BD",			0 },
+  { "ufs",	 8,  0x55c,  4, "T\031\001\000",	0 },
+  { "hpfs",	 8,	 0,  4, "I\350\225\371",	0 },
+  { "sysv",	 0,  0x3f8,  4, "\020~\030\375",	0 },
+  { "swap",	 0,  0xff6, 10, "SWAP-SPACE",		probe_swap0 },
+  { "swap",	 0,  0xff6, 10, "SWAPSPACE2",		probe_swap1 },
+  { "swap",	 0, 0x1ff6, 10, "SWAP-SPACE",		probe_swap0 },
+  { "swap",	 0, 0x1ff6, 10, "SWAPSPACE2",		probe_swap1 },
+  { "swap",	 0, 0x3ff6, 10, "SWAP-SPACE",		probe_swap0 },
+  { "swap",	 0, 0x3ff6, 10, "SWAPSPACE2",		probe_swap1 },
+  { "swap",	 0, 0x7ff6, 10, "SWAP-SPACE",		probe_swap0 },
+  { "swap",	 0, 0x7ff6, 10, "SWAPSPACE2",		probe_swap1 },
+  { "swap",	 0, 0xfff6, 10, "SWAP-SPACE",		probe_swap0 },
+  { "swap",	 0, 0xfff6, 10, "SWAPSPACE2",		probe_swap1 },
+  { "ocfs",	 0,	 8,  9,	"OracleCFS",		probe_ocfs },
+  { "ocfs2",	 1,	 0,  6,	"OCFSV2",		probe_ocfs2 },
+  { "ocfs2",	 2,	 0,  6,	"OCFSV2",		probe_ocfs2 },
+  { "ocfs2",	 4,	 0,  6,	"OCFSV2",		probe_ocfs2 },
+  { "ocfs2",	 8,	 0,  6,	"OCFSV2",		probe_ocfs2 },
+  {   NULL,	 0,	 0,  0, NULL,			NULL }
+};
+
+/*
+ * Verify that the data in dev is consistent with what is on the actual
+ * block device (using the devname field only).  Normally this will be
+ * called when finding items in the cache, but for long running processes
+ * is also desirable to revalidate an item before use.
+ *
+ * If we are unable to revalidate the data, we return the old data and
+ * do not set the BLKID_BID_FL_VERIFIED flag on it.
+ */
+blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev)
+{
+	const struct blkid_magic *id;
+	unsigned char *bufs[BLKID_BLK_OFFS + 1], *buf;
+	const char *type;
+	struct stat st;
+	time_t diff, now;
+	int fd, idx;
+
+	if (!dev)
+		return NULL;
+
+	now = time(NULL);
+	diff = now - dev->bid_time;
+
+	if ((now < dev->bid_time) ||
+	    (diff < BLKID_PROBE_MIN) ||
+	    (dev->bid_flags & BLKID_BID_FL_VERIFIED &&
+	     diff < BLKID_PROBE_INTERVAL))
+		return dev;
+
+	DBG(DEBUG_PROBE,
+	    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)) {
+		if (errno == ENXIO || errno == ENODEV || errno == ENOENT) {
+			blkid_free_dev(dev);
+			return NULL;
+		}
+		/* We don't have read permission, just return cache data. */
+		DBG(DEBUG_PROBE,
+		    printf("returning unverified data for %s\n",
+			   dev->bid_name));
+		return dev;
+	}
+
+	memset(bufs, 0, sizeof(bufs));
+
+	/*
+	 * Iterate over the type array.  If we already know the type,
+	 * then try that first.  If it doesn't work, then blow away
+	 * the type information, and try again.
+	 *
+	 */
+try_again:
+	type = 0;
+	if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) {
+		uuid_t	uuid;
+
+		if (check_mdraid(fd, uuid) == 0) {
+			set_uuid(dev, uuid);
+			type = "mdraid";
+			goto found_type;
+		}
+	}
+	for (id = type_array; id->bim_type; id++) {
+		if (dev->bid_type &&
+		    strcmp(id->bim_type, dev->bid_type))
+			continue;
+
+		idx = id->bim_kboff + (id->bim_sboff >> 10);
+		if (idx > BLKID_BLK_OFFS || idx < 0)
+			continue;
+		buf = bufs[idx];
+		if (!buf) {
+			if (lseek(fd, idx << 10, SEEK_SET) < 0)
+				continue;
+
+			buf = xmalloc(1024);
+
+			if (read(fd, buf, 1024) != 1024) {
+				free(buf);
+				continue;
+			}
+			bufs[idx] = buf;
+		}
+
+		if (memcmp(id->bim_magic, buf + (id->bim_sboff&0x3ff),
+			   id->bim_len))
+			continue;
+
+		if ((id->bim_probe == NULL) ||
+		    (id->bim_probe(fd, cache, dev, id, buf) == 0)) {
+			type = id->bim_type;
+			goto found_type;
+		}
+	}
+
+	if (!id->bim_type && dev->bid_type) {
+		/*
+		 * Zap the device filesystem type and try again
+		 */
+		blkid_set_tag(dev, "TYPE", 0, 0);
+		blkid_set_tag(dev, "SEC_TYPE", 0, 0);
+		blkid_set_tag(dev, "LABEL", 0, 0);
+		blkid_set_tag(dev, "UUID", 0, 0);
+		goto try_again;
+	}
+
+	if (!dev->bid_type) {
+		blkid_free_dev(dev);
+		return NULL;
+	}
+
+found_type:
+	if (dev && type) {
+		dev->bid_devno = st.st_rdev;
+		dev->bid_time = time(NULL);
+		dev->bid_flags |= BLKID_BID_FL_VERIFIED;
+		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+
+		blkid_set_tag(dev, "TYPE", type, 0);
+
+		DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n",
+			   dev->bid_name, st.st_rdev, type));
+	}
+
+	close(fd);
+
+	return dev;
+}
+
+int blkid_known_fstype(const char *fstype)
+{
+	const struct blkid_magic *id;
+
+	for (id = type_array; id->bim_type; id++) {
+		if (strcmp(fstype, id->bim_type) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_dev dev;
+	blkid_cache cache;
+	int ret;
+
+	blkid_debug_mask = DEBUG_ALL;
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s device\n"
+			"Probe a single device to determine type\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL);
+	if (!dev) {
+		printf("%s: %s has an unsupported type\n", argv[0], argv[1]);
+		return 1;
+	}
+	printf("%s is type %s\n", argv[1], dev->bid_type ?
+		dev->bid_type : "(null)");
+	if (dev->bid_label)
+		printf("\tlabel is '%s'\n", dev->bid_label);
+	if (dev->bid_uuid)
+		printf("\tuuid is %s\n", dev->bid_uuid);
+
+	blkid_free_dev(dev);
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/probe.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/probe.h
new file mode 100644
index 0000000..b6d8f8e
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/probe.h
@@ -0,0 +1,374 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * probe.h - constants and on-disk structures for extracting device data
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+#ifndef BLKID_PROBE_H
+#define BLKID_PROBE_H 1
+
+#include <linux/types.h>
+
+struct blkid_magic;
+
+typedef int (*blkid_probe_t)(int fd, blkid_cache cache, blkid_dev dev,
+			     const struct blkid_magic *id, unsigned char *buf);
+
+struct blkid_magic {
+	const char	*bim_type;	/* type name for this magic */
+	long		bim_kboff;	/* kilobyte offset of superblock */
+	unsigned	bim_sboff;	/* byte offset within superblock */
+	unsigned	bim_len;	/* length of magic */
+	const char	*bim_magic;	/* magic string */
+	blkid_probe_t	bim_probe;	/* probe function */
+};
+
+/*
+ * Structures for each of the content types we want to extract information
+ * from.  We do not necessarily need the magic field here, because we have
+ * already identified the content type before we get this far.  It may still
+ * be useful if there are probe functions which handle multiple content types.
+ */
+struct ext2_super_block {
+	__u32		s_inodes_count;
+	__u32		s_blocks_count;
+	__u32		s_r_blocks_count;
+	__u32		s_free_blocks_count;
+	__u32		s_free_inodes_count;
+	__u32		s_first_data_block;
+	__u32		s_log_block_size;
+	__u32		s_dummy3[7];
+	unsigned char	s_magic[2];
+	__u16		s_state;
+	__u32		s_dummy5[8];
+	__u32		s_feature_compat;
+	__u32		s_feature_incompat;
+	__u32		s_feature_ro_compat;
+	unsigned char   s_uuid[16];
+	char	   s_volume_name[16];
+};
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL		0x00000004
+#define EXT3_FEATURE_INCOMPAT_RECOVER		0x00000004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x00000008
+
+struct xfs_super_block {
+	unsigned char	xs_magic[4];
+	__u32		xs_blocksize;
+	__u64		xs_dblocks;
+	__u64		xs_rblocks;
+	__u32		xs_dummy1[2];
+	unsigned char	xs_uuid[16];
+	__u32		xs_dummy2[15];
+	char		xs_fname[12];
+	__u32		xs_dummy3[2];
+	__u64		xs_icount;
+	__u64		xs_ifree;
+	__u64		xs_fdblocks;
+};
+
+struct reiserfs_super_block {
+	__u32		rs_blocks_count;
+	__u32		rs_free_blocks;
+	__u32		rs_root_block;
+	__u32		rs_journal_block;
+	__u32		rs_journal_dev;
+	__u32		rs_orig_journal_size;
+	__u32		rs_dummy2[5];
+	__u16		rs_blocksize;
+	__u16		rs_dummy3[3];
+	unsigned char	rs_magic[12];
+	__u32		rs_dummy4[5];
+	unsigned char	rs_uuid[16];
+	char		rs_label[16];
+};
+
+struct jfs_super_block {
+	unsigned char	js_magic[4];
+	__u32		js_version;
+	__u64		js_size;
+	__u32		js_bsize;
+	__u32		js_dummy1;
+	__u32		js_pbsize;
+	__u32		js_dummy2[27];
+	unsigned char	js_uuid[16];
+	unsigned char	js_label[16];
+	unsigned char	js_loguuid[16];
+};
+
+struct romfs_super_block {
+	unsigned char	ros_magic[8];
+	__u32		ros_dummy1[2];
+	unsigned char	ros_volume[16];
+};
+
+struct cramfs_super_block {
+	__u8		magic[4];
+	__u32		size;
+	__u32		flags;
+	__u32		future;
+	__u8		signature[16];
+	struct cramfs_info {
+		__u32		crc;
+		__u32		edition;
+		__u32		blocks;
+		__u32		files;
+	} info;
+	__u8		name[16];
+};
+
+struct swap_id_block {
+/*	unsigned char	sws_boot[1024]; */
+	__u32		sws_version;
+	__u32		sws_lastpage;
+	__u32		sws_nrbad;
+	unsigned char	sws_uuid[16];
+	char		sws_volume[16];
+	unsigned char	sws_pad[117];
+	__u32		sws_badpg;
+};
+
+/* Yucky misaligned values */
+struct vfat_super_block {
+/* 00*/	unsigned char	vs_ignored[3];
+/* 03*/	unsigned char	vs_sysid[8];
+/* 0b*/	unsigned char	vs_sector_size[2];
+/* 0d*/	__u8		vs_cluster_size;
+/* 0e*/	__u16		vs_reserved;
+/* 10*/	__u8		vs_fats;
+/* 11*/	unsigned char	vs_dir_entries[2];
+/* 13*/	unsigned char	vs_sectors[2];
+/* 15*/	unsigned char	vs_media;
+/* 16*/	__u16		vs_fat_length;
+/* 18*/	__u16		vs_secs_track;
+/* 1a*/	__u16		vs_heads;
+/* 1c*/	__u32		vs_hidden;
+/* 20*/	__u32		vs_total_sect;
+/* 24*/	__u32		vs_fat32_length;
+/* 28*/	__u16		vs_flags;
+/* 2a*/	__u8		vs_version[2];
+/* 2c*/	__u32		vs_root_cluster;
+/* 30*/	__u16		vs_insfo_sector;
+/* 32*/	__u16		vs_backup_boot;
+/* 34*/	__u16		vs_reserved2[6];
+/* 40*/	unsigned char	vs_unknown[3];
+/* 43*/	unsigned char	vs_serno[4];
+/* 47*/	char		vs_label[11];
+/* 52*/	unsigned char   vs_magic[8];
+/* 5a*/	unsigned char	vs_dummy2[164];
+/*1fe*/	unsigned char	vs_pmagic[2];
+};
+
+/* Yucky misaligned values */
+struct msdos_super_block {
+/* 00*/	unsigned char	ms_ignored[3];
+/* 03*/	unsigned char	ms_sysid[8];
+/* 0b*/	unsigned char	ms_sector_size[2];
+/* 0d*/	__u8		ms_cluster_size;
+/* 0e*/	__u16		ms_reserved;
+/* 10*/	__u8		ms_fats;
+/* 11*/	unsigned char	ms_dir_entries[2];
+/* 13*/	unsigned char	ms_sectors[2];
+/* 15*/	unsigned char	ms_media;
+/* 16*/	__u16		ms_fat_length;
+/* 18*/	__u16		ms_secs_track;
+/* 1a*/	__u16		ms_heads;
+/* 1c*/	__u32		ms_hidden;
+/* 20*/	__u32		ms_total_sect;
+/* 24*/	unsigned char	ms_unknown[3];
+/* 27*/	unsigned char	ms_serno[4];
+/* 2b*/	char		ms_label[11];
+/* 36*/	unsigned char   ms_magic[8];
+/* 3d*/	unsigned char	ms_dummy2[192];
+/*1fe*/	unsigned char	ms_pmagic[2];
+};
+
+struct minix_super_block {
+	__u16		ms_ninodes;
+	__u16		ms_nzones;
+	__u16		ms_imap_blocks;
+	__u16		ms_zmap_blocks;
+	__u16		ms_firstdatazone;
+	__u16		ms_log_zone_size;
+	__u32		ms_max_size;
+	unsigned char	ms_magic[2];
+	__u16		ms_state;
+	__u32		ms_zones;
+};
+
+struct mdp_superblock_s {
+	__u32 md_magic;
+	__u32 major_version;
+	__u32 minor_version;
+	__u32 patch_version;
+	__u32 gvalid_words;
+	__u32 set_uuid0;
+	__u32 ctime;
+	__u32 level;
+	__u32 size;
+	__u32 nr_disks;
+	__u32 raid_disks;
+	__u32 md_minor;
+	__u32 not_persistent;
+	__u32 set_uuid1;
+	__u32 set_uuid2;
+	__u32 set_uuid3;
+};
+
+struct hfs_super_block {
+	char	h_magic[2];
+	char	h_dummy[18];
+	__u32	h_blksize;
+};
+
+struct ocfs_volume_header {
+	unsigned char	minor_version[4];
+	unsigned char	major_version[4];
+	unsigned char	signature[128];
+	char		mount[128];
+	unsigned char   mount_len[2];
+};
+
+struct ocfs_volume_label {
+	unsigned char	disk_lock[48];
+	char		label[64];
+	unsigned char	label_len[2];
+	unsigned char  vol_id[16];
+	unsigned char  vol_id_len[2];
+};
+
+#define ocfsmajor(o) ((__u32)o.major_version[0] \
+                   + (((__u32) o.major_version[1]) << 8) \
+                   + (((__u32) o.major_version[2]) << 16) \
+                   + (((__u32) o.major_version[3]) << 24))
+#define ocfslabellen(o)	((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8))
+#define ocfsmountlen(o)	((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8))
+
+#define OCFS_MAGIC "OracleCFS"
+
+struct ocfs2_super_block {
+	unsigned char  signature[8];
+	unsigned char  s_dummy1[184];
+	unsigned char  s_dummy2[80];
+	char	       s_label[64];
+	unsigned char  s_uuid[16];
+};
+
+#define OCFS2_MIN_BLOCKSIZE             512
+#define OCFS2_MAX_BLOCKSIZE             4096
+
+#define OCFS2_SUPER_BLOCK_BLKNO         2
+
+#define OCFS2_SUPER_BLOCK_SIGNATURE     "OCFSV2"
+
+struct oracle_asm_disk_label {
+	char dummy[32];
+	char dl_tag[8];
+	char dl_id[24];
+};
+
+#define ORACLE_ASM_DISK_LABEL_MARKED    "ORCLDISK"
+#define ORACLE_ASM_DISK_LABEL_OFFSET    32
+
+#define ISODCL(from, to) (to - from + 1)
+struct iso_volume_descriptor {
+	char type[ISODCL(1,1)]; /* 711 */
+	char id[ISODCL(2,6)];
+	char version[ISODCL(7,7)];
+	char data[ISODCL(8,2048)];
+};
+
+/*
+ * Byte swap functions
+ */
+#ifdef __GNUC__
+#define _INLINE_ static __inline__
+#else				/* For Watcom C */
+#define _INLINE_ static inline
+#endif
+
+static __u16 blkid_swab16(__u16 val);
+static __u32 blkid_swab32(__u32 val);
+static __u64 blkid_swab64(__u64 val);
+
+#if ((defined __GNUC__) && \
+     (defined(__i386__) || defined(__i486__) || defined(__i586__)))
+
+#define _BLKID_HAVE_ASM_BITOPS_
+
+_INLINE_ __u32 blkid_swab32(__u32 val)
+{
+#ifdef EXT2FS_REQUIRE_486
+	__asm__("bswap %0" : "=r" (val) : "0" (val));
+#else
+	__asm__("xchgb %b0,%h0\n\t"	/* swap lower bytes  */
+		"rorl $16,%0\n\t"	/* swap words        */
+		"xchgb %b0,%h0"		/* swap higher bytes */
+		:"=q" (val)
+		: "0" (val));
+#endif
+	return val;
+}
+
+_INLINE_ __u16 blkid_swab16(__u16 val)
+{
+	__asm__("xchgb %b0,%h0"		/* swap bytes */
+		: "=q" (val)
+		:  "0" (val));
+		return val;
+}
+
+_INLINE_ __u64 blkid_swab64(__u64 val)
+{
+	return blkid_swab32(val >> 32) |
+	       ( ((__u64)blkid_swab32((__u32)val)) << 32 );
+}
+#endif
+
+#if !defined(_BLKID_HAVE_ASM_BITOPS_)
+
+_INLINE_  __u16 blkid_swab16(__u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+_INLINE_ __u32 blkid_swab32(__u32 val)
+{
+	return (val>>24) | ((val>>8) & 0xFF00) |
+		((val<<8) & 0xFF0000) | (val<<24);
+}
+
+_INLINE_ __u64 blkid_swab64(__u64 val)
+{
+	return blkid_swab32(val >> 32) |
+	       ( ((__u64)blkid_swab32((__u32)val)) << 32 );
+}
+#endif
+
+
+
+#if  __BYTE_ORDER == __BIG_ENDIAN
+#define blkid_le16(x) blkid_swab16(x)
+#define blkid_le32(x) blkid_swab32(x)
+#define blkid_le64(x) blkid_swab64(x)
+#define blkid_be16(x) (x)
+#define blkid_be32(x) (x)
+#define blkid_be64(x) (x)
+#else
+#define blkid_le16(x) (x)
+#define blkid_le32(x) (x)
+#define blkid_le64(x) (x)
+#define blkid_be16(x) blkid_swab16(x)
+#define blkid_be32(x) blkid_swab32(x)
+#define blkid_be64(x) blkid_swab64(x)
+#endif
+
+#undef _INLINE_
+
+#endif /* _BLKID_PROBE_H */
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/read.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/read.c
new file mode 100644
index 0000000..f795a5d
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/read.c
@@ -0,0 +1,459 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * read.c - read the blkid cache from disk, to avoid scanning all devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Y. Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "blkidP.h"
+#include "../uuid/uuid.h"
+
+#ifdef HAVE_STRTOULL
+#define __USE_ISOC9X
+#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
+#else
+/* FIXME: need to support real strtoull here */
+#define STRTOULL strtoul
+#endif
+
+#include <stdlib.h>
+
+#ifdef TEST_PROGRAM
+#define blkid_debug_dump_dev(dev)  (debug_dump_dev(dev))
+static void debug_dump_dev(blkid_dev dev);
+#endif
+
+/*
+ * File format:
+ *
+ *	<device [<NAME="value"> ...]>device_name</device>
+ *
+ *	The following tags are required for each entry:
+ *	<ID="id">	unique (within this file) ID number of this device
+ *	<TIME="time">	(ascii time_t) time this entry was last read from disk
+ *	<TYPE="type">	(detected) type of filesystem/data for this partition
+ *
+ *	The following tags may be present, depending on the device contents
+ *	<LABEL="label">	(user supplied) label (volume name, etc)
+ *	<UUID="uuid">	(generated) universally unique identifier (serial no)
+ */
+
+static char *skip_over_blank(char *cp)
+{
+	while (*cp && isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+	char ch;
+
+	while ((ch = *cp)) {
+		/* If we see a backslash, skip the next character */
+		if (ch == '\\') {
+			cp++;
+			if (*cp == '\0')
+				break;
+			cp++;
+			continue;
+		}
+		if (isspace(ch) || ch == '<' || ch == '>')
+			break;
+		cp++;
+	}
+	return cp;
+}
+
+static char *strip_line(char *line)
+{
+	char	*p;
+
+	line = skip_over_blank(line);
+
+	p = line + strlen(line) - 1;
+
+	while (*line) {
+		if (isspace(*p))
+			*p-- = '\0';
+		else
+			break;
+	}
+
+	return line;
+}
+
+/*
+ * Start parsing a new line from the cache.
+ *
+ * line starts with "<device" return 1 -> continue parsing line
+ * line starts with "<foo", empty, or # return 0 -> skip line
+ * line starts with other, return -BLKID_ERR_CACHE -> error
+ */
+static int parse_start(char **cp)
+{
+	char *p;
+
+	p = strip_line(*cp);
+
+	/* Skip comment or blank lines.  We can't just NUL the first '#' char,
+	 * in case it is inside quotes, or escaped.
+	 */
+	if (*p == '\0' || *p == '#')
+		return 0;
+
+	if (!strncmp(p, "<device", 7)) {
+		DBG(DEBUG_READ, printf("found device header: %8s\n", p));
+		p += 7;
+
+		*cp = p;
+		return 1;
+	}
+
+	if (*p == '<')
+		return 0;
+
+	return -BLKID_ERR_CACHE;
+}
+
+/* Consume the remaining XML on the line (cosmetic only) */
+static int parse_end(char **cp)
+{
+	*cp = skip_over_blank(*cp);
+
+	if (!strncmp(*cp, "</device>", 9)) {
+		DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp));
+		*cp += 9;
+		return 0;
+	}
+
+	return -BLKID_ERR_CACHE;
+}
+
+/*
+ * Allocate a new device struct with device name filled in.  Will handle
+ * finding the device on lines of the form:
+ * <device foo=bar>devname</device>
+ * <device>devname<foo>bar</foo></device>
+ */
+static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp)
+{
+	char *start, *tmp, *end, *name;
+	int ret;
+
+	if ((ret = parse_start(cp)) <= 0)
+		return ret;
+
+	start = tmp = strchr(*cp, '>');
+	if (!start) {
+		DBG(DEBUG_READ,
+		    printf("blkid: short line parsing dev: %s\n", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+	start = skip_over_blank(start + 1);
+	end = skip_over_word(start);
+
+	DBG(DEBUG_READ, printf("device should be %*s\n", end - start, start));
+
+	if (**cp == '>')
+		*cp = end;
+	else
+		(*cp)++;
+
+	*tmp = '\0';
+
+	if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+		DBG(DEBUG_READ,
+		    printf("blkid: missing </device> ending: %s\n", end));
+	} else if (tmp)
+		*tmp = '\0';
+
+	if (end - start <= 1) {
+		DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+
+	name = blkid_strndup(start, end-start);
+	if (name == NULL)
+		return -BLKID_ERR_MEM;
+
+	DBG(DEBUG_READ, printf("found dev %s\n", name));
+
+	if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE)))
+		return -BLKID_ERR_MEM;
+
+	free(name);
+	return 1;
+}
+
+/*
+ * Extract a tag of the form NAME="value" from the line.
+ */
+static int parse_token(char **name, char **value, char **cp)
+{
+	char *end;
+
+	if (!name || !value || !cp)
+		return -BLKID_ERR_PARAM;
+
+	if (!(*value = strchr(*cp, '=')))
+		return 0;
+
+	**value = '\0';
+	*name = strip_line(*cp);
+	*value = skip_over_blank(*value + 1);
+
+	if (**value == '"') {
+		end = strchr(*value + 1, '"');
+		if (!end) {
+			DBG(DEBUG_READ,
+			    printf("unbalanced quotes at: %s\n", *value));
+			*cp = *value;
+			return -BLKID_ERR_CACHE;
+		}
+		(*value)++;
+		*end = '\0';
+		end++;
+	} else {
+		end = skip_over_word(*value);
+		if (*end) {
+			*end = '\0';
+			end++;
+		}
+	}
+	*cp = end;
+
+	return 1;
+}
+
+/*
+ * Extract a tag of the form <NAME>value</NAME> from the line.
+ */
+/*
+static int parse_xml(char **name, char **value, char **cp)
+{
+	char *end;
+
+	if (!name || !value || !cp)
+		return -BLKID_ERR_PARAM;
+
+	*name = strip_line(*cp);
+
+	if ((*name)[0] != '<' || (*name)[1] == '/')
+		return 0;
+
+	FIXME: finish this.
+}
+*/
+
+/*
+ * Extract a tag from the line.
+ *
+ * Return 1 if a valid tag was found.
+ * Return 0 if no tag found.
+ * Return -ve error code.
+ */
+static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp)
+{
+	char *name;
+	char *value;
+	int ret;
+
+	if (!cache || !dev)
+		return -BLKID_ERR_PARAM;
+
+	if ((ret = parse_token(&name, &value, cp)) <= 0 /* &&
+	    (ret = parse_xml(&name, &value, cp)) <= 0 */)
+		return ret;
+
+	/* Some tags are stored directly in the device struct */
+	if (!strcmp(name, "DEVNO"))
+		dev->bid_devno = STRTOULL(value, 0, 0);
+	else if (!strcmp(name, "PRI"))
+		dev->bid_pri = strtol(value, 0, 0);
+	else if (!strcmp(name, "TIME"))
+		/* FIXME: need to parse a long long eventually */
+		dev->bid_time = strtol(value, 0, 0);
+	else
+		ret = blkid_set_tag(dev, name, value, strlen(value));
+
+	DBG(DEBUG_READ, printf("    tag: %s=\"%s\"\n", name, value));
+
+	return ret < 0 ? ret : 1;
+}
+
+/*
+ * Parse a single line of data, and return a newly allocated dev struct.
+ * Add the new device to the cache struct, if one was read.
+ *
+ * Lines are of the form <device [TAG="value" ...]>/dev/foo</device>
+ *
+ * Returns -ve value on error.
+ * Returns 0 otherwise.
+ * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL
+ * (e.g. comment lines, unknown XML content, etc).
+ */
+static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
+{
+	blkid_dev dev;
+	int ret;
+
+	if (!cache || !dev_p)
+		return -BLKID_ERR_PARAM;
+
+	*dev_p = NULL;
+
+	DBG(DEBUG_READ, printf("line: %s\n", cp));
+
+	if ((ret = parse_dev(cache, dev_p, &cp)) <= 0)
+		return ret;
+
+	dev = *dev_p;
+
+	while ((ret = parse_tag(cache, dev, &cp)) > 0) {
+		;
+	}
+
+	if (dev->bid_type == NULL) {
+		DBG(DEBUG_READ,
+		    printf("blkid: device %s has no TYPE\n",dev->bid_name));
+		blkid_free_dev(dev);
+	}
+
+	DBG(DEBUG_READ, blkid_debug_dump_dev(dev));
+
+	return ret;
+}
+
+/*
+ * Parse the specified filename, and return the data in the supplied or
+ * a newly allocated cache struct.  If the file doesn't exist, return a
+ * new empty cache struct.
+ */
+void blkid_read_cache(blkid_cache cache)
+{
+	FILE *file;
+	char buf[4096];
+	int fd, lineno = 0;
+	struct stat st;
+
+	if (!cache)
+		return;
+
+	/*
+	 * If the file doesn't exist, then we just return an empty
+	 * struct so that the cache can be populated.
+	 */
+	if ((fd = open(cache->bic_filename, O_RDONLY)) < 0)
+		return;
+	if (fstat(fd, &st) < 0)
+		goto errout;
+	if ((st.st_mtime == cache->bic_ftime) ||
+	    (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+		DBG(DEBUG_CACHE, printf("skipping re-read of %s\n",
+					cache->bic_filename));
+		goto errout;
+	}
+
+	DBG(DEBUG_CACHE, printf("reading cache file %s\n",
+				cache->bic_filename));
+
+	file = xfdopen_for_read(fd);
+
+	while (fgets(buf, sizeof(buf), file)) {
+		blkid_dev dev;
+		unsigned int end;
+
+		lineno++;
+		if (buf[0] == 0)
+			continue;
+		end = strlen(buf) - 1;
+		/* Continue reading next line if it ends with a backslash */
+		while (buf[end] == '\\' && end < sizeof(buf) - 2 &&
+		       fgets(buf + end, sizeof(buf) - end, file)) {
+			end = strlen(buf) - 1;
+			lineno++;
+		}
+
+		if (blkid_parse_line(cache, &dev, buf) < 0) {
+			DBG(DEBUG_READ,
+			    printf("blkid: bad format on line %d\n", lineno));
+			continue;
+		}
+	}
+	fclose(file);
+
+	/*
+	 * Initially we do not need to write out the cache file.
+	 */
+	cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+	cache->bic_ftime = st.st_mtime;
+
+	return;
+errout:
+	close(fd);
+}
+
+#ifdef TEST_PROGRAM
+static void debug_dump_dev(blkid_dev dev)
+{
+	struct list_head *p;
+
+	if (!dev) {
+		printf("  dev: NULL\n");
+		return;
+	}
+
+	printf("  dev: name = %s\n", dev->bid_name);
+	printf("  dev: DEVNO=\"0x%0llx\"\n", dev->bid_devno);
+	printf("  dev: TIME=\"%lu\"\n", dev->bid_time);
+	printf("  dev: PRI=\"%d\"\n", dev->bid_pri);
+	printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (tag)
+			printf("    tag: %s=\"%s\"\n", tag->bit_name,
+			       tag->bit_val);
+		else
+			printf("    tag: NULL\n");
+	}
+	bb_putchar('\n');
+}
+
+int main(int argc, char**argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_debug_mask = DEBUG_ALL;
+	if (argc > 2) {
+		fprintf(stderr, "Usage: %s [filename]\n"
+			"Test parsing of the cache (filename)\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, argv[1])) < 0)
+		fprintf(stderr, "error %d reading cache file %s\n", ret,
+			argv[1] ? argv[1] : BLKID_CACHE_FILE);
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/resolve.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/resolve.c
new file mode 100644
index 0000000..295ca61
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/resolve.c
@@ -0,0 +1,139 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * resolve.c - resolve names and tags into specific devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Ts'o.
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "blkidP.h"
+#include "probe.h"
+
+/*
+ * Find a tagname (e.g. LABEL or UUID) on a specific device.
+ */
+char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+			  const char *devname)
+{
+	blkid_tag found;
+	blkid_dev dev;
+	blkid_cache c = cache;
+	char *ret = NULL;
+
+	DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname));
+
+	if (!devname)
+		return NULL;
+
+	if (!cache) {
+		if (blkid_get_cache(&c, NULL) < 0)
+			return NULL;
+	}
+
+	if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) &&
+	    (found = blkid_find_tag_dev(dev, tagname)))
+		ret = blkid_strdup(found->bit_val);
+
+	if (!cache)
+		blkid_put_cache(c);
+
+	return ret;
+}
+
+/*
+ * Locate a device name from a token (NAME=value string), or (name, value)
+ * pair.  In the case of a token, value is ignored.  If the "token" is not
+ * of the form "NAME=value" and there is no value given, then it is assumed
+ * to be the actual devname and a copy is returned.
+ */
+char *blkid_get_devname(blkid_cache cache, const char *token,
+			const char *value)
+{
+	blkid_dev dev;
+	blkid_cache c = cache;
+	char *t = NULL, *v = NULL;
+	char *ret = NULL;
+
+	if (!token)
+		return NULL;
+
+	if (!cache) {
+		if (blkid_get_cache(&c, NULL) < 0)
+			return NULL;
+	}
+
+	DBG(DEBUG_RESOLVE,
+	    printf("looking for %s%s%s %s\n", token, value ? "=" : "",
+		   value ? value : "", cache ? "in cache" : "from disk"));
+
+	if (!value) {
+		if (!strchr(token, '='))
+			return blkid_strdup(token);
+		blkid_parse_tag_string(token, &t, &v);
+		if (!t || !v)
+			goto errout;
+		token = t;
+		value = v;
+	}
+
+	dev = blkid_find_dev_with_tag(c, token, value);
+	if (!dev)
+		goto errout;
+
+	ret = blkid_strdup(blkid_dev_devname(dev));
+
+errout:
+	free(t);
+	free(v);
+	if (!cache) {
+		blkid_put_cache(c);
+	}
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	char *value;
+	blkid_cache cache;
+
+	blkid_debug_mask = DEBUG_ALL;
+	if (argc != 2 && argc != 3) {
+		fprintf(stderr, "Usage:\t%s tagname=value\n"
+			"\t%s tagname devname\n"
+			"Find which device holds a given token or\n"
+			"Find what the value of a tag is in a device\n",
+			argv[0], argv[0]);
+		exit(1);
+	}
+	if (blkid_get_cache(&cache, bb_dev_null) < 0) {
+		fprintf(stderr, "Can't get blkid cache\n");
+		exit(1);
+	}
+
+	if (argv[2]) {
+		value = blkid_get_tag_value(cache, argv[1], argv[2]);
+		printf("%s has tag %s=%s\n", argv[2], argv[1],
+		       value ? value : "<missing>");
+	} else {
+		value = blkid_get_devname(cache, argv[1], NULL);
+		printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
+	}
+	blkid_put_cache(cache);
+	return value ? 0 : 1;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/save.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/save.c
new file mode 100644
index 0000000..e60cca4
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/save.c
@@ -0,0 +1,189 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * save.c - write the cache struct to disk
+ *
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+
+static int save_dev(blkid_dev dev, FILE *file)
+{
+	struct list_head *p;
+
+	if (!dev || dev->bid_name[0] != '/')
+		return 0;
+
+	DBG(DEBUG_SAVE,
+	    printf("device %s, type %s\n", dev->bid_name, dev->bid_type));
+
+	fprintf(file,
+		"<device DEVNO=\"0x%04lx\" TIME=\"%lu\"",
+		(unsigned long) dev->bid_devno, dev->bid_time);
+	if (dev->bid_pri)
+		fprintf(file, " PRI=\"%d\"", dev->bid_pri);
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
+	}
+	fprintf(file, ">%s</device>\n", dev->bid_name);
+
+	return 0;
+}
+
+/*
+ * Write out the cache struct to the cache file on disk.
+ */
+int blkid_flush_cache(blkid_cache cache)
+{
+	struct list_head *p;
+	char *tmp = NULL;
+	const char *opened = NULL;
+	const char *filename;
+	FILE *file = NULL;
+	int fd, ret = 0;
+	struct stat st;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	if (list_empty(&cache->bic_devs) ||
+	    !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+		DBG(DEBUG_SAVE, printf("skipping cache file write\n"));
+		return 0;
+	}
+
+	filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE;
+
+	/* If we can't write to the cache file, then don't even try */
+	if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
+	    (ret == 0 && access(filename, W_OK) < 0)) {
+		DBG(DEBUG_SAVE,
+		    printf("can't write to cache file %s\n", filename));
+		return 0;
+	}
+
+	/*
+	 * Try and create a temporary file in the same directory so
+	 * that in case of error we don't overwrite the cache file.
+	 * If the cache file doesn't yet exist, it isn't a regular
+	 * file (e.g. /dev/null or a socket), or we couldn't create
+	 * a temporary file then we open it directly.
+	 */
+	if (ret == 0 && S_ISREG(st.st_mode)) {
+		tmp = xmalloc(strlen(filename) + 8);
+		sprintf(tmp, "%s-XXXXXX", filename);
+		fd = mkstemp(tmp);
+		if (fd >= 0) {
+			file = xfdopen_for_write(fd);
+			opened = tmp;
+		}
+		fchmod(fd, 0644);
+	}
+
+	if (!file) {
+		file = fopen_for_write(filename);
+		opened = filename;
+	}
+
+	DBG(DEBUG_SAVE,
+	    printf("writing cache file %s (really %s)\n",
+		   filename, opened));
+
+	if (!file) {
+		ret = errno;
+		goto errout;
+	}
+
+	list_for_each(p, &cache->bic_devs) {
+		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (!dev->bid_type)
+			continue;
+		if ((ret = save_dev(dev, file)) < 0)
+			break;
+	}
+
+	if (ret >= 0) {
+		cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+		ret = 1;
+	}
+
+	fclose(file);
+	if (opened != filename) {
+		if (ret < 0) {
+			unlink(opened);
+			DBG(DEBUG_SAVE,
+			    printf("unlinked temp cache %s\n", opened));
+		} else {
+			char *backup;
+
+			backup = xmalloc(strlen(filename) + 5);
+			sprintf(backup, "%s.old", filename);
+			unlink(backup);
+			link(filename, backup);
+			free(backup);
+			rename(opened, filename);
+			DBG(DEBUG_SAVE,
+			    printf("moved temp cache %s\n", opened));
+		}
+	}
+
+errout:
+	free(tmp);
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_debug_mask = DEBUG_ALL;
+	if (argc > 2) {
+		fprintf(stderr, "Usage: %s [filename]\n"
+			"Test loading/saving a cache (filename)\n", argv[0]);
+		exit(1);
+	}
+
+	if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if ((ret = blkid_probe_all(cache)) < 0) {
+		fprintf(stderr, "error (%d) probing devices\n", ret);
+		exit(1);
+	}
+	cache->bic_filename = blkid_strdup(argv[1]);
+
+	if ((ret = blkid_flush_cache(cache)) < 0) {
+		fprintf(stderr, "error (%d) saving cache\n", ret);
+		exit(1);
+	}
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/tag.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/tag.c
new file mode 100644
index 0000000..8337b46
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/blkid/tag.c
@@ -0,0 +1,431 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tag.c - allocation/initialization/free routines for tag structs
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blkidP.h"
+
+static blkid_tag blkid_new_tag(void)
+{
+	blkid_tag tag;
+
+	tag = xzalloc(sizeof(struct blkid_struct_tag));
+
+	INIT_LIST_HEAD(&tag->bit_tags);
+	INIT_LIST_HEAD(&tag->bit_names);
+
+	return tag;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_tag(blkid_tag tag)
+{
+	if (!tag) {
+		printf("    tag: NULL\n");
+		return;
+	}
+
+	printf("    tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+#endif
+
+void blkid_free_tag(blkid_tag tag)
+{
+	if (!tag)
+		return;
+
+	DBG(DEBUG_TAG, printf("    freeing tag %s=%s\n", tag->bit_name,
+		   tag->bit_val ? tag->bit_val : "(NULL)"));
+	DBG(DEBUG_TAG, blkid_debug_dump_tag(tag));
+
+	list_del(&tag->bit_tags);	/* list of tags for this device */
+	list_del(&tag->bit_names);	/* list of tags with this type */
+
+	free(tag->bit_name);
+	free(tag->bit_val);
+	free(tag);
+}
+
+/*
+ * Find the desired tag on a device.  If value is NULL, then the
+ * first such tag is returned, otherwise return only exact tag if found.
+ */
+blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type)
+{
+	struct list_head *p;
+
+	if (!dev || !type)
+		return NULL;
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+					   bit_tags);
+
+		if (!strcmp(tmp->bit_name, type))
+			return tmp;
+	}
+	return NULL;
+}
+
+/*
+ * Find the desired tag type in the cache.
+ * We return the head tag for this tag type.
+ */
+static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type)
+{
+	blkid_tag head = NULL, tmp;
+	struct list_head *p;
+
+	if (!cache || !type)
+		return NULL;
+
+	list_for_each(p, &cache->bic_tags) {
+		tmp = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (!strcmp(tmp->bit_name, type)) {
+			DBG(DEBUG_TAG,
+			    printf("    found cache tag head %s\n", type));
+			head = tmp;
+			break;
+		}
+	}
+	return head;
+}
+
+/*
+ * Set a tag on an existing device.
+ *
+ * If value is NULL, then delete the tagsfrom the device.
+ */
+int blkid_set_tag(blkid_dev dev, const char *name,
+		  const char *value, const int vlength)
+{
+	blkid_tag	t = 0, head = 0;
+	char		*val = NULL;
+
+	if (!dev || !name)
+		return -BLKID_ERR_PARAM;
+
+	if (!(val = blkid_strndup(value, vlength)) && value)
+		return -BLKID_ERR_MEM;
+	t = blkid_find_tag_dev(dev, name);
+	if (!value) {
+		blkid_free_tag(t);
+	} else if (t) {
+		if (!strcmp(t->bit_val, val)) {
+			/* Same thing, exit */
+			free(val);
+			return 0;
+		}
+		free(t->bit_val);
+		t->bit_val = val;
+	} else {
+		/* Existing tag not present, add to device */
+		if (!(t = blkid_new_tag()))
+			goto errout;
+		t->bit_name = blkid_strdup(name);
+		t->bit_val = val;
+		t->bit_dev = dev;
+
+		list_add_tail(&t->bit_tags, &dev->bid_tags);
+
+		if (dev->bid_cache) {
+			head = blkid_find_head_cache(dev->bid_cache,
+						     t->bit_name);
+			if (!head) {
+				head = blkid_new_tag();
+				if (!head)
+					goto errout;
+
+				DBG(DEBUG_TAG,
+				    printf("    creating new cache tag head %s\n", name));
+				head->bit_name = blkid_strdup(name);
+				if (!head->bit_name)
+					goto errout;
+				list_add_tail(&head->bit_tags,
+					      &dev->bid_cache->bic_tags);
+			}
+			list_add_tail(&t->bit_names, &head->bit_names);
+		}
+	}
+
+	/* Link common tags directly to the device struct */
+	if (!strcmp(name, "TYPE"))
+		dev->bid_type = val;
+	else if (!strcmp(name, "LABEL"))
+		dev->bid_label = val;
+	else if (!strcmp(name, "UUID"))
+		dev->bid_uuid = val;
+
+	if (dev->bid_cache)
+		dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+	return 0;
+
+errout:
+	blkid_free_tag(t);
+	if (!t)
+		free(val);
+	blkid_free_tag(head);
+	return -BLKID_ERR_MEM;
+}
+
+
+/*
+ * Parse a "NAME=value" string.  This is slightly different than
+ * parse_token, because that will end an unquoted value at a space, while
+ * this will assume that an unquoted value is the rest of the token (e.g.
+ * if we are passed an already quoted string from the command-line we don't
+ * have to both quote and escape quote so that the quotes make it to
+ * us).
+ *
+ * Returns 0 on success, and -1 on failure.
+ */
+int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val)
+{
+	char *name, *value, *cp;
+
+	DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token));
+
+	if (!token || !(cp = strchr(token, '=')))
+		return -1;
+
+	name = blkid_strdup(token);
+	if (!name)
+		return -1;
+	value = name + (cp - token);
+	*value++ = '\0';
+	if (*value == '"' || *value == '\'') {
+		char c = *value++;
+		if (!(cp = strrchr(value, c)))
+			goto errout; /* missing closing quote */
+		*cp = '\0';
+	}
+	value = blkid_strdup(value);
+	if (!value)
+		goto errout;
+
+	*ret_type = name;
+	*ret_val = value;
+
+	return 0;
+
+errout:
+	free(name);
+	return -1;
+}
+
+/*
+ * Tag iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation.  I'm not convinced I want
+ * to keep list.h in the long term, anyway.  It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application.  [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all tags in a device
+ */
+#define TAG_ITERATE_MAGIC	0x01a5284c
+
+struct blkid_struct_tag_iterate {
+	int			magic;
+	blkid_dev		dev;
+	struct list_head	*p;
+};
+
+blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+{
+	blkid_tag_iterate	iter;
+
+	iter = xmalloc(sizeof(struct blkid_struct_tag_iterate));
+	iter->magic = TAG_ITERATE_MAGIC;
+	iter->dev = dev;
+	iter->p	= dev->bid_tags.next;
+	return iter;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_tag_next(blkid_tag_iterate iter,
+			  const char **type, const char **value)
+{
+	blkid_tag tag;
+
+	*type = 0;
+	*value = 0;
+	if (!iter || iter->magic != TAG_ITERATE_MAGIC ||
+	    iter->p == &iter->dev->bid_tags)
+		return -1;
+	tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags);
+	*type = tag->bit_name;
+	*value = tag->bit_val;
+	iter->p = iter->p->next;
+	return 0;
+}
+
+void blkid_tag_iterate_end(blkid_tag_iterate iter)
+{
+	if (!iter || iter->magic != TAG_ITERATE_MAGIC)
+		return;
+	iter->magic = 0;
+	free(iter);
+}
+
+/*
+ * This function returns a device which matches a particular
+ * type/value pair.  If there is more than one device that matches the
+ * search specification, it returns the one with the highest priority
+ * value.  This allows us to give preference to EVMS or LVM devices.
+ *
+ * XXX there should also be an interface which uses an iterator so we
+ * can get all of the devices which match a type/value search parameter.
+ */
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value)
+{
+	blkid_tag	head;
+	blkid_dev	dev;
+	int		pri;
+	struct list_head *p;
+
+	if (!cache || !type || !value)
+		return NULL;
+
+	blkid_read_cache(cache);
+
+	DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value));
+
+try_again:
+	pri = -1;
+	dev = 0;
+	head = blkid_find_head_cache(cache, type);
+
+	if (head) {
+		list_for_each(p, &head->bit_names) {
+			blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+						   bit_names);
+
+			if (!strcmp(tmp->bit_val, value) &&
+			    tmp->bit_dev->bid_pri > pri) {
+				dev = tmp->bit_dev;
+				pri = dev->bid_pri;
+			}
+		}
+	}
+	if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+		dev = blkid_verify(cache, dev);
+		if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))
+			goto try_again;
+	}
+
+	if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+		if (blkid_probe_all(cache) < 0)
+			return NULL;
+		goto try_again;
+	}
+	return dev;
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+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);
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	blkid_tag_iterate	iter;
+	blkid_cache		cache = NULL;
+	blkid_dev		dev;
+	int			c, ret, found;
+	int			flags = BLKID_DEV_FIND;
+	char			*tmp;
+	char			*file = NULL;
+	char			*devname = NULL;
+	char			*search_type = NULL;
+	char			*search_value = NULL;
+	const char		*type, *value;
+
+	while ((c = getopt (argc, argv, "m:f:")) != EOF)
+		switch (c) {
+		case 'f':
+			file = optarg;
+			break;
+		case 'm':
+			blkid_debug_mask = strtoul (optarg, &tmp, 0);
+			if (*tmp) {
+				fprintf(stderr, "Invalid debug mask: %d\n",
+					optarg);
+				exit(1);
+			}
+			break;
+		case '?':
+			usage(argv[0]);
+		}
+	if (argc > optind)
+		devname = argv[optind++];
+	if (argc > optind)
+		search_type = argv[optind++];
+	if (argc > optind)
+		search_value = argv[optind++];
+	if (!devname || (argc != optind))
+		usage(argv[0]);
+
+	if ((ret = blkid_get_cache(&cache, file)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+
+	dev = blkid_get_dev(cache, devname, flags);
+	if (!dev) {
+		fprintf(stderr, "%s: cannot find device in blkid cache\n");
+		exit(1);
+	}
+	if (search_type) {
+		found = blkid_dev_has_tag(dev, search_type, search_value);
+		printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev),
+		       search_type, search_value ? search_value : "NULL",
+		       found ? "FOUND" : "NOT FOUND");
+		return !found;
+	}
+	printf("Device %s...\n", blkid_dev_devname(dev));
+
+	iter = blkid_tag_iterate_begin(dev);
+	while (blkid_tag_next(iter, &type, &value) == 0) {
+		printf("\tTag %s has value %s\n", type, value);
+	}
+	blkid_tag_iterate_end(iter);
+
+	blkid_put_cache(cache);
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/chattr.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/chattr.c
new file mode 100644
index 0000000..ae39d92
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/chattr.c
@@ -0,0 +1,220 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chattr.c		- Change file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * 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	- Ignore symlinks when working recursively (G M Sipe)
+ * 98/12/29	- Display version info only when -V specified (G M Sipe)
+ */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include "ext2fs/ext2_fs.h"
+
+#ifdef __GNUC__
+# define EXT2FS_ATTR(x) __attribute__(x)
+#else
+# define EXT2FS_ATTR(x)
+#endif
+
+#include "e2fsbb.h"
+#include "e2p/e2p.h"
+
+#define OPT_ADD 1
+#define OPT_REM 2
+#define OPT_SET 4
+#define OPT_SET_VER 8
+static int flags;
+static int recursive;
+
+static unsigned long version;
+
+static unsigned long af;
+static unsigned long rf;
+static unsigned long sf;
+
+struct flags_char {
+	unsigned long flag;
+	char optchar;
+};
+
+static const struct flags_char flags_array[] = {
+	{ EXT2_NOATIME_FL,      'A' },
+	{ EXT2_SYNC_FL,         'S' },
+	{ EXT2_DIRSYNC_FL,      'D' },
+	{ EXT2_APPEND_FL,       'a' },
+	{ EXT2_COMPR_FL,        'c' },
+	{ EXT2_NODUMP_FL,       'd' },
+	{ EXT2_IMMUTABLE_FL,    'i' },
+	{ EXT3_JOURNAL_DATA_FL, 'j' },
+	{ EXT2_SECRM_FL,        's' },
+	{ EXT2_UNRM_FL,         'u' },
+	{ EXT2_NOTAIL_FL,       't' },
+	{ EXT2_TOPDIR_FL,       'T' },
+	{ 0, 0 }
+};
+
+static unsigned long get_flag(char c)
+{
+	const struct flags_char *fp;
+	for (fp = flags_array; fp->flag; fp++)
+		if (fp->optchar == c)
+			return fp->flag;
+	bb_show_usage();
+	return 0;
+}
+
+static int decode_arg(char *arg)
+{
+	unsigned long *fl;
+	char opt = *arg++;
+
+	if (opt == '-') {
+		flags |= OPT_REM;
+		fl = &rf;
+	} else if (opt == '+') {
+		flags |= OPT_ADD;
+		fl = &af;
+	} else if (opt == '=') {
+		flags |= OPT_SET;
+		fl = &sf;
+	} else
+		return EOF;
+
+	for (; *arg; ++arg)
+		(*fl) |= get_flag(*arg);
+
+	return 1;
+}
+
+static int chattr_dir_proc(const char *, struct dirent *, void *);
+
+static void change_attributes(const char * name)
+{
+	unsigned long fsflags;
+	struct stat st;
+
+	if (lstat(name, &st) == -1) {
+		bb_error_msg("stat %s failed", name);
+		return;
+	}
+	if (S_ISLNK(st.st_mode) && recursive)
+		return;
+
+	/* Don't try to open device files, fifos etc.  We probably
+	 * ought to display an error if the file was explicitly given
+	 * on the command line (whether or not recursive was
+	 * requested).  */
+	if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
+		return;
+
+	if (flags & OPT_SET_VER)
+		if (fsetversion(name, version) == -1)
+			bb_error_msg("setting version on %s", name);
+
+	if (flags & OPT_SET) {
+		fsflags = sf;
+	} else {
+		if (fgetflags(name, &fsflags) == -1) {
+			bb_error_msg("reading flags on %s", name);
+			goto skip_setflags;
+		}
+		if (flags & OPT_REM)
+			fsflags &= ~rf;
+		if (flags & OPT_ADD)
+			fsflags |= af;
+		if (!S_ISDIR(st.st_mode))
+			fsflags &= ~EXT2_DIRSYNC_FL;
+	}
+	if (fsetflags(name, fsflags) == -1)
+		bb_error_msg("setting flags on %s", name);
+
+skip_setflags:
+	if (S_ISDIR(st.st_mode) && recursive)
+		iterate_on_dir(name, chattr_dir_proc, NULL);
+}
+
+static int chattr_dir_proc(const char *dir_name, struct dirent *de,
+			   void *private EXT2FS_ATTR((unused)))
+{
+	/*if (strcmp(de->d_name, ".") || strcmp(de->d_name, "..")) {*/
+	if (de->d_name[0] == '.'
+	 && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))
+	) {
+		char *path = concat_subpath_file(dir_name, de->d_name);
+		if (path) {
+			change_attributes(path);
+			free(path);
+		}
+	}
+	return 0;
+}
+
+int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chattr_main(int argc, char **argv)
+{
+	int i;
+	char *arg;
+
+	/* parse the args */
+	for (i = 1; i < argc; ++i) {
+		arg = argv[i];
+
+		/* take care of -R and -v <version> */
+		if (arg[0] == '-') {
+			if (arg[1] == 'R' && arg[2] == '\0') {
+				recursive = 1;
+				continue;
+			} else if (arg[1] == 'v' && arg[2] == '\0') {
+				char *tmp;
+				++i;
+				if (i >= argc)
+					bb_show_usage();
+				version = strtol(argv[i], &tmp, 0);
+				if (*tmp)
+					bb_error_msg_and_die("bad version '%s'", arg);
+				flags |= OPT_SET_VER;
+				continue;
+			}
+		}
+
+		if (decode_arg(arg) == EOF)
+			break;
+	}
+
+	/* run sanity checks on all the arguments given us */
+	if (i >= argc)
+		bb_show_usage();
+	if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM)))
+		bb_error_msg_and_die("= is incompatible with - and +");
+	if ((rf & af) != 0)
+		bb_error_msg_and_die("Can't set and unset a flag");
+	if (!flags)
+		bb_error_msg_and_die("Must use '-v', =, - or +");
+
+	/* now run chattr on all the files passed to us */
+	while (i < argc)
+		change_attributes(argv[i++]);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsbb.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsbb.h
new file mode 100644
index 0000000..d31c319
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsbb.h
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * File: e2fsbb.h
+ *
+ * Redefine a bunch of e2fsprogs stuff to use busybox routines
+ * instead.  This makes upgrade between e2fsprogs versions easy.
+ */
+
+#ifndef E2FSBB_H
+#define E2FSBB_H 1
+
+#include "libbb.h"
+
+/* version we've last synced against */
+#define E2FSPROGS_VERSION "1.38"
+#define E2FSPROGS_DATE "30-Jun-2005"
+
+typedef long errcode_t;
+#define ERRCODE_RANGE 8
+#define error_message(code) strerror((int) (code & ((1<<ERRCODE_RANGE)-1)))
+
+/* header defines */
+#define ENABLE_HTREE 1
+#define HAVE_ERRNO_H 1
+#define HAVE_EXT2_IOCTLS 1
+#define HAVE_LINUX_FD_H 1
+#define HAVE_MNTENT_H 1
+#define HAVE_NETINET_IN_H 1
+#define HAVE_NET_IF_H 1
+#define HAVE_SYS_IOCTL_H 1
+#define HAVE_SYS_MOUNT_H 1
+#define HAVE_SYS_QUEUE_H 1
+#define HAVE_SYS_STAT_H 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_UNISTD_H 1
+
+/* Endianness */
+#if BB_BIG_ENDIAN
+#define ENABLE_SWAPFS 1
+#define WORDS_BIGENDIAN 1
+#endif
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsck.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsck.c
new file mode 100644
index 0000000..8fffa7f
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsck.c
@@ -0,0 +1,13520 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * e2fsck
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ * Copyright (C) 2006 Garrett Kajmowicz
+ *
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ * Free Software License:
+ * All rights are reserved by the author, with the following exceptions:
+ * Permission is granted to freely reproduce and distribute this software,
+ * possibly in exchange for a fee, provided that this copyright notice appears
+ * intact. Permission is also granted to adapt this software to produce
+ * derivative works, as long as the modified versions carry this copyright
+ * notice and additional notices stating that the work has been modified.
+ * This source code may be translated into executable form and incorporated
+ * into proprietary software; there is no requirement for such software to
+ * contain a copyright notice related to this source.
+ *
+ * linux/fs/recovery  and linux/fs/revoke
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ *
+ * Copyright 1999-2000 Red Hat Software --- All Rights Reserved
+ *
+ * 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 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*/
+
+#define _(x) x
+#define N_(x) x
+
+/*
+ * Procedure declarations
+ */
+
+static void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf);
+
+/* pass1.c */
+static void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool);
+
+/* pass2.c */
+static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
+				    ext2_ino_t ino, char *buf);
+
+/* pass3.c */
+static int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t inode);
+static errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
+					 int num, int gauranteed_size);
+static ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix);
+static errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino,
+					   int adj);
+
+/* rehash.c */
+static void e2fsck_rehash_directories(e2fsck_t ctx);
+
+/* util.c */
+static void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
+				    const char *description);
+static int ask(e2fsck_t ctx, const char * string, int def);
+static void e2fsck_read_bitmaps(e2fsck_t ctx);
+static void preenhalt(e2fsck_t ctx);
+static void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
+			      struct ext2_inode * inode, const char * proc);
+static void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
+			       struct ext2_inode * inode, const char * proc);
+static blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs,
+			   const char *name, io_manager manager);
+
+/* unix.c */
+static void e2fsck_clear_progbar(e2fsck_t ctx);
+static int e2fsck_simple_progress(e2fsck_t ctx, const char *label,
+				  float percent, unsigned int dpynum);
+
+
+/*
+ * problem.h --- e2fsck problem error codes
+ */
+
+typedef __u32 problem_t;
+
+struct problem_context {
+	errcode_t       errcode;
+	ext2_ino_t      ino, ino2, dir;
+	struct ext2_inode *inode;
+	struct ext2_dir_entry *dirent;
+	blk_t           blk, blk2;
+	e2_blkcnt_t     blkcount;
+	int             group;
+	__u64           num;
+	const char      *str;
+};
+
+
+/*
+ * Function declarations
+ */
+static int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx);
+static int end_problem_latch(e2fsck_t ctx, int mask);
+static int set_latch_flags(int mask, int setflags, int clearflags);
+static void clear_problem_context(struct problem_context *ctx);
+
+/*
+ * Dictionary Abstract Data Type
+ * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net>
+ *
+ * dict.h v 1.22.2.6 2000/11/13 01:36:44 kaz
+ * kazlib_1_20
+ */
+
+#ifndef DICT_H
+#define DICT_H
+
+/*
+ * Blurb for inclusion into C++ translation units
+ */
+
+typedef unsigned long dictcount_t;
+#define DICTCOUNT_T_MAX ULONG_MAX
+
+/*
+ * The dictionary is implemented as a red-black tree
+ */
+
+typedef enum { dnode_red, dnode_black } dnode_color_t;
+
+typedef struct dnode_t {
+	struct dnode_t *dict_left;
+	struct dnode_t *dict_right;
+	struct dnode_t *dict_parent;
+	dnode_color_t dict_color;
+	const void *dict_key;
+	void *dict_data;
+} dnode_t;
+
+typedef int (*dict_comp_t)(const void *, const void *);
+typedef void (*dnode_free_t)(dnode_t *);
+
+typedef struct dict_t {
+	dnode_t dict_nilnode;
+	dictcount_t dict_nodecount;
+	dictcount_t dict_maxcount;
+	dict_comp_t dict_compare;
+	dnode_free_t dict_freenode;
+	int dict_dupes;
+} dict_t;
+
+typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *);
+
+typedef struct dict_load_t {
+	dict_t *dict_dictptr;
+	dnode_t dict_nilnode;
+} dict_load_t;
+
+#define dict_count(D) ((D)->dict_nodecount)
+#define dnode_get(N) ((N)->dict_data)
+#define dnode_getkey(N) ((N)->dict_key)
+
+#endif
+
+/*
+ * Compatibility header file for e2fsck which should be included
+ * instead of linux/jfs.h
+ *
+ * Copyright (C) 2000 Stephen C. Tweedie
+ */
+
+/*
+ * Pull in the definition of the e2fsck context structure
+ */
+
+struct buffer_head {
+	char            b_data[8192];
+	e2fsck_t        b_ctx;
+	io_channel      b_io;
+	int             b_size;
+	blk_t           b_blocknr;
+	int             b_dirty;
+	int             b_uptodate;
+	int             b_err;
+};
+
+
+#define K_DEV_FS        1
+#define K_DEV_JOURNAL   2
+
+#define lock_buffer(bh) do {} while (0)
+#define unlock_buffer(bh) do {} while (0)
+#define buffer_req(bh) 1
+#define do_readahead(journal, start) do {} while (0)
+
+static e2fsck_t e2fsck_global_ctx;  /* Try your very best not to use this! */
+
+typedef struct {
+	int     object_length;
+} kmem_cache_t;
+
+#define kmem_cache_alloc(cache,flags) malloc((cache)->object_length)
+
+/*
+ * We use the standard libext2fs portability tricks for inline
+ * functions.
+ */
+
+static kmem_cache_t * do_cache_create(int len)
+{
+	kmem_cache_t *new_cache;
+
+	new_cache = xmalloc(sizeof(*new_cache));
+	new_cache->object_length = len;
+	return new_cache;
+}
+
+static void do_cache_destroy(kmem_cache_t *cache)
+{
+	free(cache);
+}
+
+
+/*
+ * Dictionary Abstract Data Type
+ */
+
+
+/*
+ * These macros provide short convenient names for structure members,
+ * which are embellished with dict_ prefixes so that they are
+ * properly confined to the documented namespace. It's legal for a
+ * program which uses dict to define, for instance, a macro called ``parent''.
+ * Such a macro would interfere with the dnode_t struct definition.
+ * In general, highly portable and reusable C modules which expose their
+ * structures need to confine structure member names to well-defined spaces.
+ * The resulting identifiers aren't necessarily convenient to use, nor
+ * readable, in the implementation, however!
+ */
+
+#define left dict_left
+#define right dict_right
+#define parent dict_parent
+#define color dict_color
+#define key dict_key
+#define data dict_data
+
+#define nilnode dict_nilnode
+#define maxcount dict_maxcount
+#define compare dict_compare
+#define dupes dict_dupes
+
+#define dict_root(D) ((D)->nilnode.left)
+#define dict_nil(D) (&(D)->nilnode)
+
+static void dnode_free(dnode_t *node);
+
+/*
+ * Perform a ``left rotation'' adjustment on the tree.  The given node P and
+ * its right child C are rearranged so that the P instead becomes the left
+ * child of C.   The left subtree of C is inherited as the new right subtree
+ * for P.  The ordering of the keys within the tree is thus preserved.
+ */
+
+static void rotate_left(dnode_t *upper)
+{
+	dnode_t *lower, *lowleft, *upparent;
+
+	lower = upper->right;
+	upper->right = lowleft = lower->left;
+	lowleft->parent = upper;
+
+	lower->parent = upparent = upper->parent;
+
+	/* don't need to check for root node here because root->parent is
+	   the sentinel nil node, and root->parent->left points back to root */
+
+	if (upper == upparent->left) {
+		upparent->left = lower;
+	} else {
+		assert (upper == upparent->right);
+		upparent->right = lower;
+	}
+
+	lower->left = upper;
+	upper->parent = lower;
+}
+
+/*
+ * This operation is the ``mirror'' image of rotate_left. It is
+ * the same procedure, but with left and right interchanged.
+ */
+
+static void rotate_right(dnode_t *upper)
+{
+	dnode_t *lower, *lowright, *upparent;
+
+	lower = upper->left;
+	upper->left = lowright = lower->right;
+	lowright->parent = upper;
+
+	lower->parent = upparent = upper->parent;
+
+	if (upper == upparent->right) {
+		upparent->right = lower;
+	} else {
+		assert (upper == upparent->left);
+		upparent->left = lower;
+	}
+
+	lower->right = upper;
+	upper->parent = lower;
+}
+
+/*
+ * Do a postorder traversal of the tree rooted at the specified
+ * node and free everything under it.  Used by dict_free().
+ */
+
+static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil)
+{
+	if (node == nil)
+		return;
+	free_nodes(dict, node->left, nil);
+	free_nodes(dict, node->right, nil);
+	dict->dict_freenode(node);
+}
+
+/*
+ * Verify that the tree contains the given node. This is done by
+ * traversing all of the nodes and comparing their pointers to the
+ * given pointer. Returns 1 if the node is found, otherwise
+ * returns zero. It is intended for debugging purposes.
+ */
+
+static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node)
+{
+	if (root != nil) {
+		return root == node
+			|| verify_dict_has_node(nil, root->left, node)
+			|| verify_dict_has_node(nil, root->right, node);
+	}
+	return 0;
+}
+
+
+/*
+ * Select a different set of node allocator routines.
+ */
+
+static void dict_set_allocator(dict_t *dict, dnode_free_t fr)
+{
+	assert(dict_count(dict) == 0);
+	dict->dict_freenode = fr;
+}
+
+/*
+ * Free all the nodes in the dictionary by using the dictionary's
+ * installed free routine. The dictionary is emptied.
+ */
+
+static void dict_free_nodes(dict_t *dict)
+{
+	dnode_t *nil = dict_nil(dict), *root = dict_root(dict);
+	free_nodes(dict, root, nil);
+	dict->dict_nodecount = 0;
+	dict->nilnode.left = &dict->nilnode;
+	dict->nilnode.right = &dict->nilnode;
+}
+
+/*
+ * Initialize a user-supplied dictionary object.
+ */
+
+static dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp)
+{
+	dict->compare = comp;
+	dict->dict_freenode = dnode_free;
+	dict->dict_nodecount = 0;
+	dict->maxcount = maxcount;
+	dict->nilnode.left = &dict->nilnode;
+	dict->nilnode.right = &dict->nilnode;
+	dict->nilnode.parent = &dict->nilnode;
+	dict->nilnode.color = dnode_black;
+	dict->dupes = 0;
+	return dict;
+}
+
+/*
+ * Locate a node in the dictionary having the given key.
+ * If the node is not found, a null a pointer is returned (rather than
+ * a pointer that dictionary's nil sentinel node), otherwise a pointer to the
+ * located node is returned.
+ */
+
+static dnode_t *dict_lookup(dict_t *dict, const void *key)
+{
+	dnode_t *root = dict_root(dict);
+	dnode_t *nil = dict_nil(dict);
+	dnode_t *saved;
+	int result;
+
+	/* simple binary search adapted for trees that contain duplicate keys */
+
+	while (root != nil) {
+		result = dict->compare(key, root->key);
+		if (result < 0)
+			root = root->left;
+		else if (result > 0)
+			root = root->right;
+		else {
+			if (!dict->dupes) { /* no duplicates, return match          */
+				return root;
+			} else {            /* could be dupes, find leftmost one    */
+				do {
+					saved = root;
+					root = root->left;
+					while (root != nil && dict->compare(key, root->key))
+						root = root->right;
+				} while (root != nil);
+				return saved;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Insert a node into the dictionary. The node should have been
+ * initialized with a data field. All other fields are ignored.
+ * The behavior is undefined if the user attempts to insert into
+ * a dictionary that is already full (for which the dict_isfull()
+ * function returns true).
+ */
+
+static void dict_insert(dict_t *dict, dnode_t *node, const void *key)
+{
+	dnode_t *where = dict_root(dict), *nil = dict_nil(dict);
+	dnode_t *parent = nil, *uncle, *grandpa;
+	int result = -1;
+
+	node->key = key;
+
+	/* basic binary tree insert */
+
+	while (where != nil) {
+		parent = where;
+		result = dict->compare(key, where->key);
+		/* trap attempts at duplicate key insertion unless it's explicitly allowed */
+		assert(dict->dupes || result != 0);
+		if (result < 0)
+			where = where->left;
+		else
+			where = where->right;
+	}
+
+	assert(where == nil);
+
+	if (result < 0)
+		parent->left = node;
+	else
+		parent->right = node;
+
+	node->parent = parent;
+	node->left = nil;
+	node->right = nil;
+
+	dict->dict_nodecount++;
+
+	/* red black adjustments */
+
+	node->color = dnode_red;
+
+	while (parent->color == dnode_red) {
+		grandpa = parent->parent;
+		if (parent == grandpa->left) {
+			uncle = grandpa->right;
+			if (uncle->color == dnode_red) {    /* red parent, red uncle */
+				parent->color = dnode_black;
+				uncle->color = dnode_black;
+				grandpa->color = dnode_red;
+				node = grandpa;
+				parent = grandpa->parent;
+			} else {                            /* red parent, black uncle */
+				if (node == parent->right) {
+					rotate_left(parent);
+					parent = node;
+					assert (grandpa == parent->parent);
+					/* rotation between parent and child preserves grandpa */
+				}
+				parent->color = dnode_black;
+				grandpa->color = dnode_red;
+				rotate_right(grandpa);
+				break;
+			}
+		} else {        /* symmetric cases: parent == parent->parent->right */
+			uncle = grandpa->left;
+			if (uncle->color == dnode_red) {
+				parent->color = dnode_black;
+				uncle->color = dnode_black;
+				grandpa->color = dnode_red;
+				node = grandpa;
+				parent = grandpa->parent;
+			} else {
+				if (node == parent->left) {
+					rotate_right(parent);
+					parent = node;
+					assert (grandpa == parent->parent);
+				}
+				parent->color = dnode_black;
+				grandpa->color = dnode_red;
+				rotate_left(grandpa);
+				break;
+			}
+		}
+	}
+
+	dict_root(dict)->color = dnode_black;
+}
+
+/*
+ * Allocate a node using the dictionary's allocator routine, give it
+ * the data item.
+ */
+
+static dnode_t *dnode_init(dnode_t *dnode, void *data)
+{
+	dnode->data = data;
+	dnode->parent = NULL;
+	dnode->left = NULL;
+	dnode->right = NULL;
+	return dnode;
+}
+
+static int dict_alloc_insert(dict_t *dict, const void *key, void *data)
+{
+	dnode_t *node = xmalloc(sizeof(dnode_t));
+
+	dnode_init(node, data);
+	dict_insert(dict, node, key);
+	return 1;
+}
+
+/*
+ * Return the node with the lowest (leftmost) key. If the dictionary is empty
+ * (that is, dict_isempty(dict) returns 1) a null pointer is returned.
+ */
+
+static dnode_t *dict_first(dict_t *dict)
+{
+	dnode_t *nil = dict_nil(dict), *root = dict_root(dict), *left;
+
+	if (root != nil)
+		while ((left = root->left) != nil)
+			root = left;
+
+	return (root == nil) ? NULL : root;
+}
+
+/*
+ * Return the given node's successor node---the node which has the
+ * 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.
+ */
+
+static dnode_t *dict_next(dict_t *dict, dnode_t *curr)
+{
+	dnode_t *nil = dict_nil(dict), *parent, *left;
+
+	if (curr->right != nil) {
+		curr = curr->right;
+		while ((left = curr->left) != nil)
+			curr = left;
+		return curr;
+	}
+
+	parent = curr->parent;
+
+	while (parent != nil && curr == parent->right) {
+		curr = parent;
+		parent = curr->parent;
+	}
+
+	return (parent == nil) ? NULL : parent;
+}
+
+
+static void dnode_free(dnode_t *node)
+{
+	free(node);
+}
+
+
+#undef left
+#undef right
+#undef parent
+#undef color
+#undef key
+#undef data
+
+#undef nilnode
+#undef maxcount
+#undef compare
+#undef dupes
+
+
+/*
+ * dirinfo.c --- maintains the directory information table for e2fsck.
+ */
+
+/*
+ * This subroutine is called during pass1 to create a directory info
+ * entry.  During pass1, the passed-in parent is 0; it will get filled
+ * in during pass2.
+ */
+static void e2fsck_add_dir_info(e2fsck_t ctx, ext2_ino_t ino, ext2_ino_t parent)
+{
+	struct dir_info *dir;
+	int             i, j;
+	ext2_ino_t      num_dirs;
+	errcode_t       retval;
+	unsigned long   old_size;
+
+	if (!ctx->dir_info) {
+		ctx->dir_info_count = 0;
+		retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
+		if (retval)
+			num_dirs = 1024;        /* Guess */
+		ctx->dir_info_size = num_dirs + 10;
+		ctx->dir_info  = (struct dir_info *)
+			e2fsck_allocate_memory(ctx, ctx->dir_info_size
+					       * sizeof (struct dir_info),
+					       "directory map");
+	}
+
+	if (ctx->dir_info_count >= ctx->dir_info_size) {
+		old_size = ctx->dir_info_size * sizeof(struct dir_info);
+		ctx->dir_info_size += 10;
+		retval = ext2fs_resize_mem(old_size, ctx->dir_info_size *
+					   sizeof(struct dir_info),
+					   &ctx->dir_info);
+		if (retval) {
+			ctx->dir_info_size -= 10;
+			return;
+		}
+	}
+
+	/*
+	 * Normally, add_dir_info is called with each inode in
+	 * sequential order; but once in a while (like when pass 3
+	 * needs to recreate the root directory or lost+found
+	 * directory) it is called out of order.  In those cases, we
+	 * need to move the dir_info entries down to make room, since
+	 * the dir_info array needs to be sorted by inode number for
+	 * get_dir_info()'s sake.
+	 */
+	if (ctx->dir_info_count &&
+	    ctx->dir_info[ctx->dir_info_count-1].ino >= ino) {
+		for (i = ctx->dir_info_count-1; i > 0; i--)
+			if (ctx->dir_info[i-1].ino < ino)
+				break;
+		dir = &ctx->dir_info[i];
+		if (dir->ino != ino)
+			for (j = ctx->dir_info_count++; j > i; j--)
+				ctx->dir_info[j] = ctx->dir_info[j-1];
+	} else
+		dir = &ctx->dir_info[ctx->dir_info_count++];
+
+	dir->ino = ino;
+	dir->dotdot = parent;
+	dir->parent = parent;
+}
+
+/*
+ * get_dir_info() --- given an inode number, try to find the directory
+ * information entry for it.
+ */
+static struct dir_info *e2fsck_get_dir_info(e2fsck_t ctx, ext2_ino_t ino)
+{
+	int     low, high, mid;
+
+	low = 0;
+	high = ctx->dir_info_count-1;
+	if (!ctx->dir_info)
+		return 0;
+	if (ino == ctx->dir_info[low].ino)
+		return &ctx->dir_info[low];
+	if  (ino == ctx->dir_info[high].ino)
+		return &ctx->dir_info[high];
+
+	while (low < high) {
+		mid = (low+high)/2;
+		if (mid == low || mid == high)
+			break;
+		if (ino == ctx->dir_info[mid].ino)
+			return &ctx->dir_info[mid];
+		if (ino < ctx->dir_info[mid].ino)
+			high = mid;
+		else
+			low = mid;
+	}
+	return 0;
+}
+
+/*
+ * Free the dir_info structure when it isn't needed any more.
+ */
+static void e2fsck_free_dir_info(e2fsck_t ctx)
+{
+	ext2fs_free_mem(&ctx->dir_info);
+	ctx->dir_info_size = 0;
+	ctx->dir_info_count = 0;
+}
+
+/*
+ * Return the count of number of directories in the dir_info structure
+ */
+static int e2fsck_get_num_dirinfo(e2fsck_t ctx)
+{
+	return ctx->dir_info_count;
+}
+
+/*
+ * A simple interator function
+ */
+static struct dir_info *e2fsck_dir_info_iter(e2fsck_t ctx, int *control)
+{
+	if (*control >= ctx->dir_info_count)
+		return 0;
+
+	return ctx->dir_info + (*control)++;
+}
+
+/*
+ * dirinfo.c --- maintains the directory information table for e2fsck.
+ *
+ */
+
+#ifdef ENABLE_HTREE
+
+/*
+ * This subroutine is called during pass1 to create a directory info
+ * entry.  During pass1, the passed-in parent is 0; it will get filled
+ * in during pass2.
+ */
+static void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
+{
+	struct dx_dir_info *dir;
+	int             i, j;
+	errcode_t       retval;
+	unsigned long   old_size;
+
+	if (!ctx->dx_dir_info) {
+		ctx->dx_dir_info_count = 0;
+		ctx->dx_dir_info_size = 100; /* Guess */
+		ctx->dx_dir_info  = (struct dx_dir_info *)
+			e2fsck_allocate_memory(ctx, ctx->dx_dir_info_size
+					       * sizeof (struct dx_dir_info),
+					       "directory map");
+	}
+
+	if (ctx->dx_dir_info_count >= ctx->dx_dir_info_size) {
+		old_size = ctx->dx_dir_info_size * sizeof(struct dx_dir_info);
+		ctx->dx_dir_info_size += 10;
+		retval = ext2fs_resize_mem(old_size, ctx->dx_dir_info_size *
+					   sizeof(struct dx_dir_info),
+					   &ctx->dx_dir_info);
+		if (retval) {
+			ctx->dx_dir_info_size -= 10;
+			return;
+		}
+	}
+
+	/*
+	 * Normally, add_dx_dir_info is called with each inode in
+	 * sequential order; but once in a while (like when pass 3
+	 * needs to recreate the root directory or lost+found
+	 * directory) it is called out of order.  In those cases, we
+	 * need to move the dx_dir_info entries down to make room, since
+	 * the dx_dir_info array needs to be sorted by inode number for
+	 * get_dx_dir_info()'s sake.
+	 */
+	if (ctx->dx_dir_info_count &&
+	    ctx->dx_dir_info[ctx->dx_dir_info_count-1].ino >= ino) {
+		for (i = ctx->dx_dir_info_count-1; i > 0; i--)
+			if (ctx->dx_dir_info[i-1].ino < ino)
+				break;
+		dir = &ctx->dx_dir_info[i];
+		if (dir->ino != ino)
+			for (j = ctx->dx_dir_info_count++; j > i; j--)
+				ctx->dx_dir_info[j] = ctx->dx_dir_info[j-1];
+	} else
+		dir = &ctx->dx_dir_info[ctx->dx_dir_info_count++];
+
+	dir->ino = ino;
+	dir->numblocks = num_blocks;
+	dir->hashversion = 0;
+	dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
+				       * sizeof (struct dx_dirblock_info),
+				       "dx_block info array");
+}
+
+/*
+ * get_dx_dir_info() --- given an inode number, try to find the directory
+ * information entry for it.
+ */
+static struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino)
+{
+	int     low, high, mid;
+
+	low = 0;
+	high = ctx->dx_dir_info_count-1;
+	if (!ctx->dx_dir_info)
+		return 0;
+	if (ino == ctx->dx_dir_info[low].ino)
+		return &ctx->dx_dir_info[low];
+	if  (ino == ctx->dx_dir_info[high].ino)
+		return &ctx->dx_dir_info[high];
+
+	while (low < high) {
+		mid = (low+high)/2;
+		if (mid == low || mid == high)
+			break;
+		if (ino == ctx->dx_dir_info[mid].ino)
+			return &ctx->dx_dir_info[mid];
+		if (ino < ctx->dx_dir_info[mid].ino)
+			high = mid;
+		else
+			low = mid;
+	}
+	return 0;
+}
+
+/*
+ * Free the dx_dir_info structure when it isn't needed any more.
+ */
+static void e2fsck_free_dx_dir_info(e2fsck_t ctx)
+{
+	int     i;
+	struct dx_dir_info *dir;
+
+	if (ctx->dx_dir_info) {
+		dir = ctx->dx_dir_info;
+		for (i=0; i < ctx->dx_dir_info_count; i++) {
+			ext2fs_free_mem(&dir->dx_block);
+		}
+		ext2fs_free_mem(&ctx->dx_dir_info);
+	}
+	ctx->dx_dir_info_size = 0;
+	ctx->dx_dir_info_count = 0;
+}
+
+/*
+ * A simple interator function
+ */
+static struct dx_dir_info *e2fsck_dx_dir_info_iter(e2fsck_t ctx, int *control)
+{
+	if (*control >= ctx->dx_dir_info_count)
+		return 0;
+
+	return ctx->dx_dir_info + (*control)++;
+}
+
+#endif /* ENABLE_HTREE */
+/*
+ * e2fsck.c - a consistency checker for the new extended file system.
+ *
+ */
+
+/*
+ * This function allocates an e2fsck context
+ */
+static errcode_t e2fsck_allocate_context(e2fsck_t *ret)
+{
+	e2fsck_t        context;
+	errcode_t       retval;
+
+	retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &context);
+	if (retval)
+		return retval;
+
+	memset(context, 0, sizeof(struct e2fsck_struct));
+
+	context->process_inode_size = 256;
+	context->ext_attr_ver = 2;
+
+	*ret = context;
+	return 0;
+}
+
+struct ea_refcount_el {
+	blk_t   ea_blk;
+	int     ea_count;
+};
+
+struct ea_refcount {
+	blk_t           count;
+	blk_t           size;
+	blk_t           cursor;
+	struct ea_refcount_el   *list;
+};
+
+static void ea_refcount_free(ext2_refcount_t refcount)
+{
+	if (!refcount)
+		return;
+
+	ext2fs_free_mem(&refcount->list);
+	ext2fs_free_mem(&refcount);
+}
+
+/*
+ * This function resets an e2fsck context; it is called when e2fsck
+ * needs to be restarted.
+ */
+static errcode_t e2fsck_reset_context(e2fsck_t ctx)
+{
+	ctx->flags = 0;
+	ctx->lost_and_found = 0;
+	ctx->bad_lost_and_found = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_used_map);
+	ctx->inode_used_map = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_dir_map);
+	ctx->inode_dir_map = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_reg_map);
+	ctx->inode_reg_map = 0;
+	ext2fs_free_block_bitmap(ctx->block_found_map);
+	ctx->block_found_map = 0;
+	ext2fs_free_icount(ctx->inode_link_info);
+	ctx->inode_link_info = 0;
+	if (ctx->journal_io) {
+		if (ctx->fs && ctx->fs->io != ctx->journal_io)
+			io_channel_close(ctx->journal_io);
+		ctx->journal_io = 0;
+	}
+	if (ctx->fs) {
+		ext2fs_free_dblist(ctx->fs->dblist);
+		ctx->fs->dblist = 0;
+	}
+	e2fsck_free_dir_info(ctx);
+#ifdef ENABLE_HTREE
+	e2fsck_free_dx_dir_info(ctx);
+#endif
+	ea_refcount_free(ctx->refcount);
+	ctx->refcount = 0;
+	ea_refcount_free(ctx->refcount_extra);
+	ctx->refcount_extra = 0;
+	ext2fs_free_block_bitmap(ctx->block_dup_map);
+	ctx->block_dup_map = 0;
+	ext2fs_free_block_bitmap(ctx->block_ea_map);
+	ctx->block_ea_map = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_bad_map);
+	ctx->inode_bad_map = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
+	ctx->inode_imagic_map = 0;
+	ext2fs_u32_list_free(ctx->dirs_to_hash);
+	ctx->dirs_to_hash = 0;
+
+	/*
+	 * Clear the array of invalid meta-data flags
+	 */
+	ext2fs_free_mem(&ctx->invalid_inode_bitmap_flag);
+	ext2fs_free_mem(&ctx->invalid_block_bitmap_flag);
+	ext2fs_free_mem(&ctx->invalid_inode_table_flag);
+
+	/* Clear statistic counters */
+	ctx->fs_directory_count = 0;
+	ctx->fs_regular_count = 0;
+	ctx->fs_blockdev_count = 0;
+	ctx->fs_chardev_count = 0;
+	ctx->fs_links_count = 0;
+	ctx->fs_symlinks_count = 0;
+	ctx->fs_fast_symlinks_count = 0;
+	ctx->fs_fifo_count = 0;
+	ctx->fs_total_count = 0;
+	ctx->fs_sockets_count = 0;
+	ctx->fs_ind_count = 0;
+	ctx->fs_dind_count = 0;
+	ctx->fs_tind_count = 0;
+	ctx->fs_fragmented = 0;
+	ctx->large_files = 0;
+
+	/* Reset the superblock to the user's requested value */
+	ctx->superblock = ctx->use_superblock;
+
+	return 0;
+}
+
+static void e2fsck_free_context(e2fsck_t ctx)
+{
+	if (!ctx)
+		return;
+
+	e2fsck_reset_context(ctx);
+	if (ctx->blkid)
+		blkid_put_cache(ctx->blkid);
+
+	ext2fs_free_mem(&ctx);
+}
+
+/*
+ * ea_refcount.c
+ */
+
+/*
+ * The strategy we use for keeping track of EA refcounts is as
+ * follows.  We keep a sorted array of first EA blocks and its
+ * reference counts.  Once the refcount has dropped to zero, it is
+ * removed from the array to save memory space.  Once the EA block is
+ * checked, its bit is set in the block_ea_map bitmap.
+ */
+
+
+static errcode_t ea_refcount_create(int size, ext2_refcount_t *ret)
+{
+	ext2_refcount_t refcount;
+	errcode_t       retval;
+	size_t          bytes;
+
+	retval = ext2fs_get_mem(sizeof(struct ea_refcount), &refcount);
+	if (retval)
+		return retval;
+	memset(refcount, 0, sizeof(struct ea_refcount));
+
+	if (!size)
+		size = 500;
+	refcount->size = size;
+	bytes = (size_t) (size * sizeof(struct ea_refcount_el));
+#ifdef DEBUG
+	printf("Refcount allocated %d entries, %d bytes.\n",
+	       refcount->size, bytes);
+#endif
+	retval = ext2fs_get_mem(bytes, &refcount->list);
+	if (retval)
+		goto errout;
+	memset(refcount->list, 0, bytes);
+
+	refcount->count = 0;
+	refcount->cursor = 0;
+
+	*ret = refcount;
+	return 0;
+
+errout:
+	ea_refcount_free(refcount);
+	return retval;
+}
+
+/*
+ * collapse_refcount() --- go through the refcount array, and get rid
+ * of any count == zero entries
+ */
+static void refcount_collapse(ext2_refcount_t refcount)
+{
+	unsigned int    i, j;
+	struct ea_refcount_el   *list;
+
+	list = refcount->list;
+	for (i = 0, j = 0; i < refcount->count; i++) {
+		if (list[i].ea_count) {
+			if (i != j)
+				list[j] = list[i];
+			j++;
+		}
+	}
+#if defined(DEBUG) || defined(TEST_PROGRAM)
+	printf("Refcount_collapse: size was %d, now %d\n",
+	       refcount->count, j);
+#endif
+	refcount->count = j;
+}
+
+
+/*
+ * insert_refcount_el() --- Insert a new entry into the sorted list at a
+ *      specified position.
+ */
+static struct ea_refcount_el *insert_refcount_el(ext2_refcount_t refcount,
+						 blk_t blk, int pos)
+{
+	struct ea_refcount_el   *el;
+	errcode_t               retval;
+	blk_t                   new_size = 0;
+	int                     num;
+
+	if (refcount->count >= refcount->size) {
+		new_size = refcount->size + 100;
+#ifdef DEBUG
+		printf("Reallocating refcount %d entries...\n", new_size);
+#endif
+		retval = ext2fs_resize_mem((size_t) refcount->size *
+					   sizeof(struct ea_refcount_el),
+					   (size_t) new_size *
+					   sizeof(struct ea_refcount_el),
+					   &refcount->list);
+		if (retval)
+			return 0;
+		refcount->size = new_size;
+	}
+	num = (int) refcount->count - pos;
+	if (num < 0)
+		return 0;       /* should never happen */
+	if (num) {
+		memmove(&refcount->list[pos+1], &refcount->list[pos],
+			sizeof(struct ea_refcount_el) * num);
+	}
+	refcount->count++;
+	el = &refcount->list[pos];
+	el->ea_count = 0;
+	el->ea_blk = blk;
+	return el;
+}
+
+
+/*
+ * get_refcount_el() --- given an block number, try to find refcount
+ *      information in the sorted list.  If the create flag is set,
+ *      and we can't find an entry, create one in the sorted list.
+ */
+static struct ea_refcount_el *get_refcount_el(ext2_refcount_t refcount,
+					      blk_t blk, int create)
+{
+	float   range;
+	int     low, high, mid;
+	blk_t   lowval, highval;
+
+	if (!refcount || !refcount->list)
+		return 0;
+retry:
+	low = 0;
+	high = (int) refcount->count-1;
+	if (create && ((refcount->count == 0) ||
+		       (blk > refcount->list[high].ea_blk))) {
+		if (refcount->count >= refcount->size)
+			refcount_collapse(refcount);
+
+		return insert_refcount_el(refcount, blk,
+					  (unsigned) refcount->count);
+	}
+	if (refcount->count == 0)
+		return 0;
+
+	if (refcount->cursor >= refcount->count)
+		refcount->cursor = 0;
+	if (blk == refcount->list[refcount->cursor].ea_blk)
+		return &refcount->list[refcount->cursor++];
+#ifdef DEBUG
+	printf("Non-cursor get_refcount_el: %u\n", blk);
+#endif
+	while (low <= high) {
+		if (low == high)
+			mid = low;
+		else {
+			/* Interpolate for efficiency */
+			lowval = refcount->list[low].ea_blk;
+			highval = refcount->list[high].ea_blk;
+
+			if (blk < lowval)
+				range = 0;
+			else if (blk > highval)
+				range = 1;
+			else
+				range = ((float) (blk - lowval)) /
+					(highval - lowval);
+			mid = low + ((int) (range * (high-low)));
+		}
+
+		if (blk == refcount->list[mid].ea_blk) {
+			refcount->cursor = mid+1;
+			return &refcount->list[mid];
+		}
+		if (blk < refcount->list[mid].ea_blk)
+			high = mid-1;
+		else
+			low = mid+1;
+	}
+	/*
+	 * If we need to create a new entry, it should be right at
+	 * low (where high will be left at low-1).
+	 */
+	if (create) {
+		if (refcount->count >= refcount->size) {
+			refcount_collapse(refcount);
+			if (refcount->count < refcount->size)
+				goto retry;
+		}
+		return insert_refcount_el(refcount, blk, low);
+	}
+	return 0;
+}
+
+static errcode_t
+ea_refcount_increment(ext2_refcount_t refcount, blk_t blk, int *ret)
+{
+	struct ea_refcount_el   *el;
+
+	el = get_refcount_el(refcount, blk, 1);
+	if (!el)
+		return EXT2_ET_NO_MEMORY;
+	el->ea_count++;
+
+	if (ret)
+		*ret = el->ea_count;
+	return 0;
+}
+
+static errcode_t
+ea_refcount_decrement(ext2_refcount_t refcount, blk_t blk, int *ret)
+{
+	struct ea_refcount_el   *el;
+
+	el = get_refcount_el(refcount, blk, 0);
+	if (!el || el->ea_count == 0)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	el->ea_count--;
+
+	if (ret)
+		*ret = el->ea_count;
+	return 0;
+}
+
+static errcode_t
+ea_refcount_store(ext2_refcount_t refcount, blk_t blk, int count)
+{
+	struct ea_refcount_el   *el;
+
+	/*
+	 * Get the refcount element
+	 */
+	el = get_refcount_el(refcount, blk, count ? 1 : 0);
+	if (!el)
+		return count ? EXT2_ET_NO_MEMORY : 0;
+	el->ea_count = count;
+	return 0;
+}
+
+static inline void ea_refcount_intr_begin(ext2_refcount_t refcount)
+{
+	refcount->cursor = 0;
+}
+
+
+static blk_t ea_refcount_intr_next(ext2_refcount_t refcount, int *ret)
+{
+	struct ea_refcount_el   *list;
+
+	while (1) {
+		if (refcount->cursor >= refcount->count)
+			return 0;
+		list = refcount->list;
+		if (list[refcount->cursor].ea_count) {
+			if (ret)
+				*ret = list[refcount->cursor].ea_count;
+			return list[refcount->cursor++].ea_blk;
+		}
+		refcount->cursor++;
+	}
+}
+
+
+/*
+ * ehandler.c --- handle bad block errors which come up during the
+ *      course of an e2fsck session.
+ */
+
+
+static const char *operation;
+
+static errcode_t
+e2fsck_handle_read_error(io_channel channel, unsigned long block, int count,
+			 void *data, size_t size FSCK_ATTR((unused)),
+			 int actual FSCK_ATTR((unused)), errcode_t error)
+{
+	int     i;
+	char    *p;
+	ext2_filsys fs = (ext2_filsys) channel->app_data;
+	e2fsck_t ctx;
+
+	ctx = (e2fsck_t) fs->priv_data;
+
+	/*
+	 * If more than one block was read, try reading each block
+	 * separately.  We could use the actual bytes read to figure
+	 * out where to start, but we don't bother.
+	 */
+	if (count > 1) {
+		p = (char *) data;
+		for (i=0; i < count; i++, p += channel->block_size, block++) {
+			error = io_channel_read_blk(channel, block,
+						    1, p);
+			if (error)
+				return error;
+		}
+		return 0;
+	}
+	if (operation)
+		printf(_("Error reading block %lu (%s) while %s.  "), block,
+		       error_message(error), operation);
+	else
+		printf(_("Error reading block %lu (%s).  "), block,
+		       error_message(error));
+	preenhalt(ctx);
+	if (ask(ctx, _("Ignore error"), 1)) {
+		if (ask(ctx, _("Force rewrite"), 1))
+			io_channel_write_blk(channel, block, 1, data);
+		return 0;
+	}
+
+	return error;
+}
+
+static errcode_t
+e2fsck_handle_write_error(io_channel channel, unsigned long block, int count,
+			const void *data, size_t size FSCK_ATTR((unused)),
+			int actual FSCK_ATTR((unused)), errcode_t error)
+{
+	int             i;
+	const char      *p;
+	ext2_filsys fs = (ext2_filsys) channel->app_data;
+	e2fsck_t ctx;
+
+	ctx = (e2fsck_t) fs->priv_data;
+
+	/*
+	 * If more than one block was written, try writing each block
+	 * separately.  We could use the actual bytes read to figure
+	 * out where to start, but we don't bother.
+	 */
+	if (count > 1) {
+		p = (const char *) data;
+		for (i=0; i < count; i++, p += channel->block_size, block++) {
+			error = io_channel_write_blk(channel, block,
+						     1, p);
+			if (error)
+				return error;
+		}
+		return 0;
+	}
+
+	if (operation)
+		printf(_("Error writing block %lu (%s) while %s.  "), block,
+		       error_message(error), operation);
+	else
+		printf(_("Error writing block %lu (%s).  "), block,
+		       error_message(error));
+	preenhalt(ctx);
+	if (ask(ctx, _("Ignore error"), 1))
+		return 0;
+
+	return error;
+}
+
+static const char *ehandler_operation(const char *op)
+{
+	const char *ret = operation;
+
+	operation = op;
+	return ret;
+}
+
+static void ehandler_init(io_channel channel)
+{
+	channel->read_error = e2fsck_handle_read_error;
+	channel->write_error = e2fsck_handle_write_error;
+}
+
+/*
+ * journal.c --- code for handling the "ext3" journal
+ *
+ * Copyright (C) 2000 Andreas Dilger
+ * Copyright (C) 2000 Theodore Ts'o
+ *
+ * Parts of the code are based on fs/jfs/journal.c by Stephen C. Tweedie
+ * Copyright (C) 1999 Red Hat Software
+ *
+ * This file may be redistributed under the terms of the
+ * GNU General Public License version 2 or at your discretion
+ * any later version.
+ */
+
+/*
+ * Define USE_INODE_IO to use the inode_io.c / fileio.c codepaths.
+ * This creates a larger static binary, and a smaller binary using
+ * shared libraries.  It's also probably slightly less CPU-efficient,
+ * which is why it's not on by default.  But, it's a good way of
+ * testing the functions in inode_io.c and fileio.c.
+ */
+#undef USE_INODE_IO
+
+/* Kernel compatibility functions for handling the journal.  These allow us
+ * to use the recovery.c file virtually unchanged from the kernel, so we
+ * don't have to do much to keep kernel and user recovery in sync.
+ */
+static int journal_bmap(journal_t *journal, blk_t block, unsigned long *phys)
+{
+#ifdef USE_INODE_IO
+	*phys = block;
+	return 0;
+#else
+	struct inode    *inode = journal->j_inode;
+	errcode_t       retval;
+	blk_t           pblk;
+
+	if (!inode) {
+		*phys = block;
+		return 0;
+	}
+
+	retval= ext2fs_bmap(inode->i_ctx->fs, inode->i_ino,
+			    &inode->i_ext2, NULL, 0, block, &pblk);
+	*phys = pblk;
+	return retval;
+#endif
+}
+
+static struct buffer_head *getblk(kdev_t kdev, blk_t blocknr, int blocksize)
+{
+	struct buffer_head *bh;
+
+	bh = e2fsck_allocate_memory(kdev->k_ctx, sizeof(*bh), "block buffer");
+	if (!bh)
+		return NULL;
+
+	bh->b_ctx = kdev->k_ctx;
+	if (kdev->k_dev == K_DEV_FS)
+		bh->b_io = kdev->k_ctx->fs->io;
+	else
+		bh->b_io = kdev->k_ctx->journal_io;
+	bh->b_size = blocksize;
+	bh->b_blocknr = blocknr;
+
+	return bh;
+}
+
+static void sync_blockdev(kdev_t kdev)
+{
+	io_channel      io;
+
+	if (kdev->k_dev == K_DEV_FS)
+		io = kdev->k_ctx->fs->io;
+	else
+		io = kdev->k_ctx->journal_io;
+
+	io_channel_flush(io);
+}
+
+static void ll_rw_block(int rw, int nr, struct buffer_head *bhp[])
+{
+	int retval;
+	struct buffer_head *bh;
+
+	for (; nr > 0; --nr) {
+		bh = *bhp++;
+		if (rw == READ && !bh->b_uptodate) {
+			retval = io_channel_read_blk(bh->b_io,
+						     bh->b_blocknr,
+						     1, bh->b_data);
+			if (retval) {
+				bb_error_msg("while reading block %lu",
+					(unsigned long) bh->b_blocknr);
+				bh->b_err = retval;
+				continue;
+			}
+			bh->b_uptodate = 1;
+		} else if (rw == WRITE && bh->b_dirty) {
+			retval = io_channel_write_blk(bh->b_io,
+						      bh->b_blocknr,
+						      1, bh->b_data);
+			if (retval) {
+				bb_error_msg("while writing block %lu",
+					(unsigned long) bh->b_blocknr);
+				bh->b_err = retval;
+				continue;
+			}
+			bh->b_dirty = 0;
+			bh->b_uptodate = 1;
+		}
+	}
+}
+
+static void mark_buffer_dirty(struct buffer_head *bh)
+{
+	bh->b_dirty = 1;
+}
+
+static inline void mark_buffer_clean(struct buffer_head * bh)
+{
+	bh->b_dirty = 0;
+}
+
+static void brelse(struct buffer_head *bh)
+{
+	if (bh->b_dirty)
+		ll_rw_block(WRITE, 1, &bh);
+	ext2fs_free_mem(&bh);
+}
+
+static int buffer_uptodate(struct buffer_head *bh)
+{
+	return bh->b_uptodate;
+}
+
+static inline void mark_buffer_uptodate(struct buffer_head *bh, int val)
+{
+	bh->b_uptodate = val;
+}
+
+static void wait_on_buffer(struct buffer_head *bh)
+{
+	if (!bh->b_uptodate)
+		ll_rw_block(READ, 1, &bh);
+}
+
+
+static void e2fsck_clear_recover(e2fsck_t ctx, int error)
+{
+	ctx->fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER;
+
+	/* if we had an error doing journal recovery, we need a full fsck */
+	if (error)
+		ctx->fs->super->s_state &= ~EXT2_VALID_FS;
+	ext2fs_mark_super_dirty(ctx->fs);
+}
+
+static errcode_t e2fsck_get_journal(e2fsck_t ctx, journal_t **ret_journal)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	struct ext2_super_block jsuper;
+	struct problem_context  pctx;
+	struct buffer_head      *bh;
+	struct inode            *j_inode = NULL;
+	struct kdev_s           *dev_fs = NULL, *dev_journal;
+	const char              *journal_name = NULL;
+	journal_t               *journal = NULL;
+	errcode_t               retval = 0;
+	io_manager              io_ptr = 0;
+	unsigned long           start = 0;
+	blk_t                   blk;
+	int                     ext_journal = 0;
+	int                     tried_backup_jnl = 0;
+	int                     i;
+
+	clear_problem_context(&pctx);
+
+	journal = e2fsck_allocate_memory(ctx, sizeof(journal_t), "journal");
+	if (!journal) {
+		return EXT2_ET_NO_MEMORY;
+	}
+
+	dev_fs = e2fsck_allocate_memory(ctx, 2*sizeof(struct kdev_s), "kdev");
+	if (!dev_fs) {
+		retval = EXT2_ET_NO_MEMORY;
+		goto errout;
+	}
+	dev_journal = dev_fs+1;
+
+	dev_fs->k_ctx = dev_journal->k_ctx = ctx;
+	dev_fs->k_dev = K_DEV_FS;
+	dev_journal->k_dev = K_DEV_JOURNAL;
+
+	journal->j_dev = dev_journal;
+	journal->j_fs_dev = dev_fs;
+	journal->j_inode = NULL;
+	journal->j_blocksize = ctx->fs->blocksize;
+
+	if (uuid_is_null(sb->s_journal_uuid)) {
+		if (!sb->s_journal_inum)
+			return EXT2_ET_BAD_INODE_NUM;
+		j_inode = e2fsck_allocate_memory(ctx, sizeof(*j_inode),
+						 "journal inode");
+		if (!j_inode) {
+			retval = EXT2_ET_NO_MEMORY;
+			goto errout;
+		}
+
+		j_inode->i_ctx = ctx;
+		j_inode->i_ino = sb->s_journal_inum;
+
+		if ((retval = ext2fs_read_inode(ctx->fs,
+						sb->s_journal_inum,
+						&j_inode->i_ext2))) {
+		try_backup_journal:
+			if (sb->s_jnl_backup_type != EXT3_JNL_BACKUP_BLOCKS ||
+			    tried_backup_jnl)
+				goto errout;
+			memset(&j_inode->i_ext2, 0, sizeof(struct ext2_inode));
+			memcpy(&j_inode->i_ext2.i_block[0], sb->s_jnl_blocks,
+			       EXT2_N_BLOCKS*4);
+			j_inode->i_ext2.i_size = sb->s_jnl_blocks[16];
+			j_inode->i_ext2.i_links_count = 1;
+			j_inode->i_ext2.i_mode = LINUX_S_IFREG | 0600;
+			tried_backup_jnl++;
+		}
+		if (!j_inode->i_ext2.i_links_count ||
+		    !LINUX_S_ISREG(j_inode->i_ext2.i_mode)) {
+			retval = EXT2_ET_NO_JOURNAL;
+			goto try_backup_journal;
+		}
+		if (j_inode->i_ext2.i_size / journal->j_blocksize <
+		    JFS_MIN_JOURNAL_BLOCKS) {
+			retval = EXT2_ET_JOURNAL_TOO_SMALL;
+			goto try_backup_journal;
+		}
+		for (i=0; i < EXT2_N_BLOCKS; i++) {
+			blk = j_inode->i_ext2.i_block[i];
+			if (!blk) {
+				if (i < EXT2_NDIR_BLOCKS) {
+					retval = EXT2_ET_JOURNAL_TOO_SMALL;
+					goto try_backup_journal;
+				}
+				continue;
+			}
+			if (blk < sb->s_first_data_block ||
+			    blk >= sb->s_blocks_count) {
+				retval = EXT2_ET_BAD_BLOCK_NUM;
+				goto try_backup_journal;
+			}
+		}
+		journal->j_maxlen = j_inode->i_ext2.i_size / journal->j_blocksize;
+
+#ifdef USE_INODE_IO
+		retval = ext2fs_inode_io_intern2(ctx->fs, sb->s_journal_inum,
+						 &j_inode->i_ext2,
+						 &journal_name);
+		if (retval)
+			goto errout;
+
+		io_ptr = inode_io_manager;
+#else
+		journal->j_inode = j_inode;
+		ctx->journal_io = ctx->fs->io;
+		if ((retval = journal_bmap(journal, 0, &start)) != 0)
+			goto errout;
+#endif
+	} else {
+		ext_journal = 1;
+		if (!ctx->journal_name) {
+			char uuid[37];
+
+			uuid_unparse(sb->s_journal_uuid, uuid);
+			ctx->journal_name = blkid_get_devname(ctx->blkid,
+							      "UUID", uuid);
+			if (!ctx->journal_name)
+				ctx->journal_name = blkid_devno_to_devname(sb->s_journal_dev);
+		}
+		journal_name = ctx->journal_name;
+
+		if (!journal_name) {
+			fix_problem(ctx, PR_0_CANT_FIND_JOURNAL, &pctx);
+			return EXT2_ET_LOAD_EXT_JOURNAL;
+		}
+
+		io_ptr = unix_io_manager;
+	}
+
+#ifndef USE_INODE_IO
+	if (ext_journal)
+#endif
+		retval = io_ptr->open(journal_name, IO_FLAG_RW,
+				      &ctx->journal_io);
+	if (retval)
+		goto errout;
+
+	io_channel_set_blksize(ctx->journal_io, ctx->fs->blocksize);
+
+	if (ext_journal) {
+		if (ctx->fs->blocksize == 1024)
+			start = 1;
+		bh = getblk(dev_journal, start, ctx->fs->blocksize);
+		if (!bh) {
+			retval = EXT2_ET_NO_MEMORY;
+			goto errout;
+		}
+		ll_rw_block(READ, 1, &bh);
+		if ((retval = bh->b_err) != 0)
+			goto errout;
+		memcpy(&jsuper, start ? bh->b_data :  bh->b_data + 1024,
+		       sizeof(jsuper));
+		brelse(bh);
+#if BB_BIG_ENDIAN
+		if (jsuper.s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
+			ext2fs_swap_super(&jsuper);
+#endif
+		if (jsuper.s_magic != EXT2_SUPER_MAGIC ||
+		    !(jsuper.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+			fix_problem(ctx, PR_0_EXT_JOURNAL_BAD_SUPER, &pctx);
+			retval = EXT2_ET_LOAD_EXT_JOURNAL;
+			goto errout;
+		}
+		/* Make sure the journal UUID is correct */
+		if (memcmp(jsuper.s_uuid, ctx->fs->super->s_journal_uuid,
+			   sizeof(jsuper.s_uuid))) {
+			fix_problem(ctx, PR_0_JOURNAL_BAD_UUID, &pctx);
+			retval = EXT2_ET_LOAD_EXT_JOURNAL;
+			goto errout;
+		}
+
+		journal->j_maxlen = jsuper.s_blocks_count;
+		start++;
+	}
+
+	if (!(bh = getblk(dev_journal, start, journal->j_blocksize))) {
+		retval = EXT2_ET_NO_MEMORY;
+		goto errout;
+	}
+
+	journal->j_sb_buffer = bh;
+	journal->j_superblock = (journal_superblock_t *)bh->b_data;
+
+#ifdef USE_INODE_IO
+	ext2fs_free_mem(&j_inode);
+#endif
+
+	*ret_journal = journal;
+	return 0;
+
+errout:
+	ext2fs_free_mem(&dev_fs);
+	ext2fs_free_mem(&j_inode);
+	ext2fs_free_mem(&journal);
+	return retval;
+}
+
+static errcode_t e2fsck_journal_fix_bad_inode(e2fsck_t ctx,
+					      struct problem_context *pctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	int recover = ctx->fs->super->s_feature_incompat &
+		EXT3_FEATURE_INCOMPAT_RECOVER;
+	int has_journal = ctx->fs->super->s_feature_compat &
+		EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+
+	if (has_journal || sb->s_journal_inum) {
+		/* The journal inode is bogus, remove and force full fsck */
+		pctx->ino = sb->s_journal_inum;
+		if (fix_problem(ctx, PR_0_JOURNAL_BAD_INODE, pctx)) {
+			if (has_journal && sb->s_journal_inum)
+				printf("*** ext3 journal has been deleted - "
+				       "filesystem is now ext2 only ***\n\n");
+			sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+			sb->s_journal_inum = 0;
+			ctx->flags |= E2F_FLAG_JOURNAL_INODE; /* FIXME: todo */
+			e2fsck_clear_recover(ctx, 1);
+			return 0;
+		}
+		return EXT2_ET_BAD_INODE_NUM;
+	} else if (recover) {
+		if (fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, pctx)) {
+			e2fsck_clear_recover(ctx, 1);
+			return 0;
+		}
+		return EXT2_ET_UNSUPP_FEATURE;
+	}
+	return 0;
+}
+
+#define V1_SB_SIZE      0x0024
+static void clear_v2_journal_fields(journal_t *journal)
+{
+	e2fsck_t ctx = journal->j_dev->k_ctx;
+	struct problem_context pctx;
+
+	clear_problem_context(&pctx);
+
+	if (!fix_problem(ctx, PR_0_CLEAR_V2_JOURNAL, &pctx))
+		return;
+
+	memset(((char *) journal->j_superblock) + V1_SB_SIZE, 0,
+	       ctx->fs->blocksize-V1_SB_SIZE);
+	mark_buffer_dirty(journal->j_sb_buffer);
+}
+
+
+static errcode_t e2fsck_journal_load(journal_t *journal)
+{
+	e2fsck_t ctx = journal->j_dev->k_ctx;
+	journal_superblock_t *jsb;
+	struct buffer_head *jbh = journal->j_sb_buffer;
+	struct problem_context pctx;
+
+	clear_problem_context(&pctx);
+
+	ll_rw_block(READ, 1, &jbh);
+	if (jbh->b_err) {
+		bb_error_msg(_("reading journal superblock"));
+		return jbh->b_err;
+	}
+
+	jsb = journal->j_superblock;
+	/* If we don't even have JFS_MAGIC, we probably have a wrong inode */
+	if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER))
+		return e2fsck_journal_fix_bad_inode(ctx, &pctx);
+
+	switch (ntohl(jsb->s_header.h_blocktype)) {
+	case JFS_SUPERBLOCK_V1:
+		journal->j_format_version = 1;
+		if (jsb->s_feature_compat ||
+		    jsb->s_feature_incompat ||
+		    jsb->s_feature_ro_compat ||
+		    jsb->s_nr_users)
+			clear_v2_journal_fields(journal);
+		break;
+
+	case JFS_SUPERBLOCK_V2:
+		journal->j_format_version = 2;
+		if (ntohl(jsb->s_nr_users) > 1 &&
+		    uuid_is_null(ctx->fs->super->s_journal_uuid))
+			clear_v2_journal_fields(journal);
+		if (ntohl(jsb->s_nr_users) > 1) {
+			fix_problem(ctx, PR_0_JOURNAL_UNSUPP_MULTIFS, &pctx);
+			return EXT2_ET_JOURNAL_UNSUPP_VERSION;
+		}
+		break;
+
+	/*
+	 * These should never appear in a journal super block, so if
+	 * they do, the journal is badly corrupted.
+	 */
+	case JFS_DESCRIPTOR_BLOCK:
+	case JFS_COMMIT_BLOCK:
+	case JFS_REVOKE_BLOCK:
+		return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+	/* If we don't understand the superblock major type, but there
+	 * is a magic number, then it is likely to be a new format we
+	 * just don't understand, so leave it alone. */
+	default:
+		return EXT2_ET_JOURNAL_UNSUPP_VERSION;
+	}
+
+	if (JFS_HAS_INCOMPAT_FEATURE(journal, ~JFS_KNOWN_INCOMPAT_FEATURES))
+		return EXT2_ET_UNSUPP_FEATURE;
+
+	if (JFS_HAS_RO_COMPAT_FEATURE(journal, ~JFS_KNOWN_ROCOMPAT_FEATURES))
+		return EXT2_ET_RO_UNSUPP_FEATURE;
+
+	/* We have now checked whether we know enough about the journal
+	 * format to be able to proceed safely, so any other checks that
+	 * fail we should attempt to recover from. */
+	if (jsb->s_blocksize != htonl(journal->j_blocksize)) {
+		bb_error_msg(_("%s: no valid journal superblock found"),
+			ctx->device_name);
+		return EXT2_ET_CORRUPT_SUPERBLOCK;
+	}
+
+	if (ntohl(jsb->s_maxlen) < journal->j_maxlen)
+		journal->j_maxlen = ntohl(jsb->s_maxlen);
+	else if (ntohl(jsb->s_maxlen) > journal->j_maxlen) {
+		bb_error_msg(_("%s: journal too short"),
+			ctx->device_name);
+		return EXT2_ET_CORRUPT_SUPERBLOCK;
+	}
+
+	journal->j_tail_sequence = ntohl(jsb->s_sequence);
+	journal->j_transaction_sequence = journal->j_tail_sequence;
+	journal->j_tail = ntohl(jsb->s_start);
+	journal->j_first = ntohl(jsb->s_first);
+	journal->j_last = ntohl(jsb->s_maxlen);
+
+	return 0;
+}
+
+static void e2fsck_journal_reset_super(e2fsck_t ctx, journal_superblock_t *jsb,
+				       journal_t *journal)
+{
+	char *p;
+	union {
+		uuid_t uuid;
+		__u32 val[4];
+	} u;
+	__u32 new_seq = 0;
+	int i;
+
+	/* Leave a valid existing V1 superblock signature alone.
+	 * Anything unrecognizable we overwrite with a new V2
+	 * signature. */
+
+	if (jsb->s_header.h_magic != htonl(JFS_MAGIC_NUMBER) ||
+	    jsb->s_header.h_blocktype != htonl(JFS_SUPERBLOCK_V1)) {
+		jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
+		jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
+	}
+
+	/* Zero out everything else beyond the superblock header */
+
+	p = ((char *) jsb) + sizeof(journal_header_t);
+	memset (p, 0, ctx->fs->blocksize-sizeof(journal_header_t));
+
+	jsb->s_blocksize = htonl(ctx->fs->blocksize);
+	jsb->s_maxlen = htonl(journal->j_maxlen);
+	jsb->s_first = htonl(1);
+
+	/* Initialize the journal sequence number so that there is "no"
+	 * chance we will find old "valid" transactions in the journal.
+	 * This avoids the need to zero the whole journal (slow to do,
+	 * and risky when we are just recovering the filesystem).
+	 */
+	uuid_generate(u.uuid);
+	for (i = 0; i < 4; i ++)
+		new_seq ^= u.val[i];
+	jsb->s_sequence = htonl(new_seq);
+
+	mark_buffer_dirty(journal->j_sb_buffer);
+	ll_rw_block(WRITE, 1, &journal->j_sb_buffer);
+}
+
+static errcode_t e2fsck_journal_fix_corrupt_super(e2fsck_t ctx,
+						  journal_t *journal,
+						  struct problem_context *pctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	int recover = ctx->fs->super->s_feature_incompat &
+		EXT3_FEATURE_INCOMPAT_RECOVER;
+
+	if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
+		if (fix_problem(ctx, PR_0_JOURNAL_BAD_SUPER, pctx)) {
+			e2fsck_journal_reset_super(ctx, journal->j_superblock,
+						   journal);
+			journal->j_transaction_sequence = 1;
+			e2fsck_clear_recover(ctx, recover);
+			return 0;
+		}
+		return EXT2_ET_CORRUPT_SUPERBLOCK;
+	} else if (e2fsck_journal_fix_bad_inode(ctx, pctx))
+		return EXT2_ET_CORRUPT_SUPERBLOCK;
+
+	return 0;
+}
+
+static void e2fsck_journal_release(e2fsck_t ctx, journal_t *journal,
+				   int reset, int drop)
+{
+	journal_superblock_t *jsb;
+
+	if (drop)
+		mark_buffer_clean(journal->j_sb_buffer);
+	else if (!(ctx->options & E2F_OPT_READONLY)) {
+		jsb = journal->j_superblock;
+		jsb->s_sequence = htonl(journal->j_transaction_sequence);
+		if (reset)
+			jsb->s_start = 0; /* this marks the journal as empty */
+		mark_buffer_dirty(journal->j_sb_buffer);
+	}
+	brelse(journal->j_sb_buffer);
+
+	if (ctx->journal_io) {
+		if (ctx->fs && ctx->fs->io != ctx->journal_io)
+			io_channel_close(ctx->journal_io);
+		ctx->journal_io = 0;
+	}
+
+#ifndef USE_INODE_IO
+	ext2fs_free_mem(&journal->j_inode);
+#endif
+	ext2fs_free_mem(&journal->j_fs_dev);
+	ext2fs_free_mem(&journal);
+}
+
+/*
+ * This function makes sure that the superblock fields regarding the
+ * journal are consistent.
+ */
+static int e2fsck_check_ext3_journal(e2fsck_t ctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	journal_t *journal;
+	int recover = ctx->fs->super->s_feature_incompat &
+		EXT3_FEATURE_INCOMPAT_RECOVER;
+	struct problem_context pctx;
+	problem_t problem;
+	int reset = 0, force_fsck = 0;
+	int retval;
+
+	/* If we don't have any journal features, don't do anything more */
+	if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+	    !recover && sb->s_journal_inum == 0 && sb->s_journal_dev == 0 &&
+	    uuid_is_null(sb->s_journal_uuid))
+		return 0;
+
+	clear_problem_context(&pctx);
+	pctx.num = sb->s_journal_inum;
+
+	retval = e2fsck_get_journal(ctx, &journal);
+	if (retval) {
+		if ((retval == EXT2_ET_BAD_INODE_NUM) ||
+		    (retval == EXT2_ET_BAD_BLOCK_NUM) ||
+		    (retval == EXT2_ET_JOURNAL_TOO_SMALL) ||
+		    (retval == EXT2_ET_NO_JOURNAL))
+			return e2fsck_journal_fix_bad_inode(ctx, &pctx);
+		return retval;
+	}
+
+	retval = e2fsck_journal_load(journal);
+	if (retval) {
+		if ((retval == EXT2_ET_CORRUPT_SUPERBLOCK) ||
+		    ((retval == EXT2_ET_UNSUPP_FEATURE) &&
+		    (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_INCOMPAT,
+				  &pctx))) ||
+		    ((retval == EXT2_ET_RO_UNSUPP_FEATURE) &&
+		    (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_ROCOMPAT,
+				  &pctx))) ||
+		    ((retval == EXT2_ET_JOURNAL_UNSUPP_VERSION) &&
+		    (!fix_problem(ctx, PR_0_JOURNAL_UNSUPP_VERSION, &pctx))))
+			retval = e2fsck_journal_fix_corrupt_super(ctx, journal,
+								  &pctx);
+		e2fsck_journal_release(ctx, journal, 0, 1);
+		return retval;
+	}
+
+	/*
+	 * We want to make the flags consistent here.  We will not leave with
+	 * needs_recovery set but has_journal clear.  We can't get in a loop
+	 * with -y, -n, or -p, only if a user isn't making up their mind.
+	 */
+no_has_journal:
+	if (!(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
+		recover = sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER;
+		pctx.str = "inode";
+		if (fix_problem(ctx, PR_0_JOURNAL_HAS_JOURNAL, &pctx)) {
+			if (recover &&
+			    !fix_problem(ctx, PR_0_JOURNAL_RECOVER_SET, &pctx))
+				goto no_has_journal;
+			/*
+			 * Need a full fsck if we are releasing a
+			 * journal stored on a reserved inode.
+			 */
+			force_fsck = recover ||
+				(sb->s_journal_inum < EXT2_FIRST_INODE(sb));
+			/* Clear all of the journal fields */
+			sb->s_journal_inum = 0;
+			sb->s_journal_dev = 0;
+			memset(sb->s_journal_uuid, 0,
+			       sizeof(sb->s_journal_uuid));
+			e2fsck_clear_recover(ctx, force_fsck);
+		} else if (!(ctx->options & E2F_OPT_READONLY)) {
+			sb->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+			ext2fs_mark_super_dirty(ctx->fs);
+		}
+	}
+
+	if (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL &&
+	    !(sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) &&
+	    journal->j_superblock->s_start != 0) {
+		/* Print status information */
+		fix_problem(ctx, PR_0_JOURNAL_RECOVERY_CLEAR, &pctx);
+		if (ctx->superblock)
+			problem = PR_0_JOURNAL_RUN_DEFAULT;
+		else
+			problem = PR_0_JOURNAL_RUN;
+		if (fix_problem(ctx, problem, &pctx)) {
+			ctx->options |= E2F_OPT_FORCE;
+			sb->s_feature_incompat |=
+				EXT3_FEATURE_INCOMPAT_RECOVER;
+			ext2fs_mark_super_dirty(ctx->fs);
+		} else if (fix_problem(ctx,
+				       PR_0_JOURNAL_RESET_JOURNAL, &pctx)) {
+			reset = 1;
+			sb->s_state &= ~EXT2_VALID_FS;
+			ext2fs_mark_super_dirty(ctx->fs);
+		}
+		/*
+		 * If the user answers no to the above question, we
+		 * ignore the fact that journal apparently has data;
+		 * accidentally replaying over valid data would be far
+		 * worse than skipping a questionable recovery.
+		 *
+		 * XXX should we abort with a fatal error here?  What
+		 * will the ext3 kernel code do if a filesystem with
+		 * !NEEDS_RECOVERY but with a non-zero
+		 * journal->j_superblock->s_start is mounted?
+		 */
+	}
+
+	e2fsck_journal_release(ctx, journal, reset, 0);
+	return retval;
+}
+
+static errcode_t recover_ext3_journal(e2fsck_t ctx)
+{
+	journal_t *journal;
+	int retval;
+
+	journal_init_revoke_caches();
+	retval = e2fsck_get_journal(ctx, &journal);
+	if (retval)
+		return retval;
+
+	retval = e2fsck_journal_load(journal);
+	if (retval)
+		goto errout;
+
+	retval = journal_init_revoke(journal, 1024);
+	if (retval)
+		goto errout;
+
+	retval = -journal_recover(journal);
+	if (retval)
+		goto errout;
+
+	if (journal->j_superblock->s_errno) {
+		ctx->fs->super->s_state |= EXT2_ERROR_FS;
+		ext2fs_mark_super_dirty(ctx->fs);
+		journal->j_superblock->s_errno = 0;
+		mark_buffer_dirty(journal->j_sb_buffer);
+	}
+
+errout:
+	journal_destroy_revoke(journal);
+	journal_destroy_revoke_caches();
+	e2fsck_journal_release(ctx, journal, 1, 0);
+	return retval;
+}
+
+static int e2fsck_run_ext3_journal(e2fsck_t ctx)
+{
+	io_manager io_ptr = ctx->fs->io->manager;
+	int blocksize = ctx->fs->blocksize;
+	errcode_t       retval, recover_retval;
+
+	printf(_("%s: recovering journal\n"), ctx->device_name);
+	if (ctx->options & E2F_OPT_READONLY) {
+		printf(_("%s: won't do journal recovery while read-only\n"),
+		       ctx->device_name);
+		return EXT2_ET_FILE_RO;
+	}
+
+	if (ctx->fs->flags & EXT2_FLAG_DIRTY)
+		ext2fs_flush(ctx->fs);  /* Force out any modifications */
+
+	recover_retval = recover_ext3_journal(ctx);
+
+	/*
+	 * Reload the filesystem context to get up-to-date data from disk
+	 * because journal recovery will change the filesystem under us.
+	 */
+	ext2fs_close(ctx->fs);
+	retval = ext2fs_open(ctx->filesystem_name, EXT2_FLAG_RW,
+			     ctx->superblock, blocksize, io_ptr,
+			     &ctx->fs);
+
+	if (retval) {
+		bb_error_msg(_("while trying to re-open %s"),
+			ctx->device_name);
+		bb_error_msg_and_die(0);
+	}
+	ctx->fs->priv_data = ctx;
+
+	/* Set the superblock flags */
+	e2fsck_clear_recover(ctx, recover_retval);
+	return recover_retval;
+}
+
+/*
+ * This function will move the journal inode from a visible file in
+ * the filesystem directory hierarchy to the reserved inode if necessary.
+ */
+static const char *const journal_names[] = {
+	".journal", "journal", ".journal.dat", "journal.dat", 0 };
+
+static void e2fsck_move_ext3_journal(e2fsck_t ctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	struct problem_context  pctx;
+	struct ext2_inode       inode;
+	ext2_filsys             fs = ctx->fs;
+	ext2_ino_t              ino;
+	errcode_t               retval;
+	const char *const *    cpp;
+	int                     group, mount_flags;
+
+	clear_problem_context(&pctx);
+
+	/*
+	 * If the filesystem is opened read-only, or there is no
+	 * journal, then do nothing.
+	 */
+	if ((ctx->options & E2F_OPT_READONLY) ||
+	    (sb->s_journal_inum == 0) ||
+	    !(sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+		return;
+
+	/*
+	 * Read in the journal inode
+	 */
+	if (ext2fs_read_inode(fs, sb->s_journal_inum, &inode) != 0)
+		return;
+
+	/*
+	 * If it's necessary to backup the journal inode, do so.
+	 */
+	if ((sb->s_jnl_backup_type == 0) ||
+	    ((sb->s_jnl_backup_type == EXT3_JNL_BACKUP_BLOCKS) &&
+	     memcmp(inode.i_block, sb->s_jnl_blocks, EXT2_N_BLOCKS*4))) {
+		if (fix_problem(ctx, PR_0_BACKUP_JNL, &pctx)) {
+			memcpy(sb->s_jnl_blocks, inode.i_block,
+			       EXT2_N_BLOCKS*4);
+			sb->s_jnl_blocks[16] = inode.i_size;
+			sb->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
+			ext2fs_mark_super_dirty(fs);
+			fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+		}
+	}
+
+	/*
+	 * If the journal is already the hidden inode, then do nothing
+	 */
+	if (sb->s_journal_inum == EXT2_JOURNAL_INO)
+		return;
+
+	/*
+	 * The journal inode had better have only one link and not be readable.
+	 */
+	if (inode.i_links_count != 1)
+		return;
+
+	/*
+	 * If the filesystem is mounted, or we can't tell whether
+	 * or not it's mounted, do nothing.
+	 */
+	retval = ext2fs_check_if_mounted(ctx->filesystem_name, &mount_flags);
+	if (retval || (mount_flags & EXT2_MF_MOUNTED))
+		return;
+
+	/*
+	 * If we can't find the name of the journal inode, then do
+	 * nothing.
+	 */
+	for (cpp = journal_names; *cpp; cpp++) {
+		retval = ext2fs_lookup(fs, EXT2_ROOT_INO, *cpp,
+				       strlen(*cpp), 0, &ino);
+		if ((retval == 0) && (ino == sb->s_journal_inum))
+			break;
+	}
+	if (*cpp == 0)
+		return;
+
+	/* We need the inode bitmap to be loaded */
+	retval = ext2fs_read_bitmaps(fs);
+	if (retval)
+		return;
+
+	pctx.str = *cpp;
+	if (!fix_problem(ctx, PR_0_MOVE_JOURNAL, &pctx))
+		return;
+
+	/*
+	 * OK, we've done all the checks, let's actually move the
+	 * journal inode.  Errors at this point mean we need to force
+	 * an ext2 filesystem check.
+	 */
+	if ((retval = ext2fs_unlink(fs, EXT2_ROOT_INO, *cpp, ino, 0)) != 0)
+		goto err_out;
+	if ((retval = ext2fs_write_inode(fs, EXT2_JOURNAL_INO, &inode)) != 0)
+		goto err_out;
+	sb->s_journal_inum = EXT2_JOURNAL_INO;
+	ext2fs_mark_super_dirty(fs);
+	fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+	inode.i_links_count = 0;
+	inode.i_dtime = time(NULL);
+	if ((retval = ext2fs_write_inode(fs, ino, &inode)) != 0)
+		goto err_out;
+
+	group = ext2fs_group_of_ino(fs, ino);
+	ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
+	ext2fs_mark_ib_dirty(fs);
+	fs->group_desc[group].bg_free_inodes_count++;
+	fs->super->s_free_inodes_count++;
+	return;
+
+err_out:
+	pctx.errcode = retval;
+	fix_problem(ctx, PR_0_ERR_MOVE_JOURNAL, &pctx);
+	fs->super->s_state &= ~EXT2_VALID_FS;
+	ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * message.c --- print e2fsck messages (with compression)
+ *
+ * print_e2fsck_message() prints a message to the user, using
+ * compression techniques and expansions of abbreviations.
+ *
+ * The following % expansions are supported:
+ *
+ *      %b      <blk>                   block number
+ *      %B      <blkcount>              integer
+ *      %c      <blk2>                  block number
+ *      %Di     <dirent>->ino           inode number
+ *      %Dn     <dirent>->name          string
+ *      %Dr     <dirent>->rec_len
+ *      %Dl     <dirent>->name_len
+ *      %Dt     <dirent>->filetype
+ *      %d      <dir>                   inode number
+ *      %g      <group>                 integer
+ *      %i      <ino>                   inode number
+ *      %Is     <inode> -> i_size
+ *      %IS     <inode> -> i_extra_isize
+ *      %Ib     <inode> -> i_blocks
+ *      %Il     <inode> -> i_links_count
+ *      %Im     <inode> -> i_mode
+ *      %IM     <inode> -> i_mtime
+ *      %IF     <inode> -> i_faddr
+ *      %If     <inode> -> i_file_acl
+ *      %Id     <inode> -> i_dir_acl
+ *      %Iu     <inode> -> i_uid
+ *      %Ig     <inode> -> i_gid
+ *      %j      <ino2>                  inode number
+ *      %m      <com_err error message>
+ *      %N      <num>
+ *      %p      ext2fs_get_pathname of directory <ino>
+ *      %P      ext2fs_get_pathname of <dirent>->ino with <ino2> as
+ *                      the containing directory.  (If dirent is NULL
+ *                      then return the pathname of directory <ino2>)
+ *      %q      ext2fs_get_pathname of directory <dir>
+ *      %Q      ext2fs_get_pathname of directory <ino> with <dir> as
+ *                      the containing directory.
+ *      %s      <str>                   miscellaneous string
+ *      %S      backup superblock
+ *      %X      <num> hexadecimal format
+ *
+ * The following '@' expansions are supported:
+ *
+ *      @a      extended attribute
+ *      @A      error allocating
+ *      @b      block
+ *      @B      bitmap
+ *      @c      compress
+ *      @C      conflicts with some other fs block
+ *      @D      deleted
+ *      @d      directory
+ *      @e      entry
+ *      @E      Entry '%Dn' in %p (%i)
+ *      @f      filesystem
+ *      @F      for @i %i (%Q) is
+ *      @g      group
+ *      @h      HTREE directory inode
+ *      @i      inode
+ *      @I      illegal
+ *      @j      journal
+ *      @l      lost+found
+ *      @L      is a link
+ *      @m      multiply-claimed
+ *      @n      invalid
+ *      @o      orphaned
+ *      @p      problem in
+ *      @r      root inode
+ *      @s      should be
+ *      @S      superblock
+ *      @u      unattached
+ *      @v      device
+ *      @z      zero-length
+ */
+
+
+/*
+ * This structure defines the abbreviations used by the text strings
+ * below.  The first character in the string is the index letter.  An
+ * abbreviation of the form '@<i>' is expanded by looking up the index
+ * letter <i> in the table below.
+ */
+static const char *const abbrevs[] = {
+	N_("aextended attribute"),
+	N_("Aerror allocating"),
+	N_("bblock"),
+	N_("Bbitmap"),
+	N_("ccompress"),
+	N_("Cconflicts with some other fs @b"),
+	N_("iinode"),
+	N_("Iillegal"),
+	N_("jjournal"),
+	N_("Ddeleted"),
+	N_("ddirectory"),
+	N_("eentry"),
+	N_("E@e '%Dn' in %p (%i)"),
+	N_("ffilesystem"),
+	N_("Ffor @i %i (%Q) is"),
+	N_("ggroup"),
+	N_("hHTREE @d @i"),
+	N_("llost+found"),
+	N_("Lis a link"),
+	N_("mmultiply-claimed"),
+	N_("ninvalid"),
+	N_("oorphaned"),
+	N_("pproblem in"),
+	N_("rroot @i"),
+	N_("sshould be"),
+	N_("Ssuper@b"),
+	N_("uunattached"),
+	N_("vdevice"),
+	N_("zzero-length"),
+	"@@",
+	0
+	};
+
+/*
+ * Give more user friendly names to the "special" inodes.
+ */
+#define num_special_inodes      11
+static const char *const special_inode_name[] =
+{
+	N_("<The NULL inode>"),                 /* 0 */
+	N_("<The bad blocks inode>"),           /* 1 */
+	"/",                                    /* 2 */
+	N_("<The ACL index inode>"),            /* 3 */
+	N_("<The ACL data inode>"),             /* 4 */
+	N_("<The boot loader inode>"),          /* 5 */
+	N_("<The undelete directory inode>"),   /* 6 */
+	N_("<The group descriptor inode>"),     /* 7 */
+	N_("<The journal inode>"),              /* 8 */
+	N_("<Reserved inode 9>"),               /* 9 */
+	N_("<Reserved inode 10>"),              /* 10 */
+};
+
+/*
+ * This function does "safe" printing.  It will convert non-printable
+ * ASCII characters using '^' and M- notation.
+ */
+static void safe_print(const char *cp, int len)
+{
+	unsigned char   ch;
+
+	if (len < 0)
+		len = strlen(cp);
+
+	while (len--) {
+		ch = *cp++;
+		if (ch > 128) {
+			fputs("M-", stdout);
+			ch -= 128;
+		}
+		if ((ch < 32) || (ch == 0x7f)) {
+			bb_putchar('^');
+			ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
+		}
+		bb_putchar(ch);
+	}
+}
+
+
+/*
+ * This function prints a pathname, using the ext2fs_get_pathname
+ * function
+ */
+static void print_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino)
+{
+	errcode_t       retval;
+	char            *path;
+
+	if (!dir && (ino < num_special_inodes)) {
+		fputs(_(special_inode_name[ino]), stdout);
+		return;
+	}
+
+	retval = ext2fs_get_pathname(fs, dir, ino, &path);
+	if (retval)
+		fputs("???", stdout);
+	else {
+		safe_print(path, -1);
+		ext2fs_free_mem(&path);
+	}
+}
+
+static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
+			  struct problem_context *pctx, int first);
+/*
+ * This function handles the '@' expansion.  We allow recursive
+ * expansion; an @ expression can contain further '@' and '%'
+ * expressions.
+ */
+static void expand_at_expression(e2fsck_t ctx, char ch,
+					  struct problem_context *pctx,
+					  int *first)
+{
+	const char *const *cpp;
+	const char *str;
+
+	/* Search for the abbreviation */
+	for (cpp = abbrevs; *cpp; cpp++) {
+		if (ch == *cpp[0])
+			break;
+	}
+	if (*cpp) {
+		str = _(*cpp) + 1;
+		if (*first && islower(*str)) {
+			*first = 0;
+			bb_putchar(toupper(*str++));
+		}
+		print_e2fsck_message(ctx, str, pctx, *first);
+	} else
+		printf("@%c", ch);
+}
+
+/*
+ * This function expands '%IX' expressions
+ */
+static void expand_inode_expression(char ch,
+					     struct problem_context *ctx)
+{
+	struct ext2_inode       *inode;
+	struct ext2_inode_large *large_inode;
+	char *                  time_str;
+	time_t                  t;
+	int                     do_gmt = -1;
+
+	if (!ctx || !ctx->inode)
+		goto no_inode;
+
+	inode = ctx->inode;
+	large_inode = (struct ext2_inode_large *) inode;
+
+	switch (ch) {
+	case 's':
+		if (LINUX_S_ISDIR(inode->i_mode))
+			printf("%u", inode->i_size);
+		else {
+			printf("%"PRIu64, (inode->i_size |
+					((uint64_t) inode->i_size_high << 32)));
+		}
+		break;
+	case 'S':
+		printf("%u", large_inode->i_extra_isize);
+		break;
+	case 'b':
+		printf("%u", inode->i_blocks);
+		break;
+	case 'l':
+		printf("%d", inode->i_links_count);
+		break;
+	case 'm':
+		printf("0%o", inode->i_mode);
+		break;
+	case 'M':
+		/* The diet libc doesn't respect the TZ environemnt variable */
+		if (do_gmt == -1) {
+			time_str = getenv("TZ");
+			if (!time_str)
+				time_str = "";
+			do_gmt = !strcmp(time_str, "GMT");
+		}
+		t = inode->i_mtime;
+		time_str = asctime(do_gmt ? gmtime(&t) : localtime(&t));
+		printf("%.24s", time_str);
+		break;
+	case 'F':
+		printf("%u", inode->i_faddr);
+		break;
+	case 'f':
+		printf("%u", inode->i_file_acl);
+		break;
+	case 'd':
+		printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
+			      inode->i_dir_acl : 0));
+		break;
+	case 'u':
+		printf("%d", (inode->i_uid |
+			      (inode->osd2.linux2.l_i_uid_high << 16)));
+		break;
+	case 'g':
+		printf("%d", (inode->i_gid |
+			      (inode->osd2.linux2.l_i_gid_high << 16)));
+		break;
+	default:
+	no_inode:
+		printf("%%I%c", ch);
+		break;
+	}
+}
+
+/*
+ * This function expands '%dX' expressions
+ */
+static void expand_dirent_expression(char ch,
+					      struct problem_context *ctx)
+{
+	struct ext2_dir_entry   *dirent;
+	int     len;
+
+	if (!ctx || !ctx->dirent)
+		goto no_dirent;
+
+	dirent = ctx->dirent;
+
+	switch (ch) {
+	case 'i':
+		printf("%u", dirent->inode);
+		break;
+	case 'n':
+		len = dirent->name_len & 0xFF;
+		if (len > EXT2_NAME_LEN)
+			len = EXT2_NAME_LEN;
+		if (len > dirent->rec_len)
+			len = dirent->rec_len;
+		safe_print(dirent->name, len);
+		break;
+	case 'r':
+		printf("%u", dirent->rec_len);
+		break;
+	case 'l':
+		printf("%u", dirent->name_len & 0xFF);
+		break;
+	case 't':
+		printf("%u", dirent->name_len >> 8);
+		break;
+	default:
+	no_dirent:
+		printf("%%D%c", ch);
+		break;
+	}
+}
+
+static void expand_percent_expression(ext2_filsys fs, char ch,
+					       struct problem_context *ctx)
+{
+	if (!ctx)
+		goto no_context;
+
+	switch (ch) {
+	case '%':
+		bb_putchar('%');
+		break;
+	case 'b':
+		printf("%u", ctx->blk);
+		break;
+	case 'B':
+		printf("%"PRIi64, ctx->blkcount);
+		break;
+	case 'c':
+		printf("%u", ctx->blk2);
+		break;
+	case 'd':
+		printf("%u", ctx->dir);
+		break;
+	case 'g':
+		printf("%d", ctx->group);
+		break;
+	case 'i':
+		printf("%u", ctx->ino);
+		break;
+	case 'j':
+		printf("%u", ctx->ino2);
+		break;
+	case 'm':
+		fputs(error_message(ctx->errcode), stdout);
+		break;
+	case 'N':
+		printf("%"PRIi64, ctx->num);
+		break;
+	case 'p':
+		print_pathname(fs, ctx->ino, 0);
+		break;
+	case 'P':
+		print_pathname(fs, ctx->ino2,
+			       ctx->dirent ? ctx->dirent->inode : 0);
+		break;
+	case 'q':
+		print_pathname(fs, ctx->dir, 0);
+		break;
+	case 'Q':
+		print_pathname(fs, ctx->dir, ctx->ino);
+		break;
+	case 'S':
+		printf("%d", get_backup_sb(NULL, fs, NULL, NULL));
+		break;
+	case 's':
+		fputs((ctx->str ? ctx->str : "NULL"), stdout);
+		break;
+	case 'X':
+		printf("0x%"PRIi64, ctx->num);
+		break;
+	default:
+	no_context:
+		printf("%%%c", ch);
+		break;
+	}
+}
+
+
+static void print_e2fsck_message(e2fsck_t ctx, const char *msg,
+			  struct problem_context *pctx, int first)
+{
+	ext2_filsys fs = ctx->fs;
+	const char *    cp;
+	int             i;
+
+	e2fsck_clear_progbar(ctx);
+	for (cp = msg; *cp; cp++) {
+		if (cp[0] == '@') {
+			cp++;
+			expand_at_expression(ctx, *cp, pctx, &first);
+		} else if (cp[0] == '%' && cp[1] == 'I') {
+			cp += 2;
+			expand_inode_expression(*cp, pctx);
+		} else if (cp[0] == '%' && cp[1] == 'D') {
+			cp += 2;
+			expand_dirent_expression(*cp, pctx);
+		} else if ((cp[0] == '%')) {
+			cp++;
+			expand_percent_expression(fs, *cp, pctx);
+		} else {
+			for (i=0; cp[i]; i++)
+				if ((cp[i] == '@') || cp[i] == '%')
+					break;
+			printf("%.*s", i, cp);
+			cp += i-1;
+		}
+		first = 0;
+	}
+}
+
+
+/*
+ * region.c --- code which manages allocations within a region.
+ */
+
+struct region_el {
+	region_addr_t   start;
+	region_addr_t   end;
+	struct region_el *next;
+};
+
+struct region_struct {
+	region_addr_t   min;
+	region_addr_t   max;
+	struct region_el *allocated;
+};
+
+static region_t region_create(region_addr_t min, region_addr_t max)
+{
+	region_t        region;
+
+	region = xzalloc(sizeof(struct region_struct));
+	region->min = min;
+	region->max = max;
+	return region;
+}
+
+static void region_free(region_t region)
+{
+	struct region_el        *r, *next;
+
+	for (r = region->allocated; r; r = next) {
+		next = r->next;
+		free(r);
+	}
+	memset(region, 0, sizeof(struct region_struct));
+	free(region);
+}
+
+static int region_allocate(region_t region, region_addr_t start, int n)
+{
+	struct region_el        *r, *new_region, *prev, *next;
+	region_addr_t end;
+
+	end = start+n;
+	if ((start < region->min) || (end > region->max))
+		return -1;
+	if (n == 0)
+		return 1;
+
+	/*
+	 * Search through the linked list.  If we find that it
+	 * conflicts witih something that's already allocated, return
+	 * 1; if we can find an existing region which we can grow, do
+	 * so.  Otherwise, stop when we find the appropriate place
+	 * insert a new region element into the linked list.
+	 */
+	for (r = region->allocated, prev=NULL; r; prev = r, r = r->next) {
+		if (((start >= r->start) && (start < r->end)) ||
+		    ((end > r->start) && (end <= r->end)) ||
+		    ((start <= r->start) && (end >= r->end)))
+			return 1;
+		if (end == r->start) {
+			r->start = start;
+			return 0;
+		}
+		if (start == r->end) {
+			if ((next = r->next)) {
+				if (end > next->start)
+					return 1;
+				if (end == next->start) {
+					r->end = next->end;
+					r->next = next->next;
+					free(next);
+					return 0;
+				}
+			}
+			r->end = end;
+			return 0;
+		}
+		if (start < r->start)
+			break;
+	}
+	/*
+	 * Insert a new region element structure into the linked list
+	 */
+	new_region = xmalloc(sizeof(struct region_el));
+	new_region->start = start;
+	new_region->end = start + n;
+	new_region->next = r;
+	if (prev)
+		prev->next = new_region;
+	else
+		region->allocated = new_region;
+	return 0;
+}
+
+/*
+ * pass1.c -- pass #1 of e2fsck: sequential scan of the inode table
+ *
+ * Pass 1 of e2fsck iterates over all the inodes in the filesystems,
+ * and applies the following tests to each inode:
+ *
+ *      - The mode field of the inode must be legal.
+ *      - The size and block count fields of the inode are correct.
+ *      - A data block must not be used by another inode
+ *
+ * Pass 1 also gathers the collects the following information:
+ *
+ *      - A bitmap of which inodes are in use.          (inode_used_map)
+ *      - A bitmap of which inodes are directories.     (inode_dir_map)
+ *      - A bitmap of which inodes are regular files.   (inode_reg_map)
+ *      - A bitmap of which inodes have bad fields.     (inode_bad_map)
+ *      - A bitmap of which inodes are imagic inodes.   (inode_imagic_map)
+ *      - A bitmap of which blocks are in use.          (block_found_map)
+ *      - A bitmap of which blocks are in use by two inodes     (block_dup_map)
+ *      - The data blocks of the directory inodes.      (dir_map)
+ *
+ * Pass 1 is designed to stash away enough information so that the
+ * other passes should not need to read in the inode information
+ * during the normal course of a filesystem check.  (Althogh if an
+ * inconsistency is detected, other passes may need to read in an
+ * inode to fix it.)
+ *
+ * Note that pass 1B will be invoked if there are any duplicate blocks
+ * found.
+ */
+
+
+static int process_block(ext2_filsys fs, blk_t  *blocknr,
+			 e2_blkcnt_t blockcnt, blk_t ref_blk,
+			 int ref_offset, void *priv_data);
+static int process_bad_block(ext2_filsys fs, blk_t *block_nr,
+			     e2_blkcnt_t blockcnt, blk_t ref_blk,
+			     int ref_offset, void *priv_data);
+static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
+			 char *block_buf);
+static void mark_table_blocks(e2fsck_t ctx);
+static void alloc_imagic_map(e2fsck_t ctx);
+static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
+static void handle_fs_bad_blocks(e2fsck_t ctx);
+static void process_inodes(e2fsck_t ctx, char *block_buf);
+static int process_inode_cmp(const void *a, const void *b);
+static errcode_t scan_callback(ext2_filsys fs,
+				  dgrp_t group, void * priv_data);
+static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
+				    char *block_buf, int adjust_sign);
+/* static char *describe_illegal_block(ext2_filsys fs, blk_t block); */
+
+static void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
+			       struct ext2_inode * inode, int bufsize,
+			       const char *proc);
+
+struct process_block_struct_1 {
+	ext2_ino_t      ino;
+	unsigned        is_dir:1, is_reg:1, clear:1, suppress:1,
+				fragmented:1, compressed:1, bbcheck:1;
+	blk_t           num_blocks;
+	blk_t           max_blocks;
+	e2_blkcnt_t     last_block;
+	int             num_illegal_blocks;
+	blk_t           previous_block;
+	struct ext2_inode *inode;
+	struct problem_context *pctx;
+	ext2fs_block_bitmap fs_meta_blocks;
+	e2fsck_t        ctx;
+};
+
+struct process_inode_block {
+	ext2_ino_t ino;
+	struct ext2_inode inode;
+};
+
+struct scan_callback_struct {
+	e2fsck_t        ctx;
+	char            *block_buf;
+};
+
+/*
+ * For the inodes to process list.
+ */
+static struct process_inode_block *inodes_to_process;
+static int process_inode_count;
+
+static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
+			    EXT2_MIN_BLOCK_LOG_SIZE + 1];
+
+/*
+ * Free all memory allocated by pass1 in preparation for restarting
+ * things.
+ */
+static void unwind_pass1(void)
+{
+	ext2fs_free_mem(&inodes_to_process);
+}
+
+/*
+ * Check to make sure a device inode is real.  Returns 1 if the device
+ * checks out, 0 if not.
+ *
+ * Note: this routine is now also used to check FIFO's and Sockets,
+ * since they have the same requirement; the i_block fields should be
+ * zero.
+ */
+static int
+e2fsck_pass1_check_device_inode(ext2_filsys fs, struct ext2_inode *inode)
+{
+	int     i;
+
+	/*
+	 * If i_blocks is non-zero, or the index flag is set, then
+	 * this is a bogus device/fifo/socket
+	 */
+	if ((ext2fs_inode_data_blocks(fs, inode) != 0) ||
+	    (inode->i_flags & EXT2_INDEX_FL))
+		return 0;
+
+	/*
+	 * We should be able to do the test below all the time, but
+	 * because the kernel doesn't forcibly clear the device
+	 * inode's additional i_block fields, there are some rare
+	 * occasions when a legitimate device inode will have non-zero
+	 * additional i_block fields.  So for now, we only complain
+	 * when the immutable flag is set, which should never happen
+	 * for devices.  (And that's when the problem is caused, since
+	 * you can't set or clear immutable flags for devices.)  Once
+	 * the kernel has been fixed we can change this...
+	 */
+	if (inode->i_flags & (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)) {
+		for (i=4; i < EXT2_N_BLOCKS; i++)
+			if (inode->i_block[i])
+				return 0;
+	}
+	return 1;
+}
+
+/*
+ * Check to make sure a symlink inode is real.  Returns 1 if the symlink
+ * checks out, 0 if not.
+ */
+static int
+e2fsck_pass1_check_symlink(ext2_filsys fs, struct ext2_inode *inode, char *buf)
+{
+	unsigned int len;
+	int i;
+	blk_t   blocks;
+
+	if ((inode->i_size_high || inode->i_size == 0) ||
+	    (inode->i_flags & EXT2_INDEX_FL))
+		return 0;
+
+	blocks = ext2fs_inode_data_blocks(fs, inode);
+	if (blocks) {
+		if ((inode->i_size >= fs->blocksize) ||
+		    (blocks != fs->blocksize >> 9) ||
+		    (inode->i_block[0] < fs->super->s_first_data_block) ||
+		    (inode->i_block[0] >= fs->super->s_blocks_count))
+			return 0;
+
+		for (i = 1; i < EXT2_N_BLOCKS; i++)
+			if (inode->i_block[i])
+				return 0;
+
+		if (io_channel_read_blk(fs->io, inode->i_block[0], 1, buf))
+			return 0;
+
+		len = strnlen(buf, fs->blocksize);
+		if (len == fs->blocksize)
+			return 0;
+	} else {
+		if (inode->i_size >= sizeof(inode->i_block))
+			return 0;
+
+		len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
+		if (len == sizeof(inode->i_block))
+			return 0;
+	}
+	if (len != inode->i_size)
+		return 0;
+	return 1;
+}
+
+/*
+ * If the immutable (or append-only) flag is set on the inode, offer
+ * to clear it.
+ */
+#define BAD_SPECIAL_FLAGS (EXT2_IMMUTABLE_FL | EXT2_APPEND_FL)
+static void check_immutable(e2fsck_t ctx, struct problem_context *pctx)
+{
+	if (!(pctx->inode->i_flags & BAD_SPECIAL_FLAGS))
+		return;
+
+	if (!fix_problem(ctx, PR_1_SET_IMMUTABLE, pctx))
+		return;
+
+	pctx->inode->i_flags &= ~BAD_SPECIAL_FLAGS;
+	e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
+}
+
+/*
+ * If device, fifo or socket, check size is zero -- if not offer to
+ * clear it
+ */
+static void check_size(e2fsck_t ctx, struct problem_context *pctx)
+{
+	struct ext2_inode *inode = pctx->inode;
+
+	if ((inode->i_size == 0) && (inode->i_size_high == 0))
+		return;
+
+	if (!fix_problem(ctx, PR_1_SET_NONZSIZE, pctx))
+		return;
+
+	inode->i_size = 0;
+	inode->i_size_high = 0;
+	e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
+}
+
+static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	struct ext2_inode_large *inode;
+	struct ext2_ext_attr_entry *entry;
+	char *start, *end;
+	int storage_size, remain, offs;
+	int problem = 0;
+
+	inode = (struct ext2_inode_large *) pctx->inode;
+	storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
+		inode->i_extra_isize;
+	start = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+		inode->i_extra_isize + sizeof(__u32);
+	end = (char *) inode + EXT2_INODE_SIZE(ctx->fs->super);
+	entry = (struct ext2_ext_attr_entry *) start;
+
+	/* scan all entry's headers first */
+
+	/* take finish entry 0UL into account */
+	remain = storage_size - sizeof(__u32);
+	offs = end - start;
+
+	while (!EXT2_EXT_IS_LAST_ENTRY(entry)) {
+
+		/* header eats this space */
+		remain -= sizeof(struct ext2_ext_attr_entry);
+
+		/* is attribute name valid? */
+		if (EXT2_EXT_ATTR_SIZE(entry->e_name_len) > remain) {
+			pctx->num = entry->e_name_len;
+			problem = PR_1_ATTR_NAME_LEN;
+			goto fix;
+		}
+
+		/* attribute len eats this space */
+		remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
+
+		/* check value size */
+		if (entry->e_value_size == 0 || entry->e_value_size > remain) {
+			pctx->num = entry->e_value_size;
+			problem = PR_1_ATTR_VALUE_SIZE;
+			goto fix;
+		}
+
+		/* check value placement */
+		if (entry->e_value_offs +
+		    EXT2_XATTR_SIZE(entry->e_value_size) != offs) {
+			printf("(entry->e_value_offs + entry->e_value_size: %d, offs: %d)\n", entry->e_value_offs + entry->e_value_size, offs);
+			pctx->num = entry->e_value_offs;
+			problem = PR_1_ATTR_VALUE_OFFSET;
+			goto fix;
+		}
+
+		/* e_value_block must be 0 in inode's ea */
+		if (entry->e_value_block != 0) {
+			pctx->num = entry->e_value_block;
+			problem = PR_1_ATTR_VALUE_BLOCK;
+			goto fix;
+		}
+
+		/* e_hash must be 0 in inode's ea */
+		if (entry->e_hash != 0) {
+			pctx->num = entry->e_hash;
+			problem = PR_1_ATTR_HASH;
+			goto fix;
+		}
+
+		remain -= entry->e_value_size;
+		offs -= EXT2_XATTR_SIZE(entry->e_value_size);
+
+		entry = EXT2_EXT_ATTR_NEXT(entry);
+	}
+fix:
+	/*
+	 * it seems like a corruption. it's very unlikely we could repair
+	 * EA(s) in automatic fashion -bzzz
+	 */
+	if (problem == 0 || !fix_problem(ctx, problem, pctx))
+		return;
+
+	/* simple remove all possible EA(s) */
+	*((__u32 *)start) = 0UL;
+	e2fsck_write_inode_full(ctx, pctx->ino, (struct ext2_inode *)inode,
+				EXT2_INODE_SIZE(sb), "pass1");
+}
+
+static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	struct ext2_inode_large *inode;
+	__u32 *eamagic;
+	int min, max;
+
+	inode = (struct ext2_inode_large *) pctx->inode;
+	if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
+		/* this isn't large inode. so, nothing to check */
+		return;
+	}
+
+	/* i_extra_isize must cover i_extra_isize + i_pad1 at least */
+	min = sizeof(inode->i_extra_isize) + sizeof(inode->i_pad1);
+	max = EXT2_INODE_SIZE(sb) - EXT2_GOOD_OLD_INODE_SIZE;
+	/*
+	 * For now we will allow i_extra_isize to be 0, but really
+	 * implementations should never allow i_extra_isize to be 0
+	 */
+	if (inode->i_extra_isize &&
+	    (inode->i_extra_isize < min || inode->i_extra_isize > max)) {
+		if (!fix_problem(ctx, PR_1_EXTRA_ISIZE, pctx))
+			return;
+		inode->i_extra_isize = min;
+		e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
+					EXT2_INODE_SIZE(sb), "pass1");
+		return;
+	}
+
+	eamagic = (__u32 *) (((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
+			inode->i_extra_isize);
+	if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
+		/* it seems inode has an extended attribute(s) in body */
+		check_ea_in_inode(ctx, pctx);
+	}
+}
+
+static void e2fsck_pass1(e2fsck_t ctx)
+{
+	int     i;
+	__u64   max_sizes;
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t      ino;
+	struct ext2_inode *inode;
+	ext2_inode_scan scan;
+	char            *block_buf;
+	unsigned char   frag, fsize;
+	struct          problem_context pctx;
+	struct          scan_callback_struct scan_struct;
+	struct ext2_super_block *sb = ctx->fs->super;
+	int             imagic_fs;
+	int             busted_fs_time = 0;
+	int             inode_size;
+
+	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+
+	if ((fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
+	    !(ctx->options & E2F_OPT_NO)) {
+		if (ext2fs_u32_list_create(&ctx->dirs_to_hash, 50))
+			ctx->dirs_to_hash = 0;
+	}
+
+	/* Pass 1 */
+
+#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
+
+	for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
+		max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
+		max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
+		max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
+		max_sizes = (max_sizes * (1UL << i)) - 1;
+		ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
+	}
+#undef EXT2_BPP
+
+	imagic_fs = (sb->s_feature_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES);
+
+	/*
+	 * Allocate bitmaps structures
+	 */
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("in-use inode map"),
+					      &ctx->inode_used_map);
+	if (pctx.errcode) {
+		pctx.num = 1;
+		fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
+				_("directory inode map"), &ctx->inode_dir_map);
+	if (pctx.errcode) {
+		pctx.num = 2;
+		fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
+			_("regular file inode map"), &ctx->inode_reg_map);
+	if (pctx.errcode) {
+		pctx.num = 6;
+		fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	pctx.errcode = ext2fs_allocate_block_bitmap(fs, _("in-use block map"),
+					      &ctx->block_found_map);
+	if (pctx.errcode) {
+		pctx.num = 1;
+		fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	pctx.errcode = ext2fs_create_icount2(fs, 0, 0, 0,
+					     &ctx->inode_link_info);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1_ALLOCATE_ICOUNT, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	inode_size = EXT2_INODE_SIZE(fs->super);
+	inode = (struct ext2_inode *)
+		e2fsck_allocate_memory(ctx, inode_size, "scratch inode");
+
+	inodes_to_process = (struct process_inode_block *)
+		e2fsck_allocate_memory(ctx,
+				       (ctx->process_inode_size *
+					sizeof(struct process_inode_block)),
+				       "array of inodes to process");
+	process_inode_count = 0;
+
+	pctx.errcode = ext2fs_init_dblist(fs, 0);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1_ALLOCATE_DBCOUNT, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	/*
+	 * If the last orphan field is set, clear it, since the pass1
+	 * processing will automatically find and clear the orphans.
+	 * In the future, we may want to try using the last_orphan
+	 * linked list ourselves, but for now, we clear it so that the
+	 * ext3 mount code won't get confused.
+	 */
+	if (!(ctx->options & E2F_OPT_READONLY)) {
+		if (fs->super->s_last_orphan) {
+			fs->super->s_last_orphan = 0;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
+	mark_table_blocks(ctx);
+	block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
+						    "block interate buffer");
+	e2fsck_use_inode_shortcuts(ctx, 1);
+	ehandler_operation(_("doing inode scan"));
+	pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
+					      &scan);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	ext2fs_inode_scan_flags(scan, EXT2_SF_SKIP_MISSING_ITABLE, 0);
+	ctx->stashed_inode = inode;
+	scan_struct.ctx = ctx;
+	scan_struct.block_buf = block_buf;
+	ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
+	if (ctx->progress)
+		if ((ctx->progress)(ctx, 1, 0, ctx->fs->group_desc_count))
+			return;
+	if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
+	    (fs->super->s_mtime < fs->super->s_inodes_count))
+		busted_fs_time = 1;
+
+	while (1) {
+		pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
+							  inode, inode_size);
+		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+			return;
+		if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
+			continue;
+		}
+		if (pctx.errcode) {
+			fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		if (!ino)
+			break;
+		pctx.ino = ino;
+		pctx.inode = inode;
+		ctx->stashed_ino = ino;
+		if (inode->i_links_count) {
+			pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
+					   ino, inode->i_links_count);
+			if (pctx.errcode) {
+				pctx.num = inode->i_links_count;
+				fix_problem(ctx, PR_1_ICOUNT_STORE, &pctx);
+				ctx->flags |= E2F_FLAG_ABORT;
+				return;
+			}
+		}
+		if (ino == EXT2_BAD_INO) {
+			struct process_block_struct_1 pb;
+
+			pctx.errcode = ext2fs_copy_bitmap(ctx->block_found_map,
+							  &pb.fs_meta_blocks);
+			if (pctx.errcode) {
+				pctx.num = 4;
+				fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, &pctx);
+				ctx->flags |= E2F_FLAG_ABORT;
+				return;
+			}
+			pb.ino = EXT2_BAD_INO;
+			pb.num_blocks = pb.last_block = 0;
+			pb.num_illegal_blocks = 0;
+			pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
+			pb.is_reg = 0; pb.fragmented = 0; pb.bbcheck = 0;
+			pb.inode = inode;
+			pb.pctx = &pctx;
+			pb.ctx = ctx;
+			pctx.errcode = ext2fs_block_iterate2(fs, ino, 0,
+				     block_buf, process_bad_block, &pb);
+			ext2fs_free_block_bitmap(pb.fs_meta_blocks);
+			if (pctx.errcode) {
+				fix_problem(ctx, PR_1_BLOCK_ITERATE, &pctx);
+				ctx->flags |= E2F_FLAG_ABORT;
+				return;
+			}
+			if (pb.bbcheck)
+				if (!fix_problem(ctx, PR_1_BBINODE_BAD_METABLOCK_PROMPT, &pctx)) {
+				ctx->flags |= E2F_FLAG_ABORT;
+				return;
+			}
+			ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+			clear_problem_context(&pctx);
+			continue;
+		} else if (ino == EXT2_ROOT_INO) {
+			/*
+			 * Make sure the root inode is a directory; if
+			 * not, offer to clear it.  It will be
+			 * regnerated in pass #3.
+			 */
+			if (!LINUX_S_ISDIR(inode->i_mode)) {
+				if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx)) {
+					inode->i_dtime = time(NULL);
+					inode->i_links_count = 0;
+					ext2fs_icount_store(ctx->inode_link_info,
+							    ino, 0);
+					e2fsck_write_inode(ctx, ino, inode,
+							   "pass1");
+				}
+			}
+			/*
+			 * If dtime is set, offer to clear it.  mke2fs
+			 * version 0.2b created filesystems with the
+			 * dtime field set for the root and lost+found
+			 * directories.  We won't worry about
+			 * /lost+found, since that can be regenerated
+			 * easily.  But we will fix the root directory
+			 * as a special case.
+			 */
+			if (inode->i_dtime && inode->i_links_count) {
+				if (fix_problem(ctx, PR_1_ROOT_DTIME, &pctx)) {
+					inode->i_dtime = 0;
+					e2fsck_write_inode(ctx, ino, inode,
+							   "pass1");
+				}
+			}
+		} else if (ino == EXT2_JOURNAL_INO) {
+			ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+			if (fs->super->s_journal_inum == EXT2_JOURNAL_INO) {
+				if (!LINUX_S_ISREG(inode->i_mode) &&
+				    fix_problem(ctx, PR_1_JOURNAL_BAD_MODE,
+						&pctx)) {
+					inode->i_mode = LINUX_S_IFREG;
+					e2fsck_write_inode(ctx, ino, inode,
+							   "pass1");
+				}
+				check_blocks(ctx, &pctx, block_buf);
+				continue;
+			}
+			if ((inode->i_links_count || inode->i_blocks ||
+			     inode->i_blocks || inode->i_block[0]) &&
+			    fix_problem(ctx, PR_1_JOURNAL_INODE_NOT_CLEAR,
+					&pctx)) {
+				memset(inode, 0, inode_size);
+				ext2fs_icount_store(ctx->inode_link_info,
+						    ino, 0);
+				e2fsck_write_inode_full(ctx, ino, inode,
+							inode_size, "pass1");
+			}
+		} else if (ino < EXT2_FIRST_INODE(fs->super)) {
+			int     problem = 0;
+
+			ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+			if (ino == EXT2_BOOT_LOADER_INO) {
+				if (LINUX_S_ISDIR(inode->i_mode))
+					problem = PR_1_RESERVED_BAD_MODE;
+			} else if (ino == EXT2_RESIZE_INO) {
+				if (inode->i_mode &&
+				    !LINUX_S_ISREG(inode->i_mode))
+					problem = PR_1_RESERVED_BAD_MODE;
+			} else {
+				if (inode->i_mode != 0)
+					problem = PR_1_RESERVED_BAD_MODE;
+			}
+			if (problem) {
+				if (fix_problem(ctx, problem, &pctx)) {
+					inode->i_mode = 0;
+					e2fsck_write_inode(ctx, ino, inode,
+							   "pass1");
+				}
+			}
+			check_blocks(ctx, &pctx, block_buf);
+			continue;
+		}
+		/*
+		 * Check for inodes who might have been part of the
+		 * orphaned list linked list.  They should have gotten
+		 * dealt with by now, unless the list had somehow been
+		 * corrupted.
+		 *
+		 * FIXME: In the future, inodes which are still in use
+		 * (and which are therefore) pending truncation should
+		 * be handled specially.  Right now we just clear the
+		 * dtime field, and the normal e2fsck handling of
+		 * inodes where i_size and the inode blocks are
+		 * inconsistent is to fix i_size, instead of releasing
+		 * the extra blocks.  This won't catch the inodes that
+		 * was at the end of the orphan list, but it's better
+		 * than nothing.  The right answer is that there
+		 * shouldn't be any bugs in the orphan list handling.  :-)
+		 */
+		if (inode->i_dtime && !busted_fs_time &&
+		    inode->i_dtime < ctx->fs->super->s_inodes_count) {
+			if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
+				inode->i_dtime = inode->i_links_count ?
+					0 : time(NULL);
+				e2fsck_write_inode(ctx, ino, inode,
+						   "pass1");
+			}
+		}
+
+		/*
+		 * This code assumes that deleted inodes have
+		 * i_links_count set to 0.
+		 */
+		if (!inode->i_links_count) {
+			if (!inode->i_dtime && inode->i_mode) {
+				if (fix_problem(ctx,
+					    PR_1_ZERO_DTIME, &pctx)) {
+					inode->i_dtime = time(NULL);
+					e2fsck_write_inode(ctx, ino, inode,
+							   "pass1");
+				}
+			}
+			continue;
+		}
+		/*
+		 * n.b.  0.3c ext2fs code didn't clear i_links_count for
+		 * deleted files.  Oops.
+		 *
+		 * Since all new ext2 implementations get this right,
+		 * we now assume that the case of non-zero
+		 * i_links_count and non-zero dtime means that we
+		 * should keep the file, not delete it.
+		 *
+		 */
+		if (inode->i_dtime) {
+			if (fix_problem(ctx, PR_1_SET_DTIME, &pctx)) {
+				inode->i_dtime = 0;
+				e2fsck_write_inode(ctx, ino, inode, "pass1");
+			}
+		}
+
+		ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+		switch (fs->super->s_creator_os) {
+		    case EXT2_OS_LINUX:
+			frag = inode->osd2.linux2.l_i_frag;
+			fsize = inode->osd2.linux2.l_i_fsize;
+			break;
+		    case EXT2_OS_HURD:
+			frag = inode->osd2.hurd2.h_i_frag;
+			fsize = inode->osd2.hurd2.h_i_fsize;
+			break;
+		    case EXT2_OS_MASIX:
+			frag = inode->osd2.masix2.m_i_frag;
+			fsize = inode->osd2.masix2.m_i_fsize;
+			break;
+		    default:
+			frag = fsize = 0;
+		}
+
+		if (inode->i_faddr || frag || fsize ||
+		    (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
+			mark_inode_bad(ctx, ino);
+		if (inode->i_flags & EXT2_IMAGIC_FL) {
+			if (imagic_fs) {
+				if (!ctx->inode_imagic_map)
+					alloc_imagic_map(ctx);
+				ext2fs_mark_inode_bitmap(ctx->inode_imagic_map,
+							 ino);
+			} else {
+				if (fix_problem(ctx, PR_1_SET_IMAGIC, &pctx)) {
+					inode->i_flags &= ~EXT2_IMAGIC_FL;
+					e2fsck_write_inode(ctx, ino,
+							   inode, "pass1");
+				}
+			}
+		}
+
+		check_inode_extra_space(ctx, &pctx);
+
+		if (LINUX_S_ISDIR(inode->i_mode)) {
+			ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
+			e2fsck_add_dir_info(ctx, ino, 0);
+			ctx->fs_directory_count++;
+		} else if (LINUX_S_ISREG (inode->i_mode)) {
+			ext2fs_mark_inode_bitmap(ctx->inode_reg_map, ino);
+			ctx->fs_regular_count++;
+		} else if (LINUX_S_ISCHR (inode->i_mode) &&
+			   e2fsck_pass1_check_device_inode(fs, inode)) {
+			check_immutable(ctx, &pctx);
+			check_size(ctx, &pctx);
+			ctx->fs_chardev_count++;
+		} else if (LINUX_S_ISBLK (inode->i_mode) &&
+			   e2fsck_pass1_check_device_inode(fs, inode)) {
+			check_immutable(ctx, &pctx);
+			check_size(ctx, &pctx);
+			ctx->fs_blockdev_count++;
+		} else if (LINUX_S_ISLNK (inode->i_mode) &&
+			   e2fsck_pass1_check_symlink(fs, inode, block_buf)) {
+			check_immutable(ctx, &pctx);
+			ctx->fs_symlinks_count++;
+			if (ext2fs_inode_data_blocks(fs, inode) == 0) {
+				ctx->fs_fast_symlinks_count++;
+				check_blocks(ctx, &pctx, block_buf);
+				continue;
+			}
+		}
+		else if (LINUX_S_ISFIFO (inode->i_mode) &&
+			 e2fsck_pass1_check_device_inode(fs, inode)) {
+			check_immutable(ctx, &pctx);
+			check_size(ctx, &pctx);
+			ctx->fs_fifo_count++;
+		} else if ((LINUX_S_ISSOCK (inode->i_mode)) &&
+			   e2fsck_pass1_check_device_inode(fs, inode)) {
+			check_immutable(ctx, &pctx);
+			check_size(ctx, &pctx);
+			ctx->fs_sockets_count++;
+		} else
+			mark_inode_bad(ctx, ino);
+		if (inode->i_block[EXT2_IND_BLOCK])
+			ctx->fs_ind_count++;
+		if (inode->i_block[EXT2_DIND_BLOCK])
+			ctx->fs_dind_count++;
+		if (inode->i_block[EXT2_TIND_BLOCK])
+			ctx->fs_tind_count++;
+		if (inode->i_block[EXT2_IND_BLOCK] ||
+		    inode->i_block[EXT2_DIND_BLOCK] ||
+		    inode->i_block[EXT2_TIND_BLOCK] ||
+		    inode->i_file_acl) {
+			inodes_to_process[process_inode_count].ino = ino;
+			inodes_to_process[process_inode_count].inode = *inode;
+			process_inode_count++;
+		} else
+			check_blocks(ctx, &pctx, block_buf);
+
+		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+			return;
+
+		if (process_inode_count >= ctx->process_inode_size) {
+			process_inodes(ctx, block_buf);
+
+			if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+				return;
+		}
+	}
+	process_inodes(ctx, block_buf);
+	ext2fs_close_inode_scan(scan);
+	ehandler_operation(0);
+
+	/*
+	 * If any extended attribute blocks' reference counts need to
+	 * be adjusted, either up (ctx->refcount_extra), or down
+	 * (ctx->refcount), then fix them.
+	 */
+	if (ctx->refcount) {
+		adjust_extattr_refcount(ctx, ctx->refcount, block_buf, -1);
+		ea_refcount_free(ctx->refcount);
+		ctx->refcount = 0;
+	}
+	if (ctx->refcount_extra) {
+		adjust_extattr_refcount(ctx, ctx->refcount_extra,
+					block_buf, +1);
+		ea_refcount_free(ctx->refcount_extra);
+		ctx->refcount_extra = 0;
+	}
+
+	if (ctx->invalid_bitmaps)
+		handle_fs_bad_blocks(ctx);
+
+	/* We don't need the block_ea_map any more */
+	ext2fs_free_block_bitmap(ctx->block_ea_map);
+	ctx->block_ea_map = 0;
+
+	if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
+		ext2fs_block_bitmap save_bmap;
+
+		save_bmap = fs->block_map;
+		fs->block_map = ctx->block_found_map;
+		clear_problem_context(&pctx);
+		pctx.errcode = ext2fs_create_resize_inode(fs);
+		if (pctx.errcode) {
+			fix_problem(ctx, PR_1_RESIZE_INODE_CREATE, &pctx);
+			/* Should never get here */
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
+				  "recreate inode");
+		inode->i_mtime = time(NULL);
+		e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
+				  "recreate inode");
+		fs->block_map = save_bmap;
+		ctx->flags &= ~E2F_FLAG_RESIZE_INODE;
+	}
+
+	if (ctx->flags & E2F_FLAG_RESTART) {
+		/*
+		 * Only the master copy of the superblock and block
+		 * group descriptors are going to be written during a
+		 * restart, so set the superblock to be used to be the
+		 * master superblock.
+		 */
+		ctx->use_superblock = 0;
+		unwind_pass1();
+		goto endit;
+	}
+
+	if (ctx->block_dup_map) {
+		if (ctx->options & E2F_OPT_PREEN) {
+			clear_problem_context(&pctx);
+			fix_problem(ctx, PR_1_DUP_BLOCKS_PREENSTOP, &pctx);
+		}
+		e2fsck_pass1_dupblocks(ctx, block_buf);
+	}
+	ext2fs_free_mem(&inodes_to_process);
+endit:
+	e2fsck_use_inode_shortcuts(ctx, 0);
+
+	ext2fs_free_mem(&block_buf);
+	ext2fs_free_mem(&inode);
+}
+
+/*
+ * When the inode_scan routines call this callback at the end of the
+ * glock group, call process_inodes.
+ */
+static errcode_t scan_callback(ext2_filsys fs,
+			       dgrp_t group, void * priv_data)
+{
+	struct scan_callback_struct *scan_struct;
+	e2fsck_t ctx;
+
+	scan_struct = (struct scan_callback_struct *) priv_data;
+	ctx = scan_struct->ctx;
+
+	process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
+
+	if (ctx->progress)
+		if ((ctx->progress)(ctx, 1, group+1,
+				    ctx->fs->group_desc_count))
+			return EXT2_ET_CANCEL_REQUESTED;
+
+	return 0;
+}
+
+/*
+ * Process the inodes in the "inodes to process" list.
+ */
+static void process_inodes(e2fsck_t ctx, char *block_buf)
+{
+	int                     i;
+	struct ext2_inode       *old_stashed_inode;
+	ext2_ino_t              old_stashed_ino;
+	const char              *old_operation;
+	char                    buf[80];
+	struct problem_context  pctx;
+
+	/* begin process_inodes */
+	if (process_inode_count == 0)
+		return;
+	old_operation = ehandler_operation(0);
+	old_stashed_inode = ctx->stashed_inode;
+	old_stashed_ino = ctx->stashed_ino;
+	qsort(inodes_to_process, process_inode_count,
+		      sizeof(struct process_inode_block), process_inode_cmp);
+	clear_problem_context(&pctx);
+	for (i=0; i < process_inode_count; i++) {
+		pctx.inode = ctx->stashed_inode = &inodes_to_process[i].inode;
+		pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
+		sprintf(buf, _("reading indirect blocks of inode %u"),
+			pctx.ino);
+		ehandler_operation(buf);
+		check_blocks(ctx, &pctx, block_buf);
+		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+			break;
+	}
+	ctx->stashed_inode = old_stashed_inode;
+	ctx->stashed_ino = old_stashed_ino;
+	process_inode_count = 0;
+	/* end process inodes */
+
+	ehandler_operation(old_operation);
+}
+
+static int process_inode_cmp(const void *a, const void *b)
+{
+	const struct process_inode_block *ib_a =
+		(const struct process_inode_block *) a;
+	const struct process_inode_block *ib_b =
+		(const struct process_inode_block *) b;
+	int     ret;
+
+	ret = (ib_a->inode.i_block[EXT2_IND_BLOCK] -
+	       ib_b->inode.i_block[EXT2_IND_BLOCK]);
+	if (ret == 0)
+		ret = ib_a->inode.i_file_acl - ib_b->inode.i_file_acl;
+	return ret;
+}
+
+/*
+ * Mark an inode as being bad in some what
+ */
+static void mark_inode_bad(e2fsck_t ctx, ino_t ino)
+{
+	struct          problem_context pctx;
+
+	if (!ctx->inode_bad_map) {
+		clear_problem_context(&pctx);
+
+		pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
+			    _("bad inode map"), &ctx->inode_bad_map);
+		if (pctx.errcode) {
+			pctx.num = 3;
+			fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+			/* Should never get here */
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+	}
+	ext2fs_mark_inode_bitmap(ctx->inode_bad_map, ino);
+}
+
+
+/*
+ * This procedure will allocate the inode imagic table
+ */
+static void alloc_imagic_map(e2fsck_t ctx)
+{
+	struct          problem_context pctx;
+
+	clear_problem_context(&pctx);
+	pctx.errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
+					      _("imagic inode map"),
+					      &ctx->inode_imagic_map);
+	if (pctx.errcode) {
+		pctx.num = 5;
+		fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+		/* Should never get here */
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+}
+
+/*
+ * Marks a block as in use, setting the dup_map if it's been set
+ * already.  Called by process_block and process_bad_block.
+ *
+ * WARNING: Assumes checks have already been done to make sure block
+ * is valid.  This is true in both process_block and process_bad_block.
+ */
+static void mark_block_used(e2fsck_t ctx, blk_t block)
+{
+	struct          problem_context pctx;
+
+	clear_problem_context(&pctx);
+
+	if (ext2fs_fast_test_block_bitmap(ctx->block_found_map, block)) {
+		if (!ctx->block_dup_map) {
+			pctx.errcode = ext2fs_allocate_block_bitmap(ctx->fs,
+			      _("multiply claimed block map"),
+			      &ctx->block_dup_map);
+			if (pctx.errcode) {
+				pctx.num = 3;
+				fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR,
+					    &pctx);
+				/* Should never get here */
+				ctx->flags |= E2F_FLAG_ABORT;
+				return;
+			}
+		}
+		ext2fs_fast_mark_block_bitmap(ctx->block_dup_map, block);
+	} else {
+		ext2fs_fast_mark_block_bitmap(ctx->block_found_map, block);
+	}
+}
+
+/*
+ * Adjust the extended attribute block's reference counts at the end
+ * of pass 1, either by subtracting out references for EA blocks that
+ * are still referenced in ctx->refcount, or by adding references for
+ * EA blocks that had extra references as accounted for in
+ * ctx->refcount_extra.
+ */
+static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
+				    char *block_buf, int adjust_sign)
+{
+	struct ext2_ext_attr_header     *header;
+	struct problem_context          pctx;
+	ext2_filsys                     fs = ctx->fs;
+	blk_t                           blk;
+	__u32                           should_be;
+	int                             count;
+
+	clear_problem_context(&pctx);
+
+	ea_refcount_intr_begin(refcount);
+	while (1) {
+		if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
+			break;
+		pctx.blk = blk;
+		pctx.errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
+		if (pctx.errcode) {
+			fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
+			return;
+		}
+		header = (struct ext2_ext_attr_header *) block_buf;
+		pctx.blkcount = header->h_refcount;
+		should_be = header->h_refcount + adjust_sign * count;
+		pctx.num = should_be;
+		if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
+			header->h_refcount = should_be;
+			pctx.errcode = ext2fs_write_ext_attr(fs, blk,
+							     block_buf);
+			if (pctx.errcode) {
+				fix_problem(ctx, PR_1_EXTATTR_WRITE, &pctx);
+				continue;
+			}
+		}
+	}
+}
+
+/*
+ * Handle processing the extended attribute blocks
+ */
+static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
+			   char *block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t      ino = pctx->ino;
+	struct ext2_inode *inode = pctx->inode;
+	blk_t           blk;
+	char *          end;
+	struct ext2_ext_attr_header *header;
+	struct ext2_ext_attr_entry *entry;
+	int             count;
+	region_t        region;
+
+	blk = inode->i_file_acl;
+	if (blk == 0)
+		return 0;
+
+	/*
+	 * If the Extended attribute flag isn't set, then a non-zero
+	 * file acl means that the inode is corrupted.
+	 *
+	 * Or if the extended attribute block is an invalid block,
+	 * then the inode is also corrupted.
+	 */
+	if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) ||
+	    (blk < fs->super->s_first_data_block) ||
+	    (blk >= fs->super->s_blocks_count)) {
+		mark_inode_bad(ctx, ino);
+		return 0;
+	}
+
+	/* If ea bitmap hasn't been allocated, create it */
+	if (!ctx->block_ea_map) {
+		pctx->errcode = ext2fs_allocate_block_bitmap(fs,
+						      _("ext attr block map"),
+						      &ctx->block_ea_map);
+		if (pctx->errcode) {
+			pctx->num = 2;
+			fix_problem(ctx, PR_1_ALLOCATE_BBITMAP_ERROR, pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return 0;
+		}
+	}
+
+	/* Create the EA refcount structure if necessary */
+	if (!ctx->refcount) {
+		pctx->errcode = ea_refcount_create(0, &ctx->refcount);
+		if (pctx->errcode) {
+			pctx->num = 1;
+			fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return 0;
+		}
+	}
+
+	/* Have we seen this EA block before? */
+	if (ext2fs_fast_test_block_bitmap(ctx->block_ea_map, blk)) {
+		if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
+			return 1;
+		/* Ooops, this EA was referenced more than it stated */
+		if (!ctx->refcount_extra) {
+			pctx->errcode = ea_refcount_create(0,
+					   &ctx->refcount_extra);
+			if (pctx->errcode) {
+				pctx->num = 2;
+				fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+				ctx->flags |= E2F_FLAG_ABORT;
+				return 0;
+			}
+		}
+		ea_refcount_increment(ctx->refcount_extra, blk, 0);
+		return 1;
+	}
+
+	/*
+	 * OK, we haven't seen this EA block yet.  So we need to
+	 * validate it
+	 */
+	pctx->blk = blk;
+	pctx->errcode = ext2fs_read_ext_attr(fs, blk, block_buf);
+	if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
+		goto clear_extattr;
+	header = (struct ext2_ext_attr_header *) block_buf;
+	pctx->blk = inode->i_file_acl;
+	if (((ctx->ext_attr_ver == 1) &&
+	     (header->h_magic != EXT2_EXT_ATTR_MAGIC_v1)) ||
+	    ((ctx->ext_attr_ver == 2) &&
+	     (header->h_magic != EXT2_EXT_ATTR_MAGIC))) {
+		if (fix_problem(ctx, PR_1_BAD_EA_BLOCK, pctx))
+			goto clear_extattr;
+	}
+
+	if (header->h_blocks != 1) {
+		if (fix_problem(ctx, PR_1_EA_MULTI_BLOCK, pctx))
+			goto clear_extattr;
+	}
+
+	region = region_create(0, fs->blocksize);
+	if (!region) {
+		fix_problem(ctx, PR_1_EA_ALLOC_REGION, pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return 0;
+	}
+	if (region_allocate(region, 0, sizeof(struct ext2_ext_attr_header))) {
+		if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+			goto clear_extattr;
+	}
+
+	entry = (struct ext2_ext_attr_entry *)(header+1);
+	end = block_buf + fs->blocksize;
+	while ((char *)entry < end && *(__u32 *)entry) {
+		if (region_allocate(region, (char *)entry - (char *)header,
+				   EXT2_EXT_ATTR_LEN(entry->e_name_len))) {
+			if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+				goto clear_extattr;
+		}
+		if ((ctx->ext_attr_ver == 1 &&
+		     (entry->e_name_len == 0 || entry->e_name_index != 0)) ||
+		    (ctx->ext_attr_ver == 2 &&
+		     entry->e_name_index == 0)) {
+			if (fix_problem(ctx, PR_1_EA_BAD_NAME, pctx))
+				goto clear_extattr;
+		}
+		if (entry->e_value_block != 0) {
+			if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
+				goto clear_extattr;
+		}
+		if (entry->e_value_size &&
+		    region_allocate(region, entry->e_value_offs,
+				    EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
+			if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+				goto clear_extattr;
+		}
+		entry = EXT2_EXT_ATTR_NEXT(entry);
+	}
+	if (region_allocate(region, (char *)entry - (char *)header, 4)) {
+		if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
+			goto clear_extattr;
+	}
+	region_free(region);
+
+	count = header->h_refcount - 1;
+	if (count)
+		ea_refcount_store(ctx->refcount, blk, count);
+	mark_block_used(ctx, blk);
+	ext2fs_fast_mark_block_bitmap(ctx->block_ea_map, blk);
+
+	return 1;
+
+clear_extattr:
+	inode->i_file_acl = 0;
+	e2fsck_write_inode(ctx, ino, inode, "check_ext_attr");
+	return 0;
+}
+
+/* Returns 1 if bad htree, 0 if OK */
+static int handle_htree(e2fsck_t ctx, struct problem_context *pctx,
+			ext2_ino_t ino FSCK_ATTR((unused)),
+			struct ext2_inode *inode,
+			char *block_buf)
+{
+	struct ext2_dx_root_info        *root;
+	ext2_filsys                     fs = ctx->fs;
+	errcode_t                       retval;
+	blk_t                           blk;
+
+	if ((!LINUX_S_ISDIR(inode->i_mode) &&
+	     fix_problem(ctx, PR_1_HTREE_NODIR, pctx)) ||
+	    (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) &&
+	     fix_problem(ctx, PR_1_HTREE_SET, pctx)))
+		return 1;
+
+	blk = inode->i_block[0];
+	if (((blk == 0) ||
+	     (blk < fs->super->s_first_data_block) ||
+	     (blk >= fs->super->s_blocks_count)) &&
+	    fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+		return 1;
+
+	retval = io_channel_read_blk(fs->io, blk, 1, block_buf);
+	if (retval && fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+		return 1;
+
+	/* XXX should check that beginning matches a directory */
+	root = (struct ext2_dx_root_info *) (block_buf + 24);
+
+	if ((root->reserved_zero || root->info_length < 8) &&
+	    fix_problem(ctx, PR_1_HTREE_BADROOT, pctx))
+		return 1;
+
+	pctx->num = root->hash_version;
+	if ((root->hash_version != EXT2_HASH_LEGACY) &&
+	    (root->hash_version != EXT2_HASH_HALF_MD4) &&
+	    (root->hash_version != EXT2_HASH_TEA) &&
+	    fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
+		return 1;
+
+	if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
+	    fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
+		return 1;
+
+	pctx->num = root->indirect_levels;
+	if ((root->indirect_levels > 1) &&
+	    fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * This subroutine is called on each inode to account for all of the
+ * blocks used by that inode.
+ */
+static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
+			 char *block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	struct process_block_struct_1 pb;
+	ext2_ino_t      ino = pctx->ino;
+	struct ext2_inode *inode = pctx->inode;
+	int             bad_size = 0;
+	int             dirty_inode = 0;
+	__u64           size;
+
+	pb.ino = ino;
+	pb.num_blocks = 0;
+	pb.last_block = -1;
+	pb.num_illegal_blocks = 0;
+	pb.suppress = 0; pb.clear = 0;
+	pb.fragmented = 0;
+	pb.compressed = 0;
+	pb.previous_block = 0;
+	pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
+	pb.is_reg = LINUX_S_ISREG(inode->i_mode);
+	pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
+	pb.inode = inode;
+	pb.pctx = pctx;
+	pb.ctx = ctx;
+	pctx->ino = ino;
+	pctx->errcode = 0;
+
+	if (inode->i_flags & EXT2_COMPRBLK_FL) {
+		if (fs->super->s_feature_incompat &
+		    EXT2_FEATURE_INCOMPAT_COMPRESSION)
+			pb.compressed = 1;
+		else {
+			if (fix_problem(ctx, PR_1_COMPR_SET, pctx)) {
+				inode->i_flags &= ~EXT2_COMPRBLK_FL;
+				dirty_inode++;
+			}
+		}
+	}
+
+	if (inode->i_file_acl && check_ext_attr(ctx, pctx, block_buf))
+		pb.num_blocks++;
+
+	if (ext2fs_inode_has_valid_blocks(inode))
+		pctx->errcode = ext2fs_block_iterate2(fs, ino,
+				       pb.is_dir ? BLOCK_FLAG_HOLE : 0,
+				       block_buf, process_block, &pb);
+	end_problem_latch(ctx, PR_LATCH_BLOCK);
+	end_problem_latch(ctx, PR_LATCH_TOOBIG);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		goto out;
+	if (pctx->errcode)
+		fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
+
+	if (pb.fragmented && pb.num_blocks < fs->super->s_blocks_per_group)
+		ctx->fs_fragmented++;
+
+	if (pb.clear) {
+		inode->i_links_count = 0;
+		ext2fs_icount_store(ctx->inode_link_info, ino, 0);
+		inode->i_dtime = time(NULL);
+		dirty_inode++;
+		ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+		ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
+		ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+		/*
+		 * The inode was probably partially accounted for
+		 * before processing was aborted, so we need to
+		 * restart the pass 1 scan.
+		 */
+		ctx->flags |= E2F_FLAG_RESTART;
+		goto out;
+	}
+
+	if (inode->i_flags & EXT2_INDEX_FL) {
+		if (handle_htree(ctx, pctx, ino, inode, block_buf)) {
+			inode->i_flags &= ~EXT2_INDEX_FL;
+			dirty_inode++;
+		} else {
+#ifdef ENABLE_HTREE
+			e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
+#endif
+		}
+	}
+	if (ctx->dirs_to_hash && pb.is_dir &&
+	    !(inode->i_flags & EXT2_INDEX_FL) &&
+	    ((inode->i_size / fs->blocksize) >= 3))
+		ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+
+	if (!pb.num_blocks && pb.is_dir) {
+		if (fix_problem(ctx, PR_1_ZERO_LENGTH_DIR, pctx)) {
+			inode->i_links_count = 0;
+			ext2fs_icount_store(ctx->inode_link_info, ino, 0);
+			inode->i_dtime = time(NULL);
+			dirty_inode++;
+			ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+			ext2fs_unmark_inode_bitmap(ctx->inode_reg_map, ino);
+			ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+			ctx->fs_directory_count--;
+			goto out;
+		}
+	}
+
+	pb.num_blocks *= (fs->blocksize / 512);
+
+	if (pb.is_dir) {
+		int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
+		if (nblock > (pb.last_block + 1))
+			bad_size = 1;
+		else if (nblock < (pb.last_block + 1)) {
+			if (((pb.last_block + 1) - nblock) >
+			    fs->super->s_prealloc_dir_blocks)
+				bad_size = 2;
+		}
+	} else {
+		size = EXT2_I_SIZE(inode);
+		if ((pb.last_block >= 0) &&
+		    (size < (__u64) pb.last_block * fs->blocksize))
+			bad_size = 3;
+		else if (size > ext2_max_sizes[fs->super->s_log_block_size])
+			bad_size = 4;
+	}
+	/* i_size for symlinks is checked elsewhere */
+	if (bad_size && !LINUX_S_ISLNK(inode->i_mode)) {
+		pctx->num = (pb.last_block+1) * fs->blocksize;
+		if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
+			inode->i_size = pctx->num;
+			if (!LINUX_S_ISDIR(inode->i_mode))
+				inode->i_size_high = pctx->num >> 32;
+			dirty_inode++;
+		}
+		pctx->num = 0;
+	}
+	if (LINUX_S_ISREG(inode->i_mode) &&
+	    (inode->i_size_high || inode->i_size & 0x80000000UL))
+		ctx->large_files++;
+	if (pb.num_blocks != inode->i_blocks) {
+		pctx->num = pb.num_blocks;
+		if (fix_problem(ctx, PR_1_BAD_I_BLOCKS, pctx)) {
+			inode->i_blocks = pb.num_blocks;
+			dirty_inode++;
+		}
+		pctx->num = 0;
+	}
+out:
+	if (dirty_inode)
+		e2fsck_write_inode(ctx, ino, inode, "check_blocks");
+}
+
+
+/*
+ * This is a helper function for check_blocks().
+ */
+static int process_block(ext2_filsys fs,
+		  blk_t *block_nr,
+		  e2_blkcnt_t blockcnt,
+		  blk_t ref_block FSCK_ATTR((unused)),
+		  int ref_offset FSCK_ATTR((unused)),
+		  void *priv_data)
+{
+	struct process_block_struct_1 *p;
+	struct problem_context *pctx;
+	blk_t   blk = *block_nr;
+	int     ret_code = 0;
+	int     problem = 0;
+	e2fsck_t        ctx;
+
+	p = (struct process_block_struct_1 *) priv_data;
+	pctx = p->pctx;
+	ctx = p->ctx;
+
+	if (p->compressed && (blk == EXT2FS_COMPRESSED_BLKADDR)) {
+		/* todo: Check that the comprblk_fl is high, that the
+		   blkaddr pattern looks right (all non-holes up to
+		   first EXT2FS_COMPRESSED_BLKADDR, then all
+		   EXT2FS_COMPRESSED_BLKADDR up to end of cluster),
+		   that the feature_incompat bit is high, and that the
+		   inode is a regular file.  If we're doing a "full
+		   check" (a concept introduced to e2fsck by e2compr,
+		   meaning that we look at data blocks as well as
+		   metadata) then call some library routine that
+		   checks the compressed data.  I'll have to think
+		   about this, because one particularly important
+		   problem to be able to fix is to recalculate the
+		   cluster size if necessary.  I think that perhaps
+		   we'd better do most/all e2compr-specific checks
+		   separately, after the non-e2compr checks.  If not
+		   doing a full check, it may be useful to test that
+		   the personality is linux; e.g. if it isn't then
+		   perhaps this really is just an illegal block. */
+		return 0;
+	}
+
+	if (blk == 0) {
+		if (p->is_dir == 0) {
+			/*
+			 * Should never happen, since only directories
+			 * get called with BLOCK_FLAG_HOLE
+			 */
+#ifdef DEBUG_E2FSCK
+			printf("process_block() called with blk == 0, "
+			       "blockcnt=%d, inode %lu???\n",
+			       blockcnt, p->ino);
+#endif
+			return 0;
+		}
+		if (blockcnt < 0)
+			return 0;
+		if (blockcnt * fs->blocksize < p->inode->i_size) {
+			goto mark_dir;
+		}
+		return 0;
+	}
+
+	/*
+	 * Simplistic fragmentation check.  We merely require that the
+	 * file be contiguous.  (Which can never be true for really
+	 * big files that are greater than a block group.)
+	 */
+	if (!HOLE_BLKADDR(p->previous_block)) {
+		if (p->previous_block+1 != blk)
+			p->fragmented = 1;
+	}
+	p->previous_block = blk;
+
+	if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
+		problem = PR_1_TOOBIG_DIR;
+	if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
+		problem = PR_1_TOOBIG_REG;
+	if (!p->is_dir && !p->is_reg && blockcnt > 0)
+		problem = PR_1_TOOBIG_SYMLINK;
+
+	if (blk < fs->super->s_first_data_block ||
+	    blk >= fs->super->s_blocks_count)
+		problem = PR_1_ILLEGAL_BLOCK_NUM;
+
+	if (problem) {
+		p->num_illegal_blocks++;
+		if (!p->suppress && (p->num_illegal_blocks % 12) == 0) {
+			if (fix_problem(ctx, PR_1_TOO_MANY_BAD_BLOCKS, pctx)) {
+				p->clear = 1;
+				return BLOCK_ABORT;
+			}
+			if (fix_problem(ctx, PR_1_SUPPRESS_MESSAGES, pctx)) {
+				p->suppress = 1;
+				set_latch_flags(PR_LATCH_BLOCK,
+						PRL_SUPPRESS, 0);
+			}
+		}
+		pctx->blk = blk;
+		pctx->blkcount = blockcnt;
+		if (fix_problem(ctx, problem, pctx)) {
+			blk = *block_nr = 0;
+			ret_code = BLOCK_CHANGED;
+			goto mark_dir;
+		} else
+			return 0;
+	}
+
+	if (p->ino == EXT2_RESIZE_INO) {
+		/*
+		 * The resize inode has already be sanity checked
+		 * during pass #0 (the superblock checks).  All we
+		 * have to do is mark the double indirect block as
+		 * being in use; all of the other blocks are handled
+		 * by mark_table_blocks()).
+		 */
+		if (blockcnt == BLOCK_COUNT_DIND)
+			mark_block_used(ctx, blk);
+	} else
+		mark_block_used(ctx, blk);
+	p->num_blocks++;
+	if (blockcnt >= 0)
+		p->last_block = blockcnt;
+mark_dir:
+	if (p->is_dir && (blockcnt >= 0)) {
+		pctx->errcode = ext2fs_add_dir_block(fs->dblist, p->ino,
+						    blk, blockcnt);
+		if (pctx->errcode) {
+			pctx->blk = blk;
+			pctx->num = blockcnt;
+			fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
+			/* Should never get here */
+			ctx->flags |= E2F_FLAG_ABORT;
+			return BLOCK_ABORT;
+		}
+	}
+	return ret_code;
+}
+
+static int process_bad_block(ext2_filsys fs FSCK_ATTR((unused)),
+		      blk_t *block_nr,
+		      e2_blkcnt_t blockcnt,
+		      blk_t ref_block FSCK_ATTR((unused)),
+		      int ref_offset FSCK_ATTR((unused)),
+		      void *priv_data EXT2FS_ATTR((unused)))
+{
+	/*
+	 * Note: This function processes blocks for the bad blocks
+	 * inode, which is never compressed.  So we don't use HOLE_BLKADDR().
+	 */
+
+	printf("Unrecoverable Error: Found %"PRIi64" bad blocks starting at block number: %u\n", blockcnt, *block_nr);
+	return BLOCK_ERROR;
+}
+
+/*
+ * This routine gets called at the end of pass 1 if bad blocks are
+ * detected in the superblock, group descriptors, inode_bitmaps, or
+ * block bitmaps.  At this point, all of the blocks have been mapped
+ * out, so we can try to allocate new block(s) to replace the bad
+ * blocks.
+ */
+static void handle_fs_bad_blocks(e2fsck_t ctx)
+{
+	printf("Bad blocks detected on your filesystem\n"
+		"You should get your data off as the device will soon die\n");
+}
+
+/*
+ * This routine marks all blocks which are used by the superblock,
+ * group descriptors, inode bitmaps, and block bitmaps.
+ */
+static void mark_table_blocks(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t   block, b;
+	dgrp_t  i;
+	int     j;
+	struct problem_context pctx;
+
+	clear_problem_context(&pctx);
+
+	block = fs->super->s_first_data_block;
+	for (i = 0; i < fs->group_desc_count; i++) {
+		pctx.group = i;
+
+		ext2fs_reserve_super_and_bgd(fs, i, ctx->block_found_map);
+
+		/*
+		 * Mark the blocks used for the inode table
+		 */
+		if (fs->group_desc[i].bg_inode_table) {
+			for (j = 0, b = fs->group_desc[i].bg_inode_table;
+			     j < fs->inode_blocks_per_group;
+			     j++, b++) {
+				if (ext2fs_test_block_bitmap(ctx->block_found_map,
+							     b)) {
+					pctx.blk = b;
+					if (fix_problem(ctx,
+						PR_1_ITABLE_CONFLICT, &pctx)) {
+						ctx->invalid_inode_table_flag[i]++;
+						ctx->invalid_bitmaps++;
+					}
+				} else {
+					ext2fs_mark_block_bitmap(ctx->block_found_map, b);
+				}
+			}
+		}
+
+		/*
+		 * Mark block used for the block bitmap
+		 */
+		if (fs->group_desc[i].bg_block_bitmap) {
+			if (ext2fs_test_block_bitmap(ctx->block_found_map,
+				     fs->group_desc[i].bg_block_bitmap)) {
+				pctx.blk = fs->group_desc[i].bg_block_bitmap;
+				if (fix_problem(ctx, PR_1_BB_CONFLICT, &pctx)) {
+					ctx->invalid_block_bitmap_flag[i]++;
+					ctx->invalid_bitmaps++;
+				}
+			} else {
+				ext2fs_mark_block_bitmap(ctx->block_found_map,
+					fs->group_desc[i].bg_block_bitmap);
+			}
+		}
+		/*
+		 * Mark block used for the inode bitmap
+		 */
+		if (fs->group_desc[i].bg_inode_bitmap) {
+			if (ext2fs_test_block_bitmap(ctx->block_found_map,
+				     fs->group_desc[i].bg_inode_bitmap)) {
+				pctx.blk = fs->group_desc[i].bg_inode_bitmap;
+				if (fix_problem(ctx, PR_1_IB_CONFLICT, &pctx)) {
+					ctx->invalid_inode_bitmap_flag[i]++;
+					ctx->invalid_bitmaps++;
+				}
+			} else {
+				ext2fs_mark_block_bitmap(ctx->block_found_map,
+					fs->group_desc[i].bg_inode_bitmap);
+			}
+		}
+		block += fs->super->s_blocks_per_group;
+	}
+}
+
+/*
+ * Thes subroutines short circuits ext2fs_get_blocks and
+ * ext2fs_check_directory; we use them since we already have the inode
+ * structure, so there's no point in letting the ext2fs library read
+ * the inode again.
+ */
+static errcode_t pass1_get_blocks(ext2_filsys fs, ext2_ino_t ino,
+				  blk_t *blocks)
+{
+	e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+	int     i;
+
+	if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
+		return EXT2_ET_CALLBACK_NOTHANDLED;
+
+	for (i=0; i < EXT2_N_BLOCKS; i++)
+		blocks[i] = ctx->stashed_inode->i_block[i];
+	return 0;
+}
+
+static errcode_t pass1_read_inode(ext2_filsys fs, ext2_ino_t ino,
+				  struct ext2_inode *inode)
+{
+	e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+	if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
+		return EXT2_ET_CALLBACK_NOTHANDLED;
+	*inode = *ctx->stashed_inode;
+	return 0;
+}
+
+static errcode_t pass1_write_inode(ext2_filsys fs, ext2_ino_t ino,
+			    struct ext2_inode *inode)
+{
+	e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+	if ((ino == ctx->stashed_ino) && ctx->stashed_inode)
+		*ctx->stashed_inode = *inode;
+	return EXT2_ET_CALLBACK_NOTHANDLED;
+}
+
+static errcode_t pass1_check_directory(ext2_filsys fs, ext2_ino_t ino)
+{
+	e2fsck_t ctx = (e2fsck_t) fs->priv_data;
+
+	if ((ino != ctx->stashed_ino) || !ctx->stashed_inode)
+		return EXT2_ET_CALLBACK_NOTHANDLED;
+
+	if (!LINUX_S_ISDIR(ctx->stashed_inode->i_mode))
+		return EXT2_ET_NO_DIRECTORY;
+	return 0;
+}
+
+void e2fsck_use_inode_shortcuts(e2fsck_t ctx, int bool)
+{
+	ext2_filsys fs = ctx->fs;
+
+	if (bool) {
+		fs->get_blocks = pass1_get_blocks;
+		fs->check_directory = pass1_check_directory;
+		fs->read_inode = pass1_read_inode;
+		fs->write_inode = pass1_write_inode;
+		ctx->stashed_ino = 0;
+	} else {
+		fs->get_blocks = 0;
+		fs->check_directory = 0;
+		fs->read_inode = 0;
+		fs->write_inode = 0;
+	}
+}
+
+/*
+ * pass1b.c --- Pass #1b of e2fsck
+ *
+ * This file contains pass1B, pass1C, and pass1D of e2fsck.  They are
+ * only invoked if pass 1 discovered blocks which are in use by more
+ * than one inode.
+ *
+ * Pass1B scans the data blocks of all the inodes again, generating a
+ * complete list of duplicate blocks and which inodes have claimed
+ * them.
+ *
+ * Pass1C does a tree-traversal of the filesystem, to determine the
+ * parent directories of these inodes.  This step is necessary so that
+ * e2fsck can print out the pathnames of affected inodes.
+ *
+ * Pass1D is a reconciliation pass.  For each inode with duplicate
+ * blocks, the user is prompted if s/he would like to clone the file
+ * (so that the file gets a fresh copy of the duplicated blocks) or
+ * simply to delete the file.
+ *
+ */
+
+
+/* Needed for architectures where sizeof(int) != sizeof(void *) */
+#define INT_TO_VOIDPTR(val)  ((void *)(intptr_t)(val))
+#define VOIDPTR_TO_INT(ptr)  ((int)(intptr_t)(ptr))
+
+/* Define an extension to the ext2 library's block count information */
+#define BLOCK_COUNT_EXTATTR     (-5)
+
+struct block_el {
+	blk_t   block;
+	struct block_el *next;
+};
+
+struct inode_el {
+	ext2_ino_t      inode;
+	struct inode_el *next;
+};
+
+struct dup_block {
+	int             num_bad;
+	struct inode_el *inode_list;
+};
+
+/*
+ * This structure stores information about a particular inode which
+ * is sharing blocks with other inodes.  This information is collected
+ * to display to the user, so that the user knows what files he or she
+ * is dealing with, when trying to decide how to resolve the conflict
+ * of multiply-claimed blocks.
+ */
+struct dup_inode {
+	ext2_ino_t              dir;
+	int                     num_dupblocks;
+	struct ext2_inode       inode;
+	struct block_el         *block_list;
+};
+
+static int process_pass1b_block(ext2_filsys fs, blk_t   *blocknr,
+				e2_blkcnt_t blockcnt, blk_t ref_blk,
+				int ref_offset, void *priv_data);
+static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
+			struct dup_inode *dp, char *block_buf);
+static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
+		      struct dup_inode *dp, char* block_buf);
+static int check_if_fs_block(e2fsck_t ctx, blk_t test_blk);
+
+static void pass1b(e2fsck_t ctx, char *block_buf);
+static void pass1c(e2fsck_t ctx, char *block_buf);
+static void pass1d(e2fsck_t ctx, char *block_buf);
+
+static int dup_inode_count = 0;
+
+static dict_t blk_dict, ino_dict;
+
+static ext2fs_inode_bitmap inode_dup_map;
+
+static int dict_int_cmp(const void *a, const void *b)
+{
+	intptr_t        ia, ib;
+
+	ia = (intptr_t)a;
+	ib = (intptr_t)b;
+
+	return (ia-ib);
+}
+
+/*
+ * Add a duplicate block record
+ */
+static void add_dupe(e2fsck_t ctx, ext2_ino_t ino, blk_t blk,
+		     struct ext2_inode *inode)
+{
+	dnode_t *n;
+	struct dup_block        *db;
+	struct dup_inode        *di;
+	struct block_el         *blk_el;
+	struct inode_el         *ino_el;
+
+	n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
+	if (n)
+		db = (struct dup_block *) dnode_get(n);
+	else {
+		db = (struct dup_block *) e2fsck_allocate_memory(ctx,
+			 sizeof(struct dup_block), "duplicate block header");
+		db->num_bad = 0;
+		db->inode_list = 0;
+		dict_alloc_insert(&blk_dict, INT_TO_VOIDPTR(blk), db);
+	}
+	ino_el = (struct inode_el *) e2fsck_allocate_memory(ctx,
+			 sizeof(struct inode_el), "inode element");
+	ino_el->inode = ino;
+	ino_el->next = db->inode_list;
+	db->inode_list = ino_el;
+	db->num_bad++;
+
+	n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino));
+	if (n)
+		di = (struct dup_inode *) dnode_get(n);
+	else {
+		di = (struct dup_inode *) e2fsck_allocate_memory(ctx,
+			 sizeof(struct dup_inode), "duplicate inode header");
+		di->dir = (ino == EXT2_ROOT_INO) ? EXT2_ROOT_INO : 0;
+		di->num_dupblocks = 0;
+		di->block_list = 0;
+		di->inode = *inode;
+		dict_alloc_insert(&ino_dict, INT_TO_VOIDPTR(ino), di);
+	}
+	blk_el = (struct block_el *) e2fsck_allocate_memory(ctx,
+			 sizeof(struct block_el), "block element");
+	blk_el->block = blk;
+	blk_el->next = di->block_list;
+	di->block_list = blk_el;
+	di->num_dupblocks++;
+}
+
+/*
+ * Free a duplicate inode record
+ */
+static void inode_dnode_free(dnode_t *node)
+{
+	struct dup_inode        *di;
+	struct block_el         *p, *next;
+
+	di = (struct dup_inode *) dnode_get(node);
+	for (p = di->block_list; p; p = next) {
+		next = p->next;
+		free(p);
+	}
+	free(node);
+}
+
+/*
+ * Free a duplicate block record
+ */
+static void block_dnode_free(dnode_t *node)
+{
+	struct dup_block        *db;
+	struct inode_el         *p, *next;
+
+	db = (struct dup_block *) dnode_get(node);
+	for (p = db->inode_list; p; p = next) {
+		next = p->next;
+		free(p);
+	}
+	free(node);
+}
+
+
+/*
+ * Main procedure for handling duplicate blocks
+ */
+void e2fsck_pass1_dupblocks(e2fsck_t ctx, char *block_buf)
+{
+	ext2_filsys             fs = ctx->fs;
+	struct problem_context  pctx;
+
+	clear_problem_context(&pctx);
+
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
+		      _("multiply claimed inode map"), &inode_dup_map);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1B_ALLOCATE_IBITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	dict_init(&ino_dict, DICTCOUNT_T_MAX, dict_int_cmp);
+	dict_init(&blk_dict, DICTCOUNT_T_MAX, dict_int_cmp);
+	dict_set_allocator(&ino_dict, inode_dnode_free);
+	dict_set_allocator(&blk_dict, block_dnode_free);
+
+	pass1b(ctx, block_buf);
+	pass1c(ctx, block_buf);
+	pass1d(ctx, block_buf);
+
+	/*
+	 * Time to free all of the accumulated data structures that we
+	 * don't need anymore.
+	 */
+	dict_free_nodes(&ino_dict);
+	dict_free_nodes(&blk_dict);
+}
+
+/*
+ * Scan the inodes looking for inodes that contain duplicate blocks.
+ */
+struct process_block_struct_1b {
+	e2fsck_t        ctx;
+	ext2_ino_t      ino;
+	int             dup_blocks;
+	struct ext2_inode *inode;
+	struct problem_context *pctx;
+};
+
+static void pass1b(e2fsck_t ctx, char *block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t ino;
+	struct ext2_inode inode;
+	ext2_inode_scan scan;
+	struct process_block_struct_1b pb;
+	struct problem_context pctx;
+
+	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_1B_PASS_HEADER, &pctx);
+	pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
+					      &scan);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	ctx->stashed_inode = &inode;
+	pb.ctx = ctx;
+	pb.pctx = &pctx;
+	pctx.str = "pass1b";
+	while (1) {
+		pctx.errcode = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+			continue;
+		if (pctx.errcode) {
+			fix_problem(ctx, PR_1B_ISCAN_ERROR, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		if (!ino)
+			break;
+		pctx.ino = ctx->stashed_ino = ino;
+		if ((ino != EXT2_BAD_INO) &&
+		    !ext2fs_test_inode_bitmap(ctx->inode_used_map, ino))
+			continue;
+
+		pb.ino = ino;
+		pb.dup_blocks = 0;
+		pb.inode = &inode;
+
+		if (ext2fs_inode_has_valid_blocks(&inode) ||
+		    (ino == EXT2_BAD_INO))
+			pctx.errcode = ext2fs_block_iterate2(fs, ino,
+				     0, block_buf, process_pass1b_block, &pb);
+		if (inode.i_file_acl)
+			process_pass1b_block(fs, &inode.i_file_acl,
+					     BLOCK_COUNT_EXTATTR, 0, 0, &pb);
+		if (pb.dup_blocks) {
+			end_problem_latch(ctx, PR_LATCH_DBLOCK);
+			if (ino >= EXT2_FIRST_INODE(fs->super) ||
+			    ino == EXT2_ROOT_INO)
+				dup_inode_count++;
+		}
+		if (pctx.errcode)
+			fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
+	}
+	ext2fs_close_inode_scan(scan);
+	e2fsck_use_inode_shortcuts(ctx, 0);
+}
+
+static int process_pass1b_block(ext2_filsys fs FSCK_ATTR((unused)),
+				blk_t   *block_nr,
+				e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
+				blk_t ref_blk FSCK_ATTR((unused)),
+				int ref_offset FSCK_ATTR((unused)),
+				void *priv_data)
+{
+	struct process_block_struct_1b *p;
+	e2fsck_t ctx;
+
+	if (HOLE_BLKADDR(*block_nr))
+		return 0;
+	p = (struct process_block_struct_1b *) priv_data;
+	ctx = p->ctx;
+
+	if (!ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr))
+		return 0;
+
+	/* OK, this is a duplicate block */
+	if (p->ino != EXT2_BAD_INO) {
+		p->pctx->blk = *block_nr;
+		fix_problem(ctx, PR_1B_DUP_BLOCK, p->pctx);
+	}
+	p->dup_blocks++;
+	ext2fs_mark_inode_bitmap(inode_dup_map, p->ino);
+
+	add_dupe(ctx, p->ino, *block_nr, p->inode);
+
+	return 0;
+}
+
+/*
+ * Pass 1c: Scan directories for inodes with duplicate blocks.  This
+ * is used so that we can print pathnames when prompting the user for
+ * what to do.
+ */
+struct search_dir_struct {
+	int             count;
+	ext2_ino_t      first_inode;
+	ext2_ino_t      max_inode;
+};
+
+static int search_dirent_proc(ext2_ino_t dir, int entry,
+			      struct ext2_dir_entry *dirent,
+			      int offset FSCK_ATTR((unused)),
+			      int blocksize FSCK_ATTR((unused)),
+			      char *buf FSCK_ATTR((unused)),
+			      void *priv_data)
+{
+	struct search_dir_struct *sd;
+	struct dup_inode        *p;
+	dnode_t                 *n;
+
+	sd = (struct search_dir_struct *) priv_data;
+
+	if (dirent->inode > sd->max_inode)
+		/* Should abort this inode, but not everything */
+		return 0;
+
+	if ((dirent->inode < sd->first_inode) || (entry < DIRENT_OTHER_FILE) ||
+	    !ext2fs_test_inode_bitmap(inode_dup_map, dirent->inode))
+		return 0;
+
+	n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(dirent->inode));
+	if (!n)
+		return 0;
+	p = (struct dup_inode *) dnode_get(n);
+	p->dir = dir;
+	sd->count--;
+
+	return sd->count ? 0 : DIRENT_ABORT;
+}
+
+
+static void pass1c(e2fsck_t ctx, char *block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	struct search_dir_struct sd;
+	struct problem_context pctx;
+
+	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_1C_PASS_HEADER, &pctx);
+
+	/*
+	 * Search through all directories to translate inodes to names
+	 * (by searching for the containing directory for that inode.)
+	 */
+	sd.count = dup_inode_count;
+	sd.first_inode = EXT2_FIRST_INODE(fs->super);
+	sd.max_inode = fs->super->s_inodes_count;
+	ext2fs_dblist_dir_iterate(fs->dblist, 0, block_buf,
+				  search_dirent_proc, &sd);
+}
+
+static void pass1d(e2fsck_t ctx, char *block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	struct dup_inode        *p, *t;
+	struct dup_block        *q;
+	ext2_ino_t              *shared, ino;
+	int     shared_len;
+	int     i;
+	int     file_ok;
+	int     meta_data = 0;
+	struct problem_context pctx;
+	dnode_t *n, *m;
+	struct block_el *s;
+	struct inode_el *r;
+
+	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
+	e2fsck_read_bitmaps(ctx);
+
+	pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
+	fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
+	shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
+				sizeof(ext2_ino_t) * dict_count(&ino_dict),
+				"Shared inode list");
+	for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
+		p = (struct dup_inode *) dnode_get(n);
+		shared_len = 0;
+		file_ok = 1;
+		ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
+		if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
+			continue;
+
+		/*
+		 * Find all of the inodes which share blocks with this
+		 * one.  First we find all of the duplicate blocks
+		 * belonging to this inode, and then search each block
+		 * get the list of inodes, and merge them together.
+		 */
+		for (s = p->block_list; s; s = s->next) {
+			m = dict_lookup(&blk_dict, INT_TO_VOIDPTR(s->block));
+			if (!m)
+				continue; /* Should never happen... */
+			q = (struct dup_block *) dnode_get(m);
+			if (q->num_bad > 1)
+				file_ok = 0;
+			if (check_if_fs_block(ctx, s->block)) {
+				file_ok = 0;
+				meta_data = 1;
+			}
+
+			/*
+			 * Add all inodes used by this block to the
+			 * shared[] --- which is a unique list, so
+			 * if an inode is already in shared[], don't
+			 * add it again.
+			 */
+			for (r = q->inode_list; r; r = r->next) {
+				if (r->inode == ino)
+					continue;
+				for (i = 0; i < shared_len; i++)
+					if (shared[i] == r->inode)
+						break;
+				if (i == shared_len) {
+					shared[shared_len++] = r->inode;
+				}
+			}
+		}
+
+		/*
+		 * Report the inode that we are working on
+		 */
+		pctx.inode = &p->inode;
+		pctx.ino = ino;
+		pctx.dir = p->dir;
+		pctx.blkcount = p->num_dupblocks;
+		pctx.num = meta_data ? shared_len+1 : shared_len;
+		fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
+		pctx.blkcount = 0;
+		pctx.num = 0;
+
+		if (meta_data)
+			fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);
+
+		for (i = 0; i < shared_len; i++) {
+			m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
+			if (!m)
+				continue; /* should never happen */
+			t = (struct dup_inode *) dnode_get(m);
+			/*
+			 * Report the inode that we are sharing with
+			 */
+			pctx.inode = &t->inode;
+			pctx.ino = shared[i];
+			pctx.dir = t->dir;
+			fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
+		}
+		if (file_ok) {
+			fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
+			continue;
+		}
+		if (fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
+			pctx.errcode = clone_file(ctx, ino, p, block_buf);
+			if (pctx.errcode)
+				fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
+			else
+				continue;
+		}
+		if (fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
+			delete_file(ctx, ino, p, block_buf);
+		else
+			ext2fs_unmark_valid(fs);
+	}
+	ext2fs_free_mem(&shared);
+}
+
+/*
+ * Drop the refcount on the dup_block structure, and clear the entry
+ * in the block_dup_map if appropriate.
+ */
+static void decrement_badcount(e2fsck_t ctx, blk_t block, struct dup_block *p)
+{
+	p->num_bad--;
+	if (p->num_bad <= 0 ||
+	    (p->num_bad == 1 && !check_if_fs_block(ctx, block)))
+		ext2fs_unmark_block_bitmap(ctx->block_dup_map, block);
+}
+
+static int delete_file_block(ext2_filsys fs,
+			     blk_t      *block_nr,
+			     e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
+			     blk_t ref_block FSCK_ATTR((unused)),
+			     int ref_offset FSCK_ATTR((unused)),
+			     void *priv_data)
+{
+	struct process_block_struct_1b *pb;
+	struct dup_block *p;
+	dnode_t *n;
+	e2fsck_t ctx;
+
+	pb = (struct process_block_struct_1b *) priv_data;
+	ctx = pb->ctx;
+
+	if (HOLE_BLKADDR(*block_nr))
+		return 0;
+
+	if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
+		n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
+		if (n) {
+			p = (struct dup_block *) dnode_get(n);
+			decrement_badcount(ctx, *block_nr, p);
+		} else
+			bb_error_msg(_("internal error; can't find dup_blk for %d"),
+				*block_nr);
+	} else {
+		ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
+		ext2fs_block_alloc_stats(fs, *block_nr, -1);
+	}
+
+	return 0;
+}
+
+static void delete_file(e2fsck_t ctx, ext2_ino_t ino,
+			struct dup_inode *dp, char* block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	struct process_block_struct_1b pb;
+	struct ext2_inode       inode;
+	struct problem_context  pctx;
+	unsigned int            count;
+
+	clear_problem_context(&pctx);
+	pctx.ino = pb.ino = ino;
+	pb.dup_blocks = dp->num_dupblocks;
+	pb.ctx = ctx;
+	pctx.str = "delete_file";
+
+	e2fsck_read_inode(ctx, ino, &inode, "delete_file");
+	if (ext2fs_inode_has_valid_blocks(&inode))
+		pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+						     delete_file_block, &pb);
+	if (pctx.errcode)
+		fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
+	ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+	ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+	if (ctx->inode_bad_map)
+		ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
+	ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
+
+	/* Inode may have changed by block_iterate, so reread it */
+	e2fsck_read_inode(ctx, ino, &inode, "delete_file");
+	inode.i_links_count = 0;
+	inode.i_dtime = time(NULL);
+	if (inode.i_file_acl &&
+	    (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
+		count = 1;
+		pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
+						   block_buf, -1, &count);
+		if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
+			pctx.errcode = 0;
+			count = 1;
+		}
+		if (pctx.errcode) {
+			pctx.blk = inode.i_file_acl;
+			fix_problem(ctx, PR_1B_ADJ_EA_REFCOUNT, &pctx);
+		}
+		/*
+		 * If the count is zero, then arrange to have the
+		 * block deleted.  If the block is in the block_dup_map,
+		 * also call delete_file_block since it will take care
+		 * of keeping the accounting straight.
+		 */
+		if ((count == 0) ||
+		    ext2fs_test_block_bitmap(ctx->block_dup_map,
+					     inode.i_file_acl))
+			delete_file_block(fs, &inode.i_file_acl,
+					  BLOCK_COUNT_EXTATTR, 0, 0, &pb);
+	}
+	e2fsck_write_inode(ctx, ino, &inode, "delete_file");
+}
+
+struct clone_struct {
+	errcode_t       errcode;
+	ext2_ino_t      dir;
+	char    *buf;
+	e2fsck_t ctx;
+};
+
+static int clone_file_block(ext2_filsys fs,
+			    blk_t       *block_nr,
+			    e2_blkcnt_t blockcnt,
+			    blk_t ref_block FSCK_ATTR((unused)),
+			    int ref_offset FSCK_ATTR((unused)),
+			    void *priv_data)
+{
+	struct dup_block *p;
+	blk_t   new_block;
+	errcode_t       retval;
+	struct clone_struct *cs = (struct clone_struct *) priv_data;
+	dnode_t *n;
+	e2fsck_t ctx;
+
+	ctx = cs->ctx;
+
+	if (HOLE_BLKADDR(*block_nr))
+		return 0;
+
+	if (ext2fs_test_block_bitmap(ctx->block_dup_map, *block_nr)) {
+		n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(*block_nr));
+		if (n) {
+			p = (struct dup_block *) dnode_get(n);
+			retval = ext2fs_new_block(fs, 0, ctx->block_found_map,
+						  &new_block);
+			if (retval) {
+				cs->errcode = retval;
+				return BLOCK_ABORT;
+			}
+			if (cs->dir && (blockcnt >= 0)) {
+				retval = ext2fs_set_dir_block(fs->dblist,
+				      cs->dir, new_block, blockcnt);
+				if (retval) {
+					cs->errcode = retval;
+					return BLOCK_ABORT;
+				}
+			}
+
+			retval = io_channel_read_blk(fs->io, *block_nr, 1,
+						     cs->buf);
+			if (retval) {
+				cs->errcode = retval;
+				return BLOCK_ABORT;
+			}
+			retval = io_channel_write_blk(fs->io, new_block, 1,
+						      cs->buf);
+			if (retval) {
+				cs->errcode = retval;
+				return BLOCK_ABORT;
+			}
+			decrement_badcount(ctx, *block_nr, p);
+			*block_nr = new_block;
+			ext2fs_mark_block_bitmap(ctx->block_found_map,
+						 new_block);
+			ext2fs_mark_block_bitmap(fs->block_map, new_block);
+			return BLOCK_CHANGED;
+		} else
+			bb_error_msg(_("internal error; can't find dup_blk for %d"),
+				*block_nr);
+	}
+	return 0;
+}
+
+static int clone_file(e2fsck_t ctx, ext2_ino_t ino,
+		      struct dup_inode *dp, char* block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	errcode_t       retval;
+	struct clone_struct cs;
+	struct problem_context  pctx;
+	blk_t           blk;
+	dnode_t         *n;
+	struct inode_el *ino_el;
+	struct dup_block        *db;
+	struct dup_inode        *di;
+
+	clear_problem_context(&pctx);
+	cs.errcode = 0;
+	cs.dir = 0;
+	cs.ctx = ctx;
+	retval = ext2fs_get_mem(fs->blocksize, &cs.buf);
+	if (retval)
+		return retval;
+
+	if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
+		cs.dir = ino;
+
+	pctx.ino = ino;
+	pctx.str = "clone_file";
+	if (ext2fs_inode_has_valid_blocks(&dp->inode))
+		pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+						     clone_file_block, &cs);
+	ext2fs_mark_bb_dirty(fs);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_1B_BLOCK_ITERATE, &pctx);
+		retval = pctx.errcode;
+		goto errout;
+	}
+	if (cs.errcode) {
+		bb_error_msg(_("returned from clone_file_block"));
+		retval = cs.errcode;
+		goto errout;
+	}
+	/* The inode may have changed on disk, so we have to re-read it */
+	e2fsck_read_inode(ctx, ino, &dp->inode, "clone file EA");
+	blk = dp->inode.i_file_acl;
+	if (blk && (clone_file_block(fs, &dp->inode.i_file_acl,
+				     BLOCK_COUNT_EXTATTR, 0, 0, &cs) ==
+		    BLOCK_CHANGED)) {
+		e2fsck_write_inode(ctx, ino, &dp->inode, "clone file EA");
+		/*
+		 * If we cloned the EA block, find all other inodes
+		 * which refered to that EA block, and modify
+		 * them to point to the new EA block.
+		 */
+		n = dict_lookup(&blk_dict, INT_TO_VOIDPTR(blk));
+		db = (struct dup_block *) dnode_get(n);
+		for (ino_el = db->inode_list; ino_el; ino_el = ino_el->next) {
+			if (ino_el->inode == ino)
+				continue;
+			n = dict_lookup(&ino_dict, INT_TO_VOIDPTR(ino_el->inode));
+			di = (struct dup_inode *) dnode_get(n);
+			if (di->inode.i_file_acl == blk) {
+				di->inode.i_file_acl = dp->inode.i_file_acl;
+				e2fsck_write_inode(ctx, ino_el->inode,
+					   &di->inode, "clone file EA");
+				decrement_badcount(ctx, blk, db);
+			}
+		}
+	}
+	retval = 0;
+errout:
+	ext2fs_free_mem(&cs.buf);
+	return retval;
+}
+
+/*
+ * This routine returns 1 if a block overlaps with one of the superblocks,
+ * group descriptors, inode bitmaps, or block bitmaps.
+ */
+static int check_if_fs_block(e2fsck_t ctx, blk_t test_block)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t   block;
+	dgrp_t  i;
+
+	block = fs->super->s_first_data_block;
+	for (i = 0; i < fs->group_desc_count; i++) {
+
+		/* Check superblocks/block group descriptros */
+		if (ext2fs_bg_has_super(fs, i)) {
+			if (test_block >= block &&
+			    (test_block <= block + fs->desc_blocks))
+				return 1;
+		}
+
+		/* Check the inode table */
+		if ((fs->group_desc[i].bg_inode_table) &&
+		    (test_block >= fs->group_desc[i].bg_inode_table) &&
+		    (test_block < (fs->group_desc[i].bg_inode_table +
+				   fs->inode_blocks_per_group)))
+			return 1;
+
+		/* Check the bitmap blocks */
+		if ((test_block == fs->group_desc[i].bg_block_bitmap) ||
+		    (test_block == fs->group_desc[i].bg_inode_bitmap))
+			return 1;
+
+		block += fs->super->s_blocks_per_group;
+	}
+	return 0;
+}
+/*
+ * pass2.c --- check directory structure
+ *
+ * Pass 2 of e2fsck iterates through all active directory inodes, and
+ * applies to following tests to each directory entry in the directory
+ * blocks in the inodes:
+ *
+ *      - The length of the directory entry (rec_len) should be at
+ *              least 8 bytes, and no more than the remaining space
+ *              left in the directory block.
+ *      - The length of the name in the directory entry (name_len)
+ *              should be less than (rec_len - 8).
+ *      - The inode number in the directory entry should be within
+ *              legal bounds.
+ *      - The inode number should refer to a in-use inode.
+ *      - The first entry should be '.', and its inode should be
+ *              the inode of the directory.
+ *      - The second entry should be '..'.
+ *
+ * To minimize disk seek time, the directory blocks are processed in
+ * sorted order of block numbers.
+ *
+ * Pass 2 also collects the following information:
+ *      - The inode numbers of the subdirectories for each directory.
+ *
+ * Pass 2 relies on the following information from previous passes:
+ *      - The directory information collected in pass 1.
+ *      - The inode_used_map bitmap
+ *      - The inode_bad_map bitmap
+ *      - The inode_dir_map bitmap
+ *
+ * Pass 2 frees the following data structures
+ *      - The inode_bad_map bitmap
+ *      - The inode_reg_map bitmap
+ */
+
+/*
+ * Keeps track of how many times an inode is referenced.
+ */
+static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
+static int check_dir_block(ext2_filsys fs,
+			   struct ext2_db_entry *dir_blocks_info,
+			   void *priv_data);
+static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *dir_blocks_info,
+			      struct problem_context *pctx);
+static int update_dir_block(ext2_filsys fs,
+			    blk_t       *block_nr,
+			    e2_blkcnt_t blockcnt,
+			    blk_t       ref_block,
+			    int         ref_offset,
+			    void        *priv_data);
+static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
+static int htree_depth(struct dx_dir_info *dx_dir,
+		       struct dx_dirblock_info *dx_db);
+static int special_dir_block_cmp(const void *a, const void *b);
+
+struct check_dir_struct {
+	char *buf;
+	struct problem_context  pctx;
+	int     count, max;
+	e2fsck_t ctx;
+};
+
+static void e2fsck_pass2(e2fsck_t ctx)
+{
+	struct ext2_super_block *sb = ctx->fs->super;
+	struct problem_context  pctx;
+	ext2_filsys             fs = ctx->fs;
+	char                    *buf;
+	struct dir_info         *dir;
+	struct check_dir_struct cd;
+	struct dx_dir_info      *dx_dir;
+	struct dx_dirblock_info *dx_db, *dx_parent;
+	int                     b;
+	int                     i, depth;
+	problem_t               code;
+	int                     bad_dir;
+
+	clear_problem_context(&cd.pctx);
+
+	/* Pass 2 */
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
+
+	cd.pctx.errcode = ext2fs_create_icount2(fs, EXT2_ICOUNT_OPT_INCREMENT,
+						0, ctx->inode_link_info,
+						&ctx->inode_count);
+	if (cd.pctx.errcode) {
+		fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
+					      "directory scan buffer");
+
+	/*
+	 * Set up the parent pointer for the root directory, if
+	 * present.  (If the root directory is not present, we will
+	 * create it in pass 3.)
+	 */
+	dir = e2fsck_get_dir_info(ctx, EXT2_ROOT_INO);
+	if (dir)
+		dir->parent = EXT2_ROOT_INO;
+
+	cd.buf = buf;
+	cd.ctx = ctx;
+	cd.count = 1;
+	cd.max = ext2fs_dblist_count(fs->dblist);
+
+	if (ctx->progress)
+		(void) (ctx->progress)(ctx, 2, 0, cd.max);
+
+	if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
+		ext2fs_dblist_sort(fs->dblist, special_dir_block_cmp);
+
+	cd.pctx.errcode = ext2fs_dblist_iterate(fs->dblist, check_dir_block,
+						&cd);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
+	if (cd.pctx.errcode) {
+		fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+#ifdef ENABLE_HTREE
+	for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
+		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+			return;
+		if (dx_dir->numblocks == 0)
+			continue;
+		clear_problem_context(&pctx);
+		bad_dir = 0;
+		pctx.dir = dx_dir->ino;
+		dx_db = dx_dir->dx_block;
+		if (dx_db->flags & DX_FLAG_REFERENCED)
+			dx_db->flags |= DX_FLAG_DUP_REF;
+		else
+			dx_db->flags |= DX_FLAG_REFERENCED;
+		/*
+		 * Find all of the first and last leaf blocks, and
+		 * update their parent's min and max hash values
+		 */
+		for (b=0, dx_db = dx_dir->dx_block;
+		     b < dx_dir->numblocks;
+		     b++, dx_db++) {
+			if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
+			    !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
+				continue;
+			dx_parent = &dx_dir->dx_block[dx_db->parent];
+			/*
+			 * XXX Make sure dx_parent->min_hash > dx_db->min_hash
+			 */
+			if (dx_db->flags & DX_FLAG_FIRST)
+				dx_parent->min_hash = dx_db->min_hash;
+			/*
+			 * XXX Make sure dx_parent->max_hash < dx_db->max_hash
+			 */
+			if (dx_db->flags & DX_FLAG_LAST)
+				dx_parent->max_hash = dx_db->max_hash;
+		}
+
+		for (b=0, dx_db = dx_dir->dx_block;
+		     b < dx_dir->numblocks;
+		     b++, dx_db++) {
+			pctx.blkcount = b;
+			pctx.group = dx_db->parent;
+			code = 0;
+			if (!(dx_db->flags & DX_FLAG_FIRST) &&
+			    (dx_db->min_hash < dx_db->node_min_hash)) {
+				pctx.blk = dx_db->min_hash;
+				pctx.blk2 = dx_db->node_min_hash;
+				code = PR_2_HTREE_MIN_HASH;
+				fix_problem(ctx, code, &pctx);
+				bad_dir++;
+			}
+			if (dx_db->type == DX_DIRBLOCK_LEAF) {
+				depth = htree_depth(dx_dir, dx_db);
+				if (depth != dx_dir->depth) {
+					code = PR_2_HTREE_BAD_DEPTH;
+					fix_problem(ctx, code, &pctx);
+					bad_dir++;
+				}
+			}
+			/*
+			 * This test doesn't apply for the root block
+			 * at block #0
+			 */
+			if (b &&
+			    (dx_db->max_hash > dx_db->node_max_hash)) {
+				pctx.blk = dx_db->max_hash;
+				pctx.blk2 = dx_db->node_max_hash;
+				code = PR_2_HTREE_MAX_HASH;
+				fix_problem(ctx, code, &pctx);
+				bad_dir++;
+			}
+			if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
+				code = PR_2_HTREE_NOTREF;
+				fix_problem(ctx, code, &pctx);
+				bad_dir++;
+			} else if (dx_db->flags & DX_FLAG_DUP_REF) {
+				code = PR_2_HTREE_DUPREF;
+				fix_problem(ctx, code, &pctx);
+				bad_dir++;
+			}
+			if (code == 0)
+				continue;
+		}
+		if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
+			clear_htree(ctx, dx_dir->ino);
+			dx_dir->numblocks = 0;
+		}
+	}
+#endif
+	ext2fs_free_mem(&buf);
+	ext2fs_free_dblist(fs->dblist);
+
+	ext2fs_free_inode_bitmap(ctx->inode_bad_map);
+	ctx->inode_bad_map = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_reg_map);
+	ctx->inode_reg_map = 0;
+
+	clear_problem_context(&pctx);
+	if (ctx->large_files) {
+		if (!(sb->s_feature_ro_compat &
+		      EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
+		    fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
+			sb->s_feature_ro_compat |=
+				EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+			ext2fs_mark_super_dirty(fs);
+		}
+		if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
+		    fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
+			ext2fs_update_dynamic_rev(fs);
+			ext2fs_mark_super_dirty(fs);
+		}
+	} else if (!ctx->large_files &&
+	    (sb->s_feature_ro_compat &
+	      EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
+		if (fs->flags & EXT2_FLAG_RW) {
+			sb->s_feature_ro_compat &=
+				~EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+}
+
+#define MAX_DEPTH 32000
+static int htree_depth(struct dx_dir_info *dx_dir,
+		       struct dx_dirblock_info *dx_db)
+{
+	int     depth = 0;
+
+	while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
+		dx_db = &dx_dir->dx_block[dx_db->parent];
+		depth++;
+	}
+	return depth;
+}
+
+static int dict_de_cmp(const void *a, const void *b)
+{
+	const struct ext2_dir_entry *de_a, *de_b;
+	int     a_len, b_len;
+
+	de_a = (const struct ext2_dir_entry *) a;
+	a_len = de_a->name_len & 0xFF;
+	de_b = (const struct ext2_dir_entry *) b;
+	b_len = de_b->name_len & 0xFF;
+
+	if (a_len != b_len)
+		return (a_len - b_len);
+
+	return strncmp(de_a->name, de_b->name, a_len);
+}
+
+/*
+ * This is special sort function that makes sure that directory blocks
+ * with a dirblock of zero are sorted to the beginning of the list.
+ * This guarantees that the root node of the htree directories are
+ * processed first, so we know what hash version to use.
+ */
+static int special_dir_block_cmp(const void *a, const void *b)
+{
+	const struct ext2_db_entry *db_a =
+		(const struct ext2_db_entry *) a;
+	const struct ext2_db_entry *db_b =
+		(const struct ext2_db_entry *) b;
+
+	if (db_a->blockcnt && !db_b->blockcnt)
+		return 1;
+
+	if (!db_a->blockcnt && db_b->blockcnt)
+		return -1;
+
+	if (db_a->blk != db_b->blk)
+		return (int) (db_a->blk - db_b->blk);
+
+	if (db_a->ino != db_b->ino)
+		return (int) (db_a->ino - db_b->ino);
+
+	return (int) (db_a->blockcnt - db_b->blockcnt);
+}
+
+
+/*
+ * Make sure the first entry in the directory is '.', and that the
+ * directory entry is sane.
+ */
+static int check_dot(e2fsck_t ctx,
+		     struct ext2_dir_entry *dirent,
+		     ext2_ino_t ino, struct problem_context *pctx)
+{
+	struct ext2_dir_entry *nextdir;
+	int     status = 0;
+	int     created = 0;
+	int     new_len;
+	int     problem = 0;
+
+	if (!dirent->inode)
+		problem = PR_2_MISSING_DOT;
+	else if (((dirent->name_len & 0xFF) != 1) ||
+		 (dirent->name[0] != '.'))
+		problem = PR_2_1ST_NOT_DOT;
+	else if (dirent->name[1] != '\0')
+		problem = PR_2_DOT_NULL_TERM;
+
+	if (problem) {
+		if (fix_problem(ctx, problem, pctx)) {
+			if (dirent->rec_len < 12)
+				dirent->rec_len = 12;
+			dirent->inode = ino;
+			dirent->name_len = 1;
+			dirent->name[0] = '.';
+			dirent->name[1] = '\0';
+			status = 1;
+			created = 1;
+		}
+	}
+	if (dirent->inode != ino) {
+		if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
+			dirent->inode = ino;
+			status = 1;
+		}
+	}
+	if (dirent->rec_len > 12) {
+		new_len = dirent->rec_len - 12;
+		if (new_len > 12) {
+			if (created ||
+			    fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
+				nextdir = (struct ext2_dir_entry *)
+					((char *) dirent + 12);
+				dirent->rec_len = 12;
+				nextdir->rec_len = new_len;
+				nextdir->inode = 0;
+				nextdir->name_len = 0;
+				status = 1;
+			}
+		}
+	}
+	return status;
+}
+
+/*
+ * Make sure the second entry in the directory is '..', and that the
+ * directory entry is sane.  We do not check the inode number of '..'
+ * here; this gets done in pass 3.
+ */
+static int check_dotdot(e2fsck_t ctx,
+			struct ext2_dir_entry *dirent,
+			struct dir_info *dir, struct problem_context *pctx)
+{
+	int             problem = 0;
+
+	if (!dirent->inode)
+		problem = PR_2_MISSING_DOT_DOT;
+	else if (((dirent->name_len & 0xFF) != 2) ||
+		 (dirent->name[0] != '.') ||
+		 (dirent->name[1] != '.'))
+		problem = PR_2_2ND_NOT_DOT_DOT;
+	else if (dirent->name[2] != '\0')
+		problem = PR_2_DOT_DOT_NULL_TERM;
+
+	if (problem) {
+		if (fix_problem(ctx, problem, pctx)) {
+			if (dirent->rec_len < 12)
+				dirent->rec_len = 12;
+			/*
+			 * Note: we don't have the parent inode just
+			 * yet, so we will fill it in with the root
+			 * inode.  This will get fixed in pass 3.
+			 */
+			dirent->inode = EXT2_ROOT_INO;
+			dirent->name_len = 2;
+			dirent->name[0] = '.';
+			dirent->name[1] = '.';
+			dirent->name[2] = '\0';
+			return 1;
+		}
+		return 0;
+	}
+	dir->dotdot = dirent->inode;
+	return 0;
+}
+
+/*
+ * Check to make sure a directory entry doesn't contain any illegal
+ * characters.
+ */
+static int check_name(e2fsck_t ctx,
+		      struct ext2_dir_entry *dirent,
+		      struct problem_context *pctx)
+{
+	int     i;
+	int     fixup = -1;
+	int     ret = 0;
+
+	for ( i = 0; i < (dirent->name_len & 0xFF); i++) {
+		if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
+			if (fixup < 0) {
+				fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
+			}
+			if (fixup) {
+				dirent->name[i] = '.';
+				ret = 1;
+			}
+		}
+	}
+	return ret;
+}
+
+/*
+ * Check the directory filetype (if present)
+ */
+
+/*
+ * Given a mode, return the ext2 file type
+ */
+static int ext2_file_type(unsigned int mode)
+{
+	if (LINUX_S_ISREG(mode))
+		return EXT2_FT_REG_FILE;
+
+	if (LINUX_S_ISDIR(mode))
+		return EXT2_FT_DIR;
+
+	if (LINUX_S_ISCHR(mode))
+		return EXT2_FT_CHRDEV;
+
+	if (LINUX_S_ISBLK(mode))
+		return EXT2_FT_BLKDEV;
+
+	if (LINUX_S_ISLNK(mode))
+		return EXT2_FT_SYMLINK;
+
+	if (LINUX_S_ISFIFO(mode))
+		return EXT2_FT_FIFO;
+
+	if (LINUX_S_ISSOCK(mode))
+		return EXT2_FT_SOCK;
+
+	return 0;
+}
+
+static int check_filetype(e2fsck_t ctx,
+				   struct ext2_dir_entry *dirent,
+				   struct problem_context *pctx)
+{
+	int     filetype = dirent->name_len >> 8;
+	int     should_be = EXT2_FT_UNKNOWN;
+	struct ext2_inode       inode;
+
+	if (!(ctx->fs->super->s_feature_incompat &
+	      EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+		if (filetype == 0 ||
+		    !fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
+			return 0;
+		dirent->name_len = dirent->name_len & 0xFF;
+		return 1;
+	}
+
+	if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dirent->inode)) {
+		should_be = EXT2_FT_DIR;
+	} else if (ext2fs_test_inode_bitmap(ctx->inode_reg_map,
+					    dirent->inode)) {
+		should_be = EXT2_FT_REG_FILE;
+	} else if (ctx->inode_bad_map &&
+		   ext2fs_test_inode_bitmap(ctx->inode_bad_map,
+					    dirent->inode))
+		should_be = 0;
+	else {
+		e2fsck_read_inode(ctx, dirent->inode, &inode,
+				  "check_filetype");
+		should_be = ext2_file_type(inode.i_mode);
+	}
+	if (filetype == should_be)
+		return 0;
+	pctx->num = should_be;
+
+	if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
+			pctx) == 0)
+		return 0;
+
+	dirent->name_len = (dirent->name_len & 0xFF) | should_be << 8;
+	return 1;
+}
+
+#ifdef ENABLE_HTREE
+static void parse_int_node(ext2_filsys fs,
+			   struct ext2_db_entry *db,
+			   struct check_dir_struct *cd,
+			   struct dx_dir_info   *dx_dir,
+			   char *block_buf)
+{
+	struct          ext2_dx_root_info  *root;
+	struct          ext2_dx_entry *ent;
+	struct          ext2_dx_countlimit *limit;
+	struct dx_dirblock_info *dx_db;
+	int             i, expect_limit, count;
+	blk_t           blk;
+	ext2_dirhash_t  min_hash = 0xffffffff;
+	ext2_dirhash_t  max_hash = 0;
+	ext2_dirhash_t  hash = 0, prev_hash;
+
+	if (db->blockcnt == 0) {
+		root = (struct ext2_dx_root_info *) (block_buf + 24);
+		ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+	} else {
+		ent = (struct ext2_dx_entry *) (block_buf+8);
+	}
+	limit = (struct ext2_dx_countlimit *) ent;
+
+	count = ext2fs_le16_to_cpu(limit->count);
+	expect_limit = (fs->blocksize - ((char *) ent - block_buf)) /
+		sizeof(struct ext2_dx_entry);
+	if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
+		cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
+		if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
+			goto clear_and_exit;
+	}
+	if (count > expect_limit) {
+		cd->pctx.num = count;
+		if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
+			goto clear_and_exit;
+		count = expect_limit;
+	}
+
+	for (i=0; i < count; i++) {
+		prev_hash = hash;
+		hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
+		blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
+		/* Check to make sure the block is valid */
+		if (blk > (blk_t) dx_dir->numblocks) {
+			cd->pctx.blk = blk;
+			if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
+					&cd->pctx))
+				goto clear_and_exit;
+		}
+		if (hash < prev_hash &&
+		    fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
+			goto clear_and_exit;
+		dx_db = &dx_dir->dx_block[blk];
+		if (dx_db->flags & DX_FLAG_REFERENCED) {
+			dx_db->flags |= DX_FLAG_DUP_REF;
+		} else {
+			dx_db->flags |= DX_FLAG_REFERENCED;
+			dx_db->parent = db->blockcnt;
+		}
+		if (hash < min_hash)
+			min_hash = hash;
+		if (hash > max_hash)
+			max_hash = hash;
+		dx_db->node_min_hash = hash;
+		if ((i+1) < count)
+			dx_db->node_max_hash =
+			  ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
+		else {
+			dx_db->node_max_hash = 0xfffffffe;
+			dx_db->flags |= DX_FLAG_LAST;
+		}
+		if (i == 0)
+			dx_db->flags |= DX_FLAG_FIRST;
+	}
+	dx_db = &dx_dir->dx_block[db->blockcnt];
+	dx_db->min_hash = min_hash;
+	dx_db->max_hash = max_hash;
+	return;
+
+clear_and_exit:
+	clear_htree(cd->ctx, cd->pctx.ino);
+	dx_dir->numblocks = 0;
+}
+#endif /* ENABLE_HTREE */
+
+/*
+ * Given a busted directory, try to salvage it somehow.
+ *
+ */
+static void salvage_directory(ext2_filsys fs,
+			      struct ext2_dir_entry *dirent,
+			      struct ext2_dir_entry *prev,
+			      unsigned int *offset)
+{
+	char    *cp = (char *) dirent;
+	int left = fs->blocksize - *offset - dirent->rec_len;
+	int name_len = dirent->name_len & 0xFF;
+
+	/*
+	 * Special case of directory entry of size 8: copy what's left
+	 * of the directory block up to cover up the invalid hole.
+	 */
+	if ((left >= 12) && (dirent->rec_len == 8)) {
+		memmove(cp, cp+8, left);
+		memset(cp + left, 0, 8);
+		return;
+	}
+	/*
+	 * If the directory entry overruns the end of the directory
+	 * block, and the name is small enough to fit, then adjust the
+	 * record length.
+	 */
+	if ((left < 0) &&
+	    (name_len + 8 <= dirent->rec_len + left) &&
+	    dirent->inode <= fs->super->s_inodes_count &&
+	    strnlen(dirent->name, name_len) == name_len) {
+		dirent->rec_len += left;
+		return;
+	}
+	/*
+	 * If the directory entry is a multiple of four, so it is
+	 * valid, let the previous directory entry absorb the invalid
+	 * one.
+	 */
+	if (prev && dirent->rec_len && (dirent->rec_len % 4) == 0) {
+		prev->rec_len += dirent->rec_len;
+		*offset += dirent->rec_len;
+		return;
+	}
+	/*
+	 * Default salvage method --- kill all of the directory
+	 * entries for the rest of the block.  We will either try to
+	 * absorb it into the previous directory entry, or create a
+	 * new empty directory entry the rest of the directory block.
+	 */
+	if (prev) {
+		prev->rec_len += fs->blocksize - *offset;
+		*offset = fs->blocksize;
+	} else {
+		dirent->rec_len = fs->blocksize - *offset;
+		dirent->name_len = 0;
+		dirent->inode = 0;
+	}
+}
+
+static int check_dir_block(ext2_filsys fs,
+			   struct ext2_db_entry *db,
+			   void *priv_data)
+{
+	struct dir_info         *subdir, *dir;
+	struct dx_dir_info      *dx_dir;
+#ifdef ENABLE_HTREE
+	struct dx_dirblock_info *dx_db = NULL;
+#endif /* ENABLE_HTREE */
+	struct ext2_dir_entry   *dirent, *prev;
+	ext2_dirhash_t          hash;
+	unsigned int            offset = 0;
+	int                     dir_modified = 0;
+	int                     dot_state;
+	blk_t                   block_nr = db->blk;
+	ext2_ino_t              ino = db->ino;
+	__u16                   links;
+	struct check_dir_struct *cd;
+	char                    *buf;
+	e2fsck_t                ctx;
+	int                     problem;
+	struct ext2_dx_root_info *root;
+	struct ext2_dx_countlimit *limit;
+	static dict_t de_dict;
+	struct problem_context  pctx;
+	int     dups_found = 0;
+
+	cd = (struct check_dir_struct *) priv_data;
+	buf = cd->buf;
+	ctx = cd->ctx;
+
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return DIRENT_ABORT;
+
+	if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
+		return DIRENT_ABORT;
+
+	/*
+	 * Make sure the inode is still in use (could have been
+	 * deleted in the duplicate/bad blocks pass.
+	 */
+	if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, ino)))
+		return 0;
+
+	cd->pctx.ino = ino;
+	cd->pctx.blk = block_nr;
+	cd->pctx.blkcount = db->blockcnt;
+	cd->pctx.ino2 = 0;
+	cd->pctx.dirent = 0;
+	cd->pctx.num = 0;
+
+	if (db->blk == 0) {
+		if (allocate_dir_block(ctx, db, &cd->pctx))
+			return 0;
+		block_nr = db->blk;
+	}
+
+	if (db->blockcnt)
+		dot_state = 2;
+	else
+		dot_state = 0;
+
+	if (ctx->dirs_to_hash &&
+	    ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
+		dups_found++;
+
+	cd->pctx.errcode = ext2fs_read_dir_block(fs, block_nr, buf);
+	if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
+		cd->pctx.errcode = 0; /* We'll handle this ourselves */
+	if (cd->pctx.errcode) {
+		if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
+			ctx->flags |= E2F_FLAG_ABORT;
+			return DIRENT_ABORT;
+		}
+		memset(buf, 0, fs->blocksize);
+	}
+#ifdef ENABLE_HTREE
+	dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
+	if (dx_dir && dx_dir->numblocks) {
+		if (db->blockcnt >= dx_dir->numblocks) {
+			printf("XXX should never happen!!!\n");
+			abort();
+		}
+		dx_db = &dx_dir->dx_block[db->blockcnt];
+		dx_db->type = DX_DIRBLOCK_LEAF;
+		dx_db->phys = block_nr;
+		dx_db->min_hash = ~0;
+		dx_db->max_hash = 0;
+
+		dirent = (struct ext2_dir_entry *) buf;
+		limit = (struct ext2_dx_countlimit *) (buf+8);
+		if (db->blockcnt == 0) {
+			root = (struct ext2_dx_root_info *) (buf + 24);
+			dx_db->type = DX_DIRBLOCK_ROOT;
+			dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
+			if ((root->reserved_zero ||
+			     root->info_length < 8 ||
+			     root->indirect_levels > 1) &&
+			    fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
+				clear_htree(ctx, ino);
+				dx_dir->numblocks = 0;
+				dx_db = 0;
+			}
+			dx_dir->hashversion = root->hash_version;
+			dx_dir->depth = root->indirect_levels + 1;
+		} else if ((dirent->inode == 0) &&
+			   (dirent->rec_len == fs->blocksize) &&
+			   (dirent->name_len == 0) &&
+			   (ext2fs_le16_to_cpu(limit->limit) ==
+			    ((fs->blocksize-8) /
+			     sizeof(struct ext2_dx_entry))))
+			dx_db->type = DX_DIRBLOCK_NODE;
+	}
+#endif /* ENABLE_HTREE */
+
+	dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
+	prev = 0;
+	do {
+		problem = 0;
+		dirent = (struct ext2_dir_entry *) (buf + offset);
+		cd->pctx.dirent = dirent;
+		cd->pctx.num = offset;
+		if (((offset + dirent->rec_len) > fs->blocksize) ||
+		    (dirent->rec_len < 12) ||
+		    ((dirent->rec_len % 4) != 0) ||
+		    (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+			if (fix_problem(ctx, PR_2_DIR_CORRUPTED, &cd->pctx)) {
+				salvage_directory(fs, dirent, prev, &offset);
+				dir_modified++;
+				continue;
+			} else
+				goto abort_free_dict;
+		}
+		if ((dirent->name_len & 0xFF) > EXT2_NAME_LEN) {
+			if (fix_problem(ctx, PR_2_FILENAME_LONG, &cd->pctx)) {
+				dirent->name_len = EXT2_NAME_LEN;
+				dir_modified++;
+			}
+		}
+
+		if (dot_state == 0) {
+			if (check_dot(ctx, dirent, ino, &cd->pctx))
+				dir_modified++;
+		} else if (dot_state == 1) {
+			dir = e2fsck_get_dir_info(ctx, ino);
+			if (!dir) {
+				fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
+				goto abort_free_dict;
+			}
+			if (check_dotdot(ctx, dirent, dir, &cd->pctx))
+				dir_modified++;
+		} else if (dirent->inode == ino) {
+			problem = PR_2_LINK_DOT;
+			if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
+				dirent->inode = 0;
+				dir_modified++;
+				goto next;
+			}
+		}
+		if (!dirent->inode)
+			goto next;
+
+		/*
+		 * Make sure the inode listed is a legal one.
+		 */
+		if (((dirent->inode != EXT2_ROOT_INO) &&
+		     (dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
+		    (dirent->inode > fs->super->s_inodes_count)) {
+			problem = PR_2_BAD_INO;
+		} else if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map,
+					       dirent->inode))) {
+			/*
+			 * If the inode is unused, offer to clear it.
+			 */
+			problem = PR_2_UNUSED_INODE;
+		} else if ((dot_state > 1) &&
+			   ((dirent->name_len & 0xFF) == 1) &&
+			   (dirent->name[0] == '.')) {
+			/*
+			 * If there's a '.' entry in anything other
+			 * than the first directory entry, it's a
+			 * duplicate entry that should be removed.
+			 */
+			problem = PR_2_DUP_DOT;
+		} else if ((dot_state > 1) &&
+			   ((dirent->name_len & 0xFF) == 2) &&
+			   (dirent->name[0] == '.') &&
+			   (dirent->name[1] == '.')) {
+			/*
+			 * If there's a '..' entry in anything other
+			 * than the second directory entry, it's a
+			 * duplicate entry that should be removed.
+			 */
+			problem = PR_2_DUP_DOT_DOT;
+		} else if ((dot_state > 1) &&
+			   (dirent->inode == EXT2_ROOT_INO)) {
+			/*
+			 * Don't allow links to the root directory.
+			 * We check this specially to make sure we
+			 * catch this error case even if the root
+			 * directory hasn't been created yet.
+			 */
+			problem = PR_2_LINK_ROOT;
+		} else if ((dot_state > 1) &&
+			   (dirent->name_len & 0xFF) == 0) {
+			/*
+			 * Don't allow zero-length directory names.
+			 */
+			problem = PR_2_NULL_NAME;
+		}
+
+		if (problem) {
+			if (fix_problem(ctx, problem, &cd->pctx)) {
+				dirent->inode = 0;
+				dir_modified++;
+				goto next;
+			} else {
+				ext2fs_unmark_valid(fs);
+				if (problem == PR_2_BAD_INO)
+					goto next;
+			}
+		}
+
+		/*
+		 * If the inode was marked as having bad fields in
+		 * pass1, process it and offer to fix/clear it.
+		 * (We wait until now so that we can display the
+		 * pathname to the user.)
+		 */
+		if (ctx->inode_bad_map &&
+		    ext2fs_test_inode_bitmap(ctx->inode_bad_map,
+					     dirent->inode)) {
+			if (e2fsck_process_bad_inode(ctx, ino,
+						     dirent->inode,
+						     buf + fs->blocksize)) {
+				dirent->inode = 0;
+				dir_modified++;
+				goto next;
+			}
+			if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+				return DIRENT_ABORT;
+		}
+
+		if (check_name(ctx, dirent, &cd->pctx))
+			dir_modified++;
+
+		if (check_filetype(ctx, dirent, &cd->pctx))
+			dir_modified++;
+
+#ifdef ENABLE_HTREE
+		if (dx_db) {
+			ext2fs_dirhash(dx_dir->hashversion, dirent->name,
+				       (dirent->name_len & 0xFF),
+				       fs->super->s_hash_seed, &hash, 0);
+			if (hash < dx_db->min_hash)
+				dx_db->min_hash = hash;
+			if (hash > dx_db->max_hash)
+				dx_db->max_hash = hash;
+		}
+#endif
+
+		/*
+		 * If this is a directory, then mark its parent in its
+		 * dir_info structure.  If the parent field is already
+		 * filled in, then this directory has more than one
+		 * hard link.  We assume the first link is correct,
+		 * and ask the user if he/she wants to clear this one.
+		 */
+		if ((dot_state > 1) &&
+		    (ext2fs_test_inode_bitmap(ctx->inode_dir_map,
+					      dirent->inode))) {
+			subdir = e2fsck_get_dir_info(ctx, dirent->inode);
+			if (!subdir) {
+				cd->pctx.ino = dirent->inode;
+				fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
+				goto abort_free_dict;
+			}
+			if (subdir->parent) {
+				cd->pctx.ino2 = subdir->parent;
+				if (fix_problem(ctx, PR_2_LINK_DIR,
+						&cd->pctx)) {
+					dirent->inode = 0;
+					dir_modified++;
+					goto next;
+				}
+				cd->pctx.ino2 = 0;
+			} else
+				subdir->parent = ino;
+		}
+
+		if (dups_found) {
+			;
+		} else if (dict_lookup(&de_dict, dirent)) {
+			clear_problem_context(&pctx);
+			pctx.ino = ino;
+			pctx.dirent = dirent;
+			fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
+			if (!ctx->dirs_to_hash)
+				ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+			if (ctx->dirs_to_hash)
+				ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+			dups_found++;
+		} else
+			dict_alloc_insert(&de_dict, dirent, dirent);
+
+		ext2fs_icount_increment(ctx->inode_count, dirent->inode,
+					&links);
+		if (links > 1)
+			ctx->fs_links_count++;
+		ctx->fs_total_count++;
+	next:
+		prev = dirent;
+		offset += dirent->rec_len;
+		dot_state++;
+	} while (offset < fs->blocksize);
+#ifdef ENABLE_HTREE
+	if (dx_db) {
+		cd->pctx.dir = cd->pctx.ino;
+		if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
+		    (dx_db->type == DX_DIRBLOCK_NODE))
+			parse_int_node(fs, db, cd, dx_dir, buf);
+	}
+#endif /* ENABLE_HTREE */
+	if (offset != fs->blocksize) {
+		cd->pctx.num = dirent->rec_len - fs->blocksize + offset;
+		if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+			dirent->rec_len = cd->pctx.num;
+			dir_modified++;
+		}
+	}
+	if (dir_modified) {
+		cd->pctx.errcode = ext2fs_write_dir_block(fs, block_nr, buf);
+		if (cd->pctx.errcode) {
+			if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
+					 &cd->pctx))
+				goto abort_free_dict;
+		}
+		ext2fs_mark_changed(fs);
+	}
+	dict_free_nodes(&de_dict);
+	return 0;
+abort_free_dict:
+	dict_free_nodes(&de_dict);
+	ctx->flags |= E2F_FLAG_ABORT;
+	return DIRENT_ABORT;
+}
+
+/*
+ * This function is called to deallocate a block, and is an interator
+ * functioned called by deallocate inode via ext2fs_iterate_block().
+ */
+static int deallocate_inode_block(ext2_filsys fs, blk_t *block_nr,
+				  e2_blkcnt_t blockcnt FSCK_ATTR((unused)),
+				  blk_t ref_block FSCK_ATTR((unused)),
+				  int ref_offset FSCK_ATTR((unused)),
+				  void *priv_data)
+{
+	e2fsck_t        ctx = (e2fsck_t) priv_data;
+
+	if (HOLE_BLKADDR(*block_nr))
+		return 0;
+	if ((*block_nr < fs->super->s_first_data_block) ||
+	    (*block_nr >= fs->super->s_blocks_count))
+		return 0;
+	ext2fs_unmark_block_bitmap(ctx->block_found_map, *block_nr);
+	ext2fs_block_alloc_stats(fs, *block_nr, -1);
+	return 0;
+}
+
+/*
+ * This fuction deallocates an inode
+ */
+static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
+{
+	ext2_filsys fs = ctx->fs;
+	struct ext2_inode       inode;
+	struct problem_context  pctx;
+	__u32                   count;
+
+	ext2fs_icount_store(ctx->inode_link_info, ino, 0);
+	e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
+	inode.i_links_count = 0;
+	inode.i_dtime = time(NULL);
+	e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
+	clear_problem_context(&pctx);
+	pctx.ino = ino;
+
+	/*
+	 * Fix up the bitmaps...
+	 */
+	e2fsck_read_bitmaps(ctx);
+	ext2fs_unmark_inode_bitmap(ctx->inode_used_map, ino);
+	ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, ino);
+	if (ctx->inode_bad_map)
+		ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
+	ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
+
+	if (inode.i_file_acl &&
+	    (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
+		pctx.errcode = ext2fs_adjust_ea_refcount(fs, inode.i_file_acl,
+						   block_buf, -1, &count);
+		if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
+			pctx.errcode = 0;
+			count = 1;
+		}
+		if (pctx.errcode) {
+			pctx.blk = inode.i_file_acl;
+			fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		if (count == 0) {
+			ext2fs_unmark_block_bitmap(ctx->block_found_map,
+						   inode.i_file_acl);
+			ext2fs_block_alloc_stats(fs, inode.i_file_acl, -1);
+		}
+		inode.i_file_acl = 0;
+	}
+
+	if (!ext2fs_inode_has_valid_blocks(&inode))
+		return;
+
+	if (LINUX_S_ISREG(inode.i_mode) &&
+	    (inode.i_size_high || inode.i_size & 0x80000000UL))
+		ctx->large_files--;
+
+	pctx.errcode = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+					    deallocate_inode_block, ctx);
+	if (pctx.errcode) {
+		fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+}
+
+/*
+ * This fuction clears the htree flag on an inode
+ */
+static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
+{
+	struct ext2_inode       inode;
+
+	e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
+	inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
+	e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
+	if (ctx->dirs_to_hash)
+		ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+}
+
+
+static int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
+				    ext2_ino_t ino, char *buf)
+{
+	ext2_filsys fs = ctx->fs;
+	struct ext2_inode       inode;
+	int                     inode_modified = 0;
+	int                     not_fixed = 0;
+	unsigned char           *frag, *fsize;
+	struct problem_context  pctx;
+	int     problem = 0;
+
+	e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
+
+	clear_problem_context(&pctx);
+	pctx.ino = ino;
+	pctx.dir = dir;
+	pctx.inode = &inode;
+
+	if (inode.i_file_acl &&
+	    !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) &&
+	    fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
+		inode.i_file_acl = 0;
+#if BB_BIG_ENDIAN
+		/*
+		 * This is a special kludge to deal with long symlinks
+		 * on big endian systems.  i_blocks had already been
+		 * decremented earlier in pass 1, but since i_file_acl
+		 * hadn't yet been cleared, ext2fs_read_inode()
+		 * assumed that the file was short symlink and would
+		 * not have byte swapped i_block[0].  Hence, we have
+		 * to byte-swap it here.
+		 */
+		if (LINUX_S_ISLNK(inode.i_mode) &&
+		    (fs->flags & EXT2_FLAG_SWAP_BYTES) &&
+		    (inode.i_blocks == fs->blocksize >> 9))
+			inode.i_block[0] = ext2fs_swab32(inode.i_block[0]);
+#endif
+		inode_modified++;
+	} else
+		not_fixed++;
+
+	if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
+	    !LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
+	    !LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
+	    !(LINUX_S_ISSOCK(inode.i_mode)))
+		problem = PR_2_BAD_MODE;
+	else if (LINUX_S_ISCHR(inode.i_mode)
+		 && !e2fsck_pass1_check_device_inode(fs, &inode))
+		problem = PR_2_BAD_CHAR_DEV;
+	else if (LINUX_S_ISBLK(inode.i_mode)
+		 && !e2fsck_pass1_check_device_inode(fs, &inode))
+		problem = PR_2_BAD_BLOCK_DEV;
+	else if (LINUX_S_ISFIFO(inode.i_mode)
+		 && !e2fsck_pass1_check_device_inode(fs, &inode))
+		problem = PR_2_BAD_FIFO;
+	else if (LINUX_S_ISSOCK(inode.i_mode)
+		 && !e2fsck_pass1_check_device_inode(fs, &inode))
+		problem = PR_2_BAD_SOCKET;
+	else if (LINUX_S_ISLNK(inode.i_mode)
+		 && !e2fsck_pass1_check_symlink(fs, &inode, buf)) {
+		problem = PR_2_INVALID_SYMLINK;
+	}
+
+	if (problem) {
+		if (fix_problem(ctx, problem, &pctx)) {
+			deallocate_inode(ctx, ino, 0);
+			if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+				return 0;
+			return 1;
+		} else
+			not_fixed++;
+		problem = 0;
+	}
+
+	if (inode.i_faddr) {
+		if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
+			inode.i_faddr = 0;
+			inode_modified++;
+		} else
+			not_fixed++;
+	}
+
+	switch (fs->super->s_creator_os) {
+	    case EXT2_OS_LINUX:
+		frag = &inode.osd2.linux2.l_i_frag;
+		fsize = &inode.osd2.linux2.l_i_fsize;
+		break;
+	    case EXT2_OS_HURD:
+		frag = &inode.osd2.hurd2.h_i_frag;
+		fsize = &inode.osd2.hurd2.h_i_fsize;
+		break;
+	    case EXT2_OS_MASIX:
+		frag = &inode.osd2.masix2.m_i_frag;
+		fsize = &inode.osd2.masix2.m_i_fsize;
+		break;
+	    default:
+		frag = fsize = 0;
+	}
+	if (frag && *frag) {
+		pctx.num = *frag;
+		if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
+			*frag = 0;
+			inode_modified++;
+		} else
+			not_fixed++;
+		pctx.num = 0;
+	}
+	if (fsize && *fsize) {
+		pctx.num = *fsize;
+		if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
+			*fsize = 0;
+			inode_modified++;
+		} else
+			not_fixed++;
+		pctx.num = 0;
+	}
+
+	if (inode.i_file_acl &&
+	    ((inode.i_file_acl < fs->super->s_first_data_block) ||
+	     (inode.i_file_acl >= fs->super->s_blocks_count))) {
+		if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
+			inode.i_file_acl = 0;
+			inode_modified++;
+		} else
+			not_fixed++;
+	}
+	if (inode.i_dir_acl &&
+	    LINUX_S_ISDIR(inode.i_mode)) {
+		if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
+			inode.i_dir_acl = 0;
+			inode_modified++;
+		} else
+			not_fixed++;
+	}
+
+	if (inode_modified)
+		e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
+	if (!not_fixed)
+		ext2fs_unmark_inode_bitmap(ctx->inode_bad_map, ino);
+	return 0;
+}
+
+
+/*
+ * allocate_dir_block --- this function allocates a new directory
+ *      block for a particular inode; this is done if a directory has
+ *      a "hole" in it, or if a directory has a illegal block number
+ *      that was zeroed out and now needs to be replaced.
+ */
+static int allocate_dir_block(e2fsck_t ctx, struct ext2_db_entry *db,
+			      struct problem_context *pctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t                   blk;
+	char                    *block;
+	struct ext2_inode       inode;
+
+	if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
+		return 1;
+
+	/*
+	 * Read the inode and block bitmaps in; we'll be messing with
+	 * them.
+	 */
+	e2fsck_read_bitmaps(ctx);
+
+	/*
+	 * First, find a free block
+	 */
+	pctx->errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_new_block";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+		return 1;
+	}
+	ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
+	ext2fs_mark_block_bitmap(fs->block_map, blk);
+	ext2fs_mark_bb_dirty(fs);
+
+	/*
+	 * Now let's create the actual data block for the inode
+	 */
+	if (db->blockcnt)
+		pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
+	else
+		pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
+						     EXT2_ROOT_INO, &block);
+
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_new_dir_block";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+		return 1;
+	}
+
+	pctx->errcode = ext2fs_write_dir_block(fs, blk, block);
+	ext2fs_free_mem(&block);
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_write_dir_block";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+		return 1;
+	}
+
+	/*
+	 * Update the inode block count
+	 */
+	e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
+	inode.i_blocks += fs->blocksize / 512;
+	if (inode.i_size < (db->blockcnt+1) * fs->blocksize)
+		inode.i_size = (db->blockcnt+1) * fs->blocksize;
+	e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
+
+	/*
+	 * Finally, update the block pointers for the inode
+	 */
+	db->blk = blk;
+	pctx->errcode = ext2fs_block_iterate2(fs, db->ino, BLOCK_FLAG_HOLE,
+				      0, update_dir_block, db);
+	if (pctx->errcode) {
+		pctx->str = "ext2fs_block_iterate";
+		fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
+		return 1;
+	}
+
+	return 0;
+}
+
+/*
+ * This is a helper function for allocate_dir_block().
+ */
+static int update_dir_block(ext2_filsys fs FSCK_ATTR((unused)),
+			    blk_t       *block_nr,
+			    e2_blkcnt_t blockcnt,
+			    blk_t ref_block FSCK_ATTR((unused)),
+			    int ref_offset FSCK_ATTR((unused)),
+			    void *priv_data)
+{
+	struct ext2_db_entry *db;
+
+	db = (struct ext2_db_entry *) priv_data;
+	if (db->blockcnt == (int) blockcnt) {
+		*block_nr = db->blk;
+		return BLOCK_CHANGED;
+	}
+	return 0;
+}
+
+/*
+ * pass3.c -- pass #3 of e2fsck: Check for directory connectivity
+ *
+ * Pass #3 assures that all directories are connected to the
+ * filesystem tree, using the following algorithm:
+ *
+ * First, the root directory is checked to make sure it exists; if
+ * not, e2fsck will offer to create a new one.  It is then marked as
+ * "done".
+ *
+ * Then, pass3 interates over all directory inodes; for each directory
+ * it attempts to trace up the filesystem tree, using dirinfo.parent
+ * until it reaches a directory which has been marked "done".  If it
+ * cannot do so, then the directory must be disconnected, and e2fsck
+ * will offer to reconnect it to /lost+found.  While it is chasing
+ * parent pointers up the filesystem tree, if pass3 sees a directory
+ * twice, then it has detected a filesystem loop, and it will again
+ * offer to reconnect the directory to /lost+found in to break the
+ * filesystem loop.
+ *
+ * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to
+ * reconnect inodes to /lost+found; this subroutine is also used by
+ * pass 4.  e2fsck_reconnect_file() calls get_lost_and_found(), which
+ * is responsible for creating /lost+found if it does not exist.
+ *
+ * Pass 3 frees the following data structures:
+ *      - The dirinfo directory information cache.
+ */
+
+static void check_root(e2fsck_t ctx);
+static int check_directory(e2fsck_t ctx, struct dir_info *dir,
+			   struct problem_context *pctx);
+static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent);
+
+static ext2fs_inode_bitmap inode_loop_detect;
+static ext2fs_inode_bitmap inode_done_map;
+
+static void e2fsck_pass3(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	int             i;
+	struct problem_context  pctx;
+	struct dir_info *dir;
+	unsigned long maxdirs, count;
+
+	clear_problem_context(&pctx);
+
+	/* Pass 3 */
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_3_PASS_HEADER, &pctx);
+
+	/*
+	 * Allocate some bitmaps to do loop detection.
+	 */
+	pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
+						    &inode_done_map);
+	if (pctx.errcode) {
+		pctx.num = 2;
+		fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		goto abort_exit;
+	}
+	check_root(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		goto abort_exit;
+
+	ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
+
+	maxdirs = e2fsck_get_num_dirinfo(ctx);
+	count = 1;
+
+	if (ctx->progress)
+		if ((ctx->progress)(ctx, 3, 0, maxdirs))
+			goto abort_exit;
+
+	for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
+		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+			goto abort_exit;
+		if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs))
+			goto abort_exit;
+		if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
+			if (check_directory(ctx, dir, &pctx))
+				goto abort_exit;
+	}
+
+	/*
+	 * Force the creation of /lost+found if not present
+	 */
+	if ((ctx->flags & E2F_OPT_READONLY) == 0)
+		e2fsck_get_lost_and_found(ctx, 1);
+
+	/*
+	 * If there are any directories that need to be indexed or
+	 * optimized, do it here.
+	 */
+	e2fsck_rehash_directories(ctx);
+
+abort_exit:
+	e2fsck_free_dir_info(ctx);
+	ext2fs_free_inode_bitmap(inode_loop_detect);
+	inode_loop_detect = 0;
+	ext2fs_free_inode_bitmap(inode_done_map);
+	inode_done_map = 0;
+}
+
+/*
+ * This makes sure the root inode is present; if not, we ask if the
+ * user wants us to create it.  Not creating it is a fatal error.
+ */
+static void check_root(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t                   blk;
+	struct ext2_inode       inode;
+	char *                  block;
+	struct problem_context  pctx;
+
+	clear_problem_context(&pctx);
+
+	if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) {
+		/*
+		 * If the root inode is not a directory, die here.  The
+		 * user must have answered 'no' in pass1 when we
+		 * offered to clear it.
+		 */
+		if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map,
+					       EXT2_ROOT_INO))) {
+			fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+		}
+		return;
+	}
+
+	if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) {
+		fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	e2fsck_read_bitmaps(ctx);
+
+	/*
+	 * First, find a free block
+	 */
+	pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_new_block";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
+	ext2fs_mark_block_bitmap(fs->block_map, blk);
+	ext2fs_mark_bb_dirty(fs);
+
+	/*
+	 * Now let's create the actual data block for the inode
+	 */
+	pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO,
+					    &block);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_new_dir_block";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	pctx.errcode = ext2fs_write_dir_block(fs, blk, block);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_write_dir_block";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	ext2fs_free_mem(&block);
+
+	/*
+	 * Set up the inode structure
+	 */
+	memset(&inode, 0, sizeof(inode));
+	inode.i_mode = 040755;
+	inode.i_size = fs->blocksize;
+	inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
+	inode.i_links_count = 2;
+	inode.i_blocks = fs->blocksize / 512;
+	inode.i_block[0] = blk;
+
+	/*
+	 * Write out the inode.
+	 */
+	pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_write_inode";
+		fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	/*
+	 * Miscellaneous bookkeeping...
+	 */
+	e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
+	ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2);
+	ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2);
+
+	ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO);
+	ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO);
+	ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO);
+	ext2fs_mark_ib_dirty(fs);
+}
+
+/*
+ * This subroutine is responsible for making sure that a particular
+ * directory is connected to the root; if it isn't we trace it up as
+ * far as we can go, and then offer to connect the resulting parent to
+ * the lost+found.  We have to do loop detection; if we ever discover
+ * a loop, we treat that as a disconnected directory and offer to
+ * reparent it to lost+found.
+ *
+ * However, loop detection is expensive, because for very large
+ * filesystems, the inode_loop_detect bitmap is huge, and clearing it
+ * is non-trivial.  Loops in filesystems are also a rare error case,
+ * and we shouldn't optimize for error cases.  So we try two passes of
+ * the algorithm.  The first time, we ignore loop detection and merely
+ * increment a counter; if the counter exceeds some extreme threshold,
+ * then we try again with the loop detection bitmap enabled.
+ */
+static int check_directory(e2fsck_t ctx, struct dir_info *dir,
+			   struct problem_context *pctx)
+{
+	ext2_filsys     fs = ctx->fs;
+	struct dir_info *p = dir;
+	int             loop_pass = 0, parent_count = 0;
+
+	if (!p)
+		return 0;
+
+	while (1) {
+		/*
+		 * Mark this inode as being "done"; by the time we
+		 * return from this function, the inode we either be
+		 * verified as being connected to the directory tree,
+		 * or we will have offered to reconnect this to
+		 * lost+found.
+		 *
+		 * If it was marked done already, then we've reached a
+		 * parent we've already checked.
+		 */
+		if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino))
+			break;
+
+		/*
+		 * If this directory doesn't have a parent, or we've
+		 * seen the parent once already, then offer to
+		 * reparent it to lost+found
+		 */
+		if (!p->parent ||
+		    (loop_pass &&
+		     (ext2fs_test_inode_bitmap(inode_loop_detect,
+					      p->parent)))) {
+			pctx->ino = p->ino;
+			if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
+				if (e2fsck_reconnect_file(ctx, pctx->ino))
+					ext2fs_unmark_valid(fs);
+				else {
+					p = e2fsck_get_dir_info(ctx, pctx->ino);
+					p->parent = ctx->lost_and_found;
+					fix_dotdot(ctx, p, ctx->lost_and_found);
+				}
+			}
+			break;
+		}
+		p = e2fsck_get_dir_info(ctx, p->parent);
+		if (!p) {
+			fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
+			return 0;
+		}
+		if (loop_pass) {
+			ext2fs_mark_inode_bitmap(inode_loop_detect,
+						 p->ino);
+		} else if (parent_count++ > 2048) {
+			/*
+			 * If we've run into a path depth that's
+			 * greater than 2048, try again with the inode
+			 * loop bitmap turned on and start from the
+			 * top.
+			 */
+			loop_pass = 1;
+			if (inode_loop_detect)
+				ext2fs_clear_inode_bitmap(inode_loop_detect);
+			else {
+				pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect);
+				if (pctx->errcode) {
+					pctx->num = 1;
+					fix_problem(ctx,
+				    PR_3_ALLOCATE_IBITMAP_ERROR, pctx);
+					ctx->flags |= E2F_FLAG_ABORT;
+					return -1;
+				}
+			}
+			p = dir;
+		}
+	}
+
+	/*
+	 * Make sure that .. and the parent directory are the same;
+	 * offer to fix it if not.
+	 */
+	if (dir->parent != dir->dotdot) {
+		pctx->ino = dir->ino;
+		pctx->ino2 = dir->dotdot;
+		pctx->dir = dir->parent;
+		if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx))
+			fix_dotdot(ctx, dir, dir->parent);
+	}
+	return 0;
+}
+
+/*
+ * This routine gets the lost_and_found inode, making it a directory
+ * if necessary
+ */
+ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix)
+{
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t                      ino;
+	blk_t                   blk;
+	errcode_t               retval;
+	struct ext2_inode       inode;
+	char *                  block;
+	static const char       name[] = "lost+found";
+	struct  problem_context pctx;
+	struct dir_info         *dirinfo;
+
+	if (ctx->lost_and_found)
+		return ctx->lost_and_found;
+
+	clear_problem_context(&pctx);
+
+	retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
+			       sizeof(name)-1, 0, &ino);
+	if (retval && !fix)
+		return 0;
+	if (!retval) {
+		if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) {
+			ctx->lost_and_found = ino;
+			return ino;
+		}
+
+		/* Lost+found isn't a directory! */
+		if (!fix)
+			return 0;
+		pctx.ino = ino;
+		if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
+			return 0;
+
+		/* OK, unlink the old /lost+found file. */
+		pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
+		if (pctx.errcode) {
+			pctx.str = "ext2fs_unlink";
+			fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
+			return 0;
+		}
+		dirinfo = e2fsck_get_dir_info(ctx, ino);
+		if (dirinfo)
+			dirinfo->parent = 0;
+		e2fsck_adjust_inode_count(ctx, ino, -1);
+	} else if (retval != EXT2_ET_FILE_NOT_FOUND) {
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
+	}
+	if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0))
+		return 0;
+
+	/*
+	 * Read the inode and block bitmaps in; we'll be messing with
+	 * them.
+	 */
+	e2fsck_read_bitmaps(ctx);
+
+	/*
+	 * First, find a free block
+	 */
+	retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx);
+		return 0;
+	}
+	ext2fs_mark_block_bitmap(ctx->block_found_map, blk);
+	ext2fs_block_alloc_stats(fs, blk, +1);
+
+	/*
+	 * Next find a free inode.
+	 */
+	retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700,
+				  ctx->inode_used_map, &ino);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx);
+		return 0;
+	}
+	ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino);
+	ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino);
+	ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
+
+	/*
+	 * Now let's create the actual data block for the inode
+	 */
+	retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx);
+		return 0;
+	}
+
+	retval = ext2fs_write_dir_block(fs, blk, block);
+	ext2fs_free_mem(&block);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx);
+		return 0;
+	}
+
+	/*
+	 * Set up the inode structure
+	 */
+	memset(&inode, 0, sizeof(inode));
+	inode.i_mode = 040700;
+	inode.i_size = fs->blocksize;
+	inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
+	inode.i_links_count = 2;
+	inode.i_blocks = fs->blocksize / 512;
+	inode.i_block[0] = blk;
+
+	/*
+	 * Next, write out the inode.
+	 */
+	pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_write_inode";
+		fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
+		return 0;
+	}
+	/*
+	 * Finally, create the directory link
+	 */
+	pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
+	if (pctx.errcode) {
+		pctx.str = "ext2fs_link";
+		fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
+		return 0;
+	}
+
+	/*
+	 * Miscellaneous bookkeeping that needs to be kept straight.
+	 */
+	e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO);
+	e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1);
+	ext2fs_icount_store(ctx->inode_count, ino, 2);
+	ext2fs_icount_store(ctx->inode_link_info, ino, 2);
+	ctx->lost_and_found = ino;
+	return ino;
+}
+
+/*
+ * This routine will connect a file to lost+found
+ */
+int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino)
+{
+	ext2_filsys fs = ctx->fs;
+	errcode_t       retval;
+	char            name[80];
+	struct problem_context  pctx;
+	struct ext2_inode       inode;
+	int             file_type = 0;
+
+	clear_problem_context(&pctx);
+	pctx.ino = ino;
+
+	if (!ctx->bad_lost_and_found && !ctx->lost_and_found) {
+		if (e2fsck_get_lost_and_found(ctx, 1) == 0)
+			ctx->bad_lost_and_found++;
+	}
+	if (ctx->bad_lost_and_found) {
+		fix_problem(ctx, PR_3_NO_LPF, &pctx);
+		return 1;
+	}
+
+	sprintf(name, "#%u", ino);
+	if (ext2fs_read_inode(fs, ino, &inode) == 0)
+		file_type = ext2_file_type(inode.i_mode);
+	retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type);
+	if (retval == EXT2_ET_DIR_NO_SPACE) {
+		if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
+			return 1;
+		retval = e2fsck_expand_directory(ctx, ctx->lost_and_found,
+						 1, 0);
+		if (retval) {
+			pctx.errcode = retval;
+			fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
+			return 1;
+		}
+		retval = ext2fs_link(fs, ctx->lost_and_found, name,
+				     ino, file_type);
+	}
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
+		return 1;
+	}
+	e2fsck_adjust_inode_count(ctx, ino, 1);
+
+	return 0;
+}
+
+/*
+ * Utility routine to adjust the inode counts on an inode.
+ */
+errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj)
+{
+	ext2_filsys fs = ctx->fs;
+	errcode_t               retval;
+	struct ext2_inode       inode;
+
+	if (!ino)
+		return 0;
+
+	retval = ext2fs_read_inode(fs, ino, &inode);
+	if (retval)
+		return retval;
+
+	if (adj == 1) {
+		ext2fs_icount_increment(ctx->inode_count, ino, 0);
+		if (inode.i_links_count == (__u16) ~0)
+			return 0;
+		ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
+		inode.i_links_count++;
+	} else if (adj == -1) {
+		ext2fs_icount_decrement(ctx->inode_count, ino, 0);
+		if (inode.i_links_count == 0)
+			return 0;
+		ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
+		inode.i_links_count--;
+	}
+
+	retval = ext2fs_write_inode(fs, ino, &inode);
+	if (retval)
+		return retval;
+
+	return 0;
+}
+
+/*
+ * Fix parent --- this routine fixes up the parent of a directory.
+ */
+struct fix_dotdot_struct {
+	ext2_filsys     fs;
+	ext2_ino_t      parent;
+	int             done;
+	e2fsck_t        ctx;
+};
+
+static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
+			   int  offset FSCK_ATTR((unused)),
+			   int  blocksize FSCK_ATTR((unused)),
+			   char *buf FSCK_ATTR((unused)),
+			   void *priv_data)
+{
+	struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
+	errcode_t       retval;
+	struct problem_context pctx;
+
+	if ((dirent->name_len & 0xFF) != 2)
+		return 0;
+	if (strncmp(dirent->name, "..", 2))
+		return 0;
+
+	clear_problem_context(&pctx);
+
+	retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
+	}
+	retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1);
+	if (retval) {
+		pctx.errcode = retval;
+		fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
+	}
+	dirent->inode = fp->parent;
+
+	fp->done++;
+	return DIRENT_ABORT | DIRENT_CHANGED;
+}
+
+static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent)
+{
+	ext2_filsys fs = ctx->fs;
+	errcode_t       retval;
+	struct fix_dotdot_struct fp;
+	struct problem_context pctx;
+
+	fp.fs = fs;
+	fp.parent = parent;
+	fp.done = 0;
+	fp.ctx = ctx;
+
+	retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
+				    0, fix_dotdot_proc, &fp);
+	if (retval || !fp.done) {
+		clear_problem_context(&pctx);
+		pctx.ino = dir->ino;
+		pctx.errcode = retval;
+		fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
+			    PR_3_FIX_PARENT_NOFIND, &pctx);
+		ext2fs_unmark_valid(fs);
+	}
+	dir->dotdot = parent;
+}
+
+/*
+ * These routines are responsible for expanding a /lost+found if it is
+ * too small.
+ */
+
+struct expand_dir_struct {
+	int                     num;
+	int                     guaranteed_size;
+	int                     newblocks;
+	int                     last_block;
+	errcode_t               err;
+	e2fsck_t                ctx;
+};
+
+static int expand_dir_proc(ext2_filsys fs,
+			   blk_t        *blocknr,
+			   e2_blkcnt_t  blockcnt,
+			   blk_t ref_block FSCK_ATTR((unused)),
+			   int ref_offset FSCK_ATTR((unused)),
+			   void *priv_data)
+{
+	struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
+	blk_t   new_blk;
+	static blk_t    last_blk = 0;
+	char            *block;
+	errcode_t       retval;
+	e2fsck_t        ctx;
+
+	ctx = es->ctx;
+
+	if (es->guaranteed_size && blockcnt >= es->guaranteed_size)
+		return BLOCK_ABORT;
+
+	if (blockcnt > 0)
+		es->last_block = blockcnt;
+	if (*blocknr) {
+		last_blk = *blocknr;
+		return 0;
+	}
+	retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
+				  &new_blk);
+	if (retval) {
+		es->err = retval;
+		return BLOCK_ABORT;
+	}
+	if (blockcnt > 0) {
+		retval = ext2fs_new_dir_block(fs, 0, 0, &block);
+		if (retval) {
+			es->err = retval;
+			return BLOCK_ABORT;
+		}
+		es->num--;
+		retval = ext2fs_write_dir_block(fs, new_blk, block);
+	} else {
+		retval = ext2fs_get_mem(fs->blocksize, &block);
+		if (retval) {
+			es->err = retval;
+			return BLOCK_ABORT;
+		}
+		memset(block, 0, fs->blocksize);
+		retval = io_channel_write_blk(fs->io, new_blk, 1, block);
+	}
+	if (retval) {
+		es->err = retval;
+		return BLOCK_ABORT;
+	}
+	ext2fs_free_mem(&block);
+	*blocknr = new_blk;
+	ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
+	ext2fs_block_alloc_stats(fs, new_blk, +1);
+	es->newblocks++;
+
+	if (es->num == 0)
+		return (BLOCK_CHANGED | BLOCK_ABORT);
+	else
+		return BLOCK_CHANGED;
+}
+
+errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir,
+				  int num, int guaranteed_size)
+{
+	ext2_filsys fs = ctx->fs;
+	errcode_t       retval;
+	struct expand_dir_struct es;
+	struct ext2_inode       inode;
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+
+	/*
+	 * Read the inode and block bitmaps in; we'll be messing with
+	 * them.
+	 */
+	e2fsck_read_bitmaps(ctx);
+
+	retval = ext2fs_check_directory(fs, dir);
+	if (retval)
+		return retval;
+
+	es.num = num;
+	es.guaranteed_size = guaranteed_size;
+	es.last_block = 0;
+	es.err = 0;
+	es.newblocks = 0;
+	es.ctx = ctx;
+
+	retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
+				       0, expand_dir_proc, &es);
+
+	if (es.err)
+		return es.err;
+
+	/*
+	 * Update the size and block count fields in the inode.
+	 */
+	retval = ext2fs_read_inode(fs, dir, &inode);
+	if (retval)
+		return retval;
+
+	inode.i_size = (es.last_block + 1) * fs->blocksize;
+	inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+
+	e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
+
+	return 0;
+}
+
+/*
+ * pass4.c -- pass #4 of e2fsck: Check reference counts
+ *
+ * Pass 4 frees the following data structures:
+ *      - A bitmap of which inodes are imagic inodes.   (inode_imagic_map)
+ */
+
+/*
+ * This routine is called when an inode is not connected to the
+ * directory tree.
+ *
+ * This subroutine returns 1 then the caller shouldn't bother with the
+ * rest of the pass 4 tests.
+ */
+static int disconnect_inode(e2fsck_t ctx, ext2_ino_t i)
+{
+	ext2_filsys fs = ctx->fs;
+	struct ext2_inode       inode;
+	struct problem_context  pctx;
+
+	e2fsck_read_inode(ctx, i, &inode, "pass4: disconnect_inode");
+	clear_problem_context(&pctx);
+	pctx.ino = i;
+	pctx.inode = &inode;
+
+	/*
+	 * Offer to delete any zero-length files that does not have
+	 * blocks.  If there is an EA block, it might have useful
+	 * information, so we won't prompt to delete it, but let it be
+	 * reconnected to lost+found.
+	 */
+	if (!inode.i_blocks && (LINUX_S_ISREG(inode.i_mode) ||
+				LINUX_S_ISDIR(inode.i_mode))) {
+		if (fix_problem(ctx, PR_4_ZERO_LEN_INODE, &pctx)) {
+			ext2fs_icount_store(ctx->inode_link_info, i, 0);
+			inode.i_links_count = 0;
+			inode.i_dtime = time(NULL);
+			e2fsck_write_inode(ctx, i, &inode,
+					   "disconnect_inode");
+			/*
+			 * Fix up the bitmaps...
+			 */
+			e2fsck_read_bitmaps(ctx);
+			ext2fs_unmark_inode_bitmap(ctx->inode_used_map, i);
+			ext2fs_unmark_inode_bitmap(ctx->inode_dir_map, i);
+			ext2fs_inode_alloc_stats2(fs, i, -1,
+						  LINUX_S_ISDIR(inode.i_mode));
+			return 0;
+		}
+	}
+
+	/*
+	 * Prompt to reconnect.
+	 */
+	if (fix_problem(ctx, PR_4_UNATTACHED_INODE, &pctx)) {
+		if (e2fsck_reconnect_file(ctx, i))
+			ext2fs_unmark_valid(fs);
+	} else {
+		/*
+		 * If we don't attach the inode, then skip the
+		 * i_links_test since there's no point in trying to
+		 * force i_links_count to zero.
+		 */
+		ext2fs_unmark_valid(fs);
+		return 1;
+	}
+	return 0;
+}
+
+
+static void e2fsck_pass4(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t      i;
+	struct ext2_inode       inode;
+	struct problem_context  pctx;
+	__u16   link_count, link_counted;
+	char    *buf = NULL;
+	int     group, maxgroup;
+
+	/* Pass 4 */
+
+	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_4_PASS_HEADER, &pctx);
+
+	group = 0;
+	maxgroup = fs->group_desc_count;
+	if (ctx->progress)
+		if ((ctx->progress)(ctx, 4, 0, maxgroup))
+			return;
+
+	for (i=1; i <= fs->super->s_inodes_count; i++) {
+		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+			return;
+		if ((i % fs->super->s_inodes_per_group) == 0) {
+			group++;
+			if (ctx->progress)
+				if ((ctx->progress)(ctx, 4, group, maxgroup))
+					return;
+		}
+		if (i == EXT2_BAD_INO ||
+		    (i > EXT2_ROOT_INO && i < EXT2_FIRST_INODE(fs->super)))
+			continue;
+		if (!(ext2fs_test_inode_bitmap(ctx->inode_used_map, i)) ||
+		    (ctx->inode_imagic_map &&
+		     ext2fs_test_inode_bitmap(ctx->inode_imagic_map, i)))
+			continue;
+		ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
+		ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
+		if (link_counted == 0) {
+			if (!buf)
+				buf = e2fsck_allocate_memory(ctx,
+				     fs->blocksize, "bad_inode buffer");
+			if (e2fsck_process_bad_inode(ctx, 0, i, buf))
+				continue;
+			if (disconnect_inode(ctx, i))
+				continue;
+			ext2fs_icount_fetch(ctx->inode_link_info, i,
+					    &link_count);
+			ext2fs_icount_fetch(ctx->inode_count, i,
+					    &link_counted);
+		}
+		if (link_counted != link_count) {
+			e2fsck_read_inode(ctx, i, &inode, "pass4");
+			pctx.ino = i;
+			pctx.inode = &inode;
+			if (link_count != inode.i_links_count) {
+				pctx.num = link_count;
+				fix_problem(ctx,
+					    PR_4_INCONSISTENT_COUNT, &pctx);
+			}
+			pctx.num = link_counted;
+			if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
+				inode.i_links_count = link_counted;
+				e2fsck_write_inode(ctx, i, &inode, "pass4");
+			}
+		}
+	}
+	ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0;
+	ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_imagic_map);
+	ctx->inode_imagic_map = 0;
+	ext2fs_free_mem(&buf);
+}
+
+/*
+ * pass5.c --- check block and inode bitmaps against on-disk bitmaps
+ */
+
+#define NO_BLK ((blk_t) -1)
+
+static void print_bitmap_problem(e2fsck_t ctx, int problem,
+			    struct problem_context *pctx)
+{
+	switch (problem) {
+	case PR_5_BLOCK_UNUSED:
+		if (pctx->blk == pctx->blk2)
+			pctx->blk2 = 0;
+		else
+			problem = PR_5_BLOCK_RANGE_UNUSED;
+		break;
+	case PR_5_BLOCK_USED:
+		if (pctx->blk == pctx->blk2)
+			pctx->blk2 = 0;
+		else
+			problem = PR_5_BLOCK_RANGE_USED;
+		break;
+	case PR_5_INODE_UNUSED:
+		if (pctx->ino == pctx->ino2)
+			pctx->ino2 = 0;
+		else
+			problem = PR_5_INODE_RANGE_UNUSED;
+		break;
+	case PR_5_INODE_USED:
+		if (pctx->ino == pctx->ino2)
+			pctx->ino2 = 0;
+		else
+			problem = PR_5_INODE_RANGE_USED;
+		break;
+	}
+	fix_problem(ctx, problem, pctx);
+	pctx->blk = pctx->blk2 = NO_BLK;
+	pctx->ino = pctx->ino2 = 0;
+}
+
+static void check_block_bitmaps(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t   i;
+	int     *free_array;
+	int     group = 0;
+	unsigned int    blocks = 0;
+	unsigned int    free_blocks = 0;
+	int     group_free = 0;
+	int     actual, bitmap;
+	struct problem_context  pctx;
+	int     problem, save_problem, fixit, had_problem;
+	errcode_t       retval;
+
+	clear_problem_context(&pctx);
+	free_array = (int *) e2fsck_allocate_memory(ctx,
+	    fs->group_desc_count * sizeof(int), "free block count array");
+
+	if ((fs->super->s_first_data_block <
+	     ext2fs_get_block_bitmap_start(ctx->block_found_map)) ||
+	    (fs->super->s_blocks_count-1 >
+	     ext2fs_get_block_bitmap_end(ctx->block_found_map))) {
+		pctx.num = 1;
+		pctx.blk = fs->super->s_first_data_block;
+		pctx.blk2 = fs->super->s_blocks_count -1;
+		pctx.ino = ext2fs_get_block_bitmap_start(ctx->block_found_map);
+		pctx.ino2 = ext2fs_get_block_bitmap_end(ctx->block_found_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+
+	if ((fs->super->s_first_data_block <
+	     ext2fs_get_block_bitmap_start(fs->block_map)) ||
+	    (fs->super->s_blocks_count-1 >
+	     ext2fs_get_block_bitmap_end(fs->block_map))) {
+		pctx.num = 2;
+		pctx.blk = fs->super->s_first_data_block;
+		pctx.blk2 = fs->super->s_blocks_count -1;
+		pctx.ino = ext2fs_get_block_bitmap_start(fs->block_map);
+		pctx.ino2 = ext2fs_get_block_bitmap_end(fs->block_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+
+redo_counts:
+	had_problem = 0;
+	save_problem = 0;
+	pctx.blk = pctx.blk2 = NO_BLK;
+	for (i = fs->super->s_first_data_block;
+	     i < fs->super->s_blocks_count;
+	     i++) {
+		actual = ext2fs_fast_test_block_bitmap(ctx->block_found_map, i);
+		bitmap = ext2fs_fast_test_block_bitmap(fs->block_map, i);
+
+		if (actual == bitmap)
+			goto do_counts;
+
+		if (!actual && bitmap) {
+			/*
+			 * Block not used, but marked in use in the bitmap.
+			 */
+			problem = PR_5_BLOCK_UNUSED;
+		} else {
+			/*
+			 * Block used, but not marked in use in the bitmap.
+			 */
+			problem = PR_5_BLOCK_USED;
+		}
+		if (pctx.blk == NO_BLK) {
+			pctx.blk = pctx.blk2 = i;
+			save_problem = problem;
+		} else {
+			if ((problem == save_problem) &&
+			    (pctx.blk2 == i-1))
+				pctx.blk2++;
+			else {
+				print_bitmap_problem(ctx, save_problem, &pctx);
+				pctx.blk = pctx.blk2 = i;
+				save_problem = problem;
+			}
+		}
+		ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
+		had_problem++;
+
+	do_counts:
+		if (!bitmap) {
+			group_free++;
+			free_blocks++;
+		}
+		blocks ++;
+		if ((blocks == fs->super->s_blocks_per_group) ||
+		    (i == fs->super->s_blocks_count-1)) {
+			free_array[group] = group_free;
+			group ++;
+			blocks = 0;
+			group_free = 0;
+			if (ctx->progress)
+				if ((ctx->progress)(ctx, 5, group,
+						    fs->group_desc_count*2))
+					return;
+		}
+	}
+	if (pctx.blk != NO_BLK)
+		print_bitmap_problem(ctx, save_problem, &pctx);
+	if (had_problem)
+		fixit = end_problem_latch(ctx, PR_LATCH_BBITMAP);
+	else
+		fixit = -1;
+	ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
+
+	if (fixit == 1) {
+		ext2fs_free_block_bitmap(fs->block_map);
+		retval = ext2fs_copy_bitmap(ctx->block_found_map,
+						  &fs->block_map);
+		if (retval) {
+			clear_problem_context(&pctx);
+			fix_problem(ctx, PR_5_COPY_BBITMAP_ERROR, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		ext2fs_set_bitmap_padding(fs->block_map);
+		ext2fs_mark_bb_dirty(fs);
+
+		/* Redo the counts */
+		blocks = 0; free_blocks = 0; group_free = 0; group = 0;
+		memset(free_array, 0, fs->group_desc_count * sizeof(int));
+		goto redo_counts;
+	} else if (fixit == 0)
+		ext2fs_unmark_valid(fs);
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		if (free_array[i] != fs->group_desc[i].bg_free_blocks_count) {
+			pctx.group = i;
+			pctx.blk = fs->group_desc[i].bg_free_blocks_count;
+			pctx.blk2 = free_array[i];
+
+			if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT_GROUP,
+					&pctx)) {
+				fs->group_desc[i].bg_free_blocks_count =
+					free_array[i];
+				ext2fs_mark_super_dirty(fs);
+			} else
+				ext2fs_unmark_valid(fs);
+		}
+	}
+	if (free_blocks != fs->super->s_free_blocks_count) {
+		pctx.group = 0;
+		pctx.blk = fs->super->s_free_blocks_count;
+		pctx.blk2 = free_blocks;
+
+		if (fix_problem(ctx, PR_5_FREE_BLOCK_COUNT, &pctx)) {
+			fs->super->s_free_blocks_count = free_blocks;
+			ext2fs_mark_super_dirty(fs);
+		} else
+			ext2fs_unmark_valid(fs);
+	}
+	ext2fs_free_mem(&free_array);
+}
+
+static void check_inode_bitmaps(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t      i;
+	unsigned int    free_inodes = 0;
+	int             group_free = 0;
+	int             dirs_count = 0;
+	int             group = 0;
+	unsigned int    inodes = 0;
+	int             *free_array;
+	int             *dir_array;
+	int             actual, bitmap;
+	errcode_t       retval;
+	struct problem_context  pctx;
+	int             problem, save_problem, fixit, had_problem;
+
+	clear_problem_context(&pctx);
+	free_array = (int *) e2fsck_allocate_memory(ctx,
+	    fs->group_desc_count * sizeof(int), "free inode count array");
+
+	dir_array = (int *) e2fsck_allocate_memory(ctx,
+	   fs->group_desc_count * sizeof(int), "directory count array");
+
+	if ((1 < ext2fs_get_inode_bitmap_start(ctx->inode_used_map)) ||
+	    (fs->super->s_inodes_count >
+	     ext2fs_get_inode_bitmap_end(ctx->inode_used_map))) {
+		pctx.num = 3;
+		pctx.blk = 1;
+		pctx.blk2 = fs->super->s_inodes_count;
+		pctx.ino = ext2fs_get_inode_bitmap_start(ctx->inode_used_map);
+		pctx.ino2 = ext2fs_get_inode_bitmap_end(ctx->inode_used_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+	if ((1 < ext2fs_get_inode_bitmap_start(fs->inode_map)) ||
+	    (fs->super->s_inodes_count >
+	     ext2fs_get_inode_bitmap_end(fs->inode_map))) {
+		pctx.num = 4;
+		pctx.blk = 1;
+		pctx.blk2 = fs->super->s_inodes_count;
+		pctx.ino = ext2fs_get_inode_bitmap_start(fs->inode_map);
+		pctx.ino2 = ext2fs_get_inode_bitmap_end(fs->inode_map);
+		fix_problem(ctx, PR_5_BMAP_ENDPOINTS, &pctx);
+
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+
+redo_counts:
+	had_problem = 0;
+	save_problem = 0;
+	pctx.ino = pctx.ino2 = 0;
+	for (i = 1; i <= fs->super->s_inodes_count; i++) {
+		actual = ext2fs_fast_test_inode_bitmap(ctx->inode_used_map, i);
+		bitmap = ext2fs_fast_test_inode_bitmap(fs->inode_map, i);
+
+		if (actual == bitmap)
+			goto do_counts;
+
+		if (!actual && bitmap) {
+			/*
+			 * Inode wasn't used, but marked in bitmap
+			 */
+			problem = PR_5_INODE_UNUSED;
+		} else /* if (actual && !bitmap) */ {
+			/*
+			 * Inode used, but not in bitmap
+			 */
+			problem = PR_5_INODE_USED;
+		}
+		if (pctx.ino == 0) {
+			pctx.ino = pctx.ino2 = i;
+			save_problem = problem;
+		} else {
+			if ((problem == save_problem) &&
+			    (pctx.ino2 == i-1))
+				pctx.ino2++;
+			else {
+				print_bitmap_problem(ctx, save_problem, &pctx);
+				pctx.ino = pctx.ino2 = i;
+				save_problem = problem;
+			}
+		}
+		ctx->flags |= E2F_FLAG_PROG_SUPPRESS;
+		had_problem++;
+
+do_counts:
+		if (!bitmap) {
+			group_free++;
+			free_inodes++;
+		} else {
+			if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i))
+				dirs_count++;
+		}
+		inodes++;
+		if ((inodes == fs->super->s_inodes_per_group) ||
+		    (i == fs->super->s_inodes_count)) {
+			free_array[group] = group_free;
+			dir_array[group] = dirs_count;
+			group ++;
+			inodes = 0;
+			group_free = 0;
+			dirs_count = 0;
+			if (ctx->progress)
+				if ((ctx->progress)(ctx, 5,
+					    group + fs->group_desc_count,
+					    fs->group_desc_count*2))
+					return;
+		}
+	}
+	if (pctx.ino)
+		print_bitmap_problem(ctx, save_problem, &pctx);
+
+	if (had_problem)
+		fixit = end_problem_latch(ctx, PR_LATCH_IBITMAP);
+	else
+		fixit = -1;
+	ctx->flags &= ~E2F_FLAG_PROG_SUPPRESS;
+
+	if (fixit == 1) {
+		ext2fs_free_inode_bitmap(fs->inode_map);
+		retval = ext2fs_copy_bitmap(ctx->inode_used_map,
+						  &fs->inode_map);
+		if (retval) {
+			clear_problem_context(&pctx);
+			fix_problem(ctx, PR_5_COPY_IBITMAP_ERROR, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		ext2fs_set_bitmap_padding(fs->inode_map);
+		ext2fs_mark_ib_dirty(fs);
+
+		/* redo counts */
+		inodes = 0; free_inodes = 0; group_free = 0;
+		dirs_count = 0; group = 0;
+		memset(free_array, 0, fs->group_desc_count * sizeof(int));
+		memset(dir_array, 0, fs->group_desc_count * sizeof(int));
+		goto redo_counts;
+	} else if (fixit == 0)
+		ext2fs_unmark_valid(fs);
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		if (free_array[i] != fs->group_desc[i].bg_free_inodes_count) {
+			pctx.group = i;
+			pctx.ino = fs->group_desc[i].bg_free_inodes_count;
+			pctx.ino2 = free_array[i];
+			if (fix_problem(ctx, PR_5_FREE_INODE_COUNT_GROUP,
+					&pctx)) {
+				fs->group_desc[i].bg_free_inodes_count =
+					free_array[i];
+				ext2fs_mark_super_dirty(fs);
+			} else
+				ext2fs_unmark_valid(fs);
+		}
+		if (dir_array[i] != fs->group_desc[i].bg_used_dirs_count) {
+			pctx.group = i;
+			pctx.ino = fs->group_desc[i].bg_used_dirs_count;
+			pctx.ino2 = dir_array[i];
+
+			if (fix_problem(ctx, PR_5_FREE_DIR_COUNT_GROUP,
+					&pctx)) {
+				fs->group_desc[i].bg_used_dirs_count =
+					dir_array[i];
+				ext2fs_mark_super_dirty(fs);
+			} else
+				ext2fs_unmark_valid(fs);
+		}
+	}
+	if (free_inodes != fs->super->s_free_inodes_count) {
+		pctx.group = -1;
+		pctx.ino = fs->super->s_free_inodes_count;
+		pctx.ino2 = free_inodes;
+
+		if (fix_problem(ctx, PR_5_FREE_INODE_COUNT, &pctx)) {
+			fs->super->s_free_inodes_count = free_inodes;
+			ext2fs_mark_super_dirty(fs);
+		} else
+			ext2fs_unmark_valid(fs);
+	}
+	ext2fs_free_mem(&free_array);
+	ext2fs_free_mem(&dir_array);
+}
+
+static void check_inode_end(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t      end, save_inodes_count, i;
+	struct problem_context  pctx;
+
+	clear_problem_context(&pctx);
+
+	end = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count;
+	pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map, end,
+						     &save_inodes_count);
+	if (pctx.errcode) {
+		pctx.num = 1;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+	if (save_inodes_count == end)
+		return;
+
+	for (i = save_inodes_count + 1; i <= end; i++) {
+		if (!ext2fs_test_inode_bitmap(fs->inode_map, i)) {
+			if (fix_problem(ctx, PR_5_INODE_BMAP_PADDING, &pctx)) {
+				for (i = save_inodes_count + 1; i <= end; i++)
+					ext2fs_mark_inode_bitmap(fs->inode_map,
+								 i);
+				ext2fs_mark_ib_dirty(fs);
+			} else
+				ext2fs_unmark_valid(fs);
+			break;
+		}
+	}
+
+	pctx.errcode = ext2fs_fudge_inode_bitmap_end(fs->inode_map,
+						     save_inodes_count, 0);
+	if (pctx.errcode) {
+		pctx.num = 2;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+}
+
+static void check_block_end(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t   end, save_blocks_count, i;
+	struct problem_context  pctx;
+
+	clear_problem_context(&pctx);
+
+	end = fs->block_map->start +
+		(EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count) - 1;
+	pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map, end,
+						     &save_blocks_count);
+	if (pctx.errcode) {
+		pctx.num = 3;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+	if (save_blocks_count == end)
+		return;
+
+	for (i = save_blocks_count + 1; i <= end; i++) {
+		if (!ext2fs_test_block_bitmap(fs->block_map, i)) {
+			if (fix_problem(ctx, PR_5_BLOCK_BMAP_PADDING, &pctx)) {
+				for (i = save_blocks_count + 1; i <= end; i++)
+					ext2fs_mark_block_bitmap(fs->block_map,
+								 i);
+				ext2fs_mark_bb_dirty(fs);
+			} else
+				ext2fs_unmark_valid(fs);
+			break;
+		}
+	}
+
+	pctx.errcode = ext2fs_fudge_block_bitmap_end(fs->block_map,
+						     save_blocks_count, 0);
+	if (pctx.errcode) {
+		pctx.num = 4;
+		fix_problem(ctx, PR_5_FUDGE_BITMAP_ERROR, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT; /* fatal */
+		return;
+	}
+}
+
+static void e2fsck_pass5(e2fsck_t ctx)
+{
+	struct problem_context  pctx;
+
+	/* Pass 5 */
+
+	clear_problem_context(&pctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		fix_problem(ctx, PR_5_PASS_HEADER, &pctx);
+
+	if (ctx->progress)
+		if ((ctx->progress)(ctx, 5, 0, ctx->fs->group_desc_count*2))
+			return;
+
+	e2fsck_read_bitmaps(ctx);
+
+	check_block_bitmaps(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
+	check_inode_bitmaps(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
+	check_inode_end(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
+	check_block_end(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
+
+	ext2fs_free_inode_bitmap(ctx->inode_used_map);
+	ctx->inode_used_map = 0;
+	ext2fs_free_inode_bitmap(ctx->inode_dir_map);
+	ctx->inode_dir_map = 0;
+	ext2fs_free_block_bitmap(ctx->block_found_map);
+	ctx->block_found_map = 0;
+}
+
+/*
+ * problem.c --- report filesystem problems to the user
+ */
+
+#define PR_PREEN_OK     0x000001 /* Don't need to do preenhalt */
+#define PR_NO_OK        0x000002 /* If user answers no, don't make fs invalid */
+#define PR_NO_DEFAULT   0x000004 /* Default to no */
+#define PR_MSG_ONLY     0x000008 /* Print message only */
+
+/* Bit positions 0x000ff0 are reserved for the PR_LATCH flags */
+
+#define PR_FATAL        0x001000 /* Fatal error */
+#define PR_AFTER_CODE   0x002000 /* After asking the first question, */
+				 /* ask another */
+#define PR_PREEN_NOMSG  0x004000 /* Don't print a message if we're preening */
+#define PR_NOCOLLATE    0x008000 /* Don't collate answers for this latch */
+#define PR_NO_NOMSG     0x010000 /* Don't print a message if e2fsck -n */
+#define PR_PREEN_NO     0x020000 /* Use No as an answer if preening */
+#define PR_PREEN_NOHDR  0x040000 /* Don't print the preen header */
+
+
+#define PROMPT_NONE     0
+#define PROMPT_FIX      1
+#define PROMPT_CLEAR    2
+#define PROMPT_RELOCATE 3
+#define PROMPT_ALLOCATE 4
+#define PROMPT_EXPAND   5
+#define PROMPT_CONNECT  6
+#define PROMPT_CREATE   7
+#define PROMPT_SALVAGE  8
+#define PROMPT_TRUNCATE 9
+#define PROMPT_CLEAR_INODE 10
+#define PROMPT_ABORT    11
+#define PROMPT_SPLIT    12
+#define PROMPT_CONTINUE 13
+#define PROMPT_CLONE    14
+#define PROMPT_DELETE   15
+#define PROMPT_SUPPRESS 16
+#define PROMPT_UNLINK   17
+#define PROMPT_CLEAR_HTREE 18
+#define PROMPT_RECREATE 19
+#define PROMPT_NULL     20
+
+struct e2fsck_problem {
+	problem_t       e2p_code;
+	const char *    e2p_description;
+	char            prompt;
+	int             flags;
+	problem_t       second_code;
+};
+
+struct latch_descr {
+	int             latch_code;
+	problem_t       question;
+	problem_t       end_message;
+	int             flags;
+};
+
+/*
+ * These are the prompts which are used to ask the user if they want
+ * to fix a problem.
+ */
+static const char *const prompt[] = {
+	N_("(no prompt)"),      /* 0 */
+	N_("Fix"),              /* 1 */
+	N_("Clear"),            /* 2 */
+	N_("Relocate"),         /* 3 */
+	N_("Allocate"),         /* 4 */
+	N_("Expand"),           /* 5 */
+	N_("Connect to /lost+found"), /* 6 */
+	N_("Create"),           /* 7 */
+	N_("Salvage"),          /* 8 */
+	N_("Truncate"),         /* 9 */
+	N_("Clear inode"),      /* 10 */
+	N_("Abort"),            /* 11 */
+	N_("Split"),            /* 12 */
+	N_("Continue"),         /* 13 */
+	N_("Clone multiply-claimed blocks"), /* 14 */
+	N_("Delete file"),      /* 15 */
+	N_("Suppress messages"),/* 16 */
+	N_("Unlink"),           /* 17 */
+	N_("Clear HTree index"),/* 18 */
+	N_("Recreate"),         /* 19 */
+	"",                     /* 20 */
+};
+
+/*
+ * These messages are printed when we are preen mode and we will be
+ * automatically fixing the problem.
+ */
+static const char *const preen_msg[] = {
+	N_("(NONE)"),           /* 0 */
+	N_("FIXED"),            /* 1 */
+	N_("CLEARED"),          /* 2 */
+	N_("RELOCATED"),        /* 3 */
+	N_("ALLOCATED"),        /* 4 */
+	N_("EXPANDED"),         /* 5 */
+	N_("RECONNECTED"),      /* 6 */
+	N_("CREATED"),          /* 7 */
+	N_("SALVAGED"),         /* 8 */
+	N_("TRUNCATED"),        /* 9 */
+	N_("INODE CLEARED"),    /* 10 */
+	N_("ABORTED"),          /* 11 */
+	N_("SPLIT"),            /* 12 */
+	N_("CONTINUING"),       /* 13 */
+	N_("MULTIPLY-CLAIMED BLOCKS CLONED"), /* 14 */
+	N_("FILE DELETED"),     /* 15 */
+	N_("SUPPRESSED"),       /* 16 */
+	N_("UNLINKED"),         /* 17 */
+	N_("HTREE INDEX CLEARED"),/* 18 */
+	N_("WILL RECREATE"),    /* 19 */
+	"",                     /* 20 */
+};
+
+static const struct e2fsck_problem problem_table[] = {
+
+	/* Pre-Pass 1 errors */
+
+	/* Block bitmap not in group */
+	{ PR_0_BB_NOT_GROUP, N_("@b @B for @g %g is not in @g.  (@b %b)\n"),
+	  PROMPT_RELOCATE, PR_LATCH_RELOC },
+
+	/* Inode bitmap not in group */
+	{ PR_0_IB_NOT_GROUP, N_("@i @B for @g %g is not in @g.  (@b %b)\n"),
+	  PROMPT_RELOCATE, PR_LATCH_RELOC },
+
+	/* Inode table not in group */
+	{ PR_0_ITABLE_NOT_GROUP,
+	  N_("@i table for @g %g is not in @g.  (@b %b)\n"
+	  "WARNING: SEVERE DATA LOSS POSSIBLE.\n"),
+	  PROMPT_RELOCATE, PR_LATCH_RELOC },
+
+	/* Superblock corrupt */
+	{ PR_0_SB_CORRUPT,
+	  N_("\nThe @S could not be read or does not describe a correct ext2\n"
+	  "@f.  If the @v is valid and it really contains an ext2\n"
+	  "@f (and not swap or ufs or something else), then the @S\n"
+	  "is corrupt, and you might try running e2fsck with an alternate @S:\n"
+	  "    e2fsck -b %S <@v>\n\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Filesystem size is wrong */
+	{ PR_0_FS_SIZE_WRONG,
+	  N_("The @f size (according to the @S) is %b @bs\n"
+	  "The physical size of the @v is %c @bs\n"
+	  "Either the @S or the partition table is likely to be corrupt!\n"),
+	  PROMPT_ABORT, 0 },
+
+	/* Fragments not supported */
+	{ PR_0_NO_FRAGMENTS,
+	  N_("@S @b_size = %b, fragsize = %c.\n"
+	  "This version of e2fsck does not support fragment sizes different\n"
+	  "from the @b size.\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	  /* Bad blocks_per_group */
+	{ PR_0_BLOCKS_PER_GROUP,
+	  N_("@S @bs_per_group = %b, should have been %c\n"),
+	  PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+	/* Bad first_data_block */
+	{ PR_0_FIRST_DATA_BLOCK,
+	  N_("@S first_data_@b = %b, should have been %c\n"),
+	  PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+	/* Adding UUID to filesystem */
+	{ PR_0_ADD_UUID,
+	  N_("@f did not have a UUID; generating one.\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Relocate hint */
+	{ PR_0_RELOCATE_HINT,
+	  N_("Note: if several inode or block bitmap blocks or part\n"
+	  "of the inode table require relocation, you may wish to try\n"
+	  "running e2fsck with the '-b %S' option first.  The problem\n"
+	  "may lie only with the primary block group descriptors, and\n"
+	  "the backup block group descriptors may be OK.\n\n"),
+	  PROMPT_NONE, PR_PREEN_OK | PR_NOCOLLATE },
+
+	/* Miscellaneous superblock corruption */
+	{ PR_0_MISC_CORRUPT_SUPER,
+	  N_("Corruption found in @S.  (%s = %N).\n"),
+	  PROMPT_NONE, PR_AFTER_CODE, PR_0_SB_CORRUPT },
+
+	/* Error determing physical device size of filesystem */
+	{ PR_0_GETSIZE_ERROR,
+	  N_("Error determining size of the physical @v: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Inode count in superblock is incorrect */
+	{ PR_0_INODE_COUNT_WRONG,
+	  N_("@i count in @S is %i, @s %j.\n"),
+	  PROMPT_FIX, 0 },
+
+	{ PR_0_HURD_CLEAR_FILETYPE,
+	  N_("The Hurd does not support the filetype feature.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Journal inode is invalid */
+	{ PR_0_JOURNAL_BAD_INODE,
+	  N_("@S has an @n ext3 @j (@i %i).\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* The external journal has (unsupported) multiple filesystems */
+	{ PR_0_JOURNAL_UNSUPP_MULTIFS,
+	  N_("External @j has multiple @f users (unsupported).\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Can't find external journal */
+	{ PR_0_CANT_FIND_JOURNAL,
+	  N_("Can't find external @j\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* External journal has bad superblock */
+	{ PR_0_EXT_JOURNAL_BAD_SUPER,
+	  N_("External @j has bad @S\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Superblock has a bad journal UUID */
+	{ PR_0_JOURNAL_BAD_UUID,
+	  N_("External @j does not support this @f\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Journal has an unknown superblock type */
+	{ PR_0_JOURNAL_UNSUPP_SUPER,
+	  N_("Ext3 @j @S is unknown type %N (unsupported).\n"
+	     "It is likely that your copy of e2fsck is old and/or doesn't "
+	     "support this @j format.\n"
+	     "It is also possible the @j @S is corrupt.\n"),
+	  PROMPT_ABORT, PR_NO_OK | PR_AFTER_CODE, PR_0_JOURNAL_BAD_SUPER },
+
+	/* Journal superblock is corrupt */
+	{ PR_0_JOURNAL_BAD_SUPER,
+	  N_("Ext3 @j @S is corrupt.\n"),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Superblock flag should be cleared */
+	{ PR_0_JOURNAL_HAS_JOURNAL,
+	  N_("@S doesn't have has_@j flag, but has ext3 @j %s.\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Superblock flag is incorrect */
+	{ PR_0_JOURNAL_RECOVER_SET,
+	  N_("@S has ext3 needs_recovery flag set, but no @j.\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Journal has data, but recovery flag is clear */
+	{ PR_0_JOURNAL_RECOVERY_CLEAR,
+	  N_("ext3 recovery flag is clear, but @j has data.\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Ask if we should clear the journal */
+	{ PR_0_JOURNAL_RESET_JOURNAL,
+	  N_("Clear @j"),
+	  PROMPT_NULL, PR_PREEN_NOMSG },
+
+	/* Ask if we should run the journal anyway */
+	{ PR_0_JOURNAL_RUN,
+	  N_("Run @j anyway"),
+	  PROMPT_NULL, 0 },
+
+	/* Run the journal by default */
+	{ PR_0_JOURNAL_RUN_DEFAULT,
+	  N_("Recovery flag not set in backup @S, so running @j anyway.\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Clearing orphan inode */
+	{ PR_0_ORPHAN_CLEAR_INODE,
+	  N_("%s @o @i %i (uid=%Iu, gid=%Ig, mode=%Im, size=%Is)\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Illegal block found in orphaned inode */
+	{ PR_0_ORPHAN_ILLEGAL_BLOCK_NUM,
+	   N_("@I @b #%B (%b) found in @o @i %i.\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Already cleared block found in orphaned inode */
+	{ PR_0_ORPHAN_ALREADY_CLEARED_BLOCK,
+	   N_("Already cleared @b #%B (%b) found in @o @i %i.\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Illegal orphan inode in superblock */
+	{ PR_0_ORPHAN_ILLEGAL_HEAD_INODE,
+	  N_("@I @o @i %i in @S.\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Illegal inode in orphaned inode list */
+	{ PR_0_ORPHAN_ILLEGAL_INODE,
+	  N_("@I @i %i in @o @i list.\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Filesystem revision is 0, but feature flags are set */
+	{ PR_0_FS_REV_LEVEL,
+	  N_("@f has feature flag(s) set, but is a revision 0 @f.  "),
+	  PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
+
+	/* Journal superblock has an unknown read-only feature flag set */
+	{ PR_0_JOURNAL_UNSUPP_ROCOMPAT,
+	  N_("Ext3 @j @S has an unknown read-only feature flag set.\n"),
+	  PROMPT_ABORT, 0 },
+
+	/* Journal superblock has an unknown incompatible feature flag set */
+	{ PR_0_JOURNAL_UNSUPP_INCOMPAT,
+	  N_("Ext3 @j @S has an unknown incompatible feature flag set.\n"),
+	  PROMPT_ABORT, 0 },
+
+	/* Journal has unsupported version number */
+	{ PR_0_JOURNAL_UNSUPP_VERSION,
+	  N_("@j version not supported by this e2fsck.\n"),
+	  PROMPT_ABORT, 0 },
+
+	/* Moving journal to hidden file */
+	{ PR_0_MOVE_JOURNAL,
+	  N_("Moving @j from /%s to hidden @i.\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error moving journal to hidden file */
+	{ PR_0_ERR_MOVE_JOURNAL,
+	  N_("Error moving @j: %m\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Clearing V2 journal superblock */
+	{ PR_0_CLEAR_V2_JOURNAL,
+	  N_("Found @n V2 @j @S fields (from V1 @j).\n"
+	     "Clearing fields beyond the V1 @j @S...\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Backup journal inode blocks */
+	{ PR_0_BACKUP_JNL,
+	  N_("Backing up @j @i @b information.\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Reserved blocks w/o resize_inode */
+	{ PR_0_NONZERO_RESERVED_GDT_BLOCKS,
+	  N_("@f does not have resize_@i enabled, but s_reserved_gdt_@bs\n"
+	     "is %N; @s zero.  "),
+	  PROMPT_FIX, 0 },
+
+	/* Resize_inode not enabled, but resize inode is non-zero */
+	{ PR_0_CLEAR_RESIZE_INODE,
+	  N_("Resize_@i not enabled, but the resize @i is non-zero.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Resize inode invalid */
+	{ PR_0_RESIZE_INODE_INVALID,
+	  N_("Resize @i not valid.  "),
+	  PROMPT_RECREATE, 0 },
+
+	/* Pass 1 errors */
+
+	/* Pass 1: Checking inodes, blocks, and sizes */
+	{ PR_1_PASS_HEADER,
+	  N_("Pass 1: Checking @is, @bs, and sizes\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Root directory is not an inode */
+	{ PR_1_ROOT_NO_DIR, N_("@r is not a @d.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Root directory has dtime set */
+	{ PR_1_ROOT_DTIME,
+	  N_("@r has dtime set (probably due to old mke2fs).  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Reserved inode has bad mode */
+	{ PR_1_RESERVED_BAD_MODE,
+	  N_("Reserved @i %i (%Q) has @n mode.  "),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Deleted inode has zero dtime */
+	{ PR_1_ZERO_DTIME,
+	  N_("@D @i %i has zero dtime.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Inode in use, but dtime set */
+	{ PR_1_SET_DTIME,
+	  N_("@i %i is in use, but has dtime set.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Zero-length directory */
+	{ PR_1_ZERO_LENGTH_DIR,
+	  N_("@i %i is a @z @d.  "),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Block bitmap conflicts with some other fs block */
+	{ PR_1_BB_CONFLICT,
+	  N_("@g %g's @b @B at %b @C.\n"),
+	  PROMPT_RELOCATE, 0 },
+
+	/* Inode bitmap conflicts with some other fs block */
+	{ PR_1_IB_CONFLICT,
+	  N_("@g %g's @i @B at %b @C.\n"),
+	  PROMPT_RELOCATE, 0 },
+
+	/* Inode table conflicts with some other fs block */
+	{ PR_1_ITABLE_CONFLICT,
+	  N_("@g %g's @i table at %b @C.\n"),
+	  PROMPT_RELOCATE, 0 },
+
+	/* Block bitmap is on a bad block */
+	{ PR_1_BB_BAD_BLOCK,
+	  N_("@g %g's @b @B (%b) is bad.  "),
+	  PROMPT_RELOCATE, 0 },
+
+	/* Inode bitmap is on a bad block */
+	{ PR_1_IB_BAD_BLOCK,
+	  N_("@g %g's @i @B (%b) is bad.  "),
+	  PROMPT_RELOCATE, 0 },
+
+	/* Inode has incorrect i_size */
+	{ PR_1_BAD_I_SIZE,
+	  N_("@i %i, i_size is %Is, @s %N.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Inode has incorrect i_blocks */
+	{ PR_1_BAD_I_BLOCKS,
+	  N_("@i %i, i_@bs is %Ib, @s %N.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Illegal blocknumber in inode */
+	{ PR_1_ILLEGAL_BLOCK_NUM,
+	  N_("@I @b #%B (%b) in @i %i.  "),
+	  PROMPT_CLEAR, PR_LATCH_BLOCK },
+
+	/* Block number overlaps fs metadata */
+	{ PR_1_BLOCK_OVERLAPS_METADATA,
+	  N_("@b #%B (%b) overlaps @f metadata in @i %i.  "),
+	  PROMPT_CLEAR, PR_LATCH_BLOCK },
+
+	/* Inode has illegal blocks (latch question) */
+	{ PR_1_INODE_BLOCK_LATCH,
+	  N_("@i %i has illegal @b(s).  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Too many bad blocks in inode */
+	{ PR_1_TOO_MANY_BAD_BLOCKS,
+	  N_("Too many illegal @bs in @i %i.\n"),
+	  PROMPT_CLEAR_INODE, PR_NO_OK },
+
+	/* Illegal block number in bad block inode */
+	{ PR_1_BB_ILLEGAL_BLOCK_NUM,
+	  N_("@I @b #%B (%b) in bad @b @i.  "),
+	  PROMPT_CLEAR, PR_LATCH_BBLOCK },
+
+	/* Bad block inode has illegal blocks (latch question) */
+	{ PR_1_INODE_BBLOCK_LATCH,
+	  N_("Bad @b @i has illegal @b(s).  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Duplicate or bad blocks in use! */
+	{ PR_1_DUP_BLOCKS_PREENSTOP,
+	  N_("Duplicate or bad @b in use!\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Bad block used as bad block indirect block */
+	{ PR_1_BBINODE_BAD_METABLOCK,
+	  N_("Bad @b %b used as bad @b @i indirect @b.  "),
+	  PROMPT_CLEAR, PR_LATCH_BBLOCK },
+
+	/* Inconsistency can't be fixed prompt */
+	{ PR_1_BBINODE_BAD_METABLOCK_PROMPT,
+	  N_("\nThe bad @b @i has probably been corrupted.  You probably\n"
+	     "should stop now and run ""e2fsck -c"" to scan for bad blocks\n"
+	     "in the @f.\n"),
+	  PROMPT_CONTINUE, PR_PREEN_NOMSG },
+
+	/* Bad primary block */
+	{ PR_1_BAD_PRIMARY_BLOCK,
+	  N_("\nIf the @b is really bad, the @f cannot be fixed.\n"),
+	  PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK_PROMPT },
+
+	/* Bad primary block prompt */
+	{ PR_1_BAD_PRIMARY_BLOCK_PROMPT,
+	  N_("You can remove this @b from the bad @b list and hope\n"
+	     "that the @b is really OK.  But there are no guarantees.\n\n"),
+	  PROMPT_CLEAR, PR_PREEN_NOMSG },
+
+	/* Bad primary superblock */
+	{ PR_1_BAD_PRIMARY_SUPERBLOCK,
+	  N_("The primary @S (%b) is on the bad @b list.\n"),
+	  PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
+
+	/* Bad primary block group descriptors */
+	{ PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR,
+	  N_("Block %b in the primary @g descriptors "
+	  "is on the bad @b list\n"),
+	  PROMPT_NONE, PR_AFTER_CODE, PR_1_BAD_PRIMARY_BLOCK },
+
+	/* Bad superblock in group */
+	{ PR_1_BAD_SUPERBLOCK,
+	  N_("Warning: Group %g's @S (%b) is bad.\n"),
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Bad block group descriptors in group */
+	{ PR_1_BAD_GROUP_DESCRIPTORS,
+	  N_("Warning: Group %g's copy of the @g descriptors has a bad "
+	  "@b (%b).\n"),
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block claimed for no reason */
+	{ PR_1_PROGERR_CLAIMED_BLOCK,
+	  N_("Programming error?  @b #%b claimed for no reason in "
+	  "process_bad_@b.\n"),
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Error allocating blocks for relocating metadata */
+	{ PR_1_RELOC_BLOCK_ALLOCATE,
+	  N_("@A %N contiguous @b(s) in @b @g %g for %s: %m\n"),
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Error allocating block buffer during relocation process */
+	{ PR_1_RELOC_MEMORY_ALLOCATE,
+	  N_("@A @b buffer for relocating %s\n"),
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Relocating metadata group information from X to Y */
+	{ PR_1_RELOC_FROM_TO,
+	  N_("Relocating @g %g's %s from %b to %c...\n"),
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Relocating metatdata group information to X */
+	{ PR_1_RELOC_TO,
+	  N_("Relocating @g %g's %s to %c...\n"), /* xgettext:no-c-format */
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Block read error during relocation process */
+	{ PR_1_RELOC_READ_ERR,
+	  N_("Warning: could not read @b %b of %s: %m\n"),
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Block write error during relocation process */
+	{ PR_1_RELOC_WRITE_ERR,
+	  N_("Warning: could not write @b %b for %s: %m\n"),
+	  PROMPT_NONE, PR_PREEN_OK },
+
+	/* Error allocating inode bitmap */
+	{ PR_1_ALLOCATE_IBITMAP_ERROR,
+	  N_("@A @i @B (%N): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating block bitmap */
+	{ PR_1_ALLOCATE_BBITMAP_ERROR,
+	  N_("@A @b @B (%N): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating icount structure */
+	{ PR_1_ALLOCATE_ICOUNT,
+	  N_("@A icount link information: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating dbcount */
+	{ PR_1_ALLOCATE_DBCOUNT,
+	  N_("@A @d @b array: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while scanning inodes */
+	{ PR_1_ISCAN_ERROR,
+	  N_("Error while scanning @is (%i): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while iterating over blocks */
+	{ PR_1_BLOCK_ITERATE,
+	  N_("Error while iterating over @bs in @i %i: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while storing inode count information */
+	{ PR_1_ICOUNT_STORE,
+	  N_("Error storing @i count information (@i=%i, count=%N): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while storing directory block information */
+	{ PR_1_ADD_DBLOCK,
+	  N_("Error storing @d @b information "
+	  "(@i=%i, @b=%b, num=%N): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while reading inode (for clearing) */
+	{ PR_1_READ_INODE,
+	  N_("Error reading @i %i: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Suppress messages prompt */
+	{ PR_1_SUPPRESS_MESSAGES, "", PROMPT_SUPPRESS, PR_NO_OK },
+
+	/* Imagic flag set on an inode when filesystem doesn't support it */
+	{ PR_1_SET_IMAGIC,
+	  N_("@i %i has imagic flag set.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Immutable flag set on a device or socket inode */
+	{ PR_1_SET_IMMUTABLE,
+	  N_("Special (@v/socket/fifo/symlink) file (@i %i) has immutable\n"
+	     "or append-only flag set.  "),
+	  PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
+
+	/* Compression flag set on an inode when filesystem doesn't support it */
+	{ PR_1_COMPR_SET,
+	  N_("@i %i has @cion flag set on @f without @cion support.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Non-zero size for device, fifo or socket inode */
+	{ PR_1_SET_NONZSIZE,
+	  N_("Special (@v/socket/fifo) @i %i has non-zero size.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Filesystem revision is 0, but feature flags are set */
+	{ PR_1_FS_REV_LEVEL,
+	  N_("@f has feature flag(s) set, but is a revision 0 @f.  "),
+	  PROMPT_FIX, PR_PREEN_OK | PR_NO_OK },
+
+	/* Journal inode is not in use, but contains data */
+	{ PR_1_JOURNAL_INODE_NOT_CLEAR,
+	  N_("@j @i is not in use, but contains data.  "),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Journal has bad mode */
+	{ PR_1_JOURNAL_BAD_MODE,
+	  N_("@j is not regular file.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Deal with inodes that were part of orphan linked list */
+	{ PR_1_LOW_DTIME,
+	  N_("@i %i was part of the @o @i list.  "),
+	  PROMPT_FIX, PR_LATCH_LOW_DTIME, 0 },
+
+	/* Deal with inodes that were part of corrupted orphan linked
+	   list (latch question) */
+	{ PR_1_ORPHAN_LIST_REFUGEES,
+	  N_("@is that were part of a corrupted orphan linked list found.  "),
+	  PROMPT_FIX, 0 },
+
+	/* Error allocating refcount structure */
+	{ PR_1_ALLOCATE_REFCOUNT,
+	  N_("@A refcount structure (%N): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error reading extended attribute block */
+	{ PR_1_READ_EA_BLOCK,
+	  N_("Error reading @a @b %b for @i %i.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Invalid extended attribute block */
+	{ PR_1_BAD_EA_BLOCK,
+	  N_("@i %i has a bad @a @b %b.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Error reading Extended Attribute block while fixing refcount */
+	{ PR_1_EXTATTR_READ_ABORT,
+	  N_("Error reading @a @b %b (%m).  "),
+	  PROMPT_ABORT, 0 },
+
+	/* Extended attribute reference count incorrect */
+	{ PR_1_EXTATTR_REFCOUNT,
+	  N_("@a @b %b has reference count %B, @s %N.  "),
+	  PROMPT_FIX, 0 },
+
+	/* Error writing Extended Attribute block while fixing refcount */
+	{ PR_1_EXTATTR_WRITE,
+	  N_("Error writing @a @b %b (%m).  "),
+	  PROMPT_ABORT, 0 },
+
+	/* Multiple EA blocks not supported */
+	{ PR_1_EA_MULTI_BLOCK,
+	  N_("@a @b %b has h_@bs > 1.  "),
+	  PROMPT_CLEAR, 0},
+
+	/* Error allocating EA region allocation structure */
+	{ PR_1_EA_ALLOC_REGION,
+	  N_("@A @a @b %b.  "),
+	  PROMPT_ABORT, 0},
+
+	/* Error EA allocation collision */
+	{ PR_1_EA_ALLOC_COLLISION,
+	  N_("@a @b %b is corrupt (allocation collision).  "),
+	  PROMPT_CLEAR, 0},
+
+	/* Bad extended attribute name */
+	{ PR_1_EA_BAD_NAME,
+	  N_("@a @b %b is corrupt (@n name).  "),
+	  PROMPT_CLEAR, 0},
+
+	/* Bad extended attribute value */
+	{ PR_1_EA_BAD_VALUE,
+	  N_("@a @b %b is corrupt (@n value).  "),
+	  PROMPT_CLEAR, 0},
+
+	/* Inode too big (latch question) */
+	{ PR_1_INODE_TOOBIG,
+	  N_("@i %i is too big.  "), PROMPT_TRUNCATE, 0 },
+
+	/* Directory too big */
+	{ PR_1_TOOBIG_DIR,
+	  N_("@b #%B (%b) causes @d to be too big.  "),
+	  PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+	/* Regular file too big */
+	{ PR_1_TOOBIG_REG,
+	  N_("@b #%B (%b) causes file to be too big.  "),
+	  PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+	/* Symlink too big */
+	{ PR_1_TOOBIG_SYMLINK,
+	  N_("@b #%B (%b) causes symlink to be too big.  "),
+	  PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+	/* INDEX_FL flag set on a non-HTREE filesystem */
+	{ PR_1_HTREE_SET,
+	  N_("@i %i has INDEX_FL flag set on @f without htree support.\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* INDEX_FL flag set on a non-directory */
+	{ PR_1_HTREE_NODIR,
+	  N_("@i %i has INDEX_FL flag set but is not a @d.\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* Invalid root node in HTREE directory */
+	{ PR_1_HTREE_BADROOT,
+	  N_("@h %i has an @n root node.\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* Unsupported hash version in HTREE directory */
+	{ PR_1_HTREE_HASHV,
+	  N_("@h %i has an unsupported hash version (%N)\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* Incompatible flag in HTREE root node */
+	{ PR_1_HTREE_INCOMPAT,
+	  N_("@h %i uses an incompatible htree root node flag.\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* HTREE too deep */
+	{ PR_1_HTREE_DEPTH,
+	  N_("@h %i has a tree depth (%N) which is too big\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* Bad block has indirect block that conflicts with filesystem block */
+	{ PR_1_BB_FS_BLOCK,
+	  N_("Bad @b @i has an indirect @b (%b) that conflicts with\n"
+	     "@f metadata.  "),
+	  PROMPT_CLEAR, PR_LATCH_BBLOCK },
+
+	/* Resize inode failed */
+	{ PR_1_RESIZE_INODE_CREATE,
+	  N_("Resize @i (re)creation failed: %m."),
+	  PROMPT_ABORT, 0 },
+
+	/* invalid inode->i_extra_isize */
+	{ PR_1_EXTRA_ISIZE,
+	  N_("@i %i has a extra size (%IS) which is @n\n"),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* invalid ea entry->e_name_len */
+	{ PR_1_ATTR_NAME_LEN,
+	  N_("@a in @i %i has a namelen (%N) which is @n\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* invalid ea entry->e_value_size */
+	{ PR_1_ATTR_VALUE_SIZE,
+	  N_("@a in @i %i has a value size (%N) which is @n\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* invalid ea entry->e_value_offs */
+	{ PR_1_ATTR_VALUE_OFFSET,
+	  N_("@a in @i %i has a value offset (%N) which is @n\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* invalid ea entry->e_value_block */
+	{ PR_1_ATTR_VALUE_BLOCK,
+	  N_("@a in @i %i has a value @b (%N) which is @n (must be 0)\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* invalid ea entry->e_hash */
+	{ PR_1_ATTR_HASH,
+	  N_("@a in @i %i has a hash (%N) which is @n (must be 0)\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Pass 1b errors */
+
+	/* Pass 1B: Rescan for duplicate/bad blocks */
+	{ PR_1B_PASS_HEADER,
+	  N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
+	  "Pass 1B: Rescanning for @m @bs\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Duplicate/bad block(s) header */
+	{ PR_1B_DUP_BLOCK_HEADER,
+	  N_("@m @b(s) in @i %i:"),
+	  PROMPT_NONE, 0 },
+
+	/* Duplicate/bad block(s) in inode */
+	{ PR_1B_DUP_BLOCK,
+	  " %b",
+	  PROMPT_NONE, PR_LATCH_DBLOCK | PR_PREEN_NOHDR },
+
+	/* Duplicate/bad block(s) end */
+	{ PR_1B_DUP_BLOCK_END,
+	  "\n",
+	  PROMPT_NONE, PR_PREEN_NOHDR },
+
+	/* Error while scanning inodes */
+	{ PR_1B_ISCAN_ERROR,
+	  N_("Error while scanning inodes (%i): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error allocating inode bitmap */
+	{ PR_1B_ALLOCATE_IBITMAP_ERROR,
+	  N_("@A @i @B (@i_dup_map): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error while iterating over blocks */
+	{ PR_1B_BLOCK_ITERATE,
+	  N_("Error while iterating over @bs in @i %i (%s): %m\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error adjusting EA refcount */
+	{ PR_1B_ADJ_EA_REFCOUNT,
+	  N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
+	  PROMPT_NONE, 0 },
+
+
+	/* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
+	{ PR_1C_PASS_HEADER,
+	  N_("Pass 1C: Scanning directories for @is with @m @bs.\n"),
+	  PROMPT_NONE, 0 },
+
+
+	/* Pass 1D: Reconciling multiply-claimed blocks */
+	{ PR_1D_PASS_HEADER,
+	  N_("Pass 1D: Reconciling @m @bs\n"),
+	  PROMPT_NONE, 0 },
+
+	/* File has duplicate blocks */
+	{ PR_1D_DUP_FILE,
+	  N_("File %Q (@i #%i, mod time %IM)\n"
+	  "  has %B @m @b(s), shared with %N file(s):\n"),
+	  PROMPT_NONE, 0 },
+
+	/* List of files sharing duplicate blocks */
+	{ PR_1D_DUP_FILE_LIST,
+	  N_("\t%Q (@i #%i, mod time %IM)\n"),
+	  PROMPT_NONE, 0 },
+
+	/* File sharing blocks with filesystem metadata  */
+	{ PR_1D_SHARE_METADATA,
+	  N_("\t<@f metadata>\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Report of how many duplicate/bad inodes */
+	{ PR_1D_NUM_DUP_INODES,
+	  N_("(There are %N @is containing @m @bs.)\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Duplicated blocks already reassigned or cloned. */
+	{ PR_1D_DUP_BLOCKS_DEALT,
+	  N_("@m @bs already reassigned or cloned.\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Clone duplicate/bad blocks? */
+	{ PR_1D_CLONE_QUESTION,
+	  "", PROMPT_CLONE, PR_NO_OK },
+
+	/* Delete file? */
+	{ PR_1D_DELETE_QUESTION,
+	  "", PROMPT_DELETE, 0 },
+
+	/* Couldn't clone file (error) */
+	{ PR_1D_CLONE_ERROR,
+	  N_("Couldn't clone file: %m\n"), PROMPT_NONE, 0 },
+
+	/* Pass 2 errors */
+
+	/* Pass 2: Checking directory structure */
+	{ PR_2_PASS_HEADER,
+	  N_("Pass 2: Checking @d structure\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Bad inode number for '.' */
+	{ PR_2_BAD_INODE_DOT,
+	  N_("@n @i number for '.' in @d @i %i.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Directory entry has bad inode number */
+	{ PR_2_BAD_INO,
+	  N_("@E has @n @i #: %Di.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Directory entry has deleted or unused inode */
+	{ PR_2_UNUSED_INODE,
+	  N_("@E has @D/unused @i %Di.  "),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Directry entry is link to '.' */
+	{ PR_2_LINK_DOT,
+	  N_("@E @L to '.'  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Directory entry points to inode now located in a bad block */
+	{ PR_2_BB_INODE,
+	  N_("@E points to @i (%Di) located in a bad @b.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Directory entry contains a link to a directory */
+	{ PR_2_LINK_DIR,
+	  N_("@E @L to @d %P (%Di).\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Directory entry contains a link to the root directry */
+	{ PR_2_LINK_ROOT,
+	  N_("@E @L to the @r.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Directory entry has illegal characters in its name */
+	{ PR_2_BAD_NAME,
+	  N_("@E has illegal characters in its name.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Missing '.' in directory inode */
+	{ PR_2_MISSING_DOT,
+	  N_("Missing '.' in @d @i %i.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Missing '..' in directory inode */
+	{ PR_2_MISSING_DOT_DOT,
+	  N_("Missing '..' in @d @i %i.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* First entry in directory inode doesn't contain '.' */
+	{ PR_2_1ST_NOT_DOT,
+	  N_("First @e '%Dn' (@i=%Di) in @d @i %i (%p) @s '.'\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Second entry in directory inode doesn't contain '..' */
+	{ PR_2_2ND_NOT_DOT_DOT,
+	  N_("Second @e '%Dn' (@i=%Di) in @d @i %i @s '..'\n"),
+	  PROMPT_FIX, 0 },
+
+	/* i_faddr should be zero */
+	{ PR_2_FADDR_ZERO,
+	  N_("i_faddr @F %IF, @s zero.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* i_file_acl should be zero */
+	{ PR_2_FILE_ACL_ZERO,
+	  N_("i_file_acl @F %If, @s zero.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* i_dir_acl should be zero */
+	{ PR_2_DIR_ACL_ZERO,
+	  N_("i_dir_acl @F %Id, @s zero.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* i_frag should be zero */
+	{ PR_2_FRAG_ZERO,
+	  N_("i_frag @F %N, @s zero.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* i_fsize should be zero */
+	{ PR_2_FSIZE_ZERO,
+	  N_("i_fsize @F %N, @s zero.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* inode has bad mode */
+	{ PR_2_BAD_MODE,
+	  N_("@i %i (%Q) has @n mode (%Im).\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* directory corrupted */
+	{ PR_2_DIR_CORRUPTED,
+	  N_("@d @i %i, @b %B, offset %N: @d corrupted\n"),
+	  PROMPT_SALVAGE, 0 },
+
+	/* filename too long */
+	{ PR_2_FILENAME_LONG,
+	  N_("@d @i %i, @b %B, offset %N: filename too long\n"),
+	  PROMPT_TRUNCATE, 0 },
+
+	/* Directory inode has a missing block (hole) */
+	{ PR_2_DIRECTORY_HOLE,
+	  N_("@d @i %i has an unallocated @b #%B.  "),
+	  PROMPT_ALLOCATE, 0 },
+
+	/* '.' is not NULL terminated */
+	{ PR_2_DOT_NULL_TERM,
+	  N_("'.' @d @e in @d @i %i is not NULL terminated\n"),
+	  PROMPT_FIX, 0 },
+
+	/* '..' is not NULL terminated */
+	{ PR_2_DOT_DOT_NULL_TERM,
+	  N_("'..' @d @e in @d @i %i is not NULL terminated\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Illegal character device inode */
+	{ PR_2_BAD_CHAR_DEV,
+	  N_("@i %i (%Q) is an @I character @v.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Illegal block device inode */
+	{ PR_2_BAD_BLOCK_DEV,
+	  N_("@i %i (%Q) is an @I @b @v.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Duplicate '.' entry */
+	{ PR_2_DUP_DOT,
+	  N_("@E is duplicate '.' @e.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Duplicate '..' entry */
+	{ PR_2_DUP_DOT_DOT,
+	  N_("@E is duplicate '..' @e.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Internal error: couldn't find dir_info */
+	{ PR_2_NO_DIRINFO,
+	  N_("Internal error: cannot find dir_info for %i.\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Final rec_len is wrong */
+	{ PR_2_FINAL_RECLEN,
+	  N_("@E has rec_len of %Dr, @s %N.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Error allocating icount structure */
+	{ PR_2_ALLOCATE_ICOUNT,
+	  N_("@A icount structure: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error iterating over directory blocks */
+	{ PR_2_DBLIST_ITERATE,
+	  N_("Error iterating over @d @bs: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error reading directory block */
+	{ PR_2_READ_DIRBLOCK,
+	  N_("Error reading @d @b %b (@i %i): %m\n"),
+	  PROMPT_CONTINUE, 0 },
+
+	/* Error writing directory block */
+	{ PR_2_WRITE_DIRBLOCK,
+	  N_("Error writing @d @b %b (@i %i): %m\n"),
+	  PROMPT_CONTINUE, 0 },
+
+	/* Error allocating new directory block */
+	{ PR_2_ALLOC_DIRBOCK,
+	  N_("@A new @d @b for @i %i (%s): %m\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error deallocating inode */
+	{ PR_2_DEALLOC_INODE,
+	  N_("Error deallocating @i %i: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Directory entry for '.' is big.  Split? */
+	{ PR_2_SPLIT_DOT,
+	  N_("@d @e for '.' is big.  "),
+	  PROMPT_SPLIT, PR_NO_OK },
+
+	/* Illegal FIFO inode */
+	{ PR_2_BAD_FIFO,
+	  N_("@i %i (%Q) is an @I FIFO.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Illegal socket inode */
+	{ PR_2_BAD_SOCKET,
+	  N_("@i %i (%Q) is an @I socket.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Directory filetype not set */
+	{ PR_2_SET_FILETYPE,
+	  N_("Setting filetype for @E to %N.\n"),
+	  PROMPT_NONE, PR_PREEN_OK | PR_NO_OK | PR_NO_NOMSG },
+
+	/* Directory filetype incorrect */
+	{ PR_2_BAD_FILETYPE,
+	  N_("@E has an incorrect filetype (was %Dt, @s %N).\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Directory filetype set on filesystem */
+	{ PR_2_CLEAR_FILETYPE,
+	  N_("@E has filetype set.\n"),
+	  PROMPT_CLEAR, PR_PREEN_OK },
+
+	/* Directory filename is null */
+	{ PR_2_NULL_NAME,
+	  N_("@E has a @z name.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Invalid symlink */
+	{ PR_2_INVALID_SYMLINK,
+	  N_("Symlink %Q (@i #%i) is @n.\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* i_file_acl (extended attribute block) is bad */
+	{ PR_2_FILE_ACL_BAD,
+	  N_("@a @b @F @n (%If).\n"),
+	  PROMPT_CLEAR, 0 },
+
+	/* Filesystem contains large files, but has no such flag in sb */
+	{ PR_2_FEATURE_LARGE_FILES,
+	  N_("@f contains large files, but lacks LARGE_FILE flag in @S.\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Node in HTREE directory not referenced */
+	{ PR_2_HTREE_NOTREF,
+	  N_("@p @h %d: node (%B) not referenced\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Node in HTREE directory referenced twice */
+	{ PR_2_HTREE_DUPREF,
+	  N_("@p @h %d: node (%B) referenced twice\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Node in HTREE directory has bad min hash */
+	{ PR_2_HTREE_MIN_HASH,
+	  N_("@p @h %d: node (%B) has bad min hash\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Node in HTREE directory has bad max hash */
+	{ PR_2_HTREE_MAX_HASH,
+	  N_("@p @h %d: node (%B) has bad max hash\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Clear invalid HTREE directory */
+	{ PR_2_HTREE_CLEAR,
+	  N_("@n @h %d (%q).  "), PROMPT_CLEAR, 0 },
+
+	/* Bad block in htree interior node */
+	{ PR_2_HTREE_BADBLK,
+	  N_("@p @h %d (%q): bad @b number %b.\n"),
+	  PROMPT_CLEAR_HTREE, 0 },
+
+	/* Error adjusting EA refcount */
+	{ PR_2_ADJ_EA_REFCOUNT,
+	  N_("Error adjusting refcount for @a @b %b (@i %i): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Invalid HTREE root node */
+	{ PR_2_HTREE_BAD_ROOT,
+	  N_("@p @h %d: root node is @n\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* Invalid HTREE limit */
+	{ PR_2_HTREE_BAD_LIMIT,
+	  N_("@p @h %d: node (%B) has @n limit (%N)\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* Invalid HTREE count */
+	{ PR_2_HTREE_BAD_COUNT,
+	  N_("@p @h %d: node (%B) has @n count (%N)\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* HTREE interior node has out-of-order hashes in table */
+	{ PR_2_HTREE_HASH_ORDER,
+	  N_("@p @h %d: node (%B) has an unordered hash table\n"),
+	  PROMPT_CLEAR_HTREE, PR_PREEN_OK },
+
+	/* Node in HTREE directory has invalid depth */
+	{ PR_2_HTREE_BAD_DEPTH,
+	  N_("@p @h %d: node (%B) has @n depth\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Duplicate directory entry found */
+	{ PR_2_DUPLICATE_DIRENT,
+	  N_("Duplicate @E found.  "),
+	  PROMPT_CLEAR, 0 },
+
+	/* Non-unique filename found */
+	{ PR_2_NON_UNIQUE_FILE, /* xgettext: no-c-format */
+	  N_("@E has a non-unique filename.\nRename to %s"),
+	  PROMPT_NULL, 0 },
+
+	/* Duplicate directory entry found */
+	{ PR_2_REPORT_DUP_DIRENT,
+	  N_("Duplicate @e '%Dn' found.\n\tMarking %p (%i) to be rebuilt.\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Pass 3 errors */
+
+	/* Pass 3: Checking directory connectivity */
+	{ PR_3_PASS_HEADER,
+	  N_("Pass 3: Checking @d connectivity\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Root inode not allocated */
+	{ PR_3_NO_ROOT_INODE,
+	  N_("@r not allocated.  "),
+	  PROMPT_ALLOCATE, 0 },
+
+	/* No room in lost+found */
+	{ PR_3_EXPAND_LF_DIR,
+	  N_("No room in @l @d.  "),
+	  PROMPT_EXPAND, 0 },
+
+	/* Unconnected directory inode */
+	{ PR_3_UNCONNECTED_DIR,
+	  N_("Unconnected @d @i %i (%p)\n"),
+	  PROMPT_CONNECT, 0 },
+
+	/* /lost+found not found */
+	{ PR_3_NO_LF_DIR,
+	  N_("/@l not found.  "),
+	  PROMPT_CREATE, PR_PREEN_OK },
+
+	/* .. entry is incorrect */
+	{ PR_3_BAD_DOT_DOT,
+	  N_("'..' in %Q (%i) is %P (%j), @s %q (%d).\n"),
+	  PROMPT_FIX, 0 },
+
+	/* Bad or non-existent /lost+found.  Cannot reconnect */
+	{ PR_3_NO_LPF,
+	  N_("Bad or non-existent /@l.  Cannot reconnect.\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Could not expand /lost+found */
+	{ PR_3_CANT_EXPAND_LPF,
+	  N_("Could not expand /@l: %m\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Could not reconnect inode */
+	{ PR_3_CANT_RECONNECT,
+	  N_("Could not reconnect %i: %m\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error while trying to find /lost+found */
+	{ PR_3_ERR_FIND_LPF,
+	  N_("Error while trying to find /@l: %m\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error in ext2fs_new_block while creating /lost+found */
+	{ PR_3_ERR_LPF_NEW_BLOCK,
+	  N_("ext2fs_new_@b: %m while trying to create /@l @d\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error in ext2fs_new_inode while creating /lost+found */
+	{ PR_3_ERR_LPF_NEW_INODE,
+	  N_("ext2fs_new_@i: %m while trying to create /@l @d\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error in ext2fs_new_dir_block while creating /lost+found */
+	{ PR_3_ERR_LPF_NEW_DIR_BLOCK,
+	  N_("ext2fs_new_dir_@b: %m while creating new @d @b\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error while writing directory block for /lost+found */
+	{ PR_3_ERR_LPF_WRITE_BLOCK,
+	  N_("ext2fs_write_dir_@b: %m while writing the @d @b for /@l\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error while adjusting inode count */
+	{ PR_3_ADJUST_INODE,
+	  N_("Error while adjusting @i count on @i %i\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Couldn't fix parent directory -- error */
+	{ PR_3_FIX_PARENT_ERR,
+	  N_("Couldn't fix parent of @i %i: %m\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Couldn't fix parent directory -- couldn't find it */
+	{ PR_3_FIX_PARENT_NOFIND,
+	  N_("Couldn't fix parent of @i %i: Couldn't find parent @d @e\n\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Error allocating inode bitmap */
+	{ PR_3_ALLOCATE_IBITMAP_ERROR,
+	  N_("@A @i @B (%N): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error creating root directory */
+	{ PR_3_CREATE_ROOT_ERROR,
+	  N_("Error creating root @d (%s): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error creating lost and found directory */
+	{ PR_3_CREATE_LPF_ERROR,
+	  N_("Error creating /@l @d (%s): %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Root inode is not directory; aborting */
+	{ PR_3_ROOT_NOT_DIR_ABORT,
+	  N_("@r is not a @d; aborting.\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Cannot proceed without a root inode. */
+	{ PR_3_NO_ROOT_INODE_ABORT,
+	  N_("can't proceed without a @r.\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Internal error: couldn't find dir_info */
+	{ PR_3_NO_DIRINFO,
+	  N_("Internal error: cannot find dir_info for %i.\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Lost+found not a directory */
+	{ PR_3_LPF_NOTDIR,
+	  N_("/@l is not a @d (ino=%i)\n"),
+	  PROMPT_UNLINK, 0 },
+
+	/* Pass 3A Directory Optimization       */
+
+	/* Pass 3A: Optimizing directories */
+	{ PR_3A_PASS_HEADER,
+	  N_("Pass 3A: Optimizing directories\n"),
+	  PROMPT_NONE, PR_PREEN_NOMSG },
+
+	/* Error iterating over directories */
+	{ PR_3A_OPTIMIZE_ITER,
+	  N_("Failed to create dirs_to_hash iterator: %m"),
+	  PROMPT_NONE, 0 },
+
+	/* Error rehash directory */
+	{ PR_3A_OPTIMIZE_DIR_ERR,
+	  N_("Failed to optimize directory %q (%d): %m"),
+	  PROMPT_NONE, 0 },
+
+	/* Rehashing dir header */
+	{ PR_3A_OPTIMIZE_DIR_HEADER,
+	  N_("Optimizing directories: "),
+	  PROMPT_NONE, PR_MSG_ONLY },
+
+	/* Rehashing directory %d */
+	{ PR_3A_OPTIMIZE_DIR,
+	  " %d",
+	  PROMPT_NONE, PR_LATCH_OPTIMIZE_DIR | PR_PREEN_NOHDR},
+
+	/* Rehashing dir end */
+	{ PR_3A_OPTIMIZE_DIR_END,
+	  "\n",
+	  PROMPT_NONE, PR_PREEN_NOHDR },
+
+	/* Pass 4 errors */
+
+	/* Pass 4: Checking reference counts */
+	{ PR_4_PASS_HEADER,
+	  N_("Pass 4: Checking reference counts\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Unattached zero-length inode */
+	{ PR_4_ZERO_LEN_INODE,
+	  N_("@u @z @i %i.  "),
+	  PROMPT_CLEAR, PR_PREEN_OK|PR_NO_OK },
+
+	/* Unattached inode */
+	{ PR_4_UNATTACHED_INODE,
+	  N_("@u @i %i\n"),
+	  PROMPT_CONNECT, 0 },
+
+	/* Inode ref count wrong */
+	{ PR_4_BAD_REF_COUNT,
+	  N_("@i %i ref count is %Il, @s %N.  "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	{ PR_4_INCONSISTENT_COUNT,
+	  N_("WARNING: PROGRAMMING BUG IN E2FSCK!\n"
+	  "\tOR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.\n"
+	  "@i_link_info[%i] is %N, @i.i_links_count is %Il.  "
+	  "They @s the same!\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Pass 5 errors */
+
+	/* Pass 5: Checking group summary information */
+	{ PR_5_PASS_HEADER,
+	  N_("Pass 5: Checking @g summary information\n"),
+	  PROMPT_NONE, 0 },
+
+	/* Padding at end of inode bitmap is not set. */
+	{ PR_5_INODE_BMAP_PADDING,
+	  N_("Padding at end of @i @B is not set. "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Padding at end of block bitmap is not set. */
+	{ PR_5_BLOCK_BMAP_PADDING,
+	  N_("Padding at end of @b @B is not set. "),
+	  PROMPT_FIX, PR_PREEN_OK },
+
+	/* Block bitmap differences header */
+	{ PR_5_BLOCK_BITMAP_HEADER,
+	  N_("@b @B differences: "),
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG},
+
+	/* Block not used, but marked in bitmap */
+	{ PR_5_BLOCK_UNUSED,
+	  " -%b",
+	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block used, but not marked used in bitmap */
+	{ PR_5_BLOCK_USED,
+	  " +%b",
+	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block bitmap differences end */
+	{ PR_5_BLOCK_BITMAP_END,
+	  "\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode bitmap differences header */
+	{ PR_5_INODE_BITMAP_HEADER,
+	  N_("@i @B differences: "),
+	  PROMPT_NONE, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode not used, but marked in bitmap */
+	{ PR_5_INODE_UNUSED,
+	  " -%i",
+	  PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode used, but not marked used in bitmap */
+	{ PR_5_INODE_USED,
+	  " +%i",
+	  PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode bitmap differences end */
+	{ PR_5_INODE_BITMAP_END,
+	  "\n",
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free inodes count for group wrong */
+	{ PR_5_FREE_INODE_COUNT_GROUP,
+	  N_("Free @is count wrong for @g #%g (%i, counted=%j).\n"),
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Directories count for group wrong */
+	{ PR_5_FREE_DIR_COUNT_GROUP,
+	  N_("Directories count wrong for @g #%g (%i, counted=%j).\n"),
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free inodes count wrong */
+	{ PR_5_FREE_INODE_COUNT,
+	  N_("Free @is count wrong (%i, counted=%j).\n"),
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free blocks count for group wrong */
+	{ PR_5_FREE_BLOCK_COUNT_GROUP,
+	  N_("Free @bs count wrong for @g #%g (%b, counted=%c).\n"),
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Free blocks count wrong */
+	{ PR_5_FREE_BLOCK_COUNT,
+	  N_("Free @bs count wrong (%b, counted=%c).\n"),
+	  PROMPT_FIX, PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Programming error: bitmap endpoints don't match */
+	{ PR_5_BMAP_ENDPOINTS,
+	  N_("PROGRAMMING ERROR: @f (#%N) @B endpoints (%b, %c) don't "
+	  "match calculated @B endpoints (%i, %j)\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Internal error: fudging end of bitmap */
+	{ PR_5_FUDGE_BITMAP_ERROR,
+	  N_("Internal error: fudging end of bitmap (%N)\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error copying in replacement inode bitmap */
+	{ PR_5_COPY_IBITMAP_ERROR,
+	  N_("Error copying in replacement @i @B: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Error copying in replacement block bitmap */
+	{ PR_5_COPY_BBITMAP_ERROR,
+	  N_("Error copying in replacement @b @B: %m\n"),
+	  PROMPT_NONE, PR_FATAL },
+
+	/* Block range not used, but marked in bitmap */
+	{ PR_5_BLOCK_RANGE_UNUSED,
+	  " -(%b--%c)",
+	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Block range used, but not marked used in bitmap */
+	{ PR_5_BLOCK_RANGE_USED,
+	  " +(%b--%c)",
+	  PROMPT_NONE, PR_LATCH_BBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode range not used, but marked in bitmap */
+	{ PR_5_INODE_RANGE_UNUSED,
+	  " -(%i--%j)",
+	  PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	/* Inode range used, but not marked used in bitmap */
+	{ PR_5_INODE_RANGE_USED,
+	  " +(%i--%j)",
+	  PROMPT_NONE, PR_LATCH_IBITMAP | PR_PREEN_OK | PR_PREEN_NOMSG },
+
+	{ 0 }
+};
+
+/*
+ * This is the latch flags register.  It allows several problems to be
+ * "latched" together.  This means that the user has to answer but one
+ * question for the set of problems, and all of the associated
+ * problems will be either fixed or not fixed.
+ */
+static struct latch_descr pr_latch_info[] = {
+	{ PR_LATCH_BLOCK, PR_1_INODE_BLOCK_LATCH, 0 },
+	{ PR_LATCH_BBLOCK, PR_1_INODE_BBLOCK_LATCH, 0 },
+	{ PR_LATCH_IBITMAP, PR_5_INODE_BITMAP_HEADER, PR_5_INODE_BITMAP_END },
+	{ PR_LATCH_BBITMAP, PR_5_BLOCK_BITMAP_HEADER, PR_5_BLOCK_BITMAP_END },
+	{ PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
+	{ PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
+	{ PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
+	{ PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
+	{ PR_LATCH_OPTIMIZE_DIR, PR_3A_OPTIMIZE_DIR_HEADER, PR_3A_OPTIMIZE_DIR_END },
+	{ -1, 0, 0 },
+};
+
+static const struct e2fsck_problem *find_problem(problem_t code)
+{
+	int     i;
+
+	for (i=0; problem_table[i].e2p_code; i++) {
+		if (problem_table[i].e2p_code == code)
+			return &problem_table[i];
+	}
+	return 0;
+}
+
+static struct latch_descr *find_latch(int code)
+{
+	int     i;
+
+	for (i=0; pr_latch_info[i].latch_code >= 0; i++) {
+		if (pr_latch_info[i].latch_code == code)
+			return &pr_latch_info[i];
+	}
+	return 0;
+}
+
+int end_problem_latch(e2fsck_t ctx, int mask)
+{
+	struct latch_descr *ldesc;
+	struct problem_context pctx;
+	int answer = -1;
+
+	ldesc = find_latch(mask);
+	if (ldesc->end_message && (ldesc->flags & PRL_LATCHED)) {
+		clear_problem_context(&pctx);
+		answer = fix_problem(ctx, ldesc->end_message, &pctx);
+	}
+	ldesc->flags &= ~(PRL_VARIABLE);
+	return answer;
+}
+
+int set_latch_flags(int mask, int setflags, int clearflags)
+{
+	struct latch_descr *ldesc;
+
+	ldesc = find_latch(mask);
+	if (!ldesc)
+		return -1;
+	ldesc->flags |= setflags;
+	ldesc->flags &= ~clearflags;
+	return 0;
+}
+
+void clear_problem_context(struct problem_context *ctx)
+{
+	memset(ctx, 0, sizeof(struct problem_context));
+	ctx->blkcount = -1;
+	ctx->group = -1;
+}
+
+int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
+{
+	ext2_filsys fs = ctx->fs;
+	const struct e2fsck_problem *ptr;
+	struct latch_descr *ldesc = NULL;
+	const char *message;
+	int             def_yn, answer, ans;
+	int             print_answer = 0;
+	int             suppress = 0;
+
+	ptr = find_problem(code);
+	if (!ptr) {
+		printf(_("Unhandled error code (0x%x)!\n"), code);
+		return 0;
+	}
+	def_yn = 1;
+	if ((ptr->flags & PR_NO_DEFAULT) ||
+	    ((ptr->flags & PR_PREEN_NO) && (ctx->options & E2F_OPT_PREEN)) ||
+	    (ctx->options & E2F_OPT_NO))
+		def_yn= 0;
+
+	/*
+	 * Do special latch processing.  This is where we ask the
+	 * latch question, if it exists
+	 */
+	if (ptr->flags & PR_LATCH_MASK) {
+		ldesc = find_latch(ptr->flags & PR_LATCH_MASK);
+		if (ldesc->question && !(ldesc->flags & PRL_LATCHED)) {
+			ans = fix_problem(ctx, ldesc->question, pctx);
+			if (ans == 1)
+				ldesc->flags |= PRL_YES;
+			if (ans == 0)
+				ldesc->flags |= PRL_NO;
+			ldesc->flags |= PRL_LATCHED;
+		}
+		if (ldesc->flags & PRL_SUPPRESS)
+			suppress++;
+	}
+	if ((ptr->flags & PR_PREEN_NOMSG) &&
+	    (ctx->options & E2F_OPT_PREEN))
+		suppress++;
+	if ((ptr->flags & PR_NO_NOMSG) &&
+	    (ctx->options & E2F_OPT_NO))
+		suppress++;
+	if (!suppress) {
+		message = ptr->e2p_description;
+		if ((ctx->options & E2F_OPT_PREEN) &&
+		    !(ptr->flags & PR_PREEN_NOHDR)) {
+			printf("%s: ", ctx->device_name ?
+			       ctx->device_name : ctx->filesystem_name);
+		}
+		if (*message)
+			print_e2fsck_message(ctx, _(message), pctx, 1);
+	}
+	if (!(ptr->flags & PR_PREEN_OK) && (ptr->prompt != PROMPT_NONE))
+		preenhalt(ctx);
+
+	if (ptr->flags & PR_FATAL)
+		bb_error_msg_and_die(0);
+
+	if (ptr->prompt == PROMPT_NONE) {
+		if (ptr->flags & PR_NOCOLLATE)
+			answer = -1;
+		else
+			answer = def_yn;
+	} else {
+		if (ctx->options & E2F_OPT_PREEN) {
+			answer = def_yn;
+			if (!(ptr->flags & PR_PREEN_NOMSG))
+				print_answer = 1;
+		} else if ((ptr->flags & PR_LATCH_MASK) &&
+			   (ldesc->flags & (PRL_YES | PRL_NO))) {
+			if (!suppress)
+				print_answer = 1;
+			if (ldesc->flags & PRL_YES)
+				answer = 1;
+			else
+				answer = 0;
+		} else
+			answer = ask(ctx, _(prompt[(int) ptr->prompt]), def_yn);
+		if (!answer && !(ptr->flags & PR_NO_OK))
+			ext2fs_unmark_valid(fs);
+
+		if (print_answer)
+			printf("%s.\n", answer ?
+			       _(preen_msg[(int) ptr->prompt]) : _("IGNORED"));
+	}
+
+	if ((ptr->prompt == PROMPT_ABORT) && answer)
+		bb_error_msg_and_die(0);
+
+	if (ptr->flags & PR_AFTER_CODE)
+		answer = fix_problem(ctx, ptr->second_code, pctx);
+
+	return answer;
+}
+
+/*
+ * linux/fs/recovery.c
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>, 1999
+ */
+
+/*
+ * Maintain information about the progress of the recovery job, so that
+ * the different passes can carry information between them.
+ */
+struct recovery_info
+{
+	tid_t           start_transaction;
+	tid_t           end_transaction;
+
+	int             nr_replays;
+	int             nr_revokes;
+	int             nr_revoke_hits;
+};
+
+enum passtype {PASS_SCAN, PASS_REVOKE, PASS_REPLAY};
+static int do_one_pass(journal_t *journal,
+				struct recovery_info *info, enum passtype pass);
+static int scan_revoke_records(journal_t *, struct buffer_head *,
+				tid_t, struct recovery_info *);
+
+/*
+ * Read a block from the journal
+ */
+
+static int jread(struct buffer_head **bhp, journal_t *journal,
+		 unsigned int offset)
+{
+	int err;
+	unsigned long blocknr;
+	struct buffer_head *bh;
+
+	*bhp = NULL;
+
+	err = journal_bmap(journal, offset, &blocknr);
+
+	if (err) {
+		printf("JBD: bad block at offset %u\n", offset);
+		return err;
+	}
+
+	bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
+	if (!bh)
+		return -ENOMEM;
+
+	if (!buffer_uptodate(bh)) {
+		/* If this is a brand new buffer, start readahead.
+		   Otherwise, we assume we are already reading it.  */
+		if (!buffer_req(bh))
+			do_readahead(journal, offset);
+		wait_on_buffer(bh);
+	}
+
+	if (!buffer_uptodate(bh)) {
+		printf("JBD: Failed to read block at offset %u\n", offset);
+		brelse(bh);
+		return -EIO;
+	}
+
+	*bhp = bh;
+	return 0;
+}
+
+
+/*
+ * Count the number of in-use tags in a journal descriptor block.
+ */
+
+static int count_tags(struct buffer_head *bh, int size)
+{
+	char *                  tagp;
+	journal_block_tag_t *   tag;
+	int                     nr = 0;
+
+	tagp = &bh->b_data[sizeof(journal_header_t)];
+
+	while ((tagp - bh->b_data + sizeof(journal_block_tag_t)) <= size) {
+		tag = (journal_block_tag_t *) tagp;
+
+		nr++;
+		tagp += sizeof(journal_block_tag_t);
+		if (!(tag->t_flags & htonl(JFS_FLAG_SAME_UUID)))
+			tagp += 16;
+
+		if (tag->t_flags & htonl(JFS_FLAG_LAST_TAG))
+			break;
+	}
+
+	return nr;
+}
+
+
+/* Make sure we wrap around the log correctly! */
+#define wrap(journal, var)					      \
+do {					                            \
+	if (var >= (journal)->j_last)                                   \
+		var -= ((journal)->j_last - (journal)->j_first);        \
+} while (0)
+
+/**
+ * int journal_recover(journal_t *journal) - recovers a on-disk journal
+ * @journal: the journal to recover
+ *
+ * The primary function for recovering the log contents when mounting a
+ * journaled device.
+ *
+ * Recovery is done in three passes.  In the first pass, we look for the
+ * end of the log.  In the second, we assemble the list of revoke
+ * blocks.  In the third and final pass, we replay any un-revoked blocks
+ * in the log.
+ */
+int journal_recover(journal_t *journal)
+{
+	int                     err;
+	journal_superblock_t *  sb;
+
+	struct recovery_info    info;
+
+	memset(&info, 0, sizeof(info));
+	sb = journal->j_superblock;
+
+	/*
+	 * The journal superblock's s_start field (the current log head)
+	 * is always zero if, and only if, the journal was cleanly
+	 * unmounted.
+	 */
+
+	if (!sb->s_start) {
+		journal->j_transaction_sequence = ntohl(sb->s_sequence) + 1;
+		return 0;
+	}
+
+	err = do_one_pass(journal, &info, PASS_SCAN);
+	if (!err)
+		err = do_one_pass(journal, &info, PASS_REVOKE);
+	if (!err)
+		err = do_one_pass(journal, &info, PASS_REPLAY);
+
+	/* Restart the log at the next transaction ID, thus invalidating
+	 * any existing commit records in the log. */
+	journal->j_transaction_sequence = ++info.end_transaction;
+
+	journal_clear_revoke(journal);
+	sync_blockdev(journal->j_fs_dev);
+	return err;
+}
+
+static int do_one_pass(journal_t *journal,
+			struct recovery_info *info, enum passtype pass)
+{
+	unsigned int            first_commit_ID, next_commit_ID;
+	unsigned long           next_log_block;
+	int                     err, success = 0;
+	journal_superblock_t *  sb;
+	journal_header_t *      tmp;
+	struct buffer_head *    bh;
+	unsigned int            sequence;
+	int                     blocktype;
+
+	/* Precompute the maximum metadata descriptors in a descriptor block */
+	int                     MAX_BLOCKS_PER_DESC;
+	MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t))
+			       / sizeof(journal_block_tag_t));
+
+	/*
+	 * First thing is to establish what we expect to find in the log
+	 * (in terms of transaction IDs), and where (in terms of log
+	 * block offsets): query the superblock.
+	 */
+
+	sb = journal->j_superblock;
+	next_commit_ID = ntohl(sb->s_sequence);
+	next_log_block = ntohl(sb->s_start);
+
+	first_commit_ID = next_commit_ID;
+	if (pass == PASS_SCAN)
+		info->start_transaction = first_commit_ID;
+
+	/*
+	 * Now we walk through the log, transaction by transaction,
+	 * making sure that each transaction has a commit block in the
+	 * expected place.  Each complete transaction gets replayed back
+	 * into the main filesystem.
+	 */
+
+	while (1) {
+		int                     flags;
+		char *                  tagp;
+		journal_block_tag_t *   tag;
+		struct buffer_head *    obh;
+		struct buffer_head *    nbh;
+
+		/* If we already know where to stop the log traversal,
+		 * check right now that we haven't gone past the end of
+		 * the log. */
+
+		if (pass != PASS_SCAN)
+			if (tid_geq(next_commit_ID, info->end_transaction))
+				break;
+
+		/* Skip over each chunk of the transaction looking
+		 * either the next descriptor block or the final commit
+		 * record. */
+
+		err = jread(&bh, journal, next_log_block);
+		if (err)
+			goto failed;
+
+		next_log_block++;
+		wrap(journal, next_log_block);
+
+		/* What kind of buffer is it?
+		 *
+		 * If it is a descriptor block, check that it has the
+		 * expected sequence number.  Otherwise, we're all done
+		 * here. */
+
+		tmp = (journal_header_t *)bh->b_data;
+
+		if (tmp->h_magic != htonl(JFS_MAGIC_NUMBER)) {
+			brelse(bh);
+			break;
+		}
+
+		blocktype = ntohl(tmp->h_blocktype);
+		sequence = ntohl(tmp->h_sequence);
+
+		if (sequence != next_commit_ID) {
+			brelse(bh);
+			break;
+		}
+
+		/* OK, we have a valid descriptor block which matches
+		 * all of the sequence number checks.  What are we going
+		 * to do with it?  That depends on the pass... */
+
+		switch (blocktype) {
+		case JFS_DESCRIPTOR_BLOCK:
+			/* If it is a valid descriptor block, replay it
+			 * in pass REPLAY; otherwise, just skip over the
+			 * blocks it describes. */
+			if (pass != PASS_REPLAY) {
+				next_log_block +=
+					count_tags(bh, journal->j_blocksize);
+				wrap(journal, next_log_block);
+				brelse(bh);
+				continue;
+			}
+
+			/* A descriptor block: we can now write all of
+			 * the data blocks.  Yay, useful work is finally
+			 * getting done here! */
+
+			tagp = &bh->b_data[sizeof(journal_header_t)];
+			while ((tagp - bh->b_data +sizeof(journal_block_tag_t))
+			       <= journal->j_blocksize) {
+				unsigned long io_block;
+
+				tag = (journal_block_tag_t *) tagp;
+				flags = ntohl(tag->t_flags);
+
+				io_block = next_log_block++;
+				wrap(journal, next_log_block);
+				err = jread(&obh, journal, io_block);
+				if (err) {
+					/* Recover what we can, but
+					 * report failure at the end. */
+					success = err;
+					printf("JBD: IO error %d recovering "
+						"block %ld in log\n",
+						err, io_block);
+				} else {
+					unsigned long blocknr;
+
+					blocknr = ntohl(tag->t_blocknr);
+
+					/* If the block has been
+					 * revoked, then we're all done
+					 * here. */
+					if (journal_test_revoke
+					    (journal, blocknr,
+					     next_commit_ID)) {
+						brelse(obh);
+						++info->nr_revoke_hits;
+						goto skip_write;
+					}
+
+					/* Find a buffer for the new
+					 * data being restored */
+					nbh = getblk(journal->j_fs_dev,
+						       blocknr,
+						     journal->j_blocksize);
+					if (nbh == NULL) {
+						printf("JBD: Out of memory "
+						       "during recovery.\n");
+						err = -ENOMEM;
+						brelse(bh);
+						brelse(obh);
+						goto failed;
+					}
+
+					lock_buffer(nbh);
+					memcpy(nbh->b_data, obh->b_data,
+							journal->j_blocksize);
+					if (flags & JFS_FLAG_ESCAPE) {
+						*((unsigned int *)bh->b_data) =
+							htonl(JFS_MAGIC_NUMBER);
+					}
+
+					mark_buffer_uptodate(nbh, 1);
+					mark_buffer_dirty(nbh);
+					++info->nr_replays;
+					/* ll_rw_block(WRITE, 1, &nbh); */
+					unlock_buffer(nbh);
+					brelse(obh);
+					brelse(nbh);
+				}
+
+			skip_write:
+				tagp += sizeof(journal_block_tag_t);
+				if (!(flags & JFS_FLAG_SAME_UUID))
+					tagp += 16;
+
+				if (flags & JFS_FLAG_LAST_TAG)
+					break;
+			}
+
+			brelse(bh);
+			continue;
+
+		case JFS_COMMIT_BLOCK:
+			/* Found an expected commit block: not much to
+			 * do other than move on to the next sequence
+			 * number. */
+			brelse(bh);
+			next_commit_ID++;
+			continue;
+
+		case JFS_REVOKE_BLOCK:
+			/* If we aren't in the REVOKE pass, then we can
+			 * just skip over this block. */
+			if (pass != PASS_REVOKE) {
+				brelse(bh);
+				continue;
+			}
+
+			err = scan_revoke_records(journal, bh,
+						  next_commit_ID, info);
+			brelse(bh);
+			if (err)
+				goto failed;
+			continue;
+
+		default:
+			goto done;
+		}
+	}
+
+ done:
+	/*
+	 * We broke out of the log scan loop: either we came to the
+	 * known end of the log or we found an unexpected block in the
+	 * log.  If the latter happened, then we know that the "current"
+	 * transaction marks the end of the valid log.
+	 */
+
+	if (pass == PASS_SCAN)
+		info->end_transaction = next_commit_ID;
+	else {
+		/* It's really bad news if different passes end up at
+		 * different places (but possible due to IO errors). */
+		if (info->end_transaction != next_commit_ID) {
+			printf("JBD: recovery pass %d ended at "
+				"transaction %u, expected %u\n",
+				pass, next_commit_ID, info->end_transaction);
+			if (!success)
+				success = -EIO;
+		}
+	}
+
+	return success;
+
+ failed:
+	return err;
+}
+
+
+/* Scan a revoke record, marking all blocks mentioned as revoked. */
+
+static int scan_revoke_records(journal_t *journal, struct buffer_head *bh,
+			       tid_t sequence, struct recovery_info *info)
+{
+	journal_revoke_header_t *header;
+	int offset, max;
+
+	header = (journal_revoke_header_t *) bh->b_data;
+	offset = sizeof(journal_revoke_header_t);
+	max = ntohl(header->r_count);
+
+	while (offset < max) {
+		unsigned long blocknr;
+		int err;
+
+		blocknr = ntohl(* ((unsigned int *) (bh->b_data+offset)));
+		offset += 4;
+		err = journal_set_revoke(journal, blocknr, sequence);
+		if (err)
+			return err;
+		++info->nr_revokes;
+	}
+	return 0;
+}
+
+
+/*
+ * rehash.c --- rebuild hash tree directories
+ *
+ * This algorithm is designed for simplicity of implementation and to
+ * pack the directory as much as possible.  It however requires twice
+ * as much memory as the size of the directory.  The maximum size
+ * directory supported using a 4k blocksize is roughly a gigabyte, and
+ * so there may very well be problems with machines that don't have
+ * virtual memory, and obscenely large directories.
+ *
+ * An alternate algorithm which is much more disk intensive could be
+ * written, and probably will need to be written in the future.  The
+ * design goals of such an algorithm are: (a) use (roughly) constant
+ * amounts of memory, no matter how large the directory, (b) the
+ * directory must be safe at all times, even if e2fsck is interrupted
+ * in the middle, (c) we must use minimal amounts of extra disk
+ * blocks.  This pretty much requires an incremental approach, where
+ * we are reading from one part of the directory, and inserting into
+ * the front half.  So the algorithm will have to keep track of a
+ * moving block boundary between the new tree and the old tree, and
+ * files will need to be moved from the old directory and inserted
+ * into the new tree.  If the new directory requires space which isn't
+ * yet available, blocks from the beginning part of the old directory
+ * may need to be moved to the end of the directory to make room for
+ * the new tree:
+ *
+ *    --------------------------------------------------------
+ *    |  new tree   |        | old tree                      |
+ *    --------------------------------------------------------
+ *                  ^ ptr    ^ptr
+ *                tail new   head old
+ *
+ * This is going to be a pain in the tuckus to implement, and will
+ * require a lot more disk accesses.  So I'm going to skip it for now;
+ * it's only really going to be an issue for really, really big
+ * filesystems (when we reach the level of tens of millions of files
+ * in a single directory).  It will probably be easier to simply
+ * require that e2fsck use VM first.
+ */
+
+struct fill_dir_struct {
+	char *buf;
+	struct ext2_inode *inode;
+	int err;
+	e2fsck_t ctx;
+	struct hash_entry *harray;
+	int max_array, num_array;
+	int dir_size;
+	int compress;
+	ino_t parent;
+};
+
+struct hash_entry {
+	ext2_dirhash_t  hash;
+	ext2_dirhash_t  minor_hash;
+	struct ext2_dir_entry   *dir;
+};
+
+struct out_dir {
+	int             num;
+	int             max;
+	char            *buf;
+	ext2_dirhash_t  *hashes;
+};
+
+static int fill_dir_block(ext2_filsys fs,
+			  blk_t *block_nr,
+			  e2_blkcnt_t blockcnt,
+			  blk_t ref_block FSCK_ATTR((unused)),
+			  int ref_offset FSCK_ATTR((unused)),
+			  void *priv_data)
+{
+	struct fill_dir_struct  *fd = (struct fill_dir_struct *) priv_data;
+	struct hash_entry       *new_array, *ent;
+	struct ext2_dir_entry   *dirent;
+	char                    *dir;
+	unsigned int            offset, dir_offset;
+
+	if (blockcnt < 0)
+		return 0;
+
+	offset = blockcnt * fs->blocksize;
+	if (offset + fs->blocksize > fd->inode->i_size) {
+		fd->err = EXT2_ET_DIR_CORRUPTED;
+		return BLOCK_ABORT;
+	}
+	dir = (fd->buf+offset);
+	if (HOLE_BLKADDR(*block_nr)) {
+		memset(dir, 0, fs->blocksize);
+		dirent = (struct ext2_dir_entry *) dir;
+		dirent->rec_len = fs->blocksize;
+	} else {
+		fd->err = ext2fs_read_dir_block(fs, *block_nr, dir);
+		if (fd->err)
+			return BLOCK_ABORT;
+	}
+	/* While the directory block is "hot", index it. */
+	dir_offset = 0;
+	while (dir_offset < fs->blocksize) {
+		dirent = (struct ext2_dir_entry *) (dir + dir_offset);
+		if (((dir_offset + dirent->rec_len) > fs->blocksize) ||
+		    (dirent->rec_len < 8) ||
+		    ((dirent->rec_len % 4) != 0) ||
+		    (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+			fd->err = EXT2_ET_DIR_CORRUPTED;
+			return BLOCK_ABORT;
+		}
+		dir_offset += dirent->rec_len;
+		if (dirent->inode == 0)
+			continue;
+		if (!fd->compress && ((dirent->name_len&0xFF) == 1) &&
+		    (dirent->name[0] == '.'))
+			continue;
+		if (!fd->compress && ((dirent->name_len&0xFF) == 2) &&
+		    (dirent->name[0] == '.') && (dirent->name[1] == '.')) {
+			fd->parent = dirent->inode;
+			continue;
+		}
+		if (fd->num_array >= fd->max_array) {
+			new_array = xrealloc(fd->harray,
+			    sizeof(struct hash_entry) * (fd->max_array+500));
+			fd->harray = new_array;
+			fd->max_array += 500;
+		}
+		ent = fd->harray + fd->num_array++;
+		ent->dir = dirent;
+		fd->dir_size += EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+		if (fd->compress)
+			ent->hash = ent->minor_hash = 0;
+		else {
+			fd->err = ext2fs_dirhash(fs->super->s_def_hash_version,
+						 dirent->name,
+						 dirent->name_len & 0xFF,
+						 fs->super->s_hash_seed,
+						 &ent->hash, &ent->minor_hash);
+			if (fd->err)
+				return BLOCK_ABORT;
+		}
+	}
+
+	return 0;
+}
+
+/* Used for sorting the hash entry */
+static int name_cmp(const void *a, const void *b)
+{
+	const struct hash_entry *he_a = (const struct hash_entry *) a;
+	const struct hash_entry *he_b = (const struct hash_entry *) b;
+	int     ret;
+	int     min_len;
+
+	min_len = he_a->dir->name_len;
+	if (min_len > he_b->dir->name_len)
+		min_len = he_b->dir->name_len;
+
+	ret = strncmp(he_a->dir->name, he_b->dir->name, min_len);
+	if (ret == 0) {
+		if (he_a->dir->name_len > he_b->dir->name_len)
+			ret = 1;
+		else if (he_a->dir->name_len < he_b->dir->name_len)
+			ret = -1;
+		else
+			ret = he_b->dir->inode - he_a->dir->inode;
+	}
+	return ret;
+}
+
+/* Used for sorting the hash entry */
+static int hash_cmp(const void *a, const void *b)
+{
+	const struct hash_entry *he_a = (const struct hash_entry *) a;
+	const struct hash_entry *he_b = (const struct hash_entry *) b;
+	int     ret;
+
+	if (he_a->hash > he_b->hash)
+		ret = 1;
+	else if (he_a->hash < he_b->hash)
+		ret = -1;
+	else {
+		if (he_a->minor_hash > he_b->minor_hash)
+			ret = 1;
+		else if (he_a->minor_hash < he_b->minor_hash)
+			ret = -1;
+		else
+			ret = name_cmp(a, b);
+	}
+	return ret;
+}
+
+static errcode_t alloc_size_dir(ext2_filsys fs, struct out_dir *outdir,
+				int blocks)
+{
+	void                    *new_mem;
+
+	if (outdir->max) {
+		new_mem = xrealloc(outdir->buf, blocks * fs->blocksize);
+		outdir->buf = new_mem;
+		new_mem = xrealloc(outdir->hashes,
+				  blocks * sizeof(ext2_dirhash_t));
+		outdir->hashes = new_mem;
+	} else {
+		outdir->buf = xmalloc(blocks * fs->blocksize);
+		outdir->hashes = xmalloc(blocks * sizeof(ext2_dirhash_t));
+		outdir->num = 0;
+	}
+	outdir->max = blocks;
+	return 0;
+}
+
+static void free_out_dir(struct out_dir *outdir)
+{
+	free(outdir->buf);
+	free(outdir->hashes);
+	outdir->max = 0;
+	outdir->num =0;
+}
+
+static errcode_t get_next_block(ext2_filsys fs, struct out_dir *outdir,
+			 char ** ret)
+{
+	errcode_t       retval;
+
+	if (outdir->num >= outdir->max) {
+		retval = alloc_size_dir(fs, outdir, outdir->max + 50);
+		if (retval)
+			return retval;
+	}
+	*ret = outdir->buf + (outdir->num++ * fs->blocksize);
+	memset(*ret, 0, fs->blocksize);
+	return 0;
+}
+
+/*
+ * This function is used to make a unique filename.  We do this by
+ * appending ~0, and then incrementing the number.  However, we cannot
+ * expand the length of the filename beyond the padding available in
+ * the directory entry.
+ */
+static void mutate_name(char *str, __u16 *len)
+{
+	int     i;
+	__u16   l = *len & 0xFF, h = *len & 0xff00;
+
+	/*
+	 * First check to see if it looks the name has been mutated
+	 * already
+	 */
+	for (i = l-1; i > 0; i--) {
+		if (!isdigit(str[i]))
+			break;
+	}
+	if ((i == l-1) || (str[i] != '~')) {
+		if (((l-1) & 3) < 2)
+			l += 2;
+		else
+			l = (l+3) & ~3;
+		str[l-2] = '~';
+		str[l-1] = '0';
+		*len = l | h;
+		return;
+	}
+	for (i = l-1; i >= 0; i--) {
+		if (isdigit(str[i])) {
+			if (str[i] == '9')
+				str[i] = '0';
+			else {
+				str[i]++;
+				return;
+			}
+			continue;
+		}
+		if (i == 1) {
+			if (str[0] == 'z')
+				str[0] = 'A';
+			else if (str[0] == 'Z') {
+				str[0] = '~';
+				str[1] = '0';
+			} else
+				str[0]++;
+		} else if (i > 0) {
+			str[i] = '1';
+			str[i-1] = '~';
+		} else {
+			if (str[0] == '~')
+				str[0] = 'a';
+			else
+				str[0]++;
+		}
+		break;
+	}
+}
+
+static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
+				    ext2_ino_t ino,
+				    struct fill_dir_struct *fd)
+{
+	struct problem_context  pctx;
+	struct hash_entry       *ent, *prev;
+	int                     i, j;
+	int                     fixed = 0;
+	char                    new_name[256];
+	__u16                   new_len;
+
+	clear_problem_context(&pctx);
+	pctx.ino = ino;
+
+	for (i=1; i < fd->num_array; i++) {
+		ent = fd->harray + i;
+		prev = ent - 1;
+		if (!ent->dir->inode ||
+		    ((ent->dir->name_len & 0xFF) !=
+		     (prev->dir->name_len & 0xFF)) ||
+		    (strncmp(ent->dir->name, prev->dir->name,
+			     ent->dir->name_len & 0xFF)))
+			continue;
+		pctx.dirent = ent->dir;
+		if ((ent->dir->inode == prev->dir->inode) &&
+		    fix_problem(ctx, PR_2_DUPLICATE_DIRENT, &pctx)) {
+			e2fsck_adjust_inode_count(ctx, ent->dir->inode, -1);
+			ent->dir->inode = 0;
+			fixed++;
+			continue;
+		}
+		memcpy(new_name, ent->dir->name, ent->dir->name_len & 0xFF);
+		new_len = ent->dir->name_len;
+		mutate_name(new_name, &new_len);
+		for (j=0; j < fd->num_array; j++) {
+			if ((i==j) ||
+			    ((ent->dir->name_len & 0xFF) !=
+			     (fd->harray[j].dir->name_len & 0xFF)) ||
+			    (strncmp(new_name, fd->harray[j].dir->name,
+				     new_len & 0xFF)))
+				continue;
+			mutate_name(new_name, &new_len);
+
+			j = -1;
+		}
+		new_name[new_len & 0xFF] = 0;
+		pctx.str = new_name;
+		if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
+			memcpy(ent->dir->name, new_name, new_len & 0xFF);
+			ent->dir->name_len = new_len;
+			ext2fs_dirhash(fs->super->s_def_hash_version,
+				       ent->dir->name,
+				       ent->dir->name_len & 0xFF,
+				       fs->super->s_hash_seed,
+				       &ent->hash, &ent->minor_hash);
+			fixed++;
+		}
+	}
+	return fixed;
+}
+
+
+static errcode_t copy_dir_entries(ext2_filsys fs,
+				  struct fill_dir_struct *fd,
+				  struct out_dir *outdir)
+{
+	errcode_t               retval;
+	char                    *block_start;
+	struct hash_entry       *ent;
+	struct ext2_dir_entry   *dirent;
+	int                     i, rec_len, left;
+	ext2_dirhash_t          prev_hash;
+	int                     offset;
+
+	outdir->max = 0;
+	retval = alloc_size_dir(fs, outdir,
+				(fd->dir_size / fs->blocksize) + 2);
+	if (retval)
+		return retval;
+	outdir->num = fd->compress ? 0 : 1;
+	offset = 0;
+	outdir->hashes[0] = 0;
+	prev_hash = 1;
+	if ((retval = get_next_block(fs, outdir, &block_start)))
+		return retval;
+	dirent = (struct ext2_dir_entry *) block_start;
+	left = fs->blocksize;
+	for (i=0; i < fd->num_array; i++) {
+		ent = fd->harray + i;
+		if (ent->dir->inode == 0)
+			continue;
+		rec_len = EXT2_DIR_REC_LEN(ent->dir->name_len & 0xFF);
+		if (rec_len > left) {
+			if (left)
+				dirent->rec_len += left;
+			if ((retval = get_next_block(fs, outdir,
+						      &block_start)))
+				return retval;
+			offset = 0;
+		}
+		left = fs->blocksize - offset;
+		dirent = (struct ext2_dir_entry *) (block_start + offset);
+		if (offset == 0) {
+			if (ent->hash == prev_hash)
+				outdir->hashes[outdir->num-1] = ent->hash | 1;
+			else
+				outdir->hashes[outdir->num-1] = ent->hash;
+		}
+		dirent->inode = ent->dir->inode;
+		dirent->name_len = ent->dir->name_len;
+		dirent->rec_len = rec_len;
+		memcpy(dirent->name, ent->dir->name, dirent->name_len & 0xFF);
+		offset += rec_len;
+		left -= rec_len;
+		if (left < 12) {
+			dirent->rec_len += left;
+			offset += left;
+			left = 0;
+		}
+		prev_hash = ent->hash;
+	}
+	if (left)
+		dirent->rec_len += left;
+
+	return 0;
+}
+
+
+static struct ext2_dx_root_info *set_root_node(ext2_filsys fs, char *buf,
+				    ext2_ino_t ino, ext2_ino_t parent)
+{
+	struct ext2_dir_entry           *dir;
+	struct ext2_dx_root_info        *root;
+	struct ext2_dx_countlimit       *limits;
+	int                             filetype = 0;
+
+	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
+		filetype = EXT2_FT_DIR << 8;
+
+	memset(buf, 0, fs->blocksize);
+	dir = (struct ext2_dir_entry *) buf;
+	dir->inode = ino;
+	dir->name[0] = '.';
+	dir->name_len = 1 | filetype;
+	dir->rec_len = 12;
+	dir = (struct ext2_dir_entry *) (buf + 12);
+	dir->inode = parent;
+	dir->name[0] = '.';
+	dir->name[1] = '.';
+	dir->name_len = 2 | filetype;
+	dir->rec_len = fs->blocksize - 12;
+
+	root = (struct ext2_dx_root_info *) (buf+24);
+	root->reserved_zero = 0;
+	root->hash_version = fs->super->s_def_hash_version;
+	root->info_length = 8;
+	root->indirect_levels = 0;
+	root->unused_flags = 0;
+
+	limits = (struct ext2_dx_countlimit *) (buf+32);
+	limits->limit = (fs->blocksize - 32) / sizeof(struct ext2_dx_entry);
+	limits->count = 0;
+
+	return root;
+}
+
+
+static struct ext2_dx_entry *set_int_node(ext2_filsys fs, char *buf)
+{
+	struct ext2_dir_entry           *dir;
+	struct ext2_dx_countlimit       *limits;
+
+	memset(buf, 0, fs->blocksize);
+	dir = (struct ext2_dir_entry *) buf;
+	dir->inode = 0;
+	dir->rec_len = fs->blocksize;
+
+	limits = (struct ext2_dx_countlimit *) (buf+8);
+	limits->limit = (fs->blocksize - 8) / sizeof(struct ext2_dx_entry);
+	limits->count = 0;
+
+	return (struct ext2_dx_entry *) limits;
+}
+
+/*
+ * This function takes the leaf nodes which have been written in
+ * outdir, and populates the root node and any necessary interior nodes.
+ */
+static errcode_t calculate_tree(ext2_filsys fs,
+				struct out_dir *outdir,
+				ext2_ino_t ino,
+				ext2_ino_t parent)
+{
+	struct ext2_dx_root_info        *root_info;
+	struct ext2_dx_entry            *root, *dx_ent = NULL;
+	struct ext2_dx_countlimit       *root_limit, *limit;
+	errcode_t                       retval;
+	char                            * block_start;
+	int                             i, c1, c2, nblks;
+	int                             limit_offset, root_offset;
+
+	root_info = set_root_node(fs, outdir->buf, ino, parent);
+	root_offset = limit_offset = ((char *) root_info - outdir->buf) +
+		root_info->info_length;
+	root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
+	c1 = root_limit->limit;
+	nblks = outdir->num;
+
+	/* Write out the pointer blocks */
+	if (nblks-1 <= c1) {
+		/* Just write out the root block, and we're done */
+		root = (struct ext2_dx_entry *) (outdir->buf + root_offset);
+		for (i=1; i < nblks; i++) {
+			root->block = ext2fs_cpu_to_le32(i);
+			if (i != 1)
+				root->hash =
+					ext2fs_cpu_to_le32(outdir->hashes[i]);
+			root++;
+			c1--;
+		}
+	} else {
+		c2 = 0;
+		limit = 0;
+		root_info->indirect_levels = 1;
+		for (i=1; i < nblks; i++) {
+			if (c1 == 0)
+				return ENOSPC;
+			if (c2 == 0) {
+				if (limit)
+					limit->limit = limit->count =
+		ext2fs_cpu_to_le16(limit->limit);
+				root = (struct ext2_dx_entry *)
+					(outdir->buf + root_offset);
+				root->block = ext2fs_cpu_to_le32(outdir->num);
+				if (i != 1)
+					root->hash =
+			ext2fs_cpu_to_le32(outdir->hashes[i]);
+				if ((retval =  get_next_block(fs, outdir,
+							      &block_start)))
+					return retval;
+				dx_ent = set_int_node(fs, block_start);
+				limit = (struct ext2_dx_countlimit *) dx_ent;
+				c2 = limit->limit;
+				root_offset += sizeof(struct ext2_dx_entry);
+				c1--;
+			}
+			dx_ent->block = ext2fs_cpu_to_le32(i);
+			if (c2 != limit->limit)
+				dx_ent->hash =
+					ext2fs_cpu_to_le32(outdir->hashes[i]);
+			dx_ent++;
+			c2--;
+		}
+		limit->count = ext2fs_cpu_to_le16(limit->limit - c2);
+		limit->limit = ext2fs_cpu_to_le16(limit->limit);
+	}
+	root_limit = (struct ext2_dx_countlimit *) (outdir->buf + limit_offset);
+	root_limit->count = ext2fs_cpu_to_le16(root_limit->limit - c1);
+	root_limit->limit = ext2fs_cpu_to_le16(root_limit->limit);
+
+	return 0;
+}
+
+struct write_dir_struct {
+	struct out_dir *outdir;
+	errcode_t       err;
+	e2fsck_t        ctx;
+	int             cleared;
+};
+
+/*
+ * Helper function which writes out a directory block.
+ */
+static int write_dir_block(ext2_filsys fs,
+			   blk_t        *block_nr,
+			   e2_blkcnt_t blockcnt,
+			   blk_t ref_block FSCK_ATTR((unused)),
+			   int ref_offset FSCK_ATTR((unused)),
+			   void *priv_data)
+{
+	struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
+	blk_t   blk;
+	char    *dir;
+
+	if (*block_nr == 0)
+		return 0;
+	if (blockcnt >= wd->outdir->num) {
+		e2fsck_read_bitmaps(wd->ctx);
+		blk = *block_nr;
+		ext2fs_unmark_block_bitmap(wd->ctx->block_found_map, blk);
+		ext2fs_block_alloc_stats(fs, blk, -1);
+		*block_nr = 0;
+		wd->cleared++;
+		return BLOCK_CHANGED;
+	}
+	if (blockcnt < 0)
+		return 0;
+
+	dir = wd->outdir->buf + (blockcnt * fs->blocksize);
+	wd->err = ext2fs_write_dir_block(fs, *block_nr, dir);
+	if (wd->err)
+		return BLOCK_ABORT;
+	return 0;
+}
+
+static errcode_t write_directory(e2fsck_t ctx, ext2_filsys fs,
+				 struct out_dir *outdir,
+				 ext2_ino_t ino, int compress)
+{
+	struct write_dir_struct wd;
+	errcode_t       retval;
+	struct ext2_inode       inode;
+
+	retval = e2fsck_expand_directory(ctx, ino, -1, outdir->num);
+	if (retval)
+		return retval;
+
+	wd.outdir = outdir;
+	wd.err = 0;
+	wd.ctx = ctx;
+	wd.cleared = 0;
+
+	retval = ext2fs_block_iterate2(fs, ino, 0, 0,
+				       write_dir_block, &wd);
+	if (retval)
+		return retval;
+	if (wd.err)
+		return wd.err;
+
+	e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+	if (compress)
+		inode.i_flags &= ~EXT2_INDEX_FL;
+	else
+		inode.i_flags |= EXT2_INDEX_FL;
+	inode.i_size = outdir->num * fs->blocksize;
+	inode.i_blocks -= (fs->blocksize / 512) * wd.cleared;
+	e2fsck_write_inode(ctx, ino, &inode, "rehash_dir");
+
+	return 0;
+}
+
+static errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
+{
+	ext2_filsys             fs = ctx->fs;
+	errcode_t               retval;
+	struct ext2_inode       inode;
+	char                    *dir_buf = NULL;
+	struct fill_dir_struct  fd;
+	struct out_dir          outdir;
+
+	outdir.max = outdir.num = 0;
+	outdir.buf = 0;
+	outdir.hashes = 0;
+	e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+
+	retval = ENOMEM;
+	fd.harray = 0;
+	dir_buf = xmalloc(inode.i_size);
+
+	fd.max_array = inode.i_size / 32;
+	fd.num_array = 0;
+	fd.harray = xmalloc(fd.max_array * sizeof(struct hash_entry));
+
+	fd.ctx = ctx;
+	fd.buf = dir_buf;
+	fd.inode = &inode;
+	fd.err = 0;
+	fd.dir_size = 0;
+	fd.compress = 0;
+	if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
+	    (inode.i_size / fs->blocksize) < 2)
+		fd.compress = 1;
+	fd.parent = 0;
+
+	/* Read in the entire directory into memory */
+	retval = ext2fs_block_iterate2(fs, ino, 0, 0,
+				       fill_dir_block, &fd);
+	if (fd.err) {
+		retval = fd.err;
+		goto errout;
+	}
+
+	/* Sort the list */
+resort:
+	if (fd.compress)
+		qsort(fd.harray+2, fd.num_array-2,
+		      sizeof(struct hash_entry), name_cmp);
+	else
+		qsort(fd.harray, fd.num_array,
+		      sizeof(struct hash_entry), hash_cmp);
+
+	/*
+	 * Look for duplicates
+	 */
+	if (duplicate_search_and_fix(ctx, fs, ino, &fd))
+		goto resort;
+
+	if (ctx->options & E2F_OPT_NO) {
+		retval = 0;
+		goto errout;
+	}
+
+	/*
+	 * Copy the directory entries.  In a htree directory these
+	 * will become the leaf nodes.
+	 */
+	retval = copy_dir_entries(fs, &fd, &outdir);
+	if (retval)
+		goto errout;
+
+	free(dir_buf); dir_buf = 0;
+
+	if (!fd.compress) {
+		/* Calculate the interior nodes */
+		retval = calculate_tree(fs, &outdir, ino, fd.parent);
+		if (retval)
+			goto errout;
+	}
+
+	retval = write_directory(ctx, fs, &outdir, ino, fd.compress);
+
+errout:
+	free(dir_buf);
+	free(fd.harray);
+
+	free_out_dir(&outdir);
+	return retval;
+}
+
+void e2fsck_rehash_directories(e2fsck_t ctx)
+{
+	struct problem_context  pctx;
+	struct dir_info         *dir;
+	ext2_u32_iterate        iter;
+	ext2_ino_t              ino;
+	errcode_t               retval;
+	int                     i, cur, max, all_dirs, dir_index, first = 1;
+
+	all_dirs = ctx->options & E2F_OPT_COMPRESS_DIRS;
+
+	if (!ctx->dirs_to_hash && !all_dirs)
+		return;
+
+	e2fsck_get_lost_and_found(ctx, 0);
+
+	clear_problem_context(&pctx);
+
+	dir_index = ctx->fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX;
+	cur = 0;
+	if (all_dirs) {
+		i = 0;
+		max = e2fsck_get_num_dirinfo(ctx);
+	} else {
+		retval = ext2fs_u32_list_iterate_begin(ctx->dirs_to_hash,
+						       &iter);
+		if (retval) {
+			pctx.errcode = retval;
+			fix_problem(ctx, PR_3A_OPTIMIZE_ITER, &pctx);
+			return;
+		}
+		max = ext2fs_u32_list_count(ctx->dirs_to_hash);
+	}
+	while (1) {
+		if (all_dirs) {
+			if ((dir = e2fsck_dir_info_iter(ctx, &i)) == 0)
+				break;
+			ino = dir->ino;
+		} else {
+			if (!ext2fs_u32_list_iterate(iter, &ino))
+				break;
+		}
+		if (ino == ctx->lost_and_found)
+			continue;
+		pctx.dir = ino;
+		if (first) {
+			fix_problem(ctx, PR_3A_PASS_HEADER, &pctx);
+			first = 0;
+		}
+		pctx.errcode = e2fsck_rehash_dir(ctx, ino);
+		if (pctx.errcode) {
+			end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
+			fix_problem(ctx, PR_3A_OPTIMIZE_DIR_ERR, &pctx);
+		}
+		if (ctx->progress && !ctx->progress_fd)
+			e2fsck_simple_progress(ctx, "Rebuilding directory",
+			       100.0 * (float) (++cur) / (float) max, ino);
+	}
+	end_problem_latch(ctx, PR_LATCH_OPTIMIZE_DIR);
+	if (!all_dirs)
+		ext2fs_u32_list_iterate_end(iter);
+
+	ext2fs_u32_list_free(ctx->dirs_to_hash);
+	ctx->dirs_to_hash = 0;
+}
+
+/*
+ * linux/fs/revoke.c
+ *
+ * Journal revoke routines for the generic filesystem journaling code;
+ * part of the ext2fs journaling system.
+ *
+ * Revoke is the mechanism used to prevent old log records for deleted
+ * metadata from being replayed on top of newer data using the same
+ * blocks.  The revoke mechanism is used in two separate places:
+ *
+ * + Commit: during commit we write the entire list of the current
+ *   transaction's revoked blocks to the journal
+ *
+ * + Recovery: during recovery we record the transaction ID of all
+ *   revoked blocks.  If there are multiple revoke records in the log
+ *   for a single block, only the last one counts, and if there is a log
+ *   entry for a block beyond the last revoke, then that log entry still
+ *   gets replayed.
+ *
+ * We can get interactions between revokes and new log data within a
+ * single transaction:
+ *
+ * Block is revoked and then journaled:
+ *   The desired end result is the journaling of the new block, so we
+ *   cancel the revoke before the transaction commits.
+ *
+ * Block is journaled and then revoked:
+ *   The revoke must take precedence over the write of the block, so we
+ *   need either to cancel the journal entry or to write the revoke
+ *   later in the log than the log block.  In this case, we choose the
+ *   latter: journaling a block cancels any revoke record for that block
+ *   in the current transaction, so any revoke for that block in the
+ *   transaction must have happened after the block was journaled and so
+ *   the revoke must take precedence.
+ *
+ * Block is revoked and then written as data:
+ *   The data write is allowed to succeed, but the revoke is _not_
+ *   cancelled.  We still need to prevent old log records from
+ *   overwriting the new data.  We don't even need to clear the revoke
+ *   bit here.
+ *
+ * Revoke information on buffers is a tri-state value:
+ *
+ * RevokeValid clear:   no cached revoke status, need to look it up
+ * RevokeValid set, Revoked clear:
+ *                      buffer has not been revoked, and cancel_revoke
+ *                      need do nothing.
+ * RevokeValid set, Revoked set:
+ *                      buffer has been revoked.
+ */
+
+static kmem_cache_t *revoke_record_cache;
+static kmem_cache_t *revoke_table_cache;
+
+/* Each revoke record represents one single revoked block.  During
+   journal replay, this involves recording the transaction ID of the
+   last transaction to revoke this block. */
+
+struct jbd_revoke_record_s
+{
+	struct list_head  hash;
+	tid_t             sequence;     /* Used for recovery only */
+	unsigned long     blocknr;
+};
+
+
+/* The revoke table is just a simple hash table of revoke records. */
+struct jbd_revoke_table_s
+{
+	/* It is conceivable that we might want a larger hash table
+	 * for recovery.  Must be a power of two. */
+	int               hash_size;
+	int               hash_shift;
+	struct list_head *hash_table;
+};
+
+
+/* Utility functions to maintain the revoke table */
+
+/* Borrowed from buffer.c: this is a tried and tested block hash function */
+static int hash(journal_t *journal, unsigned long block)
+{
+	struct jbd_revoke_table_s *table = journal->j_revoke;
+	int hash_shift = table->hash_shift;
+
+	return ((block << (hash_shift - 6)) ^
+		(block >> 13) ^
+		(block << (hash_shift - 12))) & (table->hash_size - 1);
+}
+
+static int insert_revoke_hash(journal_t *journal, unsigned long blocknr,
+			      tid_t seq)
+{
+	struct list_head *hash_list;
+	struct jbd_revoke_record_s *record;
+
+	record = kmem_cache_alloc(revoke_record_cache, GFP_NOFS);
+	if (!record)
+		goto oom;
+
+	record->sequence = seq;
+	record->blocknr = blocknr;
+	hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
+	list_add(&record->hash, hash_list);
+	return 0;
+
+oom:
+	return -ENOMEM;
+}
+
+/* Find a revoke record in the journal's hash table. */
+
+static struct jbd_revoke_record_s *find_revoke_record(journal_t *journal,
+						      unsigned long blocknr)
+{
+	struct list_head *hash_list;
+	struct jbd_revoke_record_s *record;
+
+	hash_list = &journal->j_revoke->hash_table[hash(journal, blocknr)];
+
+	record = (struct jbd_revoke_record_s *) hash_list->next;
+	while (&(record->hash) != hash_list) {
+		if (record->blocknr == blocknr)
+			return record;
+		record = (struct jbd_revoke_record_s *) record->hash.next;
+	}
+	return NULL;
+}
+
+int journal_init_revoke_caches(void)
+{
+	revoke_record_cache = do_cache_create(sizeof(struct jbd_revoke_record_s));
+	if (revoke_record_cache == 0)
+		return -ENOMEM;
+
+	revoke_table_cache = do_cache_create(sizeof(struct jbd_revoke_table_s));
+	if (revoke_table_cache == 0) {
+		do_cache_destroy(revoke_record_cache);
+		revoke_record_cache = NULL;
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+void journal_destroy_revoke_caches(void)
+{
+	do_cache_destroy(revoke_record_cache);
+	revoke_record_cache = 0;
+	do_cache_destroy(revoke_table_cache);
+	revoke_table_cache = 0;
+}
+
+/* Initialise the revoke table for a given journal to a given size. */
+
+int journal_init_revoke(journal_t *journal, int hash_size)
+{
+	int shift, tmp;
+
+	journal->j_revoke = kmem_cache_alloc(revoke_table_cache, GFP_KERNEL);
+	if (!journal->j_revoke)
+		return -ENOMEM;
+
+	/* Check that the hash_size is a power of two */
+	journal->j_revoke->hash_size = hash_size;
+
+	shift = 0;
+	tmp = hash_size;
+	while ((tmp >>= 1UL) != 0UL)
+		shift++;
+	journal->j_revoke->hash_shift = shift;
+
+	journal->j_revoke->hash_table = xmalloc(hash_size * sizeof(struct list_head));
+
+	for (tmp = 0; tmp < hash_size; tmp++)
+		INIT_LIST_HEAD(&journal->j_revoke->hash_table[tmp]);
+
+	return 0;
+}
+
+/* Destoy a journal's revoke table.  The table must already be empty! */
+
+void journal_destroy_revoke(journal_t *journal)
+{
+	struct jbd_revoke_table_s *table;
+	struct list_head *hash_list;
+	int i;
+
+	table = journal->j_revoke;
+	if (!table)
+		return;
+
+	for (i=0; i<table->hash_size; i++) {
+		hash_list = &table->hash_table[i];
+	}
+
+	free(table->hash_table);
+	free(table);
+	journal->j_revoke = NULL;
+}
+
+/*
+ * Revoke support for recovery.
+ *
+ * Recovery needs to be able to:
+ *
+ *  record all revoke records, including the tid of the latest instance
+ *  of each revoke in the journal
+ *
+ *  check whether a given block in a given transaction should be replayed
+ *  (ie. has not been revoked by a revoke record in that or a subsequent
+ *  transaction)
+ *
+ *  empty the revoke table after recovery.
+ */
+
+/*
+ * First, setting revoke records.  We create a new revoke record for
+ * every block ever revoked in the log as we scan it for recovery, and
+ * we update the existing records if we find multiple revokes for a
+ * single block.
+ */
+
+int journal_set_revoke(journal_t *journal, unsigned long blocknr,
+		       tid_t sequence)
+{
+	struct jbd_revoke_record_s *record;
+
+	record = find_revoke_record(journal, blocknr);
+	if (record) {
+		/* If we have multiple occurences, only record the
+		 * latest sequence number in the hashed record */
+		if (tid_gt(sequence, record->sequence))
+			record->sequence = sequence;
+		return 0;
+	}
+	return insert_revoke_hash(journal, blocknr, sequence);
+}
+
+/*
+ * Test revoke records.  For a given block referenced in the log, has
+ * that block been revoked?  A revoke record with a given transaction
+ * sequence number revokes all blocks in that transaction and earlier
+ * ones, but later transactions still need replayed.
+ */
+
+int journal_test_revoke(journal_t *journal, unsigned long blocknr,
+			tid_t sequence)
+{
+	struct jbd_revoke_record_s *record;
+
+	record = find_revoke_record(journal, blocknr);
+	if (!record)
+		return 0;
+	if (tid_gt(sequence, record->sequence))
+		return 0;
+	return 1;
+}
+
+/*
+ * Finally, once recovery is over, we need to clear the revoke table so
+ * that it can be reused by the running filesystem.
+ */
+
+void journal_clear_revoke(journal_t *journal)
+{
+	int i;
+	struct list_head *hash_list;
+	struct jbd_revoke_record_s *record;
+	struct jbd_revoke_table_s *revoke_var;
+
+	revoke_var = journal->j_revoke;
+
+	for (i = 0; i < revoke_var->hash_size; i++) {
+		hash_list = &revoke_var->hash_table[i];
+		while (!list_empty(hash_list)) {
+			record = (struct jbd_revoke_record_s*) hash_list->next;
+			list_del(&record->hash);
+			free(record);
+		}
+	}
+}
+
+/*
+ * e2fsck.c - superblock checks
+ */
+
+#define MIN_CHECK 1
+#define MAX_CHECK 2
+
+static void check_super_value(e2fsck_t ctx, const char *descr,
+			      unsigned long value, int flags,
+			      unsigned long min_val, unsigned long max_val)
+{
+	struct          problem_context pctx;
+
+	if (((flags & MIN_CHECK) && (value < min_val)) ||
+	    ((flags & MAX_CHECK) && (value > max_val))) {
+		clear_problem_context(&pctx);
+		pctx.num = value;
+		pctx.str = descr;
+		fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
+	}
+}
+
+/*
+ * This routine may get stubbed out in special compilations of the
+ * e2fsck code..
+ */
+#ifndef EXT2_SPECIAL_DEVICE_SIZE
+static errcode_t e2fsck_get_device_size(e2fsck_t ctx)
+{
+	return (ext2fs_get_device_size(ctx->filesystem_name,
+				       EXT2_BLOCK_SIZE(ctx->fs->super),
+				       &ctx->num_blocks));
+}
+#endif
+
+/*
+ * helper function to release an inode
+ */
+struct process_block_struct {
+	e2fsck_t        ctx;
+	char            *buf;
+	struct problem_context *pctx;
+	int             truncating;
+	int             truncate_offset;
+	e2_blkcnt_t     truncate_block;
+	int             truncated_blocks;
+	int             abort;
+	errcode_t       errcode;
+};
+
+static int release_inode_block(ext2_filsys fs, blk_t *block_nr,
+			       e2_blkcnt_t blockcnt,
+			       blk_t    ref_blk FSCK_ATTR((unused)),
+			       int      ref_offset FSCK_ATTR((unused)),
+			       void *priv_data)
+{
+	struct process_block_struct *pb;
+	e2fsck_t                ctx;
+	struct problem_context  *pctx;
+	blk_t                   blk = *block_nr;
+	int                     retval = 0;
+
+	pb = (struct process_block_struct *) priv_data;
+	ctx = pb->ctx;
+	pctx = pb->pctx;
+
+	pctx->blk = blk;
+	pctx->blkcount = blockcnt;
+
+	if (HOLE_BLKADDR(blk))
+		return 0;
+
+	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:
+		pb->abort = 1;
+		return BLOCK_ABORT;
+	}
+
+	if (!ext2fs_test_block_bitmap(fs->block_map, blk)) {
+		fix_problem(ctx, PR_0_ORPHAN_ALREADY_CLEARED_BLOCK, pctx);
+		goto return_abort;
+	}
+
+	/*
+	 * If we are deleting an orphan, then we leave the fields alone.
+	 * If we are truncating an orphan, then update the inode fields
+	 * and clean up any partial block data.
+	 */
+	if (pb->truncating) {
+		/*
+		 * We only remove indirect blocks if they are
+		 * completely empty.
+		 */
+		if (blockcnt < 0) {
+			int     i, limit;
+			blk_t   *bp;
+
+			pb->errcode = io_channel_read_blk(fs->io, blk, 1,
+							pb->buf);
+			if (pb->errcode)
+				goto return_abort;
+
+			limit = fs->blocksize >> 2;
+			for (i = 0, bp = (blk_t *) pb->buf;
+			     i < limit;  i++, bp++)
+				if (*bp)
+					return 0;
+		}
+		/*
+		 * We don't remove direct blocks until we've reached
+		 * the truncation block.
+		 */
+		if (blockcnt >= 0 && blockcnt < pb->truncate_block)
+			return 0;
+		/*
+		 * If part of the last block needs truncating, we do
+		 * it here.
+		 */
+		if ((blockcnt == pb->truncate_block) && pb->truncate_offset) {
+			pb->errcode = io_channel_read_blk(fs->io, blk, 1,
+							pb->buf);
+			if (pb->errcode)
+				goto return_abort;
+			memset(pb->buf + pb->truncate_offset, 0,
+			       fs->blocksize - pb->truncate_offset);
+			pb->errcode = io_channel_write_blk(fs->io, blk, 1,
+							 pb->buf);
+			if (pb->errcode)
+				goto return_abort;
+		}
+		pb->truncated_blocks++;
+		*block_nr = 0;
+		retval |= BLOCK_CHANGED;
+	}
+
+	ext2fs_block_alloc_stats(fs, blk, -1);
+	return retval;
+}
+
+/*
+ * This function releases an inode.  Returns 1 if an inconsistency was
+ * found.  If the inode has a link count, then it is being truncated and
+ * not deleted.
+ */
+static int release_inode_blocks(e2fsck_t ctx, ext2_ino_t ino,
+				struct ext2_inode *inode, char *block_buf,
+				struct problem_context *pctx)
+{
+	struct process_block_struct     pb;
+	ext2_filsys                     fs = ctx->fs;
+	errcode_t                       retval;
+	__u32                           count;
+
+	if (!ext2fs_inode_has_valid_blocks(inode))
+		return 0;
+
+	pb.buf = block_buf + 3 * ctx->fs->blocksize;
+	pb.ctx = ctx;
+	pb.abort = 0;
+	pb.errcode = 0;
+	pb.pctx = pctx;
+	if (inode->i_links_count) {
+		pb.truncating = 1;
+		pb.truncate_block = (e2_blkcnt_t)
+			((((long long)inode->i_size_high << 32) +
+			  inode->i_size + fs->blocksize - 1) /
+			 fs->blocksize);
+		pb.truncate_offset = inode->i_size % fs->blocksize;
+	} else {
+		pb.truncating = 0;
+		pb.truncate_block = 0;
+		pb.truncate_offset = 0;
+	}
+	pb.truncated_blocks = 0;
+	retval = ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_DEPTH_TRAVERSE,
+				      block_buf, release_inode_block, &pb);
+	if (retval) {
+		bb_error_msg(_("while calling ext2fs_block_iterate for inode %d"),
+			ino);
+		return 1;
+	}
+	if (pb.abort)
+		return 1;
+
+	/* Refresh the inode since ext2fs_block_iterate may have changed it */
+	e2fsck_read_inode(ctx, ino, inode, "release_inode_blocks");
+
+	if (pb.truncated_blocks)
+		inode->i_blocks -= pb.truncated_blocks *
+			(fs->blocksize / 512);
+
+	if (inode->i_file_acl) {
+		retval = ext2fs_adjust_ea_refcount(fs, inode->i_file_acl,
+						   block_buf, -1, &count);
+		if (retval == EXT2_ET_BAD_EA_BLOCK_NUM) {
+			retval = 0;
+			count = 1;
+		}
+		if (retval) {
+			bb_error_msg(_("while calling ext2fs_adjust_ea_refocunt for inode %d"),
+				ino);
+			return 1;
+		}
+		if (count == 0)
+			ext2fs_block_alloc_stats(fs, inode->i_file_acl, -1);
+		inode->i_file_acl = 0;
+	}
+	return 0;
+}
+
+/*
+ * This function releases all of the orphan inodes.  It returns 1 if
+ * it hit some error, and 0 on success.
+ */
+static int release_orphan_inodes(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	ext2_ino_t      ino, next_ino;
+	struct ext2_inode inode;
+	struct problem_context pctx;
+	char *block_buf;
+
+	if ((ino = fs->super->s_last_orphan) == 0)
+		return 0;
+
+	/*
+	 * Win or lose, we won't be using the head of the orphan inode
+	 * list again.
+	 */
+	fs->super->s_last_orphan = 0;
+	ext2fs_mark_super_dirty(fs);
+
+	/*
+	 * If the filesystem contains errors, don't run the orphan
+	 * list, since the orphan list can't be trusted; and we're
+	 * going to be running a full e2fsck run anyway...
+	 */
+	if (fs->super->s_state & EXT2_ERROR_FS)
+		return 0;
+
+	if ((ino < EXT2_FIRST_INODE(fs->super)) ||
+	    (ino > fs->super->s_inodes_count)) {
+		clear_problem_context(&pctx);
+		pctx.ino = ino;
+		fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_HEAD_INODE, &pctx);
+		return 1;
+	}
+
+	block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
+						    "block iterate buffer");
+	e2fsck_read_bitmaps(ctx);
+
+	while (ino) {
+		e2fsck_read_inode(ctx, ino, &inode, "release_orphan_inodes");
+		clear_problem_context(&pctx);
+		pctx.ino = ino;
+		pctx.inode = &inode;
+		pctx.str = inode.i_links_count ? _("Truncating") :
+			_("Clearing");
+
+		fix_problem(ctx, PR_0_ORPHAN_CLEAR_INODE, &pctx);
+
+		next_ino = inode.i_dtime;
+		if (next_ino &&
+		    ((next_ino < EXT2_FIRST_INODE(fs->super)) ||
+		     (next_ino > fs->super->s_inodes_count))) {
+			pctx.ino = next_ino;
+			fix_problem(ctx, PR_0_ORPHAN_ILLEGAL_INODE, &pctx);
+			goto return_abort;
+		}
+
+		if (release_inode_blocks(ctx, ino, &inode, block_buf, &pctx))
+			goto return_abort;
+
+		if (!inode.i_links_count) {
+			ext2fs_inode_alloc_stats2(fs, ino, -1,
+						  LINUX_S_ISDIR(inode.i_mode));
+			inode.i_dtime = time(NULL);
+		} else {
+			inode.i_dtime = 0;
+		}
+		e2fsck_write_inode(ctx, ino, &inode, "delete_file");
+		ino = next_ino;
+	}
+	ext2fs_free_mem(&block_buf);
+	return 0;
+ return_abort:
+	ext2fs_free_mem(&block_buf);
+	return 1;
+}
+
+/*
+ * Check the resize inode to make sure it is sane.  We check both for
+ * the case where on-line resizing is not enabled (in which case the
+ * resize inode should be cleared) as well as the case where on-line
+ * resizing is enabled.
+ */
+static void check_resize_inode(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	struct ext2_inode inode;
+	struct problem_context  pctx;
+	int             i, j, gdt_off, ind_off;
+	blk_t           blk, pblk, expect;
+	__u32           *dind_buf = NULL, *ind_buf;
+	errcode_t       retval;
+
+	clear_problem_context(&pctx);
+
+	/*
+	 * If the resize inode feature isn't set, then
+	 * s_reserved_gdt_blocks must be zero.
+	 */
+	if (!(fs->super->s_feature_compat &
+	      EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
+		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,
+					&pctx)) {
+				fs->super->s_reserved_gdt_blocks = 0;
+				ext2fs_mark_super_dirty(fs);
+			}
+		}
+	}
+
+	/* Read the resize inode */
+	pctx.ino = EXT2_RESIZE_INO;
+	retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+	if (retval) {
+		if (fs->super->s_feature_compat &
+		    EXT2_FEATURE_COMPAT_RESIZE_INODE)
+			ctx->flags |= E2F_FLAG_RESIZE_INODE;
+		return;
+	}
+
+	/*
+	 * If the resize inode feature isn't set, check to make sure
+	 * the resize inode is cleared; then we're done.
+	 */
+	if (!(fs->super->s_feature_compat &
+	      EXT2_FEATURE_COMPAT_RESIZE_INODE)) {
+		for (i=0; i < EXT2_N_BLOCKS; i++) {
+			if (inode.i_block[i])
+				break;
+		}
+		if ((i < EXT2_N_BLOCKS) &&
+		    fix_problem(ctx, PR_0_CLEAR_RESIZE_INODE, &pctx)) {
+			memset(&inode, 0, sizeof(inode));
+			e2fsck_write_inode(ctx, EXT2_RESIZE_INO, &inode,
+					   "clear_resize");
+		}
+		return;
+	}
+
+	/*
+	 * The resize inode feature is enabled; check to make sure the
+	 * only block in use is the double indirect block
+	 */
+	blk = inode.i_block[EXT2_DIND_BLOCK];
+	for (i=0; i < EXT2_N_BLOCKS; i++) {
+		if (i != EXT2_DIND_BLOCK && inode.i_block[i])
+			break;
+	}
+	if ((i < EXT2_N_BLOCKS) || !blk || !inode.i_links_count ||
+	    !(inode.i_mode & LINUX_S_IFREG) ||
+	    (blk < fs->super->s_first_data_block ||
+	     blk >= fs->super->s_blocks_count)) {
+ 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,
+					   "clear_resize");
+			ctx->flags |= E2F_FLAG_RESIZE_INODE;
+		}
+		if (!(ctx->options & E2F_OPT_READONLY)) {
+			fs->super->s_state &= ~EXT2_VALID_FS;
+			ext2fs_mark_super_dirty(fs);
+		}
+		goto cleanup;
+	}
+	dind_buf = (__u32 *) e2fsck_allocate_memory(ctx, fs->blocksize * 2,
+						    "resize dind buffer");
+	ind_buf = (__u32 *) ((char *) dind_buf + fs->blocksize);
+
+	retval = ext2fs_read_ind_block(fs, blk, dind_buf);
+	if (retval)
+		goto resize_inode_invalid;
+
+	gdt_off = fs->desc_blocks;
+	pblk = fs->super->s_first_data_block + 1 + fs->desc_blocks;
+	for (i = 0; i < fs->super->s_reserved_gdt_blocks / 4;
+	     i++, gdt_off++, pblk++) {
+		gdt_off %= fs->blocksize/4;
+		if (dind_buf[gdt_off] != pblk)
+			goto resize_inode_invalid;
+		retval = ext2fs_read_ind_block(fs, pblk, ind_buf);
+		if (retval)
+			goto resize_inode_invalid;
+		ind_off = 0;
+		for (j = 1; j < fs->group_desc_count; j++) {
+			if (!ext2fs_bg_has_super(fs, j))
+				continue;
+			expect = pblk + (j * fs->super->s_blocks_per_group);
+			if (ind_buf[ind_off] != expect)
+				goto resize_inode_invalid;
+			ind_off++;
+		}
+	}
+
+ cleanup:
+	ext2fs_free_mem(&dind_buf);
+}
+
+static void check_super_block(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	blk_t   first_block, last_block;
+	struct ext2_super_block *sb = fs->super;
+	struct ext2_group_desc *gd;
+	blk_t   blocks_per_group = fs->super->s_blocks_per_group;
+	blk_t   bpg_max;
+	int     inodes_per_block;
+	int     ipg_max;
+	int     inode_size;
+	dgrp_t  i;
+	blk_t   should_be;
+	struct problem_context  pctx;
+	__u32   free_blocks = 0, free_inodes = 0;
+
+	inodes_per_block = EXT2_INODES_PER_BLOCK(fs->super);
+	ipg_max = inodes_per_block * (blocks_per_group - 4);
+	if (ipg_max > EXT2_MAX_INODES_PER_GROUP(sb))
+		ipg_max = EXT2_MAX_INODES_PER_GROUP(sb);
+	bpg_max = 8 * EXT2_BLOCK_SIZE(sb);
+	if (bpg_max > EXT2_MAX_BLOCKS_PER_GROUP(sb))
+		bpg_max = EXT2_MAX_BLOCKS_PER_GROUP(sb);
+
+	ctx->invalid_inode_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
+		 sizeof(int) * fs->group_desc_count, "invalid_inode_bitmap");
+	ctx->invalid_block_bitmap_flag = (int *) e2fsck_allocate_memory(ctx,
+		 sizeof(int) * fs->group_desc_count, "invalid_block_bitmap");
+	ctx->invalid_inode_table_flag = (int *) e2fsck_allocate_memory(ctx,
+		sizeof(int) * fs->group_desc_count, "invalid_inode_table");
+
+	clear_problem_context(&pctx);
+
+	/*
+	 * Verify the super block constants...
+	 */
+	check_super_value(ctx, "inodes_count", sb->s_inodes_count,
+			  MIN_CHECK, 1, 0);
+	check_super_value(ctx, "blocks_count", sb->s_blocks_count,
+			  MIN_CHECK, 1, 0);
+	check_super_value(ctx, "first_data_block", sb->s_first_data_block,
+			  MAX_CHECK, 0, sb->s_blocks_count);
+	check_super_value(ctx, "log_block_size", sb->s_log_block_size,
+			  MIN_CHECK | MAX_CHECK, 0,
+			  EXT2_MAX_BLOCK_LOG_SIZE - EXT2_MIN_BLOCK_LOG_SIZE);
+	check_super_value(ctx, "log_frag_size", sb->s_log_frag_size,
+			  MIN_CHECK | MAX_CHECK, 0, sb->s_log_block_size);
+	check_super_value(ctx, "frags_per_group", sb->s_frags_per_group,
+			  MIN_CHECK | MAX_CHECK, sb->s_blocks_per_group,
+			  bpg_max);
+	check_super_value(ctx, "blocks_per_group", sb->s_blocks_per_group,
+			  MIN_CHECK | MAX_CHECK, 8, bpg_max);
+	check_super_value(ctx, "inodes_per_group", sb->s_inodes_per_group,
+			  MIN_CHECK | MAX_CHECK, inodes_per_block, ipg_max);
+	check_super_value(ctx, "r_blocks_count", sb->s_r_blocks_count,
+			  MAX_CHECK, 0, sb->s_blocks_count / 2);
+	check_super_value(ctx, "reserved_gdt_blocks",
+			  sb->s_reserved_gdt_blocks, MAX_CHECK, 0,
+			  fs->blocksize/4);
+	inode_size = EXT2_INODE_SIZE(sb);
+	check_super_value(ctx, "inode_size",
+			  inode_size, MIN_CHECK | MAX_CHECK,
+			  EXT2_GOOD_OLD_INODE_SIZE, fs->blocksize);
+	if (inode_size & (inode_size - 1)) {
+		pctx.num = inode_size;
+		pctx.str = "inode_size";
+		fix_problem(ctx, PR_0_MISC_CORRUPT_SUPER, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT; /* never get here! */
+		return;
+	}
+
+	if (!ctx->num_blocks) {
+		pctx.errcode = e2fsck_get_device_size(ctx);
+		if (pctx.errcode && pctx.errcode != EXT2_ET_UNIMPLEMENTED) {
+			fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		if ((pctx.errcode != EXT2_ET_UNIMPLEMENTED) &&
+		    (ctx->num_blocks < sb->s_blocks_count)) {
+			pctx.blk = sb->s_blocks_count;
+			pctx.blk2 = ctx->num_blocks;
+			if (fix_problem(ctx, PR_0_FS_SIZE_WRONG, &pctx)) {
+				ctx->flags |= E2F_FLAG_ABORT;
+				return;
+			}
+		}
+	}
+
+	if (sb->s_log_block_size != (__u32) sb->s_log_frag_size) {
+		pctx.blk = EXT2_BLOCK_SIZE(sb);
+		pctx.blk2 = EXT2_FRAG_SIZE(sb);
+		fix_problem(ctx, PR_0_NO_FRAGMENTS, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	should_be = sb->s_frags_per_group >>
+		(sb->s_log_block_size - sb->s_log_frag_size);
+	if (sb->s_blocks_per_group != should_be) {
+		pctx.blk = sb->s_blocks_per_group;
+		pctx.blk2 = should_be;
+		fix_problem(ctx, PR_0_BLOCKS_PER_GROUP, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	should_be = (sb->s_log_block_size == 0) ? 1 : 0;
+	if (sb->s_first_data_block != should_be) {
+		pctx.blk = sb->s_first_data_block;
+		pctx.blk2 = should_be;
+		fix_problem(ctx, PR_0_FIRST_DATA_BLOCK, &pctx);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+
+	should_be = sb->s_inodes_per_group * fs->group_desc_count;
+	if (sb->s_inodes_count != should_be) {
+		pctx.ino = sb->s_inodes_count;
+		pctx.ino2 = should_be;
+		if (fix_problem(ctx, PR_0_INODE_COUNT_WRONG, &pctx)) {
+			sb->s_inodes_count = should_be;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
+	/*
+	 * Verify the group descriptors....
+	 */
+	first_block =  sb->s_first_data_block;
+	last_block = first_block + blocks_per_group;
+
+	for (i = 0, gd=fs->group_desc; i < fs->group_desc_count; i++, gd++) {
+		pctx.group = i;
+
+		if (i == fs->group_desc_count - 1)
+			last_block = sb->s_blocks_count;
+		if ((gd->bg_block_bitmap < first_block) ||
+		    (gd->bg_block_bitmap >= last_block)) {
+			pctx.blk = gd->bg_block_bitmap;
+			if (fix_problem(ctx, PR_0_BB_NOT_GROUP, &pctx))
+				gd->bg_block_bitmap = 0;
+		}
+		if (gd->bg_block_bitmap == 0) {
+			ctx->invalid_block_bitmap_flag[i]++;
+			ctx->invalid_bitmaps++;
+		}
+		if ((gd->bg_inode_bitmap < first_block) ||
+		    (gd->bg_inode_bitmap >= last_block)) {
+			pctx.blk = gd->bg_inode_bitmap;
+			if (fix_problem(ctx, PR_0_IB_NOT_GROUP, &pctx))
+				gd->bg_inode_bitmap = 0;
+		}
+		if (gd->bg_inode_bitmap == 0) {
+			ctx->invalid_inode_bitmap_flag[i]++;
+			ctx->invalid_bitmaps++;
+		}
+		if ((gd->bg_inode_table < first_block) ||
+		    ((gd->bg_inode_table +
+		      fs->inode_blocks_per_group - 1) >= last_block)) {
+			pctx.blk = gd->bg_inode_table;
+			if (fix_problem(ctx, PR_0_ITABLE_NOT_GROUP, &pctx))
+				gd->bg_inode_table = 0;
+		}
+		if (gd->bg_inode_table == 0) {
+			ctx->invalid_inode_table_flag[i]++;
+			ctx->invalid_bitmaps++;
+		}
+		free_blocks += gd->bg_free_blocks_count;
+		free_inodes += gd->bg_free_inodes_count;
+		first_block += sb->s_blocks_per_group;
+		last_block += sb->s_blocks_per_group;
+
+		if ((gd->bg_free_blocks_count > sb->s_blocks_per_group) ||
+		    (gd->bg_free_inodes_count > sb->s_inodes_per_group) ||
+		    (gd->bg_used_dirs_count > sb->s_inodes_per_group))
+			ext2fs_unmark_valid(fs);
+	}
+
+	/*
+	 * Update the global counts from the block group counts.  This
+	 * is needed for an experimental patch which eliminates
+	 * locking the entire filesystem when allocating blocks or
+	 * inodes; if the filesystem is not unmounted cleanly, the
+	 * global counts may not be accurate.
+	 */
+	if ((free_blocks != sb->s_free_blocks_count) ||
+	    (free_inodes != sb->s_free_inodes_count)) {
+		if (ctx->options & E2F_OPT_READONLY)
+			ext2fs_unmark_valid(fs);
+		else {
+			sb->s_free_blocks_count = free_blocks;
+			sb->s_free_inodes_count = free_inodes;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
+	if ((sb->s_free_blocks_count > sb->s_blocks_count) ||
+	    (sb->s_free_inodes_count > sb->s_inodes_count))
+		ext2fs_unmark_valid(fs);
+
+
+	/*
+	 * If we have invalid bitmaps, set the error state of the
+	 * filesystem.
+	 */
+	if (ctx->invalid_bitmaps && !(ctx->options & E2F_OPT_READONLY)) {
+		sb->s_state &= ~EXT2_VALID_FS;
+		ext2fs_mark_super_dirty(fs);
+	}
+
+	clear_problem_context(&pctx);
+
+	/*
+	 * If the UUID field isn't assigned, assign it.
+	 */
+	if (!(ctx->options & E2F_OPT_READONLY) && uuid_is_null(sb->s_uuid)) {
+		if (fix_problem(ctx, PR_0_ADD_UUID, &pctx)) {
+			uuid_generate(sb->s_uuid);
+			ext2fs_mark_super_dirty(fs);
+			fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+		}
+	}
+
+	/* FIXME - HURD support?
+	 * For the Hurd, check to see if the filetype option is set,
+	 * since it doesn't support it.
+	 */
+	if (!(ctx->options & E2F_OPT_READONLY) &&
+	    fs->super->s_creator_os == EXT2_OS_HURD &&
+	    (fs->super->s_feature_incompat &
+	     EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+		if (fix_problem(ctx, PR_0_HURD_CLEAR_FILETYPE, &pctx)) {
+			fs->super->s_feature_incompat &=
+				~EXT2_FEATURE_INCOMPAT_FILETYPE;
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
+	/*
+	 * If we have any of the compatibility flags set, we need to have a
+	 * revision 1 filesystem.  Most kernels will not check the flags on
+	 * a rev 0 filesystem and we may have corruption issues because of
+	 * the incompatible changes to the filesystem.
+	 */
+	if (!(ctx->options & E2F_OPT_READONLY) &&
+	    fs->super->s_rev_level == EXT2_GOOD_OLD_REV &&
+	    (fs->super->s_feature_compat ||
+	     fs->super->s_feature_ro_compat ||
+	     fs->super->s_feature_incompat) &&
+	    fix_problem(ctx, PR_0_FS_REV_LEVEL, &pctx)) {
+		ext2fs_update_dynamic_rev(fs);
+		ext2fs_mark_super_dirty(fs);
+	}
+
+	check_resize_inode(ctx);
+
+	/*
+	 * Clean up any orphan inodes, if present.
+	 */
+	if (!(ctx->options & E2F_OPT_READONLY) && release_orphan_inodes(ctx)) {
+		fs->super->s_state &= ~EXT2_VALID_FS;
+		ext2fs_mark_super_dirty(fs);
+	}
+
+	/*
+	 * Move the ext3 journal file, if necessary.
+	 */
+	e2fsck_move_ext3_journal(ctx);
+}
+
+/*
+ * swapfs.c --- byte-swap an ext2 filesystem
+ */
+
+#ifdef ENABLE_SWAPFS
+
+struct swap_block_struct {
+	ext2_ino_t      ino;
+	int             isdir;
+	errcode_t       errcode;
+	char            *dir_buf;
+	struct ext2_inode *inode;
+};
+
+/*
+ * This is a helper function for block_iterate.  We mark all of the
+ * indirect and direct blocks as changed, so that block_iterate will
+ * write them out.
+ */
+static int swap_block(ext2_filsys fs, blk_t *block_nr, int blockcnt,
+		      void *priv_data)
+{
+	errcode_t       retval;
+
+	struct swap_block_struct *sb = (struct swap_block_struct *) priv_data;
+
+	if (sb->isdir && (blockcnt >= 0) && *block_nr) {
+		retval = ext2fs_read_dir_block(fs, *block_nr, sb->dir_buf);
+		if (retval) {
+			sb->errcode = retval;
+			return BLOCK_ABORT;
+		}
+		retval = ext2fs_write_dir_block(fs, *block_nr, sb->dir_buf);
+		if (retval) {
+			sb->errcode = retval;
+			return BLOCK_ABORT;
+		}
+	}
+	if (blockcnt >= 0) {
+		if (blockcnt < EXT2_NDIR_BLOCKS)
+			return 0;
+		return BLOCK_CHANGED;
+	}
+	if (blockcnt == BLOCK_COUNT_IND) {
+		if (*block_nr == sb->inode->i_block[EXT2_IND_BLOCK])
+			return 0;
+		return BLOCK_CHANGED;
+	}
+	if (blockcnt == BLOCK_COUNT_DIND) {
+		if (*block_nr == sb->inode->i_block[EXT2_DIND_BLOCK])
+			return 0;
+		return BLOCK_CHANGED;
+	}
+	if (blockcnt == BLOCK_COUNT_TIND) {
+		if (*block_nr == sb->inode->i_block[EXT2_TIND_BLOCK])
+			return 0;
+		return BLOCK_CHANGED;
+	}
+	return BLOCK_CHANGED;
+}
+
+/*
+ * This function is responsible for byte-swapping all of the indirect,
+ * block pointers.  It is also responsible for byte-swapping directories.
+ */
+static void swap_inode_blocks(e2fsck_t ctx, ext2_ino_t ino, char *block_buf,
+			      struct ext2_inode *inode)
+{
+	errcode_t                       retval;
+	struct swap_block_struct        sb;
+
+	sb.ino = ino;
+	sb.inode = inode;
+	sb.dir_buf = block_buf + ctx->fs->blocksize*3;
+	sb.errcode = 0;
+	sb.isdir = 0;
+	if (LINUX_S_ISDIR(inode->i_mode))
+		sb.isdir = 1;
+
+	retval = ext2fs_block_iterate(ctx->fs, ino, 0, block_buf,
+				      swap_block, &sb);
+	if (retval) {
+		bb_error_msg(_("while calling ext2fs_block_iterate"));
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	if (sb.errcode) {
+		bb_error_msg(_("while calling iterator function"));
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+}
+
+static void swap_inodes(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	dgrp_t                  group;
+	unsigned int            i;
+	ext2_ino_t              ino = 1;
+	char                    *buf, *block_buf;
+	errcode_t               retval;
+	struct ext2_inode *     inode;
+
+	e2fsck_use_inode_shortcuts(ctx, 1);
+
+	retval = ext2fs_get_mem(fs->blocksize * fs->inode_blocks_per_group,
+				&buf);
+	if (retval) {
+		bb_error_msg(_("while allocating inode buffer"));
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 4,
+						    "block interate buffer");
+	for (group = 0; group < fs->group_desc_count; group++) {
+		retval = io_channel_read_blk(fs->io,
+		      fs->group_desc[group].bg_inode_table,
+		      fs->inode_blocks_per_group, buf);
+		if (retval) {
+			bb_error_msg(_("while reading inode table (group %d)"),
+				group);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+		inode = (struct ext2_inode *) buf;
+		for (i=0; i < fs->super->s_inodes_per_group;
+		     i++, ino++, inode++) {
+			ctx->stashed_ino = ino;
+			ctx->stashed_inode = inode;
+
+			if (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)
+				ext2fs_swap_inode(fs, inode, inode, 0);
+
+			/*
+			 * Skip deleted files.
+			 */
+			if (inode->i_links_count == 0)
+				continue;
+
+			if (LINUX_S_ISDIR(inode->i_mode) ||
+			    ((inode->i_block[EXT2_IND_BLOCK] ||
+			      inode->i_block[EXT2_DIND_BLOCK] ||
+			      inode->i_block[EXT2_TIND_BLOCK]) &&
+			     ext2fs_inode_has_valid_blocks(inode)))
+				swap_inode_blocks(ctx, ino, block_buf, inode);
+
+			if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+				return;
+
+			if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
+				ext2fs_swap_inode(fs, inode, inode, 1);
+		}
+		retval = io_channel_write_blk(fs->io,
+		      fs->group_desc[group].bg_inode_table,
+		      fs->inode_blocks_per_group, buf);
+		if (retval) {
+			bb_error_msg(_("while writing inode table (group %d)"),
+				group);
+			ctx->flags |= E2F_FLAG_ABORT;
+			return;
+		}
+	}
+	ext2fs_free_mem(&buf);
+	ext2fs_free_mem(&block_buf);
+	e2fsck_use_inode_shortcuts(ctx, 0);
+	ext2fs_flush_icache(fs);
+}
+
+#if defined(__powerpc__) && BB_BIG_ENDIAN
+/*
+ * On the PowerPC, the big-endian variant of the ext2 filesystem
+ * has its bitmaps stored as 32-bit words with bit 0 as the LSB
+ * of each word.  Thus a bitmap with only bit 0 set would be, as
+ * a string of bytes, 00 00 00 01 00 ...
+ * To cope with this, we byte-reverse each word of a bitmap if
+ * we have a big-endian filesystem, that is, if we are *not*
+ * byte-swapping other word-sized numbers.
+ */
+#define EXT2_BIG_ENDIAN_BITMAPS
+#endif
+
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+static void ext2fs_swap_bitmap(ext2fs_generic_bitmap bmap)
+{
+	__u32 *p = (__u32 *) bmap->bitmap;
+	int n, nbytes = (bmap->end - bmap->start + 7) / 8;
+
+	for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
+		*p = ext2fs_swab32(*p);
+}
+#endif
+
+
+#ifdef ENABLE_SWAPFS
+static void swap_filesys(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	if (!(ctx->options & E2F_OPT_PREEN))
+		printf(_("Pass 0: Doing byte-swap of filesystem\n"));
+
+	/* Byte swap */
+
+	if (fs->super->s_mnt_count) {
+		fprintf(stderr, _("%s: the filesystem must be freshly "
+			"checked using fsck\n"
+			"and not mounted before trying to "
+			"byte-swap it.\n"), ctx->device_name);
+		ctx->flags |= E2F_FLAG_ABORT;
+		return;
+	}
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+		fs->flags &= ~(EXT2_FLAG_SWAP_BYTES|
+			       EXT2_FLAG_SWAP_BYTES_WRITE);
+		fs->flags |= EXT2_FLAG_SWAP_BYTES_READ;
+	} else {
+		fs->flags &= ~EXT2_FLAG_SWAP_BYTES_READ;
+		fs->flags |= EXT2_FLAG_SWAP_BYTES_WRITE;
+	}
+	swap_inodes(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		return;
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)
+		fs->flags |= EXT2_FLAG_SWAP_BYTES;
+	fs->flags &= ~(EXT2_FLAG_SWAP_BYTES_READ|
+		       EXT2_FLAG_SWAP_BYTES_WRITE);
+
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+	e2fsck_read_bitmaps(ctx);
+	ext2fs_swap_bitmap(fs->inode_map);
+	ext2fs_swap_bitmap(fs->block_map);
+	fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
+#endif
+	fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+	ext2fs_flush(fs);
+	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+}
+#endif  /* ENABLE_SWAPFS */
+
+#endif
+
+/*
+ * util.c --- miscellaneous utilities
+ */
+
+
+void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
+			     const char *description)
+{
+	void *ret;
+	char buf[256];
+
+	ret = xzalloc(size);
+	return ret;
+}
+
+static char *string_copy(const char *str, int len)
+{
+	char    *ret;
+
+	if (!str)
+		return NULL;
+	if (!len)
+		len = strlen(str);
+	ret = xmalloc(len+1);
+	strncpy(ret, str, len);
+	ret[len] = 0;
+	return ret;
+}
+
+#ifndef HAVE_CONIO_H
+static int read_a_char(void)
+{
+	char    c;
+	int     r;
+	int     fail = 0;
+
+	while (1) {
+		if (e2fsck_global_ctx &&
+		    (e2fsck_global_ctx->flags & E2F_FLAG_CANCEL)) {
+			return 3;
+		}
+		r = read(0, &c, 1);
+		if (r == 1)
+			return c;
+		if (fail++ > 100)
+			break;
+	}
+	return EOF;
+}
+#endif
+
+static int ask_yn(const char * string, int def)
+{
+	int             c;
+	const char      *defstr;
+	static const char short_yes[] = "yY";
+	static const char short_no[] = "nN";
+
+#ifdef HAVE_TERMIOS_H
+	struct termios  termios, tmp;
+
+	tcgetattr (0, &termios);
+	tmp = termios;
+	tmp.c_lflag &= ~(ICANON | ECHO);
+	tmp.c_cc[VMIN] = 1;
+	tmp.c_cc[VTIME] = 0;
+	tcsetattr_stdin_TCSANOW(&tmp);
+#endif
+
+	if (def == 1)
+		defstr = "<y>";
+	else if (def == 0)
+		defstr = "<n>";
+	else
+		defstr = " (y/n)";
+	printf("%s%s? ", string, defstr);
+	while (1) {
+		fflush (stdout);
+		if ((c = read_a_char()) == EOF)
+			break;
+		if (c == 3) {
+#ifdef HAVE_TERMIOS_H
+			tcsetattr_stdin_TCSANOW(&termios);
+#endif
+			if (e2fsck_global_ctx &&
+			    e2fsck_global_ctx->flags & E2F_FLAG_SETJMP_OK) {
+				puts("\n");
+				longjmp(e2fsck_global_ctx->abort_loc, 1);
+			}
+			puts(_("cancelled!\n"));
+			return 0;
+		}
+		if (strchr(short_yes, (char) c)) {
+			def = 1;
+			break;
+		}
+		else if (strchr(short_no, (char) c)) {
+			def = 0;
+			break;
+		}
+		else if ((c == ' ' || c == '\n') && (def != -1))
+			break;
+	}
+	if (def)
+		puts("yes\n");
+	else
+		puts ("no\n");
+#ifdef HAVE_TERMIOS_H
+	tcsetattr_stdin_TCSANOW(&termios);
+#endif
+	return def;
+}
+
+int ask (e2fsck_t ctx, const char * string, int def)
+{
+	if (ctx->options & E2F_OPT_NO) {
+		printf(_("%s? no\n\n"), string);
+		return 0;
+	}
+	if (ctx->options & E2F_OPT_YES) {
+		printf(_("%s? yes\n\n"), string);
+		return 1;
+	}
+	if (ctx->options & E2F_OPT_PREEN) {
+		printf("%s? %s\n\n", string, def ? _("yes") : _("no"));
+		return def;
+	}
+	return ask_yn(string, def);
+}
+
+void e2fsck_read_bitmaps(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	errcode_t       retval;
+
+	if (ctx->invalid_bitmaps) {
+		bb_error_msg(_("e2fsck_read_bitmaps: illegal bitmap block(s) for %s"),
+			ctx->device_name);
+		bb_error_msg_and_die(0);
+	}
+
+	ehandler_operation(_("reading inode and block bitmaps"));
+	retval = ext2fs_read_bitmaps(fs);
+	ehandler_operation(0);
+	if (retval) {
+		bb_error_msg(_("while retrying to read bitmaps for %s"),
+			ctx->device_name);
+		bb_error_msg_and_die(0);
+	}
+}
+
+static void e2fsck_write_bitmaps(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	errcode_t       retval;
+
+	if (ext2fs_test_bb_dirty(fs)) {
+		ehandler_operation(_("writing block bitmaps"));
+		retval = ext2fs_write_block_bitmap(fs);
+		ehandler_operation(0);
+		if (retval) {
+			bb_error_msg(_("while retrying to write block bitmaps for %s"),
+				ctx->device_name);
+			bb_error_msg_and_die(0);
+		}
+	}
+
+	if (ext2fs_test_ib_dirty(fs)) {
+		ehandler_operation(_("writing inode bitmaps"));
+		retval = ext2fs_write_inode_bitmap(fs);
+		ehandler_operation(0);
+		if (retval) {
+			bb_error_msg(_("while retrying to write inode bitmaps for %s"),
+				ctx->device_name);
+			bb_error_msg_and_die(0);
+		}
+	}
+}
+
+void preenhalt(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+
+	if (!(ctx->options & E2F_OPT_PREEN))
+		return;
+	fprintf(stderr, _("\n\n%s: UNEXPECTED INCONSISTENCY; "
+		"RUN fsck MANUALLY.\n\t(i.e., without -a or -p options)\n"),
+	       ctx->device_name);
+	if (fs != NULL) {
+		fs->super->s_state |= EXT2_ERROR_FS;
+		ext2fs_mark_super_dirty(fs);
+		ext2fs_close(fs);
+	}
+	exit(EXIT_UNCORRECTED);
+}
+
+void e2fsck_read_inode(e2fsck_t ctx, unsigned long ino,
+			      struct ext2_inode * inode, const char *proc)
+{
+	int retval;
+
+	retval = ext2fs_read_inode(ctx->fs, ino, inode);
+	if (retval) {
+		bb_error_msg(_("while reading inode %ld in %s"), ino, proc);
+		bb_error_msg_and_die(0);
+	}
+}
+
+extern void e2fsck_write_inode_full(e2fsck_t ctx, unsigned long ino,
+			       struct ext2_inode * inode, int bufsize,
+			       const char *proc)
+{
+	int retval;
+
+	retval = ext2fs_write_inode_full(ctx->fs, ino, inode, bufsize);
+	if (retval) {
+		bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
+		bb_error_msg_and_die(0);
+	}
+}
+
+extern void e2fsck_write_inode(e2fsck_t ctx, unsigned long ino,
+			       struct ext2_inode * inode, const char *proc)
+{
+	int retval;
+
+	retval = ext2fs_write_inode(ctx->fs, ino, inode);
+	if (retval) {
+		bb_error_msg(_("while writing inode %ld in %s"), ino, proc);
+		bb_error_msg_and_die(0);
+	}
+}
+
+blk_t get_backup_sb(e2fsck_t ctx, ext2_filsys fs, const char *name,
+		   io_manager manager)
+{
+	struct ext2_super_block *sb;
+	io_channel              io = NULL;
+	void                    *buf = NULL;
+	int                     blocksize;
+	blk_t                   superblock, ret_sb = 8193;
+
+	if (fs && fs->super) {
+		ret_sb = (fs->super->s_blocks_per_group +
+			  fs->super->s_first_data_block);
+		if (ctx) {
+			ctx->superblock = ret_sb;
+			ctx->blocksize = fs->blocksize;
+		}
+		return ret_sb;
+	}
+
+	if (ctx) {
+		if (ctx->blocksize) {
+			ret_sb = ctx->blocksize * 8;
+			if (ctx->blocksize == 1024)
+				ret_sb++;
+			ctx->superblock = ret_sb;
+			return ret_sb;
+		}
+		ctx->superblock = ret_sb;
+		ctx->blocksize = 1024;
+	}
+
+	if (!name || !manager)
+		goto cleanup;
+
+	if (manager->open(name, 0, &io) != 0)
+		goto cleanup;
+
+	if (ext2fs_get_mem(SUPERBLOCK_SIZE, &buf))
+		goto cleanup;
+	sb = (struct ext2_super_block *) buf;
+
+	for (blocksize = EXT2_MIN_BLOCK_SIZE;
+	     blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
+		superblock = blocksize*8;
+		if (blocksize == 1024)
+			superblock++;
+		io_channel_set_blksize(io, blocksize);
+		if (io_channel_read_blk(io, superblock,
+					-SUPERBLOCK_SIZE, buf))
+			continue;
+#if BB_BIG_ENDIAN
+		if (sb->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC))
+			ext2fs_swap_super(sb);
+#endif
+		if (sb->s_magic == EXT2_SUPER_MAGIC) {
+			ret_sb = superblock;
+			if (ctx) {
+				ctx->superblock = superblock;
+				ctx->blocksize = blocksize;
+			}
+			break;
+		}
+	}
+
+cleanup:
+	if (io)
+		io_channel_close(io);
+	ext2fs_free_mem(&buf);
+	return ret_sb;
+}
+
+
+/*
+ * This function runs through the e2fsck passes and calls them all,
+ * returning restart, abort, or cancel as necessary...
+ */
+typedef void (*pass_t)(e2fsck_t ctx);
+
+static const pass_t e2fsck_passes[] = {
+	e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
+	e2fsck_pass5, 0 };
+
+#define E2F_FLAG_RUN_RETURN     (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
+
+static int e2fsck_run(e2fsck_t ctx)
+{
+	int     i;
+	pass_t  e2fsck_pass;
+
+	if (setjmp(ctx->abort_loc)) {
+		ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+		return (ctx->flags & E2F_FLAG_RUN_RETURN);
+	}
+	ctx->flags |= E2F_FLAG_SETJMP_OK;
+
+	for (i=0; (e2fsck_pass = e2fsck_passes[i]); i++) {
+		if (ctx->flags & E2F_FLAG_RUN_RETURN)
+			break;
+		e2fsck_pass(ctx);
+		if (ctx->progress)
+			(void) (ctx->progress)(ctx, 0, 0, 0);
+	}
+	ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+
+	if (ctx->flags & E2F_FLAG_RUN_RETURN)
+		return (ctx->flags & E2F_FLAG_RUN_RETURN);
+	return 0;
+}
+
+
+/*
+ * unix.c - The unix-specific code for e2fsck
+ */
+
+
+/* Command line options */
+static int swapfs;
+#ifdef ENABLE_SWAPFS
+static int normalize_swapfs;
+#endif
+static int cflag;               /* check disk */
+static int show_version_only;
+static int verbose;
+
+#define P_E2(singular, plural, n)       n, ((n) == 1 ? singular : plural)
+
+static void show_stats(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	int inodes, inodes_used, blocks, blocks_used;
+	int dir_links;
+	int num_files, num_links;
+	int frag_percent;
+
+	dir_links = 2 * ctx->fs_directory_count - 1;
+	num_files = ctx->fs_total_count - dir_links;
+	num_links = ctx->fs_links_count - dir_links;
+	inodes = fs->super->s_inodes_count;
+	inodes_used = (fs->super->s_inodes_count -
+		       fs->super->s_free_inodes_count);
+	blocks = fs->super->s_blocks_count;
+	blocks_used = (fs->super->s_blocks_count -
+		       fs->super->s_free_blocks_count);
+
+	frag_percent = (10000 * ctx->fs_fragmented) / inodes_used;
+	frag_percent = (frag_percent + 5) / 10;
+
+	if (!verbose) {
+		printf("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n",
+		       ctx->device_name, inodes_used, inodes,
+		       frag_percent / 10, frag_percent % 10,
+		       blocks_used, blocks);
+		return;
+	}
+	printf("\n%8d inode%s used (%d%%)\n", P_E2("", "s", inodes_used),
+		100 * inodes_used / inodes);
+	printf("%8d non-contiguous inode%s (%0d.%d%%)\n",
+		P_E2("", "s", ctx->fs_fragmented),
+		frag_percent / 10, frag_percent % 10);
+	printf(_("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
+		ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
+	printf("%8d block%s used (%d%%)\n", P_E2("", "s", blocks_used),
+		(int) ((long long) 100 * blocks_used / blocks));
+	printf("%8d large file%s\n", P_E2("", "s", ctx->large_files));
+	printf("\n%8d regular file%s\n", P_E2("", "s", ctx->fs_regular_count));
+	printf("%8d director%s\n", P_E2("y", "ies", ctx->fs_directory_count));
+	printf("%8d character device file%s\n", P_E2("", "s", ctx->fs_chardev_count));
+	printf("%8d block device file%s\n", P_E2("", "s", ctx->fs_blockdev_count));
+	printf("%8d fifo%s\n", P_E2("", "s", ctx->fs_fifo_count));
+	printf("%8d link%s\n", P_E2("", "s", ctx->fs_links_count - dir_links));
+	printf("%8d symbolic link%s", P_E2("", "s", ctx->fs_symlinks_count));
+	printf(" (%d fast symbolic link%s)\n", P_E2("", "s", ctx->fs_fast_symlinks_count));
+	printf("%8d socket%s--------\n\n", P_E2("", "s", ctx->fs_sockets_count));
+	printf("%8d file%s\n", P_E2("", "s", ctx->fs_total_count - dir_links));
+}
+
+static void check_mount(e2fsck_t ctx)
+{
+	errcode_t       retval;
+	int             cont;
+
+	retval = ext2fs_check_if_mounted(ctx->filesystem_name,
+					 &ctx->mount_flags);
+	if (retval) {
+		bb_error_msg(_("while determining whether %s is mounted"),
+			ctx->filesystem_name);
+		return;
+	}
+
+	/*
+	 * If the filesystem isn't mounted, or it's the root filesystem
+	 * and it's mounted read-only, then everything's fine.
+	 */
+	if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
+	    ((ctx->mount_flags & EXT2_MF_ISROOT) &&
+	     (ctx->mount_flags & EXT2_MF_READONLY)))
+		return;
+
+	if (ctx->options & E2F_OPT_READONLY) {
+		printf(_("Warning!  %s is mounted.\n"), ctx->filesystem_name);
+		return;
+	}
+
+	printf(_("%s is mounted.  "), ctx->filesystem_name);
+	if (!ctx->interactive)
+		bb_error_msg_and_die(_("can't continue, aborting"));
+	printf(_("\n\n\007\007\007\007WARNING!!!  "
+	       "Running e2fsck on a mounted filesystem may cause\n"
+	       "SEVERE filesystem damage.\007\007\007\n\n"));
+	cont = ask_yn(_("Do you really want to continue"), -1);
+	if (!cont) {
+		printf(_("check aborted.\n"));
+		exit(0);
+	}
+}
+
+static int is_on_batt(void)
+{
+	FILE    *f;
+	DIR     *d;
+	char    tmp[80], tmp2[80], fname[80];
+	unsigned int    acflag;
+	struct dirent*  de;
+
+	f = fopen_for_read("/proc/apm");
+	if (f) {
+		if (fscanf(f, "%s %s %s %x", tmp, tmp, tmp, &acflag) != 4)
+			acflag = 1;
+		fclose(f);
+		return (acflag != 1);
+	}
+	d = opendir("/proc/acpi/ac_adapter");
+	if (d) {
+		while ((de=readdir(d)) != NULL) {
+			if (!strncmp(".", de->d_name, 1))
+				continue;
+			snprintf(fname, 80, "/proc/acpi/ac_adapter/%s/state",
+				 de->d_name);
+			f = fopen_for_read(fname);
+			if (!f)
+				continue;
+			if (fscanf(f, "%s %s", tmp2, tmp) != 2)
+				tmp[0] = 0;
+			fclose(f);
+			if (strncmp(tmp, "off-line", 8) == 0) {
+				closedir(d);
+				return 1;
+			}
+		}
+		closedir(d);
+	}
+	return 0;
+}
+
+/*
+ * This routine checks to see if a filesystem can be skipped; if so,
+ * it will exit with EXIT_OK.  Under some conditions it will print a
+ * message explaining why a check is being forced.
+ */
+static void check_if_skip(e2fsck_t ctx)
+{
+	ext2_filsys fs = ctx->fs;
+	const char *reason = NULL;
+	unsigned int reason_arg = 0;
+	long next_check;
+	int batt = is_on_batt();
+	time_t now = time(NULL);
+
+	if ((ctx->options & E2F_OPT_FORCE) || cflag || swapfs)
+		return;
+
+	if ((fs->super->s_state & EXT2_ERROR_FS) ||
+	    !ext2fs_test_valid(fs))
+		reason = _(" contains a file system with errors");
+	else if ((fs->super->s_state & EXT2_VALID_FS) == 0)
+		reason = _(" was not cleanly unmounted");
+	else if ((fs->super->s_max_mnt_count > 0) &&
+		 (fs->super->s_mnt_count >=
+		  (unsigned) fs->super->s_max_mnt_count)) {
+		reason = _(" has been mounted %u times without being checked");
+		reason_arg = fs->super->s_mnt_count;
+		if (batt && (fs->super->s_mnt_count <
+			     (unsigned) fs->super->s_max_mnt_count*2))
+			reason = 0;
+	} else if (fs->super->s_checkinterval &&
+		   ((now - fs->super->s_lastcheck) >=
+		    fs->super->s_checkinterval)) {
+		reason = _(" has gone %u days without being checked");
+		reason_arg = (now - fs->super->s_lastcheck)/(3600*24);
+		if (batt && ((now - fs->super->s_lastcheck) <
+			     fs->super->s_checkinterval*2))
+			reason = 0;
+	}
+	if (reason) {
+		fputs(ctx->device_name, stdout);
+		printf(reason, reason_arg);
+		fputs(_(", check forced.\n"), stdout);
+		return;
+	}
+	printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
+	       fs->super->s_inodes_count - fs->super->s_free_inodes_count,
+	       fs->super->s_inodes_count,
+	       fs->super->s_blocks_count - fs->super->s_free_blocks_count,
+	       fs->super->s_blocks_count);
+	next_check = 100000;
+	if (fs->super->s_max_mnt_count > 0) {
+		next_check = fs->super->s_max_mnt_count - fs->super->s_mnt_count;
+		if (next_check <= 0)
+			next_check = 1;
+	}
+	if (fs->super->s_checkinterval &&
+	    ((now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
+		next_check = 1;
+	if (next_check <= 5) {
+		if (next_check == 1)
+			fputs(_(" (check after next mount)"), stdout);
+		else
+			printf(_(" (check in %ld mounts)"), next_check);
+	}
+	bb_putchar('\n');
+	ext2fs_close(fs);
+	ctx->fs = NULL;
+	e2fsck_free_context(ctx);
+	exit(EXIT_OK);
+}
+
+/*
+ * For completion notice
+ */
+struct percent_tbl {
+	int     max_pass;
+	int     table[32];
+};
+static const struct percent_tbl e2fsck_tbl = {
+	5, { 0, 70, 90, 92,  95, 100 }
+};
+
+static char bar[128], spaces[128];
+
+static float calc_percent(const struct percent_tbl *tbl, int pass, int curr,
+			  int max)
+{
+	float   percent;
+
+	if (pass <= 0)
+		return 0.0;
+	if (pass > tbl->max_pass || max == 0)
+		return 100.0;
+	percent = ((float) curr) / ((float) max);
+	return ((percent * (tbl->table[pass] - tbl->table[pass-1]))
+		+ tbl->table[pass-1]);
+}
+
+void e2fsck_clear_progbar(e2fsck_t ctx)
+{
+	if (!(ctx->flags & E2F_FLAG_PROG_BAR))
+		return;
+
+	printf("%s%s\r%s", ctx->start_meta, spaces + (sizeof(spaces) - 80),
+	       ctx->stop_meta);
+	fflush(stdout);
+	ctx->flags &= ~E2F_FLAG_PROG_BAR;
+}
+
+int e2fsck_simple_progress(e2fsck_t ctx, const char *label, float percent,
+			   unsigned int dpynum)
+{
+	static const char spinner[] = "\\|/-";
+	int     i;
+	unsigned int    tick;
+	struct timeval  tv;
+	int dpywidth;
+	int fixed_percent;
+
+	if (ctx->flags & E2F_FLAG_PROG_SUPPRESS)
+		return 0;
+
+	/*
+	 * Calculate the new progress position.  If the
+	 * percentage hasn't changed, then we skip out right
+	 * away.
+	 */
+	fixed_percent = (int) ((10 * percent) + 0.5);
+	if (ctx->progress_last_percent == fixed_percent)
+		return 0;
+	ctx->progress_last_percent = fixed_percent;
+
+	/*
+	 * If we've already updated the spinner once within
+	 * the last 1/8th of a second, no point doing it
+	 * again.
+	 */
+	gettimeofday(&tv, NULL);
+	tick = (tv.tv_sec << 3) + (tv.tv_usec / (1000000 / 8));
+	if ((tick == ctx->progress_last_time) &&
+	    (fixed_percent != 0) && (fixed_percent != 1000))
+		return 0;
+	ctx->progress_last_time = tick;
+
+	/*
+	 * Advance the spinner, and note that the progress bar
+	 * will be on the screen
+	 */
+	ctx->progress_pos = (ctx->progress_pos+1) & 3;
+	ctx->flags |= E2F_FLAG_PROG_BAR;
+
+	dpywidth = 66 - strlen(label);
+	dpywidth = 8 * (dpywidth / 8);
+	if (dpynum)
+		dpywidth -= 8;
+
+	i = ((percent * dpywidth) + 50) / 100;
+	printf("%s%s: |%s%s", ctx->start_meta, label,
+	       bar + (sizeof(bar) - (i+1)),
+	       spaces + (sizeof(spaces) - (dpywidth - i + 1)));
+	if (fixed_percent == 1000)
+		bb_putchar('|');
+	else
+		bb_putchar(spinner[ctx->progress_pos & 3]);
+	printf(" %4.1f%%  ", percent);
+	if (dpynum)
+		printf("%u\r", dpynum);
+	else
+		fputs(" \r", stdout);
+	fputs(ctx->stop_meta, stdout);
+
+	if (fixed_percent == 1000)
+		e2fsck_clear_progbar(ctx);
+	fflush(stdout);
+
+	return 0;
+}
+
+static int e2fsck_update_progress(e2fsck_t ctx, int pass,
+				  unsigned long cur, unsigned long max)
+{
+	char buf[80];
+	float percent;
+
+	if (pass == 0)
+		return 0;
+
+	if (ctx->progress_fd) {
+		sprintf(buf, "%d %lu %lu\n", pass, cur, max);
+		xwrite_str(ctx->progress_fd, buf);
+	} else {
+		percent = calc_percent(&e2fsck_tbl, pass, cur, max);
+		e2fsck_simple_progress(ctx, ctx->device_name,
+				       percent, 0);
+	}
+	return 0;
+}
+
+static void reserve_stdio_fds(void)
+{
+	int     fd;
+
+	while (1) {
+		fd = open(bb_dev_null, O_RDWR);
+		if (fd > 2)
+			break;
+		if (fd < 0) {
+			fprintf(stderr, _("ERROR: Cannot open "
+				"/dev/null (%s)\n"),
+				strerror(errno));
+			break;
+		}
+	}
+	close(fd);
+}
+
+static void signal_progress_on(int sig FSCK_ATTR((unused)))
+{
+	e2fsck_t ctx = e2fsck_global_ctx;
+
+	if (!ctx)
+		return;
+
+	ctx->progress = e2fsck_update_progress;
+	ctx->progress_fd = 0;
+}
+
+static void signal_progress_off(int sig FSCK_ATTR((unused)))
+{
+	e2fsck_t ctx = e2fsck_global_ctx;
+
+	if (!ctx)
+		return;
+
+	e2fsck_clear_progbar(ctx);
+	ctx->progress = 0;
+}
+
+static void signal_cancel(int sig FSCK_ATTR((unused)))
+{
+	e2fsck_t ctx = e2fsck_global_ctx;
+
+	if (!ctx)
+		exit(FSCK_CANCELED);
+
+	ctx->flags |= E2F_FLAG_CANCEL;
+}
+
+static void parse_extended_opts(e2fsck_t ctx, const char *opts)
+{
+	char    *buf, *token, *next, *p, *arg;
+	int     ea_ver;
+	int     extended_usage = 0;
+
+	buf = string_copy(opts, 0);
+	for (token = buf; token && *token; token = next) {
+		p = strchr(token, ',');
+		next = 0;
+		if (p) {
+			*p = 0;
+			next = p+1;
+		}
+		arg = strchr(token, '=');
+		if (arg) {
+			*arg = 0;
+			arg++;
+		}
+		if (strcmp(token, "ea_ver") == 0) {
+			if (!arg) {
+				extended_usage++;
+				continue;
+			}
+			ea_ver = strtoul(arg, &p, 0);
+			if (*p ||
+			    ((ea_ver != 1) && (ea_ver != 2))) {
+				fprintf(stderr,
+					_("Invalid EA version.\n"));
+				extended_usage++;
+				continue;
+			}
+			ctx->ext_attr_ver = ea_ver;
+		} else {
+			fprintf(stderr, _("Unknown extended option: %s\n"),
+				token);
+			extended_usage++;
+		}
+	}
+	if (extended_usage) {
+		bb_error_msg_and_die(
+			"Extended options are separated by commas, "
+			"and may take an argument which\n"
+			"is set off by an equals ('=') sign.  "
+			"Valid extended options are:\n"
+			"\tea_ver=<ea_version (1 or 2)>\n\n");
+	}
+}
+
+
+static errcode_t PRS(int argc, char **argv, e2fsck_t *ret_ctx)
+{
+	int             flush = 0;
+	int             c, fd;
+	e2fsck_t        ctx;
+	errcode_t       retval;
+	struct sigaction        sa;
+	char            *extended_opts = NULL;
+
+	retval = e2fsck_allocate_context(&ctx);
+	if (retval)
+		return retval;
+
+	*ret_ctx = ctx;
+
+	setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+	setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+	if (isatty(0) && isatty(1)) {
+		ctx->interactive = 1;
+	} else {
+		ctx->start_meta[0] = '\001';
+		ctx->stop_meta[0] = '\002';
+	}
+	memset(bar, '=', sizeof(bar)-1);
+	memset(spaces, ' ', sizeof(spaces)-1);
+	blkid_get_cache(&ctx->blkid, NULL);
+
+	if (argc && *argv)
+		ctx->program_name = *argv;
+	else
+		ctx->program_name = "e2fsck";
+	while ((c = getopt (argc, argv, "panyrcC:B:dE:fvtFVM:b:I:j:P:l:L:N:SsDk")) != EOF)
+		switch (c) {
+		case 'C':
+			ctx->progress = e2fsck_update_progress;
+			ctx->progress_fd = atoi(optarg);
+			if (!ctx->progress_fd)
+				break;
+			/* Validate the file descriptor to avoid disasters */
+			fd = dup(ctx->progress_fd);
+			if (fd < 0) {
+				fprintf(stderr,
+				_("Error validating file descriptor %d: %s\n"),
+					ctx->progress_fd,
+					error_message(errno));
+				bb_error_msg_and_die(_("Invalid completion information file descriptor"));
+			} else
+				close(fd);
+			break;
+		case 'D':
+			ctx->options |= E2F_OPT_COMPRESS_DIRS;
+			break;
+		case 'E':
+			extended_opts = optarg;
+			break;
+		case 'p':
+		case 'a':
+			if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
+			conflict_opt:
+				bb_error_msg_and_die(_("only one the options -p/-a, -n or -y may be specified"));
+			}
+			ctx->options |= E2F_OPT_PREEN;
+			break;
+		case 'n':
+			if (ctx->options & (E2F_OPT_YES|E2F_OPT_PREEN))
+				goto conflict_opt;
+			ctx->options |= E2F_OPT_NO;
+			break;
+		case 'y':
+			if (ctx->options & (E2F_OPT_PREEN|E2F_OPT_NO))
+				goto conflict_opt;
+			ctx->options |= E2F_OPT_YES;
+			break;
+		case 't':
+			/* FIXME - This needs to go away in a future path - will change binary */
+			fprintf(stderr, _("The -t option is not "
+				"supported on this version of e2fsck.\n"));
+			break;
+		case 'c':
+			if (cflag++)
+				ctx->options |= E2F_OPT_WRITECHECK;
+			ctx->options |= E2F_OPT_CHECKBLOCKS;
+			break;
+		case 'r':
+			/* What we do by default, anyway! */
+			break;
+		case 'b':
+			ctx->use_superblock = atoi(optarg);
+			ctx->flags |= E2F_FLAG_SB_SPECIFIED;
+			break;
+		case 'B':
+			ctx->blocksize = atoi(optarg);
+			break;
+		case 'I':
+			ctx->inode_buffer_blocks = atoi(optarg);
+			break;
+		case 'j':
+			ctx->journal_name = string_copy(optarg, 0);
+			break;
+		case 'P':
+			ctx->process_inode_size = atoi(optarg);
+			break;
+		case 'd':
+			ctx->options |= E2F_OPT_DEBUG;
+			break;
+		case 'f':
+			ctx->options |= E2F_OPT_FORCE;
+			break;
+		case 'F':
+			flush = 1;
+			break;
+		case 'v':
+			verbose = 1;
+			break;
+		case 'V':
+			show_version_only = 1;
+			break;
+		case 'N':
+			ctx->device_name = optarg;
+			break;
+#ifdef ENABLE_SWAPFS
+		case 's':
+			normalize_swapfs = 1;
+		case 'S':
+			swapfs = 1;
+			break;
+#else
+		case 's':
+		case 'S':
+			fprintf(stderr, _("Byte-swapping filesystems "
+					  "not compiled in this version "
+					  "of e2fsck\n"));
+			exit(1);
+#endif
+		default:
+			bb_show_usage();
+		}
+	if (show_version_only)
+		return 0;
+	if (optind != argc - 1)
+		bb_show_usage();
+	if ((ctx->options & E2F_OPT_NO) &&
+	    !cflag && !swapfs && !(ctx->options & E2F_OPT_COMPRESS_DIRS))
+		ctx->options |= E2F_OPT_READONLY;
+	ctx->io_options = strchr(argv[optind], '?');
+	if (ctx->io_options)
+		*ctx->io_options++ = 0;
+	ctx->filesystem_name = blkid_get_devname(ctx->blkid, argv[optind], 0);
+	if (!ctx->filesystem_name) {
+		bb_error_msg(_("Unable to resolve '%s'"), argv[optind]);
+		bb_error_msg_and_die(0);
+	}
+	if (extended_opts)
+		parse_extended_opts(ctx, extended_opts);
+
+	if (flush) {
+		fd = open(ctx->filesystem_name, O_RDONLY, 0);
+		if (fd < 0) {
+			bb_error_msg(_("while opening %s for flushing"),
+				ctx->filesystem_name);
+			bb_error_msg_and_die(0);
+		}
+		if ((retval = ext2fs_sync_device(fd, 1))) {
+			bb_error_msg(_("while trying to flush %s"),
+				ctx->filesystem_name);
+			bb_error_msg_and_die(0);
+		}
+		close(fd);
+	}
+#ifdef ENABLE_SWAPFS
+	if (swapfs && cflag) {
+			fprintf(stderr, _("Incompatible options not "
+					  "allowed when byte-swapping.\n"));
+			exit(EXIT_USAGE);
+	}
+#endif
+	/*
+	 * Set up signal action
+	 */
+	memset(&sa, 0, sizeof(struct sigaction));
+	sa.sa_handler = signal_cancel;
+	sigaction(SIGINT, &sa, 0);
+	sigaction(SIGTERM, &sa, 0);
+#ifdef SA_RESTART
+	sa.sa_flags = SA_RESTART;
+#endif
+	e2fsck_global_ctx = ctx;
+	sa.sa_handler = signal_progress_on;
+	sigaction(SIGUSR1, &sa, 0);
+	sa.sa_handler = signal_progress_off;
+	sigaction(SIGUSR2, &sa, 0);
+
+	/* Update our PATH to include /sbin if we need to run badblocks  */
+	if (cflag)
+		e2fs_set_sbin_path();
+	return 0;
+}
+
+static const char my_ver_string[] = E2FSPROGS_VERSION;
+static const char my_ver_date[] = E2FSPROGS_DATE;
+
+int e2fsck_main (int argc, char **argv);
+int e2fsck_main (int argc, char **argv)
+{
+	errcode_t       retval;
+	int             exit_value = EXIT_OK;
+	ext2_filsys     fs = 0;
+	io_manager      io_ptr;
+	struct ext2_super_block *sb;
+	const char      *lib_ver_date;
+	int             my_ver, lib_ver;
+	e2fsck_t        ctx;
+	struct problem_context pctx;
+	int flags, run_result;
+
+	clear_problem_context(&pctx);
+
+	my_ver = ext2fs_parse_version_string(my_ver_string);
+	lib_ver = ext2fs_get_library_version(0, &lib_ver_date);
+	if (my_ver > lib_ver) {
+		fprintf( stderr, _("Error: ext2fs library version "
+			"out of date!\n"));
+		show_version_only++;
+	}
+
+	retval = PRS(argc, argv, &ctx);
+	if (retval) {
+		bb_error_msg(_("while trying to initialize program"));
+		exit(EXIT_ERROR);
+	}
+	reserve_stdio_fds();
+
+	if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
+		fprintf(stderr, "e2fsck %s (%s)\n", my_ver_string,
+			 my_ver_date);
+
+	if (show_version_only) {
+		fprintf(stderr, _("\tUsing %s, %s\n"),
+			error_message(EXT2_ET_BASE), lib_ver_date);
+		exit(EXIT_OK);
+	}
+
+	check_mount(ctx);
+
+	if (!(ctx->options & E2F_OPT_PREEN) &&
+	    !(ctx->options & E2F_OPT_NO) &&
+	    !(ctx->options & E2F_OPT_YES)) {
+		if (!ctx->interactive)
+			bb_error_msg_and_die(_("need terminal for interactive repairs"));
+	}
+	ctx->superblock = ctx->use_superblock;
+restart:
+#ifdef CONFIG_TESTIO_DEBUG
+	io_ptr = test_io_manager;
+	test_io_backing_manager = unix_io_manager;
+#else
+	io_ptr = unix_io_manager;
+#endif
+	flags = 0;
+	if ((ctx->options & E2F_OPT_READONLY) == 0)
+		flags |= EXT2_FLAG_RW;
+
+	if (ctx->superblock && ctx->blocksize) {
+		retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
+				      flags, ctx->superblock, ctx->blocksize,
+				      io_ptr, &fs);
+	} else if (ctx->superblock) {
+		int blocksize;
+		for (blocksize = EXT2_MIN_BLOCK_SIZE;
+		     blocksize <= EXT2_MAX_BLOCK_SIZE; blocksize *= 2) {
+			retval = ext2fs_open2(ctx->filesystem_name,
+					      ctx->io_options, flags,
+					      ctx->superblock, blocksize,
+					      io_ptr, &fs);
+			if (!retval)
+				break;
+		}
+	} else
+		retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options,
+				      flags, 0, 0, io_ptr, &fs);
+	if (!ctx->superblock && !(ctx->options & E2F_OPT_PREEN) &&
+	    !(ctx->flags & E2F_FLAG_SB_SPECIFIED) &&
+	    ((retval == EXT2_ET_BAD_MAGIC) ||
+	     ((retval == 0) && ext2fs_check_desc(fs)))) {
+		if (!fs || (fs->group_desc_count > 1)) {
+			printf(_("%s trying backup blocks...\n"),
+			       retval ? _("Couldn't find ext2 superblock,") :
+			       _("Group descriptors look bad..."));
+			get_backup_sb(ctx, fs, ctx->filesystem_name, io_ptr);
+			if (fs)
+				ext2fs_close(fs);
+			goto restart;
+		}
+	}
+	if (retval) {
+		bb_error_msg(_("while trying to open %s"),
+			ctx->filesystem_name);
+		if (retval == EXT2_ET_REV_TOO_HIGH) {
+			printf(_("The filesystem revision is apparently "
+			       "too high for this version of e2fsck.\n"
+			       "(Or the filesystem superblock "
+			       "is corrupt)\n\n"));
+			fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
+		} else if (retval == EXT2_ET_SHORT_READ)
+			printf(_("Could this be a zero-length partition?\n"));
+		else if ((retval == EPERM) || (retval == EACCES))
+			printf(_("You must have %s access to the "
+			       "filesystem or be root\n"),
+			       (ctx->options & E2F_OPT_READONLY) ?
+			       "r/o" : "r/w");
+		else if (retval == ENXIO)
+			printf(_("Possibly non-existent or swap device?\n"));
+#ifdef EROFS
+		else if (retval == EROFS)
+			printf(_("Disk write-protected; use the -n option "
+			       "to do a read-only\n"
+			       "check of the device.\n"));
+#endif
+		else
+			fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
+		bb_error_msg_and_die(0);
+	}
+	ctx->fs = fs;
+	fs->priv_data = ctx;
+	sb = fs->super;
+	if (sb->s_rev_level > E2FSCK_CURRENT_REV) {
+		bb_error_msg(_("while trying to open %s"),
+			ctx->filesystem_name);
+	get_newer:
+		bb_error_msg_and_die(_("Get a newer version of e2fsck!"));
+	}
+
+	/*
+	 * Set the device name, which is used whenever we print error
+	 * or informational messages to the user.
+	 */
+	if (ctx->device_name == 0 &&
+	    (sb->s_volume_name[0] != 0)) {
+		ctx->device_name = string_copy(sb->s_volume_name,
+					       sizeof(sb->s_volume_name));
+	}
+	if (ctx->device_name == 0)
+		ctx->device_name = ctx->filesystem_name;
+
+	/*
+	 * Make sure the ext3 superblock fields are consistent.
+	 */
+	retval = e2fsck_check_ext3_journal(ctx);
+	if (retval) {
+		bb_error_msg(_("while checking ext3 journal for %s"),
+			ctx->device_name);
+		bb_error_msg_and_die(0);
+	}
+
+	/*
+	 * Check to see if we need to do ext3-style recovery.  If so,
+	 * do it, and then restart the fsck.
+	 */
+	if (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) {
+		if (ctx->options & E2F_OPT_READONLY) {
+			printf(_("Warning: skipping journal recovery "
+				 "because doing a read-only filesystem "
+				 "check.\n"));
+			io_channel_flush(ctx->fs->io);
+		} else {
+			if (ctx->flags & E2F_FLAG_RESTARTED) {
+				/*
+				 * Whoops, we attempted to run the
+				 * journal twice.  This should never
+				 * happen, unless the hardware or
+				 * device driver is being bogus.
+				 */
+				bb_error_msg(_("can't set superblock flags on %s"), ctx->device_name);
+				bb_error_msg_and_die(0);
+			}
+			retval = e2fsck_run_ext3_journal(ctx);
+			if (retval) {
+				bb_error_msg(_("while recovering ext3 journal of %s"),
+					ctx->device_name);
+				bb_error_msg_and_die(0);
+			}
+			ext2fs_close(ctx->fs);
+			ctx->fs = 0;
+			ctx->flags |= E2F_FLAG_RESTARTED;
+			goto restart;
+		}
+	}
+
+	/*
+	 * Check for compatibility with the feature sets.  We need to
+	 * be more stringent than ext2fs_open().
+	 */
+	if ((sb->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) ||
+	    (sb->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP)) {
+		bb_error_msg("(%s)", ctx->device_name);
+		goto get_newer;
+	}
+	if (sb->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+		bb_error_msg("(%s)", ctx->device_name);
+		goto get_newer;
+	}
+#ifdef ENABLE_COMPRESSION
+	/* FIXME - do we support this at all? */
+	if (sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION)
+		bb_error_msg(_("warning: compression support is experimental"));
+#endif
+#ifndef ENABLE_HTREE
+	if (sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) {
+		bb_error_msg(_("E2fsck not compiled with HTREE support,\n\t"
+			  "but filesystem %s has HTREE directories."),
+			ctx->device_name);
+		goto get_newer;
+	}
+#endif
+
+	/*
+	 * If the user specified a specific superblock, presumably the
+	 * master superblock has been trashed.  So we mark the
+	 * superblock as dirty, so it can be written out.
+	 */
+	if (ctx->superblock &&
+	    !(ctx->options & E2F_OPT_READONLY))
+		ext2fs_mark_super_dirty(fs);
+
+	/*
+	 * We only update the master superblock because (a) paranoia;
+	 * we don't want to corrupt the backup superblocks, and (b) we
+	 * don't need to update the mount count and last checked
+	 * fields in the backup superblock (the kernel doesn't
+	 * update the backup superblocks anyway).
+	 */
+	fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+
+	ehandler_init(fs->io);
+
+	if (ctx->superblock)
+		set_latch_flags(PR_LATCH_RELOC, PRL_LATCHED, 0);
+	ext2fs_mark_valid(fs);
+	check_super_block(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		bb_error_msg_and_die(0);
+	check_if_skip(ctx);
+	if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+		bb_error_msg_and_die(0);
+#ifdef ENABLE_SWAPFS
+
+#ifdef WORDS_BIGENDIAN
+#define NATIVE_FLAG EXT2_FLAG_SWAP_BYTES
+#else
+#define NATIVE_FLAG 0
+#endif
+
+
+	if (normalize_swapfs) {
+		if ((fs->flags & EXT2_FLAG_SWAP_BYTES) == NATIVE_FLAG) {
+			fprintf(stderr, _("%s: Filesystem byte order "
+				"already normalized.\n"), ctx->device_name);
+			bb_error_msg_and_die(0);
+		}
+	}
+	if (swapfs) {
+		swap_filesys(ctx);
+		if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+			bb_error_msg_and_die(0);
+	}
+#endif
+
+	/*
+	 * Mark the system as valid, 'til proven otherwise
+	 */
+	ext2fs_mark_valid(fs);
+
+	retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
+	if (retval) {
+		bb_error_msg(_("while reading bad blocks inode"));
+		preenhalt(ctx);
+		printf(_("This doesn't bode well,"
+			 " but we'll try to go on...\n"));
+	}
+
+	run_result = e2fsck_run(ctx);
+	e2fsck_clear_progbar(ctx);
+	if (run_result == E2F_FLAG_RESTART) {
+		printf(_("Restarting e2fsck from the beginning...\n"));
+		retval = e2fsck_reset_context(ctx);
+		if (retval) {
+			bb_error_msg(_("while resetting context"));
+			bb_error_msg_and_die(0);
+		}
+		ext2fs_close(fs);
+		goto restart;
+	}
+	if (run_result & E2F_FLAG_CANCEL) {
+		printf(_("%s: e2fsck canceled.\n"), ctx->device_name ?
+		       ctx->device_name : ctx->filesystem_name);
+		exit_value |= FSCK_CANCELED;
+	}
+	if (run_result & E2F_FLAG_ABORT)
+		bb_error_msg_and_die(_("aborted"));
+
+	/* Cleanup */
+	if (ext2fs_test_changed(fs)) {
+		exit_value |= EXIT_NONDESTRUCT;
+		if (!(ctx->options & E2F_OPT_PREEN))
+		    printf(_("\n%s: ***** FILE SYSTEM WAS MODIFIED *****\n"),
+			       ctx->device_name);
+		if (ctx->mount_flags & EXT2_MF_ISROOT) {
+			printf(_("%s: ***** REBOOT LINUX *****\n"),
+			       ctx->device_name);
+			exit_value |= EXIT_DESTRUCT;
+		}
+	}
+	if (!ext2fs_test_valid(fs)) {
+		printf(_("\n%s: ********** WARNING: Filesystem still has "
+			 "errors **********\n\n"), ctx->device_name);
+		exit_value |= EXIT_UNCORRECTED;
+		exit_value &= ~EXIT_NONDESTRUCT;
+	}
+	if (exit_value & FSCK_CANCELED)
+		exit_value &= ~EXIT_NONDESTRUCT;
+	else {
+		show_stats(ctx);
+		if (!(ctx->options & E2F_OPT_READONLY)) {
+			if (ext2fs_test_valid(fs)) {
+				if (!(sb->s_state & EXT2_VALID_FS))
+					exit_value |= EXIT_NONDESTRUCT;
+				sb->s_state = EXT2_VALID_FS;
+			} else
+				sb->s_state &= ~EXT2_VALID_FS;
+			sb->s_mnt_count = 0;
+			sb->s_lastcheck = time(NULL);
+			ext2fs_mark_super_dirty(fs);
+		}
+	}
+
+	e2fsck_write_bitmaps(ctx);
+
+	ext2fs_close(fs);
+	ctx->fs = NULL;
+	free(ctx->filesystem_name);
+	free(ctx->journal_name);
+	e2fsck_free_context(ctx);
+
+	return exit_value;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsck.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsck.h
new file mode 100644
index 0000000..c159fab
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2fsck.h
@@ -0,0 +1,638 @@
+/* vi: set sw=4 ts=4: */
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <time.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stddef.h>
+#include <assert.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <mntent.h>
+#include <dirent.h>
+#include "ext2fs/kernel-list.h"
+#include <sys/types.h>
+#include <linux/types.h>
+
+/*
+ * Now pull in the real linux/jfs.h definitions.
+ */
+#include "ext2fs/kernel-jbd.h"
+
+
+
+#include "fsck.h"
+
+#include "ext2fs/ext2_fs.h"
+#include "blkid/blkid.h"
+#include "ext2fs/ext2_ext_attr.h"
+#include "uuid/uuid.h"
+#include "libbb.h"
+
+#ifdef HAVE_CONIO_H
+#undef HAVE_TERMIOS_H
+#include <conio.h>
+#define read_a_char()   getch()
+#else
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+#endif
+
+
+/*
+ * The last ext2fs revision level that this version of e2fsck is able to
+ * support
+ */
+#define E2FSCK_CURRENT_REV      1
+
+/* Used by the region allocation code */
+typedef __u32 region_addr_t;
+typedef struct region_struct *region_t;
+
+struct dx_dirblock_info {
+	int             type;
+	blk_t           phys;
+	int             flags;
+	blk_t           parent;
+	ext2_dirhash_t  min_hash;
+	ext2_dirhash_t  max_hash;
+	ext2_dirhash_t  node_min_hash;
+	ext2_dirhash_t  node_max_hash;
+};
+
+/*
+These defines are used in the type field of dx_dirblock_info
+*/
+
+#define DX_DIRBLOCK_ROOT        1
+#define DX_DIRBLOCK_LEAF        2
+#define DX_DIRBLOCK_NODE        3
+
+
+/*
+The following defines are used in the 'flags' field of a dx_dirblock_info
+*/
+#define DX_FLAG_REFERENCED      1
+#define DX_FLAG_DUP_REF         2
+#define DX_FLAG_FIRST           4
+#define DX_FLAG_LAST            8
+
+/*
+ * E2fsck options
+ */
+#define E2F_OPT_READONLY        0x0001
+#define E2F_OPT_PREEN           0x0002
+#define E2F_OPT_YES             0x0004
+#define E2F_OPT_NO              0x0008
+#define E2F_OPT_TIME            0x0010
+#define E2F_OPT_CHECKBLOCKS     0x0040
+#define E2F_OPT_DEBUG           0x0080
+#define E2F_OPT_FORCE           0x0100
+#define E2F_OPT_WRITECHECK      0x0200
+#define E2F_OPT_COMPRESS_DIRS   0x0400
+
+/*
+ * E2fsck flags
+ */
+#define E2F_FLAG_ABORT          0x0001 /* Abort signaled */
+#define E2F_FLAG_CANCEL         0x0002 /* Cancel signaled */
+#define E2F_FLAG_SIGNAL_MASK    0x0003
+#define E2F_FLAG_RESTART        0x0004 /* Restart signaled */
+
+#define E2F_FLAG_SETJMP_OK      0x0010 /* Setjmp valid for abort */
+
+#define E2F_FLAG_PROG_BAR       0x0020 /* Progress bar on screen */
+#define E2F_FLAG_PROG_SUPPRESS  0x0040 /* Progress suspended */
+#define E2F_FLAG_JOURNAL_INODE  0x0080 /* Create a new ext3 journal inode */
+#define E2F_FLAG_SB_SPECIFIED   0x0100 /* The superblock was explicitly
+					* specified by the user */
+#define E2F_FLAG_RESTARTED      0x0200 /* E2fsck has been restarted */
+#define E2F_FLAG_RESIZE_INODE   0x0400 /* Request to recreate resize inode */
+
+
+/*Don't know where these come from*/
+#define READ 0
+#define WRITE 1
+#define cpu_to_be32(n) htonl(n)
+#define be32_to_cpu(n) ntohl(n)
+
+/*
+ * We define a set of "latch groups"; these are problems which are
+ * handled as a set.  The user answers once for a particular latch
+ * group.
+ */
+#define PR_LATCH_MASK         0x0ff0 /* Latch mask */
+#define PR_LATCH_BLOCK        0x0010 /* Latch for illegal blocks (pass 1) */
+#define PR_LATCH_BBLOCK       0x0020 /* Latch for bad block inode blocks (pass 1) */
+#define PR_LATCH_IBITMAP      0x0030 /* Latch for pass 5 inode bitmap proc. */
+#define PR_LATCH_BBITMAP      0x0040 /* Latch for pass 5 inode bitmap proc. */
+#define PR_LATCH_RELOC        0x0050 /* Latch for superblock relocate hint */
+#define PR_LATCH_DBLOCK       0x0060 /* Latch for pass 1b dup block headers */
+#define PR_LATCH_LOW_DTIME    0x0070 /* Latch for pass1 orphaned list refugees */
+#define PR_LATCH_TOOBIG       0x0080 /* Latch for file to big errors */
+#define PR_LATCH_OPTIMIZE_DIR 0x0090 /* Latch for optimize directories */
+
+#define PR_LATCH(x)     ((((x) & PR_LATCH_MASK) >> 4) - 1)
+
+/*
+ * Latch group descriptor flags
+ */
+#define PRL_YES         0x0001  /* Answer yes */
+#define PRL_NO          0x0002  /* Answer no */
+#define PRL_LATCHED     0x0004  /* The latch group is latched */
+#define PRL_SUPPRESS    0x0008  /* Suppress all latch group questions */
+
+#define PRL_VARIABLE    0x000f  /* All the flags that need to be reset */
+
+/*
+ * Pre-Pass 1 errors
+ */
+
+#define PR_0_BB_NOT_GROUP       0x000001  /* Block bitmap not in group */
+#define PR_0_IB_NOT_GROUP       0x000002  /* Inode bitmap not in group */
+#define PR_0_ITABLE_NOT_GROUP   0x000003  /* Inode table not in group */
+#define PR_0_SB_CORRUPT         0x000004  /* Superblock corrupt */
+#define PR_0_FS_SIZE_WRONG      0x000005  /* Filesystem size is wrong */
+#define PR_0_NO_FRAGMENTS       0x000006  /* Fragments not supported */
+#define PR_0_BLOCKS_PER_GROUP   0x000007  /* Bad blocks_per_group */
+#define PR_0_FIRST_DATA_BLOCK   0x000008  /* Bad first_data_block */
+#define PR_0_ADD_UUID           0x000009  /* Adding UUID to filesystem */
+#define PR_0_RELOCATE_HINT      0x00000A  /* Relocate hint */
+#define PR_0_MISC_CORRUPT_SUPER 0x00000B  /* Miscellaneous superblock corruption */
+#define PR_0_GETSIZE_ERROR      0x00000C  /* Error determing physical device size of filesystem */
+#define PR_0_INODE_COUNT_WRONG  0x00000D  /* Inode count in the superblock incorrect */
+#define PR_0_HURD_CLEAR_FILETYPE 0x00000E /* The Hurd does not support the filetype feature */
+#define PR_0_JOURNAL_BAD_INODE  0x00000F  /* The Hurd does not support the filetype feature */
+#define PR_0_JOURNAL_UNSUPP_MULTIFS 0x000010 /* The external journal has multiple filesystems (which we can't handle yet) */
+#define PR_0_CANT_FIND_JOURNAL  0x000011  /* Can't find external journal */
+#define PR_0_EXT_JOURNAL_BAD_SUPER 0x000012/* External journal has bad superblock */
+#define PR_0_JOURNAL_BAD_UUID   0x000013  /* Superblock has a bad journal UUID */
+#define PR_0_JOURNAL_UNSUPP_SUPER 0x000014 /* Journal has an unknown superblock type */
+#define PR_0_JOURNAL_BAD_SUPER  0x000015  /* Journal superblock is corrupt */
+#define PR_0_JOURNAL_HAS_JOURNAL 0x000016 /* Journal superblock is corrupt */
+#define PR_0_JOURNAL_RECOVER_SET 0x000017 /* Superblock has recovery flag set but no journal */
+#define PR_0_JOURNAL_RECOVERY_CLEAR 0x000018 /* Journal has data, but recovery flag is clear */
+#define PR_0_JOURNAL_RESET_JOURNAL 0x000019 /* Ask if we should clear the journal */
+#define PR_0_FS_REV_LEVEL       0x00001A  /* Filesystem revision is 0, but feature flags are set */
+#define PR_0_ORPHAN_CLEAR_INODE             0x000020 /* Clearing orphan inode */
+#define PR_0_ORPHAN_ILLEGAL_BLOCK_NUM       0x000021 /* Illegal block found in orphaned inode */
+#define PR_0_ORPHAN_ALREADY_CLEARED_BLOCK   0x000022 /* Already cleared block found in orphaned inode */
+#define PR_0_ORPHAN_ILLEGAL_HEAD_INODE      0x000023 /* Illegal orphan inode in superblock */
+#define PR_0_ORPHAN_ILLEGAL_INODE           0x000024 /* Illegal inode in orphaned inode list */
+#define PR_0_JOURNAL_UNSUPP_ROCOMPAT        0x000025 /* Journal has unsupported read-only feature - abort */
+#define PR_0_JOURNAL_UNSUPP_INCOMPAT        0x000026 /* Journal has unsupported incompatible feature - abort */
+#define PR_0_JOURNAL_UNSUPP_VERSION         0x000027 /* Journal has unsupported version number */
+#define PR_0_MOVE_JOURNAL                   0x000028 /* Moving journal to hidden file */
+#define PR_0_ERR_MOVE_JOURNAL               0x000029 /* Error moving journal */
+#define PR_0_CLEAR_V2_JOURNAL               0x00002A /* Clearing V2 journal superblock */
+#define PR_0_JOURNAL_RUN                    0x00002B /* Run journal anyway */
+#define PR_0_JOURNAL_RUN_DEFAULT            0x00002C /* Run journal anyway by default */
+#define PR_0_BACKUP_JNL                     0x00002D /* Backup journal inode blocks */
+#define PR_0_NONZERO_RESERVED_GDT_BLOCKS    0x00002E /* Reserved blocks w/o resize_inode */
+#define PR_0_CLEAR_RESIZE_INODE             0x00002F /* Resize_inode not enabled, but resize inode is non-zero */
+#define PR_0_RESIZE_INODE_INVALID           0x000030 /* Resize inode invalid */
+
+/*
+ * Pass 1 errors
+ */
+
+#define PR_1_PASS_HEADER              0x010000  /* Pass 1: Checking inodes, blocks, and sizes */
+#define PR_1_ROOT_NO_DIR              0x010001  /* Root directory is not an inode */
+#define PR_1_ROOT_DTIME               0x010002  /* Root directory has dtime set */
+#define PR_1_RESERVED_BAD_MODE        0x010003  /* Reserved inode has bad mode */
+#define PR_1_ZERO_DTIME               0x010004  /* Deleted inode has zero dtime */
+#define PR_1_SET_DTIME                0x010005  /* Inode in use, but dtime set */
+#define PR_1_ZERO_LENGTH_DIR          0x010006  /* Zero-length directory */
+#define PR_1_BB_CONFLICT              0x010007  /* Block bitmap conflicts with some other fs block */
+#define PR_1_IB_CONFLICT              0x010008  /* Inode bitmap conflicts with some other fs block */
+#define PR_1_ITABLE_CONFLICT          0x010009  /* Inode table conflicts with some other fs block */
+#define PR_1_BB_BAD_BLOCK             0x01000A  /* Block bitmap is on a bad block */
+#define PR_1_IB_BAD_BLOCK             0x01000B  /* Inode bitmap is on a bad block */
+#define PR_1_BAD_I_SIZE               0x01000C  /* Inode has incorrect i_size */
+#define PR_1_BAD_I_BLOCKS             0x01000D  /* Inode has incorrect i_blocks */
+#define PR_1_ILLEGAL_BLOCK_NUM        0x01000E  /* Illegal block number in inode */
+#define PR_1_BLOCK_OVERLAPS_METADATA  0x01000F  /* Block number overlaps fs metadata */
+#define PR_1_INODE_BLOCK_LATCH        0x010010  /* Inode has illegal blocks (latch question) */
+#define PR_1_TOO_MANY_BAD_BLOCKS      0x010011  /* Too many bad blocks in inode */
+#define PR_1_BB_ILLEGAL_BLOCK_NUM     0x010012  /* Illegal block number in bad block inode */
+#define PR_1_INODE_BBLOCK_LATCH       0x010013  /* Bad block inode has illegal blocks (latch question) */
+#define PR_1_DUP_BLOCKS_PREENSTOP     0x010014  /* Duplicate or bad blocks in use! */
+#define PR_1_BBINODE_BAD_METABLOCK    0x010015  /* Bad block used as bad block indirect block */
+#define PR_1_BBINODE_BAD_METABLOCK_PROMPT 0x010016 /* Inconsistency can't be fixed prompt */
+#define PR_1_BAD_PRIMARY_BLOCK        0x010017  /* Bad primary block */
+#define PR_1_BAD_PRIMARY_BLOCK_PROMPT 0x010018  /* Bad primary block prompt */
+#define PR_1_BAD_PRIMARY_SUPERBLOCK   0x010019  /* Bad primary superblock */
+#define PR_1_BAD_PRIMARY_GROUP_DESCRIPTOR 0x01001A /* Bad primary block group descriptors */
+#define PR_1_BAD_SUPERBLOCK           0x01001B  /* Bad superblock in group */
+#define PR_1_BAD_GROUP_DESCRIPTORS    0x01001C  /* Bad block group descriptors in group */
+#define PR_1_PROGERR_CLAIMED_BLOCK    0x01001D  /* Block claimed for no reason */
+#define PR_1_RELOC_BLOCK_ALLOCATE     0x01001E  /* Error allocating blocks for relocating metadata */
+#define PR_1_RELOC_MEMORY_ALLOCATE    0x01001F  /* Error allocating block buffer during relocation process */
+#define PR_1_RELOC_FROM_TO            0x010020  /* Relocating metadata group information from X to Y */
+#define PR_1_RELOC_TO                 0x010021  /* Relocating metatdata group information to X */
+#define PR_1_RELOC_READ_ERR           0x010022  /* Block read error during relocation process */
+#define PR_1_RELOC_WRITE_ERR          0x010023  /* Block write error during relocation process */
+#define PR_1_ALLOCATE_IBITMAP_ERROR   0x010024  /* Error allocating inode bitmap */
+#define PR_1_ALLOCATE_BBITMAP_ERROR   0x010025  /* Error allocating block bitmap */
+#define PR_1_ALLOCATE_ICOUNT          0x010026  /* Error allocating icount structure */
+#define PR_1_ALLOCATE_DBCOUNT         0x010027  /* Error allocating dbcount */
+#define PR_1_ISCAN_ERROR              0x010028  /* Error while scanning inodes */
+#define PR_1_BLOCK_ITERATE            0x010029  /* Error while iterating over blocks */
+#define PR_1_ICOUNT_STORE             0x01002A  /* Error while storing inode count information */
+#define PR_1_ADD_DBLOCK               0x01002B  /* Error while storing directory block information */
+#define PR_1_READ_INODE               0x01002C  /* Error while reading inode (for clearing) */
+#define PR_1_SUPPRESS_MESSAGES        0x01002D  /* Suppress messages prompt */
+#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 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 */
+#define PR_1_LOW_DTIME                0x010036  /* Inode that was part of orphan linked list */
+#define PR_1_ORPHAN_LIST_REFUGEES     0x010037  /* Latch question which asks how to deal with low dtime inodes */
+#define PR_1_ALLOCATE_REFCOUNT        0x010038  /* Error allocating refcount structure */
+#define PR_1_READ_EA_BLOCK            0x010039  /* Error reading Extended Attribute block */
+#define PR_1_BAD_EA_BLOCK             0x01003A  /* Invalid Extended Attribute block */
+#define PR_1_EXTATTR_READ_ABORT   0x01003B  /* Error reading Extended Attribute block while fixing refcount -- abort */
+#define PR_1_EXTATTR_REFCOUNT         0x01003C  /* Extended attribute reference count incorrect */
+#define PR_1_EXTATTR_WRITE            0x01003D  /* Error writing Extended Attribute block while fixing refcount */
+#define PR_1_EA_MULTI_BLOCK           0x01003E  /* Multiple EA blocks not supported */
+#define PR_1_EA_ALLOC_REGION          0x01003F  /* Error allocating EA region allocation structure */
+#define PR_1_EA_ALLOC_COLLISION       0x010040  /* Error EA allocation collision */
+#define PR_1_EA_BAD_NAME              0x010041  /* Bad extended attribute name */
+#define PR_1_EA_BAD_VALUE             0x010042  /* Bad extended attribute value */
+#define PR_1_INODE_TOOBIG             0x010043  /* Inode too big (latch question) */
+#define PR_1_TOOBIG_DIR               0x010044  /* Directory too big */
+#define PR_1_TOOBIG_REG               0x010045  /* Regular file too big */
+#define PR_1_TOOBIG_SYMLINK           0x010046  /* Symlink too big */
+#define PR_1_HTREE_SET                0x010047  /* INDEX_FL flag set on a non-HTREE filesystem */
+#define PR_1_HTREE_NODIR              0x010048  /* INDEX_FL flag set on a non-directory */
+#define PR_1_HTREE_BADROOT            0x010049  /* Invalid root node in HTREE directory */
+#define PR_1_HTREE_HASHV              0x01004A  /* Unsupported hash version in HTREE directory */
+#define PR_1_HTREE_INCOMPAT           0x01004B  /* Incompatible flag in HTREE root node */
+#define PR_1_HTREE_DEPTH              0x01004C  /* HTREE too deep */
+#define PR_1_BB_FS_BLOCK   0x01004D  /* Bad block has indirect block that conflicts with filesystem block */
+#define PR_1_RESIZE_INODE_CREATE      0x01004E  /* Resize inode failed */
+#define PR_1_EXTRA_ISIZE              0x01004F  /* inode->i_size is too long */
+#define PR_1_ATTR_NAME_LEN            0x010050  /* attribute name is too long */
+#define PR_1_ATTR_VALUE_OFFSET        0x010051  /* wrong EA value offset */
+#define PR_1_ATTR_VALUE_BLOCK         0x010052  /* wrong EA blocknumber */
+#define PR_1_ATTR_VALUE_SIZE          0x010053  /* wrong EA value size */
+#define PR_1_ATTR_HASH                0x010054  /* wrong EA hash value */
+
+/*
+ * Pass 1b errors
+ */
+
+#define PR_1B_PASS_HEADER       0x011000  /* Pass 1B: Rescan for duplicate/bad blocks */
+#define PR_1B_DUP_BLOCK_HEADER  0x011001  /* Duplicate/bad block(s) header */
+#define PR_1B_DUP_BLOCK         0x011002  /* Duplicate/bad block(s) in inode */
+#define PR_1B_DUP_BLOCK_END     0x011003  /* Duplicate/bad block(s) end */
+#define PR_1B_ISCAN_ERROR       0x011004  /* Error while scanning inodes */
+#define PR_1B_ALLOCATE_IBITMAP_ERROR 0x011005  /* Error allocating inode bitmap */
+#define PR_1B_BLOCK_ITERATE     0x0110006  /* Error while iterating over blocks */
+#define PR_1B_ADJ_EA_REFCOUNT   0x0110007  /* Error adjusting EA refcount */
+#define PR_1C_PASS_HEADER       0x012000  /* Pass 1C: Scan directories for inodes with dup blocks. */
+#define PR_1D_PASS_HEADER       0x013000  /* Pass 1D: Reconciling duplicate blocks */
+#define PR_1D_DUP_FILE          0x013001  /* File has duplicate blocks */
+#define PR_1D_DUP_FILE_LIST     0x013002  /* List of files sharing duplicate blocks */
+#define PR_1D_SHARE_METADATA    0x013003  /* File sharing blocks with filesystem metadata  */
+#define PR_1D_NUM_DUP_INODES    0x013004  /* Report of how many duplicate/bad inodes */
+#define PR_1D_DUP_BLOCKS_DEALT  0x013005  /* Duplicated blocks already reassigned or cloned. */
+#define PR_1D_CLONE_QUESTION    0x013006  /* Clone duplicate/bad blocks? */
+#define PR_1D_DELETE_QUESTION   0x013007  /* Delete file? */
+#define PR_1D_CLONE_ERROR       0x013008  /* Couldn't clone file (error) */
+
+/*
+ * Pass 2 errors
+ */
+
+#define PR_2_PASS_HEADER        0x020000  /* Pass 2: Checking directory structure */
+#define PR_2_BAD_INODE_DOT      0x020001  /* Bad inode number for '.' */
+#define PR_2_BAD_INO            0x020002  /* Directory entry has bad inode number */
+#define PR_2_UNUSED_INODE       0x020003  /* Directory entry has deleted or unused inode */
+#define PR_2_LINK_DOT           0x020004  /* Directry entry is link to '.' */
+#define PR_2_BB_INODE           0x020005  /* Directory entry points to inode now located in a bad block */
+#define PR_2_LINK_DIR           0x020006  /* Directory entry contains a link to a directory */
+#define PR_2_LINK_ROOT          0x020007  /* Directory entry contains a link to the root directry */
+#define PR_2_BAD_NAME           0x020008  /* Directory entry has illegal characters in its name */
+#define PR_2_MISSING_DOT        0x020009  /* Missing '.' in directory inode */
+#define PR_2_MISSING_DOT_DOT    0x02000A  /* Missing '..' in directory inode */
+#define PR_2_1ST_NOT_DOT        0x02000B  /* First entry in directory inode doesn't contain '.' */
+#define PR_2_2ND_NOT_DOT_DOT    0x02000C  /* Second entry in directory inode doesn't contain '..' */
+#define PR_2_FADDR_ZERO         0x02000D  /* i_faddr should be zero */
+#define PR_2_FILE_ACL_ZERO      0x02000E  /* i_file_acl should be zero */
+#define PR_2_DIR_ACL_ZERO       0x02000F  /* i_dir_acl should be zero */
+#define PR_2_FRAG_ZERO          0x020010  /* i_frag should be zero */
+#define PR_2_FSIZE_ZERO         0x020011  /* i_fsize should be zero */
+#define PR_2_BAD_MODE           0x020012  /* inode has bad mode */
+#define PR_2_DIR_CORRUPTED      0x020013  /* directory corrupted */
+#define PR_2_FILENAME_LONG      0x020014  /* filename too long */
+#define PR_2_DIRECTORY_HOLE     0x020015  /* Directory inode has a missing block (hole) */
+#define PR_2_DOT_NULL_TERM      0x020016  /* '.' is not NULL terminated */
+#define PR_2_DOT_DOT_NULL_TERM  0x020017  /* '..' is not NULL terminated */
+#define PR_2_BAD_CHAR_DEV       0x020018  /* Illegal character device in inode */
+#define PR_2_BAD_BLOCK_DEV      0x020019  /* Illegal block device in inode */
+#define PR_2_DUP_DOT            0x02001A  /* Duplicate '.' entry */
+#define PR_2_DUP_DOT_DOT        0x02001B  /* Duplicate '..' entry */
+#define PR_2_NO_DIRINFO         0x02001C  /* Internal error: couldn't find dir_info */
+#define PR_2_FINAL_RECLEN       0x02001D  /* Final rec_len is wrong */
+#define PR_2_ALLOCATE_ICOUNT    0x02001E  /* Error allocating icount structure */
+#define PR_2_DBLIST_ITERATE     0x02001F  /* Error iterating over directory blocks */
+#define PR_2_READ_DIRBLOCK      0x020020  /* Error reading directory block */
+#define PR_2_WRITE_DIRBLOCK     0x020021  /* Error writing directory block */
+#define PR_2_ALLOC_DIRBOCK      0x020022  /* Error allocating new directory block */
+#define PR_2_DEALLOC_INODE      0x020023  /* Error deallocating inode */
+#define PR_2_SPLIT_DOT          0x020024  /* Directory entry for '.' is big.  Split? */
+#define PR_2_BAD_FIFO           0x020025  /* Illegal FIFO */
+#define PR_2_BAD_SOCKET         0x020026  /* Illegal socket */
+#define PR_2_SET_FILETYPE       0x020027  /* Directory filetype not set */
+#define PR_2_BAD_FILETYPE       0x020028  /* Directory filetype incorrect */
+#define PR_2_CLEAR_FILETYPE     0x020029  /* Directory filetype set when it shouldn't be */
+#define PR_2_NULL_NAME          0x020030  /* Directory filename can't be zero-length  */
+#define PR_2_INVALID_SYMLINK    0x020031  /* Invalid symlink */
+#define PR_2_FILE_ACL_BAD       0x020032  /* i_file_acl (extended attribute) is bad */
+#define PR_2_FEATURE_LARGE_FILES 0x020033  /* Filesystem contains large files, but has no such flag in sb */
+#define PR_2_HTREE_NOTREF       0x020034  /* Node in HTREE directory not referenced */
+#define PR_2_HTREE_DUPREF       0x020035  /* Node in HTREE directory referenced twice */
+#define PR_2_HTREE_MIN_HASH     0x020036  /* Node in HTREE directory has bad min hash */
+#define PR_2_HTREE_MAX_HASH     0x020037  /* Node in HTREE directory has bad max hash */
+#define PR_2_HTREE_CLEAR        0x020038  /* Clear invalid HTREE directory */
+#define PR_2_HTREE_BADBLK       0x02003A  /* Bad block in htree interior node */
+#define PR_2_ADJ_EA_REFCOUNT    0x02003B  /* Error adjusting EA refcount */
+#define PR_2_HTREE_BAD_ROOT     0x02003C  /* Invalid HTREE root node */
+#define PR_2_HTREE_BAD_LIMIT    0x02003D  /* Invalid HTREE limit */
+#define PR_2_HTREE_BAD_COUNT    0x02003E  /* Invalid HTREE count */
+#define PR_2_HTREE_HASH_ORDER   0x02003F  /* HTREE interior node has out-of-order hashes in table */
+#define PR_2_HTREE_BAD_DEPTH    0x020040  /* Node in HTREE directory has bad depth */
+#define PR_2_DUPLICATE_DIRENT   0x020041  /* Duplicate directory entry found */
+#define PR_2_NON_UNIQUE_FILE    0x020042  /* Non-unique filename found */
+#define PR_2_REPORT_DUP_DIRENT  0x020043  /* Duplicate directory entry found */
+
+/*
+ * Pass 3 errors
+ */
+
+#define PR_3_PASS_HEADER            0x030000  /* Pass 3: Checking directory connectivity */
+#define PR_3_NO_ROOT_INODE          0x030001  /* Root inode not allocated */
+#define PR_3_EXPAND_LF_DIR          0x030002  /* No room in lost+found */
+#define PR_3_UNCONNECTED_DIR        0x030003  /* Unconnected directory inode */
+#define PR_3_NO_LF_DIR              0x030004  /* /lost+found not found */
+#define PR_3_BAD_DOT_DOT            0x030005  /* .. entry is incorrect */
+#define PR_3_NO_LPF                 0x030006  /* Bad or non-existent /lost+found.  Cannot reconnect */
+#define PR_3_CANT_EXPAND_LPF        0x030007  /* Could not expand /lost+found */
+#define PR_3_CANT_RECONNECT         0x030008  /* Could not reconnect inode */
+#define PR_3_ERR_FIND_LPF           0x030009  /* Error while trying to find /lost+found */
+#define PR_3_ERR_LPF_NEW_BLOCK      0x03000A  /* Error in ext2fs_new_block while creating /lost+found */
+#define PR_3_ERR_LPF_NEW_INODE      0x03000B  /* Error in ext2fs_new_inode while creating /lost+found */
+#define PR_3_ERR_LPF_NEW_DIR_BLOCK  0x03000C  /* Error in ext2fs_new_dir_block while creating /lost+found */
+#define PR_3_ERR_LPF_WRITE_BLOCK    0x03000D  /* Error while writing directory block for /lost+found */
+#define PR_3_ADJUST_INODE           0x03000E  /* Error while adjusting inode count */
+#define PR_3_FIX_PARENT_ERR         0x03000F  /* Couldn't fix parent directory -- error */
+#define PR_3_FIX_PARENT_NOFIND      0x030010  /* Couldn't fix parent directory -- couldn't find it */
+#define PR_3_ALLOCATE_IBITMAP_ERROR 0x030011  /* Error allocating inode bitmap */
+#define PR_3_CREATE_ROOT_ERROR      0x030012  /* Error creating root directory */
+#define PR_3_CREATE_LPF_ERROR       0x030013  /* Error creating lost and found directory */
+#define PR_3_ROOT_NOT_DIR_ABORT     0x030014  /* Root inode is not directory; aborting */
+#define PR_3_NO_ROOT_INODE_ABORT    0x030015  /* Cannot proceed without a root inode. */
+#define PR_3_NO_DIRINFO             0x030016  /* Internal error: couldn't find dir_info */
+#define PR_3_LPF_NOTDIR             0x030017  /* Lost+found is not a directory */
+
+/*
+ * Pass 3a --- rehashing diretories
+ */
+#define PR_3A_PASS_HEADER         0x031000  /* Pass 3a: Reindexing directories */
+#define PR_3A_OPTIMIZE_ITER       0x031001  /* Error iterating over directories */
+#define PR_3A_OPTIMIZE_DIR_ERR    0x031002  /* Error rehash directory */
+#define PR_3A_OPTIMIZE_DIR_HEADER 0x031003  /* Rehashing dir header */
+#define PR_3A_OPTIMIZE_DIR        0x031004  /* Rehashing directory %d */
+#define PR_3A_OPTIMIZE_DIR_END    0x031005  /* Rehashing dir end */
+
+/*
+ * Pass 4 errors
+ */
+
+#define PR_4_PASS_HEADER        0x040000  /* Pass 4: Checking reference counts */
+#define PR_4_ZERO_LEN_INODE     0x040001  /* Unattached zero-length inode */
+#define PR_4_UNATTACHED_INODE   0x040002  /* Unattached inode */
+#define PR_4_BAD_REF_COUNT      0x040003  /* Inode ref count wrong */
+#define PR_4_INCONSISTENT_COUNT 0x040004  /* Inconsistent inode count information cached */
+
+/*
+ * Pass 5 errors
+ */
+
+#define PR_5_PASS_HEADER            0x050000  /* Pass 5: Checking group summary information */
+#define PR_5_INODE_BMAP_PADDING     0x050001  /* Padding at end of inode bitmap is not set. */
+#define PR_5_BLOCK_BMAP_PADDING     0x050002  /* Padding at end of block bitmap is not set. */
+#define PR_5_BLOCK_BITMAP_HEADER    0x050003  /* Block bitmap differences header */
+#define PR_5_BLOCK_UNUSED           0x050004  /* Block not used, but marked in bitmap */
+#define PR_5_BLOCK_USED             0x050005  /* Block used, but not marked used in bitmap */
+#define PR_5_BLOCK_BITMAP_END       0x050006  /* Block bitmap differences end */
+#define PR_5_INODE_BITMAP_HEADER    0x050007  /* Inode bitmap differences header */
+#define PR_5_INODE_UNUSED           0x050008  /* Inode not used, but marked in bitmap */
+#define PR_5_INODE_USED             0x050009  /* Inode used, but not marked used in bitmap */
+#define PR_5_INODE_BITMAP_END       0x05000A  /* Inode bitmap differences end */
+#define PR_5_FREE_INODE_COUNT_GROUP 0x05000B  /* Free inodes count for group wrong */
+#define PR_5_FREE_DIR_COUNT_GROUP   0x05000C  /* Directories count for group wrong */
+#define PR_5_FREE_INODE_COUNT       0x05000D  /* Free inodes count wrong */
+#define PR_5_FREE_BLOCK_COUNT_GROUP 0x05000E  /* Free blocks count for group wrong */
+#define PR_5_FREE_BLOCK_COUNT       0x05000F  /* Free blocks count wrong */
+#define PR_5_BMAP_ENDPOINTS         0x050010  /* Programming error: bitmap endpoints don't match */
+#define PR_5_FUDGE_BITMAP_ERROR     0x050011  /* Internal error: fudging end of bitmap */
+#define PR_5_COPY_IBITMAP_ERROR     0x050012  /* Error copying in replacement inode bitmap */
+#define PR_5_COPY_BBITMAP_ERROR     0x050013  /* Error copying in replacement block bitmap */
+#define PR_5_BLOCK_RANGE_UNUSED     0x050014  /* Block range not used, but marked in bitmap */
+#define PR_5_BLOCK_RANGE_USED       0x050015  /* Block range used, but not marked used in bitmap */
+#define PR_5_INODE_RANGE_UNUSED     0x050016  /* Inode range not used, but marked in bitmap */
+#define PR_5_INODE_RANGE_USED       0x050017  /* Inode rangeused, but not marked used in bitmap */
+
+
+/*
+ * The directory information structure; stores directory information
+ * collected in earlier passes, to avoid disk i/o in fetching the
+ * directory information.
+ */
+struct dir_info {
+	ext2_ino_t              ino;    /* Inode number */
+	ext2_ino_t              dotdot; /* Parent according to '..' */
+	ext2_ino_t              parent; /* Parent according to treewalk */
+};
+
+
+
+/*
+ * The indexed directory information structure; stores information for
+ * directories which contain a hash tree index.
+ */
+struct dx_dir_info {
+	ext2_ino_t              ino;            /* Inode number */
+	int                     numblocks;      /* number of blocks */
+	int                     hashversion;
+	short                   depth;          /* depth of tree */
+	struct dx_dirblock_info *dx_block;      /* Array of size numblocks */
+};
+
+/*
+ * Define the extended attribute refcount structure
+ */
+typedef struct ea_refcount *ext2_refcount_t;
+
+struct e2fsck_struct {
+	ext2_filsys fs;
+	const char *program_name;
+	char *filesystem_name;
+	char *device_name;
+	char *io_options;
+	int     flags;          /* E2fsck internal flags */
+	int     options;
+	blk_t   use_superblock; /* sb requested by user */
+	blk_t   superblock;     /* sb used to open fs */
+	int     blocksize;      /* blocksize */
+	blk_t   num_blocks;     /* Total number of blocks */
+	int     mount_flags;
+	blkid_cache blkid;      /* blkid cache */
+
+	jmp_buf abort_loc;
+
+	unsigned long abort_code;
+
+	int (*progress)(e2fsck_t ctx, int pass, unsigned long cur,
+			unsigned long max);
+
+	ext2fs_inode_bitmap inode_used_map; /* Inodes which are in use */
+	ext2fs_inode_bitmap inode_bad_map; /* Inodes which are bad somehow */
+	ext2fs_inode_bitmap inode_dir_map; /* Inodes which are directories */
+	ext2fs_inode_bitmap inode_imagic_map; /* AFS inodes */
+	ext2fs_inode_bitmap inode_reg_map; /* Inodes which are regular files*/
+
+	ext2fs_block_bitmap block_found_map; /* Blocks which are in use */
+	ext2fs_block_bitmap block_dup_map; /* Blks referenced more than once */
+	ext2fs_block_bitmap block_ea_map; /* Blocks which are used by EA's */
+
+	/*
+	 * Inode count arrays
+	 */
+	ext2_icount_t   inode_count;
+	ext2_icount_t inode_link_info;
+
+	ext2_refcount_t refcount;
+	ext2_refcount_t refcount_extra;
+
+	/*
+	 * Array of flags indicating whether an inode bitmap, block
+	 * bitmap, or inode table is invalid
+	 */
+	int *invalid_inode_bitmap_flag;
+	int *invalid_block_bitmap_flag;
+	int *invalid_inode_table_flag;
+	int invalid_bitmaps;    /* There are invalid bitmaps/itable */
+
+	/*
+	 * Block buffer
+	 */
+	char *block_buf;
+
+	/*
+	 * For pass1_check_directory and pass1_get_blocks
+	 */
+	ext2_ino_t stashed_ino;
+	struct ext2_inode *stashed_inode;
+
+	/*
+	 * Location of the lost and found directory
+	 */
+	ext2_ino_t lost_and_found;
+	int bad_lost_and_found;
+
+	/*
+	 * Directory information
+	 */
+	int             dir_info_count;
+	int             dir_info_size;
+	struct dir_info *dir_info;
+
+	/*
+	 * Indexed directory information
+	 */
+	int             dx_dir_info_count;
+	int             dx_dir_info_size;
+	struct dx_dir_info *dx_dir_info;
+
+	/*
+	 * Directories to hash
+	 */
+	ext2_u32_list   dirs_to_hash;
+
+	/*
+	 * Tuning parameters
+	 */
+	int process_inode_size;
+	int inode_buffer_blocks;
+
+	/*
+	 * ext3 journal support
+	 */
+	io_channel      journal_io;
+	char    *journal_name;
+
+	/*
+	 * How we display the progress update (for unix)
+	 */
+	int progress_fd;
+	int progress_pos;
+	int progress_last_percent;
+	unsigned int progress_last_time;
+	int interactive;        /* Are we connected directly to a tty? */
+	char start_meta[2], stop_meta[2];
+
+	/* File counts */
+	int fs_directory_count;
+	int fs_regular_count;
+	int fs_blockdev_count;
+	int fs_chardev_count;
+	int fs_links_count;
+	int fs_symlinks_count;
+	int fs_fast_symlinks_count;
+	int fs_fifo_count;
+	int fs_total_count;
+	int fs_sockets_count;
+	int fs_ind_count;
+	int fs_dind_count;
+	int fs_tind_count;
+	int fs_fragmented;
+	int large_files;
+	int fs_ext_attr_inodes;
+	int fs_ext_attr_blocks;
+
+	int ext_attr_ver;
+
+	/*
+	 * For the use of callers of the e2fsck functions; not used by
+	 * e2fsck functions themselves.
+	 */
+	void *priv_data;
+};
+
+
+#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);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src
new file mode 100644
index 0000000..482630c
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/Kbuild.src
@@ -0,0 +1,18 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+NEEDED-$(CONFIG_CHATTR) = y
+NEEDED-$(CONFIG_LSATTR) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+
+INSERT
+
+lib-$(NEEDED-y) += fgetsetflags.o fgetsetversion.o pf.o iod.o mntopts.o \
+           feature.o ls.o uuid.o pe.o ostype.o ps.o hashstr.o \
+           parse_num.o
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/e2p.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/e2p.h
new file mode 100644
index 0000000..bad2d6a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/e2p.h
@@ -0,0 +1,64 @@
+/* vi: set sw=4 ts=4: */
+#include "libbb.h"
+#include <sys/types.h>		/* Needed by dirent.h on netbsd */
+#include <stdio.h>
+#include <dirent.h>
+
+#include "../ext2fs/ext2_fs.h"
+
+#define E2P_FEATURE_COMPAT	0
+#define E2P_FEATURE_INCOMPAT	1
+#define E2P_FEATURE_RO_INCOMPAT	2
+#ifndef EXT3_FEATURE_INCOMPAT_EXTENTS
+#define EXT3_FEATURE_INCOMPAT_EXTENTS           0x0040
+#endif
+
+/* `options' for print_e2flags() */
+
+#define PFOPT_LONG  1 /* Must be 1 for compatibility with `int long_format'. */
+
+/*int fgetversion (const char * name, unsigned long * version);*/
+/*int fsetversion (const char * name, unsigned long version);*/
+int fgetsetversion(const char * name, unsigned long * get_version, unsigned long set_version);
+#define fgetversion(name, version) fgetsetversion(name, version, 0)
+#define fsetversion(name, version) fgetsetversion(name, NULL, version)
+
+/*int fgetflags (const char * name, unsigned long * flags);*/
+/*int fsetflags (const char * name, unsigned long flags);*/
+int fgetsetflags(const char * name, unsigned long * get_flags, unsigned long set_flags);
+#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
+#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
+
+int getflags (int fd, unsigned long * flags);
+int getversion (int fd, unsigned long * version);
+int iterate_on_dir (const char * dir_name,
+		    int (*func) (const char *, struct dirent *, void *),
+		    void * private);
+/*void list_super(struct ext2_super_block * s);*/
+void list_super2(struct ext2_super_block * s, FILE *f);
+#define list_super(s) list_super2(s, stdout)
+void print_fs_errors (FILE *f, unsigned short errors);
+void print_flags (FILE *f, unsigned long flags, unsigned options);
+void print_fs_state (FILE *f, unsigned short state);
+int setflags (int fd, unsigned long flags);
+int setversion (int fd, unsigned long version);
+
+const char *e2p_feature2string(int compat, unsigned int mask);
+int e2p_string2feature(char *string, int *compat, unsigned int *mask);
+int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array);
+
+int e2p_is_null_uuid(void *uu);
+void e2p_uuid_to_str(void *uu, char *out);
+const char *e2p_uuid2str(void *uu);
+
+const char *e2p_hash2string(int num);
+int e2p_string2hash(char *string);
+
+const char *e2p_mntopt2string(unsigned int mask);
+int e2p_string2mntopt(char *string, unsigned int *mask);
+int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok);
+
+unsigned long parse_num_blocks(const char *arg, int log_block_size);
+
+char *e2p_os2string(int os_type);
+int e2p_string2os(char *str);
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/feature.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/feature.c
new file mode 100644
index 0000000..b45754f
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/feature.c
@@ -0,0 +1,187 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * feature.c --- convert between features and strings
+ *
+ * Copyright (C) 1999  Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+
+struct feature {
+	int		compat;
+	unsigned int	mask;
+	const char	*string;
+};
+
+static const struct feature feature_list[] = {
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_PREALLOC,
+			"dir_prealloc" },
+	{	E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL,
+			"has_journal" },
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_IMAGIC_INODES,
+			"imagic_inodes" },
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_EXT_ATTR,
+			"ext_attr" },
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX,
+			"dir_index" },
+	{	E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_RESIZE_INODE,
+			"resize_inode" },
+	{	E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER,
+			"sparse_super" },
+	{	E2P_FEATURE_RO_INCOMPAT, EXT2_FEATURE_RO_COMPAT_LARGE_FILE,
+			"large_file" },
+	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_COMPRESSION,
+			"compression" },
+	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_FILETYPE,
+			"filetype" },
+	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_RECOVER,
+			"needs_recovery" },
+	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_JOURNAL_DEV,
+			"journal_dev" },
+	{	E2P_FEATURE_INCOMPAT, EXT3_FEATURE_INCOMPAT_EXTENTS,
+			"extents" },
+	{	E2P_FEATURE_INCOMPAT, EXT2_FEATURE_INCOMPAT_META_BG,
+			"meta_bg" },
+	{	0, 0, 0 },
+};
+
+const char *e2p_feature2string(int compat, unsigned int mask)
+{
+	const struct feature *f;
+	static char buf[20];
+	char fchar;
+	int fnum;
+
+	for (f = feature_list; f->string; f++) {
+		if ((compat == f->compat) &&
+		    (mask == f->mask))
+			return f->string;
+	}
+	switch (compat) {
+	case E2P_FEATURE_COMPAT:
+		fchar = 'C';
+		break;
+	case E2P_FEATURE_INCOMPAT:
+		fchar = 'I';
+		break;
+	case E2P_FEATURE_RO_INCOMPAT:
+		fchar = 'R';
+		break;
+	default:
+		fchar = '?';
+		break;
+	}
+	for (fnum = 0; mask >>= 1; fnum++);
+		sprintf(buf, "FEATURE_%c%d", fchar, fnum);
+	return buf;
+}
+
+int e2p_string2feature(char *string, int *compat_type, unsigned int *mask)
+{
+	const struct feature *f;
+	char *eptr;
+	int num;
+
+	for (f = feature_list; f->string; f++) {
+		if (!strcasecmp(string, f->string)) {
+			*compat_type = f->compat;
+			*mask = f->mask;
+			return 0;
+		}
+	}
+	if (strncasecmp(string, "FEATURE_", 8))
+		return 1;
+
+	switch (string[8]) {
+	case 'c':
+	case 'C':
+		*compat_type = E2P_FEATURE_COMPAT;
+		break;
+	case 'i':
+	case 'I':
+		*compat_type = E2P_FEATURE_INCOMPAT;
+		break;
+	case 'r':
+	case 'R':
+		*compat_type = E2P_FEATURE_RO_INCOMPAT;
+		break;
+	default:
+		return 1;
+	}
+	if (string[9] == 0)
+		return 1;
+	num = strtol(string+9, &eptr, 10);
+	if (num > 32 || num < 0)
+		return 1;
+	if (*eptr)
+		return 1;
+	*mask = 1 << num;
+	return 0;
+}
+
+static inline char *skip_over_blanks(char *cp)
+{
+	while (*cp && isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static inline char *skip_over_word(char *cp)
+{
+	while (*cp && !isspace(*cp) && *cp != ',')
+		cp++;
+	return cp;
+}
+
+/*
+ * Edit a feature set array as requested by the user.  The ok_array,
+ * if set, allows the application to limit what features the user is
+ * allowed to set or clear using this function.
+ */
+int e2p_edit_feature(const char *str, __u32 *compat_array, __u32 *ok_array)
+{
+	char	*cp, *buf, *next;
+	int	neg;
+	unsigned int	mask;
+	int		compat_type;
+
+	buf = xstrdup(str);
+	cp = buf;
+	while (cp && *cp) {
+		neg = 0;
+		cp = skip_over_blanks(cp);
+		next = skip_over_word(cp);
+		if (*next == 0)
+			next = 0;
+		else
+			*next = 0;
+		switch (*cp) {
+		case '-':
+		case '^':
+			neg++;
+		case '+':
+			cp++;
+			break;
+		}
+		if (e2p_string2feature(cp, &compat_type, &mask))
+			return 1;
+		if (ok_array && !(ok_array[compat_type] & mask))
+			return 1;
+		if (neg)
+			compat_array[compat_type] &= ~mask;
+		else
+			compat_array[compat_type] |= mask;
+		cp = next ? next+1 : 0;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c
new file mode 100644
index 0000000..008b798
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/fgetsetflags.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fgetflags.c		- Get a file flags on an ext2 file system
+ * fsetflags.c		- Set a file flags on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30	- Creation
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_EXT2_IOCTLS
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+#include "e2p.h"
+
+#ifdef O_LARGEFILE
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE)
+#else
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
+#endif
+
+int fgetsetflags (const char * name, unsigned long * get_flags, unsigned long set_flags)
+{
+#ifdef HAVE_EXT2_IOCTLS
+	struct stat buf;
+	int fd, r, f, save_errno = 0;
+
+	if (!stat(name, &buf) &&
+	    !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)) {
+		goto notsupp;
+	}
+	fd = open (name, OPEN_FLAGS);
+	if (fd == -1)
+		return -1;
+	if (!get_flags) {
+		f = (int) set_flags;
+		r = ioctl (fd, EXT2_IOC_SETFLAGS, &f);
+	} else {
+		r = ioctl (fd, EXT2_IOC_GETFLAGS, &f);
+		*get_flags = f;
+	}
+	if (r == -1)
+		save_errno = errno;
+	close (fd);
+	if (save_errno)
+		errno = save_errno;
+	return r;
+notsupp:
+#endif /* HAVE_EXT2_IOCTLS */
+	errno = EOPNOTSUPP;
+	return -1;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c
new file mode 100644
index 0000000..8d79054
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/fgetsetversion.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fgetversion.c	- Get a file version on an ext2 file system
+ * fsetversion.c	- Set a file version on an ext2 file system
+ *
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30	- Creation
+ */
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "e2p.h"
+
+#ifdef O_LARGEFILE
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK|O_LARGEFILE)
+#else
+#define OPEN_FLAGS (O_RDONLY|O_NONBLOCK)
+#endif
+
+/*
+   To do fsetversion:     unsigned long *ptr_version must be set to NULL.
+		      and unsigned long version must be set to a value
+   To do fgetversion:     unsigned long *ptr_version must NOT be set to NULL
+		      and unsigned long version is ignored.
+	TITO.
+*/
+
+int fgetsetversion (const char * name, unsigned long * get_version, unsigned long set_version)
+{
+#ifdef HAVE_EXT2_IOCTLS
+	int fd, r, ver, save_errno = 0;
+
+	fd = open (name, OPEN_FLAGS);
+	if (fd == -1)
+		return -1;
+	if (!get_version) {
+		ver = (int) set_version;
+		r = ioctl (fd, EXT2_IOC_SETVERSION, &ver);
+	} else {
+		r = ioctl (fd, EXT2_IOC_GETVERSION, &ver);
+		*get_version = ver;
+	}
+	if (r == -1)
+		save_errno = errno;
+	close (fd);
+	if (save_errno)
+		errno = save_errno;
+	return r;
+#else /* ! HAVE_EXT2_IOCTLS */
+	errno = EOPNOTSUPP;
+	return -1;
+#endif /* ! HAVE_EXT2_IOCTLS */
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/hashstr.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/hashstr.c
new file mode 100644
index 0000000..697ffad
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/hashstr.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * feature.c --- convert between features and strings
+ *
+ * Copyright (C) 1999  Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+
+struct hash {
+	int num;
+	const char *string;
+};
+
+static const struct hash hash_list[] = {
+	{ EXT2_HASH_LEGACY,   "legacy" },
+	{ EXT2_HASH_HALF_MD4, "half_md4" },
+	{ EXT2_HASH_TEA,      "tea" },
+	{ 0, 0 },
+};
+
+const char *e2p_hash2string(int num)
+{
+	const struct hash *p;
+	static char buf[20];
+
+	for (p = hash_list; p->string; p++) {
+		if (num == p->num)
+			return p->string;
+	}
+	sprintf(buf, "HASHALG_%d", num);
+	return buf;
+}
+
+/*
+ * Returns the hash algorithm, or -1 on error
+ */
+int e2p_string2hash(char *string)
+{
+	const struct hash *p;
+	char *eptr;
+	int num;
+
+	for (p = hash_list; p->string; p++) {
+		if (!strcasecmp(string, p->string)) {
+			return p->num;
+		}
+	}
+	if (strncasecmp(string, "HASHALG_", 8))
+		return -1;
+
+	if (string[8] == 0)
+		return -1;
+	num = strtol(string+8, &eptr, 10);
+	if (num > 255 || num < 0)
+		return -1;
+	if (*eptr)
+		return -1;
+	return num;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/iod.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/iod.c
new file mode 100644
index 0000000..23ab8d5
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/iod.c
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * iod.c		- Iterate a function on each entry of a directory
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30	- Creation
+ */
+
+#include "e2p.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+int iterate_on_dir (const char * dir_name,
+		    int (*func) (const char *, struct dirent *, void *),
+		    void * private)
+{
+	DIR * dir;
+	struct dirent *de, *dep;
+	int	max_len, len;
+
+	max_len = PATH_MAX + sizeof(struct dirent);
+	de = xmalloc(max_len+1);
+	memset(de, 0, max_len+1);
+
+	dir = opendir (dir_name);
+	if (dir == NULL) {
+		free(de);
+		return -1;
+	}
+	while ((dep = readdir (dir))) {
+		len = sizeof(struct dirent);
+		if (len < dep->d_reclen)
+			len = dep->d_reclen;
+		if (len > max_len)
+			len = max_len;
+		memcpy(de, dep, len);
+		(*func) (dir_name, de, private);
+	}
+	free(de);
+	closedir(dir);
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ls.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ls.c
new file mode 100644
index 0000000..9d29db6
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ls.c
@@ -0,0 +1,273 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ls.c			- List the contents of an ext2fs superblock
+ *
+ * Copyright (C) 1992, 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                                 Laboratoire MASI, Institut Blaise Pascal
+ *                                 Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Copyright (C) 1995, 1996, 1997  Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <grp.h>
+#include <pwd.h>
+#include <time.h>
+
+#include "e2p.h"
+
+static void print_user(unsigned short uid, FILE *f)
+{
+	struct passwd *pw = getpwuid(uid);
+	fprintf(f, "%u (user %s)\n", uid,
+			(pw == NULL ? "unknown" : pw->pw_name));
+}
+
+static void print_group(unsigned short gid, FILE *f)
+{
+	struct group *gr = getgrgid(gid);
+	fprintf(f, "%u (group %s)\n", gid,
+			(gr == NULL ? "unknown" : gr->gr_name));
+}
+
+#define MONTH_INT (86400 * 30)
+#define WEEK_INT (86400 * 7)
+#define DAY_INT	(86400)
+#define HOUR_INT (60 * 60)
+#define MINUTE_INT (60)
+
+static const char *interval_string(unsigned int secs)
+{
+	static char buf[256], tmp[80];
+	int		hr, min, num;
+
+	buf[0] = 0;
+
+	if (secs == 0)
+		return "<none>";
+
+	if (secs >= MONTH_INT) {
+		num = secs / MONTH_INT;
+		secs -= num*MONTH_INT;
+		sprintf(buf, "%d month%s", num, (num>1) ? "s" : "");
+	}
+	if (secs >= WEEK_INT) {
+		num = secs / WEEK_INT;
+		secs -= num*WEEK_INT;
+		sprintf(tmp, "%s%d week%s", buf[0] ? ", " : "",
+			num, (num>1) ? "s" : "");
+		strcat(buf, tmp);
+	}
+	if (secs >= DAY_INT) {
+		num = secs / DAY_INT;
+		secs -= num*DAY_INT;
+		sprintf(tmp, "%s%d day%s", buf[0] ? ", " : "",
+			num, (num>1) ? "s" : "");
+		strcat(buf, tmp);
+	}
+	if (secs > 0) {
+		hr = secs / HOUR_INT;
+		secs -= hr*HOUR_INT;
+		min = secs / MINUTE_INT;
+		secs -= min*MINUTE_INT;
+		sprintf(tmp, "%s%d:%02d:%02d", buf[0] ? ", " : "",
+			hr, min, secs);
+		strcat(buf, tmp);
+	}
+	return buf;
+}
+
+static void print_features(struct ext2_super_block * s, FILE *f)
+{
+#ifdef EXT2_DYNAMIC_REV
+	int	i, j, printed=0;
+	__u32	*mask = &s->s_feature_compat, m;
+
+	fprintf(f, "Filesystem features:     ");
+	for (i=0; i <3; i++,mask++) {
+		for (j=0,m=1; j < 32; j++, m<<=1) {
+			if (*mask & m) {
+				fprintf(f, " %s", e2p_feature2string(i, m));
+				printed++;
+			}
+		}
+	}
+	if (printed == 0)
+		fprintf(f, " (none)");
+	fprintf(f, "\n");
+#endif
+}
+
+static void print_mntopts(struct ext2_super_block * s, FILE *f)
+{
+#ifdef EXT2_DYNAMIC_REV
+	int	i, printed=0;
+	__u32	mask = s->s_default_mount_opts, m;
+
+	fprintf(f, "Default mount options:   ");
+	if (mask & EXT3_DEFM_JMODE) {
+		fprintf(f, " %s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE));
+		printed++;
+	}
+	for (i=0,m=1; i < 32; i++, m<<=1) {
+		if (m & EXT3_DEFM_JMODE)
+			continue;
+		if (mask & m) {
+			fprintf(f, " %s", e2p_mntopt2string(m));
+			printed++;
+		}
+	}
+	if (printed == 0)
+		fprintf(f, " (none)");
+	fprintf(f, "\n");
+#endif
+}
+
+
+#ifndef EXT2_INODE_SIZE
+#define EXT2_INODE_SIZE(s) sizeof(struct ext2_inode)
+#endif
+
+#ifndef EXT2_GOOD_OLD_REV
+#define EXT2_GOOD_OLD_REV 0
+#endif
+
+void list_super2(struct ext2_super_block * sb, FILE *f)
+{
+	int inode_blocks_per_group;
+	char buf[80], *str;
+	time_t	tm;
+
+	inode_blocks_per_group = (((sb->s_inodes_per_group *
+				    EXT2_INODE_SIZE(sb)) +
+				   EXT2_BLOCK_SIZE(sb) - 1) /
+				  EXT2_BLOCK_SIZE(sb));
+	if (sb->s_volume_name[0]) {
+		memset(buf, 0, sizeof(buf));
+		strncpy(buf, sb->s_volume_name, sizeof(sb->s_volume_name));
+	} else
+		strcpy(buf, "<none>");
+	fprintf(f, "Filesystem volume name:   %s\n", buf);
+	if (sb->s_last_mounted[0]) {
+		memset(buf, 0, sizeof(buf));
+		strncpy(buf, sb->s_last_mounted, sizeof(sb->s_last_mounted));
+	} else
+		strcpy(buf, "<not available>");
+	fprintf(f,
+		"Last mounted on:          %s\n"
+		"Filesystem UUID:          %s\n"
+		"Filesystem magic number:  0x%04X\n"
+		"Filesystem revision #:    %d",
+		buf, e2p_uuid2str(sb->s_uuid), sb->s_magic, sb->s_rev_level);
+	if (sb->s_rev_level == EXT2_GOOD_OLD_REV) {
+		fprintf(f, " (original)\n");
+#ifdef EXT2_DYNAMIC_REV
+	} else if (sb->s_rev_level == EXT2_DYNAMIC_REV) {
+		fprintf(f, " (dynamic)\n");
+#endif
+	} else
+		fprintf(f, " (unknown)\n");
+	print_features(sb, f);
+	print_mntopts(sb, f);
+	fprintf(f, "Filesystem state:        ");
+	print_fs_state (f, sb->s_state);
+	fprintf(f, "\nErrors behavior:          ");
+	print_fs_errors(f, sb->s_errors);
+	str = e2p_os2string(sb->s_creator_os);
+	fprintf(f,
+		"\n"
+		"Filesystem OS type:       %s\n"
+		"Inode count:              %u\n"
+		"Block count:              %u\n"
+		"Reserved block count:     %u\n"
+		"Free blocks:              %u\n"
+		"Free inodes:              %u\n"
+		"First block:              %u\n"
+		"Block size:               %u\n"
+		"Fragment size:            %u\n",
+		str, sb->s_inodes_count, sb->s_blocks_count, sb->s_r_blocks_count,
+		sb->s_free_blocks_count, sb->s_free_inodes_count,
+		sb->s_first_data_block, EXT2_BLOCK_SIZE(sb), EXT2_FRAG_SIZE(sb));
+	free(str);
+	if (sb->s_reserved_gdt_blocks)
+		fprintf(f, "Reserved GDT blocks:      %u\n",
+			sb->s_reserved_gdt_blocks);
+	fprintf(f,
+		"Blocks per group:         %u\n"
+		"Fragments per group:      %u\n"
+		"Inodes per group:         %u\n"
+		"Inode blocks per group:   %u\n",
+		sb->s_blocks_per_group, sb->s_frags_per_group,
+		sb->s_inodes_per_group, inode_blocks_per_group);
+	if (sb->s_first_meta_bg)
+		fprintf(f, "First meta block group:   %u\n",
+			sb->s_first_meta_bg);
+	if (sb->s_mkfs_time) {
+		tm = sb->s_mkfs_time;
+		fprintf(f, "Filesystem created:       %s", ctime(&tm));
+	}
+	tm = sb->s_mtime;
+	fprintf(f, "Last mount time:          %s",
+		sb->s_mtime ? ctime(&tm) : "n/a\n");
+	tm = sb->s_wtime;
+	fprintf(f,
+		"Last write time:          %s"
+		"Mount count:              %u\n"
+		"Maximum mount count:      %d\n",
+		ctime(&tm), sb->s_mnt_count, sb->s_max_mnt_count);
+	tm = sb->s_lastcheck;
+	fprintf(f,
+		"Last checked:             %s"
+		"Check interval:           %u (%s)\n",
+		ctime(&tm),
+		sb->s_checkinterval, interval_string(sb->s_checkinterval));
+	if (sb->s_checkinterval)
+	{
+		time_t next;
+
+		next = sb->s_lastcheck + sb->s_checkinterval;
+		fprintf(f, "Next check after:         %s", ctime(&next));
+	}
+	fprintf(f, "Reserved blocks uid:      ");
+	print_user(sb->s_def_resuid, f);
+	fprintf(f, "Reserved blocks gid:      ");
+	print_group(sb->s_def_resgid, f);
+	if (sb->s_rev_level >= EXT2_DYNAMIC_REV) {
+		fprintf(f,
+			"First inode:              %d\n"
+			"Inode size:		  %d\n",
+			sb->s_first_ino, sb->s_inode_size);
+	}
+	if (!e2p_is_null_uuid(sb->s_journal_uuid))
+		fprintf(f, "Journal UUID:             %s\n",
+			e2p_uuid2str(sb->s_journal_uuid));
+	if (sb->s_journal_inum)
+		fprintf(f, "Journal inode:            %u\n",
+			sb->s_journal_inum);
+	if (sb->s_journal_dev)
+		fprintf(f, "Journal device:	          0x%04x\n",
+			sb->s_journal_dev);
+	if (sb->s_last_orphan)
+		fprintf(f, "First orphan inode:       %u\n",
+			sb->s_last_orphan);
+	if ((sb->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
+	    sb->s_def_hash_version)
+		fprintf(f, "Default directory hash:   %s\n",
+			e2p_hash2string(sb->s_def_hash_version));
+	if (!e2p_is_null_uuid(sb->s_hash_seed))
+		fprintf(f, "Directory Hash Seed:      %s\n",
+			e2p_uuid2str(sb->s_hash_seed));
+	if (sb->s_jnl_backup_type) {
+		fprintf(f, "Journal backup:           ");
+		if (sb->s_jnl_backup_type == 1)
+			fprintf(f, "inode blocks\n");
+		else
+			fprintf(f, "type %u\n", sb->s_jnl_backup_type);
+	}
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/mntopts.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/mntopts.c
new file mode 100644
index 0000000..17c26c4
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/mntopts.c
@@ -0,0 +1,134 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mountopts.c --- convert between default mount options and strings
+ *
+ * Copyright (C) 2002  Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "e2p.h"
+
+struct mntopt {
+	unsigned int	mask;
+	const char	*string;
+};
+
+static const struct mntopt mntopt_list[] = {
+	{ EXT2_DEFM_DEBUG,	"debug" },
+	{ EXT2_DEFM_BSDGROUPS,	"bsdgroups" },
+	{ EXT2_DEFM_XATTR_USER,	"user_xattr" },
+	{ EXT2_DEFM_ACL,	"acl" },
+	{ EXT2_DEFM_UID16,	"uid16" },
+	{ EXT3_DEFM_JMODE_DATA, "journal_data" },
+	{ EXT3_DEFM_JMODE_ORDERED, "journal_data_ordered" },
+	{ EXT3_DEFM_JMODE_WBACK, "journal_data_writeback" },
+	{ 0, 0 },
+};
+
+const char *e2p_mntopt2string(unsigned int mask)
+{
+	const struct mntopt  *f;
+	static char buf[20];
+	int	fnum;
+
+	for (f = mntopt_list; f->string; f++) {
+		if (mask == f->mask)
+			return f->string;
+	}
+	for (fnum = 0; mask >>= 1; fnum++);
+	sprintf(buf, "MNTOPT_%d", fnum);
+	return buf;
+}
+
+int e2p_string2mntopt(char *string, unsigned int *mask)
+{
+	const struct mntopt  *f;
+	char		*eptr;
+	int		num;
+
+	for (f = mntopt_list; f->string; f++) {
+		if (!strcasecmp(string, f->string)) {
+			*mask = f->mask;
+			return 0;
+		}
+	}
+	if (strncasecmp(string, "MNTOPT_", 8))
+		return 1;
+
+	if (string[8] == 0)
+		return 1;
+	num = strtol(string+8, &eptr, 10);
+	if (num > 32 || num < 0)
+		return 1;
+	if (*eptr)
+		return 1;
+	*mask = 1 << num;
+	return 0;
+}
+
+static char *skip_over_blanks(char *cp)
+{
+	while (*cp && isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+	while (*cp && !isspace(*cp) && *cp != ',')
+		cp++;
+	return cp;
+}
+
+/*
+ * Edit a mntopt set array as requested by the user.  The ok
+ * parameter, if non-zero, allows the application to limit what
+ * mntopts the user is allowed to set or clear using this function.
+ */
+int e2p_edit_mntopts(const char *str, __u32 *mntopts, __u32 ok)
+{
+	char	*cp, *buf, *next;
+	int	neg;
+	unsigned int	mask;
+
+	buf = xstrdup(str);
+	cp = buf;
+	while (cp && *cp) {
+		neg = 0;
+		cp = skip_over_blanks(cp);
+		next = skip_over_word(cp);
+		if (*next == 0)
+			next = 0;
+		else
+			*next = 0;
+		switch (*cp) {
+		case '-':
+		case '^':
+			neg++;
+		case '+':
+			cp++;
+			break;
+		}
+		if (e2p_string2mntopt(cp, &mask))
+			return 1;
+		if (ok && !(ok & mask))
+			return 1;
+		if (mask & EXT3_DEFM_JMODE)
+			*mntopts &= ~EXT3_DEFM_JMODE;
+		if (neg)
+			*mntopts &= ~mask;
+		else
+			*mntopts |= mask;
+		cp = next ? next+1 : 0;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ostype.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ostype.c
new file mode 100644
index 0000000..6a2f178
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ostype.c
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getostype.c          - Get the Filesystem OS type
+ *
+ * Copyright (C) 2004,2005  Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+#include "e2p.h"
+#include <string.h>
+#include <stdlib.h>
+
+static const char *const os_tab[] =
+	{ "Linux",
+	  "Hurd",
+	  "Masix",
+	  "FreeBSD",
+	  "Lites",
+	  0 };
+
+/*
+ * Convert an os_type to a string
+ */
+char *e2p_os2string(int os_type)
+{
+	const char *os;
+	char *ret;
+
+	if (os_type <= EXT2_OS_LITES)
+		os = os_tab[os_type];
+	else
+		os = "(unknown os)";
+
+	ret = xstrdup(os);
+	return ret;
+}
+
+/*
+ * Convert an os_type to a string
+ */
+int e2p_string2os(char *str)
+{
+	const char *const *cpp;
+	int i = 0;
+
+	for (cpp = os_tab; *cpp; cpp++, i++) {
+		if (!strcasecmp(str, *cpp))
+			return i;
+	}
+	return -1;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	char	*s;
+	int	i, os;
+
+	for (i=0; i <= EXT2_OS_LITES; i++) {
+		s = e2p_os2string(i);
+		os = e2p_string2os(s);
+		printf("%d: %s (%d)\n", i, s, os);
+		if (i != os) {
+			fprintf(stderr, "Failure!\n");
+			exit(1);
+		}
+	}
+	exit(0);
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/parse_num.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/parse_num.c
new file mode 100644
index 0000000..6db076f
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/parse_num.c
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_num.c		- Parse the number of blocks
+ *
+ * Copyright (C) 2004,2005  Theodore Ts'o <tytso@mit.edu>
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+#include "e2p.h"
+
+#include <stdlib.h>
+
+unsigned long parse_num_blocks(const char *arg, int log_block_size)
+{
+	char *p;
+	unsigned long long num;
+
+	num = strtoull(arg, &p, 0);
+
+	if (p[0] && p[1])
+		return 0;
+
+	switch (*p) {		/* Using fall-through logic */
+	case 'T': case 't':
+		num <<= 10;
+	case 'G': case 'g':
+		num <<= 10;
+	case 'M': case 'm':
+		num <<= 10;
+	case 'K': case 'k':
+		num >>= log_block_size;
+		break;
+	case 's':
+		num >>= 1;
+		break;
+	case '\0':
+		break;
+	default:
+		return 0;
+	}
+	return num;
+}
+
+#ifdef DEBUG
+#include <unistd.h>
+#include <stdio.h>
+
+main(int argc, char **argv)
+{
+	unsigned long num;
+	int log_block_size = 0;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s arg\n", argv[0]);
+		exit(1);
+	}
+
+	num = parse_num_blocks(argv[1], log_block_size);
+
+	printf("Parsed number: %lu\n", num);
+	exit(0);
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/pe.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/pe.c
new file mode 100644
index 0000000..835274b
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/pe.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pe.c			- Print a second extended filesystem errors behavior
+ *
+ * Copyright (C) 1992, 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                                 Laboratoire MASI, Institut Blaise Pascal
+ *                                 Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 94/01/09	- Creation
+ */
+
+#include <stdio.h>
+
+#include "e2p.h"
+
+void print_fs_errors(FILE *f, unsigned short errors)
+{
+	char *disp = NULL;
+	switch (errors) {
+	case EXT2_ERRORS_CONTINUE: disp = "Continue"; break;
+	case EXT2_ERRORS_RO:       disp = "Remount read-only"; break;
+	case EXT2_ERRORS_PANIC:    disp = "Panic"; break;
+	default:                   disp = "Unknown (continue)";
+	}
+	fprintf(f, disp);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/pf.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/pf.c
new file mode 100644
index 0000000..02cbec7
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/pf.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pf.c			- Print file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/10/30	- Creation
+ */
+
+#include <stdio.h>
+
+#include "e2p.h"
+
+struct flags_name {
+	unsigned long	flag;
+	const char	*short_name;
+	const char	*long_name;
+};
+
+static const struct flags_name flags_array[] = {
+	{ EXT2_SECRM_FL, "s", "Secure_Deletion" },
+	{ EXT2_UNRM_FL, "u" , "Undelete" },
+	{ EXT2_SYNC_FL, "S", "Synchronous_Updates" },
+	{ EXT2_DIRSYNC_FL, "D", "Synchronous_Directory_Updates" },
+	{ EXT2_IMMUTABLE_FL, "i", "Immutable" },
+	{ EXT2_APPEND_FL, "a", "Append_Only" },
+	{ EXT2_NODUMP_FL, "d", "No_Dump" },
+	{ EXT2_NOATIME_FL, "A", "No_Atime" },
+	{ EXT2_COMPR_FL, "c", "Compression_Requested" },
+#ifdef ENABLE_COMPRESSION
+	{ EXT2_COMPRBLK_FL, "B", "Compressed_File" },
+	{ EXT2_DIRTY_FL, "Z", "Compressed_Dirty_File" },
+	{ EXT2_NOCOMPR_FL, "X", "Compression_Raw_Access" },
+	{ EXT2_ECOMPR_FL, "E", "Compression_Error" },
+#endif
+	{ EXT3_JOURNAL_DATA_FL, "j", "Journaled_Data" },
+	{ EXT2_INDEX_FL, "I", "Indexed_direcctory" },
+	{ EXT2_NOTAIL_FL, "t", "No_Tailmerging" },
+	{ EXT2_TOPDIR_FL, "T", "Top_of_Directory_Hierarchies" },
+	{ 0, NULL, NULL }
+};
+
+void print_flags (FILE *f, unsigned long flags, unsigned options)
+{
+	int long_opt = (options & PFOPT_LONG);
+	const struct flags_name *fp;
+	int	first = 1;
+
+	for (fp = flags_array; fp->flag != 0; fp++) {
+		if (flags & fp->flag) {
+			if (long_opt) {
+				if (first)
+					first = 0;
+				else
+					fputs(", ", f);
+				fputs(fp->long_name, f);
+			} else
+				fputs(fp->short_name, f);
+		} else {
+			if (!long_opt)
+				fputs("-", f);
+		}
+	}
+	if (long_opt && first)
+		fputs("---", f);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ps.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ps.c
new file mode 100644
index 0000000..a6b4099
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/ps.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ps.c			- Print filesystem state
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU Library General
+ * Public License
+ */
+
+/*
+ * History:
+ * 93/12/22	- Creation
+ */
+
+#include <stdio.h>
+
+#include "e2p.h"
+
+void print_fs_state(FILE *f, unsigned short state)
+{
+	fprintf(f, (state & EXT2_VALID_FS ? " clean" : " not clean"));
+	if (state & EXT2_ERROR_FS)
+		fprintf(f, " with errors");
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/uuid.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/uuid.c
new file mode 100644
index 0000000..474d64a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/e2p/uuid.c
@@ -0,0 +1,78 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uuid.c -- utility routines for manipulating UUID's.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "../ext2fs/ext2_types.h"
+
+#include "e2p.h"
+
+struct uuid {
+	__u32	time_low;
+	__u16	time_mid;
+	__u16	time_hi_and_version;
+	__u16	clock_seq;
+	__u8	node[6];
+};
+
+/* Returns 1 if the uuid is the NULL uuid */
+int e2p_is_null_uuid(void *uu)
+{
+	__u8	*cp;
+	int	i;
+
+	for (i=0, cp = uu; i < 16; i++)
+		if (*cp)
+			return 0;
+	return 1;
+}
+
+static void e2p_unpack_uuid(void *in, struct uuid *uu)
+{
+	__u8	*ptr = in;
+	__u32	tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_low = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_mid = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_hi_and_version = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->clock_seq = tmp;
+
+	memcpy(uu->node, ptr, 6);
+}
+
+void e2p_uuid_to_str(void *uu, char *out)
+{
+	struct uuid uuid;
+
+	e2p_unpack_uuid(uu, &uuid);
+	sprintf(out,
+		"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+		uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+		uuid.node[0], uuid.node[1], uuid.node[2],
+		uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+const char *e2p_uuid2str(void *uu)
+{
+	static char buf[80];
+	if (e2p_is_null_uuid(uu))
+		return "<none>";
+	e2p_uuid_to_str(uu, buf);
+	return buf;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src
new file mode 100644
index 0000000..12adc6e
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/Kbuild.src
@@ -0,0 +1,26 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+NEEDED-$(CONFIG_E2FSCK) = y
+NEEDED-$(CONFIG_FSCK) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+
+INSERT
+
+lib-$(NEEDED-y) += gen_bitmap.o bitops.o ismounted.o mkjournal.o unix_io.o \
+                   rw_bitmaps.o initialize.o bitmaps.o block.o \
+                   ind_block.o inode.o freefs.o alloc_stats.o closefs.o \
+                   openfs.o io_manager.o finddev.o read_bb.o alloc.o badblocks.o \
+                   getsize.o getsectsize.o alloc_tables.o read_bb_file.o mkdir.o \
+                   bb_inode.o newdir.o alloc_sb.o lookup.o dirblock.o expanddir.o \
+                   dir_iterate.o link.o res_gdt.o icount.o get_pathname.o dblist.o \
+                   dirhash.o version.o flushb.o unlink.o check_desc.o valid_blk.o \
+                   ext_attr.o bmap.o dblist_dir.o ext2fs_inline.o swapfs.o
+
+CFLAGS += -include $(srctree)/e2fsprogs/e2fsbb.h
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c
new file mode 100644
index 0000000..cbb63e1
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc.c
@@ -0,0 +1,173 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc.c --- allocate new inodes, blocks for ext2fs
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Right now, just search forward from the parent directory's block
+ * group to find the next free inode.
+ *
+ * Should have a special policy for directories.
+ */
+errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir,
+			   int mode EXT2FS_ATTR((unused)),
+			   ext2fs_inode_bitmap map, ext2_ino_t *ret)
+{
+	ext2_ino_t	dir_group = 0;
+	ext2_ino_t	i;
+	ext2_ino_t	start_inode;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!map)
+		map = fs->inode_map;
+	if (!map)
+		return EXT2_ET_NO_INODE_BITMAP;
+
+	if (dir > 0)
+		dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super);
+
+	start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1;
+	if (start_inode < EXT2_FIRST_INODE(fs->super))
+		start_inode = EXT2_FIRST_INODE(fs->super);
+	i = start_inode;
+
+	do {
+		if (!ext2fs_fast_test_inode_bitmap(map, i))
+			break;
+		i++;
+		if (i > fs->super->s_inodes_count)
+			i = EXT2_FIRST_INODE(fs->super);
+	} while (i != start_inode);
+
+	if (ext2fs_test_inode_bitmap(map, i))
+		return EXT2_ET_INODE_ALLOC_FAIL;
+	*ret = i;
+	return 0;
+}
+
+/*
+ * Stupid algorithm --- we now just search forward starting from the
+ * goal.  Should put in a smarter one someday....
+ */
+errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
+			   ext2fs_block_bitmap map, blk_t *ret)
+{
+	blk_t	i;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!map)
+		map = fs->block_map;
+	if (!map)
+		return EXT2_ET_NO_BLOCK_BITMAP;
+	if (!goal || (goal >= fs->super->s_blocks_count))
+		goal = fs->super->s_first_data_block;
+	i = goal;
+	do {
+		if (!ext2fs_fast_test_block_bitmap(map, i)) {
+			*ret = i;
+			return 0;
+		}
+		i++;
+		if (i >= fs->super->s_blocks_count)
+			i = fs->super->s_first_data_block;
+	} while (i != goal);
+	return EXT2_ET_BLOCK_ALLOC_FAIL;
+}
+
+/*
+ * This function zeros out the allocated block, and updates all of the
+ * appropriate filesystem records.
+ */
+errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
+			     char *block_buf, blk_t *ret)
+{
+	errcode_t	retval;
+	blk_t		block;
+	char		*buf = NULL;
+
+	if (!block_buf) {
+		retval = ext2fs_get_mem(fs->blocksize, &buf);
+		if (retval)
+			return retval;
+		block_buf = buf;
+	}
+	memset(block_buf, 0, fs->blocksize);
+
+	if (!fs->block_map) {
+		retval = ext2fs_read_block_bitmap(fs);
+		if (retval)
+			goto fail;
+	}
+
+	retval = ext2fs_new_block(fs, goal, 0, &block);
+	if (retval)
+		goto fail;
+
+	retval = io_channel_write_blk(fs->io, block, 1, block_buf);
+	if (retval)
+		goto fail;
+
+	ext2fs_block_alloc_stats(fs, block, +1);
+	*ret = block;
+	return 0;
+
+fail:
+	if (buf)
+		ext2fs_free_mem(&buf);
+	return retval;
+}
+
+errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish,
+				 int num, ext2fs_block_bitmap map, blk_t *ret)
+{
+	blk_t	b = start;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!map)
+		map = fs->block_map;
+	if (!map)
+		return EXT2_ET_NO_BLOCK_BITMAP;
+	if (!b)
+		b = fs->super->s_first_data_block;
+	if (!finish)
+		finish = start;
+	if (!num)
+		num = 1;
+	do {
+		if (b+num-1 > fs->super->s_blocks_count)
+			b = fs->super->s_first_data_block;
+		if (ext2fs_fast_test_block_bitmap_range(map, b, num)) {
+			*ret = b;
+			return 0;
+		}
+		b++;
+	} while (b != finish);
+	return EXT2_ET_BLOCK_ALLOC_FAIL;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c
new file mode 100644
index 0000000..a7437c9
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_sb.c
@@ -0,0 +1,58 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc_sb.c --- Allocate the superblock and block group descriptors for a
+ * newly initialized filesystem.  Used by mke2fs when initializing a filesystem
+ *
+ * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
+				 dgrp_t group,
+				 ext2fs_block_bitmap bmap)
+{
+	blk_t	super_blk, old_desc_blk, new_desc_blk;
+	int	j, old_desc_blocks, num_blocks;
+
+	num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk,
+					      &old_desc_blk, &new_desc_blk, 0);
+
+	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+		old_desc_blocks = fs->super->s_first_meta_bg;
+	else
+		old_desc_blocks =
+			fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+	if (super_blk || (group == 0))
+		ext2fs_mark_block_bitmap(bmap, super_blk);
+
+	if (old_desc_blk) {
+		for (j=0; j < old_desc_blocks; j++)
+			ext2fs_mark_block_bitmap(bmap, old_desc_blk + j);
+	}
+	if (new_desc_blk)
+		ext2fs_mark_block_bitmap(bmap, new_desc_blk);
+
+	return num_blocks;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c
new file mode 100644
index 0000000..f3ab06a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_stats.c
@@ -0,0 +1,53 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc_stats.c --- Update allocation statistics for ext2fs
+ *
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
+			       int inuse, int isdir)
+{
+	int	group = ext2fs_group_of_ino(fs, ino);
+
+	if (inuse > 0)
+		ext2fs_mark_inode_bitmap(fs->inode_map, ino);
+	else
+		ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
+	fs->group_desc[group].bg_free_inodes_count -= inuse;
+	if (isdir)
+		fs->group_desc[group].bg_used_dirs_count += inuse;
+	fs->super->s_free_inodes_count -= inuse;
+	ext2fs_mark_super_dirty(fs);
+	ext2fs_mark_ib_dirty(fs);
+}
+
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse)
+{
+	ext2fs_inode_alloc_stats2(fs, ino, inuse, 0);
+}
+
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse)
+{
+	int	group = ext2fs_group_of_blk(fs, blk);
+
+	if (inuse > 0)
+		ext2fs_mark_block_bitmap(fs->block_map, blk);
+	else
+		ext2fs_unmark_block_bitmap(fs->block_map, blk);
+	fs->group_desc[group].bg_free_blocks_count -= inuse;
+	fs->super->s_free_blocks_count -= inuse;
+	ext2fs_mark_super_dirty(fs);
+	ext2fs_mark_bb_dirty(fs);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c
new file mode 100644
index 0000000..7c60e2b
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/alloc_tables.c
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * alloc_tables.c --- Allocate tables for a newly initialized
+ * filesystem.  Used by mke2fs when initializing a filesystem
+ *
+ * Copyright (C) 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
+				      ext2fs_block_bitmap bmap)
+{
+	errcode_t	retval;
+	blk_t		group_blk, start_blk, last_blk, new_blk, blk;
+	int		j;
+
+	group_blk = fs->super->s_first_data_block +
+		(group * fs->super->s_blocks_per_group);
+
+	last_blk = group_blk + fs->super->s_blocks_per_group;
+	if (last_blk >= fs->super->s_blocks_count)
+		last_blk = fs->super->s_blocks_count - 1;
+
+	if (!bmap)
+		bmap = fs->block_map;
+
+	/*
+	 * Allocate the block and inode bitmaps, if necessary
+	 */
+	if (fs->stride) {
+		start_blk = group_blk + fs->inode_blocks_per_group;
+		start_blk += ((fs->stride * group) %
+			      (last_blk - start_blk));
+		if (start_blk > last_blk)
+			start_blk = group_blk;
+	} else
+		start_blk = group_blk;
+
+	if (!fs->group_desc[group].bg_block_bitmap) {
+		retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+						1, bmap, &new_blk);
+		if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
+			retval = ext2fs_get_free_blocks(fs, group_blk,
+					last_blk, 1, bmap, &new_blk);
+		if (retval)
+			return retval;
+		ext2fs_mark_block_bitmap(bmap, new_blk);
+		fs->group_desc[group].bg_block_bitmap = new_blk;
+	}
+
+	if (!fs->group_desc[group].bg_inode_bitmap) {
+		retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+						1, bmap, &new_blk);
+		if (retval == EXT2_ET_BLOCK_ALLOC_FAIL)
+			retval = ext2fs_get_free_blocks(fs, group_blk,
+					last_blk, 1, bmap, &new_blk);
+		if (retval)
+			return retval;
+		ext2fs_mark_block_bitmap(bmap, new_blk);
+		fs->group_desc[group].bg_inode_bitmap = new_blk;
+	}
+
+	/*
+	 * Allocate the inode table
+	 */
+	if (!fs->group_desc[group].bg_inode_table) {
+		retval = ext2fs_get_free_blocks(fs, group_blk, last_blk,
+						fs->inode_blocks_per_group,
+						bmap, &new_blk);
+		if (retval)
+			return retval;
+		for (j=0, blk = new_blk;
+		     j < fs->inode_blocks_per_group;
+		     j++, blk++)
+			ext2fs_mark_block_bitmap(bmap, blk);
+		fs->group_desc[group].bg_inode_table = new_blk;
+	}
+
+	return 0;
+}
+
+errcode_t ext2fs_allocate_tables(ext2_filsys fs)
+{
+	errcode_t	retval;
+	dgrp_t		i;
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		retval = ext2fs_allocate_group_table(fs, i, fs->block_map);
+		if (retval)
+			return retval;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c
new file mode 100644
index 0000000..6e5cc10
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/badblocks.c
@@ -0,0 +1,328 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * badblocks.c --- routines to manipulate the bad block structure
+ *
+ * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * Helper function for making a badblocks list
+ */
+static errcode_t make_u32_list(int size, int num, __u32 *list,
+			       ext2_u32_list *ret)
+{
+	ext2_u32_list	bb;
+	errcode_t	retval;
+
+	retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb);
+	if (retval)
+		return retval;
+	memset(bb, 0, sizeof(struct ext2_struct_u32_list));
+	bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST;
+	bb->size = size ? size : 10;
+	bb->num = num;
+	retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list);
+	if (!bb->list) {
+		ext2fs_free_mem(&bb);
+		return retval;
+	}
+	if (list)
+		memcpy(bb->list, list, bb->size * sizeof(blk_t));
+	else
+		memset(bb->list, 0, bb->size * sizeof(blk_t));
+	*ret = bb;
+	return 0;
+}
+
+
+/*
+ * This procedure creates an empty u32 list.
+ */
+errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size)
+{
+	return make_u32_list(size, 0, 0, ret);
+}
+
+/*
+ * This procedure creates an empty badblocks list.
+ */
+errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size)
+{
+	return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret);
+}
+
+
+/*
+ * This procedure copies a badblocks list
+ */
+errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest)
+{
+	errcode_t	retval;
+
+	retval = make_u32_list(src->size, src->num, src->list, dest);
+	if (retval)
+		return retval;
+	(*dest)->badblocks_flags = src->badblocks_flags;
+	return 0;
+}
+
+errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
+				ext2_badblocks_list *dest)
+{
+	return ext2fs_u32_copy((ext2_u32_list) src,
+			       (ext2_u32_list *) dest);
+}
+
+/*
+ * This procedure frees a badblocks list.
+ *
+ * (note: moved to closefs.c)
+ */
+
+
+/*
+ * This procedure adds a block to a badblocks list.
+ */
+errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk)
+{
+	errcode_t	retval;
+	int		i, j;
+	unsigned long	old_size;
+
+	EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+	if (bb->num >= bb->size) {
+		old_size = bb->size * sizeof(__u32);
+		bb->size += 100;
+		retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32),
+					   &bb->list);
+		if (retval) {
+			bb->size -= 100;
+			return retval;
+		}
+	}
+
+	/*
+	 * Add special case code for appending to the end of the list
+	 */
+	i = bb->num-1;
+	if ((bb->num != 0) && (bb->list[i] == blk))
+		return 0;
+	if ((bb->num == 0) || (bb->list[i] < blk)) {
+		bb->list[bb->num++] = blk;
+		return 0;
+	}
+
+	j = bb->num;
+	for (i=0; i < bb->num; i++) {
+		if (bb->list[i] == blk)
+			return 0;
+		if (bb->list[i] > blk) {
+			j = i;
+			break;
+		}
+	}
+	for (i=bb->num; i > j; i--)
+		bb->list[i] = bb->list[i-1];
+	bb->list[j] = blk;
+	bb->num++;
+	return 0;
+}
+
+errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk)
+{
+	return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk);
+}
+
+/*
+ * This procedure finds a particular block is on a badblocks
+ * list.
+ */
+int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk)
+{
+	int	low, high, mid;
+
+	if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+		return -1;
+
+	if (bb->num == 0)
+		return -1;
+
+	low = 0;
+	high = bb->num-1;
+	if (blk == bb->list[low])
+		return low;
+	if (blk == bb->list[high])
+		return high;
+
+	while (low < high) {
+		mid = (low+high)/2;
+		if (mid == low || mid == high)
+			break;
+		if (blk == bb->list[mid])
+			return mid;
+		if (blk < bb->list[mid])
+			high = mid;
+		else
+			low = mid;
+	}
+	return -1;
+}
+
+/*
+ * This procedure tests to see if a particular block is on a badblocks
+ * list.
+ */
+int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk)
+{
+	if (ext2fs_u32_list_find(bb, blk) < 0)
+		return 0;
+	else
+		return 1;
+}
+
+int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk)
+{
+	return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk);
+}
+
+
+/*
+ * Remove a block from the badblock list
+ */
+int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk)
+{
+	int	remloc, i;
+
+	if (bb->num == 0)
+		return -1;
+
+	remloc = ext2fs_u32_list_find(bb, blk);
+	if (remloc < 0)
+		return -1;
+
+	for (i = remloc; i < bb->num - 1; i++)
+		bb->list[i] = bb->list[i+1];
+	bb->num--;
+	return 0;
+}
+
+void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk)
+{
+	ext2fs_u32_list_del(bb, blk);
+}
+
+errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
+					ext2_u32_iterate *ret)
+{
+	ext2_u32_iterate iter;
+	errcode_t		retval;
+
+	EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+	retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter);
+	if (retval)
+		return retval;
+
+	iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE;
+	iter->bb = bb;
+	iter->ptr = 0;
+	*ret = iter;
+	return 0;
+}
+
+errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
+					      ext2_badblocks_iterate *ret)
+{
+	return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb,
+					      (ext2_u32_iterate *) ret);
+}
+
+
+int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk)
+{
+	ext2_u32_list	bb;
+
+	if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)
+		return 0;
+
+	bb = iter->bb;
+
+	if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+		return 0;
+
+	if (iter->ptr < bb->num) {
+		*blk = bb->list[iter->ptr++];
+		return 1;
+	}
+	*blk = 0;
+	return 0;
+}
+
+int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk)
+{
+	return ext2fs_u32_list_iterate((ext2_u32_iterate) iter,
+				       (__u32 *) blk);
+}
+
+
+void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter)
+{
+	if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE))
+		return;
+
+	iter->bb = 0;
+	ext2fs_free_mem(&iter);
+}
+
+void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter)
+{
+	ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter);
+}
+
+
+int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2)
+{
+	EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+	EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+	if (bb1->num != bb2->num)
+		return 0;
+
+	if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0)
+		return 0;
+	return 1;
+}
+
+int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2)
+{
+	return ext2fs_u32_list_equal((ext2_u32_list) bb1,
+				     (ext2_u32_list) bb2);
+}
+
+int ext2fs_u32_list_count(ext2_u32_list bb)
+{
+	return bb->num;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c
new file mode 100644
index 0000000..419ac77
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bb_compat.c
@@ -0,0 +1,64 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_compat.c --- compatibility badblocks routines
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+errcode_t badblocks_list_create(badblocks_list *ret, int size)
+{
+	return ext2fs_badblocks_list_create(ret, size);
+}
+
+void badblocks_list_free(badblocks_list bb)
+{
+	ext2fs_badblocks_list_free(bb);
+}
+
+errcode_t badblocks_list_add(badblocks_list bb, blk_t blk)
+{
+	return ext2fs_badblocks_list_add(bb, blk);
+}
+
+int badblocks_list_test(badblocks_list bb, blk_t blk)
+{
+	return ext2fs_badblocks_list_test(bb, blk);
+}
+
+errcode_t badblocks_list_iterate_begin(badblocks_list bb,
+				       badblocks_iterate *ret)
+{
+	return ext2fs_badblocks_list_iterate_begin(bb, ret);
+}
+
+int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk)
+{
+	return ext2fs_badblocks_list_iterate(iter, blk);
+}
+
+void badblocks_list_iterate_end(badblocks_iterate iter)
+{
+	ext2fs_badblocks_list_iterate_end(iter);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
new file mode 100644
index 0000000..a967896
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bb_inode.c
@@ -0,0 +1,262 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_inode.c --- routines to update the bad block inode.
+ *
+ * WARNING: This routine modifies a lot of state in the filesystem; if
+ * this routine returns an error, the bad block inode may be in an
+ * inconsistent state.
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct set_badblock_record {
+	ext2_badblocks_iterate	bb_iter;
+	int		bad_block_count;
+	blk_t		*ind_blocks;
+	int		max_ind_blocks;
+	int		ind_blocks_size;
+	int		ind_blocks_ptr;
+	char		*block_buf;
+	errcode_t	err;
+};
+
+static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+			      e2_blkcnt_t blockcnt,
+			      blk_t ref_block, int ref_offset,
+			      void *priv_data);
+static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+				e2_blkcnt_t blockcnt,
+				blk_t ref_block, int ref_offset,
+				void *priv_data);
+
+/*
+ * Given a bad blocks bitmap, update the bad blocks inode to reflect
+ * the map.
+ */
+errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
+{
+	errcode_t			retval;
+	struct set_badblock_record	rec;
+	struct ext2_inode		inode;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!fs->block_map)
+		return EXT2_ET_NO_BLOCK_BITMAP;
+
+	rec.bad_block_count = 0;
+	rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
+	rec.max_ind_blocks = 10;
+	retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t),
+				&rec.ind_blocks);
+	if (retval)
+		return retval;
+	memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
+	retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf);
+	if (retval)
+		goto cleanup;
+	memset(rec.block_buf, 0, fs->blocksize);
+	rec.err = 0;
+
+	/*
+	 * First clear the old bad blocks (while saving the indirect blocks)
+	 */
+	retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
+				       BLOCK_FLAG_DEPTH_TRAVERSE, 0,
+				       clear_bad_block_proc, &rec);
+	if (retval)
+		goto cleanup;
+	if (rec.err) {
+		retval = rec.err;
+		goto cleanup;
+	}
+
+	/*
+	 * Now set the bad blocks!
+	 *
+	 * First, mark the bad blocks as used.  This prevents a bad
+	 * block from being used as an indirecto block for the bad
+	 * block inode (!).
+	 */
+	if (bb_list) {
+		retval = ext2fs_badblocks_list_iterate_begin(bb_list,
+							     &rec.bb_iter);
+		if (retval)
+			goto cleanup;
+		retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
+					       BLOCK_FLAG_APPEND, 0,
+					       set_bad_block_proc, &rec);
+		ext2fs_badblocks_list_iterate_end(rec.bb_iter);
+		if (retval)
+			goto cleanup;
+		if (rec.err) {
+			retval = rec.err;
+			goto cleanup;
+		}
+	}
+
+	/*
+	 * Update the bad block inode's mod time and block count
+	 * field.
+	 */
+	retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
+	if (retval)
+		goto cleanup;
+
+	inode.i_atime = inode.i_mtime = time(NULL);
+	if (!inode.i_ctime)
+		inode.i_ctime = time(NULL);
+	inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
+	inode.i_size = rec.bad_block_count * fs->blocksize;
+
+	retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
+	if (retval)
+		goto cleanup;
+
+cleanup:
+	ext2fs_free_mem(&rec.ind_blocks);
+	ext2fs_free_mem(&rec.block_buf);
+	return retval;
+}
+
+/*
+ * Helper function for update_bb_inode()
+ *
+ * Clear the bad blocks in the bad block inode, while saving the
+ * indirect blocks.
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+				e2_blkcnt_t blockcnt,
+				blk_t ref_block EXT2FS_ATTR((unused)),
+				int ref_offset EXT2FS_ATTR((unused)),
+				void *priv_data)
+{
+	struct set_badblock_record *rec = (struct set_badblock_record *)
+		priv_data;
+	errcode_t	retval;
+	unsigned long	old_size;
+
+	if (!*block_nr)
+		return 0;
+
+	/*
+	 * If the block number is outrageous, clear it and ignore it.
+	 */
+	if (*block_nr >= fs->super->s_blocks_count ||
+	    *block_nr < fs->super->s_first_data_block) {
+		*block_nr = 0;
+		return BLOCK_CHANGED;
+	}
+
+	if (blockcnt < 0) {
+		if (rec->ind_blocks_size >= rec->max_ind_blocks) {
+			old_size = rec->max_ind_blocks * sizeof(blk_t);
+			rec->max_ind_blocks += 10;
+			retval = ext2fs_resize_mem(old_size,
+				   rec->max_ind_blocks * sizeof(blk_t),
+				   &rec->ind_blocks);
+			if (retval) {
+				rec->max_ind_blocks -= 10;
+				rec->err = retval;
+				return BLOCK_ABORT;
+			}
+		}
+		rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
+	}
+
+	/*
+	 * Mark the block as unused, and update accounting information
+	 */
+	ext2fs_block_alloc_stats(fs, *block_nr, -1);
+
+	*block_nr = 0;
+	return BLOCK_CHANGED;
+}
+
+
+/*
+ * Helper function for update_bb_inode()
+ *
+ * Set the block list in the bad block inode, using the supplied bitmap.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+			      e2_blkcnt_t blockcnt,
+			      blk_t ref_block EXT2FS_ATTR((unused)),
+			      int ref_offset EXT2FS_ATTR((unused)),
+			      void *priv_data)
+{
+	struct set_badblock_record *rec = (struct set_badblock_record *)
+		priv_data;
+	errcode_t	retval;
+	blk_t		blk;
+
+	if (blockcnt >= 0) {
+		/*
+		 * Get the next bad block.
+		 */
+		if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
+			return BLOCK_ABORT;
+		rec->bad_block_count++;
+	} else {
+		/*
+		 * An indirect block; fetch a block from the
+		 * previously used indirect block list.  The block
+		 * most be not marked as used; if so, get another one.
+		 * If we run out of reserved indirect blocks, allocate
+		 * a new one.
+		 */
+	retry:
+		if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
+			blk = rec->ind_blocks[rec->ind_blocks_ptr++];
+			if (ext2fs_test_block_bitmap(fs->block_map, blk))
+				goto retry;
+		} else {
+			retval = ext2fs_new_block(fs, 0, 0, &blk);
+			if (retval) {
+				rec->err = retval;
+				return BLOCK_ABORT;
+			}
+		}
+		retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
+		if (retval) {
+			rec->err = retval;
+			return BLOCK_ABORT;
+		}
+	}
+
+	/*
+	 * Update block counts
+	 */
+	ext2fs_block_alloc_stats(fs, blk, +1);
+
+	*block_nr = blk;
+	return BLOCK_CHANGED;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c
new file mode 100644
index 0000000..637ed27
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitmaps.c
@@ -0,0 +1,211 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bitmaps.c --- routines to read, write, and manipulate the inode and
+ * block bitmaps.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static errcode_t make_bitmap(__u32 start, __u32 end, __u32 real_end,
+			     const char *descr, char *init_map,
+			     ext2fs_generic_bitmap *ret)
+{
+	ext2fs_generic_bitmap	bitmap;
+	errcode_t		retval;
+	size_t			size;
+
+	retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap),
+				&bitmap);
+	if (retval)
+		return retval;
+
+	bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+	bitmap->fs = NULL;
+	bitmap->start = start;
+	bitmap->end = end;
+	bitmap->real_end = real_end;
+	bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK;
+	if (descr) {
+		retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description);
+		if (retval) {
+			ext2fs_free_mem(&bitmap);
+			return retval;
+		}
+		strcpy(bitmap->description, descr);
+	} else
+		bitmap->description = 0;
+
+	size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
+	retval = ext2fs_get_mem(size, &bitmap->bitmap);
+	if (retval) {
+		ext2fs_free_mem(&bitmap->description);
+		ext2fs_free_mem(&bitmap);
+		return retval;
+	}
+
+	if (init_map)
+		memcpy(bitmap->bitmap, init_map, size);
+	else
+		memset(bitmap->bitmap, 0, size);
+	*ret = bitmap;
+	return 0;
+}
+
+errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
+					 __u32 end,
+					 __u32 real_end,
+					 const char *descr,
+					 ext2fs_generic_bitmap *ret)
+{
+	return make_bitmap(start, end, real_end, descr, 0, ret);
+}
+
+errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
+			     ext2fs_generic_bitmap *dest)
+{
+	errcode_t		retval;
+	ext2fs_generic_bitmap	new_map;
+
+	retval = make_bitmap(src->start, src->end, src->real_end,
+			     src->description, src->bitmap, &new_map);
+	if (retval)
+		return retval;
+	new_map->magic = src->magic;
+	new_map->fs = src->fs;
+	new_map->base_error_code = src->base_error_code;
+	*dest = new_map;
+	return 0;
+}
+
+void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map)
+{
+	__u32	i, j;
+
+	for (i=map->end+1, j = i - map->start; i <= map->real_end; i++, j++)
+		ext2fs_set_bit(j, map->bitmap);
+}
+
+errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
+				       const char *descr,
+				       ext2fs_inode_bitmap *ret)
+{
+	ext2fs_inode_bitmap bitmap;
+	errcode_t	retval;
+	__u32		start, end, real_end;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	fs->write_bitmaps = ext2fs_write_bitmaps;
+
+	start = 1;
+	end = fs->super->s_inodes_count;
+	real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count);
+
+	retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
+						descr, &bitmap);
+	if (retval)
+		return retval;
+
+	bitmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
+	bitmap->fs = fs;
+	bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK;
+
+	*ret = bitmap;
+	return 0;
+}
+
+errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
+				       const char *descr,
+				       ext2fs_block_bitmap *ret)
+{
+	ext2fs_block_bitmap bitmap;
+	errcode_t	retval;
+	__u32		start, end, real_end;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	fs->write_bitmaps = ext2fs_write_bitmaps;
+
+	start = fs->super->s_first_data_block;
+	end = fs->super->s_blocks_count-1;
+	real_end = (EXT2_BLOCKS_PER_GROUP(fs->super)
+		    * fs->group_desc_count)-1 + start;
+
+	retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
+						descr, &bitmap);
+	if (retval)
+		return retval;
+
+	bitmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
+	bitmap->fs = fs;
+	bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK;
+
+	*ret = bitmap;
+	return 0;
+}
+
+errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
+					ext2_ino_t end, ext2_ino_t *oend)
+{
+	EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP);
+
+	if (end > bitmap->real_end)
+		return EXT2_ET_FUDGE_INODE_BITMAP_END;
+	if (oend)
+		*oend = bitmap->end;
+	bitmap->end = end;
+	return 0;
+}
+
+errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
+					blk_t end, blk_t *oend)
+{
+	EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+	if (end > bitmap->real_end)
+		return EXT2_ET_FUDGE_BLOCK_BITMAP_END;
+	if (oend)
+		*oend = bitmap->end;
+	bitmap->end = end;
+	return 0;
+}
+
+void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap)
+{
+	if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
+		return;
+
+	memset(bitmap->bitmap, 0,
+	       (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+}
+
+void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap)
+{
+	if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
+		return;
+
+	memset(bitmap->bitmap, 0,
+	       (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c
new file mode 100644
index 0000000..3d08394
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitops.c
@@ -0,0 +1,90 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bitops.c --- Bitmap frobbing code.  See bitops.h for the inlined
+ *	routines.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef _EXT2_HAVE_ASM_BITOPS_
+
+/*
+ * For the benefit of those who are trying to port Linux to another
+ * architecture, here are some C-language equivalents.  You should
+ * recode these in the native assmebly language, if at all possible.
+ *
+ * C language equivalents written by Theodore Ts'o, 9/26/92.
+ * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian
+ * systems, as well as non-32 bit systems.
+ */
+
+int ext2fs_set_bit(unsigned int nr,void * addr)
+{
+	int		mask, retval;
+	unsigned char	*ADDR = (unsigned char *) addr;
+
+	ADDR += nr >> 3;
+	mask = 1 << (nr & 0x07);
+	retval = mask & *ADDR;
+	*ADDR |= mask;
+	return retval;
+}
+
+int ext2fs_clear_bit(unsigned int nr, void * addr)
+{
+	int		mask, retval;
+	unsigned char	*ADDR = (unsigned char *) addr;
+
+	ADDR += nr >> 3;
+	mask = 1 << (nr & 0x07);
+	retval = mask & *ADDR;
+	*ADDR &= ~mask;
+	return retval;
+}
+
+int ext2fs_test_bit(unsigned int nr, const void * addr)
+{
+	int			mask;
+	const unsigned char	*ADDR = (const unsigned char *) addr;
+
+	ADDR += nr >> 3;
+	mask = 1 << (nr & 0x07);
+	return (mask & *ADDR);
+}
+
+#endif  /* !_EXT2_HAVE_ASM_BITOPS_ */
+
+void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
+			const char *description)
+{
+#ifndef OMIT_COM_ERR
+	if (description)
+		bb_error_msg("#%lu for %s", arg, description);
+	else
+		bb_error_msg("#%lu", arg);
+#endif
+}
+
+void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
+			    int code, unsigned long arg)
+{
+#ifndef OMIT_COM_ERR
+	if (bitmap->description)
+		bb_error_msg("#%lu for %s", arg, bitmap->description);
+	else
+		bb_error_msg("#%lu", arg);
+#endif
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h
new file mode 100644
index 0000000..7271a49
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bitops.h
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bitops.h --- Bitmap frobbing code.  The byte swapping routines are
+ *	also included here.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ * i386 bitops operations taken from <asm/bitops.h>, Copyright 1992,
+ * Linus Torvalds.
+ */
+#include <string.h>
+
+extern int ext2fs_set_bit(unsigned int nr,void * addr);
+extern int ext2fs_clear_bit(unsigned int nr, void * addr);
+extern int ext2fs_test_bit(unsigned int nr, const void * addr);
+extern __u16 ext2fs_swab16(__u16 val);
+extern __u32 ext2fs_swab32(__u32 val);
+
+#ifdef WORDS_BIGENDIAN
+#define ext2fs_cpu_to_le32(x) ext2fs_swab32((x))
+#define ext2fs_le32_to_cpu(x) ext2fs_swab32((x))
+#define ext2fs_cpu_to_le16(x) ext2fs_swab16((x))
+#define ext2fs_le16_to_cpu(x) ext2fs_swab16((x))
+#define ext2fs_cpu_to_be32(x) ((__u32)(x))
+#define ext2fs_be32_to_cpu(x) ((__u32)(x))
+#define ext2fs_cpu_to_be16(x) ((__u16)(x))
+#define ext2fs_be16_to_cpu(x) ((__u16)(x))
+#else
+#define ext2fs_cpu_to_le32(x) ((__u32)(x))
+#define ext2fs_le32_to_cpu(x) ((__u32)(x))
+#define ext2fs_cpu_to_le16(x) ((__u16)(x))
+#define ext2fs_le16_to_cpu(x) ((__u16)(x))
+#define ext2fs_cpu_to_be32(x) ext2fs_swab32((x))
+#define ext2fs_be32_to_cpu(x) ext2fs_swab32((x))
+#define ext2fs_cpu_to_be16(x) ext2fs_swab16((x))
+#define ext2fs_be16_to_cpu(x) ext2fs_swab16((x))
+#endif
+
+/*
+ * EXT2FS bitmap manipulation routines.
+ */
+
+/* Support for sending warning messages from the inline subroutines */
+extern const char *ext2fs_block_string;
+extern const char *ext2fs_inode_string;
+extern const char *ext2fs_mark_string;
+extern const char *ext2fs_unmark_string;
+extern const char *ext2fs_test_string;
+extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
+			       const char *description);
+extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
+				int code, unsigned long arg);
+
+extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
+extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+				       blk_t block);
+extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
+
+extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
+extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+				       ext2_ino_t inode);
+extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
+
+extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+					  blk_t block);
+extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+					    blk_t block);
+extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
+					 blk_t block);
+
+extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+					  ext2_ino_t inode);
+extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+					    ext2_ino_t inode);
+extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+					 ext2_ino_t inode);
+extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap);
+extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap);
+extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap);
+extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap);
+
+extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+					   blk_t block, int num);
+extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+					     blk_t block, int num);
+extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+					  blk_t block, int num);
+extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+						blk_t block, int num);
+extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+						  blk_t block, int num);
+extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+					       blk_t block, int num);
+extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
+
+/* These two routines moved to gen_bitmap.c */
+extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+					 __u32 bitno);
+extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+					   blk_t bitno);
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/block.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/block.c
new file mode 100644
index 0000000..dbd04f8
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/block.c
@@ -0,0 +1,437 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * block.c --- iterate over all blocks in an inode
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct block_context {
+	ext2_filsys	fs;
+	int (*func)(ext2_filsys	fs,
+		    blk_t	*blocknr,
+		    e2_blkcnt_t	bcount,
+		    blk_t	ref_blk,
+		    int		ref_offset,
+		    void	*priv_data);
+	e2_blkcnt_t	bcount;
+	int		bsize;
+	int		flags;
+	errcode_t	errcode;
+	char	*ind_buf;
+	char	*dind_buf;
+	char	*tind_buf;
+	void	*priv_data;
+};
+
+static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
+			     int ref_offset, struct block_context *ctx)
+{
+	int	ret = 0, changed = 0;
+	int	i, flags, limit, offset;
+	blk_t	*block_nr;
+
+	limit = ctx->fs->blocksize >> 2;
+	if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
+		ret = (*ctx->func)(ctx->fs, ind_block,
+				   BLOCK_COUNT_IND, ref_block,
+				   ref_offset, ctx->priv_data);
+	if (!*ind_block || (ret & BLOCK_ABORT)) {
+		ctx->bcount += limit;
+		return ret;
+	}
+	if (*ind_block >= ctx->fs->super->s_blocks_count ||
+	    *ind_block < ctx->fs->super->s_first_data_block) {
+		ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
+		ret |= BLOCK_ERROR;
+		return ret;
+	}
+	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block,
+					     ctx->ind_buf);
+	if (ctx->errcode) {
+		ret |= BLOCK_ERROR;
+		return ret;
+	}
+
+	block_nr = (blk_t *) ctx->ind_buf;
+	offset = 0;
+	if (ctx->flags & BLOCK_FLAG_APPEND) {
+		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
+			flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
+					     *ind_block, offset,
+					     ctx->priv_data);
+			changed	|= flags;
+			if (flags & BLOCK_ABORT) {
+				ret |= BLOCK_ABORT;
+				break;
+			}
+			offset += sizeof(blk_t);
+		}
+	} else {
+		for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
+			if (*block_nr == 0)
+				continue;
+			flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
+					     *ind_block, offset,
+					     ctx->priv_data);
+			changed	|= flags;
+			if (flags & BLOCK_ABORT) {
+				ret |= BLOCK_ABORT;
+				break;
+			}
+			offset += sizeof(blk_t);
+		}
+	}
+	if (changed & BLOCK_CHANGED) {
+		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
+						      ctx->ind_buf);
+		if (ctx->errcode)
+			ret |= BLOCK_ERROR | BLOCK_ABORT;
+	}
+	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+	    !(ret & BLOCK_ABORT))
+		ret |= (*ctx->func)(ctx->fs, ind_block,
+				    BLOCK_COUNT_IND, ref_block,
+				    ref_offset, ctx->priv_data);
+	return ret;
+}
+
+static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
+			      int ref_offset, struct block_context *ctx)
+{
+	int	ret = 0, changed = 0;
+	int	i, flags, limit, offset;
+	blk_t	*block_nr;
+
+	limit = ctx->fs->blocksize >> 2;
+	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
+			    BLOCK_FLAG_DATA_ONLY)))
+		ret = (*ctx->func)(ctx->fs, dind_block,
+				   BLOCK_COUNT_DIND, ref_block,
+				   ref_offset, ctx->priv_data);
+	if (!*dind_block || (ret & BLOCK_ABORT)) {
+		ctx->bcount += limit*limit;
+		return ret;
+	}
+	if (*dind_block >= ctx->fs->super->s_blocks_count ||
+	    *dind_block < ctx->fs->super->s_first_data_block) {
+		ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
+		ret |= BLOCK_ERROR;
+		return ret;
+	}
+	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block,
+					     ctx->dind_buf);
+	if (ctx->errcode) {
+		ret |= BLOCK_ERROR;
+		return ret;
+	}
+
+	block_nr = (blk_t *) ctx->dind_buf;
+	offset = 0;
+	if (ctx->flags & BLOCK_FLAG_APPEND) {
+		for (i = 0; i < limit; i++, block_nr++) {
+			flags = block_iterate_ind(block_nr,
+						  *dind_block, offset,
+						  ctx);
+			changed |= flags;
+			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+				break;
+			}
+			offset += sizeof(blk_t);
+		}
+	} else {
+		for (i = 0; i < limit; i++, block_nr++) {
+			if (*block_nr == 0) {
+				ctx->bcount += limit;
+				continue;
+			}
+			flags = block_iterate_ind(block_nr,
+						  *dind_block, offset,
+						  ctx);
+			changed |= flags;
+			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+				break;
+			}
+			offset += sizeof(blk_t);
+		}
+	}
+	if (changed & BLOCK_CHANGED) {
+		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
+						      ctx->dind_buf);
+		if (ctx->errcode)
+			ret |= BLOCK_ERROR | BLOCK_ABORT;
+	}
+	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+	    !(ret & BLOCK_ABORT))
+		ret |= (*ctx->func)(ctx->fs, dind_block,
+				    BLOCK_COUNT_DIND, ref_block,
+				    ref_offset, ctx->priv_data);
+	return ret;
+}
+
+static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
+			      int ref_offset, struct block_context *ctx)
+{
+	int	ret = 0, changed = 0;
+	int	i, flags, limit, offset;
+	blk_t	*block_nr;
+
+	limit = ctx->fs->blocksize >> 2;
+	if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
+			    BLOCK_FLAG_DATA_ONLY)))
+		ret = (*ctx->func)(ctx->fs, tind_block,
+				   BLOCK_COUNT_TIND, ref_block,
+				   ref_offset, ctx->priv_data);
+	if (!*tind_block || (ret & BLOCK_ABORT)) {
+		ctx->bcount += limit*limit*limit;
+		return ret;
+	}
+	if (*tind_block >= ctx->fs->super->s_blocks_count ||
+	    *tind_block < ctx->fs->super->s_first_data_block) {
+		ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
+		ret |= BLOCK_ERROR;
+		return ret;
+	}
+	ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block,
+					     ctx->tind_buf);
+	if (ctx->errcode) {
+		ret |= BLOCK_ERROR;
+		return ret;
+	}
+
+	block_nr = (blk_t *) ctx->tind_buf;
+	offset = 0;
+	if (ctx->flags & BLOCK_FLAG_APPEND) {
+		for (i = 0; i < limit; i++, block_nr++) {
+			flags = block_iterate_dind(block_nr,
+						   *tind_block,
+						   offset, ctx);
+			changed |= flags;
+			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+				break;
+			}
+			offset += sizeof(blk_t);
+		}
+	} else {
+		for (i = 0; i < limit; i++, block_nr++) {
+			if (*block_nr == 0) {
+				ctx->bcount += limit*limit;
+				continue;
+			}
+			flags = block_iterate_dind(block_nr,
+						   *tind_block,
+						   offset, ctx);
+			changed |= flags;
+			if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+				ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+				break;
+			}
+			offset += sizeof(blk_t);
+		}
+	}
+	if (changed & BLOCK_CHANGED) {
+		ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
+						      ctx->tind_buf);
+		if (ctx->errcode)
+			ret |= BLOCK_ERROR | BLOCK_ABORT;
+	}
+	if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+	    !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+	    !(ret & BLOCK_ABORT))
+		ret |= (*ctx->func)(ctx->fs, tind_block,
+				    BLOCK_COUNT_TIND, ref_block,
+				    ref_offset, ctx->priv_data);
+
+	return ret;
+}
+
+errcode_t ext2fs_block_iterate2(ext2_filsys fs,
+				ext2_ino_t ino,
+				int	flags,
+				char *block_buf,
+				int (*func)(ext2_filsys fs,
+					    blk_t	*blocknr,
+					    e2_blkcnt_t	blockcnt,
+					    blk_t	ref_blk,
+					    int		ref_offset,
+					    void	*priv_data),
+				void *priv_data)
+{
+	int	i;
+	int	got_inode = 0;
+	int	ret = 0;
+	blk_t	blocks[EXT2_N_BLOCKS];	/* directory data blocks */
+	struct ext2_inode inode;
+	errcode_t	retval;
+	struct block_context ctx;
+	int	limit;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	/*
+	 * Check to see if we need to limit large files
+	 */
+	if (flags & BLOCK_FLAG_NO_LARGE) {
+		ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+		if (ctx.errcode)
+			return ctx.errcode;
+		got_inode = 1;
+		if (!LINUX_S_ISDIR(inode.i_mode) &&
+		    (inode.i_size_high != 0))
+			return EXT2_ET_FILE_TOO_BIG;
+	}
+
+	retval = ext2fs_get_blocks(fs, ino, blocks);
+	if (retval)
+		return retval;
+
+	limit = fs->blocksize >> 2;
+
+	ctx.fs = fs;
+	ctx.func = func;
+	ctx.priv_data = priv_data;
+	ctx.flags = flags;
+	ctx.bcount = 0;
+	if (block_buf) {
+		ctx.ind_buf = block_buf;
+	} else {
+		retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
+		if (retval)
+			return retval;
+	}
+	ctx.dind_buf = ctx.ind_buf + fs->blocksize;
+	ctx.tind_buf = ctx.dind_buf + fs->blocksize;
+
+	/*
+	 * Iterate over the HURD translator block (if present)
+	 */
+	if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
+	    !(flags & BLOCK_FLAG_DATA_ONLY)) {
+		ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+		if (ctx.errcode)
+			goto abort_exit;
+		got_inode = 1;
+		if (inode.osd1.hurd1.h_i_translator) {
+			ret |= (*ctx.func)(fs,
+					   &inode.osd1.hurd1.h_i_translator,
+					   BLOCK_COUNT_TRANSLATOR,
+					   0, 0, priv_data);
+			if (ret & BLOCK_ABORT)
+				goto abort_exit;
+		}
+	}
+
+	/*
+	 * Iterate over normal data blocks
+	 */
+	for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) {
+		if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
+			ret |= (*ctx.func)(fs, &blocks[i],
+					    ctx.bcount, 0, i, priv_data);
+			if (ret & BLOCK_ABORT)
+				goto abort_exit;
+		}
+	}
+	if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+		ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
+					 0, EXT2_IND_BLOCK, &ctx);
+		if (ret & BLOCK_ABORT)
+			goto abort_exit;
+	} else
+		ctx.bcount += limit;
+	if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+		ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
+					  0, EXT2_DIND_BLOCK, &ctx);
+		if (ret & BLOCK_ABORT)
+			goto abort_exit;
+	} else
+		ctx.bcount += limit * limit;
+	if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+		ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
+					  0, EXT2_TIND_BLOCK, &ctx);
+		if (ret & BLOCK_ABORT)
+			goto abort_exit;
+	}
+
+abort_exit:
+	if (ret & BLOCK_CHANGED) {
+		if (!got_inode) {
+			retval = ext2fs_read_inode(fs, ino, &inode);
+			if (retval)
+				return retval;
+		}
+		for (i=0; i < EXT2_N_BLOCKS; i++)
+			inode.i_block[i] = blocks[i];
+		retval = ext2fs_write_inode(fs, ino, &inode);
+		if (retval)
+			return retval;
+	}
+
+	if (!block_buf)
+		ext2fs_free_mem(&ctx.ind_buf);
+
+	return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
+}
+
+/*
+ * Emulate the old ext2fs_block_iterate function!
+ */
+
+struct xlate {
+	int (*func)(ext2_filsys	fs,
+		    blk_t	*blocknr,
+		    int		bcount,
+		    void	*priv_data);
+	void *real_private;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
+		      blk_t ref_block EXT2FS_ATTR((unused)),
+		      int ref_offset EXT2FS_ATTR((unused)),
+		      void *priv_data)
+{
+	struct xlate *xl = (struct xlate *) priv_data;
+
+	return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
+}
+
+errcode_t ext2fs_block_iterate(ext2_filsys fs,
+			       ext2_ino_t ino,
+			       int	flags,
+			       char *block_buf,
+			       int (*func)(ext2_filsys fs,
+					   blk_t	*blocknr,
+					   int	blockcnt,
+					   void	*priv_data),
+			       void *priv_data)
+{
+	struct xlate xl;
+
+	xl.real_private = priv_data;
+	xl.func = func;
+
+	return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
+				     block_buf, xlate_func, &xl);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
new file mode 100644
index 0000000..796b0e4
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bmap.c
@@ -0,0 +1,261 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bmap.c --- logical to physical block mapping
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
+			     struct ext2_inode *inode,
+			     char *block_buf, int bmap_flags,
+			     blk_t block, blk_t *phys_blk);
+
+#define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
+
+static errcode_t block_ind_bmap(ext2_filsys fs, int flags,
+					      blk_t ind, char *block_buf,
+					      int *blocks_alloc,
+					      blk_t nr, blk_t *ret_blk)
+{
+	errcode_t	retval;
+	blk_t		b;
+
+	if (!ind) {
+		if (flags & BMAP_SET)
+			return EXT2_ET_SET_BMAP_NO_IND;
+		*ret_blk = 0;
+		return 0;
+	}
+	retval = io_channel_read_blk(fs->io, ind, 1, block_buf);
+	if (retval)
+		return retval;
+
+	if (flags & BMAP_SET) {
+		b = *ret_blk;
+#if BB_BIG_ENDIAN
+		if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+		    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+			b = ext2fs_swab32(b);
+#endif
+		((blk_t *) block_buf)[nr] = b;
+		return io_channel_write_blk(fs->io, ind, 1, block_buf);
+	}
+
+	b = ((blk_t *) block_buf)[nr];
+
+#if BB_BIG_ENDIAN
+	if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+	    (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+		b = ext2fs_swab32(b);
+#endif
+
+	if (!b && (flags & BMAP_ALLOC)) {
+		b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
+		retval = ext2fs_alloc_block(fs, b,
+					    block_buf + fs->blocksize, &b);
+		if (retval)
+			return retval;
+
+#if BB_BIG_ENDIAN
+		if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+		    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+			((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
+		else
+#endif
+			((blk_t *) block_buf)[nr] = b;
+
+		retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
+		if (retval)
+			return retval;
+
+		(*blocks_alloc)++;
+	}
+
+	*ret_blk = b;
+	return 0;
+}
+
+static errcode_t block_dind_bmap(ext2_filsys fs, int flags,
+					       blk_t dind, char *block_buf,
+					       int *blocks_alloc,
+					       blk_t nr, blk_t *ret_blk)
+{
+	blk_t		b;
+	errcode_t	retval;
+	blk_t		addr_per_block;
+
+	addr_per_block = (blk_t) fs->blocksize >> 2;
+
+	retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf,
+				blocks_alloc, nr / addr_per_block, &b);
+	if (retval)
+		return retval;
+	retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
+				nr % addr_per_block, ret_blk);
+	return retval;
+}
+
+static errcode_t block_tind_bmap(ext2_filsys fs, int flags,
+					       blk_t tind, char *block_buf,
+					       int *blocks_alloc,
+					       blk_t nr, blk_t *ret_blk)
+{
+	blk_t		b;
+	errcode_t	retval;
+	blk_t		addr_per_block;
+
+	addr_per_block = (blk_t) fs->blocksize >> 2;
+
+	retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf,
+				 blocks_alloc, nr / addr_per_block, &b);
+	if (retval)
+		return retval;
+	retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
+				nr % addr_per_block, ret_blk);
+	return retval;
+}
+
+errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
+		      char *block_buf, int bmap_flags, blk_t block,
+		      blk_t *phys_blk)
+{
+	struct ext2_inode inode_buf;
+	blk_t addr_per_block;
+	blk_t	b;
+	char	*buf = NULL;
+	errcode_t	retval = 0;
+	int		blocks_alloc = 0, inode_dirty = 0;
+
+	if (!(bmap_flags & BMAP_SET))
+		*phys_blk = 0;
+
+	/* Read inode structure if necessary */
+	if (!inode) {
+		retval = ext2fs_read_inode(fs, ino, &inode_buf);
+		if (retval)
+			return retval;
+		inode = &inode_buf;
+	}
+	addr_per_block = (blk_t) fs->blocksize >> 2;
+
+	if (!block_buf) {
+		retval = ext2fs_get_mem(fs->blocksize * 2, &buf);
+		if (retval)
+			return retval;
+		block_buf = buf;
+	}
+
+	if (block < EXT2_NDIR_BLOCKS) {
+		if (bmap_flags & BMAP_SET) {
+			b = *phys_blk;
+#if BB_BIG_ENDIAN
+			if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+			    (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+				b = ext2fs_swab32(b);
+#endif
+			inode_bmap(inode, block) = b;
+			inode_dirty++;
+			goto done;
+		}
+
+		*phys_blk = inode_bmap(inode, block);
+		b = block ? inode_bmap(inode, block-1) : 0;
+
+		if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
+			retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+			if (retval)
+				goto done;
+			inode_bmap(inode, block) = b;
+			blocks_alloc++;
+			*phys_blk = b;
+		}
+		goto done;
+	}
+
+	/* Indirect block */
+	block -= EXT2_NDIR_BLOCKS;
+	if (block < addr_per_block) {
+		b = inode_bmap(inode, EXT2_IND_BLOCK);
+		if (!b) {
+			if (!(bmap_flags & BMAP_ALLOC)) {
+				if (bmap_flags & BMAP_SET)
+					retval = EXT2_ET_SET_BMAP_NO_IND;
+				goto done;
+			}
+
+			b = inode_bmap(inode, EXT2_IND_BLOCK-1);
+			retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+			if (retval)
+				goto done;
+			inode_bmap(inode, EXT2_IND_BLOCK) = b;
+			blocks_alloc++;
+		}
+		retval = block_ind_bmap(fs, bmap_flags, b, block_buf,
+					&blocks_alloc, block, phys_blk);
+		goto done;
+	}
+
+	/* Doubly indirect block  */
+	block -= addr_per_block;
+	if (block < addr_per_block * addr_per_block) {
+		b = inode_bmap(inode, EXT2_DIND_BLOCK);
+		if (!b) {
+			if (!(bmap_flags & BMAP_ALLOC)) {
+				if (bmap_flags & BMAP_SET)
+					retval = EXT2_ET_SET_BMAP_NO_IND;
+				goto done;
+			}
+
+			b = inode_bmap(inode, EXT2_IND_BLOCK);
+			retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+			if (retval)
+				goto done;
+			inode_bmap(inode, EXT2_DIND_BLOCK) = b;
+			blocks_alloc++;
+		}
+		retval = block_dind_bmap(fs, bmap_flags, b, block_buf,
+					 &blocks_alloc, block, phys_blk);
+		goto done;
+	}
+
+	/* Triply indirect block */
+	block -= addr_per_block * addr_per_block;
+	b = inode_bmap(inode, EXT2_TIND_BLOCK);
+	if (!b) {
+		if (!(bmap_flags & BMAP_ALLOC)) {
+			if (bmap_flags & BMAP_SET)
+				retval = EXT2_ET_SET_BMAP_NO_IND;
+			goto done;
+		}
+
+		b = inode_bmap(inode, EXT2_DIND_BLOCK);
+		retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+		if (retval)
+			goto done;
+		inode_bmap(inode, EXT2_TIND_BLOCK) = b;
+		blocks_alloc++;
+	}
+	retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
+				 &blocks_alloc, block, phys_blk);
+done:
+	ext2fs_free_mem(&buf);
+	if ((retval == 0) && (blocks_alloc || inode_dirty)) {
+		inode->i_blocks += (blocks_alloc * fs->blocksize) / 512;
+		retval = ext2fs_write_inode(fs, ino, inode);
+	}
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c
new file mode 100644
index 0000000..ec9244d
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/bmove.c
@@ -0,0 +1,155 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bmove.c --- Move blocks around to make way for a particular
+ *	filesystem structure.
+ *
+ * Copyright (C) 1997 Theodore Ts'o.  This file may be redistributed
+ * under the terms of the GNU Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+struct process_block_struct {
+	ext2_ino_t		ino;
+	struct ext2_inode *	inode;
+	ext2fs_block_bitmap	reserve;
+	ext2fs_block_bitmap	alloc_map;
+	errcode_t		error;
+	char			*buf;
+	int			add_dir;
+	int			flags;
+};
+
+static int process_block(ext2_filsys fs, blk_t	*block_nr,
+			 e2_blkcnt_t blockcnt, blk_t ref_block,
+			 int ref_offset, void *priv_data)
+{
+	struct process_block_struct *pb;
+	errcode_t	retval;
+	int		ret;
+	blk_t		block, orig;
+
+	pb = (struct process_block_struct *) priv_data;
+	block = orig = *block_nr;
+	ret = 0;
+
+	/*
+	 * Let's see if this is one which we need to relocate
+	 */
+	if (ext2fs_test_block_bitmap(pb->reserve, block)) {
+		do {
+			if (++block >= fs->super->s_blocks_count)
+				block = fs->super->s_first_data_block;
+			if (block == orig) {
+				pb->error = EXT2_ET_BLOCK_ALLOC_FAIL;
+				return BLOCK_ABORT;
+			}
+		} while (ext2fs_test_block_bitmap(pb->reserve, block) ||
+			 ext2fs_test_block_bitmap(pb->alloc_map, block));
+
+		retval = io_channel_read_blk(fs->io, orig, 1, pb->buf);
+		if (retval) {
+			pb->error = retval;
+			return BLOCK_ABORT;
+		}
+		retval = io_channel_write_blk(fs->io, block, 1, pb->buf);
+		if (retval) {
+			pb->error = retval;
+			return BLOCK_ABORT;
+		}
+		*block_nr = block;
+		ext2fs_mark_block_bitmap(pb->alloc_map, block);
+		ret = BLOCK_CHANGED;
+		if (pb->flags & EXT2_BMOVE_DEBUG)
+			printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino,
+			       blockcnt, orig, block);
+	}
+	if (pb->add_dir) {
+		retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
+					      block, (int) blockcnt);
+		if (retval) {
+			pb->error = retval;
+			ret |= BLOCK_ABORT;
+		}
+	}
+	return ret;
+}
+
+errcode_t ext2fs_move_blocks(ext2_filsys fs,
+			     ext2fs_block_bitmap reserve,
+			     ext2fs_block_bitmap alloc_map,
+			     int flags)
+{
+	ext2_ino_t	ino;
+	struct ext2_inode inode;
+	errcode_t	retval;
+	struct process_block_struct pb;
+	ext2_inode_scan	scan;
+	char		*block_buf;
+
+	retval = ext2fs_open_inode_scan(fs, 0, &scan);
+	if (retval)
+		return retval;
+
+	pb.reserve = reserve;
+	pb.error = 0;
+	pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
+	pb.flags = flags;
+
+	retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf);
+	if (retval)
+		return retval;
+	pb.buf = block_buf + fs->blocksize * 3;
+
+	/*
+	 * If GET_DBLIST is set in the flags field, then we should
+	 * gather directory block information while we're doing the
+	 * block move.
+	 */
+	if (flags & EXT2_BMOVE_GET_DBLIST) {
+		ext2fs_free_dblist(fs->dblist);
+		fs->dblist = NULL;
+		retval = ext2fs_init_dblist(fs, 0);
+		if (retval)
+			return retval;
+	}
+
+	retval = ext2fs_get_next_inode(scan, &ino, &inode);
+	if (retval)
+		return retval;
+
+	while (ino) {
+		if ((inode.i_links_count == 0) ||
+		    !ext2fs_inode_has_valid_blocks(&inode))
+			goto next;
+
+		pb.ino = ino;
+		pb.inode = &inode;
+
+		pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) &&
+			      flags & EXT2_BMOVE_GET_DBLIST);
+
+		retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+					      process_block, &pb);
+		if (retval)
+			return retval;
+		if (pb.error)
+			return pb.error;
+
+	next:
+		retval = ext2fs_get_next_inode(scan, &ino, &inode);
+		if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+			goto next;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/brel.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/brel.h
new file mode 100644
index 0000000..87bf72b
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/brel.h
@@ -0,0 +1,86 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * brel.h
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct ext2_block_relocate_entry {
+	blk_t	new;
+	__s16	offset;
+	__u16	flags;
+	union {
+		blk_t		block_ref;
+		ext2_ino_t	inode_ref;
+	} owner;
+};
+
+#define RELOCATE_TYPE_REF  0x0007
+#define RELOCATE_BLOCK_REF 0x0001
+#define RELOCATE_INODE_REF 0x0002
+
+typedef struct ext2_block_relocation_table *ext2_brel;
+
+struct ext2_block_relocation_table {
+	__u32	magic;
+	char	*name;
+	blk_t	current;
+	void	*priv_data;
+
+	/*
+	 * Add a block relocation entry.
+	 */
+	errcode_t (*put)(ext2_brel brel, blk_t old,
+			      struct ext2_block_relocate_entry *ent);
+
+	/*
+	 * Get a block relocation entry.
+	 */
+	errcode_t (*get)(ext2_brel brel, blk_t old,
+			      struct ext2_block_relocate_entry *ent);
+
+	/*
+	 * Initialize for iterating over the block relocation entries.
+	 */
+	errcode_t (*start_iter)(ext2_brel brel);
+
+	/*
+	 * The iterator function for the inode relocation entries.
+	 * Returns an inode number of 0 when out of entries.
+	 */
+	errcode_t (*next)(ext2_brel brel, blk_t *old,
+			  struct ext2_block_relocate_entry *ent);
+
+	/*
+	 * Move the inode relocation table from one block number to
+	 * another.
+	 */
+	errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new);
+
+	/*
+	 * Remove a block relocation entry.
+	 */
+	errcode_t (*delete)(ext2_brel brel, blk_t old);
+
+
+	/*
+	 * Free the block relocation table.
+	 */
+	errcode_t (*free)(ext2_brel brel);
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+				    ext2_brel *brel);
+
+#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent))
+#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent))
+#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel)))
+#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent))
+#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)))
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c
new file mode 100644
index 0000000..652a350
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/brel_ma.c
@@ -0,0 +1,196 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * brel_ma.c
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * TODO: rewrite to not use a direct array!!!  (Fortunately this
+ * module isn't really used yet.)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "brel.h"
+
+static errcode_t bma_put(ext2_brel brel, blk_t old,
+			struct ext2_block_relocate_entry *ent);
+static errcode_t bma_get(ext2_brel brel, blk_t old,
+			struct ext2_block_relocate_entry *ent);
+static errcode_t bma_start_iter(ext2_brel brel);
+static errcode_t bma_next(ext2_brel brel, blk_t *old,
+			 struct ext2_block_relocate_entry *ent);
+static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new);
+static errcode_t bma_delete(ext2_brel brel, blk_t old);
+static errcode_t bma_free(ext2_brel brel);
+
+struct brel_ma {
+	__u32 magic;
+	blk_t max_block;
+	struct ext2_block_relocate_entry *entries;
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+				      ext2_brel *new_brel)
+{
+	ext2_brel		brel = 0;
+	errcode_t	retval;
+	struct brel_ma	*ma = 0;
+	size_t		size;
+
+	*new_brel = 0;
+
+	/*
+	 * Allocate memory structures
+	 */
+	retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table),
+				&brel);
+	if (retval)
+		goto errout;
+	memset(brel, 0, sizeof(struct ext2_block_relocation_table));
+
+	retval = ext2fs_get_mem(strlen(name)+1, &brel->name);
+	if (retval)
+		goto errout;
+	strcpy(brel->name, name);
+
+	retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma);
+	if (retval)
+		goto errout;
+	memset(ma, 0, sizeof(struct brel_ma));
+	brel->priv_data = ma;
+
+	size = (size_t) (sizeof(struct ext2_block_relocate_entry) *
+			 (max_block+1));
+	retval = ext2fs_get_mem(size, &ma->entries);
+	if (retval)
+		goto errout;
+	memset(ma->entries, 0, size);
+	ma->max_block = max_block;
+
+	/*
+	 * Fill in the brel data structure
+	 */
+	brel->put = bma_put;
+	brel->get = bma_get;
+	brel->start_iter = bma_start_iter;
+	brel->next = bma_next;
+	brel->move = bma_move;
+	brel->delete = bma_delete;
+	brel->free = bma_free;
+
+	*new_brel = brel;
+	return 0;
+
+errout:
+	bma_free(brel);
+	return retval;
+}
+
+static errcode_t bma_put(ext2_brel brel, blk_t old,
+			struct ext2_block_relocate_entry *ent)
+{
+	struct brel_ma	*ma;
+
+	ma = brel->priv_data;
+	if (old > ma->max_block)
+		return EXT2_ET_INVALID_ARGUMENT;
+	ma->entries[(unsigned)old] = *ent;
+	return 0;
+}
+
+static errcode_t bma_get(ext2_brel brel, blk_t old,
+			struct ext2_block_relocate_entry *ent)
+{
+	struct brel_ma	*ma;
+
+	ma = brel->priv_data;
+	if (old > ma->max_block)
+		return EXT2_ET_INVALID_ARGUMENT;
+	if (ma->entries[(unsigned)old].new == 0)
+		return ENOENT;
+	*ent = ma->entries[old];
+	return 0;
+}
+
+static errcode_t bma_start_iter(ext2_brel brel)
+{
+	brel->current = 0;
+	return 0;
+}
+
+static errcode_t bma_next(ext2_brel brel, blk_t *old,
+			  struct ext2_block_relocate_entry *ent)
+{
+	struct brel_ma	*ma;
+
+	ma = brel->priv_data;
+	while (++brel->current < ma->max_block) {
+		if (ma->entries[(unsigned)brel->current].new == 0)
+			continue;
+		*old = brel->current;
+		*ent = ma->entries[(unsigned)brel->current];
+		return 0;
+	}
+	*old = 0;
+	return 0;
+}
+
+static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new)
+{
+	struct brel_ma	*ma;
+
+	ma = brel->priv_data;
+	if ((old > ma->max_block) || (new > ma->max_block))
+		return EXT2_ET_INVALID_ARGUMENT;
+	if (ma->entries[(unsigned)old].new == 0)
+		return ENOENT;
+	ma->entries[(unsigned)new] = ma->entries[old];
+	ma->entries[(unsigned)old].new = 0;
+	return 0;
+}
+
+static errcode_t bma_delete(ext2_brel brel, blk_t old)
+{
+	struct brel_ma	*ma;
+
+	ma = brel->priv_data;
+	if (old > ma->max_block)
+		return EXT2_ET_INVALID_ARGUMENT;
+	if (ma->entries[(unsigned)old].new == 0)
+		return ENOENT;
+	ma->entries[(unsigned)old].new = 0;
+	return 0;
+}
+
+static errcode_t bma_free(ext2_brel brel)
+{
+	struct brel_ma	*ma;
+
+	if (!brel)
+		return 0;
+
+	ma = brel->priv_data;
+
+	if (ma) {
+		ext2fs_free_mem(&ma->entries);
+		ext2fs_free_mem(&ma);
+	}
+	ext2fs_free_mem(&brel->name);
+	ext2fs_free_mem(&brel);
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c
new file mode 100644
index 0000000..dd4b0e9
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/check_desc.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * check_desc.c --- Check the group descriptors of an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * This routine sanity checks the group descriptors
+ */
+errcode_t ext2fs_check_desc(ext2_filsys fs)
+{
+	dgrp_t i;
+	blk_t block = fs->super->s_first_data_block;
+	blk_t next;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		next = block + fs->super->s_blocks_per_group;
+		/*
+		 * Check to make sure block bitmap for group is
+		 * located within the group.
+		 */
+		if (fs->group_desc[i].bg_block_bitmap < block ||
+		    fs->group_desc[i].bg_block_bitmap >= next)
+			return EXT2_ET_GDESC_BAD_BLOCK_MAP;
+		/*
+		 * Check to make sure inode bitmap for group is
+		 * located within the group
+		 */
+		if (fs->group_desc[i].bg_inode_bitmap < block ||
+		    fs->group_desc[i].bg_inode_bitmap >= next)
+			return EXT2_ET_GDESC_BAD_INODE_MAP;
+		/*
+		 * Check to make sure inode table for group is located
+		 * within the group
+		 */
+		if (fs->group_desc[i].bg_inode_table < block ||
+		    ((fs->group_desc[i].bg_inode_table +
+		      fs->inode_blocks_per_group) >= next))
+			return EXT2_ET_GDESC_BAD_INODE_TABLE;
+
+		block = next;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c
new file mode 100644
index 0000000..bfa15e2
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/closefs.c
@@ -0,0 +1,380 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * closefs.c --- close an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int test_root(int a, int b)
+{
+	if (a == 0)
+		return 1;
+	while (1) {
+		if (a == 1)
+			return 1;
+		if (a % b)
+			return 0;
+		a = a / b;
+	}
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+{
+	if (!(fs->super->s_feature_ro_compat &
+	      EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+		return 1;
+
+	if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
+	    test_root(group_block, 7))
+		return 1;
+
+	return 0;
+}
+
+int ext2fs_super_and_bgd_loc(ext2_filsys fs,
+			     dgrp_t group,
+			     blk_t *ret_super_blk,
+			     blk_t *ret_old_desc_blk,
+			     blk_t *ret_new_desc_blk,
+			     int *ret_meta_bg)
+{
+	blk_t	group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0;
+	unsigned int meta_bg, meta_bg_size;
+	int	numblocks, has_super;
+	int	old_desc_blocks;
+
+	group_block = fs->super->s_first_data_block +
+		(group * fs->super->s_blocks_per_group);
+
+	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+		old_desc_blocks = fs->super->s_first_meta_bg;
+	else
+		old_desc_blocks =
+			fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+	if (group == fs->group_desc_count-1) {
+		numblocks = (fs->super->s_blocks_count -
+			     fs->super->s_first_data_block) %
+			fs->super->s_blocks_per_group;
+		if (!numblocks)
+			numblocks = fs->super->s_blocks_per_group;
+	} else
+		numblocks = fs->super->s_blocks_per_group;
+
+	has_super = ext2fs_bg_has_super(fs, group);
+
+	if (has_super) {
+		super_blk = group_block;
+		numblocks--;
+	}
+	meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
+	meta_bg = group / meta_bg_size;
+
+	if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+	    (meta_bg < fs->super->s_first_meta_bg)) {
+		if (has_super) {
+			old_desc_blk = group_block + 1;
+			numblocks -= old_desc_blocks;
+		}
+	} else {
+		if (((group % meta_bg_size) == 0) ||
+		    ((group % meta_bg_size) == 1) ||
+		    ((group % meta_bg_size) == (meta_bg_size-1))) {
+			if (has_super)
+				has_super = 1;
+			new_desc_blk = group_block + has_super;
+			numblocks--;
+		}
+	}
+
+	numblocks -= 2 + fs->inode_blocks_per_group;
+
+	if (ret_super_blk)
+		*ret_super_blk = super_blk;
+	if (ret_old_desc_blk)
+		*ret_old_desc_blk = old_desc_blk;
+	if (ret_new_desc_blk)
+		*ret_new_desc_blk = new_desc_blk;
+	if (ret_meta_bg)
+		*ret_meta_bg = meta_bg;
+	return numblocks;
+}
+
+
+/*
+ * This function forces out the primary superblock.  We need to only
+ * write out those fields which we have changed, since if the
+ * filesystem is mounted, it may have changed some of the other
+ * fields.
+ *
+ * It takes as input a superblock which has already been byte swapped
+ * (if necessary).
+ *
+ */
+static errcode_t write_primary_superblock(ext2_filsys fs,
+					  struct ext2_super_block *super)
+{
+	__u16		*old_super, *new_super;
+	int		check_idx, write_idx, size;
+	errcode_t	retval;
+
+	if (!fs->io->manager->write_byte || !fs->orig_super) {
+		io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
+		retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE,
+					      super);
+		io_channel_set_blksize(fs->io, fs->blocksize);
+		return retval;
+	}
+
+	old_super = (__u16 *) fs->orig_super;
+	new_super = (__u16 *) super;
+
+	for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) {
+		if (old_super[check_idx] == new_super[check_idx])
+			continue;
+		write_idx = check_idx;
+		for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++)
+			if (old_super[check_idx] == new_super[check_idx])
+				break;
+		size = 2 * (check_idx - write_idx);
+		retval = io_channel_write_byte(fs->io,
+			       SUPERBLOCK_OFFSET + (2 * write_idx), size,
+					       new_super + write_idx);
+		if (retval)
+			return retval;
+	}
+	memcpy(fs->orig_super, super, SUPERBLOCK_SIZE);
+	return 0;
+}
+
+
+/*
+ * Updates the revision to EXT2_DYNAMIC_REV
+ */
+void ext2fs_update_dynamic_rev(ext2_filsys fs)
+{
+	struct ext2_super_block *sb = fs->super;
+
+	if (sb->s_rev_level > EXT2_GOOD_OLD_REV)
+		return;
+
+	sb->s_rev_level = EXT2_DYNAMIC_REV;
+	sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
+	sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
+	/* s_uuid is handled by e2fsck already */
+	/* other fields should be left alone */
+}
+
+static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
+				    blk_t group_block,
+				    struct ext2_super_block *super_shadow)
+{
+	dgrp_t	sgrp = group;
+
+	if (sgrp > ((1 << 16) - 1))
+		sgrp = (1 << 16) - 1;
+#if BB_BIG_ENDIAN
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES)
+		super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
+	else
+#endif
+		fs->super->s_block_group_nr = sgrp;
+
+	return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE,
+				    super_shadow);
+}
+
+
+errcode_t ext2fs_flush(ext2_filsys fs)
+{
+	dgrp_t		i;
+	blk_t		group_block;
+	errcode_t	retval;
+	unsigned long	fs_state;
+	struct ext2_super_block *super_shadow = NULL;
+	struct ext2_group_desc *group_shadow = NULL;
+	char	*group_ptr;
+	int	old_desc_blocks;
+#if BB_BIG_ENDIAN
+	dgrp_t		j;
+	struct ext2_group_desc *s, *t;
+#endif
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	fs_state = fs->super->s_state;
+
+	fs->super->s_wtime = time(NULL);
+	fs->super->s_block_group_nr = 0;
+#if BB_BIG_ENDIAN
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+		retval = EXT2_ET_NO_MEMORY;
+		retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
+		if (retval)
+			goto errout;
+		retval = ext2fs_get_mem((size_t)(fs->blocksize *
+						 fs->desc_blocks),
+					&group_shadow);
+		if (retval)
+			goto errout;
+		memset(group_shadow, 0, (size_t) fs->blocksize *
+		       fs->desc_blocks);
+
+		/* swap the group descriptors */
+		for (j=0, s=fs->group_desc, t=group_shadow;
+		     j < fs->group_desc_count; j++, t++, s++) {
+			*t = *s;
+			ext2fs_swap_group_desc(t);
+		}
+	} else {
+		super_shadow = fs->super;
+		group_shadow = fs->group_desc;
+	}
+#else
+	super_shadow = fs->super;
+	group_shadow = fs->group_desc;
+#endif
+
+	/*
+	 * If this is an external journal device, don't write out the
+	 * block group descriptors or any of the backup superblocks
+	 */
+	if (fs->super->s_feature_incompat &
+	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		goto write_primary_superblock_only;
+
+	/*
+	 * Set the state of the FS to be non-valid.  (The state has
+	 * already been backed up earlier, and will be restored after
+	 * we write out the backup superblocks.)
+	 */
+	fs->super->s_state &= ~EXT2_VALID_FS;
+#if BB_BIG_ENDIAN
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+		*super_shadow = *fs->super;
+		ext2fs_swap_super(super_shadow);
+	}
+#endif
+
+	/*
+	 * Write out the master group descriptors, and the backup
+	 * superblocks and group descriptors.
+	 */
+	group_block = fs->super->s_first_data_block;
+	group_ptr = (char *) group_shadow;
+	if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+		old_desc_blocks = fs->super->s_first_meta_bg;
+	else
+		old_desc_blocks = fs->desc_blocks;
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		blk_t	super_blk, old_desc_blk, new_desc_blk;
+		int	meta_bg;
+
+		ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk,
+					 &new_desc_blk, &meta_bg);
+
+		if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) {
+			retval = write_backup_super(fs, i, super_blk,
+						    super_shadow);
+			if (retval)
+				goto errout;
+		}
+		if (fs->flags & EXT2_FLAG_SUPER_ONLY)
+			continue;
+		if ((old_desc_blk) &&
+		    (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) {
+			retval = io_channel_write_blk(fs->io,
+			      old_desc_blk, old_desc_blocks, group_ptr);
+			if (retval)
+				goto errout;
+		}
+		if (new_desc_blk) {
+			retval = io_channel_write_blk(fs->io, new_desc_blk,
+				1, group_ptr + (meta_bg*fs->blocksize));
+			if (retval)
+				goto errout;
+		}
+	}
+	fs->super->s_block_group_nr = 0;
+	fs->super->s_state = fs_state;
+#if BB_BIG_ENDIAN
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+		*super_shadow = *fs->super;
+		ext2fs_swap_super(super_shadow);
+	}
+#endif
+
+	/*
+	 * If the write_bitmaps() function is present, call it to
+	 * flush the bitmaps.  This is done this way so that a simple
+	 * program that doesn't mess with the bitmaps doesn't need to
+	 * drag in the bitmaps.c code.
+	 */
+	if (fs->write_bitmaps) {
+		retval = fs->write_bitmaps(fs);
+		if (retval)
+			goto errout;
+	}
+
+write_primary_superblock_only:
+	/*
+	 * Write out master superblock.  This has to be done
+	 * separately, since it is located at a fixed location
+	 * (SUPERBLOCK_OFFSET).  We flush all other pending changes
+	 * out to disk first, just to avoid a race condition with an
+	 * insy-tinsy window....
+	 */
+	retval = io_channel_flush(fs->io);
+	retval = write_primary_superblock(fs, super_shadow);
+	if (retval)
+		goto errout;
+
+	fs->flags &= ~EXT2_FLAG_DIRTY;
+
+	retval = io_channel_flush(fs->io);
+errout:
+	fs->super->s_state = fs_state;
+	if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+		if (super_shadow)
+			ext2fs_free_mem(&super_shadow);
+		if (group_shadow)
+			ext2fs_free_mem(&group_shadow);
+	}
+	return retval;
+}
+
+errcode_t ext2fs_close(ext2_filsys fs)
+{
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (fs->flags & EXT2_FLAG_DIRTY) {
+		retval = ext2fs_flush(fs);
+		if (retval)
+			return retval;
+	}
+	if (fs->write_bitmaps) {
+		retval = fs->write_bitmaps(fs);
+		if (retval)
+			return retval;
+	}
+	ext2fs_free(fs);
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c
new file mode 100644
index 0000000..7f78ff8
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/cmp_bitmaps.c
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cmp_bitmaps.c --- routines to compare inode and block bitmaps.
+ *
+ * Copyright (C) 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
+				      ext2fs_block_bitmap bm2)
+{
+	blk_t	i;
+
+	EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_BLOCK_BITMAP);
+	EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+	if ((bm1->start != bm2->start) ||
+	    (bm1->end != bm2->end) ||
+	    (memcmp(bm1->bitmap, bm2->bitmap,
+		    (size_t) (bm1->end - bm1->start)/8)))
+		return EXT2_ET_NEQ_BLOCK_BITMAP;
+
+	for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
+		if (ext2fs_fast_test_block_bitmap(bm1, i) !=
+		    ext2fs_fast_test_block_bitmap(bm2, i))
+			return EXT2_ET_NEQ_BLOCK_BITMAP;
+
+	return 0;
+}
+
+errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
+				      ext2fs_inode_bitmap bm2)
+{
+	ext2_ino_t	i;
+
+	EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_INODE_BITMAP);
+	EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_INODE_BITMAP);
+
+	if ((bm1->start != bm2->start) ||
+	    (bm1->end != bm2->end) ||
+	    (memcmp(bm1->bitmap, bm2->bitmap,
+		    (size_t) (bm1->end - bm1->start)/8)))
+		return EXT2_ET_NEQ_INODE_BITMAP;
+
+	for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
+		if (ext2fs_fast_test_inode_bitmap(bm1, i) !=
+		    ext2fs_fast_test_inode_bitmap(bm2, i))
+			return EXT2_ET_NEQ_INODE_BITMAP;
+
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c
new file mode 100644
index 0000000..06ff6d8
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dblist.c
@@ -0,0 +1,260 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dblist.c -- directory block list functions
+ *
+ * Copyright 1997 by Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int dir_block_cmp(const void *a, const void *b);
+
+/*
+ * Returns the number of directories in the filesystem as reported by
+ * the group descriptors.  Of course, the group descriptors could be
+ * wrong!
+ */
+errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs)
+{
+	dgrp_t	i;
+	ext2_ino_t	num_dirs, max_dirs;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	num_dirs = 0;
+	max_dirs = fs->super->s_inodes_per_group;
+	for (i = 0; i < fs->group_desc_count; i++) {
+		if (fs->group_desc[i].bg_used_dirs_count > max_dirs)
+			num_dirs += max_dirs / 8;
+		else
+			num_dirs += fs->group_desc[i].bg_used_dirs_count;
+	}
+	if (num_dirs > fs->super->s_inodes_count)
+		num_dirs = fs->super->s_inodes_count;
+
+	*ret_num_dirs = num_dirs;
+
+	return 0;
+}
+
+/*
+ * helper function for making a new directory block list (for
+ * initialize and copy).
+ */
+static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count,
+			     struct ext2_db_entry *list,
+			     ext2_dblist *ret_dblist)
+{
+	ext2_dblist	dblist;
+	errcode_t	retval;
+	size_t		len;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if ((ret_dblist == 0) && fs->dblist &&
+	    (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST))
+		return 0;
+
+	retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist);
+	if (retval)
+		return retval;
+	memset(dblist, 0, sizeof(struct ext2_struct_dblist));
+
+	dblist->magic = EXT2_ET_MAGIC_DBLIST;
+	dblist->fs = fs;
+	if (size)
+		dblist->size = size;
+	else {
+		retval = ext2fs_get_num_dirs(fs, &dblist->size);
+		if (retval)
+			goto cleanup;
+		dblist->size = (dblist->size * 2) + 12;
+	}
+	len = (size_t) sizeof(struct ext2_db_entry) * dblist->size;
+	dblist->count = count;
+	retval = ext2fs_get_mem(len, &dblist->list);
+	if (retval)
+		goto cleanup;
+
+	if (list)
+		memcpy(dblist->list, list, len);
+	else
+		memset(dblist->list, 0, len);
+	if (ret_dblist)
+		*ret_dblist = dblist;
+	else
+		fs->dblist = dblist;
+	return 0;
+cleanup:
+	ext2fs_free_mem(&dblist);
+	return retval;
+}
+
+/*
+ * Initialize a directory block list
+ */
+errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist)
+{
+	ext2_dblist	dblist;
+	errcode_t	retval;
+
+	retval = make_dblist(fs, 0, 0, 0, &dblist);
+	if (retval)
+		return retval;
+
+	dblist->sorted = 1;
+	if (ret_dblist)
+		*ret_dblist = dblist;
+	else
+		fs->dblist = dblist;
+
+	return 0;
+}
+
+/*
+ * Copy a directory block list
+ */
+errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest)
+{
+	ext2_dblist	dblist;
+	errcode_t	retval;
+
+	retval = make_dblist(src->fs, src->size, src->count, src->list,
+			     &dblist);
+	if (retval)
+		return retval;
+	dblist->sorted = src->sorted;
+	*dest = dblist;
+	return 0;
+}
+
+/*
+ * Close a directory block list
+ *
+ * (moved to closefs.c)
+ */
+
+
+/*
+ * Add a directory block to the directory block list
+ */
+errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
+			       int blockcnt)
+{
+	struct ext2_db_entry	*new_entry;
+	errcode_t		retval;
+	unsigned long		old_size;
+
+	EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+	if (dblist->count >= dblist->size) {
+		old_size = dblist->size * sizeof(struct ext2_db_entry);
+		dblist->size += 100;
+		retval = ext2fs_resize_mem(old_size, (size_t) dblist->size *
+					   sizeof(struct ext2_db_entry),
+					   &dblist->list);
+		if (retval) {
+			dblist->size -= 100;
+			return retval;
+		}
+	}
+	new_entry = dblist->list + ( (int) dblist->count++);
+	new_entry->blk = blk;
+	new_entry->ino = ino;
+	new_entry->blockcnt = blockcnt;
+
+	dblist->sorted = 0;
+
+	return 0;
+}
+
+/*
+ * Change the directory block to the directory block list
+ */
+errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
+			       int blockcnt)
+{
+	dgrp_t			i;
+
+	EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+	for (i=0; i < dblist->count; i++) {
+		if ((dblist->list[i].ino != ino) ||
+		    (dblist->list[i].blockcnt != blockcnt))
+			continue;
+		dblist->list[i].blk = blk;
+		dblist->sorted = 0;
+		return 0;
+	}
+	return EXT2_ET_DB_NOT_FOUND;
+}
+
+void ext2fs_dblist_sort(ext2_dblist dblist,
+			int (*sortfunc)(const void *,
+						    const void *))
+{
+	if (!sortfunc)
+		sortfunc = dir_block_cmp;
+	qsort(dblist->list, (size_t) dblist->count,
+	      sizeof(struct ext2_db_entry), sortfunc);
+	dblist->sorted = 1;
+}
+
+/*
+ * This function iterates over the directory block list
+ */
+errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
+				int (*func)(ext2_filsys fs,
+					    struct ext2_db_entry *db_info,
+					    void	*priv_data),
+				void *priv_data)
+{
+	ext2_ino_t	i;
+	int		ret;
+
+	EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+	if (!dblist->sorted)
+		ext2fs_dblist_sort(dblist, 0);
+	for (i=0; i < dblist->count; i++) {
+		ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data);
+		if (ret & DBLIST_ABORT)
+			return 0;
+	}
+	return 0;
+}
+
+static int dir_block_cmp(const void *a, const void *b)
+{
+	const struct ext2_db_entry *db_a =
+		(const struct ext2_db_entry *) a;
+	const struct ext2_db_entry *db_b =
+		(const struct ext2_db_entry *) b;
+
+	if (db_a->blk != db_b->blk)
+		return (int) (db_a->blk - db_b->blk);
+
+	if (db_a->ino != db_b->ino)
+		return (int) (db_a->ino - db_b->ino);
+
+	return (int) (db_a->blockcnt - db_b->blockcnt);
+}
+
+int ext2fs_dblist_count(ext2_dblist dblist)
+{
+	return (int) dblist->count;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c
new file mode 100644
index 0000000..b239204
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dblist_dir.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dblist_dir.c --- iterate by directory entry
+ *
+ * Copyright 1997 by Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
+		       void *priv_data);
+
+errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist,
+				    int	flags,
+				    char	*block_buf,
+				    int (*func)(ext2_ino_t dir,
+						int	entry,
+						struct ext2_dir_entry *dirent,
+						int	offset,
+						int	blocksize,
+						char	*buf,
+						void	*priv_data),
+				    void *priv_data)
+{
+	errcode_t		retval;
+	struct dir_context	ctx;
+
+	EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+	ctx.dir = 0;
+	ctx.flags = flags;
+	if (block_buf)
+		ctx.buf = block_buf;
+	else {
+		retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf);
+		if (retval)
+			return retval;
+	}
+	ctx.func = func;
+	ctx.priv_data = priv_data;
+	ctx.errcode = 0;
+
+	retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx);
+
+	if (!block_buf)
+		ext2fs_free_mem(&ctx.buf);
+	if (retval)
+		return retval;
+	return ctx.errcode;
+}
+
+static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
+		       void *priv_data)
+{
+	struct dir_context	*ctx;
+
+	ctx = (struct dir_context *) priv_data;
+	ctx->dir = db_info->ino;
+
+	return ext2fs_process_dir_block(fs, &db_info->blk,
+					db_info->blockcnt, 0, 0, priv_data);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c
new file mode 100644
index 0000000..eb5dae0
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dir_iterate.c
@@ -0,0 +1,219 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dir_iterate.c --- ext2fs directory iteration operations
+ *
+ * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * This function checks to see whether or not a potential deleted
+ * directory entry looks valid.  What we do is check the deleted entry
+ * and each successive entry to make sure that they all look valid and
+ * that the last deleted entry ends at the beginning of the next
+ * undeleted entry.  Returns 1 if the deleted entry looks valid, zero
+ * if not valid.
+ */
+static int ext2fs_validate_entry(char *buf, int offset, int final_offset)
+{
+	struct ext2_dir_entry *dirent;
+
+	while (offset < final_offset) {
+		dirent = (struct ext2_dir_entry *)(buf + offset);
+		offset += dirent->rec_len;
+		if ((dirent->rec_len < 8) ||
+		    ((dirent->rec_len % 4) != 0) ||
+		    (((dirent->name_len & 0xFF)+8) > dirent->rec_len))
+			return 0;
+	}
+	return (offset == final_offset);
+}
+
+errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
+			      ext2_ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(ext2_ino_t	dir,
+					  int		entry,
+					  struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*priv_data),
+			      void *priv_data)
+{
+	struct		dir_context	ctx;
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_check_directory(fs, dir);
+	if (retval)
+		return retval;
+
+	ctx.dir = dir;
+	ctx.flags = flags;
+	if (block_buf)
+		ctx.buf = block_buf;
+	else {
+		retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
+		if (retval)
+			return retval;
+	}
+	ctx.func = func;
+	ctx.priv_data = priv_data;
+	ctx.errcode = 0;
+	retval = ext2fs_block_iterate2(fs, dir, 0, 0,
+				       ext2fs_process_dir_block, &ctx);
+	if (!block_buf)
+		ext2fs_free_mem(&ctx.buf);
+	if (retval)
+		return retval;
+	return ctx.errcode;
+}
+
+struct xlate {
+	int (*func)(struct ext2_dir_entry *dirent,
+		    int		offset,
+		    int		blocksize,
+		    char	*buf,
+		    void	*priv_data);
+	void *real_private;
+};
+
+static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)),
+		      int entry EXT2FS_ATTR((unused)),
+		      struct ext2_dir_entry *dirent, int offset,
+		      int blocksize, char *buf, void *priv_data)
+{
+	struct xlate *xl = (struct xlate *) priv_data;
+
+	return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private);
+}
+
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
+			      ext2_ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*priv_data),
+			      void *priv_data)
+{
+	struct xlate xl;
+
+	xl.real_private = priv_data;
+	xl.func = func;
+
+	return ext2fs_dir_iterate2(fs, dir, flags, block_buf,
+				   xlate_func, &xl);
+}
+
+
+/*
+ * Helper function which is private to this module.  Used by
+ * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate()
+ */
+int ext2fs_process_dir_block(ext2_filsys fs,
+			     blk_t	*blocknr,
+			     e2_blkcnt_t blockcnt,
+			     blk_t	ref_block EXT2FS_ATTR((unused)),
+			     int	ref_offset EXT2FS_ATTR((unused)),
+			     void	*priv_data)
+{
+	struct dir_context *ctx = (struct dir_context *) priv_data;
+	unsigned int	offset = 0;
+	unsigned int	next_real_entry = 0;
+	int		ret = 0;
+	int		changed = 0;
+	int		do_abort = 0;
+	int		entry, size;
+	struct ext2_dir_entry *dirent;
+
+	if (blockcnt < 0)
+		return 0;
+
+	entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
+
+	ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf);
+	if (ctx->errcode)
+		return BLOCK_ABORT;
+
+	while (offset < fs->blocksize) {
+		dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
+		if (((offset + dirent->rec_len) > fs->blocksize) ||
+		    (dirent->rec_len < 8) ||
+		    ((dirent->rec_len % 4) != 0) ||
+		    (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+			ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+			return BLOCK_ABORT;
+		}
+		if (!dirent->inode &&
+		    !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
+			goto next;
+
+		ret = (ctx->func)(ctx->dir,
+				  (next_real_entry > offset) ?
+				  DIRENT_DELETED_FILE : entry,
+				  dirent, offset,
+				  fs->blocksize, ctx->buf,
+				  ctx->priv_data);
+		if (entry < DIRENT_OTHER_FILE)
+			entry++;
+
+		if (ret & DIRENT_CHANGED)
+			changed++;
+		if (ret & DIRENT_ABORT) {
+			do_abort++;
+			break;
+		}
+next:
+		if (next_real_entry == offset)
+			next_real_entry += dirent->rec_len;
+
+		if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
+			size = ((dirent->name_len & 0xFF) + 11) & ~3;
+
+			if (dirent->rec_len != size)  {
+				unsigned int final_offset;
+
+				final_offset = offset + dirent->rec_len;
+				offset += size;
+				while (offset < final_offset &&
+				       !ext2fs_validate_entry(ctx->buf,
+							      offset,
+							      final_offset))
+					offset += 4;
+				continue;
+			}
+		}
+		offset += dirent->rec_len;
+	}
+
+	if (changed) {
+		ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf);
+		if (ctx->errcode)
+			return BLOCK_ABORT;
+	}
+	if (do_abort)
+		return BLOCK_ABORT;
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c
new file mode 100644
index 0000000..f9c5a10
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dirblock.c
@@ -0,0 +1,132 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dirblock.c --- directory block routines.
+ *
+ * Copyright (C) 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
+				 void *buf, int flags EXT2FS_ATTR((unused)))
+{
+	errcode_t	retval;
+	char		*p, *end;
+	struct ext2_dir_entry *dirent;
+	unsigned int	name_len, rec_len;
+#if BB_BIG_ENDIAN
+	unsigned int do_swap;
+#endif
+
+	retval = io_channel_read_blk(fs->io, block, 1, buf);
+	if (retval)
+		return retval;
+#if BB_BIG_ENDIAN
+	do_swap = (fs->flags & (EXT2_FLAG_SWAP_BYTES|
+				EXT2_FLAG_SWAP_BYTES_READ)) != 0;
+#endif
+	p = (char *) buf;
+	end = (char *) buf + fs->blocksize;
+	while (p < end-8) {
+		dirent = (struct ext2_dir_entry *) p;
+#if BB_BIG_ENDIAN
+		if (do_swap) {
+			dirent->inode = ext2fs_swab32(dirent->inode);
+			dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+			dirent->name_len = ext2fs_swab16(dirent->name_len);
+		}
+#endif
+		name_len = dirent->name_len;
+#ifdef WORDS_BIGENDIAN
+		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+			dirent->name_len = ext2fs_swab16(dirent->name_len);
+#endif
+		rec_len = dirent->rec_len;
+		if ((rec_len < 8) || (rec_len % 4)) {
+			rec_len = 8;
+			retval = EXT2_ET_DIR_CORRUPTED;
+		}
+		if (((name_len & 0xFF) + 8) > dirent->rec_len)
+			retval = EXT2_ET_DIR_CORRUPTED;
+		p += rec_len;
+	}
+	return retval;
+}
+
+errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
+				 void *buf)
+{
+	return ext2fs_read_dir_block2(fs, block, buf, 0);
+}
+
+
+errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
+				  void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+#if BB_BIG_ENDIAN
+	int		do_swap = 0;
+	errcode_t	retval;
+	char		*p, *end;
+	char		*buf = NULL;
+	struct ext2_dir_entry *dirent;
+
+	if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+	    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+		do_swap = 1;
+
+#ifndef WORDS_BIGENDIAN
+	if (!do_swap)
+		return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
+#endif
+
+	retval = ext2fs_get_mem(fs->blocksize, &buf);
+	if (retval)
+		return retval;
+	memcpy(buf, inbuf, fs->blocksize);
+	p = buf;
+	end = buf + fs->blocksize;
+	while (p < end) {
+		dirent = (struct ext2_dir_entry *) p;
+		if ((dirent->rec_len < 8) ||
+		    (dirent->rec_len % 4)) {
+			ext2fs_free_mem(&buf);
+			return EXT2_ET_DIR_CORRUPTED;
+		}
+		p += dirent->rec_len;
+		if (do_swap) {
+			dirent->inode = ext2fs_swab32(dirent->inode);
+			dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+			dirent->name_len = ext2fs_swab16(dirent->name_len);
+		}
+#ifdef WORDS_BIGENDIAN
+		if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+			dirent->name_len = ext2fs_swab16(dirent->name_len);
+#endif
+	}
+	retval = io_channel_write_blk(fs->io, block, 1, buf);
+	ext2fs_free_mem(&buf);
+	return retval;
+#else
+	return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
+#endif
+}
+
+
+errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
+				 void *inbuf)
+{
+	return ext2fs_write_dir_block2(fs, block, inbuf, 0);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c
new file mode 100644
index 0000000..09e34be
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dirhash.c
@@ -0,0 +1,234 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dirhash.c -- Calculate the hash of a directory entry
+ *
+ * Copyright (c) 2001  Daniel Phillips
+ *
+ * Copyright (c) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Keyed 32-bit hash function using TEA in a Davis-Meyer function
+ *   H0 = Key
+ *   Hi = E Mi(Hi-1) + Hi-1
+ *
+ * (see Applied Cryptography, 2nd edition, p448).
+ *
+ * Jeremy Fitzhardinge <jeremy@zip.com.au> 1998
+ *
+ * This code is made available under the terms of the GPL
+ */
+#define DELTA 0x9E3779B9
+
+static void TEA_transform(__u32 buf[4], __u32 const in[])
+{
+	__u32	sum = 0;
+	__u32	b0 = buf[0], b1 = buf[1];
+	__u32	a = in[0], b = in[1], c = in[2], d = in[3];
+	int	n = 16;
+
+	do {
+		sum += DELTA;
+		b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b);
+		b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d);
+	} while (--n);
+
+	buf[0] += b0;
+	buf[1] += b1;
+}
+
+/* F, G and H are basic MD4 functions: selection, majority, parity */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/*
+ * The generic round function.  The application is so specific that
+ * we don't bother protecting all the arguments with parens, as is generally
+ * good macro practice, in favor of extra legibility.
+ * Rotation is separate from addition to prevent recomputation
+ */
+#define ROUND(f, a, b, c, d, x, s)	\
+	(a += f(b, c, d) + x, a = (a << s) | (a >> (32-s)))
+#define K1 0
+#define K2 013240474631UL
+#define K3 015666365641UL
+
+/*
+ * Basic cut-down MD4 transform.  Returns only 32 bits of result.
+ */
+static void halfMD4Transform (__u32 buf[4], __u32 const in[])
+{
+	__u32	a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+	/* Round 1 */
+	ROUND(F, a, b, c, d, in[0] + K1,  3);
+	ROUND(F, d, a, b, c, in[1] + K1,  7);
+	ROUND(F, c, d, a, b, in[2] + K1, 11);
+	ROUND(F, b, c, d, a, in[3] + K1, 19);
+	ROUND(F, a, b, c, d, in[4] + K1,  3);
+	ROUND(F, d, a, b, c, in[5] + K1,  7);
+	ROUND(F, c, d, a, b, in[6] + K1, 11);
+	ROUND(F, b, c, d, a, in[7] + K1, 19);
+
+	/* Round 2 */
+	ROUND(G, a, b, c, d, in[1] + K2,  3);
+	ROUND(G, d, a, b, c, in[3] + K2,  5);
+	ROUND(G, c, d, a, b, in[5] + K2,  9);
+	ROUND(G, b, c, d, a, in[7] + K2, 13);
+	ROUND(G, a, b, c, d, in[0] + K2,  3);
+	ROUND(G, d, a, b, c, in[2] + K2,  5);
+	ROUND(G, c, d, a, b, in[4] + K2,  9);
+	ROUND(G, b, c, d, a, in[6] + K2, 13);
+
+	/* Round 3 */
+	ROUND(H, a, b, c, d, in[3] + K3,  3);
+	ROUND(H, d, a, b, c, in[7] + K3,  9);
+	ROUND(H, c, d, a, b, in[2] + K3, 11);
+	ROUND(H, b, c, d, a, in[6] + K3, 15);
+	ROUND(H, a, b, c, d, in[1] + K3,  3);
+	ROUND(H, d, a, b, c, in[5] + K3,  9);
+	ROUND(H, c, d, a, b, in[0] + K3, 11);
+	ROUND(H, b, c, d, a, in[4] + K3, 15);
+
+	buf[0] += a;
+	buf[1] += b;
+	buf[2] += c;
+	buf[3] += d;
+}
+
+#undef ROUND
+#undef F
+#undef G
+#undef H
+#undef K1
+#undef K2
+#undef K3
+
+/* The old legacy hash */
+static ext2_dirhash_t dx_hack_hash (const char *name, int len)
+{
+	__u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
+	while (len--) {
+		__u32 hash = hash1 + (hash0 ^ (*name++ * 7152373));
+
+		if (hash & 0x80000000) hash -= 0x7fffffff;
+		hash1 = hash0;
+		hash0 = hash;
+	}
+	return (hash0 << 1);
+}
+
+static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
+{
+	__u32	pad, val;
+	int	i;
+
+	pad = (__u32)len | ((__u32)len << 8);
+	pad |= pad << 16;
+
+	val = pad;
+	if (len > num*4)
+		len = num * 4;
+	for (i=0; i < len; i++) {
+		if ((i % 4) == 0)
+			val = pad;
+		val = msg[i] + (val << 8);
+		if ((i % 4) == 3) {
+			*buf++ = val;
+			val = pad;
+			num--;
+		}
+	}
+	if (--num >= 0)
+		*buf++ = val;
+	while (--num >= 0)
+		*buf++ = pad;
+}
+
+/*
+ * Returns the hash of a filename.  If len is 0 and name is NULL, then
+ * this function can be used to test whether or not a hash version is
+ * supported.
+ *
+ * The seed is an 4 longword (32 bits) "secret" which can be used to
+ * uniquify a hash.  If the seed is all zero's, then some default seed
+ * may be used.
+ *
+ * A particular hash version specifies whether or not the seed is
+ * represented, and whether or not the returned hash is 32 bits or 64
+ * bits.  32 bit hashes will return 0 for the minor hash.
+ */
+errcode_t ext2fs_dirhash(int version, const char *name, int len,
+			 const __u32 *seed,
+			 ext2_dirhash_t *ret_hash,
+			 ext2_dirhash_t *ret_minor_hash)
+{
+	__u32	hash;
+	__u32	minor_hash = 0;
+	const char	*p;
+	int		i;
+	__u32		in[8], buf[4];
+
+	/* Initialize the default seed for the hash checksum functions */
+	buf[0] = 0x67452301;
+	buf[1] = 0xefcdab89;
+	buf[2] = 0x98badcfe;
+	buf[3] = 0x10325476;
+
+	/* Check to see if the seed is all zero's */
+	if (seed) {
+		for (i=0; i < 4; i++) {
+			if (seed[i])
+				break;
+		}
+		if (i < 4)
+			memcpy(buf, seed, sizeof(buf));
+	}
+
+	switch (version) {
+	case EXT2_HASH_LEGACY:
+		hash = dx_hack_hash(name, len);
+		break;
+	case EXT2_HASH_HALF_MD4:
+		p = name;
+		while (len > 0) {
+			str2hashbuf(p, len, in, 8);
+			halfMD4Transform(buf, in);
+			len -= 32;
+			p += 32;
+		}
+		minor_hash = buf[2];
+		hash = buf[1];
+		break;
+	case EXT2_HASH_TEA:
+		p = name;
+		while (len > 0) {
+			str2hashbuf(p, len, in, 4);
+			TEA_transform(buf, in);
+			len -= 16;
+			p += 16;
+		}
+		hash = buf[0];
+		minor_hash = buf[1];
+		break;
+	default:
+		*ret_hash = 0;
+		return EXT2_ET_DIRHASH_UNSUPP;
+	}
+	*ret_hash = hash & ~1;
+	if (ret_minor_hash)
+		*ret_minor_hash = minor_hash;
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c
new file mode 100644
index 0000000..d187937
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/dupfs.c
@@ -0,0 +1,95 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * dupfs.c --- duplicate a ext2 filesystem handle
+ *
+ * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
+{
+	ext2_filsys	fs;
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+	if (retval)
+		return retval;
+
+	*fs = *src;
+	fs->device_name = 0;
+	fs->super = 0;
+	fs->orig_super = 0;
+	fs->group_desc = 0;
+	fs->inode_map = 0;
+	fs->block_map = 0;
+	fs->badblocks = 0;
+	fs->dblist = 0;
+
+	io_channel_bumpcount(fs->io);
+	if (fs->icache)
+		fs->icache->refcount++;
+
+	retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name);
+	if (retval)
+		goto errout;
+	strcpy(fs->device_name, src->device_name);
+
+	retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
+	if (retval)
+		goto errout;
+	memcpy(fs->super, src->super, SUPERBLOCK_SIZE);
+
+	retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
+	if (retval)
+		goto errout;
+	memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE);
+
+	retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+				&fs->group_desc);
+	if (retval)
+		goto errout;
+	memcpy(fs->group_desc, src->group_desc,
+	       (size_t) fs->desc_blocks * fs->blocksize);
+
+	if (src->inode_map) {
+		retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map);
+		if (retval)
+			goto errout;
+	}
+	if (src->block_map) {
+		retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map);
+		if (retval)
+			goto errout;
+	}
+	if (src->badblocks) {
+		retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks);
+		if (retval)
+			goto errout;
+	}
+	if (src->dblist) {
+		retval = ext2fs_copy_dblist(src->dblist, &fs->dblist);
+		if (retval)
+			goto errout;
+	}
+	*dest = fs;
+	return 0;
+errout:
+	ext2fs_free(fs);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h
new file mode 100644
index 0000000..a598d01
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/e2image.h
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * e2image.h --- header file describing the ext2 image format
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library.  So sue me.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+struct ext2_image_hdr {
+	__u32	magic_number;	/* This must be EXT2_ET_MAGIC_E2IMAGE */
+	char	magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */
+	char	fs_hostname[64];/* Hostname of machine of image */
+	char	fs_netaddr[32];	/* Network address */
+	__u32	fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */
+	__u32	fs_device;	/* Device number of image */
+	char	fs_device_name[64]; /* Device name */
+	char	fs_uuid[16];	/* UUID of filesystem */
+	__u32	fs_blocksize;	/* Block size of the filesystem */
+	__u32	fs_reserved[8];
+
+	__u32	image_device;	/* Device number of image file */
+	__u32	image_inode;	/* Inode number of image file */
+	__u32	image_time;	/* Time of image creation */
+	__u32	image_reserved[8];
+
+	__u32	offset_super;	/* Byte offset of the sb and descriptors */
+	__u32	offset_inode;	/* Byte offset of the inode table  */
+	__u32	offset_inodemap; /* Byte offset of the inode bitmaps */
+	__u32	offset_blockmap; /* Byte offset of the inode bitmaps */
+	__u32	offset_reserved[8];
+};
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c
new file mode 100644
index 0000000..8a29ae5
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/expanddir.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * expand.c --- expand an ext2fs directory
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999  Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct expand_dir_struct {
+	int		done;
+	int		newblocks;
+	errcode_t	err;
+};
+
+static int expand_dir_proc(ext2_filsys	fs,
+			   blk_t	*blocknr,
+			   e2_blkcnt_t	blockcnt,
+			   blk_t	ref_block EXT2FS_ATTR((unused)),
+			   int		ref_offset EXT2FS_ATTR((unused)),
+			   void		*priv_data)
+{
+	struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
+	blk_t	new_blk;
+	static blk_t	last_blk = 0;
+	char		*block;
+	errcode_t	retval;
+
+	if (*blocknr) {
+		last_blk = *blocknr;
+		return 0;
+	}
+	retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
+	if (retval) {
+		es->err = retval;
+		return BLOCK_ABORT;
+	}
+	if (blockcnt > 0) {
+		retval = ext2fs_new_dir_block(fs, 0, 0, &block);
+		if (retval) {
+			es->err = retval;
+			return BLOCK_ABORT;
+		}
+		es->done = 1;
+		retval = ext2fs_write_dir_block(fs, new_blk, block);
+	} else {
+		retval = ext2fs_get_mem(fs->blocksize, &block);
+		if (retval) {
+			es->err = retval;
+			return BLOCK_ABORT;
+		}
+		memset(block, 0, fs->blocksize);
+		retval = io_channel_write_blk(fs->io, new_blk, 1, block);
+	}
+	if (retval) {
+		es->err = retval;
+		return BLOCK_ABORT;
+	}
+	ext2fs_free_mem(&block);
+	*blocknr = new_blk;
+	ext2fs_block_alloc_stats(fs, new_blk, +1);
+	es->newblocks++;
+
+	if (es->done)
+		return (BLOCK_CHANGED | BLOCK_ABORT);
+	else
+		return BLOCK_CHANGED;
+}
+
+errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
+{
+	errcode_t	retval;
+	struct expand_dir_struct es;
+	struct ext2_inode	inode;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+
+	if (!fs->block_map)
+		return EXT2_ET_NO_BLOCK_BITMAP;
+
+	retval = ext2fs_check_directory(fs, dir);
+	if (retval)
+		return retval;
+
+	es.done = 0;
+	es.err = 0;
+	es.newblocks = 0;
+
+	retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
+				       0, expand_dir_proc, &es);
+
+	if (es.err)
+		return es.err;
+	if (!es.done)
+		return EXT2_ET_EXPAND_DIR_ERR;
+
+	/*
+	 * Update the size and block count fields in the inode.
+	 */
+	retval = ext2fs_read_inode(fs, dir, &inode);
+	if (retval)
+		return retval;
+
+	inode.i_size += fs->blocksize;
+	inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+
+	retval = ext2fs_write_inode(fs, dir, &inode);
+	if (retval)
+		return retval;
+
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h
new file mode 100644
index 0000000..ead3528
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_err.h
@@ -0,0 +1,116 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2_err.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#define EXT2_ET_BASE                             (2133571328L)
+#define EXT2_ET_MAGIC_EXT2FS_FILSYS              (2133571329L)
+#define EXT2_ET_MAGIC_BADBLOCKS_LIST             (2133571330L)
+#define EXT2_ET_MAGIC_BADBLOCKS_ITERATE          (2133571331L)
+#define EXT2_ET_MAGIC_INODE_SCAN                 (2133571332L)
+#define EXT2_ET_MAGIC_IO_CHANNEL                 (2133571333L)
+#define EXT2_ET_MAGIC_UNIX_IO_CHANNEL            (2133571334L)
+#define EXT2_ET_MAGIC_IO_MANAGER                 (2133571335L)
+#define EXT2_ET_MAGIC_BLOCK_BITMAP               (2133571336L)
+#define EXT2_ET_MAGIC_INODE_BITMAP               (2133571337L)
+#define EXT2_ET_MAGIC_GENERIC_BITMAP             (2133571338L)
+#define EXT2_ET_MAGIC_TEST_IO_CHANNEL            (2133571339L)
+#define EXT2_ET_MAGIC_DBLIST                     (2133571340L)
+#define EXT2_ET_MAGIC_ICOUNT                     (2133571341L)
+#define EXT2_ET_MAGIC_PQ_IO_CHANNEL              (2133571342L)
+#define EXT2_ET_MAGIC_EXT2_FILE                  (2133571343L)
+#define EXT2_ET_MAGIC_E2IMAGE                    (2133571344L)
+#define EXT2_ET_MAGIC_INODE_IO_CHANNEL           (2133571345L)
+#define EXT2_ET_MAGIC_RESERVED_9                 (2133571346L)
+#define EXT2_ET_BAD_MAGIC                        (2133571347L)
+#define EXT2_ET_REV_TOO_HIGH                     (2133571348L)
+#define EXT2_ET_RO_FILSYS                        (2133571349L)
+#define EXT2_ET_GDESC_READ                       (2133571350L)
+#define EXT2_ET_GDESC_WRITE                      (2133571351L)
+#define EXT2_ET_GDESC_BAD_BLOCK_MAP              (2133571352L)
+#define EXT2_ET_GDESC_BAD_INODE_MAP              (2133571353L)
+#define EXT2_ET_GDESC_BAD_INODE_TABLE            (2133571354L)
+#define EXT2_ET_INODE_BITMAP_WRITE               (2133571355L)
+#define EXT2_ET_INODE_BITMAP_READ                (2133571356L)
+#define EXT2_ET_BLOCK_BITMAP_WRITE               (2133571357L)
+#define EXT2_ET_BLOCK_BITMAP_READ                (2133571358L)
+#define EXT2_ET_INODE_TABLE_WRITE                (2133571359L)
+#define EXT2_ET_INODE_TABLE_READ                 (2133571360L)
+#define EXT2_ET_NEXT_INODE_READ                  (2133571361L)
+#define EXT2_ET_UNEXPECTED_BLOCK_SIZE            (2133571362L)
+#define EXT2_ET_DIR_CORRUPTED                    (2133571363L)
+#define EXT2_ET_SHORT_READ                       (2133571364L)
+#define EXT2_ET_SHORT_WRITE                      (2133571365L)
+#define EXT2_ET_DIR_NO_SPACE                     (2133571366L)
+#define EXT2_ET_NO_INODE_BITMAP                  (2133571367L)
+#define EXT2_ET_NO_BLOCK_BITMAP                  (2133571368L)
+#define EXT2_ET_BAD_INODE_NUM                    (2133571369L)
+#define EXT2_ET_BAD_BLOCK_NUM                    (2133571370L)
+#define EXT2_ET_EXPAND_DIR_ERR                   (2133571371L)
+#define EXT2_ET_TOOSMALL                         (2133571372L)
+#define EXT2_ET_BAD_BLOCK_MARK                   (2133571373L)
+#define EXT2_ET_BAD_BLOCK_UNMARK                 (2133571374L)
+#define EXT2_ET_BAD_BLOCK_TEST                   (2133571375L)
+#define EXT2_ET_BAD_INODE_MARK                   (2133571376L)
+#define EXT2_ET_BAD_INODE_UNMARK                 (2133571377L)
+#define EXT2_ET_BAD_INODE_TEST                   (2133571378L)
+#define EXT2_ET_FUDGE_BLOCK_BITMAP_END           (2133571379L)
+#define EXT2_ET_FUDGE_INODE_BITMAP_END           (2133571380L)
+#define EXT2_ET_BAD_IND_BLOCK                    (2133571381L)
+#define EXT2_ET_BAD_DIND_BLOCK                   (2133571382L)
+#define EXT2_ET_BAD_TIND_BLOCK                   (2133571383L)
+#define EXT2_ET_NEQ_BLOCK_BITMAP                 (2133571384L)
+#define EXT2_ET_NEQ_INODE_BITMAP                 (2133571385L)
+#define EXT2_ET_BAD_DEVICE_NAME                  (2133571386L)
+#define EXT2_ET_MISSING_INODE_TABLE              (2133571387L)
+#define EXT2_ET_CORRUPT_SUPERBLOCK               (2133571388L)
+#define EXT2_ET_BAD_GENERIC_MARK                 (2133571389L)
+#define EXT2_ET_BAD_GENERIC_UNMARK               (2133571390L)
+#define EXT2_ET_BAD_GENERIC_TEST                 (2133571391L)
+#define EXT2_ET_SYMLINK_LOOP                     (2133571392L)
+#define EXT2_ET_CALLBACK_NOTHANDLED              (2133571393L)
+#define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE         (2133571394L)
+#define EXT2_ET_UNSUPP_FEATURE                   (2133571395L)
+#define EXT2_ET_RO_UNSUPP_FEATURE                (2133571396L)
+#define EXT2_ET_LLSEEK_FAILED                    (2133571397L)
+#define EXT2_ET_NO_MEMORY                        (2133571398L)
+#define EXT2_ET_INVALID_ARGUMENT                 (2133571399L)
+#define EXT2_ET_BLOCK_ALLOC_FAIL                 (2133571400L)
+#define EXT2_ET_INODE_ALLOC_FAIL                 (2133571401L)
+#define EXT2_ET_NO_DIRECTORY                     (2133571402L)
+#define EXT2_ET_TOO_MANY_REFS                    (2133571403L)
+#define EXT2_ET_FILE_NOT_FOUND                   (2133571404L)
+#define EXT2_ET_FILE_RO                          (2133571405L)
+#define EXT2_ET_DB_NOT_FOUND                     (2133571406L)
+#define EXT2_ET_DIR_EXISTS                       (2133571407L)
+#define EXT2_ET_UNIMPLEMENTED                    (2133571408L)
+#define EXT2_ET_CANCEL_REQUESTED                 (2133571409L)
+#define EXT2_ET_FILE_TOO_BIG                     (2133571410L)
+#define EXT2_ET_JOURNAL_NOT_BLOCK                (2133571411L)
+#define EXT2_ET_NO_JOURNAL_SB                    (2133571412L)
+#define EXT2_ET_JOURNAL_TOO_SMALL                (2133571413L)
+#define EXT2_ET_JOURNAL_UNSUPP_VERSION           (2133571414L)
+#define EXT2_ET_LOAD_EXT_JOURNAL                 (2133571415L)
+#define EXT2_ET_NO_JOURNAL                       (2133571416L)
+#define EXT2_ET_DIRHASH_UNSUPP                   (2133571417L)
+#define EXT2_ET_BAD_EA_BLOCK_NUM                 (2133571418L)
+#define EXT2_ET_TOO_MANY_INODES                  (2133571419L)
+#define EXT2_ET_NOT_IMAGE_FILE                   (2133571420L)
+#define EXT2_ET_RES_GDT_BLOCKS                   (2133571421L)
+#define EXT2_ET_RESIZE_INODE_CORRUPT             (2133571422L)
+#define EXT2_ET_SET_BMAP_NO_IND                  (2133571423L)
+
+#if 0
+extern const struct error_table et_ext2_error_table;
+extern void initialize_ext2_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_ext2_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_ext2 (2133571328L)
+
+/* for compatibility with older versions... */
+#define init_ext2_err_tbl initialize_ext2_error_table
+#define ext2_err_base ERROR_TABLE_BASE_ext2
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h
new file mode 100644
index 0000000..ca309c0
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_ext_attr.h
@@ -0,0 +1,52 @@
+/* vi: set sw=4 ts=4: */
+/*
+  File: linux/ext2_ext_attr.h
+
+  On-disk format of extended attributes for the ext2 filesystem.
+
+  (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+*/
+
+/* Magic value in attribute blocks */
+#define EXT2_EXT_ATTR_MAGIC_v1		0xEA010000
+#define EXT2_EXT_ATTR_MAGIC		0xEA020000
+
+/* Maximum number of references to one attribute block */
+#define EXT2_EXT_ATTR_REFCOUNT_MAX	1024
+
+struct ext2_ext_attr_header {
+	__u32	h_magic;	/* magic number for identification */
+	__u32	h_refcount;	/* reference count */
+	__u32	h_blocks;	/* number of disk blocks used */
+	__u32	h_hash;		/* hash value of all attributes */
+	__u32	h_reserved[4];	/* zero right now */
+};
+
+struct ext2_ext_attr_entry {
+	__u8	e_name_len;	/* length of name */
+	__u8	e_name_index;	/* attribute name index */
+	__u16	e_value_offs;	/* offset in disk block of value */
+	__u32	e_value_block;	/* disk block attribute is stored on (n/i) */
+	__u32	e_value_size;	/* size of attribute value */
+	__u32	e_hash;		/* hash value of name and value */
+};
+
+#define EXT2_EXT_ATTR_PAD_BITS		2
+#define EXT2_EXT_ATTR_PAD		(1<<EXT2_EXT_ATTR_PAD_BITS)
+#define EXT2_EXT_ATTR_ROUND		(EXT2_EXT_ATTR_PAD-1)
+#define EXT2_EXT_ATTR_LEN(name_len) \
+	(((name_len) + EXT2_EXT_ATTR_ROUND + \
+	sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_ATTR_NEXT(entry) \
+	( (struct ext2_ext_attr_entry *)( \
+	  (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->e_name_len)) )
+#define EXT2_EXT_ATTR_SIZE(size) \
+	(((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL)
+#define EXT2_EXT_ATTR_NAME(entry) \
+	(((char *) (entry)) + sizeof(struct ext2_ext_attr_entry))
+#define EXT2_XATTR_LEN(name_len) \
+	(((name_len) + EXT2_EXT_ATTR_ROUND + \
+	sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_XATTR_SIZE(size) \
+	(((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h
new file mode 100644
index 0000000..6f4f708
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_fs.h
@@ -0,0 +1,569 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+#ifndef LINUX_EXT2_FS_H
+#define LINUX_EXT2_FS_H 1
+
+#include "ext2_types.h"		/* Changed from linux/types.h */
+
+/*
+ * Special inode numbers
+ */
+#define EXT2_BAD_INO		 1	/* Bad blocks inode */
+#define EXT2_ROOT_INO		 2	/* Root inode */
+#define EXT2_ACL_IDX_INO	 3	/* ACL inode */
+#define EXT2_ACL_DATA_INO	 4	/* ACL inode */
+#define EXT2_BOOT_LOADER_INO	 5	/* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO	 6	/* Undelete directory inode */
+#define EXT2_RESIZE_INO		 7	/* Reserved group descriptors inode */
+#define EXT2_JOURNAL_INO	 8	/* Journal inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO	11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC	0xEF53
+
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block.  This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb)	(sb)
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX		32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_LOG_SIZE		10	/* 1024 */
+#define EXT2_MAX_BLOCK_LOG_SIZE		16	/* 65536 */
+#define EXT2_MIN_BLOCK_SIZE	(1 << EXT2_MIN_BLOCK_LOG_SIZE)
+#define EXT2_MAX_BLOCK_SIZE	(1 << EXT2_MAX_BLOCK_LOG_SIZE)
+#define EXT2_BLOCK_SIZE(s)	(EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#define EXT2_BLOCK_SIZE_BITS(s)	((s)->s_log_block_size + 10)
+#define EXT2_INODE_SIZE(s)	(((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+				 EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
+#define EXT2_FIRST_INO(s)	(((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+				 EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
+#define EXT2_ADDR_PER_BLOCK(s)	(EXT2_BLOCK_SIZE(s) / sizeof(__u32))
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE		EXT2_MIN_BLOCK_SIZE
+#define EXT2_MAX_FRAG_SIZE		EXT2_MAX_BLOCK_SIZE
+#define EXT2_MIN_FRAG_LOG_SIZE		EXT2_MIN_BLOCK_LOG_SIZE
+# define EXT2_FRAG_SIZE(s)		(EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s)	(EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header	/* Header of Access Control Lists */
+{
+	__u32	aclh_size;
+	__u32	aclh_file_count;
+	__u32	aclh_acle_count;
+	__u32	aclh_first_acle;
+};
+
+struct ext2_acl_entry	/* Access Control List Entry */
+{
+	__u32	acle_size;
+	__u16	acle_perms;	/* Access permissions */
+	__u16	acle_type;	/* Type of entry */
+	__u16	acle_tag;	/* User or group identity */
+	__u16	acle_pad1;
+	__u32	acle_next;	/* Pointer on next entry for the */
+					/* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc
+{
+	__u32	bg_block_bitmap;		/* Blocks bitmap block */
+	__u32	bg_inode_bitmap;		/* Inodes bitmap block */
+	__u32	bg_inode_table;		/* Inodes table block */
+	__u16	bg_free_blocks_count;	/* Free blocks count */
+	__u16	bg_free_inodes_count;	/* Free inodes count */
+	__u16	bg_used_dirs_count;	/* Directories count */
+	__u16	bg_pad;
+	__u32	bg_reserved[3];
+};
+
+/*
+ * Data structures used by the directory indexing feature
+ *
+ * Note: all of the multibyte integer fields are little endian.
+ */
+
+/*
+ * Note: dx_root_info is laid out so that if it should somehow get
+ * overlaid by a dirent the two low bits of the hash version will be
+ * zero.  Therefore, the hash version mod 4 should never be 0.
+ * Sincerely, the paranoia department.
+ */
+struct ext2_dx_root_info {
+	__u32 reserved_zero;
+	__u8 hash_version; /* 0 now, 1 at release */
+	__u8 info_length; /* 8 */
+	__u8 indirect_levels;
+	__u8 unused_flags;
+};
+
+#define EXT2_HASH_LEGACY	0
+#define EXT2_HASH_HALF_MD4	1
+#define EXT2_HASH_TEA		2
+
+#define EXT2_HASH_FLAG_INCOMPAT	0x1
+
+struct ext2_dx_entry {
+	__u32 hash;
+	__u32 block;
+};
+
+struct ext2_dx_countlimit {
+	__u16 limit;
+	__u16 count;
+};
+
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#define EXT2_BLOCKS_PER_GROUP(s)	(EXT2_SB(s)->s_blocks_per_group)
+#define EXT2_INODES_PER_GROUP(s)	(EXT2_SB(s)->s_inodes_per_group)
+#define EXT2_INODES_PER_BLOCK(s)	(EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
+/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
+#define EXT2_MAX_BLOCKS_PER_GROUP(s)	((1 << 16) - 8)
+#define EXT2_MAX_INODES_PER_GROUP(s)	((1 << 16) - EXT2_INODES_PER_BLOCK(s))
+#define EXT2_DESC_PER_BLOCK(s)		(EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS		12
+#define EXT2_IND_BLOCK			EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK			(EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK			(EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS			(EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL			0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL			0x00000002 /* Undelete */
+#define EXT2_COMPR_FL			0x00000004 /* Compress file */
+#define EXT2_SYNC_FL			0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL		0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL			0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL			0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL			0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL			0x00000100
+#define EXT2_COMPRBLK_FL		0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMPR_FL			0x00000400 /* Access raw compressed data */
+#define EXT2_ECOMPR_FL			0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */
+#define EXT2_BTREE_FL			0x00001000 /* btree format dir */
+#define EXT2_INDEX_FL			0x00001000 /* hash-indexed directory */
+#define EXT2_IMAGIC_FL			0x00002000
+#define EXT3_JOURNAL_DATA_FL		0x00004000 /* file data should be journaled */
+#define EXT2_NOTAIL_FL			0x00008000 /* file tail should not be merged */
+#define EXT2_DIRSYNC_FL			0x00010000 /* Synchronous directory modifications */
+#define EXT2_TOPDIR_FL			0x00020000 /* Top of directory hierarchies*/
+#define EXT3_EXTENTS_FL			0x00080000 /* Inode uses extents */
+#define EXT2_RESERVED_FL		0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE		0x0003DFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE		0x000080FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define EXT2_IOC_GETFLAGS		_IOR('f', 1, long)
+#define EXT2_IOC_SETFLAGS		_IOW('f', 2, long)
+#define EXT2_IOC_GETVERSION		_IOR('v', 1, long)
+#define EXT2_IOC_SETVERSION		_IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+	__u16	i_mode;		/* File mode */
+	__u16	i_uid;		/* Low 16 bits of Owner Uid */
+	__u32	i_size;		/* Size in bytes */
+	__u32	i_atime;	/* Access time */
+	__u32	i_ctime;	/* Creation time */
+	__u32	i_mtime;	/* Modification time */
+	__u32	i_dtime;	/* Deletion Time */
+	__u16	i_gid;		/* Low 16 bits of Group Id */
+	__u16	i_links_count;	/* Links count */
+	__u32	i_blocks;	/* Blocks count */
+	__u32	i_flags;	/* File flags */
+	union {
+		struct {
+			__u32  l_i_reserved1;
+		} linux1;
+		struct {
+			__u32  h_i_translator;
+		} hurd1;
+		struct {
+			__u32  m_i_reserved1;
+		} masix1;
+	} osd1;				/* OS dependent 1 */
+	__u32	i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+	__u32	i_generation;	/* File version (for NFS) */
+	__u32	i_file_acl;	/* File ACL */
+	__u32	i_dir_acl;	/* Directory ACL */
+	__u32	i_faddr;	/* Fragment address */
+	union {
+		struct {
+			__u8	l_i_frag;	/* Fragment number */
+			__u8	l_i_fsize;	/* Fragment size */
+			__u16	i_pad1;
+			__u16	l_i_uid_high;	/* these 2 fields    */
+			__u16	l_i_gid_high;	/* were reserved2[0] */
+			__u32	l_i_reserved2;
+		} linux2;
+		struct {
+			__u8	h_i_frag;	/* Fragment number */
+			__u8	h_i_fsize;	/* Fragment size */
+			__u16	h_i_mode_high;
+			__u16	h_i_uid_high;
+			__u16	h_i_gid_high;
+			__u32	h_i_author;
+		} hurd2;
+		struct {
+			__u8	m_i_frag;	/* Fragment number */
+			__u8	m_i_fsize;	/* Fragment size */
+			__u16	m_pad1;
+			__u32	m_i_reserved2[2];
+		} masix2;
+	} osd2;				/* OS dependent 2 */
+};
+
+/*
+ * Permanent part of an large inode on the disk
+ */
+struct ext2_inode_large {
+	__u16	i_mode;		/* File mode */
+	__u16	i_uid;		/* Low 16 bits of Owner Uid */
+	__u32	i_size;		/* Size in bytes */
+	__u32	i_atime;	/* Access time */
+	__u32	i_ctime;	/* Creation time */
+	__u32	i_mtime;	/* Modification time */
+	__u32	i_dtime;	/* Deletion Time */
+	__u16	i_gid;		/* Low 16 bits of Group Id */
+	__u16	i_links_count;	/* Links count */
+	__u32	i_blocks;	/* Blocks count */
+	__u32	i_flags;	/* File flags */
+	union {
+		struct {
+			__u32  l_i_reserved1;
+		} linux1;
+		struct {
+			__u32  h_i_translator;
+		} hurd1;
+		struct {
+			__u32  m_i_reserved1;
+		} masix1;
+	} osd1;				/* OS dependent 1 */
+	__u32	i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+	__u32	i_generation;	/* File version (for NFS) */
+	__u32	i_file_acl;	/* File ACL */
+	__u32	i_dir_acl;	/* Directory ACL */
+	__u32	i_faddr;	/* Fragment address */
+	union {
+		struct {
+			__u8	l_i_frag;	/* Fragment number */
+			__u8	l_i_fsize;	/* Fragment size */
+			__u16	i_pad1;
+			__u16	l_i_uid_high;	/* these 2 fields    */
+			__u16	l_i_gid_high;	/* were reserved2[0] */
+			__u32	l_i_reserved2;
+		} linux2;
+		struct {
+			__u8	h_i_frag;	/* Fragment number */
+			__u8	h_i_fsize;	/* Fragment size */
+			__u16	h_i_mode_high;
+			__u16	h_i_uid_high;
+			__u16	h_i_gid_high;
+			__u32	h_i_author;
+		} hurd2;
+		struct {
+			__u8	m_i_frag;	/* Fragment number */
+			__u8	m_i_fsize;	/* Fragment size */
+			__u16	m_pad1;
+			__u32	m_i_reserved2[2];
+		} masix2;
+	} osd2;				/* OS dependent 2 */
+	__u16	i_extra_isize;
+	__u16	i_pad1;
+};
+
+#define i_size_high	i_dir_acl
+
+/*
+ * File system states
+ */
+#define EXT2_VALID_FS			0x0001	/* Unmounted cleanly */
+#define EXT2_ERROR_FS			0x0002	/* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK		0x0001	/* Do mount-time checks */
+#define EXT2_MOUNT_GRPID		0x0004	/* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG		0x0008	/* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT		0x0010	/* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO		0x0020	/* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC		0x0040	/* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF		0x0080	/* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_UID32		0x0200  /* Disable 32-bit UIDs */
+
+#define clear_opt(o, opt)		o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt)			o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt)		(EXT2_SB(sb)->s_mount_opt & \
+					 EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT		20	/* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL		0	/* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE		1	/* Continue execution */
+#define EXT2_ERRORS_RO			2	/* Remount fs read-only */
+#define EXT2_ERRORS_PANIC		3	/* Panic */
+#define EXT2_ERRORS_DEFAULT		EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+	__u32	s_inodes_count;		/* Inodes count */
+	__u32	s_blocks_count;		/* Blocks count */
+	__u32	s_r_blocks_count;	/* Reserved blocks count */
+	__u32	s_free_blocks_count;	/* Free blocks count */
+	__u32	s_free_inodes_count;	/* Free inodes count */
+	__u32	s_first_data_block;	/* First Data Block */
+	__u32	s_log_block_size;	/* Block size */
+	__s32	s_log_frag_size;	/* Fragment size */
+	__u32	s_blocks_per_group;	/* # Blocks per group */
+	__u32	s_frags_per_group;	/* # Fragments per group */
+	__u32	s_inodes_per_group;	/* # Inodes per group */
+	__u32	s_mtime;		/* Mount time */
+	__u32	s_wtime;		/* Write time */
+	__u16	s_mnt_count;		/* Mount count */
+	__s16	s_max_mnt_count;	/* Maximal mount count */
+	__u16	s_magic;		/* Magic signature */
+	__u16	s_state;		/* File system state */
+	__u16	s_errors;		/* Behaviour when detecting errors */
+	__u16	s_minor_rev_level;	/* minor revision level */
+	__u32	s_lastcheck;		/* time of last check */
+	__u32	s_checkinterval;	/* max. time between checks */
+	__u32	s_creator_os;		/* OS */
+	__u32	s_rev_level;		/* Revision level */
+	__u16	s_def_resuid;		/* Default uid for reserved blocks */
+	__u16	s_def_resgid;		/* Default gid for reserved blocks */
+	/*
+	 * These fields are for EXT2_DYNAMIC_REV superblocks only.
+	 *
+	 * Note: the difference between the compatible feature set and
+	 * the incompatible feature set is that if there is a bit set
+	 * in the incompatible feature set that the kernel doesn't
+	 * know about, it should refuse to mount the filesystem.
+	 *
+	 * e2fsck's requirements are more strict; if it doesn't know
+	 * about a feature in either the compatible or incompatible
+	 * feature set, it must abort and not try to meddle with
+	 * things it doesn't understand...
+	 */
+	__u32	s_first_ino;		/* First non-reserved inode */
+	__u16   s_inode_size;		/* size of inode structure */
+	__u16	s_block_group_nr;	/* block group # of this superblock */
+	__u32	s_feature_compat;	/* compatible feature set */
+	__u32	s_feature_incompat;	/* incompatible feature set */
+	__u32	s_feature_ro_compat;	/* readonly-compatible feature set */
+	__u8	s_uuid[16];		/* 128-bit uuid for volume */
+	char	s_volume_name[16];	/* volume name */
+	char	s_last_mounted[64];	/* directory where last mounted */
+	__u32	s_algorithm_usage_bitmap; /* For compression */
+	/*
+	 * Performance hints.  Directory preallocation should only
+	 * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
+	 */
+	__u8	s_prealloc_blocks;	/* Nr of blocks to try to preallocate*/
+	__u8	s_prealloc_dir_blocks;	/* Nr to preallocate for dirs */
+	__u16	s_reserved_gdt_blocks;	/* Per group table for online growth */
+	/*
+	 * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
+	 */
+	__u8	s_journal_uuid[16];	/* uuid of journal superblock */
+	__u32	s_journal_inum;		/* inode number of journal file */
+	__u32	s_journal_dev;		/* device number of journal file */
+	__u32	s_last_orphan;		/* start of list of inodes to delete */
+	__u32	s_hash_seed[4];		/* HTREE hash seed */
+	__u8	s_def_hash_version;	/* Default hash version to use */
+	__u8	s_jnl_backup_type;	/* Default type of journal backup */
+	__u16	s_reserved_word_pad;
+	__u32	s_default_mount_opts;
+	__u32	s_first_meta_bg;	/* First metablock group */
+	__u32	s_mkfs_time;		/* When the filesystem was created */
+	__u32	s_jnl_blocks[17];	/* Backup of the journal inode */
+	__u32	s_reserved[172];	/* Padding to the end of the block */
+};
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX		0
+#define EXT2_OS_HURD		1
+#define EXT2_OS_MASIX		2
+#define EXT2_OS_FREEBSD		3
+#define EXT2_OS_LITES		4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV	0	/* The good old (original) format */
+#define EXT2_DYNAMIC_REV	1	/* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV	EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV	EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Journal inode backup types
+ */
+#define EXT3_JNL_BACKUP_BLOCKS	1
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask)			\
+	( EXT2_SB(sb)->s_feature_compat & (mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask)			\
+	( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask)			\
+	( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+#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_DIR_INDEX		0x0020
+
+#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_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 EXT2_FEATURE_INCOMPAT_META_BG		0x0010
+#define EXT3_FEATURE_INCOMPAT_EXTENTS		0x0040
+
+
+#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)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define EXT2_DEF_RESUID		0
+#define EXT2_DEF_RESGID		0
+
+/*
+ * Default mount options
+ */
+#define EXT2_DEFM_DEBUG		0x0001
+#define EXT2_DEFM_BSDGROUPS	0x0002
+#define EXT2_DEFM_XATTR_USER	0x0004
+#define EXT2_DEFM_ACL		0x0008
+#define EXT2_DEFM_UID16		0x0010
+#define EXT3_DEFM_JMODE		0x0060
+#define EXT3_DEFM_JMODE_DATA	0x0020
+#define EXT3_DEFM_JMODE_ORDERED	0x0040
+#define EXT3_DEFM_JMODE_WBACK	0x0060
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+	__u32	inode;			/* Inode number */
+	__u16	rec_len;		/* Directory entry length */
+	__u16	name_len;		/* Name length */
+	char	name[EXT2_NAME_LEN];	/* File name */
+};
+
+/*
+ * The new version of the directory entry.  Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+	__u32	inode;			/* Inode number */
+	__u16	rec_len;		/* Directory entry length */
+	__u8	name_len;		/* Name length */
+	__u8	file_type;
+	char	name[EXT2_NAME_LEN];	/* File name */
+};
+
+/*
+ * Ext2 directory file types.  Only the low 3 bits are used.  The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN		0
+#define EXT2_FT_REG_FILE	1
+#define EXT2_FT_DIR		2
+#define EXT2_FT_CHRDEV		3
+#define EXT2_FT_BLKDEV		4
+#define EXT2_FT_FIFO		5
+#define EXT2_FT_SOCK		6
+#define EXT2_FT_SYMLINK		7
+
+#define EXT2_FT_MAX		8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD			4
+#define EXT2_DIR_ROUND			(EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len)	(((name_len) + 8 + EXT2_DIR_ROUND) & \
+					 ~EXT2_DIR_ROUND)
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h
new file mode 100644
index 0000000..1900a76
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_io.h
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * io.h --- the I/O manager abstraction
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+#ifndef EXT2FS_EXT2_IO_H
+#define EXT2FS_EXT2_IO_H 1
+
+/*
+ * ext2_loff_t is defined here since unix_io.c needs it.
+ */
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long	ext2_loff_t;
+#else
+typedef long		ext2_loff_t;
+#endif
+
+/* llseek.c */
+/* ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); */
+#ifdef CONFIG_LFS
+# define ext2fs_llseek lseek64
+#else
+# define ext2fs_llseek lseek
+#endif
+
+typedef struct struct_io_manager *io_manager;
+typedef struct struct_io_channel *io_channel;
+
+#define CHANNEL_FLAGS_WRITETHROUGH	0x01
+
+struct struct_io_channel {
+	errcode_t	magic;
+	io_manager	manager;
+	char		*name;
+	int		block_size;
+	errcode_t	(*read_error)(io_channel channel,
+				      unsigned long block,
+				      int count,
+				      void *data,
+				      size_t size,
+				      int actual_bytes_read,
+				      errcode_t error);
+	errcode_t       (*write_error)(io_channel channel,
+				       unsigned long block,
+				       int count,
+				       const void *data,
+				       size_t size,
+				       int actual_bytes_written,
+				       errcode_t error);
+	int		refcount;
+	int		flags;
+	int		reserved[14];
+	void		*private_data;
+	void		*app_data;
+};
+
+struct struct_io_manager {
+	errcode_t magic;
+	const char *name;
+	errcode_t (*open)(const char *name, int flags, io_channel *channel);
+	errcode_t (*close)(io_channel channel);
+	errcode_t (*set_blksize)(io_channel channel, int blksize);
+	errcode_t (*read_blk)(io_channel channel, unsigned long block,
+			      int count, void *data);
+	errcode_t (*write_blk)(io_channel channel, unsigned long block,
+			       int count, const void *data);
+	errcode_t (*flush)(io_channel channel);
+	errcode_t (*write_byte)(io_channel channel, unsigned long offset,
+				int count, const void *data);
+	errcode_t (*set_option)(io_channel channel, const char *option,
+				const char *arg);
+	int             reserved[14];
+};
+
+#define IO_FLAG_RW	1
+
+/*
+ * Convenience functions....
+ */
+#define io_channel_close(c)		((c)->manager->close((c)))
+#define io_channel_set_blksize(c,s)	((c)->manager->set_blksize((c),s))
+#define io_channel_read_blk(c,b,n,d)	((c)->manager->read_blk((c),b,n,d))
+#define io_channel_write_blk(c,b,n,d)	((c)->manager->write_blk((c),b,n,d))
+#define io_channel_flush(c)		((c)->manager->flush((c)))
+#define io_channel_bumpcount(c)		((c)->refcount++)
+
+/* io_manager.c */
+extern errcode_t io_channel_set_options(io_channel channel,
+					const char *options);
+extern errcode_t io_channel_write_byte(io_channel channel,
+				       unsigned long offset,
+				       int count, const void *data);
+
+/* unix_io.c */
+extern io_manager unix_io_manager;
+
+/* test_io.c */
+extern io_manager test_io_manager, test_io_backing_manager;
+extern void (*test_io_cb_read_blk)
+	(unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_write_blk)
+	(unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_set_blksize)
+	(int blksize, errcode_t err);
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h
new file mode 100644
index 0000000..2c1196b
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2_types.h
@@ -0,0 +1,2 @@
+/* vi: set sw=4 ts=4: */
+#include <linux/types.h>
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h
new file mode 100644
index 0000000..9f77201
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs.h
@@ -0,0 +1,922 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2fs.h --- ext2fs
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+#ifndef EXT2FS_EXT2FS_H
+#define EXT2FS_EXT2FS_H 1
+
+
+#define EXT2FS_ATTR(x)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Where the master copy of the superblock is located, and how big
+ * superblocks are supposed to be.  We define SUPERBLOCK_SIZE because
+ * the size of the superblock structure is not necessarily trustworthy
+ * (some versions have the padding set up so that the superblock is
+ * 1032 bytes long).
+ */
+#define SUPERBLOCK_OFFSET	1024
+#define SUPERBLOCK_SIZE		1024
+
+/*
+ * The last ext2fs revision level that this version of the library is
+ * able to support.
+ */
+#define EXT2_LIB_CURRENT_REV	EXT2_DYNAMIC_REV
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ext2_types.h"
+#include "ext2_fs.h"
+
+typedef __u32		ext2_ino_t;
+typedef __u32		blk_t;
+typedef __u32		dgrp_t;
+typedef __u32		ext2_off_t;
+typedef __s64		e2_blkcnt_t;
+typedef __u32		ext2_dirhash_t;
+
+#include "ext2_io.h"
+#include "ext2_err.h"
+
+typedef struct struct_ext2_filsys *ext2_filsys;
+
+struct ext2fs_struct_generic_bitmap {
+	errcode_t	magic;
+	ext2_filsys	fs;
+	__u32		start, end;
+	__u32		real_end;
+	char	*	description;
+	char	*	bitmap;
+	errcode_t	base_error_code;
+	__u32		reserved[7];
+};
+
+#define EXT2FS_MARK_ERROR	0
+#define EXT2FS_UNMARK_ERROR	1
+#define EXT2FS_TEST_ERROR	2
+
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap;
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap;
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap;
+
+#define EXT2_FIRST_INODE(s)	EXT2_FIRST_INO(s)
+
+/*
+ * badblocks list definitions
+ */
+
+typedef struct ext2_struct_u32_list *ext2_badblocks_list;
+typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate;
+
+typedef struct ext2_struct_u32_list *ext2_u32_list;
+typedef struct ext2_struct_u32_iterate *ext2_u32_iterate;
+
+/* old */
+typedef struct ext2_struct_u32_list *badblocks_list;
+typedef struct ext2_struct_u32_iterate *badblocks_iterate;
+
+#define BADBLOCKS_FLAG_DIRTY	1
+
+/*
+ * ext2_dblist structure and abstractions (see dblist.c)
+ */
+struct ext2_db_entry {
+	ext2_ino_t	ino;
+	blk_t	blk;
+	int	blockcnt;
+};
+
+typedef struct ext2_struct_dblist *ext2_dblist;
+
+#define DBLIST_ABORT	1
+
+/*
+ * ext2_fileio definitions
+ */
+
+#define EXT2_FILE_WRITE		0x0001
+#define EXT2_FILE_CREATE	0x0002
+
+#define EXT2_FILE_MASK		0x00FF
+
+#define EXT2_FILE_BUF_DIRTY	0x4000
+#define EXT2_FILE_BUF_VALID	0x2000
+
+typedef struct ext2_file *ext2_file_t;
+
+#define EXT2_SEEK_SET	0
+#define EXT2_SEEK_CUR	1
+#define EXT2_SEEK_END	2
+
+/*
+ * Flags for the ext2_filsys structure and for ext2fs_open()
+ */
+#define EXT2_FLAG_RW			0x01
+#define EXT2_FLAG_CHANGED		0x02
+#define EXT2_FLAG_DIRTY			0x04
+#define EXT2_FLAG_VALID			0x08
+#define EXT2_FLAG_IB_DIRTY		0x10
+#define EXT2_FLAG_BB_DIRTY		0x20
+#define EXT2_FLAG_SWAP_BYTES		0x40
+#define EXT2_FLAG_SWAP_BYTES_READ	0x80
+#define EXT2_FLAG_SWAP_BYTES_WRITE	0x100
+#define EXT2_FLAG_MASTER_SB_ONLY	0x200
+#define EXT2_FLAG_FORCE			0x400
+#define EXT2_FLAG_SUPER_ONLY		0x800
+#define EXT2_FLAG_JOURNAL_DEV_OK	0x1000
+#define EXT2_FLAG_IMAGE_FILE		0x2000
+
+/*
+ * Special flag in the ext2 inode i_flag field that means that this is
+ * a new inode.  (So that ext2_write_inode() can clear extra fields.)
+ */
+#define EXT2_NEW_INODE_FL	0x80000000
+
+/*
+ * Flags for mkjournal
+ *
+ * EXT2_MKJOURNAL_V1_SUPER	Make a (deprecated) V1 journal superblock
+ */
+#define EXT2_MKJOURNAL_V1_SUPER	0x0000001
+
+struct struct_ext2_filsys {
+	errcode_t			magic;
+	io_channel			io;
+	int				flags;
+	char *				device_name;
+	struct ext2_super_block	*	super;
+	unsigned int			blocksize;
+	int				fragsize;
+	dgrp_t				group_desc_count;
+	unsigned long			desc_blocks;
+	struct ext2_group_desc *	group_desc;
+	int				inode_blocks_per_group;
+	ext2fs_inode_bitmap		inode_map;
+	ext2fs_block_bitmap		block_map;
+	errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
+	errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino);
+	errcode_t (*write_bitmaps)(ext2_filsys fs);
+	errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino,
+				struct ext2_inode *inode);
+	errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino,
+				struct ext2_inode *inode);
+	ext2_badblocks_list		badblocks;
+	ext2_dblist			dblist;
+	__u32				stride;	/* for mke2fs */
+	struct ext2_super_block *	orig_super;
+	struct ext2_image_hdr *		image_header;
+	__u32				umask;
+	/*
+	 * Reserved for future expansion
+	 */
+	__u32				reserved[8];
+
+	/*
+	 * Reserved for the use of the calling application.
+	 */
+	void *				priv_data;
+
+	/*
+	 * Inode cache
+	 */
+	struct ext2_inode_cache		*icache;
+	io_channel			image_io;
+};
+
+#include "bitops.h"
+
+/*
+ * Return flags for the block iterator functions
+ */
+#define BLOCK_CHANGED	1
+#define BLOCK_ABORT	2
+#define BLOCK_ERROR	4
+
+/*
+ * Block interate flags
+ *
+ * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator
+ * function should be called on blocks where the block number is zero.
+ * This is used by ext2fs_expand_dir() to be able to add a new block
+ * to an inode.  It can also be used for programs that want to be able
+ * to deal with files that contain "holes".
+ *
+ * BLOCK_FLAG_TRAVERSE indicates that the iterator function for the
+ * indirect, doubly indirect, etc. blocks should be called after all
+ * of the blocks containined in the indirect blocks are processed.
+ * This is useful if you are going to be deallocating blocks from an
+ * inode.
+ *
+ * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be
+ * called for data blocks only.
+ *
+ * BLOCK_FLAG_NO_LARGE is for internal use only.  It informs
+ * ext2fs_block_iterate2 that large files won't be accepted.
+ */
+#define BLOCK_FLAG_APPEND	1
+#define BLOCK_FLAG_HOLE		1
+#define BLOCK_FLAG_DEPTH_TRAVERSE	2
+#define BLOCK_FLAG_DATA_ONLY	4
+
+#define BLOCK_FLAG_NO_LARGE	0x1000
+
+/*
+ * Magic "block count" return values for the block iterator function.
+ */
+#define BLOCK_COUNT_IND		(-1)
+#define BLOCK_COUNT_DIND	(-2)
+#define BLOCK_COUNT_TIND	(-3)
+#define BLOCK_COUNT_TRANSLATOR	(-4)
+
+#if 0
+/*
+ * Flags for ext2fs_move_blocks
+ */
+#define EXT2_BMOVE_GET_DBLIST	0x0001
+#define EXT2_BMOVE_DEBUG	0x0002
+#endif
+
+/*
+ * Flags for directory block reading and writing functions
+ */
+#define EXT2_DIRBLOCK_V2_STRUCT	0x0001
+
+/*
+ * Return flags for the directory iterator functions
+ */
+#define DIRENT_CHANGED	1
+#define DIRENT_ABORT	2
+#define DIRENT_ERROR	3
+
+/*
+ * Directory iterator flags
+ */
+
+#define DIRENT_FLAG_INCLUDE_EMPTY	1
+#define DIRENT_FLAG_INCLUDE_REMOVED	2
+
+#define DIRENT_DOT_FILE		1
+#define DIRENT_DOT_DOT_FILE	2
+#define DIRENT_OTHER_FILE	3
+#define DIRENT_DELETED_FILE	4
+
+/*
+ * Inode scan definitions
+ */
+typedef struct ext2_struct_inode_scan *ext2_inode_scan;
+
+/*
+ * ext2fs_scan flags
+ */
+#define EXT2_SF_CHK_BADBLOCKS	0x0001
+#define EXT2_SF_BAD_INODE_BLK	0x0002
+#define EXT2_SF_BAD_EXTRA_BYTES	0x0004
+#define EXT2_SF_SKIP_MISSING_ITABLE	0x0008
+
+/*
+ * ext2fs_check_if_mounted flags
+ */
+#define EXT2_MF_MOUNTED		1
+#define EXT2_MF_ISROOT		2
+#define EXT2_MF_READONLY	4
+#define EXT2_MF_SWAP		8
+#define EXT2_MF_BUSY		16
+
+/*
+ * Ext2/linux mode flags.  We define them here so that we don't need
+ * to depend on the OS's sys/stat.h, since we may be compiling on a
+ * non-Linux system.
+ */
+#define LINUX_S_IFMT  00170000
+#define LINUX_S_IFSOCK 0140000
+#define LINUX_S_IFLNK	 0120000
+#define LINUX_S_IFREG  0100000
+#define LINUX_S_IFBLK  0060000
+#define LINUX_S_IFDIR  0040000
+#define LINUX_S_IFCHR  0020000
+#define LINUX_S_IFIFO  0010000
+#define LINUX_S_ISUID  0004000
+#define LINUX_S_ISGID  0002000
+#define LINUX_S_ISVTX  0001000
+
+#define LINUX_S_IRWXU 00700
+#define LINUX_S_IRUSR 00400
+#define LINUX_S_IWUSR 00200
+#define LINUX_S_IXUSR 00100
+
+#define LINUX_S_IRWXG 00070
+#define LINUX_S_IRGRP 00040
+#define LINUX_S_IWGRP 00020
+#define LINUX_S_IXGRP 00010
+
+#define LINUX_S_IRWXO 00007
+#define LINUX_S_IROTH 00004
+#define LINUX_S_IWOTH 00002
+#define LINUX_S_IXOTH 00001
+
+#define LINUX_S_ISLNK(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFLNK)
+#define LINUX_S_ISREG(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFREG)
+#define LINUX_S_ISDIR(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFDIR)
+#define LINUX_S_ISCHR(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFCHR)
+#define LINUX_S_ISBLK(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFBLK)
+#define LINUX_S_ISFIFO(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFIFO)
+#define LINUX_S_ISSOCK(m)	(((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK)
+
+/*
+ * ext2 size of an inode
+ */
+#define EXT2_I_SIZE(i)	((i)->i_size | ((__u64) (i)->i_size_high << 32))
+
+/*
+ * ext2_icount_t abstraction
+ */
+#define EXT2_ICOUNT_OPT_INCREMENT	0x01
+
+typedef struct ext2_icount *ext2_icount_t;
+
+/*
+ * Flags for ext2fs_bmap
+ */
+#define BMAP_ALLOC	0x0001
+#define BMAP_SET	0x0002
+
+/*
+ * Flags for imager.c functions
+ */
+#define IMAGER_FLAG_INODEMAP	1
+#define IMAGER_FLAG_SPARSEWRITE	2
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+	  if ((struct)->magic != (code)) return (code)
+
+
+/*
+ * For ext2 compression support
+ */
+#define EXT2FS_COMPRESSED_BLKADDR ((blk_t) 0xffffffff)
+#define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR)
+
+/*
+ * Features supported by this version of the library
+ */
+#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_DIR_INDEX|\
+					 EXT2_FEATURE_COMPAT_EXT_ATTR)
+
+/* This #ifdef is temporary until compression is fully supported */
+#ifdef ENABLE_COMPRESSION
+#ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL
+/* If the below warning bugs you, then have
+   `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your
+   environment at configure time. */
+ #warning "Compression support is experimental"
+#endif
+#define EXT2_LIB_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE|\
+					 EXT2_FEATURE_INCOMPAT_COMPRESSION|\
+					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+					 EXT2_FEATURE_INCOMPAT_META_BG|\
+					 EXT3_FEATURE_INCOMPAT_RECOVER)
+#else
+#define EXT2_LIB_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE|\
+					 EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+					 EXT2_FEATURE_INCOMPAT_META_BG|\
+					 EXT3_FEATURE_INCOMPAT_RECOVER)
+#endif
+#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
+					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
+/*
+ * function prototypes
+ */
+
+/* alloc.c */
+extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode,
+				  ext2fs_inode_bitmap map, ext2_ino_t *ret);
+extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
+				  ext2fs_block_bitmap map, blk_t *ret);
+extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start,
+					blk_t finish, int num,
+					ext2fs_block_bitmap map,
+					blk_t *ret);
+extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
+				    char *block_buf, blk_t *ret);
+
+/* alloc_sb.c */
+extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs,
+					dgrp_t group,
+					ext2fs_block_bitmap bmap);
+
+/* alloc_stats.c */
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse);
+void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
+			       int inuse, int isdir);
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse);
+
+/* alloc_tables.c */
+extern errcode_t ext2fs_allocate_tables(ext2_filsys fs);
+extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
+					     ext2fs_block_bitmap bmap);
+
+/* badblocks.c */
+extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size);
+extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk);
+extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk);
+extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk);
+extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
+					       ext2_u32_iterate *ret);
+extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk);
+extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter);
+extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest);
+extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2);
+
+extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret,
+					    int size);
+extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb,
+					   blk_t blk);
+extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb,
+				    blk_t blk);
+extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk);
+extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk);
+extern errcode_t
+	ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
+					    ext2_badblocks_iterate *ret);
+extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter,
+					 blk_t *blk);
+extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter);
+extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
+				       ext2_badblocks_list *dest);
+extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1,
+				  ext2_badblocks_list bb2);
+extern int ext2fs_u32_list_count(ext2_u32_list bb);
+
+/* bb_compat */
+extern errcode_t badblocks_list_create(badblocks_list *ret, int size);
+extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk);
+extern int badblocks_list_test(badblocks_list bb, blk_t blk);
+extern errcode_t badblocks_list_iterate_begin(badblocks_list bb,
+					      badblocks_iterate *ret);
+extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk);
+extern void badblocks_list_iterate_end(badblocks_iterate iter);
+extern void badblocks_list_free(badblocks_list bb);
+
+/* bb_inode.c */
+extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs,
+					ext2_badblocks_list bb_list);
+
+/* bitmaps.c */
+extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs);
+extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs);
+extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
+						__u32 end,
+						__u32 real_end,
+						const char *descr,
+						ext2fs_generic_bitmap *ret);
+extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
+					      const char *descr,
+					      ext2fs_block_bitmap *ret);
+extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
+					      const char *descr,
+					      ext2fs_inode_bitmap *ret);
+extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
+					       ext2_ino_t end, ext2_ino_t *oend);
+extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
+					       blk_t end, blk_t *oend);
+extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap);
+extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs);
+extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs);
+
+/* block.c */
+extern errcode_t ext2fs_block_iterate(ext2_filsys fs,
+				      ext2_ino_t	ino,
+				      int	flags,
+				      char *block_buf,
+				      int (*func)(ext2_filsys fs,
+						  blk_t	*blocknr,
+						  int	blockcnt,
+						  void	*priv_data),
+				      void *priv_data);
+errcode_t ext2fs_block_iterate2(ext2_filsys fs,
+				ext2_ino_t	ino,
+				int	flags,
+				char *block_buf,
+				int (*func)(ext2_filsys fs,
+					    blk_t	*blocknr,
+					    e2_blkcnt_t	blockcnt,
+					    blk_t	ref_blk,
+					    int		ref_offset,
+					    void	*priv_data),
+				void *priv_data);
+
+/* bmap.c */
+extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
+			     struct ext2_inode *inode,
+			     char *block_buf, int bmap_flags,
+			     blk_t block, blk_t *phys_blk);
+
+
+#if 0
+/* bmove.c */
+extern errcode_t ext2fs_move_blocks(ext2_filsys fs,
+				    ext2fs_block_bitmap reserve,
+				    ext2fs_block_bitmap alloc_map,
+				    int flags);
+#endif
+
+/* check_desc.c */
+extern errcode_t ext2fs_check_desc(ext2_filsys fs);
+
+/* closefs.c */
+extern errcode_t ext2fs_close(ext2_filsys fs);
+extern errcode_t ext2fs_flush(ext2_filsys fs);
+extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block);
+extern int ext2fs_super_and_bgd_loc(ext2_filsys fs,
+				    dgrp_t group,
+				    blk_t *ret_super_blk,
+				    blk_t *ret_old_desc_blk,
+				    blk_t *ret_new_desc_blk,
+				    int *ret_meta_bg);
+extern void ext2fs_update_dynamic_rev(ext2_filsys fs);
+
+/* cmp_bitmaps.c */
+extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
+					     ext2fs_block_bitmap bm2);
+extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
+					     ext2fs_inode_bitmap bm2);
+
+/* dblist.c */
+
+extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs);
+extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist);
+extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino,
+				      blk_t blk, int blockcnt);
+extern void ext2fs_dblist_sort(ext2_dblist dblist,
+			       int (*sortfunc)(const void *,
+							   const void *));
+extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
+	int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info,
+		    void	*priv_data),
+       void *priv_data);
+extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino,
+				      blk_t blk, int blockcnt);
+extern errcode_t ext2fs_copy_dblist(ext2_dblist src,
+				    ext2_dblist *dest);
+extern int ext2fs_dblist_count(ext2_dblist dblist);
+
+/* dblist_dir.c */
+extern errcode_t
+	ext2fs_dblist_dir_iterate(ext2_dblist dblist,
+				  int	flags,
+				  char	*block_buf,
+				  int (*func)(ext2_ino_t	dir,
+					      int		entry,
+					      struct ext2_dir_entry *dirent,
+					      int	offset,
+					      int	blocksize,
+					      char	*buf,
+					      void	*priv_data),
+				  void *priv_data);
+
+/* dirblock.c */
+extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
+				       void *buf);
+extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
+					void *buf, int flags);
+extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
+					void *buf);
+extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
+					 void *buf, int flags);
+
+/* dirhash.c */
+extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
+				const __u32 *seed,
+				ext2_dirhash_t *ret_hash,
+				ext2_dirhash_t *ret_minor_hash);
+
+
+/* dir_iterate.c */
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs,
+			      ext2_ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*priv_data),
+			      void *priv_data);
+extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
+			      ext2_ino_t dir,
+			      int flags,
+			      char *block_buf,
+			      int (*func)(ext2_ino_t	dir,
+					  int	entry,
+					  struct ext2_dir_entry *dirent,
+					  int	offset,
+					  int	blocksize,
+					  char	*buf,
+					  void	*priv_data),
+			      void *priv_data);
+
+/* dupfs.c */
+extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
+
+/* expanddir.c */
+extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir);
+
+/* ext_attr.c */
+extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
+extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block,
+				       void *buf);
+extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+					   char *block_buf,
+					   int adjust, __u32 *newcount);
+
+/* fileio.c */
+extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
+				   struct ext2_inode *inode,
+				   int flags, ext2_file_t *ret);
+extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
+				  int flags, ext2_file_t *ret);
+extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file);
+extern errcode_t ext2fs_file_close(ext2_file_t file);
+extern errcode_t ext2fs_file_flush(ext2_file_t file);
+extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
+				  unsigned int wanted, unsigned int *got);
+extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
+				   unsigned int nbytes, unsigned int *written);
+extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
+				   int whence, __u64 *ret_pos);
+extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
+				   int whence, ext2_off_t *ret_pos);
+errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size);
+extern ext2_off_t ext2fs_file_get_size(ext2_file_t file);
+extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size);
+
+/* finddev.c */
+extern char *ext2fs_find_block_device(dev_t device);
+
+/* flushb.c */
+extern errcode_t ext2fs_sync_device(int fd, int flushb);
+
+/* freefs.c */
+extern void ext2fs_free(ext2_filsys fs);
+extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap);
+extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_free_dblist(ext2_dblist dblist);
+extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb);
+extern void ext2fs_u32_list_free(ext2_u32_list bb);
+
+/* getsize.c */
+extern errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+					blk_t *retblocks);
+
+/* getsectsize.c */
+errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize);
+
+/* imager.c */
+extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags);
+
+/* ind_block.c */
+errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+
+/* initialize.c */
+extern errcode_t ext2fs_initialize(const char *name, int flags,
+				   struct ext2_super_block *param,
+				   io_manager manager, ext2_filsys *ret_fs);
+
+/* icount.c */
+extern void ext2fs_free_icount(ext2_icount_t icount);
+extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags,
+				       unsigned int size,
+				       ext2_icount_t hint, ext2_icount_t *ret);
+extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
+				      unsigned int size,
+				      ext2_icount_t *ret);
+extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino,
+				     __u16 *ret);
+extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
+					 __u16 *ret);
+extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
+					 __u16 *ret);
+extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
+				     __u16 count);
+extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
+
+/* inode.c */
+extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
+extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan,
+					    ext2_ino_t *ino,
+					    struct ext2_inode *inode,
+					    int bufsize);
+extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
+				  ext2_inode_scan *ret_scan);
+extern void ext2fs_close_inode_scan(ext2_inode_scan scan);
+extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
+			       struct ext2_inode *inode);
+extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+						   int	group);
+extern void ext2fs_set_inode_callback
+	(ext2_inode_scan scan,
+	 errcode_t (*done_group)(ext2_filsys fs,
+				 dgrp_t group,
+				 void * priv_data),
+	 void *done_group_data);
+extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+				   int clear_flags);
+extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
+					struct ext2_inode * inode,
+					int bufsize);
+extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino,
+			    struct ext2_inode * inode);
+extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
+					 struct ext2_inode * inode,
+					 int bufsize);
+extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
+			    struct ext2_inode * inode);
+extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
+			    struct ext2_inode * inode);
+extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
+extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino);
+
+/* inode_io.c */
+extern io_manager inode_io_manager;
+extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
+					char **name);
+extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
+					 struct ext2_inode *inode,
+					 char **name);
+
+/* ismounted.c */
+extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags);
+extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
+					  char *mtpt, int mtlen);
+
+/* namei.c */
+extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
+			 int namelen, char *buf, ext2_ino_t *inode);
+extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+			const char *name, ext2_ino_t *inode);
+errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+			      const char *name, ext2_ino_t *inode);
+extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+			ext2_ino_t inode, ext2_ino_t *res_inode);
+
+/* native.c */
+int ext2fs_native_flag(void);
+
+/* newdir.c */
+extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
+				ext2_ino_t parent_ino, char **block);
+
+/* mkdir.c */
+extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
+			      const char *name);
+
+/* mkjournal.c */
+extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
+						  __u32 size, int flags,
+						  char  **ret_jsb);
+extern errcode_t ext2fs_add_journal_device(ext2_filsys fs,
+					   ext2_filsys journal_dev);
+extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size,
+					  int flags);
+
+/* openfs.c */
+extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
+			     unsigned int block_size, io_manager manager,
+			     ext2_filsys *ret_fs);
+extern errcode_t ext2fs_open2(const char *name, const char *io_options,
+			      int flags, int superblock,
+			      unsigned int block_size, io_manager manager,
+			      ext2_filsys *ret_fs);
+extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block,
+					 dgrp_t i);
+errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io);
+errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io);
+errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io);
+
+/* get_pathname.c */
+extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
+			       char **name);
+
+/* link.c */
+errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
+		      ext2_ino_t ino, int flags);
+errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name,
+			ext2_ino_t ino, int flags);
+
+/* read_bb.c */
+extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs,
+				      ext2_badblocks_list *bb_list);
+
+/* read_bb_file.c */
+extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
+				      ext2_badblocks_list *bb_list,
+				      void *priv_data,
+				      void (*invalid)(ext2_filsys fs,
+						      blk_t blk,
+						      char *badstr,
+						      void *priv_data));
+extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
+				     ext2_badblocks_list *bb_list,
+				     void (*invalid)(ext2_filsys fs,
+						     blk_t blk));
+
+/* res_gdt.c */
+extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
+
+/* rs_bitmap.c */
+extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end,
+					      __u32 new_real_end,
+					      ext2fs_generic_bitmap bmap);
+extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+					    ext2fs_inode_bitmap bmap);
+extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+					    ext2fs_block_bitmap bmap);
+extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
+				    ext2fs_generic_bitmap *dest);
+
+/* swapfs.c */
+extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize,
+				 int has_header);
+extern void ext2fs_swap_super(struct ext2_super_block * super);
+extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp);
+extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
+				   struct ext2_inode_large *f, int hostorder,
+				   int bufsize);
+extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
+			      struct ext2_inode *f, int hostorder);
+
+/* valid_blk.c */
+extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
+
+/* version.c */
+extern int ext2fs_parse_version_string(const char *ver_string);
+extern int ext2fs_get_library_version(const char **ver_string,
+				      const char **date_string);
+
+/* write_bb_file.c */
+extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
+				      unsigned int flags,
+				      FILE *f);
+
+
+/* inline functions */
+extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
+extern errcode_t ext2fs_free_mem(void *ptr);
+extern errcode_t ext2fs_resize_mem(unsigned long old_size,
+				   unsigned long size, void *ptr);
+extern void ext2fs_mark_super_dirty(ext2_filsys fs);
+extern void ext2fs_mark_changed(ext2_filsys fs);
+extern int ext2fs_test_changed(ext2_filsys fs);
+extern void ext2fs_mark_valid(ext2_filsys fs);
+extern void ext2fs_unmark_valid(ext2_filsys fs);
+extern int ext2fs_test_valid(ext2_filsys fs);
+extern void ext2fs_mark_ib_dirty(ext2_filsys fs);
+extern void ext2fs_mark_bb_dirty(ext2_filsys fs);
+extern int ext2fs_test_ib_dirty(ext2_filsys fs);
+extern int ext2fs_test_bb_dirty(ext2_filsys fs);
+extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk);
+extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino);
+extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
+				      struct ext2_inode *inode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h
new file mode 100644
index 0000000..7a02e9a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fsP.h
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2fsP.h --- private header file for ext2 library
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "ext2fs.h"
+
+/*
+ * Badblocks list
+ */
+struct ext2_struct_u32_list {
+	int	magic;
+	int	num;
+	int	size;
+	__u32	*list;
+	int	badblocks_flags;
+};
+
+struct ext2_struct_u32_iterate {
+	int			magic;
+	ext2_u32_list		bb;
+	int			ptr;
+};
+
+
+/*
+ * Directory block iterator definition
+ */
+struct ext2_struct_dblist {
+	int			magic;
+	ext2_filsys		fs;
+	ext2_ino_t		size;
+	ext2_ino_t		count;
+	int			sorted;
+	struct ext2_db_entry *	list;
+};
+
+/*
+ * For directory iterators
+ */
+struct dir_context {
+	ext2_ino_t		dir;
+	int		flags;
+	char		*buf;
+	int (*func)(ext2_ino_t	dir,
+		    int	entry,
+		    struct ext2_dir_entry *dirent,
+		    int	offset,
+		    int	blocksize,
+		    char	*buf,
+		    void	*priv_data);
+	void		*priv_data;
+	errcode_t	errcode;
+};
+
+/*
+ * Inode cache structure
+ */
+struct ext2_inode_cache {
+	void *				buffer;
+	blk_t				buffer_blk;
+	int				cache_last;
+	int				cache_size;
+	int				refcount;
+	struct ext2_inode_cache_ent	*cache;
+};
+
+struct ext2_inode_cache_ent {
+	ext2_ino_t		ino;
+	struct ext2_inode	inode;
+};
+
+/* Function prototypes */
+
+extern int ext2fs_process_dir_block(ext2_filsys		fs,
+				    blk_t		*blocknr,
+				    e2_blkcnt_t		blockcnt,
+				    blk_t		ref_block,
+				    int			ref_offset,
+				    void		*priv_data);
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
new file mode 100644
index 0000000..7d37d23
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext2fs_inline.c
@@ -0,0 +1,365 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext2fs.h --- ext2fs
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "ext2fs.h"
+#include "bitops.h"
+#include <string.h>
+
+/*
+ *  Allocate memory
+ */
+errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
+{
+	void **pp = (void **)ptr;
+
+	*pp = malloc(size);
+	if (!*pp)
+		return EXT2_ET_NO_MEMORY;
+	return 0;
+}
+
+/*
+ * Free memory
+ */
+errcode_t ext2fs_free_mem(void *ptr)
+{
+	void **pp = (void **)ptr;
+
+	free(*pp);
+	*pp = 0;
+	return 0;
+}
+
+/*
+ *  Resize memory
+ */
+errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
+				     unsigned long size, void *ptr)
+{
+	void *p;
+
+	/* Use "memcpy" for pointer assignments here to avoid problems
+	 * with C99 strict type aliasing rules. */
+	memcpy(&p, ptr, sizeof (p));
+	p = xrealloc(p, size);
+	memcpy(ptr, &p, sizeof (p));
+	return 0;
+}
+
+/*
+ * Mark a filesystem superblock as dirty
+ */
+void ext2fs_mark_super_dirty(ext2_filsys fs)
+{
+	fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Mark a filesystem as changed
+ */
+void ext2fs_mark_changed(ext2_filsys fs)
+{
+	fs->flags |= EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Check to see if a filesystem has changed
+ */
+int ext2fs_test_changed(ext2_filsys fs)
+{
+	return (fs->flags & EXT2_FLAG_CHANGED);
+}
+
+/*
+ * Mark a filesystem as valid
+ */
+void ext2fs_mark_valid(ext2_filsys fs)
+{
+	fs->flags |= EXT2_FLAG_VALID;
+}
+
+/*
+ * Mark a filesystem as NOT valid
+ */
+void ext2fs_unmark_valid(ext2_filsys fs)
+{
+	fs->flags &= ~EXT2_FLAG_VALID;
+}
+
+/*
+ * Check to see if a filesystem is valid
+ */
+int ext2fs_test_valid(ext2_filsys fs)
+{
+	return (fs->flags & EXT2_FLAG_VALID);
+}
+
+/*
+ * Mark the inode bitmap as dirty
+ */
+void ext2fs_mark_ib_dirty(ext2_filsys fs)
+{
+	fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Mark the block bitmap as dirty
+ */
+void ext2fs_mark_bb_dirty(ext2_filsys fs)
+{
+	fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Check to see if a filesystem's inode bitmap is dirty
+ */
+int ext2fs_test_ib_dirty(ext2_filsys fs)
+{
+	return (fs->flags & EXT2_FLAG_IB_DIRTY);
+}
+
+/*
+ * Check to see if a filesystem's block bitmap is dirty
+ */
+int ext2fs_test_bb_dirty(ext2_filsys fs)
+{
+	return (fs->flags & EXT2_FLAG_BB_DIRTY);
+}
+
+/*
+ * Return the group # of a block
+ */
+int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk)
+{
+	return (blk - fs->super->s_first_data_block) /
+		fs->super->s_blocks_per_group;
+}
+
+/*
+ * Return the group # of an inode number
+ */
+int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
+{
+	return (ino - 1) / fs->super->s_inodes_per_group;
+}
+
+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);
+}
+
+
+
+
+
+
+
+
+
+__u16 ext2fs_swab16(__u16 val)
+{
+	return (val >> 8) | (val << 8);
+}
+
+__u32 ext2fs_swab32(__u32 val)
+{
+	return ((val>>24) | ((val>>8)&0xFF00) |
+		((val<<8)&0xFF0000) | (val<<24));
+}
+
+int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
+					blk_t bitno);
+
+int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
+					blk_t bitno)
+{
+	if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+		ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno);
+		return 0;
+	}
+	return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+				       blk_t block)
+{
+	return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap)
+				       bitmap,
+					  block);
+}
+
+int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+					 blk_t block)
+{
+	return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+					    block);
+}
+
+int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap,
+				       blk_t block)
+{
+	return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+					  block);
+}
+
+int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+				       ext2_ino_t inode)
+{
+	return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+					  inode);
+}
+
+int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+					 ext2_ino_t inode)
+{
+	return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+				     inode);
+}
+
+int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+				       ext2_ino_t inode)
+{
+	return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap,
+					  inode);
+}
+
+void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+					    blk_t block)
+{
+	ext2fs_set_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+					      blk_t block)
+{
+	ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
+					    blk_t block)
+{
+	return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+					    ext2_ino_t inode)
+{
+	ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+					      ext2_ino_t inode)
+{
+	ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+					   ext2_ino_t inode)
+{
+	return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap)
+{
+	return bitmap->start;
+}
+
+ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap)
+{
+	return bitmap->start;
+}
+
+blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap)
+{
+	return bitmap->end;
+}
+
+ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap)
+{
+	return bitmap->end;
+}
+
+int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+					    blk_t block, int num)
+{
+	int	i;
+
+	if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+		ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
+				   block, bitmap->description);
+		return 0;
+	}
+	for (i=0; i < num; i++) {
+		if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
+			return 0;
+	}
+	return 1;
+}
+
+int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+						 blk_t block, int num)
+{
+	int	i;
+
+	for (i=0; i < num; i++) {
+		if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
+			return 0;
+	}
+	return 1;
+}
+
+void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+					     blk_t block, int num)
+{
+	int	i;
+
+	if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+		ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
+				   bitmap->description);
+		return;
+	}
+	for (i=0; i < num; i++)
+		ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+						  blk_t block, int num)
+{
+	int	i;
+
+	for (i=0; i < num; i++)
+		ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+					       blk_t block, int num)
+{
+	int	i;
+
+	if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+		ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
+				   bitmap->description);
+		return;
+	}
+	for (i=0; i < num; i++)
+		ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+						    blk_t block, int num)
+{
+	int	i;
+	for (i=0; i < num; i++)
+		ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c
new file mode 100644
index 0000000..a2ba12d
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ext_attr.c
@@ -0,0 +1,101 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ext_attr.c --- extended attribute blocks
+ *
+ * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * Copyright (C) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2_ext_attr.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
+{
+	errcode_t	retval;
+
+	retval = io_channel_read_blk(fs->io, block, 1, buf);
+	if (retval)
+		return retval;
+#if BB_BIG_ENDIAN
+	if ((fs->flags & (EXT2_FLAG_SWAP_BYTES|
+			  EXT2_FLAG_SWAP_BYTES_READ)) != 0)
+		ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
+#endif
+	return 0;
+}
+
+errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
+{
+	errcode_t	retval;
+	char		*write_buf;
+	char		*buf = NULL;
+
+	if (BB_BIG_ENDIAN && ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+	    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))) {
+		retval = ext2fs_get_mem(fs->blocksize, &buf);
+		if (retval)
+			return retval;
+		write_buf = buf;
+		ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
+	} else
+		write_buf = (char *) inbuf;
+	retval = io_channel_write_blk(fs->io, block, 1, write_buf);
+	if (buf)
+		ext2fs_free_mem(&buf);
+	if (!retval)
+		ext2fs_mark_changed(fs);
+	return retval;
+}
+
+/*
+ * This function adjusts the reference count of the EA block.
+ */
+errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+				    char *block_buf, int adjust,
+				    __u32 *newcount)
+{
+	errcode_t	retval;
+	struct ext2_ext_attr_header *header;
+	char	*buf = NULL;
+
+	if ((blk >= fs->super->s_blocks_count) ||
+	    (blk < fs->super->s_first_data_block))
+		return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+	if (!block_buf) {
+		retval = ext2fs_get_mem(fs->blocksize, &buf);
+		if (retval)
+			return retval;
+		block_buf = buf;
+	}
+
+	retval = ext2fs_read_ext_attr(fs, blk, block_buf);
+	if (retval)
+		goto errout;
+
+	header = (struct ext2_ext_attr_header *) block_buf;
+	header->h_refcount += adjust;
+	if (newcount)
+		*newcount = header->h_refcount;
+
+	retval = ext2fs_write_ext_attr(fs, blk, block_buf);
+	if (retval)
+		goto errout;
+
+errout:
+	if (buf)
+		ext2fs_free_mem(&buf);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c
new file mode 100644
index 0000000..5a5ad3e
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/fileio.c
@@ -0,0 +1,377 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fileio.c --- Simple file I/O routines
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct ext2_file {
+	errcode_t		magic;
+	ext2_filsys		fs;
+	ext2_ino_t		ino;
+	struct ext2_inode	inode;
+	int			flags;
+	__u64			pos;
+	blk_t			blockno;
+	blk_t			physblock;
+	char			*buf;
+};
+
+#define BMAP_BUFFER (file->buf + fs->blocksize)
+
+errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
+			    struct ext2_inode *inode,
+			    int flags, ext2_file_t *ret)
+{
+	ext2_file_t	file;
+	errcode_t	retval;
+
+	/*
+	 * Don't let caller create or open a file for writing if the
+	 * filesystem is read-only.
+	 */
+	if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
+	    !(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+
+	retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
+	if (retval)
+		return retval;
+
+	memset(file, 0, sizeof(struct ext2_file));
+	file->magic = EXT2_ET_MAGIC_EXT2_FILE;
+	file->fs = fs;
+	file->ino = ino;
+	file->flags = flags & EXT2_FILE_MASK;
+
+	if (inode) {
+		memcpy(&file->inode, inode, sizeof(struct ext2_inode));
+	} else {
+		retval = ext2fs_read_inode(fs, ino, &file->inode);
+		if (retval)
+			goto fail;
+	}
+
+	retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf);
+	if (retval)
+		goto fail;
+
+	*ret = file;
+	return 0;
+
+fail:
+	ext2fs_free_mem(&file->buf);
+	ext2fs_free_mem(&file);
+	return retval;
+}
+
+errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
+			   int flags, ext2_file_t *ret)
+{
+	return ext2fs_file_open2(fs, ino, NULL, flags, ret);
+}
+
+/*
+ * This function returns the filesystem handle of a file from the structure
+ */
+ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
+{
+	if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
+		return 0;
+	return file->fs;
+}
+
+/*
+ * This function flushes the dirty block buffer out to disk if
+ * necessary.
+ */
+errcode_t ext2fs_file_flush(ext2_file_t file)
+{
+	errcode_t	retval;
+	ext2_filsys fs;
+
+	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+	fs = file->fs;
+
+	if (!(file->flags & EXT2_FILE_BUF_VALID) ||
+	    !(file->flags & EXT2_FILE_BUF_DIRTY))
+		return 0;
+
+	/*
+	 * OK, the physical block hasn't been allocated yet.
+	 * Allocate it.
+	 */
+	if (!file->physblock) {
+		retval = ext2fs_bmap(fs, file->ino, &file->inode,
+				     BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0,
+				     file->blockno, &file->physblock);
+		if (retval)
+			return retval;
+	}
+
+	retval = io_channel_write_blk(fs->io, file->physblock,
+				      1, file->buf);
+	if (retval)
+		return retval;
+
+	file->flags &= ~EXT2_FILE_BUF_DIRTY;
+
+	return retval;
+}
+
+/*
+ * This function synchronizes the file's block buffer and the current
+ * file position, possibly invalidating block buffer if necessary
+ */
+static errcode_t sync_buffer_position(ext2_file_t file)
+{
+	blk_t	b;
+	errcode_t	retval;
+
+	b = file->pos / file->fs->blocksize;
+	if (b != file->blockno) {
+		retval = ext2fs_file_flush(file);
+		if (retval)
+			return retval;
+		file->flags &= ~EXT2_FILE_BUF_VALID;
+	}
+	file->blockno = b;
+	return 0;
+}
+
+/*
+ * This function loads the file's block buffer with valid data from
+ * the disk as necessary.
+ *
+ * If dontfill is true, then skip initializing the buffer since we're
+ * going to be replacing its entire contents anyway.  If set, then the
+ * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
+ */
+#define DONTFILL 1
+static errcode_t load_buffer(ext2_file_t file, int dontfill)
+{
+	ext2_filsys	fs = file->fs;
+	errcode_t	retval;
+
+	if (!(file->flags & EXT2_FILE_BUF_VALID)) {
+		retval = ext2fs_bmap(fs, file->ino, &file->inode,
+				     BMAP_BUFFER, 0, file->blockno,
+				     &file->physblock);
+		if (retval)
+			return retval;
+		if (!dontfill) {
+			if (file->physblock) {
+				retval = io_channel_read_blk(fs->io,
+							     file->physblock,
+							     1, file->buf);
+				if (retval)
+					return retval;
+			} else
+				memset(file->buf, 0, fs->blocksize);
+		}
+		file->flags |= EXT2_FILE_BUF_VALID;
+	}
+	return 0;
+}
+
+
+errcode_t ext2fs_file_close(ext2_file_t file)
+{
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+	retval = ext2fs_file_flush(file);
+
+	ext2fs_free_mem(&file->buf);
+	ext2fs_free_mem(&file);
+
+	return retval;
+}
+
+
+errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
+			   unsigned int wanted, unsigned int *got)
+{
+	ext2_filsys	fs;
+	errcode_t	retval = 0;
+	unsigned int	start, c, count = 0;
+	__u64		left;
+	char		*ptr = (char *) buf;
+
+	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+	fs = file->fs;
+
+	while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
+		retval = sync_buffer_position(file);
+		if (retval)
+			goto fail;
+		retval = load_buffer(file, 0);
+		if (retval)
+			goto fail;
+
+		start = file->pos % fs->blocksize;
+		c = fs->blocksize - start;
+		if (c > wanted)
+			c = wanted;
+		left = EXT2_I_SIZE(&file->inode) - file->pos;
+		if (c > left)
+			c = left;
+
+		memcpy(ptr, file->buf+start, c);
+		file->pos += c;
+		ptr += c;
+		count += c;
+		wanted -= c;
+	}
+
+fail:
+	if (got)
+		*got = count;
+	return retval;
+}
+
+
+errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
+			    unsigned int nbytes, unsigned int *written)
+{
+	ext2_filsys	fs;
+	errcode_t	retval = 0;
+	unsigned int	start, c, count = 0;
+	const char	*ptr = (const char *) buf;
+
+	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+	fs = file->fs;
+
+	if (!(file->flags & EXT2_FILE_WRITE))
+		return EXT2_ET_FILE_RO;
+
+	while (nbytes > 0) {
+		retval = sync_buffer_position(file);
+		if (retval)
+			goto fail;
+
+		start = file->pos % fs->blocksize;
+		c = fs->blocksize - start;
+		if (c > nbytes)
+			c = nbytes;
+
+		/*
+		 * We only need to do a read-modify-update cycle if
+		 * we're doing a partial write.
+		 */
+		retval = load_buffer(file, (c == fs->blocksize));
+		if (retval)
+			goto fail;
+
+		file->flags |= EXT2_FILE_BUF_DIRTY;
+		memcpy(file->buf+start, ptr, c);
+		file->pos += c;
+		ptr += c;
+		count += c;
+		nbytes -= c;
+	}
+
+fail:
+	if (written)
+		*written = count;
+	return retval;
+}
+
+errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
+			    int whence, __u64 *ret_pos)
+{
+	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+	if (whence == EXT2_SEEK_SET)
+		file->pos = offset;
+	else if (whence == EXT2_SEEK_CUR)
+		file->pos += offset;
+	else if (whence == EXT2_SEEK_END)
+		file->pos = EXT2_I_SIZE(&file->inode) + offset;
+	else
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	if (ret_pos)
+		*ret_pos = file->pos;
+
+	return 0;
+}
+
+errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
+			    int whence, ext2_off_t *ret_pos)
+{
+	__u64		loffset, ret_loffset;
+	errcode_t	retval;
+
+	loffset = offset;
+	retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
+	if (ret_pos)
+		*ret_pos = (ext2_off_t) ret_loffset;
+	return retval;
+}
+
+
+/*
+ * This function returns the size of the file, according to the inode
+ */
+errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
+{
+	if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
+		return EXT2_ET_MAGIC_EXT2_FILE;
+	*ret_size = EXT2_I_SIZE(&file->inode);
+	return 0;
+}
+
+/*
+ * This function returns the size of the file, according to the inode
+ */
+ext2_off_t ext2fs_file_get_size(ext2_file_t file)
+{
+	__u64	size;
+
+	if (ext2fs_file_get_lsize(file, &size))
+		return 0;
+	if ((size >> 32) != 0)
+		return 0;
+	return size;
+}
+
+/*
+ * This function sets the size of the file, truncating it if necessary
+ *
+ * XXX still need to call truncate
+ */
+errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
+{
+	errcode_t	retval;
+	EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+	file->inode.i_size = size;
+	file->inode.i_size_high = 0;
+	if (file->ino) {
+		retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
+		if (retval)
+			return retval;
+	}
+
+	/*
+	 * XXX truncate inode if necessary
+	 */
+
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c
new file mode 100644
index 0000000..e9e83fd
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/finddev.c
@@ -0,0 +1,199 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * finddev.c -- this routine attempts to find a particular device in
+ *	/dev
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct dir_list {
+	char	*name;
+	struct dir_list *next;
+};
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *name, struct dir_list **list)
+{
+	struct dir_list *dp;
+
+	dp = xmalloc(sizeof(struct dir_list));
+	dp->name = xmalloc(strlen(name)+1);
+	strcpy(dp->name, name);
+	dp->next = *list;
+	*list = dp;
+}
+
+/*
+ * This function frees a directory list
+ */
+static void free_dirlist(struct dir_list **list)
+{
+	struct dir_list *dp, *next;
+
+	for (dp = *list; dp; dp = next) {
+		next = dp->next;
+		free(dp->name);
+		free(dp);
+	}
+	*list = 0;
+}
+
+static int scan_dir(char *dir_name, dev_t device, struct dir_list **list,
+		    char **ret_path)
+{
+	DIR	*dir;
+	struct dirent *dp;
+	char	path[1024], *cp;
+	int	dirlen;
+	struct stat st;
+
+	dirlen = strlen(dir_name);
+	if ((dir = opendir(dir_name)) == NULL)
+		return errno;
+	dp = readdir(dir);
+	while (dp) {
+		if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path))
+			goto skip_to_next;
+		if (dp->d_name[0] == '.' &&
+		    ((dp->d_name[1] == 0) ||
+		     ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+			goto skip_to_next;
+		sprintf(path, "%s/%s", dir_name, dp->d_name);
+		if (stat(path, &st) < 0)
+			goto skip_to_next;
+		if (S_ISDIR(st.st_mode))
+			add_to_dirlist(path, list);
+		if (S_ISBLK(st.st_mode) && st.st_rdev == device) {
+			cp = xmalloc(strlen(path)+1);
+			strcpy(cp, path);
+			*ret_path = cp;
+			goto success;
+		}
+	skip_to_next:
+		dp = readdir(dir);
+	}
+success:
+	closedir(dir);
+	return 0;
+}
+
+/*
+ * This function finds the pathname to a block device with a given
+ * device number.  It returns a pointer to allocated memory to the
+ * pathname on success, and NULL on failure.
+ */
+char *ext2fs_find_block_device(dev_t device)
+{
+	struct dir_list *list = NULL, *new_list = NULL;
+	struct dir_list *current;
+	char	*ret_path = NULL;
+
+	/*
+	 * Add the starting directories to search...
+	 */
+	add_to_dirlist("/devices", &list);
+	add_to_dirlist("/devfs", &list);
+	add_to_dirlist("/dev", &list);
+
+	while (list) {
+		current = list;
+		list = list->next;
+#ifdef DEBUG
+		printf("Scanning directory %s\n", current->name);
+#endif
+		scan_dir(current->name, device, &new_list, &ret_path);
+		free(current->name);
+		free(current);
+		if (ret_path)
+			break;
+		/*
+		 * If we're done checking at this level, descend to
+		 * the next level of subdirectories. (breadth-first)
+		 */
+		if (list == 0) {
+			list = new_list;
+			new_list = 0;
+		}
+	}
+	free_dirlist(&list);
+	free_dirlist(&new_list);
+	return ret_path;
+}
+
+
+#ifdef DEBUG
+int main(int argc, char** argv)
+{
+	char	*devname, *tmp;
+	int	major, minor;
+	dev_t	device;
+	const char *errmsg = "Cannot parse %s: %s\n";
+
+	if ((argc != 2) && (argc != 3)) {
+		fprintf(stderr, "Usage: %s device_number\n", argv[0]);
+		fprintf(stderr, "\t: %s major minor\n", argv[0]);
+		exit(1);
+	}
+	if (argc == 2) {
+		device = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "device number", argv[1]);
+			exit(1);
+		}
+	} else {
+		major = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "major number", argv[1]);
+			exit(1);
+		}
+		minor = strtoul(argv[2], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "minor number", argv[2]);
+			exit(1);
+		}
+		device = makedev(major, minor);
+		printf("Looking for device 0x%04x (%d:%d)\n", device,
+		       major, minor);
+	}
+	devname = ext2fs_find_block_device(device);
+	if (devname) {
+		printf("Found device!  %s\n", devname);
+		free(devname);
+	} else {
+		printf("Cannot find device.\n");
+	}
+	return 0;
+}
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c
new file mode 100644
index 0000000..45ed765
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/flushb.c
@@ -0,0 +1,83 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * flushb.c --- Hides system-dependent information for both syncing a
+ *	device to disk and to flush any buffers from disk cache.
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/param.h>
+#include <sys/mount.h>  /* This may define BLKFLSBUF */
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since
+ * not all portable header file does so for us.  This really should be
+ * fixed in the glibc header files.  (Recent glibcs appear to define
+ * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be
+ * defined anywhere portable.)  Until then....
+ */
+#ifdef __linux__
+#ifndef BLKFLSBUF
+#define BLKFLSBUF  _IO(0x12,97)  /* flush buffer cache */
+#endif
+#ifndef FDFLUSH
+#define FDFLUSH    _IO(2,0x4b)  /* flush floppy disk */
+#endif
+#endif
+
+/*
+ * This function will sync a device/file, and optionally attempt to
+ * flush the buffer cache.  The latter is basically only useful for
+ * system benchmarks and for torturing systems in burn-in tests.  :)
+ */
+errcode_t ext2fs_sync_device(int fd, int flushb)
+{
+	/*
+	 * We always sync the device in case we're running on old
+	 * kernels for which we can lose data if we don't.  (There
+	 * still is a race condition for those kernels, but this
+	 * reduces it greatly.)
+	 */
+	if (fsync (fd) == -1)
+		return errno;
+
+	if (flushb) {
+
+#ifdef BLKFLSBUF
+		if (ioctl (fd, BLKFLSBUF, 0) == 0)
+			return 0;
+#else
+#ifdef __GNUC__
+# warning BLKFLSBUF not defined
+#endif /* __GNUC__ */
+#endif
+#ifdef FDFLUSH
+		ioctl (fd, FDFLUSH, 0);   /* In case this is a floppy */
+#else
+#ifdef __GNUC__
+# warning FDFLUSH not defined
+#endif /* __GNUC__ */
+#endif
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c
new file mode 100644
index 0000000..0c5d48b
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/freefs.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * freefs.c --- free an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
+
+void ext2fs_free(ext2_filsys fs)
+{
+	if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS))
+		return;
+	if (fs->image_io != fs->io) {
+		if (fs->image_io)
+			io_channel_close(fs->image_io);
+	}
+	if (fs->io) {
+		io_channel_close(fs->io);
+	}
+	ext2fs_free_mem(&fs->device_name);
+	ext2fs_free_mem(&fs->super);
+	ext2fs_free_mem(&fs->orig_super);
+	ext2fs_free_mem(&fs->group_desc);
+	ext2fs_free_block_bitmap(fs->block_map);
+	ext2fs_free_inode_bitmap(fs->inode_map);
+
+	ext2fs_badblocks_list_free(fs->badblocks);
+	fs->badblocks = 0;
+
+	ext2fs_free_dblist(fs->dblist);
+
+	if (fs->icache)
+		ext2fs_free_inode_cache(fs->icache);
+
+	fs->magic = 0;
+
+	ext2fs_free_mem(&fs);
+}
+
+void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap)
+{
+	if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_GENERIC_BITMAP))
+		return;
+
+	bitmap->magic = 0;
+	ext2fs_free_mem(&bitmap->description);
+	ext2fs_free_mem(&bitmap->bitmap);
+	ext2fs_free_mem(&bitmap);
+}
+
+void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap)
+{
+	if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
+		return;
+
+	bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+	ext2fs_free_generic_bitmap(bitmap);
+}
+
+void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap)
+{
+	if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
+		return;
+
+	bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+	ext2fs_free_generic_bitmap(bitmap);
+}
+
+/*
+ * Free the inode cache structure
+ */
+static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
+{
+	if (--icache->refcount)
+		return;
+	ext2fs_free_mem(&icache->buffer);
+	ext2fs_free_mem(&icache->cache);
+	icache->buffer_blk = 0;
+	ext2fs_free_mem(&icache);
+}
+
+/*
+ * This procedure frees a badblocks list.
+ */
+void ext2fs_u32_list_free(ext2_u32_list bb)
+{
+	if (!bb || bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+		return;
+
+	ext2fs_free_mem(&bb->list);
+	ext2fs_free_mem(&bb);
+}
+
+void ext2fs_badblocks_list_free(ext2_badblocks_list bb)
+{
+	ext2fs_u32_list_free((ext2_u32_list) bb);
+}
+
+
+/*
+ * Free a directory block list
+ */
+void ext2fs_free_dblist(ext2_dblist dblist)
+{
+	if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST))
+		return;
+
+	ext2fs_free_mem(&dblist->list);
+	if (dblist->fs && dblist->fs->dblist == dblist)
+		dblist->fs->dblist = 0;
+	dblist->magic = 0;
+	ext2fs_free_mem(&dblist);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c
new file mode 100644
index 0000000..d0869c9
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/gen_bitmap.c
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * gen_bitmap.c --- Generic bitmap routines that used to be inlined.
+ *
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+					 __u32 bitno)
+{
+	if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+		ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno);
+		return 0;
+	}
+	return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+					   blk_t bitno)
+{
+	if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+		ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno);
+		return 0;
+	}
+	return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c
new file mode 100644
index 0000000..2bb1cc2
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/get_pathname.c
@@ -0,0 +1,156 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * get_pathname.c --- do directry/inode -> name translation
+ *
+ * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ *	ext2fs_get_pathname(fs, dir, ino, name)
+ *
+ *	This function translates takes two inode numbers into a
+ *	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 directory <dir>.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct get_pathname_struct {
+	ext2_ino_t	search_ino;
+	ext2_ino_t	parent;
+	char		*name;
+	errcode_t	errcode;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int get_pathname_proc(struct ext2_dir_entry *dirent,
+			     int	offset EXT2FS_ATTR((unused)),
+			     int	blocksize EXT2FS_ATTR((unused)),
+			     char	*buf EXT2FS_ATTR((unused)),
+			     void	*priv_data)
+{
+	struct get_pathname_struct	*gp;
+	errcode_t			retval;
+
+	gp = (struct get_pathname_struct *) priv_data;
+
+	if (((dirent->name_len & 0xFF) == 2) &&
+	    !strncmp(dirent->name, "..", 2))
+		gp->parent = dirent->inode;
+	if (dirent->inode == gp->search_ino) {
+		retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1,
+					&gp->name);
+		if (retval) {
+			gp->errcode = retval;
+			return DIRENT_ABORT;
+		}
+		strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF));
+		gp->name[dirent->name_len & 0xFF] = '\0';
+		return DIRENT_ABORT;
+	}
+	return 0;
+}
+
+static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir,
+					 ext2_ino_t ino, int maxdepth,
+					 char *buf, char **name)
+{
+	struct get_pathname_struct gp;
+	char	*parent_name, *ret;
+	errcode_t	retval;
+
+	if (dir == ino) {
+		retval = ext2fs_get_mem(2, name);
+		if (retval)
+			return retval;
+		strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : ".");
+		return 0;
+	}
+
+	if (!dir || (maxdepth < 0)) {
+		retval = ext2fs_get_mem(4, name);
+		if (retval)
+			return retval;
+		strcpy(*name, "...");
+		return 0;
+	}
+
+	gp.search_ino = ino;
+	gp.parent = 0;
+	gp.name = 0;
+	gp.errcode = 0;
+
+	retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp);
+	if (retval)
+		goto cleanup;
+	if (gp.errcode) {
+		retval = gp.errcode;
+		goto cleanup;
+	}
+
+	retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1,
+					 buf, &parent_name);
+	if (retval)
+		goto cleanup;
+	if (!ino) {
+		*name = parent_name;
+		return 0;
+	}
+
+	if (gp.name)
+		retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2,
+					&ret);
+	else
+		retval = ext2fs_get_mem(strlen(parent_name)+5, &ret);
+	if (retval)
+		goto cleanup;
+
+	ret[0] = 0;
+	if (parent_name[1])
+		strcat(ret, parent_name);
+	strcat(ret, "/");
+	if (gp.name)
+		strcat(ret, gp.name);
+	else
+		strcat(ret, "???");
+	*name = ret;
+	ext2fs_free_mem(&parent_name);
+	retval = 0;
+
+cleanup:
+	ext2fs_free_mem(&gp.name);
+	return retval;
+}
+
+errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
+			      char **name)
+{
+	char	*buf;
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_get_mem(fs->blocksize, &buf);
+	if (retval)
+		return retval;
+	if (dir == ino)
+		ino = 0;
+	retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name);
+	ext2fs_free_mem(&buf);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c
new file mode 100644
index 0000000..163ec65
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/getsectsize.c
@@ -0,0 +1,58 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getsectsize.c --- get the sector size of a device.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2003 VMware, Inc.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <sys/ioctl.h>
+#include <linux/fd.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET)
+#define BLKSSZGET  _IO(0x12,104)/* get block device sector size */
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Returns the number of blocks in a partition
+ */
+errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize)
+{
+	int	fd;
+
+#ifdef CONFIG_LFS
+	fd = open64(file, O_RDONLY);
+#else
+	fd = open(file, O_RDONLY);
+#endif
+	if (fd < 0)
+		return errno;
+
+#ifdef BLKSSZGET
+	if (ioctl(fd, BLKSSZGET, sectsize) >= 0) {
+		close(fd);
+		return 0;
+	}
+#endif
+	*sectsize = 0;
+	close(fd);
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
new file mode 100644
index 0000000..ee4bbb7
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/getsize.c
@@ -0,0 +1,291 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2003 VMware, Inc.
+ *
+ * Windows version of ext2fs_get_device_size by Chris Li, VMware.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96)	/* return device size */
+#endif
+
+#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)	/* return device size in bytes (u64 *arg) */
+#endif
+
+#ifdef APPLE_DARWIN
+#define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif /* APPLE_DARWIN */
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#if defined(__CYGWIN__) || defined(WIN32)
+#include <windows.h>
+#include <winioctl.h>
+
+#if (_WIN32_WINNT >= 0x0500)
+#define HAVE_GET_FILE_SIZE_EX 1
+#endif
+
+errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+				 blk_t *retblocks)
+{
+	HANDLE dev;
+	PARTITION_INFORMATION pi;
+	DISK_GEOMETRY gi;
+	DWORD retbytes;
+#ifdef HAVE_GET_FILE_SIZE_EX
+	LARGE_INTEGER filesize;
+#else
+	DWORD filesize;
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+	dev = CreateFile(file, GENERIC_READ,
+			 FILE_SHARE_READ | FILE_SHARE_WRITE ,
+			 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+	if (dev == INVALID_HANDLE_VALUE)
+		return EBADF;
+	if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO,
+			    &pi, sizeof(PARTITION_INFORMATION),
+			    &pi, sizeof(PARTITION_INFORMATION),
+			    &retbytes, NULL)) {
+
+		*retblocks = pi.PartitionLength.QuadPart / blocksize;
+
+	} else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY,
+				&gi, sizeof(DISK_GEOMETRY),
+				&gi, sizeof(DISK_GEOMETRY),
+				&retbytes, NULL)) {
+
+		*retblocks = gi.BytesPerSector *
+			     gi.SectorsPerTrack *
+			     gi.TracksPerCylinder *
+			     gi.Cylinders.QuadPart / blocksize;
+
+#ifdef HAVE_GET_FILE_SIZE_EX
+	} else if (GetFileSizeEx(dev, &filesize)) {
+		*retblocks = filesize.QuadPart / blocksize;
+	}
+#else
+	} else {
+		filesize = GetFileSize(dev, NULL);
+		if (INVALID_FILE_SIZE != filesize) {
+			*retblocks = filesize / blocksize;
+		}
+	}
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+	CloseHandle(dev);
+	return 0;
+}
+
+#else
+
+static int valid_offset (int fd, ext2_loff_t offset)
+{
+	char ch;
+
+	if (ext2fs_llseek (fd, offset, 0) < 0)
+		return 0;
+	if (read (fd, &ch, 1) < 1)
+		return 0;
+	return 1;
+}
+
+/*
+ * Returns the number of blocks in a partition
+ */
+errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+				 blk_t *retblocks)
+{
+	int	fd;
+	int valid_blkgetsize64 = 1;
+#ifdef __linux__
+	struct		utsname ut;
+#endif
+	unsigned long long size64;
+	unsigned long	size;
+	ext2_loff_t high, low;
+#ifdef FDGETPRM
+	struct floppy_struct this_floppy;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+	int part;
+	struct disklabel lab;
+	struct partition *pp;
+	char ch;
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+#ifdef CONFIG_LFS
+	fd = open64(file, O_RDONLY);
+#else
+	fd = open(file, O_RDONLY);
+#endif
+	if (fd < 0)
+		return errno;
+
+#ifdef DKIOCGETBLOCKCOUNT	/* For Apple Darwin */
+	if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
+		if ((sizeof(*retblocks) < sizeof(unsigned long long))
+		    && ((size64 / (blocksize / 512)) > 0xFFFFFFFF))
+			return EFBIG;
+		close(fd);
+		*retblocks = size64 / (blocksize / 512);
+		return 0;
+	}
+#endif
+
+#ifdef BLKGETSIZE64
+#ifdef __linux__
+	uname(&ut);
+	if ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+	     (ut.release[2] < '6') && (ut.release[3] == '.'))
+		valid_blkgetsize64 = 0;
+#endif
+	if (valid_blkgetsize64 &&
+	    ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
+		if ((sizeof(*retblocks) < sizeof(unsigned long long))
+		    && ((size64 / blocksize) > 0xFFFFFFFF))
+			return EFBIG;
+		close(fd);
+		*retblocks = size64 / blocksize;
+		return 0;
+	}
+#endif
+
+#ifdef BLKGETSIZE
+	if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+		close(fd);
+		*retblocks = size / (blocksize / 512);
+		return 0;
+	}
+#endif
+
+#ifdef FDGETPRM
+	if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+		close(fd);
+		*retblocks = this_floppy.size / (blocksize / 512);
+		return 0;
+	}
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+#if defined(DIOCGMEDIASIZE)
+	{
+	    off_t ms;
+	    u_int bs;
+	    if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) {
+		*retblocks = ms / blocksize;
+		return 0;
+	    }
+	}
+#elif defined(DIOCGDINFO)
+	/* old disklabel interface */
+	part = strlen(file) - 1;
+	if (part >= 0) {
+		ch = file[part];
+		if (isdigit(ch))
+			part = 0;
+		else if (ch >= 'a' && ch <= 'h')
+			part = ch - 'a';
+		else
+			part = -1;
+	}
+	if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+		pp = &lab.d_partitions[part];
+		if (pp->p_size) {
+			close(fd);
+			*retblocks = pp->p_size / (blocksize / 512);
+			return 0;
+		}
+	}
+#endif /* defined(DIOCG*) */
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+	/*
+	 * OK, we couldn't figure it out by using a specialized ioctl,
+	 * which is generally the best way.  So do binary search to
+	 * find the size of the partition.
+	 */
+	low = 0;
+	for (high = 1024; valid_offset (fd, high); high *= 2)
+		low = high;
+	while (low < high - 1)
+	{
+		const ext2_loff_t mid = (low + high) / 2;
+
+		if (valid_offset (fd, mid))
+			low = mid;
+		else
+			high = mid;
+	}
+	valid_offset (fd, 0);
+	close(fd);
+	size64 = low + 1;
+	if ((sizeof(*retblocks) < sizeof(unsigned long long))
+	    && ((size64 / blocksize) > 0xFFFFFFFF))
+		return EFBIG;
+	*retblocks = size64 / blocksize;
+	return 0;
+}
+
+#endif /* WIN32 */
+
+#ifdef DEBUG
+int main(int argc, char **argv)
+{
+	blk_t	blocks;
+	int	retval;
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s device\n", argv[0]);
+		exit(1);
+	}
+
+	retval = ext2fs_get_device_size(argv[1], 1024, &blocks);
+	if (retval) {
+		com_err(argv[0], retval,
+			"while calling ext2fs_get_device_size");
+		exit(1);
+	}
+	printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
+	exit(0);
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/icount.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/icount.c
new file mode 100644
index 0000000..7ab5f51
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/icount.c
@@ -0,0 +1,467 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * icount.c --- an efficient inode count abstraction
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * The data storage strategy used by icount relies on the observation
+ * that most inode counts are either zero (for non-allocated inodes),
+ * one (for most files), and only a few that are two or more
+ * (directories and files that are linked to more than one directory).
+ *
+ * Also, e2fsck tends to load the icount data sequentially.
+ *
+ * So, we use an inode bitmap to indicate which inodes have a count of
+ * one, and then use a sorted list to store the counts for inodes
+ * which are greater than one.
+ *
+ * We also use an optional bitmap to indicate which inodes are already
+ * in the sorted list, to speed up the use of this abstraction by
+ * e2fsck's pass 2.  Pass 2 increments inode counts as it finds them,
+ * so this extra bitmap avoids searching the sorted list to see if a
+ * particular inode is on the sorted list already.
+ */
+
+struct ext2_icount_el {
+	ext2_ino_t	ino;
+	__u16	count;
+};
+
+struct ext2_icount {
+	errcode_t		magic;
+	ext2fs_inode_bitmap	single;
+	ext2fs_inode_bitmap	multiple;
+	ext2_ino_t		count;
+	ext2_ino_t		size;
+	ext2_ino_t		num_inodes;
+	ext2_ino_t		cursor;
+	struct ext2_icount_el	*list;
+};
+
+void ext2fs_free_icount(ext2_icount_t icount)
+{
+	if (!icount)
+		return;
+
+	icount->magic = 0;
+	ext2fs_free_mem(&icount->list);
+	ext2fs_free_inode_bitmap(icount->single);
+	ext2fs_free_inode_bitmap(icount->multiple);
+	ext2fs_free_mem(&icount);
+}
+
+errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
+				ext2_icount_t hint, ext2_icount_t *ret)
+{
+	ext2_icount_t	icount;
+	errcode_t	retval;
+	size_t		bytes;
+	ext2_ino_t	i;
+
+	if (hint) {
+		EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
+		if (hint->size > size)
+			size = (size_t) hint->size;
+	}
+
+	retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
+	if (retval)
+		return retval;
+	memset(icount, 0, sizeof(struct ext2_icount));
+
+	retval = ext2fs_allocate_inode_bitmap(fs, 0,
+					      &icount->single);
+	if (retval)
+		goto errout;
+
+	if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
+		retval = ext2fs_allocate_inode_bitmap(fs, 0,
+						      &icount->multiple);
+		if (retval)
+			goto errout;
+	} else
+		icount->multiple = 0;
+
+	if (size) {
+		icount->size = size;
+	} else {
+		/*
+		 * Figure out how many special case inode counts we will
+		 * have.  We know we will need one for each directory;
+		 * we also need to reserve some extra room for file links
+		 */
+		retval = ext2fs_get_num_dirs(fs, &icount->size);
+		if (retval)
+			goto errout;
+		icount->size += fs->super->s_inodes_count / 50;
+	}
+
+	bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
+	retval = ext2fs_get_mem(bytes, &icount->list);
+	if (retval)
+		goto errout;
+	memset(icount->list, 0, bytes);
+
+	icount->magic = EXT2_ET_MAGIC_ICOUNT;
+	icount->count = 0;
+	icount->cursor = 0;
+	icount->num_inodes = fs->super->s_inodes_count;
+
+	/*
+	 * Populate the sorted list with those entries which were
+	 * found in the hint icount (since those are ones which will
+	 * likely need to be in the sorted list this time around).
+	 */
+	if (hint) {
+		for (i=0; i < hint->count; i++)
+			icount->list[i].ino = hint->list[i].ino;
+		icount->count = hint->count;
+	}
+
+	*ret = icount;
+	return 0;
+
+errout:
+	ext2fs_free_icount(icount);
+	return retval;
+}
+
+errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
+			       unsigned int size,
+			       ext2_icount_t *ret)
+{
+	return ext2fs_create_icount2(fs, flags, size, 0, ret);
+}
+
+/*
+ * insert_icount_el() --- Insert a new entry into the sorted list at a
+ *	specified position.
+ */
+static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
+					    ext2_ino_t ino, int pos)
+{
+	struct ext2_icount_el	*el;
+	errcode_t		retval;
+	ext2_ino_t			new_size = 0;
+	int			num;
+
+	if (icount->count >= icount->size) {
+		if (icount->count) {
+			new_size = icount->list[(unsigned)icount->count-1].ino;
+			new_size = (ext2_ino_t) (icount->count *
+				((float) icount->num_inodes / new_size));
+		}
+		if (new_size < (icount->size + 100))
+			new_size = icount->size + 100;
+		retval = ext2fs_resize_mem((size_t) icount->size *
+					   sizeof(struct ext2_icount_el),
+					   (size_t) new_size *
+					   sizeof(struct ext2_icount_el),
+					   &icount->list);
+		if (retval)
+			return 0;
+		icount->size = new_size;
+	}
+	num = (int) icount->count - pos;
+	if (num < 0)
+		return 0;	/* should never happen */
+	if (num) {
+		memmove(&icount->list[pos+1], &icount->list[pos],
+			sizeof(struct ext2_icount_el) * num);
+	}
+	icount->count++;
+	el = &icount->list[pos];
+	el->count = 0;
+	el->ino = ino;
+	return el;
+}
+
+/*
+ * get_icount_el() --- given an inode number, try to find icount
+ *	information in the sorted list.  If the create flag is set,
+ *	and we can't find an entry, create one in the sorted list.
+ */
+static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
+					    ext2_ino_t ino, int create)
+{
+	float	range;
+	int	low, high, mid;
+	ext2_ino_t	lowval, highval;
+
+	if (!icount || !icount->list)
+		return 0;
+
+	if (create && ((icount->count == 0) ||
+		       (ino > icount->list[(unsigned)icount->count-1].ino))) {
+		return insert_icount_el(icount, ino, (unsigned) icount->count);
+	}
+	if (icount->count == 0)
+		return 0;
+
+	if (icount->cursor >= icount->count)
+		icount->cursor = 0;
+	if (ino == icount->list[icount->cursor].ino)
+		return &icount->list[icount->cursor++];
+	low = 0;
+	high = (int) icount->count-1;
+	while (low <= high) {
+		if (low == high)
+			mid = low;
+		else {
+			/* Interpolate for efficiency */
+			lowval = icount->list[low].ino;
+			highval = icount->list[high].ino;
+
+			if (ino < lowval)
+				range = 0;
+			else if (ino > highval)
+				range = 1;
+			else
+				range = ((float) (ino - lowval)) /
+					(highval - lowval);
+			mid = low + ((int) (range * (high-low)));
+		}
+		if (ino == icount->list[mid].ino) {
+			icount->cursor = mid+1;
+			return &icount->list[mid];
+		}
+		if (ino < icount->list[mid].ino)
+			high = mid-1;
+		else
+			low = mid+1;
+	}
+	/*
+	 * If we need to create a new entry, it should be right at
+	 * low (where high will be left at low-1).
+	 */
+	if (create)
+		return insert_icount_el(icount, ino, low);
+	return 0;
+}
+
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
+{
+	errcode_t	ret = 0;
+	unsigned int	i;
+	const char *bad = "bad icount";
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (icount->count > icount->size) {
+		fprintf(out, "%s: count > size\n", bad);
+		return EXT2_ET_INVALID_ARGUMENT;
+	}
+	for (i=1; i < icount->count; i++) {
+		if (icount->list[i-1].ino >= icount->list[i].ino) {
+			fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
+				bad, i-1, icount->list[i-1].ino,
+				i, icount->list[i].ino);
+			ret = EXT2_ET_INVALID_ARGUMENT;
+		}
+	}
+	return ret;
+}
+
+errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
+{
+	struct ext2_icount_el	*el;
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (!ino || (ino > icount->num_inodes))
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+		*ret = 1;
+		return 0;
+	}
+	if (icount->multiple &&
+	    !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+		*ret = 0;
+		return 0;
+	}
+	el = get_icount_el(icount, ino, 0);
+	if (!el) {
+		*ret = 0;
+		return 0;
+	}
+	*ret = el->count;
+	return 0;
+}
+
+errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
+				  __u16 *ret)
+{
+	struct ext2_icount_el	*el;
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (!ino || (ino > icount->num_inodes))
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+		/*
+		 * If the existing count is 1, then we know there is
+		 * no entry in the list.
+		 */
+		el = get_icount_el(icount, ino, 1);
+		if (!el)
+			return EXT2_ET_NO_MEMORY;
+		ext2fs_unmark_inode_bitmap(icount->single, ino);
+		el->count = 2;
+	} else if (icount->multiple) {
+		/*
+		 * The count is either zero or greater than 1; if the
+		 * inode is set in icount->multiple, then there should
+		 * be an entry in the list, so find it using
+		 * get_icount_el().
+		 */
+		if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+			el = get_icount_el(icount, ino, 1);
+			if (!el)
+				return EXT2_ET_NO_MEMORY;
+			el->count++;
+		} else {
+			/*
+			 * The count was zero; mark the single bitmap
+			 * and return.
+			 */
+		zero_count:
+			ext2fs_mark_inode_bitmap(icount->single, ino);
+			if (ret)
+				*ret = 1;
+			return 0;
+		}
+	} else {
+		/*
+		 * The count is either zero or greater than 1; try to
+		 * find an entry in the list to determine which.
+		 */
+		el = get_icount_el(icount, ino, 0);
+		if (!el) {
+			/* No entry means the count was zero */
+			goto zero_count;
+		}
+		el = get_icount_el(icount, ino, 1);
+		if (!el)
+			return EXT2_ET_NO_MEMORY;
+		el->count++;
+	}
+	if (icount->multiple)
+		ext2fs_mark_inode_bitmap(icount->multiple, ino);
+	if (ret)
+		*ret = el->count;
+	return 0;
+}
+
+errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
+				  __u16 *ret)
+{
+	struct ext2_icount_el	*el;
+
+	if (!ino || (ino > icount->num_inodes))
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+		ext2fs_unmark_inode_bitmap(icount->single, ino);
+		if (icount->multiple)
+			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+		else {
+			el = get_icount_el(icount, ino, 0);
+			if (el)
+				el->count = 0;
+		}
+		if (ret)
+			*ret = 0;
+		return 0;
+	}
+
+	if (icount->multiple &&
+	    !ext2fs_test_inode_bitmap(icount->multiple, ino))
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	el = get_icount_el(icount, ino, 0);
+	if (!el || el->count == 0)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	el->count--;
+	if (el->count == 1)
+		ext2fs_mark_inode_bitmap(icount->single, ino);
+	if ((el->count == 0) && icount->multiple)
+		ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+
+	if (ret)
+		*ret = el->count;
+	return 0;
+}
+
+errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
+			      __u16 count)
+{
+	struct ext2_icount_el	*el;
+
+	if (!ino || (ino > icount->num_inodes))
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+	if (count == 1) {
+		ext2fs_mark_inode_bitmap(icount->single, ino);
+		if (icount->multiple)
+			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+		return 0;
+	}
+	if (count == 0) {
+		ext2fs_unmark_inode_bitmap(icount->single, ino);
+		if (icount->multiple) {
+			/*
+			 * If the icount->multiple bitmap is enabled,
+			 * we can just clear both bitmaps and we're done
+			 */
+			ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+		} else {
+			el = get_icount_el(icount, ino, 0);
+			if (el)
+				el->count = 0;
+		}
+		return 0;
+	}
+
+	/*
+	 * Get the icount element
+	 */
+	el = get_icount_el(icount, ino, 1);
+	if (!el)
+		return EXT2_ET_NO_MEMORY;
+	el->count = count;
+	ext2fs_unmark_inode_bitmap(icount->single, ino);
+	if (icount->multiple)
+		ext2fs_mark_inode_bitmap(icount->multiple, ino);
+	return 0;
+}
+
+ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
+{
+	if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
+		return 0;
+
+	return icount->size;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/imager.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/imager.c
new file mode 100644
index 0000000..0f9cfcf
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/imager.c
@@ -0,0 +1,377 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * image.c --- writes out the critical parts of the filesystem as a
+ *	flat file.
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library.  So sue me.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef HAVE_TYPE_SSIZE_T
+typedef int ssize_t;
+#endif
+
+/*
+ * This function returns 1 if the specified block is all zeros
+ */
+static int check_zero_block(char *buf, int blocksize)
+{
+	char	*cp = buf;
+	int	left = blocksize;
+
+	while (left > 0) {
+		if (*cp++)
+			return 0;
+		left--;
+	}
+	return 1;
+}
+
+/*
+ * Write the inode table out as a single block.
+ */
+#define BUF_BLOCKS	32
+
+errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
+{
+	unsigned int	group, left, c, d;
+	char		*buf, *cp;
+	blk_t		blk;
+	ssize_t		actual;
+	errcode_t	retval;
+
+	buf = xmalloc(fs->blocksize * BUF_BLOCKS);
+
+	for (group = 0; group < fs->group_desc_count; group++) {
+		blk = fs->group_desc[(unsigned)group].bg_inode_table;
+		if (!blk)
+			return EXT2_ET_MISSING_INODE_TABLE;
+		left = fs->inode_blocks_per_group;
+		while (left) {
+			c = BUF_BLOCKS;
+			if (c > left)
+				c = left;
+			retval = io_channel_read_blk(fs->io, blk, c, buf);
+			if (retval)
+				goto errout;
+			cp = buf;
+			while (c) {
+				if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
+					d = c;
+					goto skip_sparse;
+				}
+				/* Skip zero blocks */
+				if (check_zero_block(cp, fs->blocksize)) {
+					c--;
+					blk++;
+					left--;
+					cp += fs->blocksize;
+					lseek(fd, fs->blocksize, SEEK_CUR);
+					continue;
+				}
+				/* Find non-zero blocks */
+				for (d=1; d < c; d++) {
+					if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
+						break;
+				}
+			skip_sparse:
+				actual = write(fd, cp, fs->blocksize * d);
+				if (actual == -1) {
+					retval = errno;
+					goto errout;
+				}
+				if (actual != (ssize_t) (fs->blocksize * d)) {
+					retval = EXT2_ET_SHORT_WRITE;
+					goto errout;
+				}
+				blk += d;
+				left -= d;
+				cp += fs->blocksize * d;
+				c -= d;
+			}
+		}
+	}
+	retval = 0;
+
+errout:
+	free(buf);
+	return retval;
+}
+
+/*
+ * Read in the inode table and stuff it into place
+ */
+errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd,
+				  int flags EXT2FS_ATTR((unused)))
+{
+	unsigned int	group, c, left;
+	char		*buf;
+	blk_t		blk;
+	ssize_t		actual;
+	errcode_t	retval;
+
+	buf = xmalloc(fs->blocksize * BUF_BLOCKS);
+
+	for (group = 0; group < fs->group_desc_count; group++) {
+		blk = fs->group_desc[(unsigned)group].bg_inode_table;
+		if (!blk) {
+			retval = EXT2_ET_MISSING_INODE_TABLE;
+			goto errout;
+		}
+		left = fs->inode_blocks_per_group;
+		while (left) {
+			c = BUF_BLOCKS;
+			if (c > left)
+				c = left;
+			actual = read(fd, buf, fs->blocksize * c);
+			if (actual == -1) {
+				retval = errno;
+				goto errout;
+			}
+			if (actual != (ssize_t) (fs->blocksize * c)) {
+				retval = EXT2_ET_SHORT_READ;
+				goto errout;
+			}
+			retval = io_channel_write_blk(fs->io, blk, c, buf);
+			if (retval)
+				goto errout;
+
+			blk += c;
+			left -= c;
+		}
+	}
+	retval = ext2fs_flush_icache(fs);
+
+errout:
+	free(buf);
+	return retval;
+}
+
+/*
+ * Write out superblock and group descriptors
+ */
+errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd,
+				   int flags EXT2FS_ATTR((unused)))
+{
+	char		*buf, *cp;
+	ssize_t		actual;
+	errcode_t	retval;
+
+	buf = xmalloc(fs->blocksize);
+
+	/*
+	 * Write out the superblock
+	 */
+	memset(buf, 0, fs->blocksize);
+	memcpy(buf, fs->super, SUPERBLOCK_SIZE);
+	actual = write(fd, buf, fs->blocksize);
+	if (actual == -1) {
+		retval = errno;
+		goto errout;
+	}
+	if (actual != (ssize_t) fs->blocksize) {
+		retval = EXT2_ET_SHORT_WRITE;
+		goto errout;
+	}
+
+	/*
+	 * Now write out the block group descriptors
+	 */
+	cp = (char *) fs->group_desc;
+	actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
+	if (actual == -1) {
+		retval = errno;
+		goto errout;
+	}
+	if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
+		retval = EXT2_ET_SHORT_WRITE;
+		goto errout;
+	}
+
+	retval = 0;
+
+errout:
+	free(buf);
+	return retval;
+}
+
+/*
+ * Read the superblock and group descriptors and overwrite them.
+ */
+errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd,
+				  int flags EXT2FS_ATTR((unused)))
+{
+	char		*buf;
+	ssize_t		actual, size;
+	errcode_t	retval;
+
+	size = fs->blocksize * (fs->group_desc_count + 1);
+	buf = xmalloc(size);
+
+	/*
+	 * Read it all in.
+	 */
+	actual = read(fd, buf, size);
+	if (actual == -1) {
+		retval = errno;
+		goto errout;
+	}
+	if (actual != size) {
+		retval = EXT2_ET_SHORT_READ;
+		goto errout;
+	}
+
+	/*
+	 * Now copy in the superblock and group descriptors
+	 */
+	memcpy(fs->super, buf, SUPERBLOCK_SIZE);
+
+	memcpy(fs->group_desc, buf + fs->blocksize,
+	       fs->blocksize * fs->group_desc_count);
+
+	retval = 0;
+
+errout:
+	free(buf);
+	return retval;
+}
+
+/*
+ * Write the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
+{
+	char		*ptr;
+	int		c, size;
+	char		zero_buf[1024];
+	ssize_t		actual;
+	errcode_t	retval;
+
+	if (flags & IMAGER_FLAG_INODEMAP) {
+		if (!fs->inode_map) {
+			retval = ext2fs_read_inode_bitmap(fs);
+			if (retval)
+				return retval;
+		}
+		ptr = fs->inode_map->bitmap;
+		size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
+	} else {
+		if (!fs->block_map) {
+			retval = ext2fs_read_block_bitmap(fs);
+			if (retval)
+				return retval;
+		}
+		ptr = fs->block_map->bitmap;
+		size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+	}
+	size = size * fs->group_desc_count;
+
+	actual = write(fd, ptr, size);
+	if (actual == -1) {
+		retval = errno;
+		goto errout;
+	}
+	if (actual != size) {
+		retval = EXT2_ET_SHORT_WRITE;
+		goto errout;
+	}
+	size = size % fs->blocksize;
+	memset(zero_buf, 0, sizeof(zero_buf));
+	if (size) {
+		size = fs->blocksize - size;
+		while (size) {
+			c = size;
+			if (c > (int) sizeof(zero_buf))
+				c = sizeof(zero_buf);
+			actual = write(fd, zero_buf, c);
+			if (actual == -1) {
+				retval = errno;
+				goto errout;
+			}
+			if (actual != c) {
+				retval = EXT2_ET_SHORT_WRITE;
+				goto errout;
+			}
+			size -= c;
+		}
+	}
+	retval = 0;
+errout:
+	return retval;
+}
+
+
+/*
+ * Read the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
+{
+	char		*ptr, *buf = NULL;
+	int		size;
+	ssize_t		actual;
+	errcode_t	retval;
+
+	if (flags & IMAGER_FLAG_INODEMAP) {
+		if (!fs->inode_map) {
+			retval = ext2fs_read_inode_bitmap(fs);
+			if (retval)
+				return retval;
+		}
+		ptr = fs->inode_map->bitmap;
+		size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
+	} else {
+		if (!fs->block_map) {
+			retval = ext2fs_read_block_bitmap(fs);
+			if (retval)
+				return retval;
+		}
+		ptr = fs->block_map->bitmap;
+		size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+	}
+	size = size * fs->group_desc_count;
+
+	buf = xmalloc(size);
+
+	actual = read(fd, buf, size);
+	if (actual == -1) {
+		retval = errno;
+		goto errout;
+	}
+	if (actual != size) {
+		retval = EXT2_ET_SHORT_WRITE;
+		goto errout;
+	}
+	memcpy(ptr, buf, size);
+
+	retval = 0;
+errout:
+	free(buf);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c
new file mode 100644
index 0000000..a103830
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ind_block.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ind_block.c --- indirect block I/O routines
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ *	2001, 2002, 2003, 2004, 2005 by  Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+	errcode_t	retval;
+#if BB_BIG_ENDIAN
+	blk_t		*block_nr;
+	int		i;
+	int		limit = fs->blocksize >> 2;
+#endif
+
+	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
+	    (fs->io != fs->image_io))
+		memset(buf, 0, fs->blocksize);
+	else {
+		retval = io_channel_read_blk(fs->io, blk, 1, buf);
+		if (retval)
+			return retval;
+	}
+#if BB_BIG_ENDIAN
+	if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
+		block_nr = (blk_t *) buf;
+		for (i = 0; i < limit; i++, block_nr++)
+			*block_nr = ext2fs_swab32(*block_nr);
+	}
+#endif
+	return 0;
+}
+
+errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+#if BB_BIG_ENDIAN
+	blk_t		*block_nr;
+	int		i;
+	int		limit = fs->blocksize >> 2;
+#endif
+
+	if (fs->flags & EXT2_FLAG_IMAGE_FILE)
+		return 0;
+
+#if BB_BIG_ENDIAN
+	if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) {
+		block_nr = (blk_t *) buf;
+		for (i = 0; i < limit; i++, block_nr++)
+			*block_nr = ext2fs_swab32(*block_nr);
+	}
+#endif
+	return io_channel_write_blk(fs->io, blk, 1, buf);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c
new file mode 100644
index 0000000..ef1d343
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/initialize.c
@@ -0,0 +1,388 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * initialize.c --- initialize a filesystem handle given superblock
+ *	parameters.  Used by mke2fs when initializing a filesystem.
+ *
+ * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#if defined(__linux__)    &&	defined(EXT2_OS_LINUX)
+#define CREATOR_OS EXT2_OS_LINUX
+#else
+#if defined(__GNU__)     &&	defined(EXT2_OS_HURD)
+#define CREATOR_OS EXT2_OS_HURD
+#else
+#if defined(__FreeBSD__) &&	defined(EXT2_OS_FREEBSD)
+#define CREATOR_OS EXT2_OS_FREEBSD
+#else
+#if defined(LITES)	   &&	defined(EXT2_OS_LITES)
+#define CREATOR_OS EXT2_OS_LITES
+#else
+#define CREATOR_OS EXT2_OS_LINUX /* by default */
+#endif /* defined(LITES) && defined(EXT2_OS_LITES) */
+#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */
+#endif /* defined(__GNU__)     && defined(EXT2_OS_HURD) */
+#endif /* defined(__linux__)   && defined(EXT2_OS_LINUX) */
+
+/*
+ * Note we override the kernel include file's idea of what the default
+ * check interval (never) should be.  It's a good idea to check at
+ * least *occasionally*, specially since servers will never rarely get
+ * to reboot, since Linux is so robust these days.  :-)
+ *
+ * 180 days (six months) seems like a good value.
+ */
+#ifdef EXT2_DFL_CHECKINTERVAL
+#undef EXT2_DFL_CHECKINTERVAL
+#endif
+#define EXT2_DFL_CHECKINTERVAL (86400L * 180L)
+
+/*
+ * Calculate the number of GDT blocks to reserve for online filesystem growth.
+ * The absolute maximum number of GDT blocks we can reserve is determined by
+ * the number of block pointers that can fit into a single block.
+ */
+static int calc_reserved_gdt_blocks(ext2_filsys fs)
+{
+	struct ext2_super_block *sb = fs->super;
+	unsigned long bpg = sb->s_blocks_per_group;
+	unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc);
+	unsigned long max_blocks = 0xffffffff;
+	unsigned long rsv_groups;
+	int rsv_gdb;
+
+	/* We set it at 1024x the current filesystem size, or
+	 * the upper block count limit (2^32), whichever is lower.
+	 */
+	if (sb->s_blocks_count < max_blocks / 1024)
+		max_blocks = sb->s_blocks_count * 1024;
+	rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg;
+	rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks;
+	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",
+	       max_blocks, rsv_groups, rsv_gdb);
+#endif
+
+	return rsv_gdb;
+}
+
+errcode_t ext2fs_initialize(const char *name, int flags,
+			    struct ext2_super_block *param,
+			    io_manager manager, ext2_filsys *ret_fs)
+{
+	ext2_filsys	fs;
+	errcode_t	retval;
+	struct ext2_super_block *super;
+	int		frags_per_block;
+	unsigned int	rem;
+	unsigned int	overhead = 0;
+	blk_t		group_block;
+	unsigned int	ipg;
+	dgrp_t		i;
+	blk_t		numblocks;
+	int		rsv_gdt;
+	char		*buf;
+
+	if (!param || !param->s_blocks_count)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+	if (retval)
+		return retval;
+
+	memset(fs, 0, sizeof(struct struct_ext2_filsys));
+	fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
+	fs->flags = flags | EXT2_FLAG_RW;
+	fs->umask = 022;
+#ifdef WORDS_BIGENDIAN
+	fs->flags |= EXT2_FLAG_SWAP_BYTES;
+#endif
+	retval = manager->open(name, IO_FLAG_RW, &fs->io);
+	if (retval)
+		goto cleanup;
+	fs->image_io = fs->io;
+	fs->io->app_data = fs;
+	retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
+	if (retval)
+		goto cleanup;
+
+	strcpy(fs->device_name, name);
+	retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super);
+	if (retval)
+		goto cleanup;
+	fs->super = super;
+
+	memset(super, 0, SUPERBLOCK_SIZE);
+
+#define set_field(field, default) (super->field = param->field ? \
+				   param->field : (default))
+
+	super->s_magic = EXT2_SUPER_MAGIC;
+	super->s_state = EXT2_VALID_FS;
+
+	set_field(s_log_block_size, 0);	/* default blocksize: 1024 bytes */
+	set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */
+	set_field(s_first_data_block, super->s_log_block_size ? 0 : 1);
+	set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT);
+	set_field(s_errors, EXT2_ERRORS_DEFAULT);
+	set_field(s_feature_compat, 0);
+	set_field(s_feature_incompat, 0);
+	set_field(s_feature_ro_compat, 0);
+	set_field(s_first_meta_bg, 0);
+	if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
+		retval = EXT2_ET_UNSUPP_FEATURE;
+		goto cleanup;
+	}
+	if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+		retval = EXT2_ET_RO_UNSUPP_FEATURE;
+		goto cleanup;
+	}
+
+	set_field(s_rev_level, EXT2_GOOD_OLD_REV);
+	if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
+		set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
+		set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
+	}
+
+	set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL);
+	super->s_mkfs_time = super->s_lastcheck = time(NULL);
+
+	super->s_creator_os = CREATOR_OS;
+
+	fs->blocksize = EXT2_BLOCK_SIZE(super);
+	fs->fragsize = EXT2_FRAG_SIZE(super);
+	frags_per_block = fs->blocksize / fs->fragsize;
+
+	/* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */
+	set_field(s_blocks_per_group, fs->blocksize * 8);
+	if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super))
+		super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super);
+	super->s_frags_per_group = super->s_blocks_per_group * frags_per_block;
+
+	super->s_blocks_count = param->s_blocks_count;
+	super->s_r_blocks_count = param->s_r_blocks_count;
+	if (super->s_r_blocks_count >= param->s_blocks_count) {
+		retval = EXT2_ET_INVALID_ARGUMENT;
+		goto cleanup;
+	}
+
+	/*
+	 * If we're creating an external journal device, we don't need
+	 * to bother with the rest.
+	 */
+	if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+		fs->group_desc_count = 0;
+		ext2fs_mark_super_dirty(fs);
+		*ret_fs = fs;
+		return 0;
+	}
+
+retry:
+	fs->group_desc_count = (super->s_blocks_count -
+				super->s_first_data_block +
+				EXT2_BLOCKS_PER_GROUP(super) - 1)
+		/ EXT2_BLOCKS_PER_GROUP(super);
+	if (fs->group_desc_count == 0) {
+		retval = EXT2_ET_TOOSMALL;
+		goto cleanup;
+	}
+	fs->desc_blocks = (fs->group_desc_count +
+			   EXT2_DESC_PER_BLOCK(super) - 1)
+		/ EXT2_DESC_PER_BLOCK(super);
+
+	i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize;
+	set_field(s_inodes_count, super->s_blocks_count / i);
+
+	/*
+	 * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so
+	 * that we have enough inodes for the filesystem(!)
+	 */
+	if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1)
+		super->s_inodes_count = EXT2_FIRST_INODE(super)+1;
+
+	/*
+	 * There should be at least as many inodes as the user
+	 * requested.  Figure out how many inodes per group that
+	 * should be.  But make sure that we don't allocate more than
+	 * one bitmap's worth of inodes each group.
+	 */
+	ipg = (super->s_inodes_count + fs->group_desc_count - 1) /
+		fs->group_desc_count;
+	if (ipg > fs->blocksize * 8) {
+		if (super->s_blocks_per_group >= 256) {
+			/* Try again with slightly different parameters */
+			super->s_blocks_per_group -= 8;
+			super->s_blocks_count = param->s_blocks_count;
+			super->s_frags_per_group = super->s_blocks_per_group *
+				frags_per_block;
+			goto retry;
+		} else
+			return EXT2_ET_TOO_MANY_INODES;
+	}
+
+	if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super))
+		ipg = EXT2_MAX_INODES_PER_GROUP(super);
+
+	super->s_inodes_per_group = ipg;
+	if (super->s_inodes_count > ipg * fs->group_desc_count)
+		super->s_inodes_count = ipg * fs->group_desc_count;
+
+	/*
+	 * Make sure the number of inodes per group completely fills
+	 * the inode table blocks in the descriptor.  If not, add some
+	 * additional inodes/group.  Waste not, want not...
+	 */
+	fs->inode_blocks_per_group = (((super->s_inodes_per_group *
+					EXT2_INODE_SIZE(super)) +
+				       EXT2_BLOCK_SIZE(super) - 1) /
+				      EXT2_BLOCK_SIZE(super));
+	super->s_inodes_per_group = ((fs->inode_blocks_per_group *
+				      EXT2_BLOCK_SIZE(super)) /
+				     EXT2_INODE_SIZE(super));
+	/*
+	 * Finally, make sure the number of inodes per group is a
+	 * multiple of 8.  This is needed to simplify the bitmap
+	 * splicing code.
+	 */
+	super->s_inodes_per_group &= ~7;
+	fs->inode_blocks_per_group = (((super->s_inodes_per_group *
+					EXT2_INODE_SIZE(super)) +
+				       EXT2_BLOCK_SIZE(super) - 1) /
+				      EXT2_BLOCK_SIZE(super));
+
+	/*
+	 * adjust inode count to reflect the adjusted inodes_per_group
+	 */
+	super->s_inodes_count = super->s_inodes_per_group *
+		fs->group_desc_count;
+	super->s_free_inodes_count = super->s_inodes_count;
+
+	/*
+	 * check the number of reserved group descriptor table blocks
+	 */
+	if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE)
+		rsv_gdt = calc_reserved_gdt_blocks(fs);
+	else
+		rsv_gdt = 0;
+	set_field(s_reserved_gdt_blocks, rsv_gdt);
+	if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) {
+		retval = EXT2_ET_RES_GDT_BLOCKS;
+		goto cleanup;
+	}
+
+	/*
+	 * Overhead is the number of bookkeeping blocks per group.  It
+	 * includes the superblock backup, the group descriptor
+	 * backups, the inode bitmap, the block bitmap, and the inode
+	 * table.
+	 */
+
+	overhead = (int) (2 + fs->inode_blocks_per_group);
+
+	if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1))
+		overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks;
+
+	/* This can only happen if the user requested too many inodes */
+	if (overhead > super->s_blocks_per_group)
+		return EXT2_ET_TOO_MANY_INODES;
+
+	/*
+	 * See if the last group is big enough to support the
+	 * necessary data structures.  If not, we need to get rid of
+	 * it.
+	 */
+	rem = ((super->s_blocks_count - super->s_first_data_block) %
+	       super->s_blocks_per_group);
+	if ((fs->group_desc_count == 1) && rem && (rem < overhead))
+		return EXT2_ET_TOOSMALL;
+	if (rem && (rem < overhead+50)) {
+		super->s_blocks_count -= rem;
+		goto retry;
+	}
+
+	/*
+	 * At this point we know how big the filesystem will be.  So
+	 * we can do any and all allocations that depend on the block
+	 * count.
+	 */
+
+	retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
+	if (retval)
+		goto cleanup;
+
+	sprintf(buf, "block bitmap for %s", fs->device_name);
+	retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
+	if (retval)
+		goto cleanup;
+
+	sprintf(buf, "inode bitmap for %s", fs->device_name);
+	retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
+	if (retval)
+		goto cleanup;
+
+	ext2fs_free_mem(&buf);
+
+	retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+				&fs->group_desc);
+	if (retval)
+		goto cleanup;
+
+	memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize);
+
+	/*
+	 * Reserve the superblock and group descriptors for each
+	 * group, and fill in the correct group statistics for group.
+	 * Note that although the block bitmap, inode bitmap, and
+	 * inode table have not been allocated (and in fact won't be
+	 * by this routine), they are accounted for nevertheless.
+	 */
+	group_block = super->s_first_data_block;
+	super->s_free_blocks_count = 0;
+	for (i = 0; i < fs->group_desc_count; i++) {
+		numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
+
+		super->s_free_blocks_count += numblocks;
+		fs->group_desc[i].bg_free_blocks_count = numblocks;
+		fs->group_desc[i].bg_free_inodes_count =
+			fs->super->s_inodes_per_group;
+		fs->group_desc[i].bg_used_dirs_count = 0;
+
+		group_block += super->s_blocks_per_group;
+	}
+
+	ext2fs_mark_super_dirty(fs);
+	ext2fs_mark_bb_dirty(fs);
+	ext2fs_mark_ib_dirty(fs);
+
+	io_channel_set_blksize(fs->io, fs->blocksize);
+
+	*ret_fs = fs;
+	return 0;
+cleanup:
+	ext2fs_free(fs);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inline.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inline.c
new file mode 100644
index 0000000..7457b93
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inline.c
@@ -0,0 +1,32 @@
+/* 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.
+ *
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#define INCLUDE_INLINE_FUNCS
+#include "ext2fs.h"
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inode.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inode.c
new file mode 100644
index 0000000..7a1d5c9
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inode.c
@@ -0,0 +1,766 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * inode.c --- utility routines to read and write inodes
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+#include "e2image.h"
+
+struct ext2_struct_inode_scan {
+	errcode_t		magic;
+	ext2_filsys		fs;
+	ext2_ino_t		current_inode;
+	blk_t			current_block;
+	dgrp_t			current_group;
+	ext2_ino_t		inodes_left;
+	blk_t			blocks_left;
+	dgrp_t			groups_left;
+	blk_t			inode_buffer_blocks;
+	char *			inode_buffer;
+	int			inode_size;
+	char *			ptr;
+	int			bytes_left;
+	char			*temp_buffer;
+	errcode_t		(*done_group)(ext2_filsys fs,
+					      dgrp_t group,
+					      void * priv_data);
+	void *			done_group_data;
+	int			bad_block_ptr;
+	int			scan_flags;
+	int			reserved[6];
+};
+
+/*
+ * This routine flushes the icache, if it exists.
+ */
+errcode_t ext2fs_flush_icache(ext2_filsys fs)
+{
+	int	i;
+
+	if (!fs->icache)
+		return 0;
+
+	for (i=0; i < fs->icache->cache_size; i++)
+		fs->icache->cache[i].ino = 0;
+
+	fs->icache->buffer_blk = 0;
+	return 0;
+}
+
+static errcode_t create_icache(ext2_filsys fs)
+{
+	errcode_t	retval;
+
+	if (fs->icache)
+		return 0;
+	retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache);
+	if (retval)
+		return retval;
+
+	memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
+	retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
+	if (retval) {
+		ext2fs_free_mem(&fs->icache);
+		return retval;
+	}
+	fs->icache->buffer_blk = 0;
+	fs->icache->cache_last = -1;
+	fs->icache->cache_size = 4;
+	fs->icache->refcount = 1;
+	retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
+				* fs->icache->cache_size,
+				&fs->icache->cache);
+	if (retval) {
+		ext2fs_free_mem(&fs->icache->buffer);
+		ext2fs_free_mem(&fs->icache);
+		return retval;
+	}
+	ext2fs_flush_icache(fs);
+	return 0;
+}
+
+errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
+				 ext2_inode_scan *ret_scan)
+{
+	ext2_inode_scan	scan;
+	errcode_t	retval;
+	errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	/*
+	 * If fs->badblocks isn't set, then set it --- since the inode
+	 * scanning functions require it.
+	 */
+	if (fs->badblocks == 0) {
+		/*
+		 * Temporarly save fs->get_blocks and set it to zero,
+		 * for compatibility with old e2fsck's.
+		 */
+		save_get_blocks = fs->get_blocks;
+		fs->get_blocks = 0;
+		retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
+		if (retval) {
+			ext2fs_badblocks_list_free(fs->badblocks);
+			fs->badblocks = 0;
+		}
+		fs->get_blocks = save_get_blocks;
+	}
+
+	retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
+	if (retval)
+		return retval;
+	memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
+
+	scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
+	scan->fs = fs;
+	scan->inode_size = EXT2_INODE_SIZE(fs->super);
+	scan->bytes_left = 0;
+	scan->current_group = 0;
+	scan->groups_left = fs->group_desc_count - 1;
+	scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
+	scan->current_block = scan->fs->
+		group_desc[scan->current_group].bg_inode_table;
+	scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+	scan->blocks_left = scan->fs->inode_blocks_per_group;
+	retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks *
+					  fs->blocksize),
+				&scan->inode_buffer);
+	scan->done_group = 0;
+	scan->done_group_data = 0;
+	scan->bad_block_ptr = 0;
+	if (retval) {
+		ext2fs_free_mem(&scan);
+		return retval;
+	}
+	retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer);
+	if (retval) {
+		ext2fs_free_mem(&scan->inode_buffer);
+		ext2fs_free_mem(&scan);
+		return retval;
+	}
+	if (scan->fs->badblocks && scan->fs->badblocks->num)
+		scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
+	*ret_scan = scan;
+	return 0;
+}
+
+void ext2fs_close_inode_scan(ext2_inode_scan scan)
+{
+	if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+		return;
+
+	ext2fs_free_mem(&scan->inode_buffer);
+	scan->inode_buffer = NULL;
+	ext2fs_free_mem(&scan->temp_buffer);
+	scan->temp_buffer = NULL;
+	ext2fs_free_mem(&scan);
+}
+
+void ext2fs_set_inode_callback(ext2_inode_scan scan,
+			       errcode_t (*done_group)(ext2_filsys fs,
+						       dgrp_t group,
+						       void * priv_data),
+			       void *done_group_data)
+{
+	if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+		return;
+
+	scan->done_group = done_group;
+	scan->done_group_data = done_group_data;
+}
+
+int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+			    int clear_flags)
+{
+	int	old_flags;
+
+	if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+		return 0;
+
+	old_flags = scan->scan_flags;
+	scan->scan_flags &= ~clear_flags;
+	scan->scan_flags |= set_flags;
+	return old_flags;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * get ready to read in a new blockgroup.
+ */
+static errcode_t get_next_blockgroup(ext2_inode_scan scan)
+{
+	scan->current_group++;
+	scan->groups_left--;
+
+	scan->current_block = scan->fs->
+		group_desc[scan->current_group].bg_inode_table;
+
+	scan->current_inode = scan->current_group *
+		EXT2_INODES_PER_GROUP(scan->fs->super);
+
+	scan->bytes_left = 0;
+	scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+	scan->blocks_left = scan->fs->inode_blocks_per_group;
+	return 0;
+}
+
+errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+					    int	group)
+{
+	scan->current_group = group - 1;
+	scan->groups_left = scan->fs->group_desc_count - group;
+	return get_next_blockgroup(scan);
+}
+
+/*
+ * This function is called by get_next_blocks() to check for bad
+ * blocks in the inode table.
+ *
+ * This function assumes that badblocks_list->list is sorted in
+ * increasing order.
+ */
+static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
+					    blk_t *num_blocks)
+{
+	blk_t	blk = scan->current_block;
+	badblocks_list	bb = scan->fs->badblocks;
+
+	/*
+	 * If the inode table is missing, then obviously there are no
+	 * bad blocks.  :-)
+	 */
+	if (blk == 0)
+		return 0;
+
+	/*
+	 * If the current block is greater than the bad block listed
+	 * in the bad block list, then advance the pointer until this
+	 * is no longer the case.  If we run out of bad blocks, then
+	 * we don't need to do any more checking!
+	 */
+	while (blk > bb->list[scan->bad_block_ptr]) {
+		if (++scan->bad_block_ptr >= bb->num) {
+			scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+			return 0;
+		}
+	}
+
+	/*
+	 * If the current block is equal to the bad block listed in
+	 * the bad block list, then handle that one block specially.
+	 * (We could try to handle runs of bad blocks, but that
+	 * only increases CPU efficiency by a small amount, at the
+	 * expense of a huge expense of code complexity, and for an
+	 * uncommon case at that.)
+	 */
+	if (blk == bb->list[scan->bad_block_ptr]) {
+		scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
+		*num_blocks = 1;
+		if (++scan->bad_block_ptr >= bb->num)
+			scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+		return 0;
+	}
+
+	/*
+	 * If there is a bad block in the range that we're about to
+	 * read in, adjust the number of blocks to read so that we we
+	 * don't read in the bad block.  (Then the next block to read
+	 * will be the bad block, which is handled in the above case.)
+	 */
+	if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
+		*num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
+
+	return 0;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * read in more blocks from the current blockgroup's inode table.
+ */
+static errcode_t get_next_blocks(ext2_inode_scan scan)
+{
+	blk_t		num_blocks;
+	errcode_t	retval;
+
+	/*
+	 * Figure out how many blocks to read; we read at most
+	 * inode_buffer_blocks, and perhaps less if there aren't that
+	 * many blocks left to read.
+	 */
+	num_blocks = scan->inode_buffer_blocks;
+	if (num_blocks > scan->blocks_left)
+		num_blocks = scan->blocks_left;
+
+	/*
+	 * If the past block "read" was a bad block, then mark the
+	 * left-over extra bytes as also being bad.
+	 */
+	if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
+		if (scan->bytes_left)
+			scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
+		scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
+	}
+
+	/*
+	 * Do inode bad block processing, if necessary.
+	 */
+	if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
+		retval = check_for_inode_bad_blocks(scan, &num_blocks);
+		if (retval)
+			return retval;
+	}
+
+	if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
+	    (scan->current_block == 0)) {
+		memset(scan->inode_buffer, 0,
+		       (size_t) num_blocks * scan->fs->blocksize);
+	} else {
+		retval = io_channel_read_blk(scan->fs->io,
+					     scan->current_block,
+					     (int) num_blocks,
+					     scan->inode_buffer);
+		if (retval)
+			return EXT2_ET_NEXT_INODE_READ;
+	}
+	scan->ptr = scan->inode_buffer;
+	scan->bytes_left = num_blocks * scan->fs->blocksize;
+
+	scan->blocks_left -= num_blocks;
+	if (scan->current_block)
+		scan->current_block += num_blocks;
+	return 0;
+}
+
+errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
+				     struct ext2_inode *inode, int bufsize)
+{
+	errcode_t	retval;
+	int		extra_bytes = 0;
+
+	EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
+
+	/*
+	 * Do we need to start reading a new block group?
+	 */
+	if (scan->inodes_left <= 0) {
+	force_new_group:
+		if (scan->done_group) {
+			retval = (scan->done_group)
+				(scan->fs, scan->current_group,
+				 scan->done_group_data);
+			if (retval)
+				return retval;
+		}
+		if (scan->groups_left <= 0) {
+			*ino = 0;
+			return 0;
+		}
+		retval = get_next_blockgroup(scan);
+		if (retval)
+			return retval;
+	}
+	/*
+	 * This is done outside the above if statement so that the
+	 * check can be done for block group #0.
+	 */
+	if (scan->current_block == 0) {
+		if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
+			goto force_new_group;
+		} else
+			return EXT2_ET_MISSING_INODE_TABLE;
+	}
+
+
+	/*
+	 * Have we run out of space in the inode buffer?  If so, we
+	 * need to read in more blocks.
+	 */
+	if (scan->bytes_left < scan->inode_size) {
+		memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
+		extra_bytes = scan->bytes_left;
+
+		retval = get_next_blocks(scan);
+		if (retval)
+			return retval;
+#if 0
+		/*
+		 * XXX test  Need check for used inode somehow.
+		 * (Note: this is hard.)
+		 */
+		if (is_empty_scan(scan))
+			goto force_new_group;
+#endif
+	}
+
+	retval = 0;
+	if (extra_bytes) {
+		memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
+		       scan->inode_size - extra_bytes);
+		scan->ptr += scan->inode_size - extra_bytes;
+		scan->bytes_left -= scan->inode_size - extra_bytes;
+
+#if BB_BIG_ENDIAN
+		if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+		    (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+			ext2fs_swap_inode_full(scan->fs,
+				(struct ext2_inode_large *) inode,
+				(struct ext2_inode_large *) scan->temp_buffer,
+				0, bufsize);
+		else
+#endif
+			*inode = *((struct ext2_inode *) scan->temp_buffer);
+		if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
+			retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
+		scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
+	} else {
+#if BB_BIG_ENDIAN
+		if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+		    (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+			ext2fs_swap_inode_full(scan->fs,
+				(struct ext2_inode_large *) inode,
+				(struct ext2_inode_large *) scan->ptr,
+				0, bufsize);
+		else
+#endif
+			memcpy(inode, scan->ptr, bufsize);
+		scan->ptr += scan->inode_size;
+		scan->bytes_left -= scan->inode_size;
+		if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
+			retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
+	}
+
+	scan->inodes_left--;
+	scan->current_inode++;
+	*ino = scan->current_inode;
+	return retval;
+}
+
+errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
+				struct ext2_inode *inode)
+{
+	return ext2fs_get_next_inode_full(scan, ino, inode,
+						sizeof(struct ext2_inode));
+}
+
+/*
+ * Functions to read and write a single inode.
+ */
+errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
+				 struct ext2_inode * inode, int bufsize)
+{
+	unsigned long	group, block, block_nr, offset;
+	char		*ptr;
+	errcode_t	retval;
+	int		clen, i, inodes_per_block, length;
+	io_channel	io;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	/* Check to see if user has an override function */
+	if (fs->read_inode) {
+		retval = (fs->read_inode)(fs, ino, inode);
+		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+			return retval;
+	}
+	/* Create inode cache if not present */
+	if (!fs->icache) {
+		retval = create_icache(fs);
+		if (retval)
+			return retval;
+	}
+	/* Check to see if it's in the inode cache */
+	if (bufsize == sizeof(struct ext2_inode)) {
+		/* only old good inode can be retrieve from the cache */
+		for (i=0; i < fs->icache->cache_size; i++) {
+			if (fs->icache->cache[i].ino == ino) {
+				*inode = fs->icache->cache[i].inode;
+				return 0;
+			}
+		}
+	}
+	if ((ino == 0) || (ino > fs->super->s_inodes_count))
+		return EXT2_ET_BAD_INODE_NUM;
+	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+		inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
+		block_nr = fs->image_header->offset_inode / fs->blocksize;
+		block_nr += (ino - 1) / inodes_per_block;
+		offset = ((ino - 1) % inodes_per_block) *
+			EXT2_INODE_SIZE(fs->super);
+		io = fs->image_io;
+	} else {
+		group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
+		offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+			EXT2_INODE_SIZE(fs->super);
+		block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+		if (!fs->group_desc[(unsigned)group].bg_inode_table)
+			return EXT2_ET_MISSING_INODE_TABLE;
+		block_nr = fs->group_desc[(unsigned)group].bg_inode_table +
+			block;
+		io = fs->io;
+	}
+	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+	length = EXT2_INODE_SIZE(fs->super);
+	if (bufsize < length)
+		length = bufsize;
+
+	ptr = (char *) inode;
+	while (length) {
+		clen = length;
+		if ((offset + length) > fs->blocksize)
+			clen = fs->blocksize - offset;
+
+		if (block_nr != fs->icache->buffer_blk) {
+			retval = io_channel_read_blk(io, block_nr, 1,
+						     fs->icache->buffer);
+			if (retval)
+				return retval;
+			fs->icache->buffer_blk = block_nr;
+		}
+
+		memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
+		       clen);
+
+		offset = 0;
+		length -= clen;
+		ptr += clen;
+		block_nr++;
+	}
+
+#if BB_BIG_ENDIAN
+	if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+	    (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+		ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode,
+				       (struct ext2_inode_large *) inode,
+				       0, length);
+#endif
+
+	/* Update the inode cache */
+	fs->icache->cache_last = (fs->icache->cache_last + 1) %
+		fs->icache->cache_size;
+	fs->icache->cache[fs->icache->cache_last].ino = ino;
+	fs->icache->cache[fs->icache->cache_last].inode = *inode;
+
+	return 0;
+}
+
+errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
+			    struct ext2_inode * inode)
+{
+	return ext2fs_read_inode_full(fs, ino, inode,
+					sizeof(struct ext2_inode));
+}
+
+errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
+				  struct ext2_inode * inode, int bufsize)
+{
+	unsigned long group, block, block_nr, offset;
+	errcode_t retval = 0;
+	struct ext2_inode_large temp_inode, *w_inode;
+	char *ptr;
+	int clen, i, length;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	/* Check to see if user provided an override function */
+	if (fs->write_inode) {
+		retval = (fs->write_inode)(fs, ino, inode);
+		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+			return retval;
+	}
+
+	/* Check to see if the inode cache needs to be updated */
+	if (fs->icache) {
+		for (i=0; i < fs->icache->cache_size; i++) {
+			if (fs->icache->cache[i].ino == ino) {
+				fs->icache->cache[i].inode = *inode;
+				break;
+			}
+		}
+	} else {
+		retval = create_icache(fs);
+		if (retval)
+			return retval;
+	}
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+
+	if ((ino == 0) || (ino > fs->super->s_inodes_count))
+		return EXT2_ET_BAD_INODE_NUM;
+
+	length = bufsize;
+	if (length < EXT2_INODE_SIZE(fs->super))
+		length = EXT2_INODE_SIZE(fs->super);
+
+	if (length > (int) sizeof(struct ext2_inode_large)) {
+		w_inode = xmalloc(length);
+	} else
+		w_inode = &temp_inode;
+	memset(w_inode, 0, length);
+
+#if BB_BIG_ENDIAN
+	if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+	    (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+		ext2fs_swap_inode_full(fs, w_inode,
+				       (struct ext2_inode_large *) inode,
+				       1, bufsize);
+	else
+#endif
+		memcpy(w_inode, inode, bufsize);
+
+	group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
+	offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+		EXT2_INODE_SIZE(fs->super);
+	block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+	if (!fs->group_desc[(unsigned) group].bg_inode_table)
+		return EXT2_ET_MISSING_INODE_TABLE;
+	block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
+
+	offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+	length = EXT2_INODE_SIZE(fs->super);
+	if (length > bufsize)
+		length = bufsize;
+
+	ptr = (char *) w_inode;
+
+	while (length) {
+		clen = length;
+		if ((offset + length) > fs->blocksize)
+			clen = fs->blocksize - offset;
+
+		if (fs->icache->buffer_blk != block_nr) {
+			retval = io_channel_read_blk(fs->io, block_nr, 1,
+						     fs->icache->buffer);
+			if (retval)
+				goto errout;
+			fs->icache->buffer_blk = block_nr;
+		}
+
+
+		memcpy((char *) fs->icache->buffer + (unsigned) offset,
+		       ptr, clen);
+
+		retval = io_channel_write_blk(fs->io, block_nr, 1,
+					      fs->icache->buffer);
+		if (retval)
+			goto errout;
+
+		offset = 0;
+		ptr += clen;
+		length -= clen;
+		block_nr++;
+	}
+
+	fs->flags |= EXT2_FLAG_CHANGED;
+errout:
+	if (w_inode && w_inode != &temp_inode)
+		free(w_inode);
+	return retval;
+}
+
+errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
+			     struct ext2_inode *inode)
+{
+	return ext2fs_write_inode_full(fs, ino, inode,
+				       sizeof(struct ext2_inode));
+}
+
+/*
+ * This function should be called when writing a new inode.  It makes
+ * sure that extra part of large inodes is initialized properly.
+ */
+errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
+				 struct ext2_inode *inode)
+{
+	struct ext2_inode	*buf;
+	int			size = EXT2_INODE_SIZE(fs->super);
+	struct ext2_inode_large	*large_inode;
+
+	if (size == sizeof(struct ext2_inode))
+		return ext2fs_write_inode_full(fs, ino, inode,
+					       sizeof(struct ext2_inode));
+
+	buf = xmalloc(size);
+
+	memset(buf, 0, size);
+	*buf = *inode;
+
+	large_inode = (struct ext2_inode_large *) buf;
+	large_inode->i_extra_isize = sizeof(struct ext2_inode_large) -
+		EXT2_GOOD_OLD_INODE_SIZE;
+
+	return ext2fs_write_inode_full(fs, ino, buf, size);
+}
+
+
+errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
+{
+	struct ext2_inode	inode;
+	int			i;
+	errcode_t		retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (ino > fs->super->s_inodes_count)
+		return EXT2_ET_BAD_INODE_NUM;
+
+	if (fs->get_blocks) {
+		if (!(*fs->get_blocks)(fs, ino, blocks))
+			return 0;
+	}
+	retval = ext2fs_read_inode(fs, ino, &inode);
+	if (retval)
+		return retval;
+	for (i=0; i < EXT2_N_BLOCKS; i++)
+		blocks[i] = inode.i_block[i];
+	return 0;
+}
+
+errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
+{
+	struct	ext2_inode	inode;
+	errcode_t		retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (ino > fs->super->s_inodes_count)
+		return EXT2_ET_BAD_INODE_NUM;
+
+	if (fs->check_directory) {
+		retval = (fs->check_directory)(fs, ino);
+		if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+			return retval;
+	}
+	retval = ext2fs_read_inode(fs, ino, &inode);
+	if (retval)
+		return retval;
+	if (!LINUX_S_ISDIR(inode.i_mode))
+		return EXT2_ET_NO_DIRECTORY;
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c
new file mode 100644
index 0000000..b861d5f
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/inode_io.c
@@ -0,0 +1,270 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * inode_io.c --- This is allows an inode in an ext2 filesystem image
+ *	to be accessed via the I/O manager interface.
+ *
+ * Copyright (C) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+	  if ((struct)->magic != (code)) return (code)
+
+struct inode_private_data {
+	int				magic;
+	char				name[32];
+	ext2_file_t			file;
+	ext2_filsys			fs;
+	ext2_ino_t			ino;
+	struct ext2_inode		inode;
+	int				flags;
+	struct inode_private_data	*next;
+};
+
+#define CHANNEL_HAS_INODE	0x8000
+
+static struct inode_private_data *top_intern;
+static int ino_unique = 0;
+
+static errcode_t inode_open(const char *name, int flags, io_channel *channel);
+static errcode_t inode_close(io_channel channel);
+static errcode_t inode_set_blksize(io_channel channel, int blksize);
+static errcode_t inode_read_blk(io_channel channel, unsigned long block,
+			       int count, void *data);
+static errcode_t inode_write_blk(io_channel channel, unsigned long block,
+				int count, const void *data);
+static errcode_t inode_flush(io_channel channel);
+static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
+				int size, const void *data);
+
+static struct struct_io_manager struct_inode_manager = {
+	EXT2_ET_MAGIC_IO_MANAGER,
+	"Inode I/O Manager",
+	inode_open,
+	inode_close,
+	inode_set_blksize,
+	inode_read_blk,
+	inode_write_blk,
+	inode_flush,
+	inode_write_byte
+};
+
+io_manager inode_io_manager = &struct_inode_manager;
+
+errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
+				  struct ext2_inode *inode,
+				  char **name)
+{
+	struct inode_private_data	*data;
+	errcode_t			retval;
+
+	if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data),
+				     &data)))
+		return retval;
+	data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL;
+	sprintf(data->name, "%u:%d", ino, ino_unique++);
+	data->file = 0;
+	data->fs = fs;
+	data->ino = ino;
+	data->flags = 0;
+	if (inode) {
+		memcpy(&data->inode, inode, sizeof(struct ext2_inode));
+		data->flags |= CHANNEL_HAS_INODE;
+	}
+	data->next = top_intern;
+	top_intern = data;
+	*name = data->name;
+	return 0;
+}
+
+errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
+				 char **name)
+{
+	return ext2fs_inode_io_intern2(fs, ino, NULL, name);
+}
+
+
+static errcode_t inode_open(const char *name, int flags, io_channel *channel)
+{
+	io_channel	io = NULL;
+	struct inode_private_data *prev, *data = NULL;
+	errcode_t	retval;
+	int		open_flags;
+
+	if (name == 0)
+		return EXT2_ET_BAD_DEVICE_NAME;
+
+	for (data = top_intern, prev = NULL; data;
+	     prev = data, data = data->next)
+		if (strcmp(name, data->name) == 0)
+			break;
+	if (!data)
+		return ENOENT;
+	if (prev)
+		prev->next = data->next;
+	else
+		top_intern = data->next;
+
+	retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+	if (retval)
+		goto cleanup;
+	memset(io, 0, sizeof(struct struct_io_channel));
+
+	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+	io->manager = inode_io_manager;
+	retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+	if (retval)
+		goto cleanup;
+
+	strcpy(io->name, name);
+	io->private_data = data;
+	io->block_size = 1024;
+	io->read_error = 0;
+	io->write_error = 0;
+	io->refcount = 1;
+
+	open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0;
+	retval = ext2fs_file_open2(data->fs, data->ino,
+				   (data->flags & CHANNEL_HAS_INODE) ?
+				   &data->inode : 0, open_flags,
+				   &data->file);
+	if (retval)
+		goto cleanup;
+
+	*channel = io;
+	return 0;
+
+cleanup:
+	if (data) {
+		ext2fs_free_mem(&data);
+	}
+	if (io)
+		ext2fs_free_mem(&io);
+	return retval;
+}
+
+static errcode_t inode_close(io_channel channel)
+{
+	struct inode_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct inode_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+	if (--channel->refcount > 0)
+		return 0;
+
+	retval = ext2fs_file_close(data->file);
+
+	ext2fs_free_mem(&channel->private_data);
+	if (channel->name)
+		ext2fs_free_mem(&channel->name);
+	ext2fs_free_mem(&channel);
+	return retval;
+}
+
+static errcode_t inode_set_blksize(io_channel channel, int blksize)
+{
+	struct inode_private_data *data;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct inode_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+	channel->block_size = blksize;
+	return 0;
+}
+
+
+static errcode_t inode_read_blk(io_channel channel, unsigned long block,
+			       int count, void *buf)
+{
+	struct inode_private_data *data;
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct inode_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+	if ((retval = ext2fs_file_lseek(data->file,
+					block * channel->block_size,
+					EXT2_SEEK_SET, 0)))
+		return retval;
+
+	count = (count < 0) ? -count : (count * channel->block_size);
+
+	return ext2fs_file_read(data->file, buf, count, 0);
+}
+
+static errcode_t inode_write_blk(io_channel channel, unsigned long block,
+				int count, const void *buf)
+{
+	struct inode_private_data *data;
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct inode_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+	if ((retval = ext2fs_file_lseek(data->file,
+					block * channel->block_size,
+					EXT2_SEEK_SET, 0)))
+		return retval;
+
+	count = (count < 0) ? -count : (count * channel->block_size);
+
+	return ext2fs_file_write(data->file, buf, count, 0);
+}
+
+static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
+				 int size, const void *buf)
+{
+	struct inode_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct inode_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+	if ((retval = ext2fs_file_lseek(data->file, offset,
+					EXT2_SEEK_SET, 0)))
+		return retval;
+
+	return ext2fs_file_write(data->file, buf, size, 0);
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t inode_flush(io_channel channel)
+{
+	struct inode_private_data *data;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct inode_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+	return ext2fs_file_flush(data->file);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c
new file mode 100644
index 0000000..b470386
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/io_manager.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * io_manager.c --- the I/O manager abstraction
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t io_channel_set_options(io_channel channel, const char *opts)
+{
+	errcode_t retval = 0;
+	char *next, *ptr, *options, *arg;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+	if (!opts)
+		return 0;
+
+	if (!channel->manager->set_option)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	options = malloc(strlen(opts)+1);
+	if (!options)
+		return EXT2_ET_NO_MEMORY;
+	strcpy(options, opts);
+	ptr = options;
+
+	while (ptr && *ptr) {
+		next = strchr(ptr, '&');
+		if (next)
+			*next++ = 0;
+
+		arg = strchr(ptr, '=');
+		if (arg)
+			*arg++ = 0;
+
+		retval = (channel->manager->set_option)(channel, ptr, arg);
+		if (retval)
+			break;
+		ptr = next;
+	}
+	free(options);
+	return retval;
+}
+
+errcode_t io_channel_write_byte(io_channel channel, unsigned long offset,
+				int count, const void *data)
+{
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+	if (channel->manager->write_byte)
+		return channel->manager->write_byte(channel, offset,
+						    count, data);
+
+	return EXT2_ET_UNIMPLEMENTED;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/irel.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/irel.h
new file mode 100644
index 0000000..91d1d89
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/irel.h
@@ -0,0 +1,115 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * irel.h
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct ext2_inode_reference {
+	blk_t	block;
+	__u16 offset;
+};
+
+struct ext2_inode_relocate_entry {
+	ext2_ino_t	new;
+	ext2_ino_t	orig;
+	__u16		flags;
+	__u16		max_refs;
+};
+
+typedef struct ext2_inode_relocation_table *ext2_irel;
+
+struct ext2_inode_relocation_table {
+	__u32	magic;
+	char	*name;
+	ext2_ino_t	current;
+	void	*priv_data;
+
+	/*
+	 * Add an inode relocation entry.
+	 */
+	errcode_t (*put)(ext2_irel irel, ext2_ino_t old,
+			      struct ext2_inode_relocate_entry *ent);
+	/*
+	 * Get an inode relocation entry.
+	 */
+	errcode_t (*get)(ext2_irel irel, ext2_ino_t old,
+			      struct ext2_inode_relocate_entry *ent);
+
+	/*
+	 * Get an inode relocation entry by its original inode number
+	 */
+	errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+				 struct ext2_inode_relocate_entry *ent);
+
+	/*
+	 * Initialize for iterating over the inode relocation entries.
+	 */
+	errcode_t (*start_iter)(ext2_irel irel);
+
+	/*
+	 * The iterator function for the inode relocation entries.
+	 * Returns an inode number of 0 when out of entries.
+	 */
+	errcode_t (*next)(ext2_irel irel, ext2_ino_t *old,
+			  struct ext2_inode_relocate_entry *ent);
+
+	/*
+	 * Add an inode reference (i.e., note the fact that a
+	 * particular block/offset contains a reference to an inode)
+	 */
+	errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino,
+			     struct ext2_inode_reference *ref);
+
+	/*
+	 * Initialize for iterating over the inode references for a
+	 * particular inode.
+	 */
+	errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino);
+
+	/*
+	 * The iterator function for the inode references for an
+	 * inode.  The references for only one inode can be interator
+	 * over at a time, as the iterator state is stored in ext2_irel.
+	 */
+	errcode_t (*next_ref)(ext2_irel irel,
+			      struct ext2_inode_reference *ref);
+
+	/*
+	 * Move the inode relocation table from one inode number to
+	 * another.  Note that the inode references also must move.
+	 */
+	errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
+
+	/*
+	 * Remove an inode relocation entry, along with all of the
+	 * inode references.
+	 */
+	errcode_t (*delete)(ext2_irel irel, ext2_ino_t old);
+
+	/*
+	 * Free the inode relocation table.
+	 */
+	errcode_t (*free)(ext2_irel irel);
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
+				    ext2_irel *irel);
+
+#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent))
+#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent))
+#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \
+			((irel)->get_by_orig((irel), orig, old, ent))
+#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel)))
+#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent))
+#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref))
+#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino))
+#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref))
+#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new))
+#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old))
+#define ext2fs_irel_free(irel) ((irel)->free((irel)))
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c
new file mode 100644
index 0000000..c871b18
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/irel_ma.c
@@ -0,0 +1,367 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * irel_ma.c
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "irel.h"
+
+static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
+			 struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
+			 struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+				 struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_start_iter(ext2_irel irel);
+static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
+			  struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
+			     struct ext2_inode_reference *ref);
+static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino);
+static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref);
+static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
+static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old);
+static errcode_t ima_free(ext2_irel irel);
+
+/*
+ * This data structure stores the array of inode references; there is
+ * a structure for each inode.
+ */
+struct inode_reference_entry {
+	__u16 num;
+	struct ext2_inode_reference *refs;
+};
+
+struct irel_ma {
+	__u32 magic;
+	ext2_ino_t max_inode;
+	ext2_ino_t ref_current;
+	int   ref_iter;
+	ext2_ino_t	*orig_map;
+	struct ext2_inode_relocate_entry *entries;
+	struct inode_reference_entry *ref_entries;
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
+				      ext2_irel *new_irel)
+{
+	ext2_irel		irel = 0;
+	errcode_t	retval;
+	struct irel_ma	*ma = 0;
+	size_t		size;
+
+	*new_irel = 0;
+
+	/*
+	 * Allocate memory structures
+	 */
+	retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table),
+				&irel);
+	if (retval)
+		goto errout;
+	memset(irel, 0, sizeof(struct ext2_inode_relocation_table));
+
+	retval = ext2fs_get_mem(strlen(name)+1, &irel->name);
+	if (retval)
+		goto errout;
+	strcpy(irel->name, name);
+
+	retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma);
+	if (retval)
+		goto errout;
+	memset(ma, 0, sizeof(struct irel_ma));
+	irel->priv_data = ma;
+
+	size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1));
+	retval = ext2fs_get_mem(size, &ma->orig_map);
+	if (retval)
+		goto errout;
+	memset(ma->orig_map, 0, size);
+
+	size = (size_t) (sizeof(struct ext2_inode_relocate_entry) *
+			 (max_inode+1));
+	retval = ext2fs_get_mem(size, &ma->entries);
+	if (retval)
+		goto errout;
+	memset(ma->entries, 0, size);
+
+	size = (size_t) (sizeof(struct inode_reference_entry) *
+			 (max_inode+1));
+	retval = ext2fs_get_mem(size, &ma->ref_entries);
+	if (retval)
+		goto errout;
+	memset(ma->ref_entries, 0, size);
+	ma->max_inode = max_inode;
+
+	/*
+	 * Fill in the irel data structure
+	 */
+	irel->put = ima_put;
+	irel->get = ima_get;
+	irel->get_by_orig = ima_get_by_orig;
+	irel->start_iter = ima_start_iter;
+	irel->next = ima_next;
+	irel->add_ref = ima_add_ref;
+	irel->start_iter_ref = ima_start_iter_ref;
+	irel->next_ref = ima_next_ref;
+	irel->move = ima_move;
+	irel->delete = ima_delete;
+	irel->free = ima_free;
+
+	*new_irel = irel;
+	return 0;
+
+errout:
+	ima_free(irel);
+	return retval;
+}
+
+static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
+			struct ext2_inode_relocate_entry *ent)
+{
+	struct inode_reference_entry	*ref_ent;
+	struct irel_ma			*ma;
+	errcode_t			retval;
+	size_t				size, old_size;
+
+	ma = irel->priv_data;
+	if (old > ma->max_inode)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	/*
+	 * Force the orig field to the correct value; the application
+	 * program shouldn't be messing with this field.
+	 */
+	if (ma->entries[(unsigned) old].new == 0)
+		ent->orig = old;
+	else
+		ent->orig = ma->entries[(unsigned) old].orig;
+
+	/*
+	 * If max_refs has changed, reallocate the refs array
+	 */
+	ref_ent = ma->ref_entries + (unsigned) old;
+	if (ref_ent->refs && ent->max_refs !=
+	    ma->entries[(unsigned) old].max_refs) {
+		size = (sizeof(struct ext2_inode_reference) * ent->max_refs);
+		old_size = (sizeof(struct ext2_inode_reference) *
+			    ma->entries[(unsigned) old].max_refs);
+		retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs);
+		if (retval)
+			return retval;
+	}
+
+	ma->entries[(unsigned) old] = *ent;
+	ma->orig_map[(unsigned) ent->orig] = old;
+	return 0;
+}
+
+static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
+			struct ext2_inode_relocate_entry *ent)
+{
+	struct irel_ma	*ma;
+
+	ma = irel->priv_data;
+	if (old > ma->max_inode)
+		return EXT2_ET_INVALID_ARGUMENT;
+	if (ma->entries[(unsigned) old].new == 0)
+		return ENOENT;
+	*ent = ma->entries[(unsigned) old];
+	return 0;
+}
+
+static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+			struct ext2_inode_relocate_entry *ent)
+{
+	struct irel_ma	*ma;
+	ext2_ino_t	ino;
+
+	ma = irel->priv_data;
+	if (orig > ma->max_inode)
+		return EXT2_ET_INVALID_ARGUMENT;
+	ino = ma->orig_map[(unsigned) orig];
+	if (ino == 0)
+		return ENOENT;
+	*old = ino;
+	*ent = ma->entries[(unsigned) ino];
+	return 0;
+}
+
+static errcode_t ima_start_iter(ext2_irel irel)
+{
+	irel->current = 0;
+	return 0;
+}
+
+static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
+			 struct ext2_inode_relocate_entry *ent)
+{
+	struct irel_ma	*ma;
+
+	ma = irel->priv_data;
+	while (++irel->current < ma->max_inode) {
+		if (ma->entries[(unsigned) irel->current].new == 0)
+			continue;
+		*old = irel->current;
+		*ent = ma->entries[(unsigned) irel->current];
+		return 0;
+	}
+	*old = 0;
+	return 0;
+}
+
+static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
+			     struct ext2_inode_reference *ref)
+{
+	struct irel_ma	*ma;
+	size_t		size;
+	struct inode_reference_entry *ref_ent;
+	struct ext2_inode_relocate_entry *ent;
+	errcode_t		retval;
+
+	ma = irel->priv_data;
+	if (ino > ma->max_inode)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	ref_ent = ma->ref_entries + (unsigned) ino;
+	ent = ma->entries + (unsigned) ino;
+
+	/*
+	 * If the inode reference array doesn't exist, create it.
+	 */
+	if (ref_ent->refs == 0) {
+		size = (size_t) ((sizeof(struct ext2_inode_reference) *
+				  ent->max_refs));
+		retval = ext2fs_get_mem(size, &ref_ent->refs);
+		if (retval)
+			return retval;
+		memset(ref_ent->refs, 0, size);
+		ref_ent->num = 0;
+	}
+
+	if (ref_ent->num >= ent->max_refs)
+		return EXT2_ET_TOO_MANY_REFS;
+
+	ref_ent->refs[(unsigned) ref_ent->num++] = *ref;
+	return 0;
+}
+
+static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino)
+{
+	struct irel_ma	*ma;
+
+	ma = irel->priv_data;
+	if (ino > ma->max_inode)
+		return EXT2_ET_INVALID_ARGUMENT;
+	if (ma->entries[(unsigned) ino].new == 0)
+		return ENOENT;
+	ma->ref_current = ino;
+	ma->ref_iter = 0;
+	return 0;
+}
+
+static errcode_t ima_next_ref(ext2_irel irel,
+			      struct ext2_inode_reference *ref)
+{
+	struct irel_ma	*ma;
+	struct inode_reference_entry *ref_ent;
+
+	ma = irel->priv_data;
+
+	ref_ent = ma->ref_entries + ma->ref_current;
+
+	if ((ref_ent->refs == NULL) ||
+	    (ma->ref_iter >= ref_ent->num)) {
+		ref->block = 0;
+		ref->offset = 0;
+		return 0;
+	}
+	*ref = ref_ent->refs[ma->ref_iter++];
+	return 0;
+}
+
+
+static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new)
+{
+	struct irel_ma	*ma;
+
+	ma = irel->priv_data;
+	if ((old > ma->max_inode) || (new > ma->max_inode))
+		return EXT2_ET_INVALID_ARGUMENT;
+	if (ma->entries[(unsigned) old].new == 0)
+		return ENOENT;
+
+	ma->entries[(unsigned) new] = ma->entries[(unsigned) old];
+	ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs);
+	ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old];
+
+	ma->entries[(unsigned) old].new = 0;
+	ma->ref_entries[(unsigned) old].num = 0;
+	ma->ref_entries[(unsigned) old].refs = 0;
+
+	ma->orig_map[ma->entries[new].orig] = new;
+	return 0;
+}
+
+static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old)
+{
+	struct irel_ma	*ma;
+
+	ma = irel->priv_data;
+	if (old > ma->max_inode)
+		return EXT2_ET_INVALID_ARGUMENT;
+	if (ma->entries[(unsigned) old].new == 0)
+		return ENOENT;
+
+	ma->entries[old].new = 0;
+	ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs);
+	ma->orig_map[ma->entries[(unsigned) old].orig] = 0;
+
+	ma->ref_entries[(unsigned) old].num = 0;
+	ma->ref_entries[(unsigned) old].refs = 0;
+	return 0;
+}
+
+static errcode_t ima_free(ext2_irel irel)
+{
+	struct irel_ma	*ma;
+	ext2_ino_t	ino;
+
+	if (!irel)
+		return 0;
+
+	ma = irel->priv_data;
+
+	if (ma) {
+		ext2fs_free_mem(&ma->orig_map);
+		ext2fs_free_mem(&ma->entries);
+		if (ma->ref_entries) {
+			for (ino = 0; ino <= ma->max_inode; ino++) {
+				ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs);
+			}
+			ext2fs_free_mem(&ma->ref_entries);
+		}
+		ext2fs_free_mem(&ma);
+	}
+	ext2fs_free_mem(&irel->name);
+	ext2fs_free_mem(&irel);
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c
new file mode 100644
index 0000000..f5f6f31
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/ismounted.c
@@ -0,0 +1,357 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ismounted.c --- Check to see if the filesystem was mounted
+ *
+ * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#ifdef HAVE_GETMNTINFO
+#include <paths.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif /* HAVE_GETMNTINFO */
+#include <string.h>
+#include <sys/stat.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifdef HAVE_MNTENT_H
+/*
+ * Helper function which checks a file in /etc/mtab format to see if a
+ * filesystem is mounted.  Returns an error if the file doesn't exist
+ * or can't be opened.
+ */
+static errcode_t check_mntent_file(const char *mtab_file, const char *file,
+				   int *mount_flags, char *mtpt, int mtlen)
+{
+	struct mntent	*mnt;
+	struct stat	st_buf;
+	errcode_t	retval = 0;
+	dev_t		file_dev=0, file_rdev=0;
+	ino_t		file_ino=0;
+	FILE		*f;
+	int		fd;
+
+	*mount_flags = 0;
+	if ((f = setmntent (mtab_file, "r")) == NULL)
+		return errno;
+	if (stat(file, &st_buf) == 0) {
+		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__ */
+		} else {
+			file_dev = st_buf.st_dev;
+			file_ino = st_buf.st_ino;
+		}
+	}
+	while ((mnt = getmntent (f)) != NULL) {
+		if (strcmp(file, mnt->mnt_fsname) == 0)
+			break;
+		if (stat(mnt->mnt_fsname, &st_buf) == 0) {
+			if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__
+				if (file_rdev && (file_rdev == st_buf.st_rdev))
+					break;
+#endif  /* __GNU__ */
+			} else {
+				if (file_dev && ((file_dev == st_buf.st_dev) &&
+						 (file_ino == st_buf.st_ino)))
+					break;
+			}
+		}
+	}
+
+	if (mnt == 0) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+		/*
+		 * Do an extra check to see if this is the root device.  We
+		 * can't trust /etc/mtab, and /proc/mounts will only list
+		 * /dev/root for the root filesystem.  Argh.  Instead we
+		 * check if the given device has the same major/minor number
+		 * as the device that the root directory is on.
+		 */
+		if (file_rdev && stat("/", &st_buf) == 0) {
+			if (st_buf.st_dev == file_rdev) {
+				*mount_flags = EXT2_MF_MOUNTED;
+				if (mtpt)
+					strncpy(mtpt, "/", mtlen);
+				goto is_root;
+			}
+		}
+#endif  /* __GNU__ */
+		goto errout;
+	}
+#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
+	/* Validate the entry in case /etc/mtab is out of date */
+	/*
+	 * We need to be paranoid, because some broken distributions
+	 * (read: Slackware) don't initialize /etc/mtab before checking
+	 * all of the non-root filesystems on the disk.
+	 */
+	if (stat(mnt->mnt_dir, &st_buf) < 0) {
+		retval = errno;
+		if (retval == ENOENT) {
+#ifdef DEBUG
+			printf("Bogus entry in %s!  (%s does not exist)\n",
+			       mtab_file, mnt->mnt_dir);
+#endif /* DEBUG */
+			retval = 0;
+		}
+		goto errout;
+	}
+	if (file_rdev && (st_buf.st_dev != file_rdev)) {
+#ifdef DEBUG
+		printf("Bogus entry in %s!  (%s not mounted on %s)\n",
+		       mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+		goto errout;
+	}
+#endif /* __GNU__ */
+	*mount_flags = EXT2_MF_MOUNTED;
+
+#ifdef MNTOPT_RO
+	/* Check to see if the ro option is set */
+	if (hasmntopt(mnt, MNTOPT_RO))
+		*mount_flags |= EXT2_MF_READONLY;
+#endif
+
+	if (mtpt)
+		strncpy(mtpt, mnt->mnt_dir, mtlen);
+	/*
+	 * Check to see if we're referring to the root filesystem.
+	 * If so, do a manual check to see if we can open /etc/mtab
+	 * read/write, since if the root is mounted read/only, the
+	 * contents of /etc/mtab may not be accurate.
+	 */
+	if (LONE_CHAR(mnt->mnt_dir, '/')) {
+is_root:
+#define TEST_FILE "/.ismount-test-file"
+		*mount_flags |= EXT2_MF_ISROOT;
+		fd = open(TEST_FILE, O_RDWR|O_CREAT);
+		if (fd < 0) {
+			if (errno == EROFS)
+				*mount_flags |= EXT2_MF_READONLY;
+		} else
+			close(fd);
+		(void) unlink(TEST_FILE);
+	}
+	retval = 0;
+errout:
+	endmntent (f);
+	return retval;
+}
+
+static errcode_t check_mntent(const char *file, int *mount_flags,
+			      char *mtpt, int mtlen)
+{
+	errcode_t	retval;
+
+#ifdef DEBUG
+	retval = check_mntent_file("/tmp/mtab", file, mount_flags,
+				   mtpt, mtlen);
+	if (retval == 0)
+		return 0;
+#endif /* DEBUG */
+#ifdef __linux__
+	retval = check_mntent_file("/proc/mounts", file, mount_flags,
+				   mtpt, mtlen);
+	if (retval == 0 && (*mount_flags != 0))
+		return 0;
+#endif /* __linux__ */
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif /* MOUNTED */
+	retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
+	return retval;
+#else
+	*mount_flags = 0;
+	return 0;
+#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
+}
+
+#else
+#if defined(HAVE_GETMNTINFO)
+
+static errcode_t check_getmntinfo(const char *file, int *mount_flags,
+				  char *mtpt, int mtlen)
+{
+	struct statfs *mp;
+	int    len, n;
+	const  char   *s1;
+	char	*s2;
+
+	n = getmntinfo(&mp, MNT_NOWAIT);
+	if (n == 0)
+		return errno;
+
+	len = sizeof(_PATH_DEV) - 1;
+	s1 = file;
+	if (strncmp(_PATH_DEV, s1, len) == 0)
+		s1 += len;
+
+	*mount_flags = 0;
+	while (--n >= 0) {
+		s2 = mp->f_mntfromname;
+		if (strncmp(_PATH_DEV, s2, len) == 0) {
+			s2 += len - 1;
+			*s2 = 'r';
+		}
+		if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
+			*mount_flags = EXT2_MF_MOUNTED;
+			break;
+		}
+		++mp;
+	}
+	if (mtpt)
+		strncpy(mtpt, mp->f_mntonname, mtlen);
+	return 0;
+}
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Check to see if we're dealing with the swap device.
+ */
+static int is_swap_device(const char *file)
+{
+	FILE		*f;
+	char		buf[1024], *cp;
+	dev_t		file_dev;
+	struct stat	st_buf;
+	int		ret = 0;
+
+	file_dev = 0;
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+	if ((stat(file, &st_buf) == 0) &&
+	    S_ISBLK(st_buf.st_mode))
+		file_dev = st_buf.st_rdev;
+#endif  /* __GNU__ */
+
+	if (!(f = fopen_for_read("/proc/swaps")))
+		return 0;
+	/* Skip the first line */
+	fgets(buf, sizeof(buf), f);
+	while (!feof(f)) {
+		if (!fgets(buf, sizeof(buf), f))
+			break;
+		if ((cp = strchr(buf, ' ')) != NULL)
+			*cp = 0;
+		if ((cp = strchr(buf, '\t')) != NULL)
+			*cp = 0;
+		if (strcmp(buf, file) == 0) {
+			ret++;
+			break;
+		}
+#ifndef __GNU__
+		if (file_dev && (stat(buf, &st_buf) == 0) &&
+		    S_ISBLK(st_buf.st_mode) &&
+		    file_dev == st_buf.st_rdev) {
+			ret++;
+			break;
+		}
+#endif  /* __GNU__ */
+	}
+	fclose(f);
+	return ret;
+}
+
+
+/*
+ * ext2fs_check_mount_point() returns 1 if the device is mounted, 0
+ * otherwise.  If mtpt is non-NULL, the directory where the device is
+ * mounted is copied to where mtpt is pointing, up to mtlen
+ * characters.
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
+				  char *mtpt, int mtlen)
+{
+	if (is_swap_device(device)) {
+		*mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP;
+		strncpy(mtpt, "<swap>", mtlen);
+		return 0;
+	}
+#ifdef HAVE_MNTENT_H
+	return check_mntent(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef HAVE_GETMNTINFO
+	return check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef __GNUC__
+ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+	*mount_flags = 0;
+	return 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+}
+
+/*
+ * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED,
+ * EXT2_MF_READONLY, and EXT2_MF_ROOT
+ *
+ */
+errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags)
+{
+	return ext2fs_check_mount_point(file, mount_flags, NULL, 0);
+}
+
+#ifdef DEBUG
+int main(int argc, char **argv)
+{
+	int	retval, mount_flags;
+	char	mntpt[80];
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s device\n", argv[0]);
+		exit(1);
+	}
+
+	mntpt[0] = 0;
+	retval = ext2fs_check_mount_point(argv[1], &mount_flags,
+					  mntpt, sizeof(mntpt));
+	if (retval) {
+		com_err(argv[0], retval,
+			"while calling ext2fs_check_if_mounted");
+		exit(1);
+	}
+	printf("Device %s reports flags %02x\n", argv[1], mount_flags);
+	if (mount_flags & EXT2_MF_BUSY)
+		printf("\t%s is apparently in use.\n", argv[1]);
+	if (mount_flags & EXT2_MF_MOUNTED)
+		printf("\t%s is mounted.\n", argv[1]);
+	if (mount_flags & EXT2_MF_SWAP)
+		printf("\t%s is a swap device.\n", argv[1]);
+	if (mount_flags & EXT2_MF_READONLY)
+		printf("\t%s is read-only.\n", argv[1]);
+	if (mount_flags & EXT2_MF_ISROOT)
+		printf("\t%s is the root filesystem.\n", argv[1]);
+	if (mntpt[0])
+		printf("\t%s is mounted on %s.\n", argv[1], mntpt);
+	exit(0);
+}
+#endif /* DEBUG */
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h
new file mode 100644
index 0000000..17c586a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/jfs_dat.h
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * jfs_dat.h --- stripped down header file which only contains the JFS
+ *	on-disk data structures
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * On-disk structures
+ */
+
+/*
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK	1
+#define JFS_COMMIT_BLOCK	2
+#define JFS_SUPERBLOCK		3
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+typedef struct journal_header_s
+{
+	__u32		h_magic;
+	__u32		h_blocktype;
+	__u32		h_sequence;
+} journal_header_t;
+
+
+/*
+ * The block tag: used to describe a single buffer in the journal
+ */
+typedef struct journal_block_tag_s
+{
+	__u32		t_blocknr;	/* The on-disk block number */
+	__u32		t_flags;	/* See below */
+} journal_block_tag_t;
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE		1	/* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID	2	/* block has same uuid as previous */
+#define JFS_FLAG_DELETED	4	/* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG	8	/* last tag in this descriptor block */
+
+
+/*
+ * The journal superblock
+ */
+typedef struct journal_superblock_s
+{
+	journal_header_t s_header;
+
+	/* Static information describing the journal */
+	__u32		s_blocksize;	/* journal device blocksize */
+	__u32		s_maxlen;	/* total blocks in journal file */
+	__u32		s_first;	/* first block of log information */
+
+	/* 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;
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h
new file mode 100644
index 0000000..853d97a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/kernel-jbd.h
@@ -0,0 +1,235 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linux/include/linux/jbd.h
+ *
+ * Written by Stephen C. Tweedie <sct@redhat.com>
+ *
+ * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Definitions for transaction data structures for the buffer cache
+ * filesystem journaling support.
+ */
+#ifndef LINUX_JBD_H
+#define LINUX_JBD_H 1
+
+#include <sys/types.h>
+#include <linux/types.h>
+#include "ext2fs.h"
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+
+typedef struct journal_header_s
+{
+	__u32		h_magic;
+	__u32		h_blocktype;
+	__u32		h_sequence;
+} journal_header_t;
+
+/*
+ * This is the global e2fsck structure.
+ */
+typedef struct e2fsck_struct *e2fsck_t;
+
+
+struct inode {
+	e2fsck_t        i_ctx;
+	ext2_ino_t      i_ino;
+	struct ext2_inode i_ext2;
+};
+
+
+/*
+ * The journal superblock.  All fields are in big-endian byte order.
+ */
+typedef struct journal_superblock_s
+{
+/* 0x0000 */
+	journal_header_t s_header;
+
+/* 0x000C */
+	/* Static information describing the journal */
+	__u32	s_blocksize;		/* journal device blocksize */
+	__u32	s_maxlen;		/* total blocks in journal file */
+	__u32	s_first;		/* first block of log information */
+
+/* 0x0018 */
+	/* 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 */
+
+/* 0x0020 */
+	/* Error value, as set by journal_abort(). */
+	__s32	s_errno;
+
+/* 0x0024 */
+	/* Remaining fields are only valid in a version-2 superblock */
+	__u32	s_feature_compat;	/* compatible feature set */
+	__u32	s_feature_incompat;	/* incompatible feature set */
+	__u32	s_feature_ro_compat;	/* readonly-compatible feature set */
+/* 0x0030 */
+	__u8	s_uuid[16];		/* 128-bit uuid for journal */
+
+/* 0x0040 */
+	__u32	s_nr_users;		/* Nr of filesystems sharing log */
+
+	__u32	s_dynsuper;		/* Blocknr of dynamic superblock copy*/
+
+/* 0x0048 */
+	__u32	s_max_transaction;	/* Limit of journal blocks per trans.*/
+	__u32	s_max_trans_data;	/* Limit of data blocks per trans. */
+
+/* 0x0050 */
+	__u32	s_padding[44];
+
+/* 0x0100 */
+	__u8	s_users[16*48];		/* ids of all fs'es sharing the log */
+/* 0x0400 */
+} journal_superblock_t;
+
+
+extern int journal_blocks_per_page(struct inode *inode);
+extern int jbd_blocks_per_page(struct inode *inode);
+
+#define JFS_MIN_JOURNAL_BLOCKS 1024
+
+
+/*
+ * Internal structures used by the logging mechanism:
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK	1
+#define JFS_COMMIT_BLOCK	2
+#define JFS_SUPERBLOCK_V1	3
+#define JFS_SUPERBLOCK_V2	4
+#define JFS_REVOKE_BLOCK	5
+
+/*
+ * The block tag: used to describe a single buffer in the journal
+ */
+typedef struct journal_block_tag_s
+{
+	__u32		t_blocknr;	/* The on-disk block number */
+	__u32		t_flags;	/* See below */
+} journal_block_tag_t;
+
+/*
+ * The revoke descriptor: used on disk to describe a series of blocks to
+ * be revoked from the log
+ */
+typedef struct journal_revoke_header_s
+{
+	journal_header_t r_header;
+	int		 r_count;	/* Count of bytes used in the block */
+} journal_revoke_header_t;
+
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE		1	/* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID	2	/* block has same uuid as previous */
+#define JFS_FLAG_DELETED	4	/* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG	8	/* last tag in this descriptor block */
+
+
+
+
+#define JFS_HAS_COMPAT_FEATURE(j,mask)					\
+	((j)->j_format_version >= 2 &&					\
+	 ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
+#define JFS_HAS_RO_COMPAT_FEATURE(j,mask)				\
+	((j)->j_format_version >= 2 &&					\
+	 ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
+#define JFS_HAS_INCOMPAT_FEATURE(j,mask)				\
+	((j)->j_format_version >= 2 &&					\
+	 ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
+
+#define JFS_FEATURE_INCOMPAT_REVOKE	0x00000001
+
+/* Features known to this kernel version: */
+#define JFS_KNOWN_COMPAT_FEATURES	0
+#define JFS_KNOWN_ROCOMPAT_FEATURES	0
+#define JFS_KNOWN_INCOMPAT_FEATURES	JFS_FEATURE_INCOMPAT_REVOKE
+
+/* Comparison functions for transaction IDs: perform comparisons using
+ * modulo arithmetic so that they work over sequence number wraps. */
+
+
+/*
+ * Definitions which augment the buffer_head layer
+ */
+
+/* journaling buffer types */
+#define BJ_None		0	/* Not journaled */
+#define BJ_SyncData	1	/* Normal data: flush before commit */
+#define BJ_AsyncData	2	/* writepage data: wait on it before commit */
+#define BJ_Metadata	3	/* Normal journaled metadata */
+#define BJ_Forget	4	/* Buffer superceded by this transaction */
+#define BJ_IO		5	/* Buffer is for temporary IO use */
+#define BJ_Shadow	6	/* Buffer contents being shadowed to the log */
+#define BJ_LogCtl	7	/* Buffer contains log descriptors */
+#define BJ_Reserved	8	/* Buffer is reserved for access by journal */
+#define BJ_Types	9
+
+
+struct kdev_s {
+	e2fsck_t        k_ctx;
+	int             k_dev;
+};
+
+typedef struct kdev_s *kdev_t;
+typedef unsigned int tid_t;
+
+struct journal_s
+{
+	unsigned long		j_flags;
+	int			j_errno;
+	struct buffer_head *	j_sb_buffer;
+	struct journal_superblock_s *j_superblock;
+	int			j_format_version;
+	unsigned long		j_head;
+	unsigned long		j_tail;
+	unsigned long		j_free;
+	unsigned long		j_first, j_last;
+	kdev_t			j_dev;
+	kdev_t			j_fs_dev;
+	int			j_blocksize;
+	unsigned int		j_blk_offset;
+	unsigned int		j_maxlen;
+	struct inode *		j_inode;
+	tid_t			j_tail_sequence;
+	tid_t			j_transaction_sequence;
+	__u8			j_uuid[16];
+	struct jbd_revoke_table_s *j_revoke;
+};
+
+typedef struct journal_s journal_t;
+
+extern int	   journal_recover    (journal_t *journal);
+extern int	   journal_skip_recovery (journal_t *);
+
+/* Primary revoke support */
+extern int	   journal_init_revoke(journal_t *, int);
+extern void	   journal_destroy_revoke_caches(void);
+extern int	   journal_init_revoke_caches(void);
+
+/* Recovery revoke support */
+extern int	   journal_set_revoke(journal_t *, unsigned long, tid_t);
+extern int	   journal_test_revoke(journal_t *, unsigned long, tid_t);
+extern void	   journal_clear_revoke(journal_t *);
+extern void	   journal_brelse_array(struct buffer_head *b[], int n);
+
+extern void	   journal_destroy_revoke(journal_t *);
+
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h
new file mode 100644
index 0000000..d80716a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/kernel-list.h
@@ -0,0 +1,113 @@
+/* vi: set sw=4 ts=4: */
+#ifndef LINUX_LIST_H
+#define LINUX_LIST_H 1
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = { &name, &name }
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+#if (!defined(__GNUC__) && !defined(__WATCOMC__))
+#define __inline__
+#endif
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/*
+ * Insert a new entry after the specified head..
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/*
+ * Insert a new entry at the tail
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+				  struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+static __inline__ void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+static __inline__ int list_empty(struct list_head *head)
+{
+	return head->next == head;
+}
+
+/*
+ * Splice in "list" into "head"
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+	struct list_head *first = list->next;
+
+	if (first != list) {
+		struct list_head *last = list->prev;
+		struct list_head *at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+#define list_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/link.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/link.c
new file mode 100644
index 0000000..08b2e96
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/link.c
@@ -0,0 +1,135 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * link.c --- create links in a ext2fs directory
+ *
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct link_struct  {
+	const char	*name;
+	int		namelen;
+	ext2_ino_t	inode;
+	int		flags;
+	int		done;
+	struct ext2_super_block *sb;
+};
+
+static int link_proc(struct ext2_dir_entry *dirent,
+		     int	offset,
+		     int	blocksize,
+		     char	*buf,
+		     void	*priv_data)
+{
+	struct link_struct *ls = (struct link_struct *) priv_data;
+	struct ext2_dir_entry *next;
+	int rec_len, min_rec_len;
+	int ret = 0;
+
+	rec_len = EXT2_DIR_REC_LEN(ls->namelen);
+
+	/*
+	 * See if the following directory entry (if any) is unused;
+	 * if so, absorb it into this one.
+	 */
+	next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
+	if ((offset + dirent->rec_len < blocksize - 8) &&
+	    (next->inode == 0) &&
+	    (offset + dirent->rec_len + next->rec_len <= blocksize)) {
+		dirent->rec_len += next->rec_len;
+		ret = DIRENT_CHANGED;
+	}
+
+	/*
+	 * If the directory entry is used, see if we can split the
+	 * directory entry to make room for the new name.  If so,
+	 * truncate it and return.
+	 */
+	if (dirent->inode) {
+		min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+		if (dirent->rec_len < (min_rec_len + rec_len))
+			return ret;
+		rec_len = dirent->rec_len - min_rec_len;
+		dirent->rec_len = min_rec_len;
+		next = (struct ext2_dir_entry *) (buf + offset +
+						  dirent->rec_len);
+		next->inode = 0;
+		next->name_len = 0;
+		next->rec_len = rec_len;
+		return DIRENT_CHANGED;
+	}
+
+	/*
+	 * If we get this far, then the directory entry is not used.
+	 * See if we can fit the request entry in.  If so, do it.
+	 */
+	if (dirent->rec_len < rec_len)
+		return ret;
+	dirent->inode = ls->inode;
+	dirent->name_len = ls->namelen;
+	strncpy(dirent->name, ls->name, ls->namelen);
+	if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
+		dirent->name_len |= (ls->flags & 0x7) << 8;
+
+	ls->done++;
+	return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+/*
+ * Note: the low 3 bits of the flags field are used as the directory
+ * entry filetype.
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
+		      ext2_ino_t ino, int flags)
+{
+	errcode_t		retval;
+	struct link_struct	ls;
+	struct ext2_inode	inode;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+
+	ls.name = name;
+	ls.namelen = name ? strlen(name) : 0;
+	ls.inode = ino;
+	ls.flags = flags;
+	ls.done = 0;
+	ls.sb = fs->super;
+
+	retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
+				    0, link_proc, &ls);
+	if (retval)
+		return retval;
+
+	if (!ls.done)
+		return EXT2_ET_DIR_NO_SPACE;
+
+	if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
+		return retval;
+
+	if (inode.i_flags & EXT2_INDEX_FL) {
+		inode.i_flags &= ~EXT2_INDEX_FL;
+		if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
+			return retval;
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c
new file mode 100644
index 0000000..b2e8de8
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/lookup.c
@@ -0,0 +1,68 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lookup.c --- ext2fs directory lookup operations
+ *
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct lookup_struct  {
+	const char	*name;
+	int		len;
+	ext2_ino_t	*inode;
+	int		found;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int lookup_proc(struct ext2_dir_entry *dirent,
+		       int	offset EXT2FS_ATTR((unused)),
+		       int	blocksize EXT2FS_ATTR((unused)),
+		       char	*buf EXT2FS_ATTR((unused)),
+		       void	*priv_data)
+{
+	struct lookup_struct *ls = (struct lookup_struct *) priv_data;
+
+	if (ls->len != (dirent->name_len & 0xFF))
+		return 0;
+	if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF)))
+		return 0;
+	*ls->inode = dirent->inode;
+	ls->found++;
+	return DIRENT_ABORT;
+}
+
+
+errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
+			int namelen, char *buf, ext2_ino_t *inode)
+{
+	errcode_t	retval;
+	struct lookup_struct ls;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	ls.name = name;
+	ls.len = namelen;
+	ls.inode = inode;
+	ls.found = 0;
+
+	retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls);
+	if (retval)
+		return retval;
+
+	return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c
new file mode 100644
index 0000000..a86ac8e
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/mkdir.c
@@ -0,0 +1,139 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkdir.c --- make a directory in the filesystem
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef EXT2_FT_DIR
+#define EXT2_FT_DIR		2
+#endif
+
+errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
+		       const char *name)
+{
+	errcode_t		retval;
+	struct ext2_inode	parent_inode, inode;
+	ext2_ino_t		ino = inum;
+	ext2_ino_t		scratch_ino;
+	blk_t			blk;
+	char			*block = NULL;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	/*
+	 * Allocate an inode, if necessary
+	 */
+	if (!ino) {
+		retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755,
+					  0, &ino);
+		if (retval)
+			goto cleanup;
+	}
+
+	/*
+	 * Allocate a data block for the directory
+	 */
+	retval = ext2fs_new_block(fs, 0, 0, &blk);
+	if (retval)
+		goto cleanup;
+
+	/*
+	 * Create a scratch template for the directory
+	 */
+	retval = ext2fs_new_dir_block(fs, ino, parent, &block);
+	if (retval)
+		goto cleanup;
+
+	/*
+	 * Get the parent's inode, if necessary
+	 */
+	if (parent != ino) {
+		retval = ext2fs_read_inode(fs, parent, &parent_inode);
+		if (retval)
+			goto cleanup;
+	} else
+		memset(&parent_inode, 0, sizeof(parent_inode));
+
+	/*
+	 * Create the inode structure....
+	 */
+	memset(&inode, 0, sizeof(struct ext2_inode));
+	inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
+	inode.i_uid = inode.i_gid = 0;
+	inode.i_blocks = fs->blocksize / 512;
+	inode.i_block[0] = blk;
+	inode.i_links_count = 2;
+	inode.i_ctime = inode.i_atime = inode.i_mtime = time(NULL);
+	inode.i_size = fs->blocksize;
+
+	/*
+	 * Write out the inode and inode data block
+	 */
+	retval = ext2fs_write_dir_block(fs, blk, block);
+	if (retval)
+		goto cleanup;
+	retval = ext2fs_write_new_inode(fs, ino, &inode);
+	if (retval)
+		goto cleanup;
+
+	/*
+	 * Link the directory into the filesystem hierarchy
+	 */
+	if (name) {
+		retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
+				       &scratch_ino);
+		if (!retval) {
+			retval = EXT2_ET_DIR_EXISTS;
+			name = 0;
+			goto cleanup;
+		}
+		if (retval != EXT2_ET_FILE_NOT_FOUND)
+			goto cleanup;
+		retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR);
+		if (retval)
+			goto cleanup;
+	}
+
+	/*
+	 * Update parent inode's counts
+	 */
+	if (parent != ino) {
+		parent_inode.i_links_count++;
+		retval = ext2fs_write_inode(fs, parent, &parent_inode);
+		if (retval)
+			goto cleanup;
+	}
+
+	/*
+	 * Update accounting....
+	 */
+	ext2fs_block_alloc_stats(fs, blk, +1);
+	ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
+
+cleanup:
+	ext2fs_free_mem(&block);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c
new file mode 100644
index 0000000..748d9ab
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/mkjournal.c
@@ -0,0 +1,426 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkjournal.c --- make a journal for a filesystem
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "ext2_fs.h"
+#include "../e2p/e2p.h"
+#include "../e2fsck.h"
+#include "ext2fs.h"
+#include "kernel-jbd.h"
+
+/*
+ * This function automatically sets up the journal superblock and
+ * returns it as an allocated block.
+ */
+errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
+					   __u32 size, int flags,
+					   char  **ret_jsb)
+{
+	errcode_t		retval;
+	journal_superblock_t	*jsb;
+
+	if (size < 1024)
+		return EXT2_ET_JOURNAL_TOO_SMALL;
+
+	if ((retval = ext2fs_get_mem(fs->blocksize, &jsb)))
+		return retval;
+
+	memset (jsb, 0, fs->blocksize);
+
+	jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
+	if (flags & EXT2_MKJOURNAL_V1_SUPER)
+		jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1);
+	else
+		jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
+	jsb->s_blocksize = htonl(fs->blocksize);
+	jsb->s_maxlen = htonl(size);
+	jsb->s_nr_users = htonl(1);
+	jsb->s_first = htonl(1);
+	jsb->s_sequence = htonl(1);
+	memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid));
+	/*
+	 * If we're creating an external journal device, we need to
+	 * adjust these fields.
+	 */
+	if (fs->super->s_feature_incompat &
+	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+		jsb->s_nr_users = 0;
+		if (fs->blocksize == 1024)
+			jsb->s_first = htonl(3);
+		else
+			jsb->s_first = htonl(2);
+	}
+
+	*ret_jsb = (char *) jsb;
+	return 0;
+}
+
+/*
+ * This function writes a journal using POSIX routines.  It is used
+ * for creating external journals and creating journals on live
+ * filesystems.
+ */
+static errcode_t write_journal_file(ext2_filsys fs, char *filename,
+				    blk_t size, int flags)
+{
+	errcode_t	retval;
+	char		*buf = NULL;
+	int		fd, ret_size;
+	blk_t		i;
+
+	if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
+		return retval;
+
+	/* Open the device or journal file */
+	if ((fd = open(filename, O_WRONLY)) < 0) {
+		retval = errno;
+		goto errout;
+	}
+
+	/* Write the superblock out */
+	retval = EXT2_ET_SHORT_WRITE;
+	ret_size = write(fd, buf, fs->blocksize);
+	if (ret_size < 0) {
+		retval = errno;
+		goto errout;
+	}
+	if (ret_size != (int) fs->blocksize)
+		goto errout;
+	memset(buf, 0, fs->blocksize);
+
+	for (i = 1; i < size; i++) {
+		ret_size = write(fd, buf, fs->blocksize);
+		if (ret_size < 0) {
+			retval = errno;
+			goto errout;
+		}
+		if (ret_size != (int) fs->blocksize)
+			goto errout;
+	}
+	close(fd);
+
+	retval = 0;
+errout:
+	ext2fs_free_mem(&buf);
+	return retval;
+}
+
+/*
+ * Helper function for creating the journal using direct I/O routines
+ */
+struct mkjournal_struct {
+	int		num_blocks;
+	int		newblocks;
+	char		*buf;
+	errcode_t	err;
+};
+
+static int mkjournal_proc(ext2_filsys	fs,
+			   blk_t	*blocknr,
+			   e2_blkcnt_t	blockcnt,
+			   blk_t	ref_block EXT2FS_ATTR((unused)),
+			   int		ref_offset EXT2FS_ATTR((unused)),
+			   void		*priv_data)
+{
+	struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data;
+	blk_t	new_blk;
+	static blk_t	last_blk = 0;
+	errcode_t	retval;
+
+	if (*blocknr) {
+		last_blk = *blocknr;
+		return 0;
+	}
+	retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
+	if (retval) {
+		es->err = retval;
+		return BLOCK_ABORT;
+	}
+	if (blockcnt > 0)
+		es->num_blocks--;
+
+	es->newblocks++;
+	retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf);
+
+	if (blockcnt == 0)
+		memset(es->buf, 0, fs->blocksize);
+
+	if (retval) {
+		es->err = retval;
+		return BLOCK_ABORT;
+	}
+	*blocknr = new_blk;
+	last_blk = new_blk;
+	ext2fs_block_alloc_stats(fs, new_blk, +1);
+
+	if (es->num_blocks == 0)
+		return (BLOCK_CHANGED | BLOCK_ABORT);
+	else
+		return BLOCK_CHANGED;
+}
+
+/*
+ * This function creates a journal using direct I/O routines.
+ */
+static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
+				     blk_t size, int flags)
+{
+	char			*buf;
+	errcode_t		retval;
+	struct ext2_inode	inode;
+	struct mkjournal_struct	es;
+
+	if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
+		return retval;
+
+	if ((retval = ext2fs_read_bitmaps(fs)))
+		return retval;
+
+	if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
+		return retval;
+
+	if (inode.i_blocks > 0)
+		return EEXIST;
+
+	es.num_blocks = size;
+	es.newblocks = 0;
+	es.buf = buf;
+	es.err = 0;
+
+	retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND,
+				       0, mkjournal_proc, &es);
+	if (es.err) {
+		retval = es.err;
+		goto errout;
+	}
+
+	if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
+		goto errout;
+
+	inode.i_size += fs->blocksize * size;
+	inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+	inode.i_mtime = inode.i_ctime = time(NULL);
+	inode.i_links_count = 1;
+	inode.i_mode = LINUX_S_IFREG | 0600;
+
+	if ((retval = ext2fs_write_inode(fs, journal_ino, &inode)))
+		goto errout;
+	retval = 0;
+
+	memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
+	fs->super->s_jnl_blocks[16] = inode.i_size;
+	fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
+	ext2fs_mark_super_dirty(fs);
+
+errout:
+	ext2fs_free_mem(&buf);
+	return retval;
+}
+
+/*
+ * This function adds a journal device to a filesystem
+ */
+errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev)
+{
+	struct stat	st;
+	errcode_t	retval;
+	char		buf[1024];
+	journal_superblock_t	*jsb;
+	int		start;
+	__u32		i, nr_users;
+
+	/* Make sure the device exists and is a block device */
+	if (stat(journal_dev->device_name, &st) < 0)
+		return errno;
+
+	if (!S_ISBLK(st.st_mode))
+		return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */
+
+	/* Get the journal superblock */
+	start = 1;
+	if (journal_dev->blocksize == 1024)
+		start++;
+	if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf)))
+		return retval;
+
+	jsb = (journal_superblock_t *) buf;
+	if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
+	    (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2)))
+		return EXT2_ET_NO_JOURNAL_SB;
+
+	if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize)
+		return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+
+	/* Check and see if this filesystem has already been added */
+	nr_users = ntohl(jsb->s_nr_users);
+	for (i=0; i < nr_users; i++) {
+		if (memcmp(fs->super->s_uuid,
+			   &jsb->s_users[i*16], 16) == 0)
+			break;
+	}
+	if (i >= nr_users) {
+		memcpy(&jsb->s_users[nr_users*16],
+		       fs->super->s_uuid, 16);
+		jsb->s_nr_users = htonl(nr_users+1);
+	}
+
+	/* Writeback the journal superblock */
+	if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf)))
+		return retval;
+
+	fs->super->s_journal_inum = 0;
+	fs->super->s_journal_dev = st.st_rdev;
+	memcpy(fs->super->s_journal_uuid, jsb->s_uuid,
+	       sizeof(fs->super->s_journal_uuid));
+	fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+	ext2fs_mark_super_dirty(fs);
+	return 0;
+}
+
+/*
+ * This function adds a journal inode to a filesystem, using either
+ * POSIX routines if the filesystem is mounted, or using direct I/O
+ * functions if it is not.
+ */
+errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags)
+{
+	errcode_t		retval;
+	ext2_ino_t		journal_ino;
+	struct stat		st;
+	char			jfile[1024];
+	int			fd, mount_flags, f;
+
+	retval = ext2fs_check_mount_point(fs->device_name, &mount_flags,
+					       jfile, sizeof(jfile)-10);
+	if (retval)
+		return retval;
+
+	if (mount_flags & EXT2_MF_MOUNTED) {
+		strcat(jfile, "/.journal");
+
+		/*
+		 * If .../.journal already exists, make sure any
+		 * immutable or append-only flags are cleared.
+		 */
+#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
+		(void) chflags (jfile, 0);
+#else
+#if HAVE_EXT2_IOCTLS
+		fd = open(jfile, O_RDONLY);
+		if (fd >= 0) {
+			f = 0;
+			ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+			close(fd);
+		}
+#endif
+#endif
+
+		/* Create the journal file */
+		if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0)
+			return errno;
+
+		if ((retval = write_journal_file(fs, jfile, size, flags)))
+			goto errout;
+
+		/* Get inode number of the journal file */
+		if (fstat(fd, &st) < 0)
+			goto errout;
+
+#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
+		retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE);
+#else
+#if HAVE_EXT2_IOCTLS
+		f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL;
+		retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+#endif
+#endif
+		if (retval)
+			goto errout;
+
+		close(fd);
+		journal_ino = st.st_ino;
+	} else {
+		journal_ino = EXT2_JOURNAL_INO;
+		if ((retval = write_journal_inode(fs, journal_ino,
+						  size, flags)))
+			return retval;
+	}
+
+	fs->super->s_journal_inum = journal_ino;
+	fs->super->s_journal_dev = 0;
+	memset(fs->super->s_journal_uuid, 0,
+	       sizeof(fs->super->s_journal_uuid));
+	fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+
+	ext2fs_mark_super_dirty(fs);
+	return 0;
+errout:
+	close(fd);
+	return retval;
+}
+
+#ifdef DEBUG
+main(int argc, char **argv)
+{
+	errcode_t	retval;
+	char		*device_name;
+	ext2_filsys	fs;
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s filesystem\n", argv[0]);
+		exit(1);
+	}
+	device_name = argv[1];
+
+	retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0,
+			      unix_io_manager, &fs);
+	if (retval) {
+		com_err(argv[0], retval, "while opening %s", device_name);
+		exit(1);
+	}
+
+	retval = ext2fs_add_journal_inode(fs, 1024);
+	if (retval) {
+		com_err(argv[0], retval, "while adding journal to %s",
+			device_name);
+		exit(1);
+	}
+	retval = ext2fs_flush(fs);
+	if (retval) {
+		printf("Warning, had trouble writing out superblocks.\n");
+	}
+	ext2fs_close(fs);
+	exit(0);
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/namei.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/namei.c
new file mode 100644
index 0000000..1824461
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/namei.c
@@ -0,0 +1,204 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * namei.c --- ext2fs directory lookup operations
+ *
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* #define NAMEI_DEBUG */
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
+			    const char *pathname, size_t pathlen, int follow,
+			    int link_count, char *buf, ext2_ino_t *res_inode);
+
+static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
+			     ext2_ino_t inode, int link_count,
+			     char *buf, ext2_ino_t *res_inode)
+{
+	char *pathname;
+	char *buffer = NULL;
+	errcode_t retval;
+	struct ext2_inode ei;
+
+#ifdef NAMEI_DEBUG
+	printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n",
+	       root, dir, inode, link_count);
+
+#endif
+	retval = ext2fs_read_inode (fs, inode, &ei);
+	if (retval) return retval;
+	if (!LINUX_S_ISLNK (ei.i_mode)) {
+		*res_inode = inode;
+		return 0;
+	}
+	if (link_count++ > 5) {
+		return EXT2_ET_SYMLINK_LOOP;
+	}
+	if (ext2fs_inode_data_blocks(fs, &ei)) {
+		retval = ext2fs_get_mem(fs->blocksize, &buffer);
+		if (retval)
+			return retval;
+		retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer);
+		if (retval) {
+			ext2fs_free_mem(&buffer);
+			return retval;
+		}
+		pathname = buffer;
+	} else
+		pathname = (char *)&(ei.i_block[0]);
+	retval = open_namei(fs, root, dir, pathname, ei.i_size, 1,
+			    link_count, buf, res_inode);
+	ext2fs_free_mem(&buffer);
+	return retval;
+}
+
+/*
+ * This routine interprets a pathname in the context of the current
+ * directory and the root directory, and returns the inode of the
+ * containing directory, and a pointer to the filename of the file
+ * (pointing into the pathname) and the length of the filename.
+ */
+static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
+			   const char *pathname, int pathlen,
+			   int link_count, char *buf,
+			   const char **name, int *namelen,
+			   ext2_ino_t *res_inode)
+{
+	char c;
+	const char *thisname;
+	int len;
+	ext2_ino_t inode;
+	errcode_t retval;
+
+	if ((c = *pathname) == '/') {
+		dir = root;
+		pathname++;
+		pathlen--;
+	}
+	while (1) {
+		thisname = pathname;
+		for (len=0; --pathlen >= 0;len++) {
+			c = *(pathname++);
+			if (c == '/')
+				break;
+		}
+		if (pathlen < 0)
+			break;
+		retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode);
+		if (retval) return retval;
+		retval = follow_link (fs, root, dir, inode,
+				      link_count, buf, &dir);
+		if (retval) return retval;
+	}
+	*name = thisname;
+	*namelen = len;
+	*res_inode = dir;
+	return 0;
+}
+
+static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
+			    const char *pathname, size_t pathlen, int follow,
+			    int link_count, char *buf, ext2_ino_t *res_inode)
+{
+	const char *basename;
+	int namelen;
+	ext2_ino_t dir, inode;
+	errcode_t retval;
+
+#ifdef NAMEI_DEBUG
+	printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n",
+	       root, base, pathlen, pathname, link_count);
+#endif
+	retval = dir_namei(fs, root, base, pathname, pathlen,
+			   link_count, buf, &basename, &namelen, &dir);
+	if (retval) return retval;
+	if (!namelen) {                     /* special case: '/usr/' etc */
+		*res_inode=dir;
+		return 0;
+	}
+	retval = ext2fs_lookup (fs, dir, basename, namelen, buf, &inode);
+	if (retval)
+		return retval;
+	if (follow) {
+		retval = follow_link(fs, root, dir, inode, link_count,
+				     buf, &inode);
+		if (retval)
+			return retval;
+	}
+#ifdef NAMEI_DEBUG
+	printf("open_namei: (link_count=%d) returns %lu\n",
+	       link_count, inode);
+#endif
+	*res_inode = inode;
+	return 0;
+}
+
+errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+		       const char *name, ext2_ino_t *inode)
+{
+	char *buf;
+	errcode_t retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_get_mem(fs->blocksize, &buf);
+	if (retval)
+		return retval;
+
+	retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0,
+			    buf, inode);
+
+	ext2fs_free_mem(&buf);
+	return retval;
+}
+
+errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+			      const char *name, ext2_ino_t *inode)
+{
+	char *buf;
+	errcode_t retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_get_mem(fs->blocksize, &buf);
+	if (retval)
+		return retval;
+
+	retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0,
+			    buf, inode);
+
+	ext2fs_free_mem(&buf);
+	return retval;
+}
+
+errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+			ext2_ino_t inode, ext2_ino_t *res_inode)
+{
+	char *buf;
+	errcode_t retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_get_mem(fs->blocksize, &buf);
+	if (retval)
+		return retval;
+
+	retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode);
+
+	ext2fs_free_mem(&buf);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c
new file mode 100644
index 0000000..9f15662
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/newdir.c
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * newdir.c --- create a new directory block
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef EXT2_FT_DIR
+#define EXT2_FT_DIR		2
+#endif
+
+/*
+ * Create new directory block
+ */
+errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
+			       ext2_ino_t parent_ino, char **block)
+{
+	struct ext2_dir_entry	*dir = NULL;
+	errcode_t		retval;
+	char			*buf;
+	int			rec_len;
+	int			filetype = 0;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	retval = ext2fs_get_mem(fs->blocksize, &buf);
+	if (retval)
+		return retval;
+	memset(buf, 0, fs->blocksize);
+	dir = (struct ext2_dir_entry *) buf;
+	dir->rec_len = fs->blocksize;
+
+	if (dir_ino) {
+		if (fs->super->s_feature_incompat &
+		    EXT2_FEATURE_INCOMPAT_FILETYPE)
+			filetype = EXT2_FT_DIR << 8;
+		/*
+		 * Set up entry for '.'
+		 */
+		dir->inode = dir_ino;
+		dir->name_len = 1 | filetype;
+		dir->name[0] = '.';
+		rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1);
+		dir->rec_len = EXT2_DIR_REC_LEN(1);
+
+		/*
+		 * Set up entry for '..'
+		 */
+		dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
+		dir->rec_len = rec_len;
+		dir->inode = parent_ino;
+		dir->name_len = 2 | filetype;
+		dir->name[0] = '.';
+		dir->name[1] = '.';
+	}
+	*block = buf;
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c
new file mode 100644
index 0000000..1b27119
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/openfs.c
@@ -0,0 +1,330 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * openfs.c --- open an ext2 filesystem
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+
+
+#include "ext2fs.h"
+#include "e2image.h"
+
+blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
+{
+	int	bg;
+	int	has_super = 0;
+	int	ret_blk;
+
+	if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+	    (i < fs->super->s_first_meta_bg))
+		return (group_block + i + 1);
+
+	bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i;
+	if (ext2fs_bg_has_super(fs, bg))
+		has_super = 1;
+	ret_blk = (fs->super->s_first_data_block + has_super +
+		   (bg * fs->super->s_blocks_per_group));
+	/*
+	 * If group_block is not the normal value, we're trying to use
+	 * the backup group descriptors and superblock --- so use the
+	 * alternate location of the second block group in the
+	 * metablock group.  Ideally we should be testing each bg
+	 * descriptor block individually for correctness, but we don't
+	 * have the infrastructure in place to do that.
+	 */
+	if (group_block != fs->super->s_first_data_block &&
+	    ((ret_blk + fs->super->s_blocks_per_group) <
+	     fs->super->s_blocks_count))
+		ret_blk += fs->super->s_blocks_per_group;
+	return ret_blk;
+}
+
+errcode_t ext2fs_open(const char *name, int flags, int superblock,
+		      unsigned int block_size, io_manager manager,
+		      ext2_filsys *ret_fs)
+{
+	return ext2fs_open2(name, 0, flags, superblock, block_size,
+			    manager, ret_fs);
+}
+
+/*
+ *  Note: if superblock is non-zero, block-size must also be non-zero.
+ *	Superblock and block_size can be zero to use the default size.
+ *
+ * Valid flags for ext2fs_open()
+ *
+ *	EXT2_FLAG_RW	- Open the filesystem for read/write.
+ *	EXT2_FLAG_FORCE - Open the filesystem even if some of the
+ *				features aren't supported.
+ *	EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device
+ */
+errcode_t ext2fs_open2(const char *name, const char *io_options,
+		       int flags, int superblock,
+		       unsigned int block_size, io_manager manager,
+		       ext2_filsys *ret_fs)
+{
+	ext2_filsys	fs;
+	errcode_t	retval;
+	unsigned long	i;
+	int		groups_per_block, blocks_per_group;
+	blk_t		group_block, blk;
+	char		*dest, *cp;
+#if BB_BIG_ENDIAN
+	int j;
+	struct ext2_group_desc *gdp;
+#endif
+
+	EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER);
+
+	retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+	if (retval)
+		return retval;
+
+	memset(fs, 0, sizeof(struct struct_ext2_filsys));
+	fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
+	fs->flags = flags;
+	fs->umask = 022;
+	retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
+	if (retval)
+		goto cleanup;
+	strcpy(fs->device_name, name);
+	cp = strchr(fs->device_name, '?');
+	if (!io_options && cp) {
+		*cp++ = 0;
+		io_options = cp;
+	}
+
+	retval = manager->open(fs->device_name,
+			       (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0,
+			       &fs->io);
+	if (retval)
+		goto cleanup;
+	if (io_options &&
+	    (retval = io_channel_set_options(fs->io, io_options)))
+		goto cleanup;
+	fs->image_io = fs->io;
+	fs->io->app_data = fs;
+	retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
+	if (retval)
+		goto cleanup;
+	if (flags & EXT2_FLAG_IMAGE_FILE) {
+		retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr),
+					&fs->image_header);
+		if (retval)
+			goto cleanup;
+		retval = io_channel_read_blk(fs->io, 0,
+					     -(int)sizeof(struct ext2_image_hdr),
+					     fs->image_header);
+		if (retval)
+			goto cleanup;
+		if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE)
+			return EXT2_ET_MAGIC_E2IMAGE;
+		superblock = 1;
+		block_size = fs->image_header->fs_blocksize;
+	}
+
+	/*
+	 * If the user specifies a specific block # for the
+	 * superblock, then he/she must also specify the block size!
+	 * Otherwise, read the master superblock located at offset
+	 * SUPERBLOCK_OFFSET from the start of the partition.
+	 *
+	 * Note: we only save a backup copy of the superblock if we
+	 * are reading the superblock from the primary superblock location.
+	 */
+	if (superblock) {
+		if (!block_size) {
+			retval = EXT2_ET_INVALID_ARGUMENT;
+			goto cleanup;
+		}
+		io_channel_set_blksize(fs->io, block_size);
+		group_block = superblock;
+		fs->orig_super = 0;
+	} else {
+		io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
+		superblock = 1;
+		group_block = 0;
+		retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
+		if (retval)
+			goto cleanup;
+	}
+	retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE,
+				     fs->super);
+	if (retval)
+		goto cleanup;
+	if (fs->orig_super)
+		memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);
+
+#if BB_BIG_ENDIAN
+	if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ||
+	    (fs->flags & EXT2_FLAG_SWAP_BYTES)) {
+		fs->flags |= EXT2_FLAG_SWAP_BYTES;
+
+		ext2fs_swap_super(fs->super);
+	}
+#endif
+
+	if (fs->super->s_magic != EXT2_SUPER_MAGIC) {
+		retval = EXT2_ET_BAD_MAGIC;
+		goto cleanup;
+	}
+	if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) {
+		retval = EXT2_ET_REV_TOO_HIGH;
+		goto cleanup;
+	}
+
+	/*
+	 * Check for feature set incompatibility
+	 */
+	if (!(flags & EXT2_FLAG_FORCE)) {
+		if (fs->super->s_feature_incompat &
+		    ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
+			retval = EXT2_ET_UNSUPP_FEATURE;
+			goto cleanup;
+		}
+		if ((flags & EXT2_FLAG_RW) &&
+		    (fs->super->s_feature_ro_compat &
+		     ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) {
+			retval = EXT2_ET_RO_UNSUPP_FEATURE;
+			goto cleanup;
+		}
+		if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) &&
+		    (fs->super->s_feature_incompat &
+		     EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+			retval = EXT2_ET_UNSUPP_FEATURE;
+			goto cleanup;
+		}
+	}
+
+	fs->blocksize = EXT2_BLOCK_SIZE(fs->super);
+	if (fs->blocksize == 0) {
+		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+		goto cleanup;
+	}
+	fs->fragsize = EXT2_FRAG_SIZE(fs->super);
+	fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group *
+				       EXT2_INODE_SIZE(fs->super) +
+				       EXT2_BLOCK_SIZE(fs->super) - 1) /
+				      EXT2_BLOCK_SIZE(fs->super));
+	if (block_size) {
+		if (block_size != fs->blocksize) {
+			retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+			goto cleanup;
+		}
+	}
+	/*
+	 * Set the blocksize to the filesystem's blocksize.
+	 */
+	io_channel_set_blksize(fs->io, fs->blocksize);
+
+	/*
+	 * If this is an external journal device, don't try to read
+	 * the group descriptors, because they're not there.
+	 */
+	if (fs->super->s_feature_incompat &
+	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+		fs->group_desc_count = 0;
+		*ret_fs = fs;
+		return 0;
+	}
+
+	/*
+	 * Read group descriptors
+	 */
+	blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super);
+	if (blocks_per_group == 0 ||
+	    blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) ||
+	    fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super)) {
+		retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+		goto cleanup;
+	}
+	fs->group_desc_count = (fs->super->s_blocks_count -
+				fs->super->s_first_data_block +
+				blocks_per_group - 1) / blocks_per_group;
+	fs->desc_blocks = (fs->group_desc_count +
+			   EXT2_DESC_PER_BLOCK(fs->super) - 1)
+		/ EXT2_DESC_PER_BLOCK(fs->super);
+	retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize,
+				&fs->group_desc);
+	if (retval)
+		goto cleanup;
+	if (!group_block)
+		group_block = fs->super->s_first_data_block;
+	dest = (char *) fs->group_desc;
+	groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc);
+	for (i = 0; i < fs->desc_blocks; i++) {
+		blk = ext2fs_descriptor_block_loc(fs, group_block, i);
+		retval = io_channel_read_blk(fs->io, blk, 1, dest);
+		if (retval)
+			goto cleanup;
+#if BB_BIG_ENDIAN
+		if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+			gdp = (struct ext2_group_desc *) dest;
+			for (j=0; j < groups_per_block; j++)
+				ext2fs_swap_group_desc(gdp++);
+		}
+#endif
+		dest += fs->blocksize;
+	}
+
+	*ret_fs = fs;
+	return 0;
+cleanup:
+	ext2fs_free(fs);
+	return retval;
+}
+
+/*
+ * Set/get the filesystem data I/O channel.
+ *
+ * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true.
+ */
+errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io)
+{
+	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+		return EXT2_ET_NOT_IMAGE_FILE;
+	if (old_io) {
+		*old_io = (fs->image_io == fs->io) ? 0 : fs->io;
+	}
+	return 0;
+}
+
+errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io)
+{
+	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+		return EXT2_ET_NOT_IMAGE_FILE;
+	fs->io = new_io ? new_io : fs->image_io;
+	return 0;
+}
+
+errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io)
+{
+	if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+		return EXT2_ET_NOT_IMAGE_FILE;
+	fs->io = fs->image_io = new_io;
+	fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW |
+		EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
+	fs->flags &= ~EXT2_FLAG_IMAGE_FILE;
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c
new file mode 100644
index 0000000..ce77bc9
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/read_bb.c
@@ -0,0 +1,96 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * read_bb --- read the bad blocks inode
+ *
+ * Copyright (C) 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct read_bb_record {
+	ext2_badblocks_list	bb_list;
+	errcode_t	err;
+};
+
+/*
+ * Helper function for ext2fs_read_bb_inode()
+ */
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int mark_bad_block(ext2_filsys fs, blk_t *block_nr,
+			  e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
+			  blk_t ref_block EXT2FS_ATTR((unused)),
+			  int ref_offset EXT2FS_ATTR((unused)),
+			  void *priv_data)
+{
+	struct read_bb_record *rb = (struct read_bb_record *) priv_data;
+
+	if (blockcnt < 0)
+		return 0;
+
+	if ((*block_nr < fs->super->s_first_data_block) ||
+	    (*block_nr >= fs->super->s_blocks_count))
+		return 0;	/* Ignore illegal blocks */
+
+	rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr);
+	if (rb->err)
+		return BLOCK_ABORT;
+	return 0;
+}
+
+/*
+ * Reads the current bad blocks from the bad blocks inode.
+ */
+errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list)
+{
+	errcode_t	retval;
+	struct read_bb_record rb;
+	struct ext2_inode inode;
+	blk_t	numblocks;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!*bb_list) {
+		retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
+		if (retval)
+			return retval;
+		if (inode.i_blocks < 500)
+			numblocks = (inode.i_blocks /
+				     (fs->blocksize / 512)) + 20;
+		else
+			numblocks = 500;
+		retval = ext2fs_badblocks_list_create(bb_list, numblocks);
+		if (retval)
+			return retval;
+	}
+
+	rb.bb_list = *bb_list;
+	rb.err = 0;
+	retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0,
+				      mark_bad_block, &rb);
+	if (retval)
+		return retval;
+
+	return rb.err;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c
new file mode 100644
index 0000000..bf1fc32
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/read_bb_file.c
@@ -0,0 +1,96 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * read_bb_file.c --- read a list of bad blocks from a FILE *
+ *
+ * Copyright (C) 1994, 1995, 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Reads a list of bad blocks from  a FILE *
+ */
+errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f,
+			       ext2_badblocks_list *bb_list,
+			       void *priv_data,
+			       void (*invalid)(ext2_filsys fs,
+					       blk_t blk,
+					       char *badstr,
+					       void *priv_data))
+{
+	errcode_t	retval;
+	blk_t		blockno;
+	int		count;
+	char		buf[128];
+
+	if (fs)
+		EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!*bb_list) {
+		retval = ext2fs_badblocks_list_create(bb_list, 10);
+		if (retval)
+			return retval;
+	}
+
+	while (!feof (f)) {
+		if (fgets(buf, sizeof(buf), f) == NULL)
+			break;
+		count = sscanf(buf, "%u", &blockno);
+		if (count <= 0)
+			continue;
+		if (fs &&
+		    ((blockno < fs->super->s_first_data_block) ||
+		    (blockno >= fs->super->s_blocks_count))) {
+			if (invalid)
+				(invalid)(fs, blockno, buf, priv_data);
+			continue;
+		}
+		retval = ext2fs_badblocks_list_add(*bb_list, blockno);
+		if (retval)
+			return retval;
+	}
+	return 0;
+}
+
+static void call_compat_invalid(ext2_filsys fs, blk_t blk,
+				char *badstr EXT2FS_ATTR((unused)),
+				void *priv_data)
+{
+	void (*invalid)(ext2_filsys, blk_t);
+
+	invalid = (void (*)(ext2_filsys, blk_t)) priv_data;
+	if (invalid)
+		invalid(fs, blk);
+}
+
+
+/*
+ * Reads a list of bad blocks from  a FILE *
+ */
+errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f,
+			      ext2_badblocks_list *bb_list,
+			      void (*invalid)(ext2_filsys fs, blk_t blk))
+{
+	return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid,
+				    call_compat_invalid);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
new file mode 100644
index 0000000..403463a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/res_gdt.c
@@ -0,0 +1,220 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * res_gdt.c --- reserve blocks for growing the group descriptor table
+ *               during online resizing.
+ *
+ * Copyright (C) 2002 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Iterate through the groups which hold BACKUP superblock/GDT copies in an
+ * ext3 filesystem.  The counters should be initialized to 1, 5, and 7 before
+ * calling this for the first time.  In a sparse filesystem it will be the
+ * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
+ * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
+ */
+static unsigned int list_backups(ext2_filsys fs, unsigned int *three,
+				 unsigned int *five, unsigned int *seven)
+{
+	unsigned int *min = three;
+	int mult = 3;
+	unsigned int ret;
+
+	if (!(fs->super->s_feature_ro_compat &
+	      EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
+		ret = *min;
+		*min += 1;
+		return ret;
+	}
+
+	if (*five < *min) {
+		min = five;
+		mult = 5;
+	}
+	if (*seven < *min) {
+		min = seven;
+		mult = 7;
+	}
+
+	ret = *min;
+	*min *= mult;
+
+	return ret;
+}
+
+/*
+ * This code assumes that the reserved blocks have already been marked in-use
+ * during ext2fs_initialize(), so that they are not allocated for other
+ * uses before we can add them to the resize inode (which has to come
+ * after the creation of the inode table).
+ */
+errcode_t ext2fs_create_resize_inode(ext2_filsys fs)
+{
+	errcode_t		retval, retval2;
+	struct ext2_super_block	*sb;
+	struct ext2_inode	inode;
+	__u32			*dindir_buf, *gdt_buf;
+	int			rsv_add;
+	unsigned long long	apb, inode_size;
+	blk_t			dindir_blk, rsv_off, gdt_off, gdt_blk;
+	int			dindir_dirty = 0, inode_dirty = 0;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	sb = fs->super;
+
+	retval = ext2fs_get_mem(2 * fs->blocksize, (void *)&dindir_buf);
+	if (retval)
+		goto out_free;
+	gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);
+
+	retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+	if (retval)
+		goto out_free;
+
+	/* Maximum possible file size (we donly use the dindirect blocks) */
+	apb = EXT2_ADDR_PER_BLOCK(sb);
+	rsv_add = fs->blocksize / 512;
+	if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) {
+#ifdef RES_GDT_DEBUG
+		printf("reading GDT dindir %u\n", dindir_blk);
+#endif
+		retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf);
+		if (retval)
+			goto out_inode;
+	} else {
+		blk_t goal = 3 + sb->s_reserved_gdt_blocks +
+			fs->desc_blocks + fs->inode_blocks_per_group;
+
+		retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk);
+		if (retval)
+			goto out_free;
+		inode.i_mode = LINUX_S_IFREG | 0600;
+		inode.i_links_count = 1;
+		inode.i_block[EXT2_DIND_BLOCK] = dindir_blk;
+		inode.i_blocks = rsv_add;
+		memset(dindir_buf, 0, fs->blocksize);
+#ifdef RES_GDT_DEBUG
+		printf("allocated GDT dindir %u\n", dindir_blk);
+#endif
+		dindir_dirty = inode_dirty = 1;
+		inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS;
+		inode_size *= fs->blocksize;
+		inode.i_size = inode_size & 0xFFFFFFFF;
+		inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF;
+		if (inode.i_size_high) {
+			sb->s_feature_ro_compat |=
+				EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+		}
+		inode.i_ctime = time(NULL);
+	}
+
+	for (rsv_off = 0, gdt_off = fs->desc_blocks,
+	     gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks;
+	     rsv_off < sb->s_reserved_gdt_blocks;
+	     rsv_off++, gdt_off++, gdt_blk++) {
+		unsigned int three = 1, five = 5, seven = 7;
+		unsigned int grp, last = 0;
+		int gdt_dirty = 0;
+
+		gdt_off %= apb;
+		if (!dindir_buf[gdt_off]) {
+			/* FIXME XXX XXX
+			blk_t new_blk;
+
+			retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk);
+			if (retval)
+				goto out_free;
+			if (new_blk != gdt_blk) {
+				// XXX free block
+				retval = -1; // XXX
+			}
+			*/
+			gdt_dirty = dindir_dirty = inode_dirty = 1;
+			memset(gdt_buf, 0, fs->blocksize);
+			dindir_buf[gdt_off] = gdt_blk;
+			inode.i_blocks += rsv_add;
+#ifdef RES_GDT_DEBUG
+			printf("added primary GDT block %u at %u[%u]\n",
+			       gdt_blk, dindir_blk, gdt_off);
+#endif
+		} else if (dindir_buf[gdt_off] == gdt_blk) {
+#ifdef RES_GDT_DEBUG
+			printf("reading primary GDT block %u\n", gdt_blk);
+#endif
+			retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf);
+			if (retval)
+				goto out_dindir;
+		} else {
+#ifdef RES_GDT_DEBUG
+			printf("bad primary GDT %u != %u at %u[%u]\n",
+			       dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off);
+#endif
+			retval = EXT2_ET_RESIZE_INODE_CORRUPT;
+			goto out_dindir;
+		}
+
+		while ((grp = list_backups(fs, &three, &five, &seven)) <
+		       fs->group_desc_count) {
+			blk_t expect = gdt_blk + grp * sb->s_blocks_per_group;
+
+			if (!gdt_buf[last]) {
+#ifdef RES_GDT_DEBUG
+				printf("added backup GDT %u grp %u@%u[%u]\n",
+				       expect, grp, gdt_blk, last);
+#endif
+				gdt_buf[last] = expect;
+				inode.i_blocks += rsv_add;
+				gdt_dirty = inode_dirty = 1;
+			} else if (gdt_buf[last] != expect) {
+#ifdef RES_GDT_DEBUG
+				printf("bad backup GDT %u != %u at %u[%u]\n",
+				       gdt_buf[last], expect, gdt_blk, last);
+#endif
+				retval = EXT2_ET_RESIZE_INODE_CORRUPT;
+				goto out_dindir;
+			}
+			last++;
+		}
+		if (gdt_dirty) {
+#ifdef RES_GDT_DEBUG
+			printf("writing primary GDT block %u\n", gdt_blk);
+#endif
+			retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf);
+			if (retval)
+				goto out_dindir;
+		}
+	}
+
+out_dindir:
+	if (dindir_dirty) {
+		retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf);
+		if (!retval)
+			retval = retval2;
+	}
+out_inode:
+#ifdef RES_GDT_DEBUG
+	printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks,
+	       inode.i_size);
+#endif
+	if (inode_dirty) {
+		inode.i_atime = inode.i_mtime = time(NULL);
+		retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
+		if (!retval)
+			retval = retval2;
+	}
+out_free:
+	ext2fs_free_mem((void *)&dindir_buf);
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c
new file mode 100644
index 0000000..32e87b7
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/rs_bitmap.c
@@ -0,0 +1,106 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rs_bitmap.c --- routine for changing the size of a bitmap
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end,
+				       ext2fs_generic_bitmap bmap)
+{
+	errcode_t	retval;
+	size_t		size, new_size;
+	__u32		bitno;
+
+	if (!bmap)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP);
+
+	/*
+	 * If we're expanding the bitmap, make sure all of the new
+	 * parts of the bitmap are zero.
+	 */
+	if (new_end > bmap->end) {
+		bitno = bmap->real_end;
+		if (bitno > new_end)
+			bitno = new_end;
+		for (; bitno > bmap->end; bitno--)
+			ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap);
+	}
+	if (new_real_end == bmap->real_end) {
+		bmap->end = new_end;
+		return 0;
+	}
+
+	size = ((bmap->real_end - bmap->start) / 8) + 1;
+	new_size = ((new_real_end - bmap->start) / 8) + 1;
+
+	if (size != new_size) {
+		retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap);
+		if (retval)
+			return retval;
+	}
+	if (new_size > size)
+		memset(bmap->bitmap + size, 0, new_size - size);
+
+	bmap->end = new_end;
+	bmap->real_end = new_real_end;
+	return 0;
+}
+
+errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+				     ext2fs_inode_bitmap bmap)
+{
+	errcode_t	retval;
+
+	if (!bmap)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP);
+
+	bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+	retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+					      bmap);
+	bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
+	return retval;
+}
+
+errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+				     ext2fs_block_bitmap bmap)
+{
+	errcode_t	retval;
+
+	if (!bmap)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+	bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+	retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+					      bmap);
+	bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c
new file mode 100644
index 0000000..bba4326
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/rw_bitmaps.c
@@ -0,0 +1,294 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rw_bitmaps.c --- routines to read and write the  inode and block bitmaps.
+ *
+ * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "e2image.h"
+
+#if defined(__powerpc__) && BB_BIG_ENDIAN
+/*
+ * On the PowerPC, the big-endian variant of the ext2 filesystem
+ * has its bitmaps stored as 32-bit words with bit 0 as the LSB
+ * of each word.  Thus a bitmap with only bit 0 set would be, as
+ * a string of bytes, 00 00 00 01 00 ...
+ * To cope with this, we byte-reverse each word of a bitmap if
+ * we have a big-endian filesystem, that is, if we are *not*
+ * byte-swapping other word-sized numbers.
+ */
+#define EXT2_BIG_ENDIAN_BITMAPS
+#endif
+
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+static void ext2fs_swap_bitmap(ext2_filsys fs, char *bitmap, int nbytes)
+{
+	__u32 *p = (__u32 *) bitmap;
+	int n;
+
+	for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
+		*p = ext2fs_swab32(*p);
+}
+#endif
+
+errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs)
+{
+	dgrp_t		i;
+	size_t		nbytes;
+	errcode_t	retval;
+	char * inode_bitmap = fs->inode_map->bitmap;
+	char * bitmap_block = NULL;
+	blk_t		blk;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+	if (!inode_bitmap)
+		return 0;
+	nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
+
+	retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
+	if (retval)
+		return retval;
+	memset(bitmap_block, 0xff, fs->blocksize);
+	for (i = 0; i < fs->group_desc_count; i++) {
+		memcpy(bitmap_block, inode_bitmap, nbytes);
+		blk = fs->group_desc[i].bg_inode_bitmap;
+		if (blk) {
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+			if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+			      (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
+				ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
+#endif
+			retval = io_channel_write_blk(fs->io, blk, 1,
+						      bitmap_block);
+			if (retval)
+				return EXT2_ET_INODE_BITMAP_WRITE;
+		}
+		inode_bitmap += nbytes;
+	}
+	fs->flags &= ~EXT2_FLAG_IB_DIRTY;
+	ext2fs_free_mem(&bitmap_block);
+	return 0;
+}
+
+errcode_t ext2fs_write_block_bitmap (ext2_filsys fs)
+{
+	dgrp_t		i;
+	unsigned int	j;
+	int		nbytes;
+	unsigned int	nbits;
+	errcode_t	retval;
+	char * block_bitmap = fs->block_map->bitmap;
+	char * bitmap_block = NULL;
+	blk_t		blk;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+	if (!block_bitmap)
+		return 0;
+	nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+	retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
+	if (retval)
+		return retval;
+	memset(bitmap_block, 0xff, fs->blocksize);
+	for (i = 0; i < fs->group_desc_count; i++) {
+		memcpy(bitmap_block, block_bitmap, nbytes);
+		if (i == fs->group_desc_count - 1) {
+			/* Force bitmap padding for the last group */
+			nbits = ((fs->super->s_blocks_count
+				  - fs->super->s_first_data_block)
+				 % EXT2_BLOCKS_PER_GROUP(fs->super));
+			if (nbits)
+				for (j = nbits; j < fs->blocksize * 8; j++)
+					ext2fs_set_bit(j, bitmap_block);
+		}
+		blk = fs->group_desc[i].bg_block_bitmap;
+		if (blk) {
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+			if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+			      (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
+				ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
+#endif
+			retval = io_channel_write_blk(fs->io, blk, 1,
+						      bitmap_block);
+			if (retval)
+				return EXT2_ET_BLOCK_BITMAP_WRITE;
+		}
+		block_bitmap += nbytes;
+	}
+	fs->flags &= ~EXT2_FLAG_BB_DIRTY;
+	ext2fs_free_mem(&bitmap_block);
+	return 0;
+}
+
+static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
+{
+	dgrp_t i;
+	char *block_bitmap = NULL, *inode_bitmap = NULL;
+	char *buf;
+	errcode_t retval;
+	int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+	int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8;
+	blk_t	blk;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	fs->write_bitmaps = ext2fs_write_bitmaps;
+
+	retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
+	if (retval)
+		return retval;
+	if (do_block) {
+		ext2fs_free_block_bitmap(fs->block_map);
+		sprintf(buf, "block bitmap for %s", fs->device_name);
+		retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
+		if (retval)
+			goto cleanup;
+		block_bitmap = fs->block_map->bitmap;
+	}
+	if (do_inode) {
+		ext2fs_free_inode_bitmap(fs->inode_map);
+		sprintf(buf, "inode bitmap for %s", fs->device_name);
+		retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
+		if (retval)
+			goto cleanup;
+		inode_bitmap = fs->inode_map->bitmap;
+	}
+	ext2fs_free_mem(&buf);
+
+	if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+		if (inode_bitmap) {
+			blk = (fs->image_header->offset_inodemap /
+			       fs->blocksize);
+			retval = io_channel_read_blk(fs->image_io, blk,
+			     -(inode_nbytes * fs->group_desc_count),
+			     inode_bitmap);
+			if (retval)
+				goto cleanup;
+		}
+		if (block_bitmap) {
+			blk = (fs->image_header->offset_blockmap /
+			       fs->blocksize);
+			retval = io_channel_read_blk(fs->image_io, blk,
+			     -(block_nbytes * fs->group_desc_count),
+			     block_bitmap);
+			if (retval)
+				goto cleanup;
+		}
+		return 0;
+	}
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		if (block_bitmap) {
+			blk = fs->group_desc[i].bg_block_bitmap;
+			if (blk) {
+				retval = io_channel_read_blk(fs->io, blk,
+					     -block_nbytes, block_bitmap);
+				if (retval) {
+					retval = EXT2_ET_BLOCK_BITMAP_READ;
+					goto cleanup;
+				}
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+				if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+				      (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
+					ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes);
+#endif
+			} else
+				memset(block_bitmap, 0, block_nbytes);
+			block_bitmap += block_nbytes;
+		}
+		if (inode_bitmap) {
+			blk = fs->group_desc[i].bg_inode_bitmap;
+			if (blk) {
+				retval = io_channel_read_blk(fs->io, blk,
+					     -inode_nbytes, inode_bitmap);
+				if (retval) {
+					retval = EXT2_ET_INODE_BITMAP_READ;
+					goto cleanup;
+				}
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+				if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+				      (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
+					ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes);
+#endif
+			} else
+				memset(inode_bitmap, 0, inode_nbytes);
+			inode_bitmap += inode_nbytes;
+		}
+	}
+	return 0;
+
+cleanup:
+	if (do_block) {
+		ext2fs_free_mem(&fs->block_map);
+	}
+	if (do_inode) {
+		ext2fs_free_mem(&fs->inode_map);
+	}
+	ext2fs_free_mem(&buf);
+	return retval;
+}
+
+errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs)
+{
+	return read_bitmaps(fs, 1, 0);
+}
+
+errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
+{
+	return read_bitmaps(fs, 0, 1);
+}
+
+errcode_t ext2fs_read_bitmaps(ext2_filsys fs)
+{
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (fs->inode_map && fs->block_map)
+		return 0;
+
+	return read_bitmaps(fs, !fs->inode_map, !fs->block_map);
+}
+
+errcode_t ext2fs_write_bitmaps(ext2_filsys fs)
+{
+	errcode_t	retval;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (fs->block_map && ext2fs_test_bb_dirty(fs)) {
+		retval = ext2fs_write_block_bitmap(fs);
+		if (retval)
+			return retval;
+	}
+	if (fs->inode_map && ext2fs_test_ib_dirty(fs)) {
+		retval = ext2fs_write_inode_bitmap(fs);
+		if (retval)
+			return retval;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c
new file mode 100644
index 0000000..b3d3071
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/sparse.c
@@ -0,0 +1,79 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sparse.c --- find the groups in an ext2 filesystem with metadata backups
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ * Copyright (C) 2002 Andreas Dilger.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int test_root(int a, int b)
+{
+	if (a == 0)
+		return 1;
+	while (1) {
+		if (a == 1)
+			return 1;
+		if (a % b)
+			return 0;
+		a = a / b;
+	}
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+{
+	if (!(fs->super->s_feature_ro_compat &
+	      EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+		return 1;
+
+	if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
+	    test_root(group_block, 7))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Iterate through the groups which hold BACKUP superblock/GDT copies in an
+ * ext3 filesystem.  The counters should be initialized to 1, 5, and 7 before
+ * calling this for the first time.  In a sparse filesystem it will be the
+ * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
+ * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
+ */
+unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three,
+				 unsigned int *five, unsigned int *seven)
+{
+	unsigned int *min = three;
+	int mult = 3;
+	unsigned int ret;
+
+	if (!(fs->super->s_feature_ro_compat &
+	      EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
+		ret = *min;
+		*min += 1;
+		return ret;
+	}
+
+	if (*five < *min) {
+		min = five;
+		mult = 5;
+	}
+	if (*seven < *min) {
+		min = seven;
+		mult = 7;
+	}
+
+	ret = *min;
+	*min *= mult;
+
+	return ret;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c
new file mode 100644
index 0000000..07b757a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/swapfs.c
@@ -0,0 +1,234 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * swapfs.c --- swap ext2 filesystem data structures
+ *
+ * Copyright (C) 1995, 1996, 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "ext2_ext_attr.h"
+
+#if BB_BIG_ENDIAN
+void ext2fs_swap_super(struct ext2_super_block * sb)
+{
+	int i;
+	sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count);
+	sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count);
+	sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count);
+	sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count);
+	sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count);
+	sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block);
+	sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size);
+	sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size);
+	sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group);
+	sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group);
+	sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group);
+	sb->s_mtime = ext2fs_swab32(sb->s_mtime);
+	sb->s_wtime = ext2fs_swab32(sb->s_wtime);
+	sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count);
+	sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count);
+	sb->s_magic = ext2fs_swab16(sb->s_magic);
+	sb->s_state = ext2fs_swab16(sb->s_state);
+	sb->s_errors = ext2fs_swab16(sb->s_errors);
+	sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level);
+	sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck);
+	sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval);
+	sb->s_creator_os = ext2fs_swab32(sb->s_creator_os);
+	sb->s_rev_level = ext2fs_swab32(sb->s_rev_level);
+	sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid);
+	sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid);
+	sb->s_first_ino = ext2fs_swab32(sb->s_first_ino);
+	sb->s_inode_size = ext2fs_swab16(sb->s_inode_size);
+	sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr);
+	sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat);
+	sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat);
+	sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat);
+	sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap);
+	sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks);
+	sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum);
+	sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev);
+	sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan);
+	sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts);
+	sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg);
+	sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time);
+	for (i=0; i < 4; i++)
+		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)
+{
+	gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap);
+	gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap);
+	gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table);
+	gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count);
+	gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count);
+	gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count);
+}
+
+void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)
+{
+	struct ext2_ext_attr_header *from_header =
+		(struct ext2_ext_attr_header *)from;
+	struct ext2_ext_attr_header *to_header =
+		(struct ext2_ext_attr_header *)to;
+	struct ext2_ext_attr_entry *from_entry, *to_entry;
+	char *from_end = (char *)from_header + bufsize;
+	int n;
+
+	if (to_header != from_header)
+		memcpy(to_header, from_header, bufsize);
+
+	from_entry = (struct ext2_ext_attr_entry *)from_header;
+	to_entry   = (struct ext2_ext_attr_entry *)to_header;
+
+	if (has_header) {
+		to_header->h_magic    = ext2fs_swab32(from_header->h_magic);
+		to_header->h_blocks   = ext2fs_swab32(from_header->h_blocks);
+		to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
+		for (n=0; n<4; n++)
+			to_header->h_reserved[n] =
+				ext2fs_swab32(from_header->h_reserved[n]);
+		from_entry = (struct ext2_ext_attr_entry *)(from_header+1);
+		to_entry   = (struct ext2_ext_attr_entry *)(to_header+1);
+	}
+
+	while ((char *)from_entry < from_end && *(__u32 *)from_entry) {
+		to_entry->e_value_offs  =
+			ext2fs_swab16(from_entry->e_value_offs);
+		to_entry->e_value_block =
+			ext2fs_swab32(from_entry->e_value_block);
+		to_entry->e_value_size  =
+			ext2fs_swab32(from_entry->e_value_size);
+		from_entry = EXT2_EXT_ATTR_NEXT(from_entry);
+		to_entry   = EXT2_EXT_ATTR_NEXT(to_entry);
+	}
+}
+
+void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
+			    struct ext2_inode_large *f, int hostorder,
+			    int bufsize)
+{
+	unsigned i;
+	int islnk = 0;
+	__u32 *eaf, *eat;
+
+	if (hostorder && LINUX_S_ISLNK(f->i_mode))
+		islnk = 1;
+	t->i_mode = ext2fs_swab16(f->i_mode);
+	if (!hostorder && LINUX_S_ISLNK(t->i_mode))
+		islnk = 1;
+	t->i_uid = ext2fs_swab16(f->i_uid);
+	t->i_size = ext2fs_swab32(f->i_size);
+	t->i_atime = ext2fs_swab32(f->i_atime);
+	t->i_ctime = ext2fs_swab32(f->i_ctime);
+	t->i_mtime = ext2fs_swab32(f->i_mtime);
+	t->i_dtime = ext2fs_swab32(f->i_dtime);
+	t->i_gid = ext2fs_swab16(f->i_gid);
+	t->i_links_count = ext2fs_swab16(f->i_links_count);
+	t->i_blocks = ext2fs_swab32(f->i_blocks);
+	t->i_flags = ext2fs_swab32(f->i_flags);
+	t->i_file_acl = ext2fs_swab32(f->i_file_acl);
+	t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
+	if (!islnk || ext2fs_inode_data_blocks(fs, (struct ext2_inode *)t)) {
+		for (i = 0; i < EXT2_N_BLOCKS; i++)
+			t->i_block[i] = ext2fs_swab32(f->i_block[i]);
+	} else if (t != f) {
+		for (i = 0; i < EXT2_N_BLOCKS; i++)
+			t->i_block[i] = f->i_block[i];
+	}
+	t->i_generation = ext2fs_swab32(f->i_generation);
+	t->i_faddr = ext2fs_swab32(f->i_faddr);
+
+	switch (fs->super->s_creator_os) {
+	case EXT2_OS_LINUX:
+		t->osd1.linux1.l_i_reserved1 =
+			ext2fs_swab32(f->osd1.linux1.l_i_reserved1);
+		t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag;
+		t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize;
+		t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1);
+		t->osd2.linux2.l_i_uid_high =
+		  ext2fs_swab16 (f->osd2.linux2.l_i_uid_high);
+		t->osd2.linux2.l_i_gid_high =
+		  ext2fs_swab16 (f->osd2.linux2.l_i_gid_high);
+		t->osd2.linux2.l_i_reserved2 =
+			ext2fs_swab32(f->osd2.linux2.l_i_reserved2);
+		break;
+	case EXT2_OS_HURD:
+		t->osd1.hurd1.h_i_translator =
+		  ext2fs_swab32 (f->osd1.hurd1.h_i_translator);
+		t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag;
+		t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize;
+		t->osd2.hurd2.h_i_mode_high =
+		  ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high);
+		t->osd2.hurd2.h_i_uid_high =
+		  ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high);
+		t->osd2.hurd2.h_i_gid_high =
+		  ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high);
+		t->osd2.hurd2.h_i_author =
+		  ext2fs_swab32 (f->osd2.hurd2.h_i_author);
+		break;
+	case EXT2_OS_MASIX:
+		t->osd1.masix1.m_i_reserved1 =
+			ext2fs_swab32(f->osd1.masix1.m_i_reserved1);
+		t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag;
+		t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize;
+		t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1);
+		t->osd2.masix2.m_i_reserved2[0] =
+			ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]);
+		t->osd2.masix2.m_i_reserved2[1] =
+			ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]);
+		break;
+	}
+
+	if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16)))
+		return; /* no i_extra_isize field */
+
+	t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
+	if (t->i_extra_isize > EXT2_INODE_SIZE(fs->super) -
+				sizeof(struct ext2_inode)) {
+		/* this is error case: i_extra_size is too large */
+		return;
+	}
+
+	i = sizeof(struct ext2_inode) + t->i_extra_isize + sizeof(__u32);
+	if (bufsize < (int) i)
+		return; /* no space for EA magic */
+
+	eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) +
+					f->i_extra_isize);
+
+	if (ext2fs_swab32(*eaf) != EXT2_EXT_ATTR_MAGIC)
+		return; /* it seems no magic here */
+
+	eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) +
+					f->i_extra_isize);
+	*eat = ext2fs_swab32(*eaf);
+
+	/* convert EA(s) */
+	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,
+		       struct ext2_inode *f, int hostorder)
+{
+	ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t,
+				(struct ext2_inode_large *) f, hostorder,
+				sizeof(struct ext2_inode));
+}
+
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c
new file mode 100644
index 0000000..3d40d9a
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/test_io.c
@@ -0,0 +1,380 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * test_io.c --- This is the Test I/O interface.
+ *
+ * Copyright (C) 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+	  if ((struct)->magic != (code)) return (code)
+
+struct test_private_data {
+	int	magic;
+	io_channel real;
+	int flags;
+	FILE *outfile;
+	unsigned long block;
+	int read_abort_count, write_abort_count;
+	void (*read_blk)(unsigned long block, int count, errcode_t err);
+	void (*write_blk)(unsigned long block, int count, errcode_t err);
+	void (*set_blksize)(int blksize, errcode_t err);
+	void (*write_byte)(unsigned long block, int count, errcode_t err);
+};
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel);
+static errcode_t test_close(io_channel channel);
+static errcode_t test_set_blksize(io_channel channel, int blksize);
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+			       int count, void *data);
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+				int count, const void *data);
+static errcode_t test_flush(io_channel channel);
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+				 int count, const void *buf);
+static errcode_t test_set_option(io_channel channel, const char *option,
+				 const char *arg);
+
+static struct struct_io_manager struct_test_manager = {
+	EXT2_ET_MAGIC_IO_MANAGER,
+	"Test I/O Manager",
+	test_open,
+	test_close,
+	test_set_blksize,
+	test_read_blk,
+	test_write_blk,
+	test_flush,
+	test_write_byte,
+	test_set_option
+};
+
+io_manager test_io_manager = &struct_test_manager;
+
+/*
+ * These global variable can be set by the test program as
+ * necessary *before* calling test_open
+ */
+io_manager test_io_backing_manager = 0;
+void (*test_io_cb_read_blk)
+	(unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_write_blk)
+	(unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_set_blksize)
+	(int blksize, errcode_t err) = 0;
+void (*test_io_cb_write_byte)
+	(unsigned long block, int count, errcode_t err) = 0;
+
+/*
+ * Test flags
+ */
+#define TEST_FLAG_READ			0x01
+#define TEST_FLAG_WRITE			0x02
+#define TEST_FLAG_SET_BLKSIZE		0x04
+#define TEST_FLAG_FLUSH			0x08
+#define TEST_FLAG_DUMP			0x10
+#define TEST_FLAG_SET_OPTION		0x20
+
+static void test_dump_block(io_channel channel,
+			    struct test_private_data *data,
+			    unsigned long block, const void *buf)
+{
+	const unsigned char *cp;
+	FILE *f = data->outfile;
+	int	i;
+	unsigned long	cksum = 0;
+
+	for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+		cksum += *cp;
+	}
+	fprintf(f, "Contents of block %lu, checksum %08lu:\n", block, cksum);
+	for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+		if ((i % 16) == 0)
+			fprintf(f, "%04x: ", i);
+		fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' ');
+	}
+}
+
+static void test_abort(io_channel channel, unsigned long block)
+{
+	struct test_private_data *data;
+	FILE *f;
+
+	data = (struct test_private_data *) channel->private_data;
+	f = data->outfile;
+	test_flush(channel);
+
+	fprintf(f, "Aborting due to I/O to block %lu\n", block);
+	fflush(f);
+	abort();
+}
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel)
+{
+	io_channel	io = NULL;
+	struct test_private_data *data = NULL;
+	errcode_t	retval;
+	char		*value;
+
+	if (name == 0)
+		return EXT2_ET_BAD_DEVICE_NAME;
+	retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+	if (retval)
+		return retval;
+	memset(io, 0, sizeof(struct struct_io_channel));
+	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+	retval = ext2fs_get_mem(sizeof(struct test_private_data), &data);
+	if (retval) {
+		retval = EXT2_ET_NO_MEMORY;
+		goto cleanup;
+	}
+	io->manager = test_io_manager;
+	retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+	if (retval)
+		goto cleanup;
+
+	strcpy(io->name, name);
+	io->private_data = data;
+	io->block_size = 1024;
+	io->read_error = 0;
+	io->write_error = 0;
+	io->refcount = 1;
+
+	memset(data, 0, sizeof(struct test_private_data));
+	data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
+	if (test_io_backing_manager) {
+		retval = test_io_backing_manager->open(name, flags,
+						       &data->real);
+		if (retval)
+			goto cleanup;
+	} else
+		data->real = 0;
+	data->read_blk =	test_io_cb_read_blk;
+	data->write_blk =	test_io_cb_write_blk;
+	data->set_blksize =	test_io_cb_set_blksize;
+	data->write_byte =	test_io_cb_write_byte;
+
+	data->outfile = NULL;
+	if ((value = getenv("TEST_IO_LOGFILE")) != NULL)
+		data->outfile = fopen_for_write(value);
+	if (!data->outfile)
+		data->outfile = stderr;
+
+	data->flags = 0;
+	if ((value = getenv("TEST_IO_FLAGS")) != NULL)
+		data->flags = strtoul(value, NULL, 0);
+
+	data->block = 0;
+	if ((value = getenv("TEST_IO_BLOCK")) != NULL)
+		data->block = strtoul(value, NULL, 0);
+
+	data->read_abort_count = 0;
+	if ((value = getenv("TEST_IO_READ_ABORT")) != NULL)
+		data->read_abort_count = strtoul(value, NULL, 0);
+
+	data->write_abort_count = 0;
+	if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL)
+		data->write_abort_count = strtoul(value, NULL, 0);
+
+	*channel = io;
+	return 0;
+
+cleanup:
+	ext2fs_free_mem(&io);
+	ext2fs_free_mem(&data);
+	return retval;
+}
+
+static errcode_t test_close(io_channel channel)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (--channel->refcount > 0)
+		return 0;
+
+	if (data->real)
+		retval = io_channel_close(data->real);
+
+	if (data->outfile && data->outfile != stderr)
+		fclose(data->outfile);
+
+	ext2fs_free_mem(&channel->private_data);
+	ext2fs_free_mem(&channel->name);
+	ext2fs_free_mem(&channel);
+	return retval;
+}
+
+static errcode_t test_set_blksize(io_channel channel, int blksize)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_set_blksize(data->real, blksize);
+	if (data->set_blksize)
+		data->set_blksize(blksize, retval);
+	if (data->flags & TEST_FLAG_SET_BLKSIZE)
+		fprintf(data->outfile,
+			"Test_io: set_blksize(%d) returned %s\n",
+			blksize, retval ? error_message(retval) : "OK");
+	channel->block_size = blksize;
+	return retval;
+}
+
+
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+			       int count, void *buf)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_read_blk(data->real, block, count, buf);
+	if (data->read_blk)
+		data->read_blk(block, count, retval);
+	if (data->flags & TEST_FLAG_READ)
+		fprintf(data->outfile,
+			"Test_io: read_blk(%lu, %d) returned %s\n",
+			block, count, retval ? error_message(retval) : "OK");
+	if (data->block && data->block == block) {
+		if (data->flags & TEST_FLAG_DUMP)
+			test_dump_block(channel, data, block, buf);
+		if (--data->read_abort_count == 0)
+			test_abort(channel, block);
+	}
+	return retval;
+}
+
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+			       int count, const void *buf)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_write_blk(data->real, block, count, buf);
+	if (data->write_blk)
+		data->write_blk(block, count, retval);
+	if (data->flags & TEST_FLAG_WRITE)
+		fprintf(data->outfile,
+			"Test_io: write_blk(%lu, %d) returned %s\n",
+			block, count, retval ? error_message(retval) : "OK");
+	if (data->block && data->block == block) {
+		if (data->flags & TEST_FLAG_DUMP)
+			test_dump_block(channel, data, block, buf);
+		if (--data->write_abort_count == 0)
+			test_abort(channel, block);
+	}
+	return retval;
+}
+
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+			       int count, const void *buf)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real && data->real->manager->write_byte)
+		retval = io_channel_write_byte(data->real, offset, count, buf);
+	if (data->write_byte)
+		data->write_byte(offset, count, retval);
+	if (data->flags & TEST_FLAG_WRITE)
+		fprintf(data->outfile,
+			"Test_io: write_byte(%lu, %d) returned %s\n",
+			offset, count, retval ? error_message(retval) : "OK");
+	return retval;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t test_flush(io_channel channel)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+	if (data->real)
+		retval = io_channel_flush(data->real);
+
+	if (data->flags & TEST_FLAG_FLUSH)
+		fprintf(data->outfile, "Test_io: flush() returned %s\n",
+			retval ? error_message(retval) : "OK");
+
+	return retval;
+}
+
+static errcode_t test_set_option(io_channel channel, const char *option,
+				 const char *arg)
+{
+	struct test_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct test_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+
+	if (data->flags & TEST_FLAG_SET_OPTION)
+		fprintf(data->outfile, "Test_io: set_option(%s, %s) ",
+			option, arg);
+	if (data->real && data->real->manager->set_option) {
+		retval = (data->real->manager->set_option)(data->real,
+							   option, arg);
+		if (data->flags & TEST_FLAG_SET_OPTION)
+			fprintf(data->outfile, "returned %s\n",
+				retval ? error_message(retval) : "OK");
+	} else {
+		if (data->flags & TEST_FLAG_SET_OPTION)
+			fprintf(data->outfile, "not implemented\n");
+	}
+	return retval;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c
new file mode 100644
index 0000000..474f073
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/unix_io.c
@@ -0,0 +1,703 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * unix_io.c --- This is the Unix (well, really POSIX) implementation
+ *	of the I/O manager.
+ *
+ * Implements a one-block write-through cache.
+ *
+ * Includes support for Windows NT support under Cygwin.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ *	2002 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/resource.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+	  if ((struct)->magic != (code)) return (code)
+
+struct unix_cache {
+	char		*buf;
+	unsigned long	block;
+	int		access_time;
+	unsigned	dirty:1;
+	unsigned	in_use:1;
+};
+
+#define CACHE_SIZE 8
+#define WRITE_DIRECT_SIZE 4	/* Must be smaller than CACHE_SIZE */
+#define READ_DIRECT_SIZE 4	/* Should be smaller than CACHE_SIZE */
+
+struct unix_private_data {
+	int	magic;
+	int	dev;
+	int	flags;
+	int	access_time;
+	ext2_loff_t offset;
+	struct unix_cache cache[CACHE_SIZE];
+};
+
+static errcode_t unix_open(const char *name, int flags, io_channel *channel);
+static errcode_t unix_close(io_channel channel);
+static errcode_t unix_set_blksize(io_channel channel, int blksize);
+static errcode_t unix_read_blk(io_channel channel, unsigned long block,
+			       int count, void *data);
+static errcode_t unix_write_blk(io_channel channel, unsigned long block,
+				int count, const void *data);
+static errcode_t unix_flush(io_channel channel);
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+				int size, const void *data);
+static errcode_t unix_set_option(io_channel channel, const char *option,
+				 const char *arg);
+
+static void reuse_cache(io_channel channel, struct unix_private_data *data,
+		 struct unix_cache *cache, unsigned long block);
+
+/* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel
+ * does not know buffered block devices - everything is raw. */
+#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define NEED_BOUNCE_BUFFER
+#else
+#undef NEED_BOUNCE_BUFFER
+#endif
+
+static struct struct_io_manager struct_unix_manager = {
+	EXT2_ET_MAGIC_IO_MANAGER,
+	"Unix I/O Manager",
+	unix_open,
+	unix_close,
+	unix_set_blksize,
+	unix_read_blk,
+	unix_write_blk,
+	unix_flush,
+#ifdef NEED_BOUNCE_BUFFER
+	0,
+#else
+	unix_write_byte,
+#endif
+	unix_set_option
+};
+
+io_manager unix_io_manager = &struct_unix_manager;
+
+/*
+ * Here are the raw I/O functions
+ */
+#ifndef NEED_BOUNCE_BUFFER
+static errcode_t raw_read_blk(io_channel channel,
+			      struct unix_private_data *data,
+			      unsigned long block,
+			      int count, void *buf)
+{
+	errcode_t	retval;
+	ssize_t		size;
+	ext2_loff_t	location;
+	int		actual = 0;
+
+	size = (count < 0) ? -count : count * channel->block_size;
+	location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+		goto error_out;
+	}
+	actual = read(data->dev, buf, size);
+	if (actual != size) {
+		if (actual < 0)
+			actual = 0;
+		retval = EXT2_ET_SHORT_READ;
+		goto error_out;
+	}
+	return 0;
+
+error_out:
+	memset((char *) buf+actual, 0, size-actual);
+	if (channel->read_error)
+		retval = (channel->read_error)(channel, block, count, buf,
+					       size, actual, retval);
+	return retval;
+}
+#else /* NEED_BOUNCE_BUFFER */
+/*
+ * Windows and FreeBSD block devices only allow sector alignment IO in offset and size
+ */
+static errcode_t raw_read_blk(io_channel channel,
+			      struct unix_private_data *data,
+			      unsigned long block,
+			      int count, void *buf)
+{
+	errcode_t	retval;
+	size_t		size, alignsize, fragment;
+	ext2_loff_t	location;
+	int		total = 0, actual;
+#define BLOCKALIGN 512
+	char		sector[BLOCKALIGN];
+
+	size = (count < 0) ? -count : count * channel->block_size;
+	location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+#ifdef DEBUG
+	printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n",
+			count, size, block, channel->block_size, location);
+#endif
+	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+		goto error_out;
+	}
+	fragment = size % BLOCKALIGN;
+	alignsize = size - fragment;
+	if (alignsize) {
+		actual = read(data->dev, buf, alignsize);
+		if (actual != alignsize)
+			goto short_read;
+	}
+	if (fragment) {
+		actual = read(data->dev, sector, BLOCKALIGN);
+		if (actual != BLOCKALIGN)
+			goto short_read;
+		memcpy(buf+alignsize, sector, fragment);
+	}
+	return 0;
+
+short_read:
+	if (actual>0)
+		total += actual;
+	retval = EXT2_ET_SHORT_READ;
+
+error_out:
+	memset((char *) buf+total, 0, size-actual);
+	if (channel->read_error)
+		retval = (channel->read_error)(channel, block, count, buf,
+					       size, actual, retval);
+	return retval;
+}
+#endif
+
+static errcode_t raw_write_blk(io_channel channel,
+			       struct unix_private_data *data,
+			       unsigned long block,
+			       int count, const void *buf)
+{
+	ssize_t		size;
+	ext2_loff_t	location;
+	int		actual = 0;
+	errcode_t	retval;
+
+	if (count == 1)
+		size = channel->block_size;
+	else {
+		if (count < 0)
+			size = -count;
+		else
+			size = count * channel->block_size;
+	}
+
+	location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+	if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+		retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+		goto error_out;
+	}
+
+	actual = write(data->dev, buf, size);
+	if (actual != size) {
+		retval = EXT2_ET_SHORT_WRITE;
+		goto error_out;
+	}
+	return 0;
+
+error_out:
+	if (channel->write_error)
+		retval = (channel->write_error)(channel, block, count, buf,
+						size, actual, retval);
+	return retval;
+}
+
+
+/*
+ * Here we implement the cache functions
+ */
+
+/* Allocate the cache buffers */
+static errcode_t alloc_cache(io_channel channel,
+			     struct unix_private_data *data)
+{
+	errcode_t		retval;
+	struct unix_cache	*cache;
+	int			i;
+
+	data->access_time = 0;
+	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+		cache->block = 0;
+		cache->access_time = 0;
+		cache->dirty = 0;
+		cache->in_use = 0;
+		if ((retval = ext2fs_get_mem(channel->block_size,
+					     &cache->buf)))
+			return retval;
+	}
+	return 0;
+}
+
+/* Free the cache buffers */
+static void free_cache(struct unix_private_data *data)
+{
+	struct unix_cache	*cache;
+	int			i;
+
+	data->access_time = 0;
+	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+		cache->block = 0;
+		cache->access_time = 0;
+		cache->dirty = 0;
+		cache->in_use = 0;
+		ext2fs_free_mem(&cache->buf);
+		cache->buf = 0;
+	}
+}
+
+#ifndef NO_IO_CACHE
+/*
+ * Try to find a block in the cache.  If the block is not found, and
+ * eldest is a non-zero pointer, then fill in eldest with the cache
+ * entry to that should be reused.
+ */
+static struct unix_cache *find_cached_block(struct unix_private_data *data,
+					    unsigned long block,
+					    struct unix_cache **eldest)
+{
+	struct unix_cache	*cache, *unused_cache, *oldest_cache;
+	int			i;
+
+	unused_cache = oldest_cache = 0;
+	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+		if (!cache->in_use) {
+			if (!unused_cache)
+				unused_cache = cache;
+			continue;
+		}
+		if (cache->block == block) {
+			cache->access_time = ++data->access_time;
+			return cache;
+		}
+		if (!oldest_cache ||
+		    (cache->access_time < oldest_cache->access_time))
+			oldest_cache = cache;
+	}
+	if (eldest)
+		*eldest = (unused_cache) ? unused_cache : oldest_cache;
+	return 0;
+}
+
+/*
+ * Reuse a particular cache entry for another block.
+ */
+static void reuse_cache(io_channel channel, struct unix_private_data *data,
+		 struct unix_cache *cache, unsigned long block)
+{
+	if (cache->dirty && cache->in_use)
+		raw_write_blk(channel, data, cache->block, 1, cache->buf);
+
+	cache->in_use = 1;
+	cache->dirty = 0;
+	cache->block = block;
+	cache->access_time = ++data->access_time;
+}
+
+/*
+ * Flush all of the blocks in the cache
+ */
+static errcode_t flush_cached_blocks(io_channel channel,
+				     struct unix_private_data *data,
+				     int invalidate)
+
+{
+	struct unix_cache	*cache;
+	errcode_t		retval, retval2;
+	int			i;
+
+	retval2 = 0;
+	for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+		if (!cache->in_use)
+			continue;
+
+		if (invalidate)
+			cache->in_use = 0;
+
+		if (!cache->dirty)
+			continue;
+
+		retval = raw_write_blk(channel, data,
+				       cache->block, 1, cache->buf);
+		if (retval)
+			retval2 = retval;
+		else
+			cache->dirty = 0;
+	}
+	return retval2;
+}
+#endif /* NO_IO_CACHE */
+
+static errcode_t unix_open(const char *name, int flags, io_channel *channel)
+{
+	io_channel	io = NULL;
+	struct unix_private_data *data = NULL;
+	errcode_t	retval;
+	int		open_flags;
+	struct stat	st;
+#ifdef __linux__
+	struct		utsname ut;
+#endif
+
+	if (name == 0)
+		return EXT2_ET_BAD_DEVICE_NAME;
+	retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+	if (retval)
+		return retval;
+	memset(io, 0, sizeof(struct struct_io_channel));
+	io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+	retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data);
+	if (retval)
+		goto cleanup;
+
+	io->manager = unix_io_manager;
+	retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+	if (retval)
+		goto cleanup;
+
+	strcpy(io->name, name);
+	io->private_data = data;
+	io->block_size = 1024;
+	io->read_error = 0;
+	io->write_error = 0;
+	io->refcount = 1;
+
+	memset(data, 0, sizeof(struct unix_private_data));
+	data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
+
+	if ((retval = alloc_cache(io, data)))
+		goto cleanup;
+
+	open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
+#ifdef CONFIG_LFS
+	data->dev = open64(io->name, open_flags);
+#else
+	data->dev = open(io->name, open_flags);
+#endif
+	if (data->dev < 0) {
+		retval = errno;
+		goto cleanup;
+	}
+
+#ifdef __linux__
+#undef RLIM_INFINITY
+#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4)))
+#define RLIM_INFINITY	((unsigned long)(~0UL>>1))
+#else
+#define RLIM_INFINITY  (~0UL)
+#endif
+	/*
+	 * Work around a bug in 2.4.10-2.4.18 kernels where writes to
+	 * block devices are wrongly getting hit by the filesize
+	 * limit.  This workaround isn't perfect, since it won't work
+	 * if glibc wasn't built against 2.2 header files.  (Sigh.)
+	 *
+	 */
+	if ((flags & IO_FLAG_RW) &&
+	    (uname(&ut) == 0) &&
+	    ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+	     (ut.release[2] == '4') && (ut.release[3] == '.') &&
+	     (ut.release[4] == '1') && (ut.release[5] >= '0') &&
+	     (ut.release[5] < '8')) &&
+	    (fstat(data->dev, &st) == 0) &&
+	    (S_ISBLK(st.st_mode))) {
+		struct rlimit	rlim;
+
+		rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY;
+		setrlimit(RLIMIT_FSIZE, &rlim);
+		getrlimit(RLIMIT_FSIZE, &rlim);
+		if (((unsigned long) rlim.rlim_cur) <
+		    ((unsigned long) rlim.rlim_max)) {
+			rlim.rlim_cur = rlim.rlim_max;
+			setrlimit(RLIMIT_FSIZE, &rlim);
+		}
+	}
+#endif
+	*channel = io;
+	return 0;
+
+cleanup:
+	if (data) {
+		free_cache(data);
+		ext2fs_free_mem(&data);
+	}
+	ext2fs_free_mem(&io);
+	return retval;
+}
+
+static errcode_t unix_close(io_channel channel)
+{
+	struct unix_private_data *data;
+	errcode_t	retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct unix_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+	if (--channel->refcount > 0)
+		return 0;
+
+#ifndef NO_IO_CACHE
+	retval = flush_cached_blocks(channel, data, 0);
+#endif
+
+	if (close(data->dev) < 0)
+		retval = errno;
+	free_cache(data);
+
+	ext2fs_free_mem(&channel->private_data);
+	ext2fs_free_mem(&channel->name);
+	ext2fs_free_mem(&channel);
+	return retval;
+}
+
+static errcode_t unix_set_blksize(io_channel channel, int blksize)
+{
+	struct unix_private_data *data;
+	errcode_t		retval;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct unix_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+	if (channel->block_size != blksize) {
+#ifndef NO_IO_CACHE
+		if ((retval = flush_cached_blocks(channel, data, 0)))
+			return retval;
+#endif
+
+		channel->block_size = blksize;
+		free_cache(data);
+		if ((retval = alloc_cache(channel, data)))
+			return retval;
+	}
+	return 0;
+}
+
+
+static errcode_t unix_read_blk(io_channel channel, unsigned long block,
+			       int count, void *buf)
+{
+	struct unix_private_data *data;
+	struct unix_cache *cache, *reuse[READ_DIRECT_SIZE];
+	errcode_t	retval;
+	char		*cp;
+	int		i, j;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct unix_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+	return raw_read_blk(channel, data, block, count, buf);
+#else
+	/*
+	 * If we're doing an odd-sized read or a very large read,
+	 * flush out the cache and then do a direct read.
+	 */
+	if (count < 0 || count > WRITE_DIRECT_SIZE) {
+		if ((retval = flush_cached_blocks(channel, data, 0)))
+			return retval;
+		return raw_read_blk(channel, data, block, count, buf);
+	}
+
+	cp = buf;
+	while (count > 0) {
+		/* 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);
+#endif
+			memcpy(cp, cache->buf, channel->block_size);
+			count--;
+			block++;
+			cp += channel->block_size;
+			continue;
+		}
+		/*
+		 * Find the number of uncached blocks so we can do a
+		 * single read request
+		 */
+		for (i=1; i < count; i++)
+			if (find_cached_block(data, block+i, &reuse[i]))
+				break;
+#ifdef DEBUG
+		printf("Reading %d blocks starting at %d\n", i, block);
+#endif
+		if ((retval = raw_read_blk(channel, data, block, i, cp)))
+			return retval;
+
+		/* Save the results in the cache */
+		for (j=0; j < i; j++) {
+			count--;
+			cache = reuse[j];
+			reuse_cache(channel, data, cache, block++);
+			memcpy(cache->buf, cp, channel->block_size);
+			cp += channel->block_size;
+		}
+	}
+	return 0;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_write_blk(io_channel channel, unsigned long block,
+				int count, const void *buf)
+{
+	struct unix_private_data *data;
+	struct unix_cache *cache, *reuse;
+	errcode_t	retval = 0;
+	const char	*cp;
+	int		writethrough;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct unix_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+	return raw_write_blk(channel, data, block, count, buf);
+#else
+	/*
+	 * If we're doing an odd-sized write or a very large write,
+	 * flush out the cache completely and then do a direct write.
+	 */
+	if (count < 0 || count > WRITE_DIRECT_SIZE) {
+		if ((retval = flush_cached_blocks(channel, data, 1)))
+			return retval;
+		return raw_write_blk(channel, data, block, count, buf);
+	}
+
+	/*
+	 * For a moderate-sized multi-block write, first force a write
+	 * if we're in write-through cache mode, and then fill the
+	 * cache with the blocks.
+	 */
+	writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
+	if (writethrough)
+		retval = raw_write_blk(channel, data, block, count, buf);
+
+	cp = buf;
+	while (count > 0) {
+		cache = find_cached_block(data, block, &reuse);
+		if (!cache) {
+			cache = reuse;
+			reuse_cache(channel, data, cache, block);
+		}
+		memcpy(cache->buf, cp, channel->block_size);
+		cache->dirty = !writethrough;
+		count--;
+		block++;
+		cp += channel->block_size;
+	}
+	return retval;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+				 int size, const void *buf)
+{
+	struct unix_private_data *data;
+	errcode_t	retval = 0;
+	ssize_t		actual;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct unix_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+	/*
+	 * Flush out the cache completely
+	 */
+	if ((retval = flush_cached_blocks(channel, data, 1)))
+		return retval;
+#endif
+
+	if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
+		return errno;
+
+	actual = write(data->dev, buf, size);
+	if (actual != size)
+		return EXT2_ET_SHORT_WRITE;
+
+	return 0;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t unix_flush(io_channel channel)
+{
+	struct unix_private_data *data;
+	errcode_t retval = 0;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct unix_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+	retval = flush_cached_blocks(channel, data, 0);
+#endif
+	fsync(data->dev);
+	return retval;
+}
+
+static errcode_t unix_set_option(io_channel channel, const char *option,
+				 const char *arg)
+{
+	struct unix_private_data *data;
+	unsigned long tmp;
+	char *end;
+
+	EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+	data = (struct unix_private_data *) channel->private_data;
+	EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+	if (!strcmp(option, "offset")) {
+		if (!arg)
+			return EXT2_ET_INVALID_ARGUMENT;
+
+		tmp = strtoul(arg, &end, 0);
+		if (*end)
+			return EXT2_ET_INVALID_ARGUMENT;
+		data->offset = tmp;
+		return 0;
+	}
+	return EXT2_ET_INVALID_ARGUMENT;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c
new file mode 100644
index 0000000..71a9ffc
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/unlink.c
@@ -0,0 +1,99 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * unlink.c --- delete links in a ext2fs directory
+ *
+ * Copyright (C) 1993, 1994, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct link_struct  {
+	const char	*name;
+	int		namelen;
+	ext2_ino_t	inode;
+	int		flags;
+	struct ext2_dir_entry *prev;
+	int		done;
+};
+
+#ifdef __TURBOC__
+# pragma argsused
+#endif
+static int unlink_proc(struct ext2_dir_entry *dirent,
+		     int	offset EXT2FS_ATTR((unused)),
+		     int	blocksize EXT2FS_ATTR((unused)),
+		     char	*buf EXT2FS_ATTR((unused)),
+		     void	*priv_data)
+{
+	struct link_struct *ls = (struct link_struct *) priv_data;
+	struct ext2_dir_entry *prev;
+
+	prev = ls->prev;
+	ls->prev = dirent;
+
+	if (ls->name) {
+		if ((dirent->name_len & 0xFF) != ls->namelen)
+			return 0;
+		if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF))
+			return 0;
+	}
+	if (ls->inode) {
+		if (dirent->inode != ls->inode)
+			return 0;
+	} else {
+		if (!dirent->inode)
+			return 0;
+	}
+
+	if (prev)
+		prev->rec_len += dirent->rec_len;
+	else
+		dirent->inode = 0;
+	ls->done++;
+	return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir,
+			const char *name, ext2_ino_t ino,
+			int flags EXT2FS_ATTR((unused)))
+{
+	errcode_t	retval;
+	struct link_struct ls;
+
+	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+	if (!name && !ino)
+		return EXT2_ET_INVALID_ARGUMENT;
+
+	if (!(fs->flags & EXT2_FLAG_RW))
+		return EXT2_ET_RO_FILSYS;
+
+	ls.name = name;
+	ls.namelen = name ? strlen(name) : 0;
+	ls.inode = ino;
+	ls.flags = 0;
+	ls.done = 0;
+	ls.prev = 0;
+
+	retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
+				    0, unlink_proc, &ls);
+	if (retval)
+		return retval;
+
+	return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c
new file mode 100644
index 0000000..8ed77ae
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/valid_blk.c
@@ -0,0 +1,57 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * valid_blk.c --- does the inode have valid blocks?
+ *
+ * Copyright 1997 by Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * This function returns 1 if the inode's block entries actually
+ * contain block entries.
+ */
+int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode)
+{
+	/*
+	 * Only directories, regular files, and some symbolic links
+	 * have valid block entries.
+	 */
+	if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) &&
+	    !LINUX_S_ISLNK(inode->i_mode))
+		return 0;
+
+	/*
+	 * If the symbolic link is a "fast symlink", then the symlink
+	 * target is stored in the block entries.
+	 */
+	if (LINUX_S_ISLNK (inode->i_mode)) {
+		if (inode->i_file_acl == 0) {
+			/* With no EA block, we can rely on i_blocks */
+			if (inode->i_blocks == 0)
+				return 0;
+		} else {
+			/* With an EA block, life gets more tricky */
+			if (inode->i_size >= EXT2_N_BLOCKS*4)
+				return 1; /* definitely using i_block[] */
+			if (inode->i_size > 4 && inode->i_block[1] == 0)
+				return 1; /* definitely using i_block[] */
+			return 0; /* Probably a fast symlink */
+		}
+	}
+	return 1;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/version.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/version.c
new file mode 100644
index 0000000..d2981e8
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/version.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * version.c --- Return the version of the ext2 library
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static const char *lib_version = E2FSPROGS_VERSION;
+static const char *lib_date = E2FSPROGS_DATE;
+
+int ext2fs_parse_version_string(const char *ver_string)
+{
+	const char *cp;
+	int version = 0;
+
+	for (cp = ver_string; *cp; cp++) {
+		if (*cp == '.')
+			continue;
+		if (!isdigit(*cp))
+			break;
+		version = (version * 10) + (*cp - '0');
+	}
+	return version;
+}
+
+
+int ext2fs_get_library_version(const char **ver_string,
+			       const char **date_string)
+{
+	if (ver_string)
+		*ver_string = lib_version;
+	if (date_string)
+		*date_string = lib_date;
+
+	return ext2fs_parse_version_string(lib_version);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c
new file mode 100644
index 0000000..5b19eef
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/ext2fs/write_bb_file.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * write_bb_file.c --- write a list of bad blocks to a FILE *
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
+			       unsigned int flags EXT2FS_ATTR((unused)),
+			       FILE *f)
+{
+	badblocks_iterate	bb_iter;
+	blk_t			blk;
+	errcode_t		retval;
+
+	retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
+	if (retval)
+		return retval;
+
+	while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) {
+		fprintf(f, "%d\n", blk);
+	}
+	ext2fs_badblocks_list_iterate_end(bb_iter);
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/fsck.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/fsck.c
new file mode 100644
index 0000000..3a0743b
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/fsck.c
@@ -0,0 +1,1376 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pfsck --- A generic, parallelizing front-end for the fsck program.
+ * It will automatically try to run fsck programs in parallel if the
+ * devices are on separate spindles.  It is based on the same ideas as
+ * the generic front end for fsck by David Engel and Fred van Kempen,
+ * but it has been completely rewritten from scratch to support
+ * parallel execution.
+ *
+ * Written by Theodore Ts'o, <tytso@mit.edu>
+ *
+ * Miquel van Smoorenburg (miquels@drinkel.ow.org) 20-Oct-1994:
+ *   o Changed -t fstype to behave like with mount when -A (all file
+ *     systems) or -M (like mount) is specified.
+ *   o fsck looks if it can find the fsck.type program to decide
+ *     if it should ignore the fs type. This way more fsck programs
+ *     can be added without changing this front-end.
+ *   o -R flag skip root file system.
+ *
+ * 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 source tree.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "fsck.h"
+#include "blkid/blkid.h"
+
+#include "e2fsbb.h"
+
+#include "libbb.h"
+
+#ifndef _PATH_MNTTAB
+#define _PATH_MNTTAB    "/etc/fstab"
+#endif
+
+/*
+ * fsck.h
+ */
+
+#ifndef DEFAULT_FSTYPE
+#define DEFAULT_FSTYPE	"ext2"
+#endif
+
+#define MAX_DEVICES 32
+#define MAX_ARGS 32
+
+/*
+ * Internal structure for mount tabel entries.
+ */
+
+struct fs_info {
+	char  *device;
+	char  *mountpt;
+	char  *type;
+	char  *opts;
+	int   freq;
+	int   passno;
+	int   flags;
+	struct fs_info *next;
+};
+
+#define FLAG_DONE 1
+#define FLAG_PROGRESS 2
+
+/*
+ * Structure to allow exit codes to be stored
+ */
+struct fsck_instance {
+	int	pid;
+	int	flags;
+	int	exit_status;
+	time_t	start_time;
+	char *	prog;
+	char *	type;
+	char *	device;
+	char *	base_device;
+	struct fsck_instance *next;
+};
+
+/*
+ * base_device.c
+ *
+ * Return the "base device" given a particular device; this is used to
+ * assure that we only fsck one partition on a particular drive at any
+ * one time.  Otherwise, the disk heads will be seeking all over the
+ * place.  If the base device cannot be determined, return NULL.
+ *
+ * The base_device() function returns an allocated string which must
+ * be freed.
+ *
+ */
+
+
+#ifdef CONFIG_FEATURE_DEVFS
+/*
+ * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
+ * pathames.
+ */
+static const char *const devfs_hier[] = {
+	"host", "bus", "target", "lun", 0
+};
+#endif
+
+static char *base_device(const char *device)
+{
+	char *str, *cp;
+#ifdef CONFIG_FEATURE_DEVFS
+	const char *const *hier;
+	const char *disk;
+	int len;
+#endif
+
+	cp = str = xstrdup(device);
+
+	/* Skip over /dev/; if it's not present, give up. */
+	if (strncmp(cp, "/dev/", 5) != 0)
+		goto errout;
+	cp += 5;
+
+	/*
+	 * For md devices, we treat them all as if they were all
+	 * on one disk, since we don't know how to parallelize them.
+	 */
+	if (cp[0] == 'm' && cp[1] == 'd') {
+		*(cp+2) = 0;
+		return str;
+	}
+
+	/* Handle DAC 960 devices */
+	if (strncmp(cp, "rd/", 3) == 0) {
+		cp += 3;
+		if (cp[0] != 'c' || cp[2] != 'd' ||
+		    !isdigit(cp[1]) || !isdigit(cp[3]))
+			goto errout;
+		*(cp+4) = 0;
+		return str;
+	}
+
+	/* Now let's handle /dev/hd* and /dev/sd* devices.... */
+	if ((cp[0] == 'h' || cp[0] == 's') && (cp[1] == 'd')) {
+		cp += 2;
+		/* If there's a single number after /dev/hd, skip it */
+		if (isdigit(*cp))
+			cp++;
+		/* What follows must be an alpha char, or give up */
+		if (!isalpha(*cp))
+			goto errout;
+		*(cp + 1) = 0;
+		return str;
+	}
+
+#ifdef CONFIG_FEATURE_DEVFS
+	/* Now let's handle devfs (ugh) names */
+	len = 0;
+	if (strncmp(cp, "ide/", 4) == 0)
+		len = 4;
+	if (strncmp(cp, "scsi/", 5) == 0)
+		len = 5;
+	if (len) {
+		cp += len;
+		/*
+		 * Now we proceed down the expected devfs hierarchy.
+		 * i.e., .../host1/bus2/target3/lun4/...
+		 * If we don't find the expected token, followed by
+		 * some number of digits at each level, abort.
+		 */
+		for (hier = devfs_hier; *hier; hier++) {
+			len = strlen(*hier);
+			if (strncmp(cp, *hier, len) != 0)
+				goto errout;
+			cp += len;
+			while (*cp != '/' && *cp != 0) {
+				if (!isdigit(*cp))
+					goto errout;
+				cp++;
+			}
+			cp++;
+		}
+		*(cp - 1) = 0;
+		return str;
+	}
+
+	/* Now handle devfs /dev/disc or /dev/disk names */
+	disk = 0;
+	if (strncmp(cp, "discs/", 6) == 0)
+		disk = "disc";
+	else if (strncmp(cp, "disks/", 6) == 0)
+		disk = "disk";
+	if (disk) {
+		cp += 6;
+		if (strncmp(cp, disk, 4) != 0)
+			goto errout;
+		cp += 4;
+		while (*cp != '/' && *cp != 0) {
+			if (!isdigit(*cp))
+				goto errout;
+			cp++;
+		}
+		*cp = 0;
+		return str;
+	}
+#endif
+
+errout:
+	free(str);
+	return NULL;
+}
+
+
+static const char *const ignored_types[] = {
+	"ignore",
+	"iso9660",
+	"nfs",
+	"proc",
+	"sw",
+	"swap",
+	"tmpfs",
+	"devpts",
+	NULL
+};
+
+static const char *const really_wanted[] = {
+	"minix",
+	"ext2",
+	"ext3",
+	"jfs",
+	"reiserfs",
+	"xiafs",
+	"xfs",
+	NULL
+};
+
+#define BASE_MD "/dev/md"
+
+/*
+ * Global variables for options
+ */
+static char *devices[MAX_DEVICES];
+static char *args[MAX_ARGS];
+static int num_devices, num_args;
+
+static int verbose;
+static int doall;
+static int noexecute;
+static int serialize;
+static int skip_root;
+static int like_mount;
+static int notitle;
+static int parallel_root;
+static int progress;
+static int progress_fd;
+static int force_all_parallel;
+static int num_running;
+static int max_running;
+static volatile int cancel_requested;
+static int kill_sent;
+static char *fstype;
+static struct fs_info *filesys_info, *filesys_last;
+static struct fsck_instance *instance_list;
+static char *fsck_path;
+static blkid_cache cache;
+
+static char *string_copy(const char *s)
+{
+	char    *ret;
+
+	if (!s)
+		return 0;
+	ret = xstrdup(s);
+	return ret;
+}
+
+static int string_to_int(const char *s)
+{
+	long l;
+	char *p;
+
+	l = strtol(s, &p, 0);
+	if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
+		return -1;
+	else
+		return (int) l;
+}
+
+static char *skip_over_blank(char *cp)
+{
+	while (*cp && isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+	while (*cp && !isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static void strip_line(char *line)
+{
+	char    *p;
+
+	while (*line) {
+		p = line + strlen(line) - 1;
+		if ((*p == '\n') || (*p == '\r'))
+			*p = 0;
+		else
+			break;
+	}
+}
+
+static char *parse_word(char **buf)
+{
+	char *word, *next;
+
+	word = *buf;
+	if (*word == 0)
+		return 0;
+
+	word = skip_over_blank(word);
+	next = skip_over_word(word);
+	if (*next)
+		*next++ = 0;
+	*buf = next;
+	return word;
+}
+
+static void parse_escape(char *word)
+{
+	char    *q, c;
+	const char *p;
+
+	if (!word)
+		return;
+
+	strcpy_and_process_escape_sequences(word, word);
+}
+
+static void free_instance(struct fsck_instance *i)
+{
+	if (i->prog)
+		free(i->prog);
+	if (i->device)
+		free(i->device);
+	if (i->base_device)
+		free(i->base_device);
+	free(i);
+}
+
+static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
+					const char *type, const char *opts,
+					int freq, int passno)
+{
+	struct fs_info *fs;
+
+	fs = xmalloc(sizeof(struct fs_info));
+
+	fs->device = string_copy(device);
+	fs->mountpt = string_copy(mntpnt);
+	fs->type = string_copy(type);
+	fs->opts = string_copy(opts ? opts : "");
+	fs->freq = freq;
+	fs->passno = passno;
+	fs->flags = 0;
+	fs->next = NULL;
+
+	if (!filesys_info)
+		filesys_info = fs;
+	else
+		filesys_last->next = fs;
+	filesys_last = fs;
+
+	return fs;
+}
+
+
+
+static int parse_fstab_line(char *line, struct fs_info **ret_fs)
+{
+	char    *dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp;
+	struct fs_info *fs;
+
+	*ret_fs = 0;
+	strip_line(line);
+	if ((cp = strchr(line, '#')))
+		*cp = 0;        /* Ignore everything after the comment char */
+	cp = line;
+
+	device = parse_word(&cp);
+	mntpnt = parse_word(&cp);
+	type = parse_word(&cp);
+	opts = parse_word(&cp);
+	freq = parse_word(&cp);
+	passno = parse_word(&cp);
+
+	if (!device)
+		return 0;       /* Allow blank lines */
+
+	if (!mntpnt || !type)
+		return -1;
+
+	parse_escape(device);
+	parse_escape(mntpnt);
+	parse_escape(type);
+	parse_escape(opts);
+	parse_escape(freq);
+	parse_escape(passno);
+
+	dev = blkid_get_devname(cache, device, NULL);
+	if (dev)
+		device = dev;
+
+	if (strchr(type, ','))
+		type = 0;
+
+	fs = create_fs_device(device, mntpnt, type ? type : "auto", opts,
+			      freq ? atoi(freq) : -1,
+			      passno ? atoi(passno) : -1);
+	if (dev)
+		free(dev);
+
+	if (!fs)
+		return -1;
+	*ret_fs = fs;
+	return 0;
+}
+
+static void interpret_type(struct fs_info *fs)
+{
+	char    *t;
+
+	if (strcmp(fs->type, "auto") != 0)
+		return;
+	t = blkid_get_tag_value(cache, "TYPE", fs->device);
+	if (t) {
+		free(fs->type);
+		fs->type = t;
+	}
+}
+
+/*
+ * Load the filesystem database from /etc/fstab
+ */
+static void load_fs_info(const char *filename)
+{
+	FILE    *f;
+	char    buf[1024];
+	int     lineno = 0;
+	int     old_fstab = 1;
+	struct fs_info *fs;
+
+	if ((f = fopen_or_warn(filename, "r")) == NULL) {
+		return;
+	}
+	while (!feof(f)) {
+		lineno++;
+		if (!fgets(buf, sizeof(buf), f))
+			break;
+		buf[sizeof(buf)-1] = 0;
+		if (parse_fstab_line(buf, &fs) < 0) {
+			bb_error_msg("WARNING: bad format "
+				"on line %d of %s\n", lineno, filename);
+			continue;
+		}
+		if (!fs)
+			continue;
+		if (fs->passno < 0)
+			fs->passno = 0;
+		else
+			old_fstab = 0;
+	}
+
+	fclose(f);
+
+	if (old_fstab) {
+		fputs("\007\007\007"
+		"WARNING: Your /etc/fstab does not contain the fsck passno\n"
+		"       field.  I will kludge around things for you, but you\n"
+		"       should fix your /etc/fstab file as soon as you can.\n\n", stderr);
+
+		for (fs = filesys_info; fs; fs = fs->next) {
+			fs->passno = 1;
+		}
+	}
+}
+
+/* Lookup filesys in /etc/fstab and return the corresponding entry. */
+static struct fs_info *lookup(char *filesys)
+{
+	struct fs_info *fs;
+
+	/* No filesys name given. */
+	if (filesys == NULL)
+		return NULL;
+
+	for (fs = filesys_info; fs; fs = fs->next) {
+		if (!strcmp(filesys, fs->device) ||
+		    (fs->mountpt && !strcmp(filesys, fs->mountpt)))
+			break;
+	}
+
+	return fs;
+}
+
+/* Find fsck program for a given fs type. */
+static char *find_fsck(char *type)
+{
+	char *s;
+	const char *tpl;
+	char *p = string_copy(fsck_path);
+	struct stat st;
+
+	/* Are we looking for a program or just a type? */
+	tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
+
+	for (s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
+		s = xasprintf(tpl, s, type);
+		if (stat(s, &st) == 0) break;
+		free(s);
+	}
+	free(p);
+	return s;
+}
+
+static int progress_active(void)
+{
+	struct fsck_instance *inst;
+
+	for (inst = instance_list; inst; inst = inst->next) {
+		if (inst->flags & FLAG_DONE)
+			continue;
+		if (inst->flags & FLAG_PROGRESS)
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Execute a particular fsck program, and link it into the list of
+ * child processes we are waiting for.
+ */
+static int execute(const char *type, const char *device, const char *mntpt,
+		   int interactive)
+{
+	char *s, *argv[80];
+	char *prog;
+	int  argc, i;
+	struct fsck_instance *inst, *p;
+	pid_t   pid;
+
+	inst = xzalloc(sizeof(struct fsck_instance));
+
+	prog = xasprintf("fsck.%s", type);
+	argv[0] = prog;
+	argc = 1;
+
+	for (i=0; i <num_args; i++)
+		argv[argc++] = string_copy(args[i]);
+
+	if (progress && !progress_active()) {
+		if ((strcmp(type, "ext2") == 0) ||
+		    (strcmp(type, "ext3") == 0)) {
+			char tmp[80];
+			snprintf(tmp, 80, "-C%d", progress_fd);
+			argv[argc++] = string_copy(tmp);
+			inst->flags |= FLAG_PROGRESS;
+		}
+	}
+
+	argv[argc++] = string_copy(device);
+	argv[argc] = 0;
+
+	s = find_fsck(prog);
+	if (s == NULL) {
+		bb_error_msg("%s: not found", prog);
+		return ENOENT;
+	}
+
+	if (verbose || noexecute) {
+		printf("[%s (%d) -- %s] ", s, num_running,
+		       mntpt ? mntpt : device);
+		for (i=0; i < argc; i++)
+			printf("%s ", argv[i]);
+		bb_putchar('\n');
+	}
+
+	/* Fork and execute the correct program. */
+	if (noexecute)
+		pid = -1;
+	else if ((pid = fork()) < 0) {
+		perror("vfork"+1);
+		return errno;
+	} else if (pid == 0) {
+		if (!interactive)
+			close(0);
+		(void) execv(s, argv);
+		bb_simple_perror_msg_and_die(argv[0]);
+	}
+
+	for (i = 1; i < argc; i++)
+		free(argv[i]);
+
+	free(s);
+	inst->pid = pid;
+	inst->prog = prog;
+	inst->type = string_copy(type);
+	inst->device = string_copy(device);
+	inst->base_device = base_device(device);
+	inst->start_time = time(0);
+	inst->next = NULL;
+
+	/*
+	 * Find the end of the list, so we add the instance on at the end.
+	 */
+	for (p = instance_list; p && p->next; p = p->next);
+
+	if (p)
+		p->next = inst;
+	else
+		instance_list = inst;
+
+	return 0;
+}
+
+/*
+ * Send a signal to all outstanding fsck child processes
+ */
+static int kill_all(int signum)
+{
+	struct fsck_instance *inst;
+	int     n = 0;
+
+	for (inst = instance_list; inst; inst = inst->next) {
+		if (inst->flags & FLAG_DONE)
+			continue;
+		kill(inst->pid, signum);
+		n++;
+	}
+	return n;
+}
+
+/*
+ * Wait for one child process to exit; when it does, unlink it from
+ * the list of executing child processes, and return it.
+ */
+static struct fsck_instance *wait_one(int flags)
+{
+	int     status;
+	int     sig;
+	struct fsck_instance *inst, *inst2, *prev;
+	pid_t   pid;
+
+	if (!instance_list)
+		return NULL;
+
+	if (noexecute) {
+		inst = instance_list;
+		prev = 0;
+#ifdef RANDOM_DEBUG
+		while (inst->next && (random() & 1)) {
+			prev = inst;
+			inst = inst->next;
+		}
+#endif
+		inst->exit_status = 0;
+		goto ret_inst;
+	}
+
+	/*
+	 * gcc -Wall fails saving throw against stupidity
+	 * (inst and prev are thought to be uninitialized variables)
+	 */
+	inst = prev = NULL;
+
+	do {
+		pid = waitpid(-1, &status, flags);
+		if (cancel_requested && !kill_sent) {
+			kill_all(SIGTERM);
+			kill_sent++;
+		}
+		if ((pid == 0) && (flags & WNOHANG))
+			return NULL;
+		if (pid < 0) {
+			if ((errno == EINTR) || (errno == EAGAIN))
+				continue;
+			if (errno == ECHILD) {
+				bb_error_msg("wait: no more child process?!?");
+				return NULL;
+			}
+			perror("wait");
+			continue;
+		}
+		for (prev = 0, inst = instance_list;
+		     inst;
+		     prev = inst, inst = inst->next) {
+			if (inst->pid == pid)
+				break;
+		}
+	} while (!inst);
+
+	if (WIFEXITED(status))
+		status = WEXITSTATUS(status);
+	else if (WIFSIGNALED(status)) {
+		sig = WTERMSIG(status);
+		if (sig == SIGINT) {
+			status = EXIT_UNCORRECTED;
+		} else {
+			printf("Warning... %s for device %s exited "
+			       "with signal %d.\n",
+			       inst->prog, inst->device, sig);
+			status = EXIT_ERROR;
+		}
+	} else {
+		printf("%s %s: status is %x, should never happen.\n",
+		       inst->prog, inst->device, status);
+		status = EXIT_ERROR;
+	}
+	inst->exit_status = status;
+	if (progress && (inst->flags & FLAG_PROGRESS) &&
+	    !progress_active()) {
+		for (inst2 = instance_list; inst2; inst2 = inst2->next) {
+			if (inst2->flags & FLAG_DONE)
+				continue;
+			if (strcmp(inst2->type, "ext2") &&
+			    strcmp(inst2->type, "ext3"))
+				continue;
+			/*
+			 * If we've just started the fsck, wait a tiny
+			 * bit before sending the kill, to give it
+			 * time to set up the signal handler
+			 */
+			if (inst2->start_time < time(0)+2) {
+				if (fork() == 0) {
+					sleep(1);
+					kill(inst2->pid, SIGUSR1);
+					exit(0);
+				}
+			} else
+				kill(inst2->pid, SIGUSR1);
+			inst2->flags |= FLAG_PROGRESS;
+			break;
+		}
+	}
+ret_inst:
+	if (prev)
+		prev->next = inst->next;
+	else
+		instance_list = inst->next;
+	if (verbose > 1)
+		printf("Finished with %s (exit status %d)\n",
+		       inst->device, inst->exit_status);
+	num_running--;
+	return inst;
+}
+
+#define FLAG_WAIT_ALL           0
+#define FLAG_WAIT_ATLEAST_ONE   1
+/*
+ * Wait until all executing child processes have exited; return the
+ * logical OR of all of their exit code values.
+ */
+static int wait_many(int flags)
+{
+	struct fsck_instance *inst;
+	int     global_status = 0;
+	int     wait_flags = 0;
+
+	while ((inst = wait_one(wait_flags))) {
+		global_status |= inst->exit_status;
+		free_instance(inst);
+#ifdef RANDOM_DEBUG
+		if (noexecute && (flags & WNOHANG) && !(random() % 3))
+			break;
+#endif
+		if (flags & FLAG_WAIT_ATLEAST_ONE)
+			wait_flags = WNOHANG;
+	}
+	return global_status;
+}
+
+/*
+ * Run the fsck program on a particular device
+ *
+ * If the type is specified using -t, and it isn't prefixed with "no"
+ * (as in "noext2") and only one filesystem type is specified, then
+ * use that type regardless of what is specified in /etc/fstab.
+ *
+ * If the type isn't specified by the user, then use either the type
+ * specified in /etc/fstab, or DEFAULT_FSTYPE.
+ */
+static void fsck_device(struct fs_info *fs, int interactive)
+{
+	const char *type;
+	int retval;
+
+	interpret_type(fs);
+
+	if (strcmp(fs->type, "auto") != 0)
+		type = fs->type;
+	else if (fstype && strncmp(fstype, "no", 2) &&
+	    strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
+	    !strchr(fstype, ','))
+		type = fstype;
+	else
+		type = DEFAULT_FSTYPE;
+
+	num_running++;
+	retval = execute(type, fs->device, fs->mountpt, interactive);
+	if (retval) {
+		bb_error_msg("error %d while executing fsck.%s for %s",
+						retval, type, fs->device);
+		num_running--;
+	}
+}
+
+
+/*
+ * Deal with the fsck -t argument.
+ */
+struct fs_type_compile {
+	char **list;
+	int *type;
+	int  negate;
+} fs_type_compiled;
+
+#define FS_TYPE_NORMAL  0
+#define FS_TYPE_OPT     1
+#define FS_TYPE_NEGOPT  2
+
+static const char fs_type_syntax_error[] =
+"Either all or none of the filesystem types passed to -t must be prefixed\n"
+   "with 'no' or '!'.";
+
+static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp)
+{
+	char    *cp, *list, *s;
+	int     num = 2;
+	int     negate, first_negate = 1;
+
+	if (fs_type) {
+		for (cp=fs_type; *cp; cp++) {
+			if (*cp == ',')
+				num++;
+		}
+	}
+
+	cmp->list = xzalloc(num * sizeof(char *));
+	cmp->type = xzalloc(num * sizeof(int));
+	cmp->negate = 0;
+
+	if (!fs_type)
+		return;
+
+	list = string_copy(fs_type);
+	num = 0;
+	s = strtok(list, ",");
+	while (s) {
+		negate = 0;
+		if (strncmp(s, "no", 2) == 0) {
+			s += 2;
+			negate = 1;
+		} else if (*s == '!') {
+			s++;
+			negate = 1;
+		}
+		if (strcmp(s, "loop") == 0)
+			/* loop is really short-hand for opts=loop */
+			goto loop_special_case;
+		else if (strncmp(s, "opts=", 5) == 0) {
+			s += 5;
+		loop_special_case:
+			cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT;
+		} else {
+			if (first_negate) {
+				cmp->negate = negate;
+				first_negate = 0;
+			}
+			if ((negate && !cmp->negate) ||
+			    (!negate && cmp->negate)) {
+				bb_error_msg_and_die("%s", fs_type_syntax_error);
+			}
+		}
+		cmp->list[num++] = string_copy(s);
+		s = strtok(NULL, ",");
+	}
+	free(list);
+}
+
+/*
+ * This function returns true if a particular option appears in a
+ * comma-delimited options list
+ */
+static int opt_in_list(char *opt, char *optlist)
+{
+	char    *list, *s;
+
+	if (!optlist)
+		return 0;
+	list = string_copy(optlist);
+
+	s = strtok(list, ",");
+	while (s) {
+		if (strcmp(s, opt) == 0) {
+			free(list);
+			return 1;
+		}
+		s = strtok(NULL, ",");
+	}
+	free(list);
+	return 0;
+}
+
+/* See if the filesystem matches the criteria given by the -t option */
+static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp)
+{
+	int n, ret = 0, checked_type = 0;
+	char *cp;
+
+	if (cmp->list == 0 || cmp->list[0] == 0)
+		return 1;
+
+	for (n=0; (cp = cmp->list[n]); n++) {
+		switch (cmp->type[n]) {
+		case FS_TYPE_NORMAL:
+			checked_type++;
+			if (strcmp(cp, fs->type) == 0) {
+				ret = 1;
+			}
+			break;
+		case FS_TYPE_NEGOPT:
+			if (opt_in_list(cp, fs->opts))
+				return 0;
+			break;
+		case FS_TYPE_OPT:
+			if (!opt_in_list(cp, fs->opts))
+				return 0;
+			break;
+		}
+	}
+	if (checked_type == 0)
+		return 1;
+	return (cmp->negate ? !ret : ret);
+}
+
+/* Check if we should ignore this filesystem. */
+static int ignore(struct fs_info *fs)
+{
+	int wanted;
+	char *s;
+
+	/*
+	 * If the pass number is 0, ignore it.
+	 */
+	if (fs->passno == 0)
+		return 1;
+
+	interpret_type(fs);
+
+	/*
+	 * If a specific fstype is specified, and it doesn't match,
+	 * ignore it.
+	 */
+	if (!fs_match(fs, &fs_type_compiled)) return 1;
+
+	/* Are we ignoring this type? */
+	if (index_in_str_array(ignored_types, fs->type) >= 0)
+		return 1;
+
+	/* Do we really really want to check this fs? */
+	wanted = index_in_str_array(really_wanted, fs->type) >= 0;
+
+	/* See if the <fsck.fs> program is available. */
+	s = find_fsck(fs->type);
+	if (s == NULL) {
+		if (wanted)
+			bb_error_msg("can't check %s: fsck.%s not found",
+				fs->device, fs->type);
+		return 1;
+	}
+	free(s);
+
+	/* We can and want to check this file system type. */
+	return 0;
+}
+
+/*
+ * Returns TRUE if a partition on the same disk is already being
+ * checked.
+ */
+static int device_already_active(char *device)
+{
+	struct fsck_instance *inst;
+	char *base;
+
+	if (force_all_parallel)
+		return 0;
+
+#ifdef BASE_MD
+	/* Don't check a soft raid disk with any other disk */
+	if (instance_list &&
+	    (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) ||
+	     !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)))
+		return 1;
+#endif
+
+	base = base_device(device);
+	/*
+	 * If we don't know the base device, assume that the device is
+	 * already active if there are any fsck instances running.
+	 */
+	if (!base)
+		return (instance_list != 0);
+	for (inst = instance_list; inst; inst = inst->next) {
+		if (!inst->base_device || !strcmp(base, inst->base_device)) {
+			free(base);
+			return 1;
+		}
+	}
+	free(base);
+	return 0;
+}
+
+/* Check all file systems, using the /etc/fstab table. */
+static int check_all(void)
+{
+	struct fs_info *fs = NULL;
+	int status = EXIT_OK;
+	int not_done_yet = 1;
+	int passno = 1;
+	int pass_done;
+
+	if (verbose)
+		fputs("Checking all file systems.\n", stdout);
+
+	/*
+	 * Do an initial scan over the filesystem; mark filesystems
+	 * which should be ignored as done, and resolve any "auto"
+	 * filesystem types (done as a side-effect of calling ignore()).
+	 */
+	for (fs = filesys_info; fs; fs = fs->next) {
+		if (ignore(fs))
+			fs->flags |= FLAG_DONE;
+	}
+
+	/*
+	 * Find and check the root filesystem.
+	 */
+	if (!parallel_root) {
+		for (fs = filesys_info; fs; fs = fs->next) {
+			if (LONE_CHAR(fs->mountpt, '/'))
+				break;
+		}
+		if (fs) {
+			if (!skip_root && !ignore(fs)) {
+				fsck_device(fs, 1);
+				status |= wait_many(FLAG_WAIT_ALL);
+				if (status > EXIT_NONDESTRUCT)
+					return status;
+			}
+			fs->flags |= FLAG_DONE;
+		}
+	}
+	/*
+	 * This is for the bone-headed user who enters the root
+	 * filesystem twice.  Skip root will skep all root entries.
+	 */
+	if (skip_root)
+		for (fs = filesys_info; fs; fs = fs->next)
+			if (LONE_CHAR(fs->mountpt, '/'))
+				fs->flags |= FLAG_DONE;
+
+	while (not_done_yet) {
+		not_done_yet = 0;
+		pass_done = 1;
+
+		for (fs = filesys_info; fs; fs = fs->next) {
+			if (cancel_requested)
+				break;
+			if (fs->flags & FLAG_DONE)
+				continue;
+			/*
+			 * If the filesystem's pass number is higher
+			 * than the current pass number, then we don't
+			 * do it yet.
+			 */
+			if (fs->passno > passno) {
+				not_done_yet++;
+				continue;
+			}
+			/*
+			 * If a filesystem on a particular device has
+			 * already been spawned, then we need to defer
+			 * this to another pass.
+			 */
+			if (device_already_active(fs->device)) {
+				pass_done = 0;
+				continue;
+			}
+			/*
+			 * Spawn off the fsck process
+			 */
+			fsck_device(fs, serialize);
+			fs->flags |= FLAG_DONE;
+
+			/*
+			 * Only do one filesystem at a time, or if we
+			 * have a limit on the number of fsck's extant
+			 * at one time, apply that limit.
+			 */
+			if (serialize ||
+			    (max_running && (num_running >= max_running))) {
+				pass_done = 0;
+				break;
+			}
+		}
+		if (cancel_requested)
+			break;
+		if (verbose > 1)
+			printf("--waiting-- (pass %d)\n", passno);
+		status |= wait_many(pass_done ? FLAG_WAIT_ALL :
+				    FLAG_WAIT_ATLEAST_ONE);
+		if (pass_done) {
+			if (verbose > 1)
+				printf("----------------------------------\n");
+			passno++;
+		} else
+			not_done_yet++;
+	}
+	if (cancel_requested && !kill_sent) {
+		kill_all(SIGTERM);
+		kill_sent++;
+	}
+	status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
+	return status;
+}
+
+static void signal_cancel(int sig FSCK_ATTR((unused)))
+{
+	cancel_requested++;
+}
+
+static void PRS(int argc, char **argv)
+{
+	int     i, j;
+	char    *arg, *dev, *tmp = NULL;
+	char    options[128];
+	int     opt = 0;
+	int     opts_for_fsck = 0;
+	struct sigaction        sa;
+
+	/*
+	 * Set up signal action
+	 */
+	memset(&sa, 0, sizeof(struct sigaction));
+	sa.sa_handler = signal_cancel;
+	sigaction(SIGINT, &sa, 0);
+	sigaction(SIGTERM, &sa, 0);
+
+	num_devices = 0;
+	num_args = 0;
+	instance_list = 0;
+
+	for (i=1; i < argc; i++) {
+		arg = argv[i];
+		if (!arg)
+			continue;
+		if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
+			if (num_devices >= MAX_DEVICES) {
+				bb_error_msg_and_die("too many devices");
+			}
+			dev = blkid_get_devname(cache, arg, NULL);
+			if (!dev && strchr(arg, '=')) {
+				/*
+				 * Check to see if we failed because
+				 * /proc/partitions isn't found.
+				 */
+				if (access("/proc/partitions", R_OK) < 0) {
+					bb_perror_msg_and_die("can't open /proc/partitions "
+							"(is /proc mounted?)");
+				}
+				/*
+				 * Check to see if this is because
+				 * we're not running as root
+				 */
+				if (geteuid())
+					bb_error_msg_and_die(
+		"must be root to scan for matching filesystems: %s\n", arg);
+				else
+					bb_error_msg_and_die(
+		"can't find matching filesystem: %s", arg);
+			}
+			devices[num_devices++] = dev ? dev : string_copy(arg);
+			continue;
+		}
+		if (arg[0] != '-' || opts_for_fsck) {
+			if (num_args >= MAX_ARGS) {
+				bb_error_msg_and_die("too many arguments");
+			}
+			args[num_args++] = string_copy(arg);
+			continue;
+		}
+		for (j=1; arg[j]; j++) {
+			if (opts_for_fsck) {
+				options[++opt] = arg[j];
+				continue;
+			}
+			switch (arg[j]) {
+			case 'A':
+				doall++;
+				break;
+			case 'C':
+				progress++;
+				if (arg[j+1]) {
+					progress_fd = string_to_int(arg+j+1);
+					if (progress_fd < 0)
+						progress_fd = 0;
+					else
+						goto next_arg;
+				} else if ((i+1) < argc
+				 && argv[i+1][0] != '-') {
+					progress_fd = string_to_int(argv[i]);
+					if (progress_fd < 0)
+						progress_fd = 0;
+					else {
+						goto next_arg;
+						i++;
+					}
+				}
+				break;
+			case 'V':
+				verbose++;
+				break;
+			case 'N':
+				noexecute++;
+				break;
+			case 'R':
+				skip_root++;
+				break;
+			case 'T':
+				notitle++;
+				break;
+			case 'M':
+				like_mount++;
+				break;
+			case 'P':
+				parallel_root++;
+				break;
+			case 's':
+				serialize++;
+				break;
+			case 't':
+				tmp = 0;
+				if (fstype)
+					bb_show_usage();
+				if (arg[j+1])
+					tmp = arg+j+1;
+				else if ((i+1) < argc)
+					tmp = argv[++i];
+				else
+					bb_show_usage();
+				fstype = string_copy(tmp);
+				compile_fs_type(fstype, &fs_type_compiled);
+				goto next_arg;
+			case '-':
+				opts_for_fsck++;
+				break;
+			case '?':
+				bb_show_usage();
+				break;
+			default:
+				options[++opt] = arg[j];
+				break;
+			}
+		}
+	next_arg:
+		if (opt) {
+			options[0] = '-';
+			options[++opt] = '\0';
+			if (num_args >= MAX_ARGS) {
+				bb_error_msg("too many arguments");
+			}
+			args[num_args++] = string_copy(options);
+			opt = 0;
+		}
+	}
+	if (getenv("FSCK_FORCE_ALL_PARALLEL"))
+		force_all_parallel++;
+	if ((tmp = getenv("FSCK_MAX_INST")))
+	    max_running = atoi(tmp);
+}
+
+int fsck_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fsck_main(int argc, char **argv)
+{
+	int i, status = 0;
+	int interactive = 0;
+	const char *fstab;
+	struct fs_info *fs;
+
+	setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+	setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+
+	blkid_get_cache(&cache, NULL);
+	PRS(argc, argv);
+
+	if (!notitle)
+		printf("fsck %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+
+	fstab = getenv("FSTAB_FILE");
+	if (!fstab)
+		fstab = _PATH_MNTTAB;
+	load_fs_info(fstab);
+
+	fsck_path = e2fs_set_sbin_path();
+
+	if ((num_devices == 1) || (serialize))
+		interactive = 1;
+
+	/* If -A was specified ("check all"), do that! */
+	if (doall)
+		return check_all();
+
+	if (num_devices == 0) {
+		serialize++;
+		interactive++;
+		return check_all();
+	}
+	for (i = 0; i < num_devices; i++) {
+		if (cancel_requested) {
+			if (!kill_sent) {
+				kill_all(SIGTERM);
+				kill_sent++;
+			}
+			break;
+		}
+		fs = lookup(devices[i]);
+		if (!fs) {
+			fs = create_fs_device(devices[i], 0, "auto",
+					      0, -1, -1);
+			if (!fs)
+				continue;
+		}
+		fsck_device(fs, interactive);
+		if (serialize ||
+		    (max_running && (num_running >= max_running))) {
+			struct fsck_instance *inst;
+
+			inst = wait_one(0);
+			if (inst) {
+				status |= inst->exit_status;
+				free_instance(inst);
+			}
+			if (verbose > 1)
+				printf("----------------------------------\n");
+		}
+	}
+	status |= wait_many(FLAG_WAIT_ALL);
+	blkid_put_cache(cache);
+	return status;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/fsck.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/fsck.h
new file mode 100644
index 0000000..2ca2af7
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/fsck.h
@@ -0,0 +1,16 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck.h
+ */
+
+#define FSCK_ATTR(x) __attribute__(x)
+
+#define EXIT_OK          0
+#define EXIT_NONDESTRUCT 1
+#define EXIT_DESTRUCT    2
+#define EXIT_UNCORRECTED 4
+#define EXIT_ERROR       8
+#define EXIT_USAGE       16
+#define FSCK_CANCELED    32     /* Aborted with a signal or ^C */
+
+extern char *e2fs_set_sbin_path(void);
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/lsattr.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/lsattr.c
new file mode 100644
index 0000000..9e0e4cb
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/lsattr.c
@@ -0,0 +1,129 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lsattr.c		- List file attributes on an ext2 file system
+ *
+ * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                           Laboratoire MASI, Institut Blaise Pascal
+ *                           Universite Pierre et Marie Curie (Paris VI)
+ *
+ * This file can be redistributed under the terms of the GNU General
+ * Public License
+ */
+
+/*
+ * 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)
+ */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "ext2fs/ext2_fs.h"
+#include "e2fsbb.h"
+#include "e2p/e2p.h"
+
+#define OPT_RECUR 1
+#define OPT_ALL 2
+#define OPT_DIRS_OPT 4
+#define OPT_PF_LONG 8
+#define OPT_GENERATION 16
+static int flags;
+
+static void list_attributes(const char *name)
+{
+	unsigned long fsflags;
+	unsigned long generation;
+
+	if (fgetflags(name, &fsflags) == -1)
+		goto read_err;
+	if (flags & OPT_GENERATION) {
+		if (fgetversion(name, &generation) == -1)
+			goto read_err;
+		printf("%5lu ", generation);
+	}
+
+	if (flags & OPT_PF_LONG) {
+		printf("%-28s ", name);
+		print_e2flags(stdout, fsflags, PFOPT_LONG);
+		bb_putchar('\n');
+	} else {
+		print_e2flags(stdout, fsflags, 0);
+		printf(" %s\n", name);
+	}
+
+	return;
+read_err:
+	bb_perror_msg("reading %s", name);
+}
+
+static int lsattr_dir_proc(const char *, struct dirent *, void *);
+
+static void lsattr_args(const char *name)
+{
+	struct stat st;
+
+	if (lstat(name, &st) == -1) {
+		bb_perror_msg("stating %s", name);
+	} else {
+		if (S_ISDIR(st.st_mode) && !(flags & OPT_DIRS_OPT))
+			iterate_on_dir(name, lsattr_dir_proc, NULL);
+		else
+			list_attributes(name);
+	}
+}
+
+static int lsattr_dir_proc(const char *dir_name, struct dirent *de,
+			   void *private)
+{
+	struct stat st;
+	char *path;
+
+	path = concat_path_file(dir_name, de->d_name);
+
+	if (lstat(path, &st) == -1)
+		bb_perror_msg(path);
+	else {
+		if (de->d_name[0] != '.' || (flags & OPT_ALL)) {
+			list_attributes(path);
+			if (S_ISDIR(st.st_mode) && (flags & OPT_RECUR) &&
+			   (de->d_name[0] != '.' && (de->d_name[1] != '\0' ||
+			   (de->d_name[1] != '.' && de->d_name[2] != '\0')))) {
+				printf("\n%s:\n", path);
+				iterate_on_dir(path, lsattr_dir_proc, NULL);
+				bb_putchar('\n');
+			}
+		}
+	}
+
+	free(path);
+
+	return 0;
+}
+
+int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lsattr_main(int argc, char **argv)
+{
+	int i;
+
+	flags = getopt32(argv, "Radlv");
+
+	if (optind > argc - 1)
+		lsattr_args(".");
+	else
+		for (i = optind; i < argc; i++)
+			lsattr_args(argv[i]);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/mke2fs.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/mke2fs.c
new file mode 100644
index 0000000..7555650
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/mke2fs.c
@@ -0,0 +1,1335 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mke2fs.c - Make a ext2fs filesystem.
+ *
+ * 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 source tree.
+ */
+
+/* Usage: mke2fs [options] device
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <time.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <mntent.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include "e2fsbb.h"
+#include "ext2fs/ext2_fs.h"
+#include "uuid/uuid.h"
+#include "e2p/e2p.h"
+#include "ext2fs/ext2fs.h"
+#include "util.h"
+
+#define STRIDE_LENGTH 8
+
+#ifndef __sparc__
+#define ZAP_BOOTBLOCK
+#endif
+
+static const char * device_name;
+
+/* Command line options */
+static int	cflag;
+static int	quiet;
+static int	super_only;
+static int	force;
+static int	noaction;
+static int	journal_size;
+static int	journal_flags;
+static const char *bad_blocks_filename;
+static __u32	fs_stride;
+
+static struct ext2_super_block param;
+static char *creator_os;
+static char *volume_label;
+static char *mount_dir;
+static char *journal_device = NULL;
+static int   sync_kludge; /* Set using the MKE2FS_SYNC env. option */
+
+static int sys_page_size = 4096;
+static int linux_version_code = 0;
+
+static int int_log2(int arg)
+{
+	int l = 0;
+
+	arg >>= 1;
+	while (arg) {
+		l++;
+		arg >>= 1;
+	}
+	return l;
+}
+
+static int int_log10(unsigned int arg)
+{
+	int	l;
+
+	for (l = 0; arg; l++)
+		arg = arg / 10;
+	return l;
+}
+
+/*
+ * This function sets the default parameters for a filesystem
+ *
+ * The type is specified by the user.  The size is the maximum size
+ * (in megabytes) for which a set of parameters applies, with a size
+ * of zero meaning that it is the default parameter for the type.
+ * Note that order is important in the table below.
+ */
+#define DEF_MAX_BLOCKSIZE -1
+static const char default_str[] = "default";
+struct mke2fs_defaults {
+	const char	*type;
+	int		size;
+	int		blocksize;
+	int		inode_ratio;
+};
+
+static const struct mke2fs_defaults settings[] = {
+	{ default_str,	 0, 4096, 8192 },
+	{ default_str, 512, 1024, 4096 },
+	{ default_str,	 3, 1024, 8192 },
+	{ "journal",	 0, 4096, 8192 },
+	{ "news",	 0, 4096, 4096 },
+	{ "largefile",	 0, 4096, 1024 * 1024 },
+	{ "largefile4",  0, 4096, 4096 * 1024 },
+	{ 0,		 0,    0, 0},
+};
+
+static void set_fs_defaults(const char *fs_type,
+			    struct ext2_super_block *super,
+			    int blocksize, int sector_size,
+			    int *inode_ratio)
+{
+	int	megs;
+	int	ratio = 0;
+	const struct mke2fs_defaults *p;
+	int	use_bsize = 1024;
+
+	megs = super->s_blocks_count * (EXT2_BLOCK_SIZE(super) / 1024) / 1024;
+	if (inode_ratio)
+		ratio = *inode_ratio;
+	if (!fs_type)
+		fs_type = default_str;
+	for (p = settings; p->type; p++) {
+		if ((strcmp(p->type, fs_type) != 0) &&
+		    (strcmp(p->type, default_str) != 0))
+			continue;
+		if ((p->size != 0) && (megs > p->size))
+			continue;
+		if (ratio == 0)
+			*inode_ratio = p->inode_ratio < blocksize ?
+				blocksize : p->inode_ratio;
+		use_bsize = p->blocksize;
+	}
+	if (blocksize <= 0) {
+		if (use_bsize == DEF_MAX_BLOCKSIZE) {
+			use_bsize = sys_page_size;
+			if ((linux_version_code < (2*65536 + 6*256)) &&
+			    (use_bsize > 4096))
+				use_bsize = 4096;
+		}
+		if (sector_size && use_bsize < sector_size)
+			use_bsize = sector_size;
+		if ((blocksize < 0) && (use_bsize < (-blocksize)))
+			use_bsize = -blocksize;
+		blocksize = use_bsize;
+		super->s_blocks_count /= blocksize / 1024;
+	}
+	super->s_log_frag_size = super->s_log_block_size =
+		int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+}
+
+
+/*
+ * Helper function for read_bb_file and test_disk
+ */
+static void invalid_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk_t blk)
+{
+	bb_error_msg("Bad block %u out of range; ignored", blk);
+}
+
+/*
+ * Busybox stuff
+ */
+static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+static void mke2fs_error_msg_and_die(int retval, const char *fmt, ...)
+{
+	va_list ap;
+
+	if (retval) {
+		va_start(ap, fmt);
+		fprintf(stderr, "\nCould not ");
+		vfprintf(stderr, fmt, ap);
+		fprintf(stderr, "\n");
+		va_end(ap);
+		exit(EXIT_FAILURE);
+	}
+}
+
+static void mke2fs_verbose(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
+static void mke2fs_verbose(const char *fmt, ...)
+{
+	va_list ap;
+
+	if (!quiet) {
+		va_start(ap, fmt);
+		vfprintf(stdout, fmt, ap);
+		fflush(stdout);
+		va_end(ap);
+	}
+}
+
+static void mke2fs_verbose_done(void)
+{
+	mke2fs_verbose("done\n");
+}
+
+static void mke2fs_warning_msg(int retval, char *fmt, ... ) __attribute__ ((format (printf, 2, 3)));
+static void mke2fs_warning_msg(int retval, char *fmt, ... )
+{
+	va_list ap;
+
+	if (retval) {
+		va_start(ap, fmt);
+		fprintf(stderr, "\nWarning: ");
+		vfprintf(stderr, fmt, ap);
+		fprintf(stderr, "\n");
+		va_end(ap);
+	}
+}
+
+/*
+ * Reads the bad blocks list from a file
+ */
+static void read_bb_file(ext2_filsys fs, badblocks_list *bb_list,
+			 const char *bad_blocks_file)
+{
+	FILE		*f;
+	errcode_t	retval;
+
+	f = xfopen_for_read(bad_blocks_file);
+	retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
+	fclose (f);
+	mke2fs_error_msg_and_die(retval, "read bad blocks from list");
+}
+
+/*
+ * Runs the badblocks program to test the disk
+ */
+static void test_disk(ext2_filsys fs, badblocks_list *bb_list)
+{
+	FILE		*f;
+	errcode_t	retval;
+	char		buf[1024];
+
+	sprintf(buf, "badblocks -b %d %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);
+	f = popen(buf, "r");
+	if (!f) {
+		bb_perror_msg_and_die("can't run '%s'", buf);
+	}
+	retval = ext2fs_read_bb_FILE(fs, f, bb_list, invalid_block);
+	pclose(f);
+	mke2fs_error_msg_and_die(retval, "read bad blocks from program");
+}
+
+static void handle_bad_blocks(ext2_filsys fs, badblocks_list bb_list)
+{
+	dgrp_t			i;
+	blk_t			j;
+	unsigned		must_be_good;
+	blk_t			blk;
+	badblocks_iterate	bb_iter;
+	errcode_t		retval;
+	blk_t			group_block;
+	int			group;
+	int			group_bad;
+
+	if (!bb_list)
+		return;
+
+	/*
+	 * The primary superblock and group descriptors *must* be
+	 * good; if not, abort.
+	 */
+	must_be_good = fs->super->s_first_data_block + 1 + fs->desc_blocks;
+	for (i = fs->super->s_first_data_block; i <= must_be_good; i++) {
+		if (ext2fs_badblocks_list_test(bb_list, i)) {
+			bb_error_msg_and_die(
+				"Block %d in primary superblock/group descriptor area bad\n"
+				"Blocks %d through %d must be good in order to build a filesystem\n"
+				"Aborting ...", i, fs->super->s_first_data_block, must_be_good);
+		}
+	}
+
+	/*
+	 * See if any of the bad blocks are showing up in the backup
+	 * superblocks and/or group descriptors.  If so, issue a
+	 * warning and adjust the block counts appropriately.
+	 */
+	group_block = fs->super->s_first_data_block +
+		fs->super->s_blocks_per_group;
+
+	for (i = 1; i < fs->group_desc_count; i++) {
+		group_bad = 0;
+		for (j=0; j < fs->desc_blocks+1; j++) {
+			if (ext2fs_badblocks_list_test(bb_list,
+						       group_block + j)) {
+				mke2fs_warning_msg(!group_bad,
+					"the backup superblock/group descriptors at block %d contain\n"
+					"bad blocks\n", group_block);
+				group_bad++;
+				group = ext2fs_group_of_blk(fs, group_block+j);
+				fs->group_desc[group].bg_free_blocks_count++;
+				fs->super->s_free_blocks_count++;
+			}
+		}
+		group_block += fs->super->s_blocks_per_group;
+	}
+
+	/*
+	 * Mark all the bad blocks as used...
+	 */
+	retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
+	mke2fs_error_msg_and_die(retval, "mark bad blocks as used");
+
+	while (ext2fs_badblocks_list_iterate(bb_iter, &blk))
+		ext2fs_mark_block_bitmap(fs->block_map, blk);
+	ext2fs_badblocks_list_iterate_end(bb_iter);
+}
+
+/*
+ * These functions implement a generalized progress meter.
+ */
+struct progress_struct {
+	char		format[20];
+	char		backup[80];
+	__u32		max;
+	int		skip_progress;
+};
+
+static void progress_init(struct progress_struct *progress,
+			  const char *label,__u32 max)
+{
+	int	i;
+
+	memset(progress, 0, sizeof(struct progress_struct));
+	if (quiet)
+		return;
+
+	/*
+	 * Figure out how many digits we need
+	 */
+	i = int_log10(max);
+	sprintf(progress->format, "%%%dd/%%%dld", i, i);
+	memset(progress->backup, '\b', sizeof(progress->backup)-1);
+	progress->backup[sizeof(progress->backup)-1] = 0;
+	if ((2*i)+1 < (int) sizeof(progress->backup))
+		progress->backup[(2*i)+1] = 0;
+	progress->max = max;
+
+	progress->skip_progress = 0;
+	if (getenv("MKE2FS_SKIP_PROGRESS"))
+		progress->skip_progress++;
+
+	fputs(label, stdout);
+	fflush(stdout);
+}
+
+static void progress_update(struct progress_struct *progress, __u32 val)
+{
+	if ((progress->format[0] == 0) || progress->skip_progress)
+		return;
+	printf(progress->format, val, progress->max);
+	fputs(progress->backup, stdout);
+}
+
+static void progress_close(struct progress_struct *progress)
+{
+	if (progress->format[0] == 0)
+		return;
+	printf("%-28s\n", "done");
+}
+
+
+/*
+ * Helper function which zeros out _num_ blocks starting at _blk_.  In
+ * case of an error, the details of the error is returned via _ret_blk_
+ * and _ret_count_ if they are non-NULL pointers.  Returns 0 on
+ * success, and an error code on an error.
+ *
+ * As a special case, if the first argument is NULL, then it will
+ * attempt to free the static zeroizing buffer.  (This is to keep
+ * programs that check for memory leaks happy.)
+ */
+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;
+	static char	*buf;
+	errcode_t	retval;
+
+	/* If fs is null, clean up the static buffer and return */
+	if (!fs) {
+		if (buf) {
+			free(buf);
+			buf = 0;
+		}
+		return 0;
+	}
+	/* Allocate the zeroizing buffer if necessary */
+	if (!buf) {
+		buf = xzalloc(fs->blocksize * STRIDE_LENGTH);
+	}
+	/* 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)
+			count = STRIDE_LENGTH;
+		retval = io_channel_write_blk(fs->io, blk, count, buf);
+		if (retval) {
+			if (ret_count)
+				*ret_count = count;
+			if (ret_blk)
+				*ret_blk = blk;
+			return retval;
+		}
+		if (progress && j > next_update) {
+			next_update += num / 100;
+			progress_update(progress, blk);
+		}
+	}
+	return 0;
+}
+
+static void write_inode_tables(ext2_filsys fs)
+{
+	errcode_t	retval;
+	blk_t		blk;
+	dgrp_t		i;
+	int		num;
+	struct progress_struct progress;
+
+	if (quiet)
+		memset(&progress, 0, sizeof(progress));
+	else
+		progress_init(&progress, "Writing inode tables: ",
+			      fs->group_desc_count);
+
+	for (i = 0; i < fs->group_desc_count; i++) {
+		progress_update(&progress, i);
+
+		blk = fs->group_desc[i].bg_inode_table;
+		num = fs->inode_blocks_per_group;
+
+		retval = zero_blocks(fs, blk, num, 0, &blk, &num);
+		mke2fs_error_msg_and_die(retval,
+			"write %d blocks in inode table starting at %d.",
+			num, blk);
+		if (sync_kludge) {
+			if (sync_kludge == 1)
+				sync();
+			else if ((i % sync_kludge) == 0)
+				sync();
+		}
+	}
+	zero_blocks(0, 0, 0, 0, 0, 0);
+	progress_close(&progress);
+}
+
+static void create_root_dir(ext2_filsys fs)
+{
+	errcode_t		retval;
+	struct ext2_inode	inode;
+
+	retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, 0);
+	mke2fs_error_msg_and_die(retval, "create root dir");
+	if (geteuid()) {
+		retval = ext2fs_read_inode(fs, EXT2_ROOT_INO, &inode);
+		mke2fs_error_msg_and_die(retval, "read root inode");
+		inode.i_uid = getuid();
+		if (inode.i_uid)
+			inode.i_gid = getgid();
+		retval = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode);
+		mke2fs_error_msg_and_die(retval, "set root inode ownership");
+	}
+}
+
+static void create_lost_and_found(ext2_filsys fs)
+{
+	errcode_t		retval;
+	ext2_ino_t		ino;
+	const char		*name = "lost+found";
+	int			i = 1;
+	char			*msg = "create";
+	int			lpf_size = 0;
+
+	fs->umask = 077;
+	retval = ext2fs_mkdir(fs, EXT2_ROOT_INO, 0, name);
+	if (retval) {
+		goto CREATE_LOST_AND_FOUND_ERROR;
+	}
+
+	retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, strlen(name), 0, &ino);
+	if (retval) {
+		msg = "lookup";
+		goto CREATE_LOST_AND_FOUND_ERROR;
+	}
+
+	for (; i < EXT2_NDIR_BLOCKS; i++) {
+		if ((lpf_size += fs->blocksize) >= 16*1024)
+			break;
+		retval = ext2fs_expand_dir(fs, ino);
+		msg = "expand";
+CREATE_LOST_AND_FOUND_ERROR:
+		mke2fs_error_msg_and_die(retval, "%s %s", msg, name);
+	}
+}
+
+static void create_bad_block_inode(ext2_filsys fs, badblocks_list bb_list)
+{
+	errcode_t	retval;
+
+	ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_BAD_INO);
+	fs->group_desc[0].bg_free_inodes_count--;
+	fs->super->s_free_inodes_count--;
+	retval = ext2fs_update_bb_inode(fs, bb_list);
+	mke2fs_error_msg_and_die(retval, "set bad block inode");
+}
+
+static void reserve_inodes(ext2_filsys fs)
+{
+	ext2_ino_t	i;
+	int		group;
+
+	for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->super); i++) {
+		ext2fs_mark_inode_bitmap(fs->inode_map, i);
+		group = ext2fs_group_of_ino(fs, i);
+		fs->group_desc[group].bg_free_inodes_count--;
+		fs->super->s_free_inodes_count--;
+	}
+	ext2fs_mark_ib_dirty(fs);
+}
+
+#define BSD_DISKMAGIC   (0x82564557UL)  /* The disk magic number */
+#define BSD_MAGICDISK   (0x57455682UL)  /* The disk magic number reversed */
+#define BSD_LABEL_OFFSET        64
+
+static void zap_sector(ext2_filsys fs, int sect, int nsect)
+{
+	char *buf;
+	char *fmt = "could not %s %d";
+	int retval;
+	unsigned int *magic;
+
+	buf = xmalloc(512*nsect);
+
+	if (sect == 0) {
+		/* Check for a BSD disklabel, and don't erase it if so */
+		retval = io_channel_read_blk(fs->io, 0, -512, buf);
+		if (retval)
+			mke2fs_warning_msg(retval, fmt, "read block", 0);
+		else {
+			magic = (unsigned int *) (buf + BSD_LABEL_OFFSET);
+			if ((*magic == BSD_DISKMAGIC) ||
+			    (*magic == BSD_MAGICDISK))
+				return;
+		}
+	}
+
+	memset(buf, 0, 512*nsect);
+	io_channel_set_blksize(fs->io, 512);
+	retval = io_channel_write_blk(fs->io, sect, -512*nsect, buf);
+	io_channel_set_blksize(fs->io, fs->blocksize);
+	free(buf);
+	mke2fs_warning_msg(retval, fmt, "erase sector", sect);
+}
+
+static void create_journal_dev(ext2_filsys fs)
+{
+	struct progress_struct	progress;
+	errcode_t		retval;
+	char			*buf;
+	char			*fmt = "%s journal superblock";
+	blk_t			blk;
+	int			count;
+
+	retval = ext2fs_create_journal_superblock(fs,
+				  fs->super->s_blocks_count, 0, &buf);
+	mke2fs_error_msg_and_die(retval, fmt, "init");
+	if (quiet)
+		memset(&progress, 0, sizeof(progress));
+	else
+		progress_init(&progress, "Zeroing journal device: ",
+			      fs->super->s_blocks_count);
+
+	retval = zero_blocks(fs, 0, fs->super->s_blocks_count,
+			     &progress, &blk, &count);
+	mke2fs_error_msg_and_die(retval, "zero journal device (block %u, count %d)",
+			blk, count);
+	zero_blocks(0, 0, 0, 0, 0, 0);
+
+	retval = io_channel_write_blk(fs->io,
+				      fs->super->s_first_data_block+1,
+				      1, buf);
+	mke2fs_error_msg_and_die(retval, fmt, "write");
+	progress_close(&progress);
+}
+
+static void show_stats(ext2_filsys fs)
+{
+	struct ext2_super_block *s = fs->super;
+	char			*os;
+	blk_t			group_block;
+	dgrp_t			i;
+	int			need, col_left;
+
+	mke2fs_warning_msg((param.s_blocks_count != s->s_blocks_count),
+		"%d blocks unused\n", param.s_blocks_count - s->s_blocks_count);
+	os = e2p_os2string(fs->super->s_creator_os);
+	printf(	"Filesystem label=%.*s\n"
+			"OS type: %s\n"
+			"Block size=%u (log=%u)\n"
+			"Fragment size=%u (log=%u)\n"
+			"%u inodes, %u blocks\n"
+			"%u blocks (%2.2f%%) reserved for the super user\n"
+			"First data block=%u\n",
+			(int) sizeof(s->s_volume_name),
+			s->s_volume_name,
+			os,
+			fs->blocksize, s->s_log_block_size,
+			fs->fragsize, s->s_log_frag_size,
+			s->s_inodes_count, s->s_blocks_count,
+			s->s_r_blocks_count, 100.0 * s->s_r_blocks_count / s->s_blocks_count,
+			s->s_first_data_block);
+	free(os);
+	if (s->s_reserved_gdt_blocks) {
+		printf("Maximum filesystem blocks=%lu\n",
+		       (s->s_reserved_gdt_blocks + fs->desc_blocks) *
+		       (fs->blocksize / sizeof(struct ext2_group_desc)) *
+		       s->s_blocks_per_group);
+	}
+	printf(	"%u block group%s\n"
+			"%u blocks per group, %u fragments per group\n"
+			"%u inodes per group\n",
+			fs->group_desc_count, (fs->group_desc_count > 1) ? "s" : "",
+			s->s_blocks_per_group, s->s_frags_per_group,
+			s->s_inodes_per_group);
+	if (fs->group_desc_count == 1) {
+		bb_putchar('\n');
+		return;
+	}
+
+	printf("Superblock backups stored on blocks: ");
+	group_block = s->s_first_data_block;
+	col_left = 0;
+	for (i = 1; i < fs->group_desc_count; i++) {
+		group_block += s->s_blocks_per_group;
+		if (!ext2fs_bg_has_super(fs, i))
+			continue;
+		if (i != 1)
+			printf(", ");
+		need = int_log10(group_block) + 2;
+		if (need > col_left) {
+			printf("\n\t");
+			col_left = 72;
+		}
+		col_left -= need;
+		printf("%u", group_block);
+	}
+	puts("\n");
+}
+
+/*
+ * Set the S_CREATOR_OS field.  Return true if OS is known,
+ * otherwise, 0.
+ */
+static int set_os(struct ext2_super_block *sb, char *os)
+{
+	if (isdigit (*os)) {
+		sb->s_creator_os = atoi(os);
+		return 1;
+	}
+
+	if ((sb->s_creator_os = e2p_string2os(os)) >= 0) {
+		return 1;
+	} else if (!strcasecmp("GNU", os)) {
+		sb->s_creator_os = EXT2_OS_HURD;
+		return 1;
+	}
+	return 0;
+}
+
+static void parse_extended_opts(struct ext2_super_block *sb_param,
+				const char *opts)
+{
+	char	*buf, *token, *next, *p, *arg;
+	int	r_usage = 0;
+
+	buf = xstrdup(opts);
+	for (token = buf; token && *token; token = next) {
+		p = strchr(token, ',');
+		next = 0;
+		if (p) {
+			*p = 0;
+			next = p+1;
+		}
+		arg = strchr(token, '=');
+		if (arg) {
+			*arg = 0;
+			arg++;
+		}
+		if (strcmp(token, "stride") == 0) {
+			if (!arg) {
+				r_usage++;
+				continue;
+			}
+			fs_stride = strtoul(arg, &p, 0);
+			if (*p || (fs_stride == 0)) {
+				bb_error_msg("Invalid stride parameter: %s", arg);
+				r_usage++;
+				continue;
+			}
+		} else if (!strcmp(token, "resize")) {
+			unsigned long resize, bpg, rsv_groups;
+			unsigned long group_desc_count, desc_blocks;
+			unsigned int gdpb, blocksize;
+			int rsv_gdb;
+
+			if (!arg) {
+				r_usage++;
+				continue;
+			}
+
+			resize = parse_num_blocks(arg,
+						  sb_param->s_log_block_size);
+
+			if (resize == 0) {
+				bb_error_msg("Invalid resize parameter: %s", arg);
+				r_usage++;
+				continue;
+			}
+			if (resize <= sb_param->s_blocks_count) {
+				bb_error_msg("The resize maximum must be greater "
+						"than the filesystem size");
+				r_usage++;
+				continue;
+			}
+
+			blocksize = EXT2_BLOCK_SIZE(sb_param);
+			bpg = sb_param->s_blocks_per_group;
+			if (!bpg)
+				bpg = blocksize * 8;
+			gdpb = blocksize / sizeof(struct ext2_group_desc);
+			group_desc_count = (sb_param->s_blocks_count +
+					    bpg - 1) / bpg;
+			desc_blocks = (group_desc_count +
+				       gdpb - 1) / gdpb;
+			rsv_groups = (resize + bpg - 1) / bpg;
+			rsv_gdb = (rsv_groups + gdpb - 1) / gdpb -
+				desc_blocks;
+			if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb_param))
+				rsv_gdb = EXT2_ADDR_PER_BLOCK(sb_param);
+
+			if (rsv_gdb > 0) {
+				sb_param->s_feature_compat |=
+					EXT2_FEATURE_COMPAT_RESIZE_INODE;
+
+				sb_param->s_reserved_gdt_blocks = rsv_gdb;
+			}
+		} else
+			r_usage++;
+	}
+	if (r_usage) {
+		bb_error_msg_and_die(
+			"\nBad options specified.\n\n"
+			"Extended options are separated by commas, "
+			"and may take an argument which\n"
+			"\tis set off by an equals ('=') sign.\n\n"
+			"Valid extended options are:\n"
+			"\tstride=<stride length in blocks>\n"
+			"\tresize=<resize maximum size in blocks>\n");
+	}
+}
+
+static __u32 ok_features[3] = {
+	EXT3_FEATURE_COMPAT_HAS_JOURNAL |
+		EXT2_FEATURE_COMPAT_RESIZE_INODE |
+		EXT2_FEATURE_COMPAT_DIR_INDEX,  /* Compat */
+	EXT2_FEATURE_INCOMPAT_FILETYPE|         /* Incompat */
+		EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|
+		EXT2_FEATURE_INCOMPAT_META_BG,
+	EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER     /* R/O compat */
+};
+
+static int PRS(int argc, char **argv)
+{
+	int		c;
+	int		size;
+	char *		tmp;
+	int		blocksize = 0;
+	int		inode_ratio = 0;
+	int		inode_size = 0;
+	int		reserved_ratio = 5;
+	int		sector_size = 0;
+	int		show_version_only = 0;
+	ext2_ino_t	num_inodes = 0;
+	errcode_t	retval;
+	char *		extended_opts = NULL;
+	const char *	fs_type = NULL;
+	blk_t		dev_size;
+	long		sysval;
+
+	/* Update our PATH to include /sbin  */
+	e2fs_set_sbin_path();
+
+	tmp = getenv("MKE2FS_SYNC");
+	if (tmp)
+		sync_kludge = atoi(tmp);
+
+	/* Determine the system page size if possible */
+#if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
+#define _SC_PAGESIZE _SC_PAGE_SIZE
+#endif
+#ifdef _SC_PAGESIZE
+	sysval = sysconf(_SC_PAGESIZE);
+	if (sysval > 0)
+		sys_page_size = sysval;
+#endif /* _SC_PAGESIZE */
+
+	setbuf(stdout, NULL);
+	setbuf(stderr, NULL);
+	memset(&param, 0, sizeof(struct ext2_super_block));
+	param.s_rev_level = 1;  /* Create revision 1 filesystems now */
+	param.s_feature_incompat |= EXT2_FEATURE_INCOMPAT_FILETYPE;
+	param.s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+
+#ifdef __linux__
+	linux_version_code = get_linux_version_code();
+	if (linux_version_code && linux_version_code < KERNEL_VERSION(2,2,0)) {
+		param.s_rev_level = 0;
+		param.s_feature_incompat = 0;
+		param.s_feature_compat = 0;
+		param.s_feature_ro_compat = 0;
+	}
+#endif
+
+	/* If called as mkfs.ext3, create a journal inode */
+	if (last_char_is(applet_name, '3'))
+		journal_size = -1;
+
+	while ((c = getopt (argc, argv,
+		    "b:cE:f:g:i:jl:m:no:qr:R:s:tvI:J:ST:FL:M:N:O:V")) != EOF) {
+		switch (c) {
+		case 'b':
+			blocksize = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE);
+			mke2fs_warning_msg((blocksize > 4096),
+				"blocksize %d not usable on most systems",
+				blocksize);
+			param.s_log_block_size =
+				int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+			break;
+		case 'c':	/* Check for bad blocks */
+		case 't':	/* deprecated */
+			cflag++;
+			break;
+		case 'f':
+			size = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE);
+			param.s_log_frag_size =
+				int_log2(size >> EXT2_MIN_BLOCK_LOG_SIZE);
+			mke2fs_warning_msg(1, "fragments not supported. Ignoring -f option");
+			break;
+		case 'g':
+			param.s_blocks_per_group = xatou32(optarg);
+			if ((param.s_blocks_per_group % 8) != 0) {
+				bb_error_msg_and_die("blocks per group must be multiple of 8");
+			}
+			break;
+		case 'i':
+			/* Huh? is "* 1024" correct? */
+			inode_ratio = xatou_range(optarg, EXT2_MIN_BLOCK_SIZE, EXT2_MAX_BLOCK_SIZE * 1024);
+			break;
+		case 'J':
+			parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg);
+			break;
+		case 'j':
+			param.s_feature_compat |=
+				EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+			if (!journal_size)
+				journal_size = -1;
+			break;
+		case 'l':
+			bad_blocks_filename = optarg;
+			break;
+		case 'm':
+			reserved_ratio = xatou_range(optarg, 0, 50);
+			break;
+		case 'n':
+			noaction++;
+			break;
+		case 'o':
+			creator_os = optarg;
+			break;
+		case 'r':
+			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;
+				param.s_feature_ro_compat = 0;
+			}
+			break;
+		case 's':	/* deprecated */
+			if (xatou(optarg))
+				param.s_feature_ro_compat |=
+					EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+			else
+				param.s_feature_ro_compat &=
+					~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+			break;
+#ifdef EXT2_DYNAMIC_REV
+		case 'I':
+			inode_size = xatoi_positive(optarg);
+			break;
+#endif
+		case 'N':
+			num_inodes = xatoi_positive(optarg);
+			break;
+		case 'v':
+			quiet = 0;
+			break;
+		case 'q':
+			quiet = 1;
+			break;
+		case 'F':
+			force = 1;
+			break;
+		case 'L':
+			volume_label = optarg;
+			break;
+		case 'M':
+			mount_dir = optarg;
+			break;
+		case 'O':
+			if (!strcmp(optarg, "none")) {
+				param.s_feature_compat = 0;
+				param.s_feature_incompat = 0;
+				param.s_feature_ro_compat = 0;
+				break;
+			}
+			if (e2p_edit_feature(optarg,
+					    &param.s_feature_compat,
+					    ok_features)) {
+				bb_error_msg_and_die("Invalid filesystem option set: %s", optarg);
+			}
+			break;
+		case 'E':
+		case 'R':
+			extended_opts = optarg;
+			break;
+		case 'S':
+			super_only = 1;
+			break;
+		case 'T':
+			fs_type = optarg;
+			break;
+		case 'V':
+			/* Print version number and exit */
+			show_version_only = 1;
+			quiet = 0;
+			break;
+		default:
+			bb_show_usage();
+		}
+	}
+	if ((optind == argc) /*&& !show_version_only*/)
+		bb_show_usage();
+	device_name = argv[optind++];
+
+	mke2fs_verbose("mke2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+
+	if (show_version_only) {
+		return 0;
+	}
+
+	/*
+	 * If there's no blocksize specified and there is a journal
+	 * device, use it to figure out the blocksize
+	 */
+	if (blocksize <= 0 && journal_device) {
+		ext2_filsys     jfs;
+		io_manager      io_ptr;
+
+#ifdef CONFIG_TESTIO_DEBUG
+		io_ptr = test_io_manager;
+		test_io_backing_manager = unix_io_manager;
+#else
+		io_ptr = unix_io_manager;
+#endif
+		retval = ext2fs_open(journal_device,
+				     EXT2_FLAG_JOURNAL_DEV_OK, 0,
+				     0, io_ptr, &jfs);
+		mke2fs_error_msg_and_die(retval, "open journal device %s", journal_device);
+		if ((blocksize < 0) && (jfs->blocksize < (unsigned) (-blocksize))) {
+			bb_error_msg_and_die(
+				"Journal dev blocksize (%d) smaller than "
+				"minimum blocksize %d\n", jfs->blocksize,
+				-blocksize);
+		}
+		blocksize = jfs->blocksize;
+		param.s_log_block_size =
+			int_log2(blocksize >> EXT2_MIN_BLOCK_LOG_SIZE);
+		ext2fs_close(jfs);
+	}
+
+	if (blocksize > sys_page_size) {
+		mke2fs_warning_msg(1, "%d-byte blocks too big for system (max %d)",
+			blocksize, sys_page_size);
+		if (!force) {
+			proceed_question();
+		}
+		bb_error_msg("Forced to continue");
+	}
+	mke2fs_warning_msg(((blocksize > 4096) &&
+		(param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL)),
+		"some 2.4 kernels do not support "
+		"blocksizes greater than 4096 using ext3.\n"
+		"Use -b 4096 if this is an issue for you\n");
+
+	if (optind < argc) {
+		param.s_blocks_count = parse_num_blocks(argv[optind++],
+				param.s_log_block_size);
+		mke2fs_error_msg_and_die(!param.s_blocks_count, "invalid blocks count - %s", argv[optind - 1]);
+	}
+	if (optind < argc)
+		bb_show_usage();
+
+	if (param.s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+		if (!fs_type)
+			fs_type = "journal";
+		reserved_ratio = 0;
+		param.s_feature_incompat = EXT3_FEATURE_INCOMPAT_JOURNAL_DEV;
+		param.s_feature_compat = 0;
+		param.s_feature_ro_compat = 0;
+	}
+	if (param.s_rev_level == EXT2_GOOD_OLD_REV &&
+	    (param.s_feature_compat || param.s_feature_ro_compat ||
+	     param.s_feature_incompat))
+		param.s_rev_level = 1;	/* Create a revision 1 filesystem */
+
+	check_plausibility(device_name , force);
+	check_mount(device_name, force, "filesystem");
+
+	param.s_log_frag_size = param.s_log_block_size;
+
+	if (noaction && param.s_blocks_count) {
+		dev_size = param.s_blocks_count;
+		retval = 0;
+	} else {
+	retry:
+		retval = ext2fs_get_device_size(device_name,
+						EXT2_BLOCK_SIZE(&param),
+						&dev_size);
+		if ((retval == EFBIG) &&
+		    (blocksize == 0) &&
+		    (param.s_log_block_size == 0)) {
+			param.s_log_block_size = 2;
+			blocksize = 4096;
+			goto retry;
+		}
+	}
+
+	mke2fs_error_msg_and_die((retval && (retval != EXT2_ET_UNIMPLEMENTED)),"determine filesystem size");
+
+	if (!param.s_blocks_count) {
+		if (retval == EXT2_ET_UNIMPLEMENTED) {
+			mke2fs_error_msg_and_die(1,
+				"determine device size; you "
+				"must specify\nthe size of the "
+				"filesystem");
+		} else {
+			if (dev_size == 0) {
+				bb_error_msg_and_die(
+					"Device size reported to be zero.  "
+					"Invalid partition specified, or\n\t"
+					"partition table wasn't reread "
+					"after running fdisk, due to\n\t"
+					"a modified partition being busy "
+					"and in use.  You may need to reboot\n\t"
+					"to re-read your partition table.\n"
+				);
+			}
+			param.s_blocks_count = dev_size;
+			if (sys_page_size > EXT2_BLOCK_SIZE(&param))
+				param.s_blocks_count &= ~((sys_page_size /
+							   EXT2_BLOCK_SIZE(&param))-1);
+		}
+
+	} else if (!force && (param.s_blocks_count > dev_size)) {
+		bb_error_msg("Filesystem larger than apparent device size");
+		proceed_question();
+	}
+
+	/*
+	 * If the user asked for HAS_JOURNAL, then make sure a journal
+	 * gets created.
+	 */
+	if ((param.s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+	    !journal_size)
+		journal_size = -1;
+
+	/* Set first meta blockgroup via an environment variable */
+	/* (this is mostly for debugging purposes) */
+	if ((param.s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) &&
+	    ((tmp = getenv("MKE2FS_FIRST_META_BG"))))
+		param.s_first_meta_bg = atoi(tmp);
+
+	/* Get the hardware sector size, if available */
+	retval = ext2fs_get_device_sectsize(device_name, &sector_size);
+	mke2fs_error_msg_and_die(retval, "determine hardware sector size");
+
+	if ((tmp = getenv("MKE2FS_DEVICE_SECTSIZE")) != NULL)
+		sector_size = atoi(tmp);
+
+	set_fs_defaults(fs_type, &param, blocksize, sector_size, &inode_ratio);
+	blocksize = EXT2_BLOCK_SIZE(&param);
+
+	if (extended_opts)
+		parse_extended_opts(&param, extended_opts);
+
+	/* 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) &&
+	    !(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");
+	}
+
+	if (param.s_blocks_per_group) {
+		if (param.s_blocks_per_group < 256 ||
+		    param.s_blocks_per_group > 8 * (unsigned) blocksize) {
+			bb_error_msg_and_die("blocks per group count out of range");
+		}
+	}
+
+	if (!force && param.s_blocks_count >= (1 << 31)) {
+		bb_error_msg_and_die("Filesystem too large.  No more than 2**31-1 blocks\n"
+			"\t (8TB using a blocksize of 4k) are currently supported.");
+	}
+
+	if (inode_size) {
+		if (inode_size < EXT2_GOOD_OLD_INODE_SIZE ||
+		    inode_size > EXT2_BLOCK_SIZE(&param) ||
+		    inode_size & (inode_size - 1)) {
+			bb_error_msg_and_die("invalid inode size %d (min %d/max %d)",
+				inode_size, EXT2_GOOD_OLD_INODE_SIZE,
+				blocksize);
+		}
+		mke2fs_warning_msg((inode_size != EXT2_GOOD_OLD_INODE_SIZE),
+			"%d-byte inodes not usable on most systems",
+			inode_size);
+		param.s_inode_size = inode_size;
+	}
+
+	/*
+	 * Calculate number of inodes based on the inode ratio
+	 */
+	param.s_inodes_count = num_inodes ? num_inodes :
+		((__u64) param.s_blocks_count * blocksize)
+			/ inode_ratio;
+
+	/*
+	 * Calculate number of blocks to reserve
+	 */
+	param.s_r_blocks_count = (param.s_blocks_count * reserved_ratio) / 100;
+	return 1;
+}
+
+static void mke2fs_clean_up(void)
+{
+	if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device);
+}
+
+int mke2fs_main (int argc, char **argv);
+int mke2fs_main (int argc, char **argv)
+{
+	errcode_t	retval;
+	ext2_filsys	fs;
+	badblocks_list	bb_list = 0;
+	unsigned int	i;
+	int		val;
+	io_manager	io_ptr;
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		atexit(mke2fs_clean_up);
+	if (!PRS(argc, argv))
+		return 0;
+
+#ifdef CONFIG_TESTIO_DEBUG
+	io_ptr = test_io_manager;
+	test_io_backing_manager = unix_io_manager;
+#else
+	io_ptr = unix_io_manager;
+#endif
+
+	/*
+	 * Initialize the superblock....
+	 */
+	retval = ext2fs_initialize(device_name, 0, &param,
+				   io_ptr, &fs);
+	mke2fs_error_msg_and_die(retval, "set up superblock");
+
+	/*
+	 * Wipe out the old on-disk superblock
+	 */
+	if (!noaction)
+		zap_sector(fs, 2, 6);
+
+	/*
+	 * Generate a UUID for it...
+	 */
+	uuid_generate(fs->super->s_uuid);
+
+	/*
+	 * Initialize the directory index variables
+	 */
+	fs->super->s_def_hash_version = EXT2_HASH_TEA;
+	uuid_generate((unsigned char *) fs->super->s_hash_seed);
+
+	/*
+	 * Add "jitter" to the superblock's check interval so that we
+	 * don't check all the filesystems at the same time.  We use a
+	 * kludgy hack of using the UUID to derive a random jitter value.
+	 */
+	for (i = 0, val = 0; i < sizeof(fs->super->s_uuid); i++)
+		val += fs->super->s_uuid[i];
+	fs->super->s_max_mnt_count += val % EXT2_DFL_MAX_MNT_COUNT;
+
+	/*
+	 * Override the creator OS, if applicable
+	 */
+	if (creator_os && !set_os(fs->super, creator_os)) {
+		bb_error_msg_and_die("unknown os - %s", creator_os);
+	}
+
+	/*
+	 * For the Hurd, we will turn off filetype since it doesn't
+	 * support it.
+	 */
+	if (fs->super->s_creator_os == EXT2_OS_HURD)
+		fs->super->s_feature_incompat &=
+			~EXT2_FEATURE_INCOMPAT_FILETYPE;
+
+	/*
+	 * Set the volume label...
+	 */
+	if (volume_label) {
+		snprintf(fs->super->s_volume_name, sizeof(fs->super->s_volume_name), "%s", volume_label);
+	}
+
+	/*
+	 * Set the last mount directory
+	 */
+	if (mount_dir) {
+		snprintf(fs->super->s_last_mounted, sizeof(fs->super->s_last_mounted), "%s", mount_dir);
+	}
+
+	if (!quiet || noaction)
+		show_stats(fs);
+
+	if (noaction)
+		return 0;
+
+	if (fs->super->s_feature_incompat &
+	    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+		create_journal_dev(fs);
+		return (ext2fs_close(fs) ? 1 : 0);
+	}
+
+	if (bad_blocks_filename)
+		read_bb_file(fs, &bb_list, bad_blocks_filename);
+	if (cflag)
+		test_disk(fs, &bb_list);
+
+	handle_bad_blocks(fs, bb_list);
+	fs->stride = fs_stride;
+	retval = ext2fs_allocate_tables(fs);
+	mke2fs_error_msg_and_die(retval, "allocate filesystem tables");
+	if (super_only) {
+		fs->super->s_state |= EXT2_ERROR_FS;
+		fs->flags &= ~(EXT2_FLAG_IB_DIRTY|EXT2_FLAG_BB_DIRTY);
+	} else {
+		/* rsv must be a power of two (64kB is MD RAID sb alignment) */
+		unsigned int rsv = 65536 / fs->blocksize;
+		unsigned long blocks = fs->super->s_blocks_count;
+		unsigned long start;
+		blk_t ret_blk;
+
+#ifdef ZAP_BOOTBLOCK
+		zap_sector(fs, 0, 2);
+#endif
+
+		/*
+		 * Wipe out any old MD RAID (or other) metadata at the end
+		 * of the device.  This will also verify that the device is
+		 * as large as we think.  Be careful with very small devices.
+		 */
+		start = (blocks & ~(rsv - 1));
+		if (start > rsv)
+			start -= rsv;
+		if (start > 0)
+			retval = zero_blocks(fs, start, blocks - start,
+					     NULL, &ret_blk, NULL);
+
+		mke2fs_warning_msg(retval, "can't zero block %u at end of filesystem", ret_blk);
+		write_inode_tables(fs);
+		create_root_dir(fs);
+		create_lost_and_found(fs);
+		reserve_inodes(fs);
+		create_bad_block_inode(fs, bb_list);
+		if (fs->super->s_feature_compat &
+		    EXT2_FEATURE_COMPAT_RESIZE_INODE) {
+			retval = ext2fs_create_resize_inode(fs);
+			mke2fs_error_msg_and_die(retval, "reserve blocks for online resize");
+		}
+	}
+
+	if (journal_device) {
+		make_journal_device(journal_device, fs, quiet, force);
+	} else if (journal_size) {
+		make_journal_blocks(fs, journal_size, journal_flags, quiet);
+	}
+
+	mke2fs_verbose("Writing superblocks and filesystem accounting information: ");
+	retval = ext2fs_flush(fs);
+	mke2fs_warning_msg(retval, "had trouble writing out superblocks");
+	mke2fs_verbose_done();
+	if (!quiet && !getenv("MKE2FS_SKIP_CHECK_MSG"))
+		print_check_message(fs);
+	val = ext2fs_close(fs);
+	return (retval || val) ? 1 : 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/tune2fs.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/tune2fs.c
new file mode 100644
index 0000000..3c3f4af
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/tune2fs.c
@@ -0,0 +1,710 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tune2fs.c - Change the file system parameters on an ext2 file system
+ *
+ * Copyright (C) 1992, 1993, 1994  Remy Card <card@masi.ibp.fr>
+ *                                 Laboratoire MASI, Institut Blaise Pascal
+ *                                 Universite Pierre et Marie Curie (Paris VI)
+ *
+ * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * History:
+ * 93/06/01	- Creation
+ * 93/10/31	- Added the -c option to change the maximal mount counts
+ * 93/12/14	- Added -l flag to list contents of superblock
+ *                M.J.E. Mol (marcel@duteca.et.tudelft.nl)
+ *                F.W. ten Wolde (franky@duteca.et.tudelft.nl)
+ * 93/12/29	- Added the -e option to change errors behavior
+ * 94/02/27	- Ported to use the ext2fs library
+ * 94/03/06	- Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "e2fsbb.h"
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+#include "uuid/uuid.h"
+#include "e2p/e2p.h"
+#include "ext2fs/kernel-jbd.h"
+#include "util.h"
+#include "blkid/blkid.h"
+
+#include "libbb.h"
+
+static char * device_name = NULL;
+static char * new_label, *new_last_mounted, *new_UUID;
+static char * io_options;
+static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
+static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
+static time_t last_check_time;
+static int print_label;
+static int max_mount_count, mount_count, mount_flags;
+static unsigned long interval, reserved_blocks;
+static unsigned reserved_ratio;
+static unsigned long resgid, resuid;
+static unsigned short errors;
+static int open_flag;
+static char *features_cmd;
+static char *mntopts_cmd;
+
+static int journal_size, journal_flags;
+static char *journal_device = NULL;
+
+static const char *please_fsck = "Please run e2fsck on the filesystem\n";
+
+static __u32 ok_features[3] = {
+	EXT3_FEATURE_COMPAT_HAS_JOURNAL | EXT2_FEATURE_COMPAT_DIR_INDEX,
+	EXT2_FEATURE_INCOMPAT_FILETYPE,
+	EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
+};
+
+/*
+ * Remove an external journal from the filesystem
+ */
+static void remove_journal_device(ext2_filsys fs)
+{
+	char		*journal_path;
+	ext2_filsys	jfs;
+	char		buf[1024];
+	journal_superblock_t	*jsb;
+	int		i, nr_users;
+	errcode_t	retval;
+	int		commit_remove_journal = 0;
+	io_manager	io_ptr;
+
+	if (f_flag)
+		commit_remove_journal = 1; /* force removal even if error */
+
+	uuid_unparse(fs->super->s_journal_uuid, buf);
+	journal_path = blkid_get_devname(NULL, "UUID", buf);
+
+	if (!journal_path) {
+		journal_path =
+			ext2fs_find_block_device(fs->super->s_journal_dev);
+		if (!journal_path)
+			return;
+	}
+
+	io_ptr = unix_io_manager;
+	retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
+			     EXT2_FLAG_JOURNAL_DEV_OK, 0,
+			     fs->blocksize, io_ptr, &jfs);
+	if (retval) {
+		bb_error_msg("Failed to open external journal");
+		goto no_valid_journal;
+	}
+	if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+		bb_error_msg("%s is not a journal device", journal_path);
+		goto no_valid_journal;
+	}
+
+	/* Get the journal superblock */
+	if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
+		bb_error_msg("Failed to read journal superblock");
+		goto no_valid_journal;
+	}
+
+	jsb = (journal_superblock_t *) buf;
+	if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
+	    (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
+		bb_error_msg("Journal superblock not found!");
+		goto no_valid_journal;
+	}
+
+	/* Find the filesystem UUID */
+	nr_users = ntohl(jsb->s_nr_users);
+	for (i=0; i < nr_users; i++) {
+		if (memcmp(fs->super->s_uuid,
+			   &jsb->s_users[i*16], 16) == 0)
+			break;
+	}
+	if (i >= nr_users) {
+		bb_error_msg("Filesystem's UUID not found on journal device");
+		commit_remove_journal = 1;
+		goto no_valid_journal;
+	}
+	nr_users--;
+	for (i=0; i < nr_users; i++)
+		memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
+	jsb->s_nr_users = htonl(nr_users);
+
+	/* Write back the journal superblock */
+	if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
+		bb_error_msg("Failed to write journal superblock");
+		goto no_valid_journal;
+	}
+
+	commit_remove_journal = 1;
+
+no_valid_journal:
+	if (commit_remove_journal == 0)
+		bb_error_msg_and_die("Journal NOT removed");
+	fs->super->s_journal_dev = 0;
+	uuid_clear(fs->super->s_journal_uuid);
+	ext2fs_mark_super_dirty(fs);
+	puts("Journal removed");
+	free(journal_path);
+}
+
+/* Helper function for remove_journal_inode */
+static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
+			       int blockcnt EXT2FS_ATTR((unused)),
+			       void *private EXT2FS_ATTR((unused)))
+{
+	blk_t	block;
+	int	group;
+
+	block = *blocknr;
+	ext2fs_unmark_block_bitmap(fs->block_map,block);
+	group = ext2fs_group_of_blk(fs, block);
+	fs->group_desc[group].bg_free_blocks_count++;
+	fs->super->s_free_blocks_count++;
+	return 0;
+}
+
+/*
+ * Remove the journal inode from the filesystem
+ */
+static void remove_journal_inode(ext2_filsys fs)
+{
+	struct ext2_inode	inode;
+	errcode_t		retval;
+	ino_t			ino = fs->super->s_journal_inum;
+	char *msg = "to read";
+	char *s = "journal inode";
+
+	retval = ext2fs_read_inode(fs, ino,  &inode);
+	if (retval)
+		goto REMOVE_JOURNAL_INODE_ERROR;
+	if (ino == EXT2_JOURNAL_INO) {
+		retval = ext2fs_read_bitmaps(fs);
+		if (retval) {
+			msg = "to read bitmaps";
+			s = "";
+			goto REMOVE_JOURNAL_INODE_ERROR;
+		}
+		retval = ext2fs_block_iterate(fs, ino, 0, NULL,
+					      release_blocks_proc, NULL);
+		if (retval) {
+			msg = "clearing";
+			goto REMOVE_JOURNAL_INODE_ERROR;
+		}
+		memset(&inode, 0, sizeof(inode));
+		ext2fs_mark_bb_dirty(fs);
+		fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+	} else
+		inode.i_flags &= ~EXT2_IMMUTABLE_FL;
+	retval = ext2fs_write_inode(fs, ino, &inode);
+	if (retval) {
+		msg = "writing";
+REMOVE_JOURNAL_INODE_ERROR:
+		bb_error_msg_and_die("Failed %s %s", msg, s);
+	}
+	fs->super->s_journal_inum = 0;
+	ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * Update the default mount options
+ */
+static void update_mntopts(ext2_filsys fs, char *mntopts)
+{
+	struct ext2_super_block *sb= fs->super;
+
+	if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0))
+		bb_error_msg_and_die("Invalid mount option set: %s", mntopts);
+	ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * Update the feature set as provided by the user.
+ */
+static void update_feature_set(ext2_filsys fs, char *features)
+{
+	int sparse, old_sparse, filetype, old_filetype;
+	int journal, old_journal, dxdir, old_dxdir;
+	struct ext2_super_block *sb= fs->super;
+	__u32	old_compat, old_incompat, old_ro_compat;
+
+	old_compat = sb->s_feature_compat;
+	old_ro_compat = sb->s_feature_ro_compat;
+	old_incompat = sb->s_feature_incompat;
+
+	old_sparse = sb->s_feature_ro_compat &
+		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+	old_filetype = sb->s_feature_incompat &
+		EXT2_FEATURE_INCOMPAT_FILETYPE;
+	old_journal = sb->s_feature_compat &
+		EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+	old_dxdir = sb->s_feature_compat &
+		EXT2_FEATURE_COMPAT_DIR_INDEX;
+	if (e2p_edit_feature(features, &sb->s_feature_compat, ok_features))
+		bb_error_msg_and_die("Invalid filesystem option set: %s", features);
+	sparse = sb->s_feature_ro_compat &
+		EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+	filetype = sb->s_feature_incompat &
+		EXT2_FEATURE_INCOMPAT_FILETYPE;
+	journal = sb->s_feature_compat &
+		EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+	dxdir = sb->s_feature_compat &
+		EXT2_FEATURE_COMPAT_DIR_INDEX;
+	if (old_journal && !journal) {
+		if ((mount_flags & EXT2_MF_MOUNTED) &&
+		    !(mount_flags & EXT2_MF_READONLY)) {
+			bb_error_msg_and_die(
+				"The has_journal flag may only be "
+				"cleared when the filesystem is\n"
+				"unmounted or mounted "
+				"read-only");
+		}
+		if (sb->s_feature_incompat &
+		    EXT3_FEATURE_INCOMPAT_RECOVER) {
+			bb_error_msg_and_die(
+				"The needs_recovery flag is set.  "
+				"%s before clearing the has_journal flag.",
+				please_fsck);
+		}
+		if (sb->s_journal_inum) {
+			remove_journal_inode(fs);
+		}
+		if (sb->s_journal_dev) {
+			remove_journal_device(fs);
+		}
+	}
+	if (journal && !old_journal) {
+		/*
+		 * If adding a journal flag, let the create journal
+		 * code below handle creating setting the flag and
+		 * creating the journal.  We supply a default size if
+		 * necessary.
+		 */
+		if (!journal_size)
+			journal_size = -1;
+		sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+	}
+	if (dxdir && !old_dxdir) {
+		if (!sb->s_def_hash_version)
+			sb->s_def_hash_version = EXT2_HASH_TEA;
+		if (uuid_is_null((unsigned char *) sb->s_hash_seed))
+			uuid_generate((unsigned char *) sb->s_hash_seed);
+	}
+
+	if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
+	    (sb->s_feature_compat || sb->s_feature_ro_compat ||
+	     sb->s_feature_incompat))
+		ext2fs_update_dynamic_rev(fs);
+	if ((sparse != old_sparse) ||
+	    (filetype != old_filetype)) {
+		sb->s_state &= ~EXT2_VALID_FS;
+		printf("\n%s\n", please_fsck);
+	}
+	if ((old_compat != sb->s_feature_compat) ||
+	    (old_ro_compat != sb->s_feature_ro_compat) ||
+	    (old_incompat != sb->s_feature_incompat))
+		ext2fs_mark_super_dirty(fs);
+}
+
+/*
+ * Add a journal to the filesystem.
+ */
+static void add_journal(ext2_filsys fs)
+{
+	if (fs->super->s_feature_compat &
+	    EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
+		bb_error_msg_and_die("The filesystem already has a journal");
+	}
+	if (journal_device) {
+		make_journal_device(journal_device, fs, 0, 0);
+	} else if (journal_size) {
+		make_journal_blocks(fs, journal_size, journal_flags, 0);
+		/*
+		 * If the filesystem wasn't mounted, we need to force
+		 * the block group descriptors out.
+		 */
+		if ((mount_flags & EXT2_MF_MOUNTED) == 0)
+			fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
+	}
+	print_check_message(fs);
+}
+
+/*
+ * Busybox stuff
+ */
+static char * x_blkid_get_devname(const char *token)
+{
+	char * dev_name;
+
+	if (!(dev_name = blkid_get_devname(NULL, token, NULL)))
+		bb_error_msg_and_die("Unable to resolve '%s'", token);
+	return dev_name;
+}
+
+#ifdef CONFIG_E2LABEL
+static void parse_e2label_options(int argc, char ** argv)
+{
+	if ((argc < 2) || (argc > 3))
+		bb_show_usage();
+	io_options = strchr(argv[1], '?');
+	if (io_options)
+		*io_options++ = 0;
+	device_name = x_blkid_get_devname(argv[1]);
+	if (argc == 3) {
+		open_flag = EXT2_FLAG_RW | EXT2_FLAG_JOURNAL_DEV_OK;
+		L_flag = 1;
+		new_label = argv[2];
+	} else
+		print_label++;
+}
+#else
+#define parse_e2label_options(x,y)
+#endif
+
+static time_t parse_time(char *str)
+{
+	struct	tm	ts;
+
+	if (strcmp(str, "now") == 0) {
+		return time(0);
+	}
+	memset(&ts, 0, sizeof(ts));
+#ifdef HAVE_STRPTIME
+	strptime(str, "%Y%m%d%H%M%S", &ts);
+#else
+	sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
+	       &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
+	ts.tm_year -= 1900;
+	ts.tm_mon -= 1;
+	if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
+	    ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
+	    ts.tm_min > 59 || ts.tm_sec > 61)
+		ts.tm_mday = 0;
+#endif
+	if (ts.tm_mday == 0) {
+		bb_error_msg_and_die("can't parse date/time specifier: %s", str);
+	}
+	return mktime(&ts);
+}
+
+static void parse_tune2fs_options(int argc, char **argv)
+{
+	int c;
+	char * tmp;
+
+	printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+	while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:J:L:M:O:T:U:")) != EOF)
+		switch (c)
+		{
+			case 'c':
+				max_mount_count = xatou_range(optarg, 0, 16000);
+				if (max_mount_count == 0)
+					max_mount_count = -1;
+				c_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'C':
+				mount_count = xatou_range(optarg, 0, 16000);
+				C_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'e':
+				if (strcmp (optarg, "continue") == 0)
+					errors = EXT2_ERRORS_CONTINUE;
+				else if (strcmp (optarg, "remount-ro") == 0)
+					errors = EXT2_ERRORS_RO;
+				else if (strcmp (optarg, "panic") == 0)
+					errors = EXT2_ERRORS_PANIC;
+				else {
+					bb_error_msg_and_die("bad error behavior - %s", optarg);
+				}
+				e_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'f': /* Force */
+				f_flag = 1;
+				break;
+			case 'g':
+				resgid = bb_strtoul(optarg, NULL, 10);
+				if (errno)
+					resgid = xgroup2gid(optarg);
+				g_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'i':
+				interval = strtoul(optarg, &tmp, 0);
+				switch (*tmp) {
+				case 's':
+					tmp++;
+					break;
+				case '\0':
+				case 'd':
+				case 'D': /* days */
+					interval *= 86400;
+					if (*tmp != '\0')
+						tmp++;
+					break;
+				case 'm':
+				case 'M': /* months! */
+					interval *= 86400 * 30;
+					tmp++;
+					break;
+				case 'w':
+				case 'W': /* weeks */
+					interval *= 86400 * 7;
+					tmp++;
+					break;
+				}
+				if (*tmp || interval > (365 * 86400)) {
+					bb_error_msg_and_die("bad interval - %s", optarg);
+				}
+				i_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'j':
+				if (!journal_size)
+					journal_size = -1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'J':
+				parse_journal_opts(&journal_device, &journal_flags, &journal_size, optarg);
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'l':
+				l_flag = 1;
+				break;
+			case 'L':
+				new_label = optarg;
+				L_flag = 1;
+				open_flag = EXT2_FLAG_RW |
+					EXT2_FLAG_JOURNAL_DEV_OK;
+				break;
+			case 'm':
+				reserved_ratio = xatou_range(optarg, 0, 50);
+				m_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'M':
+				new_last_mounted = optarg;
+				M_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'o':
+				if (mntopts_cmd) {
+					bb_error_msg_and_die("-o may only be specified once");
+				}
+				mntopts_cmd = optarg;
+				open_flag = EXT2_FLAG_RW;
+				break;
+
+			case 'O':
+				if (features_cmd) {
+					bb_error_msg_and_die("-O may only be specified once");
+				}
+				features_cmd = optarg;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'r':
+				reserved_blocks = xatoul(optarg);
+				r_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 's':
+				s_flag = atoi(optarg);
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'T':
+				T_flag = 1;
+				last_check_time = parse_time(optarg);
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'u':
+				resuid = bb_strtoul(optarg, NULL, 10);
+				if (errno)
+					resuid = xuname2uid(optarg);
+				u_flag = 1;
+				open_flag = EXT2_FLAG_RW;
+				break;
+			case 'U':
+				new_UUID = optarg;
+				U_flag = 1;
+				open_flag = EXT2_FLAG_RW |
+					EXT2_FLAG_JOURNAL_DEV_OK;
+				break;
+			default:
+				bb_show_usage();
+		}
+	if (optind < argc - 1 || optind == argc)
+		bb_show_usage();
+	if (!open_flag && !l_flag)
+		bb_show_usage();
+	io_options = strchr(argv[optind], '?');
+	if (io_options)
+		*io_options++ = 0;
+	device_name = x_blkid_get_devname(argv[optind]);
+}
+
+static void tune2fs_clean_up(void)
+{
+	if (ENABLE_FEATURE_CLEAN_UP && device_name) free(device_name);
+	if (ENABLE_FEATURE_CLEAN_UP && journal_device) free(journal_device);
+}
+
+int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tune2fs_main(int argc, char **argv)
+{
+	errcode_t retval;
+	ext2_filsys fs;
+	struct ext2_super_block *sb;
+	io_manager io_ptr;
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		atexit(tune2fs_clean_up);
+
+	if (ENABLE_E2LABEL && (applet_name[0] == 'e')) /* e2label */
+		parse_e2label_options(argc, argv);
+	else
+		parse_tune2fs_options(argc, argv);  /* tune2fs */
+
+	io_ptr = unix_io_manager;
+	retval = ext2fs_open2(device_name, io_options, open_flag,
+			      0, 0, io_ptr, &fs);
+	if (retval)
+		bb_error_msg_and_die("No valid superblock on %s", device_name);
+	sb = fs->super;
+	if (print_label) {
+		/* For e2label emulation */
+		printf("%.*s\n", (int) sizeof(sb->s_volume_name),
+		       sb->s_volume_name);
+		return 0;
+	}
+	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
+	if (retval)
+		bb_error_msg_and_die("can't determine if %s is mounted", device_name);
+	/* Normally we only need to write out the superblock */
+	fs->flags |= EXT2_FLAG_SUPER_ONLY;
+
+	if (c_flag) {
+		sb->s_max_mnt_count = max_mount_count;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting maximal mount count to %d\n", max_mount_count);
+	}
+	if (C_flag) {
+		sb->s_mnt_count = mount_count;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting current mount count to %d\n", mount_count);
+	}
+	if (e_flag) {
+		sb->s_errors = errors;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting error behavior to %d\n", errors);
+	}
+	if (g_flag) {
+		sb->s_def_resgid = resgid;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting reserved blocks gid to %lu\n", resgid);
+	}
+	if (i_flag) {
+		sb->s_checkinterval = interval;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting interval between check %lu seconds\n", interval);
+	}
+	if (m_flag) {
+		sb->s_r_blocks_count = (sb->s_blocks_count / 100)
+				* reserved_ratio;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting reserved blocks percentage to %u (%u blocks)\n",
+			reserved_ratio, sb->s_r_blocks_count);
+	}
+	if (r_flag) {
+		if (reserved_blocks >= sb->s_blocks_count/2)
+			bb_error_msg_and_die("reserved blocks count is too big (%lu)", reserved_blocks);
+		sb->s_r_blocks_count = reserved_blocks;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting reserved blocks count to %lu\n", reserved_blocks);
+	}
+	if (s_flag == 1) {
+		if (sb->s_feature_ro_compat &
+		    EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
+			bb_error_msg("\nThe filesystem already has sparse superblocks");
+		else {
+			sb->s_feature_ro_compat |=
+				EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+			sb->s_state &= ~EXT2_VALID_FS;
+			ext2fs_mark_super_dirty(fs);
+			printf("\nSparse superblock flag set.  %s", please_fsck);
+		}
+	}
+	if (s_flag == 0) {
+		if (!(sb->s_feature_ro_compat &
+		      EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+			bb_error_msg("\nThe filesystem already has sparse superblocks disabled");
+		else {
+			sb->s_feature_ro_compat &=
+				~EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
+			sb->s_state &= ~EXT2_VALID_FS;
+			fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+			ext2fs_mark_super_dirty(fs);
+			printf("\nSparse superblock flag cleared.  %s", please_fsck);
+		}
+	}
+	if (T_flag) {
+		sb->s_lastcheck = last_check_time;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting time filesystem last checked to %s\n",
+		       ctime(&last_check_time));
+	}
+	if (u_flag) {
+		sb->s_def_resuid = resuid;
+		ext2fs_mark_super_dirty(fs);
+		printf("Setting reserved blocks uid to %lu\n", resuid);
+	}
+	if (L_flag) {
+		if (strlen(new_label) > sizeof(sb->s_volume_name))
+			bb_error_msg("Warning: label too long, truncating");
+		memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
+		safe_strncpy(sb->s_volume_name, new_label,
+			sizeof(sb->s_volume_name));
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (M_flag) {
+		memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
+		safe_strncpy(sb->s_last_mounted, new_last_mounted,
+			sizeof(sb->s_last_mounted));
+		ext2fs_mark_super_dirty(fs);
+	}
+	if (mntopts_cmd)
+		update_mntopts(fs, mntopts_cmd);
+	if (features_cmd)
+		update_feature_set(fs, features_cmd);
+	if (journal_size || journal_device)
+		add_journal(fs);
+
+	if (U_flag) {
+		if ((strcasecmp(new_UUID, "null") == 0) ||
+		    (strcasecmp(new_UUID, "clear") == 0)) {
+			uuid_clear(sb->s_uuid);
+		} else if (strcasecmp(new_UUID, "time") == 0) {
+			uuid_generate_time(sb->s_uuid);
+		} else if (strcasecmp(new_UUID, "random") == 0) {
+			uuid_generate(sb->s_uuid);
+		} else if (uuid_parse(new_UUID, sb->s_uuid)) {
+			bb_error_msg_and_die("Invalid UUID format");
+		}
+		ext2fs_mark_super_dirty(fs);
+	}
+
+	if (l_flag)
+		list_super (sb);
+	return (ext2fs_close (fs) ? 1 : 0);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/util.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/util.c
new file mode 100644
index 0000000..a61abc2
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/util.c
@@ -0,0 +1,263 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * util.c --- helper functions used by tune2fs and mke2fs
+ *
+ * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/major.h>
+#include <sys/stat.h>
+
+#include "e2fsbb.h"
+#include "e2p/e2p.h"
+#include "ext2fs/ext2_fs.h"
+#include "ext2fs/ext2fs.h"
+#include "blkid/blkid.h"
+#include "util.h"
+
+void proceed_question(void)
+{
+	fputs("Proceed anyway? (y,n) ", stdout);
+	if (bb_ask_confirmation() == 0)
+		exit(1);
+}
+
+void check_plausibility(const char *device, int force)
+{
+	int val;
+	struct stat s;
+	val = stat(device, &s);
+	if (force)
+		return;
+	if (val == -1)
+		bb_perror_msg_and_die("can't stat '%s'", device);
+	if (!S_ISBLK(s.st_mode)) {
+		printf("%s is not a block special device.\n", device);
+		proceed_question();
+		return;
+	}
+
+#ifdef HAVE_LINUX_MAJOR_H
+#ifndef MAJOR
+#define MAJOR(dev)	((dev)>>8)
+#define MINOR(dev)	((dev) & 0xff)
+#endif
+#ifndef SCSI_BLK_MAJOR
+#ifdef SCSI_DISK0_MAJOR
+#ifdef SCSI_DISK8_MAJOR
+#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
+  ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR) || \
+  ((M) >= SCSI_DISK8_MAJOR && (M) <= SCSI_DISK15_MAJOR))
+#else
+#define SCSI_DISK_MAJOR(M) ((M) == SCSI_DISK0_MAJOR || \
+  ((M) >= SCSI_DISK1_MAJOR && (M) <= SCSI_DISK7_MAJOR))
+#endif /* defined(SCSI_DISK8_MAJOR) */
+#define SCSI_BLK_MAJOR(M) (SCSI_DISK_MAJOR((M)) || (M) == SCSI_CDROM_MAJOR)
+#else
+#define SCSI_BLK_MAJOR(M)  ((M) == SCSI_DISK_MAJOR || (M) == SCSI_CDROM_MAJOR)
+#endif /* defined(SCSI_DISK0_MAJOR) */
+#endif /* defined(SCSI_BLK_MAJOR) */
+	if (((MAJOR(s.st_rdev) == HD_MAJOR &&
+	      MINOR(s.st_rdev)%64 == 0) ||
+	     (SCSI_BLK_MAJOR(MAJOR(s.st_rdev)) &&
+	      MINOR(s.st_rdev)%16 == 0))) {
+		printf("%s is entire device, not just one partition!\n", device);
+		proceed_question();
+	}
+#endif
+}
+
+void check_mount(const char *device, int force, const char *type)
+{
+	errcode_t retval;
+	int mount_flags;
+
+	retval = ext2fs_check_if_mounted(device, &mount_flags);
+	if (retval) {
+		bb_error_msg("can't determine if %s is mounted", device);
+		return;
+	}
+	if (mount_flags & EXT2_MF_MOUNTED) {
+		bb_error_msg("%s is mounted !", device);
+force_check:
+		if (force)
+			bb_error_msg("badblocks forced anyways");
+		else
+			bb_error_msg_and_die("it's not safe to run badblocks!");
+	}
+
+	if (mount_flags & EXT2_MF_BUSY) {
+		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,
+			int *journal_size, const char *opts)
+{
+	char *buf, *token, *next, *p, *arg;
+	int journal_usage = 0;
+	buf = xstrdup(opts);
+	for (token = buf; token && *token; token = next) {
+		p = strchr(token, ',');
+		next = 0;
+		if (p) {
+			*p = 0;
+			next = p+1;
+		}
+		arg = strchr(token, '=');
+		if (arg) {
+			*arg = 0;
+			arg++;
+		}
+		if (strcmp(token, "device") == 0) {
+			*journal_device = blkid_get_devname(NULL, arg, NULL);
+			if (!journal_device) {
+				journal_usage++;
+				continue;
+			}
+		} else if (strcmp(token, "size") == 0) {
+			if (!arg) {
+				journal_usage++;
+				continue;
+			}
+			(*journal_size) = strtoul(arg, &p, 0);
+			if (*p)
+				journal_usage++;
+		} else if (strcmp(token, "v1_superblock") == 0) {
+			(*journal_flags) |= EXT2_MKJOURNAL_V1_SUPER;
+			continue;
+		} else
+			journal_usage++;
+	}
+	if (journal_usage)
+		bb_error_msg_and_die(
+			"\nBad journal options specified.\n\n"
+			"Journal options are separated by commas, "
+			"and may take an argument which\n"
+			"\tis set off by an equals ('=') sign.\n\n"
+			"Valid journal options are:\n"
+			"\tsize=<journal size in megabytes>\n"
+			"\tdevice=<journal device>\n\n"
+			"The journal size must be between "
+			"1024 and 102400 filesystem blocks.\n\n");
+}
+
+/*
+ * Determine the number of journal blocks to use, either via
+ * user-specified # of megabytes, or via some intelligently selected
+ * defaults.
+ *
+ * Find a reasonable journal file size (in blocks) given the number of blocks
+ * in the filesystem.  For very small filesystems, it is not reasonable to
+ * have a journal that fills more than half of the filesystem.
+ */
+int figure_journal_size(int size, ext2_filsys fs)
+{
+	blk_t j_blocks;
+
+	if (fs->super->s_blocks_count < 2048) {
+		bb_error_msg("Filesystem too small for a journal");
+		return 0;
+	}
+
+	if (size >= 0) {
+		j_blocks = size * 1024 / (fs->blocksize	/ 1024);
+		if (j_blocks < 1024 || j_blocks > 102400)
+			bb_error_msg_and_die("\nThe requested journal "
+				"size is %d blocks;\n it must be "
+				"between 1024 and 102400 blocks; Aborting",
+				j_blocks);
+		if (j_blocks > fs->super->s_free_blocks_count)
+			bb_error_msg_and_die("Journal size too big for filesystem");
+		return j_blocks;
+	}
+
+	if (fs->super->s_blocks_count < 32768)
+		j_blocks = 1024;
+	else if (fs->super->s_blocks_count < 256*1024)
+		j_blocks = 4096;
+	else if (fs->super->s_blocks_count < 512*1024)
+		j_blocks = 8192;
+	else if (fs->super->s_blocks_count < 1024*1024)
+		j_blocks = 16384;
+	else
+		j_blocks = 32768;
+
+	return j_blocks;
+}
+
+void print_check_message(ext2_filsys fs)
+{
+	printf("This filesystem will be automatically "
+		 "checked every %d mounts or\n"
+		 "%g days, whichever comes first.  "
+		 "Use tune2fs -c or -i to override.\n",
+	       fs->super->s_max_mnt_count,
+	       (double)fs->super->s_checkinterval / (3600 * 24));
+}
+
+void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force)
+{
+	errcode_t	retval;
+	ext2_filsys	jfs;
+	io_manager	io_ptr;
+
+	check_plausibility(journal_device, force);
+	check_mount(journal_device, force, "journal");
+	io_ptr = unix_io_manager;
+	retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
+					EXT2_FLAG_JOURNAL_DEV_OK, 0,
+					fs->blocksize, io_ptr, &jfs);
+	if (retval)
+		bb_error_msg_and_die("can't journal device %s", journal_device);
+	if (!quiet)
+		printf("Adding journal to device %s: ", journal_device);
+	fflush(stdout);
+	retval = ext2fs_add_journal_device(fs, jfs);
+	if (retval)
+		bb_error_msg_and_die("\nFailed to add journal to device %s", journal_device);
+	if (!quiet)
+		puts("done");
+	ext2fs_close(jfs);
+}
+
+void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet)
+{
+	unsigned long journal_blocks;
+	errcode_t	retval;
+
+	journal_blocks = figure_journal_size(journal_size, fs);
+	if (!journal_blocks) {
+		fs->super->s_feature_compat &=
+			~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+		return;
+	}
+	if (!quiet)
+		printf("Creating journal (%ld blocks): ", journal_blocks);
+	fflush(stdout);
+	retval = ext2fs_add_journal_inode(fs, journal_blocks,
+						  journal_flags);
+	if (retval)
+		bb_error_msg_and_die("can't create journal");
+	if (!quiet)
+		puts("done");
+}
+
+char *e2fs_set_sbin_path(void)
+{
+	char *oldpath = getenv("PATH");
+	/* Update our PATH to include /sbin  */
+#define PATH_SET "/sbin"
+	if (oldpath)
+		oldpath = xasprintf("%s:%s", PATH_SET, oldpath);
+	 else
+		oldpath = PATH_SET;
+	putenv(oldpath);
+	return oldpath;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/util.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/util.h
new file mode 100644
index 0000000..80d2417
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/util.h
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * util.h --- header file defining prototypes for helper functions
+ * used by tune2fs and mke2fs
+ *
+ * Copyright 2000 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+extern void proceed_question(void);
+extern void check_plausibility(const char *device, int force);
+extern void parse_journal_opts(char **, int *, int *, const char *opts);
+extern void check_mount(const char *device, int force, const char *type);
+extern int figure_journal_size(int size, ext2_filsys fs);
+extern void print_check_message(ext2_filsys fs);
+extern void make_journal_device(char *journal_device, ext2_filsys fs, int quiet, int force);
+extern void make_journal_blocks(ext2_filsys fs, int journal_size, int journal_flags, int quiet);
+extern char *e2fs_set_sbin_path(void);
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src
new file mode 100644
index 0000000..b8c687d
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/Kbuild.src
@@ -0,0 +1,16 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+NEEDED-$(CONFIG_E2FSCK) = y
+NEEDED-$(CONFIG_FSCK) = y
+NEEDED-$(CONFIG_MKE2FS) = y
+NEEDED-$(CONFIG_TUNE2FS) = y
+
+lib-y:=
+
+INSERT
+lib-$(NEEDED-y) += compare.o gen_uuid.o pack.o parse.o unpack.o unparse.o \
+                   uuid_time.o
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/compare.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/compare.c
new file mode 100644
index 0000000..348ea7c
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/compare.c
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * compare.c --- compare whether or not two UUID's are the same
+ *
+ * Returns 0 if the two UUID's are different, and 1 if they are the same.
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include "uuidP.h"
+#include <string.h>
+
+#define UUCMP(u1,u2) if (u1 != u2) return (u1 < u2) ? -1 : 1;
+
+int uuid_compare(const uuid_t uu1, const uuid_t uu2)
+{
+	struct uuid	uuid1, uuid2;
+
+	uuid_unpack(uu1, &uuid1);
+	uuid_unpack(uu2, &uuid2);
+
+	UUCMP(uuid1.time_low, uuid2.time_low);
+	UUCMP(uuid1.time_mid, uuid2.time_mid);
+	UUCMP(uuid1.time_hi_and_version, uuid2.time_hi_and_version);
+	UUCMP(uuid1.clock_seq, uuid2.clock_seq);
+	return memcmp(uuid1.node, uuid2.node, 6);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c
new file mode 100644
index 0000000..4310c17
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/gen_uuid.c
@@ -0,0 +1,304 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * gen_uuid.c --- generate a DCE-compatible uuid
+ *
+ * Copyright (C) 1996, 1997, 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#include <sys/socket.h>
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NET_IF_DL_H
+#include <net/if_dl.h>
+#endif
+
+#include "uuidP.h"
+
+#ifdef HAVE_SRANDOM
+#define srand(x)	srandom(x)
+#define rand()		random()
+#endif
+
+static int get_random_fd(void)
+{
+	struct timeval	tv;
+	static int	fd = -2;
+	int		i;
+
+	if (fd == -2) {
+		gettimeofday(&tv, 0);
+		fd = open("/dev/urandom", O_RDONLY);
+		if (fd == -1)
+			fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+		srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+	}
+	/* Crank the random number generator a few times */
+	gettimeofday(&tv, 0);
+	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+		rand();
+	return fd;
+}
+
+
+/*
+ * Generate a series of random bytes.  Use /dev/urandom if possible,
+ * and if not, use srandom/random.
+ */
+static void get_random_bytes(void *buf, int nbytes)
+{
+	int i, n = nbytes, fd = get_random_fd();
+	int lose_counter = 0;
+	unsigned char *cp = (unsigned char *) buf;
+
+	if (fd >= 0) {
+		while (n > 0) {
+			i = read(fd, cp, n);
+			if (i <= 0) {
+				if (lose_counter++ > 16)
+					break;
+				continue;
+			}
+			n -= i;
+			cp += i;
+			lose_counter = 0;
+		}
+	}
+
+	/*
+	 * We do this all the time, but this is the only source of
+	 * randomness if /dev/random/urandom is out to lunch.
+	 */
+	for (cp = buf, i = 0; i < nbytes; i++)
+		*cp++ ^= (rand() >> 7) & 0xFF;
+}
+
+/*
+ * Get the ethernet hardware address, if we can find it...
+ */
+static int get_node_id(unsigned char *node_id)
+{
+#ifdef HAVE_NET_IF_H
+	int		sd;
+	struct ifreq	ifr, *ifrp;
+	struct ifconf	ifc;
+	char buf[1024];
+	int		n, i;
+	unsigned char	*a;
+#ifdef HAVE_NET_IF_DL_H
+	struct sockaddr_dl *sdlp;
+#endif
+
+/*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+     sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+	sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+	if (sd < 0) {
+		return -1;
+	}
+	memset(buf, 0, sizeof(buf));
+	ifc.ifc_len = sizeof(buf);
+	ifc.ifc_buf = buf;
+	if (ioctl (sd, SIOCGIFCONF, (char *)&ifc) < 0) {
+		close(sd);
+		return -1;
+	}
+	n = ifc.ifc_len;
+	for (i = 0; i < n; i+= ifreq_size(*ifrp) ) {
+		ifrp = (struct ifreq *)((char *) ifc.ifc_buf+i);
+		strncpy_IFNAMSIZ(ifr.ifr_name, ifrp->ifr_name);
+#ifdef SIOCGIFHWADDR
+		if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
+			continue;
+		a = (unsigned char *) &ifr.ifr_hwaddr.sa_data;
+#else
+#ifdef SIOCGENADDR
+		if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
+			continue;
+		a = (unsigned char *) ifr.ifr_enaddr;
+#else
+#ifdef HAVE_NET_IF_DL_H
+		sdlp = (struct sockaddr_dl *) &ifrp->ifr_addr;
+		if ((sdlp->sdl_family != AF_LINK) || (sdlp->sdl_alen != 6))
+			continue;
+		a = (unsigned char *) &sdlp->sdl_data[sdlp->sdl_nlen];
+#else
+		/*
+		 * XXX we don't have a way of getting the hardware
+		 * address
+		 */
+		close(sd);
+		return 0;
+#endif /* HAVE_NET_IF_DL_H */
+#endif /* SIOCGENADDR */
+#endif /* SIOCGIFHWADDR */
+		if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+			continue;
+		if (node_id) {
+			memcpy(node_id, a, 6);
+			close(sd);
+			return 1;
+		}
+	}
+	close(sd);
+#endif
+	return 0;
+}
+
+/* Assume that the gettimeofday() has microsecond granularity */
+#define MAX_ADJUSTMENT 10
+
+static int get_clock(uint32_t *clock_high, uint32_t *clock_low, uint16_t *ret_clock_seq)
+{
+	static int			adjustment = 0;
+	static struct timeval		last = {0, 0};
+	static uint16_t			clock_seq;
+	struct timeval			tv;
+	unsigned long long		clock_reg;
+
+try_again:
+	gettimeofday(&tv, 0);
+	if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
+		get_random_bytes(&clock_seq, sizeof(clock_seq));
+		clock_seq &= 0x3FFF;
+		last = tv;
+		last.tv_sec--;
+	}
+	if ((tv.tv_sec < last.tv_sec) ||
+	    ((tv.tv_sec == last.tv_sec) &&
+	     (tv.tv_usec < last.tv_usec))) {
+		clock_seq = (clock_seq+1) & 0x3FFF;
+		adjustment = 0;
+		last = tv;
+	} else if ((tv.tv_sec == last.tv_sec) &&
+	    (tv.tv_usec == last.tv_usec)) {
+		if (adjustment >= MAX_ADJUSTMENT)
+			goto try_again;
+		adjustment++;
+	} else {
+		adjustment = 0;
+		last = tv;
+	}
+
+	clock_reg = tv.tv_usec*10 + adjustment;
+	clock_reg += ((unsigned long long) tv.tv_sec)*10000000;
+	clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
+
+	*clock_high = clock_reg >> 32;
+	*clock_low = clock_reg;
+	*ret_clock_seq = clock_seq;
+	return 0;
+}
+
+void uuid_generate_time(uuid_t out)
+{
+	static unsigned char node_id[6];
+	static int has_init = 0;
+	struct uuid uu;
+	uint32_t	clock_mid;
+
+	if (!has_init) {
+		if (get_node_id(node_id) <= 0) {
+			get_random_bytes(node_id, 6);
+			/*
+			 * Set multicast bit, to prevent conflicts
+			 * with IEEE 802 addresses obtained from
+			 * network cards
+			 */
+			node_id[0] |= 0x01;
+		}
+		has_init = 1;
+	}
+	get_clock(&clock_mid, &uu.time_low, &uu.clock_seq);
+	uu.clock_seq |= 0x8000;
+	uu.time_mid = (uint16_t) clock_mid;
+	uu.time_hi_and_version = ((clock_mid >> 16) & 0x0FFF) | 0x1000;
+	memcpy(uu.node, node_id, 6);
+	uuid_pack(&uu, out);
+}
+
+void uuid_generate_random(uuid_t out)
+{
+	uuid_t	buf;
+	struct uuid uu;
+
+	get_random_bytes(buf, sizeof(buf));
+	uuid_unpack(buf, &uu);
+
+	uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
+	uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
+	uuid_pack(&uu, out);
+}
+
+/*
+ * This is the generic front-end to uuid_generate_random and
+ * uuid_generate_time.  It uses uuid_generate_random only if
+ * /dev/urandom is available, since otherwise we won't have
+ * high-quality randomness.
+ */
+void uuid_generate(uuid_t out)
+{
+	if (get_random_fd() >= 0)
+		uuid_generate_random(out);
+	else
+		uuid_generate_time(out);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/pack.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/pack.c
new file mode 100644
index 0000000..217cfce
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/pack.c
@@ -0,0 +1,69 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Internal routine for packing UUID's
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_pack(const struct uuid *uu, uuid_t ptr)
+{
+	uint32_t tmp;
+	unsigned char *out = ptr;
+
+	tmp = uu->time_low;
+	out[3] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[2] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[1] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[0] = (unsigned char) tmp;
+
+	tmp = uu->time_mid;
+	out[5] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[4] = (unsigned char) tmp;
+
+	tmp = uu->time_hi_and_version;
+	out[7] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[6] = (unsigned char) tmp;
+
+	tmp = uu->clock_seq;
+	out[9] = (unsigned char) tmp;
+	tmp >>= 8;
+	out[8] = (unsigned char) tmp;
+
+	memcpy(out+10, uu->node, 6);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/parse.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/parse.c
new file mode 100644
index 0000000..9a3f9cb
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/parse.c
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse.c --- UUID parsing
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "uuidP.h"
+
+int uuid_parse(const char *in, uuid_t uu)
+{
+	struct uuid	uuid;
+	int		i;
+	const char	*cp;
+	char		buf[3];
+
+	if (strlen(in) != 36)
+		return -1;
+	for (i=0, cp = in; i <= 36; i++,cp++) {
+		if ((i == 8) || (i == 13) || (i == 18) ||
+		    (i == 23)) {
+			if (*cp == '-')
+				continue;
+			else
+				return -1;
+		}
+		if (i== 36)
+			if (*cp == 0)
+				continue;
+		if (!isxdigit(*cp))
+			return -1;
+	}
+	uuid.time_low = strtoul(in, NULL, 16);
+	uuid.time_mid = strtoul(in+9, NULL, 16);
+	uuid.time_hi_and_version = strtoul(in+14, NULL, 16);
+	uuid.clock_seq = strtoul(in+19, NULL, 16);
+	cp = in+24;
+	buf[2] = 0;
+	for (i=0; i < 6; i++) {
+		buf[0] = *cp++;
+		buf[1] = *cp++;
+		uuid.node[i] = strtoul(buf, NULL, 16);
+	}
+
+	uuid_pack(&uuid, uu);
+	return 0;
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/unpack.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/unpack.c
new file mode 100644
index 0000000..95d3aab
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/unpack.c
@@ -0,0 +1,63 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Internal routine for unpacking UUID
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <string.h>
+#include "uuidP.h"
+
+void uuid_unpack(const uuid_t in, struct uuid *uu)
+{
+	const uint8_t *ptr = in;
+	uint32_t tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_low = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_mid = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->time_hi_and_version = tmp;
+
+	tmp = *ptr++;
+	tmp = (tmp << 8) | *ptr++;
+	uu->clock_seq = tmp;
+
+	memcpy(uu->node, ptr, 6);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/unparse.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/unparse.c
new file mode 100644
index 0000000..d2948fe
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/unparse.c
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * unparse.c -- convert a UUID to string
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "uuidP.h"
+
+static const char *fmt_lower =
+	"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x";
+
+static const char *fmt_upper =
+	"%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X";
+
+#ifdef UUID_UNPARSE_DEFAULT_UPPER
+#define FMT_DEFAULT fmt_upper
+#else
+#define FMT_DEFAULT fmt_lower
+#endif
+
+static void uuid_unparse_x(const uuid_t uu, char *out, const char *fmt)
+{
+	struct uuid uuid;
+
+	uuid_unpack(uu, &uuid);
+	sprintf(out, fmt,
+		uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
+		uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
+		uuid.node[0], uuid.node[1], uuid.node[2],
+		uuid.node[3], uuid.node[4], uuid.node[5]);
+}
+
+void uuid_unparse_lower(const uuid_t uu, char *out)
+{
+	uuid_unparse_x(uu, out,	fmt_lower);
+}
+
+void uuid_unparse_upper(const uuid_t uu, char *out)
+{
+	uuid_unparse_x(uu, out,	fmt_upper);
+}
+
+void uuid_unparse(const uuid_t uu, char *out)
+{
+	uuid_unparse_x(uu, out, FMT_DEFAULT);
+}
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuid.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuid.h
new file mode 100644
index 0000000..7a97064
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuid.h
@@ -0,0 +1,103 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Public include file for the UUID library
+ *
+ * Copyright (C) 1996, 1997, 1998 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+#ifndef UUID_UUID_H
+#define UUID_UUID_H 1
+
+#include <sys/types.h>
+#include <time.h>
+
+typedef unsigned char uuid_t[16];
+
+/* UUID Variant definitions */
+#define UUID_VARIANT_NCS	0
+#define UUID_VARIANT_DCE	1
+#define UUID_VARIANT_MICROSOFT	2
+#define UUID_VARIANT_OTHER	3
+
+/* UUID Type definitions */
+#define UUID_TYPE_DCE_TIME   1
+#define UUID_TYPE_DCE_RANDOM 4
+
+/* Allow UUID constants to be defined */
+#ifdef __GNUC__
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+	static const uuid_t name UNUSED_PARAM = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#else
+#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \
+	static const uuid_t name = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* clear.c */
+/*void uuid_clear(uuid_t uu);*/
+#define uuid_clear(uu) memset(uu, 0, sizeof(uu))
+
+/* compare.c */
+int uuid_compare(const uuid_t uu1, const uuid_t uu2);
+
+/* copy.c */
+/*void uuid_copy(uuid_t dst, const uuid_t src);*/
+#define uuid_copy(dst,src) memcpy(dst, src, sizeof(dst))
+
+/* gen_uuid.c */
+void uuid_generate(uuid_t out);
+void uuid_generate_random(uuid_t out);
+void uuid_generate_time(uuid_t out);
+
+/* isnull.c */
+/*int uuid_is_null(const uuid_t uu);*/
+#define uuid_is_null(uu) (!memcmp(uu, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(uu)))
+
+/* parse.c */
+int uuid_parse(const char *in, uuid_t uu);
+
+/* unparse.c */
+void uuid_unparse(const uuid_t uu, char *out);
+void uuid_unparse_lower(const uuid_t uu, char *out);
+void uuid_unparse_upper(const uuid_t uu, char *out);
+
+/* uuid_time.c */
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
+int uuid_type(const uuid_t uu);
+int uuid_variant(const uuid_t uu);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UUID_UUID_H */
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuidP.h b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuidP.h
new file mode 100644
index 0000000..87041ef
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuidP.h
@@ -0,0 +1,60 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uuid.h -- private header file for uuids
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include "uuid.h"
+
+/*
+ * Offset between 15-Oct-1582 and 1-Jan-70
+ */
+#define TIME_OFFSET_HIGH 0x01B21DD2
+#define TIME_OFFSET_LOW  0x13814000
+
+struct uuid {
+	uint32_t	time_low;
+	uint16_t	time_mid;
+	uint16_t	time_hi_and_version;
+	uint16_t	clock_seq;
+	uint8_t	node[6];
+};
+
+
+/*
+ * prototypes
+ */
+void uuid_pack(const struct uuid *uu, uuid_t ptr);
+void uuid_unpack(const uuid_t in, struct uuid *uu);
diff --git a/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c
new file mode 100644
index 0000000..b6f73e6
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/old_e2fsprogs/uuid/uuid_time.c
@@ -0,0 +1,161 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * uuid_time.c --- Interpret the time field from a uuid.  This program
+ *	violates the UUID abstraction barrier by reaching into the guts
+ *	of a UUID and interpreting it.
+ *
+ * Copyright (C) 1998, 1999 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * 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, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "uuidP.h"
+
+time_t uuid_time(const uuid_t uu, struct timeval *ret_tv)
+{
+	struct uuid		uuid;
+	uint32_t			high;
+	struct timeval		tv;
+	unsigned long long	clock_reg;
+
+	uuid_unpack(uu, &uuid);
+
+	high = uuid.time_mid | ((uuid.time_hi_and_version & 0xFFF) << 16);
+	clock_reg = uuid.time_low | ((unsigned long long) high << 32);
+
+	clock_reg -= (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;
+	tv.tv_sec = clock_reg / 10000000;
+	tv.tv_usec = (clock_reg % 10000000) / 10;
+
+	if (ret_tv)
+		*ret_tv = tv;
+
+	return tv.tv_sec;
+}
+
+int uuid_type(const uuid_t uu)
+{
+	struct uuid		uuid;
+
+	uuid_unpack(uu, &uuid);
+	return ((uuid.time_hi_and_version >> 12) & 0xF);
+}
+
+int uuid_variant(const uuid_t uu)
+{
+	struct uuid		uuid;
+	int			var;
+
+	uuid_unpack(uu, &uuid);
+	var = uuid.clock_seq;
+
+	if ((var & 0x8000) == 0)
+		return UUID_VARIANT_NCS;
+	if ((var & 0x4000) == 0)
+		return UUID_VARIANT_DCE;
+	if ((var & 0x2000) == 0)
+		return UUID_VARIANT_MICROSOFT;
+	return UUID_VARIANT_OTHER;
+}
+
+#ifdef DEBUG
+static const char *variant_string(int variant)
+{
+	switch (variant) {
+	case UUID_VARIANT_NCS:
+		return "NCS";
+	case UUID_VARIANT_DCE:
+		return "DCE";
+	case UUID_VARIANT_MICROSOFT:
+		return "Microsoft";
+	default:
+		return "Other";
+	}
+}
+
+
+int
+main(int argc, char **argv)
+{
+	uuid_t		buf;
+	time_t		time_reg;
+	struct timeval	tv;
+	int		type, variant;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s uuid\n", argv[0]);
+		exit(1);
+	}
+	if (uuid_parse(argv[1], buf)) {
+		fprintf(stderr, "Invalid UUID: %s\n", argv[1]);
+		exit(1);
+	}
+	variant = uuid_variant(buf);
+	type = uuid_type(buf);
+	time_reg = uuid_time(buf, &tv);
+
+	printf("UUID variant is %d (%s)\n", variant, variant_string(variant));
+	if (variant != UUID_VARIANT_DCE) {
+		printf("Warning: This program only knows how to interpret "
+		       "DCE UUIDs.\n\tThe rest of the output is likely "
+		       "to be incorrect!!\n");
+	}
+	printf("UUID type is %d", type);
+	switch (type) {
+	case 1:
+		printf(" (time based)\n");
+		break;
+	case 2:
+		printf(" (DCE)\n");
+		break;
+	case 3:
+		printf(" (name-based)\n");
+		break;
+	case 4:
+		printf(" (random)\n");
+		break;
+	default:
+		bb_putchar('\n');
+	}
+	if (type != 1) {
+		printf("Warning: not a time-based UUID, so UUID time "
+		       "decoding will likely not work!\n");
+	}
+	printf("UUID time is: (%ld, %ld): %s\n", tv.tv_sec, tv.tv_usec,
+	       ctime(&time_reg));
+
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/e2fsprogs/tune2fs.c b/busybox-1.19.3/e2fsprogs/tune2fs.c
new file mode 100644
index 0000000..9daec54
--- /dev/null
+++ b/busybox-1.19.3/e2fsprogs/tune2fs.c
@@ -0,0 +1,100 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tune2fs: utility to modify EXT2 filesystem
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include <linux/fs.h>
+#include <linux/ext2_fs.h>
+
+// storage helpers
+char BUG_wrong_field_size(void);
+#define STORE_LE(field, value) \
+do { \
+	if (sizeof(field) == 4) \
+		field = SWAP_LE32(value); \
+	else if (sizeof(field) == 2) \
+		field = SWAP_LE16(value); \
+	else if (sizeof(field) == 1) \
+		field = (value); \
+	else \
+		BUG_wrong_field_size(); \
+} while (0)
+
+#define FETCH_LE32(field) \
+	(sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
+
+//usage:#define tune2fs_trivial_usage
+//usage:       "[-c MOUNT_CNT] "
+////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] [-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_c = 1 << 1, // max mount count
+	OPT_i = 1 << 2, // check interval
+};
+
+int tune2fs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tune2fs_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opts;
+	const char *label, *str_c, *str_i;
+	struct ext2_super_block *sb;
+	int fd;
+
+	opt_complementary = "=1";
+	opts = getopt32(argv, "L:c:i:", &label, &str_c, &str_i);
+	if (!opts)
+		bb_show_usage();
+	argv += optind; // argv[0] -- device
+
+	// read superblock
+	fd = xopen(argv[0], O_RDWR);
+	xlseek(fd, 1024, SEEK_SET);
+	sb = xzalloc(1024);
+	xread(fd, sb, 1024);
+
+	// mangle superblock
+	//STORE_LE(sb->s_wtime, time(NULL)); - why bother?
+
+	// set the label
+	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);
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(sb);
+	}
+
+	xclose(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/editors/Config.src b/busybox-1.19.3/editors/Config.src
new file mode 100644
index 0000000..af1e1de
--- /dev/null
+++ b/busybox-1.19.3/editors/Config.src
@@ -0,0 +1,78 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+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 SED
+	bool "sed"
+	default y
+	help
+	  sed is used to perform text transformations on a file
+	  or input from a pipeline.
+
+config FEATURE_ALLOW_EXEC
+	bool "Allow vi and awk to execute shell commands"
+	default y
+	depends on VI || AWK
+	help
+	  Enables vi and awk features which allows user to execute
+	  shell commands (using system() C call).
+
+endmenu
diff --git a/busybox-1.19.3/editors/Kbuild.src b/busybox-1.19.3/editors/Kbuild.src
new file mode 100644
index 0000000..8888cba
--- /dev/null
+++ b/busybox-1.19.3/editors/Kbuild.src
@@ -0,0 +1,14 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# 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_SED)       += sed.o
diff --git a/busybox-1.19.3/editors/awk.c b/busybox-1.19.3/editors/awk.c
new file mode 100644
index 0000000..7685546
--- /dev/null
+++ b/busybox-1.19.3/editors/awk.c
@@ -0,0 +1,3134 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * awk implementation for busybox
+ *
+ * Copyright (C) 2002 by Dmitry Zakharov <dmit@crp.bank.gov.ua>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//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"
+
+#include "libbb.h"
+#include "xregex.h"
+#include <math.h>
+
+/* This is a NOEXEC applet. Be very careful! */
+
+
+/* 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)
+
+#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
+
+
+
+#define	MAXVARFMT       240
+#define	MINNVBLOCK      64
+
+/* variable flags */
+#define	VF_NUMBER       0x0001	/* 1 = primary type is number */
+#define	VF_ARRAY        0x0002	/* 1 = it's an array */
+
+#define	VF_CACHED       0x0100	/* 1 = num/str value has cached str/num eq */
+#define	VF_USER         0x0200	/* 1 = user input (may be numeric string) */
+#define	VF_SPECIAL      0x0400	/* 1 = requires extra handling when changed */
+#define	VF_WALK         0x0800	/* 1 = variable has alloc'd x.walker list */
+#define	VF_FSTR         0x1000	/* 1 = var::string points to fstring buffer */
+#define	VF_CHILD        0x2000	/* 1 = function arg; x.parent points to source */
+#define	VF_DIRTY        0x4000	/* 1 = variable was set explicitly */
+
+/* these flags are static, don't change them when value is changed */
+#define	VF_DONTTOUCH    (VF_ARRAY | VF_SPECIAL | VF_WALK | VF_CHILD | VF_DIRTY)
+
+typedef struct walker_list {
+	char *end;
+	char *cur;
+	struct walker_list *prev;
+	char wbuf[1];
+} walker_list;
+
+/* Variable */
+typedef struct var_s {
+	unsigned type;            /* flags */
+	double number;
+	char *string;
+	union {
+		int aidx;               /* func arg idx (for compilation stage) */
+		struct xhash_s *array;  /* array ptr */
+		struct var_s *parent;   /* for func args, ptr to actual parameter */
+		walker_list *walker;    /* list of array elements (for..in) */
+	} x;
+} var;
+
+/* Node chain (pattern-action chain, BEGIN, END, function bodies) */
+typedef struct chain_s {
+	struct node_s *first;
+	struct node_s *last;
+	const char *programname;
+} chain;
+
+/* Function */
+typedef struct func_s {
+	unsigned nargs;
+	struct chain_s body;
+} func;
+
+/* I/O stream */
+typedef struct rstream_s {
+	FILE *F;
+	char *buffer;
+	int adv;
+	int size;
+	int pos;
+	smallint is_pipe;
+} rstream;
+
+typedef struct hash_item_s {
+	union {
+		struct var_s v;         /* variable/array hash */
+		struct rstream_s rs;    /* redirect streams hash */
+		struct func_s f;        /* functions hash */
+	} data;
+	struct hash_item_s *next;       /* next in chain */
+	char name[1];                   /* really it's longer */
+} hash_item;
+
+typedef struct xhash_s {
+	unsigned nel;           /* num of elements */
+	unsigned csize;         /* current hash size */
+	unsigned nprime;        /* next hash size in PRIMES[] */
+	unsigned glen;          /* summary length of item names */
+	struct hash_item_s **items;
+} xhash;
+
+/* Tree node */
+typedef struct node_s {
+	uint32_t info;
+	unsigned lineno;
+	union {
+		struct node_s *n;
+		var *v;
+		int aidx;
+		char *new_progname;
+		regex_t *re;
+	} l;
+	union {
+		struct node_s *n;
+		regex_t *ire;
+		func *f;
+	} r;
+	union {
+		struct node_s *n;
+	} a;
+} node;
+
+/* Block of temporary variables */
+typedef struct nvblock_s {
+	int size;
+	var *pos;
+	struct nvblock_s *prev;
+	struct nvblock_s *next;
+	var nv[];
+} nvblock;
+
+typedef struct tsplitter_s {
+	node n;
+	regex_t re[2];
+} tsplitter;
+
+/* simple token classes */
+/* Order and hex values are very important!!!  See next_token() */
+#define	TC_SEQSTART	 1				/* ( */
+#define	TC_SEQTERM	(1 << 1)		/* ) */
+#define	TC_REGEXP	(1 << 2)		/* /.../ */
+#define	TC_OUTRDR	(1 << 3)		/* | > >> */
+#define	TC_UOPPOST	(1 << 4)		/* unary postfix operator */
+#define	TC_UOPPRE1	(1 << 5)		/* unary prefix operator */
+#define	TC_BINOPX	(1 << 6)		/* two-opnd operator */
+#define	TC_IN		(1 << 7)
+#define	TC_COMMA	(1 << 8)
+#define	TC_PIPE		(1 << 9)		/* input redirection pipe */
+#define	TC_UOPPRE2	(1 << 10)		/* unary prefix operator */
+#define	TC_ARRTERM	(1 << 11)		/* ] */
+#define	TC_GRPSTART	(1 << 12)		/* { */
+#define	TC_GRPTERM	(1 << 13)		/* } */
+#define	TC_SEMICOL	(1 << 14)
+#define	TC_NEWLINE	(1 << 15)
+#define	TC_STATX	(1 << 16)		/* ctl statement (for, next...) */
+#define	TC_WHILE	(1 << 17)
+#define	TC_ELSE		(1 << 18)
+#define	TC_BUILTIN	(1 << 19)
+#define	TC_GETLINE	(1 << 20)
+#define	TC_FUNCDECL	(1 << 21)		/* `function' `func' */
+#define	TC_BEGIN	(1 << 22)
+#define	TC_END		(1 << 23)
+#define	TC_EOF		(1 << 24)
+#define	TC_VARIABLE	(1 << 25)
+#define	TC_ARRAY	(1 << 26)
+#define	TC_FUNCTION	(1 << 27)
+#define	TC_STRING	(1 << 28)
+#define	TC_NUMBER	(1 << 29)
+
+#define	TC_UOPPRE  (TC_UOPPRE1 | TC_UOPPRE2)
+
+/* combined token classes */
+#define	TC_BINOP   (TC_BINOPX | TC_COMMA | TC_PIPE | TC_IN)
+#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)
+
+#define	TC_STATEMNT (TC_STATX | TC_WHILE)
+#define	TC_OPTERM  (TC_SEMICOL | TC_NEWLINE)
+
+/* word tokens, cannot mean something else if not expected */
+#define	TC_WORD    (TC_IN | TC_STATEMNT | TC_ELSE | TC_BUILTIN \
+                   | TC_GETLINE | TC_FUNCDECL | TC_BEGIN | TC_END)
+
+/* discard newlines after these */
+#define	TC_NOTERM  (TC_COMMA | TC_GRPSTART | TC_GRPTERM \
+                   | TC_BINOP | TC_OPTERM)
+
+/* what can expression begin with */
+#define	TC_OPSEQ   (TC_OPERAND | TC_UOPPRE | TC_REGEXP)
+/* what can group begin with */
+#define	TC_GRPSEQ  (TC_OPSEQ | TC_OPTERM | TC_STATEMNT | TC_GRPSTART)
+
+/* if previous token class is CONCAT1 and next is CONCAT2, concatenation */
+/* operator is inserted between them */
+#define	TC_CONCAT1 (TC_VARIABLE | TC_ARRTERM | TC_SEQTERM \
+                   | TC_STRING | TC_NUMBER | TC_UOPPOST)
+#define	TC_CONCAT2 (TC_OPERAND | TC_UOPPRE)
+
+#define	OF_RES1    0x010000
+#define	OF_RES2    0x020000
+#define	OF_STR1    0x040000
+#define	OF_STR2    0x080000
+#define	OF_NUM1    0x100000
+#define	OF_CHECKED 0x200000
+
+/* combined operator flags */
+#define	xx	0
+#define	xV	OF_RES2
+#define	xS	(OF_RES2 | OF_STR2)
+#define	Vx	OF_RES1
+#define	VV	(OF_RES1 | OF_RES2)
+#define	Nx	(OF_RES1 | OF_NUM1)
+#define	NV	(OF_RES1 | OF_NUM1 | OF_RES2)
+#define	Sx	(OF_RES1 | OF_STR1)
+#define	SV	(OF_RES1 | OF_STR1 | OF_RES2)
+#define	SS	(OF_RES1 | OF_STR1 | OF_RES2 | OF_STR2)
+
+#define	OPCLSMASK 0xFF00
+#define	OPNMASK   0x007F
+
+/* operator priority is a highest byte (even: r->l, odd: l->r grouping)
+ * 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
+ */
+#define P(x)      (x << 24)
+#define PRIMASK   0x7F000000
+#define PRIMASK2  0x7E000000
+
+/* Operation classes */
+
+#define	SHIFT_TIL_THIS	0x0600
+#define	RECUR_FROM_THIS	0x1000
+
+enum {
+	OC_DELETE = 0x0100,     OC_EXEC = 0x0200,       OC_NEWSOURCE = 0x0300,
+	OC_PRINT = 0x0400,      OC_PRINTF = 0x0500,     OC_WALKINIT = 0x0600,
+
+	OC_BR = 0x0700,         OC_BREAK = 0x0800,      OC_CONTINUE = 0x0900,
+	OC_EXIT = 0x0a00,       OC_NEXT = 0x0b00,       OC_NEXTFILE = 0x0c00,
+	OC_TEST = 0x0d00,       OC_WALKNEXT = 0x0e00,
+
+	OC_BINARY = 0x1000,     OC_BUILTIN = 0x1100,    OC_COLON = 0x1200,
+	OC_COMMA = 0x1300,      OC_COMPARE = 0x1400,    OC_CONCAT = 0x1500,
+	OC_FBLTIN = 0x1600,     OC_FIELD = 0x1700,      OC_FNARG = 0x1800,
+	OC_FUNC = 0x1900,       OC_GETLINE = 0x1a00,    OC_IN = 0x1b00,
+	OC_LAND = 0x1c00,       OC_LOR = 0x1d00,        OC_MATCH = 0x1e00,
+	OC_MOVE = 0x1f00,       OC_PGETLINE = 0x2000,   OC_REGEXP = 0x2100,
+	OC_REPLACE = 0x2200,    OC_RETURN = 0x2300,     OC_SPRINTF = 0x2400,
+	OC_TERNARY = 0x2500,    OC_UNARY = 0x2600,      OC_VAR = 0x2700,
+	OC_DONE = 0x2800,
+
+	ST_IF = 0x3000,         ST_DO = 0x3100,         ST_FOR = 0x3200,
+	ST_WHILE = 0x3300
+};
+
+/* simple builtins */
+enum {
+	F_in,	F_rn,	F_co,	F_ex,	F_lg,	F_si,	F_sq,	F_sr,
+	F_ti,	F_le,	F_sy,	F_ff,	F_cl
+};
+
+/* builtins */
+enum {
+	B_a2,	B_ix,	B_ma,	B_sp,	B_ss,	B_ti,   B_mt,	B_lo,	B_up,
+	B_ge,	B_gs,	B_su,
+	B_an,	B_co,	B_ls,	B_or,	B_rs,	B_xo,
+};
+
+/* tokens and their corresponding info values */
+
+#define NTC     "\377"  /* switch to next token class (tc<<1) */
+#define NTCC    '\377'
+
+#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"
+	/* 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), /* 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, /* ] */
+	0,
+	0,
+	0,
+	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, /* 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),
+	OC_FBLTIN|Sx|F_cl, OC_FBLTIN|Sx|F_sy, OC_FBLTIN|Sx|F_ff, OC_B|B_a2|P(0x83),
+	OC_FBLTIN|Nx|F_co, OC_FBLTIN|Nx|F_ex, OC_FBLTIN|Nx|F_in, OC_FBLTIN|Nx|F_lg,
+	OC_FBLTIN|F_rn,    OC_FBLTIN|Nx|F_si, OC_FBLTIN|Nx|F_sq, OC_FBLTIN|Nx|F_sr,
+	OC_B|B_ge|P(0xd6), OC_B|B_gs|P(0xb6), OC_B|B_ix|P(0x9b), OC_FBLTIN|Sx|F_le,
+	OC_B|B_ma|P(0x89), OC_B|B_sp|P(0x8b), OC_SPRINTF,        OC_B|B_su|P(0xb6),
+	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 /* END */
+};
+
+/* internal variable names and their initial values       */
+/* asterisk marks SPECIAL vars; $ is just no-named Field0 */
+enum {
+	CONVFMT,    OFMT,       FS,         OFS,
+	ORS,        RS,         RT,         FILENAME,
+	SUBSEP,     F0,         ARGIND,     ARGC,
+	ARGV,       ERRNO,      FNR,        NR,
+	NF,         IGNORECASE, ENVIRON,    NUM_INTERNAL_VARS
+};
+
+static const char vNames[] ALIGN1 =
+	"CONVFMT\0" "OFMT\0"    "FS\0*"     "OFS\0"
+	"ORS\0"     "RS\0*"     "RT\0"      "FILENAME\0"
+	"SUBSEP\0"  "$\0*"      "ARGIND\0"  "ARGC\0"
+	"ARGV\0"    "ERRNO\0"   "FNR\0"     "NR\0"
+	"NF\0*"     "IGNORECASE\0*" "ENVIRON\0" "\0";
+
+static const char vValues[] ALIGN1 =
+	"%.6g\0"    "%.6g\0"    " \0"       " \0"
+	"\n\0"      "\n\0"      "\0"        "\0"
+	"\034\0"    "\0"        "\377";
+
+/* hash size may grow to these values */
+#define FIRST_PRIME 61
+static const uint16_t PRIMES[] ALIGN2 = { 251, 1021, 4093, 16381, 65521 };
+
+
+/* Globals. Split in two parts so that first one is addressed
+ * with (mostly short) negative offsets.
+ * NB: it's unsafe to put members of type "double"
+ * into globals2 (gcc may fail to align them).
+ */
+struct globals {
+	double t_double;
+	chain beginseq, mainseq, endseq;
+	chain *seq;
+	node *break_ptr, *continue_ptr;
+	rstream *iF;
+	xhash *vhash, *ahash, *fdhash, *fnhash;
+	const char *g_progname;
+	int g_lineno;
+	int nfields;
+	int maxfields; /* used in fsrealloc() only */
+	var *Fields;
+	nvblock *g_cb;
+	char *g_pos;
+	char *g_buf;
+	smallint icase;
+	smallint exiting;
+	smallint nextrec;
+	smallint nextfile;
+	smallint is_f0_split;
+};
+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 */
+
+	/* former statics from various functions */
+	char *split_f0__fstrings;
+
+	uint32_t next_token__save_tclass;
+	uint32_t next_token__save_info;
+	uint32_t next_token__ltclass;
+	smallint next_token__concat_inserted;
+
+	smallint next_input_file__files_happen;
+	rstream next_input_file__rsm;
+
+	var *evaluate__fnargs;
+	unsigned evaluate__seed;
+	regex_t evaluate__sreg;
+
+	var ptest__v;
+
+	tsplitter exec_builtin__tspl;
+
+	/* biggest and least used members go last */
+	tsplitter fsplitter, rsplitter;
+};
+#define G1 (ptr_to_globals[-1])
+#define G (*(struct globals2 *)ptr_to_globals)
+/* For debug. nm --size-sort awk.o | grep -vi ' [tr] ' */
+/*char G1size[sizeof(G1)]; - 0x74 */
+/*char Gsize[sizeof(G)]; - 0x1c4 */
+/* Trying to keep most of members accessible with short offsets: */
+/*char Gofs_seed[offsetof(struct globals2, evaluate__seed)]; - 0x90 */
+#define t_double     (G1.t_double    )
+#define beginseq     (G1.beginseq    )
+#define mainseq      (G1.mainseq     )
+#define endseq       (G1.endseq      )
+#define seq          (G1.seq         )
+#define break_ptr    (G1.break_ptr   )
+#define continue_ptr (G1.continue_ptr)
+#define iF           (G1.iF          )
+#define vhash        (G1.vhash       )
+#define ahash        (G1.ahash       )
+#define fdhash       (G1.fdhash      )
+#define fnhash       (G1.fnhash      )
+#define g_progname   (G1.g_progname  )
+#define g_lineno     (G1.g_lineno    )
+#define nfields      (G1.nfields     )
+#define maxfields    (G1.maxfields   )
+#define Fields       (G1.Fields      )
+#define g_cb         (G1.g_cb        )
+#define g_pos        (G1.g_pos       )
+#define g_buf        (G1.g_buf       )
+#define icase        (G1.icase       )
+#define exiting      (G1.exiting     )
+#define nextrec      (G1.nextrec     )
+#define nextfile     (G1.nextfile    )
+#define is_f0_split  (G1.is_f0_split )
+#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   )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS((char*)xzalloc(sizeof(G1)+sizeof(G)) + sizeof(G1)); \
+	G.next_token__ltclass = TC_OPTERM; \
+	G.evaluate__seed = 1; \
+} while (0)
+
+
+/* function prototypes */
+static void handle_special(var *);
+static node *parse_expr(uint32_t);
+static void chain_group(void);
+static var *evaluate(node *, var *);
+static rstream *next_input_file(void);
+static int fmt_num(char *, int, const char *, double, int);
+static int awk_exit(int) NORETURN;
+
+/* ---- error handling ---- */
+
+static const char EMSG_INTERNAL_ERROR[] ALIGN1 = "Internal error";
+static const char EMSG_UNEXP_EOS[] ALIGN1 = "Unexpected end of string";
+static const char EMSG_UNEXP_TOKEN[] ALIGN1 = "Unexpected token";
+static const char EMSG_DIV_BY_ZERO[] ALIGN1 = "Division by zero";
+static const char EMSG_INV_FMT[] ALIGN1 = "Invalid format specifier";
+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";
+static const char EMSG_NO_MATH[] ALIGN1 = "Math support is not compiled in";
+
+static void zero_out_var(var *vp)
+{
+	memset(vp, 0, sizeof(*vp));
+}
+
+static void syntax_error(const char *message) NORETURN;
+static void syntax_error(const char *message)
+{
+	bb_error_msg_and_die("%s:%i: %s", g_progname, g_lineno, message);
+}
+
+/* ---- hash stuff ---- */
+
+static unsigned hashidx(const char *name)
+{
+	unsigned idx = 0;
+
+	while (*name)
+		idx = *name++ + (idx << 6) - idx;
+	return idx;
+}
+
+/* create new hash */
+static xhash *hash_init(void)
+{
+	xhash *newhash;
+
+	newhash = xzalloc(sizeof(*newhash));
+	newhash->csize = FIRST_PRIME;
+	newhash->items = xzalloc(FIRST_PRIME * sizeof(newhash->items[0]));
+
+	return newhash;
+}
+
+/* find item in hash, return ptr to data, NULL if not found */
+static void *hash_search(xhash *hash, const char *name)
+{
+	hash_item *hi;
+
+	hi = hash->items[hashidx(name) % hash->csize];
+	while (hi) {
+		if (strcmp(hi->name, name) == 0)
+			return &hi->data;
+		hi = hi->next;
+	}
+	return NULL;
+}
+
+/* grow hash if it becomes too big */
+static void hash_rebuild(xhash *hash)
+{
+	unsigned newsize, i, idx;
+	hash_item **newitems, *hi, *thi;
+
+	if (hash->nprime == ARRAY_SIZE(PRIMES))
+		return;
+
+	newsize = PRIMES[hash->nprime++];
+	newitems = xzalloc(newsize * sizeof(newitems[0]));
+
+	for (i = 0; i < hash->csize; i++) {
+		hi = hash->items[i];
+		while (hi) {
+			thi = hi;
+			hi = thi->next;
+			idx = hashidx(thi->name) % newsize;
+			thi->next = newitems[idx];
+			newitems[idx] = thi;
+		}
+	}
+
+	free(hash->items);
+	hash->csize = newsize;
+	hash->items = newitems;
+}
+
+/* find item in hash, add it if necessary. Return ptr to data */
+static void *hash_find(xhash *hash, const char *name)
+{
+	hash_item *hi;
+	unsigned idx;
+	int l;
+
+	hi = hash_search(hash, name);
+	if (!hi) {
+		if (++hash->nel / hash->csize > 10)
+			hash_rebuild(hash);
+
+		l = strlen(name) + 1;
+		hi = xzalloc(sizeof(*hi) + l);
+		strcpy(hi->name, name);
+
+		idx = hashidx(name) % hash->csize;
+		hi->next = hash->items[idx];
+		hash->items[idx] = hi;
+		hash->glen += l;
+	}
+	return &hi->data;
+}
+
+#define findvar(hash, name) ((var*)    hash_find((hash), (name)))
+#define newvar(name)        ((var*)    hash_find(vhash, (name)))
+#define newfile(name)       ((rstream*)hash_find(fdhash, (name)))
+#define newfunc(name)       ((func*)   hash_find(fnhash, (name)))
+
+static void hash_remove(xhash *hash, const char *name)
+{
+	hash_item *hi, **phi;
+
+	phi = &hash->items[hashidx(name) % hash->csize];
+	while (*phi) {
+		hi = *phi;
+		if (strcmp(hi->name, name) == 0) {
+			hash->glen -= (strlen(name) + 1);
+			hash->nel--;
+			*phi = hi->next;
+			free(hi);
+			break;
+		}
+		phi = &hi->next;
+	}
+}
+
+/* ------ some useful functions ------ */
+
+static char *skip_spaces(char *p)
+{
+	while (1) {
+		if (*p == '\\' && p[1] == '\n') {
+			p++;
+			t_lineno++;
+		} else if (*p != ' ' && *p != '\t') {
+			break;
+		}
+		p++;
+	}
+	return p;
+}
+
+/* returns old *s, advances *s past word and terminating NUL */
+static char *nextword(char **s)
+{
+	char *p = *s;
+	while (*(*s)++ != '\0')
+		continue;
+	return p;
+}
+
+static char nextchar(char **s)
+{
+	char c, *pps;
+
+	c = *(*s)++;
+	pps = *s;
+	if (c == '\\')
+		c = bb_process_escape_sequence((const char**)s);
+	if (c == '\\' && *s == pps) { /* unrecognized \z? */
+		c = *(*s); /* yes, fetch z */
+		if (c)
+			(*s)++; /* advance unless z = NUL */
+	}
+	return c;
+}
+
+static ALWAYS_INLINE int isalnum_(int c)
+{
+	return (isalnum(c) || c == '_');
+}
+
+static double my_strtod(char **pp)
+{
+	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.
+			 */
+		}
+	}
+	return strtod(cp, pp);
+}
+
+/* -------- working with variables (set/get/copy/etc) -------- */
+
+static xhash *iamarray(var *v)
+{
+	var *a = v;
+
+	while (a->type & VF_CHILD)
+		a = a->x.parent;
+
+	if (!(a->type & VF_ARRAY)) {
+		a->type |= VF_ARRAY;
+		a->x.array = hash_init();
+	}
+	return a->x.array;
+}
+
+static void clear_array(xhash *array)
+{
+	unsigned i;
+	hash_item *hi, *thi;
+
+	for (i = 0; i < array->csize; i++) {
+		hi = array->items[i];
+		while (hi) {
+			thi = hi;
+			hi = hi->next;
+			free(thi->data.v.string);
+			free(thi);
+		}
+		array->items[i] = NULL;
+	}
+	array->glen = array->nel = 0;
+}
+
+/* clear a variable */
+static var *clrvar(var *v)
+{
+	if (!(v->type & VF_FSTR))
+		free(v->string);
+
+	v->type &= VF_DONTTOUCH;
+	v->type |= VF_DIRTY;
+	v->string = NULL;
+	return v;
+}
+
+/* assign string value to variable */
+static var *setvar_p(var *v, char *value)
+{
+	clrvar(v);
+	v->string = value;
+	handle_special(v);
+	return v;
+}
+
+/* same as setvar_p but make a copy of string */
+static var *setvar_s(var *v, const char *value)
+{
+	return setvar_p(v, (value && *value) ? xstrdup(value) : NULL);
+}
+
+/* same as setvar_s but sets USER flag */
+static var *setvar_u(var *v, const char *value)
+{
+	v = setvar_s(v, value);
+	v->type |= VF_USER;
+	return v;
+}
+
+/* set array element to user string */
+static void setari_u(var *a, int idx, const char *s)
+{
+	var *v;
+
+	v = findvar(iamarray(a), itoa(idx));
+	setvar_u(v, s);
+}
+
+/* assign numeric value to variable */
+static var *setvar_i(var *v, double value)
+{
+	clrvar(v);
+	v->type |= VF_NUMBER;
+	v->number = value;
+	handle_special(v);
+	return v;
+}
+
+static const char *getvar_s(var *v)
+{
+	/* if v is numeric and has no cached string, convert it to string */
+	if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) {
+		fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[CONVFMT]), v->number, TRUE);
+		v->string = xstrdup(g_buf);
+		v->type |= VF_CACHED;
+	}
+	return (v->string == NULL) ? "" : v->string;
+}
+
+static double getvar_i(var *v)
+{
+	char *s;
+
+	if ((v->type & (VF_NUMBER | VF_CACHED)) == 0) {
+		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;
+}
+
+/* Used for operands of bitwise ops */
+static unsigned long getvar_i_int(var *v)
+{
+	double d = getvar_i(v);
+
+	/* Casting doubles to longs is undefined for values outside
+	 * of target type range. Try to widen it as much as possible */
+	if (d >= 0)
+		return (unsigned long)d;
+	/* Why? Think about d == -4294967295.0 (assuming 32bit longs) */
+	return - (long) (unsigned long) (-d);
+}
+
+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);
+	}
+	handle_special(dest);
+	return dest;
+}
+
+static var *incvar(var *v)
+{
+	return setvar_i(v, getvar_i(v) + 1.0);
+}
+
+/* return true if v is number or numeric string */
+static int is_numeric(var *v)
+{
+	getvar_i(v);
+	return ((v->type ^ VF_DIRTY) & (VF_NUMBER | VF_USER | VF_DIRTY));
+}
+
+/* return 1 when value of v corresponds to true, 0 otherwise */
+static int istrue(var *v)
+{
+	if (is_numeric(v))
+		return (v->number != 0);
+	return (v->string && v->string[0]);
+}
+
+/* temporary variables allocator. Last allocated should be first freed */
+static var *nvalloc(int n)
+{
+	nvblock *pb = NULL;
+	var *v, *r;
+	int size;
+
+	while (g_cb) {
+		pb = g_cb;
+		if ((g_cb->pos - g_cb->nv) + n <= g_cb->size)
+			break;
+		g_cb = g_cb->next;
+	}
+
+	if (!g_cb) {
+		size = (n <= MINNVBLOCK) ? MINNVBLOCK : n;
+		g_cb = xzalloc(sizeof(nvblock) + size * sizeof(var));
+		g_cb->size = size;
+		g_cb->pos = g_cb->nv;
+		g_cb->prev = pb;
+		/*g_cb->next = NULL; - xzalloc did it */
+		if (pb)
+			pb->next = g_cb;
+	}
+
+	v = r = g_cb->pos;
+	g_cb->pos += n;
+
+	while (v < g_cb->pos) {
+		v->type = 0;
+		v->string = NULL;
+		v++;
+	}
+
+	return r;
+}
+
+static void nvfree(var *v)
+{
+	var *p;
+
+	if (v < g_cb->nv || v >= g_cb->pos)
+		syntax_error(EMSG_INTERNAL_ERROR);
+
+	for (p = v; p < g_cb->pos; p++) {
+		if ((p->type & (VF_ARRAY | VF_CHILD)) == VF_ARRAY) {
+			clear_array(iamarray(p));
+			free(p->x.array->items);
+			free(p->x.array);
+		}
+		if (p->type & VF_WALK) {
+			walker_list *n;
+			walker_list *w = p->x.walker;
+			debug_printf_walker("nvfree: freeing walker @%p\n", &p->x.walker);
+			p->x.walker = NULL;
+			while (w) {
+				n = w->prev;
+				debug_printf_walker(" free(%p)\n", w);
+				free(w);
+				w = n;
+			}
+		}
+		clrvar(p);
+	}
+
+	g_cb->pos = v;
+	while (g_cb->prev && g_cb->pos == g_cb->nv) {
+		g_cb = g_cb->prev;
+	}
+}
+
+/* ------- awk program text parsing ------- */
+
+/* Parse next token pointed by global pos, place results into global ttt.
+ * If token isn't expected, give away. Return token class
+ */
+static uint32_t next_token(uint32_t expected)
+{
+#define concat_inserted (G.next_token__concat_inserted)
+#define save_tclass     (G.next_token__save_tclass)
+#define save_info       (G.next_token__save_info)
+/* Initialized to TC_OPTERM: */
+#define ltclass         (G.next_token__ltclass)
+
+	char *p, *s;
+	const char *tl;
+	uint32_t tc;
+	const uint32_t *ti;
+
+	if (t_rollback) {
+		t_rollback = FALSE;
+
+	} else if (concat_inserted) {
+		concat_inserted = FALSE;
+		t_tclass = save_tclass;
+		t_info = save_info;
+
+	} else {
+		p = g_pos;
+ readnext:
+		p = skip_spaces(p);
+		g_lineno = t_lineno;
+		if (*p == '#')
+			while (*p != '\n' && *p != '\0')
+				p++;
+
+		if (*p == '\n')
+			t_lineno++;
+
+		if (*p == '\0') {
+			tc = TC_EOF;
+
+		} else if (*p == '\"') {
+			/* it's a string */
+			t_string = s = ++p;
+			while (*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;
+
+		} else if ((expected & TC_REGEXP) && *p == '/') {
+			/* it's regexp */
+			t_string = s = ++p;
+			while (*p != '/') {
+				if (*p == '\0' || *p == '\n')
+					syntax_error(EMSG_UNEXP_EOS);
+				*s = *p++;
+				if (*s++ == '\\') {
+					char *pp = p;
+					s[-1] = bb_process_escape_sequence((const char **)&pp);
+					if (*p == '\\')
+						*s++ = '\\';
+					if (pp == p)
+						*s++ = *p++;
+					else
+						p = pp;
+				}
+			}
+			p++;
+			*s = '\0';
+			tc = TC_REGEXP;
+
+		} else if (*p == '.' || isdigit(*p)) {
+			/* it's a number */
+			char *pp = p;
+			t_double = my_strtod(&pp);
+			p = pp;
+			if (*p == '.')
+				syntax_error(EMSG_UNEXP_TOKEN);
+			tc = TC_NUMBER;
+
+		} else {
+			/* search for something known */
+			tl = tokenlist;
+			tc = 0x00000001;
+			ti = tokeninfo;
+			while (*tl) {
+				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,
+				 */
+				if ((tc & (expected | TC_WORD | TC_NEWLINE))
+				 && strncmp(p, tl, l) == 0
+				 && !((tc & TC_WORD) && isalnum_(p[l]))
+				) {
+					/* then this is what we are looking for */
+					t_info = *ti;
+					p += l;
+					goto token_found;
+				}
+				ti++;
+				tl += l;
+			}
+			/* 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;
+			} else {
+				if (*p == '[') {
+					p++;
+					tc = TC_ARRAY;
+				}
+			}
+ token_found: ;
+		}
+		g_pos = p;
+
+		/* skipping newlines in some cases */
+		if ((ltclass & TC_NOTERM) && (tc & TC_NEWLINE))
+			goto readnext;
+
+		/* insert concatenation operator when needed */
+		if ((ltclass & TC_CONCAT1) && (tc & TC_CONCAT2) && (expected & TC_BINOP)) {
+			concat_inserted = TRUE;
+			save_tclass = tc;
+			save_info = t_info;
+			tc = TC_BINOP;
+			t_info = OC_CONCAT | SS | P(35);
+		}
+
+		t_tclass = tc;
+	}
+	ltclass = t_tclass;
+
+	/* Are we ready for this? */
+	if (!(ltclass & expected))
+		syntax_error((ltclass & (TC_NEWLINE | TC_EOF)) ?
+				EMSG_UNEXP_EOS : EMSG_UNEXP_TOKEN);
+
+	return ltclass;
+#undef concat_inserted
+#undef save_tclass
+#undef save_info
+#undef ltclass
+}
+
+static void rollback_token(void)
+{
+	t_rollback = TRUE;
+}
+
+static node *new_node(uint32_t info)
+{
+	node *n;
+
+	n = xzalloc(sizeof(node));
+	n->info = info;
+	n->lineno = g_lineno;
+	return n;
+}
+
+static void mk_re_node(const char *s, node *n, regex_t *re)
+{
+	n->info = OC_REGEXP;
+	n->l.re = re;
+	n->r.ire = re + 1;
+	xregcomp(re, s, REG_EXTENDED);
+	xregcomp(re + 1, s, REG_EXTENDED | REG_ICASE);
+}
+
+static node *condition(void)
+{
+	next_token(TC_SEQSTART);
+	return parse_expr(TC_SEQTERM);
+}
+
+/* parse expression terminated by given argument, return ptr
+ * to built subtree. Terminator is eaten by parse_expr */
+static node *parse_expr(uint32_t iexp)
+{
+	node sn;
+	node *cn = &sn;
+	node *vn, *glptr;
+	uint32_t tc, xtc;
+	var *v;
+
+	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 */
+			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)) {
+			/* for binary and postfix-unary operators, jump back over
+			 * previous operators with higher priority */
+			vn = cn;
+			while (((t_info & PRIMASK) > (vn->a.n->info & PRIMASK2))
+			    || ((t_info == vn->info) && ((t_info & OPCLSMASK) == OC_COLON))
+			) {
+				vn = vn->a.n;
+			}
+			if ((t_info & OPCLSMASK) == OC_TERNARY)
+				t_info += P(6);
+			cn = vn->a.n->r.n = new_node(t_info);
+			cn->a.n = vn->a.n;
+			if (tc & TC_BINOP) {
+				cn->l.n = vn;
+				xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
+				if ((t_info & OPCLSMASK) == OC_PGETLINE) {
+					/* it's a pipe */
+					next_token(TC_GETLINE);
+					/* give maximum priority to this pipe */
+					cn->info &= ~PRIMASK;
+					xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+				}
+			} else {
+				cn->r.n = vn;
+				xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+			}
+			vn->a.n = cn;
+
+		} else {
+			/* for operands and prefix-unary operators, attach them
+			 * to last node */
+			vn = cn;
+			cn = vn->r.n = new_node(t_info);
+			cn->a.n = vn;
+			xtc = TC_OPERAND | TC_UOPPRE | TC_REGEXP;
+			if (tc & (TC_OPERAND | TC_REGEXP)) {
+				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:
+					cn->info = OC_VAR;
+					v = hash_search(ahash, t_string);
+					if (v != NULL) {
+						cn->info = OC_FNARG;
+						cn->l.aidx = v->x.aidx;
+					} else {
+						cn->l.v = newvar(t_string);
+					}
+					if (tc & TC_ARRAY) {
+						cn->info |= xS;
+						cn->r.n = parse_expr(TC_ARRTERM);
+					}
+					break;
+
+				case TC_NUMBER:
+				case TC_STRING:
+					cn->info = OC_VAR;
+					v = cn->l.v = xzalloc(sizeof(var));
+					if (tc & TC_NUMBER)
+						setvar_i(v, t_double);
+					else
+						setvar_s(v, t_string);
+					break;
+
+				case TC_REGEXP:
+					mk_re_node(t_string, cn, xzalloc(sizeof(regex_t)*2));
+					break;
+
+				case TC_FUNCTION:
+					cn->info = OC_FUNC;
+					cn->r.f = newfunc(t_string);
+					cn->l.n = condition();
+					break;
+
+				case TC_SEQSTART:
+					cn = vn->r.n = parse_expr(TC_SEQTERM);
+					cn->a.n = vn;
+					break;
+
+				case TC_GETLINE:
+					glptr = cn;
+					xtc = TC_OPERAND | TC_UOPPRE | TC_BINOP | iexp;
+					break;
+
+				case TC_BUILTIN:
+					cn->l.n = condition();
+					break;
+				}
+			}
+		}
+	}
+	return sn.r.n;
+}
+
+/* add node to chain. Return ptr to alloc'd node */
+static node *chain_node(uint32_t info)
+{
+	node *n;
+
+	if (!seq->first)
+		seq->first = seq->last = new_node(0);
+
+	if (seq->programname != g_progname) {
+		seq->programname = g_progname;
+		n = chain_node(OC_NEWSOURCE);
+		n->l.new_progname = xstrdup(g_progname);
+	}
+
+	n = seq->last;
+	n->info = info;
+	seq->last = n->a.n = new_node(OC_DONE);
+
+	return n;
+}
+
+static void chain_expr(uint32_t info)
+{
+	node *n;
+
+	n = chain_node(info);
+	n->l.n = parse_expr(TC_OPTERM | TC_GRPTERM);
+	if (t_tclass & TC_GRPTERM)
+		rollback_token();
+}
+
+static node *chain_loop(node *nn)
+{
+	node *n, *n2, *save_brk, *save_cont;
+
+	save_brk = break_ptr;
+	save_cont = continue_ptr;
+
+	n = chain_node(OC_BR | Vx);
+	continue_ptr = new_node(OC_EXEC);
+	break_ptr = new_node(OC_EXEC);
+	chain_group();
+	n2 = chain_node(OC_EXEC | Vx);
+	n2->l.n = nn;
+	n2->a.n = n;
+	continue_ptr->a.n = n2;
+	break_ptr->a.n = n->r.n = seq->last;
+
+	continue_ptr = save_cont;
+	break_ptr = save_brk;
+
+	return n;
+}
+
+/* parse group and attach it to chain */
+static void chain_group(void)
+{
+	uint32_t c;
+	node *n, *n2, *n3;
+
+	do {
+		c = next_token(TC_GRPSEQ);
+	} while (c & TC_NEWLINE);
+
+	if (c & TC_GRPSTART) {
+		while (next_token(TC_GRPSEQ | TC_GRPTERM) != TC_GRPTERM) {
+			if (t_tclass & TC_NEWLINE)
+				continue;
+			rollback_token();
+			chain_group();
+		}
+	} else if (c & (TC_OPSEQ | TC_OPTERM)) {
+		rollback_token();
+		chain_expr(OC_EXEC | Vx);
+	} else {						/* TC_STATEMNT */
+		switch (t_info & OPCLSMASK) {
+		case ST_IF:
+			n = chain_node(OC_BR | Vx);
+			n->l.n = condition();
+			chain_group();
+			n2 = chain_node(OC_EXEC);
+			n->r.n = seq->last;
+			if (next_token(TC_GRPSEQ | TC_GRPTERM | TC_ELSE) == TC_ELSE) {
+				chain_group();
+				n2->a.n = seq->last;
+			} else {
+				rollback_token();
+			}
+			break;
+
+		case ST_WHILE:
+			n2 = condition();
+			n = chain_loop(NULL);
+			n->l.n = n2;
+			break;
+
+		case ST_DO:
+			n2 = chain_node(OC_EXEC);
+			n = chain_loop(NULL);
+			n2->a.n = n->a.n;
+			next_token(TC_WHILE);
+			n->l.n = condition();
+			break;
+
+		case ST_FOR:
+			next_token(TC_SEQSTART);
+			n2 = parse_expr(TC_SEMICOL | TC_SEQTERM);
+			if (t_tclass & TC_SEQTERM) {	/* for-in */
+				if ((n2->info & OPCLSMASK) != OC_IN)
+					syntax_error(EMSG_UNEXP_TOKEN);
+				n = chain_node(OC_WALKINIT | VV);
+				n->l.n = n2->l.n;
+				n->r.n = n2->r.n;
+				n = chain_loop(NULL);
+				n->info = OC_WALKNEXT | Vx;
+				n->l.n = n2->l.n;
+			} else {			/* for (;;) */
+				n = chain_node(OC_EXEC | Vx);
+				n->l.n = n2;
+				n2 = parse_expr(TC_SEMICOL);
+				n3 = parse_expr(TC_SEQTERM);
+				n = chain_loop(n3);
+				n->l.n = n2;
+				if (!n2)
+					n->info = OC_EXEC;
+			}
+			break;
+
+		case OC_PRINT:
+		case OC_PRINTF:
+			n = chain_node(t_info);
+			n->l.n = parse_expr(TC_OPTERM | TC_OUTRDR | TC_GRPTERM);
+			if (t_tclass & TC_OUTRDR) {
+				n->info |= t_info;
+				n->r.n = parse_expr(TC_OPTERM | TC_GRPTERM);
+			}
+			if (t_tclass & TC_GRPTERM)
+				rollback_token();
+			break;
+
+		case OC_BREAK:
+			n = chain_node(OC_EXEC);
+			n->a.n = break_ptr;
+			break;
+
+		case OC_CONTINUE:
+			n = chain_node(OC_EXEC);
+			n->a.n = continue_ptr;
+			break;
+
+		/* delete, next, nextfile, return, exit */
+		default:
+			chain_expr(t_info);
+		}
+	}
+}
+
+static void parse_program(char *p)
+{
+	uint32_t tclass;
+	node *cn;
+	func *f;
+	var *v;
+
+	g_pos = p;
+	t_lineno = 1;
+	while ((tclass = next_token(TC_EOF | TC_OPSEQ | TC_GRPSTART |
+			TC_OPTERM | TC_BEGIN | TC_END | TC_FUNCDECL)) != TC_EOF) {
+
+		if (tclass & TC_OPTERM)
+			continue;
+
+		seq = &mainseq;
+		if (tclass & TC_BEGIN) {
+			seq = &beginseq;
+			chain_group();
+
+		} else if (tclass & TC_END) {
+			seq = &endseq;
+			chain_group();
+
+		} else if (tclass & TC_FUNCDECL) {
+			next_token(TC_FUNCTION);
+			g_pos++;
+			f = newfunc(t_string);
+			f->body.first = NULL;
+			f->nargs = 0;
+			while (next_token(TC_VARIABLE | TC_SEQTERM) & TC_VARIABLE) {
+				v = findvar(ahash, t_string);
+				v->x.aidx = f->nargs++;
+
+				if (next_token(TC_COMMA | TC_SEQTERM) & TC_SEQTERM)
+					break;
+			}
+			seq = &f->body;
+			chain_group();
+			clear_array(ahash);
+
+		} else if (tclass & TC_OPSEQ) {
+			rollback_token();
+			cn = chain_node(OC_TEST);
+			cn->l.n = parse_expr(TC_OPTERM | TC_EOF | TC_GRPSTART);
+			if (t_tclass & TC_GRPSTART) {
+				rollback_token();
+				chain_group();
+			} else {
+				chain_node(OC_PRINT);
+			}
+			cn->r.n = mainseq.last;
+
+		} else /* if (tclass & TC_GRPSTART) */ {
+			rollback_token();
+			chain_group();
+		}
+	}
+}
+
+
+/* -------- program execution part -------- */
+
+static node *mk_splitter(const char *s, tsplitter *spl)
+{
+	regex_t *re, *ire;
+	node *n;
+
+	re = &spl->re[0];
+	ire = &spl->re[1];
+	n = &spl->n;
+	if ((n->info & OPCLSMASK) == OC_REGEXP) {
+		regfree(re);
+		regfree(ire); // TODO: nuke ire, use re+1?
+	}
+	if (s[0] && s[1]) { /* strlen(s) > 1 */
+		mk_re_node(s, n, re);
+	} else {
+		n->info = (uint32_t) s[0];
+	}
+
+	return n;
+}
+
+/* use node as a regular expression. Supplied with node ptr and regex_t
+ * storage space. Return ptr to regex (if result points to preg, it should
+ * be later regfree'd manually
+ */
+static regex_t *as_regex(node *op, regex_t *preg)
+{
+	int cflags;
+	var *v;
+	const char *s;
+
+	if ((op->info & OPCLSMASK) == OC_REGEXP) {
+		return icase ? op->r.ire : op->l.re;
+	}
+	v = nvalloc(1);
+	s = getvar_s(evaluate(op, v));
+
+	cflags = icase ? REG_EXTENDED | REG_ICASE : REG_EXTENDED;
+	/* Testcase where REG_EXTENDED fails (unpaired '{'):
+	 * echo Hi | awk 'gsub("@(samp|code|file)\{","");'
+	 * gawk 3.1.5 eats this. We revert to ~REG_EXTENDED
+	 * (maybe gsub is not supposed to use REG_EXTENDED?).
+	 */
+	if (regcomp(preg, s, cflags)) {
+		cflags &= ~REG_EXTENDED;
+		xregcomp(preg, s, cflags);
+	}
+	nvfree(v);
+	return preg;
+}
+
+/* gradually increasing buffer.
+ * note that we reallocate even if n == old_size,
+ * and thus there is at least one extra allocated byte.
+ */
+static char* qrealloc(char *b, int n, int *size)
+{
+	if (!b || n >= *size) {
+		*size = n + (n>>1) + 80;
+		b = xrealloc(b, *size);
+	}
+	return b;
+}
+
+/* resize field storage space */
+static void fsrealloc(int size)
+{
+	int i;
+
+	if (size >= maxfields) {
+		i = maxfields;
+		maxfields = size + 16;
+		Fields = xrealloc(Fields, maxfields * sizeof(Fields[0]));
+		for (; i < maxfields; i++) {
+			Fields[i].type = VF_SPECIAL;
+			Fields[i].string = NULL;
+		}
+	}
+	/* 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;
+	char c[4];
+	char *s1;
+	regmatch_t pmatch[2]; // TODO: why [2]? [1] is enough...
+
+	/* in worst case, each char would be a separate field */
+	*slist = s1 = xzalloc(strlen(s) * 2 + 3);
+	strcpy(s1, s);
+
+	c[0] = c[1] = (char)spl->info;
+	c[2] = c[3] = '\0';
+	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 */
+		n++; /* at least one field will be there */
+		do {
+			l = strcspn(s, c+2); /* len till next NUL or \n */
+			if (regexec(icase ? spl->r.ire : spl->l.re, s, 1, pmatch, 0) == 0
+			 && pmatch[0].rm_so <= l
+			) {
+				l = pmatch[0].rm_so;
+				if (pmatch[0].rm_eo == 0) {
+					l++;
+					pmatch[0].rm_eo++;
+				}
+				n++; /* we saw yet another delimiter */
+			} else {
+				pmatch[0].rm_eo = l;
+				if (s[l])
+					pmatch[0].rm_eo++;
+			}
+			memcpy(s1, s, l);
+			/* make sure we remove *all* of the separator chars */
+			do {
+				s1[l] = '\0';
+			} while (++l < pmatch[0].rm_eo);
+			nextword(&s1);
+			s += pmatch[0].rm_eo;
+		} while (*s);
+		return n;
+	}
+	if (c[0] == '\0') {  /* null split */
+		while (*s) {
+			*s1++ = *s++;
+			*s1++ = '\0';
+			n++;
+		}
+		return n;
+	}
+	if (c[0] != ' ') {  /* single-character split */
+		if (icase) {
+			c[0] = toupper(c[0]);
+			c[1] = tolower(c[1]);
+		}
+		if (*s1)
+			n++;
+		while ((s1 = strpbrk(s1, c)) != NULL) {
+			*s1++ = '\0';
+			n++;
+		}
+		return n;
+	}
+	/* space split */
+	while (*s) {
+		s = skip_whitespace(s);
+		if (!*s)
+			break;
+		n++;
+		while (*s && !isspace(*s))
+			*s1++ = *s++;
+		*s1++ = '\0';
+	}
+	return n;
+}
+
+static void split_f0(void)
+{
+/* static char *fstrings; */
+#define fstrings (G.split_f0__fstrings)
+
+	int i, n;
+	char *s;
+
+	if (is_f0_split)
+		return;
+
+	is_f0_split = TRUE;
+	free(fstrings);
+	fsrealloc(0);
+	n = awk_split(getvar_s(intvar[F0]), &fsplitter.n, &fstrings);
+	fsrealloc(n);
+	s = fstrings;
+	for (i = 0; i < n; i++) {
+		Fields[i].string = nextword(&s);
+		Fields[i].type |= (VF_FSTR | VF_USER | VF_DIRTY);
+	}
+
+	/* set NF manually to avoid side effects */
+	clrvar(intvar[NF]);
+	intvar[NF]->type = VF_NUMBER | VF_SPECIAL;
+	intvar[NF]->number = nfields;
+#undef fstrings
+}
+
+/* perform additional actions when some internal variables changed */
+static void handle_special(var *v)
+{
+	int n;
+	char *b;
+	const char *sep, *s;
+	int sl, l, len, i, bsize;
+
+	if (!(v->type & VF_SPECIAL))
+		return;
+
+	if (v == intvar[NF]) {
+		n = (int)getvar_i(v);
+		fsrealloc(n);
+
+		/* recalculate $0 */
+		sep = getvar_s(intvar[OFS]);
+		sl = strlen(sep);
+		b = NULL;
+		len = 0;
+		for (i = 0; i < n; i++) {
+			s = getvar_s(&Fields[i]);
+			l = strlen(s);
+			if (b) {
+				memcpy(b+len, sep, sl);
+				len += sl;
+			}
+			b = qrealloc(b, len+l+sl, &bsize);
+			memcpy(b+len, s, l);
+			len += l;
+		}
+		if (b)
+			b[len] = '\0';
+		setvar_p(intvar[F0], b);
+		is_f0_split = TRUE;
+
+	} else if (v == intvar[F0]) {
+		is_f0_split = FALSE;
+
+	} else if (v == intvar[FS]) {
+		mk_splitter(getvar_s(v), &fsplitter);
+
+	} else if (v == intvar[RS]) {
+		mk_splitter(getvar_s(v), &rsplitter);
+
+	} else if (v == intvar[IGNORECASE]) {
+		icase = istrue(v);
+
+	} else {				/* $n */
+		n = getvar_i(intvar[NF]);
+		setvar_i(intvar[NF], n > v-Fields ? n : v-Fields+1);
+		/* right here v is invalid. Just to note... */
+	}
+}
+
+/* step through func/builtin/etc arguments */
+static node *nextarg(node **pn)
+{
+	node *n;
+
+	n = *pn;
+	if (n && (n->info & OPCLSMASK) == OC_COMMA) {
+		*pn = n->r.n;
+		n = n->l.n;
+	} else {
+		*pn = NULL;
+	}
+	return n;
+}
+
+static void hashwalk_init(var *v, xhash *array)
+{
+	hash_item *hi;
+	unsigned i;
+	walker_list *w;
+	walker_list *prev_walker;
+
+	if (v->type & VF_WALK) {
+		prev_walker = v->x.walker;
+	} else {
+		v->type |= VF_WALK;
+		prev_walker = NULL;
+	}
+	debug_printf_walker("hashwalk_init: prev_walker:%p\n", prev_walker);
+
+	w = v->x.walker = xzalloc(sizeof(*w) + array->glen + 1); /* why + 1? */
+	debug_printf_walker(" walker@%p=%p\n", &v->x.walker, w);
+	w->cur = w->end = w->wbuf;
+	w->prev = prev_walker;
+	for (i = 0; i < array->csize; i++) {
+		hi = array->items[i];
+		while (hi) {
+			strcpy(w->end, hi->name);
+			nextword(&w->end);
+			hi = hi->next;
+		}
+	}
+}
+
+static int hashwalk_next(var *v)
+{
+	walker_list *w = v->x.walker;
+
+	if (w->cur >= w->end) {
+		walker_list *prev_walker = w->prev;
+
+		debug_printf_walker("end of iteration, free(walker@%p:%p), prev_walker:%p\n", &v->x.walker, w, prev_walker);
+		free(w);
+		v->x.walker = prev_walker;
+		return FALSE;
+	}
+
+	setvar_s(v, nextword(&w->cur));
+	return TRUE;
+}
+
+/* evaluate node, return 1 when result is true, 0 otherwise */
+static int ptest(node *pattern)
+{
+	/* ptest__v is "static": to save stack space? */
+	return istrue(evaluate(pattern, &G.ptest__v));
+}
+
+/* read next record from stream rsm into a variable v */
+static int awk_getline(rstream *rsm, var *v)
+{
+	char *b;
+	regmatch_t pmatch[2];
+	int size, a, p, pp = 0;
+	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
+	 */
+	fd = fileno(rsm->F);
+	m = rsm->buffer;
+	a = rsm->adv;
+	p = rsm->pos;
+	size = rsm->size;
+	c = (char) rsplitter.n.info;
+	rp = 0;
+
+	if (!m)
+		m = qrealloc(m, 256, &size);
+
+	do {
+		b = m + a;
+		so = eo = p;
+		r = 1;
+		if (p > 0) {
+			if ((rsplitter.n.info & OPCLSMASK) == OC_REGEXP) {
+				if (regexec(icase ? rsplitter.n.r.ire : rsplitter.n.l.re,
+							b, 1, pmatch, 0) == 0) {
+					so = pmatch[0].rm_so;
+					eo = pmatch[0].rm_eo;
+					if (b[eo] != '\0')
+						break;
+				}
+			} else if (c != '\0') {
+				s = strchr(b+pp, c);
+				if (!s)
+					s = memchr(b+pp, '\0', p - pp);
+				if (s) {
+					so = eo = s-b;
+					eo++;
+					break;
+				}
+			} else {
+				while (b[rp] == '\n')
+					rp++;
+				s = strstr(b+rp, "\n\n");
+				if (s) {
+					so = eo = s-b;
+					while (b[eo] == '\n')
+						eo++;
+					if (b[eo] != '\0')
+						break;
+				}
+			}
+		}
+
+		if (a > 0) {
+			memmove(m, m+a, p+1);
+			b = m;
+			a = 0;
+		}
+
+		m = qrealloc(m, a+p+128, &size);
+		b = m + a;
+		pp = p;
+		p += safe_read(fd, b+p, size-p-1);
+		if (p < pp) {
+			p = 0;
+			r = 0;
+			setvar_i(intvar[ERRNO], errno);
+		}
+		b[p] = '\0';
+
+	} while (p > pp);
+
+	if (p == 0) {
+		r--;
+	} else {
+		c = b[so]; b[so] = '\0';
+		setvar_s(v, b+rp);
+		v->type |= VF_USER;
+		b[so] = c;
+		c = b[eo]; b[eo] = '\0';
+		setvar_s(intvar[RT], b+so);
+		b[eo] = c;
+	}
+
+	rsm->buffer = m;
+	rsm->adv = a + eo;
+	rsm->pos = p - eo;
+	rsm->size = size;
+
+	debug_printf_eval("returning from %s(): %d\n", __func__, r);
+
+	return r;
+}
+
+static int fmt_num(char *b, int size, const char *format, double n, int int_as_int)
+{
+	int r = 0;
+	char c;
+	const char *s = format;
+
+	if (int_as_int && n == (int)n) {
+		r = snprintf(b, size, "%d", (int)n);
+	} else {
+		do { c = *s; } while (c && *++s);
+		if (strchr("diouxX", c)) {
+			r = snprintf(b, size, format, (int)n);
+		} else if (strchr("eEfgG", c)) {
+			r = snprintf(b, size, format, n);
+		} else {
+			syntax_error(EMSG_INV_FMT);
+		}
+	}
+	return r;
+}
+
+/* formatted output into an allocated buffer, return ptr to buffer */
+static char *awk_printf(node *n)
+{
+	char *b = NULL;
+	char *fmt, *s, *f;
+	const char *s1;
+	int i, j, incr, bsize;
+	char c, c1;
+	var *v, *arg;
+
+	v = nvalloc(1);
+	fmt = f = xstrdup(getvar_s(evaluate(nextarg(&n), v)));
+
+	i = 0;
+	while (*f) {
+		s = f;
+		while (*f && (*f != '%' || *++f == '%'))
+			f++;
+		while (*f && !isalpha(*f)) {
+			if (*f == '*')
+				syntax_error("%*x formats are not supported");
+			f++;
+		}
+
+		incr = (f - s) + MAXVARFMT;
+		b = qrealloc(b, incr + i, &bsize);
+		c = *f;
+		if (c != '\0')
+			f++;
+		c1 = *f;
+		*f = '\0';
+		arg = evaluate(nextarg(&n), v);
+
+		j = i;
+		if (c == 'c' || !c) {
+			i += sprintf(b+i, s, is_numeric(arg) ?
+					(char)getvar_i(arg) : *getvar_s(arg));
+		} else if (c == 's') {
+			s1 = getvar_s(arg);
+			b = qrealloc(b, incr+i+strlen(s1), &bsize);
+			i += sprintf(b+i, s, s1);
+		} else {
+			i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE);
+		}
+		*f = c1;
+
+		/* if there was an error while sprintf, return value is negative */
+		if (i < j)
+			i = j;
+	}
+
+	free(fmt);
+	nvfree(v);
+	b = xrealloc(b, i + 1);
+	b[i] = '\0';
+	return b;
+}
+
+/* Common substitution routine.
+ * Replace (nm)'th substring of (src) that matches (rn) with (repl),
+ * store result into (dest), return number of substitutions.
+ * If nm = 0, replace all matches.
+ * If src or dst is NULL, use $0.
+ * If subexp != 0, enable subexpression matching (\1-\9).
+ */
+static int awk_sub(node *rn, const char *repl, int nm, var *src, var *dest, int subexp)
+{
+	char *resbuf;
+	const char *sp;
+	int match_no, residx, replen, resbufsize;
+	int regexec_flags;
+	regmatch_t pmatch[10];
+	regex_t sreg, *regex;
+
+	resbuf = NULL;
+	residx = 0;
+	match_no = 0;
+	regexec_flags = 0;
+	regex = as_regex(rn, &sreg);
+	sp = getvar_s(src ? src : intvar[F0]);
+	replen = strlen(repl);
+	while (regexec(regex, sp, 10, pmatch, regexec_flags) == 0) {
+		int so = pmatch[0].rm_so;
+		int eo = pmatch[0].rm_eo;
+
+		//bb_error_msg("match %u: [%u,%u] '%s'%p", match_no+1, so, eo, sp,sp);
+		resbuf = qrealloc(resbuf, residx + eo + replen, &resbufsize);
+		memcpy(resbuf + residx, sp, eo);
+		residx += eo;
+		if (++match_no >= nm) {
+			const char *s;
+			int nbs;
+
+			/* replace */
+			residx -= (eo - so);
+			nbs = 0;
+			for (s = repl; *s; s++) {
+				char c = resbuf[residx++] = *s;
+				if (c == '\\') {
+					nbs++;
+					continue;
+				}
+				if (c == '&' || (subexp && c >= '0' && c <= '9')) {
+					int j;
+					residx -= ((nbs + 3) >> 1);
+					j = 0;
+					if (c != '&') {
+						j = c - '0';
+						nbs++;
+					}
+					if (nbs % 2) {
+						resbuf[residx++] = c;
+					} else {
+						int n = pmatch[j].rm_eo - pmatch[j].rm_so;
+						resbuf = qrealloc(resbuf, residx + replen + n, &resbufsize);
+						memcpy(resbuf + residx, sp + pmatch[j].rm_so, n);
+						residx += n;
+					}
+				}
+				nbs = 0;
+			}
+		}
+
+		regexec_flags = REG_NOTBOL;
+		sp += eo;
+		if (match_no == nm)
+			break;
+		if (eo == so) {
+			/* Empty match (e.g. "b*" will match anywhere).
+			 * Advance by one char. */
+//BUG (bug 1333):
+//gsub(/\<b*/,"") on "abc" will reach this point, advance to "bc"
+//... and will erroneously match "b" even though it is NOT at the word start.
+//we need REG_NOTBOW but it does not exist...
+//TODO: if EXTRA_COMPAT=y, use GNU matching and re_search,
+//it should be able to do it correctly.
+			/* Subtle: this is safe only because
+			 * qrealloc allocated at least one extra byte */
+			resbuf[residx] = *sp;
+			if (*sp == '\0')
+				goto ret;
+			sp++;
+			residx++;
+		}
+	}
+
+	resbuf = qrealloc(resbuf, residx + strlen(sp), &resbufsize);
+	strcpy(resbuf + residx, sp);
+ ret:
+	//bb_error_msg("end sp:'%s'%p", sp,sp);
+	setvar_p(dest ? dest : intvar[F0], resbuf);
+	if (regex == &sreg)
+		regfree(regex);
+	return match_no;
+}
+
+static NOINLINE int do_mktime(const char *ds)
+{
+	struct tm then;
+	int count;
+
+	/*memset(&then, 0, sizeof(then)); - not needed */
+	then.tm_isdst = -1; /* default is unknown */
+
+	/* manpage of mktime says these fields are ints,
+	 * so we can sscanf stuff directly into them */
+	count = sscanf(ds, "%u %u %u %u %u %u %d",
+		&then.tm_year, &then.tm_mon, &then.tm_mday,
+		&then.tm_hour, &then.tm_min, &then.tm_sec,
+		&then.tm_isdst);
+
+	if (count < 6
+	 || (unsigned)then.tm_mon < 1
+	 || (unsigned)then.tm_year < 1900
+	) {
+		return -1;
+	}
+
+	then.tm_mon -= 1;
+	then.tm_year -= 1900;
+
+	return mktime(&then);
+}
+
+static NOINLINE var *exec_builtin(node *op, var *res)
+{
+#define tspl (G.exec_builtin__tspl)
+
+	var *tv;
+	node *an[4];
+	var *av[4];
+	const char *as[4];
+	regmatch_t pmatch[2];
+	regex_t sreg, *re;
+	node *spl;
+	uint32_t isr, info;
+	int nargs;
+	time_t tt;
+	int i, l, ll, n;
+
+	tv = nvalloc(4);
+	isr = info = op->info;
+	op = op->l.n;
+
+	av[2] = av[3] = NULL;
+	for (i = 0; i < 4 && op; i++) {
+		an[i] = nextarg(&op);
+		if (isr & 0x09000000)
+			av[i] = evaluate(an[i], &tv[i]);
+		if (isr & 0x08000000)
+			as[i] = getvar_s(av[i]);
+		isr >>= 1;
+	}
+
+	nargs = i;
+	if ((uint32_t)nargs < (info >> 30))
+		syntax_error(EMSG_TOO_FEW_ARGS);
+
+	info &= OPNMASK;
+	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);
+		break;
+
+	case B_sp: {
+		char *s, *s1;
+
+		if (nargs > 2) {
+			spl = (an[2]->info & OPCLSMASK) == OC_REGEXP ?
+				an[2] : mk_splitter(getvar_s(evaluate(an[2], &tv[2])), &tspl);
+		} else {
+			spl = &fsplitter.n;
+		}
+
+		n = awk_split(as[0], spl, &s);
+		s1 = s;
+		clear_array(iamarray(av[1]));
+		for (i = 1; i <= n; i++)
+			setari_u(av[1], i, nextword(&s));
+		free(s1);
+		setvar_i(res, n);
+		break;
+	}
+
+	case B_ss: {
+		char *s;
+
+		l = strlen(as[0]);
+		i = getvar_i(av[1]) - 1;
+		if (i > l)
+			i = l;
+		if (i < 0)
+			i = 0;
+		n = (nargs > 2) ? getvar_i(av[2]) : l-i;
+		if (n < 0)
+			n = 0;
+		s = xstrndup(as[0]+i, n);
+		setvar_p(res, s);
+		break;
+	}
+
+	/* Bitwise ops must assume that operands are unsigned. GNU Awk 3.1.5:
+	 * awk '{ print or(-1,1) }' gives "4.29497e+09", not "-2.xxxe+09" */
+	case B_an:
+		setvar_i(res, getvar_i_int(av[0]) & getvar_i_int(av[1]));
+		break;
+
+	case B_co:
+		setvar_i(res, ~getvar_i_int(av[0]));
+		break;
+
+	case B_ls:
+		setvar_i(res, getvar_i_int(av[0]) << getvar_i_int(av[1]));
+		break;
+
+	case B_or:
+		setvar_i(res, getvar_i_int(av[0]) | getvar_i_int(av[1]));
+		break;
+
+	case B_rs:
+		setvar_i(res, getvar_i_int(av[0]) >> getvar_i_int(av[1]));
+		break;
+
+	case B_xo:
+		setvar_i(res, getvar_i_int(av[0]) ^ getvar_i_int(av[1]));
+		break;
+
+	case B_lo:
+	case B_up: {
+		char *s, *s1;
+		s1 = s = xstrdup(as[0]);
+		while (*s1) {
+			//*s1 = (info == B_up) ? toupper(*s1) : tolower(*s1);
+			if ((unsigned char)((*s1 | 0x20) - 'a') <= ('z' - 'a'))
+				*s1 = (info == B_up) ? (*s1 & 0xdf) : (*s1 | 0x20);
+			s1++;
+		}
+		setvar_p(res, s);
+		break;
+	}
+
+	case B_ix:
+		n = 0;
+		ll = strlen(as[1]);
+		l = strlen(as[0]) - ll;
+		if (ll > 0 && l >= 0) {
+			if (!icase) {
+				char *s = strstr(as[0], as[1]);
+				if (s)
+					n = (s - as[0]) + 1;
+			} else {
+				/* this piece of code is terribly slow and
+				 * really should be rewritten
+				 */
+				for (i = 0; i <= l; i++) {
+					if (strncasecmp(as[0]+i, as[1], ll) == 0) {
+						n = i+1;
+						break;
+					}
+				}
+			}
+		}
+		setvar_i(res, n);
+		break;
+
+	case B_ti:
+		if (nargs > 1)
+			tt = getvar_i(av[1]);
+		else
+			time(&tt);
+		//s = (nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y";
+		i = strftime(g_buf, MAXVARFMT,
+			((nargs > 0) ? as[0] : "%a %b %d %H:%M:%S %Z %Y"),
+			localtime(&tt));
+		g_buf[i] = '\0';
+		setvar_s(res, g_buf);
+		break;
+
+	case B_mt:
+		setvar_i(res, do_mktime(as[0]));
+		break;
+
+	case B_ma:
+		re = as_regex(an[1], &sreg);
+		n = regexec(re, as[0], 1, pmatch, 0);
+		if (n == 0) {
+			pmatch[0].rm_so++;
+			pmatch[0].rm_eo++;
+		} else {
+			pmatch[0].rm_so = 0;
+			pmatch[0].rm_eo = -1;
+		}
+		setvar_i(newvar("RSTART"), pmatch[0].rm_so);
+		setvar_i(newvar("RLENGTH"), pmatch[0].rm_eo - pmatch[0].rm_so);
+		setvar_i(res, pmatch[0].rm_so);
+		if (re == &sreg)
+			regfree(re);
+		break;
+
+	case B_ge:
+		awk_sub(an[0], as[1], getvar_i(av[2]), av[3], res, TRUE);
+		break;
+
+	case B_gs:
+		setvar_i(res, awk_sub(an[0], as[1], 0, av[2], av[2], FALSE));
+		break;
+
+	case B_su:
+		setvar_i(res, awk_sub(an[0], as[1], 1, av[2], av[2], FALSE));
+		break;
+	}
+
+	nvfree(tv);
+	return res;
+#undef tspl
+}
+
+/*
+ * Evaluate node - the heart of the program. Supplied with subtree
+ * and place where to store result. returns ptr to result.
+ */
+#define XC(n) ((n) >> 8)
+
+static var *evaluate(node *op, var *res)
+{
+/* This procedure is recursive so we should count every byte */
+#define fnargs (G.evaluate__fnargs)
+/* seed is initialized to 1 */
+#define seed   (G.evaluate__seed)
+#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) {
+		struct {
+			var *v;
+			const char *s;
+		} L = L; /* for compiler */
+		struct {
+			var *v;
+			const char *s;
+		} R = R;
+		double L_d = L_d;
+		uint32_t opinfo;
+		int opn;
+		node *op1;
+
+		opinfo = op->info;
+		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) {
+			L.s = getvar_s(L.v);
+			debug_printf_eval("L.s:'%s'\n", L.s);
+		}
+		if (opinfo & OF_STR2) {
+			R.s = getvar_s(R.v);
+			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 -- */
+
+		/* test pattern */
+		case XC( OC_TEST ):
+			if ((op1->info & OPCLSMASK) == OC_COMMA) {
+				/* it's range pattern */
+				if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
+					op->info |= OF_CHECKED;
+					if (ptest(op1->r.n))
+						op->info &= ~OF_CHECKED;
+					op = op->a.n;
+				} else {
+					op = op->r.n;
+				}
+			} else {
+				op = ptest(op1) ? op->a.n : op->r.n;
+			}
+			break;
+
+		/* just evaluate an expression, also used as unconditional jump */
+		case XC( OC_EXEC ):
+			break;
+
+		/* branch, used in if-else and various loops */
+		case XC( OC_BR ):
+			op = istrue(L.v) ? op->a.n : op->r.n;
+			break;
+
+		/* initialize for-in loop */
+		case XC( OC_WALKINIT ):
+			hashwalk_init(L.v, iamarray(R.v));
+			break;
+
+		/* get next array item */
+		case XC( OC_WALKNEXT ):
+			op = hashwalk_next(L.v) ? op->a.n : op->r.n;
+			break;
+
+		case XC( OC_PRINT ):
+		case XC( OC_PRINTF ): {
+			FILE *F = stdout;
+
+			if (op->r.n) {
+				rstream *rsm = newfile(R.s);
+				if (!rsm->F) {
+					if (opn == '|') {
+						rsm->F = popen(R.s, "w");
+						if (rsm->F == NULL)
+							bb_perror_msg_and_die("popen");
+						rsm->is_pipe = 1;
+					} else {
+						rsm->F = xfopen(R.s, opn=='w' ? "w" : "a");
+					}
+				}
+				F = rsm->F;
+			}
+
+			if ((opinfo & OPCLSMASK) == OC_PRINT) {
+				if (!op1) {
+					fputs(getvar_s(intvar[F0]), F);
+				} else {
+					while (op1) {
+						var *v = evaluate(nextarg(&op1), v1);
+						if (v->type & VF_NUMBER) {
+							fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]),
+									getvar_i(v), TRUE);
+							fputs(g_buf, F);
+						} else {
+							fputs(getvar_s(v), F);
+						}
+
+						if (op1)
+							fputs(getvar_s(intvar[OFS]), F);
+					}
+				}
+				fputs(getvar_s(intvar[ORS]), F);
+
+			} else {	/* OC_PRINTF */
+				char *s = awk_printf(op1);
+				fputs(s, F);
+				free(s);
+			}
+			fflush(F);
+			break;
+		}
+
+		case XC( OC_DELETE ): {
+			uint32_t info = op1->info & OPCLSMASK;
+			var *v;
+
+			if (info == OC_VAR) {
+				v = op1->l.v;
+			} else if (info == OC_FNARG) {
+				v = &fnargs[op1->l.aidx];
+			} else {
+				syntax_error(EMSG_NOT_ARRAY);
+			}
+
+			if (op1->r.n) {
+				const char *s;
+				clrvar(L.v);
+				s = getvar_s(evaluate(op1->r.n, v1));
+				hash_remove(iamarray(v), s);
+			} else {
+				clear_array(iamarray(v));
+			}
+			break;
+		}
+
+		case XC( OC_NEWSOURCE ):
+			g_progname = op->l.new_progname;
+			break;
+
+		case XC( OC_RETURN ):
+			copyvar(res, L.v);
+			break;
+
+		case XC( OC_NEXTFILE ):
+			nextfile = TRUE;
+		case XC( OC_NEXT ):
+			nextrec = TRUE;
+		case XC( OC_DONE ):
+			clrvar(res);
+			break;
+
+		case XC( OC_EXIT ):
+			awk_exit(L_d);
+
+		/* -- recursive node type -- */
+
+		case XC( OC_VAR ):
+			L.v = op->l.v;
+			if (L.v == intvar[NF])
+				split_f0();
+			goto v_cont;
+
+		case XC( OC_FNARG ):
+			L.v = &fnargs[op->l.aidx];
+ v_cont:
+			res = op->r.n ? findvar(iamarray(L.v), R.s) : L.v;
+			break;
+
+		case XC( OC_IN ):
+			setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
+			break;
+
+		case XC( OC_REGEXP ):
+			op1 = op;
+			L.s = getvar_s(intvar[F0]);
+			goto re_cont;
+
+		case XC( OC_MATCH ):
+			op1 = op->r.n;
+ re_cont:
+			{
+				regex_t *re = as_regex(op1, &sreg);
+				int i = regexec(re, L.s, 0, NULL, 0);
+				if (re == &sreg)
+					regfree(re);
+				setvar_i(res, (i == 0) ^ (opn == '!'));
+			}
+			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
+//			if (R.v == v1+1 && R.v->string) {
+//				res = setvar_p(L.v, R.v->string);
+//				R.v->string = NULL;
+//			} else {
+				res = copyvar(L.v, R.v);
+//			}
+			break;
+
+		case XC( OC_TERNARY ):
+			if ((op->r.n->info & OPCLSMASK) != OC_COLON)
+				syntax_error(EMSG_POSSIBLE_ERROR);
+			res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
+			break;
+
+		case XC( OC_FUNC ): {
+			var *vbeg, *v;
+			const char *sv_progname;
+
+			if (!op->r.f->body.first)
+				syntax_error(EMSG_UNDEF_FUNC);
+
+			vbeg = v = nvalloc(op->r.f->nargs + 1);
+			while (op1) {
+				var *arg = evaluate(nextarg(&op1), v1);
+				copyvar(v, arg);
+				v->type |= VF_CHILD;
+				v->x.parent = arg;
+				if (++v - vbeg >= op->r.f->nargs)
+					break;
+			}
+
+			v = fnargs;
+			fnargs = vbeg;
+			sv_progname = g_progname;
+
+			res = evaluate(op->r.f->body.first, res);
+
+			g_progname = sv_progname;
+			nvfree(fnargs);
+			fnargs = v;
+
+			break;
+		}
+
+		case XC( OC_GETLINE ):
+		case XC( OC_PGETLINE ): {
+			rstream *rsm;
+			int i;
+
+			if (op1) {
+				rsm = newfile(L.s);
+				if (!rsm->F) {
+					if ((opinfo & OPCLSMASK) == OC_PGETLINE) {
+						rsm->F = popen(L.s, "r");
+						rsm->is_pipe = TRUE;
+					} else {
+						rsm->F = fopen_for_read(L.s);  /* not xfopen! */
+					}
+				}
+			} else {
+				if (!iF)
+					iF = next_input_file();
+				rsm = iF;
+			}
+
+			if (!rsm->F) {
+				setvar_i(intvar[ERRNO], errno);
+				setvar_i(res, -1);
+				break;
+			}
+
+			if (!op->r.n)
+				R.v = intvar[F0];
+
+			i = awk_getline(rsm, R.v);
+			if (i > 0 && !op1) {
+				incvar(intvar[FNR]);
+				incvar(intvar[NR]);
+			}
+			setvar_i(res, i);
+			break;
+		}
+
+		/* simple builtins */
+		case XC( OC_FBLTIN ): {
+			double R_d = R_d; /* for compiler */
+
+			switch (opn) {
+			case F_in:
+				R_d = (int)L_d;
+				break;
+
+			case F_rn:
+				R_d = (double)rand() / (double)RAND_MAX;
+				break;
+
+			case F_co:
+				if (ENABLE_FEATURE_AWK_LIBM) {
+					R_d = cos(L_d);
+					break;
+				}
+
+			case F_ex:
+				if (ENABLE_FEATURE_AWK_LIBM) {
+					R_d = exp(L_d);
+					break;
+				}
+
+			case F_lg:
+				if (ENABLE_FEATURE_AWK_LIBM) {
+					R_d = log(L_d);
+					break;
+				}
+
+			case F_si:
+				if (ENABLE_FEATURE_AWK_LIBM) {
+					R_d = sin(L_d);
+					break;
+				}
+
+			case F_sq:
+				if (ENABLE_FEATURE_AWK_LIBM) {
+					R_d = sqrt(L_d);
+					break;
+				}
+
+				syntax_error(EMSG_NO_MATH);
+				break;
+
+			case F_sr:
+				R_d = (double)seed;
+				seed = op1 ? (unsigned)L_d : (unsigned)time(NULL);
+				srand(seed);
+				break;
+
+			case F_ti:
+				R_d = time(NULL);
+				break;
+
+			case F_le:
+				if (!op1)
+					L.s = getvar_s(intvar[F0]);
+				R_d = strlen(L.s);
+				break;
+
+			case F_sy:
+				fflush_all();
+				R_d = (ENABLE_FEATURE_ALLOW_EXEC && L.s && *L.s)
+						? (system(L.s) >> 8) : 0;
+				break;
+
+			case F_ff:
+				if (!op1) {
+					fflush(stdout);
+				} else if (L.s && *L.s) {
+					rstream *rsm = newfile(L.s);
+					fflush(rsm->F);
+				} else {
+					fflush_all();
+				}
+				break;
+
+			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) {
+					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 (err)
+					setvar_i(intvar[ERRNO], errno);
+				R_d = (double)err;
+				break;
+			}
+			} /* switch */
+			setvar_i(res, R_d);
+			break;
+		}
+
+		case XC( OC_BUILTIN ):
+			res = exec_builtin(op, res);
+			break;
+
+		case XC( OC_SPRINTF ):
+			setvar_p(res, awk_printf(op1));
+			break;
+
+		case XC( OC_UNARY ): {
+			double Ld, R_d;
+
+			Ld = R_d = getvar_i(R.v);
+			switch (opn) {
+			case 'P':
+				Ld = ++R_d;
+				goto r_op_change;
+			case 'p':
+				R_d++;
+				goto r_op_change;
+			case 'M':
+				Ld = --R_d;
+				goto r_op_change;
+			case 'm':
+				R_d--;
+ r_op_change:
+				setvar_i(R.v, R_d);
+				break;
+			case '!':
+				Ld = !istrue(R.v);
+				break;
+			case '-':
+				Ld = -R_d;
+				break;
+			}
+			setvar_i(res, Ld);
+			break;
+		}
+
+		case XC( OC_FIELD ): {
+			int i = (int)getvar_i(R.v);
+			if (i == 0) {
+				res = intvar[F0];
+			} else {
+				split_f0();
+				if (i > nfields)
+					fsrealloc(i);
+				res = &Fields[i - 1];
+			}
+			break;
+		}
+
+		/* concatenation (" ") and index joining (",") */
+		case XC( OC_CONCAT ):
+		case XC( OC_COMMA ): {
+			const char *sep = "";
+			if ((opinfo & OPCLSMASK) == OC_COMMA)
+				sep = getvar_s(intvar[SUBSEP]);
+			setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s));
+			break;
+		}
+
+		case XC( OC_LAND ):
+			setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
+			break;
+
+		case XC( OC_LOR ):
+			setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
+			break;
+
+		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;
+				break;
+			case '-':
+				L_d -= R_d;
+				break;
+			case '*':
+				L_d *= R_d;
+				break;
+			case '/':
+				if (R_d == 0)
+					syntax_error(EMSG_DIV_BY_ZERO);
+				L_d /= R_d;
+				break;
+			case '&':
+				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;
+				break;
+			}
+			debug_printf_eval("BINARY/REPLACE result:%f\n", L_d);
+			res = setvar_i(((opinfo & OPCLSMASK) == OC_BINARY) ? res : L.v, L_d);
+			break;
+		}
+
+		case XC( OC_COMPARE ): {
+			int i = i; /* for compiler */
+			double Ld;
+
+			if (is_numeric(L.v) && is_numeric(R.v)) {
+				Ld = getvar_i(L.v) - getvar_i(R.v);
+			} else {
+				const char *l = getvar_s(L.v);
+				const char *r = getvar_s(R.v);
+				Ld = icase ? strcasecmp(l, r) : strcmp(l, r);
+			}
+			switch (opn & 0xfe) {
+			case 0:
+				i = (Ld > 0);
+				break;
+			case 2:
+				i = (Ld >= 0);
+				break;
+			case 4:
+				i = (Ld == 0);
+				break;
+			}
+			setvar_i(res, (i == 0) ^ (opn & 1));
+			break;
+		}
+
+		default:
+			syntax_error(EMSG_POSSIBLE_ERROR);
+		}
+		if ((opinfo & OPCLSMASK) <= SHIFT_TIL_THIS)
+			op = op->a.n;
+		if ((opinfo & OPCLSMASK) >= RECUR_FROM_THIS)
+			break;
+		if (nextrec)
+			break;
+	} /* while (op) */
+
+	nvfree(v1);
+	debug_printf_eval("returning from %s(): %p\n", __func__, res);
+	return res;
+#undef fnargs
+#undef seed
+#undef sreg
+}
+
+
+/* -------- main & co. -------- */
+
+static int awk_exit(int r)
+{
+	var tv;
+	unsigned i;
+	hash_item *hi;
+
+	zero_out_var(&tv);
+
+	if (!exiting) {
+		exiting = TRUE;
+		nextrec = FALSE;
+		evaluate(endseq.first, &tv);
+	}
+
+	/* waiting for children */
+	for (i = 0; i < fdhash->csize; i++) {
+		hi = fdhash->items[i];
+		while (hi) {
+			if (hi->data.rs.F && hi->data.rs.is_pipe)
+				pclose(hi->data.rs.F);
+			hi = hi->next;
+		}
+	}
+
+	exit(r);
+}
+
+/* if expr looks like "var=value", perform assignment and return 1,
+ * otherwise return 0 */
+static int is_assignment(const char *expr)
+{
+	char *exprc, *val, *s, *s1;
+
+	if (!isalnum_(*expr) || (val = strchr(expr, '=')) == NULL) {
+		return FALSE;
+	}
+
+	exprc = xstrdup(expr);
+	val = exprc + (val - expr);
+	*val++ = '\0';
+
+	s = s1 = val;
+	while ((*s1 = nextchar(&s)) != '\0')
+		s1++;
+
+	setvar_u(newvar(exprc), val);
+	free(exprc);
+	return TRUE;
+}
+
+/* switch to next input file */
+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;
+	const char *fname, *ind;
+
+	if (rsm.F)
+		fclose(rsm.F);
+	rsm.F = NULL;
+	rsm.pos = rsm.adv = 0;
+
+	do {
+		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);
+		}
+	} while (!F);
+
+	files_happen = TRUE;
+	setvar_s(intvar[FILENAME], fname);
+	rsm.F = F;
+	return &rsm;
+#undef rsm
+#undef files_happen
+}
+
+int awk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int awk_main(int argc, char **argv)
+{
+	unsigned opt;
+	char *opt_F, *opt_W;
+	llist_t *list_v = NULL;
+	llist_t *list_f = NULL;
+	int i, j;
+	var *v;
+	var tv;
+	char **envp;
+	char *vnames = (char *)vNames; /* cheat */
+	char *vvalues = (char *)vValues;
+
+	INIT_G();
+
+	/* Undo busybox.c, or else strtod may eat ','! This breaks parsing:
+	 * $1,$2 == '$1,' '$2', NOT '$1' ',' '$2' */
+	if (ENABLE_LOCALE_SUPPORT)
+		setlocale(LC_NUMERIC, "C");
+
+	zero_out_var(&tv);
+
+	/* allocate global buffer */
+	g_buf = xmalloc(MAXVARFMT + 1);
+
+	vhash = hash_init();
+	ahash = hash_init();
+	fdhash = hash_init();
+	fnhash = hash_init();
+
+	/* initialize variables */
+	for (i = 0; *vnames; i++) {
+		intvar[i] = v = newvar(nextword(&vnames));
+		if (*vvalues != '\377')
+			setvar_s(v, nextword(&vvalues));
+		else
+			setvar_i(v, 0);
+
+		if (*vnames == '*') {
+			v->type |= VF_SPECIAL;
+			vnames++;
+		}
+	}
+
+	handle_special(intvar[FS]);
+	handle_special(intvar[RS]);
+
+	newfile("/dev/stdin")->F = stdin;
+	newfile("/dev/stdout")->F = stdout;
+	newfile("/dev/stderr")->F = stderr;
+
+	/* Huh, people report that sometimes environ is NULL. Oh well. */
+	if (environ) for (envp = environ; *envp; envp++) {
+		/* environ is writable, thus we don't strdup it needlessly */
+		char *s = *envp;
+		char *s1 = strchr(s, '=');
+		if (s1) {
+			*s1 = '\0';
+			/* Both findvar and setvar_u take const char*
+			 * as 2nd arg -> environment is not trashed */
+			setvar_u(findvar(iamarray(intvar[ENVIRON]), s), s1 + 1);
+			*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);
+	argv += optind;
+	argc -= optind;
+	if (opt & 0x1)
+		setvar_s(intvar[FS], opt_F); // -F
+	while (list_v) { /* -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)
+			bb_show_usage();
+		g_progname = "cmd. line";
+		parse_program(*argv++);
+	}
+	if (opt & 0x8) // -W
+		bb_error_msg("warning: unrecognized option '-W %s' ignored", opt_W);
+
+	/* fill in ARGV array */
+	setvar_i(intvar[ARGC], argc);
+	setari_u(intvar[ARGV], 0, "awk");
+	i = 0;
+	while (*argv)
+		setari_u(intvar[ARGV], ++i, *argv++);
+
+	evaluate(beginseq.first, &tv);
+	if (!mainseq.first && !endseq.first)
+		awk_exit(EXIT_SUCCESS);
+
+	/* input file could already be opened in BEGIN block */
+	if (!iF)
+		iF = next_input_file();
+
+	/* passing through input files */
+	while (iF) {
+		nextfile = FALSE;
+		setvar_i(intvar[FNR], 0);
+
+		while ((i = awk_getline(iF, intvar[F0])) > 0) {
+			nextrec = FALSE;
+			incvar(intvar[NR]);
+			incvar(intvar[FNR]);
+			evaluate(mainseq.first, &tv);
+
+			if (nextfile)
+				break;
+		}
+
+		if (i < 0)
+			syntax_error(strerror(errno));
+
+		iF = next_input_file();
+	}
+
+	awk_exit(EXIT_SUCCESS);
+	/*return 0;*/
+}
diff --git a/busybox-1.19.3/editors/cmp.c b/busybox-1.19.3/editors/cmp.c
new file mode 100644
index 0000000..fbe6b97
--- /dev/null
+++ b/busybox-1.19.3/editors/cmp.c
@@ -0,0 +1,130 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini cmp implementation for busybox
+ *
+ * Copyright (C) 2000,2001 by Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * 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 */
+
+//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";
+static const char fmt_differ[] ALIGN1 = "%s %s differ: char %"OFF_FMT"u, line %u\n";
+// This fmt_l_opt uses gnu-isms.  SUSv3 would be "%.0s%.0s%"OFF_FMT"u %o %o\n"
+static const char fmt_l_opt[] ALIGN1 = "%.0s%.0s%"OFF_FMT"u %3o %3o\n";
+
+static const char opt_chars[] ALIGN1 = "sl";
+#define CMP_OPT_s (1<<0)
+#define CMP_OPT_l (1<<1)
+
+int cmp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cmp_main(int argc UNUSED_PARAM, char **argv)
+{
+	FILE *fp1, *fp2, *outfile = stdout;
+	const char *filename1, *filename2 = "-";
+	off_t skip1 = 0, skip2 = 0, char_pos = 0;
+	int line_pos = 1; /* Hopefully won't overflow... */
+	const char *fmt;
+	int c1, c2;
+	unsigned opt;
+	int retval = 0;
+
+	opt_complementary = "-1"
+			IF_DESKTOP(":?4")
+			IF_NOT_DESKTOP(":?2")
+			":l--s:s--l";
+	opt = getopt32(argv, opt_chars);
+	argv += optind;
+
+	filename1 = *argv;
+	if (*++argv) {
+		filename2 = *argv;
+		if (ENABLE_DESKTOP && *++argv) {
+			skip1 = XATOOFF(*argv);
+			if (*++argv) {
+				skip2 = XATOOFF(*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.
+		 * But perhaps we should, so that other apps down the chain don't
+		 * get the input.  Consider 'echo hello | (cmp - - && cat -)'.
+		 */
+		return 0;
+	}
+	logmode = LOGMODE_STDIO;
+
+	if (opt & CMP_OPT_l)
+		fmt = fmt_l_opt;
+	else
+		fmt = fmt_differ;
+
+	if (ENABLE_DESKTOP) {
+		while (skip1) { getc(fp1); skip1--; }
+		while (skip2) { getc(fp2); skip2--; }
+	}
+	do {
+		c1 = getc(fp1);
+		c2 = getc(fp2);
+		++char_pos;
+		if (c1 != c2) {			/* Remember: a read error may have occurred. */
+			retval = 1;		/* But assume the files are different for now. */
+			if (c2 == EOF) {
+				/* We know that fp1 isn't at EOF or in an error state.  But to
+				 * save space below, things are setup to expect an EOF in fp1
+				 * if an EOF occurred.  So, swap things around.
+				 */
+				fp1 = fp2;
+				filename1 = filename2;
+				c1 = c2;
+			}
+			if (c1 == EOF) {
+				die_if_ferror(fp1, filename1);
+				fmt = fmt_eof;	/* Well, no error, so it must really be EOF. */
+				outfile = stderr;
+				/* There may have been output to stdout (option -l), so
+				 * make sure we fflush before writing to stderr. */
+				fflush_all();
+			}
+			if (!(opt & CMP_OPT_s)) {
+				if (opt & CMP_OPT_l) {
+					line_pos = c1;	/* line_pos is unused in the -l case. */
+				}
+				fprintf(outfile, fmt, filename1, filename2, char_pos, line_pos, c2);
+				if (opt) {	/* This must be -l since not -s. */
+					/* If we encountered an EOF,
+					 * the while check will catch it. */
+					continue;
+				}
+			}
+			break;
+		}
+		if (c1 == '\n') {
+			++line_pos;
+		}
+	} while (c1 != EOF);
+
+	die_if_ferror(fp1, filename1);
+	die_if_ferror(fp2, filename2);
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/busybox-1.19.3/editors/diff.c b/busybox-1.19.3/editors/diff.c
new file mode 100644
index 0000000..3a33346
--- /dev/null
+++ b/busybox-1.19.3/editors/diff.c
@@ -0,0 +1,1025 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini diff implementation for busybox, adapted from OpenBSD diff.
+ *
+ * Copyright (C) 2010 by Matheus Izvekov <mizvekov@gmail.com>
+ * Copyright (C) 2006 by Robert Sullivan <cogito.ergo.cogito@hotmail.com>
+ * Copyright (c) 2003 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * 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 source tree.
+ */
+
+/*
+ * The following code uses an algorithm due to Harold Stone,
+ * which finds a pair of longest identical subsequences in
+ * the two files.
+ *
+ * The major goal is to generate the match vector J.
+ * J[i] is the index of the line in file1 corresponding
+ * to line i in file0. J[i] = 0 if there is no
+ * such line in file1.
+ *
+ * Lines are hashed so as to work in core. All potential
+ * matches are located by sorting the lines of each file
+ * on the hash (called "value"). In particular, this
+ * collects the equivalence classes in file1 together.
+ * Subroutine equiv replaces the value of each line in
+ * file0 by the index of the first element of its
+ * matching equivalence in (the reordered) file1.
+ * To save space equiv squeezes file1 into a single
+ * array member in which the equivalence classes
+ * are simply concatenated, except that their first
+ * members are flagged by changing sign.
+ *
+ * Next the indices that point into member are unsorted into
+ * array class according to the original order of file0.
+ *
+ * The cleverness lies in routine stone. This marches
+ * through the lines of file0, developing a vector klist
+ * of "k-candidates". At step i a k-candidate is a matched
+ * pair of lines x,y (x in file0, y in file1) such that
+ * there is a common subsequence of length k
+ * between the first i lines of file0 and the first y
+ * lines of file1, but there is no such subsequence for
+ * any smaller y. x is the earliest possible mate to y
+ * that occurs in such a subsequence.
+ *
+ * Whenever any of the members of the equivalence class of
+ * lines in file1 matable to a line in file0 has serial number
+ * less than the y of some k-candidate, that k-candidate
+ * with the smallest such y is replaced. The new
+ * k-candidate is chained (via pred) to the current
+ * k-1 candidate so that the actual subsequence can
+ * be recovered. When a member has serial number greater
+ * that the y of all k-candidates, the klist is extended.
+ * At the end, the longest subsequence is pulled out
+ * and placed in the array J by unravel
+ *
+ * With J in hand, the matches there recorded are
+ * checked against reality to assure that no spurious
+ * matches have crept in due to hashing. If they have,
+ * they are broken, and "jackpot" is recorded--a harmless
+ * matter except that a true match for a spuriously
+ * mated line may now be unnecessarily reported as a change.
+ *
+ * Much of the complexity of the program comes simply
+ * from trying to minimize core utilization and
+ * maximize the range of doable problems by dynamically
+ * allocating what is needed and reusing what is not.
+ * The core requirements for problems larger than somewhat
+ * are (in words) 2*length(file0) + length(file1) +
+ * 3*(number of k-candidates installed), typically about
+ * 6n words for files of length n.
+ */
+
+//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__)
+#else
+# define dbg_error_msg(...) ((void)0)
+#endif
+
+enum {                  /* print_status() and diffreg() return values */
+	STATUS_SAME,    /* files are the same */
+	STATUS_DIFFER,  /* files differ */
+	STATUS_BINARY,  /* binary files differ */
+};
+
+enum {                  /* Commandline flags */
+	FLAG_a,
+	FLAG_b,
+	FLAG_d,
+	FLAG_i,
+	FLAG_L,         /* never used, handled by getopt32 */
+	FLAG_N,
+	FLAG_q,
+	FLAG_r,
+	FLAG_s,
+	FLAG_S,         /* never used, handled by getopt32 */
+	FLAG_t,
+	FLAG_T,
+	FLAG_U,         /* never used, handled by getopt32 */
+	FLAG_w,
+	FLAG_u,         /* ignored, this is the default */
+	FLAG_p,         /* not implemented */
+	FLAG_B,
+	FLAG_E,         /* not implemented */
+};
+#define FLAG(x) (1 << FLAG_##x)
+
+/* We cache file position to avoid excessive seeking */
+typedef struct FILE_and_pos_t {
+	FILE *ft_fp;
+	off_t ft_pos;
+} FILE_and_pos_t;
+
+struct globals {
+	smallint exit_status;
+	int opt_U_context;
+	const char *other_dir;
+	char *label[2];
+	struct stat stb[2];
+};
+#define G (*ptr_to_globals)
+#define exit_status        (G.exit_status       )
+#define opt_U_context      (G.opt_U_context     )
+#define label              (G.label             )
+#define stb                (G.stb               )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	opt_U_context = 3; \
+} while (0)
+
+typedef int token_t;
+
+enum {
+	/* Public */
+	TOK_EMPTY = 1 << 9,  /* Line fully processed, you can proceed to the next */
+	TOK_EOF   = 1 << 10, /* File ended */
+	/* Private (Only to be used by read_token() */
+	TOK_EOL   = 1 << 11, /* we saw EOL (sticky) */
+	TOK_SPACE = 1 << 12, /* used -b code, means we are skipping spaces */
+	SHIFT_EOF = (sizeof(token_t)*8 - 8) - 1,
+	CHAR_MASK = 0x1ff,   /* 8th bit is used to distinguish EOF from 0xff */
+};
+
+/* Restores full EOF from one 8th bit: */
+//#define TOK2CHAR(t) (((t) << SHIFT_EOF) >> SHIFT_EOF)
+/* We don't really need the above, we only need to have EOF != any_real_char: */
+#define TOK2CHAR(t) ((t) & CHAR_MASK)
+
+static void seek_ft(FILE_and_pos_t *ft, off_t pos)
+{
+	if (ft->ft_pos != pos) {
+		ft->ft_pos = pos;
+		fseeko(ft->ft_fp, pos, SEEK_SET);
+	}
+}
+
+/* Reads tokens from given fp, handling -b and -w flags
+ * The user must reset tok every line start
+ */
+static int read_token(FILE_and_pos_t *ft, token_t tok)
+{
+	tok |= TOK_EMPTY;
+	while (!(tok & TOK_EOL)) {
+		bool is_space;
+		int t;
+
+		t = fgetc(ft->ft_fp);
+		if (t != EOF)
+			ft->ft_pos++;
+		is_space = (t == EOF || isspace(t));
+
+		/* If t == EOF (-1), set both TOK_EOF and TOK_EOL */
+		tok |= (t & (TOK_EOF + TOK_EOL));
+		/* Only EOL? */
+		if (t == '\n')
+			tok |= TOK_EOL;
+
+		if (option_mask32 & FLAG(i)) /* Handcoded tolower() */
+			t = (t >= 'A' && t <= 'Z') ? t - ('A' - 'a') : t;
+
+		if ((option_mask32 & FLAG(w)) && is_space)
+			continue;
+
+		/* Trim char value to low 9 bits */
+		t &= CHAR_MASK;
+
+		if (option_mask32 & FLAG(b)) {
+			/* Was prev char whitespace? */
+			if (tok & TOK_SPACE) { /* yes */
+				if (is_space) /* this one too, ignore it */
+					continue;
+				tok &= ~TOK_SPACE;
+			} else if (is_space) {
+				/* 1st whitespace char.
+				 * Set TOK_SPACE and replace char by ' ' */
+				t = TOK_SPACE + ' ';
+			}
+		}
+		/* Clear EMPTY */
+		tok &= ~(TOK_EMPTY + CHAR_MASK);
+		/* Assign char value (low 9 bits) and maybe set TOK_SPACE */
+		tok |= t;
+		break;
+	}
+#if 0
+	bb_error_msg("fp:%p tok:%x '%c'%s%s%s%s", fp, tok, tok & 0xff
+		, tok & TOK_EOF ? " EOF" : ""
+		, tok & TOK_EOL ? " EOL" : ""
+		, tok & TOK_EMPTY ? " EMPTY" : ""
+		, tok & TOK_SPACE ? " SPACE" : ""
+	);
+#endif
+	return tok;
+}
+
+struct cand {
+	int x;
+	int y;
+	int pred;
+};
+
+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 */
+		return k + 1;
+
+	for (i = 0, j = k + 1;;) {
+		const int l = (i + j) >> 1;
+		if (l > i) {
+			const int t = list[c[l]].y;
+			if (t > y)
+				j = l;
+			else if (t < y)
+				i = l;
+			else
+				return l;
+		} else
+			return l + 1;
+	}
+}
+
+static unsigned isqrt(unsigned n)
+{
+	unsigned x = 1;
+	while (1) {
+		const unsigned y = x;
+		x = ((n / x) + x) >> 1;
+		if (x <= (y + 1) && x >= (y - 1))
+			return x;
+	}
+}
+
+static void stone(const int *a, int n, const int *b, int *J, int pref)
+{
+	const unsigned isq = isqrt(n);
+	const unsigned bound =
+		(option_mask32 & FLAG(d)) ? UINT_MAX : MAX(256, isq);
+	int clen = 1;
+	int clistlen = 100;
+	int k = 0;
+	struct cand *clist = xzalloc(clistlen * sizeof(clist[0]));
+	struct cand cand;
+	struct cand *q;
+	int *klist = xzalloc((n + 2) * sizeof(klist[0]));
+	/*clist[0] = (struct cand){0}; - xzalloc did it */
+	/*klist[0] = 0; */
+
+	for (cand.x = 1; cand.x <= n; cand.x++) {
+		int j = a[cand.x], oldl = 0;
+		unsigned numtries = 0;
+		if (j == 0)
+			continue;
+		cand.y = -b[j];
+		cand.pred = klist[0];
+		do {
+			int l, tc;
+			if (cand.y <= clist[cand.pred].y)
+				continue;
+			l = search(klist, k, cand.y, clist);
+			if (l != oldl + 1)
+				cand.pred = klist[l - 1];
+			if (l <= k && clist[klist[l]].y <= cand.y)
+				continue;
+			if (clen == clistlen) {
+				clistlen = clistlen * 11 / 10;
+				clist = xrealloc(clist, clistlen * sizeof(clist[0]));
+			}
+			clist[clen] = cand;
+			tc = klist[l];
+			klist[l] = clen++;
+			if (l <= k) {
+				cand.pred = tc;
+				oldl = l;
+				numtries++;
+			} else {
+				k++;
+				break;
+			}
+		} while ((cand.y = b[++j]) > 0 && numtries < bound);
+	}
+	/* Unravel */
+	for (q = clist + klist[k]; q->y; q = clist + q->pred)
+		J[q->x + pref] = q->y + pref;
+	free(klist);
+	free(clist);
+}
+
+struct line {
+	/* 'serial' is not used in the begining, so we reuse it
+	 * to store line offsets, thus reducing memory pressure
+	 */
+	union {
+		unsigned serial;
+		off_t offset;
+	};
+	unsigned value;
+};
+
+static void equiv(struct line *a, int n, struct line *b, int m, int *c)
+{
+	int i = 1, j = 1;
+
+	while (i <= n && j <= m) {
+		if (a[i].value < b[j].value)
+			a[i++].value = 0;
+		else if (a[i].value == b[j].value)
+			a[i++].value = j;
+		else
+			j++;
+	}
+	while (i <= n)
+		a[i++].value = 0;
+	b[m + 1].value = 0;
+	j = 0;
+	while (++j <= m) {
+		c[j] = -b[j].serial;
+		while (b[j + 1].value == b[j].value) {
+			j++;
+			c[j] = b[j].serial;
+		}
+	}
+	c[j] = -1;
+}
+
+static void unsort(const struct line *f, int l, int *b)
+{
+	int i;
+	int *a = xmalloc((l + 1) * sizeof(a[0]));
+	for (i = 1; i <= l; i++)
+		a[f[i].serial] = f[i].value;
+	for (i = 1; i <= l; i++)
+		b[i] = a[i];
+	free(a);
+}
+
+static int line_compar(const void *a, const void *b)
+{
+#define l0 ((const struct line*)a)
+#define l1 ((const struct line*)b)
+	int r = l0->value - l1->value;
+	if (r)
+		return r;
+	return l0->serial - l1->serial;
+#undef l0
+#undef l1
+}
+
+static void fetch(FILE_and_pos_t *ft, const off_t *ix, int a, int b, int ch)
+{
+	int i, j, col;
+	for (i = a; i <= b; i++) {
+		seek_ft(ft, ix[i - 1]);
+		putchar(ch);
+		if (option_mask32 & FLAG(T))
+			putchar('\t');
+		for (j = 0, col = 0; j < ix[i] - ix[i - 1]; j++) {
+			int c = fgetc(ft->ft_fp);
+			if (c == EOF) {
+				printf("\n\\ No newline at end of file\n");
+				return;
+			}
+			ft->ft_pos++;
+			if (c == '\t' && (option_mask32 & FLAG(t)))
+				do putchar(' '); while (++col & 7);
+			else {
+				putchar(c);
+				col++;
+			}
+		}
+	}
+}
+
+/* Creates the match vector J, where J[i] is the index
+ * of the line in the new file corresponding to the line i
+ * in the old file. Lines start at 1 instead of 0, that value
+ * being used instead to denote no corresponding line.
+ * This vector is dynamically allocated and must be freed by the caller.
+ *
+ * * fp is an input parameter, where fp[0] and fp[1] are the open
+ *   old file and new file respectively.
+ * * nlen is an output variable, where nlen[0] and nlen[1]
+ *   gets the number of lines in the old and new file respectively.
+ * * ix is an output variable, where ix[0] and ix[1] gets
+ *   assigned dynamically allocated vectors of the offsets of the lines
+ *   of the old and new file respectively. These must be freed by the caller.
+ */
+static NOINLINE int *create_J(FILE_and_pos_t ft[2], int nlen[2], off_t *ix[2])
+{
+	int *J, slen[2], *class, *member;
+	struct line *nfile[2], *sfile[2];
+	int pref = 0, suff = 0, i, j, delta;
+
+	/* Lines of both files are hashed, and in the process
+	 * their offsets are stored in the array ix[fileno]
+	 * where fileno == 0 points to the old file, and
+	 * fileno == 1 points to the new one.
+	 */
+	for (i = 0; i < 2; i++) {
+		unsigned hash;
+		token_t tok;
+		size_t sz = 100;
+		nfile[i] = xmalloc((sz + 3) * sizeof(nfile[i][0]));
+		/* ft gets here without the correct position, cant use seek_ft */
+		ft[i].ft_pos = 0;
+		fseeko(ft[i].ft_fp, 0, SEEK_SET);
+
+		nlen[i] = 0;
+		/* We could zalloc nfile, but then zalloc starts showing in gprof at ~1% */
+		nfile[i][0].offset = 0;
+		goto start; /* saves code */
+		while (1) {
+			tok = read_token(&ft[i], tok);
+			if (!(tok & TOK_EMPTY)) {
+				/* Hash algorithm taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. */
+				/*hash = hash * 128 - hash + TOK2CHAR(tok);
+				 * gcc insists on optimizing above to "hash * 127 + ...", thus... */
+				unsigned o = hash - TOK2CHAR(tok);
+				hash = hash * 128 - o; /* we want SPEED here */
+				continue;
+			}
+			if (nlen[i]++ == sz) {
+				sz = sz * 3 / 2;
+				nfile[i] = xrealloc(nfile[i], (sz + 3) * sizeof(nfile[i][0]));
+			}
+			/* line_compar needs hashes fit into positive int */
+			nfile[i][nlen[i]].value = hash & INT_MAX;
+			/* like ftello(ft[i].ft_fp) but faster (avoids lseek syscall) */
+			nfile[i][nlen[i]].offset = ft[i].ft_pos;
+			if (tok & TOK_EOF) {
+				/* EOF counts as a token, so we have to adjust it here */
+				nfile[i][nlen[i]].offset++;
+				break;
+			}
+start:
+			hash = tok = 0;
+		}
+		/* Exclude lone EOF line from the end of the file, to make fetch()'s job easier */
+		if (nfile[i][nlen[i]].offset - nfile[i][nlen[i] - 1].offset == 1)
+			nlen[i]--;
+		/* Now we copy the line offsets into ix */
+		ix[i] = xmalloc((nlen[i] + 2) * sizeof(ix[i][0]));
+		for (j = 0; j < nlen[i] + 1; j++)
+			ix[i][j] = nfile[i][j].offset;
+	}
+
+	/* length of prefix and suffix is calculated */
+	for (; pref < nlen[0] && pref < nlen[1] &&
+	       nfile[0][pref + 1].value == nfile[1][pref + 1].value;
+	       pref++);
+	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 length,
+	 * the result being sorted and stored in sfile[fileno],
+	 * and their sizes are stored in slen[fileno]
+	 */
+	for (j = 0; j < 2; j++) {
+		sfile[j] = nfile[j] + pref;
+		slen[j] = nlen[j] - pref - suff;
+		for (i = 0; i <= slen[j]; i++)
+			sfile[j][i].serial = i;
+		qsort(sfile[j] + 1, slen[j], sizeof(*sfile[j]), line_compar);
+	}
+	/* nfile arrays are reused to reduce memory pressure
+	 * The #if zeroed out section performs the same task as the
+	 * one in the #else section.
+	 * Peak memory usage is higher, but one array copy is avoided
+	 * by not using unsort()
+	 */
+#if 0
+	member = xmalloc((slen[1] + 2) * sizeof(member[0]));
+	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+	free(nfile[1]);
+
+	class = xmalloc((slen[0] + 1) * sizeof(class[0]));
+	for (i = 1; i <= slen[0]; i++) /* Unsorting */
+		class[sfile[0][i].serial] = sfile[0][i].value;
+	free(nfile[0]);
+#else
+	member = (int *)nfile[1];
+	equiv(sfile[0], slen[0], sfile[1], slen[1], member);
+	member = xrealloc(member, (slen[1] + 2) * sizeof(member[0]));
+
+	class = (int *)nfile[0];
+	unsort(sfile[0], slen[0], (int *)nfile[0]);
+	class = xrealloc(class, (slen[0] + 2) * sizeof(class[0]));
+#endif
+	J = xmalloc((nlen[0] + 2) * sizeof(J[0]));
+	/* The elements of J which fall inside the prefix and suffix regions
+	 * are marked as unchanged, while the ones which fall outside
+	 * are initialized with 0 (no matches), so that function stone can
+	 * then assign them their right values
+	 */
+	for (i = 0, delta = nlen[1] - nlen[0]; i <= nlen[0]; i++)
+		J[i] = i <= pref            ?  i :
+		       i > (nlen[0] - suff) ? (i + delta) : 0;
+	/* Here the magic is performed */
+	stone(class, slen[0], member, J, pref);
+	J[nlen[0] + 1] = nlen[1] + 1;
+
+	free(class);
+	free(member);
+
+	/* Both files are rescanned, in an effort to find any lines
+	 * which, due to limitations intrinsic to any hashing algorithm,
+	 * are different but ended up confounded as the same
+	 */
+	for (i = 1; i <= nlen[0]; i++) {
+		if (!J[i])
+			continue;
+
+		seek_ft(&ft[0], ix[0][i - 1]);
+		seek_ft(&ft[1], ix[1][J[i] - 1]);
+
+		for (j = J[i]; i <= nlen[0] && J[i] == j; i++, j++) {
+			token_t tok0 = 0, tok1 = 0;
+			do {
+				tok0 = read_token(&ft[0], tok0);
+				tok1 = read_token(&ft[1], tok1);
+
+				if (((tok0 ^ tok1) & TOK_EMPTY) != 0 /* one is empty (not both) */
+				 || (!(tok0 & TOK_EMPTY) && TOK2CHAR(tok0) != TOK2CHAR(tok1))
+				) {
+					J[i] = 0; /* Break the correspondence */
+				}
+			} while (!(tok0 & tok1 & TOK_EMPTY));
+		}
+	}
+
+	return J;
+}
+
+static bool diff(FILE* fp[2], char *file[2])
+{
+	int nlen[2];
+	off_t *ix[2];
+	FILE_and_pos_t ft[2];
+	typedef struct { int a, b; } vec_t[2];
+	vec_t *vec = NULL;
+	int i = 1, j, k, idx = -1;
+	bool anychange = false;
+	int *J;
+
+	ft[0].ft_fp = fp[0];
+	ft[1].ft_fp = fp[1];
+	/* note that ft[i].ft_pos is unintitalized, create_J()
+	 * must not assume otherwise */
+	J = create_J(ft, nlen, ix);
+
+	do {
+		bool nonempty = false;
+
+		while (1) {
+			vec_t v;
+
+			for (v[0].a = i; v[0].a <= nlen[0] && J[v[0].a] == J[v[0].a - 1] + 1; v[0].a++)
+				continue;
+			v[1].a = J[v[0].a - 1] + 1;
+
+			for (v[0].b = v[0].a - 1; v[0].b < nlen[0] && !J[v[0].b + 1]; v[0].b++)
+				continue;
+			v[1].b = J[v[0].b + 1] - 1;
+			/*
+			 * Indicate that there is a difference between lines a and b of the 'from' file
+			 * to get to lines c to d of the 'to' file. If a is greater than b then there
+			 * are no lines in the 'from' file involved and this means that there were
+			 * lines appended (beginning at b).  If c is greater than d then there are
+			 * lines missing from the 'to' file.
+			 */
+			if (v[0].a <= v[0].b || v[1].a <= v[1].b) {
+				/*
+				 * If this change is more than 'context' lines from the
+				 * previous change, dump the record and reset it.
+				 */
+				int ct = (2 * opt_U_context) + 1;
+				if (idx >= 0
+				 && v[0].a > vec[idx][0].b + ct
+				 && v[1].a > vec[idx][1].b + ct
+				) {
+					break;
+				}
+
+				for (j = 0; j < 2; j++)
+					for (k = v[j].a; k < v[j].b; k++)
+						nonempty |= (ix[j][k+1] - ix[j][k] != 1);
+
+				vec = xrealloc_vector(vec, 6, ++idx);
+				memcpy(vec[idx], v, sizeof(v));
+			}
+
+			i = v[0].b + 1;
+			if (i > nlen[0])
+				break;
+			J[v[0].b] = v[1].b;
+		}
+		if (idx < 0 || ((option_mask32 & FLAG(B)) && !nonempty))
+			goto cont;
+		if (!(option_mask32 & FLAG(q))) {
+			int lowa;
+			vec_t span, *cvp = vec;
+
+			if (!anychange) {
+				/* Print the context/unidiff header first time through */
+				printf("--- %s\n", label[0] ? label[0] : file[0]);
+				printf("+++ %s\n", label[1] ? label[1] : file[1]);
+			}
+
+			printf("@@");
+			for (j = 0; j < 2; j++) {
+				int a = span[j].a = MAX(1, (*cvp)[j].a - opt_U_context);
+				int b = span[j].b = MIN(nlen[j], vec[idx][j].b + opt_U_context);
+
+				printf(" %c%d", j ? '+' : '-', MIN(a, b));
+				if (a == b)
+					continue;
+				printf(",%d", (a < b) ? b - a + 1 : 0);
+			}
+			printf(" @@\n");
+			/*
+			 * Output changes in "unified" diff format--the old and new lines
+			 * are printed together.
+			 */
+			for (lowa = span[0].a; ; lowa = (*cvp++)[0].b + 1) {
+				bool end = cvp > &vec[idx];
+				fetch(&ft[0], ix[0], lowa, end ? span[0].b : (*cvp)[0].a - 1, ' ');
+				if (end)
+					break;
+				for (j = 0; j < 2; j++)
+					fetch(&ft[j], ix[j], (*cvp)[j].a, (*cvp)[j].b, j ? '+' : '-');
+			}
+		}
+		anychange = true;
+ cont:
+		idx = -1;
+	} while (i <= nlen[0]);
+
+	free(vec);
+	free(ix[0]);
+	free(ix[1]);
+	free(J);
+	return anychange;
+}
+
+static int diffreg(char *file[2])
+{
+	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)
+			goto out;
+		/* Our diff implementation is using seek.
+		 * When we meet non-seekable file, we must make a temp copy.
+		 */
+		if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) {
+			char name[] = "/tmp/difXXXXXX";
+			int fd_tmp = xmkstemp(name);
+
+			unlink(name);
+			if (bb_copyfd_eof(fd, fd_tmp) < 0)
+				xfunc_die();
+			if (fd) /* Prevents closing of stdin */
+				close(fd);
+			fd = fd_tmp;
+		}
+		fp[i] = fdopen(fd, "r");
+	}
+
+	while (1) {
+		const size_t sz = COMMON_BUFSIZE / 2;
+		char *const buf0 = bb_common_bufsiz1;
+		char *const buf1 = buf0 + sz;
+		int j, k;
+		i = fread(buf0, 1, sz, fp[0]);
+		j = fread(buf1, 1, sz, fp[1]);
+		if (i != j) {
+			differ = true;
+			i = MIN(i, j);
+		}
+		if (i == 0)
+			break;
+		for (k = 0; k < i; k++) {
+			if (!buf0[k] || !buf1[k])
+				binary = true;
+			if (buf0[k] != buf1[k])
+				differ = true;
+		}
+	}
+	if (differ) {
+		if (binary && !(option_mask32 & FLAG(a)))
+			status = STATUS_BINARY;
+		else if (diff(fp, file))
+			status = STATUS_DIFFER;
+	}
+	if (status != STATUS_SAME)
+		exit_status |= 1;
+out:
+	fclose_if_not_stdin(fp[0]);
+	fclose_if_not_stdin(fp[1]);
+
+	return status;
+}
+
+static void print_status(int status, char *path[2])
+{
+	switch (status) {
+	case STATUS_BINARY:
+	case STATUS_DIFFER:
+		if ((option_mask32 & FLAG(q)) || status == STATUS_BINARY)
+			printf("Files %s and %s differ\n", path[0], path[1]);
+		break;
+	case STATUS_SAME:
+		if (option_mask32 & FLAG(s))
+			printf("Files %s and %s are identical\n", path[0], path[1]);
+		break;
+	}
+}
+
+#if ENABLE_FEATURE_DIFF_DIR
+struct dlist {
+	size_t len;
+	int s, e;
+	char **dl;
+};
+
+/* This function adds a filename to dl, the directory listing. */
+static int FAST_FUNC add_to_dirlist(const char *filename,
+		struct stat *sb UNUSED_PARAM,
+		void *userdata, int depth UNUSED_PARAM)
+{
+	struct dlist *const l = userdata;
+	const char *file = filename + l->len;
+	while (*file == '/')
+		file++;
+	l->dl = xrealloc_vector(l->dl, 6, l->e);
+	l->dl[l->e] = xstrdup(file);
+	l->e++;
+	return TRUE;
+}
+
+/* If recursion is not set, this function adds the directory
+ * to the list and prevents recursive_action from recursing into it.
+ */
+static int FAST_FUNC skip_dir(const char *filename,
+		struct stat *sb, void *userdata,
+		int depth)
+{
+	if (!(option_mask32 & FLAG(r)) && depth) {
+		add_to_dirlist(filename, sb, userdata, depth);
+		return SKIP;
+	}
+	if (!(option_mask32 & FLAG(N))) {
+		/* -r without -N: no need to recurse into dirs
+		 * which do not exist on the "other side".
+		 * Testcase: diff -r /tmp /
+		 * (it would recurse deep into /proc without this code) */
+		struct dlist *const l = userdata;
+		filename += l->len;
+		if (filename[0]) {
+			struct stat osb;
+			char *othername = concat_path_file(G.other_dir, filename);
+			int r = stat(othername, &osb);
+			free(othername);
+			if (r != 0 || !S_ISDIR(osb.st_mode)) {
+				/* other dir doesn't have similarly named
+				 * directory, don't recurse; return 1 upon
+				 * exit, just like diffutils' diff */
+				exit_status |= 1;
+				return SKIP;
+			}
+		}
+	}
+	return TRUE;
+}
+
+static void diffdir(char *p[2], const char *s_start)
+{
+	struct dlist list[2];
+	int i;
+
+	memset(&list, 0, sizeof(list));
+	for (i = 0; i < 2; i++) {
+		/*list[i].s = list[i].e = 0; - memset did it */
+		/*list[i].dl = NULL; */
+
+		G.other_dir = p[1 - i];
+		/* We need to trim root directory prefix.
+		 * Using list.len to specify its length,
+		 * 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);
+		/* Sort dl alphabetically.
+		 * GNU diff does this ignoring any number of trailing dots.
+		 * We don't, so for us dotted files almost always are
+		 * first on the list.
+		 */
+		qsort_string_vector(list[i].dl, list[i].e);
+		/* If -S was set, find the starting point. */
+		if (!s_start)
+			continue;
+		while (list[i].s < list[i].e && strcmp(list[i].dl[list[i].s], s_start) < 0)
+			list[i].s++;
+	}
+	/* Now that both dirlist1 and dirlist2 contain sorted directory
+	 * listings, we can start to go through dirlist1. If both listings
+	 * contain the same file, then do a normal diff. Otherwise, behaviour
+	 * is determined by whether the -N flag is set. */
+	while (1) {
+		char *dp[2];
+		int pos;
+		int k;
+
+		dp[0] = list[0].s < list[0].e ? list[0].dl[list[0].s] : NULL;
+		dp[1] = list[1].s < list[1].e ? list[1].dl[list[1].s] : NULL;
+		if (!dp[0] && !dp[1])
+			break;
+		pos = !dp[0] ? 1 : (!dp[1] ? -1 : strcmp(dp[0], dp[1]));
+		k = pos > 0;
+		if (pos && !(option_mask32 & FLAG(N))) {
+			printf("Only in %s: %s\n", p[k], dp[k]);
+			exit_status |= 1;
+		} else {
+			char *fullpath[2], *path[2]; /* if -N */
+
+			for (i = 0; i < 2; i++) {
+				if (pos == 0 || i == k) {
+					path[i] = fullpath[i] = concat_path_file(p[i], dp[i]);
+					stat(fullpath[i], &stb[i]);
+				} else {
+					fullpath[i] = concat_path_file(p[i], dp[1 - i]);
+					path[i] = (char *)bb_dev_null;
+				}
+			}
+			if (pos)
+				stat(fullpath[k], &stb[1 - k]);
+
+			if (S_ISDIR(stb[0].st_mode) && S_ISDIR(stb[1].st_mode))
+				printf("Common subdirectories: %s and %s\n", fullpath[0], fullpath[1]);
+			else if (!S_ISREG(stb[0].st_mode) && !S_ISDIR(stb[0].st_mode))
+				printf("File %s is not a regular file or directory and was skipped\n", fullpath[0]);
+			else if (!S_ISREG(stb[1].st_mode) && !S_ISDIR(stb[1].st_mode))
+				printf("File %s is not a regular file or directory and was skipped\n", fullpath[1]);
+			else if (S_ISDIR(stb[0].st_mode) != S_ISDIR(stb[1].st_mode)) {
+				if (S_ISDIR(stb[0].st_mode))
+					printf("File %s is a %s while file %s is a %s\n", fullpath[0], "directory", fullpath[1], "regular file");
+				else
+					printf("File %s is a %s while file %s is a %s\n", fullpath[0], "regular file", fullpath[1], "directory");
+			} else
+				print_status(diffreg(path), fullpath);
+
+			free(fullpath[0]);
+			free(fullpath[1]);
+		}
+		free(dp[k]);
+		list[k].s++;
+		if (pos == 0) {
+			free(dp[1 - k]);
+			list[1 - k].s++;
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(list[0].dl);
+		free(list[1].dl);
+	}
+}
+#endif
+
+#if ENABLE_FEATURE_DIFF_LONG_OPTIONS
+static const char diff_longopts[] ALIGN1 =
+	"ignore-case\0"              No_argument       "i"
+	"ignore-tab-expansion\0"     No_argument       "E"
+	"ignore-space-change\0"      No_argument       "b"
+	"ignore-all-space\0"         No_argument       "w"
+	"ignore-blank-lines\0"       No_argument       "B"
+	"text\0"                     No_argument       "a"
+	"unified\0"                  Required_argument "U"
+	"label\0"                    Required_argument "L"
+	"show-c-function\0"          No_argument       "p"
+	"brief\0"                    No_argument       "q"
+	"expand-tabs\0"              No_argument       "t"
+	"initial-tab\0"              No_argument       "T"
+	"recursive\0"                No_argument       "r"
+	"new-file\0"                 No_argument       "N"
+	"report-identical-files\0"   No_argument       "s"
+	"starting-file\0"            Required_argument "S"
+	"minimal\0"                  No_argument       "d"
+	;
+#endif
+
+int diff_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int diff_main(int argc UNUSED_PARAM, char **argv)
+{
+	int gotstdin = 0, i;
+	char *file[2], *s_start = NULL;
+	llist_t *L_arg = NULL;
+
+	INIT_G();
+
+	/* exactly 2 params; collect multiple -L <label>; -U N */
+	opt_complementary = "=2:L::U+";
+#if ENABLE_FEATURE_DIFF_LONG_OPTIONS
+	applet_long_options = diff_longopts;
+#endif
+	getopt32(argv, "abdiL:NqrsS:tTU:wupBE",
+			&L_arg, &s_start, &opt_U_context);
+	argv += optind;
+	while (L_arg)
+		label[!!label[0]] = llist_pop(&L_arg);
+	xfunc_error_retval = 2;
+	for (i = 0; i < 2; i++) {
+		file[i] = argv[i];
+		/* Compat: "diff file name_which_doesnt_exist" exits with 2 */
+		if (LONE_DASH(file[i])) {
+			fstat(STDIN_FILENO, &stb[i]);
+			gotstdin++;
+		} else
+			xstat(file[i], &stb[i]);
+	}
+	xfunc_error_retval = 1;
+	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);
+#else
+		bb_error_msg_and_die("no support for directory comparison");
+#endif
+	} else {
+		bool dirfile = S_ISDIR(stb[0].st_mode) || S_ISDIR(stb[1].st_mode);
+		bool dir = S_ISDIR(stb[1].st_mode);
+		if (dirfile) {
+			const char *slash = strrchr(file[!dir], '/');
+			file[dir] = concat_path_file(file[dir], slash ? slash + 1 : file[!dir]);
+			xstat(file[dir], &stb[dir]);
+		}
+		/* diffreg can get non-regular files here */
+		print_status(gotstdin > 1 ? STATUS_SAME : diffreg(file), file);
+
+		if (dirfile)
+			free(file[dir]);
+	}
+
+	return exit_status;
+}
diff --git a/busybox-1.19.3/editors/ed.c b/busybox-1.19.3/editors/ed.c
new file mode 100644
index 0000000..dbb5130
--- /dev/null
+++ b/busybox-1.19.3/editors/ed.c
@@ -0,0 +1,1039 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (c) 2002 by David I. Bell
+ * Permission is granted to use, distribute, or modify this source,
+ * provided that this copyright notice remains intact.
+ *
+ * The "ed" built-in command (much simplified)
+ */
+
+//usage:#define ed_trivial_usage ""
+//usage:#define ed_full_usage ""
+
+#include "libbb.h"
+
+typedef struct LINE {
+	struct LINE *next;
+	struct LINE *prev;
+	int len;
+	char data[1];
+} LINE;
+
+
+#define searchString bb_common_bufsiz1
+
+enum {
+	USERSIZE = sizeof(searchString) > 1024 ? 1024
+	         : sizeof(searchString) - 1, /* max line length typed in by user */
+	INITBUF_SIZE = 1024, /* initial buffer size */
+};
+
+struct globals {
+	int curNum;
+	int lastNum;
+	int bufUsed;
+	int bufSize;
+	LINE *curLine;
+	char *bufBase;
+	char *bufPtr;
+	char *fileName;
+	LINE lines;
+	smallint dirty;
+	int marks[26];
+};
+#define G (*ptr_to_globals)
+#define curLine            (G.curLine           )
+#define bufBase            (G.bufBase           )
+#define bufPtr             (G.bufPtr            )
+#define fileName           (G.fileName          )
+#define curNum             (G.curNum            )
+#define lastNum            (G.lastNum           )
+#define bufUsed            (G.bufUsed           )
+#define bufSize            (G.bufSize           )
+#define dirty              (G.dirty             )
+#define lines              (G.lines             )
+#define marks              (G.marks             )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+static void doCommands(void);
+static void subCommand(const char *cmd, int num1, int num2);
+static int getNum(const char **retcp, smallint *retHaveNum, int *retNum);
+static int setCurNum(int num);
+static void addLines(int num);
+static int insertLine(int num, const char *data, int len);
+static void deleteLines(int num1, int num2);
+static int printLines(int num1, int num2, int expandFlag);
+static int writeLines(const char *file, int num1, int num2);
+static int readLines(const char *file, int num);
+static int searchLines(const char *str, int num1, int num2);
+static LINE *findLine(int num);
+static int findString(const LINE *lp, const char * str, int len, int offset);
+
+
+static int bad_nums(int num1, int num2, const char *for_what)
+{
+	if ((num1 < 1) || (num2 > lastNum) || (num1 > num2)) {
+		bb_error_msg("bad line range for %s", for_what);
+		return 1;
+	}
+	return 0;
+}
+
+
+static char *skip_blank(const char *cp)
+{
+	while (isblank(*cp))
+		cp++;
+	return (char *)cp;
+}
+
+
+int ed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ed_main(int argc UNUSED_PARAM, char **argv)
+{
+	INIT_G();
+
+	bufSize = INITBUF_SIZE;
+	bufBase = xmalloc(bufSize);
+	bufPtr = bufBase;
+	lines.next = &lines;
+	lines.prev = &lines;
+
+	if (argv[1]) {
+		fileName = xstrdup(argv[1]);
+		if (!readLines(fileName, 1)) {
+			return EXIT_SUCCESS;
+		}
+		if (lastNum)
+			setCurNum(1);
+		dirty = FALSE;
+	}
+
+	doCommands();
+	return EXIT_SUCCESS;
+}
+
+/*
+ * Read commands until we are told to stop.
+ */
+static void doCommands(void)
+{
+	const char *cp;
+	char *endbuf, buf[USERSIZE];
+	int len, num1, num2;
+	smallint have1, have2;
+
+	while (TRUE) {
+		/* Returns:
+		 * -1 on read errors or EOF, or on bare Ctrl-D.
+		 * 0  on ctrl-C,
+		 * >0 length of input string, including terminating '\n'
+		 */
+		len = read_line_input(NULL, ": ", buf, sizeof(buf), /*timeout*/ -1);
+		if (len <= 0)
+			return;
+		endbuf = &buf[len - 1];
+		while ((endbuf > buf) && isblank(endbuf[-1]))
+			endbuf--;
+		*endbuf = '\0';
+
+		cp = skip_blank(buf);
+		have1 = FALSE;
+		have2 = FALSE;
+
+		if ((curNum == 0) && (lastNum > 0)) {
+			curNum = 1;
+			curLine = lines.next;
+		}
+
+		if (!getNum(&cp, &have1, &num1))
+			continue;
+
+		cp = skip_blank(cp);
+
+		if (*cp == ',') {
+			cp++;
+			if (!getNum(&cp, &have2, &num2))
+				continue;
+			if (!have1)
+				num1 = 1;
+			if (!have2)
+				num2 = lastNum;
+			have1 = TRUE;
+			have2 = TRUE;
+		}
+		if (!have1)
+			num1 = curNum;
+		if (!have2)
+			num2 = num1;
+
+		switch (*cp++) {
+		case 'a':
+			addLines(num1 + 1);
+			break;
+
+		case 'c':
+			deleteLines(num1, num2);
+			addLines(num1);
+			break;
+
+		case 'd':
+			deleteLines(num1, num2);
+			break;
+
+		case 'f':
+			if (*cp && !isblank(*cp)) {
+				bb_error_msg("bad file command");
+				break;
+			}
+			cp = skip_blank(cp);
+			if (*cp == '\0') {
+				if (fileName)
+					printf("\"%s\"\n", fileName);
+				else
+					printf("No file name\n");
+				break;
+			}
+			free(fileName);
+			fileName = xstrdup(cp);
+			break;
+
+		case 'i':
+			addLines(num1);
+			break;
+
+		case 'k':
+			cp = skip_blank(cp);
+			if ((*cp < 'a') || (*cp > 'z') || cp[1]) {
+				bb_error_msg("bad mark name");
+				break;
+			}
+			marks[*cp - 'a'] = num2;
+			break;
+
+		case 'l':
+			printLines(num1, num2, TRUE);
+			break;
+
+		case 'p':
+			printLines(num1, num2, FALSE);
+			break;
+
+		case 'q':
+			cp = skip_blank(cp);
+			if (have1 || *cp) {
+				bb_error_msg("bad quit command");
+				break;
+			}
+			if (!dirty)
+				return;
+			len = read_line_input(NULL, "Really quit? ", buf, 16, /*timeout*/ -1);
+			/* read error/EOF - no way to continue */
+			if (len < 0)
+				return;
+			cp = skip_blank(buf);
+			if ((*cp | 0x20) == 'y') /* Y or y */
+				return;
+			break;
+
+		case 'r':
+			if (*cp && !isblank(*cp)) {
+				bb_error_msg("bad read command");
+				break;
+			}
+			cp = skip_blank(cp);
+			if (*cp == '\0') {
+				bb_error_msg("no file name");
+				break;
+			}
+			if (!have1)
+				num1 = lastNum;
+			if (readLines(cp, num1 + 1))
+				break;
+			if (fileName == NULL)
+				fileName = xstrdup(cp);
+			break;
+
+		case 's':
+			subCommand(cp, num1, num2);
+			break;
+
+		case 'w':
+			if (*cp && !isblank(*cp)) {
+				bb_error_msg("bad write command");
+				break;
+			}
+			cp = skip_blank(cp);
+			if (!have1) {
+				num1 = 1;
+				num2 = lastNum;
+			}
+			if (*cp == '\0')
+				cp = fileName;
+			if (cp == NULL) {
+				bb_error_msg("no file name specified");
+				break;
+			}
+			writeLines(cp, num1, num2);
+			break;
+
+		case 'z':
+			switch (*cp) {
+			case '-':
+				printLines(curNum - 21, curNum, FALSE);
+				break;
+			case '.':
+				printLines(curNum - 11, curNum + 10, FALSE);
+				break;
+			default:
+				printLines(curNum, curNum + 21, FALSE);
+				break;
+			}
+			break;
+
+		case '.':
+			if (have1) {
+				bb_error_msg("no arguments allowed");
+				break;
+			}
+			printLines(curNum, curNum, FALSE);
+			break;
+
+		case '-':
+			if (setCurNum(curNum - 1))
+				printLines(curNum, curNum, FALSE);
+			break;
+
+		case '=':
+			printf("%d\n", num1);
+			break;
+		case '\0':
+			if (have1) {
+				printLines(num2, num2, FALSE);
+				break;
+			}
+			if (setCurNum(curNum + 1))
+				printLines(curNum, curNum, FALSE);
+			break;
+
+		default:
+			bb_error_msg("unimplemented command");
+			break;
+		}
+	}
+}
+
+
+/*
+ * Do the substitute command.
+ * The current line is set to the last substitution done.
+ */
+static void subCommand(const char *cmd, int num1, int num2)
+{
+	char *cp, *oldStr, *newStr, buf[USERSIZE];
+	int delim, oldLen, newLen, deltaLen, offset;
+	LINE *lp, *nlp;
+	int globalFlag, printFlag, didSub, needPrint;
+
+	if (bad_nums(num1, num2, "substitute"))
+		return;
+
+	globalFlag = FALSE;
+	printFlag = FALSE;
+	didSub = FALSE;
+	needPrint = FALSE;
+
+	/*
+	 * Copy the command so we can modify it.
+	 */
+	strcpy(buf, cmd);
+	cp = buf;
+
+	if (isblank(*cp) || (*cp == '\0')) {
+		bb_error_msg("bad delimiter for substitute");
+		return;
+	}
+
+	delim = *cp++;
+	oldStr = cp;
+
+	cp = strchr(cp, delim);
+	if (cp == NULL) {
+		bb_error_msg("missing 2nd delimiter for substitute");
+		return;
+	}
+
+	*cp++ = '\0';
+
+	newStr = cp;
+	cp = strchr(cp, delim);
+
+	if (cp)
+		*cp++ = '\0';
+	else
+		cp = (char*)"";
+
+	while (*cp) switch (*cp++) {
+		case 'g':
+			globalFlag = TRUE;
+			break;
+		case 'p':
+			printFlag = TRUE;
+			break;
+		default:
+			bb_error_msg("unknown option for substitute");
+			return;
+	}
+
+	if (*oldStr == '\0') {
+		if (searchString[0] == '\0') {
+			bb_error_msg("no previous search string");
+			return;
+		}
+		oldStr = searchString;
+	}
+
+	if (oldStr != searchString)
+		strcpy(searchString, oldStr);
+
+	lp = findLine(num1);
+	if (lp == NULL)
+		return;
+
+	oldLen = strlen(oldStr);
+	newLen = strlen(newStr);
+	deltaLen = newLen - oldLen;
+	offset = 0;
+	nlp = NULL;
+
+	while (num1 <= num2) {
+		offset = findString(lp, oldStr, oldLen, offset);
+
+		if (offset < 0) {
+			if (needPrint) {
+				printLines(num1, num1, FALSE);
+				needPrint = FALSE;
+			}
+			offset = 0;
+			lp = lp->next;
+			num1++;
+			continue;
+		}
+
+		needPrint = printFlag;
+		didSub = TRUE;
+		dirty = TRUE;
+
+		/*
+		 * If the replacement string is the same size or shorter
+		 * than the old string, then the substitution is easy.
+		 */
+		if (deltaLen <= 0) {
+			memcpy(&lp->data[offset], newStr, newLen);
+			if (deltaLen) {
+				memcpy(&lp->data[offset + newLen],
+					&lp->data[offset + oldLen],
+					lp->len - offset - oldLen);
+
+				lp->len += deltaLen;
+			}
+			offset += newLen;
+			if (globalFlag)
+				continue;
+			if (needPrint) {
+				printLines(num1, num1, FALSE);
+				needPrint = FALSE;
+			}
+			lp = lp->next;
+			num1++;
+			continue;
+		}
+
+		/*
+		 * The new string is larger, so allocate a new line
+		 * structure and use that.  Link it in place of
+		 * the old line structure.
+		 */
+		nlp = xmalloc(sizeof(LINE) + lp->len + deltaLen);
+
+		nlp->len = lp->len + deltaLen;
+
+		memcpy(nlp->data, lp->data, offset);
+		memcpy(&nlp->data[offset], newStr, newLen);
+		memcpy(&nlp->data[offset + newLen],
+			&lp->data[offset + oldLen],
+			lp->len - offset - oldLen);
+
+		nlp->next = lp->next;
+		nlp->prev = lp->prev;
+		nlp->prev->next = nlp;
+		nlp->next->prev = nlp;
+
+		if (curLine == lp)
+			curLine = nlp;
+
+		free(lp);
+		lp = nlp;
+
+		offset += newLen;
+
+		if (globalFlag)
+			continue;
+
+		if (needPrint) {
+			printLines(num1, num1, FALSE);
+			needPrint = FALSE;
+		}
+
+		lp = lp->next;
+		num1++;
+	}
+
+	if (!didSub)
+		bb_error_msg("no substitutions found for \"%s\"", oldStr);
+}
+
+
+/*
+ * Search a line for the specified string starting at the specified
+ * offset in the line.  Returns the offset of the found string, or -1.
+ */
+static int findString(const LINE *lp, const char *str, int len, int offset)
+{
+	int left;
+	const char *cp, *ncp;
+
+	cp = &lp->data[offset];
+	left = lp->len - offset;
+
+	while (left >= len) {
+		ncp = memchr(cp, *str, left);
+		if (ncp == NULL)
+			return -1;
+		left -= (ncp - cp);
+		if (left < len)
+			return -1;
+		cp = ncp;
+		if (memcmp(cp, str, len) == 0)
+			return (cp - lp->data);
+		cp++;
+		left--;
+	}
+
+	return -1;
+}
+
+
+/*
+ * Add lines which are typed in by the user.
+ * The lines are inserted just before the specified line number.
+ * The lines are terminated by a line containing a single dot (ugly!),
+ * or by an end of file.
+ */
+static void addLines(int num)
+{
+	int len;
+	char buf[USERSIZE + 1];
+
+	while (1) {
+		/* Returns:
+		 * -1 on read errors or EOF, or on bare Ctrl-D.
+		 * 0  on ctrl-C,
+		 * >0 length of input string, including terminating '\n'
+		 */
+		len = read_line_input(NULL, "", buf, sizeof(buf), /*timeout*/ -1);
+		if (len <= 0) {
+			/* Previously, ctrl-C was exiting to shell.
+			 * Now we exit to ed prompt. Is in important? */
+			return;
+		}
+		if ((buf[0] == '.') && (buf[1] == '\n') && (buf[2] == '\0'))
+			return;
+		if (!insertLine(num++, buf, len))
+			return;
+	}
+}
+
+
+/*
+ * Parse a line number argument if it is present.  This is a sum
+ * or difference of numbers, '.', '$', 'x, or a search string.
+ * Returns TRUE if successful (whether or not there was a number).
+ * Returns FALSE if there was a parsing error, with a message output.
+ * Whether there was a number is returned indirectly, as is the number.
+ * The character pointer which stopped the scan is also returned.
+ */
+static int getNum(const char **retcp, smallint *retHaveNum, int *retNum)
+{
+	const char *cp;
+	char *endStr, str[USERSIZE];
+	int value, num;
+	smallint haveNum, minus;
+
+	cp = *retcp;
+	value = 0;
+	haveNum = FALSE;
+	minus = 0;
+
+	while (TRUE) {
+		cp = skip_blank(cp);
+
+		switch (*cp) {
+			case '.':
+				haveNum = TRUE;
+				num = curNum;
+				cp++;
+				break;
+
+			case '$':
+				haveNum = TRUE;
+				num = lastNum;
+				cp++;
+				break;
+
+			case '\'':
+				cp++;
+				if ((*cp < 'a') || (*cp > 'z')) {
+					bb_error_msg("bad mark name");
+					return FALSE;
+				}
+				haveNum = TRUE;
+				num = marks[*cp++ - 'a'];
+				break;
+
+			case '/':
+				strcpy(str, ++cp);
+				endStr = strchr(str, '/');
+				if (endStr) {
+					*endStr++ = '\0';
+					cp += (endStr - str);
+				} else
+					cp = "";
+				num = searchLines(str, curNum, lastNum);
+				if (num == 0)
+					return FALSE;
+				haveNum = TRUE;
+				break;
+
+			default:
+				if (!isdigit(*cp)) {
+					*retcp = cp;
+					*retHaveNum = haveNum;
+					*retNum = value;
+					return TRUE;
+				}
+				num = 0;
+				while (isdigit(*cp))
+					num = num * 10 + *cp++ - '0';
+				haveNum = TRUE;
+				break;
+		}
+
+		value += (minus ? -num : num);
+
+		cp = skip_blank(cp);
+
+		switch (*cp) {
+			case '-':
+				minus = 1;
+				cp++;
+				break;
+
+			case '+':
+				minus = 0;
+				cp++;
+				break;
+
+			default:
+				*retcp = cp;
+				*retHaveNum = haveNum;
+				*retNum = value;
+				return TRUE;
+		}
+	}
+}
+
+
+/*
+ * Read lines from a file at the specified line number.
+ * Returns TRUE if the file was successfully read.
+ */
+static int readLines(const char *file, int num)
+{
+	int fd, cc;
+	int len, lineCount, charCount;
+	char *cp;
+
+	if ((num < 1) || (num > lastNum + 1)) {
+		bb_error_msg("bad line for read");
+		return FALSE;
+	}
+
+	fd = open(file, 0);
+	if (fd < 0) {
+		bb_simple_perror_msg(file);
+		return FALSE;
+	}
+
+	bufPtr = bufBase;
+	bufUsed = 0;
+	lineCount = 0;
+	charCount = 0;
+	cc = 0;
+
+	printf("\"%s\", ", file);
+	fflush_all();
+
+	do {
+		cp = memchr(bufPtr, '\n', bufUsed);
+
+		if (cp) {
+			len = (cp - bufPtr) + 1;
+			if (!insertLine(num, bufPtr, len)) {
+				close(fd);
+				return FALSE;
+			}
+			bufPtr += len;
+			bufUsed -= len;
+			charCount += len;
+			lineCount++;
+			num++;
+			continue;
+		}
+
+		if (bufPtr != bufBase) {
+			memcpy(bufBase, bufPtr, bufUsed);
+			bufPtr = bufBase + bufUsed;
+		}
+
+		if (bufUsed >= bufSize) {
+			len = (bufSize * 3) / 2;
+			cp = xrealloc(bufBase, len);
+			bufBase = cp;
+			bufPtr = bufBase + bufUsed;
+			bufSize = len;
+		}
+
+		cc = safe_read(fd, bufPtr, bufSize - bufUsed);
+		bufUsed += cc;
+		bufPtr = bufBase;
+
+	} while (cc > 0);
+
+	if (cc < 0) {
+		bb_simple_perror_msg(file);
+		close(fd);
+		return FALSE;
+	}
+
+	if (bufUsed) {
+		if (!insertLine(num, bufPtr, bufUsed)) {
+			close(fd);
+			return -1;
+		}
+		lineCount++;
+		charCount += bufUsed;
+	}
+
+	close(fd);
+
+	printf("%d lines%s, %d chars\n", lineCount,
+		(bufUsed ? " (incomplete)" : ""), charCount);
+
+	return TRUE;
+}
+
+
+/*
+ * Write the specified lines out to the specified file.
+ * Returns TRUE if successful, or FALSE on an error with a message output.
+ */
+static int writeLines(const char *file, int num1, int num2)
+{
+	LINE *lp;
+	int fd, lineCount, charCount;
+
+	if (bad_nums(num1, num2, "write"))
+		return FALSE;
+
+	lineCount = 0;
+	charCount = 0;
+
+	fd = creat(file, 0666);
+	if (fd < 0) {
+		bb_simple_perror_msg(file);
+		return FALSE;
+	}
+
+	printf("\"%s\", ", file);
+	fflush_all();
+
+	lp = findLine(num1);
+	if (lp == NULL) {
+		close(fd);
+		return FALSE;
+	}
+
+	while (num1++ <= num2) {
+		if (full_write(fd, lp->data, lp->len) != lp->len) {
+			bb_simple_perror_msg(file);
+			close(fd);
+			return FALSE;
+		}
+		charCount += lp->len;
+		lineCount++;
+		lp = lp->next;
+	}
+
+	if (close(fd) < 0) {
+		bb_simple_perror_msg(file);
+		return FALSE;
+	}
+
+	printf("%d lines, %d chars\n", lineCount, charCount);
+	return TRUE;
+}
+
+
+/*
+ * Print lines in a specified range.
+ * The last line printed becomes the current line.
+ * If expandFlag is TRUE, then the line is printed specially to
+ * show magic characters.
+ */
+static int printLines(int num1, int num2, int expandFlag)
+{
+	const LINE *lp;
+	const char *cp;
+	int ch, count;
+
+	if (bad_nums(num1, num2, "print"))
+		return FALSE;
+
+	lp = findLine(num1);
+	if (lp == NULL)
+		return FALSE;
+
+	while (num1 <= num2) {
+		if (!expandFlag) {
+			write(STDOUT_FILENO, lp->data, lp->len);
+			setCurNum(num1++);
+			lp = lp->next;
+			continue;
+		}
+
+		/*
+		 * Show control characters and characters with the
+		 * high bit set specially.
+		 */
+		cp = lp->data;
+		count = lp->len;
+
+		if ((count > 0) && (cp[count - 1] == '\n'))
+			count--;
+
+		while (count-- > 0) {
+			ch = (unsigned char) *cp++;
+			fputc_printable(ch | PRINTABLE_META, stdout);
+		}
+
+		fputs("$\n", stdout);
+
+		setCurNum(num1++);
+		lp = lp->next;
+	}
+
+	return TRUE;
+}
+
+
+/*
+ * Insert a new line with the specified text.
+ * The line is inserted so as to become the specified line,
+ * thus pushing any existing and further lines down one.
+ * The inserted line is also set to become the current line.
+ * Returns TRUE if successful.
+ */
+static int insertLine(int num, const char *data, int len)
+{
+	LINE *newLp, *lp;
+
+	if ((num < 1) || (num > lastNum + 1)) {
+		bb_error_msg("inserting at bad line number");
+		return FALSE;
+	}
+
+	newLp = xmalloc(sizeof(LINE) + len - 1);
+
+	memcpy(newLp->data, data, len);
+	newLp->len = len;
+
+	if (num > lastNum)
+		lp = &lines;
+	else {
+		lp = findLine(num);
+		if (lp == NULL) {
+			free((char *) newLp);
+			return FALSE;
+		}
+	}
+
+	newLp->next = lp;
+	newLp->prev = lp->prev;
+	lp->prev->next = newLp;
+	lp->prev = newLp;
+
+	lastNum++;
+	dirty = TRUE;
+	return setCurNum(num);
+}
+
+
+/*
+ * Delete lines from the given range.
+ */
+static void deleteLines(int num1, int num2)
+{
+	LINE *lp, *nlp, *plp;
+	int count;
+
+	if (bad_nums(num1, num2, "delete"))
+		return;
+
+	lp = findLine(num1);
+	if (lp == NULL)
+		return;
+
+	if ((curNum >= num1) && (curNum <= num2)) {
+		if (num2 < lastNum)
+			setCurNum(num2 + 1);
+		else if (num1 > 1)
+			setCurNum(num1 - 1);
+		else
+			curNum = 0;
+	}
+
+	count = num2 - num1 + 1;
+	if (curNum > num2)
+		curNum -= count;
+	lastNum -= count;
+
+	while (count-- > 0) {
+		nlp = lp->next;
+		plp = lp->prev;
+		plp->next = nlp;
+		nlp->prev = plp;
+		free(lp);
+		lp = nlp;
+	}
+
+	dirty = TRUE;
+}
+
+
+/*
+ * Search for a line which contains the specified string.
+ * If the string is "", then the previously searched for string
+ * is used.  The currently searched for string is saved for future use.
+ * Returns the line number which matches, or 0 if there was no match
+ * with an error printed.
+ */
+static NOINLINE int searchLines(const char *str, int num1, int num2)
+{
+	const LINE *lp;
+	int len;
+
+	if (bad_nums(num1, num2, "search"))
+		return 0;
+
+	if (*str == '\0') {
+		if (searchString[0] == '\0') {
+			bb_error_msg("no previous search string");
+			return 0;
+		}
+		str = searchString;
+	}
+
+	if (str != searchString)
+		strcpy(searchString, str);
+
+	len = strlen(str);
+
+	lp = findLine(num1);
+	if (lp == NULL)
+		return 0;
+
+	while (num1 <= num2) {
+		if (findString(lp, str, len, 0) >= 0)
+			return num1;
+		num1++;
+		lp = lp->next;
+	}
+
+	bb_error_msg("can't find string \"%s\"", str);
+	return 0;
+}
+
+
+/*
+ * Return a pointer to the specified line number.
+ */
+static LINE *findLine(int num)
+{
+	LINE *lp;
+	int lnum;
+
+	if ((num < 1) || (num > lastNum)) {
+		bb_error_msg("line number %d does not exist", num);
+		return NULL;
+	}
+
+	if (curNum <= 0) {
+		curNum = 1;
+		curLine = lines.next;
+	}
+
+	if (num == curNum)
+		return curLine;
+
+	lp = curLine;
+	lnum = curNum;
+	if (num < (curNum / 2)) {
+		lp = lines.next;
+		lnum = 1;
+	} else if (num > ((curNum + lastNum) / 2)) {
+		lp = lines.prev;
+		lnum = lastNum;
+	}
+
+	while (lnum < num) {
+		lp = lp->next;
+		lnum++;
+	}
+
+	while (lnum > num) {
+		lp = lp->prev;
+		lnum--;
+	}
+	return lp;
+}
+
+
+/*
+ * Set the current line number.
+ * Returns TRUE if successful.
+ */
+static int setCurNum(int num)
+{
+	LINE *lp;
+
+	lp = findLine(num);
+	if (lp == NULL)
+		return FALSE;
+	curNum = num;
+	curLine = lp;
+	return TRUE;
+}
diff --git a/busybox-1.19.3/editors/patch.c b/busybox-1.19.3/editors/patch.c
new file mode 100644
index 0000000..1f2a49b
--- /dev/null
+++ b/busybox-1.19.3/editors/patch.c
@@ -0,0 +1,550 @@
+/* vi: set sw=4 ts=4:
+ *
+ * Apply a "universal" diff.
+ * Adapted from toybox's patch implementation.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html
+ * (But only does -u, because who still cares about "ed"?)
+ *
+ * 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"
+
+
+// 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 (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);
+		}
+		xclose(TT.fileout);
+
+		temp = xstrdup(TT.tempname);
+		temp[strlen(temp) - 6] = '\0';
+		rename(TT.tempname, temp);
+		free(temp);
+
+		free(TT.tempname);
+		TT.tempname = NULL;
+	}
+	TT.fileout = TT.filein = -1;
+}
+
+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)
+{
+	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;
+			}
+		}
+	}
+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)
+{
+	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]);
+	}
+
+	// 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(" ");
+		}
+
+		// 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;
+			}
+			fail_hunk();
+			state = 0;
+			continue;
+		}
+
+		// 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;
+			}
+
+			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);
+				}
+			}
+
+			// 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++;
+				}
+
+				// handle -p path truncation.
+				for (i=0, s = name; *s;) {
+					if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break;
+					if (*(s++)=='/') {
+						name = s;
+						i++;
+					}
+				}
+
+				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 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;
+				}
+			}
+
+			TT.hunknum++;
+
+			continue;
+		}
+
+		// 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/busybox-1.19.3/editors/patch_bbox.c b/busybox-1.19.3/editors/patch_bbox.c
new file mode 100644
index 0000000..78aa5fd
--- /dev/null
+++ b/busybox-1.19.3/editors/patch_bbox.c
@@ -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, "@@ -%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;
+			}
+			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;
+}
diff --git a/busybox-1.19.3/editors/patch_toybox.c b/busybox-1.19.3/editors/patch_toybox.c
new file mode 100644
index 0000000..a60bf07
--- /dev/null
+++ b/busybox-1.19.3/editors/patch_toybox.c
@@ -0,0 +1,591 @@
+/* Adapted from toybox's patch. */
+
+/* vi: set sw=4 ts=4:
+ *
+ * patch.c - Apply a "universal" diff.
+ *
+ * Copyright 2007 Rob Landley <rob@landley.net>
+ *
+ * see http://www.opengroup.org/onlinepubs/009695399/utilities/patch.html
+ * (But only does -u, because who still cares about "ed"?)
+ *
+ * TODO:
+ * -b backup
+ * -l treat all whitespace as a single space
+ * -N ignore already applied
+ * -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
+ *
+ * -E remove empty files --remove-empty-files
+ * -f force (no questions asked)
+ * -F fuzz (number, default 2)
+ * [file] which file to patch
+
+USE_PATCH(NEWTOY(patch, USE_TOYBOX_DEBUG("x")"up#i:R", TOYFLAG_USR|TOYFLAG_BIN))
+
+config PATCH
+	bool "patch"
+	default y
+	help
+	  usage: patch [-i file] [-p depth] [-Ru]
+
+	  Apply a unified diff to one or more files.
+
+	  -i	Input file (defaults=stdin)
+	  -p	number of '/' to strip from start of file paths (default=all)
+	  -R	Reverse patch.
+	  -u	Ignored (only handles "unified" diffs)
+
+	  This version of patch only handles unified diffs, and only modifies
+	  a file when all all hunks to that file apply.  Patch prints failed
+	  hunks to stderr, and exits with nonzero status if any hunks fail.
+
+	  A file compared against /dev/null (or with a date <= the epoch) is
+	  created/deleted as appropriate.
+*/
+#include "libbb.h"
+
+struct double_list {
+	struct double_list *next;
+	struct double_list *prev;
+	char *data;
+};
+
+// Return the first item from the list, advancing the list (which must be called
+// as &list)
+static
+void *TOY_llist_pop(void *list)
+{
+	// I'd use a void ** for the argument, and even accept the typecast in all
+	// callers as documentation you need the &, except the stupid compiler
+	// would then scream about type-punned pointers.  Screw it.
+	void **llist = (void **)list;
+	void **next = (void **)*llist;
+	*llist = *next;
+
+	return (void *)next;
+}
+
+// Free all the elements of a linked list
+// if freeit!=NULL call freeit() on each element before freeing it.
+static
+void TOY_llist_free(void *list, void (*freeit)(void *data))
+{
+	while (list) {
+		void *pop = TOY_llist_pop(&list);
+		if (freeit) freeit(pop);
+		else free(pop);
+
+		// End doubly linked list too.
+		if (list==pop) break;
+	}
+}
+
+// Add an entry to the end off a doubly linked list
+static
+struct double_list *dlist_add(struct double_list **list, char *data)
+{
+	struct double_list *line = xmalloc(sizeof(struct double_list));
+
+	line->data = data;
+	if (*list) {
+		line->next = *list;
+		line->prev = (*list)->prev;
+		(*list)->prev->next = line;
+		(*list)->prev = line;
+	} else *list = line->next = line->prev = line;
+
+	return line;
+}
+
+// Ensure entire path exists.
+// If mode != -1 set permissions on newly created dirs.
+// Requires that path string be writable (for temporary null terminators).
+static
+void xmkpath(char *path, int mode)
+{
+	char *p, old;
+	mode_t mask;
+	int rc;
+	struct stat st;
+
+	for (p = path; ; p++) {
+		if (!*p || *p == '/') {
+			old = *p;
+			*p = rc = 0;
+			if (stat(path, &st) || !S_ISDIR(st.st_mode)) {
+				if (mode != -1) {
+					mask = umask(0);
+					rc = mkdir(path, mode);
+					umask(mask);
+				} else rc = mkdir(path, 0777);
+			}
+			*p = old;
+			if(rc) bb_perror_msg_and_die("mkpath '%s'", path);
+		}
+		if (!*p) break;
+	}
+}
+
+// Slow, but small.
+static
+char *get_rawline(int fd, long *plen, char end)
+{
+	char c, *buf = NULL;
+	long len = 0;
+
+	for (;;) {
+		if (1>read(fd, &c, 1)) break;
+		if (!(len & 63)) buf=xrealloc(buf, len+65);
+		if ((buf[len++]=c) == end) break;
+	}
+	if (buf) buf[len]=0;
+	if (plen) *plen = len;
+
+	return buf;
+}
+
+static
+char *get_line(int fd)
+{
+	long len;
+	char *buf = get_rawline(fd, &len, '\n');
+
+	if (buf && buf[--len]=='\n') buf[len]=0;
+
+	return buf;
+}
+
+// Copy the rest of in to out and close both files.
+static
+void xsendfile(int in, int out)
+{
+	long len;
+	char buf[4096];
+
+	if (in<0) return;
+	for (;;) {
+		len = safe_read(in, buf, 4096);
+		if (len<1) break;
+		xwrite(out, buf, len);
+	}
+}
+
+// Copy the rest of the data and replace the original with the copy.
+static
+void replace_tempfile(int fdin, int fdout, char **tempname)
+{
+	char *temp = xstrdup(*tempname);
+
+	temp[strlen(temp)-6]=0;
+	if (fdin != -1) {
+		xsendfile(fdin, fdout);
+		xclose(fdin);
+	}
+	xclose(fdout);
+	rename(*tempname, temp);
+	free(*tempname);
+	free(temp);
+	*tempname = NULL;
+}
+
+// Open a temporary file to copy an existing file into.
+static
+int copy_tempfile(int fdin, char *name, char **tempname)
+{
+	struct stat statbuf;
+	int fd;
+
+	*tempname = xasprintf("%sXXXXXX", name);
+	fd = mkstemp(*tempname);
+	if(-1 == fd) bb_perror_msg_and_die("no temp file");
+
+	// Set permissions of output file
+	fstat(fdin, &statbuf);
+	fchmod(fd, statbuf.st_mode);
+
+	return fd;
+}
+
+// Abort the copy and delete the temporary file.
+static
+void delete_tempfile(int fdin, int fdout, char **tempname)
+{
+	close(fdin);
+	close(fdout);
+	unlink(*tempname);
+	free(*tempname);
+	*tempname = NULL;
+}
+
+
+
+struct globals {
+	char *infile;
+	long prefix;
+
+	struct double_list *current_hunk;
+	long oldline, oldlen, newline, newlen, linenum;
+	int context, state, filein, fileout, filepatch, hunknum;
+	char *tempname;
+
+	// was toys.foo:
+	int exitval;
+};
+#define TT (*ptr_to_globals)
+#define INIT_TT() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(TT))); \
+} while (0)
+
+
+//bbox had: "p:i:RN"
+#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.
+
+// 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 = (struct double_list *)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(data);
+}
+
+static void finish_oldfile(void)
+{
+	if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname);
+	TT.fileout = TT.filein = -1;
+}
+
+static void fail_hunk(void)
+{
+	if (!TT.current_hunk) return;
+	TT.current_hunk->prev->next = 0;
+
+	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.current_hunk, do_line);
+	TT.current_hunk = NULL;
+	delete_tempfile(TT.filein, TT.fileout, &TT.tempname);
+	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)
+{
+	struct double_list *plist, *buf = NULL, *check;
+	int matcheof = 0, reverse = option_mask32 & FLAG_REVERSE, backwarn = 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 < 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 (TT.context) for (;;) {
+		char *data = get_line(TT.filein);
+
+		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) {
+					fdprintf(2,"Possibly reversed hunk %d at %ld\n",
+						TT.hunknum, TT.linenum);
+					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.
+
+		// 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 = TOY_llist_pop(&buf);
+				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 = 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;
+				check = check->next;
+				if (check == buf) break;
+			}
+		}
+	}
+out:
+	// We have a match.  Emit changed data.
+	TT.state = "-+"[reverse];
+	TOY_llist_free(TT.current_hunk, do_line);
+	TT.current_hunk = NULL;
+	TT.state = 1;
+done:
+	if (buf) {
+		buf->prev->next = NULL;
+		TOY_llist_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)
+{
+	int opts;
+	int reverse, state = 0;
+	char *oldname = NULL, *newname = NULL;
+	char *opt_p, *opt_i;
+
+	INIT_TT();
+
+	opts = getopt32(argv, FLAG_STR, &opt_p, &opt_i);
+	reverse = opts & FLAG_REVERSE;
+	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(TT.filepatch);
+		if (!patchline) break;
+
+		// Other versions of patch accept damaged patches,
+		// so we need to also.
+		if (!*patchline) {
+			free(patchline);
+			patchline = xstrdup(" ");
+		}
+
+		// Are we assembling a hunk?
+		if (state >= 2) {
+			if (*patchline==' ' || *patchline=='+' || *patchline=='-') {
+				dlist_add(&TT.current_hunk, patchline);
+
+				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 (!TT.oldlen && !TT.newlen) state = apply_one_hunk();
+				continue;
+			}
+			fail_hunk();
+			state = 0;
+			continue;
+		}
+
+		// 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;
+			}
+
+			free(*name);
+			finish_oldfile();
+
+			// 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);
+			}
+
+			// 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?
+		} else if (state == 1 && !strncmp("@@ -", patchline, 4)) {
+			int i;
+
+			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);
+
+			TT.context = 0;
+			state = 2;
+
+			// If this is the first hunk, open the file.
+			if (TT.filein == -1) {
+				int oldsum, newsum, del = 0;
+				char *s, *name;
+
+				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))
+				{
+					name = reverse ? newname : oldname;
+					del++;
+				}
+
+				// handle -p path truncation.
+				for (i=0, s = name; *s;) {
+					if ((option_mask32 & FLAG_PATHLEN) && TT.prefix == i) break;
+					if (*(s++)=='/') {
+						name = s;
+						i++;
+					}
+				}
+
+				if (del) {
+					printf("removing %s\n", name);
+					xunlink(name);
+					state = 0;
+				// If we've got a file to open, do so.
+				} 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);
+						s = strrchr(name, '/');
+						if (s) {
+							*s = 0;
+							xmkpath(name, -1);
+							*s = '/';
+						}
+						TT.filein = xopen(name, O_CREAT|O_EXCL|O_RDWR);
+					} else {
+						printf("patching file %s\n", name);
+						TT.filein = xopen(name, O_RDWR);
+					}
+					TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname);
+					TT.linenum = 0;
+					TT.hunknum = 0;
+				}
+			}
+
+			TT.hunknum++;
+
+			continue;
+		}
+
+		// If we didn't continue above, discard this line.
+		free(patchline);
+	}
+
+	finish_oldfile();
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(TT.filepatch);
+		free(oldname);
+		free(newname);
+	}
+
+	return TT.exitval;
+}
diff --git a/busybox-1.19.3/editors/sed.c b/busybox-1.19.3/editors/sed.c
new file mode 100644
index 0000000..1552cf3
--- /dev/null
+++ b/busybox-1.19.3/editors/sed.c
@@ -0,0 +1,1483 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * sed.c - very minimalist version of sed
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
+ * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
+ * Copyright (C) 2002  Matt Kraai
+ * Copyright (C) 2003 by Glenn McGrath
+ * Copyright (C) 2003,2004 by Rob Landley <rob@landley.net>
+ *
+ * MAINTAINER: Rob Landley <rob@landley.net>
+ *
+ * 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).
+ *
+ * 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:
+ *
+ * - 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
+ */
+
+//usage:#define sed_trivial_usage
+//usage:       "[-efinr] SED_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	Edit files in-place (else sends result to stdout)"
+//usage:     "\n	-n	Suppress automatic printing of pattern space"
+//usage:     "\n	-r	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"
+
+#include "libbb.h"
+#include "xregex.h"
+
+#if 0
+# define dbg(...) bb_error_msg(__VA_ARGS__)
+#else
+# define dbg(...) ((void)0)
+#endif
+
+
+enum {
+	OPT_in_place = 1 << 0,
+};
+
+/* Each sed command turns into one of these structures. */
+typedef struct sed_cmd_s {
+	/* Ordered by alignment requirements: currently 36 bytes on x86 */
+	struct sed_cmd_s *next; /* Next command (linked list, NULL terminated) */
+
+	/* address storage */
+	regex_t *beg_match;     /* sed -e '/match/cmd' */
+	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. */
+	char *string;           /* Data string for (saicytb) commands. */
+
+	unsigned which_match;   /* (s) Which match to replace (0 for all) */
+
+	/* Bitfields (gcc won't group them if we don't) */
+	unsigned invert:1;      /* the '!' after the address */
+	unsigned in_match:1;    /* Next line also included in match? */
+	unsigned sub_p:1;       /* (s) print option */
+
+	char sw_last_char;      /* Last line written by (sw) had no '\n' */
+
+	/* GENERAL FIELDS */
+	char cmd;               /* The command char: abcdDgGhHilnNpPqrstwxy:={} */
+} sed_cmd_t;
+
+static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
+
+struct globals {
+	/* options */
+	int be_quiet, regex_type;
+	FILE *nonstdout;
+	char *outname, *hold_space;
+
+	/* List of input files */
+	int input_file_count, current_input_file;
+	FILE **input_file_list;
+
+	regmatch_t regmatch[10];
+	regex_t *previous_regex_ptr;
+
+	/* linked list of sed commands */
+	sed_cmd_t *sed_cmd_head, **sed_cmd_tail;
+
+	/* 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 */
+	} 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];
+};
+#define INIT_G() do { \
+	G.sed_cmd_tail = &G.sed_cmd_head; \
+} while (0)
+
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void sed_free_and_close_stuff(void)
+{
+	sed_cmd_t *sed_cmd = G.sed_cmd_head;
+
+	llist_free(G.append_head, free);
+
+	while (sed_cmd) {
+		sed_cmd_t *sed_cmd_next = sed_cmd->next;
+
+		if (sed_cmd->sw_file)
+			xprint_and_close_file(sed_cmd->sw_file);
+
+		if (sed_cmd->beg_match) {
+			regfree(sed_cmd->beg_match);
+			free(sed_cmd->beg_match);
+		}
+		if (sed_cmd->end_match) {
+			regfree(sed_cmd->end_match);
+			free(sed_cmd->end_match);
+		}
+		if (sed_cmd->sub_match) {
+			regfree(sed_cmd->sub_match);
+			free(sed_cmd->sub_match);
+		}
+		free(sed_cmd->string);
+		free(sed_cmd);
+		sed_cmd = sed_cmd_next;
+	}
+
+	free(G.hold_space);
+
+	while (G.current_input_file < G.input_file_count)
+		fclose(G.input_file_list[G.current_input_file++]);
+}
+#else
+void sed_free_and_close_stuff(void);
+#endif
+
+/* If something bad happens during -i operation, delete temp file */
+
+static void cleanup_outname(void)
+{
+	if (G.outname) unlink(G.outname);
+}
+
+/* strcpy, replacing "\from" with 'to'. If to is NUL, replacing "\any" with 'any' */
+
+static void parse_escapes(char *dest, const char *string, int len, char from, char to)
+{
+	int i = 0;
+
+	while (i < len) {
+		if (string[i] == '\\') {
+			if (!to || string[i+1] == from) {
+				*dest++ = to ? to : string[i+1];
+				i += 2;
+				continue;
+			}
+			*dest++ = string[i++];
+		}
+		/* TODO: is it safe wrt a string with trailing '\\' ? */
+		*dest++ = string[i++];
+	}
+	*dest = '\0';
+}
+
+static char *copy_parsing_escapes(const char *string, int len)
+{
+	const char *s;
+	char *dest = xmalloc(len + 1);
+
+	/* 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;
+}
+
+
+/*
+ * index_of_next_unescaped_regexp_delim - walks left to right through a string
+ * beginning at a specified index and returns the index of the next regular
+ * expression delimiter (typically a forward slash ('/')) not preceded by
+ * a backslash ('\').  A negative delimiter disables square bracket checking.
+ */
+static int index_of_next_unescaped_regexp_delim(int delimiter, const char *str)
+{
+	int bracket = -1;
+	int escaped = 0;
+	int idx = 0;
+	char ch;
+
+	if (delimiter < 0) {
+		bracket--;
+		delimiter = -delimiter;
+	}
+
+	for (; (ch = str[idx]) != '\0'; idx++) {
+		if (bracket >= 0) {
+			if (ch == ']'
+			 && !(bracket == idx - 1 || (bracket == idx - 2 && str[idx - 1] == '^'))
+			) {
+				bracket = -1;
+			}
+		} else if (escaped)
+			escaped = 0;
+		else if (ch == '\\')
+			escaped = 1;
+		else if (bracket == -1 && ch == '[')
+			bracket = idx;
+		else if (ch == delimiter)
+			return idx;
+	}
+
+	/* if we make it to here, we've hit the end of the string */
+	bb_error_msg_and_die("unmatched '%c'", delimiter);
+}
+
+/*
+ *  Returns the index of the third delimiter
+ */
+static int parse_regex_delim(const char *cmdstr, char **match, char **replace)
+{
+	const char *cmdstr_ptr = cmdstr;
+	char delimiter;
+	int idx = 0;
+
+	/* verify that the 's' or 'y' is followed by something.  That something
+	 * (typically a 'slash') is now our regexp delimiter... */
+	if (*cmdstr == '\0')
+		bb_error_msg_and_die("bad format in substitution expression");
+	delimiter = *cmdstr_ptr++;
+
+	/* save the match string */
+	idx = index_of_next_unescaped_regexp_delim(delimiter, cmdstr_ptr);
+	*match = copy_parsing_escapes(cmdstr_ptr, idx);
+
+	/* save the replacement string */
+	cmdstr_ptr += idx + 1;
+	idx = index_of_next_unescaped_regexp_delim(-delimiter, cmdstr_ptr);
+	*replace = copy_parsing_escapes(cmdstr_ptr, idx);
+
+	return ((cmdstr_ptr - cmdstr) + idx);
+}
+
+/*
+ * returns the index in the string just past where the address ends.
+ */
+static int get_address(const char *my_str, int *linenum, regex_t ** regex)
+{
+	const char *pos = my_str;
+
+	if (isdigit(*my_str)) {
+		*linenum = strtol(my_str, (char**)&pos, 10);
+		/* endstr shouldnt ever equal NULL */
+	} else if (*my_str == '$') {
+		*linenum = -1;
+		pos++;
+	} else if (*my_str == '/' || *my_str == '\\') {
+		int next;
+		char delimiter;
+		char *temp;
+
+		delimiter = '/';
+		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);
+		free(temp);
+		/* Move position to next character after last delimiter */
+		pos += (next+1);
+	}
+	return pos - my_str;
+}
+
+/* Grab a filename.  Whitespace at start is skipped, then goes to EOL. */
+static int parse_file_cmd(/*sed_cmd_t *sed_cmd,*/ const char *filecmdstr, char **retval)
+{
+	int start = 0, idx, hack = 0;
+
+	/* Skip whitespace, then grab filename to end of line */
+	while (isspace(filecmdstr[start]))
+		start++;
+	idx = start;
+	while (filecmdstr[idx] && filecmdstr[idx] != '\n')
+		idx++;
+
+	/* If lines glued together, put backslash back. */
+	if (filecmdstr[idx] == '\n')
+		hack = 1;
+	if (idx == start)
+		bb_error_msg_and_die("empty filename");
+	*retval = xstrndup(filecmdstr+start, idx-start+hack+1);
+	if (hack)
+		(*retval)[idx] = '\\';
+
+	return idx;
+}
+
+static int parse_subst_cmd(sed_cmd_t *sed_cmd, const char *substr)
+{
+	int cflags = G.regex_type;
+	char *match;
+	int idx;
+
+	/*
+	 * A substitution command should look something like this:
+	 *    s/match/replace/ #gIpw
+	 *    ||     |        |||
+	 *    mandatory       optional
+	 */
+	idx = parse_regex_delim(substr, &match, &sed_cmd->string);
+
+	/* determine the number of back references in the match string */
+	/* Note: we compute this here rather than in the do_subst_command()
+	 * function to save processor time, at the expense of a little more memory
+	 * (4 bits) per sed_cmd */
+
+	/* process the flags */
+
+	sed_cmd->which_match = 1;
+	while (substr[++idx]) {
+		/* Parse match number */
+		if (isdigit(substr[idx])) {
+			if (match[0] != '^') {
+				/* Match 0 treated as all, multiple matches we take the last one. */
+				const char *pos = substr + idx;
+/* FIXME: error check? */
+				sed_cmd->which_match = (unsigned)strtol(substr+idx, (char**) &pos, 10);
+				idx = pos - substr;
+			}
+			continue;
+		}
+		/* Skip spaces */
+		if (isspace(substr[idx]))
+			continue;
+
+		switch (substr[idx]) {
+		/* Replace all occurrences */
+		case 'g':
+			if (match[0] != '^')
+				sed_cmd->which_match = 0;
+			break;
+		/* Print pattern space */
+		case 'p':
+			sed_cmd->sub_p = 1;
+			break;
+		/* Write to file */
+		case 'w':
+		{
+			char *temp;
+			idx += parse_file_cmd(/*sed_cmd,*/ substr+idx, &temp);
+			break;
+		}
+		/* Ignore case (gnu exension) */
+		case 'I':
+			cflags |= REG_ICASE;
+			break;
+		/* Comment */
+		case '#':
+			// while (substr[++idx]) continue;
+			idx += strlen(substr + idx); // same
+			/* Fall through */
+		/* End of command */
+		case ';':
+		case '}':
+			goto out;
+		default:
+			bb_error_msg_and_die("bad option in substitution expression");
+		}
+	}
+ out:
+	/* 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));
+		xregcomp(sed_cmd->sub_match, match, cflags);
+	}
+	free(match);
+
+	return idx;
+}
+
+/*
+ *  Process the commands arguments
+ */
+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 (idx == IDX_s) {
+		cmdstr += parse_subst_cmd(sed_cmd, cmdstr);
+	}
+	/* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
+	else if (idx <= IDX_c) { /* a,i,c */
+		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");
+		for (;;) {
+			if (*cmdstr == '\n' || *cmdstr == '\\') {
+				cmdstr++;
+				break;
+			}
+			if (!isspace(*cmdstr))
+				break;
+			cmdstr++;
+		}
+		sed_cmd->string = xstrdup(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 (idx <= IDX_w) { /* r,w */
+		if (sed_cmd->end_line || sed_cmd->end_match)
+			bb_error_msg_and_die("command only uses one address");
+		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 (idx <= IDX_T) { /* :,b,t,T */
+		int length;
+
+		cmdstr = skip_whitespace(cmdstr);
+		length = strcspn(cmdstr, semicolon_whitespace);
+		if (length) {
+			sed_cmd->string = xstrndup(cmdstr, length);
+			cmdstr += length;
+		}
+	}
+	/* translation command */
+	else if (idx == IDX_y) {
+		char *match, *replace;
+		int i = cmdstr[0];
+
+		cmdstr += parse_regex_delim(cmdstr, &match, &replace)+1;
+		/* \n already parsed, but \delimiter needs unescaping. */
+		parse_escapes(match, match, strlen(match), i, i);
+		parse_escapes(replace, replace, strlen(replace), i, i);
+
+		sed_cmd->string = xzalloc((strlen(match) + 1) * 2);
+		for (i = 0; match[i] && replace[i]; i++) {
+			sed_cmd->string[i*2] = match[i];
+			sed_cmd->string[i*2+1] = replace[i];
+		}
+		free(match);
+		free(replace);
+	}
+	/* if it wasnt a single-letter command that takes no arguments
+	 * then it must be an invalid command.
+	 */
+	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);
+	}
+
+	/* give back whatever's left over */
+	return cmdstr;
+}
+
+
+/* Parse address+command sets, skipping comment lines. */
+
+static void add_cmd(const char *cmdstr)
+{
+	sed_cmd_t *sed_cmd;
+	unsigned len, n;
+
+	/* Append this line to any unfinished line from last time. */
+	if (G.add_cmd_line) {
+		char *tp = xasprintf("%s\n%s", G.add_cmd_line, cmdstr);
+		free(G.add_cmd_line);
+		cmdstr = G.add_cmd_line = tp;
+	}
+
+	/* If this line ends with unescaped backslash, request next line. */
+	n = len = strlen(cmdstr);
+	while (n && cmdstr[n-1] == '\\')
+		n--;
+	if ((len - n) & 1) { /* if odd number of trailing backslashes */
+		if (!G.add_cmd_line)
+			G.add_cmd_line = xstrdup(cmdstr);
+		G.add_cmd_line[len-1] = '\0';
+		return;
+	}
+
+	/* Loop parsing all commands in this line. */
+	while (*cmdstr) {
+		/* Skip leading whitespace and semicolons */
+		cmdstr += strspn(cmdstr, semicolon_whitespace);
+
+		/* If no more commands, exit. */
+		if (!*cmdstr) break;
+
+		/* if this is a comment, jump past it and keep going */
+		if (*cmdstr == '#') {
+			/* "#n" is the same as using -n on the command line */
+			if (cmdstr[1] == 'n')
+				G.be_quiet++;
+			cmdstr = strpbrk(cmdstr, "\n\r");
+			if (!cmdstr) break;
+			continue;
+		}
+
+		/* parse the command
+		 * format is: [addr][,addr][!]cmd
+		 *            |----||-----||-|
+		 *            part1 part2  part3
+		 */
+
+		sed_cmd = xzalloc(sizeof(sed_cmd_t));
+
+		/* 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 == ',') {
+			int idx;
+
+			cmdstr++;
+			idx = get_address(cmdstr, &sed_cmd->end_line, &sed_cmd->end_match);
+			if (!idx)
+				bb_error_msg_and_die("no address after comma");
+			cmdstr += idx;
+		}
+
+		/* skip whitespace before the command */
+		cmdstr = skip_whitespace(cmdstr);
+
+		/* Check for inversion flag */
+		if (*cmdstr == '!') {
+			sed_cmd->invert = 1;
+			cmdstr++;
+
+			/* skip whitespace before the command */
+			cmdstr = skip_whitespace(cmdstr);
+		}
+
+		/* last part (mandatory) will be a command */
+		if (!*cmdstr)
+			bb_error_msg_and_die("missing command");
+		sed_cmd->cmd = *cmdstr++;
+		cmdstr = parse_cmd_args(sed_cmd, cmdstr);
+
+		/* Add the command to the command array */
+		*G.sed_cmd_tail = sed_cmd;
+		G.sed_cmd_tail = &sed_cmd->next;
+	}
+
+	/* If we glued multiple lines together, free the memory. */
+	free(G.add_cmd_line);
+	G.add_cmd_line = NULL;
+}
+
+/* Append to a string, reallocating memory as necessary. */
+
+#define PIPE_GROW 64
+
+static void pipe_putc(char c)
+{
+	if (G.pipeline.idx == G.pipeline.len) {
+		G.pipeline.buf = xrealloc(G.pipeline.buf,
+				G.pipeline.len + PIPE_GROW);
+		G.pipeline.len += PIPE_GROW;
+	}
+	G.pipeline.buf[G.pipeline.idx++] = c;
+}
+
+static void do_subst_w_backrefs(char *line, char *replace)
+{
+	int i, j;
+
+	/* 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 (replace[i] == '\\') {
+			unsigned backref = replace[++i] - '0';
+			if (backref <= 9) {
+				/* print out the text held in G.regmatch[backref] */
+				if (G.regmatch[backref].rm_so != -1) {
+					j = G.regmatch[backref].rm_so;
+					while (j < G.regmatch[backref].rm_eo)
+						pipe_putc(line[j++]);
+				}
+				continue;
+			}
+			/* I _think_ it is impossible to get '\' to be
+			 * the last char in replace string. Thus we dont check
+			 * for replace[i] == NUL. (counterexample anyone?) */
+			/* if we find a backslash escaped character, print the character */
+			pipe_putc(replace[i]);
+			continue;
+		}
+		/* if we find an unescaped '&' print out the whole matched text. */
+		if (replace[i] == '&') {
+			j = G.regmatch[0].rm_so;
+			while (j < G.regmatch[0].rm_eo)
+				pipe_putc(line[j++]);
+			continue;
+		}
+		/* Otherwise just output the character. */
+		pipe_putc(replace[i]);
+	}
+}
+
+static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
+{
+	char *line = *line_p;
+	int altered = 0;
+	unsigned match_count = 0;
+	regex_t *current_regex;
+
+	current_regex = sed_cmd->sub_match;
+	/* Handle empty regex. */
+	if (!current_regex) {
+		current_regex = G.previous_regex_ptr;
+		if (!current_regex)
+			bb_error_msg_and_die("no previous regexp");
+	}
+	G.previous_regex_ptr = current_regex;
+
+	/* Find the first match */
+	if (REG_NOMATCH == regexec(current_regex, line, 10, G.regmatch, 0))
+		return 0;
+
+	/* Initialize temporary output buffer. */
+	G.pipeline.buf = xmalloc(PIPE_GROW);
+	G.pipeline.len = PIPE_GROW;
+	G.pipeline.idx = 0;
+
+	/* Now loop through, substituting for matches */
+	do {
+		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 */
+		if (sed_cmd->which_match
+		 && (sed_cmd->which_match != match_count)
+		) {
+			for (i = 0; i < G.regmatch[0].rm_eo; i++)
+				pipe_putc(*line++);
+			continue;
+		}
+
+		/* print everything before the match */
+		for (i = 0; i < G.regmatch[0].rm_so; i++)
+			pipe_putc(line[i]);
+
+		/* then print the substitution string */
+		do_subst_w_backrefs(line, sed_cmd->string);
+
+		/* advance past the match */
+		line += G.regmatch[0].rm_eo;
+		/* flag that something has changed */
+		altered++;
+
+		/* if we're not doing this globally, get out now */
+		if (sed_cmd->which_match)
+			break;
+
+//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);
+
+	/* Copy rest of string into output pipeline */
+	while (1) {
+		char c = *line++;
+		pipe_putc(c);
+		if (c == '\0')
+			break;
+	}
+
+	free(*line_p);
+	*line_p = G.pipeline.buf;
+	return altered;
+}
+
+/* Set command pointer to point to this label.  (Does not handle null label.) */
+static sed_cmd_t *branch_to(char *label)
+{
+	sed_cmd_t *sed_cmd;
+
+	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;
+		}
+	}
+	bb_error_msg_and_die("can't find label for jump to '%s'", label);
+}
+
+static void append(char *s)
+{
+	llist_add_to_end(&G.append_head, xstrdup(s));
+}
+
+static void flush_append(void)
+{
+	char *data;
+
+	/* Output appended lines. */
+	while ((data = (char *)llist_pop(&G.append_head))) {
+		fprintf(G.nonstdout, "%s\n", data);
+		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)
+{
+	char *temp = NULL;
+	int len;
+	char gc;
+
+	flush_append();
+
+	/* 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];
+		/* 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 */
+		temp = bb_get_chunk_from_file(fp, &len);
+		if (temp) {
+			/* len > 0 here, it's ok to do temp[len-1] */
+			char c = temp[len-1];
+			if (c == '\n' || c == '\0') {
+				temp[len-1] = '\0';
+				gc = c;
+				if (c == '\0') {
+					int ch = fgetc(fp);
+					if (ch != EOF)
+						ungetc(ch, fp);
+					else
+						gc = LAST_IS_NUL;
+				}
+			}
+			/* else we put NO_EOL_CHAR into *gets_char */
+			break;
+
+		/* NB: I had the idea of peeking next file(s) and returning
+		 * NO_EOL_CHAR only if it is the *last* non-empty
+		 * input file. But there is a case where this won't work:
+		 * file1: "a woo\nb woo"
+		 * file2: "c no\nd no"
+		 * sed -ne 's/woo/bang/p' input1 input2 => "a bang\nb bang"
+		 * (note: *no* newline after "b bang"!) */
+		}
+		/* Close this file and advance to next one */
+		fclose(fp);
+		G.current_input_file++;
+	}
+	*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)
+{
+	int retval = sed_cmd->beg_match && !regexec(sed_cmd->beg_match, pattern_space, 0, NULL, 0);
+	if (retval)
+		G.previous_regex_ptr = sed_cmd->beg_match;
+	return retval;
+}
+
+/* Process all the lines in all the files */
+
+static void process_files(void)
+{
+	char *pattern_space, *next_line;
+	int linenum = 0;
+	char last_puts_char = '\n';
+	char last_gets_char, next_gets_char;
+	sed_cmd_t *sed_cmd;
+	int substituted;
+
+	/* Prime the pump */
+	next_line = get_next_line(&next_gets_char);
+
+	/* Go through every line in each file */
+ again:
+	substituted = 0;
+
+	/* Advance to next line.  Stop if out of lines. */
+	pattern_space = next_line;
+	if (!pattern_space)
+		return;
+	last_gets_char = next_gets_char;
+
+	/* Read one line in advance so we can act on the last line,
+	 * the '$' address */
+	next_line = get_next_line(&next_gets_char);
+	linenum++;
+
+	/* For every line, go through all the commands */
+ restart:
+	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: */
+
+		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
+			/* Or is no range necessary? */
+			|| (!sed_cmd->beg_line && !sed_cmd->end_line
+				&& !sed_cmd->beg_match && !sed_cmd->end_match)
+			/* Or did we match the start of a numerical range? */
+			|| (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, 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 */
+				)
+			    )
+			/* Or does this line match our begin address regex? */
+			|| (beg_match(sed_cmd, pattern_space))
+			/* Or did we match last line of input? */
+			|| (sed_cmd->beg_line == -1 && next_line == NULL);
+
+		/* Snapshot the value */
+		matched = sed_cmd->in_match;
+
+		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) {
+				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 <= 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)
+				)
+			);
+		}
+
+		/* Skip blocks of commands we didn't match */
+		if (sed_cmd->cmd == '{') {
+			if (sed_cmd->invert ? matched : !matched) {
+				unsigned nest_cnt = 0;
+				while (1) {
+					if (sed_cmd->cmd == '{')
+						nest_cnt++;
+					if (sed_cmd->cmd == '}') {
+						nest_cnt--;
+						if (nest_cnt == 0)
+							break;
+					}
+					sed_cmd = sed_cmd->next;
+					if (!sed_cmd)
+						bb_error_msg_and_die("unterminated {");
+				}
+			}
+			continue;
+		}
+
+		/* Okay, so did this line match? */
+		if (sed_cmd->invert ? matched : !matched)
+			continue; /* no */
+
+		/* Update last used regex in case a blank substitute BRE is found */
+		if (sed_cmd->beg_match) {
+			G.previous_regex_ptr = sed_cmd->beg_match;
+		}
+
+		/* actual sedding */
+		//bb_error_msg("pattern_space:'%s' next_line:'%s' cmd:%c",
+		//pattern_space, next_line, sed_cmd->cmd);
+		switch (sed_cmd->cmd) {
+
+		/* Print line number */
+		case '=':
+			fprintf(G.nonstdout, "%d\n", linenum);
+			break;
+
+		/* Write the current pattern space up to the first newline */
+		case 'P':
+		{
+			char *tmp = strchr(pattern_space, '\n');
+			if (tmp) {
+				*tmp = '\0';
+				/* TODO: explain why '\n' below */
+				sed_puts(pattern_space, '\n');
+				*tmp = '\n';
+				break;
+			}
+			/* Fall Through */
+		}
+
+		/* Write the current pattern space to output */
+		case 'p':
+			/* NB: we print this _before_ the last line
+			 * (of current file) is printed. Even if
+			 * that line is nonterminated, we print
+			 * '\n' here (gnu sed does the same) */
+			sed_puts(pattern_space, '\n');
+			break;
+		/* Delete up through first newline */
+		case 'D':
+		{
+			char *tmp = strchr(pattern_space, '\n');
+			if (tmp) {
+				overlapping_strcpy(pattern_space, tmp + 1);
+				goto restart;
+			}
+		}
+		/* discard this line. */
+		case 'd':
+			goto discard_line;
+
+		/* Substitute with regex */
+		case 's':
+			if (!do_subst_command(sed_cmd, &pattern_space))
+				break;
+			substituted |= 1;
+
+			/* handle p option */
+			if (sed_cmd->sub_p)
+				sed_puts(pattern_space, last_gets_char);
+			/* handle w option */
+			if (sed_cmd->sw_file)
+				puts_maybe_newline(
+					pattern_space, sed_cmd->sw_file,
+					&sed_cmd->sw_last_char, last_gets_char);
+			break;
+
+		/* Append line to linked list to be printed later */
+		case 'a':
+			append(sed_cmd->string);
+			break;
+
+		/* Insert text before this line */
+		case 'i':
+			sed_puts(sed_cmd->string, '\n');
+			break;
+
+		/* Cut and paste text (replace) */
+		case 'c':
+			/* Only triggers on last line of a matching range. */
+			if (!sed_cmd->in_match)
+				sed_puts(sed_cmd->string, '\n');
+			goto discard_line;
+
+		/* Read file, append contents to output */
+		case 'r':
+		{
+			FILE *rfile;
+			rfile = fopen_for_read(sed_cmd->string);
+			if (rfile) {
+				char *line;
+
+				while ((line = xmalloc_fgetline(rfile))
+						!= NULL)
+					append(line);
+				xprint_and_close_file(rfile);
+			}
+
+			break;
+		}
+
+		/* Write pattern space to file. */
+		case 'w':
+			puts_maybe_newline(
+				pattern_space, sed_cmd->sw_file,
+				&sed_cmd->sw_last_char, last_gets_char);
+			break;
+
+		/* Read next line from input */
+		case 'n':
+			if (!G.be_quiet)
+				sed_puts(pattern_space, last_gets_char);
+			if (next_line) {
+				free(pattern_space);
+				pattern_space = next_line;
+				last_gets_char = next_gets_char;
+				next_line = get_next_line(&next_gets_char);
+				substituted = 0;
+				linenum++;
+				break;
+			}
+			/* fall through */
+
+		/* Quit.  End of script, end of input. */
+		case 'q':
+			/* Exit the outer while loop */
+			free(next_line);
+			next_line = NULL;
+			goto discard_commands;
+
+		/* Append the next line to the current line */
+		case 'N':
+		{
+			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) {
+				//goto discard_line;
+				goto discard_commands; /* GNU behavior */
+			}
+			/* Append next_line, read new next_line. */
+			len = strlen(pattern_space);
+			pattern_space = xrealloc(pattern_space, len + strlen(next_line) + 2);
+			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);
+			linenum++;
+			break;
+		}
+
+		/* Test/branch if substitution occurred */
+		case 't':
+			if (!substituted) break;
+			substituted = 0;
+			/* Fall through */
+		/* Test/branch if substitution didn't occur */
+		case 'T':
+			if (substituted) break;
+			/* Fall through */
+		/* Branch to label */
+		case 'b':
+			if (!sed_cmd->string) goto discard_commands;
+			else sed_cmd = branch_to(sed_cmd->string);
+			break;
+		/* Transliterate characters */
+		case 'y':
+		{
+			int i, j;
+			for (i = 0; pattern_space[i]; i++) {
+				for (j = 0; sed_cmd->string[j]; j += 2) {
+					if (pattern_space[i] == sed_cmd->string[j]) {
+						pattern_space[i] = sed_cmd->string[j + 1];
+						break;
+					}
+				}
+			}
+
+			break;
+		}
+		case 'g':	/* Replace pattern space with hold space */
+			free(pattern_space);
+			pattern_space = xstrdup(G.hold_space ? G.hold_space : "");
+			break;
+		case 'G':	/* Append newline and hold space to pattern space */
+		{
+			int pattern_space_size = 2;
+			int hold_space_size = 0;
+
+			if (pattern_space)
+				pattern_space_size += strlen(pattern_space);
+			if (G.hold_space)
+				hold_space_size = strlen(G.hold_space);
+			pattern_space = xrealloc(pattern_space,
+					pattern_space_size + hold_space_size);
+			if (pattern_space_size == 2)
+				pattern_space[0] = 0;
+			strcat(pattern_space, "\n");
+			if (G.hold_space)
+				strcat(pattern_space, G.hold_space);
+			last_gets_char = '\n';
+
+			break;
+		}
+		case 'h':	/* Replace hold space with pattern space */
+			free(G.hold_space);
+			G.hold_space = xstrdup(pattern_space);
+			break;
+		case 'H':	/* Append newline and pattern space to hold space */
+		{
+			int hold_space_size = 2;
+			int pattern_space_size = 0;
+
+			if (G.hold_space)
+				hold_space_size += strlen(G.hold_space);
+			if (pattern_space)
+				pattern_space_size = strlen(pattern_space);
+			G.hold_space = xrealloc(G.hold_space,
+					hold_space_size + pattern_space_size);
+
+			if (hold_space_size == 2)
+				*G.hold_space = 0;
+			strcat(G.hold_space, "\n");
+			if (pattern_space)
+				strcat(G.hold_space, pattern_space);
+
+			break;
+		}
+		case 'x': /* Exchange hold and pattern space */
+		{
+			char *tmp = pattern_space;
+			pattern_space = G.hold_space ? G.hold_space : xzalloc(1);
+			last_gets_char = '\n';
+			G.hold_space = tmp;
+			break;
+		}
+		} /* switch */
+	} /* for each cmd */
+
+	/*
+	 * Exit point from sedding...
+	 */
+ discard_commands:
+	/* we will print the line unless we were told to be quiet ('-n')
+	   or if the line was suppressed (ala 'd'elete) */
+	if (!G.be_quiet)
+		sed_puts(pattern_space, last_gets_char);
+
+	/* Delete and such jump here. */
+ discard_line:
+	flush_append();
+	free(pattern_space);
+
+	goto again;
+}
+
+/* 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.
+ */
+
+static void add_cmd_block(char *cmdstr)
+{
+	char *sv, *eol;
+
+	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;
+			}
+			*eol = '\0';
+		}
+		add_cmd(cmdstr);
+		cmdstr = eol + 1;
+	} while (eol);
+	free(sv);
+}
+
+int sed_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sed_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	llist_t *opt_e, *opt_f;
+	int status = EXIT_SUCCESS;
+
+	INIT_G();
+
+	/* destroy command strings on exit */
+	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")) {
+		puts("This is not GNU sed version 4.0");
+		return 0;
+	}
+
+	/* do normal option parsing */
+	opt_e = opt_f = NULL;
+	opt_complementary = "e::f::" /* can occur multiple times */
+	                    "nn"; /* count -n */
+	/* -i must be first, to match OPT_in_place definition */
+	opt = getopt32(argv, "irne:f:", &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
+	while (opt_e) { // -e
+		add_cmd_block(llist_pop(&opt_e));
+	}
+	while (opt_f) { // -f
+		char *line;
+		FILE *cmdfile;
+		cmdfile = xfopen_for_read(llist_pop(&opt_f));
+		while ((line = xmalloc_fgetline(cmdfile)) != NULL) {
+			add_cmd(line);
+			free(line);
+		}
+		fclose(cmdfile);
+	}
+	/* if we didn't get a pattern from -e or -f, use argv[0] */
+	if (!(opt & 0x18)) {
+		if (!*argv)
+			bb_show_usage();
+		add_cmd_block(*argv++);
+	}
+	/* Flush any unfinished commands. */
+	add_cmd("");
+
+	/* By default, we write to stdout */
+	G.nonstdout = stdout;
+
+	/* 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) {
+		if (opt & OPT_in_place)
+			bb_error_msg_and_die(bb_msg_requires_arg, "-i");
+		add_input_file(stdin);
+	} else {
+		int i;
+
+		for (i = 0; argv[i]; i++) {
+			struct stat statbuf;
+			int nonstdoutfd;
+			FILE *file;
+			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;
+			}
+			add_input_file(file);
+			if (!(opt & OPT_in_place)) {
+				continue;
+			}
+
+			/* -i: process each FILE separately: */
+
+			G.outname = xasprintf("%sXXXXXX", argv[i]);
+			nonstdoutfd = xmkstemp(G.outname);
+			G.nonstdout = xfdopen_for_write(nonstdoutfd);
+
+			/* Set permissions/owner of output file */
+			fstat(fileno(file), &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);
+
+			process_files();
+			fclose(G.nonstdout);
+			G.nonstdout = stdout;
+
+			/* unlink(argv[i]); */
+			xrename(G.outname, argv[i]);
+			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;
+		 * but it's not needed since process_files() works correctly
+		 * in this case too. */
+	}
+	process_files();
+
+	return status;
+}
diff --git a/busybox-1.19.3/editors/sed1line.txt b/busybox-1.19.3/editors/sed1line.txt
new file mode 100644
index 0000000..11a2e36
--- /dev/null
+++ b/busybox-1.19.3/editors/sed1line.txt
@@ -0,0 +1,425 @@
+http://www.student.northpark.edu/pemente/sed/sed1line.txt
+-------------------------------------------------------------------------
+HANDY ONE-LINERS FOR SED (Unix stream editor)               Apr. 26, 2004
+compiled by Eric Pement - pemente[at]northpark[dot]edu        version 5.4
+Latest version of this file is usually at:
+   http://sed.sourceforge.net/sed1line.txt
+   http://www.student.northpark.edu/pemente/sed/sed1line.txt
+This file is also available in Portuguese at:
+   http://www.lrv.ufsc.br/wmaker/sed_ptBR.html
+
+FILE SPACING:
+
+ # double space a file
+ sed G
+
+ # double space a file which already has blank lines in it. Output file
+ # should contain no more than one blank line between lines of text.
+ sed '/^$/d;G'
+
+ # triple space a file
+ sed 'G;G'
+
+ # undo double-spacing (assumes even-numbered lines are always blank)
+ sed 'n;d'
+
+ # insert a blank line above every line which matches "regex"
+ sed '/regex/{x;p;x;}'
+
+ # insert a blank line below every line which matches "regex"
+ sed '/regex/G'
+
+ # insert a blank line above and below every line which matches "regex"
+ sed '/regex/{x;p;x;G;}'
+
+NUMBERING:
+
+ # number each line of a file (simple left alignment). Using a tab (see
+ # note on '\t' at end of file) instead of space will preserve margins.
+ sed = filename | sed 'N;s/\n/\t/'
+
+ # number each line of a file (number on left, right-aligned)
+ sed = filename | sed 'N; s/^/     /; s/ *\(.\{6,\}\)\n/\1  /'
+
+ # number each line of file, but only print numbers if line is not blank
+ sed '/./=' filename | sed '/./N; s/\n/ /'
+
+ # count lines (emulates "wc -l")
+ sed -n '$='
+
+TEXT CONVERSION AND SUBSTITUTION:
+
+ # IN UNIX ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format
+ sed 's/.$//'               # assumes that all lines end with CR/LF
+ sed 's/^M$//'              # in bash/tcsh, press Ctrl-V then Ctrl-M
+ sed 's/\x0D$//'            # gsed 3.02.80, but top script is easier
+
+ # IN UNIX ENVIRONMENT: convert Unix newlines (LF) to DOS format
+ sed "s/$/`echo -e \\\r`/"            # command line under ksh
+ sed 's/$'"/`echo \\\r`/"             # command line under bash
+ sed "s/$/`echo \\\r`/"               # command line under zsh
+ sed 's/$/\r/'                        # gsed 3.02.80
+
+ # IN DOS ENVIRONMENT: convert Unix newlines (LF) to DOS format
+ sed "s/$//"                          # method 1
+ sed -n p                             # method 2
+
+ # IN DOS ENVIRONMENT: convert DOS newlines (CR/LF) to Unix format
+ # Can only be done with UnxUtils sed, version 4.0.7 or higher.
+ # Cannot be done with other DOS versions of sed. Use "tr" instead.
+ sed "s/\r//" infile >outfile         # UnxUtils sed v4.0.7 or higher
+ tr -d \r <infile >outfile            # GNU tr version 1.22 or higher
+
+ # delete leading whitespace (spaces, tabs) from front of each line
+ # aligns all text flush left
+ sed 's/^[ \t]*//'                    # see note on '\t' at end of file
+
+ # delete trailing whitespace (spaces, tabs) from end of each line
+ sed 's/[ \t]*$//'                    # see note on '\t' at end of file
+
+ # delete BOTH leading and trailing whitespace from each line
+ sed 's/^[ \t]*//;s/[ \t]*$//'
+
+ # insert 5 blank spaces at beginning of each line (make page offset)
+ sed 's/^/     /'
+
+ # align all text flush right on a 79-column width
+ sed -e :a -e 's/^.\{1,78\}$/ &/;ta'  # set at 78 plus 1 space
+
+ # center all text in the middle of 79-column width. In method 1,
+ # spaces at the beginning of the line are significant, and trailing
+ # spaces are appended at the end of the line. In method 2, spaces at
+ # the beginning of the line are discarded in centering the line, and
+ # no trailing spaces appear at the end of lines.
+ sed  -e :a -e 's/^.\{1,77\}$/ & /;ta'                     # method 1
+ sed  -e :a -e 's/^.\{1,77\}$/ &/;ta' -e 's/\( *\)\1/\1/'  # method 2
+
+ # substitute (find and replace) "foo" with "bar" on each line
+ sed 's/foo/bar/'             # replaces only 1st instance in a line
+ sed 's/foo/bar/4'            # replaces only 4th instance in a line
+ sed 's/foo/bar/g'            # replaces ALL instances in a line
+ sed 's/\(.*\)foo\(.*foo\)/\1bar\2/' # replace the next-to-last case
+ sed 's/\(.*\)foo/\1bar/'            # replace only the last case
+
+ # substitute "foo" with "bar" ONLY for lines which contain "baz"
+ sed '/baz/s/foo/bar/g'
+
+ # substitute "foo" with "bar" EXCEPT for lines which contain "baz"
+ sed '/baz/!s/foo/bar/g'
+
+ # change "scarlet" or "ruby" or "puce" to "red"
+ sed 's/scarlet/red/g;s/ruby/red/g;s/puce/red/g'   # most seds
+ gsed 's/scarlet\|ruby\|puce/red/g'                # GNU sed only
+
+ # reverse order of lines (emulates "tac")
+ # bug/feature in HHsed v1.5 causes blank lines to be deleted
+ sed '1!G;h;$!d'               # method 1
+ sed -n '1!G;h;$p'             # method 2
+
+ # reverse each character on the line (emulates "rev")
+ sed '/\n/!G;s/\(.\)\(.*\n\)/&\2\1/;//D;s/.//'
+
+ # join pairs of lines side-by-side (like "paste")
+ sed '$!N;s/\n/ /'
+
+ # if a line ends with a backslash, append the next line to it
+ sed -e :a -e '/\\$/N; s/\\\n//; ta'
+
+ # if a line begins with an equal sign, append it to the previous line
+ # and replace the "=" with a single space
+ sed -e :a -e '$!N;s/\n=/ /;ta' -e 'P;D'
+
+ # add commas to numeric strings, changing "1234567" to "1,234,567"
+ gsed ':a;s/\B[0-9]\{3\}\>/,&/;ta'                     # GNU sed
+ sed -e :a -e 's/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta'  # other seds
+
+ # add commas to numbers with decimal points and minus signs (GNU sed)
+ gsed ':a;s/\(^\|[^0-9.]\)\([0-9]\+\)\([0-9]\{3\}\)/\1\2,\3/g;ta'
+
+ # add a blank line every 5 lines (after lines 5, 10, 15, 20, etc.)
+ gsed '0~5G'                  # GNU sed only
+ sed 'n;n;n;n;G;'             # other seds
+
+SELECTIVE PRINTING OF CERTAIN LINES:
+
+ # print first 10 lines of file (emulates behavior of "head")
+ sed 10q
+
+ # print first line of file (emulates "head -1")
+ sed q
+
+ # print the last 10 lines of a file (emulates "tail")
+ sed -e :a -e '$q;N;11,$D;ba'
+
+ # print the last 2 lines of a file (emulates "tail -2")
+ sed '$!N;$!D'
+
+ # print the last line of a file (emulates "tail -1")
+ sed '$!d'                    # method 1
+ sed -n '$p'                  # method 2
+
+ # print only lines which match regular expression (emulates "grep")
+ sed -n '/regexp/p'           # method 1
+ sed '/regexp/!d'             # method 2
+
+ # print only lines which do NOT match regexp (emulates "grep -v")
+ sed -n '/regexp/!p'          # method 1, corresponds to above
+ sed '/regexp/d'              # method 2, simpler syntax
+
+ # print the line immediately before a regexp, but not the line
+ # containing the regexp
+ sed -n '/regexp/{g;1!p;};h'
+
+ # print the line immediately after a regexp, but not the line
+ # containing the regexp
+ sed -n '/regexp/{n;p;}'
+
+ # print 1 line of context before and after regexp, with line number
+ # indicating where the regexp occurred (similar to "grep -A1 -B1")
+ sed -n -e '/regexp/{=;x;1!p;g;$!N;p;D;}' -e h
+
+ # grep for AAA and BBB and CCC (in any order)
+ sed '/AAA/!d; /BBB/!d; /CCC/!d'
+
+ # grep for AAA and BBB and CCC (in that order)
+ sed '/AAA.*BBB.*CCC/!d'
+
+ # grep for AAA or BBB or CCC (emulates "egrep")
+ sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d    # most seds
+ gsed '/AAA\|BBB\|CCC/!d'                        # GNU sed only
+
+ # print paragraph if it contains AAA (blank lines separate paragraphs)
+ # HHsed v1.5 must insert a 'G;' after 'x;' in the next 3 scripts below
+ sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;'
+
+ # print paragraph if it contains AAA and BBB and CCC (in any order)
+ sed -e '/./{H;$!d;}' -e 'x;/AAA/!d;/BBB/!d;/CCC/!d'
+
+ # print paragraph if it contains AAA or BBB or CCC
+ sed -e '/./{H;$!d;}' -e 'x;/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
+ gsed '/./{H;$!d;};x;/AAA\|BBB\|CCC/b;d'         # GNU sed only
+
+ # print only lines of 65 characters or longer
+ sed -n '/^.\{65\}/p'
+
+ # print only lines of less than 65 characters
+ sed -n '/^.\{65\}/!p'        # method 1, corresponds to above
+ sed '/^.\{65\}/d'            # method 2, simpler syntax
+
+ # print section of file from regular expression to end of file
+ sed -n '/regexp/,$p'
+
+ # print section of file based on line numbers (lines 8-12, inclusive)
+ sed -n '8,12p'               # method 1
+ sed '8,12!d'                 # method 2
+
+ # print line number 52
+ sed -n '52p'                 # method 1
+ sed '52!d'                   # method 2
+ sed '52q;d'                  # method 3, efficient on large files
+
+ # beginning at line 3, print every 7th line
+ gsed -n '3~7p'               # GNU sed only
+ sed -n '3,${p;n;n;n;n;n;n;}' # other seds
+
+ # print section of file between two regular expressions (inclusive)
+ sed -n '/Iowa/,/Montana/p'             # case sensitive
+
+SELECTIVE DELETION OF CERTAIN LINES:
+
+ # print all of file EXCEPT section between 2 regular expressions
+ sed '/Iowa/,/Montana/d'
+
+ # delete duplicate, consecutive lines from a file (emulates "uniq").
+ # First line in a set of duplicate lines is kept, rest are deleted.
+ sed '$!N; /^\(.*\)\n\1$/!P; D'
+
+ # delete duplicate, nonconsecutive lines from a file. Beware not to
+ # overflow the buffer size of the hold space, or else use GNU sed.
+ sed -n 'G; s/\n/&&/; /^\([ -~]*\n\).*\n\1/d; s/\n//; h; P'
+
+ # delete all lines except duplicate lines (emulates "uniq -d").
+ sed '$!N; s/^\(.*\)\n\1$/\1/; t; D'
+
+ # delete the first 10 lines of a file
+ sed '1,10d'
+
+ # delete the last line of a file
+ sed '$d'
+
+ # delete the last 2 lines of a file
+ sed 'N;$!P;$!D;$d'
+
+ # delete the last 10 lines of a file
+ sed -e :a -e '$d;N;2,10ba' -e 'P;D'   # method 1
+ sed -n -e :a -e '1,10!{P;N;D;};N;ba'  # method 2
+
+ # delete every 8th line
+ gsed '0~8d'                           # GNU sed only
+ sed 'n;n;n;n;n;n;n;d;'                # other seds
+
+ # delete ALL blank lines from a file (same as "grep '.' ")
+ sed '/^$/d'                           # method 1
+ sed '/./!d'                           # method 2
+
+ # delete all CONSECUTIVE blank lines from file except the first; also
+ # deletes all blank lines from top and end of file (emulates "cat -s")
+ sed '/./,/^$/!d'          # method 1, allows 0 blanks at top, 1 at EOF
+ sed '/^$/N;/\n$/D'        # method 2, allows 1 blank at top, 0 at EOF
+
+ # delete all CONSECUTIVE blank lines from file except the first 2:
+ sed '/^$/N;/\n$/N;//D'
+
+ # delete all leading blank lines at top of file
+ sed '/./,$!d'
+
+ # delete all trailing blank lines at end of file
+ sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'  # works on all seds
+ sed -e :a -e '/^\n*$/N;/\n$/ba'        # ditto, except for gsed 3.02*
+
+ # delete the last line of each paragraph
+ sed -n '/^$/{p;h;};/./{x;/./p;}'
+
+SPECIAL APPLICATIONS:
+
+ # remove nroff overstrikes (char, backspace) from man pages. The 'echo'
+ # command may need an -e switch if you use Unix System V or bash shell.
+ sed "s/.`echo \\\b`//g"    # double quotes required for Unix environment
+ sed 's/.^H//g'             # in bash/tcsh, press Ctrl-V and then Ctrl-H
+ sed 's/.\x08//g'           # hex expression for sed v1.5
+
+ # get Usenet/e-mail message header
+ sed '/^$/q'                # deletes everything after first blank line
+
+ # get Usenet/e-mail message body
+ sed '1,/^$/d'              # deletes everything up to first blank line
+
+ # get Subject header, but remove initial "Subject: " portion
+ sed '/^Subject: */!d; s///;q'
+
+ # get return address header
+ sed '/^Reply-To:/q; /^From:/h; /./d;g;q'
+
+ # parse out the address proper. Pulls out the e-mail address by itself
+ # from the 1-line return address header (see preceding script)
+ sed 's/ *(.*)//; s/>.*//; s/.*[:<] *//'
+
+ # add a leading angle bracket and space to each line (quote a message)
+ sed 's/^/> /'
+
+ # delete leading angle bracket & space from each line (unquote a message)
+ sed 's/^> //'
+
+ # remove most HTML tags (accommodates multiple-line tags)
+ sed -e :a -e 's/<[^>]*>//g;/</N;//ba'
+
+ # extract multi-part uuencoded binaries, removing extraneous header
+ # info, so that only the uuencoded portion remains. Files passed to
+ # sed must be passed in the proper order. Version 1 can be entered
+ # from the command line; version 2 can be made into an executable
+ # Unix shell script. (Modified from a script by Rahul Dhesi.)
+ sed '/^end/,/^begin/d' file1 file2 ... fileX | uudecode   # vers. 1
+ sed '/^end/,/^begin/d' "$@" | uudecode                    # vers. 2
+
+ # zip up each .TXT file individually, deleting the source file and
+ # setting the name of each .ZIP file to the basename of the .TXT file
+ # (under DOS: the "dir /b" switch returns bare filenames in all caps).
+ echo @echo off >zipup.bat
+ dir /b *.txt | sed "s/^\(.*\)\.TXT/pkzip -mo \1 \1.TXT/" >>zipup.bat
+
+TYPICAL USE: Sed takes one or more editing commands and applies all of
+them, in sequence, to each line of input. After all the commands have
+been applied to the first input line, that line is output and a second
+input line is taken for processing, and the cycle repeats. The
+preceding examples assume that input comes from the standard input
+device (i.e, the console, normally this will be piped input). One or
+more filenames can be appended to the command line if the input does
+not come from stdin. Output is sent to stdout (the screen). Thus:
+
+ cat filename | sed '10q'        # uses piped input
+ sed '10q' filename              # same effect, avoids a useless "cat"
+ sed '10q' filename > newfile    # redirects output to disk
+
+For additional syntax instructions, including the way to apply editing
+commands from a disk file instead of the command line, consult "sed &
+awk, 2nd Edition," by Dale Dougherty and Arnold Robbins (O'Reilly,
+1997; http://www.ora.com), "UNIX Text Processing," by Dale Dougherty
+and Tim O'Reilly (Hayden Books, 1987) or the tutorials by Mike Arst
+distributed in U-SEDIT2.ZIP (many sites). To fully exploit the power
+of sed, one must understand "regular expressions." For this, see
+"Mastering Regular Expressions" by Jeffrey Friedl (O'Reilly, 1997).
+The manual ("man") pages on Unix systems may be helpful (try "man
+sed", "man regexp", or the subsection on regular expressions in "man
+ed"), but man pages are notoriously difficult. They are not written to
+teach sed use or regexps to first-time users, but as a reference text
+for those already acquainted with these tools.
+
+QUOTING SYNTAX: The preceding examples use single quotes ('...')
+instead of double quotes ("...") to enclose editing commands, since
+sed is typically used on a Unix platform. Single quotes prevent the
+Unix shell from intrepreting the dollar sign ($) and backquotes
+(`...`), which are expanded by the shell if they are enclosed in
+double quotes. Users of the "csh" shell and derivatives will also need
+to quote the exclamation mark (!) with the backslash (i.e., \!) to
+properly run the examples listed above, even within single quotes.
+Versions of sed written for DOS invariably require double quotes
+("...") instead of single quotes to enclose editing commands.
+
+USE OF '\t' IN SED SCRIPTS: For clarity in documentation, we have used
+the expression '\t' to indicate a tab character (0x09) in the scripts.
+However, most versions of sed do not recognize the '\t' abbreviation,
+so when typing these scripts from the command line, you should press
+the TAB key instead. '\t' is supported as a regular expression
+metacharacter in awk, perl, and HHsed, sedmod, and GNU sed v3.02.80.
+
+VERSIONS OF SED: Versions of sed do differ, and some slight syntax
+variation is to be expected. In particular, most do not support the
+use of labels (:name) or branch instructions (b,t) within editing
+commands, except at the end of those commands. We have used the syntax
+which will be portable to most users of sed, even though the popular
+GNU versions of sed allow a more succinct syntax. When the reader sees
+a fairly long command such as this:
+
+   sed -e '/AAA/b' -e '/BBB/b' -e '/CCC/b' -e d
+
+it is heartening to know that GNU sed will let you reduce it to:
+
+   sed '/AAA/b;/BBB/b;/CCC/b;d'      # or even
+   sed '/AAA\|BBB\|CCC/b;d'
+
+In addition, remember that while many versions of sed accept a command
+like "/one/ s/RE1/RE2/", some do NOT allow "/one/! s/RE1/RE2/", which
+contains space before the 's'. Omit the space when typing the command.
+
+OPTIMIZING FOR SPEED: If execution speed needs to be increased (due to
+large input files or slow processors or hard disks), substitution will
+be executed more quickly if the "find" expression is specified before
+giving the "s/.../.../" instruction. Thus:
+
+   sed 's/foo/bar/g' filename         # standard replace command
+   sed '/foo/ s/foo/bar/g' filename   # executes more quickly
+   sed '/foo/ s//bar/g' filename      # shorthand sed syntax
+
+On line selection or deletion in which you only need to output lines
+from the first part of the file, a "quit" command (q) in the script
+will drastically reduce processing time for large files. Thus:
+
+   sed -n '45,50p' filename           # print line nos. 45-50 of a file
+   sed -n '51q;45,50p' filename       # same, but executes much faster
+
+If you have any additional scripts to contribute or if you find errors
+in this document, please send e-mail to the compiler. Indicate the
+version of sed you used, the operating system it was compiled for, and
+the nature of the problem. Various scripts in this file were written
+or contributed by:
+
+ Al Aab <af137@freenet.toronto.on.ca>   # "seders" list moderator
+ Edgar Allen <era@sky.net>              # various
+ Yiorgos Adamopoulos <adamo@softlab.ece.ntua.gr>
+ Dale Dougherty <dale@songline.com>     # author of "sed & awk"
+ Carlos Duarte <cdua@algos.inesc.pt>    # author of "do it with sed"
+ Eric Pement <pemente@northpark.edu>    # author of this document
+ Ken Pizzini <ken@halcyon.com>          # author of GNU sed v3.02
+ S.G. Ravenhall <stew.ravenhall@totalise.co.uk> # great de-html script
+ Greg Ubben <gsu@romulus.ncsc.mil>      # many contributions & much help
+-------------------------------------------------------------------------
diff --git a/busybox-1.19.3/editors/sed_summary.htm b/busybox-1.19.3/editors/sed_summary.htm
new file mode 100644
index 0000000..34e72b0
--- /dev/null
+++ b/busybox-1.19.3/editors/sed_summary.htm
@@ -0,0 +1,223 @@
+<html>
+
+<head><title>Command Summary for sed (sed & awk, Second Edition)</title>
+</head>
+
+<body>
+
+<h2>Command Summary for sed</h2>
+
+<dl>
+
+<dt><b>: </b> <b> :</b><em>label</em></dt>
+<dd>Label a line in the script for the transfer of control by
+<b>b</b> or <b>t</b>.
+<em>label</em> may contain up to seven characters.
+(The POSIX standard says that an implementation can allow longer
+labels if it wishes to. GNU sed allows labels to be of any length.)
+</p></dd>
+
+
+<dt><b>=</b> [<em>address</em>]<b>=</b></dt>
+<dd>Write to standard output the line number of addressed line.</p></dd>
+
+
+<dt><b>a</b> [<em>address</em>]<b>a\</b></dt>
+<dd><em>text</em></p>
+
+<p>Append <em>text</em>
+following each line matched by <em>address</em>.  If
+<em>text</em> goes over more than one line, newlines
+must be "hidden" by preceding them with a backslash.  The
+<em>text</em> will be terminated by the first
+newline that is not hidden in this way.  The
+<em>text</em> is not available in the pattern space
+and subsequent commands cannot be applied to it.  The results of this
+command are sent to standard output when the list of editing commands
+is finished, regardless of what happens to the current line in the
+pattern space.</p></dd>
+
+
+<dt><b>b</b> [<em>address1</em>[,<em>address2</em>]]<b>b</b>[<em>label</em>]</dt>
+<dd>Transfer control unconditionally (branch) to
+<b>:</b><em>label</em> elsewhere in
+script.  That is, the command following the
+<em>label</em> is the next command applied to the
+current line.  If no <em>label</em> is specified,
+control falls through to the end of the script, so no more commands
+are applied to the current line.</p></dd>
+
+
+<dt><b>c</b> [<em>address1</em>[,<em>address2</em>]]<b>c\</b></dt>
+<dd><em>text</em></p>
+
+<p>Replace (change) the lines selected by the address with
+<em>text</em>.  When a range of lines is specified,
+all lines as a group are replaced by a single copy of
+<em>text</em>.  The newline following each line of
+<em>text</em> must be escaped by a backslash, except
+the last line.  The contents of the pattern space are, in effect,
+deleted and no subsequent editing commands can be applied to it (or to
+<em>text</em>).</p></dd>
+
+
+<dt><b>d</b> [<em>address1</em>[,<em>address2</em>]]<b>d</b></dt>
+<dd>Delete line(s) from pattern space.  Thus, the line is not passed to standard
+output. A new line of input is read and editing resumes with first
+command in script.</p></dd>
+
+
+<dt><b>D</b> [<em>address1</em>[,<em>address2</em>]]<b>D</b></dt>
+<dd>Delete first part (up to embedded newline) of multiline pattern space created
+by <b>N</b> command and resume editing with first command in
+script.  If this command empties the pattern space, then a new line
+of input is read, as if the <b>d</b> command had been executed.</p></dd>
+
+
+<dt><b>g</b> [<em>address1</em>[,<em>address2</em>]]<b>g</b></dt>
+<dd>Copy (get) contents of hold space (see <b>h</b> or
+<b>H</b> command) into the pattern space, wiping out
+previous contents.</p></dd>
+
+
+<dt><b>G</b> [<em>address1</em>[,<em>address2</em>]]<b>G</b></dt>
+<dd>Append newline followed by contents of hold space (see
+<b>h</b> or <b>H</b> command) to contents of
+the pattern space.  If hold space is empty, a newline is still
+appended to the pattern space.</p></dd>
+
+
+<dt><b>h</b> [<em>address1</em>[,<em>address2</em>]]<b>h</b></dt>
+<dd>Copy pattern space into hold space, a special temporary buffer.
+Previous contents of hold space are wiped out.</p></dd>
+
+
+<dt><b>H</b> [<em>address1</em>[,<em>address2</em>]]<b>H</b></dt>
+<dd>Append newline and contents of pattern space to contents of the hold
+space.  Even if hold space is empty, this command still appends the
+newline first.</p></dd>
+
+
+<dt><b>i</b> [<em>address1</em>]<b>i\</b></dt>
+<dd><em>text</em></p>
+
+<p>Insert <em>text</em> before each line matched by
+<em>address</em>. (See <b>a</b> for
+details on <em>text</em>.)</p></dd>
+
+
+<dt><b>l</b> [<em>address1</em>[,<em>address2</em>]]<b>l</b></dt>
+<dd>List the contents of the pattern space, showing nonprinting characters
+as ASCII codes.  Long lines are wrapped.</p></dd>
+
+
+<dt><b>n</b> [<em>address1</em>[,<em>address2</em>]]<b>n</b></dt>
+<dd>Read next line of input into pattern space.  Current line is sent to
+standard output.  New line becomes current line and increments line
+counter.  Control passes to command following <b>n</b>
+instead of resuming at the top of the script.</p></dd>
+
+
+<dt><b>N</b> [<em>address1</em>[,<em>address2</em>]]<b>N</b></dt>
+<dd>Append next input line to contents of pattern space; the new line is
+separated from the previous contents of the pattern space by a newline.
+(This command is designed to allow pattern matches across two
+lines.  Using \n to match the embedded newline, you can match
+patterns across multiple lines.)</p></dd>
+
+
+<dt><b>p</b> [<em>address1</em>[,<em>address2</em>]]<b>p</b></dt>
+<dd>Print the addressed line(s).  Note that this can result in duplicate
+output unless default output is suppressed by using "#n" or
+the <span class="option">-n</span>
+
+command-line option.  Typically used before commands that change flow
+control (<b>d</b>, <b>n</b>,
+<b>b</b>) and might prevent the current line from being
+output.</p></dd>
+
+
+<dt><b>P</b> [<em>address1</em>[,<em>address2</em>]]<b>P</b></dt>
+<dd>Print first part (up to embedded newline) of multiline pattern space
+created by <b>N</b> command.  Same as <b>p</b>
+if <b>N</b> has not been applied to a line.</p></dd>
+
+
+<dt><b>q</b> [<em>address</em>]<b>q</b></dt>
+<dd>Quit when <em>address</em> is encountered.  The
+addressed line is first written to output (if default output is not
+suppressed), along with any text appended to it by previous
+<b>a</b> or <b>r</b> commands.</p></dd>
+
+
+<dt><b>r</b> [<em>address</em>]<b>r</b> <em>file</em></dt>
+<dd>Read contents of <em>file</em> and append after the
+contents of the pattern space.  Exactly one space must be put between
+<b>r</b> and the filename.</p></dd>
+
+
+<dt><b>s</b> [<em>address1</em>[,<em>address2</em>]]<b>s</b>/<em>pattern</em>/<em>replacement</em>/[<em>flags</em>]</dt>
+<dd>Substitute <em>replacement</em> for
+<em>pattern</em> on each addressed line.  If pattern
+addresses are used, the pattern <b>//</b> represents the
+last pattern address specified.  The following flags can be specified:</p>
+
+	<dl>
+
+	<dt><b>n</b></dt>
+	<dd>Replace <em>n</em>th instance of
+	/<em>pattern</em>/ on each addressed line.
+	<em>n</em> is any number in the range 1 to 512, and
+	the default is 1.</p></dd>
+
+	<dt><b>g</b></dt>
+	<dd>Replace all instances of /<em>pattern</em>/ on each
+	addressed line, not just the first instance.</p></dd>
+
+	<dt><b>I</b></dt>
+	<dd>Matching is case-insensitive.<p></p></dd>
+
+	<dt><b>p</b></dt>
+	<dd>Print the line if a successful substitution is done.  If several
+	successful substitutions are done, multiple copies of the line will be
+	printed.</p></dd>
+
+	<dt><b>w</b> <em>file</em></dt>
+	<dd>Write the line to <em>file</em> if a replacement
+	was done.  A maximum of 10 different <em>files</em> can be opened.</p></dd>
+
+	</dl>
+
+</dd>
+
+
+<dt><b>t</b> [<em>address1</em>[,<em>address2</em>]]<b>t </b>[<em>label</em>]</dt>
+<dd>Test if successful substitutions have been made on addressed lines,
+and if so, branch to line marked by :<em>label</em>.
+(See <b>b</b> and <b>:</b>.)  If label is not
+specified, control falls through to bottom of script.</p></dd>
+
+
+<dt><b>w</b> [<em>address1</em>[,<em>address2</em>]]<b>w</b> <em>file</em></dt>
+<dd>Append contents of pattern space to <em>file</em>.
+This action occurs when the command is encountered rather than when
+the pattern space is output.  Exactly one space must separate the
+<b>w</b> and the filename.  A maximum of 10 different
+files can be opened in a script.  This command will create the file if
+it does not exist; if the file exists, its contents will be
+overwritten each time the script is executed.  Multiple write commands
+that direct output to the same file append to the end of the file.</p></dd>
+
+
+<dt><b>x</b> [<em>address1</em>[,<em>address2</em>]]<b>x</b></dt>
+<dd>Exchange contents of the pattern space with the contents of the hold
+space.</p></dd>
+
+
+<dt><b>y</b> [<em>address1</em>[,<em>address2</em>]]<b>y</b>/<em>abc</em>/<em>xyz</em>/</dt>
+<dd>Transform each character by position in string
+<em>abc</em> to its equivalent in string
+<em>xyz</em>.</p></dd>
+
+
+</dl>
diff --git a/busybox-1.19.3/editors/vi.c b/busybox-1.19.3/editors/vi.c
new file mode 100644
index 0000000..96a0c8d
--- /dev/null
+++ b/busybox-1.19.3/editors/vi.c
@@ -0,0 +1,4158 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tiny vi.c: A small 'vi' clone
+ * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * Things To Do:
+ *	EXINIT
+ *	$HOME/.exrc  and  ./.exrc
+ *	add magic to search	/foo.*bar
+ *	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
+ *	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.
+//config:
+//config:config FEATURE_VI_OPTIMIZE_CURSOR
+//config:	bool "Optimize cursor movement"
+//config:	default y
+//config:	depends on VI
+//config:	help
+//config:	  This will make the cursor movement faster, but requires more memory
+//config:	  and it makes the applet a tiny bit larger.
+
+//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	Initial command to run ($EXINIT also available)"
+//usage:	)
+//usage:	IF_FEATURE_VI_READONLY(
+//usage:     "\n	-R	Read-only"
+//usage:	)
+//usage:     "\n	-H	Short help regarding 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
+
+
+#if ENABLE_LOCALE_SUPPORT
+
+#if ENABLE_FEATURE_VI_8BIT
+//FIXME: this does not work properly for Unicode anyway
+# define Isprint(c) (isprint)(c)
+#else
+# define Isprint(c) isprint_asciionly(c)
+#endif
+
+#else
+
+/* 0x9b is Meta-ESC */
+#if ENABLE_FEATURE_VI_8BIT
+# define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
+#else
+# define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
+#endif
+
+#endif
+
+
+enum {
+	MAX_TABSTOP = 32, // sanity limit
+	// User input len. Need not be extra big.
+	// Lines in file being edited *can* be bigger than this.
+	MAX_INPUT_LEN = 128,
+	// Sanity limits. We have only one buffer of this size.
+	MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
+	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"
+
+#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
+// cmds modifying text[]
+// vda: removed "aAiIs" as they switch us into insert mode
+// and remembering input for replay after them makes no sense
+static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
+#endif
+
+enum {
+	YANKONLY = FALSE,
+	YANKDEL = TRUE,
+	FORWARD = 1,	// code depends on "1"  for array index
+	BACK = -1,	// code depends on "-1" for array index
+	LIMITED = 0,	// how much of text[] in char_search
+	FULL = 1,	// how much of text[] in char_search
+
+	S_BEFORE_WS = 1,	// used in skip_thing() for moving "dot"
+	S_TO_WS = 2,		// used in skip_thing() for moving "dot"
+	S_OVER_WS = 3,		// used in skip_thing() for moving "dot"
+	S_END_PUNCT = 4,	// used in skip_thing() for moving "dot"
+	S_END_ALNUM = 5,	// used in skip_thing() for moving "dot"
+};
+
+
+/* vi.c expects chars to be unsigned. */
+/* busybox build system provides that, but it's better */
+/* to audit and fix the source */
+
+struct globals {
+	/* many references - keep near the top of globals */
+	char *text, *end;       // pointers to the user data in memory
+	char *dot;              // where all the action takes place
+	int text_size;		// size of the allocated buffer
+
+	/* the rest */
+	smallint vi_setops;
+#define VI_AUTOINDENT 1
+#define VI_SHOWMATCH  2
+#define VI_IGNORECASE 4
+#define VI_ERR_METHOD 8
+#define autoindent (vi_setops & VI_AUTOINDENT)
+#define showmatch  (vi_setops & VI_SHOWMATCH )
+#define ignorecase (vi_setops & VI_IGNORECASE)
+/* indicate error with beep or flash */
+#define err_method (vi_setops & VI_ERR_METHOD)
+
+#if ENABLE_FEATURE_VI_READONLY
+	smallint readonly_mode;
+#define SET_READONLY_FILE(flags)        ((flags) |= 0x01)
+#define SET_READONLY_MODE(flags)        ((flags) |= 0x02)
+#define UNSET_READONLY_FILE(flags)      ((flags) &= 0xfe)
+#else
+#define SET_READONLY_FILE(flags)        ((void)0)
+#define SET_READONLY_MODE(flags)        ((void)0)
+#define UNSET_READONLY_FILE(flags)      ((void)0)
+#endif
+
+	smallint editing;        // >0 while we are editing a file
+	                         // [code audit says "can be 0, 1 or 2 only"]
+	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
+#if ENABLE_FEATURE_VI_ASK_TERMINAL
+	int get_rowcol_error;
+#endif
+	int crow, ccol;          // cursor is on Crow x Ccol
+	int offset;              // chars scrolled off the screen to the left
+	int have_status_msg;     // is default edit status needed?
+	                         // [don't make smallint!]
+	int last_status_cksum;   // hash of current status line
+	char *current_filename;
+	char *screenbegin;       // index into text[], of top line on the screen
+	char *screen;            // pointer to the virtual screen buffer
+	int screensize;          //            and its size
+	int tabstop;
+	int last_forward_char;   // last char searched for with 'f' (int because of Unicode)
+	char erase_char;         // the users erase character
+	char last_input_char;    // last char read from user
+
+#if ENABLE_FEATURE_VI_DOT_CMD
+	smallint adding2q;	 // are we currently adding user input to q
+	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
+#if ENABLE_FEATURE_VI_SEARCH
+	char *last_search_pattern; // last pattern from a '/' or '?' search
+#endif
+
+	/* former statics */
+#if ENABLE_FEATURE_VI_YANKMARK
+	char *edit_file__cur_line;
+#endif
+	int refresh__old_offset;
+	int format_edit_status__tot;
+
+	/* a few references only */
+#if ENABLE_FEATURE_VI_YANKMARK
+	int YDreg, Ureg;        // default delete register and orig line for "U"
+	char *reg[28];          // named register a-z, "D", and "U" 0-25,26,27
+	char *mark[28];         // user marks points somewhere in text[]-  a-z and previous context ''
+	char *context_start, *context_end;
+#endif
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+	sigjmp_buf restart;     // catch_sig()
+#endif
+	struct termios term_orig, term_vi; // remember what the cooked mode was
+#if ENABLE_FEATURE_VI_COLON
+	char *initial_cmds[3];  // currently 2 entries, NULL terminated
+#endif
+	// Should be just enough to hold a key sequence,
+	// but CRASHME mode uses it as generated command buffer too
+#if ENABLE_FEATURE_VI_CRASHME
+	char readbuffer[128];
+#else
+	char readbuffer[KEYCODE_BUFFER_SIZE];
+#endif
+#define STATUS_BUFFER_LEN  200
+	char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
+#if ENABLE_FEATURE_VI_DOT_CMD
+	char last_modifying_cmd[MAX_INPUT_LEN];	// last modifying cmd for "."
+#endif
+	char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
+
+	char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
+};
+#define G (*ptr_to_globals)
+#define text           (G.text          )
+#define text_size      (G.text_size     )
+#define end            (G.end           )
+#define dot            (G.dot           )
+#define reg            (G.reg           )
+
+#define vi_setops               (G.vi_setops          )
+#define editing                 (G.editing            )
+#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               )
+#define columns                 (G.columns            )
+#define crow                    (G.crow               )
+#define ccol                    (G.ccol               )
+#define offset                  (G.offset             )
+#define status_buffer           (G.status_buffer      )
+#define have_status_msg         (G.have_status_msg    )
+#define last_status_cksum       (G.last_status_cksum  )
+#define current_filename        (G.current_filename   )
+#define screen                  (G.screen             )
+#define screensize              (G.screensize         )
+#define screenbegin             (G.screenbegin        )
+#define tabstop                 (G.tabstop            )
+#define last_forward_char       (G.last_forward_char  )
+#define erase_char              (G.erase_char         )
+#define last_input_char         (G.last_input_char    )
+#if ENABLE_FEATURE_VI_READONLY
+#define readonly_mode           (G.readonly_mode      )
+#else
+#define readonly_mode           0
+#endif
+#define adding2q                (G.adding2q           )
+#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)
+
+#define edit_file__cur_line     (G.edit_file__cur_line)
+#define refresh__old_offset     (G.refresh__old_offset)
+#define format_edit_status__tot (G.format_edit_status__tot)
+
+#define YDreg          (G.YDreg         )
+#define Ureg           (G.Ureg          )
+#define mark           (G.mark          )
+#define context_start  (G.context_start )
+#define context_end    (G.context_end   )
+#define restart        (G.restart       )
+#define term_orig      (G.term_orig     )
+#define term_vi        (G.term_vi       )
+#define initial_cmds   (G.initial_cmds  )
+#define readbuffer     (G.readbuffer    )
+#define scr_out_buf    (G.scr_out_buf   )
+#define last_modifying_cmd  (G.last_modifying_cmd )
+#define get_input_line__buf (G.get_input_line__buf)
+
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	last_file_modified = -1; \
+	/* "" but has space for 2 chars: */ \
+	IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
+} while (0)
+
+
+static int init_text_buffer(char *); // init from file or create new
+static void edit_file(char *);	// edit one file
+static void do_cmd(int);	// execute a command
+static int next_tabstop(int);
+static void sync_cursor(char *, int *, int *);	// synchronize the screen cursor to dot
+static char *begin_line(char *);	// return pointer to cur line B-o-l
+static char *end_line(char *);	// return pointer to cur line E-o-l
+static char *prev_line(char *);	// return pointer to prev line B-o-l
+static char *next_line(char *);	// return pointer to next line B-o-l
+static char *end_screen(void);	// get pointer to last char on screen
+static int count_lines(char *, char *);	// count line from start to stop
+static char *find_line(int);	// find begining of line #li
+static char *move_to_col(char *, int);	// move "p" to column l
+static void dot_left(void);	// move dot left- dont leave line
+static void dot_right(void);	// move dot right- dont leave line
+static void dot_begin(void);	// move dot to B-o-l
+static void dot_end(void);	// move dot to E-o-l
+static void dot_next(void);	// move dot to next line B-o-l
+static void dot_prev(void);	// move dot to prev line B-o-l
+static void dot_scroll(int, int);	// move the screen up or down
+static void dot_skip_over_ws(void);	// move dot pat WS
+static void dot_delete(void);	// delete the char at 'dot'
+static char *bound_dot(char *);	// make sure  text[0] <= P < "end"
+static char *new_screen(int, int);	// malloc virtual screen memory
+static char *char_insert(char *, char);	// insert the char c at 'p'
+// might reallocate text[]! use p += stupid_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t stupid_insert(char *, char);	// stupidly insert the char c at 'p'
+static int find_range(char **, char **, char);	// return pointers for an object
+static int st_test(char *, int, int, char *);	// helper for skip_thing()
+static char *skip_thing(char *, int, int, int);	// skip some object
+static char *find_pair(char *, char);	// find matching pair ()  []  {}
+static char *text_hole_delete(char *, char *);	// at "p", delete a 'size' byte hole
+// might reallocate text[]! use p += text_hole_make(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t text_hole_make(char *, int);	// at "p", make a 'size' byte hole
+static char *yank_delete(char *, char *, int, int);	// yank text[] into register then delete
+static void show_help(void);	// display some help info
+static void rawmode(void);	// set "raw" mode on tty
+static void cookmode(void);	// return to "cooked" mode on tty
+// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
+static int mysleep(int);
+static int readit(void);	// read (maybe cursor) key from stdin
+static int get_one_char(void);	// read 1 char from stdin
+static int file_size(const char *);   // what is the byte size of "fn"
+#if !ENABLE_FEATURE_VI_READONLY
+#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
+#endif
+// 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 screen_erase(void);
+static void clear_to_eol(void);
+static void clear_to_eos(void);
+static void go_bottom_and_clear_to_eol(void);
+static void standout_start(void);	// send "start reverse video" sequence
+static void standout_end(void);	// send "end reverse video" sequence
+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 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
+static char* format_line(char* /*, int*/);
+static void refresh(int);	// update the terminal from screen[]
+
+static void Indicate_Error(void);       // use flash or beep to indicate error
+#define indicate_error(c) Indicate_Error()
+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
+#endif
+#if ENABLE_FEATURE_VI_COLON
+static char *get_one_address(char *, int *);	// get colon addr, if present
+static char *get_address(char *, int *, int *);	// get two colon addrs, if present
+static void colon(char *);	// execute the "colon" mode cmds
+#endif
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+static void winch_sig(int);	// catch window size changes
+static void suspend_sig(int);	// catch ctrl-Z
+static void catch_sig(int);     // catch ctrl-C and alarm time-outs
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(char);	// new queue for command
+static void end_cmd_q(void);	// stop saving input chars
+#else
+#define end_cmd_q() ((void)0)
+#endif
+#if ENABLE_FEATURE_VI_SETOPTS
+static void showmatching(char *);	// show the matching pair ()  []  {}
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
+// might reallocate text[]! use p += string_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t string_insert(char *, const char *);	// insert the string at 'p'
+#endif
+#if ENABLE_FEATURE_VI_YANKMARK
+static char *text_yank(char *, char *, int);	// save copy of "p" into a register
+static char what_reg(void);		// what is letter of current YDreg
+static void check_context(char);	// remember context for '' command
+#endif
+#if ENABLE_FEATURE_VI_CRASHME
+static void crash_dummy();
+static void crash_test();
+static int crashme = 0;
+#endif
+
+
+static void write1(const char *out)
+{
+	fputs(out, stdout);
+}
+
+int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int vi_main(int argc, char **argv)
+{
+	int c;
+
+	INIT_G();
+
+#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
+	my_pid = getpid();
+#endif
+#if ENABLE_FEATURE_VI_CRASHME
+	srand((long) my_pid);
+#endif
+#ifdef NO_SUCH_APPLET_YET
+	/* If we aren't "vi", we are "view" */
+	if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
+		SET_READONLY_MODE(readonly_mode);
+	}
+#endif
+
+	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
+#if ENABLE_FEATURE_VI_COLON
+	{
+		char *p = getenv("EXINIT");
+		if (p && *p)
+			initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
+	}
+#endif
+	while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) {
+		switch (c) {
+#if ENABLE_FEATURE_VI_CRASHME
+		case 'C':
+			crashme = 1;
+			break;
+#endif
+#if ENABLE_FEATURE_VI_READONLY
+		case 'R':		// Read-only flag
+			SET_READONLY_MODE(readonly_mode);
+			break;
+#endif
+#if ENABLE_FEATURE_VI_COLON
+		case 'c':		// cmd line vi command
+			if (*optarg)
+				initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
+			break;
+#endif
+		case 'H':
+			show_help();
+			/* fall through */
+		default:
+			bb_show_usage();
+			return 1;
+		}
+	}
+
+	// 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;
+
+	//----- This is the main file handling loop --------------
+	while (1) {
+		edit_file(argv[optind]); /* param might be NULL */
+		if (++optind >= argc)
+			break;
+	}
+	//-----------------------------------------------------------
+
+	return 0;
+}
+
+/* read text from file or create an empty buf */
+/* will also update current_filename */
+static int init_text_buffer(char *fn)
+{
+	int rc;
+	int size = file_size(fn);	// file size. -1 means does not exist.
+
+	/* allocate/reallocate text buffer */
+	free(text);
+	text_size = size + 10240;
+	screenbegin = dot = end = text = xzalloc(text_size);
+
+	if (fn != current_filename) {
+		free(current_filename);
+		current_filename = xstrdup(fn);
+	}
+	if (size < 0) {
+		// file dont exist. Start empty buf with dummy line
+		char_insert(text, '\n');
+		rc = 0;
+	} else {
+		rc = file_insert(fn, text, 1);
+	}
+	file_modified = 0;
+	last_file_modified = -1;
+#if ENABLE_FEATURE_VI_YANKMARK
+	/* init the marks. */
+	memset(mark, 0, sizeof(mark));
+#endif
+	return rc;
+}
+
+#if ENABLE_FEATURE_VI_WIN_RESIZE
+static int query_screen_dimensions(void)
+{
+	int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
+	if (rows > MAX_SCR_ROWS)
+		rows = MAX_SCR_ROWS;
+	if (columns > MAX_SCR_COLS)
+		columns = MAX_SCR_COLS;
+	return err;
+}
+#else
+# define query_screen_dimensions() (0)
+#endif
+
+static void edit_file(char *fn)
+{
+#if ENABLE_FEATURE_VI_YANKMARK
+#define cur_line edit_file__cur_line
+#endif
+	int c;
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+	int sig;
+#endif
+
+	editing = 1;	// 0 = exit, 1 = one file, 2 = multiple files
+	rawmode();
+	rows = 24;
+	columns = 80;
+	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 */) {
+		uint64_t k;
+		write1("\033[999;999H" "\033[6n");
+		fflush_all();
+		k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
+		if ((int32_t)k == KEYCODE_CURSOR_POS) {
+			uint32_t rc = (k >> 32);
+			columns = (rc & 0x7fff);
+			if (columns > MAX_SCR_COLS)
+				columns = MAX_SCR_COLS;
+			rows = ((rc >> 16) & 0x7fff);
+			if (rows > MAX_SCR_ROWS)
+				rows = MAX_SCR_ROWS;
+		}
+	}
+#endif
+	new_screen(rows, columns);	// get memory for virtual screen
+	init_text_buffer(fn);
+
+#if ENABLE_FEATURE_VI_YANKMARK
+	YDreg = 26;			// default Yank/Delete reg
+	Ureg = 27;			// hold orig line for "U" cmd
+	mark[26] = mark[27] = text;	// init "previous context"
+#endif
+
+	last_forward_char = last_input_char = '\0';
+	crow = 0;
+	ccol = 0;
+
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+	signal(SIGINT, catch_sig);
+	signal(SIGWINCH, winch_sig);
+	signal(SIGTSTP, suspend_sig);
+	sig = sigsetjmp(restart, 1);
+	if (sig != 0) {
+		screenbegin = dot = text;
+	}
+#endif
+
+	cmd_mode = 0;		// 0=command  1=insert  2='R'eplace
+	cmdcnt = 0;
+	tabstop = 8;
+	offset = 0;			// no horizontal offset
+	c = '\0';
+#if ENABLE_FEATURE_VI_DOT_CMD
+	free(ioq_start);
+	ioq = ioq_start = NULL;
+	lmc_len = 0;
+	adding2q = 0;
+#endif
+
+#if ENABLE_FEATURE_VI_COLON
+	{
+		char *p, *q;
+		int n = 0;
+
+		while ((p = initial_cmds[n]) != NULL) {
+			do {
+				q = p;
+				p = strchr(q, '\n');
+				if (p)
+					while (*p == '\n')
+						*p++ = '\0';
+				if (*q)
+					colon(q);
+			} while (p);
+			free(initial_cmds[n]);
+			initial_cmds[n] = NULL;
+			n++;
+		}
+	}
+#endif
+	redraw(FALSE);			// dont force every col re-draw
+	//------This is the main Vi cmd handling loop -----------------------
+	while (editing > 0) {
+#if ENABLE_FEATURE_VI_CRASHME
+		if (crashme > 0) {
+			if ((end - text) > 1) {
+				crash_dummy();	// generate a random command
+			} else {
+				crashme = 0;
+				string_insert(text, "\n\n#####  Ran out of text to work on.  #####\n\n"); // insert the string
+				dot = text;
+				refresh(FALSE);
+			}
+		}
+#endif
+		last_input_char = c = get_one_char();	// get a cmd from user
+#if ENABLE_FEATURE_VI_YANKMARK
+		// save a copy of the current line- for the 'U" command
+		if (begin_line(dot) != cur_line) {
+			cur_line = begin_line(dot);
+			text_yank(begin_line(dot), end_line(dot), Ureg);
+		}
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD
+		// These are commands that change text[].
+		// Remember the input for the "." command
+		if (!adding2q && ioq_start == NULL
+		 && cmd_mode == 0 // command mode
+		 && c > '\0' // exclude NUL and non-ASCII chars
+		 && c < 0x7f // (Unicode and such)
+		 && strchr(modifying_cmds, c)
+		) {
+			start_new_cmd_q(c);
+		}
+#endif
+		do_cmd(c);		// execute the user command
+
+		// poll to see if there is input already waiting. if we are
+		// not able to display output fast enough to keep up, skip
+		// the display update until we catch up with input.
+		if (!readbuffer[0] && mysleep(0) == 0) {
+			// no input pending - so update output
+			refresh(FALSE);
+			show_status_line();
+		}
+#if ENABLE_FEATURE_VI_CRASHME
+		if (crashme > 0)
+			crash_test();	// test editor variables
+#endif
+	}
+	//-------------------------------------------------------------------
+
+	go_bottom_and_clear_to_eol();
+	cookmode();
+#undef cur_line
+}
+
+//----- The Colon commands -------------------------------------
+#if ENABLE_FEATURE_VI_COLON
+static char *get_one_address(char *p, int *addr)	// get colon addr, if present
+{
+	int st;
+	char *q;
+	IF_FEATURE_VI_YANKMARK(char c;)
+	IF_FEATURE_VI_SEARCH(char *pat;)
+
+	*addr = -1;			// assume no addr
+	if (*p == '.') {	// the current line
+		p++;
+		q = begin_line(dot);
+		*addr = count_lines(text, q);
+	}
+#if ENABLE_FEATURE_VI_YANKMARK
+	else if (*p == '\'') {	// is this a mark addr
+		p++;
+		c = tolower(*p);
+		p++;
+		if (c >= 'a' && c <= 'z') {
+			// we have a mark
+			c = c - 'a';
+			q = mark[(unsigned char) c];
+			if (q != NULL) {	// is mark valid
+				*addr = count_lines(text, q);
+			}
+		}
+	}
+#endif
+#if ENABLE_FEATURE_VI_SEARCH
+	else if (*p == '/') {	// a search pattern
+		q = strchrnul(++p, '/');
+		pat = xstrndup(p, q - p); // save copy of pattern
+		p = q;
+		if (*p == '/')
+			p++;
+		q = char_search(dot, pat, FORWARD, FULL);
+		if (q != NULL) {
+			*addr = count_lines(text, q);
+		}
+		free(pat);
+	}
+#endif
+	else if (*p == '$') {	// the last line in file
+		p++;
+		q = begin_line(end - 1);
+		*addr = count_lines(text, q);
+	} else if (isdigit(*p)) {	// specific line number
+		sscanf(p, "%d%n", addr, &st);
+		p += st;
+	} else {
+		// unrecognized address - assume -1
+		*addr = -1;
+	}
+	return p;
+}
+
+static char *get_address(char *p, int *b, int *e)	// get two colon addrs, if present
+{
+	//----- get the address' i.e., 1,3   'a,'b  -----
+	// get FIRST addr, if present
+	while (isblank(*p))
+		p++;				// skip over leading spaces
+	if (*p == '%') {			// alias for 1,$
+		p++;
+		*b = 1;
+		*e = count_lines(text, end-1);
+		goto ga0;
+	}
+	p = get_one_address(p, b);
+	while (isblank(*p))
+		p++;
+	if (*p == ',') {			// is there a address separator
+		p++;
+		while (isblank(*p))
+			p++;
+		// get SECOND addr, if present
+		p = get_one_address(p, e);
+	}
+ ga0:
+	while (isblank(*p))
+		p++;				// skip over trailing spaces
+	return p;
+}
+
+#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
+static void setops(const char *args, const char *opname, int flg_no,
+			const char *short_opname, int opt)
+{
+	const char *a = args + flg_no;
+	int l = strlen(opname) - 1; /* opname have + ' ' */
+
+	// maybe strncmp? we had tons of erroneous strncasecmp's...
+	if (strncasecmp(a, opname, l) == 0
+	 || strncasecmp(a, short_opname, 2) == 0
+	) {
+		if (flg_no)
+			vi_setops &= ~opt;
+		else
+			vi_setops |= opt;
+	}
+}
+#endif
+
+// buf must be no longer than MAX_INPUT_LEN!
+static void colon(char *buf)
+{
+	char c, *orig_buf, *buf1, *q, *r;
+	char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
+	int i, l, li, ch, b, e;
+	int useforce, forced = FALSE;
+
+	// :3154	// if (-e line 3154) goto it  else stay put
+	// :4,33w! foo	// write a portion of buffer to file "foo"
+	// :w		// write all of buffer to current file
+	// :q		// quit
+	// :q!		// quit- dont care about modified file
+	// :'a,'z!sort -u   // filter block through sort
+	// :'f		// goto mark "f"
+	// :'fl		// list literal the mark "f" line
+	// :.r bar	// read file "bar" into buffer before dot
+	// :/123/,/abc/d    // delete lines from "123" line to "abc" line
+	// :/xyz/	// goto the "xyz" line
+	// :s/find/replace/ // substitute pattern "find" with "replace"
+	// :!<cmd>	// run <cmd> then return
+	//
+
+	if (!buf[0])
+		goto ret;
+	if (*buf == ':')
+		buf++;			// move past the ':'
+
+	li = ch = i = 0;
+	b = e = -1;
+	q = text;			// assume 1,$ for the range
+	r = end - 1;
+	li = count_lines(text, end - 1);
+	fn = current_filename;
+
+	// look for optional address(es)  :.  :1  :1,9   :'q,'a   :%
+	buf = get_address(buf, &b, &e);
+
+	// remember orig command line
+	orig_buf = buf;
+
+	// get the COMMAND into cmd[]
+	buf1 = cmd;
+	while (*buf != '\0') {
+		if (isspace(*buf))
+			break;
+		*buf1++ = *buf++;
+	}
+	*buf1 = '\0';
+	// get any ARGuments
+	while (isblank(*buf))
+		buf++;
+	strcpy(args, buf);
+	useforce = FALSE;
+	buf1 = last_char_is(cmd, '!');
+	if (buf1) {
+		useforce = TRUE;
+		*buf1 = '\0';   // get rid of !
+	}
+	if (b >= 0) {
+		// if there is only one addr, then the addr
+		// is the line number of the single line the
+		// user wants. So, reset the end
+		// pointer to point at end of the "b" line
+		q = find_line(b);	// what line is #b
+		r = end_line(q);
+		li = 1;
+	}
+	if (e >= 0) {
+		// we were given two addrs.  change the
+		// end pointer to the addr given by user.
+		r = find_line(e);	// what line is #e
+		r = end_line(r);
+		li = e - b + 1;
+	}
+	// ------------ now look for the command ------------
+	i = strlen(cmd);
+	if (i == 0) {		// :123CR goto line #123
+		if (b >= 0) {
+			dot = find_line(b);	// what line is #b
+			dot_skip_over_ws();
+		}
+	}
+#if ENABLE_FEATURE_ALLOW_EXEC
+	else if (cmd[0] == '!') {	// run a cmd
+		int retcode;
+		// :!ls   run the <cmd>
+		go_bottom_and_clear_to_eol();
+		cookmode();
+		retcode = system(orig_buf + 1);	// run the cmd
+		if (retcode)
+			printf("\nshell returned %i\n\n", retcode);
+		rawmode();
+		Hit_Return();			// let user see results
+	}
+#endif
+	else if (cmd[0] == '=' && !cmd[1]) {	// where is the address
+		if (b < 0) {	// no addr given- use defaults
+			b = e = count_lines(text, dot);
+		}
+		status_line("%d", b);
+	} else if (strncmp(cmd, "delete", i) == 0) {	// delete lines
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume .,. for the range
+			r = end_line(dot);
+		}
+		dot = yank_delete(q, r, 1, YANKDEL);	// save, then delete lines
+		dot_skip_over_ws();
+	} 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)");
+			goto ret;
+		}
+		if (args[0]) {
+			// the user supplied a file name
+			fn = args;
+		} else if (current_filename && current_filename[0]) {
+			// no user supplied name- use the current filename
+			// fn = current_filename;  was set by default
+		} else {
+			// no user file name, no current name- punt
+			status_line_bold("No current filename");
+			goto ret;
+		}
+
+		if (init_text_buffer(fn) < 0)
+			goto ret;
+
+#if ENABLE_FEATURE_VI_YANKMARK
+		if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
+			free(reg[Ureg]);	//   free orig line reg- for 'U'
+			reg[Ureg]= 0;
+		}
+		if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
+			free(reg[YDreg]);	//   free default yank/delete register
+			reg[YDreg]= 0;
+		}
+#endif
+		// how many lines in text[]?
+		li = count_lines(text, end - 1);
+		status_line("\"%s\"%s"
+			IF_FEATURE_VI_READONLY("%s")
+			" %dL, %dC", current_filename,
+			(file_size(fn) < 0 ? " [New file]" : ""),
+			IF_FEATURE_VI_READONLY(
+				((readonly_mode) ? " [Readonly]" : ""),
+			)
+			li, ch);
+	} else if (strncmp(cmd, "file", i) == 0) {	// what File is this
+		if (b != -1 || e != -1) {
+			status_line_bold("No address allowed on this command");
+			goto ret;
+		}
+		if (args[0]) {
+			// user wants a new filename
+			free(current_filename);
+			current_filename = xstrdup(args);
+		} else {
+			// user wants file status info
+			last_status_cksum = 0;	// force status update
+		}
+	} else if (strncmp(cmd, "features", i) == 0) {	// what features are available
+		// print out values of all features
+		go_bottom_and_clear_to_eol();
+		cookmode();
+		show_help();
+		rawmode();
+		Hit_Return();
+	} else if (strncmp(cmd, "list", i) == 0) {	// literal print line
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume .,. for the range
+			r = end_line(dot);
+		}
+		go_bottom_and_clear_to_eol();
+		puts("\r");
+		for (; q <= r; q++) {
+			int c_is_no_print;
+
+			c = *q;
+			c_is_no_print = (c & 0x80) && !Isprint(c);
+			if (c_is_no_print) {
+				c = '.';
+				standout_start();
+			}
+			if (c == '\n') {
+				write1("$\r");
+			} else if (c < ' ' || c == 127) {
+				bb_putchar('^');
+				if (c == 127)
+					c = '?';
+				else
+					c += '@';
+			}
+			bb_putchar(c);
+			if (c_is_no_print)
+				standout_end();
+		}
+		Hit_Return();
+	} else if (strncmp(cmd, "quit", i) == 0 // quit
+	        || strncmp(cmd, "next", i) == 0 // edit next file
+	) {
+		int n;
+		if (useforce) {
+			// force end of argv list
+			if (*cmd == 'q') {
+				optind = save_argc;
+			}
+			editing = 0;
+			goto ret;
+		}
+		// 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"));
+			goto ret;
+		}
+		// are there other file to edit
+		n = save_argc - optind - 1;
+		if (*cmd == 'q' && n > 0) {
+			status_line_bold("%d more file(s) to edit", n);
+			goto ret;
+		}
+		if (*cmd == 'n' && n <= 0) {
+			status_line_bold("No more files to edit");
+			goto ret;
+		}
+		editing = 0;
+	} else if (strncmp(cmd, "read", i) == 0) {	// read file into text[]
+		fn = args;
+		if (!fn[0]) {
+			status_line_bold("No filename given");
+			goto ret;
+		}
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume "dot"
+		}
+		// read after current line- unless user said ":0r foo"
+		if (b != 0)
+			q = next_line(q);
+		{ // dance around potentially-reallocated text[]
+			uintptr_t ofs = q - text;
+			ch = file_insert(fn, q, 0);
+			q = text + ofs;
+		}
+		if (ch < 0)
+			goto ret;	// nothing was inserted
+		// how many lines in text[]?
+		li = count_lines(q, q + ch - 1);
+		status_line("\"%s\""
+			IF_FEATURE_VI_READONLY("%s")
+			" %dL, %dC", fn,
+			IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
+			li, ch);
+		if (ch > 0) {
+			// if the insert is before "dot" then we need to update
+			if (q <= dot)
+				dot += ch;
+			/*file_modified++; - done by file_insert */
+		}
+	} 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)");
+		} else {
+			// reset the filenames to edit
+			optind = fn_start - 1;
+			editing = 0;
+		}
+#if ENABLE_FEATURE_VI_SET
+	} else if (strncmp(cmd, "set", i) == 0) {	// set or clear features
+#if ENABLE_FEATURE_VI_SETOPTS
+		char *argp;
+#endif
+		i = 0;			// offset into args
+		// only blank is regarded as args delmiter. What about tab '\t' ?
+		if (!args[0] || strcasecmp(args, "all") == 0) {
+			// print out values of all options
+#if ENABLE_FEATURE_VI_SETOPTS
+			status_line_bold(
+				"%sautoindent "
+				"%sflash "
+				"%signorecase "
+				"%sshowmatch "
+				"tabstop=%u",
+				autoindent ? "" : "no",
+				err_method ? "" : "no",
+				ignorecase ? "" : "no",
+				showmatch ? "" : "no",
+				tabstop
+			);
+#endif
+			goto ret;
+		}
+#if ENABLE_FEATURE_VI_SETOPTS
+		argp = args;
+		while (*argp) {
+			if (strncmp(argp, "no", 2) == 0)
+				i = 2;		// ":set noautoindent"
+			setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
+			setops(argp, "flash "     , i, "fl", VI_ERR_METHOD);
+			setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
+			setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
+			if (strncmp(argp + i, "tabstop=", 8) == 0) {
+				int t = 0;
+				sscanf(argp + i+8, "%u", &t);
+				if (t > 0 && t <= MAX_TABSTOP)
+					tabstop = t;
+			}
+			argp = skip_non_whitespace(argp);
+			argp = skip_whitespace(argp);
+		}
+#endif /* FEATURE_VI_SETOPTS */
+#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;
+
+		// F points to the "find" pattern
+		// R points to the "replace" pattern
+		// replace the cmd line delimiters "/" with NULLs
+		gflag = 0;		// global replace flag
+		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;
+		*R++ = '\0';	// terminate "find"
+		buf1 = strchr(R, c);
+		if (!buf1)
+			goto colon_s_fail;
+		*buf1++ = '\0';	// terminate "replace"
+		if (*buf1 == 'g') {	// :s/foo/bar/g
+			buf1++;
+			gflag++;	// turn on gflag
+		}
+		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
+		}
+		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
+ vc4:
+			buf1 = char_search(q, F, FORWARD, LIMITED);	// search cur line only for "find"
+			if (buf1) {
+				uintptr_t bias;
+				// we found the "find" pattern - delete it
+				text_hole_delete(buf1, buf1 + strlen(F) - 1);
+				// inset the "replace" patern
+				bias = string_insert(buf1, R);	// insert the string
+				buf1 += 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);
+						goto vc4;	// don't let q move past cur line
+					}
+				}
+			}
+			q = next_line(ls);
+		}
+#endif /* FEATURE_VI_SEARCH */
+	} else if (strncmp(cmd, "version", i) == 0) {  // show software version
+		status_line(BB_VER " " BB_BT);
+	} else if (strncmp(cmd, "write", i) == 0  // write text to file
+	        || strncmp(cmd, "wq", i) == 0
+	        || strncmp(cmd, "wn", i) == 0
+	        || (cmd[0] == 'x' && !cmd[1])
+	) {
+		// is there a file name to write to?
+		if (args[0]) {
+			fn = args;
+		}
+#if ENABLE_FEATURE_VI_READONLY
+		if (readonly_mode && !useforce) {
+			status_line_bold("\"%s\" File is read only", fn);
+			goto ret;
+		}
+#endif
+		// how many lines in text[]?
+		li = count_lines(q, r);
+		ch = r - q + 1;
+		// see if file exists- if not, its just a new file request
+		if (useforce) {
+			// if "fn" is not write-able, chmod u+w
+			// sprintf(syscmd, "chmod u+w %s", fn);
+			// system(syscmd);
+			forced = TRUE;
+		}
+		l = file_write(fn, q, r);
+		if (useforce && forced) {
+			// chmod u-w
+			// sprintf(syscmd, "chmod u-w %s", fn);
+			// system(syscmd);
+			forced = FALSE;
+		}
+		if (l < 0) {
+			if (l == -1)
+				status_line_bold("\"%s\" %s", fn, strerror(errno));
+		} else {
+			status_line("\"%s\" %dL, %dC", fn, li, l);
+			if (q == text && r == end - 1 && l == ch) {
+				file_modified = 0;
+				last_file_modified = -1;
+			}
+			if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n'
+			    || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N'
+			    )
+			 && l == ch
+			) {
+				editing = 0;
+			}
+		}
+#if ENABLE_FEATURE_VI_YANKMARK
+	} else if (strncmp(cmd, "yank", i) == 0) {	// yank lines
+		if (b < 0) {	// no addr given- use defaults
+			q = begin_line(dot);	// assume .,. for the range
+			r = end_line(dot);
+		}
+		text_yank(q, r, YDreg);
+		li = count_lines(q, r);
+		status_line("Yank %d lines (%d chars) into [%c]",
+				li, strlen(reg[YDreg]), what_reg());
+#endif
+	} else {
+		// cmd unknown
+		not_implemented(cmd);
+	}
+ ret:
+	dot = bound_dot(dot);	// make sure "dot" is valid
+	return;
+#if ENABLE_FEATURE_VI_SEARCH
+ colon_s_fail:
+	status_line(":s expression missing delimiters");
+#endif
+}
+
+#endif /* FEATURE_VI_COLON */
+
+static void Hit_Return(void)
+{
+	int c;
+
+	standout_start();
+	write1("[Hit return to continue]");
+	standout_end();
+	while ((c = get_one_char()) != '\n' && c != '\r')
+		continue;
+	redraw(TRUE);		// force redraw all
+}
+
+static int next_tabstop(int col)
+{
+	return col + ((tabstop - 1) - (col % tabstop));
+}
+
+//----- Synchronize the cursor to Dot --------------------------
+static NOINLINE void sync_cursor(char *d, int *row, int *col)
+{
+	char *beg_cur;	// begin and end of "d" line
+	char *tp;
+	int cnt, ro, co;
+
+	beg_cur = begin_line(d);	// first char of cur line
+
+	if (beg_cur < screenbegin) {
+		// "d" is before top line on screen
+		// how many lines do we have to move
+		cnt = count_lines(beg_cur, screenbegin);
+ sc1:
+		screenbegin = beg_cur;
+		if (cnt > (rows - 1) / 2) {
+			// we moved too many lines. put "dot" in middle of screen
+			for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
+				screenbegin = prev_line(screenbegin);
+			}
+		}
+	} else {
+		char *end_scr;	// begin and end of screen
+		end_scr = end_screen();	// last char of screen
+		if (beg_cur > end_scr) {
+			// "d" is after bottom line on screen
+			// how many lines do we have to move
+			cnt = count_lines(end_scr, beg_cur);
+			if (cnt > (rows - 1) / 2)
+				goto sc1;	// too many lines
+			for (ro = 0; ro < cnt - 1; ro++) {
+				// move screen begin the same amount
+				screenbegin = next_line(screenbegin);
+				// now, move the end of screen
+				end_scr = next_line(end_scr);
+				end_scr = end_line(end_scr);
+			}
+		}
+	}
+	// "d" is on screen- find out which row
+	tp = screenbegin;
+	for (ro = 0; ro < rows - 1; ro++) {	// drive "ro" to correct row
+		if (tp == beg_cur)
+			break;
+		tp = next_line(tp);
+	}
+
+	// find out what col "d" is on
+	co = 0;
+	while (tp < d) { // drive "co" to correct column
+		if (*tp == '\n') //vda || *tp == '\0')
+			break;
+		if (*tp == '\t') {
+			// handle tabs like real vi
+			if (d == tp && cmd_mode) {
+				break;
+			}
+			co = next_tabstop(co);
+		} else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
+			co++; // display as ^X, use 2 columns
+		}
+		co++;
+		tp++;
+	}
+
+	// "co" is the column where "dot" is.
+	// The screen has "columns" columns.
+	// The currently displayed columns are  0+offset -- columns+ofset
+	// |-------------------------------------------------------------|
+	//               ^ ^                                ^
+	//        offset | |------- columns ----------------|
+	//
+	// If "co" is already in this range then we do not have to adjust offset
+	//      but, we do have to subtract the "offset" bias from "co".
+	// If "co" is outside this range then we have to change "offset".
+	// If the first char of a line is a tab the cursor will try to stay
+	//  in column 7, but we have to set offset to 0.
+
+	if (co < 0 + offset) {
+		offset = co;
+	}
+	if (co >= columns + offset) {
+		offset = co - columns + 1;
+	}
+	// if the first char of the line is a tab, and "dot" is sitting on it
+	//  force offset to 0.
+	if (d == beg_cur && *d == '\t') {
+		offset = 0;
+	}
+	co -= offset;
+
+	*row = ro;
+	*col = co;
+}
+
+//----- Text Movement Routines ---------------------------------
+static char *begin_line(char *p) // return pointer to first char cur line
+{
+	if (p > text) {
+		p = memrchr(text, '\n', p - text);
+		if (!p)
+			return text;
+		return p + 1;
+	}
+	return p;
+}
+
+static char *end_line(char *p) // return pointer to NL of cur line
+{
+	if (p < end - 1) {
+		p = memchr(p, '\n', end - p - 1);
+		if (!p)
+			return end - 1;
+	}
+	return p;
+}
+
+static char *dollar_line(char *p) // return pointer to just before NL line
+{
+	p = end_line(p);
+	// Try to stay off of the Newline
+	if (*p == '\n' && (p - begin_line(p)) > 0)
+		p--;
+	return p;
+}
+
+static char *prev_line(char *p) // return pointer first char prev line
+{
+	p = begin_line(p);	// goto begining of cur line
+	if (p > text && p[-1] == '\n')
+		p--;			// step to prev line
+	p = begin_line(p);	// goto begining of prev line
+	return p;
+}
+
+static char *next_line(char *p) // return pointer first char next line
+{
+	p = end_line(p);
+	if (p < end - 1 && *p == '\n')
+		p++;			// step to next line
+	return p;
+}
+
+//----- Text Information Routines ------------------------------
+static char *end_screen(void)
+{
+	char *q;
+	int cnt;
+
+	// find new bottom line
+	q = screenbegin;
+	for (cnt = 0; cnt < rows - 2; cnt++)
+		q = next_line(q);
+	q = end_line(q);
+	return q;
+}
+
+// count line from start to stop
+static int count_lines(char *start, char *stop)
+{
+	char *q;
+	int cnt;
+
+	if (stop < start) { // start and stop are backwards- reverse them
+		q = start;
+		start = stop;
+		stop = q;
+	}
+	cnt = 0;
+	stop = end_line(stop);
+	while (start <= stop && start <= end - 1) {
+		start = end_line(start);
+		if (*start == '\n')
+			cnt++;
+		start++;
+	}
+	return cnt;
+}
+
+static char *find_line(int li)	// find begining of line #li
+{
+	char *q;
+
+	for (q = text; li > 1; li--) {
+		q = next_line(q);
+	}
+	return q;
+}
+
+//----- Dot Movement Routines ----------------------------------
+static void dot_left(void)
+{
+	if (dot > text && dot[-1] != '\n')
+		dot--;
+}
+
+static void dot_right(void)
+{
+	if (dot < end - 1 && *dot != '\n')
+		dot++;
+}
+
+static void dot_begin(void)
+{
+	dot = begin_line(dot);	// return pointer to first char cur line
+}
+
+static void dot_end(void)
+{
+	dot = end_line(dot);	// return pointer to last char cur line
+}
+
+static char *move_to_col(char *p, int l)
+{
+	int co;
+
+	p = begin_line(p);
+	co = 0;
+	while (co < l && p < end) {
+		if (*p == '\n') //vda || *p == '\0')
+			break;
+		if (*p == '\t') {
+			co = next_tabstop(co);
+		} else if (*p < ' ' || *p == 127) {
+			co++; // display as ^X, use 2 columns
+		}
+		co++;
+		p++;
+	}
+	return p;
+}
+
+static void dot_next(void)
+{
+	dot = next_line(dot);
+}
+
+static void dot_prev(void)
+{
+	dot = prev_line(dot);
+}
+
+static void dot_scroll(int cnt, int dir)
+{
+	char *q;
+
+	for (; cnt > 0; cnt--) {
+		if (dir < 0) {
+			// scroll Backwards
+			// ctrl-Y scroll up one line
+			screenbegin = prev_line(screenbegin);
+		} else {
+			// scroll Forwards
+			// ctrl-E scroll down one line
+			screenbegin = next_line(screenbegin);
+		}
+	}
+	// make sure "dot" stays on the screen so we dont scroll off
+	if (dot < screenbegin)
+		dot = screenbegin;
+	q = end_screen();	// find new bottom line
+	if (dot > q)
+		dot = begin_line(q);	// is dot is below bottom line?
+	dot_skip_over_ws();
+}
+
+static void dot_skip_over_ws(void)
+{
+	// skip WS
+	while (isspace(*dot) && *dot != '\n' && dot < end - 1)
+		dot++;
+}
+
+static void dot_delete(void)	// delete the char at 'dot'
+{
+	text_hole_delete(dot, dot);
+}
+
+static char *bound_dot(char *p) // make sure  text[0] <= P < "end"
+{
+	if (p >= end && end > text) {
+		p = end - 1;
+		indicate_error('1');
+	}
+	if (p < text) {
+		p = text;
+		indicate_error('2');
+	}
+	return p;
+}
+
+//----- Helper Utility Routines --------------------------------
+
+//----------------------------------------------------------------
+//----- Char Routines --------------------------------------------
+/* Chars that are part of a word-
+ *    0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ * Chars that are Not part of a word (stoppers)
+ *    !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
+ * Chars that are WhiteSpace
+ *    TAB NEWLINE VT FF RETURN SPACE
+ * DO NOT COUNT NEWLINE AS WHITESPACE
+ */
+
+static char *new_screen(int ro, int co)
+{
+	int li;
+
+	free(screen);
+	screensize = ro * co + 8;
+	screen = xmalloc(screensize);
+	// initialize the new screen. assume this will be a empty file.
+	screen_erase();
+	//   non-existent text[] lines start with a tilde (~).
+	for (li = 1; li < ro - 1; li++) {
+		screen[(li * co) + 0] = '~';
+	}
+	return screen;
+}
+
+#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)
+{
+	char *q;
+	struct re_pattern_buffer preg;
+	int i;
+	int size;
+
+	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 = (char *)re_compile_pattern(pat, strlen(pat), (struct re_pattern_buffer *)&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;
+	}
+
+	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;
+}
+
+# else
+
+#  if ENABLE_FEATURE_VI_SETOPTS
+static int mycmp(const char *s1, const char *s2, int len)
+{
+	if (ignorecase) {
+		return strncasecmp(s1, s2, len);
+	}
+	return strncmp(s1, s2, len);
+}
+#  else
+#   define mycmp strncmp
+#  endif
+
+static char *char_search(char *p, const char *pat, int dir, int range)
+{
+	char *start, *stop;
+	int len;
+
+	len = strlen(pat);
+	if (dir == FORWARD) {
+		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++) {
+			if (mycmp(start, pat, len) == 0) {
+				return start;
+			}
+		}
+	} else if (dir == BACK) {
+		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--) {
+			if (mycmp(start, pat, len) == 0) {
+				return start;
+			}
+		}
+	}
+	// pattern not found
+	return NULL;
+}
+
+# endif
+
+#endif /* FEATURE_VI_SEARCH */
+
+static char *char_insert(char *p, char c) // insert the char c at 'p'
+{
+	if (c == 22) {		// Is this an ctrl-V?
+		p += stupid_insert(p, '^');	// use ^ to indicate literal next
+		refresh(FALSE);	// show the ^
+		c = get_one_char();
+		*p = c;
+		p++;
+		file_modified++;
+	} else if (c == 27) {	// Is this an ESC?
+		cmd_mode = 0;
+		cmdcnt = 0;
+		end_cmd_q();	// stop adding to q
+		last_status_cksum = 0;	// force status update
+		if ((p[-1] != '\n') && (dot > text)) {
+			p--;
+		}
+	} else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
+		//     123456789
+		if ((p[-1] != '\n') && (dot>text)) {
+			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) {
+			showmatching(sp);
+		}
+		if (autoindent && c == '\n') {	// auto indent the new line
+			char *q;
+			size_t len;
+			q = prev_line(p);	// use prev line as template
+			len = strspn(q, " \t"); // space or tab
+			if (len) {
+				uintptr_t bias;
+				bias = text_hole_make(p, len);
+				p += bias;
+				q += bias;
+				memcpy(p, q, len);
+				p += len;
+			}
+		}
+#endif
+	}
+	return p;
+}
+
+// might reallocate text[]! use p += stupid_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
+{
+	uintptr_t bias;
+	bias = text_hole_make(p, 1);
+	p += bias;
+	*p = c;
+	//file_modified++; - done by text_hole_make()
+	return bias;
+}
+
+static int find_range(char **start, char **stop, char c)
+{
+	char *save_dot, *p, *q, *t;
+	int cnt, multiline = 0;
+
+	save_dot = dot;
+	p = q = dot;
+
+	if (strchr("cdy><", c)) {
+		// these cmds operate on whole lines
+		p = q = begin_line(p);
+		for (cnt = 1; cnt < cmdcnt; cnt++) {
+			q = next_line(q);
+		}
+		q = end_line(q);
+	} else if (strchr("^%$0bBeEfth\b\177", c)) {
+		// These cmds operate on char positions
+		do_cmd(c);		// execute movement cmd
+		q = dot;
+	} else if (strchr("wW", c)) {
+		do_cmd(c);		// execute movement cmd
+		// if we are at the next word's first char
+		// step back one char
+		// but check the possibilities when it is true
+		if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
+				|| (ispunct(dot[-1]) && !ispunct(dot[0]))
+				|| (isalnum(dot[-1]) && !isalnum(dot[0]))))
+			dot--;		// move back off of next word
+		if (dot > text && *dot == '\n')
+			dot--;		// stay off NL
+		q = dot;
+	} else if (strchr("H-k{", c)) {
+		// these operate on multi-lines backwards
+		q = end_line(dot);	// find NL
+		do_cmd(c);		// execute movement cmd
+		dot_begin();
+		p = dot;
+	} else if (strchr("L+j}\r\n", c)) {
+		// these operate on multi-lines forwards
+		p = begin_line(dot);
+		do_cmd(c);		// execute movement cmd
+		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.
+	    //
+	}
+	if (q < p) {
+		t = q;
+		q = p;
+		p = t;
+	}
+
+	// backward char movements don't include start position
+	if (q > p && strchr("^0bBh\b\177", c)) q--;
+
+	multiline = 0;
+	for (t = p; t <= q; t++) {
+		if (*t == '\n') {
+			multiline = 1;
+			break;
+		}
+	}
+
+	*start = p;
+	*stop = q;
+	dot = save_dot;
+	return multiline;
+}
+
+static int st_test(char *p, int type, int dir, char *tested)
+{
+	char c, c0, ci;
+	int test, inc;
+
+	inc = dir;
+	c = c0 = p[0];
+	ci = p[inc];
+	test = 0;
+
+	if (type == S_BEFORE_WS) {
+		c = ci;
+		test = (!isspace(c) || c == '\n');
+	}
+	if (type == S_TO_WS) {
+		c = c0;
+		test = (!isspace(c) || c == '\n');
+	}
+	if (type == S_OVER_WS) {
+		c = c0;
+		test = isspace(c);
+	}
+	if (type == S_END_PUNCT) {
+		c = ci;
+		test = ispunct(c);
+	}
+	if (type == S_END_ALNUM) {
+		c = ci;
+		test = (isalnum(c) || c == '_');
+	}
+	*tested = c;
+	return test;
+}
+
+static char *skip_thing(char *p, int linecnt, int dir, int type)
+{
+	char c;
+
+	while (st_test(p, type, dir, &c)) {
+		// make sure we limit search to correct number of lines
+		if (c == '\n' && --linecnt < 1)
+			break;
+		if (dir >= 0 && p >= end - 1)
+			break;
+		if (dir < 0 && p <= text)
+			break;
+		p += dir;		// move to next char
+	}
+	return p;
+}
+
+// find matching char of pair  ()  []  {}
+static char *find_pair(char *p, const char c)
+{
+	char match, *q;
+	int dir, level;
+
+	match = ')';
+	level = 1;
+	dir = 1;			// assume forward
+	switch (c) {
+	case '(': match = ')'; break;
+	case '[': match = ']'; break;
+	case '{': match = '}'; break;
+	case ')': match = '('; dir = -1; break;
+	case ']': match = '['; dir = -1; break;
+	case '}': match = '{'; dir = -1; break;
+	}
+	for (q = p + dir; text <= q && q < end; q += dir) {
+		// look for match, count levels of pairs  (( ))
+		if (*q == c)
+			level++;	// increase pair levels
+		if (*q == match)
+			level--;	// reduce pair level
+		if (level == 0)
+			break;		// found matching pair
+	}
+	if (level != 0)
+		q = NULL;		// indicate no match
+	return q;
+}
+
+#if ENABLE_FEATURE_VI_SETOPTS
+// show the matching char of a pair,  ()  []  {}
+static void showmatching(char *p)
+{
+	char *q, *save_dot;
+
+	// we found half of a pair
+	q = find_pair(p, *p);	// get loc of matching char
+	if (q == NULL) {
+		indicate_error('3');	// no matching char
+	} else {
+		// "q" now points to matching pair
+		save_dot = dot;	// remember where we are
+		dot = q;		// go to new loc
+		refresh(FALSE);	// let the user see it
+		mysleep(40);	// give user some time
+		dot = save_dot;	// go back to old loc
+		refresh(FALSE);
+	}
+}
+#endif /* FEATURE_VI_SETOPTS */
+
+// open a hole in text[]
+// might reallocate text[]! use p += text_hole_make(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t text_hole_make(char *p, int size)	// at "p", make a 'size' byte hole
+{
+	uintptr_t bias = 0;
+
+	if (size <= 0)
+		return bias;
+	end += size;		// adjust the new END
+	if (end >= (text + text_size)) {
+		char *new_text;
+		text_size += end - (text + text_size) + 10240;
+		new_text = xrealloc(text, text_size);
+		bias = (new_text - text);
+		screenbegin += bias;
+		dot         += bias;
+		end         += bias;
+		p           += bias;
+		text = new_text;
+	}
+	memmove(p + size, p, end - size - p);
+	memset(p, ' ', size);	// clear new hole
+	file_modified++;
+	return bias;
+}
+
+//  close a hole in text[]
+static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
+{
+	char *src, *dest;
+	int cnt, hole_size;
+
+	// move forwards, from beginning
+	// assume p <= q
+	src = q + 1;
+	dest = p;
+	if (q < p) {		// they are backward- swap them
+		src = p + 1;
+		dest = q;
+	}
+	hole_size = q - p + 1;
+	cnt = end - src;
+	if (src < text || src > end)
+		goto thd0;
+	if (dest < text || dest >= end)
+		goto thd0;
+	if (src >= end)
+		goto thd_atend;	// just delete the end of the buffer
+	memmove(dest, src, cnt);
+ thd_atend:
+	end = end - hole_size;	// adjust the new END
+	if (dest >= end)
+		dest = end - 1;	// make sure dest in below end-1
+	if (end <= text)
+		dest = end = text;	// keep pointers valid
+	file_modified++;
+ thd0:
+	return dest;
+}
+
+// copy text into register, then delete text.
+// if dist <= 0, do not include, or go past, a NewLine
+//
+static char *yank_delete(char *start, char *stop, int dist, int yf)
+{
+	char *p;
+
+	// make sure start <= stop
+	if (start > stop) {
+		// they are backwards, reverse them
+		p = start;
+		start = stop;
+		stop = p;
+	}
+	if (dist <= 0) {
+		// we cannot cross NL boundaries
+		p = start;
+		if (*p == '\n')
+			return p;
+		// dont go past a NewLine
+		for (; p + 1 <= stop; p++) {
+			if (p[1] == '\n') {
+				stop = p;	// "stop" just before NewLine
+				break;
+			}
+		}
+	}
+	p = start;
+#if ENABLE_FEATURE_VI_YANKMARK
+	text_yank(start, stop, YDreg);
+#endif
+	if (yf == YANKDEL) {
+		p = text_hole_delete(start, stop);
+	}					// delete lines
+	return p;
+}
+
+static void show_help(void)
+{
+	puts("These features are available:"
+#if ENABLE_FEATURE_VI_SEARCH
+	"\n\tPattern searches with / and ?"
+#endif
+#if ENABLE_FEATURE_VI_DOT_CMD
+	"\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
+	//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 \':\'"
+#endif
+#if ENABLE_FEATURE_VI_SETOPTS
+	"\n\tSettable options with \":set\""
+#endif
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+	"\n\tSignal catching- ^C"
+	"\n\tJob suspend and resume with ^Z"
+#endif
+#if ENABLE_FEATURE_VI_WIN_RESIZE
+	"\n\tAdapt to window re-sizes"
+#endif
+	);
+}
+
+#if ENABLE_FEATURE_VI_DOT_CMD
+static void start_new_cmd_q(char c)
+{
+	// get buffer for new cmd
+	// if there is a current cmd count put it in the buffer first
+	if (cmdcnt > 0) {
+		lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
+	} else { // just save char c onto queue
+		last_modifying_cmd[0] = c;
+		lmc_len = 1;
+	}
+	adding2q = 1;
+}
+
+static void end_cmd_q(void)
+{
+#if ENABLE_FEATURE_VI_YANKMARK
+	YDreg = 26;			// go back to default Yank/Delete reg
+#endif
+	adding2q = 0;
+}
+#endif /* FEATURE_VI_DOT_CMD */
+
+#if ENABLE_FEATURE_VI_YANKMARK \
+ || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
+ || ENABLE_FEATURE_VI_CRASHME
+// might reallocate text[]! use p += string_insert(p, ...),
+// and be careful to not use pointers into potentially freed text[]!
+static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
+{
+	uintptr_t bias;
+	int i;
+
+	i = strlen(s);
+	bias = text_hole_make(p, i);
+	p += bias;
+	memcpy(p, s, i);
+#if ENABLE_FEATURE_VI_YANKMARK
+	{
+		int cnt;
+		for (cnt = 0; *s != '\0'; s++) {
+			if (*s == '\n')
+				cnt++;
+		}
+		status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+	}
+#endif
+	return bias;
+}
+#endif
+
+#if ENABLE_FEATURE_VI_YANKMARK
+static char *text_yank(char *p, char *q, int dest)	// copy text into a register
+{
+	int cnt = q - p;
+	if (cnt < 0) {		// they are backwards- reverse them
+		p = q;
+		cnt = -cnt;
+	}
+	free(reg[dest]);	//  if already a yank register, free it
+	reg[dest] = xstrndup(p, cnt + 1);
+	return p;
+}
+
+static char what_reg(void)
+{
+	char c;
+
+	c = 'D';			// default to D-reg
+	if (0 <= YDreg && YDreg <= 25)
+		c = 'a' + (char) YDreg;
+	if (YDreg == 26)
+		c = 'D';
+	if (YDreg == 27)
+		c = 'U';
+	return c;
+}
+
+static void check_context(char cmd)
+{
+	// A context is defined to be "modifying text"
+	// Any modifying command establishes a new context.
+
+	if (dot < context_start || dot > context_end) {
+		if (strchr(modifying_cmds, cmd) != NULL) {
+			// we are trying to modify text[]- make this the current context
+			mark[27] = mark[26];	// move cur to prev
+			mark[26] = dot;	// move local to cur
+			context_start = prev_line(prev_line(dot));
+			context_end = next_line(next_line(dot));
+			//loiter= start_loiter= now;
+		}
+	}
+}
+
+static char *swap_context(char *p) // goto new context for '' command make this the current context
+{
+	char *tmp;
+
+	// the current context is in mark[26]
+	// the previous context is in mark[27]
+	// only swap context if other context is valid
+	if (text <= mark[27] && mark[27] <= end - 1) {
+		tmp = mark[27];
+		mark[27] = mark[26];
+		mark[26] = tmp;
+		p = mark[26];	// where we are going- previous context
+		context_start = prev_line(prev_line(prev_line(p)));
+		context_end = next_line(next_line(next_line(p)));
+	}
+	return p;
+}
+#endif /* FEATURE_VI_YANKMARK */
+
+//----- Set terminal attributes --------------------------------
+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_iflag &= (~IXON & ~ICRNL);
+	term_vi.c_oflag &= (~ONLCR);
+	term_vi.c_cc[VMIN] = 1;
+	term_vi.c_cc[VTIME] = 0;
+	erase_char = term_vi.c_cc[VERASE];
+	tcsetattr_stdin_TCSANOW(&term_vi);
+}
+
+static void cookmode(void)
+{
+	fflush_all();
+	tcsetattr_stdin_TCSANOW(&term_orig);
+}
+
+#if ENABLE_FEATURE_VI_USE_SIGNALS
+//----- Come here when we get a window resize signal ---------
+static void winch_sig(int sig UNUSED_PARAM)
+{
+	int save_errno = errno;
+	// FIXME: do it in main loop!!!
+	signal(SIGWINCH, winch_sig);
+	query_screen_dimensions();
+	new_screen(rows, columns);	// get memory for virtual screen
+	redraw(TRUE);		// re-draw the screen
+	errno = save_errno;
+}
+
+//----- Come here when we get a continue signal -------------------
+static void cont_sig(int sig UNUSED_PARAM)
+{
+	int save_errno = errno;
+	rawmode(); // terminal to "raw"
+	last_status_cksum = 0; // force status update
+	redraw(TRUE); // re-draw the screen
+
+	signal(SIGTSTP, suspend_sig);
+	signal(SIGCONT, SIG_DFL);
+	//kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
+	errno = save_errno;
+}
+
+//----- Come here when we get a Suspend signal -------------------
+static void suspend_sig(int sig UNUSED_PARAM)
+{
+	int save_errno = errno;
+	go_bottom_and_clear_to_eol();
+	cookmode(); // terminal to "cooked"
+
+	signal(SIGCONT, cont_sig);
+	signal(SIGTSTP, SIG_DFL);
+	kill(my_pid, SIGTSTP);
+	errno = save_errno;
+}
+
+//----- Come here when we get a signal ---------------------------
+static void catch_sig(int sig)
+{
+	signal(SIGINT, catch_sig);
+	siglongjmp(restart, sig);
+}
+#endif /* FEATURE_VI_USE_SIGNALS */
+
+static int mysleep(int hund)	// sleep for 'hund' 1/100 seconds or stdin ready
+{
+	struct pollfd pfd[1];
+
+	pfd[0].fd = STDIN_FILENO;
+	pfd[0].events = POLLIN;
+	return safe_poll(pfd, 1, hund*10) > 0;
+}
+
+//----- IO Routines --------------------------------------------
+static int readit(void) // read (maybe cursor) key from stdin
+{
+	int c;
+
+	fflush_all();
+	c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2);
+	if (c == -1) { // EOF/error
+		go_bottom_and_clear_to_eol();
+		cookmode(); // terminal to "cooked"
+		bb_error_msg_and_die("can't read user input");
+	}
+	return c;
+}
+
+//----- IO Routines --------------------------------------------
+static int get_one_char(void)
+{
+	int c;
+
+#if ENABLE_FEATURE_VI_DOT_CMD
+	if (!adding2q) {
+		// we are not adding to the q.
+		// but, we may be reading from a q
+		if (ioq == 0) {
+			// there is no current q, read from STDIN
+			c = readit();	// get the users input
+		} else {
+			// there is a queue to get chars from first
+			// careful with correct sign expansion!
+			c = (unsigned char)*ioq++;
+			if (c == '\0') {
+				// the end of the q, read from STDIN
+				free(ioq_start);
+				ioq_start = ioq = 0;
+				c = readit();	// get the users input
+			}
+		}
+	} else {
+		// adding STDIN chars to q
+		c = readit();	// get the users input
+		if (lmc_len >= MAX_INPUT_LEN - 1) {
+			status_line_bold("last_modifying_cmd overrun");
+		} else {
+			// add new char to q
+			last_modifying_cmd[lmc_len++] = c;
+		}
+	}
+#else
+	c = readit();		// get the users input
+#endif /* FEATURE_VI_DOT_CMD */
+	return c;
+}
+
+// Get input line (uses "status line" area)
+static char *get_input_line(const char *prompt)
+{
+	// char [MAX_INPUT_LEN]
+#define buf get_input_line__buf
+
+	int c;
+	int i;
+
+	strcpy(buf, prompt);
+	last_status_cksum = 0;	// force status update
+	go_bottom_and_clear_to_eol();
+	write1(prompt);      // write out the :, /, or ? prompt
+
+	i = strlen(buf);
+	while (i < MAX_INPUT_LEN) {
+		c = get_one_char();
+		if (c == '\n' || c == '\r' || c == 27)
+			break;		// this is end of input
+		if (c == erase_char || c == 8 || c == 127) {
+			// user wants to erase prev char
+			buf[--i] = '\0';
+			write1("\b \b"); // erase char on screen
+			if (i <= 0) // user backs up before b-o-l, exit
+				break;
+		} else if (c > 0 && c < 256) { // exclude Unicode
+			// (TODO: need to handle Unicode)
+			buf[i] = c;
+			buf[++i] = '\0';
+			bb_putchar(c);
+		}
+	}
+	refresh(FALSE);
+	return buf;
+#undef buf
+}
+
+static int file_size(const char *fn) // what is the byte size of "fn"
+{
+	struct stat st_buf;
+	int cnt;
+
+	cnt = -1;
+	if (fn && stat(fn, &st_buf) == 0)	// see if file exists
+		cnt = (int) st_buf.st_size;
+	return cnt;
+}
+
+// might reallocate text[]!
+static int file_insert(const char *fn, char *p, int update_ro_status)
+{
+	int cnt = -1;
+	int fd, size;
+	struct stat statbuf;
+
+	/* Validate file */
+	if (stat(fn, &statbuf) < 0) {
+		status_line_bold("\"%s\" %s", fn, strerror(errno));
+		goto fi0;
+	}
+	if (!S_ISREG(statbuf.st_mode)) {
+		// This is not a regular file
+		status_line_bold("\"%s\" Not a regular file", fn);
+		goto fi0;
+	}
+	if (p < text || p > end) {
+		status_line_bold("Trying to insert file outside of memory");
+		goto fi0;
+	}
+
+	// read file to buffer
+	fd = open(fn, O_RDONLY);
+	if (fd < 0) {
+		status_line_bold("\"%s\" %s", fn, strerror(errno));
+		goto fi0;
+	}
+	size = statbuf.st_size;
+	p += text_hole_make(p, size);
+	cnt = safe_read(fd, p, size);
+	if (cnt < 0) {
+		status_line_bold("\"%s\" %s", fn, strerror(errno));
+		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);
+	}
+	if (cnt >= size)
+		file_modified++;
+	close(fd);
+ fi0:
+#if ENABLE_FEATURE_VI_READONLY
+	if (update_ro_status
+	 && ((access(fn, W_OK) < 0) ||
+		/* root will always have access()
+		 * so we check fileperms too */
+		!(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
+	    )
+	) {
+		SET_READONLY_FILE(readonly_mode);
+	}
+#endif
+	return cnt;
+}
+
+static int file_write(char *fn, char *first, char *last)
+{
+	int fd, cnt, charcnt;
+
+	if (fn == 0) {
+		status_line_bold("No current filename");
+		return -2;
+	}
+	/* By popular request we do not open file with O_TRUNC,
+	 * but instead ftruncate() it _after_ successful write.
+	 * Might reduce amount of data lost on power fail etc.
+	 */
+	fd = open(fn, (O_WRONLY | O_CREAT), 0666);
+	if (fd < 0)
+		return -1;
+	cnt = last - first + 1;
+	charcnt = full_write(fd, first, cnt);
+	ftruncate(fd, charcnt);
+	if (charcnt == cnt) {
+		// good write
+		//file_modified = FALSE;
+	} else {
+		charcnt = 0;
+	}
+	close(fd);
+	return charcnt;
+}
+
+//----- Terminal Drawing ---------------------------------------
+// The terminal is made up of 'rows' line of 'columns' columns.
+// classically this would be 24 x 80.
+//  screen coordinates
+//  0,0     ...     0,79
+//  1,0     ...     1,79
+//  .       ...     .
+//  .       ...     .
+//  22,0    ...     22,79
+//  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)
+{
+	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;
+
+	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);
+}
+
+//----- Erase from cursor to end of line -----------------------
+static void clear_to_eol(void)
+{
+	write1(Ceol);   // Erase from cursor to end of line
+}
+
+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
+}
+
+//----- Erase from cursor to end of screen -----------------------
+static void clear_to_eos(void)
+{
+	write1(Ceos);   // Erase from cursor to end of screen
+}
+
+//----- Start standout mode ------------------------------------
+static void standout_start(void) // send "start reverse video" sequence
+{
+	write1(SOs);     // Start reverse video mode
+}
+
+//----- End standout mode --------------------------------------
+static void standout_end(void) // send "end reverse video" sequence
+{
+	write1(SOn);     // End reverse video mode
+}
+
+//----- Flash the screen  --------------------------------------
+static void flash(int h)
+{
+	standout_start();	// send "start reverse video" sequence
+	redraw(TRUE);
+	mysleep(h);
+	standout_end();		// send "end reverse video" sequence
+	redraw(TRUE);
+}
+
+static void Indicate_Error(void)
+{
+#if ENABLE_FEATURE_VI_CRASHME
+	if (crashme > 0)
+		return;			// generate a random command
+#endif
+	if (!err_method) {
+		write1(bell);   // send out a bell character
+	} else {
+		flash(10);
+	}
+}
+
+//----- Screen[] Routines --------------------------------------
+//----- Erase the Screen[] memory ------------------------------
+static void screen_erase(void)
+{
+	memset(screen, ' ', screensize);	// clear new screen
+}
+
+static int bufsum(char *buf, int count)
+{
+	int sum = 0;
+	char *e = buf + count;
+
+	while (buf < e)
+		sum += (unsigned char) *buf++;
+	return sum;
+}
+
+//----- Draw the status line at bottom of the screen -------------
+static void show_status_line(void)
+{
+	int cnt = 0, cksum = 0;
+
+	// either we already have an error or status message, or we
+	// create one.
+	if (!have_status_msg) {
+		cnt = format_edit_status();
+		cksum = bufsum(status_buffer, cnt);
+	}
+	if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
+		last_status_cksum = cksum;		// remember if we have seen this line
+		go_bottom_and_clear_to_eol();
+		write1(status_buffer);
+		if (have_status_msg) {
+			if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
+					(columns - 1) ) {
+				have_status_msg = 0;
+				Hit_Return();
+			}
+			have_status_msg = 0;
+		}
+		place_cursor(crow, ccol, FALSE);	// put cursor back in correct place
+	}
+	fflush_all();
+}
+
+//----- format the status buffer, the bottom line of screen ------
+// format status buffer, with STANDOUT mode
+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
+	va_end(args);
+
+	have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
+}
+
+// format status buffer
+static void status_line(const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	vsprintf(status_buffer, format, args);
+	va_end(args);
+
+	have_status_msg = 1;
+}
+
+// copy s to buf, convert unprintable
+static void print_literal(char *buf, const char *s)
+{
+	char *d;
+	unsigned char c;
+
+	buf[0] = '\0';
+	if (!s[0])
+		s = "(NULL)";
+
+	d = buf;
+	for (; *s; s++) {
+		int c_is_no_print;
+
+		c = *s;
+		c_is_no_print = (c & 0x80) && !Isprint(c);
+		if (c_is_no_print) {
+			strcpy(d, SOn);
+			d += sizeof(SOn)-1;
+			c = '.';
+		}
+		if (c < ' ' || c == 0x7f) {
+			*d++ = '^';
+			c |= '@'; /* 0x40 */
+			if (c == 0x7f)
+				c = '?';
+		}
+		*d++ = c;
+		*d = '\0';
+		if (c_is_no_print) {
+			strcpy(d, SOs);
+			d += sizeof(SOs)-1;
+		}
+		if (*s == '\n') {
+			*d++ = '$';
+			*d = '\0';
+		}
+		if (d - buf > MAX_INPUT_LEN - 10) // paranoia
+			break;
+	}
+}
+
+static void not_implemented(const char *s)
+{
+	char buf[MAX_INPUT_LEN];
+
+	print_literal(buf, s);
+	status_line_bold("\'%s\' is not implemented", buf);
+}
+
+// show file status on status line
+static int format_edit_status(void)
+{
+	static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
+
+#define tot format_edit_status__tot
+
+	int cur, percent, ret, trunc_at;
+
+	// file_modified is now a counter rather than a flag.  this
+	// helps reduce the amount of line counting we need to do.
+	// (this will cause a mis-reporting of modified status
+	// once every MAXINT editing operations.)
+
+	// it would be nice to do a similar optimization here -- if
+	// we haven't done a motion that could have changed which line
+	// we're on, then we shouldn't have to do this count_lines()
+	cur = count_lines(text, dot);
+
+	// reduce counting -- the total lines can't have
+	// changed if we haven't done any edits.
+	if (file_modified != last_file_modified) {
+		tot = cur + count_lines(dot, end - 1) - 1;
+		last_file_modified = file_modified;
+	}
+
+	//    current line         percent
+	//   -------------    ~~ ----------
+	//    total lines            100
+	if (tot > 0) {
+		percent = (100 * cur) / tot;
+	} else {
+		cur = tot = 0;
+		percent = 100;
+	}
+
+	trunc_at = columns < STATUS_BUFFER_LEN-1 ?
+		columns : STATUS_BUFFER_LEN-1;
+
+	ret = snprintf(status_buffer, trunc_at+1,
+#if ENABLE_FEATURE_VI_READONLY
+		"%c %s%s%s %d/%d %d%%",
+#else
+		"%c %s%s %d/%d %d%%",
+#endif
+		cmd_mode_indicator[cmd_mode & 3],
+		(current_filename != NULL ? current_filename : "No file"),
+#if ENABLE_FEATURE_VI_READONLY
+		(readonly_mode ? " [Readonly]" : ""),
+#endif
+		(file_modified ? " [Modified]" : ""),
+		cur, tot, percent);
+
+	if (ret >= 0 && ret < trunc_at)
+		return ret;  /* it all fit */
+
+	return trunc_at;  /* had to truncate */
+#undef tot
+}
+
+//----- 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
+	screen_erase();		// erase the internal screen buffer
+	last_status_cksum = 0;	// force status update
+	refresh(full_screen);	// this will redraw the entire display
+	show_status_line();
+}
+
+//----- Format a text[] line into a buffer ---------------------
+static char* format_line(char *src /*, int li*/)
+{
+	unsigned char c;
+	int co;
+	int ofs = offset;
+	char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
+
+	c = '~'; // char in col 0 in non-existent lines is '~'
+	co = 0;
+	while (co < columns + tabstop) {
+		// have we gone past the end?
+		if (src < end) {
+			c = *src++;
+			if (c == '\n')
+				break;
+			if ((c & 0x80) && !Isprint(c)) {
+				c = '.';
+			}
+			if (c < ' ' || c == 0x7f) {
+				if (c == '\t') {
+					c = ' ';
+					//      co %    8     !=     7
+					while ((co % tabstop) != (tabstop - 1)) {
+						dest[co++] = c;
+					}
+				} else {
+					dest[co++] = '^';
+					if (c == 0x7f)
+						c = '?';
+					else
+						c += '@'; // Ctrl-X -> 'X'
+				}
+			}
+		}
+		dest[co++] = c;
+		// discard scrolled-off-to-the-left portion,
+		// in tabstop-sized pieces
+		if (ofs >= tabstop && co >= tabstop) {
+			memmove(dest, dest + tabstop, co);
+			co -= tabstop;
+			ofs -= tabstop;
+		}
+		if (src >= end)
+			break;
+	}
+	// check "short line, gigantic offset" case
+	if (co < ofs)
+		ofs = co;
+	// discard last scrolled off part
+	co -= ofs;
+	dest += ofs;
+	// fill the rest with spaces
+	if (co < columns)
+		memset(&dest[co], ' ', columns - co);
+	return dest;
+}
+
+//----- Refresh the changed screen lines -----------------------
+// Copy the source line from text[] into the buffer and note
+// if the current screenline is different from the new buffer.
+// If they differ then that line needs redrawing on the terminal.
+//
+static void refresh(int full_screen)
+{
+#define old_offset refresh__old_offset
+
+	int li, changed;
+	char *tp, *sp;		// pointer into text[] and screen[]
+
+	if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
+		unsigned c = columns, r = rows;
+		query_screen_dimensions();
+		full_screen |= (c - columns) | (r - rows);
+	}
+	sync_cursor(dot, &crow, &ccol);	// where cursor will be (on "dot")
+	tp = screenbegin;	// index into text[] of top line
+
+	// compare text[] to screen[] and mark screen[] lines that need updating
+	for (li = 0; li < rows - 1; li++) {
+		int cs, ce;				// column start & end
+		char *out_buf;
+		// format current text line
+		out_buf = format_line(tp /*, li*/);
+
+		// skip to the end of the current text[] line
+		if (tp < end) {
+			char *t = memchr(tp, '\n', end - tp);
+			if (!t) t = end - 1;
+			tp = t + 1;
+		}
+
+		// see if there are any changes between vitual screen and out_buf
+		changed = FALSE;	// assume no change
+		cs = 0;
+		ce = columns - 1;
+		sp = &screen[li * columns];	// start of screen line
+		if (full_screen) {
+			// force re-draw of every single column from 0 - columns-1
+			goto re0;
+		}
+		// compare newly formatted buffer with virtual screen
+		// look forward for first difference between buf and screen
+		for (; cs <= ce; cs++) {
+			if (out_buf[cs] != sp[cs]) {
+				changed = TRUE;	// mark for redraw
+				break;
+			}
+		}
+
+		// look backward for last difference between out_buf and screen
+		for (; ce >= cs; ce--) {
+			if (out_buf[ce] != sp[ce]) {
+				changed = TRUE;	// mark for redraw
+				break;
+			}
+		}
+		// now, cs is index of first diff, and ce is index of last diff
+
+		// if horz offset has changed, force a redraw
+		if (offset != old_offset) {
+ re0:
+			changed = TRUE;
+		}
+
+		// make a sanity check of columns indexes
+		if (cs < 0) cs = 0;
+		if (ce > columns - 1) ce = columns - 1;
+		if (cs > ce) { cs = 0; ce = columns - 1; }
+		// is there a change between vitual screen and out_buf
+		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);
+			//}
+
+			// write line out to terminal
+			fwrite(&sp[cs], ce - cs + 1, 1, stdout);
+		}
+	}
+
+	place_cursor(crow, ccol, TRUE);
+
+	old_offset = offset;
+#undef old_offset
+}
+
+//---------------------------------------------------------------------
+//----- the Ascii Chart -----------------------------------------------
+//
+//  00 nul   01 soh   02 stx   03 etx   04 eot   05 enq   06 ack   07 bel
+//  08 bs    09 ht    0a nl    0b vt    0c np    0d cr    0e so    0f si
+//  10 dle   11 dc1   12 dc2   13 dc3   14 dc4   15 nak   16 syn   17 etb
+//  18 can   19 em    1a sub   1b esc   1c fs    1d gs    1e rs    1f us
+//  20 sp    21 !     22 "     23 #     24 $     25 %     26 &     27 '
+//  28 (     29 )     2a *     2b +     2c ,     2d -     2e .     2f /
+//  30 0     31 1     32 2     33 3     34 4     35 5     36 6     37 7
+//  38 8     39 9     3a :     3b ;     3c <     3d =     3e >     3f ?
+//  40 @     41 A     42 B     43 C     44 D     45 E     46 F     47 G
+//  48 H     49 I     4a J     4b K     4c L     4d M     4e N     4f O
+//  50 P     51 Q     52 R     53 S     54 T     55 U     56 V     57 W
+//  58 X     59 Y     5a Z     5b [     5c \     5d ]     5e ^     5f _
+//  60 `     61 a     62 b     63 c     64 d     65 e     66 f     67 g
+//  68 h     69 i     6a j     6b k     6c l     6d m     6e n     6f o
+//  70 p     71 q     72 r     73 s     74 t     75 u     76 v     77 w
+//  78 x     79 y     7a z     7b {     7c |     7d }     7e ~     7f del
+//---------------------------------------------------------------------
+
+//----- 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;
+	int cnt, i, j;
+	int c1;
+
+//	c1 = c; // quiet the compiler
+//	cnt = yf = 0; // quiet the compiler
+//	msg = p = q = save_dot = buf; // quiet the compiler
+	memset(buf, '\0', 12);
+
+	show_status_line();
+
+	/* if this is a cursor key, skip these checks */
+	switch (c) {
+		case KEYCODE_UP:
+		case KEYCODE_DOWN:
+		case KEYCODE_LEFT:
+		case KEYCODE_RIGHT:
+		case KEYCODE_HOME:
+		case KEYCODE_END:
+		case KEYCODE_PAGEUP:
+		case KEYCODE_PAGEDOWN:
+		case KEYCODE_DELETE:
+			goto key_cmd_mode;
+	}
+
+	if (cmd_mode == 2) {
+		//  flip-flop Insert/Replace mode
+		if (c == KEYCODE_INSERT)
+			goto dc_i;
+		// we are 'R'eplacing the current *dot with new char
+		if (*dot == '\n') {
+			// don't Replace past E-o-l
+			cmd_mode = 1;	// convert to insert
+		} else {
+			if (1 <= c || Isprint(c)) {
+				if (c != 27)
+					dot = yank_delete(dot, dot, 0, YANKDEL);	// delete char
+				dot = char_insert(dot, c);	// insert new char
+			}
+			goto dc1;
+		}
+	}
+	if (cmd_mode == 1) {
+		//  hitting "Insert" twice means "R" replace mode
+		if (c == KEYCODE_INSERT) goto dc5;
+		// insert the char c at "dot"
+		if (1 <= c || Isprint(c)) {
+			dot = char_insert(dot, c);
+		}
+		goto dc1;
+	}
+
+ key_cmd_mode:
+	switch (c) {
+		//case 0x01:	// soh
+		//case 0x09:	// ht
+		//case 0x0b:	// vt
+		//case 0x0e:	// so
+		//case 0x0f:	// si
+		//case 0x10:	// dle
+		//case 0x11:	// dc1
+		//case 0x13:	// dc3
+#if ENABLE_FEATURE_VI_CRASHME
+	case 0x14:			// dc4  ctrl-T
+		crashme = (crashme == 0) ? 1 : 0;
+		break;
+#endif
+		//case 0x16:	// syn
+		//case 0x17:	// etb
+		//case 0x18:	// can
+		//case 0x1c:	// fs
+		//case 0x1d:	// gs
+		//case 0x1e:	// rs
+		//case 0x1f:	// us
+		//case '!':	// !-
+		//case '#':	// #-
+		//case '&':	// &-
+		//case '(':	// (-
+		//case ')':	// )-
+		//case '*':	// *-
+		//case '=':	// =-
+		//case '@':	// @-
+		//case 'F':	// F-
+		//case 'K':	// K-
+		//case 'Q':	// Q-
+		//case 'S':	// S-
+		//case 'T':	// T-
+		//case 'V':	// V-
+		//case '[':	// [-
+		//case '\\':	// \-
+		//case ']':	// ]-
+		//case '_':	// _-
+		//case '`':	// `-
+		//case 'u':	// u- FIXME- there is no undo
+		//case 'v':	// v-
+	default:			// unrecognized command
+		buf[0] = c;
+		buf[1] = '\0';
+		not_implemented(buf);
+		end_cmd_q();	// stop adding to q
+	case 0x00:			// nul- ignore
+		break;
+	case 2:			// ctrl-B  scroll up   full screen
+	case KEYCODE_PAGEUP:	// Cursor Key Page Up
+		dot_scroll(rows - 2, -1);
+		break;
+	case 4:			// ctrl-D  scroll down half screen
+		dot_scroll((rows - 2) / 2, 1);
+		break;
+	case 5:			// ctrl-E  scroll down one line
+		dot_scroll(1, 1);
+		break;
+	case 6:			// ctrl-F  scroll down full screen
+	case KEYCODE_PAGEDOWN:	// Cursor Key Page Down
+		dot_scroll(rows - 2, 1);
+		break;
+	case 7:			// ctrl-G  show current status
+		last_status_cksum = 0;	// force status update
+		break;
+	case 'h':			// h- move left
+	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();
+		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
+		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);
+		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();
+		break;
+	case 21:			// ctrl-U  scroll up   half screen
+		dot_scroll((rows - 2) / 2, -1);
+		break;
+	case 25:			// ctrl-Y  scroll up one line
+		dot_scroll(1, -1);
+		break;
+	case 27:			// esc
+		if (cmd_mode == 0)
+			indicate_error(c);
+		cmd_mode = 0;	// stop insrting
+		end_cmd_q();
+		last_status_cksum = 0;	// force status update
+		break;
+	case ' ':			// move right
+	case 'l':			// move right
+	case KEYCODE_RIGHT:	// Cursor Key Right
+		if (--cmdcnt > 0) {
+			do_cmd(c);
+		}
+		dot_right();
+		break;
+#if ENABLE_FEATURE_VI_YANKMARK
+	case '"':			// "- name a register to use for Delete/Yank
+		c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
+		if ((unsigned)c1 <= 25) { // a-z?
+			YDreg = c1;
+		} else {
+			indicate_error(c);
+		}
+		break;
+	case '\'':			// '- goto a specific mark
+		c1 = (get_one_char() | 0x20) - 'a';
+		if ((unsigned)c1 <= 25) { // a-z?
+			// get the b-o-l
+			q = mark[c1];
+			if (text <= q && q < end) {
+				dot = q;
+				dot_begin();	// go to B-o-l
+				dot_skip_over_ws();
+			}
+		} else if (c1 == '\'') {	// goto previous context
+			dot = swap_context(dot);	// swap current and previous context
+			dot_begin();	// go to B-o-l
+			dot_skip_over_ws();
+		} else {
+			indicate_error(c);
+		}
+		break;
+	case 'm':			// m- Mark a line
+		// this is really stupid.  If there are any inserts or deletes
+		// between text[0] and dot then this mark will not point to the
+		// correct location! It could be off by many lines!
+		// Well..., at least its quick and dirty.
+		c1 = (get_one_char() | 0x20) - 'a';
+		if ((unsigned)c1 <= 25) { // a-z?
+			// remember the line
+			mark[c1] = dot;
+		} else {
+			indicate_error(c);
+		}
+		break;
+	case 'P':			// P- Put register before
+	case 'p':			// p- put register after
+		p = reg[YDreg];
+		if (p == NULL) {
+			status_line_bold("Nothing in register %c", what_reg());
+			break;
+		}
+		// are we putting whole lines or strings
+		if (strchr(p, '\n') != NULL) {
+			if (c == 'P') {
+				dot_begin();	// putting lines- Put above
+			}
+			if (c == 'p') {
+				// are we putting after very last line?
+				if (end_line(dot) == (end - 1)) {
+					dot = end;	// force dot to end of text[]
+				} else {
+					dot_next();	// next line, then put before
+				}
+			}
+		} else {
+			if (c == 'p')
+				dot_right();	// move to right, can move to NL
+		}
+		string_insert(dot, p);	// insert the string
+		end_cmd_q();	// stop adding to q
+		break;
+	case 'U':			// U- Undo; replace current line with original version
+		if (reg[Ureg] != 0) {
+			p = begin_line(dot);
+			q = end_line(dot);
+			p = text_hole_delete(p, q);	// delete cur line
+			p += string_insert(p, reg[Ureg]);	// insert orig line
+			dot = p;
+			dot_skip_over_ws();
+		}
+		break;
+#endif /* FEATURE_VI_YANKMARK */
+	case '$':			// $- goto end of line
+	case KEYCODE_END:		// Cursor Key End
+		if (--cmdcnt > 0) {
+			dot_next();
+			do_cmd(c);
+		}
+		dot = end_line(dot);
+		break;
+	case '%':			// %- find matching char of pair () [] {}
+		for (q = dot; q < end && *q != '\n'; q++) {
+			if (strchr("()[]{}", *q) != NULL) {
+				// we found half of a pair
+				p = find_pair(q, *q);
+				if (p == NULL) {
+					indicate_error(c);
+				} else {
+					dot = p;
+				}
+				break;
+			}
+		}
+		if (*q == '\n')
+			indicate_error(c);
+		break;
+	case 'f':			// f- forward to a user specified char
+		last_forward_char = get_one_char();	// get the search char
+		//
+		// dont separate these two commands. 'f' depends on ';'
+		//
+		//**** 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;
+		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;
+		break;
+
+	case '-':			// -- goto prev line
+		if (--cmdcnt > 0) {
+			do_cmd(c);
+		}
+		dot_prev();
+		dot_skip_over_ws();
+		break;
+#if ENABLE_FEATURE_VI_DOT_CMD
+	case '.':			// .- repeat the last modifying command
+		// Stuff the last_modifying_cmd back into stdin
+		// and let it be re-executed.
+		if (lmc_len > 0) {
+			last_modifying_cmd[lmc_len] = 0;
+			ioq = ioq_start = xstrdup(last_modifying_cmd);
+		}
+		break;
+#endif
+#if ENABLE_FEATURE_VI_SEARCH
+	case '?':			// /- search for a pattern
+	case '/':			// /- search for a pattern
+		buf[0] = c;
+		buf[1] = '\0';
+		q = get_input_line(buf);	// get input line- use "status line"
+		if (q[0] && !q[1]) {
+			if (last_search_pattern[0])
+				last_search_pattern[0] = c;
+			goto dc3; // if no pat re-use old pat
+		}
+		if (q[0]) {       // strlen(q) > 1: new pat- save it and find
+			// there is a new pat
+			free(last_search_pattern);
+			last_search_pattern = xstrdup(q);
+			goto dc3;	// now find the pattern
+		}
+		// 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] == '?') {
+			dir = FORWARD;
+			p = dot + 1;
+		}
+		goto dc4;		// now search for pattern
+		break;
+	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);
+		}
+ dc3:
+		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";
+			if (dir == BACK) {
+				msg = "search hit TOP, continuing at BOTTOM";
+			}
+		} else {
+			msg = "Pattern not found";
+		}
+ dc2:
+		if (*msg)
+			status_line_bold("%s", msg);
+		break;
+	case '{':			// {- move backward paragraph
+		q = char_search(dot, "\n\n", BACK, FULL);
+		if (q != NULL) {	// found blank line
+			dot = next_line(q);	// move to next blank line
+		}
+		break;
+	case '}':			// }- move forward paragraph
+		q = char_search(dot, "\n\n", FORWARD, FULL);
+		if (q != NULL) {	// found blank line
+			dot = next_line(q);	// move to next blank line
+		}
+		break;
+#endif /* FEATURE_VI_SEARCH */
+	case '0':			// 0- goto begining of line
+	case '1':			// 1-
+	case '2':			// 2-
+	case '3':			// 3-
+	case '4':			// 4-
+	case '5':			// 5-
+	case '6':			// 6-
+	case '7':			// 7-
+	case '8':			// 8-
+	case '9':			// 9-
+		if (c == '0' && cmdcnt < 1) {
+			dot_begin();	// this was a standalone zero
+		} else {
+			cmdcnt = cmdcnt * 10 + (c - '0');	// this 0 is part of a number
+		}
+		break;
+	case ':':			// :- the colon mode commands
+		p = get_input_line(":");	// get input line- use "status line"
+#if ENABLE_FEATURE_VI_COLON
+		colon(p);		// execute the command
+#else
+		if (*p == ':')
+			p++;				// move past the ':'
+		cnt = strlen(p);
+		if (cnt <= 0)
+			break;
+		if (strncmp(p, "quit", cnt) == 0
+		 || strncmp(p, "q!", cnt) == 0   // delete lines
+		) {
+			if (file_modified && p[1] != '!') {
+				status_line_bold("No write since last change (:quit! overrides)");
+			} else {
+				editing = 0;
+			}
+		} else if (strncmp(p, "write", cnt) == 0
+		        || strncmp(p, "wq", cnt) == 0
+		        || strncmp(p, "wn", cnt) == 0
+		        || (p[0] == 'x' && !p[1])
+		) {
+			cnt = file_write(current_filename, text, end - 1);
+			if (cnt < 0) {
+				if (cnt == -1)
+					status_line_bold("Write error: %s", strerror(errno));
+			} else {
+				file_modified = 0;
+				last_file_modified = -1;
+				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'
+				) {
+					editing = 0;
+				}
+			}
+		} else if (strncmp(p, "file", cnt) == 0) {
+			last_status_cksum = 0;	// force status update
+		} else if (sscanf(p, "%d", &j) > 0) {
+			dot = find_line(j);		// go to line # j
+			dot_skip_over_ws();
+		} else {		// unrecognized cmd
+			not_implemented(p);
+		}
+#endif /* !FEATURE_VI_COLON */
+		break;
+	case '<':			// <- Left  shift something
+	case '>':			// >- Right shift something
+		cnt = count_lines(text, dot);	// remember what line we are on
+		c1 = get_one_char();	// get the type of thing to delete
+		find_range(&p, &q, c1);
+		yank_delete(p, q, 1, YANKONLY);	// save copy before change
+		p = begin_line(p);
+		q = end_line(q);
+		i = count_lines(p, q);	// # of lines we are shifting
+		for ( ; i > 0; i--, p = next_line(p)) {
+			if (c == '<') {
+				// shift left- remove tab or 8 spaces
+				if (*p == '\t') {
+					// shrink buffer 1 char
+					text_hole_delete(p, p);
+				} else if (*p == ' ') {
+					// we should be calculating columns, not just SPACE
+					for (j = 0; *p == ' ' && j < tabstop; j++) {
+						text_hole_delete(p, p);
+					}
+				}
+			} else if (c == '>') {
+				// shift right -- add tab or 8 spaces
+				char_insert(p, '\t');
+			}
+		}
+		dot = find_line(cnt);	// what line were we on
+		dot_skip_over_ws();
+		end_cmd_q();	// stop adding to q
+		break;
+	case 'A':			// A- append at e-o-l
+		dot_end();		// go to e-o-l
+		//**** fall through to ... 'a'
+	case 'a':			// a- append after current char
+		if (*dot != '\n')
+			dot++;
+		goto dc_i;
+		break;
+	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);
+		break;
+	case 'C':			// C- Change to e-o-l
+	case 'D':			// D- delete to e-o-l
+		save_dot = dot;
+		dot = dollar_line(dot);	// move to before NL
+		// copy text into a register and delete
+		dot = yank_delete(save_dot, dot, 0, YANKDEL);	// delete to e-o-l
+		if (c == 'C')
+			goto dc_i;	// start inserting
+#if ENABLE_FEATURE_VI_DOT_CMD
+		if (c == 'D')
+			end_cmd_q();	// stop adding to q
+#endif
+		break;
+	case 'g': // 'gg' goto a line number (vim) (default: very first line)
+		c1 = get_one_char();
+		if (c1 != 'g') {
+			buf[0] = 'g';
+			buf[1] = c1; // TODO: if Unicode?
+			buf[2] = '\0';
+			not_implemented(buf);
+			break;
+		}
+		if (cmdcnt == 0)
+			cmdcnt = 1;
+		/* fall through */
+	case 'G':		// G- goto to a line number (default= E-O-F)
+		dot = end - 1;				// assume E-O-F
+		if (cmdcnt > 0) {
+			dot = find_line(cmdcnt);	// what line is #cmdcnt
+		}
+		dot_skip_over_ws();
+		break;
+	case 'H':			// H- goto top line on screen
+		dot = screenbegin;
+		if (cmdcnt > (rows - 1)) {
+			cmdcnt = (rows - 1);
+		}
+		if (--cmdcnt > 0) {
+			do_cmd('+');
+		}
+		dot_skip_over_ws();
+		break;
+	case 'I':			// I- insert before first non-blank
+		dot_begin();	// 0
+		dot_skip_over_ws();
+		//**** fall through to ... 'i'
+	case 'i':			// i- insert before current char
+	case KEYCODE_INSERT:	// Cursor Key Insert
+ dc_i:
+		cmd_mode = 1;	// start insrting
+		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();
+			}
+		}
+		end_cmd_q();	// stop adding to q
+		break;
+	case 'L':			// L- goto bottom line on screen
+		dot = end_screen();
+		if (cmdcnt > (rows - 1)) {
+			cmdcnt = (rows - 1);
+		}
+		if (--cmdcnt > 0) {
+			do_cmd('-');
+		}
+		dot_begin();
+		dot_skip_over_ws();
+		break;
+	case 'M':			// M- goto middle line on screen
+		dot = screenbegin;
+		for (cnt = 0; cnt < (rows-1) / 2; cnt++)
+			dot = next_line(dot);
+		break;
+	case 'O':			// O- open a empty line above
+		//    0i\n ESC -i
+		p = begin_line(dot);
+		if (p[-1] == '\n') {
+			dot_prev();
+	case 'o':			// o- open a empty line below; Yes, I know it is in the middle of the "if (..."
+			dot_end();
+			dot = char_insert(dot, '\n');
+		} else {
+			dot_begin();	// 0
+			dot = char_insert(dot, '\n');	// i\n ESC
+			dot_prev();	// -
+		}
+		goto dc_i;
+		break;
+	case 'R':			// R- continuous Replace char
+ dc5:
+		cmd_mode = 2;
+		break;
+	case KEYCODE_DELETE:
+		c = 'x';
+		// fall through
+	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
+		end_cmd_q();	// stop adding to q
+		break;
+	case 'Z':			// Z- if modified, {write}; exit
+		// ZZ means to save file (if necessary), then exit
+		c1 = get_one_char();
+		if (c1 != 'Z') {
+			indicate_error(c);
+			break;
+		}
+		if (file_modified) {
+			if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
+				status_line_bold("\"%s\" File is read only", current_filename);
+				break;
+			}
+			cnt = file_write(current_filename, text, end - 1);
+			if (cnt < 0) {
+				if (cnt == -1)
+					status_line_bold("Write error: %s", strerror(errno));
+			} else if (cnt == (end - 1 - text + 1)) {
+				editing = 0;
+			}
+		} else {
+			editing = 0;
+		}
+		break;
+	case '^':			// ^- move to first non-blank on line
+		dot_begin();
+		dot_skip_over_ws();
+		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);
+		}
+		break;
+	case 'c':			// c- change something
+	case 'd':			// d- delete something
+#if ENABLE_FEATURE_VI_YANKMARK
+	case 'y':			// y- yank   something
+	case 'Y':			// Y- Yank a line
+#endif
+	{
+		int yf, ml, whole = 0;
+		yf = YANKDEL;	// assume either "c" or "d"
+#if ENABLE_FEATURE_VI_YANKMARK
+		if (c == 'y' || c == 'Y')
+			yf = YANKONLY;
+#endif
+		c1 = 'y';
+		if (c != 'Y')
+			c1 = get_one_char();	// get the type of thing to delete
+		// determine range, and whether it spans lines
+		ml = find_range(&p, &q, c1);
+		if (c1 == 27) {	// ESC- user changed mind and wants out
+			c = c1 = 27;	// Escape- do nothing
+		} else if (strchr("wW", c1)) {
+			if (c == 'c') {
+				// don't include trailing WS as part of word
+				while (isblank(*q)) {
+					if (q <= text || q[-1] == '\n')
+						break;
+					q--;
+				}
+			}
+			dot = yank_delete(p, q, ml, yf);	// delete word
+		} else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
+			// partial line copy text into a register and delete
+			dot = yank_delete(p, q, ml, yf);	// delete word
+		} else if (strchr("cdykjHL+-{}\r\n", c1)) {
+			// whole line copy text into a register and delete
+			dot = yank_delete(p, q, ml, yf);	// delete lines
+			whole = 1;
+		} else {
+			// could not recognize object
+			c = c1 = 27;	// error-
+			ml = 0;
+			indicate_error(c);
+		}
+		if (ml && whole) {
+			if (c == 'c') {
+				dot = char_insert(dot, '\n');
+				// on the last line of file don't move to prev line
+				if (whole && dot != (end-1)) {
+					dot_prev();
+				}
+			} else if (c == 'd') {
+				dot_begin();
+				dot_skip_over_ws();
+			}
+		}
+		if (c1 != 27) {
+			// if CHANGING, not deleting, start inserting after the delete
+			if (c == 'c') {
+				strcpy(buf, "Change");
+				goto dc_i;	// start inserting
+			}
+			if (c == 'd') {
+				strcpy(buf, "Delete");
+			}
+#if ENABLE_FEATURE_VI_YANKMARK
+			if (c == 'y' || c == 'Y') {
+				strcpy(buf, "Yank");
+			}
+			p = reg[YDreg];
+			q = p + strlen(p);
+			for (cnt = 0; p <= q; p++) {
+				if (*p == '\n')
+					cnt++;
+			}
+			status_line("%s %d lines (%d chars) using [%c]",
+				buf, cnt, strlen(reg[YDreg]), what_reg());
+#endif
+			end_cmd_q();	// stop adding to q
+		}
+		break;
+	}
+	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
+		break;
+	case 'r':			// r- replace the current char with user input
+		c1 = get_one_char();	// get the replacement char
+		if (*dot != '\n') {
+			*dot = c1;
+			file_modified++;
+		}
+		end_cmd_q();	// stop adding to q
+		break;
+	case 't':			// t- move to char prior to next x
+		last_forward_char = get_one_char();
+		do_cmd(';');
+		if (*dot == last_forward_char)
+			dot_left();
+		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);
+		}
+		break;
+	case 'z':			// z-
+		c1 = get_one_char();	// get the replacement char
+		cnt = 0;
+		if (c1 == '.')
+			cnt = (rows - 2) / 2;	// put dot at center
+		if (c1 == '-')
+			cnt = rows - 2;	// put dot at bottom
+		screenbegin = begin_line(dot);	// start dot at top
+		dot_scroll(cnt, -1);
+		break;
+	case '|':			// |- move to column "cmdcnt"
+		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();
+		end_cmd_q();	// stop adding to q
+		break;
+		//----- The Cursor and Function Keys -----------------------------
+	case KEYCODE_HOME:	// Cursor Key Home
+		dot_begin();
+		break;
+		// The Fn keys could point to do_macro which could translate them
+#if 0
+	case KEYCODE_FUN1:	// Function Key F1
+	case KEYCODE_FUN2:	// Function Key F2
+	case KEYCODE_FUN3:	// Function Key F3
+	case KEYCODE_FUN4:	// Function Key F4
+	case KEYCODE_FUN5:	// Function Key F5
+	case KEYCODE_FUN6:	// Function Key F6
+	case KEYCODE_FUN7:	// Function Key F7
+	case KEYCODE_FUN8:	// Function Key F8
+	case KEYCODE_FUN9:	// Function Key F9
+	case KEYCODE_FUN10:	// Function Key F10
+	case KEYCODE_FUN11:	// Function Key F11
+	case KEYCODE_FUN12:	// Function Key F12
+		break;
+#endif
+	}
+
+ dc1:
+	// if text[] just became empty, add back an empty line
+	if (end == text) {
+		char_insert(text, '\n');	// start empty buf with dummy line
+		dot = text;
+	}
+	// it is OK for dot to exactly equal to end, otherwise check dot validity
+	if (dot != end) {
+		dot = bound_dot(dot);	// make sure "dot" is valid
+	}
+#if ENABLE_FEATURE_VI_YANKMARK
+	check_context(c);	// update the current context
+#endif
+
+	if (!isdigit(c))
+		cmdcnt = 0;		// cmd was not a number, reset cmdcnt
+	cnt = dot - begin_line(dot);
+	// Try to stay off of the Newline
+	if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
+		dot--;
+}
+
+/* NB!  the CRASHME code is unmaintained, and doesn't currently build */
+#if ENABLE_FEATURE_VI_CRASHME
+static int totalcmds = 0;
+static int Mp = 85;             // Movement command Probability
+static int Np = 90;             // Non-movement command Probability
+static int Dp = 96;             // Delete command Probability
+static int Ip = 97;             // Insert command Probability
+static int Yp = 98;             // Yank command Probability
+static int Pp = 99;             // Put command Probability
+static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
+static const char chars[20] = "\t012345 abcdABCD-=.$";
+static const char *const words[20] = {
+	"this", "is", "a", "test",
+	"broadcast", "the", "emergency", "of",
+	"system", "quick", "brown", "fox",
+	"jumped", "over", "lazy", "dogs",
+	"back", "January", "Febuary", "March"
+};
+static const char *const lines[20] = {
+	"You should have received a copy of the GNU General Public License\n",
+	"char c, cm, *cmd, *cmd1;\n",
+	"generate a command by percentages\n",
+	"Numbers may be typed as a prefix to some commands.\n",
+	"Quit, discarding changes!\n",
+	"Forced write, if permission originally not valid.\n",
+	"In general, any ex or ed command (such as substitute or delete).\n",
+	"I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
+	"Please get w/ me and I will go over it with you.\n",
+	"The following is a list of scheduled, committed changes.\n",
+	"1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
+	"Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
+	"Any question about transactions please contact Sterling Huxley.\n",
+	"I will try to get back to you by Friday, December 31.\n",
+	"This Change will be implemented on Friday.\n",
+	"Let me know if you have problems accessing this;\n",
+	"Sterling Huxley recently added you to the access list.\n",
+	"Would you like to go to lunch?\n",
+	"The last command will be automatically run.\n",
+	"This is too much english for a computer geek.\n",
+};
+static char *multilines[20] = {
+	"You should have received a copy of the GNU General Public License\n",
+	"char c, cm, *cmd, *cmd1;\n",
+	"generate a command by percentages\n",
+	"Numbers may be typed as a prefix to some commands.\n",
+	"Quit, discarding changes!\n",
+	"Forced write, if permission originally not valid.\n",
+	"In general, any ex or ed command (such as substitute or delete).\n",
+	"I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
+	"Please get w/ me and I will go over it with you.\n",
+	"The following is a list of scheduled, committed changes.\n",
+	"1.   Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
+	"Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
+	"Any question about transactions please contact Sterling Huxley.\n",
+	"I will try to get back to you by Friday, December 31.\n",
+	"This Change will be implemented on Friday.\n",
+	"Let me know if you have problems accessing this;\n",
+	"Sterling Huxley recently added you to the access list.\n",
+	"Would you like to go to lunch?\n",
+	"The last command will be automatically run.\n",
+	"This is too much english for a computer geek.\n",
+};
+
+// create a random command to execute
+static void crash_dummy()
+{
+	static int sleeptime;   // how long to pause between commands
+	char c, cm, *cmd, *cmd1;
+	int i, cnt, thing, rbi, startrbi, percent;
+
+	// "dot" movement commands
+	cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
+
+	// is there already a command running?
+	if (readbuffer[0] > 0)
+		goto cd1;
+ cd0:
+	readbuffer[0] = 'X';
+	startrbi = rbi = 1;
+	sleeptime = 0;          // how long to pause between commands
+	memset(readbuffer, '\0', sizeof(readbuffer));
+	// generate a command by percentages
+	percent = (int) lrand48() % 100;        // get a number from 0-99
+	if (percent < Mp) {     //  Movement commands
+		// available commands
+		cmd = cmd1;
+		M++;
+	} else if (percent < Np) {      //  non-movement commands
+		cmd = "mz<>\'\"";       // available commands
+		N++;
+	} else if (percent < Dp) {      //  Delete commands
+		cmd = "dx";             // available commands
+		D++;
+	} else if (percent < Ip) {      //  Inset commands
+		cmd = "iIaAsrJ";        // available commands
+		I++;
+	} else if (percent < Yp) {      //  Yank commands
+		cmd = "yY";             // available commands
+		Y++;
+	} else if (percent < Pp) {      //  Put commands
+		cmd = "pP";             // available commands
+		P++;
+	} else {
+		// We do not know how to handle this command, try again
+		U++;
+		goto cd0;
+	}
+	// randomly pick one of the available cmds from "cmd[]"
+	i = (int) lrand48() % strlen(cmd);
+	cm = cmd[i];
+	if (strchr(":\024", cm))
+		goto cd0;               // dont allow colon or ctrl-T commands
+	readbuffer[rbi++] = cm; // put cmd into input buffer
+
+	// now we have the command-
+	// there are 1, 2, and multi char commands
+	// find out which and generate the rest of command as necessary
+	if (strchr("dmryz<>\'\"", cm)) {        // 2-char commands
+		cmd1 = " \n\r0$^-+wWeEbBhjklHL";
+		if (cm == 'm' || cm == '\'' || cm == '\"') {    // pick a reg[]
+			cmd1 = "abcdefghijklmnopqrstuvwxyz";
+		}
+		thing = (int) lrand48() % strlen(cmd1); // pick a movement command
+		c = cmd1[thing];
+		readbuffer[rbi++] = c;  // add movement to input buffer
+	}
+	if (strchr("iIaAsc", cm)) {     // multi-char commands
+		if (cm == 'c') {
+			// change some thing
+			thing = (int) lrand48() % strlen(cmd1); // pick a movement command
+			c = cmd1[thing];
+			readbuffer[rbi++] = c;  // add movement to input buffer
+		}
+		thing = (int) lrand48() % 4;    // what thing to insert
+		cnt = (int) lrand48() % 10;     // how many to insert
+		for (i = 0; i < cnt; i++) {
+			if (thing == 0) {       // insert chars
+				readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
+			} else if (thing == 1) {        // insert words
+				strcat(readbuffer, words[(int) lrand48() % 20]);
+				strcat(readbuffer, " ");
+				sleeptime = 0;  // how fast to type
+			} else if (thing == 2) {        // insert lines
+				strcat(readbuffer, lines[(int) lrand48() % 20]);
+				sleeptime = 0;  // how fast to type
+			} else {        // insert multi-lines
+				strcat(readbuffer, multilines[(int) lrand48() % 20]);
+				sleeptime = 0;  // how fast to type
+			}
+		}
+		strcat(readbuffer, "\033");
+	}
+	readbuffer[0] = strlen(readbuffer + 1);
+ cd1:
+	totalcmds++;
+	if (sleeptime > 0)
+		mysleep(sleeptime);      // sleep 1/100 sec
+}
+
+// test to see if there are any errors
+static void crash_test()
+{
+	static time_t oldtim;
+
+	time_t tim;
+	char d[2], msg[80];
+
+	msg[0] = '\0';
+	if (end < text) {
+		strcat(msg, "end<text ");
+	}
+	if (end > textend) {
+		strcat(msg, "end>textend ");
+	}
+	if (dot < text) {
+		strcat(msg, "dot<text ");
+	}
+	if (dot > end) {
+		strcat(msg, "dot>end ");
+	}
+	if (screenbegin < text) {
+		strcat(msg, "screenbegin<text ");
+	}
+	if (screenbegin > end - 1) {
+		strcat(msg, "screenbegin>end-1 ");
+	}
+
+	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);
+		fflush_all();
+		while (safe_read(STDIN_FILENO, d, 1) > 0) {
+			if (d[0] == '\n' || d[0] == '\r')
+				break;
+		}
+	}
+	tim = time(NULL);
+	if (tim >= (oldtim + 3)) {
+		sprintf(status_buffer,
+				"Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
+				totalcmds, M, N, I, D, Y, P, U, end - text + 1);
+		oldtim = tim;
+	}
+}
+#endif
diff --git a/busybox-1.19.3/examples/android-build b/busybox-1.19.3/examples/android-build
new file mode 100755
index 0000000..f5fe49b
--- /dev/null
+++ b/busybox-1.19.3/examples/android-build
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Build Busybox against Android's bionic
+# Originally by Dan Fandrich
+#
+# Configure with android_defconfig
+# This file has been tested on Android Froyo (the lack of ttyname_r in
+# the 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" "$@"
diff --git a/busybox-1.19.3/examples/bootfloppy/bootfloppy.txt b/busybox-1.19.3/examples/bootfloppy/bootfloppy.txt
new file mode 100644
index 0000000..9e2cbe2
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/bootfloppy.txt
@@ -0,0 +1,180 @@
+Building a Busybox Boot Floppy
+==============================
+
+This document describes how to buid a boot floppy using the following
+components:
+
+ - Linux Kernel (http://www.kernel.org)
+ - uClibc: C library (http://www.uclibc.org/)
+ - Busybox: Unix utilities (http://busybox.net/)
+ - Syslinux: bootloader (http://syslinux.zytor.com)
+
+It is based heavily on a paper presented by Erik Andersen at the 2001 Embedded
+Systems Conference.
+
+
+
+Building The Software Components
+--------------------------------
+
+Detailed instructions on how to build Busybox, uClibc, or a working Linux
+kernel are beyond the scope of this document. The following guidelines will
+help though:
+
+	- Stock Busybox from CVS or a tarball will work with no modifications to
+	  any files. Just extract and go.
+	- Ditto uClibc.
+	- Your Linux kernel must include support for initrd or else the floppy
+	  won't be able to mount it's root file system.
+
+If you require further information on building Busybox uClibc or Linux, please
+refer to the web pages and documentation for those individual programs.
+
+
+
+Making a Root File System
+-------------------------
+
+The following steps will create a root file system.
+
+ - Create an empty file that you can format as a filesystem:
+
+	dd if=/dev/zero of=rootfs bs=1k count=4000
+
+ - Set up the rootfs file we just created to be used as a loop device (may not
+   be necessary)
+
+	losetup /dev/loop0 rootfs
+
+ - Format the rootfs file with a filesystem:
+
+	mkfs.ext2 -F -i 2000 rootfs
+
+ - Mount the file on a mountpoint so we can place files in it:
+
+	mkdir loop
+	mount -o loop rootfs loop/
+
+	(you will probably need to be root to do this)
+
+ - Copy on the C library, the dynamic linking library, and other necessary
+   libraries. For this example, we copy the following files from the uClibc
+   tree:
+
+	mkdir loop/lib
+	(chdir to uClibc directory)
+	cp -a libc.so* uClibc*.so \
+		ld.so-1/d-link/ld-linux-uclibc.so* \
+		ld.so-1/libdl/libdl.so* \
+		crypt/libcrypt.so* \
+		(path to)loop/lib
+
+ - Install the Busybox binary and accompanying symlinks:
+
+	(chdir to busybox directory)
+	make CONFIG_PREFIX=(path to)loop/ install
+
+ - Make device files in /dev:
+
+	This can be done by running the 'mkdevs.sh' script. If you want the gory
+	details, you can read the script.
+
+ - Make necessary files in /etc:
+
+	For this, just cp -a the etc/ directory onto rootfs. Again, if you want
+	all the details, you can just look at the files in the dir.
+
+ - Unmount the rootfs from the mountpoint:
+
+	umount loop
+
+ - Compress it:
+
+	gzip -9 rootfs
+
+
+Making a SYSLINUX boot floppy
+-----------------------------
+
+The following steps will create the boot floppy.
+
+Note: You will need to have the mtools package installed beforehand.
+
+ - Insert a floppy in the drive and format it with an MSDOS filesystem:
+
+	mformat a:
+
+	(if the system doesn't know what device 'a:' is, look at /etc/mtools.conf)
+
+ - Run syslinux on the floppy:
+
+	syslinux -s /dev/fd0
+
+	(the -s stands for "safe, slow, and stupid" and should work better with
+	buggy BIOSes; it can be omitted)
+
+ - Put on a syslinux.cfg file:
+
+	mcopy syslinux.cfg a:
+
+	(more on syslinux.cfg below)
+
+ - Copy the root file system you made onto the MSDOS formatted floppy
+
+	mcopy rootfs.gz a:
+
+ - Build a linux kernel and copy it onto the disk with the filename 'linux'
+
+	mcopy bzImage a:linux
+
+
+Sample syslinux.cfg
+~~~~~~~~~~~~~~~~~~~
+
+The following simple syslinux.cfg file should work. You can tweak it if you
+like.
+
+----begin-syslinux.cfg---------------
+DEFAULT linux
+APPEND initrd=rootfs.gz root=/dev/ram0
+TIMEOUT 10
+PROMPT 1
+----end-syslinux.cfg---------------
+
+Some changes you could make to syslinux.cfg:
+
+ - This value is the number seconds it will wait before booting. You can set
+   the timeout to 0 (or omit) to boot instantly, or you can set it as high as
+   10 to wait awhile.
+
+ - PROMPT can be set to 0 to disable the 'boot:' prompt.
+
+ - you can add this line to display the contents of a file as a welcome
+   message:
+
+	DISPLAY display.txt
+
+
+
+Additional Resources
+--------------------
+
+Other useful information on making a Linux bootfloppy is available at the
+following URLs:
+
+http://www.linuxdoc.org/HOWTO/Bootdisk-HOWTO/index.html
+http://www.linux-embedded.com/howto/Embedded-Linux-Howto.html
+http://linux-embedded.org/howto/LFS-HOWTO.html
+http://linux-embedded.org/pmhowto.html
+http://recycle.lbl.gov/~ldoolitt/embedded/ (Larry Doolittle's stuff)
+
+
+
+Possible TODOs
+--------------
+
+The following features that we might want to add later:
+
+ - support for additional filesystems besides ext2, i.e. minix
+ - different libc, static vs dynamic loading
+ - maybe using an alternate bootloader
diff --git a/busybox-1.19.3/examples/bootfloppy/display.txt b/busybox-1.19.3/examples/bootfloppy/display.txt
new file mode 100644
index 0000000..7cae48b
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/display.txt
@@ -0,0 +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.
diff --git a/busybox-1.19.3/examples/bootfloppy/etc/fstab b/busybox-1.19.3/examples/bootfloppy/etc/fstab
new file mode 100644
index 0000000..b31f602
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/etc/fstab
@@ -0,0 +1 @@
+proc		/proc	proc	defaults    0	0
diff --git a/busybox-1.19.3/examples/bootfloppy/etc/init.d/rcS b/busybox-1.19.3/examples/bootfloppy/etc/init.d/rcS
new file mode 100755
index 0000000..4f29b92
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/etc/init.d/rcS
@@ -0,0 +1,3 @@
+#! /bin/sh
+
+/bin/mount -a
diff --git a/busybox-1.19.3/examples/bootfloppy/etc/inittab b/busybox-1.19.3/examples/bootfloppy/etc/inittab
new file mode 100644
index 0000000..1ac9f68
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/etc/inittab
@@ -0,0 +1,4 @@
+::sysinit:/etc/init.d/rcS
+::respawn:-/bin/sh
+tty2::askfirst:-/bin/sh
+::ctrlaltdel:/bin/umount -a -r
diff --git a/busybox-1.19.3/examples/bootfloppy/etc/profile b/busybox-1.19.3/examples/bootfloppy/etc/profile
new file mode 100644
index 0000000..cf68d33
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/etc/profile
@@ -0,0 +1,7 @@
+# /etc/profile: system-wide .profile file for the Bourne shells
+
+echo
+echo -n "Processing /etc/profile... "
+# no-op
+echo "Done"
+echo
diff --git a/busybox-1.19.3/examples/bootfloppy/mkdevs.sh b/busybox-1.19.3/examples/bootfloppy/mkdevs.sh
new file mode 100755
index 0000000..8e9512f
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/mkdevs.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# makedev.sh - creates device files for a busybox boot floppy image
+
+
+# we do our work in the dev/ directory
+if [ -z "$1" ]; then
+	echo "usage: `basename $0` path/to/dev/dir"
+	exit 1
+fi
+
+cd $1
+
+
+# miscellaneous one-of-a-kind stuff
+mknod console c 5 1
+mknod full c 1 7
+mknod kmem c 1 2
+mknod mem c 1 1
+mknod null c 1 3
+mknod port c 1 4
+mknod random c 1 8
+mknod urandom c 1 9
+mknod zero c 1 5
+ln -s /proc/kcore core
+
+# IDE HD devs
+# note: not going to bother creating all concievable partitions; you can do
+# that yourself as you need 'em.
+mknod hda b 3 0
+mknod hdb b 3 64
+mknod hdc b 22 0
+mknod hdd b 22 64
+
+# loop devs
+for i in `seq 0 7`; do
+	mknod loop$i b 7 $i
+done
+
+# ram devs
+for i in `seq 0 9`; do
+	mknod ram$i b 1 $i
+done
+ln -s ram1 ram
+
+# ttys
+mknod tty c 5 0
+for i in `seq 0 9`; do
+	mknod tty$i c 4 $i
+done
+
+# virtual console screen devs
+for i in `seq 0 9`; do
+	mknod vcs$i b 7 $i
+done
+ln -s vcs0 vcs
+
+# virtual console screen w/ attributes devs
+for i in `seq 0 9`; do
+	mknod vcsa$i b 7 $((128 + i))
+done
+ln -s vcsa0 vcsa
diff --git a/busybox-1.19.3/examples/bootfloppy/mkrootfs.sh b/busybox-1.19.3/examples/bootfloppy/mkrootfs.sh
new file mode 100755
index 0000000..a7fc48b
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/mkrootfs.sh
@@ -0,0 +1,104 @@
+#!/bin/bash
+#
+# mkrootfs.sh - creates a root file system
+#
+
+# TODO: need to add checks here to verify that busybox, uClibc and bzImage
+# exist
+
+
+# command-line settable variables
+BUSYBOX_DIR=..
+UCLIBC_DIR=../../uClibc
+TARGET_DIR=./loop
+FSSIZE=4000
+CLEANUP=1
+MKFS='mkfs.ext2 -F'
+
+# don't-touch variables
+BASE_DIR=`pwd`
+
+
+while getopts 'b:u:s:t:Cm' opt
+do
+	case $opt in
+		b) BUSYBOX_DIR=$OPTARG ;;
+		u) UCLIBC_DIR=$OPTARG ;;
+		t) TARGET_DIR=$OPTARG ;;
+		s) FSSIZE=$OPTARG ;;
+		C) CLEANUP=0 ;;
+		m) MKFS='mkfs.minix' ;;
+		*)
+			echo "usage: `basename $0` [-bu]"
+			echo "  -b DIR  path to busybox direcory (default ..)"
+			echo "  -u DIR  path to uClibc direcory (default ../../uClibc)"
+			echo "  -t DIR  path to target direcory (default ./loop)"
+			echo "  -s SIZE size of root filesystem in Kbytes (default 4000)"
+			echo "  -C      don't perform cleanup (umount target dir, gzip rootfs, etc.)"
+			echo "          (this allows you to 'chroot loop/ /bin/sh' to test it)"
+			echo "  -m      use minix filesystem (default is ext2)"
+			exit 1
+			;;
+	esac
+done
+
+
+
+
+# clean up from any previous work
+mount | grep -q loop
+[ $? -eq 0 ] && umount $TARGET_DIR
+[ -d $TARGET_DIR ] && rm -rf $TARGET_DIR/
+[ -f rootfs ] && rm -f rootfs
+[ -f rootfs.gz ] && rm -f rootfs.gz
+
+
+# prepare root file system and mount as loopback
+dd if=/dev/zero of=rootfs bs=1k count=$FSSIZE
+$MKFS -i 2000 rootfs
+mkdir $TARGET_DIR
+mount -o loop,exec rootfs $TARGET_DIR # must be root
+
+
+# install uClibc
+mkdir -p $TARGET_DIR/lib
+cd $UCLIBC_DIR
+make INSTALL_DIR=
+cp -a libc.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a uClibc*.so $BASE_DIR/$TARGET_DIR/lib
+cp -a ld.so-1/d-link/ld-linux-uclibc.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a ld.so-1/libdl/libdl.so* $BASE_DIR/$TARGET_DIR/lib
+cp -a crypt/libcrypt.so* $BASE_DIR/$TARGET_DIR/lib
+cd $BASE_DIR
+
+
+# install busybox and components
+cd $BUSYBOX_DIR
+make distclean
+make CC=$BASE_DIR/$UCLIBC_DIR/extra/gcc-uClibc/i386-uclibc-gcc
+make CONFIG_PREFIX=$BASE_DIR/$TARGET_DIR install
+cd $BASE_DIR
+
+
+# make files in /dev
+mkdir $TARGET_DIR/dev
+./mkdevs.sh $TARGET_DIR/dev
+
+
+# make files in /etc
+cp -a etc $TARGET_DIR
+ln -s /proc/mounts $TARGET_DIR/etc/mtab
+
+
+# other miscellaneous setup
+mkdir $TARGET_DIR/initrd
+mkdir $TARGET_DIR/proc
+
+
+# Done. Maybe do cleanup.
+if [ $CLEANUP -eq 1 ]
+then
+	umount $TARGET_DIR
+	rmdir $TARGET_DIR
+	gzip -9 rootfs
+fi
diff --git a/busybox-1.19.3/examples/bootfloppy/mksyslinux.sh b/busybox-1.19.3/examples/bootfloppy/mksyslinux.sh
new file mode 100755
index 0000000..e254173
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/mksyslinux.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Formats a floppy to use Syslinux
+
+dummy=""
+
+
+# need to have mtools installed
+if [ -z `which mformat` -o -z `which mcopy` ]; then
+	echo "You must have the mtools package installed to run this script"
+	exit 1
+fi
+
+
+# need an arg for the location of the kernel
+if [ -z "$1" ]; then
+	echo "usage: `basename $0` path/to/linux/kernel"
+	exit 1
+fi
+
+
+# need to have a root file system built
+if [ ! -f rootfs.gz ]; then
+	echo "You need to have a rootfs built first."
+	echo "Hit RETURN to make one now or Control-C to quit."
+	read dummy
+	./mkrootfs.sh
+fi
+
+
+# prepare the floppy
+echo "Please insert a blank floppy in the drive and press RETURN to format"
+echo "(WARNING: All data will be erased! Hit Control-C to abort)"
+read dummy
+
+echo "Formatting the floppy..."
+mformat a:
+echo "Making it bootable with Syslinux..."
+syslinux -s /dev/fd0
+echo "Copying Syslinux configuration files..."
+mcopy syslinux.cfg display.txt a:
+echo "Copying root filesystem file..."
+mcopy rootfs.gz a:
+# XXX: maybe check for "no space on device" errors here
+echo "Copying linux kernel..."
+mcopy $1 a:linux
+# XXX: maybe check for "no space on device" errors here too
+echo "Finished: boot floppy created"
diff --git a/busybox-1.19.3/examples/bootfloppy/quickstart.txt b/busybox-1.19.3/examples/bootfloppy/quickstart.txt
new file mode 100644
index 0000000..0d80908
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/quickstart.txt
@@ -0,0 +1,15 @@
+Quickstart on making the Busybox boot-floppy:
+
+  1) Download Busybox and uClibc from CVS or tarballs. Make sure they share a
+     common parent directory. (i.e. busybox/ and uclibc/ are both right off of
+	 /tmp, or wherever.)
+
+  2) Build a Linux kernel. Make sure you include support for initrd.
+
+  3) Put a floppy in the drive. Make sure it is a floppy you don't care about
+     because the contents will be overwritten.
+
+  4) As root, type ./mksyslinux.sh path/to/linux/kernel from this directory.
+     Wait patiently while the magic happens.
+
+  5) Boot up on the floppy.
diff --git a/busybox-1.19.3/examples/bootfloppy/syslinux.cfg b/busybox-1.19.3/examples/bootfloppy/syslinux.cfg
new file mode 100644
index 0000000..fa2677c
--- /dev/null
+++ b/busybox-1.19.3/examples/bootfloppy/syslinux.cfg
@@ -0,0 +1,7 @@
+display display.txt
+default linux
+timeout 10
+prompt 1
+label linux
+	kernel linux
+	append initrd=rootfs.gz root=/dev/ram0
diff --git a/busybox-1.19.3/examples/busybox.spec b/busybox-1.19.3/examples/busybox.spec
new file mode 100644
index 0000000..27d0051
--- /dev/null
+++ b/busybox-1.19.3/examples/busybox.spec
@@ -0,0 +1,120 @@
+Summary: Statically linked binary providing simplified versions of system commands
+Name: busybox
+Version: 1.15.1
+Release: 1%{?dist}
+Epoch: 1
+License: GPLv2
+Group: System Environment/Shells
+Source: http://www.busybox.net/downloads/%{name}-%{version}.tar.bz2
+Source1: busybox-static.config
+Source2: busybox-petitboot.config
+Source3: http://www.uclibc.org/downloads/uClibc-0.9.30.1.tar.bz2
+Source4: uClibc.config
+Patch16: busybox-1.10.1-hwclock.patch
+# patch to avoid conflicts with getline() from stdio.h, already present in upstream VCS
+Patch22: uClibc-0.9.30.1-getline.patch
+Obsoletes: busybox-anaconda
+URL: http://www.busybox.net
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: libselinux-devel >= 1.27.7-2
+BuildRequires: libsepol-devel
+BuildRequires: libselinux-static
+BuildRequires: libsepol-static
+BuildRequires: glibc-static
+
+%define debug_package %{nil}
+
+%package petitboot
+Group: System Environment/Shells
+Summary: Version of busybox configured for use with petitboot
+
+%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.
+
+%description petitboot
+Busybox is a single binary which includes versions of a large number
+of system commands, including a shell.  The version contained in this
+package is a minimal configuration intended for use with the Petitboot
+bootloader used on PlayStation 3. The busybox package provides a binary
+better suited to normal use.
+
+%prep
+%setup -q -a3
+%patch16 -b .ia64 -p1
+cat %{SOURCE4} >uClibc-0.9.30.1/.config1
+%patch22 -b .getline -p1
+
+%build
+# create static busybox - the executable is kept as busybox-static
+# We use uclibc instead of system glibc, uclibc is several times
+# smaller, this is important for static build.
+# Build uclibc first.
+cd uClibc-0.9.30.1
+# fixme:
+mkdir kernel-include
+cp -a /usr/include/asm kernel-include
+cp -a /usr/include/asm-generic kernel-include
+cp -a /usr/include/linux kernel-include
+# uclibc can't be built on ppc64,s390,ia64, we set $arch to "" in this case
+arch=`uname -m | sed -e 's/i.86/i386/' -e 's/ppc/powerpc/' -e 's/ppc64//' -e 's/powerpc64//' -e 's/ia64//' -e 's/s390.*//'`
+echo "TARGET_$arch=y" >.config
+echo "TARGET_ARCH=\"$arch\"" >>.config
+cat .config1 >>.config
+if test "$arch"; then yes "" | make oldconfig; fi
+if test "$arch"; then cat .config; fi
+if test "$arch"; then make V=1; fi
+if test "$arch"; then make install; fi
+if test "$arch"; then make install_kernel_headers; fi
+cd ..
+# we are back in busybox-NN.MM dir now
+cp %{SOURCE1} .config
+# set all new options to defaults
+yes "" | make oldconfig
+# gcc needs to be convinced to use neither system headers, nor libs,
+# nor startfiles (i.e. crtXXX.o files)
+if test "$arch"; then \
+    mv .config .config1 && \
+    grep -v ^CONFIG_SELINUX .config1 >.config && \
+    yes "" | make oldconfig && \
+    cat .config && \
+    make V=1 \
+        EXTRA_CFLAGS="-isystem uClibc-0.9.30.1/installed/include" \
+        CFLAGS_busybox="-static -nostartfiles -LuClibc-0.9.30.1/installed/lib uClibc-0.9.30.1/installed/lib/crt1.o uClibc-0.9.30.1/installed/lib/crti.o uClibc-0.9.30.1/installed/lib/crtn.o"; \
+else \
+    cat .config && \
+    make V=1 CC="gcc $RPM_OPT_FLAGS"; \
+fi
+cp busybox busybox.static
+
+# create busybox optimized for petitboot
+make clean
+# copy new configuration file
+cp %{SOURCE2} .config
+# set all new options to defaults
+yes "" | make oldconfig
+make V=1 CC="%__cc $RPM_OPT_FLAGS"
+cp busybox busybox.petitboot
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/sbin
+install -m 755 busybox.static $RPM_BUILD_ROOT/sbin/busybox
+install -m 755 busybox.petitboot $RPM_BUILD_ROOT/sbin/busybox.petitboot
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root,-)
+%doc LICENSE docs/busybox.net/*.html
+/sbin/busybox
+
+%files petitboot
+%defattr(-,root,root,-)
+%doc LICENSE
+/sbin/busybox.petitboot
+
+%changelog
diff --git a/busybox-1.19.3/examples/depmod b/busybox-1.19.3/examples/depmod
new file mode 100755
index 0000000..d769590
--- /dev/null
+++ b/busybox-1.19.3/examples/depmod
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Simple depmod, use to generate modprobe.conf
+#
+# Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+#
+
+local BASE="${1:-/usr/lib/modules}"
+
+find "$BASE" -name '*.ko.gz' | while read I ; do
+	N=`basename "$I" '.ko.gz'`
+	echo -n "@$N"
+	zcat "$I" | strings | grep '^depends=' | sed -e 's/^depends=$//' -e 's/^depends=/,/' -e 's/,/ @/g'
+done | awk '
+{
+	# modules which has no dependencies are resolved
+	if ( NF == 1 ) { res[$1] = ""; next }
+	# others have to be resolved based on those which already resolved
+	i = $1; $1 = ""; deps[i] = $0; ++ndeps
+}
+END {
+	# resolve implicit dependencies
+	while ( ndeps ) for (mod in deps) {
+		if ( index(deps[mod], "@") > 0 ) {
+			$0 = deps[mod]
+			for ( i=1; i<=NF; ++i ) {
+				if ( substr($i,1,1) == "@" ) {
+					if ( $i in res ) {
+						$i = res[$i] " " substr($i,2)
+					}
+				}
+			}
+			deps[mod] = $0
+		} else {
+			res[mod] = deps[mod]
+			delete deps[mod]
+			--ndeps
+		}
+	}
+
+	# output dependencies in modules.dep format
+	for ( mod in res ) {
+		$0 = res[mod]
+		s = ""
+		delete a
+		for ( i=1; i<=NF; ++i ) {
+			if ( ! ($i in a) ) {
+				a[$i] = $i
+				s = " ," $i s
+			}
+		}
+		print "," substr(mod,2) ":" s
+	}
+}
+' | sort | sed -r -e "s!,([^,: ]*)!/usr/lib/modules/\\1.ko.gz!g"
diff --git a/busybox-1.19.3/examples/depmod.pl b/busybox-1.19.3/examples/depmod.pl
new file mode 100755
index 0000000..f324b12
--- /dev/null
+++ b/busybox-1.19.3/examples/depmod.pl
@@ -0,0 +1,353 @@
+#!/usr/bin/perl -w
+# vi: set sw=4 ts=4:
+# Copyright (c) 2001 David Schleef <ds@schleef.org>
+# Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
+# Copyright (c) 2001 Stuart Hughes <seh@zee2.com>
+# Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
+# Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com>
+#
+# History:
+# March 2006: Stuart Hughes <stuarth@freescale.com>.
+#             Significant updates, including implementing the '-F' option
+#             and adding support for 2.6 kernels.
+
+# This program is free software; you can redistribute it and/or modify it
+# under the same terms as Perl itself.
+use Getopt::Long qw(:config no_auto_abbrev no_ignore_case);
+use File::Find;
+use strict;
+
+# Set up some default values
+my $kdir="";
+my $basedir="";
+my $kernel="";
+my $kernelsyms="";
+my $symprefix="";
+my $all=0;
+my $quick=0;
+my $errsyms=0;
+my $stdout=0;
+my $verbose=0;
+my $help=0;
+my $nm = $ENV{'NM'} || "nm";
+
+# more globals
+my (@liblist) = ();
+my $exp = {};
+my $dep = {};
+my $mod = {};
+
+my $usage = <<TXT;
+$0 -b basedir { -k <vmlinux> | -F <System.map> } [options]...
+  Where:
+   -h --help          : Show this help screen
+   -b --basedir       : Modules base directory (e.g /lib/modules/<2.x.y>)
+   -k --kernel        : Kernel binary for the target (e.g. vmlinux)
+   -F --kernelsyms    : Kernel symbol file (e.g. System.map)
+   -n --stdout        : Write to stdout instead of <basedir>/modules.dep
+   -v --verbose       : Print out lots of debugging stuff
+   -P --symbol-prefix : Symbol prefix
+   -a --all           : Probe all modules (default/only thing supported)
+   -e --errsyms       : Report any symbols not supplied by modules/kernel
+TXT
+
+# get command-line options
+GetOptions(
+	"help|h"            => \$help,
+	"basedir|b=s"       => \$basedir,
+	"kernel|k=s"        => \$kernel,
+	"kernelsyms|F=s"    => \$kernelsyms,
+	"stdout|n"          => \$stdout,
+	"verbose|v"         => \$verbose,
+	"symbol-prefix|P=s" => \$symprefix,
+	"all|a"             => \$all,
+	# unsupported options
+	"quick|A"           => \$quick,
+	# ignored options (for historical usage)
+	"quiet|q",
+	"root|r",
+	"unresolved-error|u"
+);
+
+die $usage if $help;
+die $usage unless $basedir && ( $kernel || $kernelsyms );
+die "can't use both -k and -F\n\n$usage" if $kernel && $kernelsyms;
+die "sorry, -A/--quick is not supported" if $quick;
+die "--errsyms requires --kernelsyms" if $errsyms && !$kernelsyms;
+
+# Strip any trailing or multiple slashes from basedir
+$basedir =~ s-/+$--g;
+
+# The base directory should contain /lib/modules somewhere
+if($basedir !~ m-/lib/modules-) {
+    warn "WARNING: base directory does not match ..../lib/modules\n";
+}
+
+# if no kernel version is contained in the basedir, try to find one
+if($basedir !~ m-/lib/modules/\d\.\d-) {
+    opendir(BD, $basedir) or die "can't open basedir $basedir : $!\n";
+    foreach ( readdir(BD) ) {
+        next if /^\.\.?$/;
+        next unless -d "$basedir/$_";
+        warn "dir = $_\n" if $verbose;
+        if( /^\d\.\d/ ) {
+            $kdir = $_;
+            warn("Guessed module directory as $basedir/$kdir\n");
+            last;
+        }
+    }
+    closedir(BD);
+    die "Cannot find a kernel version under $basedir\n" unless $kdir;
+    $basedir = "$basedir/$kdir";
+}
+
+# Find the list of .o or .ko files living under $basedir
+warn "**** Locating all modules\n" if $verbose;
+find sub {
+    my $file;
+	if ( -f $_  && ! -d $_ ) {
+		$file = $File::Find::name;
+		if ( $file =~ /\.k?o$/ ) {
+			push(@liblist, $file);
+			warn "$file\n" if $verbose;
+		}
+	}
+}, $basedir;
+warn "**** Finished locating modules\n" if $verbose;
+
+foreach my $obj ( @liblist ){
+    # turn the input file name into a target tag name
+    my ($tgtname) = $obj =~ m-(/lib/modules/.*)$-;
+
+    warn "\nMODULE = $tgtname\n" if $verbose;
+
+    # get a list of symbols
+	my @output=`$nm $obj`;
+
+    build_ref_tables($tgtname, \@output, $exp, $dep);
+}
+
+
+# vmlinux is a special name that is only used to resolve symbols
+my $tgtname = 'vmlinux';
+my @output = $kernelsyms ? `cat $kernelsyms` : `$nm $kernel`;
+warn "\nMODULE = $tgtname\n" if $verbose;
+build_ref_tables($tgtname, \@output, $exp, $dep);
+
+# resolve the dependencies for each module
+# reduce dependencies: remove unresolvable and resolved from vmlinux/System.map
+# remove duplicates
+foreach my $module (keys %$dep) {
+    warn "reducing module: $module\n" if $verbose;
+    $mod->{$module} = {};
+    foreach (@{$dep->{$module}}) {
+        if( $exp->{$_} ) {
+            warn "resolved symbol $_ in file $exp->{$_}\n" if $verbose;
+            next if $exp->{$_} =~ /vmlinux/;
+            $mod->{$module}{$exp->{$_}} = 1;
+        } else {
+            warn "unresolved symbol $_ in file $module\n";
+        }
+    }
+}
+
+# build a complete dependency list for each module and make sure it
+# is kept in order proper order
+my $mod2 = {};
+sub maybe_unshift
+{
+	my ($array, $ele) = @_;
+	# chop off the leading path /lib/modules/<kver>/ as modprobe
+	# will handle relative paths just fine
+	$ele =~ s:^/lib/modules/[^/]*/::;
+	foreach (@{$array}) {
+		if ($_ eq $ele) {
+			return;
+		}
+	}
+	unshift (@{$array}, $ele);
+}
+sub add_mod_deps
+{
+	my ($depth, $mod, $mod2, $module, $this_module) = @_;
+
+	$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);
+		warn "${depth} outputting $md\n" if $verbose;
+		maybe_unshift (\@{$$mod2->{$module}}, $md);
+	}
+
+	if (!%{$mod->{$this_module}}) {
+		warn "${depth} no deps\n" if $verbose;
+	}
+}
+foreach my $module (keys %$mod) {
+	warn "filling out module: $module\n" if $verbose;
+	@{$mod2->{$module}} = ();
+	add_mod_deps ("", $mod, \$mod2, $module, $module);
+}
+
+# figure out where the output should go
+if ($stdout == 0) {
+	warn "writing $basedir/modules.dep\n" if $verbose;
+    open(STDOUT, ">$basedir/modules.dep")
+                             or die "cannot open $basedir/modules.dep: $!";
+}
+my $kseries = $basedir =~ m,/2\.6\.[^/]*, ? '2.6' : '2.4';
+
+foreach my $module ( keys %$mod ) {
+    if($kseries eq '2.4') {
+	    print "$module:\t";
+	    my @sorted = sort bydep keys %{$mod->{$module}};
+	    print join(" \\\n\t",@sorted);
+	    print "\n\n";
+    } else {
+	    my $shortmod = $module;
+	    $shortmod =~ s:^/lib/modules/[^/]*/::;
+	    print "$shortmod:";
+	    my @sorted = @{$mod2->{$module}};
+	    printf " " if @sorted;
+	    print join(" ",@sorted);
+	    print "\n";
+    }
+}
+
+
+sub build_ref_tables
+{
+    my ($name, $sym_ar, $exp, $dep) = @_;
+
+	my $ksymtab = grep m/ ${symprefix}__ksymtab/, @$sym_ar;
+
+    # gather the exported symbols
+	if($ksymtab){
+        # explicitly exported
+        foreach ( @$sym_ar ) {
+            / ${symprefix}__ksymtab_(.*)$/ and do {
+                my $sym = ${symprefix} . $1;
+                warn "sym = $sym\n" if $verbose;
+                $exp->{$sym} = $name;
+            };
+        }
+	} else {
+        # exporting all symbols
+        foreach ( @$sym_ar ) {
+            / [ABCDGRSTW] (.*)$/ and do {
+                warn "syma = $1\n" if $verbose;
+                $exp->{$1} = $name;
+            };
+        }
+	}
+
+    # this takes makes sure modules with no dependencies get listed
+    push @{$dep->{$name}}, $symprefix . 'printk' unless $name eq 'vmlinux';
+
+    # gather the unresolved symbols
+    foreach ( @$sym_ar ) {
+        !/ ${symprefix}__this_module/ && / U (.*)$/ and do {
+            warn "und = $1\n" if $verbose;
+            push @{$dep->{$name}}, $1;
+        };
+    }
+}
+
+sub bydep
+{
+    foreach my $f ( keys %{$mod->{$b}} ) {
+        if($f eq $a) {
+            return 1;
+        }
+    }
+    return -1;
+}
+
+
+
+__END__
+
+=head1 NAME
+
+depmod.pl - a cross platform script to generate kernel module
+dependency lists (modules.conf) which can then be used by modprobe
+on the target platform.
+
+It supports Linux 2.4 and 2.6 styles of modules.conf (auto-detected)
+
+=head1 SYNOPSIS
+
+depmod.pl [OPTION]... [basedir]...
+
+Example:
+
+	depmod.pl -F linux/System.map -b target/lib/modules/2.6.11
+
+=head1 DESCRIPTION
+
+The purpose of this script is to automagically generate a list of of kernel
+module dependencies.  This script produces dependency lists that should be
+identical to the depmod program from the modutils package.  Unlike the depmod
+binary, however, depmod.pl is designed to be run on your host system, not
+on your target system.
+
+This script was written by David Schleef <ds@schleef.org> to be used in
+conjunction with the BusyBox modprobe applet.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-h --help>
+
+This displays the help message.
+
+=item B<-b --basedir>
+
+The base directory uner which the target's modules will be found.  This
+defaults to the /lib/modules directory.
+
+If you don't specify the kernel version, this script will search for
+one under the specified based directory and use the first thing that
+looks like a kernel version.
+
+=item B<-k --kernel>
+
+Kernel binary for the target (vmlinux).  You must either supply a kernel binary
+or a kernel symbol file (using the -F option).
+
+=item B<-F --kernelsyms>
+
+Kernel symbol file for the target (System.map).
+
+=item B<-n --stdout>
+
+Write to stdout instead of modules.dep
+kernel binary for the target (using the -k option).
+
+=item B<--verbose>
+
+Verbose (debug) output
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+ Copyright (c) 2001 David Schleef <ds@schleef.org>
+ Copyright (c) 2001 Erik Andersen <andersen@codepoet.org>
+ Copyright (c) 2001 Stuart Hughes <seh@zee2.com>
+ Copyright (c) 2002 Steven J. Hill <shill@broadcom.com>
+ Copyright (c) 2006 Freescale Semiconductor, Inc <stuarth@freescale.com>
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=head1 AUTHOR
+
+David Schleef <ds@schleef.org>
+
+=cut
diff --git a/busybox-1.19.3/examples/devfsd.conf b/busybox-1.19.3/examples/devfsd.conf
new file mode 100644
index 0000000..10f1c87
--- /dev/null
+++ b/busybox-1.19.3/examples/devfsd.conf
@@ -0,0 +1,133 @@
+# Sample /etc/devfsd.conf configuration file.
+# Richard Gooch  <rgooch@atnf.csiro.au>		17-FEB-2002
+#
+# adapted for busybox devfsd implementation by Tito <farmatito@tiscali.it>
+#
+# Enable full compatibility mode for old device names. You may comment these
+# out if you don't use the old device names. Make sure you know what you're
+# doing!
+REGISTER	.*		MKOLDCOMPAT
+UNREGISTER	.*		RMOLDCOMPAT
+
+# You may comment out the above and uncomment the following if you've
+# configured your system to use the original "new" devfs names or the really
+# new names
+#REGISTER	^vc/		MKOLDCOMPAT
+#UNREGISTER	^vc/		RMOLDCOMPAT
+#REGISTER	^pty/		MKOLDCOMPAT
+#UNREGISTER	^pty/		RMOLDCOMPAT
+#REGISTER	^misc/		MKOLDCOMPAT
+#UNREGISTER	^misc/		RMOLDCOMPAT
+
+# You may comment these out if you don't use the original "new" names
+REGISTER	.*		MKNEWCOMPAT
+UNREGISTER	.*		RMNEWCOMPAT
+
+# Enable module autoloading. You may comment this out if you don't use
+# autoloading
+# Supported by busybox when CONFIG_DEVFSD_MODLOAD is set.
+# This actually doesn't work with busybox  modutils but needs
+# the real modutils' modprobe
+LOOKUP		.*		MODLOAD
+
+# Uncomment the following if you want to set the group to "tty" for the
+# pseudo-tty devices. This is necessary so that mesg(1) can later be used to
+# enable/disable talk requests and wall(1) messages.
+REGISTER	^pty/s.*	PERMISSIONS	-1.tty	0600
+#REGISTER	^pts/.*		PERMISSIONS	-1.tty	0600
+
+# Restoring /dev/log on startup would trigger the minilogd/initlog deadlock
+# (minilogd falsely assuming syslogd has been started).
+REGISTER	^log$		IGNORE
+CREATE		^log$		IGNORE
+CHANGE		^log$		IGNORE
+DELETE		^log$		IGNORE
+
+#
+# Uncomment this if you want permissions to be saved and restored
+# Do not do this for pseudo-terminal devices
+REGISTER	^pt[sy]		IGNORE
+CREATE		^pt[sy]		IGNORE
+CHANGE		^pt[sy]		IGNORE
+DELETE		^pt[sy]		IGNORE
+REGISTER	.*		COPY	/lib/dev-state/$devname $devpath
+CREATE		.*		COPY	$devpath /lib/dev-state/$devname
+CHANGE		.*		COPY	$devpath /lib/dev-state/$devname
+#DELETE		.*		CFUNCTION GLOBAL unlink /lib/dev-state/$devname
+# Busybox
+DELETE		.*		EXECUTE /bin/rm -f		/lib/dev-state/$devname
+
+RESTORE		/lib/dev-state
+
+#
+# Uncomment this if you want the old /dev/cdrom symlink
+#REGISTER	^cdroms/cdrom0$	CFUNCTION GLOBAL mksymlink $devname cdrom
+#UNREGISTER	^cdroms/cdrom0$	CFUNCTION GLOBAL unlink cdrom
+# busybox
+REGISTER	^cdroms/cdrom0$	EXECUTE /bin/ln -sf $devname cdrom
+UNREGISTER	^cdroms/cdrom0$	EXECUTE /bin/rm -f cdrom
+
+#REGISTER	^v4l/video0$	CFUNCTION GLOBAL mksymlink v4l/video0 video
+#UNREGISTER	^v4l/video0$	CFUNCTION GLOBAL unlink video
+#REGISTER	^radio0$	CFUNCTION GLOBAL mksymlink radio0 radio
+#UNREGISTER	^radio0$	CFUNCTION GLOBAL unlink radio
+# Busybox
+REGISTER	^v4l/video0$	EXECUTE /bin/ln -sf v4l/video0 video
+UNREGISTER	^v4l/video0$	EXECUTE /bin/rm -f video
+REGISTER	^radio0$		EXECUTE /bin/ln -sf  radio0 radio
+UNREGISTER	^radio0$		EXECUTE /bin/rm -f radio
+
+# ALSA stuff
+#LOOKUP		snd		MODLOAD ACTION snd
+
+# Uncomment this to let PAM manage devfs
+# Not supported by busybox
+#REGISTER	.*		CFUNCTION /lib/security/pam_console_apply_devfsd.so pam_console_apply_single $devpath
+
+# Uncomment this to manage USB mouse
+# Not supported by busybox
+#REGISTER	^input/mouse0$	CFUNCTION GLOBAL mksymlink $devname usbmouse
+#UNREGISTER	^input/mouse0$	CFUNCTION GLOBAL unlink usbmouse
+# Busybox
+#REGISTER	^input/mouse0$	EXECUTE /bin/ln -sf $devname usbmouse
+#UNREGISTER	^input/mouse0$	EXECUTE /bin/rm -f usbmouse
+# Not supported by busybox
+#REGISTER	^input/mice$	CFUNCTION GLOBAL mksymlink $devname usbmouse
+#UNREGISTER	^input/mice$	CFUNCTION GLOBAL unlink usbmouse
+# Busybox
+REGISTER	^input/mice$	EXECUTE /bin/ln -sf $devname usbmouse
+UNREGISTER	^input/mice$	EXECUTE /bin/rm -f usbmouse
+
+# If you have removable media and want to force media revalidation when looking
+# up new or old compatibility names, uncomment the following lines
+# SCSI NEWCOMPAT  /dev/sd/* names
+LOOKUP		^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$	EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# SCSI OLDCOMPAT  /dev/sd?? names
+LOOKUP		^(sd[a-z]+)[0-9]+$	EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE NEWCOMPAT   /dev/ide/hd/* names
+LOOKUP		^(ide/hd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$	EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE OLDCOMPAT   /dev/hd?? names
+LOOKUP		^(hd[a-z])[0-9]+$	EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+# IDE-SCSI NEWCOMPAT  /dev/sd/* names
+#LOOKUP		^(sd/c[0-9]+b[0-9]+t[0-9]+u[0-9]+)p[0-9]+$	EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+#SCSI OLDCOMPAT  /dev/scd? names
+LOOKUP		^(scd+)[0-9]+$	EXECUTE /bin/dd if=$mntpnt/\1 of=/dev/null count=1
+
+
+REGISTER ^dvb/card[0-9]+/[^/]+$ PERMISSIONS root.video 0660
+# Not supported by busybox
+#REGISTER	^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$	CFUNCTION GLOBAL mksymlink /dev/$devname ost/\2\1
+#UNREGISTER	^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$	CFUNCTION GLOBAL unlink ost/\2\1
+# Busybox
+REGISTER	^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$	EXECUTE /bin/ln -sf /dev/$devname ost/\2\1
+UNREGISTER	^dvb/card([0-9]+)/([^/0-9]*)[0-9]+$	EXECUTE /bin/rm -f ost/\2\1
+
+# Include package-generated files from /etc/devfs/conf.d
+# Supported by busybox
+# INCLUDE   /etc/devfs/conf.d/
+INCLUDE   /etc/devfs/busybox/
+# Busybox: just for testing
+#INCLUDE			/etc/devfs/nothing/
+#INCLUDE			/etc/devfs/nothing/nothing
+#OPTIONAL_INCLUDE	/etc/devfs/nothing/
+#OPTIONAL_INCLUDE	/etc/devfs/nothing/nothing
diff --git a/busybox-1.19.3/examples/dnsd.conf b/busybox-1.19.3/examples/dnsd.conf
new file mode 100644
index 0000000..8af622b
--- /dev/null
+++ b/busybox-1.19.3/examples/dnsd.conf
@@ -0,0 +1 @@
+thebox 192.168.1.5
diff --git a/busybox-1.19.3/examples/inetd.conf b/busybox-1.19.3/examples/inetd.conf
new file mode 100644
index 0000000..ca7e3d8
--- /dev/null
+++ b/busybox-1.19.3/examples/inetd.conf
@@ -0,0 +1,73 @@
+# /etc/inetd.conf:  see inetd(8) for further informations.
+#
+# Internet server configuration database
+#
+#
+# If you want to disable an entry so it isn't touched during
+# package updates just comment it out with a single '#' character.
+#
+# If you make changes to this file, either reboot your machine or
+# send the inetd process a HUP signal:
+# Do a "ps x" as root and look up the pid of inetd. Then do a
+#     kill -HUP <pid of inetd>
+# inetd will re-read this file whenever it gets that signal.
+# <service_name> <sock_type> <proto> <flags> <user> <server_path> <args>
+#
+#:INTERNAL: Internal services
+# It is generally considered safer to keep these off.
+echo     stream  tcp	nowait	root	internal
+echo     dgram   udp	wait	root	internal
+#discard  stream  tcp	nowait	root	internal
+#discard  dgram   udp	wait	root	internal
+daytime  stream  tcp	nowait	root	internal
+daytime  dgram   udp	wait	root	internal
+#chargen  stream  tcp	nowait	root	internal
+#chargen  dgram   udp	wait	root	internal
+time     stream  tcp	nowait	root	internal
+time     dgram   udp	wait	root	internal
+
+# These are standard services.
+#
+#ftp	stream	tcp	nowait	root	/usr/sbin/tcpd	in.ftpd
+#telnet	stream	tcp	nowait	root	/sbin/telnetd	/sbin/telnetd
+#nntp	stream	tcp	nowait	root	tcpd	in.nntpd
+#smtp  stream  tcp     nowait  root    tcpd    sendmail -v
+#
+# Shell, login, exec and talk are BSD protocols.
+#
+# If you run an ntalk daemon (such as netkit-ntalk) on the old talk
+# port, that is, "talk" as opposed to "ntalk", it won't work and may
+# cause certain broken talk clients to malfunction.
+#
+# The talkd from netkit-ntalk 0.12 and higher, however, can speak the
+# old talk protocol and can be used safely.
+#
+#shell	stream	tcp	nowait	root	/usr/sbin/tcpd	in.rshd -L
+#login	stream	tcp	nowait	root	/usr/sbin/tcpd	in.rlogind -L
+#exec	stream	tcp	nowait	root	/usr/sbin/tcpd	in.rexecd
+#talk	dgram	udp	wait	root	/usr/sbin/tcpd	in.talkd
+#ntalk	dgram	udp	wait	root	/usr/sbin/tcpd	in.talkd
+#
+# Pop et al
+# Leave these off unless you're using them.
+#pop2	stream	tcp	nowait	root	/usr/sbin/tcpd	in.pop2d
+#pop3	stream	tcp	nowait	root	/usr/sbin/tcpd	in.pop3d
+#
+# The Internet UUCP service.
+# uucp	stream	tcp	nowait	uucp	/usr/sbin/tcpd	/usr/lib/uucp/uucico	-l
+#
+# Tftp service is provided primarily for booting.  Most sites
+# run this only on machines acting as "boot servers." If you don't
+# need it, don't use it.
+#
+#tftp	dgram	udp	wait	nobody	/usr/sbin/tcpd	in.tftpd
+#bootps	dgram	udp	wait	root	/usr/sbin/in.bootpd	in.bootpd
+#
+# Finger, systat and netstat give out user information which may be
+# valuable to potential "system crackers."  Many sites choose to disable
+# some or all of these services to improve security.
+#
+#finger	stream	tcp	nowait	nobody	/usr/sbin/tcpd	in.fingerd -w
+#systat	stream	tcp	nowait	nobody	/usr/sbin/tcpd	/bin/ps	-auwwx
+#netstat	stream	tcp	nowait	root	/bin/netstat	/bin/netstat	-a
+#ident	stream	tcp	nowait	root	/usr/sbin/in.identd	in.identd
diff --git a/busybox-1.19.3/examples/inittab b/busybox-1.19.3/examples/inittab
new file mode 100644
index 0000000..c4e0af5
--- /dev/null
+++ b/busybox-1.19.3/examples/inittab
@@ -0,0 +1,89 @@
+# /etc/inittab init(8) configuration for BusyBox
+#
+# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+#
+#
+# Note, BusyBox init doesn't support runlevels.  The runlevels field is
+# completely ignored by BusyBox init. If you want runlevels, use sysvinit.
+#
+#
+# Format for each entry: <id>:<runlevels>:<action>:<process>
+#
+# <id>: WARNING: This field has a non-traditional meaning for BusyBox init!
+#
+#	The id field is used by BusyBox init to specify the controlling tty for
+#	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.
+#
+# <runlevels>: The runlevels field is completely ignored.
+#
+# <action>: Valid actions include: sysinit, respawn, askfirst, wait, once,
+#                                  restart, ctrlaltdel, and shutdown.
+#
+#       Note: askfirst acts just like respawn, but before running the specified
+#       process it displays the line "Please press Enter to activate this
+#       console." and then waits for the user to press enter before starting
+#       the specified process.
+#
+#       Note: unrecognized actions (like initdefault) will cause init to emit
+#       an error message, and then go along with its business.
+#
+# <process>: Specifies the process to be executed and it's command line.
+#
+# Note: BusyBox init works just fine without an inittab. If no inittab is
+# found, it has the following default behavior:
+#         ::sysinit:/etc/init.d/rcS
+#         ::askfirst:/bin/sh
+#         ::ctrlaltdel:/sbin/reboot
+#         ::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
+#
+# Boot-time system configuration/initialization script.
+# This is run first except when booting in single-user mode.
+#
+::sysinit:/etc/init.d/rcS
+
+# /bin/sh invocations on selected ttys
+#
+# Note below that we prefix the shell commands with a "-" to indicate to the
+# shell that it is supposed to be a login shell.  Normally this is handled by
+# login, but since we are bypassing login in this case, BusyBox lets you do
+# this yourself...
+#
+# Start an "askfirst" shell on the console (whatever that may be)
+::askfirst:-/bin/sh
+# Start an "askfirst" shell on /dev/tty2-4
+tty2::askfirst:-/bin/sh
+tty3::askfirst:-/bin/sh
+tty4::askfirst:-/bin/sh
+
+# /sbin/getty invocations for selected ttys
+tty4::respawn:/sbin/getty 38400 tty5
+tty5::respawn:/sbin/getty 38400 tty6
+
+# Example of how to put a getty on a serial line (for a terminal)
+#::respawn:/sbin/getty -L ttyS0 9600 vt100
+#::respawn:/sbin/getty -L ttyS1 9600 vt100
+#
+# Example how to put a getty on a modem line.
+#::respawn:/sbin/getty 57600 ttyS2
+
+# Stuff to do when restarting the init process
+::restart:/sbin/init
+
+# Stuff to do before rebooting
+::ctrlaltdel:/sbin/reboot
+::shutdown:/bin/umount -a -r
+::shutdown:/sbin/swapoff -a
diff --git a/busybox-1.19.3/examples/linux-2.6.30_proc_self_exe.patch b/busybox-1.19.3/examples/linux-2.6.30_proc_self_exe.patch
new file mode 100644
index 0000000..a36581b
--- /dev/null
+++ b/busybox-1.19.3/examples/linux-2.6.30_proc_self_exe.patch
@@ -0,0 +1,34 @@
+This patch makes /proc/self/exe executable even if proc
+is not mounted. It is especially useful on NOMMU arches.
+
+diff -urp ../linux-2.6.30.org/fs/exec.c linux-2.6.30/fs/exec.c
+--- ../linux-2.6.30.org/fs/exec.c	2009-06-10 05:05:27.000000000 +0200
++++ linux-2.6.30/fs/exec.c	2009-06-25 00:20:13.000000000 +0200
+@@ -652,9 +652,25 @@ struct file *open_exec(const char *name)
+ 	file = do_filp_open(AT_FDCWD, name,
+ 				O_LARGEFILE | O_RDONLY | FMODE_EXEC, 0,
+ 				MAY_EXEC | MAY_OPEN);
+-	if (IS_ERR(file))
+-		goto out;
++	if (IS_ERR(file)) {
++		if ((PTR_ERR(file) == -ENOENT || PTR_ERR(file) == -EACCES)
++		 && strcmp(name, "/proc/self/exe") == 0
++		) {
++			struct file *sv = file;
++			struct mm_struct *mm;
+ 
++			mm = get_task_mm(current);
++			if (!mm)
++				goto out;
++			file = get_mm_exe_file(mm);
++			mmput(mm);
++			if (file)
++				goto ok;
++			file = sv;
++		}
++		goto out;
++	}
++ok:
+ 	err = -EACCES;
+ 	if (!S_ISREG(file->f_path.dentry->d_inode->i_mode))
+ 		goto exit;
diff --git a/busybox-1.19.3/examples/mdev.conf b/busybox-1.19.3/examples/mdev.conf
new file mode 100644
index 0000000..cdbb4fc
--- /dev/null
+++ b/busybox-1.19.3/examples/mdev.conf
@@ -0,0 +1,30 @@
+#
+# This is a sample mdev.conf
+#
+
+# Provide user, group, and mode information for devices.  If a regex matches
+# the device name provided by sysfs, use the appropriate user:group and mode
+# instead of the default 0:0 660.
+#
+# Syntax:
+# %s %d:%d %s
+# devicename_regex user:group mode
+
+null		0:0 666
+zero		0:0 666
+urandom		0:0 444
+
+kmem		0:9 000
+mem		0:9 640
+port		0:9 640
+
+console		0:5 600
+ptmx		0:5 660
+tty[0-9]*	0:5 660
+
+ttyS[0-9]*	0:20 640
+
+fd[0-9]*	0:11 660
+
+sd[a-z]*	0:6 660
+hd[a-z]*	0:6 660
diff --git a/busybox-1.19.3/examples/mdev_fat.conf b/busybox-1.19.3/examples/mdev_fat.conf
new file mode 100644
index 0000000..df329b4
--- /dev/null
+++ b/busybox-1.19.3/examples/mdev_fat.conf
@@ -0,0 +1,110 @@
+#
+# This is a sample mdev.conf
+#
+
+# Provide user, group, and mode information for devices.  If a regex matches
+# the device name provided by sysfs, use the appropriate user:group and mode
+# instead of the default 0:0 660.
+#
+# Syntax:
+# [-]devicename_regex user:group mode [>|=path] [@|$|*cmd args...]
+#
+# =: move, >: move and create a symlink
+# @|$|*: run $cmd on delete, @cmd on create, *cmd on both
+
+# support module loading on hotplug
+$MODALIAS=.*	root:root 660 @modprobe "$MODALIAS"
+
+# null may already exist; therefore ownership has to be changed with command
+null		root:root 666 @chmod 666 $MDEV
+zero		root:root 666
+full		root:root 666
+random		root:root 444
+urandom		root:root 444
+hwrandom	root:root 444
+grsec		root:root 660
+
+kmem		root:root 640
+mem		root:root 640
+port		root:root 640
+# console may already exist; therefore ownership has to be changed with command
+console		root:tty 600 @chmod 600 $MDEV
+ptmx		root:tty 666
+pty.*		root:tty 660
+
+# Typical devices
+
+tty		root:tty 666
+tty[0-9]*	root:tty 660
+vcsa*[0-9]*	root:tty 660
+ttyS[0-9]*	root:uucp 660
+
+# block devices
+ram([0-9]*)	root:disk 660 >rd/%1
+loop([0-9]+)	root:disk 660 >loop/%1
+sd[a-z].*	root:disk 660 */lib/mdev/usbdisk_link
+hd[a-z][0-9]*	root:disk 660 */lib/mdev/ide_links
+md[0-9]*	root:disk 660
+sr[0-9]*	root:cdrom 660 @ln -sf $MDEV cdrom
+fd[0-9]*	root:floppy 660
+
+# net devices
+-net/.*		root:root 600 @nameif
+tun[0-9]*	root:root 600 =net/
+tap[0-9]*	root:root 600 =net/
+
+# alsa sound devices and audio stuff
+pcm.*		root:audio 660 =snd/
+control.*	root:audio 660 =snd/
+midi.*		root:audio 660 =snd/
+seq		root:audio 660 =snd/
+timer		root:audio 660 =snd/
+
+adsp		root:audio 660 >sound/
+audio		root:audio 660 >sound/
+dsp		root:audio 660 >sound/
+mixer		root:audio 660 >sound/
+sequencer.*	root:audio 660 >sound/
+
+# Less typical devices
+
+# raid controllers
+cciss!(.*)	root:disk 660 =cciss/%1
+ida!(.*)	root:disk 660 =ida/%1
+rd!(.*)		root:disk 660 =rd/%1
+
+ttyLTM[0-9]	root:dialout 660 @ln -sf $MDEV modem
+ttySHSF[0-9]	root:dialout 660 @ln -sf $MDEV modem
+slamr		root:dialout 660 @ln -sf $MDEV slamr0
+slusb		root:dialout 660 @ln -sf $MDEV slusb0
+
+fuse		root:root 666
+
+# dri device
+card[0-9]	root:video 660 =dri/
+
+# misc stuff
+agpgart		root:root 660 >misc/
+psaux		root:root 660 >misc/
+rtc		root:root 664 >misc/
+
+# input stuff
+event[0-9]+	root:root 640 =input/
+mice		root:root 640 =input/
+mouse[0-9]	root:root 640 =input/
+ts[0-9]		root:root 600 =input/
+
+# v4l stuff
+vbi[0-9]	root:video 660 >v4l/
+video[0-9]	root:video 660 >v4l/
+
+# dvb stuff
+dvb.*		root:video 660 */lib/mdev/dvbdev
+
+# load drivers for usb devices
+usbdev[0-9].[0-9]	root:root 660 */lib/mdev/usbdev
+usbdev[0-9].[0-9]_.*	root:root 660
+
+# zaptel devices
+zap(.*)		root:dialout 660 =zap/%1
+dahdi!(.*)	root:dialout 660 =dahdi/%1
diff --git a/busybox-1.19.3/examples/mk2knr.pl b/busybox-1.19.3/examples/mk2knr.pl
new file mode 100755
index 0000000..1259b84
--- /dev/null
+++ b/busybox-1.19.3/examples/mk2knr.pl
@@ -0,0 +1,84 @@
+#!/usr/bin/perl -w
+#
+# @(#) mk2knr.pl - generates a perl script that converts lexemes to K&R-style
+#
+# How to use this script:
+#  - In the busybox directory type 'examples/mk2knr.pl files-to-convert'
+#  - Review the 'convertme.pl' script generated and remove / edit any of the
+#    substitutions in there (please especially check for false positives)
+#  - Type './convertme.pl same-files-as-before'
+#  - Compile and see if it works
+#
+# BUGS: This script does not ignore strings inside comments or strings inside
+# quotes (it probably should).
+
+# set this to something else if you want
+$convertme = 'convertme.pl';
+
+# internal-use variables (don't touch)
+$convert = 0;
+%converted = ();
+
+# if no files were specified, print usage
+die "usage: $0 file.c | file.h\n" if scalar(@ARGV) == 0;
+
+# prepare the "convert me" file
+open(CM, ">$convertme") or die "convertme.pl $!";
+print CM "#!/usr/bin/perl -p -i\n\n";
+
+# process each file passed on the cmd line
+while (<>) {
+
+	# if the line says "getopt" in it anywhere, we don't want to muck with it
+	# because option lists tend to include strings like "cxtzvOf:" which get
+	# matched by the "check for mixed case" regexps below
+	next if /getopt/;
+
+	# tokenize the string into just the variables
+	while (/([a-zA-Z_][a-zA-Z0-9_]*)/g) {
+		$var = $1;
+
+		# ignore the word "BusyBox"
+		next if ($var =~ /BusyBox/);
+
+		# this checks for javaStyle or szHungarianNotation
+		$convert++ if ($var =~ /^[a-z]+[A-Z][a-z]+/);
+
+		# this checks for PascalStyle
+		$convert++ if ($var =~ /^[A-Z][a-z]+[A-Z][a-z]+/);
+
+		# if we want to add more checks, we can add 'em here, but the above
+		# checks catch "just enough" and not too much, so prolly not.
+
+		if ($convert) {
+			$convert = 0;
+
+			# skip ahead if we've already dealt with this one
+			next if ($converted{$var});
+
+			# record that we've dealt with this var
+			$converted{$var} = 1;
+
+			print CM "s/\\b$var\\b/"; # more to come in just a minute
+
+			# change the first letter to lower-case
+			$var = lcfirst($var);
+
+			# put underscores before all remaining upper-case letters
+			$var =~ s/([A-Z])/_$1/g;
+
+			# now change the remaining characters to lower-case
+			$var = lc($var);
+
+			print CM "$var/g;\n";
+		}
+	}
+}
+
+# tidy up and make the $convertme script executable
+close(CM);
+chmod 0755, $convertme;
+
+# print a helpful help message
+print "Done. Scheduled name changes are in $convertme.\n";
+print "Please review/modify it and then type ./$convertme to do the search & replace.\n";
diff --git a/busybox-1.19.3/examples/udhcp/sample.bound b/busybox-1.19.3/examples/udhcp/sample.bound
new file mode 100755
index 0000000..bd3569c
--- /dev/null
+++ b/busybox-1.19.3/examples/udhcp/sample.bound
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Sample udhcpc renew script
+
+RESOLV_CONF="/etc/udhcpc/resolv.conf"
+
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+if [ -n "$router" ]
+then
+	echo "deleting routers"
+	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++))
+	done
+fi
+
+echo -n > $RESOLV_CONF
+[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+for i in $dns
+do
+	echo adding dns $i
+	echo nameserver $i >> $RESOLV_CONF
+done
diff --git a/busybox-1.19.3/examples/udhcp/sample.deconfig b/busybox-1.19.3/examples/udhcp/sample.deconfig
new file mode 100755
index 0000000..b221bcf
--- /dev/null
+++ b/busybox-1.19.3/examples/udhcp/sample.deconfig
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Sample udhcpc deconfig script
+
+/sbin/ifconfig $interface 0.0.0.0
diff --git a/busybox-1.19.3/examples/udhcp/sample.nak b/busybox-1.19.3/examples/udhcp/sample.nak
new file mode 100755
index 0000000..f4d08e6
--- /dev/null
+++ b/busybox-1.19.3/examples/udhcp/sample.nak
@@ -0,0 +1,4 @@
+#!/bin/sh
+# Sample udhcpc nak script
+
+echo Received a NAK: $message
diff --git a/busybox-1.19.3/examples/udhcp/sample.renew b/busybox-1.19.3/examples/udhcp/sample.renew
new file mode 100755
index 0000000..ea368fc
--- /dev/null
+++ b/busybox-1.19.3/examples/udhcp/sample.renew
@@ -0,0 +1,31 @@
+#!/bin/sh
+# Sample udhcpc bound script
+
+RESOLV_CONF="/etc/udhcpc/resolv.conf"
+
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+
+/sbin/ifconfig $interface $ip $BROADCAST $NETMASK
+
+if [ -n "$router" ]
+then
+	echo "deleting routers"
+	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++))
+	done
+fi
+
+echo -n > $RESOLV_CONF
+[ -n "$domain" ] && echo domain $domain >> $RESOLV_CONF
+for i in $dns
+do
+	echo adding dns $i
+	echo nameserver $i >> $RESOLV_CONF
+done
diff --git a/busybox-1.19.3/examples/udhcp/sample.script b/busybox-1.19.3/examples/udhcp/sample.script
new file mode 100755
index 0000000..9b717ac
--- /dev/null
+++ b/busybox-1.19.3/examples/udhcp/sample.script
@@ -0,0 +1,7 @@
+#!/bin/sh
+# Currently, we only dispatch according to command.  However, a more
+# elaborate system might dispatch by command and interface or do some
+# common initialization first, especially if more dhcp event notifications
+# are added.
+
+exec /usr/share/udhcpc/sample.$1
diff --git a/busybox-1.19.3/examples/udhcp/simple.script b/busybox-1.19.3/examples/udhcp/simple.script
new file mode 100755
index 0000000..40ee738
--- /dev/null
+++ b/busybox-1.19.3/examples/udhcp/simple.script
@@ -0,0 +1,47 @@
+#!/bin/sh
+# udhcpc script edited by Tim Riker <Tim@Rikers.org>
+
+RESOLV_CONF="/etc/resolv.conf"
+
+[ -n "$1" ] || { echo "Error: should be called from udhcpc"; exit 1; }
+
+NETMASK=""
+[ -n "$subnet" ] && NETMASK="netmask $subnet"
+BROADCAST="broadcast +"
+[ -n "$broadcast" ] && BROADCAST="broadcast $broadcast"
+
+case "$1" in
+	deconfig)
+		echo "Setting IP address 0.0.0.0 on $interface"
+		ifconfig $interface 0.0.0.0
+		;;
+
+	renew|bound)
+		echo "Setting IP address $ip on $interface"
+		ifconfig $interface $ip $NETMASK $BROADCAST
+
+		if [ -n "$router" ] ; then
+			echo "Deleting routers"
+			while route del default gw 0.0.0.0 dev $interface ; do
+				:
+			done
+
+			metric=0
+			for i in $router ; do
+				echo "Adding router $i"
+				route add default gw $i dev $interface metric $((metric++))
+			done
+		fi
+
+		echo "Recreating $RESOLV_CONF"
+		echo -n > $RESOLV_CONF-$$
+		[ -n "$domain" ] && echo "search $domain" >> $RESOLV_CONF-$$
+		for i in $dns ; do
+			echo " Adding DNS server $i"
+			echo "nameserver $i" >> $RESOLV_CONF-$$
+		done
+		mv $RESOLV_CONF-$$ $RESOLV_CONF
+		;;
+esac
+
+exit 0
diff --git a/busybox-1.19.3/examples/udhcp/udhcpd.conf b/busybox-1.19.3/examples/udhcp/udhcpd.conf
new file mode 100644
index 0000000..cd2957c
--- /dev/null
+++ b/busybox-1.19.3/examples/udhcp/udhcpd.conf
@@ -0,0 +1,113 @@
+# Sample udhcpd configuration file (/etc/udhcpd.conf)
+# Values shown are defaults
+
+# The start and end of the IP lease block
+start		192.168.0.20
+end		192.168.0.254
+
+# The interface that udhcpd will use
+interface	eth0
+
+# The maximum number of leases (includes addresses reserved
+# by OFFER's, DECLINE's, and ARP conflicts). Will be corrected
+# if it's bigger than IP lease block, but it ok to make it
+# smaller than lease block.
+#max_leases	254
+
+# The amount of time that an IP will be reserved (leased to nobody)
+# if a DHCP decline message is received (seconds)
+#decline_time	3600
+
+# The amount of time that an IP will be reserved
+# if an ARP conflict occurs (seconds)
+#conflict_time	3600
+
+# How long an offered address is reserved (seconds)
+#offer_time	60
+
+# If client asks for lease below this value, it will be rounded up
+# 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 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
+#notify_file	dumpleases	# useful for debugging
+
+# The following are bootp specific options
+# next server to use in bootstrap
+#siaddr		192.168.0.22	# default: 0.0.0.0 (none)
+# tftp server name
+#sname		zorak		# default: none
+# tftp file to download (e.g. kernel image)
+#boot_file	/var/nfs_root	# default: none
+
+# 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
+
+# The remainder of options are DHCP options and can be specified 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.
+# Examples:
+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	# appended to above DNS servers for a total of 3
+option	domain	local
+option	lease	864000		# default: 10 days
+# Arbitrary option in hex form:
+option	0x08	01020304	# option 8: "cookie server IP addr: 1.2.3.4"
+
+# Currently supported options (for more info, see options.c):
+#opt lease      NUM
+#opt subnet     IP
+#opt broadcast  IP
+#opt router     IP_LIST
+#opt ipttl      NUM
+#opt mtu        NUM
+#opt hostname   STRING		# client's hostname
+#opt domain     STRING		# client's domain suffix
+#opt search     STRING_LIST	# search domains
+#opt nisdomain  STRING
+#opt timezone   NUM		# (localtime - UTC_time) in seconds. signed
+#opt tftp       STRING		# tftp server name
+#opt bootfile   STRING		# tftp file to download (e.g. kernel image)
+#opt bootsize   NUM		# size of that file
+#opt rootpath   STRING		# (NFS) path to mount as root fs
+#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
+#opt nissrv     IP_LIST
+#opt ntpsrv     IP_LIST
+#opt lprsrv     IP_LIST
+#opt swapsrv    IP
+# Options specifying routes
+#opt routes     IP_PAIR_LIST
+# 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
+#opt staticroutes   STATIC_ROUTES
+#opt msstaticroutes STATIC_ROUTES
diff --git a/busybox-1.19.3/examples/undeb b/busybox-1.19.3/examples/undeb
new file mode 100755
index 0000000..37104e9
--- /dev/null
+++ b/busybox-1.19.3/examples/undeb
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# This should work with the GNU version of tar and gzip!
+# This should work with the bash or ash shell!
+# Requires the programs (ar, tar, gzip, and the pager more or less).
+#
+usage() {
+echo "Usage: undeb -c package.deb            <Print control file info>"
+echo "       undeb -l package.deb            <List contents of deb package>"
+echo "       undeb -x package.deb /foo/boo   <Extract deb package to this directory,"
+echo "                                        put . for current directory>"
+exit
+}
+
+deb=$2
+
+exist() {
+if [ "$deb" = "" ]; then
+usage
+elif [ ! -s "$deb" ]; then
+echo "Can't find $deb!"
+exit
+fi
+}
+
+if [ "$1" = "" ]; then
+usage
+elif [ "$1" = "-l" ]; then
+exist
+type more >/dev/null 2>&1 && pager=more
+type less >/dev/null 2>&1 && pager=less
+[ "$pager" = "" ] && echo "No pager found!" && exit
+(ar -p $deb control.tar.gz | tar -xzO *control ; echo -e "\nPress enter to scroll, q to Quit!\n" ; ar -p $deb data.tar.gz | tar -tzv) | $pager
+exit
+elif [ "$1" = "-c" ]; then
+exist
+ar -p $deb control.tar.gz | tar -xzO *control
+exit
+elif [ "$1" = "-x" ]; then
+exist
+if [ "$3" = "" ]; then
+usage
+elif [ ! -d "$3" ]; then
+echo "No such directory $3!"
+exit
+fi
+ar -p $deb data.tar.gz | tar -xzvpf - -C $3 || exit
+echo
+echo "Extracted $deb to $3!"
+exit
+else
+usage
+fi
diff --git a/busybox-1.19.3/examples/unrpm b/busybox-1.19.3/examples/unrpm
new file mode 100755
index 0000000..7fd3676
--- /dev/null
+++ b/busybox-1.19.3/examples/unrpm
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# This should work with the GNU version of cpio and gzip!
+# This should work with the bash or ash shell!
+# Requires the programs (cpio, gzip, and the pager more or less).
+#
+usage() {
+echo "Usage: unrpm -l package.rpm            <List contents of rpm package>"
+echo "       unrpm -x package.rpm /foo/boo   <Extract rpm package to this directory,"
+echo "                                        put . for current directory>"
+exit
+}
+
+rpm=$2
+
+exist() {
+if [ "$rpm" = "" ]; then
+usage
+elif [ ! -s "$rpm" ]; then
+echo "Can't find $rpm!"
+exit
+fi
+}
+
+if [ "$1" = "" ]; then
+usage
+elif [ "$1" = "-l" ]; then
+exist
+type more >/dev/null 2>&1 && pager=more
+type less >/dev/null 2>&1 && pager=less
+[ "$pager" = "" ] && echo "No pager found!" && exit
+(echo -e "\nPress enter to scroll, q to Quit!\n" ; rpm2cpio $rpm | cpio -tv --quiet) | $pager
+exit
+elif [ "$1" = "-x" ]; then
+exist
+if [ "$3" = "" ]; then
+usage
+elif [ ! -d "$3" ]; then
+echo "No such directory $3!"
+exit
+fi
+rpm2cpio $rpm | (umask 0 ; cd $3 ; cpio -idmuv) || exit
+echo
+echo "Extracted $rpm to $3!"
+exit
+else
+usage
+fi
diff --git a/busybox-1.19.3/examples/var_service/README b/busybox-1.19.3/examples/var_service/README
new file mode 100644
index 0000000..06817c8
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/README
@@ -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.
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if/convert2ipconf b/busybox-1.19.3/examples/var_service/dhcp_if/convert2ipconf
new file mode 100755
index 0000000..62a288e
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if/convert2ipconf
@@ -0,0 +1,41 @@
+#!/bin/sh
+# convert:
+
+# dhcptype=5
+# serverid=172.16.42.102
+# lease=97200
+# interface=eth0
+# ip=172.16.42.177
+# subnet=255.255.255.0
+# mask=24
+# broadcast=172.16.22.255
+# router=172.16.42.98
+# dns=10.34.32.125 10.32.63.5 10.34.255.7 10.11.255.27
+# domain=lab.example.com example.com
+# ntpsrv=10.34.32.125 10.34.255.7
+
+# into:
+
+#let cfg=cfg+1
+#if[$cfg]=...; ip[$cfg]=...; ipmask[$cfg]=.../...; gw[$cfg]=...; net[$cfg]=... dns[$cfg]=...
+
+exec >/dev/null
+#exec >"$0.out"  # debug
+exec 2>&1
+
+test "$interface" || exit 1
+test "$ip" || exit 1
+
+{
+echo "let cfg=cfg+1"
+test "$interface"	&& echo "if[\$cfg]='$interface'"
+test "$ip"		&& echo "ip[\$cfg]='$ip'"
+test "$ip" && test "$mask" \
+			&& echo "ipmask[\$cfg]='$ip/$mask'"
+test "$router"		&& echo "gw[\$cfg]='$router'"
+test "$dns"		&& echo "dns[\$cfg]='$dns'"
+# TODO: I never saw a dhcp server which correctly announces
+# which subnet(s) is/are available thru advertised router
+# Assume 0/0
+echo "net[\$cfg]='0/0'"
+} >"$1"
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if/convert2ntpconf b/busybox-1.19.3/examples/var_service/dhcp_if/convert2ntpconf
new file mode 100755
index 0000000..debf1eb
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if/convert2ntpconf
@@ -0,0 +1,34 @@
+#!/bin/sh
+# convert:
+
+# dhcptype=5
+# serverid=172.16.42.102
+# lease=97200
+# interface=eth0
+# ip=172.16.42.177
+# subnet=255.255.255.0
+# mask=24
+# broadcast=172.16.22.255
+# router=172.16.42.98
+# dns=10.34.32.125 10.32.63.5 10.34.255.7 10.11.255.27
+# domain=lab.example.com example.com
+# ntpsrv=10.34.32.125 10.34.255.7
+
+# into:
+
+#let cfg=cfg+1
+#ntpip[$cfg]=...
+
+exec >/dev/null
+#exec >"$0.out"  # debug
+exec 2>&1
+
+test "$interface" || exit 1
+test "$ip" || exit 1
+
+{
+for n in $ntpsrv; do
+	echo "let cfg=cfg+1"
+	echo "ntpip[\$cfg]='$n'";
+done
+} >"$1"
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if/dhcp_handler b/busybox-1.19.3/examples/var_service/dhcp_if/dhcp_handler
new file mode 100755
index 0000000..927e02a
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if/dhcp_handler
@@ -0,0 +1,83 @@
+#!/bin/sh
+# executed by udhcpc
+# parameters: $1 and environment
+# $1 is:
+#
+# deconfig: udhcpc starts, or lease is lost.
+# Environment example: interface=eth0
+#
+# bound: lease is obtained. Environment example:
+# dhcptype=5
+# serverid=172.16.42.102
+# lease=97200
+# interface=eth0
+# ip=172.16.42.177
+# subnet=255.255.255.0
+# mask=24
+# broadcast=172.16.22.255
+# router=172.16.42.98
+# dns=10.34.32.125 10.32.63.5 10.34.255.7 10.11.255.27
+# domain=lab.example.com example.com
+# ntpsrv=10.34.32.125 10.34.255.7
+#
+# renew: lease is renewed. Environment is similar to "bound".
+# The IP address does not change, however, the other DHCP paramaters,
+# such as the default gateway, subnet mask, and dns server may change.
+#
+# nak: udhcpc received a NAK message.
+# Environment example: interface=eth0
+#
+# leasefail: udhcpc cannot obtain a lease (DHCP server not responding, etc).
+# Environment example: interface=eth0
+
+# TODO: put $domain into /etc/resolv.conf (thru /var/service/fw)
+
+service=${PWD##*/}
+file_ipconf="$service.ipconf"
+file_ntpconf="$service.ntpconf"
+dir_ipconf="/var/run/service/fw"
+dir_ntpconf="/var/run/service/ntp"
+
+exec >/dev/null
+#exec >>"$0.out"  #debug
+exec 2>&1
+
+echo "`date`: Params: $*"
+
+if test x"$1" != x"bound" && test x"$1" != x"renew" ; then
+	# Reconfigure network with this interface disabled
+	echo "Deconfiguring"
+	rm "$service.out"
+	rm "$file_ipconf"
+	rm "$file_ntpconf"
+	rm "$dir_ipconf/$file_ipconf"
+	rm "$dir_ntpconf/$file_ntpconf"
+	sv u /var/service/fw
+	exit
+fi
+
+# Bound: we've got the lease
+#env >"$service.out"  # debug
+
+./convert2ipconf "$file_ipconf"
+# Reconfigure routing and firewall if needed
+diff --brief "$file_ipconf" "$dir_ipconf/$file_ipconf" >/dev/null 2>&1
+if test $? != 0; then
+	echo "Reconfiguring fw"
+	mkdir -p "$dir_ipconf" 2>/dev/null
+	cp "$file_ipconf" "$dir_ipconf/$file_ipconf"
+	sv u /var/service/fw
+fi
+
+if test -d /var/service/ntp; then
+	./convert2ntpconf "$file_ntpconf"
+	# Reconfigure ntp server addresses if needed
+	diff --brief "$file_ntpconf" "$dir_ntpconf/$file_ntpconf" >/dev/null 2>&1
+	if test $? != 0; then
+		echo "Reconfiguring ntp"
+		mkdir -p "$dir_ntpconf" 2>/dev/null
+		cp "$file_ntpconf" "$dir_ntpconf/$file_ntpconf"
+		sv t /var/service/ntp
+		sv u /var/service/ntp
+	fi
+fi
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if/log/run b/busybox-1.19.3/examples/var_service/dhcp_if/log/run
new file mode 100755
index 0000000..560d1b1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if/p_log b/busybox-1.19.3/examples/var_service/dhcp_if/p_log
new file mode 100755
index 0000000..a2521be
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if/run b/busybox-1.19.3/examples/var_service/dhcp_if/run
new file mode 100755
index 0000000..aec79e0
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if/run
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+exec 2>&1
+exec </dev/null
+
+pwd="$PWD"
+
+if="${PWD##*/dhcp_}"
+
+echo "* Upping iface $if"
+ip link set dev "$if" up
+
+echo "* Starting udhcpc"
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid root \
+udhcpc -vv \
+--hostname=null \
+--foreground \
+--interface="$if" \
+--pidfile="$pwd/udhcpc.pid" \
+--script="$pwd/dhcp_handler"
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if/w_log b/busybox-1.19.3/examples/var_service/dhcp_if/w_log
new file mode 100755
index 0000000..aa36ef1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/busybox-1.19.3/examples/var_service/dhcp_if_pinger/run b/busybox-1.19.3/examples/var_service/dhcp_if_pinger/run
new file mode 100755
index 0000000..20b2fc5
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/dhcp_if_pinger/run
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+delay=67
+
+if=${PWD##*/dhcp_}
+if=${if%%_pinger}
+
+if test -f "$0.log"; then
+	tail -999 "$0.log" >"$0.log.new"
+	mv "$0.log.new" "$0.log"
+fi
+
+test -f "/var/service/dhcp_$if/dhcp_$if.out" || exec env - sleep "$delay"
+. "/var/service/dhcp_$if/dhcp_$if.out"
+test x"$router" != x"" || exec env - sleep "$delay"
+
+#echo "`date '+%Y-%m-%d %H:%M:%S'` Testing ping -c3 $router" >>"$0.log"
+ping -c3 "$router" && exec env - sleep "$delay"
+
+echo "`date '+%Y-%m-%d %H:%M:%S'` Restarting /var/service/dhcp_$if" >>"$0.log"
+sv t "/var/service/dhcp_$if"
+
+exec env - sleep "$delay"
diff --git a/busybox-1.19.3/examples/var_service/ftpd/log/run b/busybox-1.19.3/examples/var_service/ftpd/log/run
new file mode 100755
index 0000000..560d1b1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ftpd/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/busybox-1.19.3/examples/var_service/ftpd/p_log b/busybox-1.19.3/examples/var_service/ftpd/p_log
new file mode 100755
index 0000000..a2521be
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ftpd/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/ftpd/run b/busybox-1.19.3/examples/var_service/ftpd/run
new file mode 100755
index 0000000..87b7d2b
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ftpd/run
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+#exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+user=www
+user=root
+
+exec \
+env - PATH="$PATH" \
+softlimit \
+tcpsvd \
+  -vE -l 0 -c 40 \
+  0.0.0.0 21 \
+setuidgid "$user" \
+ftpd -vv -t10 /pub/ftpd_root
diff --git a/busybox-1.19.3/examples/var_service/ftpd/w_log b/busybox-1.19.3/examples/var_service/ftpd/w_log
new file mode 100755
index 0000000..aa36ef1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ftpd/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/busybox-1.19.3/examples/var_service/fw/conf/11.22.33.44.ipconf-- b/busybox-1.19.3/examples/var_service/fw/conf/11.22.33.44.ipconf--
new file mode 100644
index 0000000..9b44e90
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/fw/conf/11.22.33.44.ipconf--
@@ -0,0 +1,10 @@
+#!/bin/sh
+# If we have simple static address...
+#
+let cfg=cfg+1
+if[$cfg]=if
+ip[$cfg]=11.22.33.44
+ipmask[$cfg]=11.22.33.44/24
+gw[$cfg]=11.22.33.1
+net[$cfg]=0/0
+dns[$cfg]='11.22.33.2 11.22.33.3'
diff --git a/busybox-1.19.3/examples/var_service/fw/conf/192.168.0.1.ipconf b/busybox-1.19.3/examples/var_service/fw/conf/192.168.0.1.ipconf
new file mode 100644
index 0000000..5cf55db
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/fw/conf/192.168.0.1.ipconf
@@ -0,0 +1,11 @@
+#!/bin/sh
+# A small network with no routers
+# (maybe *we* are their router)
+#
+let cfg=cfg+1
+if[$cfg]=if
+ip[$cfg]=192.168.0.1
+ipmask[$cfg]=192.168.0.1/24
+### gw[$cfg]=
+### net[$cfg]=0/0
+### dns[$cfg]=''
diff --git a/busybox-1.19.3/examples/var_service/fw/conf/lo.ipconf b/busybox-1.19.3/examples/var_service/fw/conf/lo.ipconf
new file mode 100644
index 0000000..e6be5f0
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/fw/conf/lo.ipconf
@@ -0,0 +1,10 @@
+#!/bin/bash
+# Mostly redundant except when you need dns[]=your_static_dns_srv
+#
+let cfg=cfg+1
+if[$cfg]=lo
+ip[$cfg]=127.0.0.1
+ipmask[$cfg]=127.0.0.1/8
+gw[$cfg]=''
+net[$cfg]=''
+#dns[$cfg]=127.0.0.1
diff --git a/busybox-1.19.3/examples/var_service/fw/etc/hosts b/busybox-1.19.3/examples/var_service/fw/etc/hosts
new file mode 100644
index 0000000..f7ee533
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/fw/etc/hosts
@@ -0,0 +1,21 @@
+#!/bin/sh
+echo "\
+# This file is automagically regenerated
+# Note! /etc/nsswitch.conf may override this!
+
+# For loopbacking
+127.0.0.1 localhost
+
+# Our local IPs"
+
+hostname=`hostname`
+test "$hostname" || hostname=localhost
+domain=`(. /boot.conf; echo "$DNSDOMAINNAME")`
+test "$domain" && hostname="$hostname $hostname.$domain"
+
+ip -o a l \
+| grep -F 'inet ' \
+| sed -e 's/^.*inet //' -e 's:[ /].*$: '"$hostname"':'
+
+echo
+echo "# End of /etc/hosts"
diff --git a/busybox-1.19.3/examples/var_service/fw/etc/resolv.conf b/busybox-1.19.3/examples/var_service/fw/etc/resolv.conf
new file mode 100644
index 0000000..6561987
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/fw/etc/resolv.conf
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+domain=`(. /boot.conf; echo "$DNSDOMAINNAME") 2>/dev/null`
+
+echo "# This file is automagically regenerated with each boot"
+echo
+test "$domain" && echo "domain $domain"
+test "$domain" && echo "search $domain"
+echo
+echo "# Note that nslookup can choke on DNS server which itself"
+echo "# does NOT have domain name. Other things can work fine."
+echo
+# # If we run DNS cache:
+# echo "nameserver 127.0.0.1"
+# exit
+
+prio=0
+i=0; while test "${if[$i]}"; do
+	test x"${dns_prio[$i]}" != x"" \
+	&& test "${dns_prio[$i]}" -gt "$prio" \
+	&& prio="${dns_prio[$i]}"
+let i++; done
+
+i=0; while test "${if[$i]}"; do
+	for d in ${dns[$i]}; do
+		p="${dns_prio[$i]}"
+		test x"$p" == x"" && p=0
+		test x"$p" == x"$prio" || continue
+		echo "nameserver $d"
+	done
+let i++; done
diff --git a/busybox-1.19.3/examples/var_service/fw/run b/busybox-1.19.3/examples/var_service/fw/run
new file mode 100755
index 0000000..396b678
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/fw/run
@@ -0,0 +1,211 @@
+#!/bin/bash
+# (using bashism: arrays)
+
+service="${PWD##*/}"
+rundir="/var/run/service/$service"
+
+user=root
+extif=if
+ext_open_tcp="21 22 80" # space-separated
+
+# Make ourself one-shot
+sv o .
+# Debug
+#date '+%Y-%m-%d %H:%M:%S' >>"$0.log"
+
+### filter This is the default table (if no -t option is passed).  It contains
+###        the  built-in chains INPUT (for packets coming into the box itself),
+###        FORWARD (for packets being routed through the box), and OUTPUT (for
+###        locally-generated packets).
+###
+### nat    This table is consulted when a packet that creates a new connection
+###        is encountered.  It consists of three built-ins: PREROUTING (for
+###        altering packets as soon as they come in), OUTPUT (for altering
+###        locally-generated packets before routing), and POSTROUTING (for
+###        altering packets as they are about to go out).
+###
+### mangle It had two built-in chains: PREROUTING (for altering incoming
+###        packets before routing) and OUTPUT (for altering locally-generated
+###        packets before routing).  Recently three other built-in
+###        chains are added: INPUT (for packets coming into the box
+###        itself), FORWARD (for altering packets being routed through the
+###        box), and POSTROUTING (for altering packets as they are about to go
+###        out).
+###
+###       ...iface...                              ...iface...
+###          |                                        ^
+###          v                                        |
+### -mangle,NAT-               -mangle,filter-   -mangle,NAT--
+### |PREROUTING|-->[Routing]-->|FORWARD      |-->|POSTROUTING|
+### ------------    |    ^     ---------------   -------------
+###                 |    |                           ^
+###                 |    +--if NATed------------+    |
+###                 v                           |    |
+###      -mangle,filter-                -mangle,NAT,filter-
+###      |INPUT        |  +->[Routing]->|OUTPUT           |
+###      ---------------  |             -------------------
+###                 |     |
+###                 v     |
+###         ... Local Process...
+
+doit() {
+	echo "# $*"
+	"$@"
+}
+
+#exec >/dev/null
+exec >"$0.out"
+exec 2>&1
+exec </dev/null
+
+umask 077
+
+# Make sure rundir/ exists
+mkdir -p "$rundir" 2>/dev/null
+chown -R "$user:" "$rundir"
+chmod -R a=rX "$rundir"
+rm -rf rundir 2>/dev/null
+ln -s "$rundir" rundir
+
+# Timestamping
+date '+%Y-%m-%d %H:%M:%S'
+
+
+echo; echo "* Reading IP config"
+cfg=-1
+#             static cfg    dhcp,zeroconf etc
+for ipconf in conf/*.ipconf "$rundir"/*.ipconf; do
+	if test -f "$ipconf"; then
+		echo "+ $ipconf"
+		. "$ipconf"
+	fi
+done
+
+echo; echo "* Configuring hardware"
+#doit ethtool -s if autoneg off speed 100 duplex full
+#doit ethtool -K if rx off tx off sg off tso off
+
+echo; echo "* Resetting address and routing info"
+doit ip a f dev lo
+i=0; while test "${if[$i]}"; do
+	doit ip a f dev "${if[$i]}"
+	doit ip r f dev "${if[$i]}" root 0/0
+let i++; done
+
+echo; echo "* Configuring addresses"
+doit ip a a dev lo 127.0.0.1/8 scope host
+doit ip a a dev lo ::1/128 scope host
+i=0; while test "${if[$i]}"; do
+	if test "${ipmask[$i]}"; then
+		doit ip a a dev "${if[$i]}" "${ipmask[$i]}" brd +
+		doit ip l set dev "${if[$i]}" up
+	fi
+let i++; done
+
+echo; echo "* Configuring routes"
+i=0; while test "${if[$i]}"; do
+	if test "${net[$i]}" && test "${gw[$i]}"; then
+		doit ip r a "${net[$i]}" via "${gw[$i]}"
+	fi
+let i++; done
+
+echo; echo "* Recreating /etc/* files reflecting new network configuration:"
+for i in etc/*; do
+	n=`basename "$i"`
+	echo "+ $n"
+	(. "$i") >"/etc/$n"
+	chmod 644 "/etc/$n"
+done
+
+
+# Usage: new_chain <chain> [<table>]
+new_chain() {
+	local t=""
+	test x"$2" != x"" && t="-t $2"
+	doit iptables $t -N $1
+	ipt="iptables $t -A $1"
+}
+
+echo; echo "* Reset iptables"
+doit iptables           --flush
+doit iptables           --delete-chain
+doit iptables           --zero
+doit iptables -t nat    --flush
+doit iptables -t nat    --delete-chain
+doit iptables -t nat    --zero
+doit iptables -t mangle --flush
+doit iptables -t mangle --delete-chain
+doit iptables -t mangle --zero
+
+echo; echo "* Configure iptables"
+doit modprobe nf_nat_ftp
+doit modprobe nf_nat_tftp
+doit modprobe nf_conntrack_ftp
+doit modprobe nf_conntrack_tftp
+
+#       *** nat ***
+#       INCOMING TRAFFIC
+ipt="iptables -t nat -A PREROUTING"
+# nothing here
+
+#       LOCALLY ORIGINATED TRAFFIC
+ipt="iptables -t nat -A OUTPUT"
+# nothing here
+
+#       OUTGOING TRAFFIC
+ipt="iptables -t nat -A POSTROUTING"
+# Masquerade boxes on my private net
+doit $ipt -s 192.168.0.0/24 -o $extif -j MASQUERADE
+
+#       *** mangle ***
+### DEBUG
+### ipt="iptables -t mangle -A PREROUTING"
+### doit $ipt -s 192.168.0.0/24 -j RETURN
+### ipt="iptables -t mangle -A FORWARD"
+### doit $ipt -s 192.168.0.0/24 -j RETURN
+### ipt="iptables -t mangle -A POSTROUTING"
+### doit $ipt -s 192.168.0.0/24 -j RETURN
+# nothing here
+
+#       *** filter ***
+#
+new_chain iext filter
+#doit $ipt -s 203.177.104.72 -j DROP	# Some idiot probes my ssh
+#doit $ipt -d 203.177.104.72 -j DROP	# Some idiot probes my ssh
+doit $ipt -m state --state ESTABLISHED,RELATED -j RETURN  # FTP data etc is ok
+if test "$ext_open_tcp"; then
+	portlist="${ext_open_tcp// /,}"
+	doit $ipt -p tcp -m multiport --dports $portlist -j RETURN
+fi
+doit $ipt -p tcp -j REJECT	# Anything else isn't ok. REJECT = irc opens faster
+				# (it probes proxy ports, DROP will incur timeout delays)
+ipt="iptables -t filter -A INPUT"
+doit $ipt -i $extif -j iext
+
+
+echo; echo "* Enabling forwarding"
+echo 1 >/proc/sys/net/ipv4/ip_forward
+echo "/proc/sys/net/ipv4/ip_forward: `cat /proc/sys/net/ipv4/ip_forward`"
+
+
+# Signal everybody that firewall is up
+date '+%Y-%m-%d %H:%M:%S' >"$rundir/up"
+
+# Ok, spew out gobs of info and disable ourself
+echo; echo "* IP:"
+ip a l
+echo; echo "* Routing:"
+ip r l
+echo; echo "* Firewall:"
+{
+echo '---FILTER--';
+iptables -v -L -x -n;
+echo '---NAT-----';
+iptables -t nat -v -L -x -n;
+echo '---MANGLE--';
+iptables -t mangle -v -L -x -n;
+} \
+| grep -v '^$' | grep -Fv 'bytes target'
+echo
+
+echo "* End of firewall configuration"
diff --git a/busybox-1.19.3/examples/var_service/fw/stat b/busybox-1.19.3/examples/var_service/fw/stat
new file mode 100755
index 0000000..08736ad
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/fw/stat
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+echo; echo "* Firewall:"
+{
+echo '---FILTER--';
+iptables -v -L -x -n;
+echo '---NAT-----';
+iptables -t nat -v -L -x -n;
+echo '---MANGLE--';
+iptables -t mangle -v -L -x -n;
+} \
+| grep -v '^$' | grep -Fv 'bytes target' | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/getty_tty1/alt08x16+unimap.fnt b/busybox-1.19.3/examples/var_service/getty_tty1/alt08x16+unimap.fnt
new file mode 100644
index 0000000..9bcc457
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/getty_tty1/alt08x16+unimap.fnt
Binary files differ
diff --git a/busybox-1.19.3/examples/var_service/getty_tty1/cfg b/busybox-1.19.3/examples/var_service/getty_tty1/cfg
new file mode 100755
index 0000000..0f63e52
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/getty_tty1/cfg
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+if test x"$TERM" = x"" -o x"$TERM" = x"unknown"; then
+	TERM="linux"
+	echo "* Setting TERM='$TERM'"
+fi
+export TERM
+
+ttyname=`tty`
+ttybase="${ttyname%%[0123456789]*}"     # strip numeric tail
+
+if test x"$ttybase" = x"/dev/vc/" -o x"$ttybase" = x"/dev/tty"; then
+	echo "* Activating Cyrillic KOI8-R -> CP866 font map"
+	echo -ne "\033(K" >"$ttyname"
+
+	echo "* Loading screen font"
+	setfont \
+		-C "$ttyname" \
+		-m "$PWD/koi8r_to_uni.trans" \
+		"$PWD/alt08x16+unimap.fnt" \
+	|| echo "! setfont failure"
+
+	echo "* Loading keymap"
+	loadkeys "$PWD/ru_koi8r.keymap" \
+	|| echo "! loadkeys failure"
+fi
diff --git a/busybox-1.19.3/examples/var_service/getty_tty1/koi8r_to_uni.trans b/busybox-1.19.3/examples/var_service/getty_tty1/koi8r_to_uni.trans
new file mode 100644
index 0000000..6c6bd01
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/getty_tty1/koi8r_to_uni.trans
@@ -0,0 +1,256 @@
+0x00 U+0000  #  NULL (NUL)
+0x01 U+0001  #  START OF HEADING (SOH)
+0x02 U+0002  #  START OF TEXT (STX)
+0x03 U+0003  #  END OF TEXT (ETX)
+0x04 U+0004  #  END OF TRANSMISSION (EOT)
+0x05 U+0005  #  ENQUIRY (ENQ)
+0x06 U+0006  #  ACKNOWLEDGE (ACK)
+0x07 U+0007  #  BELL (BEL)
+0x08 U+0008  #  BACKSPACE (BS)
+0x09 U+0009  #  CHARACTER TABULATION (HT)
+0x0a U+000A  #  LINE FEED (LF)
+0x0b U+000B  #  LINE TABULATION (VT)
+0x0c U+000C  #  FORM FEED (FF)
+0x0d U+000D  #  CARRIAGE RETURN (CR)
+0x0e U+000E  #  SHIFT OUT (SO)
+0x0f U+000F  #  SHIFT IN (SI)
+0x10 U+0010  #  DATALINK ESCAPE (DLE)
+0x11 U+0011  #  DEVICE CONTROL ONE (DC1)
+0x12 U+0012  #  DEVICE CONTROL TWO (DC2)
+0x13 U+0013  #  DEVICE CONTROL THREE (DC3)
+0x14 U+0014  #  DEVICE CONTROL FOUR (DC4)
+0x15 U+0015  #  NEGATIVE ACKNOWLEDGE (NAK)
+0x16 U+0016  #  SYNCHRONOUS IDLE (SYN)
+0x17 U+0017  #  END OF TRANSMISSION BLOCK (ETB)
+0x18 U+0018  #  CANCEL (CAN)
+0x19 U+0019  #  END OF MEDIUM (EM)
+0x1a U+001A  #  SUBSTITUTE (SUB)
+0x1b U+001B  #  ESCAPE (ESC)
+0x1c U+001C  #  FILE SEPARATOR (IS4)
+0x1d U+001D  #  GROUP SEPARATOR (IS3)
+0x1e U+001E  #  RECORD SEPARATOR (IS2)
+0x1f U+001F  #  UNIT SEPARATOR (IS1)
+0x20 U+0020  #  SPACE
+0x21 U+0021  #  EXCLAMATION MARK
+0x22 U+0022  #  QUOTATION MARK
+0x23 U+0023  #  NUMBER SIGN
+0x24 U+0024  #  DOLLAR SIGN
+0x25 U+0025  #  PERCENT SIGN
+0x26 U+0026  #  AMPERSAND
+0x27 U+0027  #  APOSTROPHE
+0x28 U+0028  #  LEFT PARENTHESIS
+0x29 U+0029  #  RIGHT PARENTHESIS
+0x2a U+002A  #  ASTERISK
+0x2b U+002B  #  PLUS SIGN
+0x2c U+002C  #  COMMA
+0x2d U+002D  #  HYPHEN-MINUS
+0x2e U+002E  #  FULL STOP
+0x2f U+002F  #  SOLIDUS
+0x30 U+0030  #  DIGIT ZERO
+0x31 U+0031  #  DIGIT ONE
+0x32 U+0032  #  DIGIT TWO
+0x33 U+0033  #  DIGIT THREE
+0x34 U+0034  #  DIGIT FOUR
+0x35 U+0035  #  DIGIT FIVE
+0x36 U+0036  #  DIGIT SIX
+0x37 U+0037  #  DIGIT SEVEN
+0x38 U+0038  #  DIGIT EIGHT
+0x39 U+0039  #  DIGIT NINE
+0x3a U+003A  #  COLON
+0x3b U+003B  #  SEMICOLON
+0x3c U+003C  #  LESS-THAN SIGN
+0x3d U+003D  #  EQUALS SIGN
+0x3e U+003E  #  GREATER-THAN SIGN
+0x3f U+003F  #  QUESTION MARK
+0x40 U+0040  #  COMMERCIAL AT
+0x41 U+0041  #  LATIN CAPITAL LETTER A
+0x42 U+0042  #  LATIN CAPITAL LETTER B
+0x43 U+0043  #  LATIN CAPITAL LETTER C
+0x44 U+0044  #  LATIN CAPITAL LETTER D
+0x45 U+0045  #  LATIN CAPITAL LETTER E
+0x46 U+0046  #  LATIN CAPITAL LETTER F
+0x47 U+0047  #  LATIN CAPITAL LETTER G
+0x48 U+0048  #  LATIN CAPITAL LETTER H
+0x49 U+0049  #  LATIN CAPITAL LETTER I
+0x4a U+004A  #  LATIN CAPITAL LETTER J
+0x4b U+004B  #  LATIN CAPITAL LETTER K
+0x4c U+004C  #  LATIN CAPITAL LETTER L
+0x4d U+004D  #  LATIN CAPITAL LETTER M
+0x4e U+004E  #  LATIN CAPITAL LETTER N
+0x4f U+004F  #  LATIN CAPITAL LETTER O
+0x50 U+0050  #  LATIN CAPITAL LETTER P
+0x51 U+0051  #  LATIN CAPITAL LETTER Q
+0x52 U+0052  #  LATIN CAPITAL LETTER R
+0x53 U+0053  #  LATIN CAPITAL LETTER S
+0x54 U+0054  #  LATIN CAPITAL LETTER T
+0x55 U+0055  #  LATIN CAPITAL LETTER U
+0x56 U+0056  #  LATIN CAPITAL LETTER V
+0x57 U+0057  #  LATIN CAPITAL LETTER W
+0x58 U+0058  #  LATIN CAPITAL LETTER X
+0x59 U+0059  #  LATIN CAPITAL LETTER Y
+0x5a U+005A  #  LATIN CAPITAL LETTER Z
+0x5b U+005B  #  LEFT SQUARE BRACKET
+0x5c U+005C  #  REVERSE SOLIDUS
+0x5d U+005D  #  RIGHT SQUARE BRACKET
+0x5e U+005E  #  CIRCUMFLEX ACCENT
+0x5f U+005F  #  LOW LINE
+0x60 U+0060  #  GRAVE ACCENT
+0x61 U+0061  #  LATIN SMALL LETTER A
+0x62 U+0062  #  LATIN SMALL LETTER B
+0x63 U+0063  #  LATIN SMALL LETTER C
+0x64 U+0064  #  LATIN SMALL LETTER D
+0x65 U+0065  #  LATIN SMALL LETTER E
+0x66 U+0066  #  LATIN SMALL LETTER F
+0x67 U+0067  #  LATIN SMALL LETTER G
+0x68 U+0068  #  LATIN SMALL LETTER H
+0x69 U+0069  #  LATIN SMALL LETTER I
+0x6a U+006A  #  LATIN SMALL LETTER J
+0x6b U+006B  #  LATIN SMALL LETTER K
+0x6c U+006C  #  LATIN SMALL LETTER L
+0x6d U+006D  #  LATIN SMALL LETTER M
+0x6e U+006E  #  LATIN SMALL LETTER N
+0x6f U+006F  #  LATIN SMALL LETTER O
+0x70 U+0070  #  LATIN SMALL LETTER P
+0x71 U+0071  #  LATIN SMALL LETTER Q
+0x72 U+0072  #  LATIN SMALL LETTER R
+0x73 U+0073  #  LATIN SMALL LETTER S
+0x74 U+0074  #  LATIN SMALL LETTER T
+0x75 U+0075  #  LATIN SMALL LETTER U
+0x76 U+0076  #  LATIN SMALL LETTER V
+0x77 U+0077  #  LATIN SMALL LETTER W
+0x78 U+0078  #  LATIN SMALL LETTER X
+0x79 U+0079  #  LATIN SMALL LETTER Y
+0x7a U+007A  #  LATIN SMALL LETTER Z
+0x7b U+007B  #  LEFT CURLY BRACKET
+0x7c U+007C  #  VERTICAL LINE
+0x7d U+007D  #  RIGHT CURLY BRACKET
+0x7e U+007E  #  TILDE
+0x7f U+007F  #  DELETE (DEL)
+0x80 U+2500  #  BOX DRAWINGS LIGHT HORIZONTAL
+0x81 U+2502  #  BOX DRAWINGS LIGHT VERTICAL
+0x82 U+250C  #  BOX DRAWINGS LIGHT DOWN AND RIGHT
+0x83 U+2510  #  BOX DRAWINGS LIGHT DOWN AND LEFT
+0x84 U+2514  #  BOX DRAWINGS LIGHT UP AND RIGHT
+0x85 U+2518  #  BOX DRAWINGS LIGHT UP AND LEFT
+0x86 U+251C  #  BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0x87 U+2524  #  BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0x88 U+252C  #  BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0x89 U+2534  #  BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0x8a U+253C  #  BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0x8b U+2580  #  UPPER HALF BLOCK
+0x8c U+2584  #  LOWER HALF BLOCK
+0x8d U+2588  #  FULL BLOCK
+0x8e U+258C  #  LEFT HALF BLOCK
+0x8f U+2590  #  RIGHT HALF BLOCK
+0x90 U+2591  #  LIGHT SHADE
+0x91 U+2592  #  MEDIUM SHADE
+0x92 U+2593  #  DARK SHADE
+0x93 U+2320  #  TOP HALF INTEGRAL
+0x94 U+25A0  #  BLACK SQUARE
+0x95 U+2219  #  BULLET OPERATOR
+0x96 U+221A  #  SQUARE ROOT
+0x97 U+2248  #  ALMOST EQUAL TO
+0x98 U+2264  #  LESS-THAN OR EQUAL TO
+0x99 U+2265  #  GREATER-THAN OR EQUAL TO
+0x9a U+00A0  #  NO-BREAK SPACE
+0x9b U+2321  #  BOTTOM HALF INTEGRAL
+0x9c U+00B0  #  DEGREE SIGN
+0x9d U+00B2  #  SUPERSCRIPT TWO
+0x9e U+00B7  #  MIDDLE DOT
+0x9f U+00F7  #  DIVISION SIGN
+0xa0 U+2550  #  BOX DRAWINGS DOUBLE HORIZONTAL
+0xa1 U+2551  #  BOX DRAWINGS DOUBLE VERTICAL
+0xa2 U+2552  #  BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+0xa3 U+0451  #  CYRILLIC SMALL LETTER IO
+0xa4 U+2553  #  BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+0xa5 U+2554  #  BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xa6 U+2555  #  BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+0xa7 U+2556  #  BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+0xa8 U+2557  #  BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xa9 U+2558  #  BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+0xaa U+2559  #  BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+0xab U+255A  #  BOX DRAWINGS DOUBLE UP AND RIGHT
+0xac U+255B  #  BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+0xad U+255C  #  BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+0xae U+255D  #  BOX DRAWINGS DOUBLE UP AND LEFT
+0xaf U+255E  #  BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+0xb0 U+255F  #  BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+0xb1 U+2560  #  BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xb2 U+2561  #  BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+0xb3 U+0401  #  CYRILLIC CAPITAL LETTER IO
+0xb4 U+2562  #  BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+0xb5 U+2563  #  BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xb6 U+2564  #  BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+0xb7 U+2565  #  BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+0xb8 U+2566  #  BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xb9 U+2567  #  BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+0xba U+2568  #  BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+0xbb U+2569  #  BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xbc U+256A  #  BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xbd U+256B  #  BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+0xbe U+256C  #  BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xbf U+00A9  #  COPYRIGHT SIGN
+0xc0 U+044E  #  CYRILLIC SMALL LETTER YU
+0xc1 U+0430  #  CYRILLIC SMALL LETTER A
+0xc2 U+0431  #  CYRILLIC SMALL LETTER BE
+0xc3 U+0446  #  CYRILLIC SMALL LETTER TSE
+0xc4 U+0434  #  CYRILLIC SMALL LETTER DE
+0xc5 U+0435  #  CYRILLIC SMALL LETTER IE
+0xc6 U+0444  #  CYRILLIC SMALL LETTER EF
+0xc7 U+0433  #  CYRILLIC SMALL LETTER GHE
+0xc8 U+0445  #  CYRILLIC SMALL LETTER HA
+0xc9 U+0438  #  CYRILLIC SMALL LETTER I
+0xca U+0439  #  CYRILLIC SMALL LETTER SHORT I
+0xcb U+043A  #  CYRILLIC SMALL LETTER KA
+0xcc U+043B  #  CYRILLIC SMALL LETTER EL
+0xcd U+043C  #  CYRILLIC SMALL LETTER EM
+0xce U+043D  #  CYRILLIC SMALL LETTER EN
+0xcf U+043E  #  CYRILLIC SMALL LETTER O
+0xd0 U+043F  #  CYRILLIC SMALL LETTER PE
+0xd1 U+044F  #  CYRILLIC SMALL LETTER YA
+0xd2 U+0440  #  CYRILLIC SMALL LETTER ER
+0xd3 U+0441  #  CYRILLIC SMALL LETTER ES
+0xd4 U+0442  #  CYRILLIC SMALL LETTER TE
+0xd5 U+0443  #  CYRILLIC SMALL LETTER U
+0xd6 U+0436  #  CYRILLIC SMALL LETTER ZHE
+0xd7 U+0432  #  CYRILLIC SMALL LETTER VE
+0xd8 U+044C  #  CYRILLIC SMALL LETTER SOFT SIGN
+0xd9 U+044B  #  CYRILLIC SMALL LETTER YERU
+0xda U+0437  #  CYRILLIC SMALL LETTER ZE
+0xdb U+0448  #  CYRILLIC SMALL LETTER SHA
+0xdc U+044D  #  CYRILLIC SMALL LETTER E
+0xdd U+0449  #  CYRILLIC SMALL LETTER SHCHA
+0xde U+0447  #  CYRILLIC SMALL LETTER CHE
+0xdf U+044A  #  CYRILLIC SMALL LETTER HARD SIGN
+0xe0 U+042E  #  CYRILLIC CAPITAL LETTER YU
+0xe1 U+0410  #  CYRILLIC CAPITAL LETTER A
+0xe2 U+0411  #  CYRILLIC CAPITAL LETTER BE
+0xe3 U+0426  #  CYRILLIC CAPITAL LETTER TSE
+0xe4 U+0414  #  CYRILLIC CAPITAL LETTER DE
+0xe5 U+0415  #  CYRILLIC CAPITAL LETTER IE
+0xe6 U+0424  #  CYRILLIC CAPITAL LETTER EF
+0xe7 U+0413  #  CYRILLIC CAPITAL LETTER GHE
+0xe8 U+0425  #  CYRILLIC CAPITAL LETTER HA
+0xe9 U+0418  #  CYRILLIC CAPITAL LETTER I
+0xea U+0419  #  CYRILLIC CAPITAL LETTER SHORT I
+0xeb U+041A  #  CYRILLIC CAPITAL LETTER KA
+0xec U+041B  #  CYRILLIC CAPITAL LETTER EL
+0xed U+041C  #  CYRILLIC CAPITAL LETTER EM
+0xee U+041D  #  CYRILLIC CAPITAL LETTER EN
+0xef U+041E  #  CYRILLIC CAPITAL LETTER O
+0xf0 U+041F  #  CYRILLIC CAPITAL LETTER PE
+0xf1 U+042F  #  CYRILLIC CAPITAL LETTER YA
+0xf2 U+0420  #  CYRILLIC CAPITAL LETTER ER
+0xf3 U+0421  #  CYRILLIC CAPITAL LETTER ES
+0xf4 U+0422  #  CYRILLIC CAPITAL LETTER TE
+0xf5 U+0423  #  CYRILLIC CAPITAL LETTER U
+0xf6 U+0416  #  CYRILLIC CAPITAL LETTER ZHE
+0xf7 U+0412  #  CYRILLIC CAPITAL LETTER VE
+0xf8 U+042C  #  CYRILLIC CAPITAL LETTER SOFT SIGN
+0xf9 U+042B  #  CYRILLIC CAPITAL LETTER YERU
+0xfa U+0417  #  CYRILLIC CAPITAL LETTER ZE
+0xfb U+0428  #  CYRILLIC CAPITAL LETTER SHA
+0xfc U+042D  #  CYRILLIC CAPITAL LETTER E
+0xfd U+0429  #  CYRILLIC CAPITAL LETTER SHCHA
+0xfe U+0427  #  CYRILLIC CAPITAL LETTER CHE
+0xff U+042A  #  CYRILLIC CAPITAL LETTER HARD SIGN
diff --git a/busybox-1.19.3/examples/var_service/getty_tty1/login.sh b/busybox-1.19.3/examples/var_service/getty_tty1/login.sh
new file mode 100755
index 0000000..d69b6fd
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/getty_tty1/login.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+ttyname=`tty`
+ttybase="${ttyname%%[0123456789]*}"     # strip numeric tail
+
+if test "$ttybase" = "/dev/tty"; then
+	tail="${ttyname:8}"
+	echo "* Setting terminal device's owner to $LOGIN_UID:$LOGIN_GID"
+	chown "$LOGIN_UID:$LOGIN_GID" "/dev/vcs$tail" "/dev/vcsa$tail"
+fi
+# We can do this also, but login does it itself
+# chown "$LOGIN_UID:$LOGIN_GID" "$ttyname"
diff --git a/busybox-1.19.3/examples/var_service/getty_tty1/ru_koi8r.keymap b/busybox-1.19.3/examples/var_service/getty_tty1/ru_koi8r.keymap
new file mode 100644
index 0000000..6c81153
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/getty_tty1/ru_koi8r.keymap
@@ -0,0 +1,183 @@
+keymaps 0,1, 2,3, 4,6, 8,10, 12,14
+#
+# This one is for generating koi8r Russian chars
+# Cyr/Lat switches:  RightAlt, Shift+Ctrl, Ctrl+Shift
+# (last one does not work for dark and obscure reasons 8( )
+#
+# plain,shift, plain,shift, ctrl,ctrl alt,alt ctrlalt,ctrlalt
+# lat--------  cyr--------  lat  cyr  lat cyr lat     cyr
+#
+#Shift                     1
+#AltGr (cyr)               2
+#Control                   4
+#Alt                       8
+#ShiftL                   16
+#ShiftR                   32
+#CtrlL                    64
+#CtrlR                   128
+#============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr ===========
+keycode   1 =   Escape          Escape          Escape          Escape          Escape                  Escape                  Meta_Escape             Meta_Escape             SAK                     SAK
+keycode   2 =   one             exclam          one             exclam          exclam                  exclam                  Meta_one                Meta_one
+keycode   3 =   two             at              two             at              nul                     nul                     Meta_two                Meta_two
+keycode   4 =   three           numbersign      three           numbersign      three                   three                   Meta_three              Meta_three
+keycode   5 =   four            dollar          four            dollar          Control_backslash       Control_backslash       Meta_four               Meta_four
+keycode   6 =   five            percent         five            percent         Control_bracketright    Control_bracketright    Meta_five               Meta_five
+keycode   7 =   six             asciicircum     six             asciicircum     Control_asciicircum     Control_asciicircum     Meta_six                Meta_six
+keycode   8 =   seven           ampersand       seven           ampersand       Control_underscore      Control_underscore      Meta_seven              Meta_seven
+keycode   9 =   eight           asterisk        eight           asterisk        eight                   eight                   Meta_eight              Meta_eight
+keycode  10 =   nine            parenleft       nine            parenleft       nine                    nine                    Meta_nine               Meta_nine
+keycode  11 =   zero            parenright      zero            parenright      zero                    zero                    Meta_zero               Meta_zero
+keycode  12 =   minus           underscore      minus           underscore      Control_underscore      Control_underscore      Meta_minus              Meta_minus
+keycode  13 =   equal           plus            equal           plus            equal                   equal                   Meta_equal              Meta_equal
+keycode  14 =   Delete          Delete          Delete          Delete          BackSpace               BackSpace               Meta_Delete             Meta_Delete
+keycode  15 =   Tab             Tab             Tab             Tab             Tab                     Tab                     Meta_Tab                Meta_Tab
+keycode  16 =   q               Q               202             234             Control_q               Control_q               Meta_q                  Meta_q                  Meta_Control_q          Meta_Control_q
+keycode  17 =   w               W               195             227             Control_w               Control_w               Meta_w                  Meta_w                  Meta_Control_w          Meta_Control_w
+keycode  18 =   e               E               213             245             Control_e               Control_e               Meta_e                  Meta_e                  Meta_Control_e          Meta_Control_e
+keycode  19 =   r               R               203             235             Control_r               Control_r               Meta_r                  Meta_r                  Meta_Control_r          Meta_Control_r
+keycode  20 =   t               T               197             229             Control_t               Control_t               Meta_t                  Meta_t                  Meta_Control_t          Meta_Control_t
+#============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr ===========
+keycode  21 =   y               Y               206             238             Control_y               Control_y               Meta_y                  Meta_y                  Meta_Control_y          Meta_Control_y
+keycode  22 =   u               U               199             231             Control_u               Control_u               Meta_u                  Meta_u                  Meta_Control_u          Meta_Control_u
+keycode  23 =   i               I               219             251             Control_i               Control_i               Meta_i                  Meta_i                  Meta_Control_i          Meta_Control_i
+keycode  24 =   o               O               221             253             Control_o               Control_o               Meta_o                  Meta_o                  Meta_Control_o          Meta_Control_o
+keycode  25 =   p               P               218             250             Control_p               Control_p               Meta_p                  Meta_p                  Meta_Control_p          Meta_Control_p
+keycode  26 =   bracketleft     braceleft       200             232             Escape                  Escape                  Meta_bracketleft        Meta_bracketleft
+keycode  27 =   bracketright    braceright      223             255             Control_bracketright    Control_bracketright
+keycode  28 =   Return
+# Shift+Ctrl - Cyrillic
+keycode  29 =   Control		AltGr_Lock      Control         AltGr_Lock      Control		        Control		        Control		        Control		        Control		        Control
+keycode  30 =   a               A               198             230             Control_a               Control_a               Meta_a                  Meta_a                  Meta_Control_a          Meta_Control_a
+keycode  31 =   s               S               217             249             Control_s               Control_s               Meta_s                  Meta_s                  Meta_Control_s          Meta_Control_s
+keycode  32 =   d               D               215             247             Control_d               Control_d               Meta_d                  Meta_d                  Meta_Control_d          Meta_Control_d
+keycode  33 =   f               F               193             225             Control_f               Control_f               Meta_f                  Meta_f                  Meta_Control_f          Meta_Control_f
+keycode  34 =   g               G               208             240             Control_g               Control_g               Meta_g                  Meta_g                  Meta_Control_g          Meta_Control_g
+keycode  35 =   h               H               210             242             Control_h               Control_h               Meta_h                  Meta_h                  Meta_Control_h          Meta_Control_h
+keycode  36 =   j               J               207             239             Control_j               Control_j               Meta_j                  Meta_j                  Meta_Control_j          Meta_Control_j
+keycode  37 =   k               K               204             236             Control_k               Control_k               Meta_k                  Meta_k                  Meta_Control_k          Meta_Control_k
+keycode  38 =   l               L               196             228             Control_l               Control_l               Meta_l                  Meta_l                  Meta_Control_l          Meta_Control_l
+keycode  39 =   semicolon       colon           214             246             semicolon               semicolon               Meta_semicolon          Meta_semicolon
+keycode  40 =   apostrophe      quotedbl        220             252             Control_g               Control_g               Meta_apostrophe         Meta_apostrophe
+#============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr ===========
+keycode  41 =   grave           asciitilde      grave           asciitilde      nul                     nul                     Meta_grave              Meta_grave
+keycode  42 =   Shift
+keycode  43 =   backslash       bar             backslash       bar             Control_backslash       Control_backslash       Meta_backslash          Meta_backslash
+keycode  44 =   z               Z               209             241             Control_z               Control_z               Meta_z                  Meta_z                  Meta_Control_z          Meta_Control_z
+keycode  45 =   x               X               222             254             Control_x               Control_x               Meta_x                  Meta_x                  Meta_Control_x          Meta_Control_x
+keycode  46 =   c               C               211             243             Control_c               Control_c               Meta_c                  Meta_c                  Meta_Control_c          Meta_Control_c
+keycode  47 =   v               V               205             237             Control_v               Control_v               Meta_v                  Meta_v                  Meta_Control_v          Meta_Control_v
+keycode  48 =   b               B               201             233             Control_b               Control_b               Meta_b                  Meta_b                  Meta_Control_b          Meta_Control_b
+keycode  49 =   n               N               212             244             Control_n               Control_n               Meta_n                  Meta_n                  Meta_Control_n          Meta_Control_n
+keycode  50 =   m               M               216             248             Control_m               Control_m               Meta_m                  Meta_m                  Meta_Control_m          Meta_Control_m
+keycode  51 =   comma           less            194             226             comma                   comma                   Meta_comma              Meta_comma
+keycode  52 =   period          greater         192             224             Compose                 Compose                 Meta_period             Meta_period
+keycode  53 =   slash           question        slash           question        Delete                  Delete                  Meta_slash              Meta_slash              Meta_question           Meta_question
+# Ctrl+Shift - Cyrillic (not working???)
+keycode  54 =   Shift           Shift           Shift           Shift           AltGr_Lock              AltGr_Lock              Shift                   Shift                   Shift                   Shift
+keycode  55 =   KP_Multiply
+keycode  56 =   Alt
+keycode  57 =   space           space           space           space           nul                     nul                     Meta_space              Meta_space
+keycode  58 =   Caps_Lock
+keycode  59 =   F1              F11             F1              F11             F1                      F1                      Console_1               Console_1               Console_1               Console_1
+keycode  60 =   F2              F12             F2              F12             F2                      F2                      Console_2               Console_2               Console_2               Console_2
+#============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr ===========
+keycode  61 =   F3              F13             F3              F13             F3                      F3                      Console_3               Console_3               Console_3               Console_3
+keycode  62 =   F4              F14             F4              F14             F4                      F4                      Console_4               Console_4               Console_4               Console_4
+keycode  63 =   F5              F15             F5              F15             F5                      F5                      Console_5               Console_5               Console_5               Console_5
+keycode  64 =   F6              F16             F6              F16             F6                      F6                      Console_6               Console_6               Console_6               Console_6
+keycode  65 =   F7              F17             F7              F17             F7                      F7                      Console_7               Console_7               Console_7               Console_7
+keycode  66 =   F8              F18             F8              F18             F8                      F8                      Console_8               Console_8               Console_8               Console_8
+keycode  67 =   F9              F19             F9              F19             F9                      F9                      Console_9               Console_9               Console_9               Console_9
+keycode  68 =   F10             F20             F10             F20             F10                     F10                     Console_10              Console_10              Console_10              Console_10
+keycode  69 =   Num_Lock        Bare_Num_Lock   Num_Lock        Bare_Num_Lock
+keycode  70 =   Scroll_Lock     Show_Memory     Scroll_Lock     Show_Memory     Show_State              Show_State
+keycode  71 =   KP_7            KP_7            KP_7            KP_7            KP_7                    KP_7                    Ascii_7                 Ascii_7
+keycode  72 =   KP_8            KP_8            KP_8            KP_8            KP_8                    KP_8                    Ascii_8                 Ascii_8
+keycode  73 =   KP_9            KP_9            KP_9            KP_9            KP_9                    KP_9                    Ascii_9                 Ascii_9
+keycode  74 =   KP_Subtract     KP_Subtract     KP_Subtract     KP_Subtract     KP_Subtract             KP_Subtract             KP_Subtract             KP_Subtract
+keycode  75 =   KP_4            KP_4            KP_4            KP_4            KP_4                    KP_4                    Ascii_4                 Ascii_4
+keycode  76 =   KP_5            KP_5            KP_5            KP_5            KP_5                    KP_5                    Ascii_5                 Ascii_5
+keycode  77 =   KP_6            KP_6            KP_6            KP_6            KP_6                    KP_6                    Ascii_6                 Ascii_6
+keycode  78 =   KP_Add          KP_Add          KP_Add          KP_Add          KP_Add                  KP_Add                  KP_Add                  KP_Add
+keycode  79 =   KP_1            KP_1            KP_1            KP_1            KP_1                    KP_1                    Ascii_1                 Ascii_1
+keycode  80 =   KP_2            KP_2            KP_2            KP_2            KP_2                    KP_2                    Ascii_2                 Ascii_2
+#============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr ===========
+keycode  81 =   KP_3            KP_3            KP_3            KP_3            KP_3                    KP_3                    Ascii_3                 Ascii_3
+keycode  82 =   KP_0            KP_0            KP_0            KP_0            KP_0                    KP_0                    Ascii_0                 Ascii_0
+keycode  83 =   KP_Period       KP_Period       KP_Period       KP_Period       KP_Period               KP_Period               KP_Period               KP_Period               Boot                    Boot
+keycode  84 =   Last_Console
+keycode  85 =
+keycode  86 =   less            greater         less            greater         less                    less                    Meta_less               Meta_less
+keycode  87 =   F11             F11             F11             F11             F11                     F11                     Console_11              Console_11              Console_11              Console_11
+keycode  88 =   F12             F12             F12             F12             F12                     F12                     Console_12              Console_12              Console_12              Console_12
+keycode  89 =
+keycode  90 =
+keycode  91 =
+keycode  92 =
+keycode  93 =
+keycode  94 =
+keycode  95 =
+keycode  96 =   KP_Enter
+keycode  97 =   Control
+keycode  98 =   KP_Divide
+keycode  99 =   Control_backslash
+# Right Alt - Cyrillic
+keycode 100 =   AltGr_Lock
+#============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr ===========
+keycode 101 =   Break
+keycode 102 =   Find
+keycode 103 =   Up
+keycode 104 =   Prior           Scroll_Backward Prior           Scroll_Backward Prior                   Prior                   Prior                   Prior                   Prior                   Prior
+keycode 105 =   Left            Left            Left            Left            Left                    Left                    Left                    Left                    Decr_Console            Decr_Console
+keycode 106 =   Right           Right           Right           Right           Right                   Right                   Right                   Right                   Incr_Console            Incr_Console
+keycode 107 =   Select
+keycode 108 =   Down
+keycode 109 =   Next            Scroll_Forward  Next            Scroll_Forward  Next                    Next                    Next                    Next                    Next                    Next
+keycode 110 =   Insert
+keycode 111 =   Remove          Remove          Remove          Remove          Remove                  Remove                  Remove                  Remove                  Boot                    Boot
+keycode 112 =   Macro
+keycode 113 =   F13
+keycode 114 =   F14
+keycode 115 =   Help
+keycode 116 =   Do
+keycode 117 =   F17
+keycode 118 =   KP_MinPlus
+keycode 119 =   Pause
+keycode 120 =
+#============== plain ========= shift========== plain cyr ===== shift cyr ===== ctrl ================== ctrl cyr ============== alt =================== alt cyr =============== ctrlalt =============== ctrlalt cyr ===========
+keycode 121 =
+keycode 122 =
+keycode 123 =
+keycode 124 =
+keycode 125 =
+keycode 126 =
+keycode 127 =
+
+string F1 = "\033[[A"
+string F2 = "\033[[B"
+string F3 = "\033[[C"
+string F4 = "\033[[D"
+string F5 = "\033[[E"
+string F6 = "\033[17~"
+string F7 = "\033[18~"
+string F8 = "\033[19~"
+string F9 = "\033[20~"
+string F10 = "\033[21~"
+string F11 = "\033[23~"
+string F12 = "\033[24~"
+string F13 = "\033[25~"
+string F14 = "\033[26~"
+string F15 = "\033[28~"
+string F16 = "\033[29~"
+string F17 = "\033[31~"
+string F18 = "\033[32~"
+string F19 = "\033[33~"
+string F20 = "\033[34~"
+string Find = "\033[1~"
+string Insert = "\033[2~"
+string Remove = "\033[3~"
+string Select = "\033[4~"
+string Prior = "\033[5~"
+string Next = "\033[6~"
+string Macro = "\033[M"
+string Pause = "\033[P"
diff --git a/busybox-1.19.3/examples/var_service/getty_tty1/run b/busybox-1.19.3/examples/var_service/getty_tty1/run
new file mode 100755
index 0000000..c7c413b
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/getty_tty1/run
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+user=root
+baud=38400
+delay=3
+export TERM=linux
+
+tty="/dev/${PWD##*/getty_}"
+
+if ! test -e "$tty"; then
+	exec env - sleep 32000
+fi
+
+sleep "$delay"
+
+chown "$user" "$tty" # - devfs made happy
+
+exec <"$tty" >"$tty" 2>&1
+# using . in order to be able to set env (TERM etc) in cfg
+test -x ./cfg && . ./cfg
+
+exec \
+env - "TERM=$TERM" PATH="$PATH" LOGIN_PRE_SUID_SCRIPT="$PWD/login.sh" \
+softlimit \
+setuidgid "$user" \
+getty "$baud" "$tty" "$TERM"
diff --git a/busybox-1.19.3/examples/var_service/gpm/run b/busybox-1.19.3/examples/var_service/gpm/run
new file mode 100755
index 0000000..a13fdcd
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/gpm/run
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+user=root
+options="-D -2 -m /dev/psaux -t ps2"
+#options="-D -2 -m /dev/ttyS0 -t bare"
+
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+gpm $options
diff --git a/busybox-1.19.3/examples/var_service/httpd/log/run b/busybox-1.19.3/examples/var_service/httpd/log/run
new file mode 100755
index 0000000..560d1b1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/httpd/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/busybox-1.19.3/examples/var_service/httpd/p_log b/busybox-1.19.3/examples/var_service/httpd/p_log
new file mode 100755
index 0000000..a2521be
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/httpd/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/httpd/run b/busybox-1.19.3/examples/var_service/httpd/run
new file mode 100755
index 0000000..ff8869b
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/httpd/run
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+#exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+user=www
+user=root
+
+echo "* Starting tcpsvd for httpd [$$]"
+exec \
+env - PATH="$PATH" \
+softlimit \
+tcpsvd \
+  -v -E -l localhost -c 5 \
+  0 88 \
+setuidgid "$user" \
+httpd -vvv -i -h /pub/httpd_root
diff --git a/busybox-1.19.3/examples/var_service/httpd/w_log b/busybox-1.19.3/examples/var_service/httpd/w_log
new file mode 100755
index 0000000..aa36ef1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/httpd/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/busybox-1.19.3/examples/var_service/ifplugd_if/ifplugd_handler b/busybox-1.19.3/examples/var_service/ifplugd_if/ifplugd_handler
new file mode 100755
index 0000000..4962fcf
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ifplugd_if/ifplugd_handler
@@ -0,0 +1,15 @@
+#!/bin/sh
+# parameters:
+# $1: interface
+# $2: state
+
+if test -d "/var/service/dhcp_$1"; then
+	if test x"$2" = x"down"; then
+		echo "Downing /var/service/dhcp_$1"
+		sv d "/var/service/dhcp_$1"
+	fi
+	if test x"$2" = x"up"; then
+		echo "Upping /var/service/dhcp_$1"
+		sv u "/var/service/dhcp_$1"
+	fi
+fi
diff --git a/busybox-1.19.3/examples/var_service/ifplugd_if/log/run b/busybox-1.19.3/examples/var_service/ifplugd_if/log/run
new file mode 100755
index 0000000..560d1b1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ifplugd_if/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/busybox-1.19.3/examples/var_service/ifplugd_if/p_log b/busybox-1.19.3/examples/var_service/ifplugd_if/p_log
new file mode 100755
index 0000000..a2521be
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ifplugd_if/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/ifplugd_if/run b/busybox-1.19.3/examples/var_service/ifplugd_if/run
new file mode 100755
index 0000000..2781cf9
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ifplugd_if/run
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+#exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+pwd="$PWD"
+
+if="${PWD##*/ifplugd_}"
+
+echo "* Starting ifplugd on $if [$$]"
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid root \
+ifplugd -apqns -t3 -u8 -d8 -i "$if" -r "$pwd/ifplugd_handler"
+
+# We use -t3 to wake ifplugd up less often.
+# If after three tests (3*3=9 > 8) link state seen to be different,
+# the handler will be called.
+# IOW: short link losses will be ignored, longer ones
+# will trigger DHCP reconfiguration and such (see handler code).
+
+#-a       Do not up interface automatically
+#-p       Dont run script on daemon startup
+#-q       Dont run script on daemon quit
+#-n       Do not daemonize
+#-s       Do not log to syslog
+#-t SECS  Poll time in seconds
+#-u SECS  Delay before running script after link up
+#-d SECS  Delay after link down
+#-i IFACE Interface
+#-r PROG  Script to run
+#-f/-F    Treat link detection error as link down/link up (otherwise exit on error)
+#-M       Monitor creation/destruction of interface (otherwise it must exist)
+#-x ARG   Extra argument for script
+#-I       Dont exit on nonzero exit code from script
+#-l       Run script on startup even if no cable is detected
+#-m MODE  API mode (mii, priv, ethtool, wlan, auto)
diff --git a/busybox-1.19.3/examples/var_service/ifplugd_if/w_log b/busybox-1.19.3/examples/var_service/ifplugd_if/w_log
new file mode 100755
index 0000000..aa36ef1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ifplugd_if/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/busybox-1.19.3/examples/var_service/inetd/inetd.conf b/busybox-1.19.3/examples/var_service/inetd/inetd.conf
new file mode 100644
index 0000000..c5f151b
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/inetd/inetd.conf
@@ -0,0 +1,18 @@
+# [ADDR:]service_name           must be in /etc/services, or port number
+# socket_type                   stream/dgram/raw/rdm/seqpacket
+# protocol                      tcp/udp
+# wait/nowait[.max]             wait is usually for udp, nowait for tcp
+#                               max: max copies to run
+# user[.group] or user[:group]  user and group to run under
+# binary                        program to run
+# arg0 arg1 arg2...             arguments, INCLUDING program name (arg0)
+
+# serv	socket pro  w/nw   user binary args
+
+# IPv6
+555	dgram  udp6 wait   root echo echo Hello IPv6 udp world
+# ...with ADDR prefix:
+::1:444	stream tcp6 nowait root echo echo Hello IPv6 localhost
+
+# Rarely seen case: tcp *wait* service
+telnet	stream tcp  wait   root telnetd telnetd -w10
diff --git a/busybox-1.19.3/examples/var_service/inetd/log/run b/busybox-1.19.3/examples/var_service/inetd/log/run
new file mode 100755
index 0000000..560d1b1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/inetd/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/busybox-1.19.3/examples/var_service/inetd/p_log b/busybox-1.19.3/examples/var_service/inetd/p_log
new file mode 100755
index 0000000..a2521be
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/inetd/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/inetd/run b/busybox-1.19.3/examples/var_service/inetd/run
new file mode 100755
index 0000000..910c1b3
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/inetd/run
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+#exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+echo "* Starting inetd [$$]"
+exec \
+env - PATH="$PATH" \
+softlimit \
+inetd -f -e "$PWD/inetd.conf"
+
+# inetd [-f] [-q len] [conf]
+# -f      Run in foreground
+# -e      Log to stderr (default is syslog)
+# -q N    Set the size of the socket listen queue to N
+#         (default: 128)
diff --git a/busybox-1.19.3/examples/var_service/inetd/w_log b/busybox-1.19.3/examples/var_service/inetd/w_log
new file mode 100755
index 0000000..aa36ef1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/inetd/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/busybox-1.19.3/examples/var_service/nmeter/run b/busybox-1.19.3/examples/var_service/nmeter/run
new file mode 100755
index 0000000..7e51124
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/nmeter/run
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+# Since per-process /proc/net/ (-> /proc/self/net/) appeared,
+# we need to be root
+user="root"
+tty="/dev/tty9"
+cmd="nmeter '%t %c x %x p%p f %f b %b m %m if%[nif]'"
+
+chmod -R a+X . # or else env will moan
+chown "$user": "$tty" # devfs made happy
+
+eval exec \
+env - PATH="$PATH" \
+setuidgid "$user" \
+<"$tty" >"$tty" 2>&1 \
+$cmd
diff --git a/busybox-1.19.3/examples/var_service/ntpd/log/run b/busybox-1.19.3/examples/var_service/ntpd/log/run
new file mode 100755
index 0000000..560d1b1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ntpd/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/busybox-1.19.3/examples/var_service/ntpd/ntp.script b/busybox-1.19.3/examples/var_service/ntpd/ntp.script
new file mode 100755
index 0000000..76c34bf
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ntpd/ntp.script
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Note that there is no provision to prevent several copies of the script
+# to be run 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.
+#
+# Script should be ready to deal with this.
+
+dt=`date '+%Y-%m-%d %H:%M:%S'`
+
+if test x"$stratum" != x"" \
+&& test x"$poll_interval" != x"" \
+&& test 4 -ge "$stratum" \
+&& test 128 -le "$poll_interval" \
+; then
+	echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
+	echo "$dt: $1"\
+		"freq_drift_ppm=$freq_drift_ppm"\
+		"offset=$offset"\
+		"stratum=$stratum"\
+		"poll_interval=$poll_interval,"\
+		"setting hardware clock"\
+		>>"$0.log.$$"
+	mv -- "$0.log.$$" "$0.log"
+	exec hwclock --systohc
+fi
+
+echo "`tail -n 199 -- "$0.log" 2>/dev/null`" >"$0.log.$$"
+echo "$dt: $1"\
+	"freq_drift_ppm=$freq_drift_ppm"\
+	"offset=$offset"\
+	"stratum=$stratum"\
+	"poll_interval=$poll_interval"\
+	>>"$0.log.$$"
+mv -- "$0.log.$$" "$0.log"
diff --git a/busybox-1.19.3/examples/var_service/ntpd/p_log b/busybox-1.19.3/examples/var_service/ntpd/p_log
new file mode 100755
index 0000000..a2521be
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ntpd/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/ntpd/run b/busybox-1.19.3/examples/var_service/ntpd/run
new file mode 100755
index 0000000..581d231
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ntpd/run
@@ -0,0 +1,60 @@
+#!/bin/bash
+# (using bashism (arrays) in dhcp config)
+
+#exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+user=root
+pool="us.pool.ntp.org" # replace "us" with your country code
+
+service="${PWD##*/}"
+rundir="/var/run/service/$service"
+default_p_opt="-p 0.$pool -p 1.$pool -p 2.$pool -p 3.$pool"
+
+
+# Make sure rundir/ exists
+mkdir -p "$rundir" 2>/dev/null
+chown -R "$user:" "$rundir"
+chmod -R a=rX "$rundir"
+rm -rf rundir 2>/dev/null
+ln -s "$rundir" rundir
+
+
+echo "* Checking network"
+test -f /var/run/service/fw/up || exec sleep 7
+sleep 5  # to let it settle
+
+# Grab config from dhcp
+cfg=-1
+for f in rundir/*.ntpconf; do
+        test -f "$f" || continue
+        . "$f"
+done
+
+# Select peers
+p_opt=""
+cfg=0
+while test x"${ntpip[$cfg]}" != x""; do
+        p_opt="$p_opt -p ${ntpip[$cfg]}"
+        let cfg=cfg+1
+done
+test x"$p_opt" == x"" && p_opt="$default_p_opt"
+
+if test x"$p_opt" == x""; then
+	echo "* No NTP peers configured, stopping"
+	sv o .
+	exec sleep 1
+fi
+
+
+# Let others know that we are up
+date '+%Y-%m-%d %H:%M:%S %Z' >rundir/up
+
+# Go go go
+echo "* Starting ntpd[$$]"
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+ntpd -ddnNl -S ./ntp.script $p_opt
diff --git a/busybox-1.19.3/examples/var_service/ntpd/w_log b/busybox-1.19.3/examples/var_service/ntpd/w_log
new file mode 100755
index 0000000..aa36ef1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/ntpd/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/busybox-1.19.3/examples/var_service/tftpd/log/run b/busybox-1.19.3/examples/var_service/tftpd/log/run
new file mode 100755
index 0000000..560d1b1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/tftpd/log/run
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+user=logger
+
+logdir="/var/log/service/`(cd ..;basename $PWD)`"
+mkdir -p "$logdir" 2>/dev/null
+chown -R "$user": "$logdir"
+chmod -R go-rwxst,u+rwX "$logdir"
+rm logdir
+ln -s "$logdir" logdir
+
+# make this dir accessible to logger
+chmod a+rX .
+
+exec >/dev/null
+exec 2>&1
+exec \
+env - PATH="$PATH" \
+softlimit \
+setuidgid "$user" \
+svlogd -tt "$logdir"
diff --git a/busybox-1.19.3/examples/var_service/tftpd/p_log b/busybox-1.19.3/examples/var_service/tftpd/p_log
new file mode 100755
index 0000000..a2521be
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/tftpd/p_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+cat @* current | $PAGER
diff --git a/busybox-1.19.3/examples/var_service/tftpd/run b/busybox-1.19.3/examples/var_service/tftpd/run
new file mode 100755
index 0000000..e492d84
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/tftpd/run
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+#exec >/dev/null
+exec 2>&1
+exec </dev/null
+
+user=root # for bind to port 69
+
+exec \
+env - \
+softlimit \
+setuidgid "$user" \
+udpsvd -v -c 10 -l localhost \
+  0 69 \
+tftpd /pub/tftpd_root
diff --git a/busybox-1.19.3/examples/var_service/tftpd/w_log b/busybox-1.19.3/examples/var_service/tftpd/w_log
new file mode 100755
index 0000000..aa36ef1
--- /dev/null
+++ b/busybox-1.19.3/examples/var_service/tftpd/w_log
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd log/logdir || exit 1
+watch -n2 'w=`ttysize w`; h=`ttysize h`; tail -$((h-3)) current 2>&1 | cut -b1-$((w-2))'
diff --git a/busybox-1.19.3/examples/zcip.script b/busybox-1.19.3/examples/zcip.script
new file mode 100755
index 0000000..e543c30
--- /dev/null
+++ b/busybox-1.19.3/examples/zcip.script
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# only for use as a "zcip" callback script
+if [ "x$interface" = x ]
+then
+	exit 1
+fi
+
+# zcip should start on boot/resume and various media changes
+case "$1" in
+init)
+	# for now, zcip requires the link to be already up,
+	# and it drops links when they go down.  that isn't
+	# the most robust model...
+	exit 0
+	;;
+config)
+	if [ "x$ip" = x ]
+	then
+		exit 1
+	fi
+	# remember $ip for $interface, to use on restart
+	if [ "x$ip" != x -a -w "$ip.$interface" ]
+	then
+		echo $ip > "$ip.$interface"
+	fi
+	exec ip address add dev $interface \
+		scope link local "$ip/16" broadcast +
+	;;
+deconfig)
+	if [ x$ip = x ]
+	then
+		exit 1
+	fi
+	exec ip address del dev $interface local $ip
+	;;
+esac
+exit 1
diff --git a/busybox-1.19.3/findutils/Config.src b/busybox-1.19.3/findutils/Config.src
new file mode 100644
index 0000000..9ee71a8
--- /dev/null
+++ b/busybox-1.19.3/findutils/Config.src
@@ -0,0 +1,10 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Finding Utilities"
+
+INSERT
+
+endmenu
diff --git a/busybox-1.19.3/findutils/Kbuild.src b/busybox-1.19.3/findutils/Kbuild.src
new file mode 100644
index 0000000..6b4fb74
--- /dev/null
+++ b/busybox-1.19.3/findutils/Kbuild.src
@@ -0,0 +1,9 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
diff --git a/busybox-1.19.3/findutils/find.c b/busybox-1.19.3/findutils/find.c
new file mode 100644
index 0000000..fc0fc5c
--- /dev/null
+++ b/busybox-1.19.3/findutils/find.c
@@ -0,0 +1,1290 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini find implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Reworked by David Douthitt <n9ubh@callsign.net> and
+ *  Matt Kraai <kraai@alumni.carnegiemellon.edu>.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* findutils-4.1.20:
+ *
+ * # find file.txt -exec 'echo {}' '{}  {}' ';'
+ * find: echo file.txt: No such file or directory
+ * # find file.txt -exec 'echo' '{}  {}' '; '
+ * find: missing argument to `-exec'
+ * # find file.txt -exec 'echo {}' '{}  {}' ';' junk
+ * find: paths must precede expression
+ * # find file.txt -exec 'echo {}' '{}  {}' ';' junk ';'
+ * find: paths must precede expression
+ * # find file.txt -exec 'echo' '{}  {}' ';'
+ * file.txt  file.txt
+ * (strace: execve("/bin/echo", ["echo", "file.txt  file.txt"], [ 30 vars ]))
+ * # find file.txt -exec 'echo' '{}  {}' ';' -print -exec pwd ';'
+ * file.txt  file.txt
+ * file.txt
+ * /tmp
+ * # find -name '*.c' -o -name '*.h'
+ * [shows files, *.c and *.h intermixed]
+ * # find file.txt -name '*f*' -o -name '*t*'
+ * file.txt
+ * # find file.txt -name '*z*' -o -name '*t*'
+ * file.txt
+ * # find file.txt -name '*f*' -o -name '*z*'
+ * file.txt
+ *
+ * # find t z -name '*t*' -print -o -name '*z*'
+ * t
+ * # find t z t z -name '*t*' -o -name '*z*' -print
+ * z
+ * z
+ * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print
+ * (no output)
+ */
+
+/* Testing script
+ * ./busybox find "$@" | tee /tmp/bb_find
+ * echo ==================
+ * /path/to/gnu/find "$@" | tee /tmp/std_find
+ * echo ==================
+ * diff -u /tmp/std_find /tmp/bb_find && echo Identical
+ */
+
+//config:config FIND
+//config:	bool "find"
+//config:	default y
+//config:	help
+//config:	  find is used to search your system to find specified files.
+//config:
+//config:config FEATURE_FIND_PRINT0
+//config:	bool "Enable -print0: NUL-terminated output"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Causes output names to be separated by a NUL character
+//config:	  rather than a newline. This allows names that contain
+//config:	  newlines and other whitespace to be more easily
+//config:	  interpreted by other programs.
+//config:
+//config:config FEATURE_FIND_MTIME
+//config:	bool "Enable -mtime: modified time matching"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Allow searching based on the modification time of
+//config:	  files, in days.
+//config:
+//config:config FEATURE_FIND_MMIN
+//config:	bool "Enable -mmin: modified time matching by minutes"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Allow searching based on the modification time of
+//config:	  files, in minutes.
+//config:
+//config:config FEATURE_FIND_PERM
+//config:	bool "Enable -perm: permissions matching"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Enable searching based on file permissions.
+//config:
+//config:config FEATURE_FIND_TYPE
+//config:	bool "Enable -type: file type matching (file/dir/link/...)"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Enable searching based on file type (file,
+//config:	  directory, socket, device, etc.).
+//config:
+//config:config FEATURE_FIND_XDEV
+//config:	bool "Enable -xdev: 'stay in filesystem'"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  This option allows find to restrict searches to a single filesystem.
+//config:
+//config:config FEATURE_FIND_MAXDEPTH
+//config:	bool "Enable -mindepth N and -maxdepth N"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//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:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Support the 'find -newer' option for finding any files which have
+//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:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Support the 'find -inum' option for searching by inode number.
+//config:
+//config:config FEATURE_FIND_EXEC
+//config:	bool "Enable -exec: execute commands"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Support the 'find -exec' option for executing commands based upon
+//config:	  the files matched.
+//config:
+//config:config FEATURE_FIND_USER
+//config:	bool "Enable -user: username/uid matching"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Support the 'find -user' option for searching by username or uid.
+//config:
+//config:config FEATURE_FIND_GROUP
+//config:	bool "Enable -group: group/gid matching"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Support the 'find -group' option for searching by group name or gid.
+//config:
+//config:config FEATURE_FIND_NOT
+//config:	bool "Enable the 'not' (!) operator"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Support the '!' operator to invert the test results.
+//config:	  If 'Enable full-blown desktop' is enabled, then will also support
+//config:	  the non-POSIX notation '-not'.
+//config:
+//config:config FEATURE_FIND_DEPTH
+//config:	bool "Enable -depth"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Process each directory's contents before the directory itself.
+//config:
+//config:config FEATURE_FIND_PAREN
+//config:	bool "Enable parens in options"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Enable usage of parens '(' to specify logical order of arguments.
+//config:
+//config:config FEATURE_FIND_SIZE
+//config:	bool "Enable -size: file size matching"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  Support the 'find -size' option for searching by file size.
+//config:
+//config:config FEATURE_FIND_PRUNE
+//config:	bool "Enable -prune: exclude subdirectories"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  If the file is a directory, dont descend into it. Useful for
+//config:	  exclusion .svn and CVS directories.
+//config:
+//config:config FEATURE_FIND_DELETE
+//config:	bool "Enable -delete: delete files/dirs"
+//config:	default y
+//config:	depends on FIND && FEATURE_FIND_DEPTH
+//config:	help
+//config:	  Support the 'find -delete' option for deleting files and directories.
+//config:	  WARNING: This option can do much harm if used wrong. Busybox will not
+//config:	  try to protect the user from doing stupid things. Use with care.
+//config:
+//config:config FEATURE_FIND_PATH
+//config:	bool "Enable -path: match pathname with shell pattern"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  The -path option matches whole pathname instead of just filename.
+//config:
+//config:config FEATURE_FIND_REGEX
+//config:	bool "Enable -regex: match pathname with regex"
+//config:	default y
+//config:	depends on FIND
+//config:	help
+//config:	  The -regex option matches whole pathname against regular expression.
+//config:
+//config:config FEATURE_FIND_CONTEXT
+//config:	bool "Enable -context: security context matching"
+//config:	default n
+//config:	depends on FIND && SELINUX
+//config:	help
+//config:	  Support the 'find -context' option for matching security context.
+//config:
+//config:config FEATURE_FIND_LINKS
+//config:	bool "Enable -links: link count matching"
+//config:	default y
+//config:	depends on FIND
+//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:       "[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	-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			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"
+#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! */
+
+
+typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC;
+
+typedef struct {
+	action_fp f;
+#if ENABLE_FEATURE_FIND_NOT
+	bool invert;
+#endif
+} action;
+
+#define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name;
+#define ACTF(name) \
+	static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \
+		const struct stat *statbuf UNUSED_PARAM, \
+		action_##name* ap UNUSED_PARAM)
+
+                        ACTS(print)
+                        ACTS(name,  const char *pattern; bool iname;)
+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;))
+IF_FEATURE_FIND_PERM(   ACTS(perm,  char perm_char; mode_t perm_mask;))
+IF_FEATURE_FIND_MTIME(  ACTS(mtime, char mtime_char; unsigned mtime_days;))
+IF_FEATURE_FIND_MMIN(   ACTS(mmin,  char mmin_char; unsigned mmin_mins;))
+IF_FEATURE_FIND_NEWER(  ACTS(newer, time_t newer_mtime;))
+IF_FEATURE_FIND_INUM(   ACTS(inum,  ino_t inode_num;))
+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_PAREN(  ACTS(paren, action ***subexpr;))
+IF_FEATURE_FIND_PRUNE(  ACTS(prune))
+IF_FEATURE_FIND_DELETE( ACTS(delete))
+IF_FEATURE_FIND_EXEC(   ACTS(exec,  char **exec_argv; unsigned *subst_count; int exec_argc;))
+IF_FEATURE_FIND_GROUP(  ACTS(group, gid_t gid;))
+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;
+	smallint need_print;
+	smallint xdev_on;
+	recurse_flags_t recurse_flags;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	struct G_sizecheck { \
+		char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
+	}; \
+	/* we have to zero it out because of NOEXEC */ \
+	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)
+
+#if ENABLE_FEATURE_FIND_EXEC
+static unsigned count_subst(const char *str)
+{
+	unsigned count = 0;
+	while ((str = strstr(str, "{}")) != NULL) {
+		count++;
+		str++;
+	}
+	return count;
+}
+
+
+static char* subst(const char *src, unsigned count, const char* filename)
+{
+	char *buf, *dst, *end;
+	size_t flen = strlen(filename);
+	/* we replace each '{}' with filename: growth by strlen-2 */
+	buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
+	while ((end = strstr(src, "{}"))) {
+		memcpy(dst, src, end - src);
+		dst += end - src;
+		src = end + 2;
+		memcpy(dst, filename, flen);
+		dst += flen;
+	}
+	strcpy(dst, src);
+	return buf;
+}
+#endif
+
+/* Return values of ACTFs ('action functions') are a bit mask:
+ * bit 1=1: prune (use SKIP constant for setting it)
+ * bit 0=1: matched successfully (TRUE)
+ */
+
+static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf)
+{
+	int cur_group;
+	int cur_action;
+	int rc = 0;
+	action **app, *ap;
+
+	/* "action group" is a set of actions ANDed together.
+	 * groups are ORed together.
+	 * We simply evaluate each group until we find one in which all actions
+	 * succeed. */
+
+	/* -prune is special: if it is encountered, then we won't
+	 * descend into current directory. It doesn't matter whether
+	 * action group (in which -prune sits) will succeed or not:
+	 * find * -prune -name 'f*' -o -name 'm*' -- prunes every dir
+	 * find * -name 'f*' -o -prune -name 'm*' -- prunes all dirs
+	 *     not starting with 'f' */
+
+	/* We invert TRUE bit (bit 0). Now 1 there means 'failure'.
+	 * and bitwise OR in "rc |= TRUE ^ ap->f()" will:
+	 * (1) make SKIP (-prune) bit stick; and (2) detect 'failure'.
+	 * On return, bit is restored.  */
+
+	cur_group = -1;
+	while ((app = appp[++cur_group]) != NULL) {
+		rc &= ~TRUE; /* 'success' so far, clear TRUE bit */
+		cur_action = -1;
+		while (1) {
+			ap = app[++cur_action];
+			if (!ap) /* all actions in group were successful */
+				return rc ^ TRUE; /* restore TRUE bit */
+			rc |= TRUE ^ ap->f(fileName, statbuf, ap);
+#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);
+	if (tmp != fileName && *tmp == '\0') {
+		/* "foo/bar/". Oh no... go back to 'b' */
+		tmp--;
+		while (tmp != fileName && *--tmp != '/')
+			continue;
+		if (*tmp == '/')
+			tmp++;
+	}
+	/* Was using FNM_PERIOD flag too,
+	 * 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
+ACTF(regex)
+{
+	regmatch_t match;
+	if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/))
+		return 0; /* no match */
+	if (match.rm_so)
+		return 0; /* match doesn't start at pos 0 */
+	if (fileName[match.rm_eo])
+		return 0; /* match doesn't end exactly at end of pathname */
+	return 1;
+}
+#endif
+#if ENABLE_FEATURE_FIND_TYPE
+ACTF(type)
+{
+	return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
+}
+#endif
+#if ENABLE_FEATURE_FIND_PERM
+ACTF(perm)
+{
+	/* -perm +mode: at least one of perm_mask bits are set */
+	if (ap->perm_char == '+')
+		return (statbuf->st_mode & ap->perm_mask) != 0;
+	/* -perm -mode: all of perm_mask are set */
+	if (ap->perm_char == '-')
+		return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask;
+	/* -perm mode: file mode must match perm_mask */
+	return (statbuf->st_mode & 07777) == ap->perm_mask;
+}
+#endif
+#if ENABLE_FEATURE_FIND_MTIME
+ACTF(mtime)
+{
+	time_t file_age = time(NULL) - statbuf->st_mtime;
+	time_t mtime_secs = ap->mtime_days * 24*60*60;
+	if (ap->mtime_char == '+')
+		return file_age >= mtime_secs + 24*60*60;
+	if (ap->mtime_char == '-')
+		return file_age < mtime_secs;
+	/* just numeric mtime */
+	return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60);
+}
+#endif
+#if ENABLE_FEATURE_FIND_MMIN
+ACTF(mmin)
+{
+	time_t file_age = time(NULL) - statbuf->st_mtime;
+	time_t mmin_secs = ap->mmin_mins * 60;
+	if (ap->mmin_char == '+')
+		return file_age >= mmin_secs + 60;
+	if (ap->mmin_char == '-')
+		return file_age < mmin_secs;
+	/* just numeric mmin */
+	return file_age >= mmin_secs && file_age < (mmin_secs + 60);
+}
+#endif
+#if ENABLE_FEATURE_FIND_NEWER
+ACTF(newer)
+{
+	return (ap->newer_mtime < statbuf->st_mtime);
+}
+#endif
+#if ENABLE_FEATURE_FIND_INUM
+ACTF(inum)
+{
+	return (statbuf->st_ino == ap->inode_num);
+}
+#endif
+#if ENABLE_FEATURE_FIND_EXEC
+ACTF(exec)
+{
+	int i, rc;
+#if ENABLE_USE_PORTABLE_CODE
+	char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
+#else /* gcc 4.3.1 generates smaller code: */
+	char *argv[ap->exec_argc + 1];
+#endif
+	for (i = 0; i < ap->exec_argc; i++)
+		argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName);
+	argv[i] = NULL; /* terminate the list */
+
+	rc = spawn_and_wait(argv);
+	if (rc < 0)
+		bb_simple_perror_msg(argv[0]);
+
+	i = 0;
+	while (argv[i])
+		free(argv[i++]);
+	return rc == 0; /* return 1 if exitcode 0 */
+}
+#endif
+#if ENABLE_FEATURE_FIND_USER
+ACTF(user)
+{
+	return (statbuf->st_uid == ap->uid);
+}
+#endif
+#if ENABLE_FEATURE_FIND_GROUP
+ACTF(group)
+{
+	return (statbuf->st_gid == ap->gid);
+}
+#endif
+#if ENABLE_FEATURE_FIND_PRINT0
+ACTF(print0)
+{
+	printf("%s%c", fileName, '\0');
+	return TRUE;
+}
+#endif
+ACTF(print)
+{
+	puts(fileName);
+	return TRUE;
+}
+#if ENABLE_FEATURE_FIND_PAREN
+ACTF(paren)
+{
+	return exec_actions(ap->subexpr, fileName, statbuf);
+}
+#endif
+#if ENABLE_FEATURE_FIND_SIZE
+ACTF(size)
+{
+	if (ap->size_char == '+')
+		return statbuf->st_size > ap->size;
+	if (ap->size_char == '-')
+		return statbuf->st_size < ap->size;
+	return statbuf->st_size == ap->size;
+}
+#endif
+#if ENABLE_FEATURE_FIND_PRUNE
+/*
+ * -prune: if -depth is not given, return true and do not descend
+ * current dir; if -depth is given, return false with no effect.
+ * Example:
+ * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print
+ */
+ACTF(prune)
+{
+	return SKIP + TRUE;
+}
+#endif
+#if ENABLE_FEATURE_FIND_DELETE
+ACTF(delete)
+{
+	int rc;
+	if (S_ISDIR(statbuf->st_mode)) {
+		rc = rmdir(fileName);
+	} else {
+		rc = unlink(fileName);
+	}
+	if (rc < 0)
+		bb_simple_perror_msg(fileName);
+	return TRUE;
+}
+#endif
+#if ENABLE_FEATURE_FIND_CONTEXT
+ACTF(context)
+{
+	security_context_t con;
+	int rc;
+
+	if (G.recurse_flags & ACTION_FOLLOWLINKS) {
+		rc = getfilecon(fileName, &con);
+	} else {
+		rc = lgetfilecon(fileName, &con);
+	}
+	if (rc < 0)
+		return FALSE;
+	rc = strcmp(ap->context, con);
+	freecon(con);
+	return rc == 0;
+}
+#endif
+#if ENABLE_FEATURE_FIND_LINKS
+ACTF(links)
+{
+	switch(ap->links_char) {
+	case '-' : return (statbuf->st_nlink <  ap->links_count);
+	case '+' : return (statbuf->st_nlink >  ap->links_count);
+	default:   return (statbuf->st_nlink == ap->links_count);
+	}
+}
+#endif
+
+static int FAST_FUNC fileAction(const char *fileName,
+		struct stat *statbuf,
+		void *userData UNUSED_PARAM,
+		int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
+{
+	int r;
+
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+	if (depth < G.minmaxdepth[0])
+		return TRUE; /* skip this, continue recursing */
+	if (depth > G.minmaxdepth[1])
+		return SKIP; /* stop recursing */
+#endif
+
+	r = exec_actions(G.actions, fileName, statbuf);
+	/* Had no explicit -print[0] or -exec? then print */
+	if ((r & TRUE) && G.need_print)
+		puts(fileName);
+
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+	if (S_ISDIR(statbuf->st_mode)) {
+		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: ;
+		}
+	}
+#endif
+
+	/* Cannot return 0: our caller, recursive_action(),
+	 * will perror() and skip dirs (if called on dir) */
+	return (r & SKIP) ? SKIP : TRUE;
+}
+
+
+#if ENABLE_FEATURE_FIND_TYPE
+static int find_type(const char *type)
+{
+	int mask = 0;
+
+	if (*type == 'b')
+		mask = S_IFBLK;
+	else if (*type == 'c')
+		mask = S_IFCHR;
+	else if (*type == 'd')
+		mask = S_IFDIR;
+	else if (*type == 'p')
+		mask = S_IFIFO;
+	else if (*type == 'f')
+		mask = S_IFREG;
+	else if (*type == 'l')
+		mask = S_IFLNK;
+	else if (*type == 's')
+		mask = S_IFSOCK;
+
+	if (mask == 0 || type[1] != '\0')
+		bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
+
+	return mask;
+}
+#endif
+
+#if ENABLE_FEATURE_FIND_PERM \
+ || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \
+ || ENABLE_FEATURE_FIND_SIZE  || ENABLE_FEATURE_FIND_LINKS
+static const char* plus_minus_num(const char* str)
+{
+	if (*str == '-' || *str == '+')
+		str++;
+	return str;
+}
+#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  ,)
+#if ENABLE_DESKTOP
+	                        PARM_and       ,
+	                        PARM_or        ,
+	IF_FEATURE_FIND_NOT(    PARM_not       ,)
+#endif
+	                        PARM_print     ,
+	IF_FEATURE_FIND_PRINT0( PARM_print0    ,)
+	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/actions starting from here require argument */
+	                        PARM_name      ,
+	                        PARM_iname     ,
+	IF_FEATURE_FIND_PATH(   PARM_path      ,)
+	IF_FEATURE_FIND_PATH(   PARM_ipath     ,)
+	IF_FEATURE_FIND_REGEX(  PARM_regex     ,)
+	IF_FEATURE_FIND_TYPE(   PARM_type      ,)
+	IF_FEATURE_FIND_PERM(   PARM_perm      ,)
+	IF_FEATURE_FIND_MTIME(  PARM_mtime     ,)
+	IF_FEATURE_FIND_MMIN(   PARM_mmin      ,)
+	IF_FEATURE_FIND_NEWER(  PARM_newer     ,)
+	IF_FEATURE_FIND_INUM(   PARM_inum      ,)
+	IF_FEATURE_FIND_USER(   PARM_user      ,)
+	IF_FEATURE_FIND_GROUP(  PARM_group     ,)
+	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 =
+	                        "-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"    )
+#endif
+	                        "-print\0"
+	IF_FEATURE_FIND_PRINT0( "-print0\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/actions starting from here require argument */
+	                         "-name\0"
+	                         "-iname\0"
+	IF_FEATURE_FIND_PATH(   "-path\0"   )
+	IF_FEATURE_FIND_PATH(   "-ipath\0"  )
+	IF_FEATURE_FIND_REGEX(  "-regex\0"  )
+	IF_FEATURE_FIND_TYPE(   "-type\0"   )
+	IF_FEATURE_FIND_PERM(   "-perm\0"   )
+	IF_FEATURE_FIND_MTIME(  "-mtime\0"  )
+	IF_FEATURE_FIND_MMIN(   "-mmin\0"   )
+	IF_FEATURE_FIND_NEWER(  "-newer\0"  )
+	IF_FEATURE_FIND_INUM(   "-inum\0"   )
+	IF_FEATURE_FIND_USER(   "-user\0"   )
+	IF_FEATURE_FIND_GROUP(  "-group\0"  )
+	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")
+	;
+
+	action*** appp;
+	unsigned cur_group = 0;
+	unsigned cur_action = 0;
+	IF_FEATURE_FIND_NOT( bool invert_flag = 0; )
+
+	/* This is the only place in busybox where we use nested function.
+	 * So far more standard alternatives were bigger. */
+	/* 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 = 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)
+
+	appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
+
+	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/actions starting from -name require argument */
+			if (!arg1)
+				bb_error_msg_and_die(bb_msg_requires_arg, arg);
+			argv++;
+		}
+
+		/* We can use big switch() here, but on i386
+		 * it doesn't give smaller code. Other arches? */
+
+/* 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[cur_group] = NULL; - already NULL */
+			appp[cur_group+1] = NULL;
+			cur_action = 0;
+		}
+#if ENABLE_FEATURE_FIND_NOT
+		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
+		/* Actions */
+		else if (parm == PARM_print) {
+			dbg("%d", __LINE__);
+			G.need_print = 0;
+			(void) ALLOC_ACTION(print);
+		}
+#if ENABLE_FEATURE_FIND_PRINT0
+		else if (parm == PARM_print0) {
+			dbg("%d", __LINE__);
+			G.need_print = 0;
+			(void) ALLOC_ACTION(print0);
+		}
+#endif
+#if ENABLE_FEATURE_FIND_PRUNE
+		else if (parm == PARM_prune) {
+			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);
+		}
+#endif
+#if ENABLE_FEATURE_FIND_EXEC
+		else if (parm == PARM_exec) {
+			int i;
+			action_exec *ap;
+			dbg("%d", __LINE__);
+			G.need_print = 0;
+			ap = ALLOC_ACTION(exec);
+			ap->exec_argv = ++argv; /* first arg after -exec */
+			/*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");
+				// 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;
+				}
+				argv++;
+				ap->exec_argc++;
+			}
+			if (ap->exec_argc == 0)
+				bb_error_msg_and_die(bb_msg_requires_arg, arg);
+			ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
+			i = ap->exec_argc;
+			while (i--)
+				ap->subst_count[i] = count_subst(ap->exec_argv[i]);
+		}
+#endif
+#if ENABLE_FEATURE_FIND_PAREN
+		else if (parm == PARM_char_brace) {
+			action_paren *ap;
+			char **endarg;
+			unsigned nested = 1;
+
+			dbg("%d", __LINE__);
+			endarg = argv;
+			while (1) {
+				if (!*++endarg)
+					bb_error_msg_and_die("unpaired '('");
+				if (LONE_CHAR(*endarg, '('))
+					nested++;
+				else if (LONE_CHAR(*endarg, ')') && !--nested) {
+					*endarg = NULL;
+					break;
+				}
+			}
+			ap = ALLOC_ACTION(paren);
+			ap->subexpr = parse_params(argv + 1);
+			*endarg = (char*) ")"; /* restore NULLed parameter */
+			argv = endarg;
+		}
+#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 || 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*/);
+		}
+#endif
+#if ENABLE_FEATURE_FIND_TYPE
+		else if (parm == PARM_type) {
+			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 BITS   File's mode bits are exactly BITS (octal or symbolic).
+ *              Symbolic modes use mode 0 as a point of departure.
+ * -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; - ALLOC_ACTION did it */
+			if (!bb_parse_mode(arg1, &ap->perm_mask))
+				bb_error_msg_and_die("invalid mode '%s'", arg1);
+		}
+#endif
+#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));
+		}
+#endif
+#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));
+		}
+#endif
+#if ENABLE_FEATURE_FIND_NEWER
+		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;
+		}
+#endif
+#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);
+		}
+#endif
+#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)
+				ap->uid = xuname2uid(arg1);
+		}
+#endif
+#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)
+				ap->gid = xgroup2gid(arg1);
+		}
+#endif
+#if ENABLE_FEATURE_FIND_SIZE
+		else if (parm == PARM_size) {
+/* -size n[bckw]: file uses n units of space
+ * b (default): units are 512-byte blocks
+ * c: 1 byte
+ * k: kilobytes
+ * w: 2-byte words
+ */
+#if ENABLE_LFS
+#define XATOU_SFX xatoull_sfx
+#else
+#define XATOU_SFX xatoul_sfx
+#endif
+			static const struct suffix_mult find_suffixes[] = {
+				{ "c", 1 },
+				{ "w", 2 },
+				{ "", 512 },
+				{ "b", 512 },
+				{ "k", 1024 },
+				{ "", 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);
+		}
+#endif
+#if ENABLE_FEATURE_FIND_CONTEXT
+		else if (parm == PARM_context) {
+			action_context *ap;
+			dbg("%d", __LINE__);
+			ap = ALLOC_ACTION(context);
+			/*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);
+		}
+#endif
+#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));
+		}
+#endif
+		else {
+			bb_error_msg("unrecognized: %s", arg);
+			bb_show_usage();
+		}
+		argv++;
+	}
+	dbg("exiting %s", __func__);
+	return appp;
+#undef ALLOC_ACTION
+}
+
+int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int find_main(int argc UNUSED_PARAM, char **argv)
+{
+	int i, firstopt, status = EXIT_SUCCESS;
+
+	INIT_G();
+
+	argv++;
+	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 && LONE_CHAR(argv[firstopt], '('))
+			break;
+	}
+	if (firstopt == 0) {
+		*--argv = (char*)".";
+		firstopt++;
+	}
+
+	G.actions = parse_params(&argv[firstopt]);
+	argv[firstopt] = NULL;
+
+#if ENABLE_FEATURE_FIND_XDEV
+	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
+
+	for (i = 0; argv[i]; i++) {
+		if (!recursive_action(argv[i],
+				G.recurse_flags,/* flags */
+				fileAction,     /* file action */
+				fileAction,     /* dir action */
+				NULL,           /* user data */
+				0)              /* depth */
+		) {
+			status = EXIT_FAILURE;
+		}
+	}
+
+	return status;
+}
diff --git a/busybox-1.19.3/findutils/grep.c b/busybox-1.19.3/findutils/grep.c
new file mode 100644
index 0000000..5f42242
--- /dev/null
+++ b/busybox-1.19.3/findutils/grep.c
@@ -0,0 +1,781 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini grep implementation for busybox using libc regex.
+ *
+ * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
+ * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
+ *
+ * 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.  */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */
+/*
+ * 2004,2006 (C) Vladimir Oleynik <dzo@simtreas.ru> -
+ * correction "-e pattern1 -e pattern2" logic and more optimizations.
+ * precompiled regex
+ *
+ * (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))
+
+//kbuild:lib-$(CONFIG_GREP) += grep.o
+
+//config:config GREP
+//config:	bool "grep"
+//config:	default y
+//config:	help
+//config:	  grep is used to search files for a specified pattern.
+//config:
+//config:config FEATURE_GREP_EGREP_ALIAS
+//config:	bool "Enable extended regular expressions (egrep & grep -E)"
+//config:	default y
+//config:	depends on GREP
+//config:	help
+//config:	  Enabled support for extended regular expressions. Extended
+//config:	  regular expressions allow for alternation (foo|bar), grouping,
+//config:	  and various repetition operators.
+//config:
+//config:config FEATURE_GREP_FGREP_ALIAS
+//config:	bool "Alias fgrep to grep -F"
+//config:	default y
+//config:	depends on GREP
+//config:	help
+//config:	  fgrep sees the search pattern as a normal string rather than
+//config:	  regular expressions.
+//config:	  grep -F always works, this just creates the fgrep alias.
+//config:
+//config:config FEATURE_GREP_CONTEXT
+//config:	bool "Enable before and after context flags (-A, -B and -C)"
+//config:	default y
+//config:	depends on GREP
+//config:	help
+//config:	  Print the specified number of leading (-B) and/or trailing (-A)
+//config:	  context surrounding our matching lines.
+//config:	  Print the specified number of context lines (-C).
+
+#include "libbb.h"
+#include "xregex.h"
+
+
+/* options */
+//usage:#define grep_trivial_usage
+//usage:       "[-HhnlLoqvsriw"
+//usage:       "F"
+//usage:	IF_FEATURE_GREP_EGREP_ALIAS("E")
+//usage:	IF_EXTRA_COMPAT("z")
+//usage:       "] [-m N] "
+//usage:	IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ")
+//usage:       "PATTERN/-e PATTERN.../-f FILE [FILE]..."
+//usage:#define grep_full_usage "\n\n"
+//usage:       "Search for PATTERN in FILEs (or stdin)\n"
+//usage:     "\n	-H	Add 'filename:' prefix"
+//usage:     "\n	-h	Do not add 'filename:' prefix"
+//usage:     "\n	-n	Add 'line_no:' prefix"
+//usage:     "\n	-l	Show only names of files that match"
+//usage:     "\n	-L	Show only names of files that don't match"
+//usage:     "\n	-c	Show only count of matching lines"
+//usage:     "\n	-o	Show only the matching part of line"
+//usage:     "\n	-q	Quiet. Return 0 if PATTERN is found, 1 otherwise"
+//usage:     "\n	-v	Select non-matching lines"
+//usage:     "\n	-s	Suppress open and read errors"
+//usage:     "\n	-r	Recurse"
+//usage:     "\n	-i	Ignore case"
+//usage:     "\n	-w	Match whole words 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:	)
+//usage:	IF_EXTRA_COMPAT(
+//usage:     "\n	-z	Input is NUL terminated"
+//usage:	)
+//usage:     "\n	-m N	Match up to N times per file"
+//usage:	IF_FEATURE_GREP_CONTEXT(
+//usage:     "\n	-A N	Print N lines of trailing context"
+//usage:     "\n	-B N	Print N lines of leading context"
+//usage:     "\n	-C N	Same as '-A N -B N'"
+//usage:	)
+//usage:     "\n	-e PTRN	Pattern to match"
+//usage:     "\n	-f FILE	Read pattern from file"
+//usage:
+//usage:#define grep_example_usage
+//usage:       "$ grep root /etc/passwd\n"
+//usage:       "root:x:0:0:root:/root:/bin/bash\n"
+//usage:       "$ grep ^[rR]oo. /etc/passwd\n"
+//usage:       "root:x:0:0:root:/root:/bin/bash\n"
+//usage:
+//usage:#define egrep_trivial_usage NOUSAGE_STR
+//usage:#define egrep_full_usage ""
+//usage:#define fgrep_trivial_usage NOUSAGE_STR
+//usage:#define fgrep_full_usage ""
+
+#define OPTSTR_GREP \
+	"lnqvscFiHhe:f:Lorm:w" \
+	IF_FEATURE_GREP_CONTEXT("A:B:C:") \
+	IF_FEATURE_GREP_EGREP_ALIAS("E") \
+	IF_EXTRA_COMPAT("z") \
+	"aI"
+/* ignored: -a "assume all files to be text" */
+/* ignored: -I "assume binary files have no matches" */
+enum {
+	OPTBIT_l, /* list matched file names only */
+	OPTBIT_n, /* print line# */
+	OPTBIT_q, /* quiet - exit(EXIT_SUCCESS) of first match */
+	OPTBIT_v, /* invert the match, to select non-matching lines */
+	OPTBIT_s, /* suppress errors about file open errors */
+	OPTBIT_c, /* count matches per file (suppresses normal output) */
+	OPTBIT_F, /* literal match */
+	OPTBIT_i, /* case-insensitive */
+	OPTBIT_H, /* force filename display */
+	OPTBIT_h, /* inhibit filename display */
+	OPTBIT_e, /* -e PATTERN */
+	OPTBIT_f, /* -f FILE_WITH_PATTERNS */
+	OPTBIT_L, /* list unmatched file names only */
+	OPTBIT_o, /* show only matching parts of lines */
+	OPTBIT_r, /* recurse dirs */
+	OPTBIT_m, /* -m MAX_MATCHES */
+	OPTBIT_w, /* -w whole word 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 */
+	IF_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */
+	IF_EXTRA_COMPAT(            OPTBIT_z ,) /* input is NUL terminated */
+	OPT_l = 1 << OPTBIT_l,
+	OPT_n = 1 << OPTBIT_n,
+	OPT_q = 1 << OPTBIT_q,
+	OPT_v = 1 << OPTBIT_v,
+	OPT_s = 1 << OPTBIT_s,
+	OPT_c = 1 << OPTBIT_c,
+	OPT_F = 1 << OPTBIT_F,
+	OPT_i = 1 << OPTBIT_i,
+	OPT_H = 1 << OPTBIT_H,
+	OPT_h = 1 << OPTBIT_h,
+	OPT_e = 1 << OPTBIT_e,
+	OPT_f = 1 << OPTBIT_f,
+	OPT_L = 1 << OPTBIT_L,
+	OPT_o = 1 << OPTBIT_o,
+	OPT_r = 1 << OPTBIT_r,
+	OPT_m = 1 << OPTBIT_m,
+	OPT_w = 1 << OPTBIT_w,
+	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,
+	OPT_E = IF_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0,
+	OPT_z = IF_EXTRA_COMPAT(            (1 << OPTBIT_z)) + 0,
+};
+
+#define PRINT_FILES_WITH_MATCHES    (option_mask32 & OPT_l)
+#define PRINT_LINE_NUM              (option_mask32 & OPT_n)
+#define BE_QUIET                    (option_mask32 & OPT_q)
+#define SUPPRESS_ERR_MSGS           (option_mask32 & OPT_s)
+#define PRINT_MATCH_COUNTS          (option_mask32 & OPT_c)
+#define FGREP_FLAG                  (option_mask32 & OPT_F)
+#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
+#define NUL_DELIMITED               (option_mask32 & OPT_z)
+
+struct globals {
+	int max_matches;
+#if !ENABLE_EXTRA_COMPAT
+	int reflags;
+#else
+	RE_TRANSLATE_TYPE case_fold; /* RE_TRANSLATE_TYPE is [[un]signed] char* */
+#endif
+	smalluint invert_search;
+	smalluint print_filename;
+	smalluint open_errors;
+#if ENABLE_FEATURE_GREP_CONTEXT
+	smalluint did_print_line;
+	int lines_before;
+	int lines_after;
+	char **before_buf;
+	IF_EXTRA_COMPAT(size_t *before_buf_size;)
+	int last_line_printed;
+#endif
+	/* globals used internally */
+	llist_t *pattern_head;   /* growable list of patterns to match */
+	const char *cur_file;    /* the current file we are reading */
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	struct G_sizecheck { \
+		char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
+	}; \
+} while (0)
+#define max_matches       (G.max_matches         )
+#if !ENABLE_EXTRA_COMPAT
+# define reflags          (G.reflags             )
+#else
+# define case_fold        (G.case_fold           )
+/* http://www.delorie.com/gnu/docs/regex/regex_46.html */
+# define reflags           re_syntax_options
+# undef REG_NOSUB
+# undef REG_EXTENDED
+# undef REG_ICASE
+# define REG_NOSUB    bug:is:here /* should not be used */
+/* Just RE_SYNTAX_EGREP is not enough, need to enable {n[,[m]]} too */
+# define REG_EXTENDED (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
+# define REG_ICASE    bug:is:here /* should not be used */
+#endif
+#define invert_search     (G.invert_search       )
+#define print_filename    (G.print_filename      )
+#define open_errors       (G.open_errors         )
+#define did_print_line    (G.did_print_line      )
+#define lines_before      (G.lines_before        )
+#define lines_after       (G.lines_after         )
+#define before_buf        (G.before_buf          )
+#define before_buf_size   (G.before_buf_size     )
+#define last_line_printed (G.last_line_printed   )
+#define pattern_head      (G.pattern_head        )
+#define cur_file          (G.cur_file            )
+
+
+typedef struct grep_list_data_t {
+	char *pattern;
+/* for GNU regex, matched_range must be persistent across grep_file() calls */
+#if !ENABLE_EXTRA_COMPAT
+	regex_t compiled_regex;
+	regmatch_t matched_range;
+#else
+	struct re_pattern_buffer compiled_regex;
+	struct re_registers matched_range;
+#endif
+#define ALLOCATED 1
+#define COMPILED 2
+	int flg_mem_alocated_compiled;
+} grep_list_data_t;
+
+#if !ENABLE_EXTRA_COMPAT
+#define print_line(line, line_len, linenum, decoration) \
+	print_line(line, linenum, decoration)
+#endif
+static void print_line(const char *line, size_t line_len, int linenum, char decoration)
+{
+#if ENABLE_FEATURE_GREP_CONTEXT
+	/* Happens when we go to next file, immediately hit match
+	 * and try to print prev context... from prev file! Don't do it */
+	if (linenum < 1)
+		return;
+	/* possibly print the little '--' separator */
+	if ((lines_before || lines_after) && did_print_line
+	 && last_line_printed != linenum - 1
+	) {
+		puts("--");
+	}
+	/* guard against printing "--" before first line of first file */
+	did_print_line = 1;
+	last_line_printed = linenum;
+#endif
+	if (print_filename)
+		printf("%s%c", cur_file, decoration);
+	if (PRINT_LINE_NUM)
+		printf("%i%c", linenum, decoration);
+	/* Emulate weird GNU grep behavior with -ov */
+	if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) {
+#if !ENABLE_EXTRA_COMPAT
+		puts(line);
+#else
+		fwrite(line, 1, line_len, stdout);
+		putchar(NUL_DELIMITED ? '\0' : '\n');
+#endif
+	}
+}
+
+#if ENABLE_EXTRA_COMPAT
+/* Unlike getline, this one removes trailing '\n' */
+static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file)
+{
+	ssize_t res_sz;
+	char *line;
+	int delim = (NUL_DELIMITED ? '\0' : '\n');
+
+	res_sz = getdelim(line_ptr, line_alloc_len, delim, file);
+	line = *line_ptr;
+
+	if (res_sz > 0) {
+		if (line[res_sz - 1] == delim)
+			line[--res_sz] = '\0';
+	} else {
+		free(line); /* uclibc allocates a buffer even on EOF. WTF? */
+	}
+	return res_sz;
+}
+#endif
+
+static int grep_file(FILE *file)
+{
+	smalluint found;
+	int linenum = 0;
+	int nmatches = 0;
+#if !ENABLE_EXTRA_COMPAT
+	char *line;
+#else
+	char *line = NULL;
+	ssize_t line_len;
+	size_t line_alloc_len;
+# define rm_so start[0]
+# define rm_eo end[0]
+#endif
+#if ENABLE_FEATURE_GREP_CONTEXT
+	int print_n_lines_after = 0;
+	int curpos = 0; /* track where we are in the circular 'before' buffer */
+	int idx = 0; /* used for iteration through the circular buffer */
+#else
+	enum { print_n_lines_after = 0 };
+#endif
+
+	while (
+#if !ENABLE_EXTRA_COMPAT
+		(line = xmalloc_fgetline(file)) != NULL
+#else
+		(line_len = bb_getline(&line, &line_alloc_len, file)) >= 0
+#endif
+	) {
+		llist_t *pattern_ptr = pattern_head;
+		grep_list_data_t *gl = gl; /* for gcc */
+
+		linenum++;
+		found = 0;
+		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);
+			} else {
+				if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
+					gl->flg_mem_alocated_compiled |= COMPILED;
+#if !ENABLE_EXTRA_COMPAT
+					xregcomp(&gl->compiled_regex, gl->pattern, reflags);
+#else
+					memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex));
+					gl->compiled_regex.translate = case_fold; /* for -i */
+					if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex))
+						bb_error_msg_and_die("bad regex '%s'", gl->pattern);
+#endif
+				}
+#if !ENABLE_EXTRA_COMPAT
+				gl->matched_range.rm_so = 0;
+				gl->matched_range.rm_eo = 0;
+#endif
+				if (
+#if !ENABLE_EXTRA_COMPAT
+					regexec(&gl->compiled_regex, line, 1, &gl->matched_range, 0) == 0
+#else
+					re_search(&gl->compiled_regex, line, line_len,
+							/*start:*/ 0, /*range:*/ line_len,
+							&gl->matched_range) >= 0
+#endif
+				) {
+					if (!(option_mask32 & OPT_w))
+						found = 1;
+					else {
+						char c = ' ';
+						if (gl->matched_range.rm_so)
+							c = line[gl->matched_range.rm_so - 1];
+						if (!isalnum(c) && c != '_') {
+							c = line[gl->matched_range.rm_eo];
+							if (!c || (!isalnum(c) && c != '_'))
+								found = 1;
+						}
+					}
+				}
+			}
+			/* If it's non-inverted search, we can stop
+			 * at first match */
+			if (found && !invert_search)
+				goto do_found;
+			pattern_ptr = pattern_ptr->link;
+		} /* while (pattern_ptr) */
+
+		if (found ^ invert_search) {
+ do_found:
+			/* keep track of matches */
+			nmatches++;
+
+			/* quiet/print (non)matching file names only? */
+			if (option_mask32 & (OPT_q|OPT_l|OPT_L)) {
+				free(line); /* we don't need line anymore */
+				if (BE_QUIET) {
+					/* manpage says about -q:
+					 * "exit immediately with zero status
+					 * if any match is found,
+					 * even if errors were detected" */
+					exit(EXIT_SUCCESS);
+				}
+				/* if we're just printing filenames, we stop after the first match */
+				if (PRINT_FILES_WITH_MATCHES) {
+					puts(cur_file);
+					/* fall through to "return 1" */
+				}
+				/* OPT_L aka PRINT_FILES_WITHOUT_MATCHES: return early */
+				return 1; /* one match */
+			}
+
+#if ENABLE_FEATURE_GREP_CONTEXT
+			/* Were we printing context and saw next (unwanted) match? */
+			if ((option_mask32 & OPT_m) && nmatches > max_matches)
+				break;
+#endif
+
+			/* print the matched line */
+			if (PRINT_MATCH_COUNTS == 0) {
+#if ENABLE_FEATURE_GREP_CONTEXT
+				int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
+
+				/* if we were told to print 'before' lines and there is at least
+				 * one line in the circular buffer, print them */
+				if (lines_before && before_buf[prevpos] != NULL) {
+					int first_buf_entry_line_num = linenum - lines_before;
+
+					/* advance to the first entry in the circular buffer, and
+					 * figure out the line number is of the first line in the
+					 * buffer */
+					idx = curpos;
+					while (before_buf[idx] == NULL) {
+						idx = (idx + 1) % lines_before;
+						first_buf_entry_line_num++;
+					}
+
+					/* now print each line in the buffer, clearing them as we go */
+					while (before_buf[idx] != NULL) {
+						print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-');
+						free(before_buf[idx]);
+						before_buf[idx] = NULL;
+						idx = (idx + 1) % lines_before;
+						first_buf_entry_line_num++;
+					}
+				}
+
+				/* make a note that we need to print 'after' lines */
+				print_n_lines_after = lines_after;
+#endif
+				if (option_mask32 & OPT_o) {
+					if (FGREP_FLAG) {
+						/* -Fo just prints the pattern
+						 * (unless -v: -Fov doesnt print anything at all) */
+						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';
+						/* 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)
+							break;
+						gl->matched_range.rm_so += end;
+						gl->matched_range.rm_eo += end;
+#else
+						if (re_search(&gl->compiled_regex, line, line_len,
+								end, line_len - end,
+								&gl->matched_range) < 0)
+							break;
+#endif
+					}
+				} else {
+					print_line(line, line_len, linenum, ':');
+				}
+			}
+		}
+#if ENABLE_FEATURE_GREP_CONTEXT
+		else { /* no match */
+			/* if we need to print some context lines after the last match, do so */
+			if (print_n_lines_after) {
+				print_line(line, strlen(line), linenum, '-');
+				print_n_lines_after--;
+			} else if (lines_before) {
+				/* Add the line to the circular 'before' buffer */
+				free(before_buf[curpos]);
+				before_buf[curpos] = line;
+				IF_EXTRA_COMPAT(before_buf_size[curpos] = line_len;)
+				curpos = (curpos + 1) % lines_before;
+				/* avoid free(line) - we took the line */
+				line = NULL;
+			}
+		}
+
+#endif /* ENABLE_FEATURE_GREP_CONTEXT */
+#if !ENABLE_EXTRA_COMPAT
+		free(line);
+#endif
+		/* Did we print all context after last requested match? */
+		if ((option_mask32 & OPT_m)
+		 && !print_n_lines_after
+		 && nmatches == max_matches
+		) {
+			break;
+		}
+	} /* while (read line) */
+
+	/* special-case file post-processing for options where we don't print line
+	 * matches, just filenames and possibly match counts */
+
+	/* grep -c: print [filename:]count, even if count is zero */
+	if (PRINT_MATCH_COUNTS) {
+		if (print_filename)
+			printf("%s:", cur_file);
+		printf("%d\n", nmatches);
+	}
+
+	/* grep -L: print just the filename */
+	if (PRINT_FILES_WITHOUT_MATCHES) {
+		/* nmatches is zero, no need to check it:
+		 * we return 1 early if we detected a match
+		 * and PRINT_FILES_WITHOUT_MATCHES is set */
+		puts(cur_file);
+	}
+
+	return nmatches;
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+#define new_grep_list_data(p, m) add_grep_list_data(p, m)
+static char *add_grep_list_data(char *pattern, int flg_used_mem)
+#else
+#define new_grep_list_data(p, m) add_grep_list_data(p)
+static char *add_grep_list_data(char *pattern)
+#endif
+{
+	grep_list_data_t *gl = xzalloc(sizeof(*gl));
+	gl->pattern = pattern;
+#if ENABLE_FEATURE_CLEAN_UP
+	gl->flg_mem_alocated_compiled = flg_used_mem;
+#else
+	/*gl->flg_mem_alocated_compiled = 0;*/
+#endif
+	return (char *)gl;
+}
+
+static void load_regexes_from_file(llist_t *fopt)
+{
+	while (fopt) {
+		char *line;
+		FILE *fp;
+		llist_t *cur = fopt;
+		char *ffile = cur->data;
+
+		fopt = cur->link;
+		free(cur);
+		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);
+	}
+}
+
+static int FAST_FUNC file_action_grep(const char *filename,
+			struct stat *statbuf UNUSED_PARAM,
+			void* matched,
+			int depth UNUSED_PARAM)
+{
+	FILE *file = fopen_for_read(filename);
+	if (file == NULL) {
+		if (!SUPPRESS_ERR_MSGS)
+			bb_simple_perror_msg(filename);
+		open_errors = 1;
+		return 0;
+	}
+	cur_file = filename;
+	*(int*)matched += grep_file(file);
+	fclose(file);
+	return 1;
+}
+
+static int grep_dir(const char *dir)
+{
+	int matched = 0;
+	recursive_action(dir,
+		/* recurse=yes */ ACTION_RECURSE |
+		/* followLinks=no */
+		/* depthFirst=yes */ ACTION_DEPTHFIRST,
+		/* fileAction= */ file_action_grep,
+		/* dirAction= */ NULL,
+		/* userData= */ &matched,
+		/* depth= */ 0);
+	return matched;
+}
+
+int grep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int grep_main(int argc UNUSED_PARAM, char **argv)
+{
+	FILE *file;
+	int matched;
+	llist_t *fopt = NULL;
+
+	/* do normal option parsing */
+#if ENABLE_FEATURE_GREP_CONTEXT
+	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+";
+	opts = getopt32(argv,
+		OPTSTR_GREP,
+		&pattern_head, &fopt, &max_matches,
+		&lines_after, &lines_before, &Copt);
+
+	if (opts & OPT_C) {
+		/* -C unsets prev -A and -B, but following -A or -B
+		   may override it */
+		if (!(opts & OPT_A)) /* not overridden */
+			lines_after = Copt;
+		if (!(opts & OPT_B)) /* not overridden */
+			lines_before = Copt;
+	}
+	/* sanity checks */
+	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]));)
+	}
+#else
+	/* with auto sanity checks */
+	/* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */
+	opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+";
+	getopt32(argv, OPTSTR_GREP,
+		&pattern_head, &fopt, &max_matches);
+#endif
+	invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
+
+	{	/* 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) {
+		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)))
+		reflags = REG_NOSUB;
+#endif
+
+	if (ENABLE_FEATURE_GREP_EGREP_ALIAS
+	 && (applet_name[0] == 'e' || (option_mask32 & OPT_E))
+	) {
+		reflags |= REG_EXTENDED;
+	}
+#if ENABLE_EXTRA_COMPAT
+	else {
+		reflags = RE_SYNTAX_GREP;
+	}
+#endif
+
+	if (option_mask32 & OPT_i) {
+#if !ENABLE_EXTRA_COMPAT
+		reflags |= REG_ICASE;
+#else
+		int i;
+		case_fold = xmalloc(256);
+		for (i = 0; i < 256; i++)
+			case_fold[i] = (unsigned char)i;
+		for (i = 'a'; i <= 'z'; i++)
+			case_fold[i] = (unsigned char)(i - ('a' - 'A'));
+#endif
+	}
+
+	argv += optind;
+
+	/* if we didn't get a pattern from -e and no command file was specified,
+	 * first parameter should be the pattern. no pattern, no worky */
+	if (pattern_head == NULL) {
+		char *pattern;
+		if (*argv == NULL)
+			bb_show_usage();
+		pattern = new_grep_list_data(*argv++, 0);
+		llist_add_to(&pattern_head, pattern);
+	}
+
+	/* argv[0..(argc-1)] should be names of file to grep through. If
+	 * there is more than one file to grep, we will print the filenames. */
+	if (argv[0] && argv[1])
+		print_filename = 1;
+	/* -H / -h of course override */
+	if (option_mask32 & OPT_H)
+		print_filename = 1;
+	if (option_mask32 & OPT_h)
+		print_filename = 0;
+
+	/* If no files were specified, or '-' was specified, take input from
+	 * stdin. Otherwise, we grep through all the files specified. */
+	matched = 0;
+	do {
+		cur_file = *argv;
+		file = stdin;
+		if (!cur_file || LONE_DASH(cur_file)) {
+			cur_file = "(standard input)";
+		} else {
+			if (option_mask32 & OPT_r) {
+				struct stat st;
+				if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
+					if (!(option_mask32 & OPT_h))
+						print_filename = 1;
+					matched += grep_dir(cur_file);
+					goto grep_done;
+				}
+			}
+			/* else: fopen(dir) will succeed, but reading won't */
+			file = fopen_for_read(cur_file);
+			if (file == NULL) {
+				if (!SUPPRESS_ERR_MSGS)
+					bb_simple_perror_msg(cur_file);
+				open_errors = 1;
+				continue;
+			}
+		}
+		matched += grep_file(file);
+		fclose_if_not_stdin(file);
+ grep_done: ;
+	} while (*argv && *++argv);
+
+	/* destroy all the elments in the pattern list */
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		while (pattern_head) {
+			llist_t *pattern_head_ptr = pattern_head;
+			grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
+
+			pattern_head = pattern_head->link;
+			if (gl->flg_mem_alocated_compiled & ALLOCATED)
+				free(gl->pattern);
+			if (gl->flg_mem_alocated_compiled & COMPILED)
+				regfree(&gl->compiled_regex);
+			free(gl);
+			free(pattern_head_ptr);
+		}
+	}
+	/* 0 = success, 1 = failed, 2 = error */
+	if (open_errors)
+		return 2;
+	return !matched; /* invert return value: 0 = success, 1 = failed */
+}
diff --git a/busybox-1.19.3/findutils/xargs.c b/busybox-1.19.3/findutils/xargs.c
new file mode 100644
index 0000000..0d1bb43
--- /dev/null
+++ b/busybox-1.19.3/findutils/xargs.c
@@ -0,0 +1,565 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini xargs implementation for busybox
+ *
+ * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Special thanks
+ * - Mark Whitley and Glenn McGrath for stimulus to rewrite :)
+ * - Mike Rendell <michael@cs.mun.ca>
+ * and David MacKenzie <djm@gnu.ai.mit.edu>.
+ *
+ * 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
+ */
+
+//config:config XARGS
+//config:	bool "xargs"
+//config:	default y
+//config:	help
+//config:	  xargs is used to execute a specified command for
+//config:	  every item from standard input.
+//config:
+//config:config FEATURE_XARGS_SUPPORT_CONFIRMATION
+//config:	bool "Enable -p: prompt and confirmation"
+//config:	default y
+//config:	depends on XARGS
+//config:	help
+//config:	  Support -p: prompt the user whether to run each command
+//config:	  line and read a line from the terminal.
+//config:
+//config:config FEATURE_XARGS_SUPPORT_QUOTES
+//config:	bool "Enable single and double quotes and backslash"
+//config:	default y
+//config:	depends on XARGS
+//config:	help
+//config:	  Support quoting in the input.
+//config:
+//config:config FEATURE_XARGS_SUPPORT_TERMOPT
+//config:	bool "Enable -x: exit if -s or -n is exceeded"
+//config:	default y
+//config:	depends on XARGS
+//config:	help
+//config:	  Support -x: exit if the command size (see the -s or -n option)
+//config:	  is exceeded.
+//config:
+//config:config FEATURE_XARGS_SUPPORT_ZERO_TERM
+//config:	bool "Enable -0: NUL-terminated input"
+//config:	default y
+//config:	depends on XARGS
+//config:	help
+//config:	  Support -0: input items are terminated by a NUL character
+//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! */
+
+
+//#define dbg_msg(...) bb_error_msg(__VA_ARGS__)
+#define dbg_msg(...) ((void)0)
+
+
+#ifdef TEST
+# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
+#  define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1
+# endif
+# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
+#  define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1
+# endif
+# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
+#  define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1
+# endif
+# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
+#  define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1
+# endif
+#endif
+
+
+struct globals {
+	char **args;
+	const char *eof_str;
+	int idx;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	G.eof_str = NULL; /* need to clear by hand because we are NOEXEC applet */ \
+} while (0)
+
+
+/*
+ * This function has special algorithm.
+ * Don't use fork and include to main!
+ */
+static int xargs_exec(void)
+{
+	int status;
+
+	status = spawn_and_wait(G.args);
+	if (status < 0) {
+		bb_simple_perror_msg(G.args[0]);
+		return errno == ENOENT ? 127 : 126;
+	}
+	if (status == 255) {
+		bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
+		return 124;
+	}
+	if (status >= 0x180) {
+		bb_error_msg("%s: terminated by signal %d",
+			G.args[0], status - 0x180);
+		return 125;
+	}
+	if (status)
+		return 123;
+	return 0;
+}
+
+/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
+ * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
+ */
+#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
+
+static void store_param(char *s)
+{
+	/* Grow by 256 elements at once */
+	if (!(G.idx & 0xff)) { /* G.idx == N*256 */
+		/* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */
+		G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
+	}
+	G.args[G.idx++] = s;
+}
+
+/* process[0]_stdin:
+ * Read characters into buf[n_max_chars+1], and when parameter delimiter
+ * is seen, store the address of a new parameter to args[].
+ * If reading discovers that last chars do not form the complete
+ * parameter, the pointer to the first such "tail character" is returned.
+ * (buf has extra byte at the end to accomodate terminating NUL
+ * of "tail characters" string).
+ * Otherwise, the returned pointer points to NUL byte.
+ * On entry, buf[] may contain some "seed chars" which are to become
+ * the beginning of the first parameter.
+ */
+
+#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
+static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
+{
+#define NORM      0
+#define QUOTE     1
+#define BACKSLASH 2
+#define SPACE     4
+	char q = '\0';             /* quote char */
+	char state = NORM;
+	char *s = buf;             /* start of the word */
+	char *p = s + strlen(buf); /* end of the word */
+
+	buf += n_max_chars;        /* past buffer's end */
+
+	/* "goto ret" is used instead of "break" to make control flow
+	 * more obvious: */
+
+	while (1) {
+		int c = getchar();
+		if (c == EOF) {
+			if (p != s)
+				goto close_word;
+			goto ret;
+		}
+		if (state == BACKSLASH) {
+			state = NORM;
+			goto set;
+		}
+		if (state == QUOTE) {
+			if (c != q)
+				goto set;
+			q = '\0';
+			state = NORM;
+		} else { /* if (state == NORM) */
+			if (ISSPACE(c)) {
+				if (p != s) {
+ close_word:
+					state = SPACE;
+					c = '\0';
+					goto set;
+				}
+			} else {
+				if (c == '\\') {
+					state = BACKSLASH;
+				} else if (c == '\'' || c == '"') {
+					q = c;
+					state = QUOTE;
+				} else {
+ set:
+					*p++ = c;
+				}
+			}
+		}
+		if (state == SPACE) {   /* word's delimiter or EOF detected */
+			if (q) {
+				bb_error_msg_and_die("unmatched %s quote",
+					q == '\'' ? "single" : "double");
+			}
+			/* A full word is loaded */
+			if (G.eof_str) {
+				if (strcmp(s, G.eof_str) == 0) {
+					while (getchar() != EOF)
+						continue;
+					p = s;
+					goto ret;
+				}
+			}
+			store_param(s);
+			dbg_msg("args[]:'%s'", s);
+			s = p;
+			n_max_arg--;
+			if (n_max_arg == 0) {
+				goto ret;
+			}
+			state = NORM;
+		}
+		if (p == buf) {
+			goto ret;
+		}
+	}
+ ret:
+	*p = '\0';
+	/* store_param(NULL) - caller will do it */
+	dbg_msg("return:'%s'", s);
+	return s;
+}
+#else
+/* The variant does not support single quotes, double quotes or backslash */
+static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
+{
+	char *s = buf;             /* start of the word */
+	char *p = s + strlen(buf); /* end of the word */
+
+	buf += n_max_chars;        /* past buffer's end */
+
+	while (1) {
+		int c = getchar();
+		if (c == EOF) {
+			if (p == s)
+				goto ret;
+		}
+		if (c == EOF || ISSPACE(c)) {
+			if (p == s)
+				continue;
+			c = EOF;
+		}
+		*p++ = (c == EOF ? '\0' : c);
+		if (c == EOF) { /* word's delimiter or EOF detected */
+			/* A full word is loaded */
+			if (G.eof_str) {
+				if (strcmp(s, G.eof_str) == 0) {
+					while (getchar() != EOF)
+						continue;
+					p = s;
+					goto ret;
+				}
+			}
+			store_param(s);
+			dbg_msg("args[]:'%s'", s);
+			s = p;
+			n_max_arg--;
+			if (n_max_arg == 0) {
+				goto ret;
+			}
+		}
+		if (p == buf) {
+			goto ret;
+		}
+	}
+ ret:
+	*p = '\0';
+	/* store_param(NULL) - caller will do it */
+	dbg_msg("return:'%s'", s);
+	return s;
+}
+#endif /* FEATURE_XARGS_SUPPORT_QUOTES */
+
+#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
+static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
+{
+	char *s = buf;             /* start of the word */
+	char *p = s + strlen(buf); /* end of the word */
+
+	buf += n_max_chars;        /* past buffer's end */
+
+	while (1) {
+		int c = getchar();
+		if (c == EOF) {
+			if (p == s)
+				goto ret;
+			c = '\0';
+		}
+		*p++ = c;
+		if (c == '\0') {   /* word's delimiter or EOF detected */
+			/* A full word is loaded */
+			store_param(s);
+			dbg_msg("args[]:'%s'", s);
+			s = p;
+			n_max_arg--;
+			if (n_max_arg == 0) {
+				goto ret;
+			}
+		}
+		if (p == buf) {
+			goto ret;
+		}
+	}
+ ret:
+	*p = '\0';
+	/* store_param(NULL) - caller will do it */
+	dbg_msg("return:'%s'", s);
+	return s;
+}
+#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
+
+#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
+/* Prompt the user for a response, and
+   if the user responds affirmatively, return true;
+   otherwise, return false. Uses "/dev/tty", not stdin. */
+static int xargs_ask_confirmation(void)
+{
+	FILE *tty_stream;
+	int c, savec;
+
+	tty_stream = xfopen_for_read(CURRENT_TTY);
+	fputs(" ?...", stderr);
+	fflush_all();
+	c = savec = getc(tty_stream);
+	while (c != EOF && c != '\n')
+		c = getc(tty_stream);
+	fclose(tty_stream);
+	return (savec == 'y' || savec == 'Y');
+}
+#else
+# define xargs_ask_confirmation() 1
+#endif
+
+//usage:#define xargs_trivial_usage
+//usage:       "[OPTIONS] [PROG ARGS]"
+//usage:#define xargs_full_usage "\n\n"
+//usage:       "Run PROG on every item given by stdin\n"
+//usage:	IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(
+//usage:     "\n	-p	Ask user whether to run each command"
+//usage:	)
+//usage:     "\n	-r	Don't run command if input is empty"
+//usage:	IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(
+//usage:     "\n	-0	Input is separated by NUL characters"
+//usage:	)
+//usage:     "\n	-t	Print the command on stderr before execution"
+//usage:     "\n	-e[STR]	STR stops input processing"
+//usage:     "\n	-n N	Pass no more than N args to PROG"
+//usage:     "\n	-s N	Pass command line of no more than N bytes"
+//usage:	IF_FEATURE_XARGS_SUPPORT_TERMOPT(
+//usage:     "\n	-x	Exit if size is exceeded"
+//usage:	)
+//usage:#define xargs_example_usage
+//usage:       "$ ls | xargs gzip\n"
+//usage:       "$ find . -name '*.c' -print | xargs rm\n"
+
+/* Correct regardless of combination of CONFIG_xxx */
+enum {
+	OPTBIT_VERBOSE = 0,
+	OPTBIT_NO_EMPTY,
+	OPTBIT_UPTO_NUMBER,
+	OPTBIT_UPTO_SIZE,
+	OPTBIT_EOF_STRING,
+	OPTBIT_EOF_STRING1,
+	IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
+	IF_FEATURE_XARGS_SUPPORT_TERMOPT(     OPTBIT_TERMINATE  ,)
+	IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   OPTBIT_ZEROTERM   ,)
+
+	OPT_VERBOSE     = 1 << OPTBIT_VERBOSE    ,
+	OPT_NO_EMPTY    = 1 << OPTBIT_NO_EMPTY   ,
+	OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER,
+	OPT_UPTO_SIZE   = 1 << OPTBIT_UPTO_SIZE  ,
+	OPT_EOF_STRING  = 1 << OPTBIT_EOF_STRING , /* GNU: -e[<param>] */
+	OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E<param> */
+	OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
+	OPT_TERMINATE   = IF_FEATURE_XARGS_SUPPORT_TERMOPT(     (1 << OPTBIT_TERMINATE  )) + 0,
+	OPT_ZEROTERM    = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   (1 << OPTBIT_ZEROTERM   )) + 0,
+};
+#define OPTION_STR "+trn:s:e::E:" \
+	IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
+	IF_FEATURE_XARGS_SUPPORT_TERMOPT(     "x") \
+	IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   "0")
+
+int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int xargs_main(int argc, char **argv)
+{
+	int i;
+	int child_error = 0;
+	char *max_args;
+	char *max_chars;
+	char *buf;
+	unsigned opt;
+	int n_max_chars;
+	int n_max_arg;
+#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
+	char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
+#else
+#define read_args process_stdin
+#endif
+
+	INIT_G();
+
+#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?
+	 * This is used for portability:
+	 * old xargs was using "_" as default for -E / -e */
+	if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
+		G.eof_str = NULL;
+
+	if (opt & OPT_ZEROTERM)
+		IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
+
+	argv += optind;
+	argc -= optind;
+	if (!argv[0]) {
+		/* default behavior is to echo all the filenames */
+		*--argv = (char*)"echo";
+		argc++;
+	}
+
+	/* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate
+	 * to use such a big value - first need to change code to use
+	 * growable buffer instead of fixed one.
+	 */
+	n_max_chars = 32 * 1024;
+	/* Make smaller if system does not allow our default value.
+	 * The Open Group Base Specifications Issue 6:
+	 * "The xargs utility shall limit the command line length such that
+	 * when the command line is invoked, the combined argument
+	 * and environment lists (see the exec family of functions
+	 * in the System Interfaces volume of IEEE Std 1003.1-2001)
+	 * shall not exceed {ARG_MAX}-2048 bytes".
+	 */
+	{
+		long arg_max = 0;
+#if defined _SC_ARG_MAX
+		arg_max = sysconf(_SC_ARG_MAX) - 2048;
+#elif defined ARG_MAX
+		arg_max = ARG_MAX - 2048;
+#endif
+		if (arg_max > 0 && n_max_chars > arg_max)
+			n_max_chars = arg_max;
+	}
+	if (opt & OPT_UPTO_SIZE) {
+		n_max_chars = xatou_range(max_chars, 1, INT_MAX);
+	}
+	/* Account for prepended fixed arguments */
+	{
+		size_t n_chars = 0;
+		for (i = 0; argv[i]; i++) {
+			n_chars += strlen(argv[i]) + 1;
+		}
+		n_max_chars -= n_chars;
+	}
+	/* Sanity check */
+	if (n_max_chars <= 0) {
+		bb_error_msg_and_die("can't fit single argument within argument list size limit");
+	}
+
+	buf = xzalloc(n_max_chars + 1);
+
+	n_max_arg = n_max_chars;
+	if (opt & OPT_UPTO_NUMBER) {
+		n_max_arg = xatou_range(max_args, 1, INT_MAX);
+		/* Not necessary, we use growable args[]: */
+		/* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */
+	}
+
+	/* Allocate pointers for execvp */
+	/* We can statically allocate (argc + n_max_arg + 1) elements
+	 * and do not bother with resizing args[], but on 64-bit machines
+	 * this results in args[] vector which is ~8 times bigger
+	 * than n_max_chars! That is, with n_max_chars == 20k,
+	 * args[] will take 160k (!), which will most likely be
+	 * almost entirely unused.
+	 */
+	/* See store_param() for matching 256-step growth logic */
+	G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
+
+	/* Store the command to be executed, part 1 */
+	for (i = 0; argv[i]; i++)
+		G.args[i] = argv[i];
+
+	while (1) {
+		char *rem;
+
+		G.idx = argc;
+		rem = read_args(n_max_chars, n_max_arg, buf);
+		store_param(NULL);
+
+		if (!G.args[argc]) {
+			if (*rem != '\0')
+				bb_error_msg_and_die("argument line too long");
+			if (opt & OPT_NO_EMPTY)
+				break;
+		}
+		opt |= OPT_NO_EMPTY;
+
+		if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
+			const char *fmt = " %s" + 1;
+			char **args = G.args;
+			for (i = 0; args[i]; i++) {
+				fprintf(stderr, fmt, args[i]);
+				fmt = " %s";
+			}
+			if (!(opt & OPT_INTERACTIVE))
+				bb_putchar_stderr('\n');
+		}
+
+		if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
+			child_error = xargs_exec();
+		}
+
+		if (child_error > 0 && child_error != 123) {
+			break;
+		}
+
+		overlapping_strcpy(buf, rem);
+	} /* while */
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(G.args);
+		free(buf);
+	}
+
+	return child_error;
+}
+
+
+#ifdef TEST
+
+const char *applet_name = "debug stuff usage";
+
+void bb_show_usage(void)
+{
+	fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
+		applet_name);
+	exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv)
+{
+	return xargs_main(argc, argv);
+}
+#endif /* TEST */
diff --git a/busybox-1.19.3/include/applet_metadata.h b/busybox-1.19.3/include/applet_metadata.h
new file mode 100644
index 0000000..566ef35
--- /dev/null
+++ b/busybox-1.19.3/include/applet_metadata.h
@@ -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
diff --git a/busybox-1.19.3/include/applets.src.h b/busybox-1.19.3/include/applets.src.h
new file mode 100644
index 0000000..87d9cbb
--- /dev/null
+++ b/busybox-1.19.3/include/applets.src.h
@@ -0,0 +1,426 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * applets.h - a listing of all busybox applets.
+ *
+ * If you write a new applet, you need to add an entry to this list to make
+ * busybox aware of it.
+ */
+
+/*
+name  - applet name as it is typed on command line
+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
+        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
+        (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)
+# define APPLET(name,l,s)                    int name##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+# define APPLET_ODDNAME(name,main,l,s,name2) int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+# define APPLET_NOEXEC(name,main,l,s,name2)  int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+# define APPLET_NOFORK(name,main,l,s,name2)  int main##_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+
+#elif defined(NAME_MAIN_CNAME)
+# define APPLET(name,l,s)                    name name##_main name
+# define APPLET_ODDNAME(name,main,l,s,name2) name main##_main name2
+# define APPLET_NOEXEC(name,main,l,s,name2)  name main##_main name2
+# define APPLET_NOFORK(name,main,l,s,name2)  name main##_main name2
+
+#elif defined(MAKE_USAGE) && ENABLE_FEATURE_VERBOSE_USAGE
+# define APPLET(name,l,s)                    MAKE_USAGE(#name, name##_trivial_usage name##_full_usage)
+# define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage)
+# define APPLET_NOEXEC(name,main,l,s,name2)  MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage)
+# define APPLET_NOFORK(name,main,l,s,name2)  MAKE_USAGE(#name, name2##_trivial_usage name2##_full_usage)
+
+#elif defined(MAKE_USAGE) && !ENABLE_FEATURE_VERBOSE_USAGE
+# define APPLET(name,l,s)                    MAKE_USAGE(#name, name##_trivial_usage)
+# define APPLET_ODDNAME(name,main,l,s,name2) MAKE_USAGE(#name, name2##_trivial_usage)
+# define APPLET_NOEXEC(name,main,l,s,name2)  MAKE_USAGE(#name, name2##_trivial_usage)
+# define APPLET_NOFORK(name,main,l,s,name2)  MAKE_USAGE(#name, name2##_trivial_usage)
+
+#elif defined(MAKE_LINKS)
+# define APPLET(name,l,c)                    LINK l name
+# define APPLET_ODDNAME(name,main,l,s,name2) LINK l name
+# define APPLET_NOEXEC(name,main,l,s,name2)  LINK l name
+# define APPLET_NOFORK(name,main,l,s,name2)  LINK l name
+
+#else
+  static struct bb_applet applets[] = { /*    name, main, location, need_suid */
+# define APPLET(name,l,s)                    { #name, #name, l, s },
+# define APPLET_ODDNAME(name,main,l,s,name2) { #name, #main, l, s },
+# define APPLET_NOEXEC(name,main,l,s,name2)  { #name, #main, l, s, 1 },
+# define APPLET_NOFORK(name,main,l,s,name2)  { #name, #main, l, s, 1, 1 },
+#endif
+
+#if ENABLE_INSTALL_NO_USR
+# 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_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_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_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_NOEXEC(cksum, cksum, BB_DIR_USR_BIN, BB_SUID_DROP, cksum))
+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))
+/* 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_BIN, BB_SUID_DROP, delgroup))
+IF_DELUSER(APPLET(deluser, BB_DIR_BIN, 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_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_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_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))
+/* 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_GUNZIP(APPLET(gunzip, BB_DIR_BIN, BB_SUID_DROP))
+IF_GZIP(APPLET(gzip, BB_DIR_BIN, 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_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_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_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_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))
+#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_DROP))
+IF_IPCS(APPLET(ipcs, BB_DIR_USR_BIN, BB_SUID_DROP))
+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_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_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_NOEXEC(md5sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, md5sum))
+IF_MDEV(APPLET(mdev, BB_DIR_SBIN, 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_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_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_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_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_SHA1SUM(APPLET_NOEXEC(sha1sum, md5_sha1_sum, BB_DIR_USR_BIN, BB_SUID_DROP, sha1sum))
+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_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 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))
+#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_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_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_NOEXEC(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))
+/* 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))
+/* Needs to be run by root or be suid root - needs to write to /dev/TTY: */
+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_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 !defined(PROTOTYPES) && !defined(NAME_MAIN_CNAME) && !defined(MAKE_USAGE)
+};
+#endif
+
+#undef APPLET
+#undef APPLET_ODDNAME
+#undef APPLET_NOEXEC
+#undef APPLET_NOFORK
diff --git a/busybox-1.19.3/include/ar.h b/busybox-1.19.3/include/ar.h
new file mode 100644
index 0000000..386fe04
--- /dev/null
+++ b/busybox-1.19.3/include/ar.h
@@ -0,0 +1,26 @@
+/*
+ * busybox ar archive data structures
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#ifndef AR_H
+#define AR_H
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+struct ar_header {
+	char name[16];
+	char date[12];
+	char uid[6];
+	char gid[6];
+	char mode[8];
+	char size[10];
+	char magic[2];
+};
+
+#define AR_HEADER_LEN sizeof(struct ar_header)
+#define AR_MAGIC      "!<arch>"
+#define AR_MAGIC_LEN  7
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/archive.h b/busybox-1.19.3/include/archive.h
new file mode 100644
index 0000000..9e176d3
--- /dev/null
+++ b/busybox-1.19.3/include/archive.h
@@ -0,0 +1,241 @@
+/* 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 * (256 * (256 * 'z' + 'X') + 'Z') + 0,
+	/* Different form: 32 bits, then 16 bits: */
+	XZ_MAGIC1a  = 256 * (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 */
+#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__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;
+
+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;
+
+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
diff --git a/busybox-1.19.3/include/busybox.h b/busybox-1.19.3/include/busybox.h
new file mode 100644
index 0000000..315ef8f
--- /dev/null
+++ b/busybox-1.19.3/include/busybox.h
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+
+/* Defined in appletlib.c (by including generated applet_tables.h) */
+/* Keep in sync with applets/applet_tables.c! */
+extern const char applet_names[];
+extern int (*const applet_main[])(int argc, char **argv);
+extern const uint16_t applet_nameofs[];
+extern const uint8_t applet_install_loc[];
+
+#if ENABLE_FEATURE_SUID || ENABLE_FEATURE_PREFER_APPLETS
+# define APPLET_NAME(i) (applet_names + (applet_nameofs[i] & 0x0fff))
+#else
+# 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))
+#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)
+#endif
+
+#if ENABLE_FEATURE_INSTALLER
+#define APPLET_INSTALL_LOC(i) ({ \
+	unsigned v = (i); \
+	if (v & 1) v = applet_install_loc[v/2] >> 4; \
+	else v = applet_install_loc[v/2] & 0xf; \
+	v; })
+#endif
+
+
+/* Length of these names has effect on size of libbusybox
+ * and "individual" binaries. Keep them short.
+ */
+#if ENABLE_BUILD_LIBBUSYBOX
+#if ENABLE_FEATURE_SHARED_BUSYBOX
+int lbb_main(char **argv) EXTERNALLY_VISIBLE;
+#else
+int lbb_main(char **argv);
+#endif
+#endif
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/dump.h b/busybox-1.19.3/include/dump.h
new file mode 100644
index 0000000..4c237ef
--- /dev/null
+++ b/busybox-1.19.3/include/dump.h
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+#define	F_IGNORE	0x01		/* %_A */
+#define	F_SETREP	0x02		/* rep count set, not default */
+#define	F_ADDRESS	0x001		/* print offset */
+#define	F_BPAD		0x002		/* blank pad */
+#define	F_C		0x004		/* %_c */
+#define	F_CHAR		0x008		/* %c */
+#define	F_DBL		0x010		/* %[EefGf] */
+#define	F_INT		0x020		/* %[di] */
+#define	F_P		0x040		/* %_p */
+#define	F_STR		0x080		/* %s */
+#define	F_U		0x100		/* %_u */
+#define	F_UINT		0x200		/* %[ouXx] */
+#define	F_TEXT		0x400		/* no conversions */
+
+enum dump_vflag_t { ALL, DUP, FIRST, WAIT };	/* -v values */
+
+typedef struct PR {
+	struct PR *nextpr;		/* next print unit */
+	unsigned flags;			/* flag values */
+	int bcnt;			/* byte count */
+	char *cchar;			/* conversion character */
+	char *fmt;			/* printf format */
+	char *nospace;			/* no whitespace version */
+} PR;
+
+typedef struct FU {
+	struct FU *nextfu;		/* next format unit */
+	struct PR *nextpr;		/* next print unit */
+	unsigned flags;			/* flag values */
+	int reps;			/* repetition count */
+	int bcnt;			/* byte count */
+	char *fmt;			/* format string */
+} FU;
+
+typedef struct FS {			/* format strings */
+	struct FS *nextfs;		/* linked list of format strings */
+	struct FU *nextfu;		/* linked list of format units */
+	int bcnt;
+} FS;
+
+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*/
+	FS *fshead;
+} dumper_t;
+
+dumper_t* alloc_dumper(void) FAST_FUNC;
+extern void bb_dump_add(dumper_t *dumper, const char *fmt) FAST_FUNC;
+extern int bb_dump_dump(dumper_t *dumper, char **argv) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/busybox-1.19.3/include/fix_u32.h b/busybox-1.19.3/include/fix_u32.h
new file mode 100644
index 0000000..a2ba6d0
--- /dev/null
+++ b/busybox-1.19.3/include/fix_u32.h
@@ -0,0 +1,55 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * This header makes it easier to include kernel headers
+ * which use u32 and such.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#ifndef FIX_U32_H
+#define FIX_U32_H 1
+
+/* Try hard to pull in u32 types and such.
+ * Otherwise, #include "fix_u32.h" + #include <linux/foo.h>
+ * may end up typedef'ing bb_hack_u32 inside foo.h,
+ * and repeated typedefs aren't allowed in C/C++.
+ */
+#include <asm/types.h>
+#include <linux/types.h>
+
+/* In case above includes still failed to provide the types,
+ * provide them ourself
+ */
+#undef __u64
+#undef u64
+#undef u32
+#undef u16
+#undef u8
+#undef __s64
+#undef s64
+#undef s32
+#undef s16
+#undef s8
+
+#define __u64 bb_hack___u64
+#define u64   bb_hack_u64
+#define u32   bb_hack_u32
+#define u16   bb_hack_u16
+#define u8    bb_hack_u8
+#define __s64 bb_hack___s64
+#define s64   bb_hack_s64
+#define s32   bb_hack_s32
+#define s16   bb_hack_s16
+#define s8    bb_hack_s8
+
+typedef uint64_t __u64;
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t __s64;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+
+#endif
diff --git a/busybox-1.19.3/include/grp_.h b/busybox-1.19.3/include/grp_.h
new file mode 100644
index 0000000..5c24d55
--- /dev/null
+++ b/busybox-1.19.3/include/grp_.h
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 1991,92,95,96,97,98,99,2000,01 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.
+ */
+/*
+ * POSIX Standard: 9.2.1 Group Database Access	<grp.h>
+ */
+#ifndef BB_GRP_H
+#define BB_GRP_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* This file is #included after #include <grp.h>
+ * We will use libc-defined structures, but will #define function names
+ * so that function calls are directed to bb_internal_XXX replacements
+ */
+
+#define setgrent     bb_internal_setgrent
+#define endgrent     bb_internal_endgrent
+#define getgrent     bb_internal_getgrent
+#define fgetgrent    bb_internal_fgetgrent
+#define putgrent     bb_internal_putgrent
+#define getgrgid     bb_internal_getgrgid
+#define getgrnam     bb_internal_getgrnam
+#define getgrent_r   bb_internal_getgrent_r
+#define getgrgid_r   bb_internal_getgrgid_r
+#define getgrnam_r   bb_internal_getgrnam_r
+#define fgetgrent_r  bb_internal_fgetgrent_r
+#define getgrouplist bb_internal_getgrouplist
+#define initgroups   bb_internal_initgroups
+
+
+/* All function names below should be remapped by #defines above
+ * in order to not collide with libc names. */
+
+
+/* Rewind the group-file stream.  */
+extern void setgrent(void);
+
+/* Close the group-file stream.  */
+extern void endgrent(void);
+
+#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
+/* Read an entry from the group-file stream, opening it if necessary.  */
+extern struct group *getgrent(void);
+
+/* Read a group entry from STREAM.  */
+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);
+#endif
+
+/* Search for an entry with a matching group ID.  */
+extern struct group *getgrgid(gid_t __gid);
+
+/* Search for an entry with a matching group name.  */
+extern struct group *getgrnam(const char *__name);
+
+/* Reentrant versions of some of the functions above.
+
+   PLEASE NOTE: the `getgrent_r' function is not (yet) standardized.
+   The interface may change in later versions of this library.  But
+   the interface is designed following the principals used for the
+   other reentrant functions so the chances are good this is what the
+   POSIX people would choose.  */
+
+extern int getgrent_r(struct group *__restrict __resultbuf,
+		       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);
+
+/* 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);
+
+/* 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);
+
+/* 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);
+
+/* Initialize the group set for the current user
+   by reading the group database and using all groups
+   of which USER is a member.  Also include GROUP.  */
+extern int initgroups(const char *__user, gid_t __group);
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/inet_common.h b/busybox-1.19.3/include/inet_common.h
new file mode 100644
index 0000000..4638aa9
--- /dev/null
+++ b/busybox-1.19.3/include/inet_common.h
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ *                      Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III       Mar 12, 2001
+ *
+ */
+#ifndef INET_COMMON_H
+#define INET_COMMON_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* hostfirst!=0 If we expect this to be a hostname,
+   try hostname database first
+ */
+int INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst) FAST_FUNC;
+
+/* numeric: & 0x8000: "default" instead of "*",
+ *          & 0x4000: host instead of net,
+ *          & 0x0fff: don't resolve
+ */
+
+int INET6_resolve(const char *name, struct sockaddr_in6 *sin6) FAST_FUNC;
+
+/* These return malloced string */
+char *INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t netmask) FAST_FUNC;
+char *INET6_rresolve(struct sockaddr_in6 *sin6, int numeric) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/libbb.h b/busybox-1.19.3/include/libbb.h
new file mode 100644
index 0000000..63d0419
--- /dev/null
+++ b/busybox-1.19.3/include/libbb.h
@@ -0,0 +1,1861 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 GPL.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#ifndef LIBBB_H
+#define LIBBB_H 1
+
+#include "platform.h"
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#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>
+#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 <sys/param.h>
+#ifdef HAVE_MNTENT_H
+# include <mntent.h>
+#endif
+#ifdef HAVE_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>
+#endif
+#if ENABLE_FEATURE_UTMP
+# include <utmp.h>
+#endif
+#if ENABLE_LOCALE_SUPPORT
+# include <locale.h>
+#else
+# define setlocale(x,y) ((void)0)
+#endif
+#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
+#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
+#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__
+# include <netinet/in.h>
+#else
+# include <arpa/inet.h>
+# if !defined(__socklen_t_defined) && !defined(_SOCKLEN_T_DECLARED)
+/* We #define socklen_t *after* includes, otherwise we get
+ * typedef redefinition errors from system headers
+ * (in case "is it defined already" detection above failed)
+ */
+#  define socklen_t bb_socklen_t
+   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 */
+
+extern char **environ;
+#if defined(__GLIBC__) && __GLIBC__ < 2
+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
+#ifndef PATH_MAX
+# define PATH_MAX 256
+#endif
+#ifndef BUFSIZ
+# define BUFSIZ 4096
+#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
+
+
+#if ENABLE_USE_BB_PWD_GRP
+# include "pwd_.h"
+# include "grp_.h"
+#endif
+#if ENABLE_FEATURE_SHADOWPASSWDS
+# if ENABLE_USE_BB_SHADOW
+#  include "shadow_.h"
+# endif
+#endif
+
+/* Tested to work correctly with all int types (IIRC :]) */
+#define MAXINT(T) (T)( \
+	((T)-1) > 0 \
+	? (T)-1 \
+	: (T)~((T)1 << (sizeof(T)*8-1)) \
+	)
+
+#define MININT(T) (T)( \
+	((T)-1) > 0 \
+	? (T)0 \
+	: ((T)1 << (sizeof(T)*8-1)) \
+	)
+
+/* Large file support */
+/* Note that CONFIG_LFS=y forces bbox to be built with all common ops
+ * (stat, lseek etc) mapped to "largefile" variants by libc.
+ * Practically it means that open() automatically has O_LARGEFILE added
+ * and all filesize/file_offset parameters and struct members are "large"
+ * (in today's world - signed 64bit). For full support of large files,
+ * we need a few helper #defines (below) and careful use of off_t
+ * instead of int/ssize_t. No lseek64(), O_LARGEFILE etc necessary */
+#if ENABLE_LFS
+/* CONFIG_LFS is on */
+# 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)
+/* usage: sz = BB_STRTOOFF(s, NULL, 10); if (errno || sz < 0) die(); */
+#  define BB_STRTOOFF bb_strtoul
+#  define STRTOOFF strtoul
+/* usage: printf("size: %"OFF_FMT"d (%"OFF_FMT"x)\n", sz, sz); */
+#  define OFF_FMT "l"
+# else
+/* "long" is too short, need "long long" */
+typedef unsigned long long uoff_t;
+#  define XATOOFF(a) xatoull_range(a, 0, LLONG_MAX)
+#  define BB_STRTOOFF bb_strtoull
+#  define STRTOOFF strtoull
+#  define OFF_FMT "ll"
+# endif
+#else
+/* CONFIG_LFS is off */
+# if UINT_MAX == 0xffffffff
+/* 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_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 BB_STRTOOFF bb_strtoul
+#  define STRTOOFF strtol
+#  define OFF_FMT "l"
+# endif
+#endif
+/* scary. better ideas? (but do *test* them first!) */
+#define OFF_T_MAX  ((off_t)~((off_t)1 << (sizeof(off_t)*8-1)))
+
+/* Some useful definitions */
+#undef FALSE
+#define FALSE   ((int) 0)
+#undef TRUE
+#define TRUE    ((int) 1)
+#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))
+#endif
+
+#ifndef MAX
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+/* buffer allocation schemes */
+#if ENABLE_FEATURE_BUFFERS_GO_ON_STACK
+#define RESERVE_CONFIG_BUFFER(buffer,len)  char buffer[len]
+#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char buffer[len]
+#define RELEASE_CONFIG_BUFFER(buffer)      ((void)0)
+#else
+#if ENABLE_FEATURE_BUFFERS_GO_IN_BSS
+#define RESERVE_CONFIG_BUFFER(buffer,len)  static          char buffer[len]
+#define RESERVE_CONFIG_UBUFFER(buffer,len) static unsigned char buffer[len]
+#define RELEASE_CONFIG_BUFFER(buffer)      ((void)0)
+#else
+#define RESERVE_CONFIG_BUFFER(buffer,len)  char *buffer = xmalloc(len)
+#define RESERVE_CONFIG_UBUFFER(buffer,len) unsigned char *buffer = xmalloc(len)
+#define RELEASE_CONFIG_BUFFER(buffer)      free(buffer)
+#endif
+#endif
+
+#if defined(__GLIBC__)
+/* glibc uses __errno_location() to get a ptr to errno */
+/* We can just memorize it once - no multithreading in busybox :) */
+extern int *const bb_errno;
+#undef 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;
+unsigned monotonic_sec(void) FAST_FUNC;
+
+extern void chomp(char *s) FAST_FUNC;
+extern void trim(char *s) FAST_FUNC;
+extern char *skip_whitespace(const char *) FAST_FUNC;
+extern char *skip_non_whitespace(const char *) FAST_FUNC;
+extern char *skip_dev_pfx(const char *tty_name) FAST_FUNC;
+
+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;
+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 */
+	FILEUTILS_RECUR           = 1 << 2, /* -R */
+	FILEUTILS_FORCE           = 1 << 3, /* -f */
+	FILEUTILS_INTERACTIVE     = 1 << 4, /* -i */
+	FILEUTILS_MAKE_HARDLINK   = 1 << 5, /* -l */
+	FILEUTILS_MAKE_SOFTLINK   = 1 << 6, /* -s */
+	FILEUTILS_DEREF_SOFTLINK  = 1 << 7, /* -L */
+	FILEUTILS_DEREFERENCE_L0  = 1 << 8, /* -H */
+#if ENABLE_SELINUX
+	FILEUTILS_PRESERVE_SECURITY_CONTEXT = 1 << 9, /* -c */
+	FILEUTILS_SET_SECURITY_CONTEXT = 1 << 10,
+#endif
+};
+#define FILEUTILS_CP_OPTSTR "pdRfilsLH" IF_SELINUX("c")
+extern int remove_file(const char *path, int flags) FAST_FUNC;
+/* NB: without FILEUTILS_RECUR in flags, it will basically "cat"
+ * the source, not copy (unless "source" is a directory).
+ * This makes "cp /dev/null file" and "install /dev/null file" (!!!)
+ * work coreutils-compatibly. */
+extern int copy_file(const char *source, const char *dest, int flags) FAST_FUNC;
+
+enum {
+	ACTION_RECURSE        = (1 << 0),
+	ACTION_FOLLOWLINKS    = (1 << 1),
+	ACTION_FOLLOWLINKS_L0 = (1 << 2),
+	ACTION_DEPTHFIRST     = (1 << 3),
+	/*ACTION_REVERSE      = (1 << 4), - unused */
+	ACTION_QUIET          = (1 << 5),
+	ACTION_DANGLING_OK    = (1 << 6),
+};
+typedef uint8_t recurse_flags_t;
+extern int recursive_action(const char *fileName, unsigned flags,
+	int FAST_FUNC (*fileAction)(const char *fileName, struct stat* statbuf, void* userData, int depth),
+	int FAST_FUNC (*dirAction)(const char *fileName, struct stat* statbuf, void* userData, int depth),
+	void* userData, unsigned depth) FAST_FUNC;
+extern int device_open(const char *device, int mode) FAST_FUNC;
+enum { GETPTY_BUFSIZE = 16 }; /* more than enough for "/dev/ttyXXX" */
+extern int xgetpty(char *line) FAST_FUNC;
+extern int get_console_fd_or_die(void) FAST_FUNC;
+extern void console_make_active(int fd, const int vt_num) FAST_FUNC;
+extern char *find_block_device(const char *path) FAST_FUNC;
+/* bb_copyfd_XX print read/write errors and return -1 if they occur */
+extern off_t bb_copyfd_eof(int fd1, int fd2) FAST_FUNC;
+extern off_t bb_copyfd_size(int fd1, int fd2, off_t size) FAST_FUNC;
+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" !!
+ */
+char *bb_get_last_path_component_strip(char *path) FAST_FUNC;
+/* "abc/def/" -> "" and it never modifies 'path' */
+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;
+
+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;
+
+
+DIR *xopendir(const char *path) FAST_FUNC;
+DIR *warn_opendir(const char *path) FAST_FUNC;
+
+char *xmalloc_realpath(const char *path) FAST_FUNC RETURNS_MALLOC;
+char *xmalloc_readlink(const char *path) FAST_FUNC RETURNS_MALLOC;
+char *xmalloc_readlink_or_warn(const char *path) FAST_FUNC RETURNS_MALLOC;
+/* !RETURNS_MALLOC: it's a realloc-like function */
+char *xrealloc_getcwd_or_warn(char *cwd) FAST_FUNC;
+
+char *xmalloc_follow_symlinks(const char *path) FAST_FUNC RETURNS_MALLOC;
+
+
+enum {
+	/* bb_signals(BB_FATAL_SIGS, handler) catches all signals which
+	 * otherwise would kill us, except for those resulting from bugs:
+	 * SIGSEGV, SIGILL, SIGFPE.
+	 * Other fatal signals not included (TODO?):
+	 * SIGBUS   Bus error (bad memory access)
+	 * SIGPOLL  Pollable event. Synonym of SIGIO
+	 * SIGPROF  Profiling timer expired
+	 * SIGSYS   Bad argument to routine
+	 * SIGTRAP  Trace/breakpoint trap
+	 *
+	 * The only known arch with some of these sigs not fitting
+	 * into 32 bits is parisc (SIGXCPU=33, SIGXFSZ=34, SIGSTKFLT=36).
+	 * Dance around with long long to guard against that...
+	 */
+	BB_FATAL_SIGS = (int)(0
+		+ (1LL << SIGHUP)
+		+ (1LL << SIGINT)
+		+ (1LL << SIGTERM)
+		+ (1LL << SIGPIPE)   // Write to pipe with no readers
+		+ (1LL << SIGQUIT)   // Quit from keyboard
+		+ (1LL << SIGABRT)   // Abort signal from abort(3)
+		+ (1LL << SIGALRM)   // Timer signal from alarm(2)
+		+ (1LL << SIGVTALRM) // Virtual alarm clock
+		+ (1LL << SIGXCPU)   // CPU time limit exceeded
+		+ (1LL << SIGXFSZ)   // File size limit exceeded
+		+ (1LL << SIGUSR1)   // Yes kids, these are also fatal!
+		+ (1LL << SIGUSR2)
+		+ 0),
+};
+void bb_signals(int sigs, void (*f)(int)) FAST_FUNC;
+/* Unlike signal() and bb_signals, sets handler with sigaction()
+ * and in a way that while signal handler is run, no other signals
+ * will be blocked; syscalls will not be restarted: */
+void bb_signals_recursive_norestart(int sigs, void (*f)(int)) FAST_FUNC;
+/* syscalls like read() will be interrupted with EINTR: */
+void signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC;
+/* syscalls like read() won't be interrupted (though select/poll will be): */
+void signal_SA_RESTART_empty_mask(int sig, void (*handler)(int)) FAST_FUNC;
+void wait_for_any_sig(void) FAST_FUNC;
+void kill_myself_with_sig(int sig) NORETURN FAST_FUNC;
+void sig_block(int sig) FAST_FUNC;
+void sig_unblock(int sig) FAST_FUNC;
+/* Will do sigaction(signum, act, NULL): */
+int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC;
+/* SIG_BLOCK/SIG_UNBLOCK all signals: */
+int sigprocmask_allsigs(int how) FAST_FUNC;
+/* Standard handler which just records signo */
+extern smallint bb_got_signal;
+void record_signo(int signo); /* not FAST_FUNC! */
+
+
+void xsetgid(gid_t gid) FAST_FUNC;
+void xsetuid(uid_t uid) 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;
+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 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 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);
+
+void xpipe(int filedes[2]) FAST_FUNC;
+/* In this form code with pipes is much more readable */
+struct fd_pair { int rd; int wr; };
+#define piped_pair(pair)  pipe(&((pair).rd))
+#define xpiped_pair(pair) xpipe(&((pair).rd))
+
+/* Useful for having small structure members/global variables */
+typedef int8_t socktype_t;
+typedef int8_t family_t;
+struct BUG_too_small {
+	char BUG_socktype_t_too_small[(0
+			| SOCK_STREAM
+			| SOCK_DGRAM
+			| SOCK_RDM
+			| SOCK_SEQPACKET
+			| SOCK_RAW
+			) <= 127 ? 1 : -1];
+	char BUG_family_t_too_small[(0
+			| AF_UNSPEC
+			| AF_INET
+			| AF_INET6
+			| AF_UNIX
+#ifdef AF_PACKET
+			| AF_PACKET
+#endif
+#ifdef AF_NETLINK
+			| AF_NETLINK
+#endif
+			/* | AF_DECnet */
+			/* | AF_IPX */
+			) <= 127 ? 1 : -1];
+};
+
+
+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;
+
+
+int xsocket(int domain, int type, int protocol) FAST_FUNC;
+void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC;
+void xlisten(int s, int backlog) FAST_FUNC;
+void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) FAST_FUNC;
+ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
+				socklen_t tolen) FAST_FUNC;
+/* SO_REUSEADDR allows a server to rebind to an address that is already
+ * "in use" by old connections to e.g. previous server instance which is
+ * killed or crashed. Without it bind will fail until all such connections
+ * time out. Linux does not allow multiple live binds on same ip:port
+ * regardless of SO_REUSEADDR (unlike some other flavors of Unix).
+ * Turn it on before you call bind(). */
+void setsockopt_reuseaddr(int fd) FAST_FUNC; /* On Linux this never fails. */
+int setsockopt_broadcast(int fd) FAST_FUNC;
+int setsockopt_bindtodevice(int fd, const char *iface) FAST_FUNC;
+/* NB: returns port in host byte order */
+unsigned bb_lookup_port(const char *port, const char *protocol, unsigned default_port) FAST_FUNC;
+typedef struct len_and_sockaddr {
+	socklen_t len;
+	union {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+#if ENABLE_FEATURE_IPV6
+		struct sockaddr_in6 sin6;
+#endif
+	} u;
+} len_and_sockaddr;
+enum {
+	LSA_LEN_SIZE = offsetof(len_and_sockaddr, u),
+	LSA_SIZEOF_SA = sizeof(
+		union {
+			struct sockaddr sa;
+			struct sockaddr_in sin;
+#if ENABLE_FEATURE_IPV6
+			struct sockaddr_in6 sin6;
+#endif
+		}
+	)
+};
+/* Create stream socket, and allocate suitable lsa.
+ * (lsa of correct size and lsa->sa.sa_family (AF_INET/AF_INET6))
+ * af == AF_UNSPEC will result in trying to create IPv6 socket,
+ * 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,
+ * and can have ":PORT" suffix (for IPv6 use "[X:X:...:X]:PORT").
+ * Only if there is no suffix, port argument is used */
+/* NB: these set SO_REUSEADDR before bind */
+int create_and_bind_stream_or_die(const char *bindaddr, int port) FAST_FUNC;
+int create_and_bind_dgram_or_die(const char *bindaddr, int port) FAST_FUNC;
+/* Create client TCP socket connected to peer:port. Peer cannot be NULL.
+ * Peer can be numeric IP ("N.N.N.N"), numeric IPv6 address or hostname,
+ * and can have ":PORT" suffix (for IPv6 use "[X:X:...:X]:PORT").
+ * If there is no suffix, port argument is used */
+int create_and_connect_stream_or_die(const char *peer, int port) FAST_FUNC;
+/* Connect to peer identified by lsa */
+int xconnect_stream(const len_and_sockaddr *lsa) FAST_FUNC;
+/* Get local address of bound or accepted socket */
+len_and_sockaddr *get_sock_lsa(int fd) FAST_FUNC RETURNS_MALLOC;
+/* Get remote address of connected or accepted socket */
+len_and_sockaddr *get_peer_lsa(int fd) FAST_FUNC RETURNS_MALLOC;
+/* Return malloc'ed len_and_sockaddr with socket address of host:port
+ * Currently will return IPv4 or IPv6 sockaddrs only
+ * (depending on host), but in theory nothing prevents e.g.
+ * UNIX socket address being returned, IPX sockaddr etc...
+ * On error does bb_error_msg and returns NULL */
+len_and_sockaddr* host2sockaddr(const char *host, int port) FAST_FUNC RETURNS_MALLOC;
+/* Version which dies on error */
+len_and_sockaddr* xhost2sockaddr(const char *host, int port) FAST_FUNC RETURNS_MALLOC;
+len_and_sockaddr* xdotted2sockaddr(const char *host, int port) FAST_FUNC RETURNS_MALLOC;
+/* Same, useful if you want to force family (e.g. IPv6) */
+#if !ENABLE_FEATURE_IPV6
+#define host_and_af2sockaddr(host, port, af) host2sockaddr((host), (port))
+#define xhost_and_af2sockaddr(host, port, af) xhost2sockaddr((host), (port))
+#else
+len_and_sockaddr* host_and_af2sockaddr(const char *host, int port, sa_family_t af) FAST_FUNC RETURNS_MALLOC;
+len_and_sockaddr* xhost_and_af2sockaddr(const char *host, int port, sa_family_t af) FAST_FUNC RETURNS_MALLOC;
+#endif
+/* 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(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. */
+char* xmalloc_sockaddr2host(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
+/* This one doesn't append :PORTNUM */
+char* xmalloc_sockaddr2host_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
+/* This one also doesn't fall back to dotted IP (returns NULL) */
+char* xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
+/* inet_[ap]ton on steroids */
+char* xmalloc_sockaddr2dotted(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
+char* xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa) FAST_FUNC RETURNS_MALLOC;
+// "old" (ipv4 only) API
+// users: traceroute.c hostname.c - use _list_ of all IPs
+struct hostent *xgethostbyname(const char *name) FAST_FUNC;
+// Also mount.c and inetd.c are using gethostbyname(),
+// + inet_common.c has additional IPv4-only stuff
+
+
+void socket_want_pktinfo(int fd) FAST_FUNC;
+ssize_t send_to_from(int fd, void *buf, size_t len, int flags,
+		const struct sockaddr *to,
+		const struct sockaddr *from,
+		socklen_t tolen) FAST_FUNC;
+ssize_t recv_from_to(int fd, void *buf, size_t len, int flags,
+		struct sockaddr *from,
+		struct sockaddr *to,
+		socklen_t sa_size) FAST_FUNC;
+
+
+char *xstrdup(const char *s) FAST_FUNC RETURNS_MALLOC;
+char *xstrndup(const char *s, int n) FAST_FUNC RETURNS_MALLOC;
+void overlapping_strcpy(char *dst, const char *src) FAST_FUNC;
+char *safe_strncpy(char *dst, const char *src, size_t size) FAST_FUNC;
+char *strncpy_IFNAMSIZ(char *dst, const char *src) FAST_FUNC;
+/* Guaranteed to NOT be a macro (smallest code). Saves nearly 2k on uclibc.
+ * But potentially slow, don't use in one-billion-times loops */
+int bb_putchar(int ch) FAST_FUNC;
+/* Note: does not use stdio, writes to fd 2 directly */
+int bb_putchar_stderr(char ch) FAST_FUNC;
+char *xasprintf(const char *format, ...) __attribute__ ((format(printf, 1, 2))) FAST_FUNC RETURNS_MALLOC;
+// gcc-4.1.1 still isn't good enough at optimizing it
+// (+200 bytes compared to macro)
+//static ALWAYS_INLINE
+//int LONE_DASH(const char *s) { return s[0] == '-' && !s[1]; }
+//static ALWAYS_INLINE
+//int NOT_LONE_DASH(const char *s) { return s[0] != '-' || s[1]; }
+#define LONE_DASH(s)     ((s)[0] == '-' && !(s)[1])
+#define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1])
+#define LONE_CHAR(s,c)     ((s)[0] == (c) && !(s)[1])
+#define NOT_LONE_CHAR(s,c) ((s)[0] != (c) || (s)[1])
+#define DOT_OR_DOTDOT(s) ((s)[0] == '.' && (!(s)[1] || ((s)[1] == '.' && !(s)[2])))
+
+typedef struct uni_stat_t {
+	unsigned byte_count;
+	unsigned unicode_count;
+	unsigned unicode_width;
+} uni_stat_t;
+/* Returns a string with unprintable chars replaced by '?' or
+ * SUBST_WCHAR. This function is unicode-aware. */
+const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str);
+/* Prints unprintable char ch as ^C or M-c to file
+ * (M-c is used only if ch is ORed with PRINTABLE_META),
+ * else it is printed as-is (except for ch = 0x9b) */
+enum { PRINTABLE_META = 0x100 };
+void fputc_printable(int ch, FILE *file) FAST_FUNC;
+
+/* dmalloc will redefine these to it's own implementation. It is safe
+ * to have the prototypes here unconditionally.  */
+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
+ * 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. */
+#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_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;
+extern void xread(int fd, void *buf, size_t count) FAST_FUNC;
+extern unsigned char xread_char(int fd) FAST_FUNC;
+extern ssize_t read_close(int fd, void *buf, size_t maxsz) FAST_FUNC;
+extern ssize_t open_read_close(const char *filename, void *buf, size_t maxsz) FAST_FUNC;
+// 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, 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;
+/* Never returns NULL */
+extern void *xmalloc_xopen_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 \
+ || 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
+/* Autodetects .gz etc */
+extern int open_zipped(const char *fname) FAST_FUNC;
+extern void *xmalloc_open_zipped_read_close(const char *fname, 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,
+// if some data was written before error occurred
+extern ssize_t full_write(int fd, const void *buf, size_t count) FAST_FUNC;
+extern void xwrite(int fd, const void *buf, size_t count) FAST_FUNC;
+extern void xwrite_str(int fd, const char *str) FAST_FUNC;
+extern ssize_t full_write1_str(const char *str) FAST_FUNC;
+extern ssize_t full_write2_str(const char *str) FAST_FUNC;
+extern void xopen_xwrite_close(const char* file, const char *str) FAST_FUNC;
+
+/* Close fd, but check for failures (some types of write errors) */
+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;
+/* 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): */
+extern char *xmalloc_fgets_str_len(FILE *file, const char *terminating_string, size_t *maxsz_p) FAST_FUNC RETURNS_MALLOC;
+/* Chops off TERMINATING_STRING from the end: */
+extern char *xmalloc_fgetline_str(FILE *file, const char *terminating_string) FAST_FUNC RETURNS_MALLOC;
+/* Reads up to (and including) "\n" or NUL byte: */
+extern char *xmalloc_fgets(FILE *file) FAST_FUNC RETURNS_MALLOC;
+/* Chops off '\n' from the end, unlike fgets: */
+extern char *xmalloc_fgetline(FILE *file) FAST_FUNC RETURNS_MALLOC;
+/* Same, but doesn't try to conserve space (may have some slack after the end) */
+/* extern char *xmalloc_fgetline_fast(FILE *file) FAST_FUNC RETURNS_MALLOC; */
+
+void die_if_ferror(FILE *file, const char *msg) FAST_FUNC;
+void die_if_ferror_stdout(void) FAST_FUNC;
+int fflush_all(void) FAST_FUNC;
+void fflush_stdout_and_exit(int retval) NORETURN FAST_FUNC;
+int fclose_if_not_stdin(FILE *file) FAST_FUNC;
+FILE* xfopen(const char *filename, const char *mode) FAST_FUNC;
+/* Prints warning to stderr and returns NULL on failure: */
+FILE* fopen_or_warn(const char *filename, const char *mode) FAST_FUNC;
+/* "Opens" stdin if filename is special, else just opens file: */
+FILE* xfopen_stdin(const char *filename) FAST_FUNC;
+FILE* fopen_or_warn_stdin(const char *filename) FAST_FUNC;
+FILE* fopen_for_read(const char *path) FAST_FUNC;
+FILE* xfopen_for_read(const char *path) FAST_FUNC;
+FILE* fopen_for_write(const char *path) FAST_FUNC;
+FILE* xfopen_for_write(const char *path) FAST_FUNC;
+FILE* xfdopen_for_read(int fd) FAST_FUNC;
+FILE* xfdopen_for_write(int fd) FAST_FUNC;
+
+int bb_pstrcmp(const void *a, const void *b) /* not FAST_FUNC! */;
+void qsort_string_vector(char **sv, unsigned count) FAST_FUNC;
+
+/* Wrapper which restarts poll on EINTR or ENOMEM.
+ * On other errors complains [perror("poll")] and returns.
+ * Warning! May take (much) longer than timeout_ms to return!
+ * If this is a problem, use bare poll and open-code EINTR/ENOMEM handling */
+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;
+
+char *utoa(unsigned n) FAST_FUNC;
+char *itoa(int n) FAST_FUNC;
+/* Returns a pointer past the formatted number, does NOT null-terminate */
+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[4], const char *scale) FAST_FUNC;
+void 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...),
+ * else divide by display_unit and do not use suffix. */
+#define HUMAN_READABLE_MAX_WIDTH      7  /* "1024.0G" */
+#define HUMAN_READABLE_MAX_WIDTH_STR "7"
+//TODO: provide pointer to buf (avoid statics)?
+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;
+/* Reverse */
+char* hex2bin(char *dst, const char *str, int count) FAST_FUNC;
+
+/* Generate a UUID */
+void generate_uuid(uint8_t *buf) FAST_FUNC;
+
+/* Last element is marked by mult == 0 */
+struct suffix_mult {
+	char suffix[4];
+	unsigned mult;
+};
+#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.
+ * 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;
+
+
+/* These parse entries in /etc/passwd and /etc/group.  This is desirable
+ * for BusyBox since we want to avoid using the glibc NSS stuff, which
+ * increases target size and is often not needed on embedded systems.  */
+long xuname2uid(const char *name) FAST_FUNC;
+long xgroup2gid(const char *name) FAST_FUNC;
+/* wrapper: allows string to contain numeric uid or gid */
+unsigned long get_ug_id(const char *s, long FAST_FUNC (*xname2id)(const char *)) FAST_FUNC;
+/* from chpst. Does not die, returns 0 on failure */
+struct bb_uidgid_t {
+	uid_t uid;
+	gid_t gid;
+};
+/* always sets uid and gid */
+int get_uidgid(struct bb_uidgid_t*, const char*, int numeric_ok) FAST_FUNC;
+/* always sets uid and gid, allows numeric; exits on failure */
+void xget_uidgid(struct bb_uidgid_t*, const char*) FAST_FUNC;
+/* chown-like handling of "user[:[group]" */
+void parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_group) FAST_FUNC;
+struct passwd* xgetpwnam(const char *name) FAST_FUNC;
+struct group* xgetgrnam(const char *name) FAST_FUNC;
+struct passwd* xgetpwuid(uid_t uid) FAST_FUNC;
+struct group* xgetgrgid(gid_t gid) FAST_FUNC;
+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(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 = 32 - sizeof(uid_t) };
+#if ENABLE_FEATURE_CHECK_NAMES
+void die_if_bad_username(const char* name) FAST_FUNC;
+#else
+#define die_if_bad_username(name) ((void)(name))
+#endif
+
+#if ENABLE_FEATURE_UTMP
+void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
+void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname);
+#else
+# define write_new_utmp(pid, new_type, tty_name, username, hostname) ((void)0)
+# 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;
+
+/* BB_EXECxx always execs (it's not doing NOFORK/NOEXEC stuff),
+ * 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_EXECLP(prog,cmd,...) \
+	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__)
+#endif
+int BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
+
+/* xvfork() can't be a _function_, return after vfork mangles stack
+ * in the parent. It must be a macro. */
+#define xvfork() \
+({ \
+	pid_t bb__xvfork_pid = vfork(); \
+	if (bb__xvfork_pid < 0) \
+		bb_perror_msg_and_die("vfork"); \
+	bb__xvfork_pid; \
+})
+#if BB_MMU
+pid_t xfork(void) FAST_FUNC;
+#endif
+
+/* NOMMU friendy fork+exec: */
+pid_t spawn(char **argv) FAST_FUNC;
+pid_t xspawn(char **argv) FAST_FUNC;
+
+pid_t safe_waitpid(pid_t pid, int *wstat, int options) FAST_FUNC;
+pid_t wait_any_nohang(int *wstat) FAST_FUNC;
+/* wait4pid: unlike waitpid, waits ONLY for one process.
+ * Returns sig + 0x180 if child is killed by signal.
+ * It's safe to pass negative 'pids' from failed [v]fork -
+ * wait4pid will return -1 (and will not clobber [v]fork's errno).
+ * IOW: rc = wait4pid(spawn(argv));
+ *      if (rc < 0) bb_perror_msg("%s", argv[0]);
+ *      if (rc > 0) bb_error_msg("exit code: %d", rc & 0xff);
+ */
+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;
+/* Does NOT check that applet is NOFORK, just blindly runs it */
+int run_nofork_applet(int applet_no, char **argv) FAST_FUNC;
+
+/* Helpers for daemonization.
+ *
+ * bb_daemonize(flags) = daemonize, does not compile on NOMMU
+ *
+ * bb_daemonize_or_rexec(flags, argv) = daemonizes on MMU (and ignores argv),
+ *      rexec's itself on NOMMU with argv passed as command line.
+ * Thus bb_daemonize_or_rexec may cause your <applet>_main() to be re-executed
+ * from the start. (It will detect it and not reexec again second time).
+ * You have to audit carefully that you don't do something twice as a result
+ * (opening files/sockets, parsing config files etc...)!
+ *
+ * Both of the above will redirect fd 0,1,2 to /dev/null and drop ctty
+ * (will do setsid()).
+ *
+ * fork_or_rexec(argv) = bare-bones fork on MMU,
+ *      "vfork + re-exec ourself" on NOMMU. No fd redirection, no setsid().
+ *      On MMU ignores argv.
+ *
+ * Helper for network daemons in foreground mode:
+ *
+ * bb_sanitize_stdio() = make sure that fd 0,1,2 are opened by opening them
+ * to /dev/null if they are not.
+ */
+enum {
+	DAEMON_CHDIR_ROOT = 1,
+	DAEMON_DEVNULL_STDIO = 2,
+	DAEMON_CLOSE_EXTRA_FDS = 4,
+	DAEMON_ONLY_SANITIZE = 8, /* internal use */
+};
+#if BB_MMU
+  enum { re_execed = 0 };
+# define fork_or_rexec(argv)                xfork()
+# define bb_daemonize_or_rexec(flags, argv) bb_daemonize_or_rexec(flags)
+# define bb_daemonize(flags)                bb_daemonize_or_rexec(flags, bogus)
+#else
+  extern bool re_execed;
+  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;
+  int  BUG_daemon_is_unavailable_on_nommu(void) FAST_FUNC;
+  void BUG_bb_daemonize_is_unavailable_on_nommu(void) FAST_FUNC;
+# define fork()          BUG_fork_is_unavailable_on_nommu()
+# define xfork()         BUG_fork_is_unavailable_on_nommu()
+# define daemon(a,b)     BUG_daemon_is_unavailable_on_nommu()
+# define bb_daemonize(a) BUG_bb_daemonize_is_unavailable_on_nommu()
+#endif
+void bb_daemonize_or_rexec(int flags, char **argv) FAST_FUNC;
+void bb_sanitize_stdio(void) FAST_FUNC;
+/* Clear dangerous stuff, set PATH. Return 1 if was run by different user. */
+int sanitize_env_if_suid(void) FAST_FUNC;
+
+
+char* single_argv(char **argv) FAST_FUNC;
+extern const char *const bb_argv_dash[]; /* "-", NULL */
+extern const char *opt_complementary;
+#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
+#define No_argument "\0"
+#define Required_argument "\001"
+#define Optional_argument "\002"
+extern const char *applet_long_options;
+#endif
+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 {
+	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;
+void *llist_pop(llist_t **elm) FAST_FUNC;
+void llist_unlink(llist_t **head, llist_t *elm) FAST_FUNC;
+void llist_free(llist_t *elm, void (*freeit)(void *data)) FAST_FUNC;
+llist_t *llist_rev(llist_t *list) FAST_FUNC;
+llist_t *llist_find_str(llist_t *first, const char *str) FAST_FUNC;
+/* BTW, surprisingly, changing API to
+ *   llist_t *llist_add_to(llist_t *old_head, void *data)
+ * etc does not result in smaller code... */
+
+/* start_stop_daemon and udhcpc are special - they want
+ * to create pidfiles regardless of FEATURE_PIDFILE */
+#if ENABLE_FEATURE_PIDFILE || defined(WANT_PIDFILE)
+/* True only if we created pidfile which is *file*, not /dev/null etc */
+extern smallint wrote_pidfile;
+void write_pidfile(const char *path) FAST_FUNC;
+#define remove_pidfile(path) do { if (wrote_pidfile) unlink(path); } while (0)
+#else
+enum { wrote_pidfile = 0 };
+#define write_pidfile(path)  ((void)0)
+#define remove_pidfile(path) ((void)0)
+#endif
+
+enum {
+	LOGMODE_NONE = 0,
+	LOGMODE_STDIO = (1 << 0),
+	LOGMODE_SYSLOG = (1 << 1) * ENABLE_FEATURE_SYSLOG,
+	LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO,
+};
+extern const char *msg_eol;
+extern smallint logmode;
+extern int die_sleep;
+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;
+extern void bb_error_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC;
+extern void bb_error_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))) FAST_FUNC;
+extern void bb_perror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC;
+extern void bb_simple_perror_msg(const char *s) FAST_FUNC;
+extern void bb_perror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))) FAST_FUNC;
+extern void bb_simple_perror_msg_and_die(const char *s) NORETURN FAST_FUNC;
+extern void bb_herror_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC;
+extern void bb_herror_msg_and_die(const char *s, ...) __attribute__ ((noreturn, format (printf, 1, 2))) FAST_FUNC;
+extern void bb_perror_nomsg_and_die(void) NORETURN FAST_FUNC;
+extern void bb_perror_nomsg(void) FAST_FUNC;
+extern void bb_info_msg(const char *s, ...) __attribute__ ((format (printf, 1, 2))) FAST_FUNC;
+extern void bb_verror_msg(const char *s, va_list p, const char *strerr) FAST_FUNC;
+
+/* We need to export XXX_main from libbusybox
+ * only if we build "individual" binaries
+ */
+#if ENABLE_FEATURE_INDIVIDUAL
+#define MAIN_EXTERNALLY_VISIBLE EXTERNALLY_VISIBLE
+#else
+#define MAIN_EXTERNALLY_VISIBLE
+#endif
+
+
+/* Applets which are useful from another applets */
+int bb_cat(char** argv);
+/* If shell needs them, they exist even if not enabled as applets */
+int echo_main(int argc, char** argv) IF_ECHO(MAIN_EXTERNALLY_VISIBLE);
+int printf_main(int argc, char **argv) IF_PRINTF(MAIN_EXTERNALLY_VISIBLE);
+int test_main(int argc, char **argv) IF_TEST(MAIN_EXTERNALLY_VISIBLE);
+int kill_main(int argc, char **argv) IF_KILL(MAIN_EXTERNALLY_VISIBLE);
+/* Similar, but used by chgrp, not shell */
+int chown_main(int argc, char **argv) IF_CHOWN(MAIN_EXTERNALLY_VISIBLE);
+/* Used by ftpd */
+int ls_main(int argc, char **argv) IF_LS(MAIN_EXTERNALLY_VISIBLE);
+/* Don't need IF_xxx() guard for these */
+int gunzip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int bunzip2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+
+#if ENABLE_ROUTE
+void bb_displayroutes(int noresolve, int netstatfmt) FAST_FUNC;
+#endif
+
+
+/* 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;
+	const char *title;
+	int af;
+	int alen;
+	char*       FAST_FUNC (*print)(unsigned char *);
+	const char* FAST_FUNC (*sprint)(struct sockaddr *, int numeric);
+	int         FAST_FUNC (*input)(/*int type,*/ const char *bufp, struct sockaddr *);
+	void        FAST_FUNC (*herror)(char *text);
+	int         FAST_FUNC (*rprint)(int options);
+	int         FAST_FUNC (*rinput)(int typ, int ext, char **argv);
+	/* may modify src */
+	int         FAST_FUNC (*getmask)(char *src, struct sockaddr *mask, char *name);
+};
+/* This structure defines hardware protocols and their handlers. */
+struct hwtype {
+	const char *name;
+	const char *title;
+	int type;
+	int alen;
+	char* FAST_FUNC (*print)(unsigned char *);
+	int   FAST_FUNC (*input)(const char *, struct sockaddr *);
+	int   FAST_FUNC (*activate)(int fd);
+	int suppress_null_addr;
+};
+extern smallint interface_opt_a;
+int display_interfaces(char *ifname) FAST_FUNC;
+#if ENABLE_FEATURE_HWIB
+int in_ib(const char *bufp, struct sockaddr *sap) FAST_FUNC;
+#else
+#define in_ib(a, b) 1 /* fail */
+#endif
+const struct aftype *get_aftype(const char *name) FAST_FUNC;
+const struct hwtype *get_hwtype(const char *name) FAST_FUNC;
+const struct hwtype *get_hwntype(int type) FAST_FUNC;
+
+
+#ifndef BUILD_INDIVIDUAL
+extern int find_applet_by_name(const char *name) FAST_FUNC;
+/* Returns only if applet is not found. */
+extern void run_applet_and_exit(const char *name, char **argv) FAST_FUNC;
+extern void run_applet_no_and_exit(int a, char **argv) NORETURN FAST_FUNC;
+#endif
+
+#ifdef HAVE_MNTENT_H
+extern int match_fstype(const struct mntent *mt, const char *fstypes) FAST_FUNC;
+extern struct mntent *find_mount_point(const char *name, int subdir_too) FAST_FUNC;
+#endif
+extern void erase_mtab(const char * name) FAST_FUNC;
+extern unsigned int tty_baud_to_value(speed_t speed) FAST_FUNC;
+extern speed_t tty_value_to_baud(unsigned int value) FAST_FUNC;
+#if ENABLE_DESKTOP
+extern void bb_warn_ignoring_args(char *arg) FAST_FUNC;
+#else
+# define bb_warn_ignoring_args(arg) ((void)0)
+#endif
+
+extern int get_linux_version_code(void) FAST_FUNC;
+
+extern char *query_loop(const char *device) FAST_FUNC;
+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;
+
+/* Like bb_ask below, but asks on stdin with no timeout.  */
+char *bb_ask_stdin(const char * prompt) FAST_FUNC;
+//TODO: pass buf pointer or return allocated buf (avoid statics)?
+char *bb_ask(const int fd, int timeout, const char * prompt) FAST_FUNC;
+int bb_ask_confirmation(void) FAST_FUNC;
+
+int bb_parse_mode(const char* s, mode_t* theMode) FAST_FUNC;
+
+/*
+ * Config file parser
+ */
+enum {
+	PARSE_COLLAPSE  = 0x00010000, // treat consecutive delimiters as one
+	PARSE_TRIM      = 0x00020000, // trim leading and trailing delimiters
+// TODO: COLLAPSE and TRIM seem to always go in pair
+	PARSE_GREEDY    = 0x00040000, // last token takes entire remainder of the line
+	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
+	// 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,
+};
+typedef struct parser_t {
+	FILE *fp;
+	char *data;
+	char *line, *nline;
+	size_t line_alloc, nline_alloc;
+	int lineno;
+} parser_t;
+parser_t* config_open(const char *filename) FAST_FUNC;
+parser_t* config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path)) FAST_FUNC;
+/* delims[0] is a comment char (use '\0' to disable), the rest are token delimiters */
+int config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims) FAST_FUNC;
+#define config_read(parser, tokens, max, min, str, flags) \
+	config_read(parser, tokens, ((flags) | (((min) & 0xFF) << 8) | ((max) & 0xFF)), str)
+void config_close(parser_t *parser) FAST_FUNC;
+
+/* Concatenate path and filename to new allocated buffer.
+ * Add "/" only as needed (no duplicate "//" are produced).
+ * 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;
+
+
+int bb_make_directory(char *path, long mode, int flags) FAST_FUNC;
+
+int get_signum(const char *name) FAST_FUNC;
+const char *get_signame(int number) FAST_FUNC;
+void print_signames(void) FAST_FUNC;
+
+char *bb_simplify_path(const char *path) FAST_FUNC;
+/* Returns ptr to NUL */
+char *bb_simplify_abs_path_inplace(char *path) FAST_FUNC;
+
+#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);
+
+#if ENABLE_SELINUX
+extern void renew_current_security_context(void) FAST_FUNC;
+extern void set_current_security_context(security_context_t sid) FAST_FUNC;
+extern context_t set_security_context_component(security_context_t cur_context,
+						char *user, char *role, char *type, char *range) FAST_FUNC;
+extern void setfscreatecon_or_die(security_context_t scontext) FAST_FUNC;
+extern void selinux_preserve_fcontext(int fdesc) FAST_FUNC;
+#else
+#define selinux_preserve_fcontext(fdesc) ((void)0)
+#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
+ *   TERM=(old value)
+ *   USER=pw->pw_name, LOGNAME=pw->pw_name
+ *   PATH=bb_default_[root_]path
+ *   HOME=pw->pw_dir
+ *   SHELL=shell
+ * else if change_env = 1:
+ *   if not root (if pw->pw_uid != 0):
+ *     USER=pw->pw_name, LOGNAME=pw->pw_name
+ *   HOME=pw->pw_dir
+ *   SHELL=shell
+ * else does nothing
+ */
+#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;
+/* 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.
+ * 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;
+/* "$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)
+#define update_passwd(filename, username, data, member) \
+	update_passwd(filename, username, data)
+#endif
+extern int update_passwd(const char *filename,
+		const char *username,
+		const char *data,
+		const char *member) FAST_FUNC;
+
+int index_in_str_array(const char *const string_array[], const char *key) FAST_FUNC;
+int index_in_strings(const char *strings, const char *key) FAST_FUNC;
+int index_in_substr_array(const char *const string_array[], const char *key) FAST_FUNC;
+int index_in_substrings(const char *strings, const char *key) FAST_FUNC;
+const char *nth_string(const char *strings, int n) FAST_FUNC;
+
+extern void print_login_issue(const char *issue_file, const char *tty) FAST_FUNC;
+extern void print_login_prompt(void) FAST_FUNC;
+
+char *xmalloc_ttyname(int fd) FAST_FUNC RETURNS_MALLOC;
+/* NB: typically you want to pass fd 0, not 1. Think 'applet | grep something' */
+int get_terminal_width_height(int fd, unsigned *width, unsigned *height) FAST_FUNC;
+
+int tcsetattr_stdin_TCSANOW(const struct termios *tp) FAST_FUNC;
+
+/* NB: "unsigned request" is crucial! "int request" will break some arches! */
+int ioctl_or_perror(int fd, unsigned request, void *argp, const char *fmt,...) __attribute__ ((format (printf, 4, 5))) FAST_FUNC;
+int ioctl_or_perror_and_die(int fd, unsigned request, void *argp, const char *fmt,...) __attribute__ ((format (printf, 4, 5))) FAST_FUNC;
+#if ENABLE_IOCTL_HEX2STR_ERROR
+int bb_ioctl_or_warn(int fd, unsigned request, void *argp, const char *ioctl_name) FAST_FUNC;
+int bb_xioctl(int fd, unsigned request, void *argp, const char *ioctl_name) FAST_FUNC;
+#define ioctl_or_warn(fd,request,argp) bb_ioctl_or_warn(fd,request,argp,#request)
+#define xioctl(fd,request,argp)        bb_xioctl(fd,request,argp,#request)
+#else
+int bb_ioctl_or_warn(int fd, unsigned request, void *argp) FAST_FUNC;
+int bb_xioctl(int fd, unsigned request, void *argp) FAST_FUNC;
+#define ioctl_or_warn(fd,request,argp) bb_ioctl_or_warn(fd,request,argp)
+#define xioctl(fd,request,argp)        bb_xioctl(fd,request,argp)
+#endif
+
+char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC;
+void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC;
+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 major, unsigned minor) FAST_FUNC;
+#undef makedev
+#define makedev(a,b) bb_makedev(a,b)
+#endif
+
+
+/* "Keycodes" that report an escape sequence.
+ * We use something which fits into signed char,
+ * yet doesn't represent any valid Unicode character.
+ * Also, -1 is reserved for error indication and we don't use it. */
+enum {
+	KEYCODE_UP       =  -2,
+	KEYCODE_DOWN     =  -3,
+	KEYCODE_RIGHT    =  -4,
+	KEYCODE_LEFT     =  -5,
+	KEYCODE_HOME     =  -6,
+	KEYCODE_END      =  -7,
+	KEYCODE_INSERT   =  -8,
+	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,
+#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,
+#endif
+	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
+	 * cursor position sequence "ESC [ 9999 ; 9999 R"
+	 */
+	KEYCODE_BUFFER_SIZE = 16
+};
+/* Note: fd may be in blocking or non-blocking mode, both make sense.
+ * For one, less uses non-blocking mode.
+ * Only the first read syscall inside read_key may block indefinitely
+ * (unless fd is in non-blocking mode),
+ * subsequent reads will time out after a few milliseconds.
+ * Return of -1 means EOF or error (errno == 0 on EOF).
+ * buffer[0] is used as a counter of buffered chars and must be 0
+ * on first call.
+ * timeout:
+ * -2: do not poll for input;
+ * -1: poll(-1) (i.e. block);
+ * >=0: poll for TIMEOUT milliseconds, return -1/EAGAIN on timeout
+ */
+int64_t read_key(int fd, char *buffer, int timeout) FAST_FUNC;
+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: */
+# 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);
+# else
+#  define MAX_HISTORY 0
+# endif
+typedef struct line_input_t {
+	int flags;
+	const char *path_lookup;
+# if MAX_HISTORY
+	int cnt_history;
+	int cur_history;
+	int max_history; /* must never be <= 0 */
+#  if ENABLE_FEATURE_EDITING_SAVEHISTORY
+	unsigned cnt_history_in_file;
+	const char *hist_file;
+#  endif
+	char *history[MAX_HISTORY + 1];
+# 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,
+};
+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.
+ * 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(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout) FAST_FUNC;
+#else
+#define MAX_HISTORY 0
+int read_line_input(const char* prompt, char* command, int maxsize) FAST_FUNC;
+#define read_line_input(state, prompt, command, maxsize, timeout) \
+	read_line_input(prompt, command, maxsize)
+#endif
+
+
+#ifndef COMM_LEN
+# ifdef TASK_COMM_LEN
+enum { COMM_LEN = TASK_COMM_LEN };
+# else
+/* synchronize with sizeof(task_struct.comm) in /usr/include/linux/sched.h */
+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;)
+	uint8_t shift_pages_to_bytes;
+	uint8_t shift_pages_to_kb;
+/* Fields are set to 0/NULL if failed to determine (or not requested) */
+	uint16_t argv_len;
+	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 */
+	unsigned long stime, utime;
+	unsigned long start_time;
+	unsigned pid;
+	unsigned ppid;
+	unsigned pgid;
+	unsigned sid;
+	unsigned uid;
+	unsigned gid;
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+	unsigned ruid;
+	unsigned rgid;
+	int niceness;
+#endif
+	unsigned tty_major,tty_minor;
+#if ENABLE_FEATURE_TOPMEM
+	struct smaprec smaps;
+#endif
+	char state[4];
+	/* basename of executable in exec(2), read from /proc/N/stat
+	 * (if executable is symlink or script, it is NOT replaced
+	 * by link target or interpreter name) */
+	char comm[COMM_LEN];
+	/* user/group? - use passwd/group parsing functions */
+#if ENABLE_FEATURE_TOP_SMP_PROCESS
+	int last_seen_on_cpu;
+#endif
+} procps_status_t;
+/* flag bits for procps_scan(xx, flags) calls */
+enum {
+	PSSCAN_PID      = 1 << 0,
+	PSSCAN_PPID     = 1 << 1,
+	PSSCAN_PGID     = 1 << 2,
+	PSSCAN_SID      = 1 << 3,
+	PSSCAN_UIDGID   = 1 << 4,
+	PSSCAN_COMM     = 1 << 5,
+	/* PSSCAN_CMD      = 1 << 6, - use read_cmdline instead */
+	PSSCAN_ARGV0    = 1 << 7,
+	PSSCAN_EXE      = 1 << 8,
+	PSSCAN_STATE    = 1 << 9,
+	PSSCAN_VSZ      = 1 << 10,
+	PSSCAN_RSS      = 1 << 11,
+	PSSCAN_STIME    = 1 << 12,
+	PSSCAN_UTIME    = 1 << 13,
+	PSSCAN_TTY      = 1 << 14,
+	PSSCAN_SMAPS	= (1 << 15) * ENABLE_FEATURE_TOPMEM,
+	/* NB: used by find_pid_by_name(). Any applet using it
+	 * needs to be mentioned here. */
+	PSSCAN_ARGVN    = (1 << 16) * (ENABLE_KILLALL
+				|| ENABLE_PGREP || ENABLE_PKILL
+				|| ENABLE_PIDOF
+				|| ENABLE_SESTATUS
+				),
+	PSSCAN_CONTEXT  = (1 << 17) * ENABLE_SELINUX,
+	PSSCAN_START_TIME = 1 << 18,
+	PSSCAN_CPU      = (1 << 19) * ENABLE_FEATURE_TOP_SMP_PROCESS,
+	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,
+};
+//procps_status_t* alloc_procps_scan(void) FAST_FUNC;
+void free_procps_scan(procps_status_t* sp) FAST_FUNC;
+procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC;
+/* Format cmdline (up to col chars) into char buf[size] */
+/* Puts [comm] if cmdline is empty (-> process is a kernel thread) */
+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[];
+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,
+};
+void FAST_FUNC read_base64(FILE *src_stream, FILE *dst_stream, int flags);
+
+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];
+	uint8_t wbuffer[128]; /* always correctly aligned for uint64_t */
+} sha512_ctx_t;
+void md5_begin(md5_ctx_t *ctx) FAST_FUNC;
+void md5_hash(md5_ctx_t *ctx, const void *data, size_t length) 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;
+
+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;
+	const int masks[];
+} masks_labels_t;
+int print_flags_separated(const int *masks, const char *labels,
+		int flags, const char *separator) FAST_FUNC;
+int print_flags(const masks_labels_t *ml, int flags) FAST_FUNC;
+
+typedef struct bb_progress_t {
+	unsigned last_size;
+	unsigned last_update_sec;
+	unsigned last_change_sec;
+	unsigned start_sec;
+	const char *curfile;
+} bb_progress_t;
+
+#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[];
+#define bb_msg_read_error "read error"
+#define bb_msg_write_error "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[];
+
+/* NB: (bb_hexdigits_upcase[i] | 0x20) -> lowercase hex digit */
+extern const char bb_hexdigits_upcase[];
+
+extern const char bb_path_wtmp_file[];
+
+/* 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[];
+/* 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" */
+#define bb_default_root_path (bb_PATH_root_path + sizeof("PATH"))
+#define bb_default_path      (bb_PATH_root_path + sizeof("PATH=/sbin:/usr/sbin"))
+
+extern const int const_int_0;
+extern const int const_int_1;
+
+
+/* Providing hard guarantee on minimum size (think of BUFSIZ == 128) */
+enum { COMMON_BUFSIZE = (BUFSIZ >= 256*sizeof(void*) ? BUFSIZ+1 : 256*sizeof(void*)) };
+extern char bb_common_bufsiz1[COMMON_BUFSIZE];
+/* This struct is deliberately not defined. */
+/* See docs/keep_data_small.txt */
+struct globals;
+/* '*const' ptr makes gcc optimize code much better.
+ * Magic prevents ptr_to_globals from going into rodata.
+ * If you want to assign a value, use SET_PTR_TO_GLOBALS(x) */
+extern struct globals *const ptr_to_globals;
+/* At least gcc 3.4.6 on mipsel system needs optimization barrier */
+#define barrier() __asm__ __volatile__("":::"memory")
+#define SET_PTR_TO_GLOBALS(x) do { \
+	(*(struct globals**)&ptr_to_globals) = (void*)(x); \
+	barrier(); \
+} 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[];
+/* "/bin/sh" */
+#define DEFAULT_SHELL              (bb_default_login_shell+1)
+/* "sh" */
+#define DEFAULT_SHELL_SHORT_NAME   (bb_default_login_shell+6)
+
+/* 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"
+# define VC_FORMAT "/dev/vc/%d"
+# define LOOP_FORMAT "/dev/loop/%d"
+# 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"
+# define VC_FORMAT "/dev/tty%d"
+# define LOOP_FORMAT "/dev/loop%d"
+# define LOOP_NAMESIZE (sizeof("/dev/loop") + sizeof(int)*3 + 1)
+# define LOOP_NAME "/dev/loop"
+# define FB_0 "/dev/fb0"
+#endif
+
+
+#define ARRAY_SIZE(x) ((unsigned)(sizeof(x) / sizeof((x)[0])))
+
+
+/* We redefine ctype macros. Unicode-correct handling of char types
+ * can't be done with such byte-oriented operations anyway,
+ * we don't lose anything.
+ */
+#undef isalnum
+#undef isalpha
+#undef isascii
+#undef isblank
+#undef iscntrl
+#undef isdigit
+#undef isgraph
+#undef islower
+#undef isprint
+#undef ispunct
+#undef isspace
+#undef isupper
+#undef isxdigit
+#undef toupper
+#undef tolower
+
+/* We save ~500 bytes on isdigit alone.
+ * BTW, x86 likes (unsigned char) cast more than (unsigned). */
+
+/* These work the same for ASCII and Unicode,
+ * assuming no one asks "is this a *Unicode* letter?" using isalpha(letter) */
+#define isascii(a) ((unsigned char)(a) <= 0x7f)
+#define isdigit(a) ((unsigned char)((a) - '0') <= 9)
+#define isupper(a) ((unsigned char)((a) - 'A') <= ('Z' - 'A'))
+#define islower(a) ((unsigned char)((a) - 'a') <= ('z' - 'a'))
+#define isalpha(a) ((unsigned char)(((a)|0x20) - 'a') <= ('z' - 'a'))
+#define isblank(a) ({ unsigned char bb__isblank = (a); bb__isblank == ' ' || bb__isblank == '\t'; })
+#define iscntrl(a) ({ unsigned char bb__iscntrl = (a); bb__iscntrl < ' ' || bb__iscntrl == 0x7f; })
+/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
+ * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
+ */
+#define isspace(a) ({ unsigned char bb__isspace = (a) - 9; bb__isspace == (' ' - 9) || bb__isspace <= (13 - 9); })
+// Unsafe wrt NUL: #define ispunct(a) (strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", (a)) != NULL)
+#define ispunct(a) (strchrnul("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", (a))[0])
+// Bigger code: #define isalnum(a) ({ unsigned char bb__isalnum = (a) - '0'; bb__isalnum <= 9 || ((bb__isalnum - ('A' - '0')) & 0xdf) <= 25; })
+#define isalnum(a) bb_ascii_isalnum(a)
+static ALWAYS_INLINE int bb_ascii_isalnum(unsigned char a)
+{
+	unsigned char b = a - '0';
+	if (b <= 9)
+		return (b <= 9);
+	b = (a|0x20) - 'a';
+	return b <= 'z' - 'a';
+}
+#define isxdigit(a) bb_ascii_isxdigit(a)
+static ALWAYS_INLINE int bb_ascii_isxdigit(unsigned char a)
+{
+	unsigned char b = a - '0';
+	if (b <= 9)
+		return (b <= 9);
+	b = (a|0x20) - 'a';
+	return b <= 'f' - 'a';
+}
+#define toupper(a) bb_ascii_toupper(a)
+static ALWAYS_INLINE unsigned char bb_ascii_toupper(unsigned char a)
+{
+	unsigned char b = a - 'a';
+	if (b <= ('z' - 'a'))
+		a -= 'a' - 'A';
+	return a;
+}
+#define tolower(a) bb_ascii_tolower(a)
+static ALWAYS_INLINE unsigned char bb_ascii_tolower(unsigned char a)
+{
+	unsigned char b = a - 'A';
+	if (b <= ('Z' - 'A'))
+		a += 'a' - 'A';
+	return a;
+}
+
+/* In ASCII and Unicode, these are likely to be very different.
+ * Let's prevent ambiguous usage from the start */
+#define isgraph(a) isgraph_is_ambiguous_dont_use(a)
+#define isprint(a) isprint_is_ambiguous_dont_use(a)
+/* NB: must not treat EOF as isgraph or isprint */
+#define isgraph_asciionly(a) ((unsigned)((a) - 0x21) <= 0x7e - 0x21)
+#define isprint_asciionly(a) ((unsigned)((a) - 0x20) <= 0x7e - 0x20)
+
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/liblzo_interface.h b/busybox-1.19.3/include/liblzo_interface.h
new file mode 100644
index 0000000..9a84c0b
--- /dev/null
+++ b/busybox-1.19.3/include/liblzo_interface.h
@@ -0,0 +1,71 @@
+/*
+   This file is part of the LZO real-time data compression library.
+
+   Copyright (C) 1996..2008 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   Markus F.X.J. Oberhumer <markus@oberhumer.com>
+   http://www.oberhumer.com/opensource/lzo/
+
+   The LZO library 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.
+
+   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
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#define LZO1X
+#undef LZO1Y
+
+#undef assert
+/*
+static void die_at(int line)
+{
+        bb_error_msg_and_die("internal error at %d", line);
+}
+#define assert(v) if (!(v)) die_at(__LINE__)
+*/
+#define assert(v) ((void)0)
+
+int lzo1x_1_compress(const uint8_t* src, unsigned src_len,
+		uint8_t* dst, unsigned* dst_len,
+		void* wrkmem);
+int lzo1x_1_15_compress(const uint8_t* src, unsigned src_len,
+		uint8_t* dst, unsigned* dst_len,
+		void* wrkmem);
+int lzo1x_999_compress_level(const uint8_t* in, unsigned in_len,
+		uint8_t* out, unsigned* out_len,
+		void* wrkmem,
+		int compression_level);
+
+/* decompression */
+//int lzo1x_decompress(const uint8_t* src, unsigned src_len,
+//		uint8_t* dst, unsigned* dst_len,
+//		void* wrkmem /* NOT USED */);
+/* safe decompression with overrun testing */
+int lzo1x_decompress_safe(const uint8_t* src, unsigned src_len,
+		uint8_t* dst, unsigned* dst_len,
+		void* wrkmem /* NOT USED */);
+
+#define LZO_E_OK                    0
+#define LZO_E_ERROR                 (-1)
+#define LZO_E_OUT_OF_MEMORY         (-2)    /* [not used right now] */
+#define LZO_E_NOT_COMPRESSIBLE      (-3)    /* [not used right now] */
+#define LZO_E_INPUT_OVERRUN         (-4)
+#define LZO_E_OUTPUT_OVERRUN        (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN    (-6)
+#define LZO_E_EOF_NOT_FOUND         (-7)
+#define LZO_E_INPUT_NOT_CONSUMED    (-8)
+#define LZO_E_NOT_YET_IMPLEMENTED   (-9)    /* [not used right now] */
+
+/* lzo-2.03/include/lzo/lzoconf.h */
+#define LZO_VERSION   0x2030
diff --git a/busybox-1.19.3/include/platform.h b/busybox-1.19.3/include/platform.h
new file mode 100644
index 0000000..aa1bc33
--- /dev/null
+++ b/busybox-1.19.3/include/platform.h
@@ -0,0 +1,503 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+
+
+/* Convenience macros to test the version of gcc. */
+#undef __GNUC_PREREQ
+#if defined __GNUC__ && defined __GNUC_MINOR__
+# define __GNUC_PREREQ(maj, min) \
+		((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+#else
+# define __GNUC_PREREQ(maj, min) 0
+#endif
+
+/* __restrict is known in EGCS 1.2 and above. */
+#if !__GNUC_PREREQ(2,92)
+# ifndef __restrict
+#  define __restrict
+# endif
+#endif
+
+#if !__GNUC_PREREQ(2,7)
+# ifndef __attribute__
+#  define __attribute__(x)
+# endif
+#endif
+
+#undef inline
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ > 199901L
+/* it's a keyword */
+#elif __GNUC_PREREQ(2,7)
+# define inline __inline__
+#else
+# define inline
+#endif
+
+#ifndef __const
+# define __const const
+#endif
+
+#define UNUSED_PARAM __attribute__ ((__unused__))
+#define NORETURN __attribute__ ((__noreturn__))
+/* "The malloc attribute is used to tell the compiler that a function
+ * may be treated as if any non-NULL pointer it returns cannot alias
+ * any other pointer valid when the function returns. This will often
+ * improve optimization. Standard functions with this property include
+ * malloc and calloc. realloc-like functions have this property as long
+ * as the old pointer is never referred to (including comparing it
+ * to the new pointer) after the function returns a non-NULL value."
+ */
+#define RETURNS_MALLOC __attribute__ ((malloc))
+#define PACKED __attribute__ ((__packed__))
+#define ALIGNED(m) __attribute__ ((__aligned__(m)))
+
+/* __NO_INLINE__: some gcc's do not honor inlining! :( */
+#if __GNUC_PREREQ(3,0) && !defined(__NO_INLINE__)
+# define ALWAYS_INLINE __attribute__ ((always_inline)) inline
+/* I've seen a toolchain where I needed __noinline__ instead of noinline */
+# define NOINLINE      __attribute__((__noinline__))
+# if !ENABLE_WERROR
+#  define DEPRECATED __attribute__ ((__deprecated__))
+#  define UNUSED_PARAM_RESULT __attribute__ ((warn_unused_result))
+# else
+#  define DEPRECATED
+#  define UNUSED_PARAM_RESULT
+# endif
+#else
+# define ALWAYS_INLINE inline
+# define NOINLINE
+# define DEPRECATED
+# define UNUSED_PARAM_RESULT
+#endif
+
+/* -fwhole-program makes all symbols local. The attribute externally_visible
+ * forces a symbol global.  */
+#if __GNUC_PREREQ(4,1)
+# define EXTERNALLY_VISIBLE __attribute__(( visibility("default") ))
+//__attribute__ ((__externally_visible__))
+#else
+# define EXTERNALLY_VISIBLE
+#endif
+
+/* At 4.4 gcc become much more anal about this, need to use "aliased" types */
+#if __GNUC_PREREQ(4,4)
+# define FIX_ALIASING __attribute__((__may_alias__))
+#else
+# define FIX_ALIASING
+#endif
+
+/* We use __extension__ in some places to suppress -pedantic warnings
+ * about GCC extensions.  This feature didn't work properly before
+ * gcc 2.8.  */
+#if !__GNUC_PREREQ(2,8)
+# ifndef __extension__
+#  define __extension__
+# 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
+ * optimize statics automatically. FAST_FUNC on static is required
+ * only if you need to match a function pointer's type */
+#if __GNUC_PREREQ(3,0) && defined(i386) /* || defined(__x86_64__)? */
+/* stdcall makes callee to pop arguments from stack, not caller */
+# define FAST_FUNC __attribute__((regparm(3),stdcall))
+/* #elif ... - add your favorite arch today! */
+#else
+# define FAST_FUNC
+#endif
+
+/* Make all declarations hidden (-fvisibility flag only affects definitions) */
+/* (don't include system headers after this until corresponding pop!) */
+#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 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+# 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>
+#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
+#else
+# include <byteswap.h>
+# include <endian.h>
+#endif
+
+#if 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(_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(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_BE32(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) 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) 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 uint16_t bb__aliased_uint16_t FIX_ALIASING;
+typedef uint32_t bb__aliased_uint32_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__)
+# define move_from_unaligned_int(v, intp) ((v) = *(bb__aliased_int*)(intp))
+# 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))
+# define move_to_unaligned32(u32p, v)   (*(bb__aliased_uint32_t*)(u32p) = (v))
+/* #elif ... - add your favorite arch today! */
+#else
+/* performs reasonably well (gcc usually inlines memcpy here) */
+# define move_from_unaligned_int(v, intp) (memcpy(&(v), (intp), sizeof(int)))
+# 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); \
+} while (0)
+# define move_to_unaligned32(u32p, v) do { \
+	uint32_t __t = (v); \
+	memcpy((u32p), &__t, 4); \
+} while (0)
+#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;
+typedef unsigned char smalluint;
+#else
+/* for arches where byte accesses generate larger code: */
+typedef int smallint;
+typedef unsigned smalluint;
+#endif
+
+/* ISO C Standard:  7.16  Boolean type and values  <stdbool.h> */
+#if (defined __digital__ && defined __unix__)
+/* old system without (proper) C99 support */
+# define bool smalluint
+#else
+/* modern system, so use it */
+# include <stdbool.h>
+#endif
+
+
+/*----- Kernel versioning ------------------------------------*/
+
+#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
+
+
+/* ---- 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 1 /* if needed: !defined(arch1) && !defined(arch2) */
+# define ALIGN1 __attribute__((aligned(1)))
+# define ALIGN2 __attribute__((aligned(2)))
+# define ALIGN4 __attribute__((aligned(4)))
+#else
+/* Arches which MUST have 2 or 4 byte alignment for everything are here */
+# define ALIGN1
+# define ALIGN2
+# define ALIGN4
+#endif
+
+/*
+ * 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__)
+# define BB_MMU 0
+# define USE_FOR_NOMMU(...) __VA_ARGS__
+# define USE_FOR_MMU(...)
+#else
+# define BB_MMU 1
+# define USE_FOR_NOMMU(...)
+# define USE_FOR_MMU(...) __VA_ARGS__
+#endif
+
+#if defined(__digital__) && defined(__unix__)
+# include <standards.h>
+# include <inttypes.h>
+# define PRIu32 "u"
+# if !defined ADJ_OFFSET_SINGLESHOT && defined MOD_CLKA && defined MOD_OFFSET
+#  define ADJ_OFFSET_SINGLESHOT (MOD_CLKA | MOD_OFFSET)
+# endif
+# if !defined ADJ_FREQUENCY && defined MOD_FREQUENCY
+#  define ADJ_FREQUENCY MOD_FREQUENCY
+# endif
+# if !defined ADJ_TIMECONST && defined MOD_TIMECONST
+#  define ADJ_TIMECONST MOD_TIMECONST
+# endif
+# if !defined ADJ_TICK && defined MOD_CLKB
+#  define ADJ_TICK MOD_CLKB
+# endif
+#endif
+
+#if defined(__CYGWIN__)
+# define MAXSYMLINKS SYMLOOP_MAX
+#endif
+
+
+/* ---- 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_MAJOR__)
+# if __UCLIBC_MAJOR__ == 0 \
+  && (   __UCLIBC_MINOR__ < 9 \
+     || (__UCLIBC_MINOR__ == 9 && __UCLIBC_SUBLEVEL__ < 31) \
+     )
+#  undef HAVE_STRVERSCMP
+# endif
+#endif
+
+#if defined(__dietlibc__)
+# undef HAVE_STRCHRNUL
+#endif
+
+#if defined(__WATCOMC__)
+# 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
+
+/* 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__)
+# 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
+
+/*
+ * Now, define prototypes for all the functions defined in platform.c
+ * These must come after all the HAVE_* macros are defined (or not)
+ */
+
+#ifndef HAVE_DPRINTF
+extern int dprintf(int fd, const char *format, ...);
+#endif
+
+#ifndef HAVE_MEMRCHR
+extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC;
+#endif
+
+#ifndef HAVE_MKDTEMP
+extern char *mkdtemp(char *template) FAST_FUNC;
+#endif
+
+#ifndef HAVE_SETBIT
+# define setbit(a, b)  ((a)[(b) >> 3] |= 1 << ((b) & 7))
+# 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
+
+#ifndef HAVE_STRCHRNUL
+extern char *strchrnul(const char *s, int c) FAST_FUNC;
+#endif
+
+#ifndef HAVE_STRSEP
+extern char *strsep(char **stringp, const char *delim) FAST_FUNC;
+#endif
+
+#ifndef HAVE_STRSIGNAL
+/* Not exactly the same: instead of "Stopped" it shows "STOP" etc */
+# define strsignal(sig) get_signame(sig)
+#endif
+
+#ifndef HAVE_VASPRINTF
+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
diff --git a/busybox-1.19.3/include/pwd_.h b/busybox-1.19.3/include/pwd_.h
new file mode 100644
index 0000000..e40b71d
--- /dev/null
+++ b/busybox-1.19.3/include/pwd_.h
@@ -0,0 +1,106 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 1991,92,95,96,97,98,99,2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/*
+ * POSIX Standard: 9.2.2 User Database Access	<pwd.h>
+ */
+
+#ifndef BB_PWD_H
+#define BB_PWD_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* This file is #included after #include <pwd.h>
+ * We will use libc-defined structures, but will #define function names
+ * so that function calls are directed to bb_internal_XXX replacements
+ */
+
+#define setpwent    bb_internal_setpwent
+#define endpwent    bb_internal_endpwent
+#define getpwent    bb_internal_getpwent
+#define fgetpwent   bb_internal_fgetpwent
+#define putpwent    bb_internal_putpwent
+#define getpwuid    bb_internal_getpwuid
+#define getpwnam    bb_internal_getpwnam
+#define getpwent_r  bb_internal_getpwent_r
+#define getpwuid_r  bb_internal_getpwuid_r
+#define getpwnam_r  bb_internal_getpwnam_r
+#define fgetpwent_r bb_internal_fgetpwent_r
+
+
+/* All function names below should be remapped by #defines above
+ * in order to not collide with libc names. */
+
+
+/* Rewind the password-file stream.  */
+extern void setpwent(void);
+
+/* Close the password-file stream.  */
+extern void endpwent(void);
+
+#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
+/* Read an entry from the password-file stream, opening it if necessary.  */
+extern struct passwd *getpwent(void);
+
+/* Read an entry from STREAM.  */
+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);
+#endif
+
+/* Search for an entry with a matching user ID.  */
+extern struct passwd *getpwuid(uid_t __uid);
+
+/* Search for an entry with a matching username.  */
+extern struct passwd *getpwnam(const char *__name);
+
+/* Reentrant versions of some of the functions above.
+
+   PLEASE NOTE: the `getpwent_r' function is not (yet) standardized.
+   The interface may change in later versions of this library.  But
+   the interface is designed following the principals used for the
+   other reentrant functions so the chances are good this is what the
+   POSIX people would choose.  */
+
+extern int getpwent_r(struct passwd *__restrict __resultbuf,
+		       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);
+
+extern int getpwnam_r(const char *__restrict __name,
+		       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);
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/rtc_.h b/busybox-1.19.3/include/rtc_.h
new file mode 100644
index 0000000..750fc20
--- /dev/null
+++ b/busybox-1.19.3/include/rtc_.h
@@ -0,0 +1,76 @@
+/*
+ * Common defines/structures/etc... for applets that need to work with the RTC.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#ifndef BB_RTC_H
+#define BB_RTC_H 1
+
+#include "libbb.h"
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+int rtc_adjtime_is_utc(void) FAST_FUNC;
+int rtc_xopen(const char **default_rtc, int flags) FAST_FUNC;
+void rtc_read_tm(struct tm *ptm, int fd) FAST_FUNC;
+time_t rtc_tm2time(struct tm *ptm, int utc) FAST_FUNC;
+
+
+/*
+ * Everything below this point has been copied from linux/rtc.h
+ * to eliminate the kernel header dependency
+ */
+
+struct linux_rtc_time {
+	int tm_sec;
+	int tm_min;
+	int tm_hour;
+	int tm_mday;
+	int tm_mon;
+	int tm_year;
+	int tm_wday;
+	int tm_yday;
+	int tm_isdst;
+};
+
+struct linux_rtc_wkalrm {
+	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 */
+};
+
+/*
+ * ioctl calls that are permitted to the /dev/rtc interface, if
+ * any of the RTC drivers are enabled.
+ */
+#define RTC_AIE_ON      _IO('p', 0x01)  /* Alarm int. enable on         */
+#define RTC_AIE_OFF     _IO('p', 0x02)  /* ... off                      */
+#define RTC_UIE_ON      _IO('p', 0x03)  /* Update int. enable on        */
+#define RTC_UIE_OFF     _IO('p', 0x04)  /* ... off                      */
+#define RTC_PIE_ON      _IO('p', 0x05)  /* Periodic int. enable on      */
+#define RTC_PIE_OFF     _IO('p', 0x06)  /* ... off                      */
+#define RTC_WIE_ON      _IO('p', 0x0f)  /* Watchdog int. enable on      */
+#define RTC_WIE_OFF     _IO('p', 0x10)  /* ... off                      */
+
+#define RTC_ALM_SET     _IOW('p', 0x07, struct linux_rtc_time) /* Set alarm time  */
+#define RTC_ALM_READ    _IOR('p', 0x08, struct linux_rtc_time) /* Read alarm time */
+#define RTC_RD_TIME     _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time   */
+#define RTC_SET_TIME    _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time    */
+#define RTC_IRQP_READ   _IOR('p', 0x0b, unsigned long)   /* Read IRQ rate   */
+#define RTC_IRQP_SET    _IOW('p', 0x0c, unsigned long)   /* Set IRQ rate    */
+#define RTC_EPOCH_READ  _IOR('p', 0x0d, unsigned long)   /* Read epoch      */
+#define RTC_EPOCH_SET   _IOW('p', 0x0e, unsigned long)   /* Set epoch       */
+
+#define RTC_WKALM_SET   _IOW('p', 0x0f, struct linux_rtc_wkalrm)/* Set wakeup alarm*/
+#define RTC_WKALM_RD    _IOR('p', 0x10, struct linux_rtc_wkalrm)/* Get wakeup alarm*/
+
+/* interrupt flags */
+#define RTC_IRQF 0x80 /* any of the following is active */
+#define RTC_PF 0x40
+#define RTC_AF 0x20
+#define RTC_UF 0x10
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/shadow_.h b/busybox-1.19.3/include/shadow_.h
new file mode 100644
index 0000000..648a62a
--- /dev/null
+++ b/busybox-1.19.3/include/shadow_.h
@@ -0,0 +1,106 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 1996, 1997, 1998, 1999 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C 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.
+
+   The GNU C 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 the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* Declaration of types and functions for shadow password suite */
+
+#ifndef BB_SHADOW_H
+#define BB_SHADOW_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* Structure of the password file */
+struct spwd {
+	char *sp_namp;          /* Login name */
+	char *sp_pwdp;          /* Encrypted password */
+	long sp_lstchg;         /* Date of last change */
+	long sp_min;            /* Minimum number of days between changes */
+	long sp_max;            /* Maximum number of days between changes */
+	long sp_warn;           /* Number of days to warn user to change the password */
+	long sp_inact;          /* Number of days the account may be inactive */
+	long sp_expire;         /* Number of days since 1970-01-01 until account expires */
+	unsigned long sp_flag;  /* Reserved */
+};
+
+#define setspent    bb_internal_setspent
+#define endspent    bb_internal_endspent
+#define getspent    bb_internal_getspent
+#define getspnam    bb_internal_getspnam
+#define sgetspent   bb_internal_sgetspent
+#define fgetspent   bb_internal_fgetspent
+#define putspent    bb_internal_putspent
+#define getspent_r  bb_internal_getspent_r
+#define getspnam_r  bb_internal_getspnam_r
+#define sgetspent_r bb_internal_sgetspent_r
+#define fgetspent_r bb_internal_fgetspent_r
+#define lckpwdf     bb_internal_lckpwdf
+#define ulckpwdf    bb_internal_ulckpwdf
+
+
+/* All function names below should be remapped by #defines above
+ * in order to not collide with libc names. */
+
+#ifdef UNUSED_FOR_NOW
+/* Open database for reading */
+extern void setspent(void);
+
+/* Close database */
+extern void endspent(void);
+
+/* Get next entry from database, perhaps after opening the file */
+extern struct spwd *getspent(void);
+
+/* Get shadow entry matching NAME */
+extern struct spwd *getspnam(const char *__name);
+
+/* Read shadow entry from STRING */
+extern struct spwd *sgetspent(const char *__string);
+
+/* Read next shadow entry from STREAM */
+extern struct spwd *fgetspent(FILE *__stream);
+
+/* Write line containing shadow password entry to stream */
+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);
+#endif
+
+extern int getspnam_r(const char *__name, struct spwd *__result_buf,
+		       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);
+
+extern int fgetspent_r(FILE *__stream, struct spwd *__result_buf,
+			char *__buffer, size_t __buflen,
+			struct spwd **__result);
+/* Protect password file against multi writers */
+extern int lckpwdf(void);
+
+/* Unlock password file */
+extern int ulckpwdf(void);
+#endif
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif /* shadow.h */
diff --git a/busybox-1.19.3/include/unicode.h b/busybox-1.19.3/include/unicode.h
new file mode 100644
index 0000000..0317a21
--- /dev/null
+++ b/busybox-1.19.3/include/unicode.h
@@ -0,0 +1,129 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#ifndef UNICODE_H
+#define UNICODE_H 1
+
+#if ENABLE_UNICODE_USING_LOCALE
+# include <wchar.h>
+# include <wctype.h>
+#endif
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+enum {
+	UNICODE_UNKNOWN = 0,
+	UNICODE_OFF = 1,
+	UNICODE_ON = 2,
+};
+
+#define unicode_bidi_isrtl(wc) 0
+#define unicode_bidi_is_neutral_wchar(wc) (wc <= 126 && !isalpha(wc))
+
+#if !ENABLE_UNICODE_SUPPORT
+
+# define unicode_strlen(string)   strlen(string)
+# define unicode_strwidth(string) strlen(string)
+# define unicode_status UNICODE_OFF
+# define init_unicode() ((void)0)
+# define reinit_unicode(LANG) ((void)0)
+
+#else
+
+# if CONFIG_LAST_SUPPORTED_WCHAR < 126 || CONFIG_LAST_SUPPORTED_WCHAR >= 0x30000
+#  undef CONFIG_LAST_SUPPORTED_WCHAR
+#  define CONFIG_LAST_SUPPORTED_WCHAR 0x2ffff
+# endif
+
+# if CONFIG_LAST_SUPPORTED_WCHAR < 0x300
+#  undef ENABLE_UNICODE_COMBINING_WCHARS
+#  define ENABLE_UNICODE_COMBINING_WCHARS 0
+# endif
+
+# if CONFIG_LAST_SUPPORTED_WCHAR < 0x1100
+#  undef ENABLE_UNICODE_WIDE_WCHARS
+#  define ENABLE_UNICODE_WIDE_WCHARS 0
+# endif
+
+# if CONFIG_LAST_SUPPORTED_WCHAR < 0x590
+#  undef  ENABLE_UNICODE_BIDI_SUPPORT
+#  define ENABLE_UNICODE_BIDI_SUPPORT 0
+# endif
+
+/* Number of unicode chars. Falls back to strlen() on invalid unicode */
+size_t FAST_FUNC unicode_strlen(const char *string);
+/* Width on terminal */
+size_t FAST_FUNC unicode_strwidth(const char *string);
+enum {
+	UNI_FLAG_PAD = (1 << 0),
+};
+//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);
+//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
+
+/* Homegrown Unicode support. It knows only C and Unicode locales. */
+
+#  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
+#  define MB_CUR_MAX 6
+
+/* Prevent name collisions */
+#  define wint_t    bb_wint_t
+#  define mbstate_t bb_mbstate_t
+#  define mbstowcs  bb_mbstowcs
+#  define wcstombs  bb_wcstombs
+#  define wcrtomb   bb_wcrtomb
+#  define iswspace  bb_iswspace
+#  define iswalnum  bb_iswalnum
+#  define iswpunct  bb_iswpunct
+#  define wcwidth   bb_wcwidth
+
+typedef int32_t wint_t;
+typedef struct {
+	char bogus;
+} mbstate_t;
+
+size_t mbstowcs(wchar_t *dest, const char *src, size_t n) FAST_FUNC;
+size_t wcstombs(char *dest, const wchar_t *src, size_t n) FAST_FUNC;
+size_t wcrtomb(char *s, wchar_t wc, mbstate_t *ps) FAST_FUNC;
+int iswspace(wint_t wc) FAST_FUNC;
+int iswalnum(wint_t wc) FAST_FUNC;
+int iswpunct(wint_t wc) FAST_FUNC;
+int wcwidth(unsigned ucs) FAST_FUNC;
+#  if ENABLE_UNICODE_BIDI_SUPPORT
+#   undef unicode_bidi_isrtl
+int unicode_bidi_isrtl(wint_t wc) FAST_FUNC;
+#   if ENABLE_UNICODE_NEUTRAL_TABLE
+#    undef unicode_bidi_is_neutral_wchar
+int unicode_bidi_is_neutral_wchar(wint_t wc) FAST_FUNC;
+#   endif
+#  endif
+
+
+# endif /* !UNICODE_USING_LOCALE */
+
+#endif /* UNICODE_SUPPORT */
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/include/usage.src.h b/busybox-1.19.3/include/usage.src.h
new file mode 100644
index 0000000..78beccf
--- /dev/null
+++ b/busybox-1.19.3/include/usage.src.h
@@ -0,0 +1,22 @@
+/* vi: set sw=8 ts=8: */
+/*
+ * This file suffers from chronically incorrect tabification
+ * of messages. Before editing this file:
+ * 1. Switch you editor to 8-space tab mode.
+ * 2. Do not use \t in messages, use real tab character.
+ * 3. Start each source line with message as follows:
+ *    |<7 spaces>"text with tabs"....
+ * or
+ *    |<5 spaces>"\ntext with tabs"....
+ */
+#ifndef BB_USAGE_H
+#define BB_USAGE_H 1
+
+#define NOUSAGE_STR "\b"
+
+INSERT
+
+#define busybox_notes_usage \
+       "Hello world!\n"
+
+#endif
diff --git a/busybox-1.19.3/include/volume_id.h b/busybox-1.19.3/include/volume_id.h
new file mode 100644
index 0000000..4a78cd1
--- /dev/null
+++ b/busybox-1.19.3/include/volume_id.h
@@ -0,0 +1,31 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+char *get_devname_from_label(const char *spec);
+char *get_devname_from_uuid(const char *spec);
+void display_uuid_cache(void);
+
+/* Returns:
+ * 0: no UUID= or LABEL= prefix found
+ * 1: UUID= or LABEL= prefix found. In this case,
+ *    *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);
diff --git a/busybox-1.19.3/include/xatonum.h b/busybox-1.19.3/include/xatonum.h
new file mode 100644
index 0000000..6f76a3c
--- /dev/null
+++ b/busybox-1.19.3/include/xatonum.h
@@ -0,0 +1,176 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ascii-to-numbers implementations for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* Provides extern declarations of functions */
+#define DECLARE_STR_CONV(type, T, UT) \
+\
+unsigned type xstrto##UT##_range_sfx(const char *str, int b, unsigned type l, unsigned type u, const struct suffix_mult *sfx) FAST_FUNC; \
+unsigned type xstrto##UT##_range(const char *str, int b, unsigned type l, unsigned type u) FAST_FUNC; \
+unsigned type xstrto##UT##_sfx(const char *str, int b, const struct suffix_mult *sfx) FAST_FUNC; \
+unsigned type xstrto##UT(const char *str, int b) FAST_FUNC; \
+unsigned type xato##UT##_range_sfx(const char *str, unsigned type l, unsigned type u, const struct suffix_mult *sfx) FAST_FUNC; \
+unsigned type xato##UT##_range(const char *str, unsigned type l, unsigned type u) FAST_FUNC; \
+unsigned type xato##UT##_sfx(const char *str, const struct suffix_mult *sfx) FAST_FUNC; \
+unsigned type xato##UT(const char *str) FAST_FUNC; \
+type xstrto##T##_range_sfx(const char *str, int b, type l, type u, const struct suffix_mult *sfx) FAST_FUNC; \
+type xstrto##T##_range(const char *str, int b, type l, type u) FAST_FUNC; \
+type xstrto##T(const char *str, int b) FAST_FUNC; \
+type xato##T##_range_sfx(const char *str, type l, type u, const struct suffix_mult *sfx) FAST_FUNC; \
+type xato##T##_range(const char *str, type l, type u) FAST_FUNC; \
+type xato##T##_sfx(const char *str, const struct suffix_mult *sfx) FAST_FUNC; \
+type xato##T(const char *str) FAST_FUNC; \
+
+/* Unsigned long long functions always exist */
+DECLARE_STR_CONV(long long, ll, ull)
+
+
+/* Provides inline definitions of functions */
+/* (useful for mapping them to the type of the same width) */
+#define DEFINE_EQUIV_STR_CONV(narrow, N, W, UN, UW) \
+\
+static ALWAYS_INLINE \
+unsigned narrow xstrto##UN##_range_sfx(const char *str, int b, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \
+{ return xstrto##UW##_range_sfx(str, b, l, u, sfx); } \
+static ALWAYS_INLINE \
+unsigned narrow xstrto##UN##_range(const char *str, int b, unsigned narrow l, unsigned narrow u) \
+{ return xstrto##UW##_range(str, b, l, u); } \
+static ALWAYS_INLINE \
+unsigned narrow xstrto##UN##_sfx(const char *str, int b, const struct suffix_mult *sfx) \
+{ return xstrto##UW##_sfx(str, b, sfx); } \
+static ALWAYS_INLINE \
+unsigned narrow xstrto##UN(const char *str, int b) \
+{ return xstrto##UW(str, b); } \
+static ALWAYS_INLINE \
+unsigned narrow xato##UN##_range_sfx(const char *str, unsigned narrow l, unsigned narrow u, const struct suffix_mult *sfx) \
+{ return xato##UW##_range_sfx(str, l, u, sfx); } \
+static ALWAYS_INLINE \
+unsigned narrow xato##UN##_range(const char *str, unsigned narrow l, unsigned narrow u) \
+{ return xato##UW##_range(str, l, u); } \
+static ALWAYS_INLINE \
+unsigned narrow xato##UN##_sfx(const char *str, const struct suffix_mult *sfx) \
+{ return xato##UW##_sfx(str, sfx); } \
+static ALWAYS_INLINE \
+unsigned narrow xato##UN(const char *str) \
+{ return xato##UW(str); } \
+static ALWAYS_INLINE \
+narrow xstrto##N##_range_sfx(const char *str, int b, narrow l, narrow u, const struct suffix_mult *sfx) \
+{ return xstrto##W##_range_sfx(str, b, l, u, sfx); } \
+static ALWAYS_INLINE \
+narrow xstrto##N##_range(const char *str, int b, narrow l, narrow u) \
+{ return xstrto##W##_range(str, b, l, u); } \
+static ALWAYS_INLINE \
+narrow xstrto##N(const char *str, int b) \
+{ return xstrto##W(str, b); } \
+static ALWAYS_INLINE \
+narrow xato##N##_range_sfx(const char *str, narrow l, narrow u, const struct suffix_mult *sfx) \
+{ return xato##W##_range_sfx(str, l, u, sfx); } \
+static ALWAYS_INLINE \
+narrow xato##N##_range(const char *str, narrow l, narrow u) \
+{ return xato##W##_range(str, l, u); } \
+static ALWAYS_INLINE \
+narrow xato##N##_sfx(const char *str, const struct suffix_mult *sfx) \
+{ return xato##W##_sfx(str, sfx); } \
+static ALWAYS_INLINE \
+narrow xato##N(const char *str) \
+{ return xato##W(str); } \
+
+/* If long == long long, then just map them one-to-one */
+#if ULONG_MAX == ULLONG_MAX
+DEFINE_EQUIV_STR_CONV(long, l, ll, ul, ull)
+#else
+/* Else provide extern defs */
+DECLARE_STR_CONV(long, l, ul)
+#endif
+
+/* Same for int -> [long] long */
+#if UINT_MAX == ULLONG_MAX
+DEFINE_EQUIV_STR_CONV(int, i, ll, u, ull)
+#elif UINT_MAX == ULONG_MAX
+DEFINE_EQUIV_STR_CONV(int, i, l, u, ul)
+#else
+DECLARE_STR_CONV(int, i, u)
+#endif
+
+/* Specialized */
+
+uint32_t BUG_xatou32_unimplemented(void);
+static ALWAYS_INLINE uint32_t xatou32(const char *numstr)
+{
+	if (UINT_MAX == 0xffffffff)
+		return xatou(numstr);
+	if (ULONG_MAX == 0xffffffff)
+		return xatoul(numstr);
+	return BUG_xatou32_unimplemented();
+}
+
+/* Non-aborting kind of convertors: bb_strto[u][l]l */
+
+/* On exit: errno = 0 only if there was non-empty, '\0' terminated value
+ * errno = EINVAL if value was not '\0' terminated, but otherwise ok
+ *    Return value is still valid, caller should just check whether end[0]
+ *    is a valid terminating char for particular case. OTOH, if caller
+ *    requires '\0' terminated input, [s]he can just check errno == 0.
+ * errno = ERANGE if value had alphanumeric terminating char ("1234abcg").
+ * errno = ERANGE if value is out of range, missing, etc.
+ * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok )
+ *    return value is all-ones in this case.
+ */
+
+unsigned long long bb_strtoull(const char *arg, char **endp, int base) FAST_FUNC;
+long long bb_strtoll(const char *arg, char **endp, int base) FAST_FUNC;
+
+#if ULONG_MAX == ULLONG_MAX
+static ALWAYS_INLINE
+unsigned long bb_strtoul(const char *arg, char **endp, int base)
+{ return bb_strtoull(arg, endp, base); }
+static ALWAYS_INLINE
+long bb_strtol(const char *arg, char **endp, int base)
+{ return bb_strtoll(arg, endp, base); }
+#else
+unsigned long bb_strtoul(const char *arg, char **endp, int base) FAST_FUNC;
+long bb_strtol(const char *arg, char **endp, int base) FAST_FUNC;
+#endif
+
+#if UINT_MAX == ULLONG_MAX
+static ALWAYS_INLINE
+unsigned bb_strtou(const char *arg, char **endp, int base)
+{ return bb_strtoull(arg, endp, base); }
+static ALWAYS_INLINE
+int bb_strtoi(const char *arg, char **endp, int base)
+{ return bb_strtoll(arg, endp, base); }
+#elif UINT_MAX == ULONG_MAX
+static ALWAYS_INLINE
+unsigned bb_strtou(const char *arg, char **endp, int base)
+{ return bb_strtoul(arg, endp, base); }
+static ALWAYS_INLINE
+int bb_strtoi(const char *arg, char **endp, int base)
+{ return bb_strtol(arg, endp, base); }
+#else
+unsigned bb_strtou(const char *arg, char **endp, int base) FAST_FUNC;
+int bb_strtoi(const char *arg, char **endp, int base) FAST_FUNC;
+#endif
+
+uint32_t BUG_bb_strtou32_unimplemented(void);
+static ALWAYS_INLINE
+uint32_t bb_strtou32(const char *arg, char **endp, int base)
+{
+	if (sizeof(uint32_t) == sizeof(unsigned))
+		return bb_strtou(arg, endp, base);
+	if (sizeof(uint32_t) == sizeof(unsigned long))
+		return bb_strtoul(arg, endp, base);
+	return BUG_bb_strtou32_unimplemented();
+}
+
+/* Floating point */
+
+double bb_strtod(const char *arg, char **endp) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/busybox-1.19.3/include/xregex.h b/busybox-1.19.3/include/xregex.h
new file mode 100644
index 0000000..5e5e6a2
--- /dev/null
+++ b/busybox-1.19.3/include/xregex.h
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox xregcomp utility routine.  This isn't in libbb.h because the
+ * 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 GPL.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#ifndef BB_REGEX_H
+#define BB_REGEX_H 1
+
+#include <regex.h>
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+char* regcomp_or_errmsg(regex_t *preg, const char *regex, int cflags) FAST_FUNC;
+void xregcomp(regex_t *preg, const char *regex, int cflags) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/init/Config.src b/busybox-1.19.3/init/Config.src
new file mode 100644
index 0000000..5767c93
--- /dev/null
+++ b/busybox-1.19.3/init/Config.src
@@ -0,0 +1,10 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Init Utilities"
+
+INSERT
+
+endmenu
diff --git a/busybox-1.19.3/init/Kbuild.src b/busybox-1.19.3/init/Kbuild.src
new file mode 100644
index 0000000..6b4fb74
--- /dev/null
+++ b/busybox-1.19.3/init/Kbuild.src
@@ -0,0 +1,9 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
diff --git a/busybox-1.19.3/init/bootchartd.c b/busybox-1.19.3/init/bootchartd.c
new file mode 100644
index 0000000..5f6121f
--- /dev/null
+++ b/busybox-1.19.3/init/bootchartd.c
@@ -0,0 +1,451 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+//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
+//config:	  by the kernel as the init process. This is configured by adding
+//config:	  the init=/sbin/bootchartd option to the kernel command line.
+//config:
+//config:	  It can also be used to monitor the resource usage of a specific
+//config:	  application or the running system in general. In this case,
+//config:	  bootchartd is started interactively by running bootchartd start
+//config:	  and stopped using bootchartd stop.
+//config:
+//config:config FEATURE_BOOTCHARTD_BLOATED_HEADER
+//config:	bool "Compatible, bloated header"
+//config:	default y
+//config:	depends on BOOTCHARTD
+//config:	help
+//config:	  Create extended header file compatible with "big" bootchartd.
+//config:	  "Big" bootchartd is a shell script and it dumps some
+//config:	  "convenient" info int the header, such as:
+//config:	    title = Boot chart for `hostname` (`date`)
+//config:	    system.uname = `uname -srvm`
+//config:	    system.release = `cat /etc/DISTRO-release`
+//config:	    system.cpu = `grep '^model name' /proc/cpuinfo | head -1` ($cpucount)
+//config:	    system.kernel.options = `cat /proc/cmdline`
+//config:	  This data is not mandatory for bootchart graph generation,
+//config:	  and is considered bloat. Nevertheless, this option
+//config:	  makes bootchartd applet to dump a subset of it.
+//config:
+//config:config FEATURE_BOOTCHARTD_CONFIG_FILE
+//config:	bool "Support bootchartd.conf"
+//config:	default y
+//config:	depends on BOOTCHARTD
+//config:	help
+//config:	  Enable reading and parsing of $PWD/bootchartd.conf
+//config:	  and /etc/bootchartd.conf files.
+
+#include "libbb.h"
+/* After libbb.h, since it needs sys/types.h on some systems */
+#include <sys/utsname.h>
+
+#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"
+
+/* For debugging, set to 0:
+ * strace won't work with DO_SIGNAL_SYNC set to 1.
+ */
+#define DO_SIGNAL_SYNC 1
+
+
+//$PWD/bootchartd.conf and /etc/bootchartd.conf:
+//supported options:
+//# Sampling period (in seconds)
+//SAMPLE_PERIOD=0.2
+//
+//not yet supported:
+//# tmpfs size
+//# (32 MB should suffice for ~20 minutes worth of log data, but YMMV)
+//TMPFS_SIZE=32m
+//
+//# Whether to enable and store BSD process accounting information.  The
+//# kernel needs to be configured to enable v3 accounting
+//# (CONFIG_BSD_PROCESS_ACCT_V3). accton from the GNU accounting utilities
+//# is also required.
+//PROCESS_ACCOUNTING="no"
+//
+//# Tarball for the various boot log files
+//BOOTLOG_DEST=/var/log/bootchart.tgz
+//
+//# Whether to automatically stop logging as the boot process completes.
+//# The logger will look for known processes that indicate bootup completion
+//# at a specific runlevel (e.g. gdm-binary, mingetty, etc.).
+//AUTO_STOP_LOGGER="yes"
+//
+//# Whether to automatically generate the boot chart once the boot logger
+//# completes.  The boot chart will be generated in $AUTO_RENDER_DIR.
+//# Note that the bootchart package must be installed.
+//AUTO_RENDER="no"
+//
+//# Image format to use for the auto-generated boot chart
+//# (choose between png, svg and eps).
+//AUTO_RENDER_FORMAT="png"
+//
+//# Output directory for auto-generated boot charts
+//AUTO_RENDER_DIR="/var/log"
+
+
+/* Globals */
+struct globals {
+	char jiffy_line[COMMON_BUFSIZE];
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
+
+static void dump_file(FILE *fp, const char *filename)
+{
+	int fd = open(filename, O_RDONLY);
+	if (fd >= 0) {
+		fputs(G.jiffy_line, fp);
+		fflush(fp);
+		bb_copyfd_eof(fd, fileno(fp));
+		close(fd);
+		fputc('\n', fp);
+	}
+}
+
+static int dump_procs(FILE *fp, int look_for_login_process)
+{
+	struct dirent *entry;
+	DIR *dir = opendir("/proc");
+	int found_login_process = 0;
+
+	fputs(G.jiffy_line, fp);
+	while ((entry = readdir(dir)) != NULL) {
+		char name[sizeof("/proc/%u/cmdline") + sizeof(int)*3];
+		int stat_fd;
+		unsigned pid = bb_strtou(entry->d_name, NULL, 10);
+		if (errno)
+			continue;
+
+		/* Android's version reads /proc/PID/cmdline and extracts
+		 * non-truncated process name. Do we want to do that? */
+
+		sprintf(name, "/proc/%u/stat", pid);
+		stat_fd = open(name, O_RDONLY);
+		if (stat_fd >= 0) {
+			char *p;
+			char stat_line[4*1024];
+			int rd = safe_read(stat_fd, stat_line, sizeof(stat_line)-2);
+
+			close(stat_fd);
+			if (rd < 0)
+				continue;
+			stat_line[rd] = '\0';
+			p = strchrnul(stat_line, '\n');
+			*p++ = '\n';
+			*p = '\0';
+			fputs(stat_line, fp);
+			if (!look_for_login_process)
+				continue;
+			p = strchr(stat_line, '(');
+			if (!p)
+				continue;
+			p++;
+			strchrnul(p, ')')[0] = '\0';
+			/* Is it gdm, kdm or a getty? */
+			if (((p[0] == 'g' || p[0] == 'k' || p[0] == 'x') && p[1] == 'd' && p[2] == 'm')
+			 || strstr(p, "getty")
+			) {
+				found_login_process = 1;
+			}
+		}
+	}
+	closedir(dir);
+	fputc('\n', fp);
+	return found_login_process;
+}
+
+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.
+		 * Try a few locations which are likely ti exist.
+		 */
+		static const char dirs[] = "/mnt\0""/tmp\0""/boot\0""/proc\0";
+		const char *try_dir = dirs;
+		while (mount("none", try_dir, "tmpfs", MS_SILENT, "size=16m") != 0) {
+			try_dir += strlen(try_dir) + 1;
+			if (!try_dir[0])
+				bb_perror_msg_and_die("can't %smount tmpfs", "");
+		}
+		//bb_error_msg("mounted tmpfs on %s", try_dir);
+		xchdir(try_dir);
+		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)
+{
+	//# 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");
+	FILE *proc_ps = xfopen("proc_ps.log", "w");
+	int look_for_login_process = (getppid() == 1);
+	unsigned count = 60*1000*1000 / sample_period_us; /* ~1 minute */
+
+	while (--count && !bb_got_signal) {
+		char *p;
+		int len = open_read_close("/proc/uptime", G.jiffy_line, sizeof(G.jiffy_line)-2);
+		if (len < 0)
+			goto wait_more;
+		/* /proc/uptime has format "NNNNNN.MM NNNNNNN.MM" */
+		/* we convert it to "NNNNNNMM\n" (using first value) */
+		G.jiffy_line[len] = '\0';
+		p = strchr(G.jiffy_line, '.');
+		if (!p)
+			goto wait_more;
+		while (isdigit(*++p))
+			p[-1] = *p;
+		p[-1] = '\n';
+		p[0] = '\0';
+
+		dump_file(proc_stat, "/proc/stat");
+		dump_file(proc_diskstats, "/proc/diskstats");
+		//dump_file(proc_netdev, "/proc/net/dev");
+		if (dump_procs(proc_ps, look_for_login_process)) {
+			/* dump_procs saw a getty or {g,k,x}dm
+			 * stop logging in 2 seconds:
+			 */
+			if (count > 2*1000*1000 / sample_period_us)
+				count = 2*1000*1000 / sample_period_us;
+		}
+		fflush_all();
+ wait_more:
+		usleep(sample_period_us);
+	}
+
+	// [ -e kernel_pacct ] && accton off
+}
+
+static void finalize(char *tempdir, const char *prog)
+{
+	//# Stop process accounting if configured
+	//local pacct=
+	//[ -e kernel_pacct ] && pacct=kernel_pacct
+
+	FILE *header_fp = xfopen("header", "w");
+
+	if (prog)
+		fprintf(header_fp, "profile.process = %s\n", prog);
+
+	fputs("version = "BC_VERSION_STR"\n", header_fp);
+	if (ENABLE_FEATURE_BOOTCHARTD_BLOATED_HEADER) {
+		char *hostname;
+		char *kcmdline;
+		time_t t;
+		struct tm tm_time;
+		/* x2 for possible localized weekday/month names */
+		char date_buf[sizeof("Mon Jun 21 05:29:03 CEST 2010") * 2];
+		struct utsname unamebuf;
+
+		hostname = safe_gethostname();
+		time(&t);
+		localtime_r(&t, &tm_time);
+		strftime(date_buf, sizeof(date_buf), "%a %b %e %H:%M:%S %Z %Y", &tm_time);
+		fprintf(header_fp, "title = Boot chart for %s (%s)\n", hostname, date_buf);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(hostname);
+
+		uname(&unamebuf); /* never fails */
+		/* same as uname -srvm */
+		fprintf(header_fp, "system.uname = %s %s %s %s\n",
+				unamebuf.sysname,
+				unamebuf.release,
+				unamebuf.version,
+				unamebuf.machine
+		);
+
+		//system.release = `cat /etc/DISTRO-release`
+		//system.cpu = `grep '^model name' /proc/cpuinfo | head -1` ($cpucount)
+
+		kcmdline = xmalloc_open_read_close("/proc/cmdline", NULL);
+		/* kcmdline includes trailing "\n" */
+		fprintf(header_fp, "system.kernel.options = %s", kcmdline);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(kcmdline);
+	}
+	fclose(header_fp);
+
+	/* Package log files */
+	system("tar -zcf /var/log/bootchart.tgz header *.log"); // + $pacct
+	/* Clean up (if we are not in detached tmpfs) */
+	if (tempdir) {
+		unlink("header");
+		unlink("proc_stat.log");
+		unlink("proc_diskstats.log");
+		//unlink("proc_netdev.log");
+		unlink("proc_ps.log");
+		rmdir(tempdir);
+	}
+
+	/* shell-based bootchartd tries to run /usr/bin/bootchart if $AUTO_RENDER=yes:
+	 * /usr/bin/bootchart -o "$AUTO_RENDER_DIR" -f $AUTO_RENDER_FORMAT "$BOOTLOG_DEST"
+	 */
+}
+
+//usage:#define bootchartd_trivial_usage
+//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:     "\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)"
+//usage:     "\nUnder PID 1: as init, then exec $bootchart_init, /init, /sbin/init"
+
+int bootchartd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int bootchartd_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned sample_period_us;
+	pid_t parent_pid, logger_pid;
+	smallint cmd;
+	enum {
+		CMD_STOP = 0,
+		CMD_START,
+		CMD_INIT,
+		CMD_PID1, /* used to mark pid 1 case */
+	};
+
+	INIT_G();
+
+	parent_pid = getpid();
+	if (argv[1]) {
+		cmd = index_in_strings("stop\0""start\0""init\0", argv[1]);
+		if (cmd < 0)
+			bb_show_usage();
+		if (cmd == CMD_STOP) {
+			pid_t *pidList = find_pid_by_name("bootchartd");
+			while (*pidList != 0) {
+				if (*pidList != parent_pid)
+					kill(*pidList, SIGUSR1);
+				pidList++;
+			}
+			return EXIT_SUCCESS;
+		}
+	} else {
+		if (parent_pid != 1)
+			bb_show_usage();
+		cmd = CMD_PID1;
+	}
+
+	/* Here we are in START, INIT or CMD_PID1 state */
+
+	/* Read config file: */
+	sample_period_us = 200 * 1000;
+	if (ENABLE_FEATURE_BOOTCHARTD_CONFIG_FILE) {
+		char* token[2];
+		parser_t *parser = config_open2("/etc/bootchartd.conf" + 5, fopen_for_read);
+		if (!parser)
+			parser = config_open2("/etc/bootchartd.conf", fopen_for_read);
+		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;
+		}
+		config_close(parser);
+	}
+	if ((int)sample_period_us <= 0)
+		sample_period_us = 1; /* prevent division by 0 */
+
+	/* Create logger child: */
+	logger_pid = fork_or_rexec(argv);
+
+	if (logger_pid == 0) { /* child */
+		char *tempdir;
+
+		bb_signals(0
+			+ (1 << SIGUSR1)
+			+ (1 << SIGUSR2)
+			+ (1 << SIGTERM)
+			+ (1 << SIGQUIT)
+			+ (1 << SIGINT)
+			+ (1 << SIGHUP)
+			, record_signo);
+
+		if (DO_SIGNAL_SYNC)
+			/* Inform parent that we are ready */
+			raise(SIGSTOP);
+
+		/* If we are started by kernel, PATH might be unset.
+		 * In order to find "tar", let's set some sane PATH:
+		 */
+		if (cmd == CMD_PID1 && !getenv("PATH"))
+			putenv((char*)bb_PATH_root_path);
+
+		tempdir = make_tempdir();
+		do_logging(sample_period_us);
+		finalize(tempdir, cmd == CMD_START ? argv[2] : NULL);
+		return EXIT_SUCCESS;
+	}
+
+	/* parent */
+
+	if (DO_SIGNAL_SYNC) {
+		/* Wait for logger child to set handlers, then unpause it.
+		 * Otherwise with short-lived PROG (e.g. "bootchartd start true")
+		 * we might send SIGUSR1 before logger sets its handler.
+		 */
+		waitpid(logger_pid, NULL, WUNTRACED);
+		kill(logger_pid, SIGCONT);
+	}
+
+	if (cmd == CMD_PID1) {
+		char *bootchart_init = getenv("bootchart_init");
+		if (bootchart_init)
+			execl(bootchart_init, bootchart_init, NULL);
+		execl("/init", "init", NULL);
+		execl("/sbin/init", "init", NULL);
+		bb_perror_msg_and_die("can't execute '%s'", "/sbin/init");
+	}
+
+	if (cmd == CMD_START && argv[2]) { /* "start PROG ARGS" */
+		pid_t pid = xvfork();
+		if (pid == 0) { /* child */
+			argv += 2;
+			BB_EXECVP_or_die(argv);
+		}
+		/* parent */
+		waitpid(pid, NULL, 0);
+		kill(logger_pid, SIGUSR1);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/init/halt.c b/busybox-1.19.3/init/halt.c
new file mode 100644
index 0000000..7974adb
--- /dev/null
+++ b/busybox-1.19.3/init/halt.c
@@ -0,0 +1,173 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Poweroff reboot and halt, oh my.
+ *
+ * Copyright 2006 by Rob Landley <rob@landley.net>
+ *
+ * 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 "reboot.h"
+
+#if ENABLE_FEATURE_WTMP
+#include <sys/utsname.h>
+
+static void write_wtmp(void)
+{
+	struct utmp utmp;
+	struct utsname uts;
+	/* "man utmp" says wtmp file should *not* be created automagically */
+	/*if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
+		close(creat(bb_path_wtmp_file, 0664));
+	}*/
+	memset(&utmp, 0, sizeof(utmp));
+	utmp.ut_tv.tv_sec = time(NULL);
+	strcpy(utmp.ut_user, "shutdown"); /* it is wide enough */
+	utmp.ut_type = RUN_LVL;
+	utmp.ut_id[0] = '~'; utmp.ut_id[1] = '~'; /* = strcpy(utmp.ut_id, "~~"); */
+	utmp.ut_line[0] = '~'; utmp.ut_line[1] = '~'; /* = strcpy(utmp.ut_line, "~~"); */
+	uname(&uts);
+	safe_strncpy(utmp.ut_host, uts.release, sizeof(utmp.ut_host));
+	updwtmp(bb_path_wtmp_file, &utmp);
+}
+#else
+#define write_wtmp() ((void)0)
+#endif
+
+
+int halt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int halt_main(int argc UNUSED_PARAM, char **argv)
+{
+	static const int magic[] = {
+		RB_HALT_SYSTEM,
+		RB_POWER_OFF,
+		RB_AUTOBOOT
+	};
+	static const smallint signals[] = { SIGUSR1, SIGUSR2, SIGTERM };
+
+	int delay = 0;
+	int which, flags, rc;
+
+	/* Figure out which applet we're running */
+	for (which = 0; "hpr"[which] != applet_name[0]; which++)
+		continue;
+
+	/* Parse and handle arguments */
+	opt_complementary = "d+"; /* -d N */
+	/* We support -w even if !ENABLE_FEATURE_WTMP,
+	 * in order to not break scripts.
+	 * -i (shut down network interfaces) is ignored.
+	 */
+	flags = getopt32(argv, "d:nfwi", &delay);
+
+	sleep(delay);
+
+	write_wtmp();
+
+	if (flags & 8) /* -w */
+		return EXIT_SUCCESS;
+
+	if (!(flags & 2)) /* no -n */
+		sync();
+
+	/* Perform action. */
+	rc = 1;
+	if (!(flags & 4)) { /* no -f */
+//TODO: I tend to think that signalling linuxrc is wrong
+// pity original author didn't comment on it...
+		if (ENABLE_FEATURE_INITRD) {
+			/* talk to linuxrc */
+			/* bbox init/linuxrc assumed */
+			pid_t *pidlist = find_pid_by_name("linuxrc");
+			if (pidlist[0] > 0)
+				rc = kill(pidlist[0], signals[which]);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(pidlist);
+		}
+		if (rc) {
+			/* talk to init */
+			if (!ENABLE_FEATURE_CALL_TELINIT) {
+				/* bbox init assumed */
+				rc = kill(1, signals[which]);
+			} else {
+				/* SysV style init assumed */
+				/* runlevels:
+				 * 0 == shutdown
+				 * 6 == reboot */
+				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 {
+		rc = reboot(magic[which]);
+	}
+
+	if (rc)
+		bb_perror_nomsg_and_die();
+	return rc;
+}
diff --git a/busybox-1.19.3/init/init.c b/busybox-1.19.3/init/init.c
new file mode 100644
index 0000000..645f694
--- /dev/null
+++ b/busybox-1.19.3/init/init.c
@@ -0,0 +1,1292 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini init implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * 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 source tree.
+ */
+
+//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
+
+//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.
+
+#include "libbb.h"
+#include <syslog.h>
+#include <paths.h>
+#include <sys/resource.h>
+#ifdef __linux__
+# include <linux/vt.h>
+# include <sys/sysinfo.h>
+#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. */
+#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"
+#endif
+
+/* Each type of actions can appear many times. They will be
+ * handled in order. RESTART is an exception, only 1st is used.
+ */
+/* Start these actions first and wait for completion */
+#define SYSINIT     0x01
+/* Start these after SYSINIT and wait for completion */
+#define WAIT        0x02
+/* Start these after WAIT and *dont* wait for completion */
+#define ONCE        0x04
+/*
+ * NB: while SYSINIT/WAIT/ONCE are being processed,
+ * SIGHUP ("reread /etc/inittab") will be processed only after
+ * each group of actions. If new inittab adds, say, a SYSINIT action,
+ * it will not be run, since init is already "past SYSINIT stage".
+ */
+/* Start these after ONCE are started, restart on exit */
+#define RESPAWN     0x08
+/* Like RESPAWN, but wait for <Enter> to be pressed on tty */
+#define ASKFIRST    0x10
+/*
+ * Start these on SIGINT, and wait for completion.
+ * Then go back to respawning RESPAWN and ASKFIRST actions.
+ * NB: kernel sends SIGINT to us if Ctrl-Alt-Del was pressed.
+ */
+#define CTRLALTDEL  0x20
+/*
+ * Start these before killing all processes in preparation for
+ * running RESTART actions or doing low-level halt/reboot/poweroff
+ * (initiated by SIGUSR1/SIGTERM/SIGUSR2).
+ * Wait for completion before proceeding.
+ */
+#define SHUTDOWN    0x40
+/*
+ * exec() on SIGQUIT. SHUTDOWN actions are started and waited for,
+ * then all processes are killed, then init exec's 1st RESTART action,
+ * replacing itself by it. If no RESTART action specified,
+ * SIGQUIT has no effect.
+ */
+#define RESTART     0x80
+
+
+/* A linked list of init_actions, to be read from inittab */
+struct init_action {
+	struct init_action *next;
+	pid_t pid;
+	uint8_t action_type;
+	char terminal[CONSOLE_NAME_SIZE];
+	char command[COMMAND_SIZE];
+};
+
+static struct init_action *init_action_list = NULL;
+
+static const char *log_console = VC_5;
+
+enum {
+	L_LOG = 0x1,
+	L_CONSOLE = 0x2,
+};
+
+/* Print a message to the specified device.
+ * "where" may be bitwise-or'd from L_LOG | L_CONSOLE
+ * NB: careful, we can be called after vfork!
+ */
+#define dbg_message(...) do { if (DEBUG_INIT) message(__VA_ARGS__); } while (0)
+static void message(int where, const char *fmt, ...)
+	__attribute__ ((format(printf, 2, 3)));
+static void message(int where, const char *fmt, ...)
+{
+	va_list arguments;
+	unsigned l;
+	char msg[128];
+
+	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;
+	va_end(arguments);
+
+#if ENABLE_FEATURE_INIT_SYSLOG
+	msg[l] = '\0';
+	if (where & L_LOG) {
+		/* Log the message to syslogd */
+		openlog(applet_name, 0, LOG_DAEMON);
+		/* don't print "\r" */
+		syslog(LOG_INFO, "%s", msg + 1);
+		closelog();
+	}
+	msg[l++] = '\n';
+	msg[l] = '\0';
+#else
+	{
+		static int log_fd = -1;
+
+		msg[l++] = '\n';
+		msg[l] = '\0';
+		/* Take full control of the log tty, and never close it.
+		 * It's mine, all mine!  Muhahahaha! */
+		if (log_fd < 0) {
+			if (!log_console) {
+				log_fd = STDERR_FILENO;
+			} else {
+				log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY);
+				if (log_fd < 0) {
+					bb_error_msg("can't log to %s", log_console);
+					where = L_CONSOLE;
+				} else {
+					close_on_exec_on(log_fd);
+				}
+			}
+		}
+		if (where & L_LOG) {
+			full_write(log_fd, msg, l);
+			if (log_fd == STDERR_FILENO)
+				return; /* don't print dup messages */
+		}
+	}
+#endif
+
+	if (where & L_CONSOLE) {
+		/* Send console messages to console so people will see them. */
+		full_write(STDERR_FILENO, msg, l);
+	}
+}
+
+static void console_init(void)
+{
+#ifdef VT_OPENQRY
+	int vtno;
+#endif
+	char *s;
+
+	s = getenv("CONSOLE");
+	if (!s)
+		s = getenv("console");
+	if (s) {
+		int fd = open(s, O_RDWR | O_NONBLOCK | O_NOCTTY);
+		if (fd >= 0) {
+			dup2(fd, STDIN_FILENO);
+			dup2(fd, STDOUT_FILENO);
+			xmove_fd(fd, STDERR_FILENO);
+		}
+		dbg_message(L_LOG, "console='%s'", s);
+	} else {
+		/* Make sure fd 0,1,2 are not closed
+		 * (so that they won't be used by future opens) */
+		bb_sanitize_stdio();
+// Users report problems
+//		/* Make sure init can't be blocked by writing to stderr */
+//		fcntl(STDERR_FILENO, F_SETFL, fcntl(STDERR_FILENO, F_GETFL) | O_NONBLOCK);
+	}
+
+	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
+		 * if TERM is set to linux (the default) */
+		if (!s || strcmp(s, "linux") == 0)
+			putenv((char*)"TERM=vt102");
+		if (!ENABLE_FEATURE_INIT_SYSLOG)
+			log_console = NULL;
+	} else
+#endif
+	if (!s)
+		putenv((char*)"TERM=" CONFIG_INIT_TERMINAL_TYPE);
+}
+
+/* Set terminal settings to reasonable defaults.
+ * NB: careful, we can be called after vfork! */
+static void set_sane_term(void)
+{
+	struct termios tty;
+
+	tcgetattr(STDIN_FILENO, &tty);
+
+	/* set control chars */
+	tty.c_cc[VINTR] = 3;	/* C-c */
+	tty.c_cc[VQUIT] = 28;	/* C-\ */
+	tty.c_cc[VERASE] = 127;	/* C-? */
+	tty.c_cc[VKILL] = 21;	/* C-u */
+	tty.c_cc[VEOF] = 4;	/* C-d */
+	tty.c_cc[VSTART] = 17;	/* C-q */
+	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 */
+#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 */
+	tty.c_iflag = ICRNL | IXON | IXOFF;
+
+	/* output modes */
+	tty.c_oflag = OPOST | ONLCR;
+
+	/* local modes */
+	tty.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
+
+	tcsetattr_stdin_TCSANOW(&tty);
+}
+
+/* Open the new terminal device.
+ * NB: careful, we can be called after vfork! */
+static int open_stdio_to_tty(const char* tty_name)
+{
+	/* empty tty_name means "use init's tty", else... */
+	if (tty_name[0]) {
+		int fd;
+
+		close(STDIN_FILENO);
+		/* fd can be only < 0 or 0: */
+		fd = device_open(tty_name, O_RDWR);
+		if (fd) {
+			message(L_LOG | L_CONSOLE, "can't open %s: %s",
+				tty_name, strerror(errno));
+			return 0; /* failure */
+		}
+		dup2(STDIN_FILENO, STDOUT_FILENO);
+		dup2(STDIN_FILENO, STDERR_FILENO);
+	}
+	set_sane_term();
+	return 1; /* success */
+}
+
+static void reset_sighandlers_and_unblock_sigs(void)
+{
+	bb_signals(0
+		+ (1 << SIGUSR1)
+		+ (1 << SIGUSR2)
+		+ (1 << SIGTERM)
+		+ (1 << SIGQUIT)
+		+ (1 << SIGINT)
+		+ (1 << SIGHUP)
+		+ (1 << SIGTSTP)
+		+ (1 << SIGSTOP)
+		, SIG_DFL);
+	sigprocmask_allsigs(SIG_UNBLOCK);
+}
+
+/* Wrapper around exec:
+ * Takes string (max COMMAND_SIZE chars).
+ * If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'.
+ * Otherwise splits words on whitespace, deals with leading dash,
+ * and uses plain exec().
+ * NB: careful, we can be called after vfork!
+ */
+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] == '/' */);
+
+	command += dash;
+
+	/* See if any special /bin/sh requiring characters are present */
+	if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
+		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 - dash); /* command including "-" */
+		command = next + dash;
+		while ((word = strsep(&next, " \t")) != NULL) {
+			if (*word != '\0') { /* not two spaces/tabs together? */
+				cmd[i] = word;
+				i++;
+			}
+		}
+		cmd[i] = NULL;
+	}
+	/* If we saw leading "-", it is interactive shell.
+	 * Try harder to give it a controlling tty.
+	 */
+	if (ENABLE_FEATURE_INIT_SCTTY && dash) {
+		/* _Attempt_ to make stdin a controlling tty. */
+		ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/);
+	}
+	/* 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 */
+}
+
+/* Used only by run_actions */
+static pid_t run(const struct init_action *a)
+{
+	pid_t pid;
+
+	/* Careful: don't be affected by a signal in vforked child */
+	sigprocmask_allsigs(SIG_BLOCK);
+	if (BB_MMU && (a->action_type & ASKFIRST))
+		pid = fork();
+	else
+		pid = vfork();
+	if (pid < 0)
+		message(L_LOG | L_CONSOLE, "can't fork");
+	if (pid) {
+		sigprocmask_allsigs(SIG_UNBLOCK);
+		return pid; /* Parent or error */
+	}
+
+	/* Child */
+
+	/* Reset signal handlers that were set by the parent process */
+	reset_sighandlers_and_unblock_sigs();
+
+	/* Create a new session and make ourself the process group leader */
+	setsid();
+
+	/* Open the new terminal device */
+	if (!open_stdio_to_tty(a->terminal))
+		_exit(EXIT_FAILURE);
+
+	/* NB: on NOMMU we can't wait for input in child, so
+	 * "askfirst" will work the same as "respawn". */
+	if (BB_MMU && (a->action_type & ASKFIRST)) {
+		static const char press_enter[] ALIGN1 =
+#ifdef CUSTOMIZED_BANNER
+#include CUSTOMIZED_BANNER
+#endif
+			"\nPlease press Enter to activate this console. ";
+		char c;
+		/*
+		 * Save memory by not exec-ing anything large (like a shell)
+		 * before the user wants it. This is critical if swap is not
+		 * enabled and the system has low memory. Generally this will
+		 * be run on the second virtual console, and the first will
+		 * be allowed to start a shell or whatever an init script
+		 * specifies.
+		 */
+		dbg_message(L_LOG, "waiting for enter to start '%s'"
+					"(pid %d, tty '%s')\n",
+				a->command, getpid(), a->terminal);
+		full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1);
+		while (safe_read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
+			continue;
+	}
+
+	/*
+	 * When a file named /.init_enable_core exists, setrlimit is called
+	 * before processes are spawned to set core file size as unlimited.
+	 * This is for debugging only.  Don't use this is production, unless
+	 * you want core dumps lying about....
+	 */
+	if (ENABLE_FEATURE_INIT_COREDUMPS) {
+		if (access("/.init_enable_core", F_OK) == 0) {
+			struct rlimit limit;
+			limit.rlim_cur = RLIM_INFINITY;
+			limit.rlim_max = RLIM_INFINITY;
+			setrlimit(RLIMIT_CORE, &limit);
+		}
+	}
+
+	/* Log the process name and args */
+	message(L_LOG, "starting pid %d, tty '%s': '%s'",
+			  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. */
+	init_exec(a->command);
+	/* We're still here?  Some error happened. */
+	_exit(-1);
+}
+
+static struct init_action *mark_terminated(pid_t pid)
+{
+	struct init_action *a;
+
+	if (pid > 0) {
+		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;
+}
+
+static void waitfor(pid_t pid)
+{
+	/* waitfor(run(x)): protect against failed fork inside run() */
+	if (pid <= 0)
+		return;
+
+	/* Wait for any child (prevent zombies from exiting orphaned processes)
+	 * but exit the loop only when specified one has exited. */
+	while (1) {
+		pid_t wpid = wait(NULL);
+		mark_terminated(wpid);
+		/* Unsafe. SIGTSTP handler might have wait'ed it already */
+		/*if (wpid == pid) break;*/
+		/* More reliable: */
+		if (kill(pid, 0))
+			break;
+	}
+}
+
+/* Run all commands of a particular type */
+static void run_actions(int action_type)
+{
+	struct init_action *a;
+
+	for (a = init_action_list; a; a = a->next) {
+		if (!(a->action_type & action_type))
+			continue;
+
+		if (a->action_type & (SYSINIT | WAIT | ONCE | CTRLALTDEL | SHUTDOWN)) {
+			pid_t pid = run(a);
+			if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN))
+				waitfor(pid);
+		}
+		if (a->action_type & (RESPAWN | ASKFIRST)) {
+			/* Only run stuff with pid == 0. If pid != 0,
+			 * it is already running
+			 */
+			if (a->pid == 0)
+				a->pid = run(a);
+		}
+	}
+}
+
+static void new_init_action(uint8_t action_type, const char *command, const char *cons)
+{
+	struct init_action *a, **nextp;
+
+	/* Scenario:
+	 * old inittab:
+	 * ::shutdown:umount -a -r
+	 * ::shutdown:swapoff -a
+	 * new inittab:
+	 * ::shutdown:swapoff -a
+	 * ::shutdown:umount -a -r
+	 * On reload, we must ensure entries end up in correct order.
+	 * To achieve that, if we find a matching entry, we move it
+	 * to the end.
+	 */
+	nextp = &init_action_list;
+	while ((a = *nextp) != NULL) {
+		/* Don't enter action if it's already in the list,
+		 * This prevents losing running RESPAWNs.
+		 */
+		if (strcmp(a->command, command) == 0
+		 && strcmp(a->terminal, cons) == 0
+		) {
+			/* Remove from list */
+			*nextp = a->next;
+			/* Find the end of the list */
+			while (*nextp != NULL)
+				nextp = &(*nextp)->next;
+			a->next = NULL;
+			break;
+		}
+		nextp = &a->next;
+	}
+
+	if (!a)
+		a = xzalloc(sizeof(*a));
+	/* Append to the end of the list */
+	*nextp = a;
+	a->action_type = action_type;
+	safe_strncpy(a->command, command, sizeof(a->command));
+	safe_strncpy(a->terminal, cons, sizeof(a->terminal));
+	dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%d 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
+ * of "askfirst" shells).  If CONFIG_FEATURE_USE_INITTAB
+ * _is_ defined, but /etc/inittab is missing, this
+ * results in the same set of default behaviors.
+ */
+static void parse_inittab(void)
+{
+#if ENABLE_FEATURE_USE_INITTAB
+	char *token[4];
+	parser_t *parser = config_open2("/etc/inittab", fopen_for_read);
+
+	if (parser == NULL)
+#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", "");
+		/* 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, "");
+		return;
+	}
+
+#if ENABLE_FEATURE_USE_INITTAB
+	/* optional_tty:ignored_runlevel:action:command
+	 * Delims are not to be collapsed and need exactly 4 tokens
+	 */
+	while (config_read(parser, token, 4, 0, "#:",
+				PARSE_NORMAL & ~(PARSE_TRIM | PARSE_COLLAPSE))) {
+		/* order must correspond to SYSINIT..RESTART constants */
+		static const char actions[] ALIGN1 =
+			"sysinit\0""wait\0""once\0""respawn\0""askfirst\0"
+			"ctrlaltdel\0""shutdown\0""restart\0";
+		int action;
+		char *tty = token[0];
+
+		if (!token[3]) /* less than 4 tokens */
+			goto bad_entry;
+		action = index_in_strings(actions, token[2]);
+		if (action < 0 || !token[3][0]) /* token[3]: command */
+			goto bad_entry;
+		/* turn .*TTY -> /dev/TTY */
+		if (tty[0]) {
+			tty = concat_path_file("/dev/", skip_dev_pfx(tty));
+		}
+		new_init_action(1 << action, token[3], tty);
+		if (tty[0])
+			free(tty);
+		continue;
+ bad_entry:
+		message(L_LOG | L_CONSOLE, "Bad inittab entry at line %d",
+				parser->lineno);
+	}
+	config_close(parser);
+#endif
+}
+
+static void pause_and_low_level_reboot(unsigned magic) NORETURN;
+static void pause_and_low_level_reboot(unsigned magic)
+{
+	pid_t pid;
+
+	/* Allow time for last message to reach serial console, etc */
+	sleep(1);
+
+	/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
+	 * in linux/kernel/sys.c, which can cause the machine to panic when
+	 * the init process exits... */
+	pid = vfork();
+	if (pid == 0) { /* child */
+		reboot(magic);
+		_exit(EXIT_SUCCESS);
+	}
+	while (1)
+		sleep(1);
+}
+
+static void run_shutdown_and_kill_processes(void)
+{
+	/* Run everything to be run at "shutdown".  This is done _prior_
+	 * to killing everything, in case people wish to use scripts to
+	 * shut things down gracefully... */
+	run_actions(SHUTDOWN);
+
+	message(L_CONSOLE | L_LOG, "The system is going down NOW!");
+
+	/* Send signals to every process _except_ pid 1 */
+	kill(-1, SIGTERM);
+	message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
+	sync();
+	sleep(1);
+
+	kill(-1, SIGKILL);
+	message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
+	sync();
+	/*sleep(1); - callers take care about making a pause */
+}
+
+/* Signal handling by init:
+ *
+ * For process with PID==1, on entry kernel sets all signals to SIG_DFL
+ * and unmasks all signals. However, for process with PID==1,
+ * default action (SIG_DFL) on any signal is to ignore it,
+ * even for special signals SIGKILL and SIGCONT.
+ * Also, any signal can be caught or blocked.
+ * (but SIGSTOP is still handled specially, at least in 2.6.20)
+ *
+ * We install two kinds of handlers, "immediate" and "delayed".
+ *
+ * Immediate handlers execute at any time, even while, say, sysinit
+ * is running.
+ *
+ * Delayed handlers just set a flag variable. The variable is checked
+ * in the main loop and acted upon.
+ *
+ * halt/poweroff/reboot and restart have immediate handlers.
+ * They only traverse linked list of struct action's, never modify it,
+ * this should be safe to do even in signal handler. Also they
+ * never return.
+ *
+ * SIGSTOP and SIGTSTP have immediate handlers. They just wait
+ * for SIGCONT to happen.
+ *
+ * SIGHUP has a delayed handler, because modifying linked list
+ * of struct action's from a signal handler while it is manipulated
+ * by the program may be disastrous.
+ *
+ * Ctrl-Alt-Del has a delayed handler. Not a must, but allowing
+ * it to happen even somewhere inside "sysinit" would be a bit awkward.
+ *
+ * There is a tiny probability that SIGHUP and Ctrl-Alt-Del will collide
+ * and only one will be remembered and acted upon.
+ */
+
+/* The SIGUSR[12]/SIGTERM handler */
+static void halt_reboot_pwoff(int sig) NORETURN;
+static void halt_reboot_pwoff(int sig)
+{
+	const char *m;
+	unsigned rb;
+
+	/* We may call run() and it unmasks signals,
+	 * including the one masked inside this signal handler.
+	 * Testcase which would start multiple reboot scripts:
+	 *  while true; do reboot; done
+	 * Preventing it:
+	 */
+	reset_sighandlers_and_unblock_sigs();
+
+	run_shutdown_and_kill_processes();
+
+	m = "halt";
+	rb = RB_HALT_SYSTEM;
+	if (sig == SIGTERM) {
+		m = "reboot";
+		rb = RB_AUTOBOOT;
+	} else if (sig == SIGUSR2) {
+		m = "poweroff";
+		rb = RB_POWER_OFF;
+	}
+	message(L_CONSOLE, "Requesting system %s", m);
+	pause_and_low_level_reboot(rb);
+	/* not reached */
+}
+
+/* Handler for QUIT - exec "restart" action,
+ * else (no such action defined) do nothing */
+static void restart_handler(int sig UNUSED_PARAM)
+{
+	struct init_action *a;
+
+	for (a = init_action_list; a; a = a->next) {
+		if (!(a->action_type & RESTART))
+			continue;
+
+		/* Starting from here, we won't return.
+		 * Thus don't need to worry about preserving errno
+		 * and such.
+		 */
+
+		reset_sighandlers_and_unblock_sigs();
+
+		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);
+			/* Theoretically should be safe.
+			 * But in practice, kernel bugs may leave
+			 * unkillable processes, and wait() may block forever.
+			 * Oh well. Hoping "new" init won't be too surprised
+			 * by having children it didn't create.
+			 */
+			//while (wait(NULL) > 0)
+			//	continue;
+			init_exec(a->command);
+		}
+		/* Open or exec failed */
+		pause_and_low_level_reboot(RB_HALT_SYSTEM);
+		/* not reached */
+	}
+}
+
+/* The SIGSTOP/SIGTSTP handler
+ * NB: inside it, all signals except SIGCONT are masked
+ * via appropriate setup in sigaction().
+ */
+static void stop_handler(int sig UNUSED_PARAM)
+{
+	smallint saved_bb_got_signal;
+	int saved_errno;
+
+	saved_bb_got_signal = bb_got_signal;
+	saved_errno = errno;
+	signal(SIGCONT, record_signo);
+
+	while (1) {
+		pid_t wpid;
+
+		if (bb_got_signal == SIGCONT)
+			break;
+		/* NB: this can accidentally wait() for a process
+		 * which we waitfor() elsewhere! waitfor() must have
+		 * code which is resilient against this.
+		 */
+		wpid = wait_any_nohang(NULL);
+		mark_terminated(wpid);
+		sleep(1);
+	}
+
+	signal(SIGCONT, SIG_DFL);
+	errno = saved_errno;
+	bb_got_signal = saved_bb_got_signal;
+}
+
+#if ENABLE_FEATURE_USE_INITTAB
+static void reload_inittab(void)
+{
+	struct init_action *a, **nextp;
+
+	message(L_LOG, "reloading /etc/inittab");
+
+	/* Disable old entries */
+	for (a = init_action_list; a; a = a->next)
+		a->action_type = 0;
+
+	/* Append new entries, or modify existing entries
+	 * (incl. setting a->action_type) if cmd and device name
+	 * match new ones. End result: only entries with
+	 * a->action_type == 0 are stale.
+	 */
+	parse_inittab();
+
+#if ENABLE_FEATURE_KILL_REMOVED
+	/* Kill stale entries */
+	/* Be nice and send SIGTERM first */
+	for (a = init_action_list; a; a = a->next)
+		if (a->action_type == 0 && a->pid != 0)
+			kill(a->pid, SIGTERM);
+	if (CONFIG_FEATURE_KILL_DELAY) {
+		/* NB: parent will wait in NOMMU case */
+		if ((BB_MMU ? fork() : vfork()) == 0) { /* child */
+			sleep(CONFIG_FEATURE_KILL_DELAY);
+			for (a = init_action_list; a; a = a->next)
+				if (a->action_type == 0 && a->pid != 0)
+					kill(a->pid, SIGKILL);
+			_exit(EXIT_SUCCESS);
+		}
+	}
+#endif
+
+	/* Remove stale entries and SYSINIT entries.
+	 * We never rerun SYSINIT entries anyway,
+	 * removing them too saves a few bytes */
+	nextp = &init_action_list;
+	while ((a = *nextp) != NULL) {
+		if ((a->action_type & ~SYSINIT) == 0) {
+			*nextp = a->next;
+			free(a);
+		} else {
+			nextp = &a->next;
+		}
+	}
+
+	/* Not needed: */
+	/* run_actions(RESPAWN | ASKFIRST); */
+	/* - we return to main loop, which does this automagically */
+}
+#endif
+
+static int check_delayed_sigs(void)
+{
+	int sigs_seen = 0;
+
+	while (1) {
+		smallint sig = bb_got_signal;
+
+		if (!sig)
+			return sigs_seen;
+		bb_got_signal = 0;
+		sigs_seen = 1;
+#if ENABLE_FEATURE_USE_INITTAB
+		if (sig == SIGHUP)
+			reload_inittab();
+#endif
+		if (sig == SIGINT)
+			run_actions(CTRLALTDEL);
+	}
+}
+
+int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int init_main(int argc UNUSED_PARAM, char **argv)
+{
+	if (argv[1] && strcmp(argv[1], "-q") == 0) {
+		return kill(1, SIGHUP);
+	}
+
+	if (!DEBUG_INIT) {
+		/* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
+		if (getpid() != 1
+		 && (!ENABLE_FEATURE_INITRD || applet_name[0] != 'l') /* not linuxrc? */
+		) {
+			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();
+	xchdir("/");
+	setsid();
+
+	/* Make sure environs is set to something sane */
+	putenv((char *) "HOME=/");
+	putenv((char *) bb_PATH_root_path);
+	putenv((char *) "SHELL=/bin/sh");
+	putenv((char *) "USER=root"); /* needed? why? */
+
+	if (argv[1])
+		xsetenv("RUNLEVEL", argv[1]);
+
+#if !ENABLE_FEATURE_EXTRA_QUIET
+	/* Hello world */
+	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;
+
+		if (sysinfo(&info) == 0
+		 && (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024
+		) {
+			message(L_CONSOLE, "Low memory, forcing swapon");
+			/* swapon -a requires /proc typically */
+			new_init_action(SYSINIT, "mount -t proc proc /proc", "");
+			/* Try to turn on swap */
+			new_init_action(SYSINIT, "swapon -a", "");
+			run_actions(SYSINIT);   /* wait and removing */
+		}
+	}
+#endif
+
+	/* Check if we are supposed to be in single user mode */
+	if (argv[1]
+	 && (strcmp(argv[1], "single") == 0 || strcmp(argv[1], "-s") == 0 || LONE_CHAR(argv[1], '1'))
+	) {
+		/* ??? shouldn't we set RUNLEVEL="b" here? */
+		/* Start a shell on console */
+		new_init_action(RESPAWN, bb_default_login_shell, "");
+	} else {
+		/* Not in single user mode - see what inittab says */
+
+		/* 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 */
+		parse_inittab();
+	}
+
+#if ENABLE_SELINUX
+	if (getenv("SELINUX_INIT") == NULL) {
+		int enforce = 0;
+
+		putenv((char*)"SELINUX_INIT=YES");
+		if (selinux_init_load_policy(&enforce) == 0) {
+			BB_EXECVP(argv[0], argv);
+		} else if (enforce > 0) {
+			/* SELinux in enforcing mode but load_policy failed */
+			message(L_CONSOLE, "can't load SELinux Policy. "
+				"Machine is in enforcing mode. Halting now.");
+			return EXIT_FAILURE;
+		}
+	}
+#endif
+
+	/* Make the command line just say "init"  - thats all, nothing else */
+	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));
+
+	/* Set up signal handlers */
+	if (!DEBUG_INIT) {
+		struct sigaction sa;
+
+		bb_signals(0
+			+ (1 << SIGUSR1) /* halt */
+			+ (1 << SIGTERM) /* reboot */
+			+ (1 << SIGUSR2) /* poweroff */
+			, halt_reboot_pwoff);
+		signal(SIGQUIT, restart_handler); /* re-exec another init */
+
+		/* Stop handler must allow only SIGCONT inside itself */
+		memset(&sa, 0, sizeof(sa));
+		sigfillset(&sa.sa_mask);
+		sigdelset(&sa.sa_mask, SIGCONT);
+		sa.sa_handler = stop_handler;
+		/* NB: sa_flags doesn't have SA_RESTART.
+		 * It must be able to interrupt wait().
+		 */
+		sigaction_set(SIGTSTP, &sa); /* pause */
+		/* Does not work as intended, at least in 2.6.20.
+		 * SIGSTOP is simply ignored by init:
+		 */
+		sigaction_set(SIGSTOP, &sa); /* pause */
+
+		/* SIGINT (Ctrl-Alt-Del) must interrupt wait(),
+		 * setting handler without SA_RESTART flag.
+		 */
+		bb_signals_recursive_norestart((1 << SIGINT), record_signo);
+	}
+
+	/* Set up "reread /etc/inittab" handler.
+	 * Handler is set up without SA_RESTART, it will interrupt syscalls.
+	 */
+	if (!DEBUG_INIT && ENABLE_FEATURE_USE_INITTAB)
+		bb_signals_recursive_norestart((1 << SIGHUP), record_signo);
+
+	/* Now run everything that needs to be run */
+	/* First run the sysinit command */
+	run_actions(SYSINIT);
+	check_delayed_sigs();
+	/* Next run anything that wants to block */
+	run_actions(WAIT);
+	check_delayed_sigs();
+	/* Next run anything to be run only once */
+	run_actions(ONCE);
+
+	/* Now run the looping stuff for the rest of forever.
+	 */
+	while (1) {
+		int maybe_WNOHANG;
+
+		maybe_WNOHANG = check_delayed_sigs();
+
+		/* (Re)run the respawn/askfirst stuff */
+		run_actions(RESPAWN | ASKFIRST);
+		maybe_WNOHANG |= check_delayed_sigs();
+
+		/* Don't consume all CPU time - sleep a bit */
+		sleep(1);
+		maybe_WNOHANG |= check_delayed_sigs();
+
+		/* Wait for any child process(es) to exit.
+		 *
+		 * If check_delayed_sigs above reported that a signal
+		 * was caught, wait will be nonblocking. This ensures
+		 * that if SIGHUP has reloaded inittab, respawn and askfirst
+		 * actions will not be delayed until next child death.
+		 */
+		if (maybe_WNOHANG)
+			maybe_WNOHANG = WNOHANG;
+		while (1) {
+			pid_t wpid;
+			struct init_action *a;
+
+			/* If signals happen _in_ the wait, they interrupt it,
+			 * bb_signals_recursive_norestart set them up that way
+			 */
+			wpid = waitpid(-1, NULL, maybe_WNOHANG);
+			if (wpid <= 0)
+				break;
+
+			a = mark_terminated(wpid);
+			if (a) {
+				message(L_LOG, "process '%s' (pid %d) exited. "
+						"Scheduling for restart.",
+						a->command, wpid);
+			}
+			/* See if anyone else is waiting to be reaped */
+			maybe_WNOHANG = WNOHANG;
+		}
+	} /* 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 parent of all processes"
+//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:	"\n"
+//usage:	"if it detects that /dev/console is _not_ a serial console, it will also run:\n"
+//usage:	"\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, the controlling tty is set to the console. Also\n"
+//usage:	"		note that if BusyBox detects that a serial console is in use, then only\n"
+//usage:	"		entries whose controlling tty is either the serial console or /dev/null\n"
+//usage:	"		will be run. BusyBox init does nothing with utmp. We don't need no\n"
+//usage:	"		stinkin' utmp.\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"
diff --git a/busybox-1.19.3/init/mesg.c b/busybox-1.19.3/init/mesg.c
new file mode 100644
index 0000000..45c13b8
--- /dev/null
+++ b/busybox-1.19.3/init/mesg.c
@@ -0,0 +1,76 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mesg implementation for busybox
+ *
+ * Copyright (c) 2002 Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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"
+
+#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)
+#endif
+
+int mesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mesg_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat sb;
+	mode_t m;
+	char c = 0;
+
+	argv++;
+
+	if (argv[0]
+	 && (argv[1] || ((c = argv[0][0]) != 'y' && c != 'n'))
+	) {
+		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;
+	}
+	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/busybox-1.19.3/init/reboot.h b/busybox-1.19.3/init/reboot.h
new file mode 100644
index 0000000..9497639
--- /dev/null
+++ b/busybox-1.19.3/init/reboot.h
@@ -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
diff --git a/busybox-1.19.3/libbb/Config.src b/busybox-1.19.3/libbb/Config.src
new file mode 100644
index 0000000..aa44236
--- /dev/null
+++ b/busybox-1.19.3/libbb/Config.src
@@ -0,0 +1,218 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Busybox Library Tuning"
+
+INSERT
+
+config PASSWORD_MINLEN
+	int "Minimum password length"
+	default 6
+	range 5 32
+	help
+	  Minimum allowable password length.
+
+config MD5_SIZE_VS_SPEED
+	int "MD5: Trade bytes for speed (0:fast, 3:slow)"
+	default 2
+	range 0 3
+	help
+	  Trade binary size versus speed for the md5sum algorithm.
+	  Approximate values running uClibc and hashing
+	  linux-2.4.4.tar.bz2 were:
+	                    user times (sec)  text size (386)
+	  0 (fastest)         1.1                6144
+	  1                   1.4                5392
+	  2                   3.0                5088
+	  3 (smallest)        5.1                4912
+
+config FEATURE_FAST_TOP
+	bool "Faster /proc scanning code (+100 bytes)"
+	default y
+	help
+	  This option makes top (and ps) ~20% faster (or 20% less CPU hungry),
+	  but code size is slightly bigger.
+
+config FEATURE_ETC_NETWORKS
+	bool "Support for /etc/networks"
+	default n
+	help
+	  Enable support for network names in /etc/networks. This is
+	  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
+	help
+	  Enable line editing (mainly for shell command line).
+
+config FEATURE_EDITING_MAX_LEN
+	int "Maximum length of input"
+	range 128 8192
+	default 1024
+	depends on FEATURE_EDITING
+	help
+	  Line editing code uses on-stack buffers for storage.
+	  You may want to decrease this parameter if your target machine
+	  benefits from smaller stack usage.
+
+config FEATURE_EDITING_VI
+	bool "vi-style line editing commands"
+	default n
+	depends on FEATURE_EDITING
+	help
+	  Enable vi-style line editing. In shells, this mode can be
+	  turned on and off with "set -o vi" and "set +o vi".
+
+config FEATURE_EDITING_HISTORY
+	int "History size"
+	# 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 (0 - disable).
+
+config FEATURE_EDITING_SAVEHISTORY
+	bool "History saving"
+	default y
+	depends on FEATURE_EDITING
+	help
+	  Enable history saving in shells.
+
+config FEATURE_REVERSE_SEARCH
+	bool "Reverse history search"
+	default y
+	depends on FEATURE_EDITING_SAVEHISTORY
+	help
+	  Enable readline-like Ctrl-R combination for reverse history search.
+	  Increases code by about 0.5k.
+
+config FEATURE_TAB_COMPLETION
+	bool "Tab completion"
+	default y
+	depends on FEATURE_EDITING
+	help
+	  Enable tab completion.
+
+config FEATURE_USERNAME_COMPLETION
+	bool "Username completion"
+	default n
+	depends on FEATURE_TAB_COMPLETION
+	help
+	  Enable username completion.
+
+config FEATURE_EDITING_FANCY_PROMPT
+	bool "Fancy shell prompts"
+	default y
+	depends on FEATURE_EDITING
+	help
+	  Setting this option allows for prompts to use things like \w and
+	  \$ and escape codes.
+
+config FEATURE_EDITING_ASK_TERMINAL
+	bool "Query cursor position from terminal"
+	default n
+	depends on FEATURE_EDITING
+	help
+	  Allow usage of "ESC [ 6 n" sequence. Terminal answers back with
+	  current cursor position. This information is used to make line
+	  editing more robust in some cases.
+	  If you are not sure whether your terminals respond to this code
+	  correctly, or want to save on code size (about 400 bytes),
+	  then do not turn this option on.
+
+config FEATURE_NON_POSIX_CP
+	bool "Non-POSIX, but safer, copying to special nodes"
+	default y
+	help
+	  With this option, "cp file symlink" will delete symlink
+	  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 do that, use "cat file >device")
+
+config FEATURE_VERBOSE_CP_MESSAGE
+	bool "Give more precise messages when copy fails (cp, mv etc)"
+	default n
+	help
+	  Error messages with this feature enabled:
+	    $ cp file /does_not_exist/file
+	    cp: cannot create '/does_not_exist/file': Path does not exist
+	    $ cp file /vmlinuz/file
+	    cp: cannot stat '/vmlinuz/file': Path has non-directory component
+	  If this feature is not enabled, they will be, respectively:
+	    cp: cannot create '/does_not_exist/file': No such file or directory
+	    cp: cannot stat '/vmlinuz/file': Not a directory
+	  This will cost you ~60 bytes.
+
+config FEATURE_COPYBUF_KB
+	int "Copy buffer size, in kilobytes"
+	range 1 1024
+	default 4
+	help
+	  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 useful to turn this option off to make df show
+	  initramfs statistic.
+
+	  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).
+	  Probably requires Linux 2.6+. If not selected, gettimeofday
+	  will be used instead (which gives wrong results if date/time
+	  is reset).
+
+config IOCTL_HEX2STR_ERROR
+	bool "Use ioctl names rather than hex values in error messages"
+	default y
+	help
+	  Use ioctl names rather than hex values in error messages
+	  (e.g. VT_DISALLOCATE rather than 0x5608). If disabled this
+	  saves about 1400 bytes.
+
+config FEATURE_HWIB
+	bool "Support infiniband HW"
+	default y
+	help
+	  Support for printing infiniband addresses in
+	  network applets.
+
+endmenu
diff --git a/busybox-1.19.3/libbb/Kbuild.src b/busybox-1.19.3/libbb/Kbuild.src
new file mode 100644
index 0000000..875d024
--- /dev/null
+++ b/busybox-1.19.3/libbb/Kbuild.src
@@ -0,0 +1,182 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+libbb/appletlib.o: include/usage_compressed.h
+
+lib-y:=
+
+INSERT
+
+lib-y += appletlib.o
+lib-y += ask_confirmation.o
+lib-y += bb_askpass.o
+lib-y += bb_bswap_64.o
+lib-y += bb_do_delay.o
+lib-y += bb_pwd.o
+lib-y += bb_qsort.o
+#lib-y += bb_strtod.o
+lib-y += bb_strtonum.o
+lib-y += change_identity.o
+lib-y += chomp.o
+lib-y += compare_string_array.o
+lib-y += concat_path_file.o
+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
+lib-y += execable.o
+lib-y += fclose_nonstdin.o
+lib-y += fflush_stdout_and_exit.o
+lib-y += fgets_str.o
+lib-y += find_pid_by_name.o
+lib-y += find_root_device.o
+lib-y += full_write.o
+lib-y += get_console.o
+lib-y += get_last_path_component.o
+lib-y += get_line_from_file.o
+lib-y += getopt32.o
+lib-y += getpty.o
+lib-y += get_volsize.o
+lib-y += herror_msg.o
+lib-y += human_readable.o
+lib-y += inet_common.o
+lib-y += info_msg.o
+lib-y += inode_hash.o
+lib-y += isdirectory.o
+lib-y += kernel_version.o
+lib-y += last_char_is.o
+lib-y += lineedit.o lineedit_ptr_hack.o
+lib-y += llist.o
+lib-y += login.o
+lib-y += make_directory.o
+lib-y += makedev.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 += parse_mode.o
+lib-y += perror_msg.o
+lib-y += perror_nomsg.o
+lib-y += perror_nomsg_and_die.o
+lib-y += pidfile.o
+lib-y += platform.o
+lib-y += printable.o
+lib-y += printable_string.o
+lib-y += print_flags.o
+lib-y += process_escape_sequence.o
+lib-y += procps.o
+lib-y += progress.o
+lib-y += ptr_to_globals.o
+lib-y += read.o
+lib-y += read_printf.o
+lib-y += read_key.o
+lib-y += recursive_action.o
+lib-y += remove_file.o
+lib-y += run_shell.o
+lib-y += safe_gethostname.o
+lib-y += safe_poll.o
+lib-y += safe_strncpy.o
+lib-y += safe_write.o
+lib-y += setup_environment.o
+lib-y += signals.o
+lib-y += simplify_path.o
+lib-y += single_argv.o
+lib-y += skip_whitespace.o
+lib-y += speed_table.o
+lib-y += str_tolower.o
+lib-y += strrstr.o
+lib-y += time.o
+lib-y += trim.o
+lib-y += u_signal_names.o
+lib-y += uuencode.o
+lib-y += vdprintf.o
+lib-y += verror_msg.o
+lib-y += vfork_daemon_rexec.o
+lib-y += warn_ignoring_args.o
+lib-y += wfopen.o
+lib-y += wfopen_input.o
+lib-y += write.o
+lib-y += xatonum.o
+lib-y += xconnect.o
+lib-y += xfuncs.o
+lib-y += xfuncs_printf.o
+lib-y += xfunc_die.o
+lib-y += xgetcwd.o
+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)
+# and objects which may fail to build (SELinux on selinux-less system)
+lib-$(CONFIG_SELINUX) += selinux_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
+
+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
+
+lib-$(CONFIG_ADDGROUP) += update_passwd.o
+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 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_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
+lib-$(CONFIG_FEATURE_HTTPD_AUTH_MD5) += pw_encrypt.o
+
+lib-$(CONFIG_DF) += find_mount_point.o
+lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o
+lib-$(CONFIG_MKFS_EXT2) += find_mount_point.o
+lib-$(CONFIG_MKFS_REISER) += find_mount_point.o
+lib-$(CONFIG_FSCK_MINIX) += find_mount_point.o
+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
+
+# 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.
+
+lib-$(CONFIG_AWK) += xregcomp.o
+lib-$(CONFIG_SED) += xregcomp.o
+lib-$(CONFIG_GREP) += xregcomp.o
+lib-$(CONFIG_EXPR) += xregcomp.o
+lib-$(CONFIG_MDEV) += xregcomp.o
+lib-$(CONFIG_LESS) += xregcomp.o
+lib-$(CONFIG_PGREP) += xregcomp.o
+lib-$(CONFIG_PKILL) += xregcomp.o
+lib-$(CONFIG_DEVFSD) += xregcomp.o
+lib-$(CONFIG_FEATURE_FIND_REGEX) += xregcomp.o
diff --git a/busybox-1.19.3/libbb/README b/busybox-1.19.3/libbb/README
new file mode 100644
index 0000000..6e63dc5
--- /dev/null
+++ b/busybox-1.19.3/libbb/README
@@ -0,0 +1,10 @@
+Please see the LICENSE file for copyright information (GPLv2)
+
+libbb is BusyBox's utility library.  All of this stuff used to be stuffed into
+a single file named utility.c.  When I split utility.c to create libbb, some of
+the very oldest stuff ended up without their original copyright and licensing
+information (which is now lost in the mists of time).  If you see something
+that you wrote that is mis-attributed, do let me know so we can fix that up.
+
+	Erik Andersen
+	<andersen@codepoet.org>
diff --git a/busybox-1.19.3/libbb/appletlib.c b/busybox-1.19.3/libbb/appletlib.c
new file mode 100644
index 0000000..8157b4f
--- /dev/null
+++ b/busybox-1.19.3/libbb/appletlib.c
@@ -0,0 +1,823 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) tons of folks.  Tracking down who wrote what
+ * isn't something I'm going to worry about...  If you wrote something
+ * 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 GPL.
+ *
+ * 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
+ * applets are really simple. Example:
+ *
+ * $ ./busybox
+ * ...
+ * Currently defined functions:
+ *         basename, false, true
+ *
+ * $ size busybox
+ *    text    data     bss     dec     hex filename
+ *    4473      52      72    4597    11f5 busybox
+ *
+ * FEATURE_INSTALLER or FEATURE_SUID will still link printf routines in. :(
+ */
+#include "busybox.h"
+
+#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
+        || defined(__APPLE__) \
+    )
+# include <malloc.h> /* for mallopt */
+#endif
+
+
+/* Declare <applet>_main() */
+#define PROTOTYPES
+#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... */
+#ifdef SINGLE_APPLET_MAIN
+# undef ENABLE_FEATURE_INDIVIDUAL
+# define ENABLE_FEATURE_INDIVIDUAL 1
+# undef IF_FEATURE_INDIVIDUAL
+# 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
+# define usage_messages 0
+#endif
+
+#if ENABLE_FEATURE_COMPRESS_USAGE
+
+static const char packed_usage[] ALIGN1 = { PACKED_USAGE };
+# include "archive.h"
+static const char *unpack_usage_messages(void)
+{
+	char *outbuf = NULL;
+	bunzip_data *bd;
+	int i;
+
+	i = start_bunzip(&bd,
+			/* src_fd: */ -1,
+			/* 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 */
+	if (!i) {
+		/* Cannot use xmalloc: will leak bd in NOFORK case! */
+		outbuf = malloc_or_warn(sizeof(UNPACKED_USAGE));
+		if (outbuf)
+			read_bunzip(bd, outbuf, sizeof(UNPACKED_USAGE));
+	}
+	dealloc_bunzip(bd);
+	return outbuf;
+}
+# define dealloc_usage_messages(s) free(s)
+
+#else
+
+# define unpack_usage_messages() usage_messages
+# define dealloc_usage_messages(s) ((void)(s))
+
+#endif /* FEATURE_COMPRESS_USAGE */
+
+
+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 *usage_string = unpack_usage_messages();
+
+		if (*usage_string == '\b') {
+			full_write2_str("No help available.\n\n");
+		} else {
+			full_write2_str("Usage: "SINGLE_APPLET_STR" ");
+			full_write2_str(usage_string);
+			full_write2_str("\n\n");
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			dealloc_usage_messages((char*)usage_string);
+#else
+		const char *p;
+		const char *usage_string = p = unpack_usage_messages();
+		int ap = find_applet_by_name(applet_name);
+
+		if (ap < 0) /* never happens, paranoia */
+			xfunc_die();
+		while (ap) {
+			while (*p++) continue;
+			ap--;
+		}
+		full_write2_str(bb_banner);
+		full_write2_str(" multi-call binary.\n");
+		if (*p == '\b')
+			full_write2_str("\nNo help available.\n\n");
+		else {
+			full_write2_str("\nUsage: ");
+			full_write2_str(applet_name);
+			full_write2_str(" ");
+			full_write2_str(p);
+			full_write2_str("\n\n");
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			dealloc_usage_messages((char*)usage_string);
+#endif
+	}
+	xfunc_die();
+}
+
+#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)
+{
+	int i = (const char *)v - applet_names;
+	return strcmp(name, APPLET_NAME(i));
+}
+#endif
+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;
+#else
+	/* A version which does not pull in bsearch */
+	int i = 0;
+	const char *p = applet_names;
+	while (i < NUM_APPLETS) {
+		if (strcmp(name, p) == 0)
+			return i;
+		p += strlen(p) + 1;
+		i++;
+	}
+	return -1;
+#endif
+}
+
+
+void lbb_prepare(const char *applet
+		IF_FEATURE_INDIVIDUAL(, char **argv))
+				MAIN_EXTERNALLY_VISIBLE;
+void lbb_prepare(const char *applet
+		IF_FEATURE_INDIVIDUAL(, char **argv))
+{
+#ifdef __GLIBC__
+	(*(int **)&bb_errno) = __errno_location();
+	barrier();
+#endif
+	applet_name = applet;
+
+	/* Set locale for everybody except 'init' */
+	if (ENABLE_LOCALE_SUPPORT && getpid() != 1)
+		setlocale(LC_ALL, "");
+
+#if ENABLE_FEATURE_INDIVIDUAL
+	/* Redundant for busybox (run_applet_and_exit covers that case)
+	 * but needed for "individual applet" mode */
+	if (argv[1]
+	 && !argv[2]
+	 && strcmp(argv[1], "--help") == 0
+	 && strncmp(applet, "busybox", 7) != 0
+	) {
+		/* Special case. POSIX says "test --help"
+		 * should be no different from e.g. "test --foo".  */
+		if (!ENABLE_TEST || strcmp(applet_name, "test") != 0)
+			bb_show_usage();
+	}
+#endif
+}
+
+/* The code below can well be in applets/applets.c, as it is used only
+ * for busybox binary, not "individual" binaries.
+ * However, keeping it here and linking it into libbusybox.so
+ * (together with remaining tiny applets/applets.o)
+ * makes it possible to avoid --whole-archive at link time.
+ * This makes (shared busybox) + libbusybox smaller.
+ * (--gc-sections would be even better....)
+ */
+
+const char *applet_name;
+#if !BB_MMU
+bool re_execed;
+#endif
+
+
+/* If not built as a single-applet executable... */
+#if !defined(SINGLE_APPLET_MAIN)
+
+IF_FEATURE_SUID(static uid_t ruid;)  /* real uid */
+
+# if ENABLE_FEATURE_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;
+	mode_t m_mode;
+} *suid_config;
+
+static bool suid_cfg_readable;
+
+/* check if u is member of group g */
+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;
+		}
+	}
+	return 0;
+}
+
+/* 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
+	 * reach a non-space char.  Set the char after that (possibly at
+	 * the original e) to nul. */
+	while (e-- > s) {
+		if (!isspace(*e)) {
+			break;
+		}
+	}
+	e[1] = '\0';
+
+	/* Next, advance past all leading space and return a ptr to the
+	 * first non-space char; possibly the terminating nul. */
+	return skip_whitespace(s);
+}
+
+static void parse_config_file(void)
+{
+	/* 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;
+	unsigned lc;
+	smallint section;
+	struct stat st;
+
+	ruid = getuid();
+	if (ruid == 0) /* run by root - don't need to even read config file */
+		return;
+
+	if ((stat(config_file, &st) != 0)       /* No config file? */
+	 || !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? */
+	) {
+		return;
+	}
+
+	suid_cfg_readable = 1;
+	sct_head = NULL;
+	section = lc = 0;
+
+	while (1) {
+		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.
+		 * The following test does mistreat one corner case though.
+		 * If the final line of the file does not end with a newline and
+		 * yet exactly fills the buffer, it will be treated as too long
+		 * even though there isn't really a problem.  But it isn't really
+		 * worth adding code to deal with such an unlikely situation, and
+		 * 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)) {
+			errmsg = "line too long";
+			goto pe_label;
+		}
+
+		/* Trim leading and trailing whitespace, ignoring comments, and
+		 * check if the resulting string is empty. */
+		s = get_trimmed_slice(s, strchrnul(s, '#'));
+		if (!*s) {
+			continue;
+		}
+
+		/* Check for a section header. */
+
+		if (*s == '[') {
+			/* 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. */
+			char *e = strchr(s, ']');
+			if (!e   /* Missing right bracket? */
+			 || e[1] /* Trailing characters? */
+			 || !*(s = get_trimmed_slice(s+1, e)) /* Missing name? */
+			) {
+				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
+			 * resort to cascading ifs with multiple strcasecmp calls.
+			 * That kind of bloated code is all too common.  A loop
+			 * and a string table would be a better choice unless the
+			 * number of sections is very small. */
+			if (strcasecmp(s, "SUID") == 0) {
+				section = 1;
+				continue;
+			}
+			section = -1;	/* Unknown section so set to skip. */
+			continue;
+		}
+
+		/* Process sections. */
+
+		if (section == 1) {		/* SUID */
+			/* Since we trimmed leading and trailing space above, we're
+			 * now looking for strings of the form
+			 *    <key>[::space::]*=[::space::]*<value>
+			 * where both key and value could contain inner whitespace. */
+
+			/* First get the key (an applet name in our case). */
+			char *e = strchr(s, '=');
+			if (e) {
+				s = get_trimmed_slice(s, e);
+			}
+			if (!e || !*s) {	/* Missing '=' or empty key. */
+				errmsg = "keyword";
+				goto pe_label;
+			}
+
+			/* Ok, we have an applet name.  Process the rhs if this
+			 * applet is currently built in and ignore it otherwise.
+			 * Note: this can hide config file bugs which only pop
+			 * 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 = xzalloc(sizeof(*sct));
+				sct->m_applet = applet_no;
+				/*sct->m_mode = 0;*/
+				sct->m_next = sct_head;
+				sct_head = sct;
+
+				/* Get the specified mode. */
+
+				e = skip_whitespace(e+1);
+
+				for (i = 0; i < 3; i++) {
+					/* 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;
+					}
+					sct->m_mode |= mode_mask[q - mode_chars];
+					e++;
+				}
+
+				/* Now get the user/group info. */
+
+				s = skip_whitespace(e);
+				/* 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;
+					}
+					*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;
+					}
+				}
+			}
+			continue;
+		}
+
+		/* Unknown sections are ignored. */
+
+		/* Encountering configuration lines prior to seeing a
+		 * section header is treated as an error.  This is how
+		 * the old code worked, but it may not be desirable.
+		 * We may want to simply ignore such lines in case they
+		 * are used in some future version of busybox. */
+		if (!section) {
+			errmsg = "keyword outside section";
+			goto pe_label;
+		}
+
+	} /* while (1) */
+
+ pe_label:
+	fclose(f);
+	bb_error_msg("parse error in %s, line %u: %s", config_file, lc, errmsg);
+
+	/* Release any allocated memory before returning. */
+	llist_free((llist_t*)sct_head, NULL);
+}
+# else
+static inline void parse_config_file(void)
+{
+	IF_FEATURE_SUID(ruid = getuid();)
+}
+# endif /* FEATURE_SUID_CONFIG */
+
+
+# if ENABLE_FEATURE_SUID
+static void check_suid(int applet_no)
+{
+	gid_t rgid;  /* real gid */
+
+	if (ruid == 0) /* set by parse_config_file() */
+		return; /* run by root - no need to check more */
+	rgid = getgid();
+
+#  if ENABLE_FEATURE_SUID_CONFIG
+	if (suid_cfg_readable) {
+		uid_t uid;
+		struct suid_config_t *sct;
+		mode_t m;
+
+		for (sct = suid_config; sct; sct = sct->m_next) {
+			if (sct->m_applet == applet_no)
+				goto found;
+		}
+		goto check_need_suid;
+ found:
+		/* Is this user allowed to run this applet? */
+		m = sct->m_mode;
+		if (sct->m_ugid.uid == ruid)
+			/* same uid */
+			m >>= 6;
+		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");
+
+		/* We set effective AND saved ids. If saved-id is not set
+		 * 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");
+
+		/* 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_ugid.uid;
+		/* else: we will set euid = ruid, thus dropping suid effect */
+		if (setresuid(-1, uid, uid))
+			bb_perror_msg_and_die("setresuid");
+
+		goto ret;
+	}
+#   if !ENABLE_FEATURE_SUID_CONFIG_QUIET
+	{
+		static bool onetime = 0;
+
+		if (!onetime) {
+			onetime = 1;
+			bb_error_msg("using fallback suid method");
+		}
+	}
+#   endif
+ check_need_suid:
+#  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) {
+		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 */
+
+
+# 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/" */
+#  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)
+{
+	/* directory table
+	 * this should be consistent w/ the enum,
+	 * busybox.h::bb_install_loc_t, or else... */
+	int (*lf)(const char *, const char *);
+	char *fpc;
+	unsigned i;
+	int rc;
+
+	lf = link;
+	if (use_symbolic_links)
+		lf = symlink;
+
+	for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
+		fpc = concat_path_file(
+				custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
+				APPLET_NAME(i));
+		// debug: bb_error_msg("%slinking %s to busybox",
+		//		use_symbolic_links ? "sym" : "", fpc);
+		rc = lf(busybox, fpc);
+		if (rc != 0 && errno != EEXIST) {
+			bb_simple_perror_msg(fpc);
+		}
+		free(fpc);
+	}
+}
+# else
+#  define install_links(x,y,z) ((void)0)
+# endif
+
+/* If we were called as "busybox..." */
+static int busybox_main(char **argv)
+{
+	if (!argv[1]) {
+		/* Called without arguments */
+		const char *a;
+		int col;
+		unsigned output_width;
+ help:
+		output_width = 80;
+		if (ENABLE_FEATURE_AUTOWIDTH) {
+			/* Obtain the terminal width */
+			get_terminal_width_height(0, &output_width, NULL);
+		}
+
+		dup2(1, 2);
+		full_write2_str(bb_banner); /* reuse const string */
+		full_write2_str(" multi-call binary.\n"); /* reuse */
+		full_write2_str(
+			"Copyright (C) 1998-2011 Erik Andersen, Rob Landley, Denys Vlasenko\n"
+			"and others. Licensed under GPLv2.\n"
+			"See source distribution for full notice.\n"
+			"\n"
+			"Usage: busybox [function] [arguments]...\n"
+			"   or: busybox --list[-full]\n"
+			"   or: function [arguments]...\n"
+			"\n"
+			"\tBusyBox is a multi-call binary that combines many common Unix\n"
+			"\tutilities into a single executable.  Most people will create a\n"
+			"\tlink to busybox for each function they wish to use and BusyBox\n"
+			"\twill act like whatever it was invoked as.\n"
+			"\n"
+			"Currently defined functions:\n"
+		);
+		col = 0;
+		a = applet_names;
+		/* prevent last comma to be in the very last pos */
+		output_width--;
+		while (*a) {
+			int len2 = strlen(a) + 2;
+			if (col >= (int)output_width - len2) {
+				full_write2_str(",\n");
+				col = 0;
+			}
+			if (col == 0) {
+				col = 6;
+				full_write2_str("\t");
+			} else {
+				full_write2_str(", ");
+			}
+			full_write2_str(a);
+			col += len2;
+			a += len2 - 1;
+		}
+		full_write2_str("\n\n");
+		return 0;
+	}
+
+	if (strncmp(argv[1], "--list", 6) == 0) {
+		unsigned i = 0;
+		const char *a = applet_names;
+		dup2(1, 2);
+		while (*a) {
+# if ENABLE_FEATURE_INSTALLER
+			if (argv[1][6]) /* --list-path? */
+				full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
+# endif
+			full_write2_str(a);
+			full_write2_str("\n");
+			i++;
+			a += strlen(a) + 1;
+		}
+		return 0;
+	}
+
+	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) {
+			/* 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;
+	}
+
+	if (strcmp(argv[1], "--help") == 0) {
+		/* "busybox --help [<applet>]" */
+		if (!argv[2])
+			goto help;
+		/* convert to "<applet> --help" */
+		argv[0] = argv[2];
+		argv[2] = NULL;
+	} else {
+		/* "busybox <applet> arg1 arg2 ..." */
+		argv++;
+	}
+	/* We support "busybox /a/path/to/applet args..." too. Allows for
+	 * "#!/bin/busybox"-style wrappers */
+	applet_name = bb_get_last_path_component_nostrip(argv[0]);
+	run_applet_and_exit(applet_name, argv);
+
+	/*bb_error_msg_and_die("applet not found"); - sucks in printf */
+	full_write2_str(applet_name);
+	full_write2_str(": applet not found\n");
+	xfunc_die();
+}
+
+void FAST_FUNC run_applet_no_and_exit(int applet_no, char **argv)
+{
+	int argc = 1;
+
+	while (argv[argc])
+		argc++;
+
+	/* Reinit some shared global data */
+	xfunc_error_retval = EXIT_FAILURE;
+
+	applet_name = APPLET_NAME(applet_no);
+	if (argc == 2 && strcmp(argv[1], "--help") == 0) {
+		/* 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)
+			bb_show_usage();
+	}
+	if (ENABLE_FEATURE_SUID)
+		check_suid(applet_no);
+	exit(applet_main[applet_no](argc, argv));
+}
+
+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) == 0)
+		exit(busybox_main(argv));
+}
+
+#endif /* !defined(SINGLE_APPLET_MAIN) */
+
+
+
+#if ENABLE_BUILD_LIBBUSYBOX
+int lbb_main(char **argv)
+#else
+int main(int argc UNUSED_PARAM, char **argv)
+#endif
+{
+	/* Tweak malloc for reduced memory consumption */
+#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, 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, 32 * 1024 - 256);
+#endif
+
+#if !BB_MMU
+	/* NOMMU re-exec trick sets high-order bit in first byte of name */
+	if (argv[0][0] & 0x80) {
+		re_execed = 1;
+		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++;
+	applet_name = bb_basename(applet_name);
+
+	parse_config_file(); /* ...maybe, if FEATURE_SUID_CONFIG */
+
+	run_applet_and_exit(applet_name, argv);
+
+	/*bb_error_msg_and_die("applet not found"); - sucks in printf */
+	full_write2_str(applet_name);
+	full_write2_str(": applet not found\n");
+	xfunc_die();
+#endif
+}
diff --git a/busybox-1.19.3/libbb/ask_confirmation.c b/busybox-1.19.3/libbb/ask_confirmation.c
new file mode 100644
index 0000000..d95729c
--- /dev/null
+++ b/busybox-1.19.3/libbb/ask_confirmation.c
@@ -0,0 +1,27 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_ask_confirmation implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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',
+ * return 1.  Otherwise return 0.
+ */
+#include "libbb.h"
+
+int FAST_FUNC bb_ask_confirmation(void)
+{
+	char first = 0;
+	int c;
+
+	while (((c = getchar()) != EOF) && (c != '\n')) {
+		if (first == 0 && !isblank(c)) {
+			first = c|0x20;
+		}
+	}
+
+	return first == 'y';
+}
diff --git a/busybox-1.19.3/libbb/bb_askpass.c b/busybox-1.19.3/libbb/bb_askpass.c
new file mode 100644
index 0000000..9a4188f
--- /dev/null
+++ b/busybox-1.19.3/libbb/bb_askpass.c
@@ -0,0 +1,84 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Ask for a password
+ * I use a static buffer in this function.  Plan accordingly.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* do nothing signal handler */
+static void askpass_timeout(int UNUSED_PARAM ignore)
+{
+}
+
+char* FAST_FUNC bb_ask_stdin(const char *prompt)
+{
+	return bb_ask(STDIN_FILENO, 0, prompt);
+}
+char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
+{
+	/* Was static char[BIGNUM] */
+	enum { sizeof_passwd = 128 };
+	static char *passwd;
+
+	char *ret;
+	int i;
+	struct sigaction sa, oldsa;
+	struct termios tio, oldtio;
+
+	tcgetattr(fd, &oldtio);
+	tcflush(fd, TCIFLUSH);
+	tio = oldtio;
+#ifndef IUCLC
+# define IUCLC 0
+#endif
+	tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
+	tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP);
+	tcsetattr(fd, TCSANOW, &tio);
+
+	memset(&sa, 0, sizeof(sa));
+	/* sa.sa_flags = 0; - no SA_RESTART! */
+	/* SIGINT and SIGALRM will interrupt reads below */
+	sa.sa_handler = askpass_timeout;
+	sigaction(SIGINT, &sa, &oldsa);
+	if (timeout) {
+		sigaction_set(SIGALRM, &sa);
+		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) {
+			/* read is interrupted by timeout or ^C */
+			ret = NULL;
+			break;
+		}
+		if (r == 0 /* EOF */
+		 || ret[i] == '\r' || ret[i] == '\n' /* EOL */
+		 || ++i == sizeof_passwd-1 /* line limit */
+		) {
+			ret[i] = '\0';
+			break;
+		}
+	}
+
+	if (timeout) {
+		alarm(0);
+	}
+	sigaction_set(SIGINT, &oldsa);
+	tcsetattr(fd, TCSANOW, &oldtio);
+	bb_putchar('\n');
+	fflush_all();
+	return ret;
+}
diff --git a/busybox-1.19.3/libbb/bb_bswap_64.c b/busybox-1.19.3/libbb/bb_bswap_64.c
new file mode 100644
index 0000000..ce9d53b
--- /dev/null
+++ b/busybox-1.19.3/libbb/bb_bswap_64.c
@@ -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
diff --git a/busybox-1.19.3/libbb/bb_do_delay.c b/busybox-1.19.3/libbb/bb_do_delay.c
new file mode 100644
index 0000000..05c879f
--- /dev/null
+++ b/busybox-1.19.3/libbb/bb_do_delay.c
@@ -0,0 +1,21 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Busybox utility routines.
+ *
+ * Copyright (C) 2005 by Tito Ragusa <tito-wolit@tiscali.it>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+void FAST_FUNC bb_do_delay(int seconds)
+{
+	time_t start, now;
+
+	start = time(NULL);
+	do {
+		sleep(seconds);
+		now = time(NULL);
+	} while ((now - start) < seconds);
+}
diff --git a/busybox-1.19.3/libbb/bb_pwd.c b/busybox-1.19.3/libbb/bb_pwd.c
new file mode 100644
index 0000000..4829b72
--- /dev/null
+++ b/busybox-1.19.3/libbb/bb_pwd.c
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * password utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2008 by Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* TODO: maybe change API to return malloced data?
+ * This will allow to stop using libc functions returning
+ * pointers to static data (getpwuid)
+ */
+
+struct passwd* FAST_FUNC xgetpwnam(const char *name)
+{
+	struct passwd *pw = getpwnam(name);
+	if (!pw)
+		bb_error_msg_and_die("unknown user %s", name);
+	return pw;
+}
+
+struct group* FAST_FUNC xgetgrnam(const char *name)
+{
+	struct group *gr = getgrnam(name);
+	if (!gr)
+		bb_error_msg_and_die("unknown group %s", name);
+	return gr;
+}
+
+
+struct passwd* FAST_FUNC xgetpwuid(uid_t uid)
+{
+	struct passwd *pw = getpwuid(uid);
+	if (!pw)
+		bb_error_msg_and_die("unknown uid %u", (unsigned)uid);
+	return pw;
+}
+
+struct group* FAST_FUNC xgetgrgid(gid_t gid)
+{
+	struct group *gr = getgrgid(gid);
+	if (!gr)
+		bb_error_msg_and_die("unknown gid %u", (unsigned)gid);
+	return gr;
+}
+
+char* FAST_FUNC xuid2uname(uid_t uid)
+{
+	struct passwd *pw = xgetpwuid(uid);
+	return pw->pw_name;
+}
+
+char* FAST_FUNC xgid2group(gid_t gid)
+{
+	struct group *gr = xgetgrgid(gid);
+	return gr->gr_name;
+}
+
+char* FAST_FUNC uid2uname(uid_t uid)
+{
+	struct passwd *pw = getpwuid(uid);
+	return (pw) ? pw->pw_name : NULL;
+}
+
+char* FAST_FUNC gid2group(gid_t gid)
+{
+	struct group *gr = getgrgid(gid);
+	return (gr) ? gr->gr_name : NULL;
+}
+
+char* FAST_FUNC uid2uname_utoa(uid_t uid)
+{
+	char *name = uid2uname(uid);
+	return (name) ? name : utoa(uid);
+}
+
+char* FAST_FUNC gid2group_utoa(gid_t gid)
+{
+	char *name = gid2group(gid);
+	return (name) ? name : utoa(gid);
+}
+
+long FAST_FUNC xuname2uid(const char *name)
+{
+	struct passwd *myuser;
+
+	myuser = xgetpwnam(name);
+	return myuser->pw_uid;
+}
+
+long FAST_FUNC xgroup2gid(const char *name)
+{
+	struct group *mygroup;
+
+	mygroup = xgetgrnam(name);
+	return mygroup->gr_gid;
+}
+
+unsigned long FAST_FUNC get_ug_id(const char *s,
+		long FAST_FUNC (*xname2id)(const char *))
+{
+	unsigned long r;
+
+	r = bb_strtoul(s, NULL, 10);
+	if (errno)
+		return xname2id(s);
+	return r;
+}
diff --git a/busybox-1.19.3/libbb/bb_qsort.c b/busybox-1.19.3/libbb/bb_qsort.c
new file mode 100644
index 0000000..a54e723
--- /dev/null
+++ b/busybox-1.19.3/libbb/bb_qsort.c
@@ -0,0 +1,20 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Wrapper for common string vector sorting operation
+ *
+ * Copyright (c) 2008 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+int /* not FAST_FUNC! */ bb_pstrcmp(const void *a, const void *b)
+{
+	return strcmp(*(char**)a, *(char**)b);
+}
+
+void FAST_FUNC qsort_string_vector(char **sv, unsigned count)
+{
+	qsort(sv, count, sizeof(char*), bb_pstrcmp);
+}
diff --git a/busybox-1.19.3/libbb/bb_strtod.c b/busybox-1.19.3/libbb/bb_strtod.c
new file mode 100644
index 0000000..5dde784
--- /dev/null
+++ b/busybox-1.19.3/libbb/bb_strtod.c
@@ -0,0 +1,88 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+#include <math.h>  /* just for HUGE_VAL */
+
+#define NOT_DIGIT(a) (((unsigned char)(a-'0')) > 9)
+
+#if 0 // UNUSED
+double FAST_FUNC bb_strtod(const char *arg, char **endp)
+{
+	double v;
+	char *endptr;
+
+	/* Allow .NN form. People want to use "sleep .15" etc */
+	if (arg[0] != '-' && arg[0] != '.' && NOT_DIGIT(arg[0]))
+		goto err;
+	errno = 0;
+	v = strtod(arg, &endptr);
+	if (endp)
+		*endp = endptr;
+	if (endptr[0]) {
+		/* "1234abcg" or out-of-range? */
+		if (isalnum(endptr[0]) || errno) {
+ err:
+			errno = ERANGE;
+			return HUGE_VAL;
+		}
+		/* good number, just suspicious terminator */
+		errno = EINVAL;
+	}
+	return v;
+}
+#endif
+
+#if 0
+/* String to timespec: "NNNN[.NNNNN]" -> struct timespec.
+ * Can be used for other fixed-point needs.
+ * Returns pointer past last converted char,
+ * and returns errno similar to bb_strtoXX functions.
+ */
+char* FAST_FUNC bb_str_to_ts(struct timespec *ts, const char *arg)
+{
+	if (sizeof(ts->tv_sec) <= sizeof(int))
+		ts->tv_sec = bb_strtou(arg, &arg, 10);
+	else if (sizeof(ts->tv_sec) <= sizeof(long))
+		ts->tv_sec = bb_strtoul(arg, &arg, 10);
+	else
+		ts->tv_sec = bb_strtoull(arg, &arg, 10);
+	ts->tv_nsec = 0;
+
+	if (*arg != '.')
+		return arg;
+
+	/* !EINVAL: number is not ok (alphanumeric ending, overflow etc) */
+	if (errno != EINVAL)
+		return arg;
+
+	if (!*++arg) /* "NNN." */
+		return arg;
+
+	{ /* "NNN.xxx" - parse xxx */
+		int ndigits;
+		char *p;
+		char buf[10]; /* we never use more than 9 digits */
+
+		/* Need to make a copy to avoid false overflow */
+		safe_strncpy(buf, arg, 10);
+		ts->tv_nsec = bb_strtou(buf, &p, 10);
+		ndigits = p - buf;
+		arg += ndigits;
+		/* normalize to nsec */
+		while (ndigits < 9) {
+			ndigits++;
+			ts->tv_nsec *= 10;
+		}
+		while (isdigit(*arg)) /* skip possible 10th plus digits */
+			arg++;
+	}
+	return arg;
+}
+#endif
diff --git a/busybox-1.19.3/libbb/bb_strtonum.c b/busybox-1.19.3/libbb/bb_strtonum.c
new file mode 100644
index 0000000..c66c774
--- /dev/null
+++ b/busybox-1.19.3/libbb/bb_strtonum.c
@@ -0,0 +1,139 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+/* On exit: errno = 0 only if there was non-empty, '\0' terminated value
+ * errno = EINVAL if value was not '\0' terminated, but otherwise ok
+ *    Return value is still valid, caller should just check whether end[0]
+ *    is a valid terminating char for particular case. OTOH, if caller
+ *    requires '\0' terminated input, [s]he can just check errno == 0.
+ * errno = ERANGE if value had alphanumeric terminating char ("1234abcg").
+ * errno = ERANGE if value is out of range, missing, etc.
+ * errno = ERANGE if value had minus sign for strtouXX (even "-0" is not ok )
+ *    return value is all-ones in this case.
+ *
+ * Test code:
+ * char *endptr;
+ * const char *minus = "-";
+ * errno = 0;
+ * bb_strtoi(minus, &endptr, 0); // must set ERANGE
+ * printf("minus:%p endptr:%p errno:%d EINVAL:%d\n", minus, endptr, errno, EINVAL);
+ * errno = 0;
+ * bb_strtoi("-0-", &endptr, 0); // must set EINVAL and point to second '-'
+ * printf("endptr[0]:%c errno:%d EINVAL:%d\n", endptr[0], errno, EINVAL);
+ */
+
+static unsigned long long ret_ERANGE(void)
+{
+	errno = ERANGE; /* this ain't as small as it looks (on glibc) */
+	return ULLONG_MAX;
+}
+
+static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr)
+{
+	if (endp) *endp = endptr;
+
+	/* errno is already set to ERANGE by strtoXXX if value overflowed */
+	if (endptr[0]) {
+		/* "1234abcg" or out-of-range? */
+		if (isalnum(endptr[0]) || errno)
+			return ret_ERANGE();
+		/* good number, just suspicious terminator */
+		errno = EINVAL;
+	}
+	return v;
+}
+
+
+unsigned long long FAST_FUNC bb_strtoull(const char *arg, char **endp, int base)
+{
+	unsigned long long v;
+	char *endptr;
+
+	/* 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);
+}
+
+long long FAST_FUNC bb_strtoll(const char *arg, char **endp, int base)
+{
+	unsigned long long v;
+	char *endptr;
+
+	/* 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]);
+	if (!isalnum(first)) return ret_ERANGE();
+
+	errno = 0;
+	v = strtoll(arg, &endptr, base);
+	return handle_errors(v, endp, endptr);
+}
+
+#if ULONG_MAX != ULLONG_MAX
+unsigned long FAST_FUNC bb_strtoul(const char *arg, char **endp, int base)
+{
+	unsigned long v;
+	char *endptr;
+
+	if (!isalnum(arg[0])) return ret_ERANGE();
+	errno = 0;
+	v = strtoul(arg, &endptr, base);
+	return handle_errors(v, endp, endptr);
+}
+
+long FAST_FUNC bb_strtol(const char *arg, char **endp, int base)
+{
+	long v;
+	char *endptr;
+
+	char 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);
+}
+#endif
+
+#if UINT_MAX != ULONG_MAX
+unsigned FAST_FUNC bb_strtou(const char *arg, char **endp, int base)
+{
+	unsigned long v;
+	char *endptr;
+
+	if (!isalnum(arg[0])) return ret_ERANGE();
+	errno = 0;
+	v = strtoul(arg, &endptr, base);
+	if (v > UINT_MAX) return ret_ERANGE();
+	return handle_errors(v, endp, endptr);
+}
+
+int FAST_FUNC bb_strtoi(const char *arg, char **endp, int base)
+{
+	long v;
+	char *endptr;
+
+	char first = (arg[0] != '-' ? arg[0] : arg[1]);
+	if (!isalnum(first)) return ret_ERANGE();
+
+	errno = 0;
+	v = strtol(arg, &endptr, base);
+	if (v > INT_MAX) return ret_ERANGE();
+	if (v < INT_MIN) return ret_ERANGE();
+	return handle_errors(v, endp, endptr);
+}
+#endif
diff --git a/busybox-1.19.3/libbb/change_identity.c b/busybox-1.19.3/libbb/change_identity.c
new file mode 100644
index 0000000..619db09
--- /dev/null
+++ b/busybox-1.19.3/libbb/change_identity.c
@@ -0,0 +1,41 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * 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. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``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 JULIE HAUGH OR CONTRIBUTORS 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.
+ */
+
+#include "libbb.h"
+
+/* Become the user and group(s) specified by PW.  */
+void FAST_FUNC change_identity(const struct passwd *pw)
+{
+	if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+		bb_perror_msg_and_die("can't set groups");
+	endgrent(); /* helps to close a fd used internally by libc */
+	xsetgid(pw->pw_gid);
+	xsetuid(pw->pw_uid);
+}
diff --git a/busybox-1.19.3/libbb/chomp.c b/busybox-1.19.3/libbb/chomp.c
new file mode 100644
index 0000000..cb92bef
--- /dev/null
+++ b/busybox-1.19.3/libbb/chomp.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+void FAST_FUNC chomp(char *s)
+{
+	char *lc = last_char_is(s, '\n');
+
+	if (lc)
+		*lc = '\0';
+}
diff --git a/busybox-1.19.3/libbb/compare_string_array.c b/busybox-1.19.3/libbb/compare_string_array.c
new file mode 100644
index 0000000..4b10cc1
--- /dev/null
+++ b/busybox-1.19.3/libbb/compare_string_array.c
@@ -0,0 +1,95 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* returns the array index of the string */
+/* (index of first match is returned, or -1) */
+int FAST_FUNC index_in_str_array(const char *const string_array[], const char *key)
+{
+	int i;
+
+	for (i = 0; string_array[i] != 0; i++) {
+		if (strcmp(string_array[i], key) == 0) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+int FAST_FUNC index_in_strings(const char *strings, const char *key)
+{
+	int idx = 0;
+
+	while (*strings) {
+		if (strcmp(strings, key) == 0) {
+			return idx;
+		}
+		strings += strlen(strings) + 1; /* skip NUL */
+		idx++;
+	}
+	return -1;
+}
+
+/* returns the array index of the string, even if it matches only a beginning */
+/* (index of first match is returned, or -1) */
+#ifdef UNUSED
+int FAST_FUNC index_in_substr_array(const char *const string_array[], const char *key)
+{
+	int i;
+	int len = strlen(key);
+	if (len) {
+		for (i = 0; string_array[i] != 0; i++) {
+			if (strncmp(string_array[i], key, len) == 0) {
+				return i;
+			}
+		}
+	}
+	return -1;
+}
+#endif
+
+int FAST_FUNC index_in_substrings(const char *strings, const char *key)
+{
+	int matched_idx = -1;
+	const int len = strlen(key);
+
+	if (len) {
+		int idx = 0;
+		while (*strings) {
+			if (strncmp(strings, key, len) == 0) {
+				if (strings[len] == '\0')
+					return idx; /* exact match */
+				if (matched_idx >= 0)
+					return -1; /* ambiguous match */
+				matched_idx = idx;
+			}
+			strings += strlen(strings) + 1; /* skip NUL */
+			idx++;
+		}
+	}
+	return matched_idx;
+}
+
+const char* FAST_FUNC nth_string(const char *strings, int n)
+{
+	while (n) {
+		n--;
+		strings += strlen(strings) + 1;
+	}
+	return strings;
+}
+
+#ifdef UNUSED_SO_FAR /* only brctl.c needs it yet */
+/* Returns 0 for no, 1 for yes or a negative value on error.  */
+smallint FAST_FUNC yesno(const char *str)
+{
+	static const char no_yes[] ALIGN1 =
+		"0\0" "off\0" "no\0"
+		"1\0" "on\0" "yes\0";
+	int ret = index_in_substrings(no_yes, str);
+	return ret / 3;
+}
+#endif
diff --git a/busybox-1.19.3/libbb/concat_path_file.c b/busybox-1.19.3/libbb/concat_path_file.c
new file mode 100644
index 0000000..9ed2959
--- /dev/null
+++ b/busybox-1.19.3/libbb/concat_path_file.c
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Concatenate path and filename to new allocated buffer.
+ * Add '/' only as needed (no duplicate // are produced).
+ * If path is NULL, it is assumed to be "/".
+ * filename should not be NULL.
+ */
+
+#include "libbb.h"
+
+char* FAST_FUNC concat_path_file(const char *path, const char *filename)
+{
+	char *lc;
+
+	if (!path)
+		path = "";
+	lc = last_char_is(path, '/');
+	while (*filename == '/')
+		filename++;
+	return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename);
+}
diff --git a/busybox-1.19.3/libbb/concat_subpath_file.c b/busybox-1.19.3/libbb/concat_subpath_file.c
new file mode 100644
index 0000000..c9167d4
--- /dev/null
+++ b/busybox-1.19.3/libbb/concat_subpath_file.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) (C) 2003  Vladimir Oleynik  <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+   This function make special for recursive actions with usage
+   concat_path_file(path, filename)
+   and skipping "." and ".." directory entries
+*/
+
+#include "libbb.h"
+
+char* FAST_FUNC concat_subpath_file(const char *path, const char *f)
+{
+	if (f && DOT_OR_DOTDOT(f))
+		return NULL;
+	return concat_path_file(path, f);
+}
diff --git a/busybox-1.19.3/libbb/copy_file.c b/busybox-1.19.3/libbb/copy_file.c
new file mode 100644
index 0000000..9333a8d
--- /dev/null
+++ b/busybox-1.19.3/libbb/copy_file.c
@@ -0,0 +1,393 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini copy_file implementation for busybox
+ *
+ * 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 source tree.
+ */
+#include "libbb.h"
+
+// FEATURE_NON_POSIX_CP:
+//
+// POSIX: if exists and -i, ask (w/o -i assume yes).
+// Then open w/o EXCL (yes, not unlink!).
+// If open still fails and -f, try unlink, then try open again.
+// Result: a mess:
+// If dest is a (sym)link, we overwrite link destination!
+// (or fail, if it points to dir/nonexistent location/etc).
+// This is strange, but POSIX-correct.
+// coreutils cp has --remove-destination to override this...
+
+/* Called if open of destination, link creation etc fails.
+ * errno must be set to relevant value ("why we cannot create dest?")
+ * to give reasonable error message */
+static int ask_and_unlink(const char *dest, int flags)
+{
+	int e = errno;
+
+#if !ENABLE_FEATURE_NON_POSIX_CP
+	if (!(flags & (FILEUTILS_FORCE|FILEUTILS_INTERACTIVE))) {
+		/* Either it exists, or the *path* doesnt exist */
+		bb_perror_msg("can't create '%s'", dest);
+		return -1;
+	}
+#endif
+	// else: act as if -f is always in effect.
+	// We don't want "can't create" msg, we want unlink to be done
+	// (silently unless -i). Why? POSIX cp usually succeeds with
+	// O_TRUNC open of existing file, and user is left ignorantly happy.
+	// With above block unconditionally enabled, non-POSIX cp
+	// will complain a lot more than POSIX one.
+
+	/* TODO: maybe we should do it only if ctty is present? */
+	if (flags & FILEUTILS_INTERACTIVE) {
+		// We would not do POSIX insanity. -i asks,
+		// then _unlinks_ the offender. Presto.
+		// (No "opening without O_EXCL", no "unlink only if -f")
+		// Or else we will end up having 3 open()s!
+		fprintf(stderr, "%s: overwrite '%s'? ", applet_name, dest);
+		if (!bb_ask_confirmation())
+			return 0; /* not allowed to overwrite */
+	}
+	if (unlink(dest) < 0) {
+#if ENABLE_FEATURE_VERBOSE_CP_MESSAGE
+		if (e == errno && e == ENOENT) {
+			/* e == ENOTDIR is similar: path has non-dir component,
+			 * but in this case we don't even reach copy_file() */
+			bb_error_msg("can't create '%s': Path does not exist", dest);
+			return -1; /* error */
+		}
+#endif
+		errno = e; /* do not use errno from unlink */
+		bb_perror_msg("can't create '%s'", dest);
+		return -1; /* error */
+	}
+	return 1; /* ok (to try again) */
+}
+
+/* Return:
+ * -1 error, copy not made
+ *  0 copy is made or user answered "no" in interactive mode
+ *    (failures to preserve mode/owner/times are not reported in exit code)
+ */
+int FAST_FUNC copy_file(const char *source, const char *dest, int flags)
+{
+	/* This is a recursive function, try to minimize stack usage */
+	/* NB: each struct stat is ~100 bytes */
+	struct stat source_stat;
+	struct stat dest_stat;
+	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))
+
+	if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) {
+		/* This may be a dangling symlink.
+		 * Making [sym]links to dangling symlinks works, so... */
+		if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK))
+			goto make_links;
+		bb_perror_msg("can't stat '%s'", source);
+		return -1;
+	}
+
+	if (lstat(dest, &dest_stat) < 0) {
+		if (errno != ENOENT) {
+			bb_perror_msg("can't stat '%s'", dest);
+			return -1;
+		}
+	} else {
+		if (source_stat.st_dev == dest_stat.st_dev
+		 && source_stat.st_ino == dest_stat.st_ino
+		) {
+			bb_error_msg("'%s' and '%s' are the same file", source, dest);
+			return -1;
+		}
+		dest_exists = 1;
+	}
+
+#if ENABLE_SELINUX
+	if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) {
+		security_context_t con;
+		if (lgetfilecon(source, &con) >= 0) {
+			if (setfscreatecon(con) < 0) {
+				bb_perror_msg("can't set setfscreatecon %s", con);
+				freecon(con);
+				return -1;
+			}
+		} else if (errno == ENOTSUP || errno == ENODATA) {
+			setfscreatecon_or_die(NULL);
+		} else {
+			bb_perror_msg("can't lgetfilecon %s", source);
+			return -1;
+		}
+	}
+#endif
+
+	if (S_ISDIR(source_stat.st_mode)) {
+		DIR *dp;
+		const char *tp;
+		struct dirent *d;
+		mode_t saved_umask = 0;
+
+		if (!(flags & FILEUTILS_RECUR)) {
+			bb_error_msg("omitting directory '%s'", source);
+			return -1;
+		}
+
+		/* Did we ever create source ourself before? */
+		tp = is_in_ino_dev_hashtable(&source_stat);
+		if (tp) {
+			/* We did! it's a recursion! man the lifeboats... */
+			bb_error_msg("recursion detected, omitting directory '%s'",
+					source);
+			return -1;
+		}
+
+		if (dest_exists) {
+			if (!S_ISDIR(dest_stat.st_mode)) {
+				bb_error_msg("target '%s' is not a directory", dest);
+				return -1;
+			}
+			/* 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);
+
+			mode = source_stat.st_mode;
+			if (!(flags & FILEUTILS_PRESERVE_STATUS))
+				mode = source_stat.st_mode & ~saved_umask;
+			/* Allow owner to access new dir (at least for now) */
+			mode |= S_IRWXU;
+			if (mkdir(dest, mode) < 0) {
+				umask(saved_umask);
+				bb_perror_msg("can't create directory '%s'", dest);
+				return -1;
+			}
+			umask(saved_umask);
+			/* need stat info for add_to_ino_dev_hashtable */
+			if (lstat(dest, &dest_stat) < 0) {
+				bb_perror_msg("can't stat '%s'", dest);
+				return -1;
+			}
+		}
+		/* remember (dev,inode) of each created dir.
+		 * NULL: name is not remembered */
+		add_to_ino_dev_hashtable(&dest_stat, NULL);
+
+		/* Recursively copy files in SOURCE */
+		dp = opendir(source);
+		if (dp == NULL) {
+			retval = -1;
+			goto preserve_mode_ugid_time;
+		}
+
+		while ((d = readdir(dp)) != NULL) {
+			char *new_source, *new_dest;
+
+			new_source = concat_subpath_file(source, d->d_name);
+			if (new_source == NULL)
+				continue;
+			new_dest = concat_path_file(dest, d->d_name);
+			if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0)
+				retval = -1;
+			free(new_source);
+			free(new_dest);
+		}
+		closedir(dp);
+
+		if (!dest_exists
+		 && chmod(dest, source_stat.st_mode & ~saved_umask) < 0
+		) {
+			bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+			/* retval = -1; - WRONG! copy *WAS* made */
+		}
+		goto preserve_mode_ugid_time;
+	}
+
+	if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) {
+		int (*lf)(const char *oldpath, const char *newpath);
+ make_links:
+		/* Hmm... maybe
+		 * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ?
+		 * (but realpath returns NULL on dangling symlinks...) */
+		lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link;
+		if (lf(source, dest) < 0) {
+			ovr = ask_and_unlink(dest, flags);
+			if (ovr <= 0)
+				return ovr;
+			if (lf(source, dest) < 0) {
+				bb_perror_msg("can't create link '%s'", dest);
+				return -1;
+			}
+		}
+		/* _Not_ jumping to preserve_mode_ugid_time:
+		 * (sym)links don't have those */
+		return 0;
+	}
+
+	if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */
+	    !(flags & FILEUTILS_RECUR)
+	    /* "cp [-opts] regular_file thing2" */
+	 || S_ISREG(source_stat.st_mode)
+	 /* DEREF uses stat, which never returns S_ISLNK() == true.
+	  * So the below is never true: */
+	 /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */
+	) {
+		int src_fd;
+		int dst_fd;
+		mode_t new_mode;
+
+		if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) {
+			/* "cp -d symlink dst": create a link */
+			goto dont_cat;
+		}
+
+		if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) {
+			const char *link_target;
+			link_target = is_in_ino_dev_hashtable(&source_stat);
+			if (link_target) {
+				if (link(link_target, dest) < 0) {
+					ovr = ask_and_unlink(dest, flags);
+					if (ovr <= 0)
+						return ovr;
+					if (link(link_target, dest) < 0) {
+						bb_perror_msg("can't create link '%s'", dest);
+						return -1;
+					}
+				}
+				return 0;
+			}
+			add_to_ino_dev_hashtable(&source_stat, dest);
+		}
+
+		src_fd = open_or_warn(source, O_RDONLY);
+		if (src_fd < 0)
+			return -1;
+
+		/* Do not try to open with weird mode fields */
+		new_mode = source_stat.st_mode;
+		if (!S_ISREG(source_stat.st_mode))
+			new_mode = 0666;
+
+		// POSIX way is a security problem versus (sym)link attacks
+		if (!ENABLE_FEATURE_NON_POSIX_CP) {
+			dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode);
+		} else { /* safe way: */
+			dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+		}
+		if (dst_fd == -1) {
+			ovr = ask_and_unlink(dest, flags);
+			if (ovr <= 0) {
+				close(src_fd);
+				return ovr;
+			}
+			/* It shouldn't exist. If it exists, do not open (symlink attack?) */
+			dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode);
+			if (dst_fd < 0) {
+				close(src_fd);
+				return -1;
+			}
+		}
+
+#if ENABLE_SELINUX
+		if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT))
+		 && is_selinux_enabled() > 0
+		) {
+			security_context_t con;
+			if (getfscreatecon(&con) == -1) {
+				bb_perror_msg("getfscreatecon");
+				return -1;
+			}
+			if (con) {
+				if (setfilecon(dest, con) == -1) {
+					bb_perror_msg("setfilecon:%s,%s", dest, con);
+					freecon(con);
+					return -1;
+				}
+				freecon(con);
+			}
+		}
+#endif
+		if (bb_copyfd_eof(src_fd, dst_fd) == -1)
+			retval = -1;
+		/* Careful with writing... */
+		if (close(dst_fd) < 0) {
+			bb_perror_msg("error writing to '%s'", dest);
+			retval = -1;
+		}
+		/* ...but read size is already checked by bb_copyfd_eof */
+		close(src_fd);
+		/* "cp /dev/something new_file" should not
+		 * copy mode of /dev/something */
+		if (!S_ISREG(source_stat.st_mode))
+			return retval;
+		goto preserve_mode_ugid_time;
+	}
+ dont_cat:
+
+	/* Source is a symlink or a special file */
+	/* We are lazy here, a bit lax with races... */
+	if (dest_exists) {
+		errno = EEXIST;
+		ovr = ask_and_unlink(dest, flags);
+		if (ovr <= 0)
+			return ovr;
+	}
+	if (S_ISLNK(source_stat.st_mode)) {
+		char *lpath = xmalloc_readlink_or_warn(source);
+		if (lpath) {
+			int r = symlink(lpath, dest);
+			free(lpath);
+			if (r < 0) {
+				bb_perror_msg("can't create symlink '%s'", dest);
+				return -1;
+			}
+			if (flags & FILEUTILS_PRESERVE_STATUS)
+				if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
+					bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+		}
+		/* _Not_ jumping to preserve_mode_ugid_time:
+		 * symlinks don't have those */
+		return 0;
+	}
+	if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode)
+	 || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode)
+	) {
+		if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) {
+			bb_perror_msg("can't create '%s'", dest);
+			return -1;
+		}
+	} else {
+		bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode);
+		return -1;
+	}
+
+ preserve_mode_ugid_time:
+
+	if (flags & FILEUTILS_PRESERVE_STATUS
+	/* Cannot happen: */
+	/* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */
+	) {
+		struct timeval times[2];
+
+		times[1].tv_sec = times[0].tv_sec = source_stat.st_mtime;
+		times[1].tv_usec = times[0].tv_usec = 0;
+		/* BTW, utimes sets usec-precision time - just FYI */
+		if (utimes(dest, times) < 0)
+			bb_perror_msg("can't preserve %s of '%s'", "times", dest);
+		if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) {
+			source_stat.st_mode &= ~(S_ISUID | S_ISGID);
+			bb_perror_msg("can't preserve %s of '%s'", "ownership", dest);
+		}
+		if (chmod(dest, source_stat.st_mode) < 0)
+			bb_perror_msg("can't preserve %s of '%s'", "permissions", dest);
+	}
+
+	return retval;
+}
diff --git a/busybox-1.19.3/libbb/copyfd.c b/busybox-1.19.3/libbb/copyfd.c
new file mode 100644
index 0000000..eda2747
--- /dev/null
+++ b/busybox-1.19.3/libbb/copyfd.c
@@ -0,0 +1,135 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Used by NOFORK applets (e.g. cat) - must not use xmalloc.
+ * size < 0 means "ignore write errors", used by tar --to-command
+ * size = 0 means "copy till EOF"
+ */
+static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
+{
+	int status = -1;
+	off_t total = 0;
+	bool continue_on_write_error = 0;
+#if CONFIG_FEATURE_COPYBUF_KB <= 4
+	char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024];
+	enum { buffer_size = sizeof(buffer) };
+#else
+	char *buffer;
+	int buffer_size;
+#endif
+
+	if (size < 0) {
+		size = -size;
+		continue_on_write_error = 1;
+	}
+
+#if CONFIG_FEATURE_COPYBUF_KB > 4
+	if (size > 0 && size <= 4 * 1024)
+		goto use_small_buf;
+	/* We want page-aligned buffer, just in case kernel is clever
+	 * and can do page-aligned io more efficiently */
+	buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024,
+			PROT_READ | PROT_WRITE,
+			MAP_PRIVATE | MAP_ANON,
+			/* ignored: */ -1, 0);
+	buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024;
+	if (buffer == MAP_FAILED) {
+ use_small_buf:
+		buffer = alloca(4 * 1024);
+		buffer_size = 4 * 1024;
+	}
+#endif
+
+	if (src_fd < 0)
+		goto out;
+
+	if (!size) {
+		size = buffer_size;
+		status = 1; /* copy until eof */
+	}
+
+	while (1) {
+		ssize_t rd;
+
+		rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size);
+
+		if (!rd) { /* eof - all done */
+			status = 0;
+			break;
+		}
+		if (rd < 0) {
+			bb_perror_msg(bb_msg_read_error);
+			break;
+		}
+		/* dst_fd == -1 is a fake, else... */
+		if (dst_fd >= 0) {
+			ssize_t wr = full_write(dst_fd, buffer, rd);
+			if (wr < rd) {
+				if (!continue_on_write_error) {
+					bb_perror_msg(bb_msg_write_error);
+					break;
+				}
+				dst_fd = -1;
+			}
+		}
+		total += rd;
+		if (status < 0) { /* if we aren't copying till EOF... */
+			size -= rd;
+			if (!size) {
+				/* 'size' bytes copied - all done */
+				status = 0;
+				break;
+			}
+		}
+	}
+ out:
+
+#if CONFIG_FEATURE_COPYBUF_KB > 4
+	if (buffer_size != 4 * 1024)
+		munmap(buffer, buffer_size);
+#endif
+	return status ? -1 : total;
+}
+
+
+#if 0
+void FAST_FUNC complain_copyfd_and_die(off_t sz)
+{
+	if (sz != -1)
+		bb_error_msg_and_die("short read");
+	/* if sz == -1, bb_copyfd_XX already complained */
+	xfunc_die();
+}
+#endif
+
+off_t FAST_FUNC bb_copyfd_size(int fd1, int fd2, off_t size)
+{
+	if (size) {
+		return bb_full_fd_action(fd1, fd2, size);
+	}
+	return 0;
+}
+
+void FAST_FUNC bb_copyfd_exact_size(int fd1, int fd2, off_t size)
+{
+	off_t sz = bb_copyfd_size(fd1, fd2, size);
+	if (sz == (size >= 0 ? size : -size))
+		return;
+	if (sz != -1)
+		bb_error_msg_and_die("short read");
+	/* if sz == -1, bb_copyfd_XX already complained */
+	xfunc_die();
+}
+
+off_t FAST_FUNC bb_copyfd_eof(int fd1, int fd2)
+{
+	return bb_full_fd_action(fd1, fd2, 0);
+}
diff --git a/busybox-1.19.3/libbb/correct_password.c b/busybox-1.19.3/libbb/correct_password.c
new file mode 100644
index 0000000..6301589
--- /dev/null
+++ b/busybox-1.19.3/libbb/correct_password.c
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * 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. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``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 JULIE HAUGH OR CONTRIBUTORS 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.
+ */
+
+#include "libbb.h"
+
+/* Ask the user for a password.
+ * 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.
+ *
+ * NULL pw means "just fake it for login with bad username" */
+
+int FAST_FUNC correct_password(const struct passwd *pw)
+{
+	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) {
+		/* "aa" will never match */
+		goto fake_it;
+	}
+	correct = pw->pw_passwd;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+	if ((correct[0] == 'x' || correct[0] == '*') && !correct[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, buffer, sizeof(buffer), &result);
+		correct = (r || !result) ? "aa" : result->sp_pwdp;
+	}
+#endif
+
+	if (!correct[0]) /* empty password field? */
+		return 1;
+
+ fake_it:
+	unencrypted = bb_ask_stdin("Password: ");
+	if (!unencrypted) {
+		return 0;
+	}
+	encrypted = pw_encrypt(unencrypted, correct, 1);
+	r = (strcmp(encrypted, correct) == 0);
+	free(encrypted);
+	memset(unencrypted, 0, strlen(unencrypted));
+	return r;
+}
diff --git a/busybox-1.19.3/libbb/crc32.c b/busybox-1.19.3/libbb/crc32.c
new file mode 100644
index 0000000..ac9836c
--- /dev/null
+++ b/busybox-1.19.3/libbb/crc32.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * CRC32 table fill function
+ * Copyright (C) 2006 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
+ * (I can't really claim much credit however, as the algorithm is
+ * very well-known)
+ *
+ * The following function creates a CRC32 table depending on whether
+ * a big-endian (0x04c11db7) or little-endian (0xedb88320) CRC32 is
+ * required. Admittedly, there are other CRC32 polynomials floating
+ * around, but Busybox doesn't use them.
+ *
+ * endian = 1: big-endian
+ * endian = 0: little-endian
+ *
+ * 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;
+	uint32_t c;
+	int i, j;
+
+	if (!crc_table)
+		crc_table = xmalloc(256 * sizeof(uint32_t));
+
+	for (i = 0; i < 256; i++) {
+		c = endian ? (i << 24) : i;
+		for (j = 8; j; j--) {
+			if (endian)
+				c = (c&0x80000000) ? ((c << 1) ^ polynomial) : (c << 1);
+			else
+				c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1);
+		}
+		*crc_table++ = c;
+	}
+
+	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/busybox-1.19.3/libbb/create_icmp6_socket.c b/busybox-1.19.3/libbb/create_icmp6_socket.c
new file mode 100644
index 0000000..368c690
--- /dev/null
+++ b/busybox-1.19.3/libbb/create_icmp6_socket.c
@@ -0,0 +1,38 @@
+/* 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 source tree.
+ */
+
+#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/busybox-1.19.3/libbb/create_icmp_socket.c b/busybox-1.19.3/libbb/create_icmp_socket.c
new file mode 100644
index 0000000..5856269
--- /dev/null
+++ b/busybox-1.19.3/libbb/create_icmp_socket.c
@@ -0,0 +1,36 @@
+/* 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 source tree.
+ */
+
+#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;
+}
diff --git a/busybox-1.19.3/libbb/default_error_retval.c b/busybox-1.19.3/libbb/default_error_retval.c
new file mode 100644
index 0000000..4f6395f
--- /dev/null
+++ b/busybox-1.19.3/libbb/default_error_retval.c
@@ -0,0 +1,18 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Seems silly to copyright a global variable.  ;-)  Oh well.
+ *
+ * At least one applet (cmp) returns a value different from the typical
+ * EXIT_FAILURE values (1) when an error occurs.  So, make it configurable
+ * by the applet.  I suppose we could use a wrapper function to set it, but
+ * that too seems silly.
+ */
+
+#include "libbb.h"
+
+uint8_t xfunc_error_retval = EXIT_FAILURE;
diff --git a/busybox-1.19.3/libbb/device_open.c b/busybox-1.19.3/libbb/device_open.c
new file mode 100644
index 0000000..a8fe2fc
--- /dev/null
+++ b/busybox-1.19.3/libbb/device_open.c
@@ -0,0 +1,32 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+/* try to open up the specified device */
+int FAST_FUNC device_open(const char *device, int mode)
+{
+	int m, f, fd;
+
+	m = mode | O_NONBLOCK;
+
+	/* Retry up to 5 times */
+	/* TODO: explain why it can't be considered insane */
+	for (f = 0; f < 5; f++) {
+		fd = open(device, m, 0600);
+		if (fd >= 0)
+			break;
+	}
+	if (fd < 0)
+		return fd;
+	/* Reset original flags. */
+	if (m != mode)
+		fcntl(fd, F_SETFL, mode);
+	return fd;
+}
diff --git a/busybox-1.19.3/libbb/die_if_bad_username.c b/busybox-1.19.3/libbb/die_if_bad_username.c
new file mode 100644
index 0000000..cf1297b
--- /dev/null
+++ b/busybox-1.19.3/libbb/die_if_bad_username.c
@@ -0,0 +1,62 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Check user and group names for illegal characters
+ *
+ * Copyright (C) 2008 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* To avoid problems, the username should consist only of
+ * letters, digits, underscores, periods, at signs and dashes,
+ * and not start with a dash (as defined by IEEE Std 1003.1-2001).
+ * For compatibility with Samba machine accounts $ is also supported
+ * at the end of the username.
+ */
+
+void FAST_FUNC die_if_bad_username(const char *name)
+{
+	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 {
+		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:
+		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 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");
+}
diff --git a/busybox-1.19.3/libbb/dump.c b/busybox-1.19.3/libbb/dump.c
new file mode 100644
index 0000000..919fe13
--- /dev/null
+++ b/busybox-1.19.3/libbb/dump.c
@@ -0,0 +1,834 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Support code for the hexdump and od applets,
+ * based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+#include "libbb.h"
+#include "dump.h"
+
+static const char index_str[] ALIGN1 = ".#-+ 0123456789";
+
+static const char size_conv_str[] ALIGN1 =
+"\x1\x4\x4\x4\x4\x4\x4\x8\x8\x8\x8\010cdiouxXeEfgG";
+
+static const char lcc[] ALIGN1 = "diouxX";
+
+
+typedef struct priv_dumper_t {
+	dumper_t pub;
+
+	char **argv;
+	FU *endfu;
+	off_t savaddress;        /* saved address/offset in stream */
+	off_t eaddress;          /* end address */
+	off_t address;           /* address/offset in stream */
+	int blocksize;
+	smallint exitval;        /* final exit value */
+
+	/* former statics */
+	smallint next__done;
+	smallint get__ateof; // = 1;
+	unsigned char *get__curp;
+	unsigned char *get__savp;
+} priv_dumper_t;
+
+dumper_t* FAST_FUNC alloc_dumper(void)
+{
+	priv_dumper_t *dumper = xzalloc(sizeof(*dumper));
+	dumper->pub.dump_length = -1;
+	dumper->pub.dump_vflag = FIRST;
+	dumper->get__ateof = 1;
+	return &dumper->pub;
+}
+
+
+static NOINLINE int bb_dump_size(FS *fs)
+{
+	FU *fu;
+	int bcnt, cur_size;
+	char *fmt;
+	const char *p;
+	int prec;
+
+	/* figure out the data block bb_dump_size needed for each format unit */
+	for (cur_size = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+		if (fu->bcnt) {
+			cur_size += fu->bcnt * fu->reps;
+			continue;
+		}
+		for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+			if (*fmt != '%')
+				continue;
+			/*
+			 * skip any special chars -- save precision in
+			 * case it's a %s format.
+			 */
+			while (strchr(index_str + 1, *++fmt));
+			if (*fmt == '.' && isdigit(*++fmt)) {
+				prec = atoi(fmt);
+				while (isdigit(*++fmt))
+					continue;
+			}
+			p = strchr(size_conv_str + 12, *fmt);
+			if (!p) {
+				if (*fmt == 's') {
+					bcnt += prec;
+				} else if (*fmt == '_') {
+					++fmt;
+					if ((*fmt == 'c') || (*fmt == 'p') || (*fmt == 'u')) {
+						bcnt += 1;
+					}
+				}
+			} else {
+				bcnt += size_conv_str[p - (size_conv_str + 12)];
+			}
+		}
+		cur_size += bcnt * fu->reps;
+	}
+	return cur_size;
+}
+
+static NOINLINE void rewrite(priv_dumper_t *dumper, FS *fs)
+{
+	enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+	PR *pr;
+	FU *fu;
+	char *p1, *p2, *p3;
+	char savech, *fmtp;
+	const char *byte_count_str;
+	int nconv, prec = 0;
+
+	for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+		/*
+		 * break each format unit into print units; each
+		 * conversion character gets its own.
+		 */
+		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;
+
+			/* skip preceding text and up to the next % sign */
+			for (p1 = fmtp; *p1 && *p1 != '%'; ++p1)
+				continue;
+
+			/* only text in the string */
+			if (!*p1) {
+				pr->fmt = fmtp;
+				pr->flags = F_TEXT;
+				break;
+			}
+
+			/*
+			 * get precision for %s -- if have a byte count, don't
+			 * need it.
+			 */
+			if (fu->bcnt) {
+				sokay = USEBCNT;
+				/* skip to conversion character */
+				for (++p1; strchr(index_str, *p1); ++p1)
+					continue;
+			} else {
+				/* skip any special chars, field width */
+				while (strchr(index_str + 1, *++p1))
+					continue;
+				if (*p1 == '.' && isdigit(*++p1)) {
+					sokay = USEPREC;
+					prec = atoi(p1);
+					while (isdigit(*++p1))
+						continue;
+				} else
+					sokay = NOTOKAY;
+			}
+
+			p2 = p1 + 1; /* set end pointer */
+
+			/*
+			 * figure out the byte count for each conversion;
+			 * rewrite the format as necessary, set up blank-
+			 * pbb_dump_adding for end of data.
+			 */
+			if (*p1 == 'c') {
+				pr->flags = F_CHAR;
+ DO_BYTE_COUNT_1:
+				byte_count_str = "\001";
+ DO_BYTE_COUNT:
+				if (fu->bcnt) {
+					do {
+						if (fu->bcnt == *byte_count_str) {
+							break;
+						}
+					} while (*++byte_count_str);
+				}
+				/* Unlike the original, output the remainder of the format string. */
+				if (!*byte_count_str) {
+					bb_error_msg_and_die("bad byte count for conversion character %s", p1);
+				}
+				pr->bcnt = *byte_count_str;
+			} else if (*p1 == 'l') {
+				++p2;
+				++p1;
+ DO_INT_CONV:
+				{
+					const char *e;
+					e = strchr(lcc, *p1);
+					if (!e) {
+						goto DO_BAD_CONV_CHAR;
+					}
+					pr->flags = F_INT;
+					if (e > lcc + 1) {
+						pr->flags = F_UINT;
+					}
+					byte_count_str = "\004\002\001";
+					goto DO_BYTE_COUNT;
+				}
+				/* NOTREACHED */
+			} else if (strchr(lcc, *p1)) {
+				goto DO_INT_CONV;
+			} else if (strchr("eEfgG", *p1)) {
+				pr->flags = F_DBL;
+				byte_count_str = "\010\004";
+				goto DO_BYTE_COUNT;
+			} else if (*p1 == 's') {
+				pr->flags = F_STR;
+				if (sokay == USEBCNT) {
+					pr->bcnt = fu->bcnt;
+				} else if (sokay == USEPREC) {
+					pr->bcnt = prec;
+				} else {   /* NOTOKAY */
+					bb_error_msg_and_die("%%s requires a precision or a byte count");
+				}
+			} else if (*p1 == '_') {
+				++p2;
+				switch (p1[1]) {
+				case 'A':
+					dumper->endfu = fu;
+					fu->flags |= F_IGNORE;
+					/* FALLTHROUGH */
+				case 'a':
+					pr->flags = F_ADDRESS;
+					++p2;
+					if ((p1[2] != 'd') && (p1[2] != 'o') && (p1[2] != 'x')) {
+						goto DO_BAD_CONV_CHAR;
+					}
+					*p1 = p1[2];
+					break;
+				case 'c':
+					pr->flags = F_C;
+					/* *p1 = 'c';   set in conv_c */
+					goto DO_BYTE_COUNT_1;
+				case 'p':
+					pr->flags = F_P;
+					*p1 = 'c';
+					goto DO_BYTE_COUNT_1;
+				case 'u':
+					pr->flags = F_U;
+					/* *p1 = 'c';   set in conv_u */
+					goto DO_BYTE_COUNT_1;
+				default:
+					goto DO_BAD_CONV_CHAR;
+				}
+			} else {
+ DO_BAD_CONV_CHAR:
+				bb_error_msg_and_die("bad conversion character %%%s", p1);
+			}
+
+			/*
+			 * copy to PR format string, set conversion character
+			 * pointer, update original.
+			 */
+			savech = *p2;
+			p1[1] = '\0';
+			pr->fmt = xstrdup(fmtp);
+			*p2 = savech;
+			//Too early! xrealloc can move pr->fmt!
+			//pr->cchar = pr->fmt + (p1 - fmtp);
+
+			/* DBU:[dave@cray.com] w/o this, trailing fmt text, space is lost.
+			 * Skip subsequent text and up to the next % sign and tack the
+			 * additional text onto fmt: eg. if fmt is "%x is a HEX number",
+			 * we lose the " is a HEX number" part of fmt.
+			 */
+			for (p3 = p2; *p3 && *p3 != '%'; p3++)
+				continue;
+			if (p3 > p2) {
+				savech = *p3;
+				*p3 = '\0';
+				pr->fmt = xrealloc(pr->fmt, strlen(pr->fmt) + (p3-p2) + 1);
+				strcat(pr->fmt, p2);
+				*p3 = savech;
+				p2 = p3;
+			}
+
+			pr->cchar = pr->fmt + (p1 - fmtp);
+			fmtp = p2;
+
+			/* only one conversion character if byte count */
+			if (!(pr->flags & F_ADDRESS) && fu->bcnt && nconv++) {
+				bb_error_msg_and_die("byte count with multiple conversion characters");
+			}
+		}
+		/*
+		 * if format unit byte count not specified, figure it out
+		 * so can adjust rep count later.
+		 */
+		if (!fu->bcnt)
+			for (pr = fu->nextpr; pr; pr = pr->nextpr)
+				fu->bcnt += pr->bcnt;
+	}
+	/*
+	 * if the format string interprets any data at all, and it's
+	 * not the same as the blocksize, and its last format unit
+	 * interprets any data at all, and has no iteration count,
+	 * repeat it as necessary.
+	 *
+	 * 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
+		) {
+			fu->reps += (dumper->blocksize - fs->bcnt) / fu->bcnt;
+		}
+		if (fu->reps > 1) {
+			for (pr = fu->nextpr;; pr = pr->nextpr)
+				if (!pr->nextpr)
+					break;
+			for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+				p2 = isspace(*p1) ? p1 : NULL;
+			if (p2)
+				pr->nospace = p2;
+		}
+		if (!fu->nextfu)
+			break;
+	}
+}
+
+static void do_skip(priv_dumper_t *dumper, const char *fname, int statok)
+{
+	struct stat sbuf;
+
+	if (statok) {
+		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
+		) {
+			/* If bb_dump_size valid and pub.dump_skip >= size */
+			dumper->pub.dump_skip -= sbuf.st_size;
+			dumper->address += sbuf.st_size;
+			return;
+		}
+	}
+	if (fseek(stdin, dumper->pub.dump_skip, SEEK_SET)) {
+		bb_simple_perror_msg_and_die(fname);
+	}
+	dumper->address += dumper->pub.dump_skip;
+	dumper->savaddress = dumper->address;
+	dumper->pub.dump_skip = 0;
+}
+
+static NOINLINE int next(priv_dumper_t *dumper)
+{
+	int statok;
+
+	for (;;) {
+		if (*dumper->argv) {
+			dumper->next__done = statok = 1;
+			if (!(freopen(*dumper->argv, "r", stdin))) {
+				bb_simple_perror_msg(*dumper->argv);
+				dumper->exitval = 1;
+				++dumper->argv;
+				continue;
+			}
+		} else {
+			if (dumper->next__done)
+				return 0; /* no next file */
+			dumper->next__done = 1;
+			statok = 0;
+		}
+		if (dumper->pub.dump_skip)
+			do_skip(dumper, statok ? *dumper->argv : "stdin", statok);
+		if (*dumper->argv)
+			++dumper->argv;
+		if (!dumper->pub.dump_skip)
+			return 1;
+	}
+	/* NOTREACHED */
+}
+
+static unsigned char *get(priv_dumper_t *dumper)
+{
+	int n;
+	int need, nread;
+	int blocksize = dumper->blocksize;
+
+	if (!dumper->get__curp) {
+		dumper->address = (off_t)0; /*DBU:[dave@cray.com] initialize,initialize..*/
+		dumper->get__curp = xmalloc(blocksize);
+		dumper->get__savp = xzalloc(blocksize); /* need to be initialized */
+	} else {
+		unsigned char *tmp = dumper->get__curp;
+		dumper->get__curp = dumper->get__savp;
+		dumper->get__savp = tmp;
+		dumper->savaddress += blocksize;
+		dumper->address = dumper->savaddress;
+	}
+	need = blocksize;
+	nread = 0;
+	while (1) {
+		/*
+		 * if read the right number of bytes, or at EOF for one file,
+		 * and no other files are available, zero-pad the rest of the
+		 * block and set the end flag.
+		 */
+		if (!dumper->pub.dump_length || (dumper->get__ateof && !next(dumper))) {
+			if (need == blocksize) {
+				return NULL;
+			}
+			if (dumper->pub.dump_vflag != ALL && !memcmp(dumper->get__curp, dumper->get__savp, nread)) {
+				if (dumper->pub.dump_vflag != DUP) {
+					puts("*");
+				}
+				return NULL;
+			}
+			memset(dumper->get__curp + nread, 0, need);
+			dumper->eaddress = dumper->address + nread;
+			return dumper->get__curp;
+		}
+		n = fread(dumper->get__curp + nread, sizeof(unsigned char),
+				dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
+		if (!n) {
+			if (ferror(stdin)) {
+				bb_simple_perror_msg(dumper->argv[-1]);
+			}
+			dumper->get__ateof = 1;
+			continue;
+		}
+		dumper->get__ateof = 0;
+		if (dumper->pub.dump_length != -1) {
+			dumper->pub.dump_length -= n;
+		}
+		need -= n;
+		if (!need) {
+			if (dumper->pub.dump_vflag == ALL || dumper->pub.dump_vflag == FIRST
+			 || memcmp(dumper->get__curp, dumper->get__savp, blocksize)
+			) {
+				if (dumper->pub.dump_vflag == DUP || dumper->pub.dump_vflag == FIRST) {
+					dumper->pub.dump_vflag = WAIT;
+				}
+				return dumper->get__curp;
+			}
+			if (dumper->pub.dump_vflag == WAIT) {
+				puts("*");
+			}
+			dumper->pub.dump_vflag = DUP;
+			dumper->savaddress += blocksize;
+			dumper->address = dumper->savaddress;
+			need = blocksize;
+			nread = 0;
+		} else {
+			nread += n;
+		}
+	}
+}
+
+static void bpad(PR *pr)
+{
+	char *p1, *p2;
+
+	/*
+	 * remove all conversion flags; '-' is the only one valid
+	 * with %s, and it's not useful here.
+	 */
+	pr->flags = F_BPAD;
+	*pr->cchar = 's';
+	for (p1 = pr->fmt; *p1 != '%'; ++p1)
+		continue;
+	for (p2 = ++p1; *p1 && strchr(" -0+#", *p1); ++p1)
+		if (pr->nospace)
+			pr->nospace--;
+	while ((*p2++ = *p1++) != 0)
+		continue;
+}
+
+static const char conv_str[] ALIGN1 =
+	"\0\\0\0"
+	"\007\\a\0"  /* \a */
+	"\b\\b\0"
+	"\f\\b\0"
+	"\n\\n\0"
+	"\r\\r\0"
+	"\t\\t\0"
+	"\v\\v\0"
+	;
+
+
+static void conv_c(PR *pr, unsigned char *p)
+{
+	const char *str = conv_str;
+	char buf[10];
+
+	do {
+		if (*p == *str) {
+			++str;
+			goto strpr;
+		}
+		str += 4;
+	} while (*str);
+
+	if (isprint_asciionly(*p)) {
+		*pr->cchar = 'c';
+		printf(pr->fmt, *p);
+	} else {
+		sprintf(buf, "%03o", (int) *p);
+		str = buf;
+ strpr:
+		*pr->cchar = 's';
+		printf(pr->fmt, str);
+	}
+}
+
+static void conv_u(PR *pr, unsigned char *p)
+{
+	static const char list[] ALIGN1 =
+		"nul\0soh\0stx\0etx\0eot\0enq\0ack\0bel\0"
+		"bs\0_ht\0_lf\0_vt\0_ff\0_cr\0_so\0_si\0_"
+		"dle\0dcl\0dc2\0dc3\0dc4\0nak\0syn\0etb\0"
+		"can\0em\0_sub\0esc\0fs\0_gs\0_rs\0_us";
+
+	/* od used nl, not lf */
+	if (*p <= 0x1f) {
+		*pr->cchar = 's';
+		printf(pr->fmt, list + (4 * (int)*p));
+	} else if (*p == 0x7f) {
+		*pr->cchar = 's';
+		printf(pr->fmt, "del");
+	} else if (*p < 0x7f) { /* isprint() */
+		*pr->cchar = 'c';
+		printf(pr->fmt, *p);
+	} else {
+		*pr->cchar = 'x';
+		printf(pr->fmt, (int) *p);
+	}
+}
+
+static void display(priv_dumper_t* dumper)
+{
+	FS *fs;
+	FU *fu;
+	PR *pr;
+	int cnt;
+	unsigned char *bp, *savebp;
+	off_t saveaddress;
+	unsigned char savech = '\0';
+
+	while ((bp = get(dumper)) != NULL) {
+		fs = dumper->pub.fshead;
+		savebp = bp;
+		saveaddress = dumper->address;
+		for (; fs; fs = fs->nextfs, bp = savebp, dumper->address = saveaddress) {
+			for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+				if (fu->flags & F_IGNORE) {
+					break;
+				}
+				for (cnt = fu->reps; cnt; --cnt) {
+					for (pr = fu->nextpr; pr; dumper->address += pr->bcnt,
+								bp += pr->bcnt, pr = pr->nextpr) {
+						if (dumper->eaddress && dumper->address >= dumper->eaddress
+						 && !(pr->flags & (F_TEXT | F_BPAD))
+						) {
+							bpad(pr);
+						}
+						if (cnt == 1 && pr->nospace) {
+							savech = *pr->nospace;
+							*pr->nospace = '\0';
+						}
+/*                      PRINT; */
+						switch (pr->flags) {
+						case F_ADDRESS:
+							printf(pr->fmt, (unsigned) dumper->address);
+							break;
+						case F_BPAD:
+							printf(pr->fmt, "");
+							break;
+						case F_C:
+							conv_c(pr, bp);
+							break;
+						case F_CHAR:
+							printf(pr->fmt, *bp);
+							break;
+						case F_DBL: {
+							double dval;
+							float fval;
+
+							switch (pr->bcnt) {
+							case 4:
+								memcpy(&fval, bp, sizeof(fval));
+								printf(pr->fmt, fval);
+								break;
+							case 8:
+								memcpy(&dval, bp, sizeof(dval));
+								printf(pr->fmt, dval);
+								break;
+							}
+							break;
+						}
+						case F_INT: {
+							int ival;
+							short sval;
+
+							switch (pr->bcnt) {
+							case 1:
+								printf(pr->fmt, (int) *bp);
+								break;
+							case 2:
+								memcpy(&sval, bp, sizeof(sval));
+								printf(pr->fmt, (int) sval);
+								break;
+							case 4:
+								memcpy(&ival, bp, sizeof(ival));
+								printf(pr->fmt, ival);
+								break;
+							}
+							break;
+						}
+						case F_P:
+							printf(pr->fmt, isprint_asciionly(*bp) ? *bp : '.');
+							break;
+						case F_STR:
+							printf(pr->fmt, (char *) bp);
+							break;
+						case F_TEXT:
+							printf(pr->fmt);
+							break;
+						case F_U:
+							conv_u(pr, bp);
+							break;
+						case F_UINT: {
+							unsigned ival;
+							unsigned short sval;
+
+							switch (pr->bcnt) {
+							case 1:
+								printf(pr->fmt, (unsigned) *bp);
+								break;
+							case 2:
+								memcpy(&sval, bp, sizeof(sval));
+								printf(pr->fmt, (unsigned) sval);
+								break;
+							case 4:
+								memcpy(&ival, bp, sizeof(ival));
+								printf(pr->fmt, ival);
+								break;
+							}
+							break;
+						}
+						}
+						if (cnt == 1 && pr->nospace) {
+							*pr->nospace = savech;
+						}
+					}
+				}
+			}
+		}
+	}
+	if (dumper->endfu) {
+		/*
+		 * if eaddress not set, error or file size was multiple
+		 * of blocksize, and no partial block ever found.
+		 */
+		if (!dumper->eaddress) {
+			if (!dumper->address) {
+				return;
+			}
+			dumper->eaddress = dumper->address;
+		}
+		for (pr = dumper->endfu->nextpr; pr; pr = pr->nextpr) {
+			switch (pr->flags) {
+			case F_ADDRESS:
+				printf(pr->fmt, (unsigned) dumper->eaddress);
+				break;
+			case F_TEXT:
+				printf(pr->fmt);
+				break;
+			}
+		}
+	}
+}
+
+#define dumper ((priv_dumper_t*)pub_dumper)
+int FAST_FUNC bb_dump_dump(dumper_t *pub_dumper, char **argv)
+{
+	FS *tfs;
+	int blocksize;
+
+	/* figure out the data block bb_dump_size */
+	blocksize = 0;
+	tfs = dumper->pub.fshead;
+	while (tfs) {
+		tfs->bcnt = bb_dump_size(tfs);
+		if (blocksize < tfs->bcnt) {
+			blocksize = tfs->bcnt;
+		}
+		tfs = tfs->nextfs;
+	}
+	dumper->blocksize = blocksize;
+
+	/* rewrite the rules, do syntax checking */
+	for (tfs = dumper->pub.fshead; tfs; tfs = tfs->nextfs) {
+		rewrite(dumper, tfs);
+	}
+
+	dumper->argv = argv;
+	display(dumper);
+
+	return dumper->exitval;
+}
+
+void FAST_FUNC bb_dump_add(dumper_t* pub_dumper, const char *fmt)
+{
+	const char *p;
+	char *p1;
+	char *p2;
+	FS *tfs;
+	FU *tfu, **nextfupp;
+	const char *savep;
+
+	/* start new linked list of format units */
+	tfs = xzalloc(sizeof(FS)); /*DBU:[dave@cray.com] start out NULL */
+	if (!dumper->pub.fshead) {
+		dumper->pub.fshead = tfs;
+	} else {
+		FS *fslast = dumper->pub.fshead;
+		while (fslast->nextfs)
+			fslast = fslast->nextfs;
+		fslast->nextfs = tfs;
+	}
+	nextfupp = &tfs->nextfu;
+
+	/* take the format string and break it up into format units */
+	p = fmt;
+	for (;;) {
+		p = skip_whitespace(p);
+		if (!*p) {
+			break;
+		}
+
+		/* allocate a new format unit and link it in */
+		/* NOSTRICT */
+		/* DBU:[dave@cray.com] zalloc so that forward pointers start out NULL */
+		tfu = xzalloc(sizeof(FU));
+		*nextfupp = tfu;
+		nextfupp = &tfu->nextfu;
+		tfu->reps = 1;
+
+		/* if leading digit, repetition count */
+		if (isdigit(*p)) {
+			for (savep = p; isdigit(*p); ++p)
+				continue;
+			if (!isspace(*p) && *p != '/') {
+				bb_error_msg_and_die("bad format {%s}", fmt);
+			}
+			/* may overwrite either white space or slash */
+			tfu->reps = atoi(savep);
+			tfu->flags = F_SETREP;
+			/* skip trailing white space */
+			p = skip_whitespace(++p);
+		}
+
+		/* skip slash and trailing white space */
+		if (*p == '/') {
+			p = skip_whitespace(++p);
+		}
+
+		/* byte count */
+		if (isdigit(*p)) {
+// TODO: use bb_strtou
+			savep = p;
+			while (isdigit(*++p))
+				continue;
+			if (!isspace(*p)) {
+				bb_error_msg_and_die("bad format {%s}", fmt);
+			}
+			tfu->bcnt = atoi(savep);
+			/* skip trailing white space */
+			p = skip_whitespace(++p);
+		}
+
+		/* format */
+		if (*p != '"') {
+			bb_error_msg_and_die("bad format {%s}", fmt);
+		}
+		for (savep = ++p; *p != '"';) {
+			if (*p++ == 0) {
+				bb_error_msg_and_die("bad format {%s}", fmt);
+			}
+		}
+		tfu->fmt = xstrndup(savep, p - savep);
+/*      escape(tfu->fmt); */
+
+		p1 = tfu->fmt;
+
+		/* alphabetic escape sequences have to be done in place */
+		for (p2 = p1;; ++p1, ++p2) {
+			if (!*p1) {
+				*p2 = *p1;
+				break;
+			}
+			if (*p1 == '\\') {
+				const char *cs = conv_str + 4;
+				++p1;
+				*p2 = *p1;
+				do {
+					if (*p1 == cs[2]) {
+						*p2 = cs[0];
+						break;
+					}
+					cs += 4;
+				} while (*cs);
+			}
+		}
+
+		p++;
+	}
+}
+
+/*
+ * Copyright (c) 1989 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
+ * 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. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/libbb/execable.c b/busybox-1.19.3/libbb/execable.c
new file mode 100644
index 0000000..178a00a
--- /dev/null
+++ b/busybox-1.19.3/libbb/execable.c
@@ -0,0 +1,86 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2006 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* check if path points to an executable file;
+ * return 1 if found;
+ * return 0 otherwise;
+ */
+int FAST_FUNC execable_file(const char *name)
+{
+	struct stat s;
+	return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode));
+}
+
+/* search (*PATHp) for an executable file;
+ * return allocated string containing full path if found;
+ *  PATHp points to the component after the one where it was found
+ *  (or NULL),
+ *  you may call find_execable again with this PATHp to continue
+ *  (if it's not NULL).
+ * return NULL otherwise; (PATHp is undefined)
+ * in all cases (*PATHp) contents will be trashed (s/:/NUL/).
+ */
+char* FAST_FUNC find_execable(const char *filename, char **PATHp)
+{
+	char *p, *n;
+
+	p = *PATHp;
+	while (p) {
+		n = strchr(p, ':');
+		if (n)
+			*n++ = '\0';
+		if (*p != '\0') { /* it's not a PATH="foo::bar" situation */
+			p = concat_path_file(p, filename);
+			if (execable_file(p)) {
+				*PATHp = n;
+				return p;
+			}
+			free(p);
+		}
+		p = n;
+	} /* on loop exit p == NULL */
+	return p;
+}
+
+/* search $PATH for an executable file;
+ * return 1 if found;
+ * return 0 otherwise;
+ */
+int FAST_FUNC exists_execable(const char *filename)
+{
+	char *path = xstrdup(getenv("PATH"));
+	char *tmp = path;
+	char *ret = find_execable(filename, &tmp);
+	free(path);
+	if (ret) {
+		free(ret);
+		return 1;
+	}
+	return 0;
+}
+
+#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[])
+{
+	if (find_applet_by_name(file) >= 0)
+		execvp(bb_busybox_exec_path, argv);
+	return execvp(file, argv);
+}
+#endif
+
+int FAST_FUNC BB_EXECVP_or_die(char **argv)
+{
+	BB_EXECVP(argv[0], argv);
+	/* SUSv3-mandated exit codes */
+	xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
+	bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+}
diff --git a/busybox-1.19.3/libbb/fclose_nonstdin.c b/busybox-1.19.3/libbb/fclose_nonstdin.c
new file mode 100644
index 0000000..5ce9d5b
--- /dev/null
+++ b/busybox-1.19.3/libbb/fclose_nonstdin.c
@@ -0,0 +1,25 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fclose_nonstdin implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* A number of standard utilities can accept multiple command line args
+ * of '-' for stdin, according to SUSv3.  So we encapsulate the check
+ * here to save a little space.
+ */
+
+#include "libbb.h"
+
+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 (f != stdin)
+		return (r | fclose(f)); /* fclose does set errno on error */
+	return r;
+}
diff --git a/busybox-1.19.3/libbb/fflush_stdout_and_exit.c b/busybox-1.19.3/libbb/fflush_stdout_and_exit.c
new file mode 100644
index 0000000..9ad5dbf
--- /dev/null
+++ b/busybox-1.19.3/libbb/fflush_stdout_and_exit.c
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fflush_stdout_and_exit implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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
+ * in an error state.
+ */
+
+#include "libbb.h"
+
+void FAST_FUNC fflush_stdout_and_exit(int retval)
+{
+	if (fflush(stdout))
+		bb_perror_msg_and_die(bb_msg_standard_output);
+
+	if (ENABLE_FEATURE_PREFER_APPLETS && die_sleep < 0) {
+		/* We are in NOFORK applet. Do not exit() directly,
+		 * but use xfunc_die() */
+		xfunc_error_retval = retval;
+		xfunc_die();
+	}
+
+	exit(retval);
+}
diff --git a/busybox-1.19.3/libbb/fgets_str.c b/busybox-1.19.3/libbb/fgets_str.c
new file mode 100644
index 0000000..89210a3
--- /dev/null
+++ b/busybox-1.19.3/libbb/fgets_str.c
@@ -0,0 +1,86 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+static char *xmalloc_fgets_internal(FILE *file, const char *terminating_string, int chop_off, size_t *maxsz_p)
+{
+	char *linebuf = NULL;
+	const int term_length = strlen(terminating_string);
+	int end_string_offset;
+	int linebufsz = 0;
+	int idx = 0;
+	int ch;
+	size_t maxsz = *maxsz_p;
+
+	while (1) {
+		ch = fgetc(file);
+		if (ch == EOF) {
+			if (idx == 0)
+				return linebuf; /* NULL */
+			break;
+		}
+
+		if (idx >= linebufsz) {
+			linebufsz += 200;
+			linebuf = xrealloc(linebuf, linebufsz);
+			if (idx >= maxsz) {
+				linebuf[idx] = ch;
+				idx++;
+				break;
+			}
+		}
+
+		linebuf[idx] = ch;
+		idx++;
+
+		/* Check for terminating string */
+		end_string_offset = idx - term_length;
+		if (end_string_offset >= 0
+		 && memcmp(&linebuf[end_string_offset], terminating_string, term_length) == 0
+		) {
+			if (chop_off)
+				idx -= term_length;
+			break;
+		}
+	}
+	/* Grow/shrink *first*, then store NUL */
+	linebuf = xrealloc(linebuf, idx + 1);
+	linebuf[idx] = '\0';
+	*maxsz_p = idx;
+	return linebuf;
+}
+
+/* Read up to TERMINATING_STRING from FILE and return it,
+ * including terminating string.
+ * Non-terminated string can be returned if EOF is reached.
+ * Return NULL if EOF is reached immediately.  */
+char* FAST_FUNC xmalloc_fgets_str(FILE *file, const char *terminating_string)
+{
+	size_t maxsz = INT_MAX - 4095;
+	return xmalloc_fgets_internal(file, terminating_string, 0, &maxsz);
+}
+
+char* FAST_FUNC xmalloc_fgets_str_len(FILE *file, const char *terminating_string, size_t *maxsz_p)
+{
+	size_t maxsz;
+
+	if (!maxsz_p) {
+		maxsz = INT_MAX - 4095;
+		maxsz_p = &maxsz;
+	}
+	return xmalloc_fgets_internal(file, terminating_string, 0, maxsz_p);
+}
+
+char* FAST_FUNC xmalloc_fgetline_str(FILE *file, const char *terminating_string)
+{
+	size_t maxsz = INT_MAX - 4095;
+	return xmalloc_fgets_internal(file, terminating_string, 1, &maxsz);
+}
diff --git a/busybox-1.19.3/libbb/find_mount_point.c b/busybox-1.19.3/libbb/find_mount_point.c
new file mode 100644
index 0000000..56637ad
--- /dev/null
+++ b/busybox-1.19.3/libbb/find_mount_point.c
@@ -0,0 +1,68 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+#include <mntent.h>
+
+/*
+ * Given a block device, find the mount table entry if that block device
+ * is mounted.
+ *
+ * Given any other file (or directory), find the mount table entry for its
+ * filesystem.
+ */
+struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
+{
+	struct stat s;
+	FILE *mtab_fp;
+	struct mntent *mountEntry;
+	dev_t devno_of_name;
+	bool block_dev;
+
+	if (stat(name, &s) != 0)
+		return NULL;
+
+	devno_of_name = s.st_dev;
+	block_dev = 0;
+	if (S_ISBLK(s.st_mode)) {
+		devno_of_name = s.st_rdev;
+		block_dev = 1;
+	}
+
+	mtab_fp = setmntent(bb_path_mtab_file, "r");
+	if (!mtab_fp)
+		return NULL;
+
+	while ((mountEntry = getmntent(mtab_fp)) != NULL) {
+		/* 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 (ENABLE_FEATURE_SKIP_ROOTFS && strcmp(mountEntry->mnt_fsname, "rootfs") == 0)
+			continue;
+
+		if (strcmp(name, mountEntry->mnt_dir) == 0
+		 || strcmp(name, mountEntry->mnt_fsname) == 0
+		) { /* String match. */
+			break;
+		}
+
+		if (!(subdir_too || block_dev))
+			continue;
+
+		/* Is device's dev_t == name's dev_t? */
+		if (stat(mountEntry->mnt_fsname, &s) == 0 && s.st_rdev == devno_of_name)
+			break;
+		/* Match the directory's mount point. */
+		if (stat(mountEntry->mnt_dir, &s) == 0 && s.st_dev == devno_of_name)
+			break;
+	}
+	endmntent(mtab_fp);
+
+	return mountEntry;
+}
diff --git a/busybox-1.19.3/libbb/find_pid_by_name.c b/busybox-1.19.3/libbb/find_pid_by_name.c
new file mode 100644
index 0000000..db823d0
--- /dev/null
+++ b/busybox-1.19.3/libbb/find_pid_by_name.c
@@ -0,0 +1,120 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+/*
+In Linux we have three ways to determine "process name":
+1. /proc/PID/stat has "...(name)...", among other things. It's so-called "comm" field.
+2. /proc/PID/cmdline's first NUL-terminated string. It's argv[0] from exec syscall.
+3. /proc/PID/exe symlink. Points to the running executable file.
+
+kernel threads:
+ comm: thread name
+ cmdline: empty
+ exe: <readlink fails>
+
+executable
+ comm: first 15 chars of base name
+ (if executable is a symlink, then first 15 chars of symlink name are used)
+ cmdline: argv[0] from exec syscall
+ exe: points to executable (resolves symlink, unlike comm)
+
+script (an executable with #!/path/to/interpreter):
+ comm: first 15 chars of script's base name (symlinks are not resolved)
+ cmdline: /path/to/interpreter (symlinks are not resolved)
+ (script name is in argv[1], args are pushed into argv[2] etc)
+ exe: points to interpreter's executable (symlinks are resolved)
+
+If FEATURE_PREFER_APPLETS=y (and more so if FEATURE_SH_STANDALONE=y),
+some commands started from busybox shell, xargs or find are started by
+execXXX("/proc/self/exe", applet_name, params....)
+and therefore comm field contains "exe".
+*/
+
+static int comm_match(procps_status_t *p, const char *procName)
+{
+	int argv1idx;
+	const char *argv1;
+
+	if (strncmp(p->comm, procName, 15) != 0)
+		return 0; /* comm does not match */
+
+	/* In Linux, if comm is 15 chars, it is truncated.
+	 * (or maybe the name was exactly 15 chars, but there is
+	 * no way to know that) */
+	if (p->comm[14] == '\0')
+		return 1; /* comm is not truncated - matches */
+
+	/* comm is truncated, but first 15 chars match.
+	 * This can be crazily_long_script_name.sh!
+	 * The telltale sign is basename(argv[1]) == procName */
+
+	if (!p->argv0)
+		return 0;
+
+	argv1idx = strlen(p->argv0) + 1;
+	if (argv1idx >= p->argv_len)
+		return 0;
+	argv1 = p->argv0 + argv1idx;
+
+	if (strcmp(bb_basename(argv1), procName) != 0)
+		return 0;
+
+	return 1;
+}
+
+/* This finds the pid of the specified process.
+ * Currently, it's implemented by rummaging through
+ * the proc filesystem.
+ *
+ * Returns a list of all matching PIDs
+ * It is the caller's duty to free the returned pidlist.
+ *
+ * Modified by Vladimir Oleynik for use with libbb/procps.c
+ */
+pid_t* FAST_FUNC find_pid_by_name(const char *procName)
+{
+	pid_t* pidList;
+	int i = 0;
+	procps_status_t* p = NULL;
+
+	pidList = xzalloc(sizeof(*pidList));
+	while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN|PSSCAN_EXE))) {
+		if (comm_match(p, procName)
+		/* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/
+		 || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0)
+		/* or we require /proc/PID/exe link to match */
+		 || (p->exe && strcmp(bb_basename(p->exe), procName) == 0)
+		) {
+			pidList = xrealloc_vector(pidList, 2, i);
+			pidList[i++] = p->pid;
+		}
+	}
+
+	pidList[i] = 0;
+	return pidList;
+}
+
+pid_t* FAST_FUNC pidlist_reverse(pid_t *pidList)
+{
+	int i = 0;
+	while (pidList[i])
+		i++;
+	if (--i >= 0) {
+		pid_t k;
+		int j;
+		for (j = 0; i > j; i--, j++) {
+			k = pidList[i];
+			pidList[i] = pidList[j];
+			pidList[j] = k;
+		}
+	}
+	return pidList;
+}
diff --git a/busybox-1.19.3/libbb/find_root_device.c b/busybox-1.19.3/libbb/find_root_device.c
new file mode 100644
index 0000000..f63e195
--- /dev/null
+++ b/busybox-1.19.3/libbb/find_root_device.c
@@ -0,0 +1,128 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+/* #define DEBUGGING 1 */
+
+#ifdef DEBUGGING
+#define debug(...) do { printf(__VA_ARGS__); } while (0)
+#else
+#define debug(...) ((void)0)
+#endif
+
+
+/* Find block device /dev/XXX which contains specified file
+ * We handle /dev/dir/dir/dir too, at a cost of ~80 more bytes code */
+
+/* Do not reallocate all this stuff on each recursion */
+enum { DEVNAME_MAX = 256 };
+struct arena {
+	struct stat st;
+	dev_t dev;
+	/* Was PATH_MAX, but we recurse _/dev_. We can assume
+	 * people are not crazy enough to have mega-deep tree there */
+	char devpath[DEVNAME_MAX];
+};
+
+static char *find_block_device_in_dir(struct arena *ap)
+{
+	DIR *dir;
+	struct dirent *entry;
+	char *retpath = NULL;
+	int len, rem;
+
+	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) {
+		safe_strncpy(ap->devpath + len, entry->d_name, rem);
+		/* lstat: do not follow links */
+		if (lstat(ap->devpath, &ap->st) != 0)
+			continue;
+		if (S_ISBLK(ap->st.st_mode) && ap->st.st_rdev == ap->dev) {
+			retpath = xstrdup(ap->devpath);
+			break;
+		}
+		if (S_ISDIR(ap->st.st_mode)) {
+			/* Do not recurse for '.' and '..' */
+			if (DOT_OR_DOTDOT(entry->d_name))
+				continue;
+			retpath = find_block_device_in_dir(ap);
+			if (retpath)
+				break;
+		}
+	}
+	closedir(dir);
+
+	return retpath;
+}
+
+#define PROC_MOUNTINFO  "/proc/self/mountinfo"
+/* A major of zero indicates a non-device mount. Use the kernel's show_mountinfo(),
+ * exposed through /proc/self/mountinfo to lookup the device reference. */
+static char *find_device_in_mountinfo(struct arena *ap)
+{
+	char line[1024];
+	char *linePtr;
+	char *retpath = NULL;
+
+	FILE *fp = fopen_for_read(PROC_MOUNTINFO);
+	if (!fp)
+		return NULL;
+
+	debug("Looking for device %u:%u\n", major(ap->dev), minor(ap->dev));
+	while (fgets(line, sizeof(line), fp)) {
+			int mnt_id, parent_mnt_id;
+			unsigned int major, minor;
+			char mnt_typename[1024], mnt_devname[1024];
+
+			linePtr = line;
+			if (sscanf(linePtr, "%i %i %u:%u", &mnt_id, &parent_mnt_id, &major, &minor) != 4) {
+				debug("Couldn't parse line: '%s'\n", line);
+			} else if ((linePtr = strstr(linePtr, " - ")) == NULL) {
+				debug("Couldn't find ' - ': '%s'\n", line);
+			} else if (sscanf(linePtr, " - %s %s ", mnt_typename, mnt_devname) != 2) {
+				debug("Couldn't parse line: '%s'\n", line);
+			} else if ((major(ap->dev) != major) || (minor(ap->dev) != minor)) {
+				debug("Non-matching device %u:%u --> %s\n", major, minor, mnt_devname);
+			} else {
+				debug("Found a match %u:%u --> %s\n", major, minor, mnt_devname);
+				retpath = xstrdup(mnt_devname);
+				break;
+			}
+	}
+
+	fclose(fp);
+
+	return retpath;
+}
+
+char* FAST_FUNC find_block_device(const char *path)
+{
+	struct arena a;
+
+	if (stat(path, &a.st) != 0)
+		return NULL;
+	a.dev = S_ISBLK(a.st.st_mode) ? a.st.st_rdev : a.st.st_dev;
+	if (major(a.dev) != 0) {
+		strcpy(a.devpath, "/dev");
+		return find_block_device_in_dir(&a);
+	} else {
+		return find_device_in_mountinfo(&a);
+	}
+}
diff --git a/busybox-1.19.3/libbb/full_write.c b/busybox-1.19.3/libbb/full_write.c
new file mode 100644
index 0000000..777fbd9
--- /dev/null
+++ b/busybox-1.19.3/libbb/full_write.c
@@ -0,0 +1,42 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+/*
+ * Write all of the supplied buffer out to a file.
+ * This does multiple writes as necessary.
+ * Returns the amount written, or -1 on an error.
+ */
+ssize_t FAST_FUNC full_write(int fd, const void *buf, size_t len)
+{
+	ssize_t cc;
+	ssize_t total;
+
+	total = 0;
+
+	while (len) {
+		cc = safe_write(fd, buf, len);
+
+		if (cc < 0) {
+			if (total) {
+				/* we already wrote some! */
+				/* user can do another write to know the error code */
+				return total;
+			}
+			return cc;  /* write() returns -1 on failure. */
+		}
+
+		total += cc;
+		buf = ((const char *)buf) + cc;
+		len -= cc;
+	}
+
+	return total;
+}
diff --git a/busybox-1.19.3/libbb/get_console.c b/busybox-1.19.3/libbb/get_console.c
new file mode 100644
index 0000000..9b6407b
--- /dev/null
+++ b/busybox-1.19.3/libbb/get_console.c
@@ -0,0 +1,80 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.  If you wrote this, please
+ * acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* From <linux/kd.h> */
+enum { KDGKBTYPE = 0x4B33 };  /* get keyboard type */
+
+static int open_a_console(const char *fnam)
+{
+	int fd;
+
+	/* try read-write */
+	fd = open(fnam, O_RDWR);
+
+	/* if failed, try read-only */
+	if (fd < 0 && errno == EACCES)
+		fd = open(fnam, O_RDONLY);
+
+	/* if failed, try write-only */
+	if (fd < 0 && errno == EACCES)
+		fd = open(fnam, O_WRONLY);
+
+	return fd;
+}
+
+/*
+ * Get an fd for use with kbd/console ioctls.
+ * We try several things because opening /dev/console will fail
+ * if someone else used X (which does a chown on /dev/console).
+ */
+int FAST_FUNC get_console_fd_or_die(void)
+{
+	static const char *const console_names[] = {
+		DEV_CONSOLE, CURRENT_VC, CURRENT_TTY
+	};
+
+	int fd;
+
+	for (fd = 2; fd >= 0; fd--) {
+		int fd4name;
+		int choice_fd;
+		char arg;
+
+		fd4name = open_a_console(console_names[fd]);
+ chk_std:
+		choice_fd = (fd4name >= 0 ? fd4name : fd);
+
+		arg = 0;
+		if (ioctl(choice_fd, KDGKBTYPE, &arg) == 0)
+			return choice_fd;
+		if (fd4name >= 0) {
+			close(fd4name);
+			fd4name = -1;
+			goto chk_std;
+		}
+	}
+
+	bb_error_msg_and_die("can't open console");
+	/*return fd; - total failure */
+}
+
+/* From <linux/vt.h> */
+enum {
+	VT_ACTIVATE = 0x5606,   /* make vt active */
+	VT_WAITACTIVE = 0x5607  /* wait for vt active */
+};
+
+void FAST_FUNC console_make_active(int fd, const int vt_num)
+{
+	xioctl(fd, VT_ACTIVATE, (void *)(ptrdiff_t)vt_num);
+	xioctl(fd, VT_WAITACTIVE, (void *)(ptrdiff_t)vt_num);
+}
diff --git a/busybox-1.19.3/libbb/get_cpu_count.c b/busybox-1.19.3/libbb/get_cpu_count.c
new file mode 100644
index 0000000..ab468af
--- /dev/null
+++ b/busybox-1.19.3/libbb/get_cpu_count.c
@@ -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;
+}
diff --git a/busybox-1.19.3/libbb/get_last_path_component.c b/busybox-1.19.3/libbb/get_last_path_component.c
new file mode 100644
index 0000000..04fdf2a
--- /dev/null
+++ b/busybox-1.19.3/libbb/get_last_path_component.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_get_last_path_component implementation for busybox
+ *
+ * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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"
+ * "abc/def"  -> "def"
+ * "abc/def/" -> ""
+ */
+char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path)
+{
+	char *slash = strrchr(path, '/');
+
+	if (!slash || (slash == path && !slash[1]))
+		return (char*)path;
+
+	return slash + 1;
+}
+
+/*
+ * "/"        -> "/"
+ * "abc"      -> "abc"
+ * "abc/def"  -> "def"
+ * "abc/def/" -> "def" !!
+ */
+char* FAST_FUNC bb_get_last_path_component_strip(char *path)
+{
+	char *slash = last_char_is(path, '/');
+
+	if (slash)
+		while (*slash == '/' && slash != path)
+			*slash-- = '\0';
+
+	return bb_get_last_path_component_nostrip(path);
+}
diff --git a/busybox-1.19.3/libbb/get_line_from_file.c b/busybox-1.19.3/libbb/get_line_from_file.c
new file mode 100644
index 0000000..a98dd35
--- /dev/null
+++ b/busybox-1.19.3/libbb/get_line_from_file.c
@@ -0,0 +1,180 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2005, 2006 Rob Landley <rob@landley.net>
+ * Copyright (C) 2004 Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2001 Matt Krai
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+char* FAST_FUNC bb_get_chunk_from_file(FILE *file, int *end)
+{
+	int ch;
+	unsigned idx = 0;
+	char *linebuf = NULL;
+
+	while ((ch = getc(file)) != EOF) {
+		/* grow the line buffer as necessary */
+		if (!(idx & 0xff))
+			linebuf = xrealloc(linebuf, idx + 0x100);
+		linebuf[idx++] = (char) ch;
+		if (ch == '\0')
+			break;
+		if (end && ch == '\n')
+			break;
+	}
+	if (end)
+		*end = idx;
+	if (linebuf) {
+		// huh, does fgets discard prior data on error like this?
+		// I don't think so....
+		//if (ferror(file)) {
+		//	free(linebuf);
+		//	return NULL;
+		//}
+		linebuf = xrealloc(linebuf, idx + 1);
+		linebuf[idx] = '\0';
+	}
+	return linebuf;
+}
+
+/* Get line, including trailing \n if any */
+char* FAST_FUNC xmalloc_fgets(FILE *file)
+{
+	int i;
+
+	return bb_get_chunk_from_file(file, &i);
+}
+/* Get line.  Remove trailing \n */
+char* FAST_FUNC xmalloc_fgetline(FILE *file)
+{
+	int i;
+	char *c = bb_get_chunk_from_file(file, &i);
+
+	if (i && c[--i] == '\n')
+		c[i] = '\0';
+
+	return c;
+}
+
+#if 0
+/* GNUism getline() should be faster (not tested) than a loop with fgetc */
+
+/* Get line, including trailing \n if any */
+char* FAST_FUNC xmalloc_fgets(FILE *file)
+{
+	char *res_buf = NULL;
+	size_t res_sz;
+
+	if (getline(&res_buf, &res_sz, file) == -1) {
+		free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */
+		res_buf = NULL;
+	}
+//TODO: trimming to res_sz?
+	return res_buf;
+}
+/* Get line.  Remove trailing \n */
+char* FAST_FUNC xmalloc_fgetline(FILE *file)
+{
+	char *res_buf = NULL;
+	size_t res_sz;
+
+	res_sz = getline(&res_buf, &res_sz, file);
+
+	if ((ssize_t)res_sz != -1) {
+		if (res_buf[res_sz - 1] == '\n')
+			res_buf[--res_sz] = '\0';
+//TODO: trimming to res_sz?
+	} else {
+		free(res_buf); /* uclibc allocates a buffer even on EOF. WTF? */
+		res_buf = NULL;
+	}
+	return res_buf;
+}
+
+#endif
+
+#if 0
+/* Faster routines (~twice as fast). +170 bytes. Unused as of 2008-07.
+ *
+ * NB: they stop at NUL byte too.
+ * Performance is important here. Think "grep 50gigabyte_file"...
+ * Ironically, grep can't use it because of NUL issue.
+ * We sorely need C lib to provide fgets which reports size!
+ *
+ * Update:
+ * Actually, uclibc and glibc have it. man getline. It's GNUism,
+ *   but very useful one (if it's as fast as this code).
+ * TODO:
+ * - currently, sed and sort use bb_get_chunk_from_file and heavily
+ *   depend on its "stop on \n or \0" behavior, and STILL they fail
+ *   to handle all cases with embedded NULs correctly. So:
+ * - audit sed and sort; convert them to getline FIRST.
+ * - THEN ditch bb_get_chunk_from_file, replace it with getline.
+ * - provide getline implementation for non-GNU systems.
+ */
+
+static char* xmalloc_fgets_internal(FILE *file, int *sizep)
+{
+	int len;
+	int idx = 0;
+	char *linebuf = NULL;
+
+	while (1) {
+		char *r;
+
+		linebuf = xrealloc(linebuf, idx + 0x100);
+		r = fgets(&linebuf[idx], 0x100, file);
+		if (!r) {
+			/* need to terminate in case this is error
+			 * (EOF puts NUL itself) */
+			linebuf[idx] = '\0';
+			break;
+		}
+		/* stupid. fgets knows the len, it should report it somehow */
+		len = strlen(&linebuf[idx]);
+		idx += len;
+		if (len != 0xff || linebuf[idx - 1] == '\n')
+			break;
+	}
+	*sizep = idx;
+	if (idx) {
+		/* xrealloc(linebuf, idx + 1) is up to caller */
+		return linebuf;
+	}
+	free(linebuf);
+	return NULL;
+}
+
+/* Get line, remove trailing \n */
+char* FAST_FUNC xmalloc_fgetline_fast(FILE *file)
+{
+	int sz;
+	char *r = xmalloc_fgets_internal(file, &sz);
+	if (r && r[sz - 1] == '\n')
+		r[--sz] = '\0';
+	return r; /* not xrealloc(r, sz + 1)! */
+}
+
+char* FAST_FUNC xmalloc_fgets(FILE *file)
+{
+	int sz;
+	return xmalloc_fgets_internal(file, &sz);
+}
+
+/* Get line, remove trailing \n */
+char* FAST_FUNC xmalloc_fgetline(FILE *file)
+{
+	int sz;
+	char *r = xmalloc_fgets_internal(file, &sz);
+	if (!r)
+		return r;
+	if (r[sz - 1] == '\n')
+		r[--sz] = '\0';
+	return xrealloc(r, sz + 1);
+}
+#endif
diff --git a/busybox-1.19.3/libbb/get_shell_name.c b/busybox-1.19.3/libbb/get_shell_name.c
new file mode 100644
index 0000000..c930afd
--- /dev/null
+++ b/busybox-1.19.3/libbb/get_shell_name.c
@@ -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 *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;
+}
diff --git a/busybox-1.19.3/libbb/get_volsize.c b/busybox-1.19.3/libbb/get_volsize.c
new file mode 100644
index 0000000..241ceda
--- /dev/null
+++ b/busybox-1.19.3/libbb/get_volsize.c
@@ -0,0 +1,48 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2010 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+uoff_t FAST_FUNC get_volume_size_in_bytes(int fd,
+		const char *override,
+		unsigned override_units,
+		int extend)
+{
+	uoff_t result;
+
+	if (override) {
+		result = XATOOFF(override);
+		if (result >= (uoff_t)(MAXINT(off_t)) / override_units)
+			bb_error_msg_and_die("image size is too big");
+		result *= override_units;
+		/* seek past end fails on block devices but works on files */
+		if (lseek(fd, result - 1, SEEK_SET) != (off_t)-1) {
+			if (extend)
+				xwrite(fd, "", 1); /* file grows if needed */
+		}
+		//else {
+		//	bb_error_msg("warning, block device is smaller");
+		//}
+	} else {
+		/* more portable than BLKGETSIZE[64] */
+		result = xlseek(fd, 0, SEEK_END);
+	}
+
+	xlseek(fd, 0, SEEK_SET);
+
+	/* Prevent things like this:
+	 * $ dd if=/dev/zero of=foo count=1 bs=1024
+	 * $ mkswap foo
+	 * Setting up swapspace version 1, size = 18446744073709548544 bytes
+	 *
+	 * Picked 16k arbitrarily: */
+	if (result < 16*1024)
+		bb_error_msg_and_die("image is too small");
+
+	return result;
+}
diff --git a/busybox-1.19.3/libbb/getopt32.c b/busybox-1.19.3/libbb/getopt32.c
new file mode 100644
index 0000000..d0e83d8
--- /dev/null
+++ b/busybox-1.19.3/libbb/getopt32.c
@@ -0,0 +1,615 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * universal getopt32 implementation for busybox
+ *
+ * Copyright (C) 2003-2005  Vladimir Oleynik  <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
+# include <getopt.h>
+#endif
+#include "libbb.h"
+
+/*      Documentation
+
+uint32_t
+getopt32(char **argv, const char *applet_opts, ...)
+
+        The command line options must be declared in const char
+        *applet_opts as a string of chars, for example:
+
+        flags = getopt32(argv, "rnug");
+
+        If one of the given options is found, a flag value is added to
+        the return value (an unsigned long).
+
+        The flag value is determined by the position of the char in
+        applet_opts string.  For example, in the above case:
+
+        flags = getopt32(argv, "rnug");
+
+        "r" will add 1    (bit 0)
+        "n" will add 2    (bit 1)
+        "u" will add 4    (bit 2)
+        "g" will add 8    (bit 3)
+
+        and so on.  You can also look at the return value as a bit
+        field and each option sets one bit.
+
+        On exit, global variable optind is set so that if you
+        will do argc -= optind; argv += optind; then
+        argc will be equal to number of remaining non-option
+        arguments, first one would be in argv[0], next in argv[1] and so on
+        (options and their parameters will be moved into argv[]
+        positions prior to argv[optind]).
+
+ ":"    If one of the options requires an argument, then add a ":"
+        after the char in applet_opts and provide a pointer to store
+        the argument.  For example:
+
+        char *pointer_to_arg_for_a;
+        char *pointer_to_arg_for_b;
+        char *pointer_to_arg_for_c;
+        char *pointer_to_arg_for_d;
+
+        flags = getopt32(argv, "a:b:c:d:",
+                        &pointer_to_arg_for_a, &pointer_to_arg_for_b,
+                        &pointer_to_arg_for_c, &pointer_to_arg_for_d);
+
+        The type of the pointer (char* or llist_t*) may be controlled
+        by the "::" special separator that is set in the external string
+        opt_complementary (see below for more info).
+
+ "::"   If option can have an *optional* argument, then add a "::"
+        after its char in applet_opts and provide a pointer to store
+        the argument.  Note that optional arguments _must_
+        immediately follow the option: -oparam, not -o param.
+
+ "+"    If the first character in the applet_opts string is a plus,
+        then option processing will stop as soon as a non-option is
+        encountered in the argv array.  Useful for applets like env
+        which should not process arguments to subprograms:
+        env -i ls -d /
+        Here we want env to process just the '-i', not the '-d'.
+
+ "!"    Report bad option, missing required options,
+        inconsistent options with all-ones return value (instead of abort).
+
+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"
+                ;
+        applet_long_options = applet_longopts;
+
+        The last member of struct option (val) typically is set to
+        matching short option from applet_opts. If there is no matching
+        char in applet_opts, then:
+        - return bit have next position after short options
+        - if has_arg is not "No_argument", use ptr for arg also
+        - opt_complementary affects it too
+
+        Note: a good applet will make long options configurable via the
+        config process and not a required feature.  The current standard
+        is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
+
+const char *opt_complementary
+
+ ":"    The colon (":") is used to separate groups of two or more chars
+        and/or groups of chars and special characters (stating some
+        conditions to be checked).
+
+ "abc"  If groups of two or more chars are specified, the first char
+        is the main option and the other chars are secondary options.
+        Their flags will be turned on if the main option is found even
+        if they are not specifed on the command line.  For example:
+
+        opt_complementary = "abc";
+        flags = getopt32(argv, "abcd")
+
+        If getopt() finds "-a" on the command line, then
+        getopt32's return value will be as if "-a -b -c" were
+        found.
+
+ "ww"   Adjacent double options have a counter associated which indicates
+        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"
+
+        int w_counter = 0; // must be initialized!
+        opt_complementary = "ww";
+        getopt32(argv, "w", &w_counter);
+        if (w_counter)
+                width = (w_counter == 1) ? 132 : INT_MAX;
+        else
+                get_terminal_width(...&width...);
+
+        w_counter is a pointer to an integer. It has to be passed to
+        getopt32() after all other option argument sinks.
+
+        For example: accept multiple -v to indicate the level of verbosity
+        and for each -b optarg, add optarg to my_b. Finally, if b is given,
+        turn off c and vice versa:
+
+        llist_t *my_b = NULL;
+        int verbose_level = 0;
+        opt_complementary = "vv:b::b-c:c-b";
+        f = getopt32(argv, "vb:c", &my_b, &verbose_level);
+        if (f & 2)       // -c after -b unsets -b flag
+                while (my_b) dosomething_with(llist_pop(&my_b));
+        if (my_b)        // but llist is stored if -b is specified
+                free_llist(my_b);
+        if (verbose_level) printf("verbose level is %d\n", verbose_level);
+
+Special characters:
+
+ "-"    A group consisting of just a dash forces all arguments
+        to be treated as options, even if they have no leading dashes.
+        Next char in this case can't be a digit (0-9), use ':' or end of line.
+        Example:
+
+        opt_complementary = "-:w-x:x-w"; // "-w-x:x-w" would also work,
+        getopt32(argv, "wx");            // but is less readable
+
+        This makes it possible to use options without a dash (./program w x)
+        as well as with a dash (./program -x).
+
+        NB: getopt32() will leak a small amount of memory if you use
+        this option! Do not use it if there is a possibility of recursive
+        getopt32() calls.
+
+ "--"   A double dash at the beginning of opt_complementary means the
+        argv[1] string should always be treated as options, even if it isn't
+        prefixed with a "-".  This is useful for special syntax in applets
+        such as "ar" and "tar":
+        tar xvf foo.tar
+
+        NB: getopt32() will leak a small amount of memory if you use
+        this option! Do not use it if there is a possibility of recursive
+        getopt32() calls.
+
+ "-N"   A dash as the first char in a opt_complementary group followed
+        by a single digit (0-9) means that at least N non-option
+        arguments must be present on the command line
+
+ "=N"   An equal sign as the first char in a opt_complementary group followed
+        by a single digit (0-9) means that exactly N non-option
+        arguments must be present on the command line
+
+ "?N"   A "?" as the first char in a opt_complementary group followed
+        by a single digit (0-9) means that at most N arguments must be present
+        on the command line.
+
+ "V-"   An option with dash before colon or end-of-line results in
+        bb_show_usage() being called if this option is encountered.
+        This is typically used to implement "print verbose usage message
+        and exit" option.
+
+ "a-b"  A dash between two options causes the second of the two
+        to be unset (and ignored) if it is given on the command line.
+
+        [FIXME: what if they are the same? like "x-x"? Is it ever useful?]
+
+        For example:
+        The du applet has the options "-s" and "-d depth".  If
+        getopt32 finds -s, then -d is unset or if it finds -d
+        then -s is unset.  (Note:  busybox implements the GNU
+        "--max-depth" option as "-d".)  To obtain this behavior, you
+        set opt_complementary = "s-d:d-s".  Only one flag value is
+        added to getopt32's return value depending on the
+        position of the options on the command line.  If one of the
+        two options requires an argument pointer (":" in applet_opts
+        as in "d:") optarg is set accordingly.
+
+        char *smax_print_depth;
+
+        opt_complementary = "s-d:d-s:x-x";
+        opt = getopt32(argv, "sd:x", &smax_print_depth);
+
+        if (opt & 2)
+                max_print_depth = atoi(smax_print_depth);
+        if (opt & 4)
+                printf("Detected odd -x usage\n");
+
+ "a--b" A double dash between two options, or between an option and a group
+        of options, means that they are mutually exclusive.  Unlike
+        the "-" case above, an error will be forced if the options
+        are used together.
+
+        For example:
+        The cut applet must have only one type of list specified, so
+        -b, -c and -f are mutually exclusive and should raise an error
+        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.
+
+ "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_positive() - allowed range is 0..INT_MAX.
+
+        int param;  // "unsigned param;" will also work
+        opt_complementary = "p+";
+        getopt32(argv, "p:", &param);
+
+ "a::"  A double colon after a char in opt_complementary means that the
+        option can occur multiple times. Each occurrence will be saved as
+        a llist_t element instead of char*.
+
+        For example:
+        The grep applet can have one or more "-e pattern" arguments.
+        In this case you should use getopt32() as follows:
+
+        llist_t *patterns = NULL;
+
+        (this pointer must be initializated to NULL if the list is empty
+        as required by llist_add_to_end(llist_t **old_head, char *new_item).)
+
+        opt_complementary = "e::";
+
+        getopt32(argv, "e:", &patterns);
+        $ grep -e user -e root /etc/passwd
+        root:x:0:0:root:/root:/bin/bash
+        user:x:500:500::/home/user:/bin/bash
+
+ "a?b"  A "?" between an option and a group of options means that
+        at least one of them is required to occur if the first option
+        occurs in preceding command line arguments.
+
+        For example from "id" applet:
+
+        // Don't allow -n -r -rn -ug -rug -nug -rnug
+        opt_complementary = "r?ug:n?ug:u--g:g--u";
+        flags = getopt32(argv, "rnug");
+
+        This example allowed only:
+        $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
+
+ "X"    A opt_complementary group with just a single letter means
+        that this option is required. If more than one such group exists,
+        at least one option is required to occur (not all of them).
+        For example from "start-stop-daemon" applet:
+
+        // Don't allow -KS -SK, but -S or -K is required
+        opt_complementary = "K:S:K--S:S--K";
+        flags = getopt32(argv, "KS...);
+
+
+        Don't forget to use ':'. For example, "?322-22-23X-x-a"
+        is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" -
+        max 3 args; count uses of '-2'; min 2 args; if there is
+        a '-2' option then unset '-3', '-X' and '-a'; if there is
+        a '-2' and after it a '-x' then error out.
+        But it's far too obfuscated. Use ':' to separate groups.
+*/
+
+/* Code here assumes that 'unsigned' is at least 32 bits wide */
+
+const char *const bb_argv_dash[] = { "-", NULL };
+
+const char *opt_complementary;
+
+enum {
+	PARAM_STRING,
+	PARAM_LIST,
+	PARAM_INT,
+};
+
+typedef struct {
+	unsigned char opt_char;
+	smallint param_type;
+	unsigned switch_on;
+	unsigned switch_off;
+	unsigned incongruously;
+	unsigned requires;
+	void **optarg;  /* char**, llist_t** or int *. */
+	int *counter;
+} t_complementary;
+
+/* You can set applet_long_options for parse called long options */
+#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
+static const struct option bb_null_long_options[1] = {
+	{ 0, 0, 0, 0 }
+};
+const char *applet_long_options;
+#endif
+
+uint32_t option_mask32;
+
+uint32_t FAST_FUNC
+getopt32(char **argv, const char *applet_opts, ...)
+{
+	int argc;
+	unsigned flags = 0;
+	unsigned requires = 0;
+	t_complementary complementary[33]; /* last stays zero-filled */
+	char first_char;
+	int c;
+	const unsigned char *s;
+	t_complementary *on_off;
+	va_list p;
+#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
+	const struct option *l_o;
+	struct option *long_options = (struct option *) &bb_null_long_options;
+#endif
+	unsigned trigger;
+	char **pargv;
+	int min_arg = 0;
+	int max_arg = -1;
+
+#define SHOW_USAGE_IF_ERROR     1
+#define ALL_ARGV_IS_OPTS        2
+#define FIRST_ARGV_IS_OPT       4
+
+	int spec_flgs = 0;
+
+	/* skip 0: some applets cheat: they do not actually HAVE argv[0] */
+	argc = 1;
+	while (argv[argc])
+		argc++;
+
+	va_start(p, applet_opts);
+
+	c = 0;
+	on_off = complementary;
+	memset(on_off, 0, sizeof(complementary));
+
+	/* skip bbox extension */
+	first_char = applet_opts[0];
+	if (first_char == '!')
+		applet_opts++;
+
+	/* skip GNU extension */
+	s = (const unsigned char *)applet_opts;
+	if (*s == '+' || *s == '-')
+		s++;
+	while (*s) {
+		if (c >= 32)
+			break;
+		on_off->opt_char = *s;
+		on_off->switch_on = (1 << c);
+		if (*++s == ':') {
+			on_off->optarg = va_arg(p, void **);
+			while (*++s == ':')
+				continue;
+		}
+		on_off++;
+		c++;
+	}
+
+#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
+	if (applet_long_options) {
+		const char *optstr;
+		unsigned i, count;
+
+		count = 1;
+		optstr = applet_long_options;
+		while (optstr[0]) {
+			optstr += strlen(optstr) + 3; /* skip NUL, has_arg, val */
+			count++;
+		}
+		/* count == no. of longopts + 1 */
+		long_options = alloca(count * sizeof(*long_options));
+		memset(long_options, 0, count * sizeof(*long_options));
+		i = 0;
+		optstr = applet_long_options;
+		while (--count) {
+			long_options[i].name = optstr;
+			optstr += strlen(optstr) + 1;
+			long_options[i].has_arg = (unsigned char)(*optstr++);
+			/* long_options[i].flag = NULL; */
+			long_options[i].val = (unsigned char)(*optstr++);
+			i++;
+		}
+		for (l_o = long_options; l_o->name; l_o++) {
+			if (l_o->flag)
+				continue;
+			for (on_off = complementary; on_off->opt_char; on_off++)
+				if (on_off->opt_char == l_o->val)
+					goto next_long;
+			if (c >= 32)
+				break;
+			on_off->opt_char = l_o->val;
+			on_off->switch_on = (1 << c);
+			if (l_o->has_arg != no_argument)
+				on_off->optarg = va_arg(p, void **);
+			c++;
+ next_long: ;
+		}
+		/* Make it unnecessary to clear applet_long_options
+		 * by hand after each call to getopt32
+		 */
+		applet_long_options = NULL;
+	}
+#endif /* ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG */
+	for (s = (const unsigned char *)opt_complementary; s && *s; s++) {
+		t_complementary *pair;
+		unsigned *pair_switch;
+
+		if (*s == ':')
+			continue;
+		c = s[1];
+		if (*s == '?') {
+			if (c < '0' || c > '9') {
+				spec_flgs |= SHOW_USAGE_IF_ERROR;
+			} else {
+				max_arg = c - '0';
+				s++;
+			}
+			continue;
+		}
+		if (*s == '-') {
+			if (c < '0' || c > '9') {
+				if (c == '-') {
+					spec_flgs |= FIRST_ARGV_IS_OPT;
+					s++;
+				} else
+					spec_flgs |= ALL_ARGV_IS_OPTS;
+			} else {
+				min_arg = c - '0';
+				s++;
+			}
+			continue;
+		}
+		if (*s == '=') {
+			min_arg = max_arg = c - '0';
+			s++;
+			continue;
+		}
+		for (on_off = complementary; on_off->opt_char; on_off++)
+			if (on_off->opt_char == *s)
+				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') {
+			requires |= on_off->switch_on;
+			continue;
+		}
+		if (c == '-' && (s[2] == ':' || s[2] == '\0')) {
+			flags |= on_off->switch_on;
+			on_off->incongruously |= on_off->switch_on;
+			s++;
+			continue;
+		}
+		if (c == *s) {
+			on_off->counter = va_arg(p, int *);
+			s++;
+		}
+		pair = on_off;
+		pair_switch = &pair->switch_on;
+		for (s++; *s && *s != ':'; s++) {
+			if (*s == '?') {
+				pair_switch = &pair->requires;
+			} else if (*s == '-') {
+				if (pair_switch == &pair->switch_off)
+					pair_switch = &pair->incongruously;
+				else
+					pair_switch = &pair->switch_off;
+			} else {
+				for (on_off = complementary; on_off->opt_char; on_off++)
+					if (on_off->opt_char == *s) {
+						*pair_switch |= on_off->switch_on;
+						break;
+					}
+			}
+		}
+		s--;
+	}
+	opt_complementary = NULL;
+	va_end(p);
+
+	if (spec_flgs & (FIRST_ARGV_IS_OPT | ALL_ARGV_IS_OPTS)) {
+		pargv = argv + 1;
+		while (*pargv) {
+			if (pargv[0][0] != '-' && pargv[0][0] != '\0') {
+				/* Can't use alloca: opts with params will
+				 * return pointers to stack!
+				 * NB: we leak these allocations... */
+				char *pp = xmalloc(strlen(*pargv) + 2);
+				*pp = '-';
+				strcpy(pp + 1, *pargv);
+				*pargv = pp;
+			}
+			if (!(spec_flgs & ALL_ARGV_IS_OPTS))
+				break;
+			pargv++;
+		}
+	}
+
+	/* In case getopt32 was already called:
+	 * reset the libc getopt() function, which keeps internal state.
+	 * run_nofork_applet() does this, but we might end up here
+	 * also via gunzip_main() -> gzip_main(). Play safe.
+	 */
+#ifdef __GLIBC__
+	optind = 0;
+#else /* BSD style */
+	optind = 1;
+	/* optreset = 1; */
+#endif
+	/* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
+
+	/* Note: just "getopt() <= 0" will not work well for
+	 * "fake" short options, like this one:
+	 * wget $'-\203' "Test: test" http://kernel.org/
+	 * (supposed to act as --header, but doesn't) */
+#if ENABLE_LONG_OPTS || ENABLE_FEATURE_GETOPT_LONG
+	while ((c = getopt_long(argc, argv, applet_opts,
+			long_options, NULL)) != -1) {
+#else
+	while ((c = getopt(argc, argv, applet_opts)) != -1) {
+#endif
+		/* getopt prints "option requires an argument -- X"
+		 * and returns '?' if an option has no arg, but one is reqd */
+		c &= 0xff; /* fight libc's sign extension */
+		for (on_off = complementary; on_off->opt_char != c; on_off++) {
+			/* c can be NUL if long opt has non-NULL ->flag,
+			 * but we construct long opts so that flag
+			 * is always NULL (see above) */
+			if (on_off->opt_char == '\0' /* && c != '\0' */) {
+				/* c is probably '?' - "bad option" */
+				goto error;
+			}
+		}
+		if (flags & on_off->incongruously)
+			goto error;
+		trigger = on_off->switch_on & on_off->switch_off;
+		flags &= ~(on_off->switch_off ^ trigger);
+		flags |= on_off->switch_on ^ trigger;
+		flags ^= trigger;
+		if (on_off->counter)
+			(*(on_off->counter))++;
+		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) {
+//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;
+			}
+		}
+	}
+
+	/* check depending requires for given options */
+	for (on_off = complementary; on_off->opt_char; on_off++) {
+		if (on_off->requires
+		 && (flags & on_off->switch_on)
+		 && (flags & on_off->requires) == 0
+		) {
+			goto error;
+		}
+	}
+	if (requires && (flags & requires) == 0)
+		goto error;
+	argc -= optind;
+	if (argc < min_arg || (max_arg >= 0 && argc > max_arg))
+		goto error;
+
+	option_mask32 = flags;
+	return flags;
+
+ error:
+	if (first_char != '!')
+		bb_show_usage();
+	return (int32_t)-1;
+}
diff --git a/busybox-1.19.3/libbb/getpty.c b/busybox-1.19.3/libbb/getpty.c
new file mode 100644
index 0000000..435e4d0
--- /dev/null
+++ b/busybox-1.19.3/libbb/getpty.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini getpty implementation for busybox
+ * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+#define DEBUG 0
+
+int FAST_FUNC xgetpty(char *line)
+{
+	int p;
+
+#if ENABLE_FEATURE_DEVPTS
+	p = open("/dev/ptmx", O_RDWR);
+	if (p > 0) {
+		grantpt(p); /* chmod+chown corresponding slave pty */
+		unlockpt(p); /* (what does this do?) */
+# 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);
+		}
+# 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
+		return p;
+	}
+#else
+	struct stat stb;
+	int i;
+	int j;
+
+	strcpy(line, "/dev/ptyXX");
+
+	for (i = 0; i < 16; i++) {
+		line[8] = "pqrstuvwxyzabcde"[i];
+		line[9] = '0';
+		if (stat(line, &stb) < 0) {
+			continue;
+		}
+		for (j = 0; j < 16; j++) {
+			line[9] = j < 10 ? j + '0' : j - 10 + 'a';
+			if (DEBUG)
+				fprintf(stderr, "Trying to open device: %s\n", line);
+			p = open(line, O_RDWR | O_NOCTTY);
+			if (p >= 0) {
+				line[5] = 't';
+				return p;
+			}
+		}
+	}
+#endif /* FEATURE_DEVPTS */
+	bb_error_msg_and_die("can't find free pty");
+}
diff --git a/busybox-1.19.3/libbb/hash_md5_sha.c b/busybox-1.19.3/libbb/hash_md5_sha.c
new file mode 100644
index 0000000..b87d1dd
--- /dev/null
+++ b/busybox-1.19.3/libbb/hash_md5_sha.c
@@ -0,0 +1,898 @@
+/* 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));
+}
+
+
+/* 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 != 64) 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 */
+			*(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_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
+
+/* 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_SIZE_VS_SPEED > 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_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 */
+	};
+#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_SIZE_VS_SPEED >= 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]);
+# endif
+
+# if MD5_SIZE_VS_SPEED == 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_SIZE_VS_SPEED == 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_SIZE_VS_SPEED == 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_SIZE_VS_SPEED == 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_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
+	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_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
+
+	/* 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
+
+	/* 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);
+# 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]);
+#endif
+	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 != 128) 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);
+			*(uint64_t *) (&ctx->wbuffer[128 - 8]) = t;
+			t = (ctx->total64[1] << 3) | (ctx->total64[0] >> 61);
+			t = SWAP_BE64(t);
+			*(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));
+}
diff --git a/busybox-1.19.3/libbb/hash_md5prime.c b/busybox-1.19.3/libbb/hash_md5prime.c
new file mode 100644
index 0000000..7986f4d
--- /dev/null
+++ b/busybox-1.19.3/libbb/hash_md5prime.c
@@ -0,0 +1,460 @@
+/* This file is not used by busybox right now.
+ * However, the code here seems to be a tiny bit smaller
+ * than one in md5.c. Need to investigate which one
+ * is better overall...
+ * Hint: grep for md5prime to find places where you can switch
+ * md5.c/md5prime.c
+ */
+
+/*
+ * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ *
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ *
+ * $FreeBSD: src/lib/libmd/md5c.c,v 1.9.2.1 1999/08/29 14:57:12 peter Exp $
+ *
+ * This code is the same as the code published by RSA Inc.  It has been
+ * edited for clarity and style only.
+ *
+ * ----------------------------------------------------------------------------
+ * The md5_crypt() function was taken from freeBSD's libcrypt and contains
+ * this license:
+ *    "THE BEER-WARE LICENSE" (Revision 42):
+ *     <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
+ *     can do whatever you want with this stuff. If we meet some day, and you think
+ *     this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
+ *
+ * $FreeBSD: src/lib/libcrypt/crypt.c,v 1.7.2.1 1999/08/29 14:56:33 peter Exp $
+ *
+ * ----------------------------------------------------------------------------
+ * On April 19th, 2001 md5_crypt() was modified to make it reentrant
+ * by Erik Andersen <andersen@uclibc.org>
+ *
+ * June 28, 2001             Manuel Novoa III
+ *
+ * "Un-inlined" code using loops and static const tables in order to
+ * reduce generated code size (on i386 from approx 4k to approx 2.5k).
+ *
+ * June 29, 2001             Manuel Novoa III
+ *
+ * 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:
+ *       0    fully unrolled loops
+ *       1    partially unrolled (4 ops per loop)
+ *       2    no unrolling -- introduces the need to swap 4 variables (slow)
+ *       3    no unrolling and all 4 loops merged into one with switch
+ *               in each loop (glacial)
+ * On i386, sizes are roughly (-Os -fno-builtin):
+ *     0: 3k     1: 2.5k     2: 2.2k     3: 2k
+ *
+ * Since SuSv3 does not require crypt_r, modified again August 7, 2002
+ * by Erik Andersen to remove reentrance stuff...
+ */
+
+#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
+#else
+# define MD5_SIZE_VS_SPEED CONFIG_MD5_SIZE_VS_SPEED
+#endif
+
+#if BB_LITTLE_ENDIAN
+#define memcpy32_cpu2le memcpy
+#define memcpy32_le2cpu memcpy
+#else
+/* Encodes input (uint32_t) into output (unsigned char).
+ * Assumes len is a multiple of 4. */
+static void
+memcpy32_cpu2le(unsigned char *output, uint32_t *input, unsigned len)
+{
+	unsigned i, j;
+	for (i = 0, j = 0; j < len; i++, j += 4) {
+		output[j] = input[i];
+		output[j+1] = (input[i] >> 8);
+		output[j+2] = (input[i] >> 16);
+		output[j+3] = (input[i] >> 24);
+	}
+}
+/* Decodes input (unsigned char) into output (uint32_t).
+ * Assumes len is a multiple of 4. */
+static void
+memcpy32_le2cpu(uint32_t *output, const unsigned char *input, unsigned len)
+{
+	unsigned i, j;
+	for (i = 0, j = 0; j < len; i++, j += 4)
+		output[i] = ((uint32_t)input[j])
+			| (((uint32_t)input[j+1]) << 8)
+			| (((uint32_t)input[j+2]) << 16)
+			| (((uint32_t)input[j+3]) << 24);
+}
+#endif /* i386 */
+
+/* F, G, H and I are basic MD5 functions. */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/* rotl32 rotates x left n bits. */
+#define rotl32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+/*
+ * FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+ * Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+	(a) += F((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = rotl32((a), (s)); \
+	(a) += (b); \
+	}
+#define GG(a, b, c, d, x, s, ac) { \
+	(a) += G((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = rotl32((a), (s)); \
+	(a) += (b); \
+	}
+#define HH(a, b, c, d, x, s, ac) { \
+	(a) += H((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = rotl32((a), (s)); \
+	(a) += (b); \
+	}
+#define II(a, b, c, d, x, s, ac) { \
+	(a) += I((b), (c), (d)) + (x) + (uint32_t)(ac); \
+	(a) = rotl32((a), (s)); \
+	(a) += (b); \
+	}
+
+/* MD5 basic transformation. Transforms state based on block. */
+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
+	uint32_t temp;
+	const unsigned char *ps;
+
+	static const unsigned char S[] = {
+		7, 12, 17, 22,
+		5, 9, 14, 20,
+		4, 11, 16, 23,
+		6, 10, 15, 21
+	};
+#endif /* MD5_SIZE_VS_SPEED > 1 */
+
+#if MD5_SIZE_VS_SPEED > 0
+	const uint32_t *pc;
+	const unsigned char *pp;
+	int i;
+
+	static const uint32_t C[] = {
+		/* 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 unsigned char P[] = {
+		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, /* 1 */
+		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 /* MD5_SIZE_VS_SPEED > 0 */
+
+	memcpy32_le2cpu(x, block, 64);
+
+	a = state[0];
+	b = state[1];
+	c = state[2];
+	d = state[3];
+
+#if MD5_SIZE_VS_SPEED > 2
+	pc = C;
+	pp = P;
+	ps = S - 4;
+	for (i = 0; i < 64; i++) {
+		if ((i & 0x0f) == 0) ps += 4;
+		temp = a;
+		switch (i>>4) {
+			case 0:
+				temp += F(b, c, d);
+				break;
+			case 1:
+				temp += G(b, c, d);
+				break;
+			case 2:
+				temp += H(b, c, d);
+				break;
+			case 3:
+				temp += I(b, c, d);
+				break;
+		}
+		temp += x[*pp++] + *pc++;
+		temp = rotl32(temp, ps[i & 3]);
+		temp += b;
+		a = d; d = c; c = b; b = temp;
+	}
+#elif MD5_SIZE_VS_SPEED > 1
+	pc = C;
+	pp = P;
+	ps = S;
+	/* Round 1 */
+	for (i = 0; i < 16; i++) {
+		FF(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++;
+		temp = d; d = c; c = b; b = a; a = temp;
+	}
+	/* Round 2 */
+	ps += 4;
+	for (; i < 32; i++) {
+		GG(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++;
+		temp = d; d = c; c = b; b = a; a = temp;
+	}
+	/* Round 3 */
+	ps += 4;
+	for (; i < 48; i++) {
+		HH(a, b, c, d, x[*pp], ps[i & 0x3], *pc); pp++; pc++;
+		temp = d; d = c; c = b; b = a; a = temp;
+	}
+	/* Round 4 */
+	ps += 4;
+	for (; i < 64; i++) {
+		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
+	pc = C;
+	pp = P;
+	/* Round 1 */
+	for (i = 0; i < 4; i++) {
+		FF(a, b, c, d, x[*pp],  7, *pc); pp++; pc++;
+		FF(d, a, b, c, x[*pp], 12, *pc); pp++; pc++;
+		FF(c, d, a, b, x[*pp], 17, *pc); pp++; pc++;
+		FF(b, c, d, a, x[*pp], 22, *pc); pp++; pc++;
+	}
+	/* Round 2 */
+	for (i = 0; i < 4; i++) {
+		GG(a, b, c, d, x[*pp],  5, *pc); pp++; pc++;
+		GG(d, a, b, c, x[*pp],  9, *pc); pp++; pc++;
+		GG(c, d, a, b, x[*pp], 14, *pc); pp++; pc++;
+		GG(b, c, d, a, x[*pp], 20, *pc); pp++; pc++;
+	}
+	/* Round 3 */
+	for (i = 0; i < 4; i++) {
+		HH(a, b, c, d, x[*pp],  4, *pc); pp++; pc++;
+		HH(d, a, b, c, x[*pp], 11, *pc); pp++; pc++;
+		HH(c, d, a, b, x[*pp], 16, *pc); pp++; pc++;
+		HH(b, c, d, a, x[*pp], 23, *pc); pp++; pc++;
+	}
+	/* Round 4 */
+	for (i = 0; i < 4; i++) {
+		II(a, b, c, d, x[*pp],  6, *pc); pp++; pc++;
+		II(d, a, b, c, x[*pp], 10, *pc); pp++; pc++;
+		II(c, d, a, b, x[*pp], 15, *pc); pp++; pc++;
+		II(b, c, d, a, x[*pp], 21, *pc); pp++; pc++;
+	}
+#else
+	/* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+	FF(a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+	FF(d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+	FF(c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+	FF(b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+	FF(a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+	FF(d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+	FF(c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+	FF(b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+	FF(a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+	FF(d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+	FF(c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+	FF(b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+	FF(a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+	FF(d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+	FF(c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+	FF(b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+	/* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+	GG(a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+	GG(d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+	GG(c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+	GG(b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+	GG(a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+	GG(d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+	GG(c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+	GG(b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+	GG(a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+	GG(d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+	GG(c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+	GG(b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+	GG(a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+	GG(d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+	GG(c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+	GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+	/* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+	HH(a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+	HH(d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+	HH(c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+	HH(b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+	HH(a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+	HH(d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+	HH(c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+	HH(b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+	HH(a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+	HH(d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+	HH(c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+	HH(b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+	HH(a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+	HH(d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+	HH(c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+	HH(b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+	/* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+	II(a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+	II(d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+	II(c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+	II(b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+	II(a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+	II(d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+	II(c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+	II(b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+	II(a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+	II(d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+	II(c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+	II(b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+	II(a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+	II(d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+	II(c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+	II(b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+#endif
+
+	state[0] += a;
+	state[1] += b;
+	state[2] += c;
+	state[3] += d;
+
+	/* Zeroize sensitive information. */
+	memset(x, 0, sizeof(x));
+}
+
+
+/* MD5 initialization. */
+void FAST_FUNC md5_begin(md5_ctx_t *context)
+{
+	context->count[0] = context->count[1] = 0;
+	/* Load magic initialization constants.  */
+	context->state[0] = 0x67452301;
+	context->state[1] = 0xefcdab89;
+	context->state[2] = 0x98badcfe;
+	context->state[3] = 0x10325476;
+}
+
+/*
+ * MD5 block update operation. Continues an MD5 message-digest
+ * operation, processing another message block, and updating
+ * the context.
+ */
+void FAST_FUNC md5_hash(const void *buffer, size_t inputLen, md5_ctx_t *context)
+{
+	unsigned i, idx, partLen;
+	const unsigned char *input = buffer;
+
+	/* Compute number of bytes mod 64 */
+	idx = (context->count[0] >> 3) & 0x3F;
+
+	/* Update number of bits */
+	context->count[0] += (inputLen << 3);
+	if (context->count[0] < (inputLen << 3))
+		context->count[1]++;
+	context->count[1] += (inputLen >> 29);
+
+	/* Transform as many times as possible. */
+	i = 0;
+	partLen = 64 - idx;
+	if (inputLen >= partLen) {
+		memcpy(&context->buffer[idx], input, partLen);
+		md5_transform(context->state, context->buffer);
+		for (i = partLen; i + 63 < inputLen; i += 64)
+			md5_transform(context->state, &input[i]);
+		idx = 0;
+	}
+
+	/* Buffer remaining input */
+	memcpy(&context->buffer[idx], &input[i], inputLen - i);
+}
+
+/*
+ * MD5 finalization. Ends an MD5 message-digest operation,
+ * writing the message digest.
+ */
+void FAST_FUNC md5_end(void *digest, md5_ctx_t *context)
+{
+	unsigned idx, padLen;
+	unsigned char bits[8];
+	unsigned char padding[64];
+
+	/* Add padding followed by original length. */
+	memset(padding, 0, sizeof(padding));
+	padding[0] = 0x80;
+	/* save number of bits */
+	memcpy32_cpu2le(bits, context->count, 8);
+	/* pad out to 56 mod 64 */
+	idx = (context->count[0] >> 3) & 0x3f;
+	padLen = (idx < 56) ? (56 - idx) : (120 - idx);
+	md5_hash(padding, padLen, context);
+	/* append length (before padding) */
+	md5_hash(bits, 8, context);
+
+	/* Store state in digest */
+	memcpy32_cpu2le(digest, context->state, 16);
+}
diff --git a/busybox-1.19.3/libbb/herror_msg.c b/busybox-1.19.3/libbb/herror_msg.c
new file mode 100644
index 0000000..d041076
--- /dev/null
+++ b/busybox-1.19.3/libbb/herror_msg.c
@@ -0,0 +1,28 @@
+/* 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 source tree.
+ */
+#include "libbb.h"
+
+void FAST_FUNC bb_herror_msg(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	bb_verror_msg(s, p, hstrerror(h_errno));
+	va_end(p);
+}
+
+void FAST_FUNC bb_herror_msg_and_die(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	bb_verror_msg(s, p, hstrerror(h_errno));
+	va_end(p);
+	xfunc_die();
+}
diff --git a/busybox-1.19.3/libbb/human_readable.c b/busybox-1.19.3/libbb/human_readable.c
new file mode 100644
index 0000000..8b22b0c
--- /dev/null
+++ b/busybox-1.19.3/libbb/human_readable.c
@@ -0,0 +1,197 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * June 30, 2001                 Manuel Novoa III
+ *
+ * All-integer version (hey, not everyone has floating point) of
+ * make_human_readable_str, modified from similar code I had written
+ * for busybox several months ago.
+ *
+ * Notes:
+ *   1) I'm using an unsigned long long to hold the product size * block_size,
+ *      as df (which calls this routine) could request a representation of a
+ *      partition size in bytes > max of unsigned long.  If long longs aren't
+ *      available, it would be possible to do what's needed using polynomial
+ *      representations (say, powers of 1024) and manipulating coefficients.
+ *      The base ten "bytes" output could be handled similarly.
+ *
+ *   2) This routine always outputs a decimal point and a tenths digit when
+ *      display_unit != 0.  Hence, it isn't uncommon for the returned string
+ *      to have a length of 5 or 6.
+ *
+ *      It might be nice to add a flag to indicate no decimal digits in
+ *      that case.  This could be either an additional parameter, or a
+ *      special value of display_unit.  Such a flag would also be nice for du.
+ *
+ *      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 source tree.
+ */
+
+#include "libbb.h"
+
+const char* FAST_FUNC make_human_readable_str(unsigned long long val,
+	unsigned long block_size, unsigned long display_unit)
+{
+	static const char unit_chars[] ALIGN1 = {
+		'\0', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'
+	};
+
+	static char *str;
+
+	unsigned frac; /* 0..9 - the fractional digit */
+	const char *u;
+	const char *fmt;
+
+	if (val == 0)
+		return "0";
+
+	fmt = "%llu";
+	if (block_size > 1)
+		val *= block_size;
+	frac = 0;
+	u = unit_chars;
+
+	if (display_unit) {
+		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)
+		 /* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */
+		) {
+			fmt = "%llu.%u%c";
+			u++;
+			frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024;
+			val /= 1024;
+		}
+		if (frac >= 10) { /* we need to round up here */
+			++val;
+			frac = 0;
+		}
+#if 1
+		/* If block_size is 0, dont print fractional part */
+		if (block_size == 0) {
+			if (frac >= 5) {
+				++val;
+			}
+			fmt = "%llu%*c";
+			frac = 1;
+		}
+#endif
+	}
+
+	if (!str) {
+		/* sufficient for any width of val */
+		str = xmalloc(sizeof(val)*3 + 2 + 3);
+	}
+	sprintf(str, fmt, val, frac, *u);
+	return str;
+}
+
+
+/* vda's implementations of the similar idea */
+
+/* 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[5], const char *scale)
+{
+	const char *fmt;
+	char c;
+	unsigned v, u, idx = 0;
+
+	if (ul > 99999) { // do not scale if 99999 or less
+		ul *= 10;
+		do {
+			ul /= 1024;
+			idx++;
+		} while (ul >= 100000);
+	}
+	v = ul; // ullong divisions are expensive, avoid them
+
+	fmt = " 123456789";
+	u = v / 10;
+	v = v % 10;
+	if (!idx) {
+		// 99999 or less: use "12345" format
+		// u is value/10, v is last digit
+		c = buf[0] = " 123456789"[u/1000];
+		if (c != ' ') fmt = "0123456789";
+		c = buf[1] = fmt[u/100%10];
+		if (c != ' ') fmt = "0123456789";
+		c = buf[2] = fmt[u/10%10];
+		if (c != ' ') fmt = "0123456789";
+		buf[3] = fmt[u%10];
+		buf[4] = "0123456789"[v];
+	} else {
+		// value has been scaled into 0..9999.9 range
+		// u is value, v is 1/10ths (allows for 92.1M format)
+		if (u >= 100) {
+			// value is >= 100: use "1234M', " 123M" formats
+			c = buf[0] = " 123456789"[u/1000];
+			if (c != ' ') fmt = "0123456789";
+			c = buf[1] = fmt[u/100%10];
+			if (c != ' ') fmt = "0123456789";
+			v = u % 10;
+			u = u / 10;
+			buf[2] = fmt[u%10];
+		} else {
+			// value is < 100: use "92.1M" format
+			c = buf[0] = " 123456789"[u/10];
+			if (c != ' ') fmt = "0123456789";
+			buf[1] = fmt[u%10];
+			buf[2] = '.';
+		}
+		buf[3] = "0123456789"[v];
+		buf[4] = scale[idx]; /* typically scale = " kmgt..." */
+	}
+}
+
+/* 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[4], const char *scale)
+{
+	const char *fmt;
+	char c;
+	unsigned v, u, idx = 0;
+
+	if (ul > 9999) { // do not scale if 9999 or less
+		ul *= 10;
+		do {
+			ul /= 1024;
+			idx++;
+		} while (ul >= 10000);
+	}
+	v = ul; // ullong divisions are expensive, avoid them
+
+	fmt = " 123456789";
+	u = v / 10;
+	v = v % 10;
+	if (!idx) {
+		// 9999 or less: use "1234" format
+		// u is value/10, v is last digit
+		c = buf[0] = " 123456789"[u/100];
+		if (c != ' ') fmt = "0123456789";
+		c = buf[1] = fmt[u/10%10];
+		if (c != ' ') fmt = "0123456789";
+		buf[2] = fmt[u%10];
+		buf[3] = "0123456789"[v];
+	} else {
+		// u is value, v is 1/10ths (allows for 9.2M format)
+		if (u >= 10) {
+			// value is >= 10: use "123M', " 12M" formats
+			c = buf[0] = " 123456789"[u/100];
+			if (c != ' ') fmt = "0123456789";
+			v = u % 10;
+			u = u / 10;
+			buf[1] = fmt[u%10];
+		} else {
+			// value is < 10: use "9.2M" format
+			buf[0] = "0123456789"[u];
+			buf[1] = '.';
+		}
+		buf[2] = "0123456789"[v];
+		buf[3] = scale[idx]; /* typically scale = " kmgt..." */
+	}
+}
diff --git a/busybox-1.19.3/libbb/inet_common.c b/busybox-1.19.3/libbb/inet_common.c
new file mode 100644
index 0000000..7208db9
--- /dev/null
+++ b/busybox-1.19.3/libbb/inet_common.c
@@ -0,0 +1,224 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ *                      Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III       Mar 12, 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "inet_common.h"
+
+int FAST_FUNC INET_resolve(const char *name, struct sockaddr_in *s_in, int hostfirst)
+{
+	struct hostent *hp;
+#if ENABLE_FEATURE_ETC_NETWORKS
+	struct netent *np;
+#endif
+
+	/* Grmpf. -FvK */
+	s_in->sin_family = AF_INET;
+	s_in->sin_port = 0;
+
+	/* Default is special, meaning 0.0.0.0. */
+	if (strcmp(name, "default") == 0) {
+		s_in->sin_addr.s_addr = INADDR_ANY;
+		return 1;
+	}
+	/* Look to see if it's a dotted quad. */
+	if (inet_aton(name, &s_in->sin_addr)) {
+		return 0;
+	}
+	/* If we expect this to be a hostname, try hostname database first */
+#ifdef DEBUG
+	if (hostfirst) {
+		bb_error_msg("gethostbyname(%s)", name);
+	}
+#endif
+	if (hostfirst) {
+		hp = gethostbyname(name);
+		if (hp != NULL) {
+			memcpy(&s_in->sin_addr, hp->h_addr_list[0],
+				sizeof(struct in_addr));
+			return 0;
+		}
+	}
+#if ENABLE_FEATURE_ETC_NETWORKS
+	/* Try the NETWORKS database to see if this is a known network. */
+#ifdef DEBUG
+	bb_error_msg("getnetbyname(%s)", name);
+#endif
+	np = getnetbyname(name);
+	if (np != NULL) {
+		s_in->sin_addr.s_addr = htonl(np->n_net);
+		return 1;
+	}
+#endif
+	if (hostfirst) {
+		/* Don't try again */
+		return -1;
+	}
+#ifdef DEBUG
+	res_init();
+	_res.options |= RES_DEBUG;
+	bb_error_msg("gethostbyname(%s)", name);
+#endif
+	hp = gethostbyname(name);
+	if (hp == NULL) {
+		return -1;
+	}
+	memcpy(&s_in->sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+	return 0;
+}
+
+
+/* numeric: & 0x8000: default instead of *,
+ *          & 0x4000: host instead of net,
+ *          & 0x0fff: don't resolve
+ */
+char* FAST_FUNC INET_rresolve(struct sockaddr_in *s_in, int numeric, uint32_t netmask)
+{
+	/* addr-to-name cache */
+	struct addr {
+		struct addr *next;
+		struct sockaddr_in addr;
+		int host;
+		char name[1];
+	};
+	static struct addr *cache = NULL;
+
+	struct addr *pn;
+	char *name;
+	uint32_t ad, host_ad;
+	int host = 0;
+
+	if (s_in->sin_family != AF_INET) {
+#ifdef DEBUG
+		bb_error_msg("rresolve: unsupported address family %d!",
+				  s_in->sin_family);
+#endif
+		errno = EAFNOSUPPORT;
+		return NULL;
+	}
+	ad = s_in->sin_addr.s_addr;
+#ifdef DEBUG
+	bb_error_msg("rresolve: %08x, mask %08x, num %08x", (unsigned)ad, netmask, numeric);
+#endif
+	if (ad == INADDR_ANY) {
+		if ((numeric & 0x0FFF) == 0) {
+			if (numeric & 0x8000)
+				return xstrdup("default");
+			return xstrdup("*");
+		}
+	}
+	if (numeric & 0x0FFF)
+		return xstrdup(inet_ntoa(s_in->sin_addr));
+
+	if ((ad & (~netmask)) != 0 || (numeric & 0x4000))
+		host = 1;
+	pn = cache;
+	while (pn) {
+		if (pn->addr.sin_addr.s_addr == ad && pn->host == host) {
+#ifdef DEBUG
+			bb_error_msg("rresolve: found %s %08x in cache",
+					  (host ? "host" : "net"), (unsigned)ad);
+#endif
+			return xstrdup(pn->name);
+		}
+		pn = pn->next;
+	}
+
+	host_ad = ntohl(ad);
+	name = NULL;
+	if (host) {
+		struct hostent *ent;
+#ifdef DEBUG
+		bb_error_msg("gethostbyaddr (%08x)", (unsigned)ad);
+#endif
+		ent = gethostbyaddr((char *) &ad, 4, AF_INET);
+		if (ent)
+			name = xstrdup(ent->h_name);
+	} else if (ENABLE_FEATURE_ETC_NETWORKS) {
+		struct netent *np;
+#ifdef DEBUG
+		bb_error_msg("getnetbyaddr (%08x)", (unsigned)host_ad);
+#endif
+		np = getnetbyaddr(host_ad, AF_INET);
+		if (np)
+			name = xstrdup(np->n_name);
+	}
+	if (!name)
+		name = xstrdup(inet_ntoa(s_in->sin_addr));
+	pn = xmalloc(sizeof(*pn) + strlen(name)); /* no '+ 1', it's already accounted for */
+	pn->next = cache;
+	pn->addr = *s_in;
+	pn->host = host;
+	strcpy(pn->name, name);
+	cache = pn;
+	return name;
+}
+
+#if ENABLE_FEATURE_IPV6
+
+int FAST_FUNC INET6_resolve(const char *name, struct sockaddr_in6 *sin6)
+{
+	struct addrinfo req, *ai = NULL;
+	int s;
+
+	memset(&req, 0, sizeof(req));
+	req.ai_family = AF_INET6;
+	s = getaddrinfo(name, NULL, &req, &ai);
+	if (s != 0) {
+		bb_error_msg("getaddrinfo: %s: %d", name, s);
+		return -1;
+	}
+	memcpy(sin6, ai->ai_addr, sizeof(*sin6));
+	if (ai)
+		freeaddrinfo(ai);
+	return 0;
+}
+
+#ifndef IN6_IS_ADDR_UNSPECIFIED
+# define IN6_IS_ADDR_UNSPECIFIED(a) \
+	(((uint32_t *) (a))[0] == 0 && ((uint32_t *) (a))[1] == 0 && \
+	 ((uint32_t *) (a))[2] == 0 && ((uint32_t *) (a))[3] == 0)
+#endif
+
+
+char* FAST_FUNC INET6_rresolve(struct sockaddr_in6 *sin6, int numeric)
+{
+	char name[128];
+	int s;
+
+	if (sin6->sin6_family != AF_INET6) {
+#ifdef DEBUG
+		bb_error_msg("rresolve: unsupported address family %d!",
+				  sin6->sin6_family);
+#endif
+		errno = EAFNOSUPPORT;
+		return NULL;
+	}
+	if (numeric & 0x7FFF) {
+		inet_ntop(AF_INET6, &sin6->sin6_addr, name, sizeof(name));
+		return xstrdup(name);
+	}
+	if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+		if (numeric & 0x8000)
+			return xstrdup("default");
+		return xstrdup("*");
+	}
+
+	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 */
diff --git a/busybox-1.19.3/libbb/info_msg.c b/busybox-1.19.3/libbb/info_msg.c
new file mode 100644
index 0000000..56ca2ef
--- /dev/null
+++ b/busybox-1.19.3/libbb/info_msg.c
@@ -0,0 +1,62 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+#if ENABLE_FEATURE_SYSLOG
+# include <syslog.h>
+#endif
+
+void FAST_FUNC bb_info_msg(const char *s, ...)
+{
+#ifdef THIS_ONE_DOESNT_DO_SINGLE_WRITE
+	va_list p;
+	/* va_copy is used because it is not portable
+	 * to use va_list p twice */
+	va_list p2;
+
+	va_start(p, s);
+	va_copy(p2, p);
+	if (logmode & LOGMODE_STDIO) {
+		vprintf(s, p);
+		fputs(msg_eol, stdout);
+	}
+# if ENABLE_FEATURE_SYSLOG
+	if (logmode & LOGMODE_SYSLOG)
+		vsyslog(LOG_INFO, s, p2);
+# endif
+	va_end(p2);
+	va_end(p);
+#else
+	int used;
+	char *msg;
+	va_list p;
+
+	if (logmode == 0)
+		return;
+
+	va_start(p, s);
+	used = vasprintf(&msg, s, p);
+	va_end(p);
+	if (used < 0)
+		return;
+
+# if ENABLE_FEATURE_SYSLOG
+	if (logmode & LOGMODE_SYSLOG)
+		syslog(LOG_INFO, "%s", msg);
+# endif
+	if (logmode & LOGMODE_STDIO) {
+		fflush_all();
+		/* used = strlen(msg); - must be true already */
+		msg[used++] = '\n';
+		full_write(STDOUT_FILENO, msg, used);
+	}
+
+	free(msg);
+#endif
+}
diff --git a/busybox-1.19.3/libbb/inode_hash.c b/busybox-1.19.3/libbb/inode_hash.c
new file mode 100644
index 0000000..715535e
--- /dev/null
+++ b/busybox-1.19.3/libbb/inode_hash.c
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+typedef struct ino_dev_hash_bucket_struct {
+	struct ino_dev_hash_bucket_struct *next;
+	ino_t ino;
+	dev_t dev;
+	char name[1];
+} ino_dev_hashtable_bucket_t;
+
+#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;
+
+/*
+ * Return name if statbuf->st_ino && statbuf->st_dev are recorded in
+ * ino_dev_hashtable, else return NULL
+ */
+char* FAST_FUNC is_in_ino_dev_hashtable(const struct stat *statbuf)
+{
+	ino_dev_hashtable_bucket_t *bucket;
+
+	if (!ino_dev_hashtable)
+		return NULL;
+
+	bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)];
+	while (bucket != NULL) {
+		if ((bucket->ino == statbuf->st_ino)
+		 && (bucket->dev == statbuf->st_dev)
+		) {
+			return bucket->name;
+		}
+		bucket = bucket->next;
+	}
+	return NULL;
+}
+
+/* Add statbuf to statbuf hash table */
+void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name)
+{
+	int i;
+	ino_dev_hashtable_bucket_t *bucket;
+
+	i = hash_inode(statbuf->st_ino);
+	if (!name)
+		name = "";
+	bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + strlen(name));
+	bucket->ino = statbuf->st_ino;
+	bucket->dev = statbuf->st_dev;
+	strcpy(bucket->name, name);
+
+	if (!ino_dev_hashtable)
+		ino_dev_hashtable = xzalloc(HASH_SIZE * sizeof(*ino_dev_hashtable));
+
+	bucket->next = ino_dev_hashtable[i];
+	ino_dev_hashtable[i] = bucket;
+}
+
+#if ENABLE_DU || ENABLE_FEATURE_CLEAN_UP
+/* Clear statbuf hash table */
+void FAST_FUNC reset_ino_dev_hashtable(void)
+{
+	int i;
+	ino_dev_hashtable_bucket_t *bucket;
+
+	for (i = 0; ino_dev_hashtable && i < HASH_SIZE; i++) {
+		while (ino_dev_hashtable[i] != NULL) {
+			bucket = ino_dev_hashtable[i]->next;
+			free(ino_dev_hashtable[i]);
+			ino_dev_hashtable[i] = bucket;
+		}
+	}
+	free(ino_dev_hashtable);
+	ino_dev_hashtable = NULL;
+}
+#endif
diff --git a/busybox-1.19.3/libbb/isdirectory.c b/busybox-1.19.3/libbb/isdirectory.c
new file mode 100644
index 0000000..9861be6
--- /dev/null
+++ b/busybox-1.19.3/libbb/isdirectory.c
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 GPL.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include <sys/stat.h>
+#include "libbb.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 status;
+	struct stat astatBuf;
+
+	if (statBuf == NULL) {
+		/* use auto stack buffer */
+		statBuf = &astatBuf;
+	}
+
+	if (followLinks)
+		status = stat(fileName, statBuf);
+	else
+		status = lstat(fileName, statBuf);
+
+	status = (status == 0 && S_ISDIR(statBuf->st_mode));
+
+	return status;
+}
diff --git a/busybox-1.19.3/libbb/kernel_version.c b/busybox-1.19.3/libbb/kernel_version.c
new file mode 100644
index 0000000..a168a1e
--- /dev/null
+++ b/busybox-1.19.3/libbb/kernel_version.c
@@ -0,0 +1,38 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+/* After libbb.h, since it needs sys/types.h on some systems */
+#include <sys/utsname.h>  /* for uname(2) */
+
+
+/* Returns current kernel version encoded as major*65536 + minor*256 + patch,
+ * so, for example,  to check if the kernel is greater than 2.2.11:
+ *
+ *     if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { <stuff> }
+ */
+int FAST_FUNC get_linux_version_code(void)
+{
+	struct utsname name;
+	char *s;
+	int i, r;
+
+	if (uname(&name) == -1) {
+		bb_perror_msg("can't get system information");
+		return 0;
+	}
+
+	s = name.release;
+	r = 0;
+	for (i = 0; i < 3; i++) {
+		r = r * 256 + atoi(strtok(s, "."));
+		s = NULL;
+	}
+	return r;
+}
diff --git a/busybox-1.19.3/libbb/last_char_is.c b/busybox-1.19.3/libbb/last_char_is.c
new file mode 100644
index 0000000..65e6cdf
--- /dev/null
+++ b/busybox-1.19.3/libbb/last_char_is.c
@@ -0,0 +1,24 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * busybox library eXtended function
+ *
+ * Copyright (C) 2001 Larry Doolittle, <ldoolitt@recycle.lbl.gov>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Find out if the last character of a string matches the one given.
+ * Don't underrun the buffer if the string length is 0.
+ */
+char* FAST_FUNC last_char_is(const char *s, int c)
+{
+	if (s && *s) {
+		size_t sz = strlen(s) - 1;
+		s += sz;
+		if ( (unsigned char)*s == c)
+			return (char*)s;
+	}
+	return NULL;
+}
diff --git a/busybox-1.19.3/libbb/lineedit.c b/busybox-1.19.3/libbb/lineedit.c
new file mode 100644
index 0000000..2ea373c
--- /dev/null
+++ b/busybox-1.19.3/libbb/lineedit.c
@@ -0,0 +1,2678 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Command line editing.
+ *
+ * Copyright (c) 1986-2003 may safely be consumed by a BSD or GPL license.
+ * Written by:   Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Used ideas:
+ *      Adam Rogoyski    <rogoyski@cs.utexas.edu>
+ *      Dave Cinege      <dcinege@psychosis.com>
+ *      Jakub Jelinek (c) 1995
+ *      Erik Andersen    <andersen@codepoet.org> (Majorly adjusted for busybox)
+ *
+ * This code is 'as is' with no warranty.
+ */
+
+/*
+ * Usage and known bugs:
+ * Terminal key codes are not extensive, more needs to be added.
+ * This version was created on Debian GNU/Linux 2.x.
+ * Delete, Backspace, Home, End, and the arrow keys were tested
+ * to work in an Xterm and console. Ctrl-A also works as Home.
+ * Ctrl-E also works as End.
+ *
+ * The following readline-like commands are not implemented:
+ * ESC-b -- Move back one word
+ * ESC-f -- Move forward one word
+ * ESC-d -- Delete forward one word
+ * CTL-t -- Transpose two characters
+ *
+ * lineedit does not know that the terminal escape sequences do not
+ * take up space on the screen. The redisplay code assumes, unless
+ * told otherwise, that each character in the prompt is a printable
+ * character that takes up one character position on the screen.
+ * You need to tell lineedit that some sequences of characters
+ * in the prompt take up no screen space. Compatibly with readline,
+ * use the \[ escape to begin a sequence of non-printing characters,
+ * and the \] escape to signal the end of such a sequence. Example:
+ *
+ * PS1='\[\033[01;32m\]\u@\h\[\033[01;34m\] \w \$\[\033[00m\] '
+ */
+#include "libbb.h"
+#include "unicode.h"
+#ifndef _POSIX_VDISABLE
+# define _POSIX_VDISABLE '\0'
+#endif
+
+
+#ifdef TEST
+# define ENABLE_FEATURE_EDITING 0
+# define ENABLE_FEATURE_TAB_COMPLETION 0
+# define ENABLE_FEATURE_USERNAME_COMPLETION 0
+#endif
+
+
+/* Entire file (except TESTing part) sits inside this #if */
+#if ENABLE_FEATURE_EDITING
+
+
+#define ENABLE_USERNAME_OR_HOMEDIR \
+	(ENABLE_FEATURE_USERNAME_COMPLETION || ENABLE_FEATURE_EDITING_FANCY_PROMPT)
+#define IF_USERNAME_OR_HOMEDIR(...)
+#if ENABLE_USERNAME_OR_HOMEDIR
+# undef IF_USERNAME_OR_HOMEDIR
+# define IF_USERNAME_OR_HOMEDIR(...) __VA_ARGS__
+#endif
+
+
+#undef CHAR_T
+#if ENABLE_UNICODE_SUPPORT
+# define BB_NUL ((wchar_t)0)
+# define CHAR_T wchar_t
+static bool BB_isspace(CHAR_T c) { return ((unsigned)c < 256 && isspace(c)); }
+# if ENABLE_FEATURE_EDITING_VI
+static bool BB_isalnum(CHAR_T c) { return ((unsigned)c < 256 && isalnum(c)); }
+# endif
+static bool BB_ispunct(CHAR_T c) { return ((unsigned)c < 256 && ispunct(c)); }
+# undef isspace
+# undef isalnum
+# undef ispunct
+# undef isprint
+# define isspace isspace_must_not_be_used
+# define isalnum isalnum_must_not_be_used
+# define ispunct ispunct_must_not_be_used
+# define isprint isprint_must_not_be_used
+#else
+# define BB_NUL '\0'
+# define CHAR_T char
+# define BB_isspace(c) isspace(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
+
+
+#define ESC "\033"
+
+#define SEQ_CLEAR_TILL_END_OF_SCREEN  ESC"[J"
+//#define SEQ_CLEAR_TILL_END_OF_LINE  ESC"[K"
+
+
+enum {
+	MAX_LINELEN = CONFIG_FEATURE_EDITING_MAX_LEN < 0x7ff0
+	              ? CONFIG_FEATURE_EDITING_MAX_LEN
+	              : 0x7ff0
+};
+
+#if ENABLE_USERNAME_OR_HOMEDIR
+static const char null_str[] ALIGN1 = "";
+#endif
+
+/* We try to minimize both static and stack usage. */
+struct lineedit_statics {
+	line_input_t *state;
+
+	volatile unsigned cmdedit_termw; /* = 80; */ /* actual terminal width */
+	sighandler_t previous_SIGWINCH_handler;
+
+	unsigned cmdedit_x;        /* real x (col) terminal position */
+	unsigned cmdedit_y;        /* pseudoreal y (row) terminal position */
+	unsigned cmdedit_prmt_len; /* length of prompt (without colors etc) */
+
+	unsigned cursor;
+	int command_len; /* must be signed */
+	/* signed maxsize: we want x in "if (x > S.maxsize)"
+	 * to _not_ be promoted to unsigned */
+	int maxsize;
+	CHAR_T *command_ps;
+
+	const char *cmdedit_prompt;
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+	int num_ok_lines; /* = 1; */
+#endif
+
+#if ENABLE_USERNAME_OR_HOMEDIR
+	char *user_buf;
+	char *home_pwd_buf; /* = (char*)null_str; */
+#endif
+
+#if ENABLE_FEATURE_TAB_COMPLETION
+	char **matches;
+	unsigned num_matches;
+#endif
+
+#if ENABLE_FEATURE_EDITING_VI
+# define DELBUFSIZ 128
+	CHAR_T *delptr;
+	smallint newdelflag;     /* whether delbuf should be reused yet */
+	CHAR_T delbuf[DELBUFSIZ];  /* a place to store deleted characters */
+#endif
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+	smallint sent_ESC_br6n;
+#endif
+};
+
+/* See lineedit_ptr_hack.c */
+extern struct lineedit_statics *const lineedit_ptr_to_statics;
+
+#define S (*lineedit_ptr_to_statics)
+#define state            (S.state           )
+#define cmdedit_termw    (S.cmdedit_termw   )
+#define previous_SIGWINCH_handler (S.previous_SIGWINCH_handler)
+#define cmdedit_x        (S.cmdedit_x       )
+#define cmdedit_y        (S.cmdedit_y       )
+#define cmdedit_prmt_len (S.cmdedit_prmt_len)
+#define cursor           (S.cursor          )
+#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         )
+#define num_matches      (S.num_matches     )
+#define delptr           (S.delptr          )
+#define newdelflag       (S.newdelflag      )
+#define delbuf           (S.delbuf          )
+
+#define INIT_S() do { \
+	(*(struct lineedit_statics**)&lineedit_ptr_to_statics) = xzalloc(sizeof(S)); \
+	barrier(); \
+	cmdedit_termw = 80; \
+	IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines = 1;) \
+	IF_USERNAME_OR_HOMEDIR(home_pwd_buf = (char*)null_str;) \
+} 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)) */
+	free((char*)cmdedit_prompt);
+#endif
+#if ENABLE_USERNAME_OR_HOMEDIR
+	free(user_buf);
+	if (home_pwd_buf != null_str)
+		free(home_pwd_buf);
+#endif
+	free(lineedit_ptr_to_statics);
+}
+#define DEINIT_S() deinit_S()
+
+
+#if ENABLE_UNICODE_SUPPORT
+static size_t load_string(const char *src)
+{
+	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;
+# else
+		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;
+		}
+		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)
+{
+	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
+static wchar_t adjust_width_and_validate_wc(unsigned *width_adj, wchar_t wc)
+# else
+static wchar_t adjust_width_and_validate_wc(wchar_t wc)
+#  define adjust_width_and_validate_wc(width_adj, wc) \
+	((*(width_adj))++, adjust_width_and_validate_wc(wc))
+# endif
+{
+	int w = 1;
+
+	if (unicode_status == UNICODE_ON) {
+		if (wc > CONFIG_LAST_SUPPORTED_WCHAR) {
+			/* note: also true for unicode_is_raw_byte(wc) */
+			goto subst;
+		}
+		w = wcwidth(wc);
+		if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0)
+		 || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
+		 || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
+		) {
+ subst:
+			w = 1;
+			wc = CONFIG_SUBST_WCHAR;
+		}
+	}
+
+# if ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS
+	*width_adj += w;
+#endif
+	return wc;
+}
+#else /* !UNICODE */
+static size_t load_string(const char *src)
+{
+	safe_strncpy(command_ps, src, S.maxsize);
+	return strlen(command_ps);
+}
+# if ENABLE_FEATURE_TAB_COMPLETION
+static void save_string(char *dst, unsigned maxsize)
+{
+	safe_strncpy(dst, command_ps, maxsize);
+}
+# endif
+# define BB_PUTCHAR(c) bb_putchar(c)
+/* Should never be called: */
+int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
+#endif
+
+
+/* Put 'command_ps[cursor]', cursor++.
+ * Advance cursor on screen. If we reached right margin, scroll text up
+ * and remove terminal margin effect by printing 'next_char' */
+#define HACK_FOR_WRONG_WIDTH 1
+static void put_cur_glyph_and_inc_cursor(void)
+{
+	CHAR_T c = command_ps[cursor];
+	unsigned width = 0;
+	int ofs_to_right;
+
+	if (c == BB_NUL) {
+		/* erase character after end of input string */
+		c = ' ';
+	} else {
+		/* advance cursor only if we aren't at the end yet */
+		cursor++;
+		if (unicode_status == UNICODE_ON) {
+			IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
+			c = adjust_width_and_validate_wc(&cmdedit_x, c);
+			IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
+		} else {
+			cmdedit_x++;
+		}
+	}
+
+	ofs_to_right = cmdedit_x - cmdedit_termw;
+	if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
+		/* c fits on this line */
+		BB_PUTCHAR(c);
+	}
+
+	if (ofs_to_right >= 0) {
+		/* we go to the next line */
+#if HACK_FOR_WRONG_WIDTH
+		/* This works better if our idea of term width is wrong
+		 * and it is actually wider (often happens on serial lines).
+		 * Printing CR,LF *forces* cursor to next line.
+		 * OTOH if terminal width is correct AND terminal does NOT
+		 * have automargin (IOW: it is moving cursor to next line
+		 * by itself (which is wrong for VT-10x terminals)),
+		 * this will break things: there will be one extra empty line */
+		puts("\r"); /* + implicit '\n' */
+#else
+		/* VT-10x terminals don't wrap cursor to next line when last char
+		 * on the line is printed - cursor stays "over" this char.
+		 * Need to print _next_ char too (first one to appear on next line)
+		 * to make cursor move down to next line.
+		 */
+		/* Works ok only if cmdedit_termw is correct. */
+		c = command_ps[cursor];
+		if (c == BB_NUL)
+			c = ' ';
+		BB_PUTCHAR(c);
+		bb_putchar('\b');
+#endif
+		cmdedit_y++;
+		if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
+			width = 0;
+		} else { /* ofs_to_right > 0 */
+			/* wide char c didn't fit on prev line */
+			BB_PUTCHAR(c);
+		}
+		cmdedit_x = width;
+	}
+}
+
+/* Move to end of line (by printing all chars till the end) */
+static void put_till_end_and_adv_cursor(void)
+{
+	while (cursor < command_len)
+		put_cur_glyph_and_inc_cursor();
+}
+
+/* Go to the next line */
+static void goto_new_line(void)
+{
+	put_till_end_and_adv_cursor();
+	if (cmdedit_x != 0)
+		bb_putchar('\n');
+}
+
+static void beep(void)
+{
+	bb_putchar('\007');
+}
+
+static void put_prompt(void)
+{
+	unsigned w;
+
+	fputs(cmdedit_prompt, stdout);
+	fflush_all();
+	cursor = 0;
+	w = cmdedit_termw; /* read volatile var once */
+	cmdedit_y = cmdedit_prmt_len / w; /* new quasireal y */
+	cmdedit_x = cmdedit_prmt_len % w;
+}
+
+/* Move back one character */
+/* (optimized for slow terminals) */
+static void input_backward(unsigned num)
+{
+	if (num > cursor)
+		num = cursor;
+	if (num == 0)
+		return;
+	cursor -= num;
+
+	if ((ENABLE_UNICODE_COMBINING_WCHARS || ENABLE_UNICODE_WIDE_WCHARS)
+	 && unicode_status == UNICODE_ON
+	) {
+		/* correct NUM to be equal to _screen_ width */
+		int n = num;
+		num = 0;
+		while (--n >= 0)
+			adjust_width_and_validate_wc(&num, command_ps[cursor + n]);
+		if (num == 0)
+			return;
+	}
+
+	if (cmdedit_x >= num) {
+		cmdedit_x -= num;
+		if (num <= 4) {
+			/* This is longer by 5 bytes on x86.
+			 * Also gets miscompiled for ARM users
+			 * (busybox.net/bugs/view.php?id=2274).
+			 * printf(("\b\b\b\b" + 4) - num);
+			 * return;
+			 */
+			do {
+				bb_putchar('\b');
+			} while (--num);
+			return;
+		}
+		printf(ESC"[%uD", num);
+		return;
+	}
+
+	/* Need to go one or more lines up */
+	if (ENABLE_UNICODE_WIDE_WCHARS) {
+		/* With wide chars, it is hard to "backtrack"
+		 * and reliably figure out where to put cursor.
+		 * Example (<> is a wide char; # is an ordinary char, _ cursor):
+		 * |prompt: <><> |
+		 * |<><><><><><> |
+		 * |_            |
+		 * and user presses left arrow. num = 1, cmdedit_x = 0,
+		 * We need to go up one line, and then - how do we know that
+		 * we need to go *10* positions to the right? Because
+		 * |prompt: <>#<>|
+		 * |<><><>#<><><>|
+		 * |_            |
+		 * in this situation we need to go *11* positions to the right.
+		 *
+		 * A simpler thing to do is to redraw everything from the start
+		 * up to new cursor position (which is already known):
+		 */
+		unsigned sv_cursor;
+		/* go to 1st column; go up to first line */
+		printf("\r" ESC"[%uA", cmdedit_y);
+		cmdedit_y = 0;
+		sv_cursor = cursor;
+		put_prompt(); /* sets cursor to 0 */
+		while (cursor < sv_cursor)
+			put_cur_glyph_and_inc_cursor();
+	} else {
+		int lines_up;
+		unsigned width;
+		/* num = chars to go back from the beginning of current line: */
+		num -= cmdedit_x;
+		width = cmdedit_termw; /* read volatile var once */
+		/* num=1...w: one line up, w+1...2w: two, etc: */
+		lines_up = 1 + (num - 1) / width;
+		cmdedit_x = (width * cmdedit_y - num) % width;
+		cmdedit_y -= lines_up;
+		/* go to 1st column; go 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(ESC"[%uC", cmdedit_x);
+	}
+}
+
+/* draw prompt, editor line, and clear tail */
+static void redraw(int y, int back_cursor)
+{
+	if (y > 0) /* up y lines */
+		printf(ESC"[%uA", y);
+	bb_putchar('\r');
+	put_prompt();
+	put_till_end_and_adv_cursor();
+	printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
+	input_backward(back_cursor);
+}
+
+/* Delete the char in front of the cursor, optionally saving it
+ * for later putback */
+#if !ENABLE_FEATURE_EDITING_VI
+static void input_delete(void)
+#define input_delete(save) input_delete()
+#else
+static void input_delete(int save)
+#endif
+{
+	int j = cursor;
+
+	if (j == (int)command_len)
+		return;
+
+#if ENABLE_FEATURE_EDITING_VI
+	if (save) {
+		if (newdelflag) {
+			delptr = delbuf;
+			newdelflag = 0;
+		}
+		if ((delptr - delbuf) < DELBUFSIZ)
+			*delptr++ = command_ps[j];
+	}
+#endif
+
+	memmove(command_ps + j, command_ps + j + 1,
+			/* (command_len + 1 [because of NUL]) - (j + 1)
+			 * simplified into (command_len - j) */
+			(command_len - j) * sizeof(command_ps[0]));
+	command_len--;
+	put_till_end_and_adv_cursor();
+	/* Last char is still visible, erase it (and more) */
+	printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
+	input_backward(cursor - j);     /* back to old pos cursor */
+}
+
+#if ENABLE_FEATURE_EDITING_VI
+static void put(void)
+{
+	int ocursor;
+	int j = delptr - delbuf;
+
+	if (j == 0)
+		return;
+	ocursor = cursor;
+	/* open hole and then fill it */
+	memmove(command_ps + cursor + j, command_ps + cursor,
+			(command_len - cursor + 1) * sizeof(command_ps[0]));
+	memcpy(command_ps + cursor, delbuf, j * sizeof(command_ps[0]));
+	command_len += j;
+	put_till_end_and_adv_cursor();
+	input_backward(cursor - ocursor - j + 1); /* at end of new text */
+}
+#endif
+
+/* Delete the char in back of the cursor */
+static void input_backspace(void)
+{
+	if (cursor > 0) {
+		input_backward(1);
+		input_delete(0);
+	}
+}
+
+/* Move forward one character */
+static void input_forward(void)
+{
+	if (cursor < command_len)
+		put_cur_glyph_and_inc_cursor();
+}
+
+#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) {
+		while (num_matches)
+			free(matches[--num_matches]);
+		free(matches);
+		matches = NULL;
+	}
+}
+
+static void add_match(char *matched)
+{
+	matches = xrealloc_vector(matches, 4, num_matches);
+	matches[num_matches] = matched;
+	num_matches++;
+}
+
+# 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;
+	char *tilde_name = ud;
+	char *home = NULL;
+
+	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;
+}
+
+/* ~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;
+
+	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();
+
+	return 1 + userlen;
+}
+# endif  /* FEATURE_USERNAME_COMPLETION */
+
+enum {
+	FIND_EXE_ONLY = 0,
+	FIND_DIR_ONLY = 1,
+	FIND_FILE_ONLY = 2,
+};
+
+static int path_parse(char ***p)
+{
+	int npth;
+	const char *pth;
+	char *tmp;
+	char **res;
+
+	if (state->flags & WITH_PATH_LOOKUP)
+		pth = state->path_lookup;
+	else
+		pth = getenv("PATH");
+
+	/* PATH="" or PATH=":"? */
+	if (!pth || !pth[0] || LONE_CHAR(pth, ':'))
+		return 1;
+
+	tmp = (char*)pth;
+	npth = 1; /* path component count */
+	while (1) {
+		tmp = strchr(tmp, ':');
+		if (!tmp)
+			break;
+		tmp++;
+		if (*tmp == '\0')
+			break;  /* :<empty> */
+		npth++;
+	}
+
+	*p = res = xmalloc(npth * sizeof(res[0]));
+	res[0] = tmp = xstrdup(pth);
+	npth = 1;
+	while (1) {
+		tmp = strchr(tmp, ':');
+		if (!tmp)
+			break;
+		*tmp++ = '\0'; /* ':' -> '\0' */
+		if (*tmp == '\0')
+			break; /* :<empty> */
+		res[npth++] = tmp;
+	}
+	return npth;
+}
+
+/* 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)
+{
+	char *path1[1];
+	char **paths = path1;
+	int npaths;
+	int i;
+	unsigned pf_len;
+	const char *pfind;
+	char *dirbuf = NULL;
+
+	npaths = 1;
+	path1[0] = (char*)".";
+
+	pfind = strrchr(command, '/');
+	if (!pfind) {
+		if (type == FIND_EXE_ONLY)
+			npaths = path_parse(&paths);
+		pfind = command;
+	} else {
+		/* 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) {
+			unsigned len;
+			const char *name_found = next->d_name;
+
+			/* .../<tab>: bash 3.2.0 shows dotfiles, but not . and .. */
+			if (!pfind[0] && DOT_OR_DOTDOT(name_found))
+				continue;
+			/* 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; /* hmm, remove in progress? */
+
+			/* 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, add slash */
+				found[len] = '/';
+				found[len + 1] = '\0';
+			} else {
+				/* skip files if looking for dirs only (example: cd) */
+				if (type == FIND_DIR_ONLY)
+					goto cont;
+			}
+			/* 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);
+	}
+	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)
+static void remove_chunk(int16_t *int_buf, int beg, int end)
+{
+	/* 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');
+	}
+}
+/* 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;
+	int16_t *int_buf = (int16_t*)match_buf;
+
+	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;
+		}
+	}
+	/* 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;
+				}
+			}
+			if (in_quote)
+				int_buf[i] = cur | QUOT;
+			i++;
+		}
+	}
+
+	/* Remove everything up to command delimiters:
+	 * ';' ';;' '&' '|' '&&' '||',
+	 * but careful with '>&' '<&' '>|'
+	 */
+	for (i = 0; int_buf[i]; i++) {
+		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 */
+		}
+	}
+	/* Remove all `cmd` */
+	for (i = 0; int_buf[i]; i++) {
+		if (int_buf[i] == '`') {
+			for (j = i + 1; int_buf[j]; j++) {
+				if (int_buf[j] == '`') {
+					/* `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;
+				}
+			}
+			/* No closing ` - command mode, remove all up to ` */
+			remove_chunk(int_buf, 0, i + 1);
+			break;
+ next: ;
+		}
+	}
+
+	/* 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] == '{') {
+			remove_chunk(int_buf, 0, i + 1);
+			i = -1;  /* back to square 1 */
+		}
+	}
+
+	/* Remove leading unquoted spaces */
+	for (i = 0; int_buf[i]; i++)
+		if (int_buf[i] != ' ')
+			break;
+	remove_chunk(int_buf, 0, i);
+
+	/* 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
+			 && (char)int_buf[0] == 'c'
+			 && (char)int_buf[1] == 'd'
+			 && i == 2 /* -> int_buf[2] == ' ' */
+			) {
+				command_mode = FIND_DIR_ONLY;
+			} else {
+				command_mode = FIND_FILE_ONLY;
+				break;
+			}
+		}
+	}
+	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--) {
+		int cur = int_buf[i];
+		if (cur == ' ' || cur == '<' || cur == '>' || cur == '|' || cur == '&') {
+			remove_chunk(int_buf, 0, i + 1);
+			break;
+		}
+	}
+
+	/* Convert back to string of _chars_ */
+	i = 0;
+	while ((match_buf[i] = int_buf[i]) != '\0')
+		i++;
+
+	if (dbg_bmp) printf("final match_buf:'%s'\n", match_buf);
+
+	return command_mode;
+}
+
+/*
+ * Display by column (original idea from ls applet,
+ * very optimized by me [Vladimir] :)
+ */
+static void showfiles(void)
+{
+	int ncols, row;
+	int column_width = 0;
+	int nfiles = num_matches;
+	int nrows = nfiles;
+	int l;
+
+	/* find the longest file name - use that as the column width */
+	for (row = 0; row < nrows; row++) {
+		l = unicode_strwidth(matches[row]);
+		if (column_width < l)
+			column_width = l;
+	}
+	column_width += 2;              /* min space for columns */
+	ncols = cmdedit_termw / column_width;
+
+	if (ncols > 1) {
+		nrows /= ncols;
+		if (nfiles % ncols)
+			nrows++;        /* round up fractionals */
+	} else {
+		ncols = 1;
+	}
+	for (row = 0; row < nrows; row++) {
+		int n = row;
+		int nc;
+
+		for (nc = 1; nc < ncols && n+nrows < nfiles; n += nrows, nc++) {
+			printf("%s%-*s", matches[n],
+				(int)(column_width - unicode_strwidth(matches[n])), ""
+			);
+		}
+		if (ENABLE_UNICODE_SUPPORT)
+			puts(printable_string(NULL, matches[n]));
+		else
+			puts(matches[n]);
+	}
+}
+
+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 (is_special_char(*found))
+			s[l++] = '\\';
+		s[l++] = *found++;
+	}
+	/* s[l] = '\0'; - already is */
+	return s;
+}
+
+/* Do TAB completion */
+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) {
+		/* 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 = 1;
+	chosen_match = NULL;
+
+	/* 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);
+
+	/* 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);
+
+	/* 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;
+	}
+
+	/* 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;
+				}
+			}
+		}
+ 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';
+		}
+	}
+
+# 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(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[cursor_mb], "%s%s", chosen_match + match_pfx_len, match_buf);
+			command_len = load_string(command);
+			/* write out the matched command */
+			/* 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_TAB_COMPLETION */
+
+
+line_input_t* FAST_FUNC new_line_input_t(int flags)
+{
+	line_input_t *n = xzalloc(sizeof(*n));
+	n->flags = flags;
+	n->max_history = MAX_HISTORY;
+	return n;
+}
+
+
+#if MAX_HISTORY > 0
+
+unsigned 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) {
+		int cur = state->cur_history;
+		free(state->history[cur]);
+
+# if ENABLE_UNICODE_SUPPORT
+		{
+			char tbuf[MAX_LINELEN];
+			save_string(tbuf, sizeof(tbuf));
+			state->history[cur] = xstrdup(tbuf);
+		}
+# else
+		state->history[cur] = xstrdup(command_ps);
+# endif
+	}
+}
+
+/* state->flags is already checked to be nonzero */
+static int get_previous_history(void)
+{
+	if ((state->flags & DO_HISTORY) && state->cur_history) {
+		save_command_ps_at_cur_history();
+		state->cur_history--;
+		return 1;
+	}
+	beep();
+	return 0;
+}
+
+static int get_next_history(void)
+{
+	if (state->flags & DO_HISTORY) {
+		if (state->cur_history < state->cnt_history) {
+			save_command_ps_at_cur_history(); /* save the current history line */
+			return ++state->cur_history;
+		}
+	}
+	beep();
+	return 0;
+}
+
+# if ENABLE_FEATURE_EDITING_SAVEHISTORY
+/* We try to ensure that concurrent additions to the history
+ * do not overwrite each other.
+ * Otherwise shell users get unhappy.
+ *
+ * History file is trimmed lazily, when it grows several times longer
+ * than configured MAX_HISTORY lines.
+ */
+
+static void free_line_input_t(line_input_t *n)
+{
+	int i = n->cnt_history;
+	while (i > 0)
+		free(n->history[--i]);
+	free(n);
+}
+
+/* state->flags is already checked to be nonzero */
+static void load_history(line_input_t *st_parm)
+{
+	char *temp_h[MAX_HISTORY];
+	char *line;
+	FILE *fp;
+	unsigned idx, i, line_len;
+
+	/* NB: do not trash old history if file can't be opened */
+
+	fp = fopen_for_read(st_parm->hist_file);
+	if (fp) {
+		/* clean up old history */
+		for (idx = st_parm->cnt_history; idx > 0;) {
+			idx--;
+			free(st_parm->history[idx]);
+			st_parm->history[idx] = NULL;
+		}
+
+		/* fill temp_h[], retaining only last MAX_HISTORY lines */
+		memset(temp_h, 0, sizeof(temp_h));
+		st_parm->cnt_history_in_file = idx = 0;
+		while ((line = xmalloc_fgetline(fp)) != NULL) {
+			if (line[0] == '\0') {
+				free(line);
+				continue;
+			}
+			free(temp_h[idx]);
+			temp_h[idx] = line;
+			st_parm->cnt_history_in_file++;
+			idx++;
+			if (idx == st_parm->max_history)
+				idx = 0;
+		}
+		fclose(fp);
+
+		/* find first non-NULL temp_h[], if any */
+		if (st_parm->cnt_history_in_file) {
+			while (temp_h[idx] == NULL) {
+				idx++;
+				if (idx == st_parm->max_history)
+					idx = 0;
+			}
+		}
+
+		/* copy temp_h[] to st_parm->history[] */
+		for (i = 0; i < st_parm->max_history;) {
+			line = temp_h[idx];
+			if (!line)
+				break;
+			idx++;
+			if (idx == st_parm->max_history)
+				idx = 0;
+			line_len = strlen(line);
+			if (line_len >= MAX_LINELEN)
+				line[MAX_LINELEN-1] = '\0';
+			st_parm->history[i++] = line;
+		}
+		st_parm->cnt_history = i;
+	}
+}
+
+/* state->flags is already checked to be nonzero */
+static void save_history(char *str)
+{
+	int fd;
+	int len, len2;
+
+	fd = open(state->hist_file, O_WRONLY | O_CREAT | O_APPEND, 0600);
+	if (fd < 0)
+		return;
+	xlseek(fd, 0, SEEK_END); /* paranoia */
+	len = strlen(str);
+	str[len] = '\n'; /* we (try to) do atomic write */
+	len2 = full_write(fd, str, len + 1);
+	str[len] = '\0';
+	close(fd);
+	if (len2 != len + 1)
+		return; /* "wtf?" */
+
+	/* did we write so much that history file needs trimming? */
+	state->cnt_history_in_file++;
+	if (state->cnt_history_in_file > state->max_history * 4) {
+		char *new_name;
+		line_input_t *st_temp;
+
+		/* 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());
+		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);
+			if (rename(new_name, state->hist_file) == 0)
+				state->cnt_history_in_file = st_temp->cnt_history;
+		}
+		free(new_name);
+		free_line_input_t(st_temp);
+	}
+}
+# else
+#  define load_history(a) ((void)0)
+#  define save_history(a) ((void)0)
+# endif /* FEATURE_COMMAND_SAVEHISTORY */
+
+static void remember_in_history(char *str)
+{
+	int i;
+
+	if (!(state->flags & DO_HISTORY))
+		return;
+	if (str[0] == '\0')
+		return;
+	i = state->cnt_history;
+	/* Don't save dupes */
+	if (i && strcmp(state->history[i-1], str) == 0)
+		return;
+
+	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[state->max_history] empty, hence >=, not > */
+	if (i >= state->max_history) {
+		free(state->history[0]);
+		for (i = 0; i < state->max_history-1; i++)
+			state->history[i] = state->history[i+1];
+		/* i == state->max_history-1 */
+	}
+	/* i <= state->max_history-1 */
+	state->history[i++] = xstrdup(str);
+	/* 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);
+# endif
+	IF_FEATURE_EDITING_FANCY_PROMPT(num_ok_lines++;)
+}
+
+#else /* MAX_HISTORY == 0 */
+# define remember_in_history(a) ((void)0)
+#endif /* MAX_HISTORY */
+
+
+#if ENABLE_FEATURE_EDITING_VI
+/*
+ * vi mode implemented 2005 by Paul Fox <pgf@foxharp.boston.ma.us>
+ */
+static void
+vi_Word_motion(int eat)
+{
+	CHAR_T *command = command_ps;
+
+	while (cursor < command_len && !BB_isspace(command[cursor]))
+		input_forward();
+	if (eat) while (cursor < command_len && BB_isspace(command[cursor]))
+		input_forward();
+}
+
+static void
+vi_word_motion(int eat)
+{
+	CHAR_T *command = command_ps;
+
+	if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
+		while (cursor < command_len
+		 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
+		) {
+			input_forward();
+		}
+	} else if (BB_ispunct(command[cursor])) {
+		while (cursor < command_len && BB_ispunct(command[cursor+1]))
+			input_forward();
+	}
+
+	if (cursor < command_len)
+		input_forward();
+
+	if (eat) {
+		while (cursor < command_len && BB_isspace(command[cursor]))
+			input_forward();
+	}
+}
+
+static void
+vi_End_motion(void)
+{
+	CHAR_T *command = command_ps;
+
+	input_forward();
+	while (cursor < command_len && BB_isspace(command[cursor]))
+		input_forward();
+	while (cursor < command_len-1 && !BB_isspace(command[cursor+1]))
+		input_forward();
+}
+
+static void
+vi_end_motion(void)
+{
+	CHAR_T *command = command_ps;
+
+	if (cursor >= command_len-1)
+		return;
+	input_forward();
+	while (cursor < command_len-1 && BB_isspace(command[cursor]))
+		input_forward();
+	if (cursor >= command_len-1)
+		return;
+	if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
+		while (cursor < command_len-1
+		 && (BB_isalnum(command[cursor+1]) || command[cursor+1] == '_')
+		) {
+			input_forward();
+		}
+	} else if (BB_ispunct(command[cursor])) {
+		while (cursor < command_len-1 && BB_ispunct(command[cursor+1]))
+			input_forward();
+	}
+}
+
+static void
+vi_Back_motion(void)
+{
+	CHAR_T *command = command_ps;
+
+	while (cursor > 0 && BB_isspace(command[cursor-1]))
+		input_backward(1);
+	while (cursor > 0 && !BB_isspace(command[cursor-1]))
+		input_backward(1);
+}
+
+static void
+vi_back_motion(void)
+{
+	CHAR_T *command = command_ps;
+
+	if (cursor <= 0)
+		return;
+	input_backward(1);
+	while (cursor > 0 && BB_isspace(command[cursor]))
+		input_backward(1);
+	if (cursor <= 0)
+		return;
+	if (BB_isalnum(command[cursor]) || command[cursor] == '_') {
+		while (cursor > 0
+		 && (BB_isalnum(command[cursor-1]) || command[cursor-1] == '_')
+		) {
+			input_backward(1);
+		}
+	} else if (BB_ispunct(command[cursor])) {
+		while (cursor > 0 && BB_ispunct(command[cursor-1]))
+			input_backward(1);
+	}
+}
+#endif
+
+/* Modelled after bash 4.0 behavior of Ctrl-<arrow> */
+static void ctrl_left(void)
+{
+	CHAR_T *command = command_ps;
+
+	while (1) {
+		CHAR_T c;
+
+		input_backward(1);
+		if (cursor == 0)
+			break;
+		c = command[cursor];
+		if (c != ' ' && !BB_ispunct(c)) {
+			/* we reached a "word" delimited by spaces/punct.
+			 * go to its beginning */
+			while (1) {
+				c = command[cursor - 1];
+				if (c == ' ' || BB_ispunct(c))
+					break;
+				input_backward(1);
+				if (cursor == 0)
+					break;
+			}
+			break;
+		}
+	}
+}
+static void ctrl_right(void)
+{
+	CHAR_T *command = command_ps;
+
+	while (1) {
+		CHAR_T c;
+
+		c = command[cursor];
+		if (c == BB_NUL)
+			break;
+		if (c != ' ' && !BB_ispunct(c)) {
+			/* we reached a "word" delimited by spaces/punct.
+			 * go to its end + 1 */
+			while (1) {
+				input_forward();
+				c = command[cursor];
+				if (c == BB_NUL || c == ' ' || BB_ispunct(c))
+					break;
+			}
+			break;
+		}
+		input_forward();
+	}
+}
+
+
+/*
+ * read_line_input and its helpers
+ */
+
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+static void ask_terminal(void)
+{
+	/* Ask terminal where is the cursor now.
+	 * lineedit_read_key handles response and corrects
+	 * our idea of current cursor position.
+	 * Testcase: run "echo -n long_line_long_line_long_line",
+	 * then type in a long, wrapping command and try to
+	 * delete it using backspace key.
+	 * Note: we print it _after_ prompt, because
+	 * prompt may contain CR. Example: PS1='\[\r\n\]\w '
+	 */
+	/* Problem: if there is buffered input on stdin,
+	 * the response will be delivered later,
+	 * possibly to an unsuspecting application.
+	 * Testcase: "sleep 1; busybox ash" + press and hold [Enter].
+	 * Result:
+	 * ~/srcdevel/bbox/fix/busybox.t4 #
+	 * ~/srcdevel/bbox/fix/busybox.t4 #
+	 * ^[[59;34~/srcdevel/bbox/fix/busybox.t4 #  <-- garbage
+	 * ~/srcdevel/bbox/fix/busybox.t4 #
+	 *
+	 * Checking for input with poll only makes the race narrower,
+	 * I still can trigger it. Strace:
+	 *
+	 * 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, -1) = 1 ([{fd=0, revents=POLLIN}])
+	 * read(0, "\n", 1)      = 1  <-- oh crap, user's input got in first
+	 */
+	struct pollfd pfd;
+
+	pfd.fd = STDIN_FILENO;
+	pfd.events = POLLIN;
+	if (safe_poll(&pfd, 1, 0) == 0) {
+		S.sent_ESC_br6n = 1;
+		fputs(ESC"[6n", stdout);
+		fflush_all(); /* make terminal see it ASAP! */
+	}
+}
+#else
+#define ask_terminal() ((void)0)
+#endif
+
+#if !ENABLE_FEATURE_EDITING_FANCY_PROMPT
+static void parse_and_put_prompt(const char *prmt_ptr)
+{
+	cmdedit_prompt = prmt_ptr;
+	cmdedit_prmt_len = strlen(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 = '[';
+	char *prmt_mem_ptr = xzalloc(1);
+	char *cwd_buf = xrealloc_getcwd_or_warn(NULL);
+	char cbuf[2];
+	char c;
+	char *pbuf;
+
+	cmdedit_prmt_len = 0;
+
+	if (!cwd_buf) {
+		cwd_buf = (char *)bb_msg_unknown;
+	}
+
+	cbuf[1] = '\0'; /* never changes */
+
+	while (*prmt_ptr) {
+		char *free_me = NULL;
+
+		pbuf = cbuf;
+		c = *prmt_ptr++;
+		if (c == '\\') {
+			const char *cp = prmt_ptr;
+			int l;
+
+			c = bb_process_escape_sequence(&prmt_ptr);
+			if (prmt_ptr == cp) {
+				if (*cp == '\0')
+					break;
+				c = *prmt_ptr++;
+
+				switch (c) {
+# if ENABLE_USERNAME_OR_HOMEDIR
+				case 'u':
+					pbuf = user_buf ? user_buf : (char*)"";
+					break;
+# endif
+				case 'h':
+					pbuf = free_me = safe_gethostname();
+					*strchrnul(pbuf, '.') = '\0';
+					break;
+				case '$':
+					c = (geteuid() == 0 ? '#' : '$');
+					break;
+# if ENABLE_USERNAME_OR_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);
+					}
+					break;
+# endif
+				case 'W':
+					pbuf = cwd_buf;
+					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';
+					break;
+				case 'x': case 'X': {
+					char buf2[4];
+					for (l = 0; l < 3;) {
+						unsigned h;
+						buf2[l++] = *prmt_ptr;
+						buf2[l] = '\0';
+						h = strtoul(buf2, &pbuf, 16);
+						if (h > UCHAR_MAX || (pbuf - buf2) < l) {
+							buf2[--l] = '\0';
+							break;
+						}
+						prmt_ptr++;
+					}
+					c = (char)strtoul(buf2, NULL, 16);
+					if (c == 0)
+						c = '?';
+					pbuf = cbuf;
+					break;
+				}
+				case '[': case ']':
+					if (c == flg_not_length) {
+						flg_not_length = (flg_not_length == '[' ? ']' : '[');
+						continue;
+					}
+					break;
+				} /* switch */
+			} /* 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);
+		free(free_me);
+	} /* while */
+
+	if (cwd_buf != (char *)bb_msg_unknown)
+		free(cwd_buf);
+	cmdedit_prompt = prmt_mem_ptr;
+	put_prompt();
+}
+#endif
+
+static void cmdedit_setwidth(unsigned w, int redraw_flg)
+{
+	cmdedit_termw = w;
+	if (redraw_flg) {
+		/* new y for current cursor */
+		int new_y = (cursor + cmdedit_prmt_len) / w;
+		/* redraw */
+		redraw((new_y >= cmdedit_y ? new_y : cmdedit_y), command_len - cursor);
+		fflush_all();
+	}
+}
+
+static void win_changed(int nsig)
+{
+	int sv_errno = errno;
+	unsigned width;
+
+	get_terminal_width_height(0, &width, NULL);
+//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, int timeout)
+{
+	int64_t ic;
+#if ENABLE_UNICODE_SUPPORT
+	char unicode_buf[MB_CUR_MAX + 1];
+	int unicode_idx = 0;
+#endif
+
+	while (1) {
+		/* Wait for input. TIMEOUT = -1 makes read_key wait even
+		 * on nonblocking stdin, TIMEOUT = 50 makes sure we won't
+		 * insist on full MB_CUR_MAX buffer to declare input like
+		 * "\xff\n",pause,"ls\n" invalid and thus won't lose "ls".
+		 *
+		 * Note: read_key sets errno to 0 on success.
+		 */
+		ic = read_key(STDIN_FILENO, read_key_buffer, timeout);
+		if (errno) {
+#if ENABLE_UNICODE_SUPPORT
+			if (errno == EAGAIN && unicode_idx != 0)
+				goto pushback;
+#endif
+			break;
+		}
+
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+		if ((int32_t)ic == KEYCODE_CURSOR_POS
+		 && S.sent_ESC_br6n
+		) {
+			S.sent_ESC_br6n = 0;
+			if (cursor == 0) { /* otherwise it may be bogus */
+				int col = ((ic >> 32) & 0x7fff) - 1;
+				if (col > cmdedit_prmt_len) {
+					cmdedit_x += (col - cmdedit_prmt_len);
+					while (cmdedit_x >= cmdedit_termw) {
+						cmdedit_x -= cmdedit_termw;
+						cmdedit_y++;
+					}
+				}
+			}
+			continue;
+		}
+#endif
+
+#if ENABLE_UNICODE_SUPPORT
+		if (unicode_status == UNICODE_ON) {
+			wchar_t wc;
+
+			if ((int32_t)ic < 0) /* KEYCODE_xxx */
+				break;
+			// TODO: imagine sequence like: 0xff,<left-arrow>: we are currently losing 0xff...
+
+			unicode_buf[unicode_idx++] = ic;
+			unicode_buf[unicode_idx] = '\0';
+			if (mbstowcs(&wc, unicode_buf, 1) != 1) {
+				/* Not (yet?) a valid unicode char */
+				if (unicode_idx < MB_CUR_MAX) {
+					timeout = 50;
+					continue;
+				}
+ pushback:
+				/* Invalid sequence. Save all "bad bytes" except first */
+				read_key_ungets(read_key_buffer, unicode_buf + 1, unicode_idx - 1);
+# if !ENABLE_UNICODE_PRESERVE_BROKEN
+				ic = CONFIG_SUBST_WCHAR;
+# else
+				ic = unicode_mark_raw_byte(unicode_buf[0]);
+# endif
+			} else {
+				/* Valid unicode char, return its code */
+				ic = wc;
+			}
+		}
+#endif
+		break;
+	}
+
+	return ic;
+}
+
+#if ENABLE_UNICODE_BIDI_SUPPORT
+static int isrtl_str(void)
+{
+	int idx = cursor;
+
+	while (idx < command_len && unicode_bidi_is_neutral_wchar(command_ps[idx]))
+		idx++;
+	return unicode_bidi_isrtl(command_ps[idx]);
+}
+#else
+# define isrtl_str() 0
+#endif
+
+/* leave out the "vi-mode"-only case labels if vi editing isn't
+ * configured. */
+#define vi_case(caselabel) IF_FEATURE_EDITING_VI(case caselabel)
+
+/* convert uppercase ascii to equivalent control char, for readability */
+#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;
+	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;
+	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 = strlen(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 = strlen(cmdedit_prompt);
+	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(line_input_t *st, const char *prompt, char *command, int maxsize, int timeout)
+{
+	int len;
+#if ENABLE_FEATURE_TAB_COMPLETION
+	smallint lastWasTab = 0;
+#endif
+	smallint break_out = 0;
+#if ENABLE_FEATURE_EDITING_VI
+	smallint vi_cmdmode = 0;
+#endif
+	struct termios initial_settings;
+	struct termios new_settings;
+	char read_key_buffer[KEYCODE_BUFFER_SIZE];
+
+	INIT_S();
+
+	if (tcgetattr(STDIN_FILENO, &initial_settings) < 0
+	 || !(initial_settings.c_lflag & ECHO)
+	) {
+		/* Happens when e.g. stty -echo was run before */
+		parse_and_put_prompt(prompt);
+		/* fflush_all(); - done by parse_and_put_prompt */
+		if (fgets(command, maxsize, stdin) == NULL)
+			len = -1; /* EOF or error */
+		else
+			len = strlen(command);
+		DEINIT_S();
+		return len;
+	}
+
+	init_unicode();
+
+// FIXME: audit & improve this
+	if (maxsize > MAX_LINELEN)
+		maxsize = MAX_LINELEN;
+	S.maxsize = maxsize;
+
+	/* 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->cnt_history == 0)
+			load_history(state);
+# endif
+	if (state->flags & DO_HISTORY)
+		state->cur_history = state->cnt_history;
+#endif
+
+	/* prepare before init handlers */
+	cmdedit_y = 0;  /* quasireal y, not true if line > xt*yt */
+	command_len = 0;
+#if ENABLE_UNICODE_SUPPORT
+	command_ps = xzalloc(maxsize * sizeof(command_ps[0]));
+#else
+	command_ps = command;
+	command[0] = '\0';
+#endif
+#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 */
+	new_settings.c_cc[VMIN] = 1;
+	new_settings.c_cc[VTIME] = 0;
+	/* Turn off CTRL-C, so we can trap it */
+	new_settings.c_cc[VINTR] = _POSIX_VDISABLE;
+	tcsetattr_stdin_TCSANOW(&new_settings);
+
+#if ENABLE_USERNAME_OR_HOMEDIR
+	{
+		struct passwd *entry;
+
+		entry = getpwuid(geteuid());
+		if (entry) {
+			user_buf = xstrdup(entry->pw_name);
+			home_pwd_buf = xstrdup(entry->pw_dir);
+		}
+	}
+#endif
+
+#if 0
+	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
+
+	/* Print out the command prompt, optionally ask where cursor is */
+	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) {
+		/*
+		 * The emacs and vi modes share much of the code in the big
+		 * command loop.  Commands entered when in vi's command mode
+		 * (aka "escape mode") get an extra bit added to distinguish
+		 * them - this keeps them from being self-inserted. This
+		 * clutters the big switch a bit, but keeps all the code
+		 * in one place.
+		 */
+		int32_t ic, ic_raw;
+
+		fflush_all();
+		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) {
+			/* btw, since KEYCODE_xxx are all < 0, this doesn't
+			 * change ic if it contains one of them: */
+			ic |= VI_CMDMODE_BIT;
+		}
+#endif
+
+		switch (ic) {
+		case '\n':
+		case '\r':
+		vi_case('\n'|VI_CMDMODE_BIT:)
+		vi_case('\r'|VI_CMDMODE_BIT:)
+			/* Enter */
+			goto_new_line();
+			break_out = 1;
+			break;
+		case CTRL('A'):
+		vi_case('0'|VI_CMDMODE_BIT:)
+			/* Control-a -- Beginning of line */
+			input_backward(cursor);
+			break;
+		case CTRL('B'):
+		vi_case('h'|VI_CMDMODE_BIT:)
+		vi_case('\b'|VI_CMDMODE_BIT:) /* ^H */
+		vi_case('\x7f'|VI_CMDMODE_BIT:) /* DEL */
+			input_backward(1); /* Move back one character */
+			break;
+		case CTRL('E'):
+		vi_case('$'|VI_CMDMODE_BIT:)
+			/* Control-e -- End of line */
+			put_till_end_and_adv_cursor();
+			break;
+		case CTRL('F'):
+		vi_case('l'|VI_CMDMODE_BIT:)
+		vi_case(' '|VI_CMDMODE_BIT:)
+			input_forward(); /* Move forward one character */
+			break;
+		case '\b':   /* ^H */
+		case '\x7f': /* DEL */
+			if (!isrtl_str())
+				input_backspace();
+			else
+				input_delete(0);
+			break;
+		case KEYCODE_DELETE:
+			if (!isrtl_str())
+				input_delete(0);
+			else
+				input_backspace();
+			break;
+#if ENABLE_FEATURE_TAB_COMPLETION
+		case '\t':
+			input_tab(&lastWasTab);
+			break;
+#endif
+		case CTRL('K'):
+			/* Control-k -- clear to end of line */
+			command_ps[cursor] = BB_NUL;
+			command_len = cursor;
+			printf(SEQ_CLEAR_TILL_END_OF_SCREEN);
+			break;
+		case CTRL('L'):
+		vi_case(CTRL('L')|VI_CMDMODE_BIT:)
+			/* Control-l -- clear screen */
+			printf(ESC"[H"); /* cursor to top,left */
+			redraw(0, command_len - cursor);
+			break;
+#if MAX_HISTORY > 0
+		case CTRL('N'):
+		vi_case(CTRL('N')|VI_CMDMODE_BIT:)
+		vi_case('j'|VI_CMDMODE_BIT:)
+			/* Control-n -- Get next command in history */
+			if (get_next_history())
+				goto rewrite_line;
+			break;
+		case CTRL('P'):
+		vi_case(CTRL('P')|VI_CMDMODE_BIT:)
+		vi_case('k'|VI_CMDMODE_BIT:)
+			/* Control-p -- Get previous command from history */
+			if (get_previous_history())
+				goto rewrite_line;
+			break;
+#endif
+		case CTRL('U'):
+		vi_case(CTRL('U')|VI_CMDMODE_BIT:)
+			/* Control-U -- Clear line before cursor */
+			if (cursor) {
+				command_len -= cursor;
+				memmove(command_ps, command_ps + cursor,
+					(command_len + 1) * sizeof(command_ps[0]));
+				redraw(cmdedit_y, command_len);
+			}
+			break;
+		case CTRL('W'):
+		vi_case(CTRL('W')|VI_CMDMODE_BIT:)
+			/* Control-W -- Remove the last word */
+			while (cursor > 0 && BB_isspace(command_ps[cursor-1]))
+				input_backspace();
+			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:
+			vi_cmdmode = 0;
+			break;
+		case 'I'|VI_CMDMODE_BIT:
+			input_backward(cursor);
+			vi_cmdmode = 0;
+			break;
+		case 'a'|VI_CMDMODE_BIT:
+			input_forward();
+			vi_cmdmode = 0;
+			break;
+		case 'A'|VI_CMDMODE_BIT:
+			put_till_end_and_adv_cursor();
+			vi_cmdmode = 0;
+			break;
+		case 'x'|VI_CMDMODE_BIT:
+			input_delete(1);
+			break;
+		case 'X'|VI_CMDMODE_BIT:
+			if (cursor > 0) {
+				input_backward(1);
+				input_delete(1);
+			}
+			break;
+		case 'W'|VI_CMDMODE_BIT:
+			vi_Word_motion(1);
+			break;
+		case 'w'|VI_CMDMODE_BIT:
+			vi_word_motion(1);
+			break;
+		case 'E'|VI_CMDMODE_BIT:
+			vi_End_motion();
+			break;
+		case 'e'|VI_CMDMODE_BIT:
+			vi_end_motion();
+			break;
+		case 'B'|VI_CMDMODE_BIT:
+			vi_Back_motion();
+			break;
+		case 'b'|VI_CMDMODE_BIT:
+			vi_back_motion();
+			break;
+		case 'C'|VI_CMDMODE_BIT:
+			vi_cmdmode = 0;
+			/* fall through */
+		case 'D'|VI_CMDMODE_BIT:
+			goto clear_to_eol;
+
+		case 'c'|VI_CMDMODE_BIT:
+			vi_cmdmode = 0;
+			/* fall through */
+		case 'd'|VI_CMDMODE_BIT: {
+			int nc, sc;
+
+			ic = lineedit_read_key(read_key_buffer, timeout);
+			if (errno) /* error */
+				goto return_error_indicator;
+			if (ic == ic_raw) { /* "cc", "dd" */
+				input_backward(cursor);
+				goto clear_to_eol;
+				break;
+			}
+
+			sc = cursor;
+			switch (ic) {
+			case 'w':
+			case 'W':
+			case 'e':
+			case 'E':
+				switch (ic) {
+				case 'w':   /* "dw", "cw" */
+					vi_word_motion(vi_cmdmode);
+					break;
+				case 'W':   /* 'dW', 'cW' */
+					vi_Word_motion(vi_cmdmode);
+					break;
+				case 'e':   /* 'de', 'ce' */
+					vi_end_motion();
+					input_forward();
+					break;
+				case 'E':   /* 'dE', 'cE' */
+					vi_End_motion();
+					input_forward();
+					break;
+				}
+				nc = cursor;
+				input_backward(cursor - sc);
+				while (nc-- > cursor)
+					input_delete(1);
+				break;
+			case 'b':  /* "db", "cb" */
+			case 'B':  /* implemented as B */
+				if (ic == 'b')
+					vi_back_motion();
+				else
+					vi_Back_motion();
+				while (sc-- > cursor)
+					input_delete(1);
+				break;
+			case ' ':  /* "d ", "c " */
+				input_delete(1);
+				break;
+			case '$':  /* "d$", "c$" */
+ clear_to_eol:
+				while (cursor < command_len)
+					input_delete(1);
+				break;
+			}
+			break;
+		}
+		case 'p'|VI_CMDMODE_BIT:
+			input_forward();
+			/* fallthrough */
+		case 'P'|VI_CMDMODE_BIT:
+			put();
+			break;
+		case 'r'|VI_CMDMODE_BIT:
+//FIXME: unicode case?
+			ic = lineedit_read_key(read_key_buffer, timeout);
+			if (errno) /* error */
+				goto return_error_indicator;
+			if (ic < ' ' || ic > 255) {
+				beep();
+			} else {
+				command_ps[cursor] = ic;
+				bb_putchar(ic);
+				bb_putchar('\b');
+			}
+			break;
+		case '\x1b': /* ESC */
+			if (state->flags & VI_MODE) {
+				/* insert mode --> command mode */
+				vi_cmdmode = 1;
+				input_backward(1);
+			}
+			break;
+#endif /* FEATURE_COMMAND_EDITING_VI */
+
+#if MAX_HISTORY > 0
+		case KEYCODE_UP:
+			if (get_previous_history())
+				goto rewrite_line;
+			beep();
+			break;
+		case KEYCODE_DOWN:
+			if (!get_next_history())
+				break;
+ rewrite_line:
+			/* Rewrite the line with the selected history item */
+			/* change command */
+			command_len = load_string(state->history[state->cur_history] ?
+					state->history[state->cur_history] : "");
+			/* redraw and go to eol (bol, in vi) */
+			redraw(cmdedit_y, (state->flags & VI_MODE) ? 9999 : 0);
+			break;
+#endif
+		case KEYCODE_RIGHT:
+			input_forward();
+			break;
+		case KEYCODE_LEFT:
+			input_backward(1);
+			break;
+		case KEYCODE_CTRL_LEFT:
+			ctrl_left();
+			break;
+		case KEYCODE_CTRL_RIGHT:
+			ctrl_right();
+			break;
+		case KEYCODE_HOME:
+			input_backward(cursor);
+			break;
+		case KEYCODE_END:
+			put_till_end_and_adv_cursor();
+			break;
+
+		default:
+			if (initial_settings.c_cc[VINTR] != 0
+			 && ic_raw == initial_settings.c_cc[VINTR]
+			) {
+				/* Ctrl-C (usually) - stop gathering input */
+				goto_new_line();
+				command_len = 0;
+				break_out = -1; /* "do not append '\n'" */
+				break;
+			}
+			if (initial_settings.c_cc[VEOF] != 0
+			 && ic_raw == initial_settings.c_cc[VEOF]
+			) {
+				/* Ctrl-D (usually) - delete one character,
+				 * or exit if len=0 and no chars to delete */
+				if (command_len == 0) {
+					errno = 0;
+
+		case -1: /* error (e.g. EIO when tty is destroyed) */
+ IF_FEATURE_EDITING_VI(return_error_indicator:)
+					break_out = command_len = -1;
+					break;
+				}
+				input_delete(0);
+				break;
+			}
+//			/* Control-V -- force insert of next char */
+//			if (c == CTRL('V')) {
+//				if (safe_read(STDIN_FILENO, &c, 1) < 1)
+//					goto return_error_indicator;
+//				if (c == 0) {
+//					beep();
+//					break;
+//				}
+//			}
+			if (ic < ' '
+			 || (!ENABLE_UNICODE_SUPPORT && ic >= 256)
+			 || (ENABLE_UNICODE_SUPPORT && ic >= VI_CMDMODE_BIT)
+			) {
+				/* If VI_CMDMODE_BIT is set, ic is >= 256
+				 * and vi mode ignores unexpected chars.
+				 * Otherwise, we are here if ic is a
+				 * control char or an unhandled ESC sequence,
+				 * which is also ignored.
+				 */
+				break;
+			}
+			if ((int)command_len >= (maxsize - 2)) {
+				/* Not enough space for the char and EOL */
+				break;
+			}
+
+			command_len++;
+			if (cursor == (command_len - 1)) {
+				/* We are at the end, append */
+				command_ps[cursor] = ic;
+				command_ps[cursor + 1] = BB_NUL;
+				put_cur_glyph_and_inc_cursor();
+				if (unicode_bidi_isrtl(ic))
+					input_backward(1);
+			} else {
+				/* In the middle, insert */
+				int sc = cursor;
+
+				memmove(command_ps + sc + 1, command_ps + sc,
+					(command_len - sc) * sizeof(command_ps[0]));
+				command_ps[sc] = ic;
+				/* is right-to-left char, or neutral one (e.g. comma) was just added to rtl text? */
+				if (!isrtl_str())
+					sc++; /* no */
+				put_till_end_and_adv_cursor();
+				/* to prev x pos + 1 */
+				input_backward(cursor - sc);
+			}
+			break;
+		} /* switch (ic) */
+
+		if (break_out)
+			break;
+
+#if ENABLE_FEATURE_TAB_COMPLETION
+		if (ic_raw != '\t')
+			lastWasTab = 0;
+#endif
+	} /* while (1) */
+
+#if ENABLE_FEATURE_EDITING_ASK_TERMINAL
+	if (S.sent_ESC_br6n) {
+		/* "sleep 1; busybox ash" + hold [Enter] to trigger.
+		 * We sent "ESC [ 6 n", but got '\n' first, and
+		 * KEYCODE_CURSOR_POS response is now buffered from terminal.
+		 * It's bad already and not much can be done with it
+		 * (it _will_ be visible for the next process to read stdin),
+		 * but without this delay it even shows up on the screen
+		 * as garbage because we restore echo settings with tcsetattr
+		 * before it comes in. UGLY!
+		 */
+		usleep(20*1000);
+	}
+#endif
+
+/* End of bug-catching "command_must_not_be_used" trick */
+#undef command
+
+#if ENABLE_UNICODE_SUPPORT
+	command[0] = '\0';
+	if (command_len > 0)
+		command_len = save_string(command, maxsize - 1);
+	free(command_ps);
+#endif
+
+	if (command_len > 0)
+		remember_in_history(command);
+
+	if (break_out > 0) {
+		command[command_len++] = '\n';
+		command[command_len] = '\0';
+	}
+
+#if ENABLE_FEATURE_TAB_COMPLETION
+	free_tab_completion_data();
+#endif
+
+	/* restore initial_settings */
+	tcsetattr_stdin_TCSANOW(&initial_settings);
+	/* restore SIGWINCH handler */
+	signal(SIGWINCH, previous_SIGWINCH_handler);
+	fflush_all();
+
+	len = command_len;
+	DEINIT_S();
+
+	return len; /* can't return command_len, DEINIT_S() destroys it */
+}
+
+#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);
+	return strlen(command);
+}
+
+#endif  /* !FEATURE_EDITING */
+
+
+/*
+ * Testing
+ */
+
+#ifdef TEST
+
+#include <locale.h>
+
+const char *applet_name = "debug stuff usage";
+
+int main(int argc, char **argv)
+{
+	char buff[MAX_LINELEN];
+	char *prompt =
+#if ENABLE_FEATURE_EDITING_FANCY_PROMPT
+		"\\[\\033[32;1m\\]\\u@\\[\\x1b[33;1m\\]\\h:"
+		"\\[\\033[34;1m\\]\\w\\[\\033[35;1m\\] "
+		"\\!\\[\\e[36;1m\\]\\$ \\[\\E[0m\\]";
+#else
+		"% ";
+#endif
+
+	while (1) {
+		int l;
+		l = read_line_input(prompt, buff);
+		if (l <= 0 || buff[l-1] != '\n')
+			break;
+		buff[l-1] = '\0';
+		printf("*** read_line_input() returned line =%s=\n", buff);
+	}
+	printf("*** read_line_input() detect ^D\n");
+	return 0;
+}
+
+#endif  /* TEST */
diff --git a/busybox-1.19.3/libbb/lineedit_ptr_hack.c b/busybox-1.19.3/libbb/lineedit_ptr_hack.c
new file mode 100644
index 0000000..dc45855
--- /dev/null
+++ b/busybox-1.19.3/libbb/lineedit_ptr_hack.c
@@ -0,0 +1,23 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+struct lineedit_statics;
+
+#ifndef GCC_COMBINE
+
+/* We cheat here. It is declared as const ptr in libbb.h,
+ * but here we make it live in R/W memory */
+struct lineedit_statics *lineedit_ptr_to_statics;
+
+#else
+
+/* gcc -combine will see through and complain */
+/* Using alternative method which is more likely to break
+ * on weird architectures, compilers, linkers and so on */
+struct lineedit_statics *const lineedit_ptr_to_statics __attribute__ ((section (".data")));
+
+#endif
diff --git a/busybox-1.19.3/libbb/llist.c b/busybox-1.19.3/libbb/llist.c
new file mode 100644
index 0000000..032e9fa
--- /dev/null
+++ b/busybox-1.19.3/libbb/llist.c
@@ -0,0 +1,98 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linked list helper functions.
+ *
+ * Copyright (C) 2003 Glenn McGrath
+ * Copyright (C) 2005 Vladimir Oleynik
+ * Copyright (C) 2005 Bernhard Reutner-Fischer
+ * Copyright (C) 2006 Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Add data to the start of the linked list.  */
+void FAST_FUNC llist_add_to(llist_t **old_head, void *data)
+{
+	llist_t *new_head = xmalloc(sizeof(llist_t));
+
+	new_head->data = data;
+	new_head->link = *old_head;
+	*old_head = new_head;
+}
+
+/* Add data to the end of the linked list.  */
+void FAST_FUNC llist_add_to_end(llist_t **list_head, void *data)
+{
+	while (*list_head)
+		list_head = &(*list_head)->link;
+	*list_head = xzalloc(sizeof(llist_t));
+	(*list_head)->data = data;
+	/*(*list_head)->link = NULL;*/
+}
+
+/* Remove first element from the list and return it */
+void* FAST_FUNC llist_pop(llist_t **head)
+{
+	void *data = NULL;
+	llist_t *temp = *head;
+
+	if (temp) {
+		data = temp->data;
+		*head = temp->link;
+		free(temp);
+	}
+	return data;
+}
+
+/* Unlink arbitrary given element from the list */
+void FAST_FUNC llist_unlink(llist_t **head, llist_t *elm)
+{
+	if (!elm)
+		return;
+	while (*head) {
+		if (*head == elm) {
+			*head = (*head)->link;
+			break;
+		}
+		head = &(*head)->link;
+	}
+}
+
+/* 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))
+{
+	while (elm) {
+		void *data = llist_pop(&elm);
+
+		if (freeit)
+			freeit(data);
+	}
+}
+
+/* Reverse list order. */
+llist_t* FAST_FUNC llist_rev(llist_t *list)
+{
+	llist_t *rev = NULL;
+
+	while (list) {
+		llist_t *next = list->link;
+
+		list->link = rev;
+		rev = list;
+		list = next;
+	}
+	return rev;
+}
+
+llist_t* FAST_FUNC llist_find_str(llist_t *list, const char *str)
+{
+	while (list) {
+		if (strcmp(list->data, str) == 0)
+			break;
+		list = list->link;
+	}
+	return list;
+}
diff --git a/busybox-1.19.3/libbb/login.c b/busybox-1.19.3/libbb/login.c
new file mode 100644
index 0000000..8a82c6a
--- /dev/null
+++ b/busybox-1.19.3/libbb/login.c
@@ -0,0 +1,132 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * issue.c: issue printing code
+ *
+ * Copyright (C) 2003 Bastian Blank <waldi@tuxbox.org>
+ *
+ * Optimize and correcting OCRNL by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+/* After libbb.h, since it needs sys/types.h on some systems */
+#include <sys/utsname.h>
+
+#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)
+{
+	FILE *fp;
+	int c;
+	char buf[256+1];
+	const char *outbuf;
+	time_t t;
+	struct utsname uts;
+
+	time(&t);
+	uname(&uts);
+
+	puts("\r");  /* start a new line */
+
+	fp = fopen_for_read(issue_file);
+	if (!fp)
+		return;
+	while ((c = fgetc(fp)) != EOF) {
+		outbuf = buf;
+		buf[0] = c;
+		buf[1] = '\0';
+		if (c == '\n') {
+			buf[1] = '\r';
+			buf[2] = '\0';
+		}
+		if (c == '\\' || c == '%') {
+			c = fgetc(fp);
+			switch (c) {
+			case 's':
+				outbuf = uts.sysname;
+				break;
+			case 'n':
+			case 'h':
+				outbuf = uts.nodename;
+				break;
+			case 'r':
+				outbuf = uts.release;
+				break;
+			case 'v':
+				outbuf = uts.version;
+				break;
+			case 'm':
+				outbuf = uts.machine;
+				break;
+/* The field domainname of struct utsname is Linux specific. */
+#if defined(__linux__)
+			case 'D':
+			case 'o':
+				outbuf = uts.domainname;
+				break;
+#endif
+			case 'd':
+				strftime(buf, sizeof(buf), fmtstr_d, localtime(&t));
+				break;
+			case 't':
+				strftime(buf, sizeof(buf), fmtstr_t, localtime(&t));
+				break;
+			case 'l':
+				outbuf = tty;
+				break;
+			default:
+				buf[0] = c;
+			}
+		}
+		fputs(outbuf, stdout);
+	}
+	fclose(fp);
+	fflush_all();
+}
+
+void FAST_FUNC print_login_prompt(void)
+{
+	char *hostname = safe_gethostname();
+
+	fputs(hostname, stdout);
+	fputs(LOGIN, stdout);
+	fflush_all();
+	free(hostname);
+}
+
+/* Clear dangerous stuff, set PATH */
+static const char forbid[] ALIGN1 =
+	"ENV" "\0"
+	"BASH_ENV" "\0"
+	"HOME" "\0"
+	"IFS" "\0"
+	"SHELL" "\0"
+	"LD_LIBRARY_PATH" "\0"
+	"LD_PRELOAD" "\0"
+	"LD_TRACE_LOADED_OBJECTS" "\0"
+	"LD_BIND_NOW" "\0"
+	"LD_AOUT_LIBRARY_PATH" "\0"
+	"LD_AOUT_PRELOAD" "\0"
+	"LD_NOWARN" "\0"
+	"LD_KEEPDIR" "\0";
+
+int FAST_FUNC sanitize_env_if_suid(void)
+{
+	const char *p;
+
+	if (getuid() == geteuid())
+		return 0;
+
+	p = forbid;
+	do {
+		unsetenv(p);
+		p += strlen(p) + 1;
+	} while (*p);
+	putenv((char*)bb_PATH_root_path);
+
+	return 1; /* we indeed were run by different user! */
+}
diff --git a/busybox-1.19.3/libbb/loop.c b/busybox-1.19.3/libbb/loop.c
new file mode 100644
index 0000000..b798932
--- /dev/null
+++ b/busybox-1.19.3/libbb/loop.c
@@ -0,0 +1,172 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Rob Landley <rob@landley.net>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+
+/* For 2.6, use the cleaned up header to get the 64 bit API. */
+// Commented out per Rob's request
+//# include "fix_u32.h" /* some old toolchains need __u64 for linux/loop.h */
+# include <linux/loop.h>
+typedef struct loop_info64 bb_loop_info;
+# define BB_LOOP_SET_STATUS LOOP_SET_STATUS64
+# define BB_LOOP_GET_STATUS LOOP_GET_STATUS64
+
+#else
+
+/* For 2.4 and earlier, use the 32 bit API (and don't trust the headers) */
+/* Stuff stolen from linux/loop.h for 2.4 and earlier kernels */
+# include <linux/posix_types.h>
+# define LO_NAME_SIZE        64
+# define LO_KEY_SIZE         32
+# define LOOP_SET_FD         0x4C00
+# define LOOP_CLR_FD         0x4C01
+# define BB_LOOP_SET_STATUS  0x4C02
+# define BB_LOOP_GET_STATUS  0x4C03
+typedef struct {
+	int                lo_number;
+	__kernel_dev_t     lo_device;
+	unsigned long      lo_inode;
+	__kernel_dev_t     lo_rdevice;
+	int                lo_offset;
+	int                lo_encrypt_type;
+	int                lo_encrypt_key_size;
+	int                lo_flags;
+	char               lo_file_name[LO_NAME_SIZE];
+	unsigned char      lo_encrypt_key[LO_KEY_SIZE];
+	unsigned long      lo_init[2];
+	char               reserved[4];
+} bb_loop_info;
+#endif
+
+char* FAST_FUNC query_loop(const char *device)
+{
+	int fd;
+	bb_loop_info loopinfo;
+	char *dev = NULL;
+
+	fd = open(device, O_RDONLY);
+	if (fd >= 0) {
+		if (ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo) == 0) {
+			dev = xasprintf("%"OFF_FMT"u %s", (off_t) loopinfo.lo_offset,
+					(char *)loopinfo.lo_file_name);
+		}
+		close(fd);
+	}
+
+	return dev;
+}
+
+int FAST_FUNC del_loop(const char *device)
+{
+	int fd, rc;
+
+	fd = open(device, O_RDONLY);
+	if (fd < 0)
+		return 1;
+	rc = ioctl(fd, LOOP_CLR_FD, 0);
+	close(fd);
+
+	return rc;
+}
+
+/* Returns 0 if mounted RW, 1 if mounted read-only, <0 for error.
+   *device is loop device to use, or if *device==NULL finds a loop device to
+   mount it on and sets *device to a strdup of that loop device name.  This
+   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)
+{
+	char dev[LOOP_NAMESIZE];
+	char *try;
+	bb_loop_info loopinfo;
+	struct stat statbuf;
+	int i, dfd, ffd, mode, rc = -1;
+
+	/* Open the file.  Barf if this doesn't work.  */
+	mode = O_RDWR;
+	ffd = open(file, mode);
+	if (ffd < 0) {
+		mode = O_RDONLY;
+		ffd = open(file, mode);
+		if (ffd < 0)
+			return -errno;
+	}
+
+	/* Find a loop device.  */
+	try = *device ? *device : dev;
+	/* 1048575 is a max possible minor number in Linux circa 2010 */
+	for (i = 0; rc && i < 1048576; i++) {
+		sprintf(dev, LOOP_FORMAT, i);
+
+		IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;)
+		if (stat(try, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) {
+			if (ENABLE_FEATURE_MOUNT_LOOP_CREATE
+			 && errno == ENOENT
+			 && try == dev
+			) {
+				/* Node doesn't exist, try to create it.  */
+				if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0)
+					goto try_to_open;
+			}
+			/* Ran out of block devices, return failure.  */
+			rc = -ENOENT;
+			break;
+		}
+ try_to_open:
+		/* Open the sucker and check its loopiness.  */
+		dfd = open(try, mode);
+		if (dfd < 0 && errno == EROFS) {
+			mode = O_RDONLY;
+			dfd = open(try, mode);
+		}
+		if (dfd < 0)
+			goto try_again;
+
+		rc = ioctl(dfd, BB_LOOP_GET_STATUS, &loopinfo);
+
+		/* If device is free, claim it.  */
+		if (rc && errno == ENXIO) {
+			memset(&loopinfo, 0, sizeof(loopinfo));
+			safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
+			loopinfo.lo_offset = offset;
+			/* Associate free loop device with file.  */
+			if (ioctl(dfd, LOOP_SET_FD, ffd) == 0) {
+				if (ioctl(dfd, BB_LOOP_SET_STATUS, &loopinfo) == 0)
+					rc = 0;
+				else
+					ioctl(dfd, LOOP_CLR_FD, 0);
+			}
+
+		/* 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.)
+		 */
+		} else
+		if (strcmp(file, (char *)loopinfo.lo_file_name) != 0
+		 || offset != loopinfo.lo_offset
+		) {
+			rc = -1;
+		}
+		close(dfd);
+ try_again:
+		if (*device) break;
+	}
+	close(ffd);
+	if (rc == 0) {
+		if (!*device)
+			*device = xstrdup(dev);
+		return (mode == O_RDONLY); /* 1:ro, 0:rw */
+	}
+	return rc;
+}
diff --git a/busybox-1.19.3/libbb/make_directory.c b/busybox-1.19.3/libbb/make_directory.c
new file mode 100644
index 0000000..72303e7
--- /dev/null
+++ b/busybox-1.19.3/libbb/make_directory.c
@@ -0,0 +1,129 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_mode implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Mar 5, 2003    Manuel Novoa III
+ *
+ * This is the main work function for the 'mkdir' applet.  As such, it
+ * strives to be SUSv3 compliant in it's behaviour when recursively
+ * making missing parent dirs, and in it's mode setting of the final
+ * directory 'path'.
+ *
+ * To recursively build all missing intermediate directories, make
+ * sure that (flags & FILEUTILS_RECUR) is non-zero.  Newly created
+ * intermediate directories will have at least u+wx perms.
+ *
+ * To set specific permissions on 'path', pass the appropriate 'mode'
+ * val.  Otherwise, pass -1 to get default permissions.
+ */
+
+#include "libbb.h"
+
+/* This function is used from NOFORK applets. It must not allocate anything */
+
+int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
+{
+	mode_t cur_mask;
+	mode_t org_mask;
+	const char *fail_msg;
+	char *s;
+	char c;
+	struct stat st;
+
+	/* Happens on bb_make_directory(dirname("no_slashes"),...) */
+	if (LONE_CHAR(path, '.'))
+		return 0;
+
+	org_mask = cur_mask = (mode_t)-1L;
+	s = path;
+	while (1) {
+		c = '\0';
+
+		if (flags & FILEUTILS_RECUR) {  /* Get the parent */
+			/* Bypass leading non-'/'s and then subsequent '/'s */
+			while (*s) {
+				if (*s == '/') {
+					do {
+						++s;
+					} while (*s == '/');
+					c = *s; /* Save the current char */
+					*s = '\0'; /* and replace it with nul */
+					break;
+				}
+				++s;
+			}
+		}
+
+		if (c != '\0') {
+			/* Intermediate dirs: must have wx for user */
+			if (cur_mask == (mode_t)-1L) { /* wasn't done yet? */
+				mode_t new_mask;
+				org_mask = umask(0);
+				cur_mask = 0;
+				/* Clear u=wx in umask - this ensures
+				 * they won't be cleared on mkdir */
+				new_mask = (org_mask & ~(mode_t)0300);
+				//bb_error_msg("org_mask:%o cur_mask:%o", org_mask, new_mask);
+				if (new_mask != cur_mask) {
+					cur_mask = new_mask;
+					umask(new_mask);
+				}
+			}
+		} else {
+			/* Last component: uses original umask */
+			//bb_error_msg("1 org_mask:%o", org_mask);
+			if (org_mask != cur_mask) {
+				cur_mask = org_mask;
+				umask(org_mask);
+			}
+		}
+
+		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 && errno != EISDIR)
+			 || !(flags & FILEUTILS_RECUR)
+			 || ((stat(path, &st) < 0) || !S_ISDIR(st.st_mode))
+			) {
+				fail_msg = "create";
+				break;
+			}
+			/* Since the directory exists, don't attempt to change
+			 * permissions if it was the full target.  Note that
+			 * this is not an error condition. */
+			if (!c) {
+				goto ret0;
+			}
+		}
+
+		if (!c) {
+			/* Done.  If necessary, update perms on the newly
+			 * created directory.  Failure to update here _is_
+			 * an error. */
+			if ((mode != -1) && (chmod(path, mode) < 0)) {
+				fail_msg = "set permissions of";
+				break;
+			}
+			goto ret0;
+		}
+
+		/* Remove any inserted nul from the path (recursive mode) */
+		*s = c;
+	} /* while (1) */
+
+	bb_perror_msg("can't %s directory '%s'", fail_msg, path);
+	flags = -1;
+	goto ret;
+ ret0:
+	flags = 0;
+ ret:
+	//bb_error_msg("2 org_mask:%o", org_mask);
+	if (org_mask != cur_mask)
+		umask(org_mask);
+	return flags;
+}
diff --git a/busybox-1.19.3/libbb/makedev.c b/busybox-1.19.3/libbb/makedev.c
new file mode 100644
index 0000000..06c4039
--- /dev/null
+++ b/busybox-1.19.3/libbb/makedev.c
@@ -0,0 +1,31 @@
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2006 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* We do not include libbb.h - #define makedev() is there! */
+#include "platform.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. */
+/* uclibc people please check - do we need "&& !__UCLIBC__" above? */
+
+/* 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);
+}
+#endif
diff --git a/busybox-1.19.3/libbb/match_fstype.c b/busybox-1.19.3/libbb/match_fstype.c
new file mode 100644
index 0000000..32c3d7f
--- /dev/null
+++ b/busybox-1.19.3/libbb/match_fstype.c
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Match fstypes for use in mount unmount
+ * We accept notmpfs,nfs but not notmpfs,nonfs
+ * This allows us to match fstypes that start with no like so
+ *   mount -at ,noddy
+ *
+ * Returns 1 for a match, otherwise 0
+ *
+ * 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;
+	int len;
+
+	if (!t_fstype)
+		return match;
+
+	if (t_fstype[0] == 'n' && t_fstype[1] == 'o') {
+		match--;
+		t_fstype += 2;
+	}
+
+	len = strlen(mt->mnt_type);
+	while (1) {
+		if (strncmp(mt->mnt_type, t_fstype, len) == 0
+		 && (t_fstype[len] == '\0' || t_fstype[len] == ',')
+		) {
+			return match;
+		}
+		t_fstype = strchr(t_fstype, ',');
+		if (!t_fstype)
+			break;
+		t_fstype++;
+	}
+
+	return !match;
+}
+
+#endif /* HAVE_MNTENT_H */
diff --git a/busybox-1.19.3/libbb/messages.c b/busybox-1.19.3/libbb/messages.c
new file mode 100644
index 0000000..fad82c9
--- /dev/null
+++ b/busybox-1.19.3/libbb/messages.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* allow default system PATH to be extended via CFLAGS */
+#ifndef BB_ADDITIONAL_PATH
+#define BB_ADDITIONAL_PATH ""
+#endif
+
+/* allow version to be extended, via CFLAGS */
+#ifndef BB_EXTRA_VERSION
+#define BB_EXTRA_VERSION BB_BT
+#endif
+
+#define BANNER "BusyBox v" BB_VER " (" BB_EXTRA_VERSION ")"
+
+const char bb_banner[] ALIGN1 = BANNER;
+
+
+const char bb_msg_memory_exhausted[] ALIGN1 = "out of memory";
+const char bb_msg_invalid_date[] ALIGN1 = "invalid date '%s'";
+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?)";
+const char bb_msg_you_must_be_root[] ALIGN1 = "you must be root";
+const char bb_msg_requires_arg[] ALIGN1 = "%s requires an argument";
+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_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF";
+
+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,
+ * but I want to save a few bytes here. Check libbb.h before changing! */
+const char bb_PATH_root_path[] ALIGN1 =
+	"PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH;
+
+
+const int const_int_1 = 1;
+/* explicitly = 0, otherwise gcc may make it a common variable
+ * and it will end up in bss */
+const int const_int_0 = 0;
+
+#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
+	_PATH_WTMP;
+# elif defined WTMP_FILE
+	WTMP_FILE;
+# else
+#  error unknown path to wtmp file
+# endif
+#endif
+
+/* We use it for "global" data via *(struct global*)&bb_common_bufsiz1.
+ * Since gcc insists on aligning struct global's members, it would be a pity
+ * (and an alignment fault on some CPUs) to mess it up. */
+char bb_common_bufsiz1[COMMON_BUFSIZE] ALIGNED(sizeof(long long));
diff --git a/busybox-1.19.3/libbb/mode_string.c b/busybox-1.19.3/libbb/mode_string.c
new file mode 100644
index 0000000..f1afe7d
--- /dev/null
+++ b/busybox-1.19.3/libbb/mode_string.c
@@ -0,0 +1,128 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mode_string implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Aug 13, 2003
+ * Fix a bug reported by junkio@cox.net involving the mode_chars index.
+ */
+
+
+#include <assert.h>
+#include <sys/stat.h>
+
+#include "libbb.h"
+
+#if ( S_ISUID != 04000 ) || ( S_ISGID != 02000 ) || ( S_ISVTX != 01000 ) \
+ || ( S_IRUSR != 00400 ) || ( S_IWUSR != 00200 ) || ( S_IXUSR != 00100 ) \
+ || ( S_IRGRP != 00040 ) || ( S_IWGRP != 00020 ) || ( S_IXGRP != 00010 ) \
+ || ( S_IROTH != 00004 ) || ( S_IWOTH != 00002 ) || ( S_IXOTH != 00001 )
+#error permission bitflag value assumption(s) violated!
+#endif
+
+#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
+ || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \
+ || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
+ || ( S_IFIFO != 0010000 )
+#warning mode type bitflag value assumption(s) violated! falling back to larger version
+
+#if (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX) == 07777
+#undef mode_t
+#define mode_t unsigned short
+#endif
+
+static const mode_t mode_flags[] = {
+	S_IRUSR, S_IWUSR, S_IXUSR, S_ISUID,
+	S_IRGRP, S_IWGRP, S_IXGRP, S_ISGID,
+	S_IROTH, S_IWOTH, S_IXOTH, S_ISVTX
+};
+
+/* The static const char arrays below are duplicated for the two cases
+ * because moving them ahead of the mode_flags declaration cause a text
+ * size increase with the gcc version I'm using. */
+
+/* The previous version used "0pcCd?bB-?l?s???".  However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux.  So I removed them. */
+static const char type_chars[16] ALIGN1 = "?pc?d?b?-?l?s???";
+/***************************************** 0123456789abcdef */
+static const char mode_chars[7] ALIGN1 = "rwxSTst";
+
+const char* FAST_FUNC bb_mode_string(mode_t mode)
+{
+	static char buf[12];
+	char *p = buf;
+
+	int i, j, k;
+
+	*p = type_chars[ (mode >> 12) & 0xf ];
+	i = 0;
+	do {
+		j = k = 0;
+		do {
+			*++p = '-';
+			if (mode & mode_flags[i+j]) {
+				*p = mode_chars[j];
+				k = j;
+			}
+		} while (++j < 3);
+		if (mode & mode_flags[i+j]) {
+			*p = mode_chars[3 + (k & 2) + ((i&8) >> 3)];
+		}
+		i += 4;
+	} while (i < 12);
+
+	/* Note: We don't bother with nul termination because bss initialization
+	 * should have taken care of that for us.  If the user scribbled in buf
+	 * memory, they deserve whatever happens.  But we'll at least assert. */
+	assert(buf[10] == 0);
+
+	return buf;
+}
+
+#else
+
+/* The previous version used "0pcCd?bB-?l?s???".  However, the '0', 'C',
+ * and 'B' types don't appear to be available on linux.  So I removed them. */
+static const char type_chars[16] = "?pc?d?b?-?l?s???";
+/********************************** 0123456789abcdef */
+static const char mode_chars[7] = "rwxSTst";
+
+const char* FAST_FUNC bb_mode_string(mode_t mode)
+{
+	static char buf[12];
+	char *p = buf;
+
+	int i, j, k, m;
+
+	*p = type_chars[ (mode >> 12) & 0xf ];
+	i = 0;
+	m = 0400;
+	do {
+		j = k = 0;
+		do {
+			*++p = '-';
+			if (mode & m) {
+				*p = mode_chars[j];
+				k = j;
+			}
+			m >>= 1;
+		} while (++j < 3);
+		++i;
+		if (mode & (010000 >> i)) {
+			*p = mode_chars[3 + (k & 2) + (i == 3)];
+		}
+	} while (i < 3);
+
+	/* Note: We don't bother with nul termination because bss initialization
+	 * should have taken care of that for us.  If the user scribbled in buf
+	 * memory, they deserve whatever happens.  But we'll at least assert. */
+	assert(buf[10] == 0);
+
+	return buf;
+}
+
+#endif
diff --git a/busybox-1.19.3/libbb/mtab.c b/busybox-1.19.3/libbb/mtab.c
new file mode 100644
index 0000000..22bff64
--- /dev/null
+++ b/busybox-1.19.3/libbb/mtab.c
@@ -0,0 +1,55 @@
+/* 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 source tree.
+ */
+
+#include <mntent.h>
+#include "libbb.h"
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+void FAST_FUNC erase_mtab(const char *name)
+{
+	struct mntent *entries;
+	int i, count;
+	FILE *mountTable;
+	struct mntent *m;
+
+	mountTable = setmntent(bb_path_mtab_file, "r");
+	/* Bummer. Fall back on trying the /proc filesystem */
+	if (!mountTable) mountTable = setmntent("/proc/mounts", "r");
+	if (!mountTable) {
+		bb_perror_msg(bb_path_mtab_file);
+		return;
+	}
+
+	entries = NULL;
+	count = 0;
+	while ((m = getmntent(mountTable)) != 0) {
+		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);
+
+//TODO: make update atomic
+	mountTable = setmntent(bb_path_mtab_file, "w");
+	if (mountTable) {
+		for (i = 0; i < count; i++) {
+			if (strcmp(entries[i].mnt_fsname, name) != 0
+			 && strcmp(entries[i].mnt_dir, name) != 0)
+				addmntent(mountTable, &entries[i]);
+		}
+		endmntent(mountTable);
+	} else if (errno != EROFS)
+		bb_perror_msg(bb_path_mtab_file);
+}
+#endif
diff --git a/busybox-1.19.3/libbb/obscure.c b/busybox-1.19.3/libbb/obscure.c
new file mode 100644
index 0000000..9ecc1f6
--- /dev/null
+++ b/busybox-1.19.3/libbb/obscure.c
@@ -0,0 +1,184 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini weak password checker implementation for busybox
+ *
+ * Copyright (C) 2006 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*	A good password:
+	1)	should contain at least six characters (man passwd);
+	2)	empty passwords are not permitted;
+	3)	should contain a mix of four different types of characters
+		upper case letters,
+		lower case letters,
+		numbers,
+		special characters such as !@#$%^&*,;".
+	This password types should not  be permitted:
+	a)	pure numbers: birthdates, social security number, license plate, phone numbers;
+	b)	words and all letters only passwords (uppercase, lowercase or mixed)
+		as palindromes, consecutive or repetitive letters
+		or adjacent letters on your keyboard;
+	c)	username, real name, company name or (e-mail?) address
+		in any form (as-is, reversed, capitalized, doubled, etc.).
+		(we can check only against username, gecos and hostname)
+	d)	common and obvious letter-number replacements
+		(e.g. replace the letter O with number 0)
+		such as "M1cr0$0ft" or "P@ssw0rd" (CAVEAT: we cannot check for them
+		without the use of a dictionary).
+
+	For each missing type of characters an increase of password length is
+	requested.
+
+	If user is root we warn only.
+
+	CAVEAT: some older versions of crypt() truncates passwords to 8 chars,
+	so that aaaaaaaa1Q$ is equal to aaaaaaaa making it possible to fool
+	some of our checks. We don't test for this special case as newer versions
+	of crypt do not truncate passwords.
+*/
+
+#include "libbb.h"
+
+static int string_checker_helper(const char *p1, const char *p2) __attribute__ ((__pure__));
+
+static int string_checker_helper(const char *p1, const char *p2)
+{
+	/* as sub-string */
+	if (strcasestr(p2, p1) != NULL
+	/* invert in case haystack is shorter than needle */
+	 || 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, i;
+	/* check string */
+	int ret = string_checker_helper(p1, p2);
+	/* make our own copy */
+	char *p = xstrdup(p1);
+
+	/* reverse string */
+	i = size = strlen(p1);
+	while (--i >= 0) {
+		*p++ = p1[i];
+	}
+	p -= size; /* restore pointer */
+
+	/* check reversed string */
+	ret |= string_checker_helper(p, p2);
+
+	/* clean up */
+	memset(p, 0, size);
+	free(p);
+
+	return ret;
+}
+
+#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)
+{
+	unsigned length;
+	unsigned size;
+	unsigned mixed;
+	unsigned c;
+	unsigned i;
+	const char *p;
+	char *hostname;
+
+	/* size */
+	if (!new_p || (length = strlen(new_p)) < CONFIG_PASSWORD_MINLEN)
+		return "too short";
+
+	/* no username as-is, as sub-string, reversed, capitalized, doubled */
+	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[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);
+	free(hostname);
+	if (i)
+		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;
+		} else if (isupper(new_p[i])) { /* A-Z */
+			mixed |= UPPERCASE;
+		} else if (isdigit(new_p[i])) { /* 0-9 */
+			mixed |= NUMBERS;
+		} else  {                       /* special characters */
+			mixed |= SPECIAL;
+		}
+		/* Count i'th char */
+		c = 0;
+		p = new_p;
+		while (1) {
+			p = strchr(p, new_p[i]);
+			if (p == NULL) {
+				break;
+			}
+			c++;
+			p++;
+			if (!*p) {
+				break;
+			}
+		}
+		/* More than 50% similar characters ? */
+		if (c*2 >= length) {
+			return "too many similar characters";
+		}
+	}
+
+	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]) {
+		/* check vs. old password */
+		if (string_checker(new_p, old_p)) {
+			return "similar to old password";
+		}
+	}
+
+	return NULL;
+}
+
+int FAST_FUNC obscure(const char *old, const char *newval, const struct passwd *pw)
+{
+	const char *msg;
+
+	msg = obscure_msg(old, newval, pw);
+	if (msg) {
+		printf("Bad password: %s\n", msg);
+		return 1;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/libbb/parse_config.c b/busybox-1.19.3/libbb/parse_config.c
new file mode 100644
index 0000000..cf5ba4d
--- /dev/null
+++ b/busybox-1.19.3/libbb/parse_config.c
@@ -0,0 +1,243 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * config file parser helper
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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
+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+";
+	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);
+		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);
+		argv++;
+	}
+	return EXIT_SUCCESS;
+}
+#endif
+
+parser_t* FAST_FUNC config_open2(const char *filename, FILE* FAST_FUNC (*fopen_func)(const char *path))
+{
+	FILE* fp;
+	parser_t *parser;
+
+	fp = fopen_func(filename);
+	if (!fp)
+		return NULL;
+	parser = xzalloc(sizeof(*parser));
+	parser->fp = fp;
+	return parser;
+}
+
+parser_t* FAST_FUNC config_open(const char *filename)
+{
+	return config_open2(filename, fopen_or_warn_stdin);
+}
+
+void FAST_FUNC config_close(parser_t *parser)
+{
+	if (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.
+   Handle continuation character. Advance lineno for each physical line.
+   Discard everything past comment character.
+2. if PARSE_TRIM is set (default), remove leading and trailing delimiters.
+3. If resulting line is empty goto 1.
+4. Look for first delimiter. If !PARSE_COLLAPSE or !PARSE_TRIM is set then
+   remember the token as empty.
+5. Else (default) if number of seen tokens is equal to max number of tokens
+   (token is the last one) and PARSE_GREEDY is set then the remainder
+   of the line is the last token.
+   Else (token is not last or PARSE_GREEDY is not set) just replace
+   first delimiter with '\0' thus delimiting the token.
+6. Advance line pointer past the end of token. If number of seen tokens
+   is less than required number of tokens then goto 4.
+7. Check the number of seen tokens is not less the min number of tokens.
+   Complain or die otherwise depending on PARSE_MIN_DIE.
+8. Return the number of seen tokens.
+
+mintokens > 0 make config_read() print error message if less than mintokens
+(but more than 0) are found. Empty lines are always skipped (not warned about).
+*/
+#undef config_read
+int FAST_FUNC config_read(parser_t *parser, char **tokens, unsigned flags, const char *delims)
+{
+	char *line;
+	int ntokens, mintokens;
+	int t;
+
+	if (!parser)
+		return 0;
+
+	ntokens = (uint8_t)flags;
+	mintokens = (uint8_t)(flags >> 8);
+
+ again:
+	memset(tokens, 0, sizeof(tokens[0]) * ntokens);
+
+	/* Read one line (handling continuations with backslash) */
+	if (get_line_with_continuation(parser) < 0)
+		return 0;
+
+	line = parser->line;
+
+	/* Skip token in the start of line? */
+	if (flags & PARSE_TRIM)
+		line += strspn(line, delims + 1);
+
+	if (line[0] == '\0' || line[0] == delims[0])
+		goto again;
+
+	if (flags & PARSE_KEEP_COPY) {
+		free(parser->data);
+		parser->data = xstrdup(line);
+	}
+
+	/* Tokenize the line */
+	t = 0;
+	do {
+		/* Pin token */
+		tokens[t] = line;
+
+		/* Combine remaining arguments? */
+		if ((t != (ntokens-1)) || !(flags & PARSE_GREEDY)) {
+			/* Vanilla token, find next delimiter */
+			line += strcspn(line, delims[0] ? delims : delims + 1);
+		} else {
+			/* Combining, find comment char if any */
+			line = strchrnul(line, delims[0]);
+
+			/* Trim any extra delimiters from the end */
+			if (flags & PARSE_TRIM) {
+				while (strchr(delims + 1, line[-1]) != NULL)
+					line--;
+			}
+		}
+
+		/* Token not terminated? */
+		if (*line == delims[0])
+			*line = '\0';
+		else if (*line != '\0')
+			*line++ = '\0';
+
+#if 0 /* unused so far */
+		if (flags & PARSE_ESCAPE) {
+			strcpy_and_process_escape_sequences(tokens[t], tokens[t]);
+		}
+#endif
+		/* Skip possible delimiters */
+		if (flags & PARSE_COLLAPSE)
+			line += strspn(line, delims + 1);
+
+		t++;
+	} while (*line && *line != delims[0] && t < ntokens);
+
+	if (t < mintokens) {
+		bb_error_msg("bad line %u: %d tokens found, %d needed",
+				parser->lineno, t, mintokens);
+		if (flags & PARSE_MIN_DIE)
+			xfunc_die();
+		goto again;
+	}
+
+	return t;
+}
diff --git a/busybox-1.19.3/libbb/parse_mode.c b/busybox-1.19.3/libbb/parse_mode.c
new file mode 100644
index 0000000..5a4e1c5
--- /dev/null
+++ b/busybox-1.19.3/libbb/parse_mode.c
@@ -0,0 +1,150 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * parse_mode implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
+
+#include "libbb.h"
+
+/* This function is used from NOFORK applets. It must not allocate anything */
+
+#define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+
+int FAST_FUNC bb_parse_mode(const char *s, mode_t *current_mode)
+{
+	static const mode_t who_mask[] = {
+		S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
+		S_ISUID | S_IRWXU,           /* u */
+		S_ISGID | S_IRWXG,           /* g */
+		S_IRWXO                      /* o */
+	};
+	static const mode_t perm_mask[] = {
+		S_IRUSR | S_IRGRP | S_IROTH, /* r */
+		S_IWUSR | S_IWGRP | S_IWOTH, /* w */
+		S_IXUSR | S_IXGRP | S_IXOTH, /* x */
+		S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
+		S_ISUID | S_ISGID,           /* s */
+		S_ISVTX                      /* t */
+	};
+	static const char who_chars[] ALIGN1 = "augo";
+	static const char perm_chars[] ALIGN1 = "rwxXst";
+
+	const char *p;
+	mode_t wholist;
+	mode_t permlist;
+	mode_t new_mode;
+	char op;
+
+	if ((unsigned char)(*s - '0') < 8) {
+		unsigned long tmp;
+		char *e;
+
+		tmp = strtoul(s, &e, 8);
+		if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
+			return 0;
+		}
+		*current_mode = tmp;
+		return 1;
+	}
+
+	new_mode = *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. */
+			++s;
+			continue;
+		}
+
+		/* Get a wholist. */
+		wholist = 0;
+ WHO_LIST:
+		p = who_chars;
+		do {
+			if (*p == *s) {
+				wholist |= who_mask[(int)(p-who_chars)];
+				if (!*++s) {
+					return 0;
+				}
+				goto WHO_LIST;
+			}
+		} while (*++p);
+
+		do {    /* Process action list. */
+			if ((*s != '+') && (*s != '-')) {
+				if (*s != '=') {
+					return 0;
+				}
+				/* Since op is '=', clear all bits corresponding to the
+				 * wholist, or all file bits if wholist is empty. */
+				permlist = ~FILEMODEBITS;
+				if (wholist) {
+					permlist = ~wholist;
+				}
+				new_mode &= permlist;
+			}
+			op = *s++;
+
+			/* Check for permcopy. */
+			p = who_chars + 1;  /* Skip 'a' entry. */
+			do {
+				if (*p == *s) {
+					int i = 0;
+					permlist = who_mask[(int)(p-who_chars)]
+							 & (S_IRWXU | S_IRWXG | S_IRWXO)
+							 & new_mode;
+					do {
+						if (permlist & perm_mask[i]) {
+							permlist |= perm_mask[i];
+						}
+					} while (++i < 3);
+					++s;
+					goto GOT_ACTION;
+				}
+			} while (*++p);
+
+			/* It was not a permcopy, so get a permlist. */
+			permlist = 0;
+ PERM_LIST:
+			p = perm_chars;
+			do {
+				if (*p == *s) {
+					if ((*p != 'X')
+					 || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
+					) {
+						permlist |= perm_mask[(int)(p-perm_chars)];
+					}
+					if (!*++s) {
+						break;
+					}
+					goto PERM_LIST;
+				}
+			} while (*++p);
+ GOT_ACTION:
+			if (permlist) { /* The permlist was nonempty. */
+				mode_t tmp = wholist;
+				if (!wholist) {
+					mode_t u_mask = umask(0);
+					umask(u_mask);
+					tmp = ~u_mask;
+				}
+				permlist &= tmp;
+				if (op == '-') {
+					new_mode &= ~permlist;
+				} else {
+					new_mode |= permlist;
+				}
+			}
+		} while (*s && (*s != ','));
+	}
+
+	*current_mode = new_mode;
+	return 1;
+}
diff --git a/busybox-1.19.3/libbb/perror_msg.c b/busybox-1.19.3/libbb/perror_msg.c
new file mode 100644
index 0000000..fa1f0d3
--- /dev/null
+++ b/busybox-1.19.3/libbb/perror_msg.c
@@ -0,0 +1,40 @@
+/* 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 source tree.
+ */
+#include "libbb.h"
+
+void FAST_FUNC bb_perror_msg(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	/* Guard against "<error message>: Success" */
+	bb_verror_msg(s, p, errno ? strerror(errno) : NULL);
+	va_end(p);
+}
+
+void FAST_FUNC bb_perror_msg_and_die(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	/* Guard against "<error message>: Success" */
+	bb_verror_msg(s, p, errno ? strerror(errno) : NULL);
+	va_end(p);
+	xfunc_die();
+}
+
+void FAST_FUNC bb_simple_perror_msg(const char *s)
+{
+	bb_perror_msg("%s", s);
+}
+
+void FAST_FUNC bb_simple_perror_msg_and_die(const char *s)
+{
+	bb_perror_msg_and_die("%s", s);
+}
diff --git a/busybox-1.19.3/libbb/perror_nomsg.c b/busybox-1.19.3/libbb/perror_nomsg.c
new file mode 100644
index 0000000..a2a11cc
--- /dev/null
+++ b/busybox-1.19.3/libbb/perror_nomsg.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_perror_nomsg implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* gcc warns about a null format string, therefore we provide
+ * modified definition without "attribute (format)"
+ * instead of including libbb.h */
+//#include "libbb.h"
+#include "platform.h"
+extern void bb_perror_msg(const char *s, ...) FAST_FUNC;
+
+/* suppress gcc "no previous prototype" warning */
+void FAST_FUNC bb_perror_nomsg(void);
+void FAST_FUNC bb_perror_nomsg(void)
+{
+	bb_perror_msg(0);
+}
diff --git a/busybox-1.19.3/libbb/perror_nomsg_and_die.c b/busybox-1.19.3/libbb/perror_nomsg_and_die.c
new file mode 100644
index 0000000..543ff51
--- /dev/null
+++ b/busybox-1.19.3/libbb/perror_nomsg_and_die.c
@@ -0,0 +1,22 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_perror_nomsg_and_die implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* gcc warns about a null format string, therefore we provide
+ * modified definition without "attribute (format)"
+ * instead of including libbb.h */
+//#include "libbb.h"
+#include "platform.h"
+extern void bb_perror_msg_and_die(const char *s, ...) FAST_FUNC;
+
+/* suppress gcc "no previous prototype" warning */
+void FAST_FUNC bb_perror_nomsg_and_die(void);
+void FAST_FUNC bb_perror_nomsg_and_die(void)
+{
+	bb_perror_msg_and_die(0);
+}
diff --git a/busybox-1.19.3/libbb/pidfile.c b/busybox-1.19.3/libbb/pidfile.c
new file mode 100644
index 0000000..a48dfc3
--- /dev/null
+++ b/busybox-1.19.3/libbb/pidfile.c
@@ -0,0 +1,40 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pid file routines
+ *
+ * Copyright (C) 2007 by Stephane Billiart <stephane.billiart@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Override ENABLE_FEATURE_PIDFILE */
+#define WANT_PIDFILE 1
+#include "libbb.h"
+
+smallint wrote_pidfile;
+
+void FAST_FUNC write_pidfile(const char *path)
+{
+	int pid_fd;
+	char *end;
+	char buf[sizeof(int)*3 + 2];
+	struct stat sb;
+
+	if (!path)
+		return;
+	/* we will overwrite stale pidfile */
+	pid_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+	if (pid_fd < 0)
+		return;
+
+	/* path can be "/dev/null"! Test for such cases */
+	wrote_pidfile = (fstat(pid_fd, &sb) == 0) && S_ISREG(sb.st_mode);
+
+	if (wrote_pidfile) {
+		/* few bytes larger, but doesn't use stdio */
+		end = utoa_to_buf(getpid(), buf, sizeof(buf));
+		*end = '\n';
+		full_write(pid_fd, buf, end - buf + 1);
+	}
+	close(pid_fd);
+}
diff --git a/busybox-1.19.3/libbb/platform.c b/busybox-1.19.3/libbb/platform.c
new file mode 100644
index 0000000..2bf34f5
--- /dev/null
+++ b/busybox-1.19.3/libbb/platform.c
@@ -0,0 +1,176 @@
+/*
+ * Replacements for common but usually nonstandard functions that aren't
+ * supplied by all platforms.
+ *
+ * Copyright (C) 2009 by Dan Fandrich <dan@coneharvesters.com>, et. al.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+#ifndef HAVE_STRCHRNUL
+char* FAST_FUNC strchrnul(const char *s, int c)
+{
+	while (*s != '\0' && *s != c)
+		s++;
+	return (char*)s;
+}
+#endif
+
+#ifndef HAVE_VASPRINTF
+int FAST_FUNC vasprintf(char **string_ptr, const char *format, va_list p)
+{
+	int r;
+	va_list p2;
+	char buf[128];
+
+	va_copy(p2, p);
+	r = vsnprintf(buf, 128, format, p);
+	va_end(p);
+
+	if (r < 128) {
+		va_end(p2);
+		*string_ptr = xstrdup(buf);
+		return r;
+	}
+
+	*string_ptr = xmalloc(r+1);
+	r = vsnprintf(*string_ptr, r+1, format, p2);
+	va_end(p2);
+
+	return r;
+}
+#endif
+
+#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;
+	char *string_ptr;
+
+	va_start(p, format);
+	r = vasprintf(&string_ptr, format, p);
+	va_end(p);
+	if (r >= 0) {
+		r = full_write(fd, string_ptr, r);
+		free(string_ptr);
+	}
+	return r;
+}
+#endif
+
+#ifndef HAVE_MEMRCHR
+/* Copyright (C) 2005 Free Software Foundation, Inc.
+ * memrchr() is a GNU function that might not be available everywhere.
+ * It's basically the inverse of memchr() - search backwards in a
+ * memory block for a particular character.
+ */
+void* FAST_FUNC memrchr(const void *s, int c, size_t n)
+{
+	const char *start = s, *end = s;
+
+	end += n - 1;
+
+	while (end >= start) {
+		if (*end == (char)c)
+			return (void *) end;
+		end--;
+	}
+
+	return NULL;
+}
+#endif
+
+#ifndef HAVE_MKDTEMP
+/* This is now actually part of POSIX.1, but was only added in 2008 */
+char* FAST_FUNC mkdtemp(char *template)
+{
+	if (mktemp(template) == NULL || mkdir(template, 0700) != 0)
+		return NULL;
+	return template;
+}
+#endif
+
+#ifndef HAVE_STRCASESTR
+/* Copyright (c) 1999, 2000 The ht://Dig Group */
+char* FAST_FUNC strcasestr(const char *s, const char *pattern)
+{
+	int length = strlen(pattern);
+
+	while (*s) {
+		if (strncasecmp(s, pattern, length) == 0)
+			return (char *)s;
+		s++;
+	}
+	return 0;
+}
+#endif
+
+#ifndef HAVE_STRSEP
+/* Copyright (C) 2004 Free Software Foundation, Inc. */
+char* FAST_FUNC strsep(char **stringp, const char *delim)
+{
+	char *start = *stringp;
+	char *ptr;
+
+	if (!start)
+		return NULL;
+
+	if (!*delim)
+		ptr = start + strlen(start);
+	else {
+		ptr = strpbrk(start, delim);
+		if (!ptr) {
+			*stringp = NULL;
+			return start;
+		}
+	}
+
+	*ptr = '\0';
+	*stringp = ptr + 1;
+
+	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
diff --git a/busybox-1.19.3/libbb/print_flags.c b/busybox-1.19.3/libbb/print_flags.c
new file mode 100644
index 0000000..eaec731
--- /dev/null
+++ b/busybox-1.19.3/libbb/print_flags.c
@@ -0,0 +1,31 @@
+/* vi: set sw=4 ts=4: */
+/* Print string that matches bit masked flags
+ *
+ * Copyright (C) 2008 Natanael Copa <natanael.copa@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+/* returns a set with the flags not printed */
+int FAST_FUNC print_flags_separated(const int *masks, const char *labels, int flags, const char *separator)
+{
+	const char *need_separator = NULL;
+	while (*labels) {
+		if (flags & *masks) {
+			printf("%s%s",
+				need_separator ? need_separator : "",
+				labels);
+			need_separator = separator;
+			flags &= ~ *masks;
+		}
+		masks++;
+		labels += strlen(labels) + 1;
+	}
+	return flags;
+}
+
+int FAST_FUNC print_flags(const masks_labels_t *ml, int flags)
+{
+	return print_flags_separated(ml->masks, ml->labels, flags, NULL);
+}
diff --git a/busybox-1.19.3/libbb/printable.c b/busybox-1.19.3/libbb/printable.c
new file mode 100644
index 0000000..f6ada49
--- /dev/null
+++ b/busybox-1.19.3/libbb/printable.c
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+void FAST_FUNC fputc_printable(int ch, FILE *file)
+{
+	if ((ch & (0x80 + PRINTABLE_META)) == (0x80 + PRINTABLE_META)) {
+		fputs("M-", file);
+		ch &= 0x7f;
+	}
+	ch = (unsigned char) ch;
+	if (ch == 0x9b) {
+		/* VT100's CSI, aka Meta-ESC, is not printable on vt-100 */
+		ch = '{';
+		goto print_caret;
+	}
+	if (ch < ' ') {
+		ch += '@';
+		goto print_caret;
+	}
+	if (ch == 0x7f) {
+		ch = '?';
+ print_caret:
+		fputc('^', file);
+	}
+	fputc(ch, file);
+}
diff --git a/busybox-1.19.3/libbb/printable_string.c b/busybox-1.19.3/libbb/printable_string.c
new file mode 100644
index 0000000..a316f60
--- /dev/null
+++ b/busybox-1.19.3/libbb/printable_string.c
@@ -0,0 +1,65 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Unicode support routines.
+ *
+ * Copyright (C) 2010 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "unicode.h"
+
+const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str)
+{
+	static char *saved[4];
+	static unsigned cur_saved; /* = 0 */
+
+	char *dst;
+	const char *s;
+
+	s = str;
+	while (1) {
+		unsigned char c = *s;
+		if (c == '\0') {
+			/* 99+% of inputs do not need conversion */
+			if (stats) {
+				stats->byte_count = (s - str);
+				stats->unicode_count = (s - str);
+				stats->unicode_width = (s - str);
+			}
+			return str;
+		}
+		if (c < ' ')
+			break;
+		if (c >= 0x7f)
+			break;
+		s++;
+	}
+
+#if ENABLE_UNICODE_SUPPORT
+	dst = unicode_conv_to_printable(stats, str);
+#else
+	{
+		char *d = dst = xstrdup(str);
+		while (1) {
+			unsigned char c = *d;
+			if (c == '\0')
+				break;
+			if (c < ' ' || c >= 0x7f)
+				*d = '?';
+			d++;
+		}
+		if (stats) {
+			stats->byte_count = (d - dst);
+			stats->unicode_count = (d - dst);
+			stats->unicode_width = (d - dst);
+		}
+	}
+#endif
+
+	free(saved[cur_saved]);
+	saved[cur_saved] = dst;
+	cur_saved = (cur_saved + 1) & (ARRAY_SIZE(saved)-1);
+
+	return dst;
+}
diff --git a/busybox-1.19.3/libbb/process_escape_sequence.c b/busybox-1.19.3/libbb/process_escape_sequence.c
new file mode 100644
index 0000000..346ecfa
--- /dev/null
+++ b/busybox-1.19.3/libbb/process_escape_sequence.c
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) Manuel Novoa III <mjn3@codepoet.org>
+ * and Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+#define WANT_HEX_ESCAPES 1
+
+/* Usual "this only works for ascii compatible encodings" disclaimer. */
+#undef _tolower
+#define _tolower(X) ((X)|((char) 0x20))
+
+char FAST_FUNC bb_process_escape_sequence(const char **ptr)
+{
+	const char *q;
+	unsigned num_digits;
+	unsigned n;
+	unsigned base;
+
+	num_digits = n = 0;
+	base = 8;
+	q = *ptr;
+
+	if (WANT_HEX_ESCAPES && *q == 'x') {
+		++q;
+		base = 16;
+		++num_digits;
+	}
+
+	/* 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 {
+		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) {
+			if (WANT_HEX_ESCAPES && base == 16) {
+				--num_digits;
+				if (num_digits == 0) {
+					/* \x<bad_char>: return '\',
+					 * leave ptr pointing to x */
+					return '\\';
+				}
+			}
+			break;
+		}
+
+		r = n * base + d;
+		if (r > UCHAR_MAX) {
+			break;
+		}
+
+		n = r;
+		++q;
+	} while (++num_digits < 3);
+
+	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 != '\0');
+		/* p points to found escape char or NUL,
+		 * 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++;
+	}
+}
diff --git a/busybox-1.19.3/libbb/procps.c b/busybox-1.19.3/libbb/procps.c
new file mode 100644
index 0000000..e15ddd1
--- /dev/null
+++ b/busybox-1.19.3/libbb/procps.c
@@ -0,0 +1,655 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * 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 source tree.
+ */
+
+#include "libbb.h"
+
+
+typedef struct id_to_name_map_t {
+	uid_t id;
+	char name[USERNAME_MAX_SIZE];
+} id_to_name_map_t;
+
+typedef struct cache_t {
+	id_to_name_map_t *cache;
+	int size;
+} cache_t;
+
+static cache_t username, groupname;
+
+static void clear_cache(cache_t *cp)
+{
+	free(cp->cache);
+	cp->cache = NULL;
+	cp->size = 0;
+}
+void FAST_FUNC clear_username_cache(void)
+{
+	clear_cache(&username);
+	clear_cache(&groupname);
+}
+
+#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, uid_t id)
+{
+	int i;
+	for (i = 0; i < cp->size; i++)
+		if (cp->cache[i].id == id)
+			return i;
+	i = cp->size++;
+	cp->cache = xrealloc_vector(cp->cache, 2, i);
+	cp->cache[i++].id = id;
+	return -i;
+}
+#endif
+
+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++)
+		if (cp->cache[i].id == id)
+			return cp->cache[i].name;
+	i = cp->size++;
+	cp->cache = xrealloc_vector(cp->cache, 2, i);
+	cp->cache[i].id = id;
+	/* Never fails. Generates numeric string if name isn't found */
+	safe_strncpy(cp->cache[i].name, x2x_utoa(id), sizeof(cp->cache[i].name));
+	return cp->cache[i].name;
+}
+const char* FAST_FUNC get_cached_username(uid_t uid)
+{
+	return get_cached(&username, uid, uid2uname_utoa);
+}
+const char* FAST_FUNC get_cached_groupname(gid_t gid)
+{
+	return get_cached(&groupname, gid, gid2group_utoa);
+}
+
+
+#define PROCPS_BUFSIZE 1024
+
+static int read_to_buf(const char *filename, void *buf)
+{
+	int fd;
+	/* open_read_close() would do two reads, checking for EOF.
+	 * When you have 10000 /proc/$NUM/stat to read, it isn't desirable */
+	ssize_t ret = -1;
+	fd = open(filename, O_RDONLY);
+	if (fd >= 0) {
+		ret = read(fd, buf, PROCPS_BUFSIZE-1);
+		close(fd);
+	}
+	((char *)buf)[ret > 0 ? ret : 0] = '\0';
+	return ret;
+}
+
+static procps_status_t* FAST_FUNC alloc_procps_scan(void)
+{
+	unsigned n = getpagesize();
+	procps_status_t* sp = xzalloc(sizeof(procps_status_t));
+	sp->dir = xopendir("/proc");
+	while (1) {
+		n >>= 1;
+		if (!n) break;
+		sp->shift_pages_to_bytes++;
+	}
+	sp->shift_pages_to_kb = sp->shift_pages_to_bytes - 10;
+	return sp;
+}
+
+void FAST_FUNC free_procps_scan(procps_status_t* sp)
+{
+	closedir(sp->dir);
+#if ENABLE_FEATURE_SHOW_THREADS
+	if (sp->task_dir)
+		closedir(sp->task_dir);
+#endif
+	free(sp->argv0);
+	free(sp->exe);
+	IF_SELINUX(free(sp->context);)
+	free(sp);
+}
+
+#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++) != ' ') {
+		c = ((c|0x20) - '0');
+		if (c > 9)
+			// c = c + '0' - 'a' + 10:
+			c = c - ('a' - '0' - 10);
+		n = n*16 + c;
+	}
+	*endptr = str; /* We skip trailing space! */
+	return n;
+}
+#endif
+
+#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;
+	char *str = *endptr;
+	unsigned long n = *str - '0';
+
+	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 != '-')
+		return fast_strtoul_10(endptr);
+
+	(*endptr)++;
+	return - (long)fast_strtoul_10(endptr);
+}
+# endif
+
+static char *skip_fields(char *str, int count)
+{
+	do {
+		while (*str++ != ' ')
+			continue;
+		/* we found a space char, str points after it */
+	} while (--count);
+	return str;
+}
+#endif
+
+#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)
+{
+	FILE *file;
+	struct smaprec currec;
+	char filename[sizeof("/proc/%u/smaps") + sizeof(int)*3];
+	char buf[PROCPS_BUFSIZE];
+#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 ADR M:m OFS FILE
+		// 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 ADR M:m OFS FILE
+
+			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 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 (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 (sp->task_dir) {
+			entry = readdir(sp->task_dir);
+			if (entry)
+				goto got_entry;
+			closedir(sp->task_dir);
+			sp->task_dir = NULL;
+		}
+#endif
+		entry = readdir(sp->dir);
+		if (entry == NULL) {
+			free_procps_scan(sp);
+			return NULL;
+		}
+ IF_FEATURE_SHOW_THREADS(got_entry:)
+		pid = bb_strtou(entry->d_name, NULL, 10);
+		if (errno)
+			continue;
+#if ENABLE_FEATURE_SHOW_THREADS
+		if ((flags & PSSCAN_TASKS) && !sp->task_dir) {
+			/* 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. */
+			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
+
+		/* After this point we can:
+		 * "break": stop parsing, return the data
+		 * "continue": try next /proc/XXX
+		 */
+
+		memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
+
+		sp->pid = pid;
+		if (!(flags & ~PSSCAN_PID))
+			break; /* we needed only pid, we got it */
+
+#if ENABLE_SELINUX
+		if (flags & PSSCAN_CONTEXT) {
+			if (getpidcon(sp->pid, &sp->context) < 0)
+				sp->context = NULL;
+		}
+#endif
+
+#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 */
+			sp->uid = sb.st_uid;
+			sp->gid = sb.st_gid;
+		}
+
+		/* 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
+			unsigned long vsz, rss;
+#endif
+			/* see proc(5) for some details on this */
+			strcpy(filename_tail, "stat");
+			n = read_to_buf(filename, buf);
+			if (n < 0)
+				continue; /* process probably exited */
+			cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
+			/*if (!cp || cp[1] != ' ')
+				continue;*/
+			cp[0] = '\0';
+			if (sizeof(sp->comm) < 16)
+				BUG_comm_size();
+			comm1 = strchr(buf, '(');
+			/*if (comm1)*/
+				safe_strncpy(sp->comm, comm1 + 1, sizeof(sp->comm));
+
+#if !ENABLE_FEATURE_FAST_TOP
+			n = sscanf(cp+2,
+				"%c %u "               /* state, ppid */
+				"%u %u %d %*s "        /* pgid, sid, tty, tpgid */
+				"%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
+				"%lu %lu "             /* utime, stime */
+				"%*s %*s %*s "         /* cutime, cstime, priority */
+				"%ld "                 /* nice */
+				"%*s %*s "             /* timeout, it_real_value */
+				"%lu "                 /* start_time */
+				"%lu "                 /* vsize */
+				"%lu "                 /* rss */
+# if ENABLE_FEATURE_TOP_SMP_PROCESS
+				"%*s %*s %*s %*s %*s %*s " /*rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
+				"%*s %*s %*s %*s "         /*signal, blocked, sigignore, sigcatch */
+				"%*s %*s %*s %*s "         /*wchan, nswap, cnswap, exit_signal */
+				"%d"                       /*cpu last seen on*/
+# endif
+				,
+				sp->state, &sp->ppid,
+				&sp->pgid, &sp->sid, &tty,
+				&sp->utime, &sp->stime,
+				&tasknice,
+				&sp->start_time,
+				&vsz,
+				&rss
+# if ENABLE_FEATURE_TOP_SMP_PROCESS
+				, &sp->last_seen_on_cpu
+# endif
+				);
+
+			if (n < 11)
+				continue; /* bogus data, get next /proc/XXX */
+# if ENABLE_FEATURE_TOP_SMP_PROCESS
+			if (n < 11+15)
+				sp->last_seen_on_cpu = 0;
+# endif
+
+			/* vsz is in bytes and we want kb */
+			sp->vsz = vsz >> 10;
+			/* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
+			sp->rss = rss << sp->shift_pages_to_kb;
+			sp->tty_major = (tty >> 8) & 0xfff;
+			sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
+#else
+/* This costs ~100 bytes more but makes top faster by 20%
+ * If you run 10000 processes, this may be important for you */
+			sp->state[0] = cp[2];
+			cp += 4;
+			sp->ppid = fast_strtoul_10(&cp);
+			sp->pgid = fast_strtoul_10(&cp);
+			sp->sid = fast_strtoul_10(&cp);
+			tty = fast_strtoul_10(&cp);
+			sp->tty_major = (tty >> 8) & 0xfff;
+			sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
+			cp = skip_fields(cp, 6); /* tpgid, flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
+			sp->utime = fast_strtoul_10(&cp);
+			sp->stime = fast_strtoul_10(&cp);
+			cp = skip_fields(cp, 3); /* cutime, cstime, priority */
+			tasknice = fast_strtol_10(&cp);
+			cp = skip_fields(cp, 2); /* timeout, it_real_value */
+			sp->start_time = fast_strtoul_10(&cp);
+			/* vsz is in bytes and we want kb */
+			sp->vsz = fast_strtoul_10(&cp) >> 10;
+			/* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
+			sp->rss = fast_strtoul_10(&cp) << sp->shift_pages_to_kb;
+# if ENABLE_FEATURE_TOP_SMP_PROCESS
+			/* (6): rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
+			/* (4): signal, blocked, sigignore, sigcatch */
+			/* (4): wchan, nswap, cnswap, exit_signal */
+			cp = skip_fields(cp, 14);
+//FIXME: is it safe to assume this field exists?
+			sp->last_seen_on_cpu = fast_strtoul_10(&cp);
+# endif
+#endif /* FEATURE_FAST_TOP */
+
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+			sp->niceness = tasknice;
+#endif
+
+			if (sp->vsz == 0 && sp->state[0] != 'Z')
+				sp->state[1] = 'W';
+			else
+				sp->state[1] = ' ';
+			if (tasknice < 0)
+				sp->state[2] = '<';
+			else if (tasknice) /* > 0 */
+				sp->state[2] = 'N';
+			else
+				sp->state[2] = ' ';
+		}
+
+#if ENABLE_FEATURE_TOPMEM
+		if (flags & PSSCAN_SMAPS)
+			procps_read_smaps(pid, &sp->smaps, NULL, NULL);
+#endif /* TOPMEM */
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+		if (flags & PSSCAN_RUIDGID) {
+			FILE *file;
+
+			strcpy(filename_tail, "status");
+			file = fopen_for_read(filename);
+			if (file) {
+				while (fgets(buf, sizeof(buf), file)) {
+					char *tp;
+#define SCAN_TWO(str, name, statement) \
+	if (strncmp(buf, str, sizeof(str)-1) == 0) { \
+		tp = skip_whitespace(buf + sizeof(str)-1); \
+		sscanf(tp, "%u", &sp->name); \
+		statement; \
+	}
+					SCAN_TWO("Uid:", ruid, continue);
+					SCAN_TWO("Gid:", rgid, break);
+#undef SCAN_TWO
+				}
+				fclose(file);
+			}
+		}
+#endif /* PS_ADDITIONAL_COLUMNS */
+		if (flags & PSSCAN_EXE) {
+			strcpy(filename_tail, "exe");
+			free(sp->exe);
+			sp->exe = xmalloc_readlink(filename);
+		}
+		/* Note: if /proc/PID/cmdline is empty,
+		 * code below "breaks". Therefore it must be
+		 * the last code to parse /proc/PID/xxx data
+		 * (we used to have /proc/PID/exe parsing after it
+		 * and were getting stale sp->exe).
+		 */
+#if 0 /* PSSCAN_CMD is not used */
+		if (flags & (PSSCAN_CMD|PSSCAN_ARGV0)) {
+			free(sp->argv0);
+			sp->argv0 = NULL;
+			free(sp->cmd);
+			sp->cmd = NULL;
+			strcpy(filename_tail, "cmdline");
+			/* TODO: to get rid of size limits, read into malloc buf,
+			 * then realloc it down to real size. */
+			n = read_to_buf(filename, buf);
+			if (n <= 0)
+				break;
+			if (flags & PSSCAN_ARGV0)
+				sp->argv0 = xstrdup(buf);
+			if (flags & PSSCAN_CMD) {
+				do {
+					n--;
+					if ((unsigned char)(buf[n]) < ' ')
+						buf[n] = ' ';
+				} while (n);
+				sp->cmd = xstrdup(buf);
+			}
+		}
+#else
+		if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) {
+			free(sp->argv0);
+			sp->argv0 = NULL;
+			strcpy(filename_tail, "cmdline");
+			n = read_to_buf(filename, buf);
+			if (n <= 0)
+				break;
+			if (flags & PSSCAN_ARGVN) {
+				sp->argv_len = n;
+				sp->argv0 = xmalloc(n + 1);
+				memcpy(sp->argv0, buf, n + 1);
+				/* sp->argv0[n] = '\0'; - buf has it */
+			} else {
+				sp->argv_len = 0;
+				sp->argv0 = xstrdup(buf);
+			}
+		}
+#endif
+		break;
+	} /* for (;;) */
+
+	return sp;
+}
+
+void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
+{
+	int sz;
+	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;
+		base = bb_basename(buf); /* before we replace argv0's NUL with space */
+		while (sz >= 0) {
+			if ((unsigned char)(buf[sz]) < ' ')
+				buf[sz] = ' ';
+			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);
+	}
+}
+
+/* from kernel:
+	//             pid comm S ppid pgid sid tty_nr tty_pgrp flg
+	sprintf(buffer,"%d (%s) %c %d  %d   %d  %d     %d       %lu %lu \
+%lu %lu %lu %lu %lu %ld %ld %ld %ld %d 0 %llu %lu %ld %lu %lu %lu %lu %lu \
+%lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %lu %llu\n",
+		task->pid,
+		tcomm,
+		state,
+		ppid,
+		pgid,
+		sid,
+		tty_nr,
+		tty_pgrp,
+		task->flags,
+		min_flt,
+		cmin_flt,
+		maj_flt,
+		cmaj_flt,
+		cputime_to_clock_t(utime),
+		cputime_to_clock_t(stime),
+		cputime_to_clock_t(cutime),
+		cputime_to_clock_t(cstime),
+		priority,
+		nice,
+		num_threads,
+		// 0,
+		start_time,
+		vsize,
+		mm ? get_mm_rss(mm) : 0,
+		rsslim,
+		mm ? mm->start_code : 0,
+		mm ? mm->end_code : 0,
+		mm ? mm->start_stack : 0,
+		esp,
+		eip,
+the rest is some obsolete cruft
+*/
diff --git a/busybox-1.19.3/libbb/progress.c b/busybox-1.19.3/libbb/progress.c
new file mode 100644
index 0000000..372feb0
--- /dev/null
+++ b/busybox-1.19.3/libbb/progress.c
@@ -0,0 +1,207 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Progress bar code.
+ */
+/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
+ * much of which was blatantly stolen from openssh.
+ */
+/*-
+ * Copyright (c) 1992, 1993
+ * 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
+ * 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. 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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+#include "libbb.h"
+#include "unicode.h"
+
+enum {
+	/* Seconds when xfer considered "stalled" */
+	STALLTIME = 5
+};
+
+static unsigned int get_tty2_width(void)
+{
+	unsigned width;
+	get_terminal_width_height(2, &width, NULL);
+	return width;
+}
+
+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->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,
+		uoff_t beg_size,
+		uoff_t transferred,
+		uoff_t totalsize)
+{
+	uoff_t beg_and_transferred;
+	unsigned since_last_update, elapsed;
+	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;
+	}
+
+	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;
+		}
+	}
+
+	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);
+		}
+	}
+
+	while (beg_and_transferred >= 100000) {
+		beg_and_transferred >>= 10;
+		kiloscale++;
+	}
+	/* see http://en.wikipedia.org/wiki/Tera */
+	fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]);
+#define beg_and_transferred dont_use_beg_and_transferred_below()
+
+	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 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 -");
+	} else if (!totalsize || !transferred || (int)elapsed < 0) {
+		fprintf(stderr, " --:--:-- ETA");
+	} else {
+		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);
+	}
+}
diff --git a/busybox-1.19.3/libbb/ptr_to_globals.c b/busybox-1.19.3/libbb/ptr_to_globals.c
new file mode 100644
index 0000000..1074538
--- /dev/null
+++ b/busybox-1.19.3/libbb/ptr_to_globals.c
@@ -0,0 +1,35 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include <errno.h>
+
+struct globals;
+
+#ifndef GCC_COMBINE
+
+/* We cheat here. It is declared as const ptr in libbb.h,
+ * but here we make it live in R/W memory */
+struct globals *ptr_to_globals;
+
+#ifdef __GLIBC__
+int *bb_errno;
+#endif
+
+
+#else
+
+
+/* gcc -combine will see through and complain */
+/* Using alternative method which is more likely to break
+ * on weird architectures, compilers, linkers and so on */
+struct globals *const ptr_to_globals __attribute__ ((section (".data")));
+
+#ifdef __GLIBC__
+int *const bb_errno __attribute__ ((section (".data")));
+#endif
+
+#endif
diff --git a/busybox-1.19.3/libbb/pw_encrypt.c b/busybox-1.19.3/libbb/pw_encrypt.c
new file mode 100644
index 0000000..39ffa08
--- /dev/null
+++ b/busybox-1.19.3/libbb/pw_encrypt.c
@@ -0,0 +1,148 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+/* static const uint8_t ascii64[] =
+ * "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ */
+
+static int i64c(int i)
+{
+	i &= 0x3f;
+	if (i == 0)
+		return '.';
+	if (i == 1)
+		return '/';
+	if (i < 12)
+		return ('0' - 2 + i);
+	if (i < 38)
+		return ('A' - 12 + i);
+	return ('a' - 38 + i);
+}
+
+int FAST_FUNC crypt_make_salt(char *p, int cnt /*, int x */)
+{
+	/* was: x += ... */
+	int x = getpid() + monotonic_us();
+	do {
+		/* x = (x*1664525 + 1013904223) % 2^32 generator is lame
+		 * (low-order bit is not "random", etc...),
+		 * but for our purposes it is good enough */
+		x = x*1664525 + 1013904223;
+		/* BTW, Park and Miller's "minimal standard generator" is
+		 * x = x*16807 % ((2^31)-1)
+		 * It has no problem with visibly alternating lowest bit
+		 * but is also weak in cryptographic sense + needs div,
+		 * which needs more code (and slower) on many CPUs */
+		*p++ = i64c(x >> 16);
+		*p++ = i64c(x >> 22);
+	} while (--cnt);
+	*p = '\0';
+	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*
+to64(char *s, unsigned v, int n)
+{
+	while (--n >= 0) {
+		/* *s++ = ascii64[v & 0x3f]; */
+		*s++ = i64c(v);
+		v >>= 6;
+	}
+	return s;
+}
+
+/*
+ * DES and MD5 crypt implementations are taken from uclibc.
+ * They were modified to not use static buffers.
+ */
+
+#include "pw_encrypt_des.c"
+#include "pw_encrypt_md5.c"
+#if ENABLE_USE_BB_CRYPT_SHA
+#include "pw_encrypt_sha.c"
+#endif
+
+/* Other advanced crypt ids (TODO?): */
+/* $2$ or $2a$: Blowfish */
+
+static struct const_des_ctx *des_cctx;
+static struct des_ctx *des_ctx;
+
+/* my_crypt returns malloc'ed data */
+static char *my_crypt(const char *key, const char *salt)
+{
+	/* MD5 or SHA? */
+	if (salt[0] == '$' && salt[1] && salt[2] == '$') {
+		if (salt[1] == '1')
+			return md5_crypt(xzalloc(MD5_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt);
+#if ENABLE_USE_BB_CRYPT_SHA
+		if (salt[1] == '5' || salt[1] == '6')
+			return sha_crypt((char*)key, (char*)salt);
+#endif
+	}
+
+	if (!des_cctx)
+		des_cctx = const_des_init();
+	des_ctx = des_init(des_ctx, des_cctx);
+	return des_crypt(des_ctx, xzalloc(DES_OUT_BUFSIZE), (unsigned char*)key, (unsigned char*)salt);
+}
+
+/* So far nobody wants to have it public */
+static void my_crypt_cleanup(void)
+{
+	free(des_cctx);
+	free(des_ctx);
+	des_cctx = NULL;
+	des_ctx = NULL;
+}
+
+char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup)
+{
+	char *encrypted;
+
+	encrypted = my_crypt(clear, salt);
+
+	if (cleanup)
+		my_crypt_cleanup();
+
+	return encrypted;
+}
+
+#else /* if !ENABLE_USE_BB_CRYPT */
+
+char* FAST_FUNC pw_encrypt(const char *clear, const char *salt, int cleanup)
+{
+	return xstrdup(crypt(clear, salt));
+}
+
+#endif
diff --git a/busybox-1.19.3/libbb/pw_encrypt_des.c b/busybox-1.19.3/libbb/pw_encrypt_des.c
new file mode 100644
index 0000000..c8e02dd
--- /dev/null
+++ b/busybox-1.19.3/libbb/pw_encrypt_des.c
@@ -0,0 +1,820 @@
+/*
+ * FreeSec: libcrypt for NetBSD
+ *
+ * Copyright (c) 1994 David Burren
+ * All rights reserved.
+ *
+ * Adapted for FreeBSD-2.0 by Geoffrey M. Rehmet
+ *	this file should now *only* export crypt(), in order to make
+ *	binaries of libcrypt exportable from the USA
+ *
+ * Adapted for FreeBSD-4.0 by Mark R V Murray
+ *	this file should now *only* export crypt_des(), in order to make
+ *	a module that can be optionally included in libcrypt.
+ *
+ * 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. Neither the name of the author nor the names of other contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ *
+ * This is an original implementation of the DES and the crypt(3) interfaces
+ * by David Burren <davidb@werj.com.au>.
+ *
+ * An excellent reference on the underlying algorithm (and related
+ * algorithms) is:
+ *
+ *	B. Schneier, Applied Cryptography: protocols, algorithms,
+ *	and source code in C, John Wiley & Sons, 1994.
+ *
+ * Note that in that book's description of DES the lookups for the initial,
+ * pbox, and final permutations are inverted (this has been brought to the
+ * attention of the author).  A list of errata for this book has been
+ * posted to the sci.crypt newsgroup by the author and is available for FTP.
+ *
+ * ARCHITECTURE ASSUMPTIONS:
+ *	It is assumed that the 8-byte arrays passed by reference can be
+ *	addressed as arrays of uint32_t's (ie. the CPU is not picky about
+ *	alignment).
+ */
+
+
+/* Parts busybox doesn't need or had optimized */
+#define USE_PRECOMPUTED_u_sbox 1
+#define USE_REPETITIVE_SPEEDUP 0
+#define USE_ip_mask 0
+#define USE_de_keys 0
+
+
+/* A pile of data */
+static const uint8_t IP[64] = {
+	58, 50, 42, 34, 26, 18, 10,  2, 60, 52, 44, 36, 28, 20, 12,  4,
+	62, 54, 46, 38, 30, 22, 14,  6, 64, 56, 48, 40, 32, 24, 16,  8,
+	57, 49, 41, 33, 25, 17,  9,  1, 59, 51, 43, 35, 27, 19, 11,  3,
+	61, 53, 45, 37, 29, 21, 13,  5, 63, 55, 47, 39, 31, 23, 15,  7
+};
+
+static const uint8_t key_perm[56] = {
+	57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
+	10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,
+	63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
+	14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4
+};
+
+static const uint8_t key_shifts[16] = {
+	1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+};
+
+static const uint8_t comp_perm[48] = {
+	14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
+	23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
+	41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
+	44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32
+};
+
+/*
+ * No E box is used, as it's replaced by some ANDs, shifts, and ORs.
+ */
+#if !USE_PRECOMPUTED_u_sbox
+static const uint8_t sbox[8][64] = {
+	{	14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
+		 0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
+		 4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
+		15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13
+	},
+	{	15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
+		 3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
+		 0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
+		13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9
+	},
+	{	10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
+		13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
+		13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
+		 1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12
+	},
+	{	 7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
+		13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
+		10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
+		 3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14
+	},
+	{	 2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
+		14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
+		 4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
+		11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3
+	},
+	{	12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
+		10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
+		 9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
+		 4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13
+	},
+	{	 4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
+		13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
+		 1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
+		 6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12
+	},
+	{	13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
+		 1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
+		 7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
+		 2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11
+	}
+};
+#else /* precomputed, with half-bytes packed into one byte */
+static const uint8_t u_sbox[8][32] = {
+	{	0x0e, 0xf4, 0x7d, 0x41, 0xe2, 0x2f, 0xdb, 0x18,
+		0xa3, 0x6a, 0xc6, 0xbc, 0x95, 0x59, 0x30, 0x87,
+		0xf4, 0xc1, 0x8e, 0x28, 0x4d, 0x96, 0x12, 0x7b,
+		0x5f, 0xbc, 0x39, 0xe7, 0xa3, 0x0a, 0x65, 0xd0,
+	},
+	{	0x3f, 0xd1, 0x48, 0x7e, 0xf6, 0x2b, 0x83, 0xe4,
+		0xc9, 0x07, 0x12, 0xad, 0x6c, 0x90, 0xb5, 0x5a,
+		0xd0, 0x8e, 0xa7, 0x1b, 0x3a, 0xf4, 0x4d, 0x21,
+		0xb5, 0x68, 0x7c, 0xc6, 0x09, 0x53, 0xe2, 0x9f,
+	},
+	{	0xda, 0x70, 0x09, 0x9e, 0x36, 0x43, 0x6f, 0xa5,
+		0x21, 0x8d, 0x5c, 0xe7, 0xcb, 0xb4, 0xf2, 0x18,
+		0x1d, 0xa6, 0xd4, 0x09, 0x68, 0x9f, 0x83, 0x70,
+		0x4b, 0xf1, 0xe2, 0x3c, 0xb5, 0x5a, 0x2e, 0xc7,
+	},
+	{	0xd7, 0x8d, 0xbe, 0x53, 0x60, 0xf6, 0x09, 0x3a,
+		0x41, 0x72, 0x28, 0xc5, 0x1b, 0xac, 0xe4, 0x9f,
+		0x3a, 0xf6, 0x09, 0x60, 0xac, 0x1b, 0xd7, 0x8d,
+		0x9f, 0x41, 0x53, 0xbe, 0xc5, 0x72, 0x28, 0xe4,
+	},
+	{	0xe2, 0xbc, 0x24, 0xc1, 0x47, 0x7a, 0xdb, 0x16,
+		0x58, 0x05, 0xf3, 0xaf, 0x3d, 0x90, 0x8e, 0x69,
+		0xb4, 0x82, 0xc1, 0x7b, 0x1a, 0xed, 0x27, 0xd8,
+		0x6f, 0xf9, 0x0c, 0x95, 0xa6, 0x43, 0x50, 0x3e,
+	},
+	{	0xac, 0xf1, 0x4a, 0x2f, 0x79, 0xc2, 0x96, 0x58,
+		0x60, 0x1d, 0xd3, 0xe4, 0x0e, 0xb7, 0x35, 0x8b,
+		0x49, 0x3e, 0x2f, 0xc5, 0x92, 0x58, 0xfc, 0xa3,
+		0xb7, 0xe0, 0x14, 0x7a, 0x61, 0x0d, 0x8b, 0xd6,
+	},
+	{	0xd4, 0x0b, 0xb2, 0x7e, 0x4f, 0x90, 0x18, 0xad,
+		0xe3, 0x3c, 0x59, 0xc7, 0x25, 0xfa, 0x86, 0x61,
+		0x61, 0xb4, 0xdb, 0x8d, 0x1c, 0x43, 0xa7, 0x7e,
+		0x9a, 0x5f, 0x06, 0xf8, 0xe0, 0x25, 0x39, 0xc2,
+	},
+	{	0x1d, 0xf2, 0xd8, 0x84, 0xa6, 0x3f, 0x7b, 0x41,
+		0xca, 0x59, 0x63, 0xbe, 0x05, 0xe0, 0x9c, 0x27,
+		0x27, 0x1b, 0xe4, 0x71, 0x49, 0xac, 0x8e, 0xd2,
+		0xf0, 0xc6, 0x9a, 0x0d, 0x3f, 0x53, 0x65, 0xb8,
+	},
+};
+#endif
+
+static const uint8_t pbox[32] = {
+	16,  7, 20, 21, 29, 12, 28, 17,  1, 15, 23, 26,  5, 18, 31, 10,
+	 2,  8, 24, 14, 32, 27,  3,  9, 19, 13, 30,  6, 22, 11,  4, 25
+};
+
+static const uint32_t bits32[32] =
+{
+	0x80000000, 0x40000000, 0x20000000, 0x10000000,
+	0x08000000, 0x04000000, 0x02000000, 0x01000000,
+	0x00800000, 0x00400000, 0x00200000, 0x00100000,
+	0x00080000, 0x00040000, 0x00020000, 0x00010000,
+	0x00008000, 0x00004000, 0x00002000, 0x00001000,
+	0x00000800, 0x00000400, 0x00000200, 0x00000100,
+	0x00000080, 0x00000040, 0x00000020, 0x00000010,
+	0x00000008, 0x00000004, 0x00000002, 0x00000001
+};
+
+static const uint8_t bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+
+static int
+ascii_to_bin(char ch)
+{
+	if (ch > 'z')
+		return 0;
+	if (ch >= 'a')
+		return (ch - 'a' + 38);
+	if (ch > 'Z')
+		return 0;
+	if (ch >= 'A')
+		return (ch - 'A' + 12);
+	if (ch > '9')
+		return 0;
+	if (ch >= '.')
+		return (ch - '.');
+	return 0;
+}
+
+
+/* Static stuff that stays resident and doesn't change after
+ * being initialized, and therefore doesn't need to be made
+ * reentrant. */
+struct const_des_ctx {
+#if USE_ip_mask
+	uint8_t	init_perm[64]; /* referenced 2 times */
+#endif
+	uint8_t	final_perm[64]; /* 2 times */
+	uint8_t	m_sbox[4][4096]; /* 5 times */
+};
+#define C (*cctx)
+#define init_perm  (C.init_perm )
+#define final_perm (C.final_perm)
+#define m_sbox     (C.m_sbox    )
+
+static struct const_des_ctx*
+const_des_init(void)
+{
+	unsigned i, j, b;
+	struct const_des_ctx *cctx;
+
+#if !USE_PRECOMPUTED_u_sbox
+	uint8_t	u_sbox[8][64];
+
+	cctx = xmalloc(sizeof(*cctx));
+
+	/* Invert the S-boxes, reordering the input bits. */
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 64; j++) {
+			b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf);
+			u_sbox[i][j] = sbox[i][b];
+		}
+	}
+	for (i = 0; i < 8; i++) {
+		fprintf(stderr, "\t{\t");
+		for (j = 0; j < 64; j+=2)
+			fprintf(stderr, " 0x%02x,", u_sbox[i][j] + u_sbox[i][j+1]*16);
+		fprintf(stderr, "\n\t},\n");
+	}
+	/*
+	 * Convert the inverted S-boxes into 4 arrays of 8 bits.
+	 * Each will handle 12 bits of the S-box input.
+	 */
+	for (b = 0; b < 4; b++)
+		for (i = 0; i < 64; i++)
+			for (j = 0; j < 64; j++)
+				m_sbox[b][(i << 6) | j] =
+					(uint8_t)((u_sbox[(b << 1)][i] << 4) |
+						u_sbox[(b << 1) + 1][j]);
+#else
+	cctx = xmalloc(sizeof(*cctx));
+
+	/*
+	 * Convert the inverted S-boxes into 4 arrays of 8 bits.
+	 * Each will handle 12 bits of the S-box input.
+	 */
+	for (b = 0; b < 4; b++)
+	 for (i = 0; i < 64; i++)
+	  for (j = 0; j < 64; j++) {
+		uint8_t lo, hi;
+		hi = u_sbox[(b << 1)][i / 2];
+		if (!(i & 1))
+			hi <<= 4;
+		lo = u_sbox[(b << 1) + 1][j / 2];
+		if (j & 1)
+			lo >>= 4;
+		m_sbox[b][(i << 6) | j] = (hi & 0xf0) | (lo & 0x0f);
+	}
+#endif
+
+	/*
+	 * Set up the initial & final permutations into a useful form.
+	 */
+	for (i = 0; i < 64; i++) {
+		final_perm[i] = IP[i] - 1;
+#if USE_ip_mask
+		init_perm[final_perm[i]] = (uint8_t)i;
+#endif
+	}
+
+	return cctx;
+}
+
+
+struct des_ctx {
+	const struct const_des_ctx *const_ctx;
+	uint32_t saltbits; /* referenced 5 times */
+#if USE_REPETITIVE_SPEEDUP
+	uint32_t old_salt; /* 3 times */
+	uint32_t old_rawkey0, old_rawkey1; /* 3 times each */
+#endif
+	uint8_t	un_pbox[32]; /* 2 times */
+	uint8_t	inv_comp_perm[56]; /* 3 times */
+	uint8_t	inv_key_perm[64]; /* 3 times */
+	uint32_t en_keysl[16], en_keysr[16]; /* 2 times each */
+#if USE_de_keys
+	uint32_t de_keysl[16], de_keysr[16]; /* 2 times each */
+#endif
+#if USE_ip_mask
+	uint32_t ip_maskl[8][256], ip_maskr[8][256]; /* 9 times each */
+#endif
+	uint32_t fp_maskl[8][256], fp_maskr[8][256]; /* 9 times each */
+	uint32_t key_perm_maskl[8][128], key_perm_maskr[8][128]; /* 9 times */
+	uint32_t comp_maskl[8][128], comp_maskr[8][128]; /* 9 times each */
+	uint32_t psbox[4][256]; /* 5 times */
+};
+#define D (*ctx)
+#define const_ctx       (D.const_ctx      )
+#define saltbits        (D.saltbits       )
+#define old_salt        (D.old_salt       )
+#define old_rawkey0     (D.old_rawkey0    )
+#define old_rawkey1     (D.old_rawkey1    )
+#define un_pbox         (D.un_pbox        )
+#define inv_comp_perm   (D.inv_comp_perm  )
+#define inv_key_perm    (D.inv_key_perm   )
+#define en_keysl        (D.en_keysl       )
+#define en_keysr        (D.en_keysr       )
+#define de_keysl        (D.de_keysl       )
+#define de_keysr        (D.de_keysr       )
+#define ip_maskl        (D.ip_maskl       )
+#define ip_maskr        (D.ip_maskr       )
+#define fp_maskl        (D.fp_maskl       )
+#define fp_maskr        (D.fp_maskr       )
+#define key_perm_maskl  (D.key_perm_maskl )
+#define key_perm_maskr  (D.key_perm_maskr )
+#define comp_maskl      (D.comp_maskl     )
+#define comp_maskr      (D.comp_maskr     )
+#define psbox           (D.psbox          )
+
+static struct des_ctx*
+des_init(struct des_ctx *ctx, const struct const_des_ctx *cctx)
+{
+	int i, j, b, k, inbit, obit;
+	uint32_t p;
+	const uint32_t *bits28, *bits24;
+
+	if (!ctx)
+		ctx = xmalloc(sizeof(*ctx));
+	const_ctx = cctx;
+
+#if USE_REPETITIVE_SPEEDUP
+	old_rawkey0 = old_rawkey1 = 0;
+	old_salt = 0;
+#endif
+	saltbits = 0;
+	bits28 = bits32 + 4;
+	bits24 = bits28 + 4;
+
+	/* Initialise the inverted key permutation. */
+	for (i = 0; i < 64; i++) {
+		inv_key_perm[i] = 255;
+	}
+
+	/*
+	 * Invert the key permutation and initialise the inverted key
+	 * compression permutation.
+	 */
+	for (i = 0; i < 56; i++) {
+		inv_key_perm[key_perm[i] - 1] = (uint8_t)i;
+		inv_comp_perm[i] = 255;
+	}
+
+	/* Invert the key compression permutation. */
+	for (i = 0; i < 48; i++) {
+		inv_comp_perm[comp_perm[i] - 1] = (uint8_t)i;
+	}
+
+	/*
+	 * Set up the OR-mask arrays for the initial and final permutations,
+	 * and for the key initial and compression permutations.
+	 */
+	for (k = 0; k < 8; k++) {
+		uint32_t il, ir;
+		uint32_t fl, fr;
+		for (i = 0; i < 256; i++) {
+#if USE_ip_mask
+			il = 0;
+			ir = 0;
+#endif
+			fl = 0;
+			fr = 0;
+			for (j = 0; j < 8; j++) {
+				inbit = 8 * k + j;
+				if (i & bits8[j]) {
+#if USE_ip_mask
+					obit = init_perm[inbit];
+					if (obit < 32)
+						il |= bits32[obit];
+					else
+						ir |= bits32[obit - 32];
+#endif
+					obit = final_perm[inbit];
+					if (obit < 32)
+						fl |= bits32[obit];
+					else
+						fr |= bits32[obit - 32];
+				}
+			}
+#if USE_ip_mask
+			ip_maskl[k][i] = il;
+			ip_maskr[k][i] = ir;
+#endif
+			fp_maskl[k][i] = fl;
+			fp_maskr[k][i] = fr;
+		}
+		for (i = 0; i < 128; i++) {
+			il = 0;
+			ir = 0;
+			for (j = 0; j < 7; j++) {
+				inbit = 8 * k + j;
+				if (i & bits8[j + 1]) {
+					obit = inv_key_perm[inbit];
+					if (obit == 255)
+						continue;
+					if (obit < 28)
+						il |= bits28[obit];
+					else
+						ir |= bits28[obit - 28];
+				}
+			}
+			key_perm_maskl[k][i] = il;
+			key_perm_maskr[k][i] = ir;
+			il = 0;
+			ir = 0;
+			for (j = 0; j < 7; j++) {
+				inbit = 7 * k + j;
+				if (i & bits8[j + 1]) {
+					obit = inv_comp_perm[inbit];
+					if (obit == 255)
+						continue;
+					if (obit < 24)
+						il |= bits24[obit];
+					else
+						ir |= bits24[obit - 24];
+				}
+			}
+			comp_maskl[k][i] = il;
+			comp_maskr[k][i] = ir;
+		}
+	}
+
+	/*
+	 * Invert the P-box permutation, and convert into OR-masks for
+	 * handling the output of the S-box arrays setup above.
+	 */
+	for (i = 0; i < 32; i++)
+		un_pbox[pbox[i] - 1] = (uint8_t)i;
+
+	for (b = 0; b < 4; b++) {
+		for (i = 0; i < 256; i++) {
+			p = 0;
+			for (j = 0; j < 8; j++) {
+				if (i & bits8[j])
+					p |= bits32[un_pbox[8 * b + j]];
+			}
+			psbox[b][i] = p;
+		}
+	}
+
+	return ctx;
+}
+
+
+static void
+setup_salt(struct des_ctx *ctx, uint32_t salt)
+{
+	uint32_t obit, saltbit;
+	int i;
+
+#if USE_REPETITIVE_SPEEDUP
+	if (salt == old_salt)
+		return;
+	old_salt = salt;
+#endif
+
+	saltbits = 0;
+	saltbit = 1;
+	obit = 0x800000;
+	for (i = 0; i < 24; i++) {
+		if (salt & saltbit)
+			saltbits |= obit;
+		saltbit <<= 1;
+		obit >>= 1;
+	}
+}
+
+static void
+des_setkey(struct des_ctx *ctx, const char *key)
+{
+	uint32_t k0, k1, rawkey0, rawkey1;
+	int shifts, round;
+
+	rawkey0 = ntohl(*(const uint32_t *) key);
+	rawkey1 = ntohl(*(const uint32_t *) (key + 4));
+
+#if USE_REPETITIVE_SPEEDUP
+	if ((rawkey0 | rawkey1)
+	 && rawkey0 == old_rawkey0
+	 && rawkey1 == old_rawkey1
+	) {
+		/*
+		 * Already setup for this key.
+		 * This optimisation fails on a zero key (which is weak and
+		 * has bad parity anyway) in order to simplify the starting
+		 * conditions.
+		 */
+		return;
+	}
+	old_rawkey0 = rawkey0;
+	old_rawkey1 = rawkey1;
+#endif
+
+	/*
+	 * Do key permutation and split into two 28-bit subkeys.
+	 */
+	k0 = key_perm_maskl[0][rawkey0 >> 25]
+	   | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f]
+	   | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f]
+	   | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f]
+	   | key_perm_maskl[4][rawkey1 >> 25]
+	   | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f]
+	   | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f]
+	   | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f];
+	k1 = key_perm_maskr[0][rawkey0 >> 25]
+	   | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f]
+	   | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f]
+	   | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f]
+	   | key_perm_maskr[4][rawkey1 >> 25]
+	   | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f]
+	   | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f]
+	   | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f];
+	/*
+	 * Rotate subkeys and do compression permutation.
+	 */
+	shifts = 0;
+	for (round = 0; round < 16; round++) {
+		uint32_t t0, t1;
+
+		shifts += key_shifts[round];
+
+		t0 = (k0 << shifts) | (k0 >> (28 - shifts));
+		t1 = (k1 << shifts) | (k1 >> (28 - shifts));
+
+#if USE_de_keys
+		de_keysl[15 - round] =
+#endif
+		en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f]
+				| comp_maskl[1][(t0 >> 14) & 0x7f]
+				| comp_maskl[2][(t0 >> 7) & 0x7f]
+				| comp_maskl[3][t0 & 0x7f]
+				| comp_maskl[4][(t1 >> 21) & 0x7f]
+				| comp_maskl[5][(t1 >> 14) & 0x7f]
+				| comp_maskl[6][(t1 >> 7) & 0x7f]
+				| comp_maskl[7][t1 & 0x7f];
+
+#if USE_de_keys
+		de_keysr[15 - round] =
+#endif
+		en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f]
+				| comp_maskr[1][(t0 >> 14) & 0x7f]
+				| comp_maskr[2][(t0 >> 7) & 0x7f]
+				| comp_maskr[3][t0 & 0x7f]
+				| comp_maskr[4][(t1 >> 21) & 0x7f]
+				| comp_maskr[5][(t1 >> 14) & 0x7f]
+				| comp_maskr[6][(t1 >> 7) & 0x7f]
+				| comp_maskr[7][t1 & 0x7f];
+	}
+}
+
+
+static void
+do_des(struct des_ctx *ctx, /*uint32_t l_in, uint32_t r_in,*/ uint32_t *l_out, uint32_t *r_out, int count)
+{
+	const struct const_des_ctx *cctx = const_ctx;
+	/*
+	 * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format.
+	 */
+	uint32_t l, r, *kl, *kr;
+	uint32_t f = f; /* silence gcc */
+	uint32_t r48l, r48r;
+	int round;
+
+	/* Do initial permutation (IP). */
+#if USE_ip_mask
+	uint32_t l_in = 0;
+	uint32_t r_in = 0;
+	l = ip_maskl[0][l_in >> 24]
+	  | ip_maskl[1][(l_in >> 16) & 0xff]
+	  | ip_maskl[2][(l_in >> 8) & 0xff]
+	  | ip_maskl[3][l_in & 0xff]
+	  | ip_maskl[4][r_in >> 24]
+	  | ip_maskl[5][(r_in >> 16) & 0xff]
+	  | ip_maskl[6][(r_in >> 8) & 0xff]
+	  | ip_maskl[7][r_in & 0xff];
+	r = ip_maskr[0][l_in >> 24]
+	  | ip_maskr[1][(l_in >> 16) & 0xff]
+	  | ip_maskr[2][(l_in >> 8) & 0xff]
+	  | ip_maskr[3][l_in & 0xff]
+	  | ip_maskr[4][r_in >> 24]
+	  | ip_maskr[5][(r_in >> 16) & 0xff]
+	  | ip_maskr[6][(r_in >> 8) & 0xff]
+	  | ip_maskr[7][r_in & 0xff];
+#elif 0 /* -65 bytes (using the fact that l_in == r_in == 0) */
+	l = r = 0;
+	for (round = 0; round < 8; round++) {
+		l |= ip_maskl[round][0];
+		r |= ip_maskr[round][0];
+	}
+	bb_error_msg("l:%x r:%x", l, r); /* reports 0, 0 always! */
+#else /* using the fact that ip_maskX[] is constant (written to by des_init) */
+	l = r = 0;
+#endif
+
+	do {
+		/* Do each round. */
+		kl = en_keysl;
+		kr = en_keysr;
+		round = 16;
+		do {
+			/* Expand R to 48 bits (simulate the E-box). */
+			r48l	= ((r & 0x00000001) << 23)
+				| ((r & 0xf8000000) >> 9)
+				| ((r & 0x1f800000) >> 11)
+				| ((r & 0x01f80000) >> 13)
+				| ((r & 0x001f8000) >> 15);
+
+			r48r	= ((r & 0x0001f800) << 7)
+				| ((r & 0x00001f80) << 5)
+				| ((r & 0x000001f8) << 3)
+				| ((r & 0x0000001f) << 1)
+				| ((r & 0x80000000) >> 31);
+			/*
+			 * Do salting for crypt() and friends, and
+			 * XOR with the permuted key.
+			 */
+			f = (r48l ^ r48r) & saltbits;
+			r48l ^= f ^ *kl++;
+			r48r ^= f ^ *kr++;
+			/*
+			 * Do sbox lookups (which shrink it back to 32 bits)
+			 * and do the pbox permutation at the same time.
+			 */
+			f = psbox[0][m_sbox[0][r48l >> 12]]
+			  | psbox[1][m_sbox[1][r48l & 0xfff]]
+			  | psbox[2][m_sbox[2][r48r >> 12]]
+			  | psbox[3][m_sbox[3][r48r & 0xfff]];
+			/* Now that we've permuted things, complete f(). */
+			f ^= l;
+			l = r;
+			r = f;
+		} while (--round);
+		r = l;
+		l = f;
+	} while (--count);
+
+	/* Do final permutation (inverse of IP). */
+	*l_out	= fp_maskl[0][l >> 24]
+		| fp_maskl[1][(l >> 16) & 0xff]
+		| fp_maskl[2][(l >> 8) & 0xff]
+		| fp_maskl[3][l & 0xff]
+		| fp_maskl[4][r >> 24]
+		| fp_maskl[5][(r >> 16) & 0xff]
+		| fp_maskl[6][(r >> 8) & 0xff]
+		| fp_maskl[7][r & 0xff];
+	*r_out	= fp_maskr[0][l >> 24]
+		| fp_maskr[1][(l >> 16) & 0xff]
+		| fp_maskr[2][(l >> 8) & 0xff]
+		| fp_maskr[3][l & 0xff]
+		| fp_maskr[4][r >> 24]
+		| fp_maskr[5][(r >> 16) & 0xff]
+		| fp_maskr[6][(r >> 8) & 0xff]
+		| fp_maskr[7][r & 0xff];
+}
+
+#define DES_OUT_BUFSIZE 21
+
+static void
+to64_msb_first(char *s, unsigned v)
+{
+#if 0
+	*s++ = ascii64[(v >> 18) & 0x3f]; /* bits 23..18 */
+	*s++ = ascii64[(v >> 12) & 0x3f]; /* bits 17..12 */
+	*s++ = ascii64[(v >> 6) & 0x3f]; /* bits 11..6 */
+	*s   = ascii64[v & 0x3f]; /* bits 5..0 */
+#endif
+	*s++ = i64c(v >> 18); /* bits 23..18 */
+	*s++ = i64c(v >> 12); /* bits 17..12 */
+	*s++ = i64c(v >> 6); /* bits 11..6 */
+	*s   = i64c(v); /* bits 5..0 */
+}
+
+static char *
+NOINLINE
+des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE],
+		const unsigned char *key, const unsigned char *setting)
+{
+	uint32_t salt, r0, r1, keybuf[2];
+	uint8_t *q;
+
+	/*
+	 * Copy the key, shifting each character up by one bit
+	 * and padding with zeros.
+	 */
+	q = (uint8_t *)keybuf;
+	while (q - (uint8_t *)keybuf != 8) {
+		*q = *key << 1;
+		if (*q)
+			key++;
+		q++;
+	}
+	des_setkey(ctx, (char *)keybuf);
+
+	/*
+	 * setting - 2 bytes of salt
+	 * key - up to 8 characters
+	 */
+	salt = (ascii_to_bin(setting[1]) << 6)
+	     |  ascii_to_bin(setting[0]);
+
+	output[0] = setting[0];
+	/*
+	 * If the encrypted password that the salt was extracted from
+	 * is only 1 character long, the salt will be corrupted.  We
+	 * need to ensure that the output string doesn't have an extra
+	 * NUL in it!
+	 */
+	output[1] = setting[1] ? setting[1] : output[0];
+
+	setup_salt(ctx, salt);
+	/* Do it. */
+	do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */);
+
+	/* Now encode the result. */
+#if 0
+{
+	uint32_t l = (r0 >> 8);
+	q = (uint8_t *)output + 2;
+	*q++ = ascii64[(l >> 18) & 0x3f]; /* bits 31..26 of r0 */
+	*q++ = ascii64[(l >> 12) & 0x3f]; /* bits 25..20 of r0 */
+	*q++ = ascii64[(l >> 6) & 0x3f]; /* bits 19..14 of r0 */
+	*q++ = ascii64[l & 0x3f]; /* bits 13..8 of r0 */
+	l = ((r0 << 16) | (r1 >> 16));
+	*q++ = ascii64[(l >> 18) & 0x3f]; /* bits 7..2 of r0 */
+	*q++ = ascii64[(l >> 12) & 0x3f]; /* bits 1..2 of r0 and 31..28 of r1 */
+	*q++ = ascii64[(l >> 6) & 0x3f]; /* bits 27..22 of r1 */
+	*q++ = ascii64[l & 0x3f]; /* bits 21..16 of r1 */
+	l = r1 << 2;
+	*q++ = ascii64[(l >> 12) & 0x3f]; /* bits 15..10 of r1 */
+	*q++ = ascii64[(l >> 6) & 0x3f]; /* bits 9..4 of r1 */
+	*q++ = ascii64[l & 0x3f]; /* bits 3..0 of r1 + 00 */
+	*q = 0;
+}
+#else
+	/* Each call takes low-order 24 bits and stores 4 chars */
+	/* bits 31..8 of r0 */
+	to64_msb_first(output + 2, (r0 >> 8));
+	/* bits 7..0 of r0 and 31..16 of r1 */
+	to64_msb_first(output + 6, (r0 << 16) | (r1 >> 16));
+	/* bits 15..0 of r1 and two zero bits (plus extra zero byte) */
+	to64_msb_first(output + 10, (r1 << 8));
+	/* extra zero byte is encoded as '.', fixing it */
+	output[13] = '\0';
+#endif
+
+	return output;
+}
+
+#undef USE_PRECOMPUTED_u_sbox
+#undef USE_REPETITIVE_SPEEDUP
+#undef USE_ip_mask
+#undef USE_de_keys
+
+#undef C
+#undef init_perm
+#undef final_perm
+#undef m_sbox
+#undef D
+#undef const_ctx
+#undef saltbits
+#undef old_salt
+#undef old_rawkey0
+#undef old_rawkey1
+#undef un_pbox
+#undef inv_comp_perm
+#undef inv_key_perm
+#undef en_keysl
+#undef en_keysr
+#undef de_keysl
+#undef de_keysr
+#undef ip_maskl
+#undef ip_maskr
+#undef fp_maskl
+#undef fp_maskr
+#undef key_perm_maskl
+#undef key_perm_maskr
+#undef comp_maskl
+#undef comp_maskr
+#undef psbox
diff --git a/busybox-1.19.3/libbb/pw_encrypt_md5.c b/busybox-1.19.3/libbb/pw_encrypt_md5.c
new file mode 100644
index 0000000..889e09c
--- /dev/null
+++ b/busybox-1.19.3/libbb/pw_encrypt_md5.c
@@ -0,0 +1,161 @@
+/*
+ * MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ *
+ * Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ * rights reserved.
+ *
+ * License to copy and use this software is granted provided that it
+ * is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ * Algorithm" in all material mentioning or referencing this software
+ * or this function.
+ *
+ * License is also granted to make and use derivative works provided
+ * that such works are identified as "derived from the RSA Data
+ * Security, Inc. MD5 Message-Digest Algorithm" in all material
+ * mentioning or referencing the derived work.
+ *
+ * RSA Data Security, Inc. makes no representations concerning either
+ * the merchantability of this software or the suitability of this
+ * software for any particular purpose. It is provided "as is"
+ * without express or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this
+ * documentation and/or software.
+ *
+ * $FreeBSD: src/lib/libmd/md5c.c,v 1.9.2.1 1999/08/29 14:57:12 peter Exp $
+ *
+ * This code is the same as the code published by RSA Inc.  It has been
+ * edited for clarity and style only.
+ *
+ * ----------------------------------------------------------------------------
+ * The md5_crypt() function was taken from freeBSD's libcrypt and contains
+ * this license:
+ *    "THE BEER-WARE LICENSE" (Revision 42):
+ *     <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
+ *     can do whatever you want with this stuff. If we meet some day, and you think
+ *     this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
+ *
+ * $FreeBSD: src/lib/libcrypt/crypt.c,v 1.7.2.1 1999/08/29 14:56:33 peter Exp $
+ *
+ * ----------------------------------------------------------------------------
+ * On April 19th, 2001 md5_crypt() was modified to make it reentrant
+ * by Erik Andersen <andersen@uclibc.org>
+ *
+ *
+ * June 28, 2001             Manuel Novoa III
+ *
+ * "Un-inlined" code using loops and static const tables in order to
+ * reduce generated code size (on i386 from approx 4k to approx 2.5k).
+ *
+ * June 29, 2001             Manuel Novoa III
+ *
+ * Completely removed static PADDING array.
+ *
+ * Reintroduced the loop unrolling in MD5_Transform and added the
+ * MD5_SIZE_OVER_SPEED 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)
+ *       3    no unrolling and all 4 loops merged into one with switch
+ *               in each loop (glacial)
+ * On i386, sizes are roughly (-Os -fno-builtin):
+ *     0: 3k     1: 2.5k     2: 2.2k     3: 2k
+ *
+ * Since SuSv3 does not require crypt_r, modified again August 7, 2002
+ * by Erik Andersen to remove reentrance stuff...
+ */
+
+/*
+ * UNIX password
+ *
+ * Use MD5 for what it is best at...
+ */
+#define MD5_OUT_BUFSIZE 36
+static char *
+NOINLINE
+md5_crypt(char result[MD5_OUT_BUFSIZE], const unsigned char *pw, const unsigned char *salt)
+{
+	char *p;
+	unsigned char final[17]; /* final[16] exists only to aid in looping */
+	int sl, pl, i, pw_len;
+	md5_ctx_t ctx, ctx1;
+
+	/* NB: in busybox, "$1$" in salt is always present */
+
+	/* Refine the Salt first */
+
+	/* Get the length of the salt including "$1$" */
+	sl = 3;
+	while (salt[sl] && salt[sl] != '$' && sl < (3 + 8))
+		sl++;
+
+	/* Hash. the password first, since that is what is most unknown */
+	md5_begin(&ctx);
+	pw_len = strlen((char*)pw);
+	md5_hash(&ctx, pw, pw_len);
+
+	/* Then the salt including "$1$" */
+	md5_hash(&ctx, salt, sl);
+
+	/* Copy salt to result; skip "$1$" */
+	memcpy(result, salt, sl);
+	result[sl] = '$';
+	salt += 3;
+	sl -= 3;
+
+	/* Then just as many characters of the MD5(pw, salt, pw) */
+	md5_begin(&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(&ctx, final, pl > 16 ? 16 : pl);
+
+	/* Then something really weird... */
+	memset(final, 0, sizeof(final));
+	for (i = pw_len; i; i >>= 1) {
+		md5_hash(&ctx, ((i & 1) ? final : (const unsigned char *) pw), 1);
+	}
+	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
+	 * need 30 seconds to build a 1000 entry dictionary...
+	 */
+	for (i = 0; i < 1000; i++) {
+		md5_begin(&ctx1);
+		if (i & 1)
+			md5_hash(&ctx1, pw, pw_len);
+		else
+			md5_hash(&ctx1, final, 16);
+
+		if (i % 3)
+			md5_hash(&ctx1, salt, sl);
+
+		if (i % 7)
+			md5_hash(&ctx1, pw, pw_len);
+
+		if (i & 1)
+			md5_hash(&ctx1, final, 16);
+		else
+			md5_hash(&ctx1, pw, pw_len);
+		md5_end(&ctx1, final);
+	}
+
+	p = result + sl + 4; /* 12 bytes max (sl is up to 8 bytes) */
+
+	/* Add 5*4+2 = 22 bytes of hash, + NUL byte. */
+	final[16] = final[5];
+	for (i = 0; i < 5; i++) {
+		unsigned l = (final[i] << 16) | (final[i+6] << 8) | final[i+12];
+		p = to64(p, l, 4);
+	}
+	p = to64(p, final[11], 2);
+	*p = '\0';
+
+	/* Don't leave anything around in vm they could use. */
+	memset(final, 0, sizeof(final));
+
+	return result;
+}
diff --git a/busybox-1.19.3/libbb/pw_encrypt_sha.c b/busybox-1.19.3/libbb/pw_encrypt_sha.c
new file mode 100644
index 0000000..8aeaaca
--- /dev/null
+++ b/busybox-1.19.3/libbb/pw_encrypt_sha.c
@@ -0,0 +1,286 @@
+/* SHA256 and SHA512-based Unix crypt implementation.
+ * Released into the Public Domain by Ulrich Drepper <drepper@redhat.com>.
+ */
+
+/* Prefix for optional rounds specification.  */
+static const char str_rounds[] ALIGN1 = "rounds=%u$";
+
+/* Maximum salt string length.  */
+#define SALT_LEN_MAX 16
+/* Default number of rounds if not explicitly specified.  */
+#define ROUNDS_DEFAULT 5000
+/* Minimum number of rounds.  */
+#define ROUNDS_MIN 1000
+/* Maximum number of rounds.  */
+#define ROUNDS_MAX 999999999
+
+static char *
+NOINLINE
+sha_crypt(/*const*/ char *key_data, /*const*/ char *salt_data)
+{
+	void (*sha_begin)(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;
+
+	/* btw, sha256 needs [32] and uint32_t only */
+	struct {
+		unsigned char alt_result[64];
+		unsigned char temp_result[64];
+		union {
+			sha256_ctx_t x;
+			sha512_ctx_t y;
+		} ctx;
+		union {
+			sha256_ctx_t x;
+			sha512_ctx_t y;
+		} alt_ctx;
+	} L __attribute__((__aligned__(__alignof__(uint64_t))));
+#define alt_result  (L.alt_result )
+#define temp_result (L.temp_result)
+#define ctx         (L.ctx        )
+#define alt_ctx     (L.alt_ctx    )
+	unsigned salt_len;
+	unsigned key_len;
+	unsigned cnt;
+	unsigned rounds;
+	char *cp;
+	char is_sha512;
+
+	/* Analyze salt, construct already known part of result */
+	cnt = strlen(salt_data) + 1 + 43 + 1;
+	is_sha512 = salt_data[1];
+	if (is_sha512 == '6')
+		cnt += 43;
+	result = resptr = xzalloc(cnt); /* will provide NUL terminator */
+	*resptr++ = '$';
+	*resptr++ = is_sha512;
+	*resptr++ = '$';
+	rounds = ROUNDS_DEFAULT;
+	salt_data += 3;
+	if (strncmp(salt_data, str_rounds, 7) == 0) {
+		/* 7 == strlen("rounds=") */
+		char *endp;
+		cnt = bb_strtou(salt_data + 7, &endp, 10);
+		if (*endp == '$') {
+			salt_data = endp + 1;
+			rounds = cnt;
+			if (rounds < ROUNDS_MIN)
+				rounds = ROUNDS_MIN;
+			if (rounds > ROUNDS_MAX)
+				rounds = ROUNDS_MAX;
+			/* add "rounds=NNNNN$" to result */
+			resptr += sprintf(resptr, str_rounds, rounds);
+		}
+	}
+	salt_len = strchrnul(salt_data, '$') - salt_data;
+	if (salt_len > SALT_LEN_MAX)
+		salt_len = SALT_LEN_MAX;
+	/* xstrdup assures suitable alignment; also we will use it
+	   as a scratch space later. */
+	salt_data = xstrndup(salt_data, salt_len);
+	/* add "salt$" to result */
+	strcpy(resptr, salt_data);
+	resptr += salt_len;
+	*resptr++ = '$';
+	/* key data doesn't need much processing */
+	key_len = strlen(key_data);
+	key_data = xstrdup(key_data);
+
+	/* Which flavor of SHAnnn ops to use? */
+	sha_begin = (void*)sha256_begin;
+	sha_hash = (void*)sha256_hash;
+	sha_end = (void*)sha256_end;
+	_32or64 = 32;
+	if (is_sha512 == '6') {
+		sha_begin = (void*)sha512_begin;
+		sha_hash = (void*)sha512_hash;
+		sha_end = (void*)sha512_end;
+		_32or64 = 64;
+	}
+
+	/* Add KEY, SALT.  */
+	sha_begin(&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(&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(&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(&ctx, alt_result, _32or64);
+		else
+			sha_hash(&ctx, key_data, key_len);
+
+	/* Create intermediate result.  */
+	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(&alt_ctx, key_data, key_len);
+	sha_end(&alt_ctx, temp_result);
+
+	/* NB: past this point, raw key_data is not used anymore */
+
+	/* Create byte sequence P.  */
+#define p_bytes key_data /* reuse the buffer as it is of the key_len size */
+	cp = p_bytes; /* was: ... = alloca(key_len); */
+	for (cnt = key_len; cnt >= _32or64; cnt -= _32or64) {
+		cp = memcpy(cp, temp_result, _32or64);
+		cp += _32or64;
+	}
+	memcpy(cp, temp_result, cnt);
+
+	/* Start computation of S byte sequence.  */
+	/* 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(&alt_ctx, salt_data, salt_len);
+	sha_end(&alt_ctx, temp_result);
+
+	/* NB: past this point, raw salt_data is not used anymore */
+
+	/* Create byte sequence S.  */
+#define s_bytes salt_data /* reuse the buffer as it is of the salt_len size */
+	cp = s_bytes; /* was: ... = alloca(salt_len); */
+	for (cnt = salt_len; cnt >= _32or64; cnt -= _32or64) {
+		cp = memcpy(cp, temp_result, _32or64);
+		cp += _32or64;
+	}
+	memcpy(cp, temp_result, cnt);
+
+	/* Repeatedly run the collected hash value through SHA to burn
+	   CPU cycles.  */
+	for (cnt = 0; cnt < rounds; ++cnt) {
+		sha_begin(&ctx);
+
+		/* Add key or last result.  */
+		if ((cnt & 1) != 0)
+			sha_hash(&ctx, p_bytes, key_len);
+		else
+			sha_hash(&ctx, alt_result, _32or64);
+		/* Add salt for numbers not divisible by 3.  */
+		if (cnt % 3 != 0)
+			sha_hash(&ctx, s_bytes, salt_len);
+		/* Add key for numbers not divisible by 7.  */
+		if (cnt % 7 != 0)
+			sha_hash(&ctx, p_bytes, key_len);
+		/* Add key or last result.  */
+		if ((cnt & 1) != 0)
+			sha_hash(&ctx, alt_result, _32or64);
+		else
+			sha_hash(&ctx, p_bytes, key_len);
+
+		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); \
+} while (0)
+	if (is_sha512 == '5') {
+		unsigned i = 0;
+		while (1) {
+			unsigned j = i + 10;
+			unsigned k = i + 20;
+			if (j >= 30) j -= 30;
+			if (k >= 30) k -= 30;
+			b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4);
+			if (k == 29)
+				break;
+			i = k + 1;
+		}
+		b64_from_24bit(0, alt_result[31], alt_result[30], 3);
+		/* was:
+		b64_from_24bit(alt_result[0], alt_result[10], alt_result[20], 4);
+		b64_from_24bit(alt_result[21], alt_result[1], alt_result[11], 4);
+		b64_from_24bit(alt_result[12], alt_result[22], alt_result[2], 4);
+		b64_from_24bit(alt_result[3], alt_result[13], alt_result[23], 4);
+		b64_from_24bit(alt_result[24], alt_result[4], alt_result[14], 4);
+		b64_from_24bit(alt_result[15], alt_result[25], alt_result[5], 4);
+		b64_from_24bit(alt_result[6], alt_result[16], alt_result[26], 4);
+		b64_from_24bit(alt_result[27], alt_result[7], alt_result[17], 4);
+		b64_from_24bit(alt_result[18], alt_result[28], alt_result[8], 4);
+		b64_from_24bit(alt_result[9], alt_result[19], alt_result[29], 4);
+		b64_from_24bit(0, alt_result[31], alt_result[30], 3);
+		*/
+	} else {
+		unsigned i = 0;
+		while (1) {
+			unsigned j = i + 21;
+			unsigned k = i + 42;
+			if (j >= 63) j -= 63;
+			if (k >= 63) k -= 63;
+			b64_from_24bit(alt_result[i], alt_result[j], alt_result[k], 4);
+			if (j == 20)
+				break;
+			i = j + 1;
+		}
+		b64_from_24bit(0, 0, alt_result[63], 2);
+		/* was:
+		b64_from_24bit(alt_result[0], alt_result[21], alt_result[42], 4);
+		b64_from_24bit(alt_result[22], alt_result[43], alt_result[1], 4);
+		b64_from_24bit(alt_result[44], alt_result[2], alt_result[23], 4);
+		b64_from_24bit(alt_result[3], alt_result[24], alt_result[45], 4);
+		b64_from_24bit(alt_result[25], alt_result[46], alt_result[4], 4);
+		b64_from_24bit(alt_result[47], alt_result[5], alt_result[26], 4);
+		b64_from_24bit(alt_result[6], alt_result[27], alt_result[48], 4);
+		b64_from_24bit(alt_result[28], alt_result[49], alt_result[7], 4);
+		b64_from_24bit(alt_result[50], alt_result[8], alt_result[29], 4);
+		b64_from_24bit(alt_result[9], alt_result[30], alt_result[51], 4);
+		b64_from_24bit(alt_result[31], alt_result[52], alt_result[10], 4);
+		b64_from_24bit(alt_result[53], alt_result[11], alt_result[32], 4);
+		b64_from_24bit(alt_result[12], alt_result[33], alt_result[54], 4);
+		b64_from_24bit(alt_result[34], alt_result[55], alt_result[13], 4);
+		b64_from_24bit(alt_result[56], alt_result[14], alt_result[35], 4);
+		b64_from_24bit(alt_result[15], alt_result[36], alt_result[57], 4);
+		b64_from_24bit(alt_result[37], alt_result[58], alt_result[16], 4);
+		b64_from_24bit(alt_result[59], alt_result[17], alt_result[38], 4);
+		b64_from_24bit(alt_result[18], alt_result[39], alt_result[60], 4);
+		b64_from_24bit(alt_result[40], alt_result[61], alt_result[19], 4);
+		b64_from_24bit(alt_result[62], alt_result[20], alt_result[41], 4);
+		b64_from_24bit(0, 0, alt_result[63], 2);
+		*/
+	}
+	/* *resptr = '\0'; - xzalloc did it */
+#undef b64_from_24bit
+
+	/* Clear the buffer for the intermediate result so that people
+	   attaching to processes or reading core dumps cannot get any
+	   information.  */
+	memset(&L, 0, sizeof(L)); /* [alt]_ctx and XXX_result buffers */
+	memset(key_data, 0, key_len); /* also p_bytes */
+	memset(salt_data, 0, salt_len); /* also s_bytes */
+	free(key_data);
+	free(salt_data);
+#undef p_bytes
+#undef s_bytes
+
+	return result;
+#undef alt_result
+#undef temp_result
+#undef ctx
+#undef alt_ctx
+}
diff --git a/busybox-1.19.3/libbb/read.c b/busybox-1.19.3/libbb/read.c
new file mode 100644
index 0000000..5906bc2
--- /dev/null
+++ b/busybox-1.19.3/libbb/read.c
@@ -0,0 +1,72 @@
+/* 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 source tree.
+ */
+#include "libbb.h"
+
+ssize_t FAST_FUNC safe_read(int fd, void *buf, size_t count)
+{
+	ssize_t n;
+
+	do {
+		n = read(fd, buf, count);
+	} while (n < 0 && errno == EINTR);
+
+	return n;
+}
+
+/*
+ * Read all of the supplied buffer from a file.
+ * This does multiple reads as necessary.
+ * Returns the amount read, or -1 on an error.
+ * A short read is returned on an end of file.
+ */
+ssize_t FAST_FUNC full_read(int fd, void *buf, size_t len)
+{
+	ssize_t cc;
+	ssize_t total;
+
+	total = 0;
+
+	while (len) {
+		cc = safe_read(fd, buf, len);
+
+		if (cc < 0) {
+			if (total) {
+				/* we already have some! */
+				/* user can do another read to know the error code */
+				return total;
+			}
+			return cc; /* read() returns -1 on failure. */
+		}
+		if (cc == 0)
+			break;
+		buf = ((char *)buf) + cc;
+		total += cc;
+		len -= cc;
+	}
+
+	return total;
+}
+
+ssize_t FAST_FUNC read_close(int fd, void *buf, size_t size)
+{
+	/*int e;*/
+	size = full_read(fd, buf, size);
+	/*e = errno;*/
+	close(fd);
+	/*errno = e;*/
+	return size;
+}
+
+ssize_t FAST_FUNC open_read_close(const char *filename, void *buf, size_t size)
+{
+	int fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return fd;
+	return read_close(fd, buf, size);
+}
diff --git a/busybox-1.19.3/libbb/read_key.c b/busybox-1.19.3/libbb/read_key.c
new file mode 100644
index 0000000..5dcd19c
--- /dev/null
+++ b/busybox-1.19.3/libbb/read_key.c
@@ -0,0 +1,271 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2008 Rob Landley <rob@landley.net>
+ * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+int64_t FAST_FUNC read_key(int fd, char *buffer, int timeout)
+{
+	struct pollfd pfd;
+	const char *seq;
+	int n;
+
+	/* Known escape sequences for cursor and function keys */
+	static const char esccmds[] ALIGN1 = {
+		'O','A'        |0x80,KEYCODE_UP      ,
+		'O','B'        |0x80,KEYCODE_DOWN    ,
+		'O','C'        |0x80,KEYCODE_RIGHT   ,
+		'O','D'        |0x80,KEYCODE_LEFT    ,
+		'O','H'        |0x80,KEYCODE_HOME    ,
+		'O','F'        |0x80,KEYCODE_END     ,
+#if 0
+		'O','P'        |0x80,KEYCODE_FUN1    ,
+		/* [ESC] ESC O [2] P - [Alt-][Shift-]F1 */
+		/* ESC [ O 1 ; 2 P - Shift-F1 */
+		/* ESC [ O 1 ; 3 P - Alt-F1 */
+		/* ESC [ O 1 ; 4 P - Alt-Shift-F1 */
+		/* ESC [ O 1 ; 5 P - Ctrl-F1 */
+		/* ESC [ O 1 ; 6 P - Ctrl-Shift-F1 */
+		'O','Q'        |0x80,KEYCODE_FUN2    ,
+		'O','R'        |0x80,KEYCODE_FUN3    ,
+		'O','S'        |0x80,KEYCODE_FUN4    ,
+#endif
+		'[','A'        |0x80,KEYCODE_UP      ,
+		'[','B'        |0x80,KEYCODE_DOWN    ,
+		'[','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 ; 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> */
+		'[','H'        |0x80,KEYCODE_HOME    , /* xterm */
+		/* [ESC] ESC [ [2] H - [Alt-][Shift-]Home */
+		'[','F'        |0x80,KEYCODE_END     , /* xterm */
+		'[','1','~'    |0x80,KEYCODE_HOME    , /* vt100? linux vt? or what? */
+		'[','2','~'    |0x80,KEYCODE_INSERT  ,
+		/* ESC [ 2 ; 3 ~ - Alt-Insert */
+		'[','3','~'    |0x80,KEYCODE_DELETE  ,
+		/* [ESC] ESC [ 3 [;2] ~ - [Alt-][Shift-]Delete */
+		/* ESC [ 3 ; 3 ~ - Alt-Delete */
+		/* ESC [ 3 ; 5 ~ - Ctrl-Delete */
+		'[','4','~'    |0x80,KEYCODE_END     , /* vt100? linux vt? or what? */
+		'[','5','~'    |0x80,KEYCODE_PAGEUP  ,
+		/* ESC [ 5 ; 3 ~ - Alt-PgUp */
+		/* ESC [ 5 ; 5 ~ - Ctrl-PgUp */
+		/* ESC [ 5 ; 7 ~ - Ctrl-Alt-PgUp */
+		'[','6','~'    |0x80,KEYCODE_PAGEDOWN,
+		'[','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','5','~'|0x80,KEYCODE_FUN5    ,
+		/* [ESC] ESC [ 1 5 [;2] ~ - [Alt-][Shift-]F5 */
+		'[','1','7','~'|0x80,KEYCODE_FUN6    ,
+		'[','1','8','~'|0x80,KEYCODE_FUN7    ,
+		'[','1','9','~'|0x80,KEYCODE_FUN8    ,
+		'[','2','0','~'|0x80,KEYCODE_FUN9    ,
+		'[','2','1','~'|0x80,KEYCODE_FUN10   ,
+		'[','2','3','~'|0x80,KEYCODE_FUN11   ,
+		'[','2','4','~'|0x80,KEYCODE_FUN12   ,
+		/* ESC [ 2 4 ; 2 ~ - Shift-F12 */
+		/* ESC [ 2 4 ; 3 ~ - Alt-F12 */
+		/* ESC [ 2 4 ; 4 ~ - Alt-Shift-F12 */
+		/* ESC [ 2 4 ; 5 ~ - Ctrl-F12 */
+		/* ESC [ 2 4 ; 6 ~ - Ctrl-Shift-F12 */
+#endif
+		/* '[','1',';','5','A' |0x80,KEYCODE_CTRL_UP   , - unused */
+		/* '[','1',';','5','B' |0x80,KEYCODE_CTRL_DOWN , - unused */
+		'[','1',';','5','C' |0x80,KEYCODE_CTRL_RIGHT,
+		'[','1',';','5','D' |0x80,KEYCODE_CTRL_LEFT ,
+		0
+		/* ESC [ Z - Shift-Tab */
+	};
+
+	pfd.fd = fd;
+	pfd.events = POLLIN;
+
+	buffer++; /* saved chars counter is in buffer[-1] now */
+
+ start_over:
+	errno = 0;
+	n = (unsigned char)buffer[-1];
+	if (n == 0) {
+		/* If no data, wait for input.
+		 * If requested, wait TIMEOUT ms. TIMEOUT = -1 is useful
+		 * if fd can be in non-blocking mode.
+		 */
+		if (timeout >= -1) {
+			if (safe_poll(&pfd, 1, timeout) == 0) {
+				/* Timed out */
+				errno = EAGAIN;
+				return -1;
+			}
+		}
+		/* It is tempting to read more than one byte here,
+		 * but it breaks pasting. Example: at shell prompt,
+		 * user presses "c","a","t" and then pastes "\nline\n".
+		 * When we were reading 3 bytes here, we were eating
+		 * "li" too, and cat was getting wrong input.
+		 */
+		n = safe_read(fd, buffer, 1);
+		if (n <= 0)
+			return -1;
+	}
+
+	{
+		unsigned char c = buffer[0];
+		n--;
+		if (n)
+			memmove(buffer, buffer + 1, n);
+		/* Only ESC starts ESC sequences */
+		if (c != 27) {
+			buffer[-1] = n;
+			return c;
+		}
+	}
+
+	/* Loop through known ESC sequences */
+	seq = esccmds;
+	while (*seq != '\0') {
+		/* n - position in sequence we did not read yet */
+		int i = 0; /* position in sequence to compare */
+
+		/* Loop through chars in this sequence */
+		while (1) {
+			/* So far escape sequence matched up to [i-1] */
+			if (n <= i) {
+				/* Need more chars, read another one if it wouldn't block.
+				 * Note that escape sequences come in as a unit,
+				 * so if we block for long it's not really an escape sequence.
+				 * Timeout is needed to reconnect escape sequences
+				 * split up by transmission over a serial console. */
+				if (safe_poll(&pfd, 1, 50) == 0) {
+					/* No more data!
+					 * Array is sorted from shortest to longest,
+					 * we can't match anything later in array -
+					 * anything later is longer than this seq.
+					 * Break out of both loops. */
+					goto got_all;
+				}
+				errno = 0;
+				if (safe_read(fd, buffer + n, 1) <= 0) {
+					/* If EAGAIN, then fd is O_NONBLOCK and poll lied:
+					 * in fact, there is no data. */
+					if (errno != EAGAIN) {
+						/* otherwise: it's EOF/error */
+						buffer[-1] = 0;
+						return -1;
+					}
+					goto got_all;
+				}
+				n++;
+			}
+			if (buffer[i] != (seq[i] & 0x7f)) {
+				/* This seq doesn't match, go to next */
+				seq += i;
+				/* Forward to last char */
+				while (!(*seq & 0x80))
+					seq++;
+				/* Skip it and the keycode which follows */
+				seq += 2;
+				break;
+			}
+			if (seq[i] & 0x80) {
+				/* Entire seq matched */
+				n = 0;
+				/* n -= i; memmove(...);
+				 * would be more correct,
+				 * but we never read ahead that much,
+				 * and n == i here. */
+				buffer[-1] = 0;
+				return (signed char)seq[i+1];
+			}
+			i++;
+		}
+	}
+	/* We did not find matching sequence.
+	 * We possibly read and stored more input in buffer[] by now.
+	 * n = bytes read. Try to read more until we time out.
+	 */
+	while (n < KEYCODE_BUFFER_SIZE-1) { /* 1 for count byte at buffer[-1] */
+		if (safe_poll(&pfd, 1, 50) == 0) {
+			/* No more data! */
+			break;
+		}
+		errno = 0;
+		if (safe_read(fd, buffer + n, 1) <= 0) {
+			/* If EAGAIN, then fd is O_NONBLOCK and poll lied:
+			 * in fact, there is no data. */
+			if (errno != EAGAIN) {
+				/* otherwise: it's EOF/error */
+				buffer[-1] = 0;
+				return -1;
+			}
+			break;
+		}
+		n++;
+		/* Try to decipher "ESC [ NNN ; NNN R" sequence */
+		if ((ENABLE_FEATURE_EDITING_ASK_TERMINAL
+		    || ENABLE_FEATURE_VI_ASK_TERMINAL
+		    || ENABLE_FEATURE_LESS_ASK_TERMINAL
+		    )
+		 && n >= 5
+		 && buffer[0] == '['
+		 && buffer[n-1] == 'R'
+		 && isdigit(buffer[1])
+		) {
+			char *end;
+			unsigned long row, col;
+
+			row = strtoul(buffer + 1, &end, 10);
+			if (*end != ';' || !isdigit(end[1]))
+				continue;
+			col = strtoul(end + 1, &end, 10);
+			if (*end != 'R')
+				continue;
+			if (row < 1 || col < 1 || (row | col) > 0x7fff)
+				continue;
+
+			buffer[-1] = 0;
+			/* Pack into "1 <row15bits> <col16bits>" 32-bit sequence */
+			col |= (((-1 << 15) | row) << 16);
+			/* Return it in high-order word */
+			return ((int64_t) col << 32) | (uint32_t)KEYCODE_CURSOR_POS;
+		}
+	}
+ got_all:
+
+	if (n <= 1) {
+		/* Alt-x is usually returned as ESC x.
+		 * Report ESC, x is remembered for the next call.
+		 */
+		buffer[-1] = n;
+		return 27;
+	}
+
+	/* We were doing "buffer[-1] = n; return c;" here, but this results
+	 * in unknown key sequences being interpreted as ESC + garbage.
+	 * This was not useful. Pretend there was no key pressed,
+	 * go and wait for a new keypress:
+	 */
+	buffer[-1] = 0;
+	goto start_over;
+}
+
+void FAST_FUNC read_key_ungets(char *buffer, const char *str, unsigned len)
+{
+	unsigned cur_len = (unsigned char)buffer[0];
+	if (len > KEYCODE_BUFFER_SIZE-1 - cur_len)
+		len = KEYCODE_BUFFER_SIZE-1 - cur_len;
+	memcpy(buffer + 1 + cur_len, str, len);
+	buffer[0] += len;
+}
diff --git a/busybox-1.19.3/libbb/read_printf.c b/busybox-1.19.3/libbb/read_printf.c
new file mode 100644
index 0000000..192f83d
--- /dev/null
+++ b/busybox-1.19.3/libbb/read_printf.c
@@ -0,0 +1,375 @@
+/* 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 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 "archive.h"
+#endif
+
+
+/* Suppose that you are a shell. You start child processes.
+ * They work and eventually exit. You want to get user input.
+ * You read stdin. But what happens if last child switched
+ * its stdin into O_NONBLOCK mode?
+ *
+ * *** SURPRISE! It will affect the parent too! ***
+ * *** BIG SURPRISE! It stays even after child exits! ***
+ *
+ * This is a design bug in UNIX API.
+ *      fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
+ * will set nonblocking mode not only on _your_ stdin, but
+ * also on stdin of your parent, etc.
+ *
+ * In general,
+ *      fd2 = dup(fd1);
+ *      fcntl(fd2, F_SETFL, fcntl(fd2, F_GETFL) | O_NONBLOCK);
+ * sets both fd1 and fd2 to O_NONBLOCK. This includes cases
+ * where duping is done implicitly by fork() etc.
+ *
+ * We need
+ *      fcntl(fd2, F_SETFD, fcntl(fd2, F_GETFD) | O_NONBLOCK);
+ * (note SETFD, not SETFL!) but such thing doesn't exist.
+ *
+ * Alternatively, we need nonblocking_read(fd, ...) which doesn't
+ * require O_NONBLOCK dance at all. Actually, it exists:
+ *      n = recv(fd, buf, len, MSG_DONTWAIT);
+ *      "MSG_DONTWAIT:
+ *      Enables non-blocking operation; if the operation
+ *      would block, EAGAIN is returned."
+ * but recv() works only for sockets!
+ *
+ * So far I don't see any good solution, I can only propose
+ * that affected readers should be careful and use this routine,
+ * 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_immune_read(int fd, void *buf, size_t count, int loop_on_EINTR)
+{
+	struct pollfd pfd[1];
+	ssize_t n;
+
+	while (1) {
+		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;
+		/* 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, size_t *maxsz_p)
+{
+	char *p;
+	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:
+			buf = xrealloc(buf, sz + 128);
+			p = buf + sz;
+			sz += 128;
+		}
+		if (nonblock_immune_read(fd, p, 1, /*loop_on_EINTR:*/ 1) != 1) {
+			/* EOF/error */
+			if (p == buf) { /* we read nothing */
+				free(buf);
+				return NULL;
+			}
+			break;
+		}
+		if (*p == '\n')
+			break;
+		p++;
+	}
+	*p = '\0';
+	if (maxsz_p)
+		*maxsz_p  = p - buf;
+	p++;
+	return xrealloc(buf, p - buf);
+}
+
+// Read (potentially big) files in one go. File size is estimated
+// by stat. Extra '\0' byte is appended.
+void* FAST_FUNC xmalloc_read(int fd, size_t *maxsz_p)
+{
+	char *buf;
+	size_t size, rd_size, total;
+	size_t to_read;
+	struct stat st;
+
+	to_read = maxsz_p ? *maxsz_p : (INT_MAX - 4095); /* max to read */
+
+	/* Estimate file size */
+	st.st_size = 0; /* in case fstat fails, assume 0 */
+	fstat(fd, &st);
+	/* /proc/N/stat files report st_size 0 */
+	/* In order to make such files readable, we add small const */
+	size = (st.st_size | 0x3ff) + 1;
+
+	total = 0;
+	buf = NULL;
+	while (1) {
+		if (to_read < size)
+			size = to_read;
+		buf = xrealloc(buf, total + size + 1);
+		rd_size = full_read(fd, buf + total, size);
+		if ((ssize_t)rd_size == (ssize_t)(-1)) { /* error */
+			free(buf);
+			return NULL;
+		}
+		total += rd_size;
+		if (rd_size < size) /* EOF */
+			break;
+		if (to_read <= rd_size)
+			break;
+		to_read -= rd_size;
+		/* grow by 1/8, but in [1k..64k] bounds */
+		size = ((total / 8) | 0x3ff) + 1;
+		if (size > 64*1024)
+			size = 64*1024;
+	}
+	buf = xrealloc(buf, total + 1);
+	buf[total] = '\0';
+
+	if (maxsz_p)
+		*maxsz_p = total;
+	return buf;
+}
+
+#ifdef USING_LSEEK_TO_GET_SIZE
+/* Alternatively, file size can be obtained by lseek to the end.
+ * The code is slightly bigger. Retained in case fstat approach
+ * will not work for some weird cases (/proc, block devices, etc).
+ * (NB: lseek also can fail to work for some weird files) */
+
+// Read (potentially big) files in one go. File size is estimated by
+// lseek to end.
+void* FAST_FUNC xmalloc_open_read_close(const char *filename, size_t *maxsz_p)
+{
+	char *buf;
+	size_t size;
+	int fd;
+	off_t len;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	/* /proc/N/stat files report len 0 here */
+	/* In order to make such files readable, we add small const */
+	size = 0x3ff; /* read only 1k on unseekable files */
+	len = lseek(fd, 0, SEEK_END) | 0x3ff; /* + up to 1k */
+	if (len != (off_t)-1) {
+		xlseek(fd, 0, SEEK_SET);
+		size = maxsz_p ? *maxsz_p : (INT_MAX - 4095);
+		if (len < size)
+			size = len;
+	}
+
+	buf = xmalloc(size + 1);
+	size = read_close(fd, buf, size);
+	if ((ssize_t)size < 0) {
+		free(buf);
+		return NULL;
+	}
+	buf = xrealloc(buf, size + 1);
+	buf[size] = '\0';
+
+	if (maxsz_p)
+		*maxsz_p = size;
+	return buf;
+}
+#endif
+
+// Read (potentially big) files in one go. File size is estimated
+// by stat.
+void* FAST_FUNC xmalloc_open_read_close(const char *filename, size_t *maxsz_p)
+{
+	char *buf;
+	int fd;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	buf = xmalloc_read(fd, maxsz_p);
+	close(fd);
+	return buf;
+}
+
+/* Die with an error message if we can't read the entire buffer. */
+void FAST_FUNC xread(int fd, void *buf, size_t count)
+{
+	if (count) {
+		ssize_t size = full_read(fd, buf, count);
+		if ((size_t)size != count)
+			bb_error_msg_and_die("short read");
+	}
+}
+
+/* Die with an error message if we can't read one character. */
+unsigned char FAST_FUNC xread_char(int fd)
+{
+	char tmp;
+	xread(fd, &tmp, 1);
+	return tmp;
+}
+
+void* FAST_FUNC xmalloc_xopen_read_close(const char *filename, size_t *maxsz_p)
+{
+	void *buf = xmalloc_open_read_close(filename, maxsz_p);
+	if (!buf)
+		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;
+}
diff --git a/busybox-1.19.3/libbb/recursive_action.c b/busybox-1.19.3/libbb/recursive_action.c
new file mode 100644
index 0000000..b5cf7c0
--- /dev/null
+++ b/busybox-1.19.3/libbb/recursive_action.c
@@ -0,0 +1,156 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+#undef DEBUG_RECURS_ACTION
+
+/*
+ * Walk down all the directories under the specified
+ * location, and do something (something specified
+ * by the fileAction and dirAction function pointers).
+ *
+ * Unfortunately, while nftw(3) could replace this and reduce
+ * code size a bit, nftw() wasn't supported before GNU libc 2.1,
+ * and so isn't sufficiently portable to take over since glibc2.1
+ * is so stinking huge.
+ */
+
+static int FAST_FUNC true_action(const char *fileName UNUSED_PARAM,
+		struct stat *statbuf UNUSED_PARAM,
+		void* userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	return TRUE;
+}
+
+/* fileAction return value of 0 on any file in directory will make
+ * recursive_action() return 0, but it doesn't stop directory traversal
+ * (fileAction/dirAction will be called on each file).
+ *
+ * If !ACTION_RECURSE, dirAction is called on the directory and its
+ * return value is returned from recursive_action(). No recursion.
+ *
+ * If ACTION_RECURSE, recursive_action() is called on each directory.
+ * If any one of these calls returns 0, current recursive_action() returns 0.
+ *
+ * If ACTION_DEPTHFIRST, dirAction is called after recurse.
+ * If it returns 0, the warning is printed and recursive_action() returns 0.
+ *
+ * If !ACTION_DEPTHFIRST, dirAction is called before we recurse.
+ * Return value of 0 (FALSE) or 2 (SKIP) prevents recursion
+ * into that directory, instead recursive_action() returns 0 (if FALSE)
+ * or 1 (if SKIP)
+ *
+ * ACTION_FOLLOWLINKS mainly controls handling of links to dirs.
+ * 0: lstat(statbuf). Calls fileAction on link name even if points to dir.
+ * 1: stat(statbuf). Calls dirAction and optionally recurse on link to dir.
+ */
+
+int FAST_FUNC recursive_action(const char *fileName,
+		unsigned flags,
+		int FAST_FUNC (*fileAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
+		int FAST_FUNC (*dirAction)(const char *fileName, struct stat *statbuf, void* userData, int depth),
+		void* userData,
+		unsigned depth)
+{
+	struct stat statbuf;
+	unsigned follow;
+	int status;
+	DIR *dir;
+	struct dirent *next;
+
+	if (!fileAction) fileAction = true_action;
+	if (!dirAction) dirAction = true_action;
+
+	follow = ACTION_FOLLOWLINKS;
+	if (depth == 0)
+		follow = ACTION_FOLLOWLINKS | ACTION_FOLLOWLINKS_L0;
+	follow &= flags;
+	status = (follow ? stat : lstat)(fileName, &statbuf);
+	if (status < 0) {
+#ifdef DEBUG_RECURS_ACTION
+		bb_error_msg("status=%d flags=%x", status, flags);
+#endif
+		if ((flags & ACTION_DANGLING_OK)
+		 && errno == ENOENT
+		 && lstat(fileName, &statbuf) == 0
+		) {
+			/* Dangling link */
+			return fileAction(fileName, &statbuf, userData, depth);
+		}
+		goto done_nak_warn;
+	}
+
+	/* If S_ISLNK(m), then we know that !S_ISDIR(m).
+	 * Then we can skip checking first part: if it is true, then
+	 * (!dir) is also true! */
+	if ( /* (!(flags & ACTION_FOLLOWLINKS) && S_ISLNK(statbuf.st_mode)) || */
+	 !S_ISDIR(statbuf.st_mode)
+	) {
+		return fileAction(fileName, &statbuf, userData, depth);
+	}
+
+	/* It's a directory (or a link to one, and followLinks is set) */
+
+	if (!(flags & ACTION_RECURSE)) {
+		return dirAction(fileName, &statbuf, userData, depth);
+	}
+
+	if (!(flags & ACTION_DEPTHFIRST)) {
+		status = dirAction(fileName, &statbuf, userData, depth);
+		if (!status)
+			goto done_nak_warn;
+		if (status == SKIP)
+			return TRUE;
+	}
+
+	dir = opendir(fileName);
+	if (!dir) {
+		/* findutils-4.1.20 reports this */
+		/* (i.e. it doesn't silently return with exit code 1) */
+		/* To trigger: "find -exec rm -rf {} \;" */
+		goto done_nak_warn;
+	}
+	status = TRUE;
+	while ((next = readdir(dir)) != NULL) {
+		char *nextFile;
+
+		nextFile = concat_subpath_file(fileName, next->d_name);
+		if (nextFile == NULL)
+			continue;
+		/* process every file (NB: ACTION_RECURSE is set in flags) */
+		if (!recursive_action(nextFile, flags, fileAction, dirAction,
+						userData, depth + 1))
+			status = FALSE;
+//		s = recursive_action(nextFile, flags, fileAction, dirAction,
+//						userData, depth + 1);
+		free(nextFile);
+//#define RECURSE_RESULT_ABORT 3
+//		if (s == RECURSE_RESULT_ABORT) {
+//			closedir(dir);
+//			return s;
+//		}
+//		if (s == FALSE)
+//			status = FALSE;
+	}
+	closedir(dir);
+
+	if (flags & ACTION_DEPTHFIRST) {
+		if (!dirAction(fileName, &statbuf, userData, depth))
+			goto done_nak_warn;
+	}
+
+	return status;
+
+ done_nak_warn:
+	if (!(flags & ACTION_QUIET))
+		bb_simple_perror_msg(fileName);
+	return FALSE;
+}
diff --git a/busybox-1.19.3/libbb/remove_file.c b/busybox-1.19.3/libbb/remove_file.c
new file mode 100644
index 0000000..c6531a0
--- /dev/null
+++ b/busybox-1.19.3/libbb/remove_file.c
@@ -0,0 +1,102 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini remove_file implementation for busybox
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Used from NOFORK applets. Must not allocate anything */
+
+int FAST_FUNC remove_file(const char *path, int flags)
+{
+	struct stat path_stat;
+
+	if (lstat(path, &path_stat) < 0) {
+		if (errno != ENOENT) {
+			bb_perror_msg("can't stat '%s'", path);
+			return -1;
+		}
+		if (!(flags & FILEUTILS_FORCE)) {
+			bb_perror_msg("can't remove '%s'", path);
+			return -1;
+		}
+		return 0;
+	}
+
+	if (S_ISDIR(path_stat.st_mode)) {
+		DIR *dp;
+		struct dirent *d;
+		int status = 0;
+
+		if (!(flags & FILEUTILS_RECUR)) {
+			bb_error_msg("%s: is a directory", path);
+			return -1;
+		}
+
+		if ((!(flags & FILEUTILS_FORCE) && access(path, W_OK) < 0 && isatty(0))
+		 || (flags & FILEUTILS_INTERACTIVE)
+		) {
+			fprintf(stderr, "%s: descend into directory '%s'? ", applet_name,
+					path);
+			if (!bb_ask_confirmation())
+				return 0;
+		}
+
+		dp = opendir(path);
+		if (dp == NULL) {
+			return -1;
+		}
+
+		while ((d = readdir(dp)) != NULL) {
+			char *new_path;
+
+			new_path = concat_subpath_file(path, d->d_name);
+			if (new_path == NULL)
+				continue;
+			if (remove_file(new_path, flags) < 0)
+				status = -1;
+			free(new_path);
+		}
+
+		if (closedir(dp) < 0) {
+			bb_perror_msg("can't close '%s'", path);
+			return -1;
+		}
+
+		if (flags & FILEUTILS_INTERACTIVE) {
+			fprintf(stderr, "%s: remove directory '%s'? ", applet_name, path);
+			if (!bb_ask_confirmation())
+				return status;
+		}
+
+		if (rmdir(path) < 0) {
+			bb_perror_msg("can't remove '%s'", path);
+			return -1;
+		}
+
+		return status;
+	}
+
+	/* !ISDIR */
+	if ((!(flags & FILEUTILS_FORCE)
+	     && access(path, W_OK) < 0
+	     && !S_ISLNK(path_stat.st_mode)
+	     && isatty(0))
+	 || (flags & FILEUTILS_INTERACTIVE)
+	) {
+		fprintf(stderr, "%s: remove '%s'? ", applet_name, path);
+		if (!bb_ask_confirmation())
+			return 0;
+	}
+
+	if (unlink(path) < 0) {
+		bb_perror_msg("can't remove '%s'", path);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/libbb/rtc.c b/busybox-1.19.3/libbb/rtc.c
new file mode 100644
index 0000000..97455e8
--- /dev/null
+++ b/busybox-1.19.3/libbb/rtc.c
@@ -0,0 +1,83 @@
+/*
+ * Common RTC functions
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "rtc_.h"
+
+#if ENABLE_FEATURE_HWCLOCK_ADJTIME_FHS
+# define ADJTIME_PATH "/var/lib/hwclock/adjtime"
+#else
+# define ADJTIME_PATH "/etc/adjtime"
+#endif
+
+int FAST_FUNC rtc_adjtime_is_utc(void)
+{
+	int utc = 0;
+	FILE *f = fopen_for_read(ADJTIME_PATH);
+
+	if (f) {
+		char buffer[128];
+
+		while (fgets(buffer, sizeof(buffer), f)) {
+			if (strncmp(buffer, "UTC", 3) == 0) {
+				utc = 1;
+				break;
+			}
+		}
+		fclose(f);
+	}
+
+	return utc;
+}
+
+int FAST_FUNC rtc_xopen(const char **default_rtc, int flags)
+{
+	int rtc;
+
+	if (!*default_rtc) {
+		*default_rtc = "/dev/rtc";
+		rtc = open(*default_rtc, flags);
+		if (rtc >= 0)
+			return rtc;
+		*default_rtc = "/dev/rtc0";
+		rtc = open(*default_rtc, flags);
+		if (rtc >= 0)
+			return rtc;
+		*default_rtc = "/dev/misc/rtc";
+	}
+
+	return xopen(*default_rtc, flags);
+}
+
+void FAST_FUNC rtc_read_tm(struct tm *ptm, int fd)
+{
+	memset(ptm, 0, sizeof(*ptm));
+	xioctl(fd, RTC_RD_TIME, ptm);
+	ptm->tm_isdst = -1; /* "not known" */
+}
+
+time_t FAST_FUNC rtc_tm2time(struct tm *ptm, int utc)
+{
+	char *oldtz = oldtz; /* for compiler */
+	time_t t;
+
+	if (utc) {
+		oldtz = getenv("TZ");
+		putenv((char*)"TZ=UTC0");
+		tzset();
+	}
+
+	t = mktime(ptm);
+
+	if (utc) {
+		unsetenv("TZ");
+		if (oldtz)
+			putenv(oldtz - 3);
+		tzset();
+	}
+
+	return t;
+}
diff --git a/busybox-1.19.3/libbb/run_shell.c b/busybox-1.19.3/libbb/run_shell.c
new file mode 100644
index 0000000..4d92c3c
--- /dev/null
+++ b/busybox-1.19.3/libbb/run_shell.c
@@ -0,0 +1,92 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * 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. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``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 JULIE HAUGH OR CONTRIBUTORS 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.
+ */
+
+#include "libbb.h"
+#if ENABLE_SELINUX
+#include <selinux/selinux.h>  /* for setexeccon  */
+#endif
+
+#if ENABLE_SELINUX
+static security_context_t current_sid;
+
+void FAST_FUNC renew_current_security_context(void)
+{
+	freecon(current_sid);  /* Release old context  */
+	getcon(&current_sid);  /* update */
+}
+void FAST_FUNC set_current_security_context(security_context_t sid)
+{
+	freecon(current_sid);  /* Release old context  */
+	current_sid = sid;
+}
+
+#endif
+
+/* Run SHELL, or DEFAULT_SHELL if SHELL is "" or NULL.
+ * If COMMAND is nonzero, pass it to the shell with the -c option.
+ * If ADDITIONAL_ARGS is nonzero, pass it to the shell as more
+ * arguments.  */
+void FAST_FUNC run_shell(const char *shell, int loginshell, const char *command, const char **additional_args)
+{
+	const char **args;
+	int argno;
+	int additional_args_cnt = 0;
+
+	for (args = additional_args; args && *args; args++)
+		additional_args_cnt++;
+
+	args = xmalloc(sizeof(char*) * (4 + additional_args_cnt));
+
+	if (!shell || !shell[0])
+		shell = DEFAULT_SHELL;
+
+	args[0] = bb_get_last_path_component_nostrip(shell);
+	if (loginshell)
+		args[0] = xasprintf("-%s", args[0]);
+	argno = 1;
+	if (command) {
+		args[argno++] = "-c";
+		args[argno++] = command;
+	}
+	if (additional_args) {
+		for (; *additional_args; ++additional_args)
+			args[argno++] = *additional_args;
+	}
+	args[argno] = NULL;
+
+#if ENABLE_SELINUX
+	if (current_sid)
+		setexeccon(current_sid);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		freecon(current_sid);
+#endif
+	execv(shell, (char **) args);
+	bb_perror_msg_and_die("can't execute '%s'", shell);
+}
diff --git a/busybox-1.19.3/libbb/safe_gethostname.c b/busybox-1.19.3/libbb/safe_gethostname.c
new file mode 100644
index 0000000..bdb9896
--- /dev/null
+++ b/busybox-1.19.3/libbb/safe_gethostname.c
@@ -0,0 +1,74 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Safe gethostname implementation for busybox
+ *
+ * Copyright (C) 2008 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * SUSv2 guarantees that "Host names are limited to 255 bytes"
+ * POSIX.1-2001 guarantees that "Host names (not including the terminating
+ * null byte) are limited to HOST_NAME_MAX bytes" (64 bytes on my box).
+ *
+ * RFC1123 says:
+ *
+ * The syntax of a legal Internet host name was specified in RFC-952
+ * [DNS:4].  One aspect of host name syntax is hereby changed: the
+ * restriction on the first character is relaxed to allow either a
+ * letter or a digit.  Host software MUST support this more liberal
+ * syntax.
+ *
+ * Host software MUST handle host names of up to 63 characters and
+ * SHOULD handle host names of up to 255 characters.
+ */
+
+#include "libbb.h"
+#include <sys/utsname.h>
+
+/*
+ * On success return the current malloced and NUL terminated hostname.
+ * On error return malloced and NUL terminated string "?".
+ * This is an illegal first character for a hostname.
+ * The returned malloced string must be freed by the caller.
+ */
+char* FAST_FUNC safe_gethostname(void)
+{
+	struct utsname uts;
+
+	/* The length of the arrays in a struct utsname is unspecified;
+	 * the fields are terminated by a null byte.
+	 * Note that there is no standard that says that the hostname
+	 * set by sethostname(2) is the same string as the nodename field of the
+	 * struct returned by uname (indeed, some systems allow a 256-byte host-
+	 * name and an 8-byte nodename), but this is true on Linux. The same holds
+	 * for setdomainname(2) and the domainname field.
+	 */
+
+	/* Uname can fail only if you pass a bad pointer to it. */
+	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
+}
diff --git a/busybox-1.19.3/libbb/safe_poll.c b/busybox-1.19.3/libbb/safe_poll.c
new file mode 100644
index 0000000..b492a81
--- /dev/null
+++ b/busybox-1.19.3/libbb/safe_poll.c
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2007 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Wrapper which restarts poll on EINTR or ENOMEM.
+ * On other errors does perror("poll") and returns.
+ * Warning! May take longer than timeout_ms to return! */
+int FAST_FUNC safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout)
+{
+	while (1) {
+		int n = poll(ufds, nfds, timeout);
+		if (n >= 0)
+			return n;
+		/* Make sure we inch towards completion */
+		if (timeout > 0)
+			timeout--;
+		/* E.g. strace causes poll to return this */
+		if (errno == EINTR)
+			continue;
+		/* Kernel is very low on memory. Retry. */
+		/* I doubt many callers would handle this correctly! */
+		if (errno == ENOMEM)
+			continue;
+		bb_perror_msg("poll");
+		return n;
+	}
+}
diff --git a/busybox-1.19.3/libbb/safe_strncpy.c b/busybox-1.19.3/libbb/safe_strncpy.c
new file mode 100644
index 0000000..5eb0db0
--- /dev/null
+++ b/busybox-1.19.3/libbb/safe_strncpy.c
@@ -0,0 +1,32 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+/* Like strncpy but make sure the resulting string is always 0 terminated. */
+char* FAST_FUNC safe_strncpy(char *dst, const char *src, size_t size)
+{
+	if (!size) return dst;
+	dst[--size] = '\0';
+	return strncpy(dst, src, size);
+}
+
+/* Like strcpy but can copy overlapping strings. */
+void FAST_FUNC overlapping_strcpy(char *dst, const char *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++;
+		}
+	}
+}
diff --git a/busybox-1.19.3/libbb/safe_write.c b/busybox-1.19.3/libbb/safe_write.c
new file mode 100644
index 0000000..8f76280
--- /dev/null
+++ b/busybox-1.19.3/libbb/safe_write.c
@@ -0,0 +1,21 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+ssize_t FAST_FUNC safe_write(int fd, const void *buf, size_t count)
+{
+	ssize_t n;
+
+	do {
+		n = write(fd, buf, count);
+	} while (n < 0 && errno == EINTR);
+
+	return n;
+}
diff --git a/busybox-1.19.3/libbb/selinux_common.c b/busybox-1.19.3/libbb/selinux_common.c
new file mode 100644
index 0000000..62910e2
--- /dev/null
+++ b/busybox-1.19.3/libbb/selinux_common.c
@@ -0,0 +1,55 @@
+/*
+ * libbb/selinux_common.c
+ *   -- common SELinux utility functions
+ *
+ * Copyright 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+ *
+ * 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)
+{
+	context_t con = context_new(cur_context);
+	if (!con)
+		return NULL;
+
+	if (user && context_user_set(con, user))
+		goto error;
+	if (type && context_type_set(con, type))
+		goto error;
+	if (range && context_range_set(con, range))
+		goto error;
+	if (role && context_role_set(con, role))
+		goto error;
+	return con;
+
+error:
+	context_free(con);
+	return NULL;
+}
+
+void FAST_FUNC setfscreatecon_or_die(security_context_t scontext)
+{
+	if (setfscreatecon(scontext) < 0) {
+		/* Can be NULL. All known printf implementations
+		 * display "(null)", "<null>" etc */
+		bb_perror_msg_and_die("can't set default "
+				"file creation context to %s", scontext);
+	}
+}
+
+void FAST_FUNC selinux_preserve_fcontext(int fdesc)
+{
+	security_context_t context;
+
+	if (fgetfilecon(fdesc, &context) < 0) {
+		if (errno == ENODATA || errno == ENOTSUP)
+			return;
+		bb_perror_msg_and_die("fgetfilecon failed");
+	}
+	setfscreatecon_or_die(context);
+	freecon(context);
+}
diff --git a/busybox-1.19.3/libbb/setup_environment.c b/busybox-1.19.3/libbb/setup_environment.c
new file mode 100644
index 0000000..73229ca
--- /dev/null
+++ b/busybox-1.19.3/libbb/setup_environment.c
@@ -0,0 +1,72 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com>
+ * 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. Neither the name of Julianne F. Haugh nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``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 JULIE HAUGH OR CONTRIBUTORS 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.
+ */
+
+#include "libbb.h"
+
+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_CLEARENV) {
+		const char *term;
+
+		/* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH.
+		 * Unset all other environment variables.  */
+		term = getenv("TERM");
+		clearenv();
+		if (term)
+			xsetenv("TERM", term);
+		xsetenv("PATH", (pw->pw_uid ? bb_default_path : bb_default_root_path));
+		goto shortcut;
+		// No, gcc (4.2.1) is not clever enougn to do it itself.
+		//xsetenv("USER",    pw->pw_name);
+		//xsetenv("LOGNAME", pw->pw_name);
+		//xsetenv("HOME",    pw->pw_dir);
+		//xsetenv("SHELL",   shell);
+	} else if (flags & SETUP_ENV_CHANGEENV) {
+		/* Set HOME, SHELL, and if not becoming a super-user,
+		 * USER and LOGNAME.  */
+		if (pw->pw_uid) {
+ shortcut:
+			xsetenv("USER",    pw->pw_name);
+			xsetenv("LOGNAME", pw->pw_name);
+		}
+		xsetenv("HOME",    pw->pw_dir);
+		xsetenv("SHELL",   shell);
+	}
+}
diff --git a/busybox-1.19.3/libbb/signals.c b/busybox-1.19.3/libbb/signals.c
new file mode 100644
index 0000000..cdc37b1
--- /dev/null
+++ b/busybox-1.19.3/libbb/signals.c
@@ -0,0 +1,121 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2006 Rob Landley
+ * Copyright (C) 2006 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* All known arches use small ints for signals */
+smallint bb_got_signal;
+
+void record_signo(int signo)
+{
+	bb_got_signal = signo;
+}
+
+/* Saves 2 bytes on x86! Oh my... */
+int FAST_FUNC sigaction_set(int signum, const struct sigaction *act)
+{
+	return sigaction(signum, act, NULL);
+}
+
+int FAST_FUNC sigprocmask_allsigs(int how)
+{
+	sigset_t set;
+	sigfillset(&set);
+	return sigprocmask(how, &set, NULL);
+}
+
+void FAST_FUNC bb_signals(int sigs, void (*f)(int))
+{
+	int sig_no = 0;
+	int bit = 1;
+
+	while (sigs) {
+		if (sigs & bit) {
+			sigs &= ~bit;
+			signal(sig_no, f);
+		}
+		sig_no++;
+		bit <<= 1;
+	}
+}
+
+void FAST_FUNC bb_signals_recursive_norestart(int sigs, void (*f)(int))
+{
+	int sig_no = 0;
+	int bit = 1;
+	struct sigaction sa;
+
+	memset(&sa, 0, sizeof(sa));
+	sa.sa_handler = f;
+	/*sa.sa_flags = 0;*/
+	/*sigemptyset(&sa.sa_mask); - hope memset did it*/
+
+	while (sigs) {
+		if (sigs & bit) {
+			sigs &= ~bit;
+			sigaction_set(sig_no, &sa);
+		}
+		sig_no++;
+		bit <<= 1;
+	}
+}
+
+void FAST_FUNC sig_block(int sig)
+{
+	sigset_t ss;
+	sigemptyset(&ss);
+	sigaddset(&ss, sig);
+	sigprocmask(SIG_BLOCK, &ss, NULL);
+}
+
+void FAST_FUNC sig_unblock(int sig)
+{
+	sigset_t ss;
+	sigemptyset(&ss);
+	sigaddset(&ss, sig);
+	sigprocmask(SIG_UNBLOCK, &ss, NULL);
+}
+
+void FAST_FUNC wait_for_any_sig(void)
+{
+	sigset_t ss;
+	sigemptyset(&ss);
+	sigsuspend(&ss);
+}
+
+/* Assuming the sig is fatal */
+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 */
+}
+
+void FAST_FUNC signal_SA_RESTART_empty_mask(int sig, void (*handler)(int))
+{
+	struct sigaction sa;
+	memset(&sa, 0, sizeof(sa));
+	/*sigemptyset(&sa.sa_mask);*/
+	sa.sa_flags = SA_RESTART;
+	sa.sa_handler = handler;
+	sigaction_set(sig, &sa);
+}
+
+void FAST_FUNC signal_no_SA_RESTART_empty_mask(int sig, void (*handler)(int))
+{
+	struct sigaction sa;
+	memset(&sa, 0, sizeof(sa));
+	/*sigemptyset(&sa.sa_mask);*/
+	/*sa.sa_flags = 0;*/
+	sa.sa_handler = handler;
+	sigaction_set(sig, &sa);
+}
diff --git a/busybox-1.19.3/libbb/simplify_path.c b/busybox-1.19.3/libbb/simplify_path.c
new file mode 100644
index 0000000..89dc5bd
--- /dev/null
+++ b/busybox-1.19.3/libbb/simplify_path.c
@@ -0,0 +1,59 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bb_simplify_path implementation for busybox
+ *
+ * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+char* FAST_FUNC bb_simplify_abs_path_inplace(char *start)
+{
+	char *s, *p;
+
+	p = s = start;
+	do {
+		if (*p == '/') {
+			if (*s == '/') {  /* skip duplicate (or initial) slash */
+				continue;
+			}
+			if (*s == '.') {
+				if (s[1] == '/' || !s[1]) {  /* remove extra '.' */
+					continue;
+				}
+				if ((s[1] == '.') && (s[2] == '/' || !s[2])) {
+					++s;
+					if (p > start) {
+						while (*--p != '/')  /* omit previous dir */
+							continue;
+					}
+					continue;
+				}
+			}
+		}
+		*++p = *s;
+	} while (*++s);
+
+	if ((p == start) || (*p != '/')) {  /* not a trailing slash */
+		++p;  /* so keep last character */
+	}
+	*p = '\0';
+	return p;
+}
+
+char* FAST_FUNC bb_simplify_path(const char *path)
+{
+	char *s, *p;
+
+	if (path[0] == '/')
+		s = xstrdup(path);
+	else {
+		p = xrealloc_getcwd_or_warn(NULL);
+		s = concat_path_file(p, path);
+		free(p);
+	}
+
+	bb_simplify_abs_path_inplace(s);
+	return s;
+}
diff --git a/busybox-1.19.3/libbb/single_argv.c b/busybox-1.19.3/libbb/single_argv.c
new file mode 100644
index 0000000..64844dd
--- /dev/null
+++ b/busybox-1.19.3/libbb/single_argv.c
@@ -0,0 +1,18 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2009 Denys Vlasenko
+ *
+ * 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];
+}
diff --git a/busybox-1.19.3/libbb/skip_whitespace.c b/busybox-1.19.3/libbb/skip_whitespace.c
new file mode 100644
index 0000000..8c7b674
--- /dev/null
+++ b/busybox-1.19.3/libbb/skip_whitespace.c
@@ -0,0 +1,39 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * skip_whitespace implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+char* FAST_FUNC skip_whitespace(const char *s)
+{
+	/* In POSIX/C locale (the only locale we care about: do we REALLY want
+	 * to allow Unicode whitespace in, say, .conf files? nuts!)
+	 * isspace is only these chars: "\t\n\v\f\r" and space.
+	 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
+	 * Use that.
+	 */
+	while (*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9))
+		s++;
+
+	return (char *) s;
+}
+
+char* FAST_FUNC skip_non_whitespace(const char *s)
+{
+	while (*s != '\0' && *s != ' ' && (unsigned char)(*s - 9) > (13 - 9))
+		s++;
+
+	return (char *) s;
+}
+
+char* FAST_FUNC skip_dev_pfx(const char *tty_name)
+{
+	if (strncmp(tty_name, "/dev/", 5) == 0)
+		tty_name += 5;
+	return (char*)tty_name;
+}
diff --git a/busybox-1.19.3/libbb/speed_table.c b/busybox-1.19.3/libbb/speed_table.c
new file mode 100644
index 0000000..45159f1
--- /dev/null
+++ b/busybox-1.19.3/libbb/speed_table.c
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * compact speed_t <-> speed functions for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+struct speed_map {
+	unsigned short speed;
+	unsigned short value;
+};
+
+static const struct speed_map speeds[] = {
+	{B0, 0},
+	{B50, 50},
+	{B75, 75},
+	{B110, 110},
+	{B134, 134},
+	{B150, 150},
+	{B200, 200},
+	{B300, 300},
+	{B600, 600},
+	{B1200, 1200},
+	{B1800, 1800},
+	{B2400, 2400},
+	{B4800, 4800},
+	{B9600, 9600},
+#ifdef B19200
+	{B19200, 19200},
+#elif defined(EXTA)
+	{EXTA, 19200},
+#endif
+#ifdef B38400
+	{B38400, 38400/256 + 0x8000U},
+#elif defined(EXTB)
+	{EXTB, 38400/256 + 0x8000U},
+#endif
+#ifdef B57600
+	{B57600, 57600/256 + 0x8000U},
+#endif
+#ifdef B115200
+	{B115200, 115200/256 + 0x8000U},
+#endif
+#ifdef B230400
+	{B230400, 230400/256 + 0x8000U},
+#endif
+#ifdef B460800
+	{B460800, 460800/256 + 0x8000U},
+#endif
+#ifdef B921600
+	{B921600, 921600/256 + 0x8000U},
+#endif
+};
+
+enum { NUM_SPEEDS = ARRAY_SIZE(speeds) };
+
+unsigned FAST_FUNC tty_baud_to_value(speed_t speed)
+{
+	int i = 0;
+
+	do {
+		if (speed == speeds[i].speed) {
+			if (speeds[i].value & 0x8000U) {
+				return ((unsigned long) (speeds[i].value) & 0x7fffU) * 256;
+			}
+			return speeds[i].value;
+		}
+	} while (++i < NUM_SPEEDS);
+
+	return 0;
+}
+
+speed_t FAST_FUNC tty_value_to_baud(unsigned int value)
+{
+	int i = 0;
+
+	do {
+		if (value == tty_baud_to_value(speeds[i].speed)) {
+			return speeds[i].speed;
+		}
+	} while (++i < NUM_SPEEDS);
+
+	return (speed_t) - 1;
+}
+
+#if 0
+/* testing code */
+#include <stdio.h>
+
+int main(void)
+{
+	unsigned long v;
+	speed_t s;
+
+	for (v = 0 ; v < 1000000; v++) {
+		s = tty_value_to_baud(v);
+		if (s == (speed_t) -1) {
+			continue;
+		}
+		printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
+	}
+
+	printf("-------------------------------\n");
+
+	for (s = 0 ; s < 010017+1; s++) {
+		v = tty_baud_to_value(s);
+		if (!v) {
+			continue;
+		}
+		printf("v = %lu -- s = %0lo\n", v, (unsigned long) s);
+	}
+
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/libbb/str_tolower.c b/busybox-1.19.3/libbb/str_tolower.c
new file mode 100644
index 0000000..c2d5637
--- /dev/null
+++ b/busybox-1.19.3/libbb/str_tolower.c
@@ -0,0 +1,14 @@
+/* vi set: sw=4 ts=4: */
+/* Convert string str to lowercase, return str.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+char* FAST_FUNC str_tolower(char *str)
+{
+	char *c;
+	for (c = str; *c; ++c)
+		*c = tolower(*c);
+	return str;
+}
diff --git a/busybox-1.19.3/libbb/strrstr.c b/busybox-1.19.3/libbb/strrstr.c
new file mode 100644
index 0000000..d8823fc
--- /dev/null
+++ b/busybox-1.19.3/libbb/strrstr.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2008 Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#ifdef __DO_STRRSTR_TEST
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#else
+#include "libbb.h"
+#endif
+
+/*
+ * The strrstr() function finds the last occurrence of the substring needle
+ * in the string haystack. The terminating nul characters are not compared.
+ */
+char* FAST_FUNC strrstr(const char *haystack, const char *needle)
+{
+	char *r = NULL;
+
+	if (!needle[0])
+		return (char*)haystack + strlen(haystack);
+	while (1) {
+		char *p = strstr(haystack, needle);
+		if (!p)
+			return r;
+		r = p;
+		haystack = p + 1;
+	}
+}
+
+#ifdef __DO_STRRSTR_TEST
+int main(int argc, char **argv)
+{
+	static const struct {
+		const char *h, *n;
+		int pos;
+	} test_array[] = {
+		/* 0123456789 */
+		{ "baaabaaab",  "aaa", 5  },
+		{ "baaabaaaab", "aaa", 6  },
+		{ "baaabaab",   "aaa", 1  },
+		{ "aaa",        "aaa", 0  },
+		{ "aaa",        "a",   2  },
+		{ "aaa",        "bbb", -1 },
+		{ "a",          "aaa", -1 },
+		{ "aaa",        "",    3  },
+		{ "",           "aaa", -1 },
+		{ "",           "",    0  },
+	};
+
+	int i;
+
+	i = 0;
+	while (i < sizeof(test_array) / sizeof(test_array[0])) {
+		const char *r = strrstr(test_array[i].h, test_array[i].n);
+		printf("'%s' vs. '%s': '%s' - ", test_array[i].h, test_array[i].n, r);
+		if (r == NULL)
+			r = test_array[i].h - 1;
+		printf("%s\n", r == test_array[i].h + test_array[i].pos ? "PASSED" : "FAILED");
+		i++;
+	}
+
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/libbb/systemd_support.c b/busybox-1.19.3/libbb/systemd_support.c
new file mode 100644
index 0000000..542a3ef
--- /dev/null
+++ b/busybox-1.19.3/libbb/systemd_support.c
@@ -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;
+}
diff --git a/busybox-1.19.3/libbb/time.c b/busybox-1.19.3/libbb/time.c
new file mode 100644
index 0000000..e2b9384
--- /dev/null
+++ b/busybox-1.19.3/libbb/time.c
@@ -0,0 +1,256 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+void FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
+{
+	char end = '\0';
+	const char *last_colon = strrchr(date_str, ':');
+
+	if (last_colon != NULL) {
+		/* Parse input and assign appropriately to ptm */
+#if ENABLE_DESKTOP
+		const char *endp;
+#endif
+
+		/* HH:MM */
+		if (sscanf(date_str, "%u:%u%c",
+					&ptm->tm_hour,
+					&ptm->tm_min,
+					&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) {
+			/* Adjust month from 1-12 to 0-11 */
+			ptm->tm_mon -= 1;
+		} else
+		/* yyyy.mm.dd-HH:MM */
+		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
+		/* yyyy-mm-dd HH:MM */
+		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
+#if ENABLE_DESKTOP  /* strptime is BIG: ~1k in uclibc, ~10k in glibc */
+		/* month_name d HH:MM:SS YYYY. Supported by GNU date */
+		if ((endp = strptime(date_str, "%b %d %T %Y", ptm)) != NULL
+		 && *endp == '\0'
+		) {
+			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);
+		}
+		if (end == ':') {
+			/* xxx:SS */
+			if (sscanf(last_colon + 1, "%u%c", &ptm->tm_sec, &end) == 1)
+				end = '\0';
+			/* else end != NUL and we error out */
+		}
+	} else if (date_str[0] == '@') {
+		time_t t = bb_strtol(date_str + 1, NULL, 10);
+		if (!errno) {
+			struct tm *lt = localtime(&t);
+			if (lt) {
+				*ptm = *lt;
+				return;
+			}
+		}
+		end = '1';
+	} else {
+		/* Googled the following on an old date manpage:
+		 *
+		 * The canonical representation for setting the date/time is:
+		 * cc   Century (either 19 or 20)
+		 * yy   Year in abbreviated form (e.g. 89, 06)
+		 * mm   Numeric month, a number from 1 to 12
+		 * dd   Day, a number from 1 to 31
+		 * HH   Hour, a number from 0 to 23
+		 * MM   Minutes, a number from 0 to 59
+		 * .SS  Seconds, a number from 0 to 61 (with leap seconds)
+		 * Everything but the minutes is optional
+		 *
+		 * "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] */
+		if (len == 2 && sscanf(date_str, "%2u%2u%2u%2u""%2u%c" + 12,
+					&ptm->tm_min,
+					&end) >= 1) {
+		} else
+		/* HHMM[.SS] */
+		if (len == 4 && sscanf(date_str, "%2u%2u%2u""%2u%2u%c" + 9,
+					&ptm->tm_hour,
+					&ptm->tm_min,
+					&end) >= 2) {
+		} else
+		/* ddHHMM[.SS] */
+		if (len == 6 && sscanf(date_str, "%2u%2u""%2u%2u%2u%c" + 6,
+					&ptm->tm_mday,
+					&ptm->tm_hour,
+					&ptm->tm_min,
+					&end) >= 3) {
+		} else
+		/* mmddHHMM[.SS] */
+		if (len == 8 && sscanf(date_str, "%2u""%2u%2u%2u%2u%c" + 3,
+					&ptm->tm_mon,
+					&ptm->tm_mday,
+					&ptm->tm_hour,
+					&ptm->tm_min,
+					&end) >= 4) {
+			/* Adjust month from 1-12 to 0-11 */
+			ptm->tm_mon -= 1;
+		} else
+		/* yymmddHHMM[.SS] */
+		if (len == 10 && sscanf(date_str, "%2u%2u%2u%2u%2u%c",
+					&ptm->tm_year,
+					&ptm->tm_mon,
+					&ptm->tm_mday,
+					&ptm->tm_hour,
+					&ptm->tm_min,
+					&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",
+					&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 {
+			bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+		}
+		if (end == '.') {
+			/* xxx.SS */
+			if (sscanf(strchr(date_str, '.') + 1, "%u%c",
+					&ptm->tm_sec, &end) == 1)
+				end = '\0';
+			/* else end != NUL and we error out */
+		}
+	}
+	if (end != '\0') {
+		bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+	}
+}
+
+time_t FAST_FUNC validate_tm_time(const char *date_str, struct tm *ptm)
+{
+	time_t t = mktime(ptm);
+	if (t == (time_t) -1L) {
+		bb_error_msg_and_die(bb_msg_invalid_date, date_str);
+	}
+	return t;
+}
+
+#if ENABLE_MONOTONIC_SYSCALL
+
+#include <sys/syscall.h>
+/* Old glibc (< 2.3.4) does not provide this constant. We use syscall
+ * directly so this definition is safe. */
+#ifndef CLOCK_MONOTONIC
+#define CLOCK_MONOTONIC 1
+#endif
+
+/* libc has incredibly messy way of doing this,
+ * typically requiring -lrt. We just skip all this mess */
+static void get_mono(struct timespec *ts)
+{
+	if (syscall(__NR_clock_gettime, CLOCK_MONOTONIC, ts))
+		bb_error_msg_and_die("clock_gettime(MONOTONIC) failed");
+}
+unsigned long long FAST_FUNC monotonic_ns(void)
+{
+	struct timespec ts;
+	get_mono(&ts);
+	return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+unsigned long long FAST_FUNC monotonic_us(void)
+{
+	struct timespec ts;
+	get_mono(&ts);
+	return ts.tv_sec * 1000000ULL + ts.tv_nsec/1000;
+}
+unsigned long long FAST_FUNC monotonic_ms(void)
+{
+	struct timespec ts;
+	get_mono(&ts);
+	return ts.tv_sec * 1000ULL + ts.tv_nsec/1000000;
+}
+unsigned FAST_FUNC monotonic_sec(void)
+{
+	struct timespec ts;
+	get_mono(&ts);
+	return ts.tv_sec;
+}
+
+#else
+
+unsigned long long FAST_FUNC monotonic_ns(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec * 1000000000ULL + tv.tv_usec * 1000;
+}
+unsigned long long FAST_FUNC monotonic_us(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec * 1000000ULL + tv.tv_usec;
+}
+unsigned long long FAST_FUNC monotonic_ms(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	return tv.tv_sec * 1000ULL + tv.tv_usec / 1000;
+}
+unsigned FAST_FUNC monotonic_sec(void)
+{
+	return time(NULL);
+}
+
+#endif
diff --git a/busybox-1.19.3/libbb/trim.c b/busybox-1.19.3/libbb/trim.c
new file mode 100644
index 0000000..16cb4fb
--- /dev/null
+++ b/busybox-1.19.3/libbb/trim.c
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+void FAST_FUNC trim(char *s)
+{
+	size_t len = strlen(s);
+
+	/* trim trailing whitespace */
+	while (len && isspace(s[len-1]))
+		--len;
+
+	/* trim leading whitespace */
+	if (len) {
+		char *nws = skip_whitespace(s);
+		if ((nws - s) != 0) {
+			len -= (nws - s);
+			memmove(s, nws, len);
+		}
+	}
+	s[len] = '\0';
+}
diff --git a/busybox-1.19.3/libbb/u_signal_names.c b/busybox-1.19.3/libbb/u_signal_names.c
new file mode 100644
index 0000000..8c78f5e
--- /dev/null
+++ b/busybox-1.19.3/libbb/u_signal_names.c
@@ -0,0 +1,236 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Signal name/number conversion routines.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * 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!
+ * HPPA: SIGSTKFLT == 36. */
+
+static const char signals[][7] = {
+	// SUSv3 says kill must support these, and specifies the numerical values,
+	// http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html
+	// {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"},
+	// {6, "ABRT"}, {9, "KILL"}, {14, "ALRM"}, {15, "TERM"}
+	// And Posix adds the following:
+	// {SIGILL, "ILL"}, {SIGTRAP, "TRAP"}, {SIGFPE, "FPE"}, {SIGUSR1, "USR1"},
+	// {SIGSEGV, "SEGV"}, {SIGUSR2, "USR2"}, {SIGPIPE, "PIPE"}, {SIGCHLD, "CHLD"},
+	// {SIGCONT, "CONT"}, {SIGSTOP, "STOP"}, {SIGTSTP, "TSTP"}, {SIGTTIN, "TTIN"},
+	// {SIGTTOU, "TTOU"}
+
+	[0] = "EXIT",
+#ifdef SIGHUP
+	[SIGHUP   ] = "HUP",
+#endif
+#ifdef SIGINT
+	[SIGINT   ] = "INT",
+#endif
+#ifdef SIGQUIT
+	[SIGQUIT  ] = "QUIT",
+#endif
+#ifdef SIGILL
+	[SIGILL   ] = "ILL",
+#endif
+#ifdef SIGTRAP
+	[SIGTRAP  ] = "TRAP",
+#endif
+#ifdef SIGABRT
+	[SIGABRT  ] = "ABRT",
+#endif
+#ifdef SIGBUS
+	[SIGBUS   ] = "BUS",
+#endif
+#ifdef SIGFPE
+	[SIGFPE   ] = "FPE",
+#endif
+#ifdef SIGKILL
+	[SIGKILL  ] = "KILL",
+#endif
+#ifdef SIGUSR1
+	[SIGUSR1  ] = "USR1",
+#endif
+#ifdef SIGSEGV
+	[SIGSEGV  ] = "SEGV",
+#endif
+#ifdef SIGUSR2
+	[SIGUSR2  ] = "USR2",
+#endif
+#ifdef SIGPIPE
+	[SIGPIPE  ] = "PIPE",
+#endif
+#ifdef SIGALRM
+	[SIGALRM  ] = "ALRM",
+#endif
+#ifdef SIGTERM
+	[SIGTERM  ] = "TERM",
+#endif
+#ifdef SIGSTKFLT
+	[SIGSTKFLT] = "STKFLT",
+#endif
+#ifdef SIGCHLD
+	[SIGCHLD  ] = "CHLD",
+#endif
+#ifdef SIGCONT
+	[SIGCONT  ] = "CONT",
+#endif
+#ifdef SIGSTOP
+	[SIGSTOP  ] = "STOP",
+#endif
+#ifdef SIGTSTP
+	[SIGTSTP  ] = "TSTP",
+#endif
+#ifdef SIGTTIN
+	[SIGTTIN  ] = "TTIN",
+#endif
+#ifdef SIGTTOU
+	[SIGTTOU  ] = "TTOU",
+#endif
+#ifdef SIGURG
+	[SIGURG   ] = "URG",
+#endif
+#ifdef SIGXCPU
+	[SIGXCPU  ] = "XCPU",
+#endif
+#ifdef SIGXFSZ
+	[SIGXFSZ  ] = "XFSZ",
+#endif
+#ifdef SIGVTALRM
+	[SIGVTALRM] = "VTALRM",
+#endif
+#ifdef SIGPROF
+	[SIGPROF  ] = "PROF",
+#endif
+#ifdef SIGWINCH
+	[SIGWINCH ] = "WINCH",
+#endif
+#ifdef SIGPOLL
+	[SIGPOLL  ] = "POLL",
+#endif
+#ifdef SIGPWR
+	[SIGPWR   ] = "PWR",
+#endif
+#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.
+
+int FAST_FUNC get_signum(const char *name)
+{
+	unsigned i;
+
+	i = bb_strtou(name, NULL, 10);
+	if (!errno)
+		return i;
+	if (strncasecmp(name, "SIG", 3) == 0)
+		name += 3;
+	for (i = 0; i < ARRAY_SIZE(signals); i++)
+		if (strcasecmp(name, signals[i]) == 0)
+			return i;
+
+#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
+		if (!name[2])
+			return SIGIO;
+#  endif
+#  ifdef SIGIOT
+		if ((name[2] | 0x20) == 't' && !name[3])
+			return SIGIOT;
+#  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;
+}
+
+// Convert signal number to name
+
+const char* FAST_FUNC get_signame(int number)
+{
+	if ((unsigned)number < ARRAY_SIZE(signals)) {
+		if (signals[number][0]) /* if it's not an empty str */
+			return signals[number];
+	}
+
+	return itoa(number);
+}
+
+
+// Print the whole signal list
+
+void FAST_FUNC print_signames(void)
+{
+	unsigned signo;
+
+	for (signo = 1; signo < ARRAY_SIZE(signals); signo++) {
+		const char *name = signals[signo];
+		if (name[0])
+			printf("%2u) %s\n", signo, name);
+	}
+#if ENABLE_FEATURE_RTMINMAX
+# ifdef __SIGRTMAX
+	printf("%2u) %s\n", __SIGRTMAX, "RTMAX");
+# endif
+#endif
+}
diff --git a/busybox-1.19.3/libbb/udp_io.c b/busybox-1.19.3/libbb/udp_io.c
new file mode 100644
index 0000000..7985a97
--- /dev/null
+++ b/busybox-1.19.3/libbb/udp_io.c
@@ -0,0 +1,179 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+/*
+ * This asks kernel to let us know dst addr/port of incoming packets
+ * We don't check for errors here. Not supported == won't be used
+ */
+void FAST_FUNC
+socket_want_pktinfo(int fd UNUSED_PARAM)
+{
+#ifdef IP_PKTINFO
+	setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &const_int_1, sizeof(int));
+#endif
+#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
+	setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &const_int_1, sizeof(int));
+#endif
+}
+
+
+ssize_t FAST_FUNC
+send_to_from(int fd, void *buf, size_t len, int flags,
+		const struct sockaddr *to,
+		const struct sockaddr *from,
+		socklen_t tolen)
+{
+#ifndef IP_PKTINFO
+	(void)from; /* suppress "unused from" warning */
+	return sendto(fd, buf, len, flags, to, tolen);
+#else
+	struct iovec iov[1];
+	struct msghdr msg;
+	union {
+		char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
+# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
+		char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+# endif
+	} u;
+	struct cmsghdr* cmsgptr;
+
+	if (from->sa_family != AF_INET
+# if ENABLE_FEATURE_IPV6
+	 && from->sa_family != AF_INET6
+# endif
+	) {
+		/* ANY local address */
+		return sendto(fd, buf, len, flags, to, tolen);
+	}
+
+	/* man recvmsg and man cmsg is needed to make sense of code below */
+
+	iov[0].iov_base = buf;
+	iov[0].iov_len = len;
+
+	memset(&u, 0, sizeof(u));
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_name = (void *)(struct sockaddr *)to; /* or compiler will annoy us */
+	msg.msg_namelen = tolen;
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = &u;
+	msg.msg_controllen = sizeof(u);
+	msg.msg_flags = flags;
+
+	cmsgptr = CMSG_FIRSTHDR(&msg);
+	if (to->sa_family == AF_INET && from->sa_family == AF_INET) {
+		struct in_pktinfo *pktptr;
+		cmsgptr->cmsg_level = IPPROTO_IP;
+		cmsgptr->cmsg_type = IP_PKTINFO;
+		cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+		pktptr = (struct in_pktinfo *)(CMSG_DATA(cmsgptr));
+		/*pktptr->ipi_ifindex = 0; -- already done by memset(u...) */
+		/* In general, CMSG_DATA() can be unaligned, but in this case
+		 * we know for sure it is sufficiently aligned:
+		 * CMSG_FIRSTHDR simply returns &u above,
+		 * and CMSG_DATA returns &u + size_t + int + int.
+		 * Thus direct assignment is ok:
+		 */
+		pktptr->ipi_spec_dst = ((struct sockaddr_in*)from)->sin_addr;
+	}
+# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
+	else if (to->sa_family == AF_INET6 && from->sa_family == AF_INET6) {
+		struct in6_pktinfo *pktptr;
+		cmsgptr->cmsg_level = IPPROTO_IPV6;
+		cmsgptr->cmsg_type = IPV6_PKTINFO;
+		cmsgptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+		pktptr = (struct in6_pktinfo *)(CMSG_DATA(cmsgptr));
+		/* pktptr->ipi6_ifindex = 0; -- already done by memset(u...) */
+		pktptr->ipi6_addr = ((struct sockaddr_in6*)from)->sin6_addr;
+	}
+# endif
+	msg.msg_controllen = cmsgptr->cmsg_len;
+
+	return sendmsg(fd, &msg, flags);
+#endif
+}
+
+/* NB: this will never set port# in 'to'!
+ * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
+ * Typical usage is to preinit 'to' with "default" value
+ * before calling recv_from_to(). */
+ssize_t FAST_FUNC
+recv_from_to(int fd, void *buf, size_t len, int flags,
+		struct sockaddr *from, struct sockaddr *to,
+		socklen_t sa_size)
+{
+#ifndef IP_PKTINFO
+	(void)to; /* suppress "unused to" warning */
+	return recvfrom(fd, buf, len, flags, from, &sa_size);
+#else
+	/* man recvmsg and man cmsg is needed to make sense of code below */
+	struct iovec iov[1];
+	union {
+		char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
+# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
+		char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+# endif
+	} u;
+	struct cmsghdr *cmsgptr;
+	struct msghdr msg;
+	ssize_t recv_length;
+
+	iov[0].iov_base = buf;
+	iov[0].iov_len = len;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_name = (struct sockaddr *)from;
+	msg.msg_namelen = sa_size;
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = &u;
+	msg.msg_controllen = sizeof(u);
+
+	recv_length = recvmsg(fd, &msg, flags);
+	if (recv_length < 0)
+		return recv_length;
+
+# define to4 ((struct sockaddr_in*)to)
+# define to6 ((struct sockaddr_in6*)to)
+	/* Here we try to retrieve destination IP and memorize it */
+	for (cmsgptr = CMSG_FIRSTHDR(&msg);
+			cmsgptr != NULL;
+			cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)
+	) {
+		if (cmsgptr->cmsg_level == IPPROTO_IP
+		 && cmsgptr->cmsg_type == IP_PKTINFO
+		) {
+			const int IPI_ADDR_OFF = offsetof(struct in_pktinfo, ipi_addr);
+			to->sa_family = AF_INET;
+			/*# define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
+			/*to4->sin_addr = pktinfo(cmsgptr)->ipi_addr; - may be unaligned */
+			memcpy(&to4->sin_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI_ADDR_OFF, sizeof(to4->sin_addr));
+			/*to4->sin_port = 123; - this data is not supplied by kernel */
+			break;
+		}
+# if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
+		if (cmsgptr->cmsg_level == IPPROTO_IPV6
+		 && cmsgptr->cmsg_type == IPV6_PKTINFO
+		) {
+			const int IPI6_ADDR_OFF = offsetof(struct in6_pktinfo, ipi6_addr);
+			to->sa_family = AF_INET6;
+			/*#  define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )*/
+			/*to6->sin6_addr = pktinfo(cmsgptr)->ipi6_addr; - may be unaligned */
+			memcpy(&to6->sin6_addr, (char*)(CMSG_DATA(cmsgptr)) + IPI6_ADDR_OFF, sizeof(to6->sin6_addr));
+			/*to6->sin6_port = 123; */
+			break;
+		}
+# endif
+	}
+	return recv_length;
+#endif
+}
diff --git a/busybox-1.19.3/libbb/unicode.c b/busybox-1.19.3/libbb/unicode.c
new file mode 100644
index 0000000..99dc1df
--- /dev/null
+++ b/busybox-1.19.3/libbb/unicode.c
@@ -0,0 +1,1158 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Unicode support routines.
+ *
+ * Copyright (C) 2009 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "unicode.h"
+
+/* If it's not #defined as a constant in unicode.h... */
+#ifndef unicode_status
+uint8_t unicode_status;
+#endif
+
+/* This file is compiled only if UNICODE_SUPPORT is on.
+ * We check other options and decide whether to use libc support
+ * via locale, or use our own logic:
+ */
+
+#if ENABLE_UNICODE_USING_LOCALE
+
+/* Unicode support using libc locale support. */
+
+void FAST_FUNC reinit_unicode(const char *LANG)
+{
+	static const char unicode_0x394[] = { 0xce, 0x94, 0 };
+	size_t width;
+
+//TODO: avoid repeated calls by caching last string?
+	setlocale(LC_ALL, (LANG && LANG[0]) ? LANG : "C");
+
+	/* 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);
+	unicode_status = (width == 1 ? UNICODE_ON : UNICODE_OFF);
+}
+
+void FAST_FUNC init_unicode(void)
+{
+	if (unicode_status == UNICODE_UNKNOWN)
+		reinit_unicode(getenv("LANG"));
+}
+
+#else
+
+/* Homegrown Unicode support. It knows only C and Unicode locales. */
+
+# if ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
+void FAST_FUNC reinit_unicode(const char *LANG)
+{
+	unicode_status = UNICODE_OFF;
+	if (!LANG || !(strstr(LANG, ".utf") || strstr(LANG, ".UTF")))
+		return;
+	unicode_status = UNICODE_ON;
+}
+
+void FAST_FUNC init_unicode(void)
+{
+	if (unicode_status == UNICODE_UNKNOWN)
+		reinit_unicode(getenv("LANG"));
+}
+# endif
+
+static size_t wcrtomb_internal(char *s, wchar_t wc)
+{
+	int n, i;
+	uint32_t v = wc;
+
+	if (v <= 0x7f) {
+		*s = v;
+		return 1;
+	}
+
+	/* RFC 3629 says that Unicode ends at 10FFFF,
+	 * but we cover entire 32 bits */
+
+	/* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
+	/* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
+	/* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */
+	/* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */
+	/* 80-7FF -> 110yyyxx 10xxxxxx */
+
+	/* How many bytes do we need? */
+	n = 2;
+	/* (0x80000000+ would result in n = 7, limiting n to 6) */
+	while (v >= 0x800 && n < 6) {
+		v >>= 5;
+		n++;
+	}
+	/* Fill bytes n-1..1 */
+	i = n;
+	while (--i) {
+		s[i] = (wc & 0x3f) | 0x80;
+		wc >>= 6;
+	}
+	/* Fill byte 0 */
+	s[0] = wc | (uint8_t)(0x3f00 >> n);
+	return n;
+}
+size_t FAST_FUNC wcrtomb(char *s, wchar_t wc, mbstate_t *ps UNUSED_PARAM)
+{
+	if (unicode_status != UNICODE_ON) {
+		*s = wc;
+		return 1;
+	}
+
+	return wcrtomb_internal(s, wc);
+}
+size_t FAST_FUNC wcstombs(char *dest, const wchar_t *src, size_t n)
+{
+	size_t org_n = n;
+
+	if (unicode_status != UNICODE_ON) {
+		while (n) {
+			wchar_t c = *src++;
+			*dest++ = c;
+			if (c == 0)
+				break;
+			n--;
+		}
+		return org_n - n;
+	}
+
+	while (n >= MB_CUR_MAX) {
+		wchar_t wc = *src++;
+		size_t len = wcrtomb_internal(dest, wc);
+
+		if (wc == L'\0')
+			return org_n - n;
+		dest += len;
+		n -= len;
+	}
+	while (n) {
+		char tbuf[MB_CUR_MAX];
+		wchar_t wc = *src++;
+		size_t len = wcrtomb_internal(tbuf, wc);
+
+		if (len > n)
+			break;
+		memcpy(dest, tbuf, len);
+		if (wc == L'\0')
+			return org_n - n;
+		dest += len;
+		n -= len;
+	}
+	return org_n - n;
+}
+
+# define ERROR_WCHAR (~(wchar_t)0)
+
+static const char *mbstowc_internal(wchar_t *res, const char *src)
+{
+	int bytes;
+	unsigned c = (unsigned char) *src++;
+
+	if (c <= 0x7f) {
+		*res = c;
+		return src;
+	}
+
+	/* 80-7FF -> 110yyyxx 10xxxxxx */
+	/* 800-FFFF -> 1110yyyy 10yyyyxx 10xxxxxx */
+	/* 10000-1FFFFF -> 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx */
+	/* 200000-3FFFFFF -> 111110tt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
+	/* 4000000-FFFFFFFF -> 111111tt 10tttttt 10zzzzzz 10zzyyyy 10yyyyxx 10xxxxxx */
+	bytes = 0;
+	do {
+		c <<= 1;
+		bytes++;
+	} while ((c & 0x80) && bytes < 6);
+	if (bytes == 1) {
+		/* A bare "continuation" byte. Say, 80 */
+		*res = ERROR_WCHAR;
+		return src;
+	}
+	c = (uint8_t)(c) >> bytes;
+
+	while (--bytes) {
+		unsigned ch = (unsigned char) *src;
+		if ((ch & 0xc0) != 0x80) {
+			/* Missing "continuation" byte. Example: e0 80 */
+			*res = ERROR_WCHAR;
+			return src;
+		}
+		c = (c << 6) + (ch & 0x3f);
+		src++;
+	}
+
+	/* TODO */
+	/* Need to check that c isn't produced by overlong encoding */
+	/* Example: 11000000 10000000 converts to NUL */
+	/* 11110000 10000000 10000100 10000000 converts to 0x100 */
+	/* correct encoding: 11000100 10000000 */
+	if (c <= 0x7f) { /* crude check */
+		*res = ERROR_WCHAR;
+		return src;
+	}
+
+	*res = c;
+	return src;
+}
+size_t FAST_FUNC mbstowcs(wchar_t *dest, const char *src, size_t n)
+{
+	size_t org_n = n;
+
+	if (unicode_status != UNICODE_ON) {
+		while (n) {
+			unsigned char c = *src++;
+
+			if (dest)
+				*dest++ = c;
+			if (c == 0)
+				break;
+			n--;
+		}
+		return org_n - n;
+	}
+
+	while (n) {
+		wchar_t wc;
+		src = mbstowc_internal(&wc, src);
+		if (wc == ERROR_WCHAR) /* error */
+			return (size_t) -1L;
+		if (dest)
+			*dest++ = wc;
+		if (wc == 0) /* end-of-string */
+			break;
+		n--;
+	}
+
+	return org_n - n;
+}
+
+int FAST_FUNC iswspace(wint_t wc)
+{
+	return (unsigned)wc <= 0x7f && isspace(wc);
+}
+
+int FAST_FUNC iswalnum(wint_t wc)
+{
+	return (unsigned)wc <= 0x7f && isalnum(wc);
+}
+
+int FAST_FUNC iswpunct(wint_t wc)
+{
+	return (unsigned)wc <= 0x7f && ispunct(wc);
+}
+
+
+# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300
+struct interval {
+	uint16_t first;
+	uint16_t last;
+};
+
+/* auxiliary function for binary search in interval table */
+static int in_interval_table(unsigned ucs, const struct interval *table, unsigned max)
+{
+	unsigned min;
+	unsigned mid;
+
+	if (ucs < table[0].first || ucs > table[max].last)
+		return 0;
+
+	min = 0;
+	while (max >= min) {
+		mid = (min + max) / 2;
+		if (ucs > table[mid].last)
+			min = mid + 1;
+		else if (ucs < table[mid].first)
+			max = mid - 1;
+		else
+			return 1;
+	}
+	return 0;
+}
+
+static int in_uint16_table(unsigned ucs, const uint16_t *table, unsigned max)
+{
+	unsigned min;
+	unsigned mid;
+	unsigned first, last;
+
+	first = table[0] >> 2;
+	last = first + (table[0] & 3);
+	if (ucs < first || ucs > last)
+		return 0;
+
+	min = 0;
+	while (max >= min) {
+		mid = (min + max) / 2;
+		first = table[mid] >> 2;
+		last = first + (table[mid] & 3);
+		if (ucs > last)
+			min = mid + 1;
+		else if (ucs < first)
+			max = mid - 1;
+		else
+			return 1;
+	}
+	return 0;
+}
+# endif
+
+
+/*
+ * This is an implementation of wcwidth() and wcswidth() (defined in
+ * IEEE Std 1002.1-2001) for Unicode.
+ *
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
+ *
+ * In fixed-width output devices, Latin characters all occupy a single
+ * "cell" position of equal width, whereas ideographic CJK characters
+ * occupy two such cells. Interoperability between terminal-line
+ * applications and (teletype-style) character terminals using the
+ * UTF-8 encoding requires agreement on which character should advance
+ * the cursor by how many cell positions. No established formal
+ * standards exist at present on which Unicode character shall occupy
+ * how many cell positions on character terminals. These routines are
+ * a first attempt of defining such behavior based on simple rules
+ * applied to data provided by the Unicode Consortium.
+ *
+ * For some graphical characters, the Unicode standard explicitly
+ * defines a character-cell width via the definition of the East Asian
+ * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
+ * In all these cases, there is no ambiguity about which width a
+ * terminal shall use. For characters in the East Asian Ambiguous (A)
+ * class, the width choice depends purely on a preference of backward
+ * compatibility with either historic CJK or Western practice.
+ * Choosing single-width for these characters is easy to justify as
+ * the appropriate long-term solution, as the CJK practice of
+ * displaying these characters as double-width comes from historic
+ * implementation simplicity (8-bit encoded characters were displayed
+ * single-width and 16-bit ones double-width, even for Greek,
+ * Cyrillic, etc.) and not any typographic considerations.
+ *
+ * Much less clear is the choice of width for the Not East Asian
+ * (Neutral) class. Existing practice does not dictate a width for any
+ * of these characters. It would nevertheless make sense
+ * typographically to allocate two character cells to characters such
+ * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
+ * represented adequately with a single-width glyph. The following
+ * routines at present merely assign a single-cell width to all
+ * neutral characters, in the interest of simplicity. This is not
+ * entirely satisfactory and should be reconsidered before
+ * establishing a formal standard in this area. At the moment, the
+ * decision which Not East Asian (Neutral) characters should be
+ * represented by double-width glyphs cannot yet be answered by
+ * applying a simple rule from the Unicode database content. Setting
+ * up a proper standard for the behavior of UTF-8 character terminals
+ * will require a careful analysis not only of each Unicode character,
+ * but also of each presentation form, something the author of these
+ * routines has avoided to do so far.
+ *
+ * http://www.unicode.org/unicode/reports/tr11/
+ *
+ * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * for any purpose and without fee is hereby granted. The author
+ * disclaims all warranties with regard to this software.
+ *
+ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+ */
+
+/* Assigned Unicode character ranges:
+ * Plane Range
+ * 0       0000–FFFF   Basic Multilingual Plane
+ * 1      10000–1FFFF  Supplementary Multilingual Plane
+ * 2      20000–2FFFF  Supplementary Ideographic Plane
+ * 3      30000-3FFFF  Tertiary Ideographic Plane (no chars assigned yet)
+ * 4-13   40000–DFFFF  currently unassigned
+ * 14     E0000–EFFFF  Supplementary Special-purpose Plane
+ * 15     F0000–FFFFF  Supplementary Private Use Area-A
+ * 16    100000–10FFFF Supplementary Private Use Area-B
+ *
+ * "Supplementary Special-purpose Plane currently contains non-graphical
+ * characters in two blocks of 128 and 240 characters. The first block
+ * is for language tag characters for use when language cannot be indicated
+ * through other protocols (such as the xml:lang  attribute in XML).
+ * The other block contains glyph variation selectors to indicate
+ * an alternate glyph for a character that cannot be determined by context."
+ *
+ * In simpler terms: it is a tool to fix the "Han unification" mess
+ * created by Unicode committee, to select Chinese/Japanese/Korean/Taiwan
+ * version of a character. (They forgot that the whole purpose of the Unicode
+ * was to be able to write all chars in one charset without such tricks).
+ * Until East Asian users say it is actually necessary to support these
+ * code points in console applications like busybox
+ * (i.e. do these chars ever appear in filenames, hostnames, text files
+ * and such?), we are treating these code points as invalid.
+ *
+ * Tertiary Ideographic Plane is also ignored for now,
+ * until Unicode committee assigns something there.
+ */
+/* The following two functions define the column width of an ISO 10646
+ * character as follows:
+ *
+ *    - The null character (U+0000) has a column width of 0.
+ *
+ *    - Other C0/C1 control characters and DEL will lead to a return
+ *      value of -1.
+ *
+ *    - Non-spacing and enclosing combining characters (general
+ *      category code Mn or Me in the Unicode database) have a
+ *      column width of 0.
+ *
+ *    - SOFT HYPHEN (U+00AD) has a column width of 1.
+ *
+ *    - Other format characters (general category code Cf in the Unicode
+ *      database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+ *
+ *    - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+ *      have a column width of 0.
+ *
+ *    - Spacing characters in the East Asian Wide (W) or East Asian
+ *      Full-width (F) category as defined in Unicode Technical
+ *      Report #11 have a column width of 2.
+ *
+ *    - All remaining characters (including all printable
+ *      ISO 8859-1 and WGL4 characters, Unicode control characters,
+ *      etc.) have a column width of 1.
+ *
+ * This implementation assumes that wchar_t characters are encoded
+ * in ISO 10646.
+ */
+int FAST_FUNC wcwidth(unsigned ucs)
+{
+# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300
+	/* sorted list of non-overlapping intervals of non-spacing characters */
+	/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
+#  define BIG_(a,b) { a, b },
+#  define PAIR(a,b)
+#  define ARRAY /* PAIR if < 0x4000 and no more than 4 chars big */ \
+		BIG_(0x0300, 0x036F) \
+		PAIR(0x0483, 0x0486) \
+		PAIR(0x0488, 0x0489) \
+		BIG_(0x0591, 0x05BD) \
+		PAIR(0x05BF, 0x05BF) \
+		PAIR(0x05C1, 0x05C2) \
+		PAIR(0x05C4, 0x05C5) \
+		PAIR(0x05C7, 0x05C7) \
+		PAIR(0x0600, 0x0603) \
+		BIG_(0x0610, 0x0615) \
+		BIG_(0x064B, 0x065E) \
+		PAIR(0x0670, 0x0670) \
+		BIG_(0x06D6, 0x06E4) \
+		PAIR(0x06E7, 0x06E8) \
+		PAIR(0x06EA, 0x06ED) \
+		PAIR(0x070F, 0x070F) \
+		PAIR(0x0711, 0x0711) \
+		BIG_(0x0730, 0x074A) \
+		BIG_(0x07A6, 0x07B0) \
+		BIG_(0x07EB, 0x07F3) \
+		PAIR(0x0901, 0x0902) \
+		PAIR(0x093C, 0x093C) \
+		BIG_(0x0941, 0x0948) \
+		PAIR(0x094D, 0x094D) \
+		PAIR(0x0951, 0x0954) \
+		PAIR(0x0962, 0x0963) \
+		PAIR(0x0981, 0x0981) \
+		PAIR(0x09BC, 0x09BC) \
+		PAIR(0x09C1, 0x09C4) \
+		PAIR(0x09CD, 0x09CD) \
+		PAIR(0x09E2, 0x09E3) \
+		PAIR(0x0A01, 0x0A02) \
+		PAIR(0x0A3C, 0x0A3C) \
+		PAIR(0x0A41, 0x0A42) \
+		PAIR(0x0A47, 0x0A48) \
+		PAIR(0x0A4B, 0x0A4D) \
+		PAIR(0x0A70, 0x0A71) \
+		PAIR(0x0A81, 0x0A82) \
+		PAIR(0x0ABC, 0x0ABC) \
+		BIG_(0x0AC1, 0x0AC5) \
+		PAIR(0x0AC7, 0x0AC8) \
+		PAIR(0x0ACD, 0x0ACD) \
+		PAIR(0x0AE2, 0x0AE3) \
+		PAIR(0x0B01, 0x0B01) \
+		PAIR(0x0B3C, 0x0B3C) \
+		PAIR(0x0B3F, 0x0B3F) \
+		PAIR(0x0B41, 0x0B43) \
+		PAIR(0x0B4D, 0x0B4D) \
+		PAIR(0x0B56, 0x0B56) \
+		PAIR(0x0B82, 0x0B82) \
+		PAIR(0x0BC0, 0x0BC0) \
+		PAIR(0x0BCD, 0x0BCD) \
+		PAIR(0x0C3E, 0x0C40) \
+		PAIR(0x0C46, 0x0C48) \
+		PAIR(0x0C4A, 0x0C4D) \
+		PAIR(0x0C55, 0x0C56) \
+		PAIR(0x0CBC, 0x0CBC) \
+		PAIR(0x0CBF, 0x0CBF) \
+		PAIR(0x0CC6, 0x0CC6) \
+		PAIR(0x0CCC, 0x0CCD) \
+		PAIR(0x0CE2, 0x0CE3) \
+		PAIR(0x0D41, 0x0D43) \
+		PAIR(0x0D4D, 0x0D4D) \
+		PAIR(0x0DCA, 0x0DCA) \
+		PAIR(0x0DD2, 0x0DD4) \
+		PAIR(0x0DD6, 0x0DD6) \
+		PAIR(0x0E31, 0x0E31) \
+		BIG_(0x0E34, 0x0E3A) \
+		BIG_(0x0E47, 0x0E4E) \
+		PAIR(0x0EB1, 0x0EB1) \
+		BIG_(0x0EB4, 0x0EB9) \
+		PAIR(0x0EBB, 0x0EBC) \
+		BIG_(0x0EC8, 0x0ECD) \
+		PAIR(0x0F18, 0x0F19) \
+		PAIR(0x0F35, 0x0F35) \
+		PAIR(0x0F37, 0x0F37) \
+		PAIR(0x0F39, 0x0F39) \
+		BIG_(0x0F71, 0x0F7E) \
+		BIG_(0x0F80, 0x0F84) \
+		PAIR(0x0F86, 0x0F87) \
+		PAIR(0x0FC6, 0x0FC6) \
+		BIG_(0x0F90, 0x0F97) \
+		BIG_(0x0F99, 0x0FBC) \
+		PAIR(0x102D, 0x1030) \
+		PAIR(0x1032, 0x1032) \
+		PAIR(0x1036, 0x1037) \
+		PAIR(0x1039, 0x1039) \
+		PAIR(0x1058, 0x1059) \
+		BIG_(0x1160, 0x11FF) \
+		PAIR(0x135F, 0x135F) \
+		PAIR(0x1712, 0x1714) \
+		PAIR(0x1732, 0x1734) \
+		PAIR(0x1752, 0x1753) \
+		PAIR(0x1772, 0x1773) \
+		PAIR(0x17B4, 0x17B5) \
+		BIG_(0x17B7, 0x17BD) \
+		PAIR(0x17C6, 0x17C6) \
+		BIG_(0x17C9, 0x17D3) \
+		PAIR(0x17DD, 0x17DD) \
+		PAIR(0x180B, 0x180D) \
+		PAIR(0x18A9, 0x18A9) \
+		PAIR(0x1920, 0x1922) \
+		PAIR(0x1927, 0x1928) \
+		PAIR(0x1932, 0x1932) \
+		PAIR(0x1939, 0x193B) \
+		PAIR(0x1A17, 0x1A18) \
+		PAIR(0x1B00, 0x1B03) \
+		PAIR(0x1B34, 0x1B34) \
+		BIG_(0x1B36, 0x1B3A) \
+		PAIR(0x1B3C, 0x1B3C) \
+		PAIR(0x1B42, 0x1B42) \
+		BIG_(0x1B6B, 0x1B73) \
+		BIG_(0x1DC0, 0x1DCA) \
+		PAIR(0x1DFE, 0x1DFF) \
+		BIG_(0x200B, 0x200F) \
+		BIG_(0x202A, 0x202E) \
+		PAIR(0x2060, 0x2063) \
+		BIG_(0x206A, 0x206F) \
+		BIG_(0x20D0, 0x20EF) \
+		BIG_(0x302A, 0x302F) \
+		PAIR(0x3099, 0x309A) \
+		/* Too big to be packed in PAIRs: */ \
+		BIG_(0xA806, 0xA806) \
+		BIG_(0xA80B, 0xA80B) \
+		BIG_(0xA825, 0xA826) \
+		BIG_(0xFB1E, 0xFB1E) \
+		BIG_(0xFE00, 0xFE0F) \
+		BIG_(0xFE20, 0xFE23) \
+		BIG_(0xFEFF, 0xFEFF) \
+		BIG_(0xFFF9, 0xFFFB)
+	static const struct interval combining[] = { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  define BIG_(a,b)
+#  define PAIR(a,b) (a << 2) | (b-a),
+	static const uint16_t combining1[] = { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1];
+#  define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1];
+	struct CHECK { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  undef ARRAY
+# endif
+
+	if (ucs == 0)
+		return 0;
+
+	/* Test for 8-bit control characters (00-1f, 80-9f, 7f) */
+	if ((ucs & ~0x80) < 0x20 || ucs == 0x7f)
+		return -1;
+	/* Quick abort if it is an obviously invalid char */
+	if (ucs > CONFIG_LAST_SUPPORTED_WCHAR)
+		return -1;
+
+	/* Optimization: no combining chars below 0x300 */
+	if (CONFIG_LAST_SUPPORTED_WCHAR < 0x300 || ucs < 0x300)
+		return 1;
+
+# if CONFIG_LAST_SUPPORTED_WCHAR >= 0x300
+	/* Binary search in table of non-spacing characters */
+	if (in_interval_table(ucs, combining, ARRAY_SIZE(combining) - 1))
+		return 0;
+	if (in_uint16_table(ucs, combining1, ARRAY_SIZE(combining1) - 1))
+		return 0;
+
+	/* Optimization: all chars below 0x1100 are not double-width */
+	if (CONFIG_LAST_SUPPORTED_WCHAR < 0x1100 || ucs < 0x1100)
+		return 1;
+
+#  if CONFIG_LAST_SUPPORTED_WCHAR >= 0x1100
+	/* Invalid code points: */
+	/* High (d800..dbff) and low (dc00..dfff) surrogates (valid only in UTF16) */
+	/* Private Use Area (e000..f8ff) */
+	/* Noncharacters fdd0..fdef */
+	if ((CONFIG_LAST_SUPPORTED_WCHAR >= 0xd800 && ucs >= 0xd800 && ucs <= 0xf8ff)
+	 || (CONFIG_LAST_SUPPORTED_WCHAR >= 0xfdd0 && ucs >= 0xfdd0 && ucs <= 0xfdef)
+	) {
+		return -1;
+	}
+	/* 0xfffe and 0xffff in every plane are invalid */
+	if (CONFIG_LAST_SUPPORTED_WCHAR >= 0xfffe && ((ucs & 0xfffe) == 0xfffe)) {
+		return -1;
+	}
+
+#   if CONFIG_LAST_SUPPORTED_WCHAR >= 0x10000
+	if (ucs >= 0x10000) {
+		/* Combining chars in Supplementary Multilingual Plane 0x1xxxx */
+		static const struct interval combining0x10000[] = {
+			{ 0x0A01, 0x0A03 }, { 0x0A05, 0x0A06 }, { 0x0A0C, 0x0A0F },
+			{ 0x0A38, 0x0A3A }, { 0x0A3F, 0x0A3F }, { 0xD167, 0xD169 },
+			{ 0xD173, 0xD182 }, { 0xD185, 0xD18B }, { 0xD1AA, 0xD1AD },
+			{ 0xD242, 0xD244 }
+		};
+		/* Binary search in table of non-spacing characters in Supplementary Multilingual Plane */
+		if (in_interval_table(ucs ^ 0x10000, combining0x10000, ARRAY_SIZE(combining0x10000) - 1))
+			return 0;
+		/* Check a few non-spacing chars in Supplementary Special-purpose Plane 0xExxxx */
+		if (CONFIG_LAST_SUPPORTED_WCHAR >= 0xE0001
+		 && (  ucs == 0xE0001
+		    || (ucs >= 0xE0020 && ucs <= 0xE007F)
+		    || (ucs >= 0xE0100 && ucs <= 0xE01EF)
+		    )
+		) {
+			return 0;
+		}
+	}
+#   endif
+
+	/* If we arrive here, ucs is not a combining or C0/C1 control character.
+	 * Check whether it's 1 char or 2-shar wide.
+	 */
+	return 1 +
+		(  (/*ucs >= 0x1100 &&*/ ucs <= 0x115f) /* Hangul Jamo init. consonants */
+		|| ucs == 0x2329 /* left-pointing angle bracket; also CJK punct. char */
+		|| ucs == 0x232a /* right-pointing angle bracket; also CJK punct. char */
+		|| (ucs >= 0x2e80 && ucs <= 0xa4cf && ucs != 0x303f) /* CJK ... Yi */
+#   if CONFIG_LAST_SUPPORTED_WCHAR >= 0xac00
+		|| (ucs >= 0xac00 && ucs <= 0xd7a3) /* Hangul Syllables */
+		|| (ucs >= 0xf900 && ucs <= 0xfaff) /* CJK Compatibility Ideographs */
+		|| (ucs >= 0xfe10 && ucs <= 0xfe19) /* Vertical forms */
+		|| (ucs >= 0xfe30 && ucs <= 0xfe6f) /* CJK Compatibility Forms */
+		|| (ucs >= 0xff00 && ucs <= 0xff60) /* Fullwidth Forms */
+		|| (ucs >= 0xffe0 && ucs <= 0xffe6)
+		|| ((ucs >> 17) == (2 >> 1)) /* 20000..3ffff: Supplementary and Tertiary Ideographic Planes */
+#   endif
+		);
+#  endif /* >= 0x1100 */
+# endif /* >= 0x300 */
+}
+
+
+# if ENABLE_UNICODE_BIDI_SUPPORT
+int FAST_FUNC unicode_bidi_isrtl(wint_t wc)
+{
+	/* ranges taken from
+	 * http://www.unicode.org/Public/5.2.0/ucd/extracted/DerivedBidiClass.txt
+	 * Bidi_Class=Left_To_Right | Bidi_Class=Arabic_Letter
+	 */
+#  define BIG_(a,b) { a, b },
+#  define PAIR(a,b)
+#  define ARRAY \
+		PAIR(0x0590, 0x0590) \
+		PAIR(0x05BE, 0x05BE) \
+		PAIR(0x05C0, 0x05C0) \
+		PAIR(0x05C3, 0x05C3) \
+		PAIR(0x05C6, 0x05C6) \
+		BIG_(0x05C8, 0x05FF) \
+		PAIR(0x0604, 0x0605) \
+		PAIR(0x0608, 0x0608) \
+		PAIR(0x060B, 0x060B) \
+		PAIR(0x060D, 0x060D) \
+		BIG_(0x061B, 0x064A) \
+		PAIR(0x065F, 0x065F) \
+		PAIR(0x066D, 0x066F) \
+		BIG_(0x0671, 0x06D5) \
+		PAIR(0x06E5, 0x06E6) \
+		PAIR(0x06EE, 0x06EF) \
+		BIG_(0x06FA, 0x070E) \
+		PAIR(0x0710, 0x0710) \
+		BIG_(0x0712, 0x072F) \
+		BIG_(0x074B, 0x07A5) \
+		BIG_(0x07B1, 0x07EA) \
+		PAIR(0x07F4, 0x07F5) \
+		BIG_(0x07FA, 0x0815) \
+		PAIR(0x081A, 0x081A) \
+		PAIR(0x0824, 0x0824) \
+		PAIR(0x0828, 0x0828) \
+		BIG_(0x082E, 0x08FF) \
+		PAIR(0x200F, 0x200F) \
+		PAIR(0x202B, 0x202B) \
+		PAIR(0x202E, 0x202E) \
+		BIG_(0xFB1D, 0xFB1D) \
+		BIG_(0xFB1F, 0xFB28) \
+		BIG_(0xFB2A, 0xFD3D) \
+		BIG_(0xFD40, 0xFDCF) \
+		BIG_(0xFDC8, 0xFDCF) \
+		BIG_(0xFDF0, 0xFDFC) \
+		BIG_(0xFDFE, 0xFDFF) \
+		BIG_(0xFE70, 0xFEFE)
+		/* Probably not necessary
+		{0x10800, 0x1091E},
+		{0x10920, 0x10A00},
+		{0x10A04, 0x10A04},
+		{0x10A07, 0x10A0B},
+		{0x10A10, 0x10A37},
+		{0x10A3B, 0x10A3E},
+		{0x10A40, 0x10A7F},
+		{0x10B36, 0x10B38},
+		{0x10B40, 0x10E5F},
+		{0x10E7F, 0x10FFF},
+		{0x1E800, 0x1EFFF}
+		*/
+	static const struct interval rtl_b[] = { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  define BIG_(a,b)
+#  define PAIR(a,b) (a << 2) | (b-a),
+	static const uint16_t rtl_p[] = { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1];
+#  define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1];
+	struct CHECK { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  undef ARRAY
+
+	if (in_interval_table(wc, rtl_b, ARRAY_SIZE(rtl_b) - 1))
+		return 1;
+	if (in_uint16_table(wc, rtl_p, ARRAY_SIZE(rtl_p) - 1))
+		return 1;
+	return 0;
+}
+
+#  if ENABLE_UNICODE_NEUTRAL_TABLE
+int FAST_FUNC unicode_bidi_is_neutral_wchar(wint_t wc)
+{
+	/* ranges taken from
+	 * http://www.unicode.org/Public/5.2.0/ucd/extracted/DerivedBidiClass.txt
+	 * Bidi_Classes: Paragraph_Separator, Segment_Separator,
+	 * White_Space, Other_Neutral, European_Number, European_Separator,
+	 * European_Terminator, Arabic_Number, Common_Separator
+	 */
+#  define BIG_(a,b) { a, b },
+#  define PAIR(a,b)
+#  define ARRAY \
+		BIG_(0x0009, 0x000D) \
+		BIG_(0x001C, 0x0040) \
+		BIG_(0x005B, 0x0060) \
+		PAIR(0x007B, 0x007E) \
+		PAIR(0x0085, 0x0085) \
+		BIG_(0x00A0, 0x00A9) \
+		PAIR(0x00AB, 0x00AC) \
+		BIG_(0x00AE, 0x00B4) \
+		PAIR(0x00B6, 0x00B9) \
+		BIG_(0x00BB, 0x00BF) \
+		PAIR(0x00D7, 0x00D7) \
+		PAIR(0x00F7, 0x00F7) \
+		PAIR(0x02B9, 0x02BA) \
+		BIG_(0x02C2, 0x02CF) \
+		BIG_(0x02D2, 0x02DF) \
+		BIG_(0x02E5, 0x02FF) \
+		PAIR(0x0374, 0x0375) \
+		PAIR(0x037E, 0x037E) \
+		PAIR(0x0384, 0x0385) \
+		PAIR(0x0387, 0x0387) \
+		PAIR(0x03F6, 0x03F6) \
+		PAIR(0x058A, 0x058A) \
+		PAIR(0x0600, 0x0603) \
+		PAIR(0x0606, 0x0607) \
+		PAIR(0x0609, 0x060A) \
+		PAIR(0x060C, 0x060C) \
+		PAIR(0x060E, 0x060F) \
+		BIG_(0x0660, 0x066C) \
+		PAIR(0x06DD, 0x06DD) \
+		PAIR(0x06E9, 0x06E9) \
+		BIG_(0x06F0, 0x06F9) \
+		PAIR(0x07F6, 0x07F9) \
+		PAIR(0x09F2, 0x09F3) \
+		PAIR(0x09FB, 0x09FB) \
+		PAIR(0x0AF1, 0x0AF1) \
+		BIG_(0x0BF3, 0x0BFA) \
+		BIG_(0x0C78, 0x0C7E) \
+		PAIR(0x0CF1, 0x0CF2) \
+		PAIR(0x0E3F, 0x0E3F) \
+		PAIR(0x0F3A, 0x0F3D) \
+		BIG_(0x1390, 0x1400) \
+		PAIR(0x1680, 0x1680) \
+		PAIR(0x169B, 0x169C) \
+		PAIR(0x17DB, 0x17DB) \
+		BIG_(0x17F0, 0x17F9) \
+		BIG_(0x1800, 0x180A) \
+		PAIR(0x180E, 0x180E) \
+		PAIR(0x1940, 0x1940) \
+		PAIR(0x1944, 0x1945) \
+		BIG_(0x19DE, 0x19FF) \
+		PAIR(0x1FBD, 0x1FBD) \
+		PAIR(0x1FBF, 0x1FC1) \
+		PAIR(0x1FCD, 0x1FCF) \
+		PAIR(0x1FDD, 0x1FDF) \
+		PAIR(0x1FED, 0x1FEF) \
+		PAIR(0x1FFD, 0x1FFE) \
+		BIG_(0x2000, 0x200A) \
+		BIG_(0x2010, 0x2029) \
+		BIG_(0x202F, 0x205F) \
+		PAIR(0x2070, 0x2070) \
+		BIG_(0x2074, 0x207E) \
+		BIG_(0x2080, 0x208E) \
+		BIG_(0x20A0, 0x20B8) \
+		PAIR(0x2100, 0x2101) \
+		PAIR(0x2103, 0x2106) \
+		PAIR(0x2108, 0x2109) \
+		PAIR(0x2114, 0x2114) \
+		PAIR(0x2116, 0x2118) \
+		BIG_(0x211E, 0x2123) \
+		PAIR(0x2125, 0x2125) \
+		PAIR(0x2127, 0x2127) \
+		PAIR(0x2129, 0x2129) \
+		PAIR(0x212E, 0x212E) \
+		PAIR(0x213A, 0x213B) \
+		BIG_(0x2140, 0x2144) \
+		PAIR(0x214A, 0x214D) \
+		BIG_(0x2150, 0x215F) \
+		PAIR(0x2189, 0x2189) \
+		BIG_(0x2190, 0x2335) \
+		BIG_(0x237B, 0x2394) \
+		BIG_(0x2396, 0x23E8) \
+		BIG_(0x2400, 0x2426) \
+		BIG_(0x2440, 0x244A) \
+		BIG_(0x2460, 0x249B) \
+		BIG_(0x24EA, 0x26AB) \
+		BIG_(0x26AD, 0x26CD) \
+		BIG_(0x26CF, 0x26E1) \
+		PAIR(0x26E3, 0x26E3) \
+		BIG_(0x26E8, 0x26FF) \
+		PAIR(0x2701, 0x2704) \
+		PAIR(0x2706, 0x2709) \
+		BIG_(0x270C, 0x2727) \
+		BIG_(0x2729, 0x274B) \
+		PAIR(0x274D, 0x274D) \
+		PAIR(0x274F, 0x2752) \
+		BIG_(0x2756, 0x275E) \
+		BIG_(0x2761, 0x2794) \
+		BIG_(0x2798, 0x27AF) \
+		BIG_(0x27B1, 0x27BE) \
+		BIG_(0x27C0, 0x27CA) \
+		PAIR(0x27CC, 0x27CC) \
+		BIG_(0x27D0, 0x27FF) \
+		BIG_(0x2900, 0x2B4C) \
+		BIG_(0x2B50, 0x2B59) \
+		BIG_(0x2CE5, 0x2CEA) \
+		BIG_(0x2CF9, 0x2CFF) \
+		BIG_(0x2E00, 0x2E99) \
+		BIG_(0x2E9B, 0x2EF3) \
+		BIG_(0x2F00, 0x2FD5) \
+		BIG_(0x2FF0, 0x2FFB) \
+		BIG_(0x3000, 0x3004) \
+		BIG_(0x3008, 0x3020) \
+		PAIR(0x3030, 0x3030) \
+		PAIR(0x3036, 0x3037) \
+		PAIR(0x303D, 0x303D) \
+		PAIR(0x303E, 0x303F) \
+		PAIR(0x309B, 0x309C) \
+		PAIR(0x30A0, 0x30A0) \
+		PAIR(0x30FB, 0x30FB) \
+		BIG_(0x31C0, 0x31E3) \
+		PAIR(0x321D, 0x321E) \
+		BIG_(0x3250, 0x325F) \
+		PAIR(0x327C, 0x327E) \
+		BIG_(0x32B1, 0x32BF) \
+		PAIR(0x32CC, 0x32CF) \
+		PAIR(0x3377, 0x337A) \
+		PAIR(0x33DE, 0x33DF) \
+		PAIR(0x33FF, 0x33FF) \
+		BIG_(0x4DC0, 0x4DFF) \
+		BIG_(0xA490, 0xA4C6) \
+		BIG_(0xA60D, 0xA60F) \
+		BIG_(0xA673, 0xA673) \
+		BIG_(0xA67E, 0xA67F) \
+		BIG_(0xA700, 0xA721) \
+		BIG_(0xA788, 0xA788) \
+		BIG_(0xA828, 0xA82B) \
+		BIG_(0xA838, 0xA839) \
+		BIG_(0xA874, 0xA877) \
+		BIG_(0xFB29, 0xFB29) \
+		BIG_(0xFD3E, 0xFD3F) \
+		BIG_(0xFDFD, 0xFDFD) \
+		BIG_(0xFE10, 0xFE19) \
+		BIG_(0xFE30, 0xFE52) \
+		BIG_(0xFE54, 0xFE66) \
+		BIG_(0xFE68, 0xFE6B) \
+		BIG_(0xFF01, 0xFF20) \
+		BIG_(0xFF3B, 0xFF40) \
+		BIG_(0xFF5B, 0xFF65) \
+		BIG_(0xFFE0, 0xFFE6) \
+		BIG_(0xFFE8, 0xFFEE) \
+		BIG_(0xFFF9, 0xFFFD)
+		/*
+		{0x10101, 0x10101},
+		{0x10140, 0x1019B},
+		{0x1091F, 0x1091F},
+		{0x10B39, 0x10B3F},
+		{0x10E60, 0x10E7E},
+		{0x1D200, 0x1D241},
+		{0x1D245, 0x1D245},
+		{0x1D300, 0x1D356},
+		{0x1D6DB, 0x1D6DB},
+		{0x1D715, 0x1D715},
+		{0x1D74F, 0x1D74F},
+		{0x1D789, 0x1D789},
+		{0x1D7C3, 0x1D7C3},
+		{0x1D7CE, 0x1D7FF},
+		{0x1F000, 0x1F02B},
+		{0x1F030, 0x1F093},
+		{0x1F100, 0x1F10A}
+		*/
+	static const struct interval neutral_b[] = { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  define BIG_(a,b)
+#  define PAIR(a,b) (a << 2) | (b-a),
+	static const uint16_t neutral_p[] = { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  define BIG_(a,b) char big_##a[b < 0x4000 && b-a <= 3 ? -1 : 1];
+#  define PAIR(a,b) char pair##a[b >= 0x4000 || b-a > 3 ? -1 : 1];
+	struct CHECK { ARRAY };
+#  undef BIG_
+#  undef PAIR
+#  undef ARRAY
+
+	if (in_interval_table(wc, neutral_b, ARRAY_SIZE(neutral_b) - 1))
+		return 1;
+	if (in_uint16_table(wc, neutral_p, ARRAY_SIZE(neutral_p) - 1))
+		return 1;
+	return 0;
+}
+#  endif
+
+# endif /* UNICODE_BIDI_SUPPORT */
+
+#endif /* Homegrown Unicode support */
+
+
+/* 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);
+	if (width == (size_t)-1L)
+		return strlen(string);
+	return width;
+}
+#endif
+
+size_t FAST_FUNC unicode_strwidth(const char *string)
+{
+	uni_stat_t uni_stat;
+	printable_string(&uni_stat, string);
+	return uni_stat.unicode_width;
+}
+
+static char* FAST_FUNC unicode_conv_to_printable2(uni_stat_t *stats, const char *src, unsigned width, int flags)
+{
+	char *dst;
+	unsigned dst_len;
+	unsigned uni_count;
+	unsigned uni_width;
+
+	if (unicode_status != UNICODE_ON) {
+		char *d;
+		if (flags & UNI_FLAG_PAD) {
+			d = dst = xmalloc(width + 1);
+			while ((int)--width >= 0) {
+				unsigned char c = *src;
+				if (c == '\0') {
+					do
+						*d++ = ' ';
+					while ((int)--width >= 0);
+					break;
+				}
+				*d++ = (c >= ' ' && c < 0x7f) ? c : '?';
+				src++;
+			}
+			*d = '\0';
+		} else {
+			d = dst = xstrndup(src, width);
+			while (*d) {
+				unsigned char c = *d;
+				if (c < ' ' || c >= 0x7f)
+					*d = '?';
+				d++;
+			}
+		}
+		if (stats) {
+			stats->byte_count = (d - dst);
+			stats->unicode_count = (d - dst);
+			stats->unicode_width = (d - dst);
+		}
+		return dst;
+	}
+
+	dst = NULL;
+	uni_count = uni_width = 0;
+	dst_len = 0;
+	while (1) {
+		int w;
+		wchar_t wc;
+
+#if ENABLE_UNICODE_USING_LOCALE
+		{
+			mbstate_t mbst = { 0 };
+			ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst);
+			/* If invalid sequence is seen: -1 is returned,
+			 * src points to the invalid sequence, errno = EILSEQ.
+			 * Else number of wchars (excluding terminating L'\0')
+			 * written to dest is returned.
+			 * If len (here: 1) non-L'\0' wchars stored at dest,
+			 * src points to the next char to be converted.
+			 * If string is completely converted: src = NULL.
+			 */
+			if (rc == 0) /* end-of-string */
+				break;
+			if (rc < 0) { /* error */
+				src++;
+				goto subst;
+			}
+			if (!iswprint(wc))
+				goto subst;
+		}
+#else
+		src = mbstowc_internal(&wc, src);
+		/* src is advanced to next mb char
+		 * wc == ERROR_WCHAR: invalid sequence is seen
+		 * else: wc is set
+		 */
+		if (wc == ERROR_WCHAR) /* error */
+			goto subst;
+		if (wc == 0) /* end-of-string */
+			break;
+#endif
+		if (CONFIG_LAST_SUPPORTED_WCHAR && wc > CONFIG_LAST_SUPPORTED_WCHAR)
+			goto subst;
+		w = wcwidth(wc);
+		if ((ENABLE_UNICODE_COMBINING_WCHARS && w < 0) /* non-printable wchar */
+		 || (!ENABLE_UNICODE_COMBINING_WCHARS && w <= 0)
+		 || (!ENABLE_UNICODE_WIDE_WCHARS && w > 1)
+		) {
+ subst:
+			wc = CONFIG_SUBST_WCHAR;
+			w = 1;
+		}
+		width -= w;
+		/* Note: if width == 0, we still may add more chars,
+		 * they may be zero-width or combining ones */
+		if ((int)width < 0) {
+			/* can't add this wc, string would become longer than width */
+			width += w;
+			break;
+		}
+
+		uni_count++;
+		uni_width += w;
+		dst = xrealloc(dst, dst_len + MB_CUR_MAX);
+#if ENABLE_UNICODE_USING_LOCALE
+		{
+			mbstate_t mbst = { 0 };
+			dst_len += wcrtomb(&dst[dst_len], wc, &mbst);
+		}
+#else
+		dst_len += wcrtomb_internal(&dst[dst_len], wc);
+#endif
+	}
+
+	/* Pad to remaining width */
+	if (flags & UNI_FLAG_PAD) {
+		dst = xrealloc(dst, dst_len + width + 1);
+		uni_count += width;
+		uni_width += width;
+		while ((int)--width >= 0) {
+			dst[dst_len++] = ' ';
+		}
+	}
+	dst[dst_len] = '\0';
+	if (stats) {
+		stats->byte_count = dst_len;
+		stats->unicode_count = uni_count;
+		stats->unicode_width = uni_width;
+	}
+
+	return dst;
+}
+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_fixedwidth(/*uni_stat_t *stats,*/ const char *src, unsigned width)
+{
+	return unicode_conv_to_printable2(/*stats:*/ NULL, src, width, UNI_FLAG_PAD);
+}
+
+#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, maxwidth, 0);
+}
+
+unsigned FAST_FUNC unicode_padding_to_width(unsigned width, const char *src)
+{
+	if (unicode_status != UNICODE_ON) {
+		return width - strnlen(src, width);
+	}
+
+	while (1) {
+		int w;
+		wchar_t wc;
+
+#if ENABLE_UNICODE_USING_LOCALE
+		{
+			mbstate_t mbst = { 0 };
+			ssize_t rc = mbsrtowcs(&wc, &src, 1, &mbst);
+			if (rc <= 0) /* error, or end-of-string */
+				return width;
+		}
+#else
+		src = mbstowc_internal(&wc, src);
+		if (wc == ERROR_WCHAR || wc == 0) /* error, or end-of-string */
+			return width;
+#endif
+		w = wcwidth(wc);
+		if (w < 0) /* non-printable wchar */
+			return width;
+		width -= w;
+		if ((int)width <= 0) /* string is longer than width */
+			return 0;
+	}
+}
+#endif
diff --git a/busybox-1.19.3/libbb/update_passwd.c b/busybox-1.19.3/libbb/update_passwd.c
new file mode 100644
index 0000000..a30af6f
--- /dev/null
+++ b/busybox-1.19.3/libbb/update_passwd.c
@@ -0,0 +1,285 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * update_passwd
+ *
+ * update_passwd is a common function for passwd and chpasswd applets;
+ * it is responsible for updating password file (i.e. /etc/passwd or
+ * /etc/shadow) for a given user and password.
+ *
+ * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org>
+ *
+ * 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 source tree.
+ */
+#include "libbb.h"
+
+#if ENABLE_SELINUX
+static void check_selinux_update_passwd(const char *username)
+{
+	security_context_t context;
+	char *seuser;
+
+	if (getuid() != (uid_t)0 || is_selinux_enabled() == 0)
+		return;  /* No need to check */
+
+	if (getprevcon_raw(&context) < 0)
+		bb_perror_msg_and_die("getprevcon failed");
+	seuser = strtok(context, ":");
+	if (!seuser)
+		bb_error_msg_and_die("invalid context '%s'", context);
+	if (strcmp(seuser, username) != 0) {
+		if (checkPasswdAccess(PASSWD__PASSWD) != 0)
+			bb_error_msg_and_die("SELinux: access denied");
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		freecon(context);
+}
+#else
+# define check_selinux_update_passwd(username) ((void)0)
+#endif
+
+/*
+ 1) add a user: update_passwd(FILE, USER, REMAINING_PWLINE, NULL)
+    only if CONFIG_ADDUSER=y and applet_name[0] == 'a' like in adduser
+
+ 2) add a group: update_passwd(FILE, GROUP, REMAINING_GRLINE, NULL)
+    only if CONFIG_ADDGROUP=y and applet_name[0] == 'a' like in addgroup
+
+ 3) add a user to a group: update_passwd(FILE, GROUP, NULL, MEMBER)
+    only if CONFIG_FEATURE_ADDUSER_TO_GROUP=y, applet_name[0] == 'a'
+    like in addgroup and member != NULL
+
+ 4) delete a user: update_passwd(FILE, USER, NULL, NULL)
+
+ 5) delete a group: update_passwd(FILE, GROUP, NULL, NULL)
+
+ 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 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
+
+ This function does not validate the arguments fed to it
+ so the calling program should take care of that.
+
+ Returns number of lines changed, or -1 on error.
+*/
+int FAST_FUNC update_passwd(const char *filename,
+		const char *name,
+		const char *new_passwd,
+		const char *member)
+{
+#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
+#define member NULL
+#endif
+	struct stat sb;
+	struct flock lock;
+	FILE *old_fp;
+	FILE *new_fp;
+	char *fnamesfx;
+	char *sfx_char;
+	char *name_colon;
+	unsigned user_len;
+	int old_fd;
+	int new_fd;
+	int i;
+	int changed_lines;
+	int ret = -1; /* failure */
+	/* used as a bool: "are we modifying /etc/shadow?" */
+#if ENABLE_FEATURE_SHADOWPASSWDS
+	const char *shadow = strstr(filename, "shadow");
+#else
+# define shadow NULL
+#endif
+
+	filename = xmalloc_follow_symlinks(filename);
+	if (filename == NULL)
+		return ret;
+
+	check_selinux_update_passwd(name);
+
+	/* New passwd file, "/etc/passwd+" for now */
+	fnamesfx = xasprintf("%s+", filename);
+	sfx_char = &fnamesfx[strlen(fnamesfx)-1];
+	name_colon = xasprintf("%s:", name);
+	user_len = strlen(name_colon);
+
+	if (shadow)
+		old_fp = fopen(filename, "r+");
+	else
+		old_fp = fopen_or_warn(filename, "r+");
+	if (!old_fp) {
+		if (shadow)
+			ret = 0; /* missing shadow is not an error */
+		goto free_mem;
+	}
+	old_fd = fileno(old_fp);
+
+	selinux_preserve_fcontext(old_fd);
+
+	/* Try to create "/etc/passwd+". Wait if it exists. */
+	i = 30;
+	do {
+		// FIXME: on last iteration try w/o O_EXCL but with O_TRUNC?
+		new_fd = open(fnamesfx, O_WRONLY|O_CREAT|O_EXCL, 0600);
+		if (new_fd >= 0) goto created;
+		if (errno != EEXIST) break;
+		usleep(100000); /* 0.1 sec */
+	} while (--i);
+	bb_perror_msg("can't create '%s'", fnamesfx);
+	goto close_old_fp;
+
+ created:
+	if (fstat(old_fd, &sb) == 0) {
+		fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
+		fchown(new_fd, sb.st_uid, sb.st_gid);
+	}
+	errno = 0;
+	new_fp = xfdopen_for_write(new_fd);
+
+	/* Backup file is "/etc/passwd-" */
+	*sfx_char = '-';
+	/* Delete old backup */
+	i = (unlink(fnamesfx) && errno != ENOENT);
+	/* Create backup as a hardlink to current */
+	if (i || link(filename, fnamesfx))
+		bb_perror_msg("warning: can't create backup copy '%s'",
+				fnamesfx);
+	*sfx_char = '+';
+
+	/* Lock the password file before updating */
+	lock.l_type = F_WRLCK;
+	lock.l_whence = SEEK_SET;
+	lock.l_start = 0;
+	lock.l_len = 0;
+	if (fcntl(old_fd, F_SETLK, &lock) < 0)
+		bb_perror_msg("warning: can't lock '%s'", filename);
+	lock.l_type = F_UNLCK;
+
+	/* Read current password file, write updated /etc/passwd+ */
+	changed_lines = 0;
+	while (1) {
+		char *cp, *line;
+
+		line = xmalloc_fgetline(old_fp);
+		if (!line) /* EOF/error */
+			break;
+		if (strncmp(name_colon, line, user_len) != 0) {
+			fprintf(new_fp, "%s\n", line);
+			goto next;
+		}
+
+		/* We have a match with "name:"... */
+		cp = line + user_len; /* move past name: */
+
+#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
+		if (member) {
+			/* It's actually /etc/group+, not /etc/passwd+ */
+			if (ENABLE_FEATURE_ADDUSER_TO_GROUP
+			 && applet_name[0] == 'a'
+			) {
+				/* Add user to group */
+				fprintf(new_fp, "%s%s%s\n", line,
+					last_char_is(line, ':') ? "" : ",",
+					member);
+				changed_lines++;
+			} else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP
+			/* && applet_name[0] == 'd' */
+			) {
+				/* Delete user from group */
+				char *tmp;
+				const char *fmt = "%s";
+
+				/* find the start of the member list: last ':' */
+				cp = strrchr(line, ':');
+				/* cut it */
+				*cp++ = '\0';
+				/* write the cut line name:passwd:gid:
+				 * or name:!:: */
+				fprintf(new_fp, "%s:", line);
+				/* parse the tokens of the member list */
+				tmp = cp;
+				while ((cp = strsep(&tmp, ",")) != NULL) {
+					if (strcmp(member, cp) != 0) {
+						fprintf(new_fp, fmt, cp);
+						fmt = ",%s";
+					} else {
+						/* found member, skip it */
+						changed_lines++;
+					}
+				}
+				fprintf(new_fp, "\n");
+			}
+		} else
+#endif
+		if ((ENABLE_PASSWD && applet_name[0] == 'p')
+		 || (ENABLE_CHPASSWD && applet_name[0] == 'c')
+		) {
+			/* Change passwd */
+			cp = strchrnul(cp, ':'); /* move past old passwd */
+
+			if (shadow && *cp == ':') {
+				/* /etc/shadow's field 3 (passwd change date) needs updating */
+				/* move past old change date */
+				cp = strchrnul(cp + 1, ':');
+				/* "name:" + "new_passwd" + ":" + "change date" + ":rest of line" */
+				fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd,
+					(unsigned)(time(NULL)) / (24*60*60), cp);
+			} else {
+				/* "name:" + "new_passwd" + ":rest of line" */
+				fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp);
+			}
+			changed_lines++;
+		} /* else delete user or group: skip the line */
+ next:
+		free(line);
+	}
+
+	if (changed_lines == 0) {
+#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
+		if (member) {
+			if (ENABLE_ADDGROUP && applet_name[0] == 'a')
+				bb_error_msg("can't find %s in %s", name, filename);
+			if (ENABLE_DELGROUP && applet_name[0] == 'd')
+				bb_error_msg("can't find %s in %s", member, filename);
+		}
+#endif
+		if ((ENABLE_ADDUSER || ENABLE_ADDGROUP)
+		 && applet_name[0] == 'a' && !member
+		) {
+			/* add user or group */
+			fprintf(new_fp, "%s%s\n", name_colon, new_passwd);
+			changed_lines++;
+		}
+	}
+
+	fcntl(old_fd, F_SETLK, &lock);
+
+	/* We do want all of them to execute, thus | instead of || */
+	errno = 0;
+	if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
+	 || rename(fnamesfx, filename)
+	) {
+		/* At least one of those failed */
+		bb_perror_nomsg();
+		goto unlink_new;
+	}
+	/* Success: ret >= 0 */
+	ret = changed_lines;
+
+ unlink_new:
+	if (ret < 0)
+		unlink(fnamesfx);
+
+ close_old_fp:
+	fclose(old_fp);
+
+ free_mem:
+	free(fnamesfx);
+	free((char *)filename);
+	free(name_colon);
+	return ret;
+}
diff --git a/busybox-1.19.3/libbb/utmp.c b/busybox-1.19.3/libbb/utmp.c
new file mode 100644
index 0000000..09443fb
--- /dev/null
+++ b/busybox-1.19.3/libbb/utmp.c
@@ -0,0 +1,132 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * utmp/wtmp support routines.
+ *
+ * Copyright (C) 2010 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+static void touch(const char *filename)
+{
+	if (access(filename, R_OK | W_OK) == -1)
+		close(open(filename, O_WRONLY | O_CREAT, 0664));
+}
+
+void FAST_FUNC write_new_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname)
+{
+	struct utmp utent;
+	char *id;
+	unsigned width;
+
+	memset(&utent, 0, sizeof(utent));
+	utent.ut_pid = pid;
+	utent.ut_type = new_type;
+	tty_name = skip_dev_pfx(tty_name);
+	safe_strncpy(utent.ut_line, tty_name, sizeof(utent.ut_line));
+	if (username)
+		safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user));
+	if (hostname)
+		safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host));
+	utent.ut_tv.tv_sec = time(NULL);
+
+	/* Invent our own ut_id. ut_id is only 4 chars wide.
+	 * Try to fit something remotely meaningful... */
+	id = utent.ut_id;
+	width = sizeof(utent.ut_id);
+	if (tty_name[0] == 'p') {
+		/* if "ptyXXX", map to "pXXX" */
+		/* if "pts/XX", map to "p/XX" */
+		*id++ = 'p';
+		width--;
+	} /* else: usually it's "ttyXXXX", map to "XXXX" */
+	if (strlen(tty_name) > 3)
+		tty_name += 3;
+	strncpy(id, tty_name, width);
+
+	touch(_PATH_UTMP);
+	//utmpname(_PATH_UTMP);
+	setutent();
+	/* Append new one (hopefully, unless we collide on ut_id) */
+	pututline(&utent);
+	endutent();
+
+#if ENABLE_FEATURE_WTMP
+	/* "man utmp" says wtmp file should *not* be created automagically */
+	/*touch(bb_path_wtmp_file);*/
+	updwtmp(bb_path_wtmp_file, &utent);
+#endif
+}
+
+/*
+ * Read "man utmp" to make sense out of it.
+ */
+void FAST_FUNC update_utmp(pid_t pid, int new_type, const char *tty_name, const char *username, const char *hostname)
+{
+	struct utmp utent;
+	struct utmp *utp;
+
+	touch(_PATH_UTMP);
+	//utmpname(_PATH_UTMP);
+	setutent();
+
+	/* Did init/getty/telnetd/sshd/... create an entry for us?
+	 * It should be (new_type-1), but we'd also reuse
+	 * any other potentially stale xxx_PROCESS entry */
+	while ((utp = getutent()) != NULL) {
+		if (utp->ut_pid == pid
+		// && ut->ut_line[0]
+		 && utp->ut_id[0] /* must have nonzero id */
+		 && (  utp->ut_type == INIT_PROCESS
+		    || utp->ut_type == LOGIN_PROCESS
+		    || utp->ut_type == USER_PROCESS
+		    || utp->ut_type == DEAD_PROCESS
+		    )
+		) {
+			if (utp->ut_type >= new_type) {
+				/* Stale record. Nuke hostname */
+				memset(utp->ut_host, 0, sizeof(utp->ut_host));
+			}
+			/* NB: pututline (see later) searches for matching utent
+			 * using getutid(utent) - we must not change ut_id
+			 * if we want *exactly this* record to be overwritten!
+			 */
+			break;
+		}
+	}
+	//endutent(); - no need, pututline can deal with (and actually likes)
+	//the situation when utmp file is positioned on found record
+
+	if (!utp) {
+		if (new_type != DEAD_PROCESS)
+			write_new_utmp(pid, new_type, tty_name, username, hostname);
+		else
+			endutent();
+		return;
+	}
+
+	/* Make a copy. We can't use *utp, pututline's internal getutid
+	 * will overwrite it before it is used! */
+	utent = *utp;
+
+	utent.ut_type = new_type;
+	if (tty_name)
+		safe_strncpy(utent.ut_line, skip_dev_pfx(tty_name), sizeof(utent.ut_line));
+	if (username)
+		safe_strncpy(utent.ut_user, username, sizeof(utent.ut_user));
+	if (hostname)
+		safe_strncpy(utent.ut_host, hostname, sizeof(utent.ut_host));
+	utent.ut_tv.tv_sec = time(NULL);
+
+	/* Update, or append new one */
+	//setutent();
+	pututline(&utent);
+	endutent();
+
+#if ENABLE_FEATURE_WTMP
+	/* "man utmp" says wtmp file should *not* be created automagically */
+	/*touch(bb_path_wtmp_file);*/
+	updwtmp(bb_path_wtmp_file, &utent);
+#endif
+}
diff --git a/busybox-1.19.3/libbb/uuencode.c b/busybox-1.19.3/libbb/uuencode.c
new file mode 100644
index 0000000..03e708f
--- /dev/null
+++ b/busybox-1.19.3/libbb/uuencode.c
@@ -0,0 +1,145 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 source tree.
+ */
+
+#include "libbb.h"
+
+/* Conversion table.  for base 64 */
+const char bb_uuenc_tbl_base64[65 + 2] 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',
+	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+	'w', 'x', 'y', 'z', '0', '1', '2', '3',
+	'4', '5', '6', '7', '8', '9', '+', '/',
+	'=' /* termination character */,
+	'\n', '\0' /* needed for uudecode.c */
+};
+
+const char bb_uuenc_tbl_std[65] ALIGN1 = {
+	'`', '!', '"', '#', '$', '%', '&', '\'',
+	'(', ')', '*', '+', ',', '-', '.', '/',
+	'0', '1', '2', '3', '4', '5', '6', '7',
+	'8', '9', ':', ';', '<', '=', '>', '?',
+	'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+	'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+	'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
+	'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
+	'`' /* termination character */
+};
+
+/*
+ * Encode bytes at S of length LENGTH to uuencode or base64 format and place it
+ * to STORE.  STORE will be 0-terminated, and must point to a writable
+ * buffer of at least 1+BASE64_LENGTH(length) bytes.
+ * where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
+ */
+void FAST_FUNC bb_uuencode(char *p, const void *src, int length, const char *tbl)
+{
+	const unsigned char *s = src;
+
+	/* Transform the 3x8 bits to 4x6 bits */
+	while (length > 0) {
+		unsigned s1, s2;
+
+		/* Are s[1], s[2] valid or should be assumed 0? */
+		s1 = s2 = 0;
+		length -= 3; /* can be >=0, -1, -2 */
+		if (length >= -1) {
+			s1 = s[1];
+			if (length >= 0)
+				s2 = s[2];
+		}
+		*p++ = tbl[s[0] >> 2];
+		*p++ = tbl[((s[0] & 3) << 4) + (s1 >> 4)];
+		*p++ = tbl[((s1 & 0xf) << 2) + (s2 >> 6)];
+		*p++ = tbl[s2 & 0x3f];
+		s += 3;
+	}
+	/* Zero-terminate */
+	*p = '\0';
+	/* If length is -2 or -1, pad last char or two */
+	while (length) {
+		*--p = tbl[64];
+		length++;
+	}
+}
+
+/*
+ * 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)
+
+	int term_count = 0;
+
+	while (1) {
+		unsigned char translated[4];
+		int count = 0;
+
+		/* Process one group of 4 chars */
+		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
+			 *  012345678901234567890123456789012345678901234567890123456789012345
+			 * "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"
+			 */
+			do {
+				ch = fgetc(src_stream);
+				if (ch == exit_char && count == 0)
+					return;
+				if (ch == EOF)
+					bb_error_msg_and_die("truncated base64 input");
+				table_ptr = strchr(bb_uuenc_tbl_base64, ch);
+//TODO: add BASE64_FLAG_foo to die on bad char?
+//Note that then we may need to still allow '\r' (for mail processing)
+			} while (!table_ptr);
+
+			/* Convert encoded character to decimal */
+			ch = table_ptr - bb_uuenc_tbl_base64;
+
+			if (ch == 65 /* '\n' */) {
+				/* Terminating "====" line? */
+				if (uu_style_end && term_count == 4)
+					return; /* yes */
+				term_count = 0;
+				continue;
+			}
+			/* ch is 64 if char was '=', otherwise 0..63 */
+			translated[count] = ch & 63; /* 64 -> 0 */
+			if (ch == 64) {
+				term_count++;
+				break;
+			}
+			count++;
+			term_count = 0;
+		}
+
+		/* Merge 6 bit chars to 8 bit.
+		 * count can be < 4 when we decode the tail:
+		 * "eQ==" -> "y", not "y NUL NUL"
+		 */
+		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);
+	} /* while (1) */
+}
diff --git a/busybox-1.19.3/libbb/vdprintf.c b/busybox-1.19.3/libbb/vdprintf.c
new file mode 100644
index 0000000..feeb403
--- /dev/null
+++ b/busybox-1.19.3/libbb/vdprintf.c
@@ -0,0 +1,21 @@
+/* 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 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];
+	int len;
+
+	len = vsnprintf(buf, BUF_SIZE, format, ap);
+	return write(d, buf, len);
+}
+#endif
diff --git a/busybox-1.19.3/libbb/verror_msg.c b/busybox-1.19.3/libbb/verror_msg.c
new file mode 100644
index 0000000..ee95be3
--- /dev/null
+++ b/busybox-1.19.3/libbb/verror_msg.c
@@ -0,0 +1,158 @@
+/* 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 source tree.
+ */
+#include "libbb.h"
+#if ENABLE_FEATURE_SYSLOG
+# include <syslog.h>
+#endif
+
+smallint logmode = LOGMODE_STDIO;
+const char *msg_eol = "\n";
+
+void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
+{
+	char *msg, *msg1;
+	int applet_len, strerr_len, msgeol_len, used;
+
+	if (!logmode)
+		return;
+
+	if (!s) /* nomsg[_and_die] uses NULL fmt */
+		s = ""; /* some libc don't like printf(NULL) */
+
+	used = vasprintf(&msg, s, p);
+	if (used < 0)
+		return;
+
+	/* This is ugly and costs +60 bytes compared to multiple
+	 * fprintf's, but is guaranteed to do a single write.
+	 * This is needed for e.g. httpd logging, when multiple
+	 * children can produce log messages simultaneously. */
+
+	applet_len = strlen(applet_name) + 2; /* "applet: " */
+	strerr_len = strerr ? strlen(strerr) : 0;
+	msgeol_len = strlen(msg_eol);
+	/* can't use xrealloc: it calls error_msg on failure,
+	 * that may result in a recursion */
+	/* +3 is for ": " before strerr and for terminating NUL */
+	msg1 = realloc(msg, applet_len + used + strerr_len + msgeol_len + 3);
+	if (!msg1) {
+		msg[used++] = '\n'; /* overwrites NUL */
+		applet_len = 0;
+	} else {
+		msg = msg1;
+		/* TODO: maybe use writev instead of memmoving? Need full_writev? */
+		memmove(msg + applet_len, msg, used);
+		used += applet_len;
+		strcpy(msg, applet_name);
+		msg[applet_len - 2] = ':';
+		msg[applet_len - 1] = ' ';
+		if (strerr) {
+			if (s[0]) { /* not perror_nomsg? */
+				msg[used++] = ':';
+				msg[used++] = ' ';
+			}
+			strcpy(&msg[used], strerr);
+			used += strerr_len;
+		}
+		strcpy(&msg[used], msg_eol);
+		used += msgeol_len;
+	}
+
+	if (logmode & LOGMODE_STDIO) {
+		fflush_all();
+		full_write(STDERR_FILENO, msg, used);
+	}
+#if ENABLE_FEATURE_SYSLOG
+	if (logmode & LOGMODE_SYSLOG) {
+		syslog(LOG_ERR, "%s", msg + applet_len);
+	}
+#endif
+	free(msg);
+}
+
+#ifdef VERSION_WITH_WRITEV
+/* Code size is approximately the same, but currently it's the only user
+ * of writev in entire bbox. __libc_writev in uclibc is ~50 bytes. */
+void FAST_FUNC bb_verror_msg(const char *s, va_list p, const char* strerr)
+{
+	int strerr_len, msgeol_len;
+	struct iovec iov[3];
+
+#define used   (iov[2].iov_len)
+#define msgv   (iov[2].iov_base)
+#define msgc   ((char*)(iov[2].iov_base))
+#define msgptr (&(iov[2].iov_base))
+
+	if (!logmode)
+		return;
+
+	if (!s) /* nomsg[_and_die] uses NULL fmt */
+		s = ""; /* some libc don't like printf(NULL) */
+
+	/* Prevent "derefing type-punned ptr will break aliasing rules" */
+	used = vasprintf((char**)(void*)msgptr, s, p);
+	if (used < 0)
+		return;
+
+	/* This is ugly and costs +60 bytes compared to multiple
+	 * fprintf's, but is guaranteed to do a single write.
+	 * This is needed for e.g. httpd logging, when multiple
+	 * children can produce log messages simultaneously. */
+
+	strerr_len = strerr ? strlen(strerr) : 0;
+	msgeol_len = strlen(msg_eol);
+	/* +3 is for ": " before strerr and for terminating NUL */
+	msgv = xrealloc(msgv, used + strerr_len + msgeol_len + 3);
+	if (strerr) {
+		msgc[used++] = ':';
+		msgc[used++] = ' ';
+		strcpy(msgc + used, strerr);
+		used += strerr_len;
+	}
+	strcpy(msgc + used, msg_eol);
+	used += msgeol_len;
+
+	if (logmode & LOGMODE_STDIO) {
+		iov[0].iov_base = (char*)applet_name;
+		iov[0].iov_len = strlen(applet_name);
+		iov[1].iov_base = (char*)": ";
+		iov[1].iov_len = 2;
+		/*iov[2].iov_base = msgc;*/
+		/*iov[2].iov_len = used;*/
+		fflush_all();
+		writev(STDERR_FILENO, iov, 3);
+	}
+# if ENABLE_FEATURE_SYSLOG
+	if (logmode & LOGMODE_SYSLOG) {
+		syslog(LOG_ERR, "%s", msgc);
+	}
+# endif
+	free(msgc);
+}
+#endif
+
+
+void FAST_FUNC bb_error_msg_and_die(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	bb_verror_msg(s, p, NULL);
+	va_end(p);
+	xfunc_die();
+}
+
+void FAST_FUNC bb_error_msg(const char *s, ...)
+{
+	va_list p;
+
+	va_start(p, s);
+	bb_verror_msg(s, p, NULL);
+	va_end(p);
+}
diff --git a/busybox-1.19.3/libbb/vfork_daemon_rexec.c b/busybox-1.19.3/libbb/vfork_daemon_rexec.c
new file mode 100644
index 0000000..a75eafb
--- /dev/null
+++ b/busybox-1.19.3/libbb/vfork_daemon_rexec.c
@@ -0,0 +1,273 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Rexec program for system have fork() as vfork() with foreground option
+ *
+ * Copyright (C) Vladimir N. Oleynik <dzo@simtreas.ru>
+ * Copyright (C) 2003 Russ Dill <Russ.Dill@asu.edu>
+ *
+ * daemon() portion taken from uClibc:
+ *
+ * Copyright (c) 1991, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Modified for uClibc by Erik Andersen <andersee@debian.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "busybox.h" /* uses applet tables */
+
+/* This does a fork/exec in one call, using vfork().  Returns PID of new child,
+ * -1 for failure.  Runs argv[0], searching path if that has no / in it. */
+pid_t FAST_FUNC spawn(char **argv)
+{
+	/* Compiler should not optimize stores here */
+	volatile int failed;
+	pid_t pid;
+
+	fflush_all();
+
+	/* Be nice to nommu machines. */
+	failed = 0;
+	pid = vfork();
+	if (pid < 0) /* error */
+		return pid;
+	if (!pid) { /* child */
+		/* This macro is ok - it doesn't do NOEXEC/NOFORK tricks */
+		BB_EXECVP(argv[0], argv);
+
+		/* We are (maybe) sharing a stack with blocked parent,
+		 * let parent know we failed and then exit to unblock parent
+		 * (but don't run atexit() stuff, which would screw up parent.)
+		 */
+		failed = errno;
+		/* mount, for example, does not want the message */
+		/*bb_perror_msg("can't execute '%s'", argv[0]);*/
+		_exit(111);
+	}
+	/* parent */
+	/* Unfortunately, this is not reliable: according to standards
+	 * vfork() can be equivalent to fork() and we won't see value
+	 * of 'failed'.
+	 * 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;
+	}
+	return pid;
+}
+
+/* Die with an error message if we can't spawn a child process. */
+pid_t FAST_FUNC xspawn(char **argv)
+{
+	pid_t pid = spawn(argv);
+	if (pid < 0)
+		bb_simple_perror_msg_and_die(*argv);
+	return pid;
+}
+
+#if ENABLE_FEATURE_PREFER_APPLETS
+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;
+}
+static void restore_nofork_data(struct nofork_save_area *save)
+{
+	memcpy(&die_jmp, &save->die_jmp, sizeof(die_jmp));
+	applet_name = save->applet_name;
+	xfunc_error_retval = save->xfunc_error_retval;
+	option_mask32 = save->option_mask32;
+	die_sleep = save->die_sleep;
+}
+
+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;
+
+	/* In case getopt() or getopt32() was already called:
+	 * reset the libc getopt() function, which keeps internal state.
+	 *
+	 * BSD-derived getopt() functions require that optind be set to 1 in
+	 * order to reset getopt() state.  This used to be generally accepted
+	 * way of resetting getopt().  However, glibc's getopt()
+	 * has additional getopt() state beyond optind, and requires that
+	 * optind be set to zero to reset its state.  So the unfortunate state of
+	 * affairs is that BSD-derived versions of getopt() misbehave if
+	 * optind is set to 0 in order to reset getopt(), and glibc's getopt()
+	 * will core dump if optind is set 1 in order to reset getopt().
+	 *
+	 * More modern versions of BSD require that optreset be set to 1 in
+	 * order to reset getopt().  Sigh.  Standards, anyone?
+	 */
+#ifdef __GLIBC__
+	optind = 0;
+#else /* BSD style */
+	optind = 1;
+	/* optreset = 1; */
+#endif
+	/* optarg = NULL; opterr = 1; optopt = 63; - do we need this too? */
+	/* (values above are what they initialized to in glibc and uclibc) */
+	/* option_mask32 = 0; - not needed, no applet depends on it being 0 */
+
+	argc = 1;
+	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)
+		 * need argv untouched because they free argv[i]! */
+		char *tmp_argv[argc+1];
+		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);
+	} else { /* xfunc died in NOFORK applet */
+		/* in case they meant to return 0... */
+		if (rc == -2222)
+			rc = 0;
+	}
+
+	/* Restoring some globals */
+	restore_nofork_data(&old);
+
+	/* Other globals can be simply reset to defaults */
+#ifdef __GLIBC__
+	optind = 0;
+#else /* BSD style */
+	optind = 1;
+#endif
+
+	return rc & 0xff; /* don't confuse people with "exitcodes" >255 */
+}
+#endif /* FEATURE_PREFER_APPLETS */
+
+int FAST_FUNC spawn_and_wait(char **argv)
+{
+	int rc;
+#if ENABLE_FEATURE_PREFER_APPLETS
+	int a = find_applet_by_name(argv[0]);
+
+	if (a >= 0 && (APPLET_IS_NOFORK(a)
+# if BB_MMU
+			|| APPLET_IS_NOEXEC(a) /* NOEXEC trick needs fork() */
+# endif
+	)) {
+# if BB_MMU
+		if (APPLET_IS_NOFORK(a))
+# endif
+		{
+			return run_nofork_applet(a, argv);
+		}
+# if BB_MMU
+		/* MMU only */
+		/* a->noexec is true */
+		rc = fork();
+		if (rc) /* parent or error */
+			return wait4pid(rc);
+		/* child */
+		xfunc_error_retval = EXIT_FAILURE;
+		run_applet_no_and_exit(a, argv);
+# endif
+	}
+#endif /* FEATURE_PREFER_APPLETS */
+	rc = spawn(argv);
+	return wait4pid(rc);
+}
+
+#if !BB_MMU
+void FAST_FUNC re_exec(char **argv)
+{
+	/* high-order bit of first char in argv[0] is a hidden
+	 * "we have (already) re-execed, don't do it again" flag */
+	argv[0][0] |= 0x80;
+	execv(bb_busybox_exec_path, argv);
+	bb_perror_msg_and_die("can't execute '%s'", bb_busybox_exec_path);
+}
+
+pid_t FAST_FUNC fork_or_rexec(char **argv)
+{
+	pid_t pid;
+	/* Maybe we are already re-execed and come here again? */
+	if (re_execed)
+		return 0;
+	pid = xvfork();
+	if (pid) /* parent */
+		return pid;
+	/* child - re-exec ourself */
+	re_exec(argv);
+}
+#endif
+
+/* Due to a #define in libbb.h on MMU systems we actually have 1 argument -
+ * char **argv "vanishes" */
+void FAST_FUNC bb_daemonize_or_rexec(int flags, char **argv)
+{
+	int fd;
+
+	if (flags & DAEMON_CHDIR_ROOT)
+		xchdir("/");
+
+	if (flags & DAEMON_DEVNULL_STDIO) {
+		close(0);
+		close(1);
+		close(2);
+	}
+
+	fd = open(bb_dev_null, O_RDWR);
+	if (fd < 0) {
+		/* NB: we can be called as bb_sanitize_stdio() from init
+		 * or mdev, and there /dev/null may legitimately not (yet) exist!
+		 * Do not use xopen above, but obtain _ANY_ open descriptor,
+		 * even bogus one as below. */
+		fd = xopen("/", O_RDONLY); /* don't believe this can fail */
+	}
+
+	while ((unsigned)fd < 2)
+		fd = dup(fd); /* have 0,1,2 open at least to /dev/null */
+
+	if (!(flags & DAEMON_ONLY_SANITIZE)) {
+		if (fork_or_rexec(argv))
+			exit(EXIT_SUCCESS); /* parent */
+		/* if daemonizing, make sure we detach from stdio & ctty */
+		setsid();
+		dup2(fd, 0);
+		dup2(fd, 1);
+		dup2(fd, 2);
+	}
+	while (fd > 2) {
+		close(fd--);
+		if (!(flags & DAEMON_CLOSE_EXTRA_FDS))
+			return;
+		/* else close everything after fd#2 */
+	}
+}
+
+void FAST_FUNC bb_sanitize_stdio(void)
+{
+	bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
+}
diff --git a/busybox-1.19.3/libbb/warn_ignoring_args.c b/busybox-1.19.3/libbb/warn_ignoring_args.c
new file mode 100644
index 0000000..3f3025c
--- /dev/null
+++ b/busybox-1.19.3/libbb/warn_ignoring_args.c
@@ -0,0 +1,18 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * warn_ignoring_args implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+#if ENABLE_DESKTOP
+void FAST_FUNC bb_warn_ignoring_args(char *arg)
+{
+	if (arg) {
+		bb_error_msg("ignoring all arguments");
+	}
+}
+#endif
diff --git a/busybox-1.19.3/libbb/wfopen.c b/busybox-1.19.3/libbb/wfopen.c
new file mode 100644
index 0000000..76dc8b8
--- /dev/null
+++ b/busybox-1.19.3/libbb/wfopen.c
@@ -0,0 +1,56 @@
+/* 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 source tree.
+ */
+
+#include "libbb.h"
+
+FILE* FAST_FUNC fopen_or_warn(const char *path, const char *mode)
+{
+	FILE *fp = fopen(path, mode);
+	if (!fp) {
+		bb_simple_perror_msg(path);
+		//errno = 0; /* why? */
+	}
+	return fp;
+}
+
+FILE* FAST_FUNC fopen_for_read(const char *path)
+{
+	return fopen(path, "r");
+}
+
+FILE* FAST_FUNC xfopen_for_read(const char *path)
+{
+	return xfopen(path, "r");
+}
+
+FILE* FAST_FUNC fopen_for_write(const char *path)
+{
+	return fopen(path, "w");
+}
+
+FILE* FAST_FUNC xfopen_for_write(const char *path)
+{
+	return xfopen(path, "w");
+}
+
+static FILE* xfdopen_helper(unsigned fd_and_rw_bit)
+{
+	FILE* fp = fdopen(fd_and_rw_bit >> 1, fd_and_rw_bit & 1 ? "w" : "r");
+	if (!fp)
+		bb_error_msg_and_die(bb_msg_memory_exhausted);
+	return fp;
+}
+FILE* FAST_FUNC xfdopen_for_read(int fd)
+{
+	return xfdopen_helper(fd << 1);
+}
+FILE* FAST_FUNC xfdopen_for_write(int fd)
+{
+	return xfdopen_helper((fd << 1) + 1);
+}
diff --git a/busybox-1.19.3/libbb/wfopen_input.c b/busybox-1.19.3/libbb/wfopen_input.c
new file mode 100644
index 0000000..d8b1c4a
--- /dev/null
+++ b/busybox-1.19.3/libbb/wfopen_input.c
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * wfopen_input implementation for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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
+ * is a command line arg.  Since often that arg is '-' (meaning stdin),
+ * we avoid testing everywhere by consolidating things in this routine.
+ */
+
+#include "libbb.h"
+
+FILE* FAST_FUNC fopen_or_warn_stdin(const char *filename)
+{
+	FILE *fp = stdin;
+
+	if (filename != bb_msg_standard_input
+	 && NOT_LONE_DASH(filename)
+	) {
+		fp = fopen_or_warn(filename, "r");
+	}
+	return fp;
+}
+
+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. */
+}
+
+int FAST_FUNC open_or_warn_stdin(const char *filename)
+{
+	int fd = STDIN_FILENO;
+
+	if (filename != bb_msg_standard_input
+	 && NOT_LONE_DASH(filename)
+	) {
+		fd = open_or_warn(filename, O_RDONLY);
+	}
+
+	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. */
+}
diff --git a/busybox-1.19.3/libbb/write.c b/busybox-1.19.3/libbb/write.c
new file mode 100644
index 0000000..2d67a72
--- /dev/null
+++ b/busybox-1.19.3/libbb/write.c
@@ -0,0 +1,19 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2008 Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Open file and write string str to it, close file.
+ * Die on any open or write error.  */
+void FAST_FUNC xopen_xwrite_close(const char* file, const char* str)
+{
+	int fd = xopen(file, O_WRONLY);
+	xwrite_str(fd, str);
+	close(fd);
+}
diff --git a/busybox-1.19.3/libbb/xatonum.c b/busybox-1.19.3/libbb/xatonum.c
new file mode 100644
index 0000000..62bbe53
--- /dev/null
+++ b/busybox-1.19.3/libbb/xatonum.c
@@ -0,0 +1,70 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ascii-to-numbers implementations for busybox
+ *
+ * Copyright (C) 2003  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+#define type long long
+#define xstrtou(rest) xstrtoull##rest
+#define xstrto(rest) xstrtoll##rest
+#define xatou(rest) xatoull##rest
+#define xato(rest) xatoll##rest
+#define XSTR_UTYPE_MAX ULLONG_MAX
+#define XSTR_TYPE_MAX LLONG_MAX
+#define XSTR_TYPE_MIN LLONG_MIN
+#define XSTR_STRTOU strtoull
+#include "xatonum_template.c"
+
+#if ULONG_MAX != ULLONG_MAX
+#define type long
+#define xstrtou(rest) xstrtoul##rest
+#define xstrto(rest) xstrtol##rest
+#define xatou(rest) xatoul##rest
+#define xato(rest) xatol##rest
+#define XSTR_UTYPE_MAX ULONG_MAX
+#define XSTR_TYPE_MAX LONG_MAX
+#define XSTR_TYPE_MIN LONG_MIN
+#define XSTR_STRTOU strtoul
+#include "xatonum_template.c"
+#endif
+
+#if UINT_MAX != ULONG_MAX
+static ALWAYS_INLINE
+unsigned bb_strtoui(const char *str, char **end, int b)
+{
+	unsigned long v = strtoul(str, end, b);
+	if (v > UINT_MAX) {
+		errno = ERANGE;
+		return UINT_MAX;
+	}
+	return v;
+}
+#define type int
+#define xstrtou(rest) xstrtou##rest
+#define xstrto(rest) xstrtoi##rest
+#define xatou(rest) xatou##rest
+#define xato(rest) xatoi##rest
+#define XSTR_UTYPE_MAX UINT_MAX
+#define XSTR_TYPE_MAX INT_MAX
+#define XSTR_TYPE_MIN INT_MIN
+/* libc has no strtoui, so we need to create/use our own */
+#define XSTR_STRTOU bb_strtoui
+#include "xatonum_template.c"
+#endif
+
+/* A few special cases */
+
+int FAST_FUNC xatoi_positive(const char *numstr)
+{
+	return xatou_range(numstr, 0, INT_MAX);
+}
+
+uint16_t FAST_FUNC xatou16(const char *numstr)
+{
+	return xatou_range(numstr, 0, 0xffff);
+}
diff --git a/busybox-1.19.3/libbb/xatonum_template.c b/busybox-1.19.3/libbb/xatonum_template.c
new file mode 100644
index 0000000..029f662
--- /dev/null
+++ b/busybox-1.19.3/libbb/xatonum_template.c
@@ -0,0 +1,195 @@
+/*
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+/*
+You need to define the following (example):
+
+#define type long
+#define xstrtou(rest) xstrtoul##rest
+#define xstrto(rest) xstrtol##rest
+#define xatou(rest) xatoul##rest
+#define xato(rest) xatol##rest
+#define XSTR_UTYPE_MAX ULONG_MAX
+#define XSTR_TYPE_MAX LONG_MAX
+#define XSTR_TYPE_MIN LONG_MIN
+#define XSTR_STRTOU strtoul
+*/
+
+unsigned type FAST_FUNC xstrtou(_range_sfx)(const char *numstr, int base,
+		unsigned type lower,
+		unsigned type upper,
+		const struct suffix_mult *suffixes)
+{
+	unsigned type r;
+	int old_errno;
+	char *e;
+
+	/* Disallow '-' and any leading whitespace. */
+	if (*numstr == '-' || *numstr == '+' || isspace(*numstr))
+		goto inval;
+
+	/* Since this is a lib function, we're not allowed to reset errno to 0.
+	 * Doing so could break an app that is deferring checking of errno.
+	 * So, save the old value so that we can restore it if successful. */
+	old_errno = errno;
+	errno = 0;
+	r = XSTR_STRTOU(numstr, &e, base);
+	/* Do the initial validity check.  Note: The standards do not
+	 * guarantee that errno is set if no digits were found.  So we
+	 * must test for this explicitly. */
+	if (errno || numstr == e)
+		goto inval; /* error / no digits / illegal trailing chars */
+
+	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,
+	 * to allow for scaling of the numstr by some default multiplier. */
+	if (suffixes) {
+		while (suffixes->mult) {
+			if (strcmp(suffixes->suffix, e) == 0) {
+				if (XSTR_UTYPE_MAX / suffixes->mult < r)
+					goto range; /* overflow! */
+				r *= suffixes->mult;
+				goto chk_range;
+			}
+			++suffixes;
+		}
+	}
+
+	/* Note: trailing space is an error.
+	   It would be easy enough to allow though if desired. */
+	if (*e)
+		goto inval;
+ chk_range:
+	/* Finally, check for range limits. */
+	if (r >= lower && r <= upper)
+		return r;
+ range:
+	bb_error_msg_and_die("number %s is not in %llu..%llu range",
+		numstr, (unsigned long long)lower,
+		(unsigned long long)upper);
+ inval:
+	bb_error_msg_and_die("invalid number '%s'", numstr);
+}
+
+unsigned type FAST_FUNC xstrtou(_range)(const char *numstr, int base,
+		unsigned type lower,
+		unsigned type upper)
+{
+	return xstrtou(_range_sfx)(numstr, base, lower, upper, NULL);
+}
+
+unsigned type FAST_FUNC xstrtou(_sfx)(const char *numstr, int base,
+		const struct suffix_mult *suffixes)
+{
+	return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, suffixes);
+}
+
+unsigned type FAST_FUNC xstrtou()(const char *numstr, int base)
+{
+	return xstrtou(_range_sfx)(numstr, base, 0, XSTR_UTYPE_MAX, NULL);
+}
+
+unsigned type FAST_FUNC xatou(_range_sfx)(const char *numstr,
+		unsigned type lower,
+		unsigned type upper,
+		const struct suffix_mult *suffixes)
+{
+	return xstrtou(_range_sfx)(numstr, 10, lower, upper, suffixes);
+}
+
+unsigned type FAST_FUNC xatou(_range)(const char *numstr,
+		unsigned type lower,
+		unsigned type upper)
+{
+	return xstrtou(_range_sfx)(numstr, 10, lower, upper, NULL);
+}
+
+unsigned type FAST_FUNC xatou(_sfx)(const char *numstr,
+		const struct suffix_mult *suffixes)
+{
+	return xstrtou(_range_sfx)(numstr, 10, 0, XSTR_UTYPE_MAX, suffixes);
+}
+
+unsigned type FAST_FUNC xatou()(const char *numstr)
+{
+	return xatou(_sfx)(numstr, NULL);
+}
+
+/* Signed ones */
+
+type FAST_FUNC xstrto(_range_sfx)(const char *numstr, int base,
+		type lower,
+		type upper,
+		const struct suffix_mult *suffixes)
+{
+	unsigned type u = XSTR_TYPE_MAX;
+	type r;
+	const char *p = numstr;
+
+	/* NB: if you'll decide to disallow '+':
+	 * at least renice applet needs to allow it */
+	if (p[0] == '+' || p[0] == '-') {
+		++p;
+		if (p[0] == '-')
+			++u; /* = <type>_MIN (01111... + 1 == 10000...) */
+	}
+
+	r = xstrtou(_range_sfx)(p, base, 0, u, suffixes);
+
+	if (*numstr == '-') {
+		r = -r;
+	}
+
+	if (r < lower || r > upper) {
+		bb_error_msg_and_die("number %s is not in %lld..%lld range",
+				numstr, (long long)lower, (long long)upper);
+	}
+
+	return r;
+}
+
+type FAST_FUNC xstrto(_range)(const char *numstr, int base, type lower, type upper)
+{
+	return xstrto(_range_sfx)(numstr, base, lower, upper, NULL);
+}
+
+type FAST_FUNC xstrto()(const char *numstr, int base)
+{
+	return xstrto(_range_sfx)(numstr, base, XSTR_TYPE_MIN, XSTR_TYPE_MAX, NULL);
+}
+
+type FAST_FUNC xato(_range_sfx)(const char *numstr,
+		type lower,
+		type upper,
+		const struct suffix_mult *suffixes)
+{
+	return xstrto(_range_sfx)(numstr, 10, lower, upper, suffixes);
+}
+
+type FAST_FUNC xato(_range)(const char *numstr, type lower, type upper)
+{
+	return xstrto(_range_sfx)(numstr, 10, lower, upper, NULL);
+}
+
+type FAST_FUNC xato(_sfx)(const char *numstr, const struct suffix_mult *suffixes)
+{
+	return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, suffixes);
+}
+
+type FAST_FUNC xato()(const char *numstr)
+{
+	return xstrto(_range_sfx)(numstr, 10, XSTR_TYPE_MIN, XSTR_TYPE_MAX, NULL);
+}
+
+#undef type
+#undef xstrtou
+#undef xstrto
+#undef xatou
+#undef xato
+#undef XSTR_UTYPE_MAX
+#undef XSTR_TYPE_MAX
+#undef XSTR_TYPE_MIN
+#undef XSTR_STRTOU
diff --git a/busybox-1.19.3/libbb/xconnect.c b/busybox-1.19.3/libbb/xconnect.c
new file mode 100644
index 0000000..4b7c110
--- /dev/null
+++ b/busybox-1.19.3/libbb/xconnect.c
@@ -0,0 +1,484 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Connect to host at port using address resolution from getaddrinfo
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h> /* netinet/in.h needs it */
+#include <netinet/in.h>
+#include <net/if.h>
+#include <sys/un.h>
+#include "libbb.h"
+
+void FAST_FUNC setsockopt_reuseaddr(int fd)
+{
+	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1));
+}
+int FAST_FUNC setsockopt_broadcast(int fd)
+{
+	return setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &const_int_1, sizeof(const_int_1));
+}
+
+#ifdef SO_BINDTODEVICE
+int FAST_FUNC setsockopt_bindtodevice(int fd, const char *iface)
+{
+	int r;
+	struct ifreq ifr;
+	strncpy_IFNAMSIZ(ifr.ifr_name, iface);
+	/* NB: passing (iface, strlen(iface) + 1) does not work!
+	 * (maybe it works on _some_ kernels, but not on 2.6.26)
+	 * Actually, ifr_name is at offset 0, and in practice
+	 * just giving char[IFNAMSIZ] instead of struct ifreq works too.
+	 * But just in case it's not true on some obscure arch... */
+	r = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
+	if (r)
+		bb_perror_msg("can't bind to interface %s", iface);
+	return r;
+}
+#else
+int FAST_FUNC setsockopt_bindtodevice(int fd UNUSED_PARAM,
+		const char *iface UNUSED_PARAM)
+{
+	bb_error_msg("SO_BINDTODEVICE is not supported on this system");
+	return -1;
+}
+#endif
+
+static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen))
+{
+	len_and_sockaddr lsa;
+	len_and_sockaddr *lsa_ptr;
+
+	lsa.len = LSA_SIZEOF_SA;
+	if (get_name(fd, &lsa.u.sa, &lsa.len) != 0)
+		return NULL;
+
+	lsa_ptr = xzalloc(LSA_LEN_SIZE + lsa.len);
+	if (lsa.len > LSA_SIZEOF_SA) { /* rarely (if ever) happens */
+		lsa_ptr->len = lsa.len;
+		get_name(fd, &lsa_ptr->u.sa, &lsa_ptr->len);
+	} else {
+		memcpy(lsa_ptr, &lsa, LSA_LEN_SIZE + lsa.len);
+	}
+	return lsa_ptr;
+}
+
+len_and_sockaddr* FAST_FUNC get_sock_lsa(int fd)
+{
+	return get_lsa(fd, getsockname);
+}
+
+len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd)
+{
+	return get_lsa(fd, getpeername);
+}
+
+void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen)
+{
+	if (connect(s, s_addr, addrlen) < 0) {
+		if (ENABLE_FEATURE_CLEAN_UP)
+			close(s);
+		if (s_addr->sa_family == AF_INET)
+			bb_perror_msg_and_die("%s (%s)",
+				"can't connect to remote host",
+				inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr));
+		bb_perror_msg_and_die("can't connect to remote host");
+	}
+}
+
+/* Return port number for a service.
+ * If "port" is a number use it as the port.
+ * If "port" is a name it is looked up in /etc/services,
+ * if it isnt found return default_port
+ */
+unsigned FAST_FUNC bb_lookup_port(const char *port, const char *protocol, unsigned default_port)
+{
+	unsigned port_nr = default_port;
+	if (port) {
+		int old_errno;
+
+		/* Since this is a lib function, we're not allowed to reset errno to 0.
+		 * Doing so could break an app that is deferring checking of errno. */
+		old_errno = errno;
+		port_nr = bb_strtou(port, NULL, 10);
+		if (errno || port_nr > 65535) {
+			struct servent *tserv = getservbyname(port, protocol);
+			port_nr = default_port;
+			if (tserv)
+				port_nr = ntohs(tserv->s_port);
+		}
+		errno = old_errno;
+	}
+	return (uint16_t)port_nr;
+}
+
+
+/* "New" networking API */
+
+
+int FAST_FUNC get_nport(const struct sockaddr *sa)
+{
+#if ENABLE_FEATURE_IPV6
+	if (sa->sa_family == AF_INET6) {
+		return ((struct sockaddr_in6*)sa)->sin6_port;
+	}
+#endif
+	if (sa->sa_family == AF_INET) {
+		return ((struct sockaddr_in*)sa)->sin_port;
+	}
+	/* What? UNIX socket? IPX?? :) */
+	return -1;
+}
+
+void FAST_FUNC set_nport(struct sockaddr *sa, unsigned port)
+{
+#if ENABLE_FEATURE_IPV6
+	if (sa->sa_family == AF_INET6) {
+		struct sockaddr_in6 *sin6 = (void*) sa;
+		sin6->sin6_port = port;
+		return;
+	}
+#endif
+	if (sa->sa_family == AF_INET) {
+		struct sockaddr_in *sin = (void*) sa;
+		sin->sin_port = port;
+		return;
+	}
+	/* What? UNIX socket? IPX?? :) */
+}
+
+/* We hijack this constant to mean something else */
+/* It doesn't hurt because we will remove this bit anyway */
+#define DIE_ON_ERROR AI_CANONNAME
+
+/* host: "1.2.3.4[:port]", "www.google.com[:port]"
+ * port: if neither of above specifies port # */
+static len_and_sockaddr* str2sockaddr(
+		const char *host, int port,
+IF_FEATURE_IPV6(sa_family_t af,)
+		int ai_flags)
+{
+IF_NOT_FEATURE_IPV6(sa_family_t af = AF_INET;)
+	int rc;
+	len_and_sockaddr *r;
+	struct addrinfo *result = NULL;
+	struct addrinfo *used_res;
+	const char *org_host = host; /* only for error msg */
+	const char *cp;
+	struct addrinfo hint;
+
+	if (ENABLE_FEATURE_UNIX_LOCAL && strncmp(host, "local:", 6) == 0) {
+		struct sockaddr_un *sun;
+
+		r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_un));
+		r->len = sizeof(struct sockaddr_un);
+		r->u.sa.sa_family = AF_UNIX;
+		sun = (struct sockaddr_un *)&r->u.sa;
+		safe_strncpy(sun->sun_path, host + 6, sizeof(sun->sun_path));
+		return r;
+	}
+
+	r = NULL;
+
+	/* Ugly parsing of host:addr */
+	if (ENABLE_FEATURE_IPV6 && host[0] == '[') {
+		/* Even uglier parsing of [xx]:nn */
+		host++;
+		cp = strchr(host, ']');
+		if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
+			/* Malformed: must be [xx]:nn or [xx] */
+			bb_error_msg("bad address '%s'", org_host);
+			if (ai_flags & DIE_ON_ERROR)
+				xfunc_die();
+			return NULL;
+		}
+	} else {
+		cp = strrchr(host, ':');
+		if (ENABLE_FEATURE_IPV6 && cp && strchr(host, ':') != cp) {
+			/* There is more than one ':' (e.g. "::1") */
+			cp = NULL; /* it's not a port spec */
+		}
+	}
+	if (cp) { /* points to ":" or "]:" */
+		int sz = cp - host + 1;
+
+		host = safe_strncpy(alloca(sz), host, sz);
+		if (ENABLE_FEATURE_IPV6 && *cp != ':') {
+			cp++; /* skip ']' */
+			if (*cp == '\0') /* [xx] without port */
+				goto skip;
+		}
+		cp++; /* skip ':' */
+		port = bb_strtou(cp, NULL, 10);
+		if (errno || (unsigned)port > 0xffff) {
+			bb_error_msg("bad port spec '%s'", org_host);
+			if (ai_flags & DIE_ON_ERROR)
+				xfunc_die();
+			return NULL;
+		}
+ skip: ;
+	}
+
+	/* Next two if blocks allow to skip getaddrinfo()
+	 * in case host name is a numeric IP(v6) address.
+	 * getaddrinfo() initializes DNS resolution machinery,
+	 * scans network config and such - tens of syscalls.
+	 */
+	/* If we were not asked specifically for IPv6,
+	 * check whether this is a numeric IPv4 */
+	IF_FEATURE_IPV6(if(af != AF_INET6)) {
+		struct in_addr in4;
+		if (inet_aton(host, &in4) != 0) {
+			r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in));
+			r->len = sizeof(struct sockaddr_in);
+			r->u.sa.sa_family = AF_INET;
+			r->u.sin.sin_addr = in4;
+			goto set_port;
+		}
+	}
+#if ENABLE_FEATURE_IPV6
+	/* If we were not asked specifically for IPv4,
+	 * check whether this is a numeric IPv6 */
+	if (af != AF_INET) {
+		struct in6_addr in6;
+		if (inet_pton(AF_INET6, host, &in6) > 0) {
+			r = xzalloc(LSA_LEN_SIZE + sizeof(struct sockaddr_in6));
+			r->len = sizeof(struct sockaddr_in6);
+			r->u.sa.sa_family = AF_INET6;
+			r->u.sin6.sin6_addr = in6;
+			goto set_port;
+		}
+	}
+#endif
+
+	memset(&hint, 0 , sizeof(hint));
+	hint.ai_family = af;
+	/* 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;
+	rc = getaddrinfo(host, NULL, &hint, &result);
+	if (rc || !result) {
+		bb_error_msg("bad address '%s'", org_host);
+		if (ai_flags & DIE_ON_ERROR)
+			xfunc_die();
+		goto ret;
+	}
+	used_res = result;
+#if ENABLE_FEATURE_PREFER_IPV4_ADDRESS
+	while (1) {
+		if (used_res->ai_family == AF_INET)
+			break;
+		used_res = used_res->ai_next;
+		if (!used_res) {
+			used_res = result;
+			break;
+		}
+	}
+#endif
+	r = xmalloc(LSA_LEN_SIZE + used_res->ai_addrlen);
+	r->len = used_res->ai_addrlen;
+	memcpy(&r->u.sa, used_res->ai_addr, used_res->ai_addrlen);
+
+ set_port:
+	set_nport(&r->u.sa, htons(port));
+ ret:
+	if (result)
+		freeaddrinfo(result);
+	return r;
+}
+#if !ENABLE_FEATURE_IPV6
+#define str2sockaddr(host, port, af, ai_flags) str2sockaddr(host, port, ai_flags)
+#endif
+
+#if ENABLE_FEATURE_IPV6
+len_and_sockaddr* FAST_FUNC host_and_af2sockaddr(const char *host, int port, sa_family_t af)
+{
+	return str2sockaddr(host, port, af, 0);
+}
+
+len_and_sockaddr* FAST_FUNC xhost_and_af2sockaddr(const char *host, int port, sa_family_t af)
+{
+	return str2sockaddr(host, port, af, DIE_ON_ERROR);
+}
+#endif
+
+len_and_sockaddr* FAST_FUNC host2sockaddr(const char *host, int port)
+{
+	return str2sockaddr(host, port, AF_UNSPEC, 0);
+}
+
+len_and_sockaddr* FAST_FUNC xhost2sockaddr(const char *host, int port)
+{
+	return str2sockaddr(host, port, AF_UNSPEC, DIE_ON_ERROR);
+}
+
+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)
+{
+	IF_NOT_FEATURE_IPV6(enum { family = AF_INET };)
+	len_and_sockaddr *lsa;
+	int fd;
+	int len;
+
+#if ENABLE_FEATURE_IPV6
+	if (family == AF_UNSPEC) {
+		fd = socket(AF_INET6, sock_type, 0);
+		if (fd >= 0) {
+			family = AF_INET6;
+			goto done;
+		}
+		family = AF_INET;
+	}
+#endif
+	fd = xsocket(family, sock_type, 0);
+	len = sizeof(struct sockaddr_in);
+#if ENABLE_FEATURE_IPV6
+	if (family == AF_INET6) {
+ done:
+		len = sizeof(struct sockaddr_in6);
+	}
+#endif
+	lsa = xzalloc(LSA_LEN_SIZE + len);
+	lsa->len = len;
+	lsa->u.sa.sa_family = family;
+	*lsap = lsa;
+	return fd;
+}
+
+int FAST_FUNC xsocket_stream(len_and_sockaddr **lsap)
+{
+	return xsocket_type(lsap, IF_FEATURE_IPV6(AF_UNSPEC,) SOCK_STREAM);
+}
+
+static int create_and_bind_or_die(const char *bindaddr, int port, int sock_type)
+{
+	int fd;
+	len_and_sockaddr *lsa;
+
+	if (bindaddr && bindaddr[0]) {
+		lsa = xdotted2sockaddr(bindaddr, port);
+		/* 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->u.sa, htons(port));
+	}
+	setsockopt_reuseaddr(fd);
+	xbind(fd, &lsa->u.sa, lsa->len);
+	free(lsa);
+	return fd;
+}
+
+int FAST_FUNC create_and_bind_stream_or_die(const char *bindaddr, int port)
+{
+	return create_and_bind_or_die(bindaddr, port, SOCK_STREAM);
+}
+
+int FAST_FUNC create_and_bind_dgram_or_die(const char *bindaddr, int port)
+{
+	return create_and_bind_or_die(bindaddr, port, SOCK_DGRAM);
+}
+
+
+int FAST_FUNC create_and_connect_stream_or_die(const char *peer, int port)
+{
+	int fd;
+	len_and_sockaddr *lsa;
+
+	lsa = xhost2sockaddr(peer, port);
+	fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
+	setsockopt_reuseaddr(fd);
+	xconnect(fd, &lsa->u.sa, lsa->len);
+	free(lsa);
+	return fd;
+}
+
+int FAST_FUNC xconnect_stream(const len_and_sockaddr *lsa)
+{
+	int fd = xsocket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
+	xconnect(fd, &lsa->u.sa, lsa->len);
+	return fd;
+}
+
+/* We hijack this constant to mean something else */
+/* It doesn't hurt because we will add this bit anyway */
+#define IGNORE_PORT NI_NUMERICSERV
+static char* FAST_FUNC sockaddr2str(const struct sockaddr *sa, int flags)
+{
+	char host[128];
+	char serv[16];
+	int rc;
+	socklen_t salen;
+
+	if (ENABLE_FEATURE_UNIX_LOCAL && sa->sa_family == AF_UNIX) {
+		struct sockaddr_un *sun = (struct sockaddr_un *)sa;
+		return xasprintf("local:%.*s",
+				(int) sizeof(sun->sun_path),
+				sun->sun_path);
+	}
+
+	salen = LSA_SIZEOF_SA;
+#if ENABLE_FEATURE_IPV6
+	if (sa->sa_family == AF_INET)
+		salen = sizeof(struct sockaddr_in);
+	if (sa->sa_family == AF_INET6)
+		salen = sizeof(struct sockaddr_in6);
+#endif
+	rc = getnameinfo(sa, salen,
+			host, sizeof(host),
+	/* can do ((flags & IGNORE_PORT) ? NULL : serv) but why bother? */
+			serv, sizeof(serv),
+			/* do not resolve port# into service _name_ */
+			flags | NI_NUMERICSERV
+	);
+	if (rc)
+		return NULL;
+	if (flags & IGNORE_PORT)
+		return xstrdup(host);
+#if ENABLE_FEATURE_IPV6
+	if (sa->sa_family == AF_INET6) {
+		if (strchr(host, ':')) /* heh, it's not a resolved hostname */
+			return xasprintf("[%s]:%s", host, serv);
+		/*return xasprintf("%s:%s", host, serv);*/
+		/* - fall through instead */
+	}
+#endif
+	/* For now we don't support anything else, so it has to be INET */
+	/*if (sa->sa_family == AF_INET)*/
+		return xasprintf("%s:%s", host, serv);
+	/*return xstrdup(host);*/
+}
+
+char* FAST_FUNC xmalloc_sockaddr2host(const struct sockaddr *sa)
+{
+	return sockaddr2str(sa, 0);
+}
+
+char* FAST_FUNC xmalloc_sockaddr2host_noport(const struct sockaddr *sa)
+{
+	return sockaddr2str(sa, IGNORE_PORT);
+}
+
+char* FAST_FUNC xmalloc_sockaddr2hostonly_noport(const struct sockaddr *sa)
+{
+	return sockaddr2str(sa, NI_NAMEREQD | IGNORE_PORT);
+}
+char* FAST_FUNC xmalloc_sockaddr2dotted(const struct sockaddr *sa)
+{
+	return sockaddr2str(sa, NI_NUMERICHOST);
+}
+
+char* FAST_FUNC xmalloc_sockaddr2dotted_noport(const struct sockaddr *sa)
+{
+	return sockaddr2str(sa, NI_NUMERICHOST | IGNORE_PORT);
+}
diff --git a/busybox-1.19.3/libbb/xfunc_die.c b/busybox-1.19.3/libbb/xfunc_die.c
new file mode 100644
index 0000000..204e5e4
--- /dev/null
+++ b/busybox-1.19.3/libbb/xfunc_die.c
@@ -0,0 +1,40 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* Keeping it separate allows to NOT suck in stdio for VERY small applets.
+ * Try building busybox with only "true" enabled... */
+
+#include "libbb.h"
+
+int die_sleep;
+#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH
+jmp_buf die_jmp;
+#endif
+
+void FAST_FUNC xfunc_die(void)
+{
+	if (die_sleep) {
+		if ((ENABLE_FEATURE_PREFER_APPLETS || ENABLE_HUSH)
+		 && die_sleep < 0
+		) {
+			/* Special case. We arrive here if NOFORK applet
+			 * calls xfunc, which then decides to die.
+			 * We don't die, but jump instead back to caller.
+			 * NOFORK applets still cannot carelessly call xfuncs:
+			 * p = xmalloc(10);
+			 * q = xmalloc(10); // BUG! if this dies, we leak p!
+			 */
+			/* -2222 means "zero" (longjmp can't pass 0)
+			 * run_nofork_applet() catches -2222. */
+			longjmp(die_jmp, xfunc_error_retval ? xfunc_error_retval : -2222);
+		}
+		sleep(die_sleep);
+	}
+	exit(xfunc_error_retval);
+}
diff --git a/busybox-1.19.3/libbb/xfuncs.c b/busybox-1.19.3/libbb/xfuncs.c
new file mode 100644
index 0000000..23f2751
--- /dev/null
+++ b/busybox-1.19.3/libbb/xfuncs.c
@@ -0,0 +1,310 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2006 Rob Landley
+ * Copyright (C) 2006 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* We need to have separate xfuncs.c and xfuncs_printf.c because
+ * with current linkers, even with section garbage collection,
+ * if *.o module references any of XXXprintf functions, you pull in
+ * entire printf machinery. Even if you do not use the function
+ * which uses XXXprintf.
+ *
+ * xfuncs.c contains functions (not necessarily xfuncs)
+ * which do not pull in printf, directly or indirectly.
+ * xfunc_printf.c contains those which do.
+ *
+ * TODO: move xmalloc() and xatonum() here.
+ */
+
+#include "libbb.h"
+
+/* Turn on nonblocking I/O on a fd */
+void FAST_FUNC ndelay_on(int fd)
+{
+	int flags = fcntl(fd, F_GETFL);
+	if (flags & O_NONBLOCK)
+		return;
+	fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+void FAST_FUNC ndelay_off(int fd)
+{
+	int flags = fcntl(fd, F_GETFL);
+	if (!(flags & O_NONBLOCK))
+		return;
+	fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+}
+
+void FAST_FUNC close_on_exec_on(int fd)
+{
+	fcntl(fd, F_SETFD, FD_CLOEXEC);
+}
+
+char* FAST_FUNC strncpy_IFNAMSIZ(char *dst, const char *src)
+{
+#ifndef IFNAMSIZ
+	enum { IFNAMSIZ = 16 };
+#endif
+	return strncpy(dst, src, IFNAMSIZ);
+}
+
+
+/* Convert unsigned integer to ascii, writing into supplied buffer.
+ * A truncated result contains the first few digits of the result ala strncpy.
+ * Returns a pointer past last generated digit, does _not_ store NUL.
+ */
+void BUG_sizeof(void);
+char* FAST_FUNC utoa_to_buf(unsigned n, char *buf, unsigned buflen)
+{
+	unsigned i, out, res;
+
+	if (buflen) {
+		out = 0;
+		if (sizeof(n) == 4)
+		// 2^32-1 = 4294967295
+			i = 1000000000;
+#if UINT_MAX > 4294967295 /* prevents warning about "const too large" */
+		else
+		if (sizeof(n) == 8)
+		// 2^64-1 = 18446744073709551615
+			i = 10000000000000000000;
+#endif
+		else
+			BUG_sizeof();
+		for (; i; i /= 10) {
+			res = n / i;
+			n = n % i;
+			if (res || out || i == 1) {
+				if (--buflen == 0)
+					break;
+				out++;
+				*buf++ = '0' + res;
+			}
+		}
+	}
+	return buf;
+}
+
+/* Convert signed integer to ascii, like utoa_to_buf() */
+char* FAST_FUNC itoa_to_buf(int n, char *buf, unsigned buflen)
+{
+	if (!buflen)
+		return buf;
+	if (n < 0) {
+		n = -n;
+		*buf++ = '-';
+		buflen--;
+	}
+	return utoa_to_buf((unsigned)n, buf, buflen);
+}
+
+// The following two functions use a static buffer, so calling either one a
+// second time will overwrite previous results.
+//
+// The largest 32 bit integer is -2 billion plus NUL, or 1+10+1=12 bytes.
+// It so happens that sizeof(int) * 3 is enough for 32+ bit ints.
+// (sizeof(int) * 3 + 2 is correct for any width, even 8-bit)
+
+static char local_buf[sizeof(int) * 3];
+
+/* Convert unsigned integer to ascii using a static buffer (returned). */
+char* FAST_FUNC utoa(unsigned n)
+{
+	*(utoa_to_buf(n, local_buf, sizeof(local_buf) - 1)) = '\0';
+
+	return local_buf;
+}
+
+/* Convert signed integer to ascii using a static buffer (returned). */
+char* FAST_FUNC itoa(int n)
+{
+	*(itoa_to_buf(n, local_buf, sizeof(local_buf) - 1)) = '\0';
+
+	return local_buf;
+}
+
+/* Emit a string of hex representation of bytes */
+char* FAST_FUNC bin2hex(char *p, const char *cp, int count)
+{
+	while (count) {
+		unsigned char c = *cp++;
+		/* put lowercase hex digits */
+		*p++ = 0x20 | bb_hexdigits_upcase[c >> 4];
+		*p++ = 0x20 | bb_hexdigits_upcase[c & 0xf];
+		count--;
+	}
+	return p;
+}
+
+/* Convert "[x]x[:][x]x[:][x]x[:][x]x" hex string to binary, no more than COUNT bytes */
+char* FAST_FUNC hex2bin(char *dst, const char *str, int count)
+{
+	errno = EINVAL;
+	while (*str && count) {
+		uint8_t val;
+		uint8_t c = *str++;
+		if (isdigit(c))
+			val = c - '0';
+		else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
+			val = (c|0x20) - ('a' - 10);
+		else
+			return NULL;
+		val <<= 4;
+		c = *str;
+		if (isdigit(c))
+			val |= c - '0';
+		else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
+			val |= (c|0x20) - ('a' - 10);
+		else if (c == ':' || c == '\0')
+			val >>= 4;
+		else
+			return NULL;
+
+		*dst++ = val;
+		if (c != '\0')
+			str++;
+		if (*str == ':')
+			str++;
+		count--;
+	}
+	errno = (*str ? ERANGE : 0);
+	return dst;
+}
+
+/* Return how long the file at fd is, if there's any way to determine it. */
+#ifdef UNUSED
+off_t FAST_FUNC fdlength(int fd)
+{
+	off_t bottom = 0, top = 0, pos;
+	long size;
+
+	// If the ioctl works for this, return it.
+
+	if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512;
+
+	// FIXME: explain why lseek(SEEK_END) is not used here!
+
+	// If not, do a binary search for the last location we can read.  (Some
+	// block devices don't do BLKGETSIZE right.)
+
+	do {
+		char temp;
+
+		pos = bottom + (top - bottom) / 2;
+
+		// If we can read from the current location, it's bigger.
+
+		if (lseek(fd, pos, SEEK_SET)>=0 && safe_read(fd, &temp, 1)==1) {
+			if (bottom == top) bottom = top = (top+1) * 2;
+			else bottom = pos;
+
+		// If we can't, it's smaller.
+
+		} else {
+			if (bottom == top) {
+				if (!top) return 0;
+				bottom = top/2;
+			}
+			else top = pos;
+		}
+	} while (bottom + 1 != top);
+
+	return pos + 1;
+}
+#endif
+
+int FAST_FUNC bb_putchar_stderr(char ch)
+{
+	return write(STDERR_FILENO, &ch, 1);
+}
+
+ssize_t FAST_FUNC full_write1_str(const char *str)
+{
+	return full_write(STDOUT_FILENO, str, strlen(str));
+}
+
+ssize_t FAST_FUNC full_write2_str(const char *str)
+{
+	return full_write(STDERR_FILENO, str, strlen(str));
+}
+
+static int wh_helper(int value, int def_val, const char *env_name, int *err)
+{
+	if (value == 0) {
+		char *s = getenv(env_name);
+		if (s) {
+			value = atoi(s);
+			/* 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;
+		}
+	}
+	if (value <= 1 || value >= 30000)
+		value = def_val;
+	return value;
+}
+
+/* It is perfectly ok to pass in a NULL for either width or for
+ * height, in which case that value will not be set.  */
+int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *height)
+{
+	struct winsize win;
+	int err;
+
+	win.ws_row = 0;
+	win.ws_col = 0;
+	/* I've seen ioctl returning 0, but row/col is (still?) 0.
+	 * We treat that as an error too.  */
+	err = ioctl(fd, TIOCGWINSZ, &win) != 0 || win.ws_row == 0;
+	if (height)
+		*height = wh_helper(win.ws_row, 24, "LINES", &err);
+	if (width)
+		*width = wh_helper(win.ws_col, 80, "COLUMNS", &err);
+	return err;
+}
+
+int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp)
+{
+	return tcsetattr(STDIN_FILENO, TCSANOW, tp);
+}
+
+pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options)
+{
+	pid_t r;
+
+	do
+		r = waitpid(pid, wstat, options);
+	while ((r == -1) && (errno == EINTR));
+	return r;
+}
+
+pid_t FAST_FUNC wait_any_nohang(int *wstat)
+{
+	return safe_waitpid(-1, wstat, WNOHANG);
+}
+
+// Wait for the specified child PID to exit, returning child's error return.
+int FAST_FUNC wait4pid(pid_t pid)
+{
+	int status;
+
+	if (pid <= 0) {
+		/*errno = ECHILD; -- wrong. */
+		/* we expect errno to be already set from failed [v]fork/exec */
+		return -1;
+	}
+	if (safe_waitpid(pid, &status, 0) == -1)
+		return -1;
+	if (WIFEXITED(status))
+		return WEXITSTATUS(status);
+	if (WIFSIGNALED(status))
+		return WTERMSIG(status) + 0x180;
+	return 0;
+}
diff --git a/busybox-1.19.3/libbb/xfuncs_printf.c b/busybox-1.19.3/libbb/xfuncs_printf.c
new file mode 100644
index 0000000..56ee459
--- /dev/null
+++ b/busybox-1.19.3/libbb/xfuncs_printf.c
@@ -0,0 +1,624 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2006 Rob Landley
+ * Copyright (C) 2006 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* We need to have separate xfuncs.c and xfuncs_printf.c because
+ * with current linkers, even with section garbage collection,
+ * if *.o module references any of XXXprintf functions, you pull in
+ * entire printf machinery. Even if you do not use the function
+ * which uses XXXprintf.
+ *
+ * xfuncs.c contains functions (not necessarily xfuncs)
+ * which do not pull in printf, directly or indirectly.
+ * xfunc_printf.c contains those which do.
+ */
+
+#include "libbb.h"
+
+
+/* All the functions starting with "x" call bb_error_msg_and_die() if they
+ * fail, so callers never need to check for errors.  If it returned, it
+ * succeeded. */
+
+#ifndef DMALLOC
+/* dmalloc provides variants of these that do abort() on failure.
+ * Since dmalloc's prototypes overwrite the impls here as they are
+ * included after these prototypes in libbb.h, all is well.
+ */
+// Warn if we can't allocate size bytes of memory.
+void* FAST_FUNC malloc_or_warn(size_t size)
+{
+	void *ptr = malloc(size);
+	if (ptr == NULL && size != 0)
+		bb_error_msg(bb_msg_memory_exhausted);
+	return ptr;
+}
+
+// Die if we can't allocate size bytes of memory.
+void* FAST_FUNC xmalloc(size_t size)
+{
+	void *ptr = malloc(size);
+	if (ptr == NULL && size != 0)
+		bb_error_msg_and_die(bb_msg_memory_exhausted);
+	return ptr;
+}
+
+// Die if we can't resize previously allocated memory.  (This returns a pointer
+// to the new memory, which may or may not be the same as the old memory.
+// It'll copy the contents to a new chunk and free the old one if necessary.)
+void* FAST_FUNC xrealloc(void *ptr, size_t size)
+{
+	ptr = realloc(ptr, size);
+	if (ptr == NULL && size != 0)
+		bb_error_msg_and_die(bb_msg_memory_exhausted);
+	return ptr;
+}
+#endif /* DMALLOC */
+
+// Die if we can't allocate and zero size bytes of memory.
+void* FAST_FUNC xzalloc(size_t size)
+{
+	void *ptr = xmalloc(size);
+	memset(ptr, 0, size);
+	return ptr;
+}
+
+// Die if we can't copy a string to freshly allocated memory.
+char* FAST_FUNC xstrdup(const char *s)
+{
+	char *t;
+
+	if (s == NULL)
+		return NULL;
+
+	t = strdup(s);
+
+	if (t == NULL)
+		bb_error_msg_and_die(bb_msg_memory_exhausted);
+
+	return t;
+}
+
+// Die if we can't allocate n+1 bytes (space for the null terminator) and copy
+// the (possibly truncated to length n) string into it.
+char* FAST_FUNC xstrndup(const char *s, int n)
+{
+	int m;
+	char *t;
+
+	if (ENABLE_DEBUG && s == NULL)
+		bb_error_msg_and_die("xstrndup bug");
+
+	/* We can just xmalloc(n+1) and strncpy into it, */
+	/* but think about xstrndup("abc", 10000) wastage! */
+	m = n;
+	t = (char*) s;
+	while (m) {
+		if (!*t) break;
+		m--;
+		t++;
+	}
+	n -= m;
+	t = xmalloc(n + 1);
+	t[n] = '\0';
+
+	return memcpy(t, s, n);
+}
+
+// Die if we can't open a file and return a FILE* to it.
+// Notice we haven't got xfread(), This is for use with fscanf() and friends.
+FILE* FAST_FUNC xfopen(const char *path, const char *mode)
+{
+	FILE *fp = fopen(path, mode);
+	if (fp == NULL)
+		bb_perror_msg_and_die("can't open '%s'", path);
+	return fp;
+}
+
+// Die if we can't open a file and return a fd.
+int FAST_FUNC xopen3(const char *pathname, int flags, int mode)
+{
+	int ret;
+
+	ret = open(pathname, flags, mode);
+	if (ret < 0) {
+		bb_perror_msg_and_die("can't open '%s'", pathname);
+	}
+	return ret;
+}
+
+// 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)
+{
+	int ret;
+
+	ret = open(pathname, flags, mode);
+	if (ret < 0) {
+		bb_perror_msg("can't open '%s'", pathname);
+	}
+	return ret;
+}
+
+// Warn if we can't open a file and return a fd.
+int FAST_FUNC open_or_warn(const char *pathname, int flags)
+{
+	return open3_or_warn(pathname, flags, 0666);
+}
+
+void FAST_FUNC xunlink(const char *pathname)
+{
+	if (unlink(pathname))
+		bb_perror_msg_and_die("can't remove file '%s'", pathname);
+}
+
+void FAST_FUNC xrename(const char *oldpath, const char *newpath)
+{
+	if (rename(oldpath, newpath))
+		bb_perror_msg_and_die("can't move '%s' to '%s'", oldpath, newpath);
+}
+
+int FAST_FUNC rename_or_warn(const char *oldpath, const char *newpath)
+{
+	int n = rename(oldpath, newpath);
+	if (n)
+		bb_perror_msg("can't move '%s' to '%s'", oldpath, newpath);
+	return n;
+}
+
+void FAST_FUNC xpipe(int filedes[2])
+{
+	if (pipe(filedes))
+		bb_perror_msg_and_die("can't create pipe");
+}
+
+void FAST_FUNC xdup2(int from, int to)
+{
+	if (dup2(from, to) != to)
+		bb_perror_msg_and_die("can't duplicate file descriptor");
+}
+
+// "Renumber" opened fd
+void FAST_FUNC xmove_fd(int from, int to)
+{
+	if (from == to)
+		return;
+	xdup2(from, to);
+	close(from);
+}
+
+// Die with an error message if we can't write the entire buffer.
+void FAST_FUNC xwrite(int fd, const void *buf, size_t count)
+{
+	if (count) {
+		ssize_t size = full_write(fd, buf, count);
+		if ((size_t)size != count)
+			bb_error_msg_and_die("short write");
+	}
+}
+void FAST_FUNC xwrite_str(int fd, const char *str)
+{
+	xwrite(fd, str, strlen(str));
+}
+
+void FAST_FUNC xclose(int fd)
+{
+	if (close(fd))
+		bb_perror_msg_and_die("close failed");
+}
+
+// Die with an error message if we can't lseek to the right spot.
+off_t FAST_FUNC xlseek(int fd, off_t offset, int whence)
+{
+	off_t off = lseek(fd, offset, whence);
+	if (off == (off_t)-1) {
+		if (whence == SEEK_SET)
+			bb_perror_msg_and_die("lseek(%"OFF_FMT"u)", offset);
+		bb_perror_msg_and_die("lseek");
+	}
+	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)
+{
+	if (ferror(fp)) {
+		/* ferror doesn't set useful errno */
+		bb_error_msg_and_die("%s: I/O error", fn);
+	}
+}
+
+// Die with an error message if stdout has ferror set.
+void FAST_FUNC die_if_ferror_stdout(void)
+{
+	die_if_ferror(stdout, bb_msg_standard_output);
+}
+
+int FAST_FUNC fflush_all(void)
+{
+	return fflush(NULL);
+}
+
+
+int FAST_FUNC bb_putchar(int ch)
+{
+	return putchar(ch);
+}
+
+/* Die with an error message if we can't copy an entire FILE* to stdout,
+ * then close that file. */
+void FAST_FUNC xprint_and_close_file(FILE *file)
+{
+	fflush_all();
+	// copyfd outputs error messages for us.
+	if (bb_copyfd_eof(fileno(file), STDOUT_FILENO) == -1)
+		xfunc_die();
+
+	fclose(file);
+}
+
+// Die with an error message if we can't malloc() enough space and do an
+// sprintf() into that space.
+char* FAST_FUNC xasprintf(const char *format, ...)
+{
+	va_list p;
+	int r;
+	char *string_ptr;
+
+	va_start(p, format);
+	r = vasprintf(&string_ptr, format, p);
+	va_end(p);
+
+	if (r < 0)
+		bb_error_msg_and_die(bb_msg_memory_exhausted);
+	return string_ptr;
+}
+
+void FAST_FUNC xsetenv(const char *key, const char *value)
+{
+	if (setenv(key, value, 1))
+		bb_error_msg_and_die(bb_msg_memory_exhausted);
+}
+
+/* Handles "VAR=VAL" strings, even those which are part of environ
+ * _right now_
+ */
+void FAST_FUNC bb_unsetenv(const char *var)
+{
+	char *tp = strchr(var, '=');
+
+	if (!tp) {
+		unsetenv(var);
+		return;
+	}
+
+	/* In case var was putenv'ed, we can't replace '='
+	 * with NUL and unsetenv(var) - it won't work,
+	 * env is modified by the replacement, unsetenv
+	 * sees "VAR" instead of "VAR=VAL" and does not remove it!
+	 * horror :( */
+	tp = xstrndup(var, tp - var);
+	unsetenv(tp);
+	free(tp);
+}
+
+void FAST_FUNC bb_unsetenv_and_free(char *var)
+{
+	bb_unsetenv(var);
+	free(var);
+}
+
+// Die with an error message if we can't set gid.  (Because resource limits may
+// limit this user to a given number of processes, and if that fills up the
+// setgid() will fail and we'll _still_be_root_, which is bad.)
+void FAST_FUNC xsetgid(gid_t gid)
+{
+	if (setgid(gid)) bb_perror_msg_and_die("setgid");
+}
+
+// Die with an error message if we can't set uid.  (See xsetgid() for why.)
+void FAST_FUNC xsetuid(uid_t uid)
+{
+	if (setuid(uid)) bb_perror_msg_and_die("setuid");
+}
+
+// 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);
+}
+
+void FAST_FUNC xchroot(const char *path)
+{
+	if (chroot(path))
+		bb_perror_msg_and_die("can't change root directory to %s", path);
+}
+
+// Print a warning message if opendir() fails, but don't die.
+DIR* FAST_FUNC warn_opendir(const char *path)
+{
+	DIR *dp;
+
+	dp = opendir(path);
+	if (!dp)
+		bb_perror_msg("can't open '%s'", path);
+	return dp;
+}
+
+// Die with an error message if opendir() fails.
+DIR* FAST_FUNC xopendir(const char *path)
+{
+	DIR *dp;
+
+	dp = opendir(path);
+	if (!dp)
+		bb_perror_msg_and_die("can't open '%s'", path);
+	return dp;
+}
+
+// Die with an error message if we can't open a new socket.
+int FAST_FUNC xsocket(int domain, int type, int protocol)
+{
+	int r = socket(domain, type, protocol);
+
+	if (r < 0) {
+		/* 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
+		bb_perror_msg_and_die("socket");
+#endif
+	}
+
+	return r;
+}
+
+// Die with an error message if we can't bind a socket to an address.
+void FAST_FUNC xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen)
+{
+	if (bind(sockfd, my_addr, addrlen)) bb_perror_msg_and_die("bind");
+}
+
+// Die with an error message if we can't listen for connections on a socket.
+void FAST_FUNC xlisten(int s, int backlog)
+{
+	if (listen(s, backlog)) bb_perror_msg_and_die("listen");
+}
+
+/* Die with an error message if sendto failed.
+ * Return bytes sent otherwise  */
+ssize_t FAST_FUNC xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
+				socklen_t tolen)
+{
+	ssize_t ret = sendto(s, buf, len, 0, to, tolen);
+	if (ret < 0) {
+		if (ENABLE_FEATURE_CLEAN_UP)
+			close(s);
+		bb_perror_msg_and_die("sendto");
+	}
+	return ret;
+}
+
+// xstat() - a stat() which dies on failure with meaningful error message
+void FAST_FUNC xstat(const char *name, struct stat *stat_buf)
+{
+	if (stat(name, 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)
+{
+#if ENABLE_SELINUX
+	int rc = is_selinux_enabled();
+	if (rc == 0) {
+		bb_error_msg_and_die("SELinux is disabled");
+	} else if (rc < 0) {
+		bb_error_msg_and_die("is_selinux_enabled() failed");
+	}
+#else
+	bb_error_msg_and_die("SELinux support is disabled");
+#endif
+}
+
+int FAST_FUNC ioctl_or_perror_and_die(int fd, unsigned request, void *argp, const char *fmt,...)
+{
+	int ret;
+	va_list p;
+
+	ret = ioctl(fd, request, argp);
+	if (ret < 0) {
+		va_start(p, fmt);
+		bb_verror_msg(fmt, p, strerror(errno));
+		/* xfunc_die can actually longjmp, so be nice */
+		va_end(p);
+		xfunc_die();
+	}
+	return ret;
+}
+
+int FAST_FUNC ioctl_or_perror(int fd, unsigned request, void *argp, const char *fmt,...)
+{
+	va_list p;
+	int ret = ioctl(fd, request, argp);
+
+	if (ret < 0) {
+		va_start(p, fmt);
+		bb_verror_msg(fmt, p, strerror(errno));
+		va_end(p);
+	}
+	return ret;
+}
+
+#if ENABLE_IOCTL_HEX2STR_ERROR
+int FAST_FUNC bb_ioctl_or_warn(int fd, unsigned request, void *argp, const char *ioctl_name)
+{
+	int ret;
+
+	ret = ioctl(fd, request, argp);
+	if (ret < 0)
+		bb_simple_perror_msg(ioctl_name);
+	return ret;
+}
+int FAST_FUNC bb_xioctl(int fd, unsigned request, void *argp, const char *ioctl_name)
+{
+	int ret;
+
+	ret = ioctl(fd, request, argp);
+	if (ret < 0)
+		bb_simple_perror_msg_and_die(ioctl_name);
+	return ret;
+}
+#else
+int FAST_FUNC bb_ioctl_or_warn(int fd, unsigned request, void *argp)
+{
+	int ret;
+
+	ret = ioctl(fd, request, argp);
+	if (ret < 0)
+		bb_perror_msg("ioctl %#x failed", request);
+	return ret;
+}
+int FAST_FUNC bb_xioctl(int fd, unsigned request, void *argp)
+{
+	int ret;
+
+	ret = ioctl(fd, request, argp);
+	if (ret < 0)
+		bb_perror_msg_and_die("ioctl %#x failed", request);
+	return ret;
+}
+#endif
+
+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;
+}
+
+void FAST_FUNC generate_uuid(uint8_t *buf)
+{
+	/* http://www.ietf.org/rfc/rfc4122.txt
+	 *  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
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |                          time_low                             |
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |       time_mid                |         time_hi_and_version   |
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |clk_seq_and_variant            |         node (0-1)            |
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * |                         node (2-5)                            |
+	 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+	 * IOW, uuid has this layout:
+	 * uint32_t time_low (big endian)
+	 * uint16_t time_mid (big endian)
+	 * uint16_t time_hi_and_version (big endian)
+	 *  version is a 4-bit field:
+	 *   1 Time-based
+	 *   2 DCE Security, with embedded POSIX UIDs
+	 *   3 Name-based (MD5)
+	 *   4 Randomly generated
+	 *   5 Name-based (SHA-1)
+	 * uint16_t clk_seq_and_variant (big endian)
+	 *  variant is a 3-bit field:
+	 *   0xx Reserved, NCS backward compatibility
+	 *   10x The variant specified in rfc4122
+	 *   110 Reserved, Microsoft backward compatibility
+	 *   111 Reserved for future definition
+	 * uint8_t node[6]
+	 *
+	 * For version 4, these bits are set/cleared:
+	 * time_hi_and_version & 0x0fff | 0x4000
+	 * clk_seq_and_variant & 0x3fff | 0x8000
+	 */
+	pid_t pid;
+	int i;
+
+	i = open("/dev/urandom", O_RDONLY);
+	if (i >= 0) {
+		read(i, buf, 16);
+		close(i);
+	}
+	/* Paranoia. /dev/urandom may be missing.
+	 * rand() is guaranteed to generate at least [0, 2^15) range,
+	 * but lowest bits in some libc are not so "random".  */
+	srand(monotonic_us()); /* pulls in printf */
+	pid = getpid();
+	while (1) {
+		for (i = 0; i < 16; i++)
+			buf[i] ^= rand() >> 5;
+		if (pid == 0)
+			break;
+		srand(pid);
+		pid = 0;
+	}
+
+	/* version = 4 */
+	buf[4 + 2    ] = (buf[4 + 2    ] & 0x0f) | 0x40;
+	/* variant = 10x */
+	buf[4 + 2 + 2] = (buf[4 + 2 + 2] & 0x3f) | 0x80;
+}
+
+#if BB_MMU
+pid_t FAST_FUNC xfork(void)
+{
+	pid_t pid;
+	pid = fork();
+	if (pid < 0) /* wtf? */
+		bb_perror_msg_and_die("vfork"+1);
+	return pid;
+}
+#endif
diff --git a/busybox-1.19.3/libbb/xgetcwd.c b/busybox-1.19.3/libbb/xgetcwd.c
new file mode 100644
index 0000000..71720d3
--- /dev/null
+++ b/busybox-1.19.3/libbb/xgetcwd.c
@@ -0,0 +1,43 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * xgetcwd.c -- return current directory with unlimited length
+ * Copyright (C) 1992, 1996 Free Software Foundation, Inc.
+ * Written by David MacKenzie <djm@gnu.ai.mit.edu>.
+ *
+ * Special function for busybox written by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Return the current directory, newly allocated, arbitrarily long.
+   Return NULL and set errno on error.
+   If argument is not NULL (previous usage allocate memory), call free()
+*/
+
+char* FAST_FUNC
+xrealloc_getcwd_or_warn(char *cwd)
+{
+#define PATH_INCR 64
+
+	char *ret;
+	unsigned path_max;
+
+	path_max = 128; /* 128 + 64 should be enough for 99% of cases */
+
+	while (1) {
+		path_max += PATH_INCR;
+		cwd = xrealloc(cwd, path_max);
+		ret = getcwd(cwd, path_max);
+		if (ret == NULL) {
+			if (errno == ERANGE)
+				continue;
+			free(cwd);
+			bb_perror_msg("getcwd");
+			return NULL;
+		}
+		cwd = xrealloc(cwd, strlen(cwd) + 1);
+		return cwd;
+	}
+}
diff --git a/busybox-1.19.3/libbb/xgethostbyname.c b/busybox-1.19.3/libbb/xgethostbyname.c
new file mode 100644
index 0000000..89d0329
--- /dev/null
+++ b/busybox-1.19.3/libbb/xgethostbyname.c
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini xgethostbyname implementation.
+ *
+ * Copyright (C) 2001 Matt Kraai <kraai@alumni.carnegiemellon.edu>.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+struct hostent* FAST_FUNC xgethostbyname(const char *name)
+{
+	struct hostent *retval = gethostbyname(name);
+	if (!retval)
+		bb_herror_msg_and_die("%s", name);
+	return retval;
+}
diff --git a/busybox-1.19.3/libbb/xreadlink.c b/busybox-1.19.3/libbb/xreadlink.c
new file mode 100644
index 0000000..ec95af2
--- /dev/null
+++ b/busybox-1.19.3/libbb/xreadlink.c
@@ -0,0 +1,115 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * xreadlink.c - safe implementation of readlink.
+ * Returns a NULL on failure...
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/*
+ * NOTE: This function returns a malloced char* that you will have to free
+ * yourself.
+ */
+char* FAST_FUNC xmalloc_readlink(const char *path)
+{
+	enum { GROWBY = 80 }; /* how large we will grow strings by */
+
+	char *buf = NULL;
+	int bufsize = 0, readsize = 0;
+
+	do {
+		bufsize += GROWBY;
+		buf = xrealloc(buf, bufsize);
+		readsize = readlink(path, buf, bufsize);
+		if (readsize == -1) {
+			free(buf);
+			return NULL;
+		}
+	} while (bufsize < readsize + 1);
+
+	buf[readsize] = '\0';
+
+	return buf;
+}
+
+/*
+ * This routine is not the same as realpath(), which
+ * canonicalizes the given path completely. This routine only
+ * follows trailing symlinks until a real file is reached and
+ * returns its name. If the path ends in a dangling link or if
+ * the target doesn't exist, the path is returned in any case.
+ * Intermediate symlinks in the path are not expanded -- only
+ * those at the tail.
+ * A malloced char* is returned, which must be freed by the caller.
+ */
+char* FAST_FUNC xmalloc_follow_symlinks(const char *path)
+{
+	char *buf;
+	char *lpc;
+	char *linkpath;
+	int bufsize;
+	int looping = MAXSYMLINKS + 1;
+
+	buf = xstrdup(path);
+	goto jump_in;
+
+	while (1) {
+		linkpath = xmalloc_readlink(buf);
+		if (!linkpath) {
+			/* not a symlink, or doesn't exist */
+			if (errno == EINVAL || errno == ENOENT)
+				return buf;
+			goto free_buf_ret_null;
+		}
+
+		if (!--looping) {
+			free(linkpath);
+ free_buf_ret_null:
+			free(buf);
+			return NULL;
+		}
+
+		if (*linkpath != '/') {
+			bufsize += strlen(linkpath);
+			buf = xrealloc(buf, bufsize);
+			lpc = bb_get_last_path_component_strip(buf);
+			strcpy(lpc, linkpath);
+			free(linkpath);
+		} else {
+			free(buf);
+			buf = linkpath;
+ jump_in:
+			bufsize = strlen(buf) + 1;
+		}
+	}
+}
+
+char* FAST_FUNC xmalloc_readlink_or_warn(const char *path)
+{
+	char *buf = xmalloc_readlink(path);
+	if (!buf) {
+		/* EINVAL => "file: Invalid argument" => puzzled user */
+		const char *errmsg = "not a symlink";
+		int err = errno;
+		if (err != EINVAL)
+			errmsg = strerror(err);
+		bb_error_msg("%s: cannot read link: %s", path, errmsg);
+	}
+	return buf;
+}
+
+char* FAST_FUNC xmalloc_realpath(const char *path)
+{
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+	/* glibc provides a non-standard extension */
+	/* new: POSIX.1-2008 specifies this behavior as well */
+	return realpath(path, NULL);
+#else
+	char buf[PATH_MAX+1];
+
+	/* on error returns NULL (xstrdup(NULL) == NULL) */
+	return xstrdup(realpath(path, buf));
+#endif
+}
diff --git a/busybox-1.19.3/libbb/xrealloc_vector.c b/busybox-1.19.3/libbb/xrealloc_vector.c
new file mode 100644
index 0000000..e8d31b7
--- /dev/null
+++ b/busybox-1.19.3/libbb/xrealloc_vector.c
@@ -0,0 +1,46 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) 2008 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* Resize (grow) malloced vector.
+ *
+ *  #define magic packed two parameters into one:
+ *  sizeof = sizeof_and_shift >> 8
+ *  shift  = (sizeof_and_shift) & 0xff
+ *
+ * Lets say shift = 4. 1 << 4 == 0x10.
+ * If idx == 0, 0x10, 0x20 etc, vector[] is resized to next higher
+ * idx step, plus one: if idx == 0x20, vector[] is resized to 0x31,
+ * thus last usable element is vector[0x30].
+ *
+ * In other words: after xrealloc_vector(v, 4, idx), with any idx,
+ * it's ok to use at least v[idx] and v[idx+1].
+ * v[idx+2] etc generally are not ok.
+ *
+ * New elements are zeroed out, but only if realloc was done
+ * (not on every call). You can depend on v[idx] and v[idx+1] being
+ * zeroed out if you use it like this:
+ *  v = xrealloc_vector(v, 4, idx);
+ *  v[idx].some_fields = ...; - the rest stays 0/NULL
+ *  idx++;
+ * If you do not advance idx like above, you should be more careful.
+ * Next call to xrealloc_vector(v, 4, idx) may or may not zero out v[idx].
+ */
+void* FAST_FUNC xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx)
+{
+	int mask = 1 << (uint8_t)sizeof_and_shift;
+
+	if (!(idx & (mask - 1))) {
+		sizeof_and_shift >>= 8; /* sizeof(vector[0]) */
+		vector = xrealloc(vector, sizeof_and_shift * (idx + mask + 1));
+		memset((char*)vector + (sizeof_and_shift * idx), 0, sizeof_and_shift * (mask + 1));
+	}
+	return vector;
+}
diff --git a/busybox-1.19.3/libbb/xregcomp.c b/busybox-1.19.3/libbb/xregcomp.c
new file mode 100644
index 0000000..344028f
--- /dev/null
+++ b/busybox-1.19.3/libbb/xregcomp.c
@@ -0,0 +1,32 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Utility routines.
+ *
+ * Copyright (C) many different people.
+ * If you wrote this, please acknowledge your work.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "xregex.h"
+
+char* FAST_FUNC regcomp_or_errmsg(regex_t *preg, const char *regex, int cflags)
+{
+	int ret = regcomp(preg, regex, cflags);
+	if (ret) {
+		int errmsgsz = regerror(ret, preg, NULL, 0);
+		char *errmsg = xmalloc(errmsgsz);
+		regerror(ret, preg, errmsg, errmsgsz);
+		return errmsg;
+	}
+	return NULL;
+}
+
+void FAST_FUNC xregcomp(regex_t *preg, const char *regex, int cflags)
+{
+	char *errmsg = regcomp_or_errmsg(preg, regex, cflags);
+	if (errmsg) {
+		bb_error_msg_and_die("bad regex '%s': %s", regex, errmsg);
+	}
+}
diff --git a/busybox-1.19.3/libpwdgrp/Kbuild.src b/busybox-1.19.3/libpwdgrp/Kbuild.src
new file mode 100644
index 0000000..d15e3a2
--- /dev/null
+++ b/busybox-1.19.3/libpwdgrp/Kbuild.src
@@ -0,0 +1,9 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y := uidgid_get.o
+
+lib-$(CONFIG_USE_BB_PWD_GRP) += pwd_grp.o
diff --git a/busybox-1.19.3/libpwdgrp/pwd_grp.c b/busybox-1.19.3/libpwdgrp/pwd_grp.c
new file mode 100644
index 0000000..2eb9d9d
--- /dev/null
+++ b/busybox-1.19.3/libpwdgrp/pwd_grp.c
@@ -0,0 +1,1060 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 2003     Manuel Novoa III
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Nov 6, 2003  Initial version.
+ *
+ * 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:
+ *    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>
+
+/**********************************************************************/
+/* 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
+
+/**********************************************************************/
+/* Prototypes for internal functions. */
+
+static int bb__pgsreader(
+		int FAST_FUNC (*parserfunc)(void *d, char *line),
+		void *data,
+		char *__restrict line_buff,
+		size_t buflen,
+		FILE *f);
+
+static int FAST_FUNC bb__parsepwent(void *pw, char *line);
+static int FAST_FUNC bb__parsegrent(void *gr, char *line);
+#if ENABLE_USE_BB_SHADOW
+static int FAST_FUNC bb__parsespent(void *sp, char *line);
+#endif
+
+/**********************************************************************/
+/* We avoid having big global data. */
+
+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
+#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];
+#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;
+
+static struct statics *get_S(void)
+{
+	if (!ptr_to_statics)
+		ptr_to_statics = xzalloc(sizeof(*ptr_to_statics));
+	return ptr_to_statics;
+}
+
+/* Always use in this order, get_S() must be called first */
+#define RESULTBUF(name) &((S = get_S())->name##_resultbuf)
+#define BUFFER(name)    (S->name##_buffer)
+
+/**********************************************************************/
+/* For the various fget??ent_r funcs, return
+ *
+ *  0: success
+ *  ENOENT: end-of-file encountered
+ *  ERANGE: buflen too small
+ *  other error values possible. See bb__pgsreader.
+ *
+ * Also, *result == resultbuf on success and NULL on failure.
+ *
+ * NOTE: glibc difference - For the ENOENT case, glibc also sets errno.
+ *   We do not, as it really isn't an error if we reach the end-of-file.
+ *   Doing so is analogous to having fgetc() set errno on EOF.
+ */
+/**********************************************************************/
+
+int fgetpwent_r(FILE *__restrict stream, struct passwd *__restrict resultbuf,
+				char *__restrict buffer, size_t buflen,
+				struct passwd **__restrict result)
+{
+	int rv;
+
+	*result = NULL;
+
+	rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, stream);
+	if (!rv) {
+		*result = resultbuf;
+	}
+
+	return rv;
+}
+
+int fgetgrent_r(FILE *__restrict stream, struct group *__restrict resultbuf,
+				char *__restrict buffer, size_t buflen,
+				struct group **__restrict result)
+{
+	int rv;
+
+	*result = NULL;
+
+	rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, stream);
+	if (!rv) {
+		*result = resultbuf;
+	}
+
+	return rv;
+}
+
+#if ENABLE_USE_BB_SHADOW
+#ifdef UNUSED_FOR_NOW
+int fgetspent_r(FILE *__restrict stream, struct spwd *__restrict resultbuf,
+				char *__restrict buffer, size_t buflen,
+				struct spwd **__restrict result)
+{
+	int rv;
+
+	*result = NULL;
+
+	rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, stream);
+	if (!rv) {
+		*result = resultbuf;
+	}
+
+	return rv;
+}
+#endif
+#endif
+
+/**********************************************************************/
+/* For the various fget??ent funcs, return NULL on failure and a
+ * pointer to the appropriate struct (statically allocated) on success.
+ * TODO: audit & stop using these in bbox, they pull in static buffers */
+/**********************************************************************/
+
+#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
+struct passwd *fgetpwent(FILE *stream)
+{
+	struct statics *S;
+	struct passwd *resultbuf = RESULTBUF(fgetpwent);
+	char *buffer = BUFFER(fgetpwent);
+	struct passwd *result;
+
+	fgetpwent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetpwent)), &result);
+	return result;
+}
+
+struct group *fgetgrent(FILE *stream)
+{
+	struct statics *S;
+	struct group *resultbuf = RESULTBUF(fgetgrent);
+	char *buffer = BUFFER(fgetgrent);
+	struct group *result;
+
+	fgetgrent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetgrent)), &result);
+	return result;
+}
+#endif
+
+#if ENABLE_USE_BB_SHADOW
+#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
+struct spwd *fgetspent(FILE *stream)
+{
+	struct statics *S;
+	struct spwd *resultbuf = RESULTBUF(fgetspent);
+	char *buffer = BUFFER(fgetspent);
+	struct spwd *result;
+
+	fgetspent_r(stream, resultbuf, buffer, sizeof(BUFFER(fgetspent)), &result);
+	return result;
+}
+#endif
+
+#ifdef UNUSED_FOR_NOW
+int sgetspent_r(const char *string, struct spwd *result_buf,
+				char *buffer, size_t buflen, struct spwd **result)
+{
+	int rv = ERANGE;
+
+	*result = NULL;
+
+	if (buflen < PWD_BUFFER_SIZE) {
+ DO_ERANGE:
+		errno = rv;
+		goto DONE;
+	}
+
+	if (string != buffer) {
+		if (strlen(string) >= buflen) {
+			goto DO_ERANGE;
+		}
+		strcpy(buffer, string);
+	}
+
+	rv = bb__parsespent(result_buf, buffer);
+	if (!rv) {
+		*result = result_buf;
+	}
+
+ DONE:
+	return rv;
+}
+#endif
+#endif /* ENABLE_USE_BB_SHADOW */
+
+/**********************************************************************/
+
+#define GETXXKEY_R_FUNC         getpwnam_r
+#define GETXXKEY_R_PARSER       bb__parsepwent
+#define GETXXKEY_R_ENTTYPE      struct passwd
+#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->pw_name, key))
+#define GETXXKEY_R_KEYTYPE      const char *__restrict
+#define GETXXKEY_R_PATHNAME     _PATH_PASSWD
+#include "pwd_grp_internal.c"
+
+#define GETXXKEY_R_FUNC         getgrnam_r
+#define GETXXKEY_R_PARSER       bb__parsegrent
+#define GETXXKEY_R_ENTTYPE      struct group
+#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->gr_name, key))
+#define GETXXKEY_R_KEYTYPE      const char *__restrict
+#define GETXXKEY_R_PATHNAME     _PATH_GROUP
+#include "pwd_grp_internal.c"
+
+#if ENABLE_USE_BB_SHADOW
+#define GETXXKEY_R_FUNC         getspnam_r
+#define GETXXKEY_R_PARSER       bb__parsespent
+#define GETXXKEY_R_ENTTYPE      struct spwd
+#define GETXXKEY_R_TEST(ENT)    (!strcmp((ENT)->sp_namp, key))
+#define GETXXKEY_R_KEYTYPE      const char *__restrict
+#define GETXXKEY_R_PATHNAME     _PATH_SHADOW
+#include "pwd_grp_internal.c"
+#endif
+
+#define GETXXKEY_R_FUNC         getpwuid_r
+#define GETXXKEY_R_PARSER       bb__parsepwent
+#define GETXXKEY_R_ENTTYPE      struct passwd
+#define GETXXKEY_R_TEST(ENT)    ((ENT)->pw_uid == key)
+#define GETXXKEY_R_KEYTYPE      uid_t
+#define GETXXKEY_R_PATHNAME     _PATH_PASSWD
+#include "pwd_grp_internal.c"
+
+#define GETXXKEY_R_FUNC         getgrgid_r
+#define GETXXKEY_R_PARSER       bb__parsegrent
+#define GETXXKEY_R_ENTTYPE      struct group
+#define GETXXKEY_R_TEST(ENT)    ((ENT)->gr_gid == key)
+#define GETXXKEY_R_KEYTYPE      gid_t
+#define GETXXKEY_R_PATHNAME     _PATH_GROUP
+#include "pwd_grp_internal.c"
+
+/**********************************************************************/
+/* TODO: audit & stop using these in bbox, they pull in static buffers */
+
+/* This one has many users */
+struct passwd *getpwuid(uid_t uid)
+{
+	struct statics *S;
+	struct passwd *resultbuf = RESULTBUF(getpwuid);
+	char *buffer = BUFFER(getpwuid);
+	struct passwd *result;
+
+	getpwuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getpwuid)), &result);
+	return result;
+}
+
+/* This one has many users */
+struct group *getgrgid(gid_t gid)
+{
+	struct statics *S;
+	struct group *resultbuf = RESULTBUF(getgrgid);
+	char *buffer = BUFFER(getgrgid);
+	struct group *result;
+
+	getgrgid_r(gid, resultbuf, buffer, sizeof(BUFFER(getgrgid)), &result);
+	return result;
+}
+
+#if 0 //ENABLE_USE_BB_SHADOW
+/* This function is non-standard and is currently not built.  It seems
+ * 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)
+{
+	int rv;
+	struct passwd *pp;
+	struct passwd password;
+	char pwd_buff[PWD_BUFFER_SIZE];
+
+	*result = NULL;
+	rv = getpwuid_r(uid, &password, pwd_buff, sizeof(pwd_buff), &pp);
+	if (!rv) {
+		rv = getspnam_r(password.pw_name, resultbuf, buffer, buflen, result);
+	}
+
+	return rv;
+}
+
+/* This function is non-standard and is currently not built.
+ * Why it was added, I do not know. */
+struct spwd *getspuid(uid_t uid)
+{
+	struct statics *S;
+	struct spwd *resultbuf = RESULTBUF(getspuid);
+	char *buffer = BUFFER(getspuid);
+	struct spwd *result;
+
+	getspuid_r(uid, resultbuf, buffer, sizeof(BUFFER(getspuid)), &result);
+	return result;
+}
+#endif
+
+/* This one has many users */
+struct passwd *getpwnam(const char *name)
+{
+	struct statics *S;
+	struct passwd *resultbuf = RESULTBUF(getpwnam);
+	char *buffer = BUFFER(getpwnam);
+	struct passwd *result;
+
+	getpwnam_r(name, resultbuf, buffer, sizeof(BUFFER(getpwnam)), &result);
+	return result;
+}
+
+/* This one has many users */
+struct group *getgrnam(const char *name)
+{
+	struct statics *S;
+	struct group *resultbuf = RESULTBUF(getgrnam);
+	char *buffer = BUFFER(getgrnam);
+	struct group *result;
+
+	getgrnam_r(name, resultbuf, buffer, sizeof(BUFFER(getgrnam)), &result);
+	return result;
+}
+
+#if 0 //ENABLE_USE_BB_SHADOW
+struct spwd *getspnam(const char *name)
+{
+	struct statics *S;
+	struct spwd *resultbuf = RESULTBUF(getspnam);
+	char *buffer = BUFFER(getspnam);
+	struct spwd *result;
+
+	getspnam_r(name, resultbuf, buffer, sizeof(BUFFER(getspnam)), &result);
+	return result;
+}
+#endif
+
+/**********************************************************************/
+
+/* FIXME: we don't have such CONFIG_xx - ?! */
+
+#if defined CONFIG_USE_BB_THREADSAFE_SHADOW && defined PTHREAD_MUTEX_INITIALIZER
+static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;
+# define LOCK		pthread_mutex_lock(&mylock)
+# define UNLOCK		pthread_mutex_unlock(&mylock);
+#else
+# define LOCK		((void) 0)
+# define UNLOCK		((void) 0)
+#endif
+
+static FILE *pwf /*= NULL*/;
+void setpwent(void)
+{
+	LOCK;
+	if (pwf) {
+		rewind(pwf);
+	}
+	UNLOCK;
+}
+
+void endpwent(void)
+{
+	LOCK;
+	if (pwf) {
+		fclose(pwf);
+		pwf = NULL;
+	}
+	UNLOCK;
+}
+
+
+int getpwent_r(struct passwd *__restrict resultbuf,
+			   char *__restrict buffer, size_t buflen,
+			   struct passwd **__restrict result)
+{
+	int rv;
+
+	LOCK;
+	*result = NULL;				/* In case of error... */
+
+	if (!pwf) {
+		pwf = fopen_for_read(_PATH_PASSWD);
+		if (!pwf) {
+			rv = errno;
+			goto ERR;
+		}
+	}
+
+	rv = bb__pgsreader(bb__parsepwent, resultbuf, buffer, buflen, pwf);
+	if (!rv) {
+		*result = resultbuf;
+	}
+
+ ERR:
+	UNLOCK;
+	return rv;
+}
+
+static FILE *grf /*= NULL*/;
+void setgrent(void)
+{
+	LOCK;
+	if (grf) {
+		rewind(grf);
+	}
+	UNLOCK;
+}
+
+void endgrent(void)
+{
+	LOCK;
+	if (grf) {
+		fclose(grf);
+		grf = NULL;
+	}
+	UNLOCK;
+}
+
+int getgrent_r(struct group *__restrict resultbuf,
+			   char *__restrict buffer, size_t buflen,
+			   struct group **__restrict result)
+{
+	int rv;
+
+	LOCK;
+	*result = NULL;				/* In case of error... */
+
+	if (!grf) {
+		grf = fopen_for_read(_PATH_GROUP);
+		if (!grf) {
+			rv = errno;
+			goto ERR;
+		}
+	}
+
+	rv = bb__pgsreader(bb__parsegrent, resultbuf, buffer, buflen, grf);
+	if (!rv) {
+		*result = resultbuf;
+	}
+
+ ERR:
+	UNLOCK;
+	return rv;
+}
+
+#ifdef UNUSED_FOR_NOW
+#if ENABLE_USE_BB_SHADOW
+static FILE *spf /*= NULL*/;
+void setspent(void)
+{
+	LOCK;
+	if (spf) {
+		rewind(spf);
+	}
+	UNLOCK;
+}
+
+void endspent(void)
+{
+	LOCK;
+	if (spf) {
+		fclose(spf);
+		spf = NULL;
+	}
+	UNLOCK;
+}
+
+int getspent_r(struct spwd *resultbuf, char *buffer,
+			   size_t buflen, struct spwd **result)
+{
+	int rv;
+
+	LOCK;
+	*result = NULL;				/* In case of error... */
+
+	if (!spf) {
+		spf = fopen_for_read(_PATH_SHADOW);
+		if (!spf) {
+			rv = errno;
+			goto ERR;
+		}
+	}
+
+	rv = bb__pgsreader(bb__parsespent, resultbuf, buffer, buflen, spf);
+	if (!rv) {
+		*result = resultbuf;
+	}
+
+ ERR:
+	UNLOCK;
+	return rv;
+}
+#endif
+#endif /* UNUSED_FOR_NOW */
+
+#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
+struct passwd *getpwent(void)
+{
+	static char line_buff[PWD_BUFFER_SIZE];
+	static struct passwd pwd;
+	struct passwd *result;
+
+	getpwent_r(&pwd, line_buff, sizeof(line_buff), &result);
+	return result;
+}
+
+struct group *getgrent(void)
+{
+	static char line_buff[GRP_BUFFER_SIZE];
+	static struct group gr;
+	struct group *result;
+
+	getgrent_r(&gr, line_buff, sizeof(line_buff), &result);
+	return result;
+}
+
+#if ENABLE_USE_BB_SHADOW
+struct spwd *getspent(void)
+{
+	static char line_buff[PWD_BUFFER_SIZE];
+	static struct spwd spwd;
+	struct spwd *result;
+
+	getspent_r(&spwd, line_buff, sizeof(line_buff), &result);
+	return result;
+}
+
+struct spwd *sgetspent(const char *string)
+{
+	static char line_buff[PWD_BUFFER_SIZE];
+	static struct spwd spwd;
+	struct spwd *result;
+
+	sgetspent_r(string, &spwd, line_buff, sizeof(line_buff), &result);
+	return result;
+}
+#endif
+#endif /* UNUSED_SINCE_WE_AVOID_STATIC_BUFS */
+
+static gid_t *getgrouplist_internal(int *ngroups_ptr, const char *user, gid_t gid)
+{
+	FILE *grfile;
+	gid_t *group_list;
+	int ngroups;
+	struct group group;
+	char buff[PWD_BUFFER_SIZE];
+
+	/* We alloc space for 8 gids at a time. */
+	group_list = xmalloc(8 * sizeof(group_list[0]));
+	group_list[0] = gid;
+	ngroups = 1;
+
+	grfile = fopen_for_read(_PATH_GROUP);
+	if (grfile) {
+		while (!bb__pgsreader(bb__parsegrent, &group, buff, sizeof(buff), grfile)) {
+			char **m;
+			assert(group.gr_mem); /* Must have at least a NULL terminator. */
+			if (group.gr_gid == gid)
+				continue;
+			for (m = group.gr_mem; *m; m++) {
+				if (strcmp(*m, user) != 0)
+					continue;
+				group_list = xrealloc_vector(group_list, /*8=2^3:*/ 3, ngroups);
+				group_list[ngroups++] = group.gr_gid;
+				break;
+			}
+		}
+		fclose(grfile);
+	}
+	*ngroups_ptr = ngroups;
+	return group_list;
+}
+
+int initgroups(const char *user, gid_t gid)
+{
+	int ngroups;
+	gid_t *group_list = getgrouplist_internal(&ngroups, user, gid);
+
+	ngroups = setgroups(ngroups, group_list);
+	free(group_list);
+	return ngroups;
+}
+
+int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups)
+{
+	int ngroups_old = *ngroups;
+	gid_t *group_list = getgrouplist_internal(ngroups, user, gid);
+
+	if (*ngroups <= ngroups_old) {
+		ngroups_old = *ngroups;
+		memcpy(groups, group_list, ngroups_old * sizeof(groups[0]));
+	} else {
+		ngroups_old = -1;
+	}
+	free(group_list);
+	return ngroups_old;
+}
+
+#ifdef UNUSED_SINCE_WE_AVOID_STATIC_BUFS
+int putpwent(const struct passwd *__restrict p, FILE *__restrict f)
+{
+	int rv = -1;
+
+#if 0
+	/* glibc does this check */
+	if (!p || !f) {
+		errno = EINVAL;
+		return rv;
+	}
+#endif
+
+	/* No extra thread locking is needed above what fprintf does. */
+	if (fprintf(f, "%s:%s:%lu:%lu:%s:%s:%s\n",
+				p->pw_name, p->pw_passwd,
+				(unsigned long)(p->pw_uid),
+				(unsigned long)(p->pw_gid),
+				p->pw_gecos, p->pw_dir, p->pw_shell) >= 0
+		) {
+		rv = 0;
+	}
+
+	return rv;
+}
+
+int putgrent(const struct group *__restrict p, FILE *__restrict f)
+{
+	int rv = -1;
+
+#if 0
+	/* glibc does this check */
+	if (!p || !f) {
+		errno = EINVAL;
+		return rv;
+	}
+#endif
+
+	if (fprintf(f, "%s:%s:%lu:",
+				p->gr_name, p->gr_passwd,
+				(unsigned long)(p->gr_gid)) >= 0
+	) {
+		static const char format[] ALIGN1 = ",%s";
+
+		char **m;
+		const char *fmt;
+
+		fmt = format + 1;
+
+		assert(p->gr_mem);
+		m = p->gr_mem;
+
+		while (1) {
+			if (!*m) {
+				if (fputc('\n', f) >= 0) {
+					rv = 0;
+				}
+				break;
+			}
+			if (fprintf(f, fmt, *m) < 0) {
+				break;
+			}
+			m++;
+			fmt = format;
+		}
+	}
+
+	return rv;
+}
+#endif
+
+#if ENABLE_USE_BB_SHADOW
+#ifdef UNUSED_FOR_NOW
+static const unsigned char put_sp_off[] ALIGN1 = {
+	offsetof(struct spwd, sp_lstchg),       /* 2 - not a char ptr */
+	offsetof(struct spwd, sp_min),          /* 3 - not a char ptr */
+	offsetof(struct spwd, sp_max),          /* 4 - not a char ptr */
+	offsetof(struct spwd, sp_warn),         /* 5 - not a char ptr */
+	offsetof(struct spwd, sp_inact),        /* 6 - not a char ptr */
+	offsetof(struct spwd, sp_expire)        /* 7 - not a char ptr */
+};
+
+int putspent(const struct spwd *p, FILE *stream)
+{
+	const char *fmt;
+	long x;
+	int i;
+	int rv = -1;
+
+	/* Unlike putpwent and putgrent, glibc does not check the args. */
+	if (fprintf(stream, "%s:%s:", p->sp_namp,
+				(p->sp_pwdp ? p->sp_pwdp : "")) < 0
+	) {
+		goto DO_UNLOCK;
+	}
+
+	for (i = 0; i < sizeof(put_sp_off); i++) {
+		fmt = "%ld:";
+		x = *(long *)((char *)p + put_sp_off[i]);
+		if (x == -1) {
+			fmt += 3;
+		}
+		if (fprintf(stream, fmt, x) < 0) {
+			goto DO_UNLOCK;
+		}
+	}
+
+	if ((p->sp_flag != ~0UL) && (fprintf(stream, "%lu", p->sp_flag) < 0)) {
+		goto DO_UNLOCK;
+	}
+
+	if (fputc('\n', stream) > 0) {
+		rv = 0;
+	}
+
+ DO_UNLOCK:
+	return rv;
+}
+#endif
+#endif /* USE_BB_SHADOW */
+
+/**********************************************************************/
+/* Internal functions                                                 */
+/**********************************************************************/
+
+static const unsigned char pw_off[] ALIGN1 = {
+	offsetof(struct passwd, pw_name),       /* 0 */
+	offsetof(struct passwd, pw_passwd),     /* 1 */
+	offsetof(struct passwd, pw_uid),        /* 2 - not a char ptr */
+	offsetof(struct passwd, pw_gid),        /* 3 - not a char ptr */
+	offsetof(struct passwd, pw_gecos),      /* 4 */
+	offsetof(struct passwd, pw_dir),        /* 5 */
+	offsetof(struct passwd, pw_shell)       /* 6 */
+};
+
+static int FAST_FUNC bb__parsepwent(void *data, char *line)
+{
+	char *endptr;
+	char *p;
+	int i;
+
+	i = 0;
+	while (1) {
+		p = (char *) data + pw_off[i];
+
+		if (i < 2 || i > 3) {
+			*((char **) p) = line;
+			if (i == 6) {
+				return 0;
+			}
+			/* NOTE: glibc difference - glibc allows omission of
+			 * ':' seperators after the gid field if all remaining
+			 * entries are empty.  We require all separators. */
+			line = strchr(line, ':');
+			if (!line) {
+				break;
+			}
+		} else {
+			unsigned long t = strtoul(line, &endptr, 10);
+			/* Make sure we had at least one digit, and that the
+			 * failing char is the next field seperator ':'.  See
+			 * glibc difference note above. */
+			/* TODO: Also check for leading whitespace? */
+			if ((endptr == line) || (*endptr != ':')) {
+				break;
+			}
+			line = endptr;
+			if (i & 1) {		/* i == 3 -- gid */
+				*((gid_t *) p) = t;
+			} else {			/* i == 2 -- uid */
+				*((uid_t *) p) = t;
+			}
+		}
+
+		*line++ = '\0';
+		i++;
+	} /* while (1) */
+
+	return -1;
+}
+
+/**********************************************************************/
+
+static const unsigned char gr_off[] ALIGN1 = {
+	offsetof(struct group, gr_name),        /* 0 */
+	offsetof(struct group, gr_passwd),      /* 1 */
+	offsetof(struct group, gr_gid)          /* 2 - not a char ptr */
+};
+
+static int FAST_FUNC bb__parsegrent(void *data, char *line)
+{
+	char *endptr;
+	char *p;
+	int i;
+	char **members;
+	char *end_of_buf;
+
+	end_of_buf = ((struct group *) data)->gr_name; /* Evil hack! */
+	i = 0;
+	while (1) {
+		p = (char *) data + gr_off[i];
+
+		if (i < 2) {
+			*((char **) p) = line;
+			line = strchr(line, ':');
+			if (!line) {
+				break;
+			}
+			*line++ = '\0';
+			i++;
+		} else {
+			*((gid_t *) p) = strtoul(line, &endptr, 10);
+
+			/* NOTE: glibc difference - glibc allows omission of the
+			 * trailing colon when there is no member list.  We treat
+			 * this as an error. */
+
+			/* Make sure we had at least one digit, and that the
+			 * failing char is the next field seperator ':'.  See
+			 * glibc difference note above. */
+			if ((endptr == line) || (*endptr != ':')) {
+				break;
+			}
+
+			i = 1;				/* Count terminating NULL ptr. */
+			p = endptr;
+
+			if (p[1]) { /* We have a member list to process. */
+				/* Overwrite the last ':' with a ',' before counting.
+				 * This allows us to (1) test for initial ','
+				 * and (2) adds one ',' so that the number of commas
+				 * equals the member count. */
+				*p = ',';
+				do {
+					/* NOTE: glibc difference - glibc allows and trims leading
+					 * (but not trailing) space.  We treat this as an error. */
+					/* NOTE: glibc difference - glibc allows consecutive and
+					 * trailing commas, and ignores "empty string" users.  We
+					 * treat this as an error. */
+					if (*p == ',') {
+						++i;
+						*p = 0;	/* nul-terminate each member string. */
+						if (!*++p || (*p == ',') || isspace(*p)) {
+							goto ERR;
+						}
+					}
+				} while (*++p);
+			}
+
+			/* Now align (p+1), rounding up. */
+			/* Assumes sizeof(char **) is a power of 2. */
+			members = (char **)( (((intptr_t) p) + sizeof(char **))
+								 & ~((intptr_t)(sizeof(char **) - 1)) );
+
+			if (((char *)(members + i)) > end_of_buf) {	/* No space. */
+				break;
+			}
+
+			((struct group *) data)->gr_mem = members;
+
+			if (--i) {
+				p = endptr;	/* Pointing to char prior to first member. */
+				while (1) {
+					*members++ = ++p;
+					if (!--i)
+						break;
+					while (*++p)
+						continue;
+				}
+			}
+			*members = NULL;
+
+			return 0;
+		}
+	} /* while (1) */
+
+ ERR:
+	return -1;
+}
+
+/**********************************************************************/
+
+#if ENABLE_USE_BB_SHADOW
+static const unsigned char sp_off[] ALIGN1 = {
+	offsetof(struct spwd, sp_namp),         /* 0: char* */
+	offsetof(struct spwd, sp_pwdp),         /* 1: char* */
+	offsetof(struct spwd, sp_lstchg),       /* 2: long */
+	offsetof(struct spwd, sp_min),          /* 3: long */
+	offsetof(struct spwd, sp_max),          /* 4: long */
+	offsetof(struct spwd, sp_warn),         /* 5: long */
+	offsetof(struct spwd, sp_inact),        /* 6: long */
+	offsetof(struct spwd, sp_expire),       /* 7: long */
+	offsetof(struct spwd, sp_flag)          /* 8: unsigned long */
+};
+
+static int FAST_FUNC bb__parsespent(void *data, char *line)
+{
+	char *endptr;
+	char *p;
+	int i;
+
+	i = 0;
+	while (1) {
+		p = (char *) data + sp_off[i];
+		if (i < 2) {
+			*((char **) p) = line;
+			line = strchr(line, ':');
+			if (!line) {
+				break; /* error */
+			}
+		} else {
+			*((long *) p) = strtoul(line, &endptr, 10);
+			if (endptr == line) {
+				*((long *) p) = -1L;
+			}
+			line = endptr;
+			if (i == 8) {
+				if (*line != '\0') {
+					break; /* error */
+				}
+				return 0; /* all ok */
+			}
+			if (*line != ':') {
+				break; /* error */
+			}
+		}
+		*line++ = '\0';
+		i++;
+	}
+
+	return EINVAL;
+}
+#endif
+
+/**********************************************************************/
+
+/* Reads until EOF, or until it finds a line which fits in the buffer
+ * and for which the parser function succeeds.
+ *
+ * Returns 0 on success and ENOENT for end-of-file (glibc convention).
+ */
+static int bb__pgsreader(
+		int FAST_FUNC (*parserfunc)(void *d, char *line),
+		void *data,
+		char *__restrict line_buff,
+		size_t buflen,
+		FILE *f)
+{
+	int skip;
+	int rv = ERANGE;
+
+	if (buflen < PWD_BUFFER_SIZE) {
+		errno = rv;
+		return rv;
+	}
+
+	skip = 0;
+	while (1) {
+		if (!fgets(line_buff, buflen, f)) {
+			if (feof(f)) {
+				rv = ENOENT;
+			}
+			break;
+		}
+
+		{
+			int line_len = strlen(line_buff) - 1;
+			if (line_len >= 0 && line_buff[line_len] == '\n') {
+				line_buff[line_len] = '\0';
+			} else
+			if (line_len + 2 == buflen) {
+				/* A start (or continuation) of overlong line */
+				skip = 1;
+				continue;
+			} /* else: a last line in the file, and it has no '\n' */
+		}
+
+		if (skip) {
+			/* This "line" is a remainder of overlong line, ignore */
+			skip = 0;
+			continue;
+		}
+
+		/* NOTE: glibc difference - glibc strips leading whitespace from
+		 * records.  We do not allow leading whitespace. */
+
+		/* Skip empty lines, comment lines, and lines with leading
+		 * whitespace. */
+		if (line_buff[0] != '\0' && line_buff[0] != '#' && !isspace(line_buff[0])) {
+			if (parserfunc == bb__parsegrent) {
+				/* Do evil group hack:
+				 * The group entry parsing function needs to know where
+				 * the end of the buffer is so that it can construct the
+				 * group member ptr table. */
+				((struct group *) data)->gr_name = line_buff + buflen;
+			}
+			if (parserfunc(data, line_buff) == 0) {
+				rv = 0;
+				break;
+			}
+		}
+	} /* while (1) */
+
+	return rv;
+}
diff --git a/busybox-1.19.3/libpwdgrp/pwd_grp_internal.c b/busybox-1.19.3/libpwdgrp/pwd_grp_internal.c
new file mode 100644
index 0000000..d6483be
--- /dev/null
+++ b/busybox-1.19.3/libpwdgrp/pwd_grp_internal.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright (C) 2003     Manuel Novoa III
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Nov 6, 2003  Initial version.
+ *
+ * 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:
+ *    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
+#error GETXXKEY_R_FUNC is not defined!
+#endif
+
+int GETXXKEY_R_FUNC(GETXXKEY_R_KEYTYPE key,
+				GETXXKEY_R_ENTTYPE *__restrict resultbuf,
+				char *__restrict buffer, size_t buflen,
+				GETXXKEY_R_ENTTYPE **__restrict result)
+{
+	FILE *stream;
+	int rv;
+
+	*result = NULL;
+
+	stream = fopen_for_read(GETXXKEY_R_PATHNAME);
+	if (!stream)
+		return errno;
+	while (1) {
+		rv = bb__pgsreader(GETXXKEY_R_PARSER, resultbuf, buffer, buflen, stream);
+		if (!rv) {
+			if (GETXXKEY_R_TEST(resultbuf)) { /* found key? */
+				*result = resultbuf;
+				break;
+			}
+		} else {
+			if (rv == ENOENT) {  /* EOF encountered */
+				rv = 0;
+			}
+			break;
+		}
+	}
+	fclose(stream);
+
+	return rv;
+}
+
+#undef GETXXKEY_R_FUNC
+#undef GETXXKEY_R_PARSER
+#undef GETXXKEY_R_ENTTYPE
+#undef GETXXKEY_R_TEST
+#undef GETXXKEY_R_KEYTYPE
+#undef GETXXKEY_R_PATHNAME
diff --git a/busybox-1.19.3/libpwdgrp/uidgid_get.c b/busybox-1.19.3/libpwdgrp/uidgid_get.c
new file mode 100644
index 0000000..8388be0
--- /dev/null
+++ b/busybox-1.19.3/libpwdgrp/uidgid_get.c
@@ -0,0 +1,135 @@
+/*
+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.
+*/
+
+#include "libbb.h"
+
+/* Always sets uid and gid */
+int FAST_FUNC get_uidgid(struct bb_uidgid_t *u, const char *ug, int numeric_ok)
+{
+	struct passwd *pwd;
+	struct group *gr;
+	char *user, *group;
+	unsigned n;
+
+	user = (char*)ug;
+	group = strchr(ug, ':');
+	if (group) {
+		int sz = (++group) - ug;
+		user = alloca(sz);
+		/* copies sz-1 bytes, stores terminating '\0' */
+		safe_strncpy(user, ug, sz);
+	}
+	if (numeric_ok) {
+		n = bb_strtou(user, NULL, 10);
+		if (!errno) {
+			u->uid = n;
+			pwd = getpwuid(n);
+			/* If we have e.g. "500" string without user */
+			/* with uid 500 in /etc/passwd, we set gid == uid */
+			u->gid = pwd ? pwd->pw_gid : n;
+			goto skip;
+		}
+	}
+	/* Either it is not numeric, or caller disallows numeric username */
+	pwd = getpwnam(user);
+	if (!pwd)
+		return 0;
+	u->uid = pwd->pw_uid;
+	u->gid = pwd->pw_gid;
+
+ skip:
+	if (group) {
+		if (numeric_ok) {
+			n = bb_strtou(group, NULL, 10);
+			if (!errno) {
+				u->gid = n;
+				return 1;
+			}
+		}
+		gr = getgrnam(group);
+		if (!gr)
+			return 0;
+		u->gid = gr->gr_gid;
+	}
+	return 1;
+}
+void FAST_FUNC xget_uidgid(struct bb_uidgid_t *u, const char *ug)
+{
+	if (!get_uidgid(u, ug, 1))
+		bb_error_msg_and_die("unknown user/group %s", ug);
+}
+
+/* chown-like:
+ * "user" sets uid only,
+ * ":group" sets gid only
+ * "user:" sets uid and gid (to user's primary group id)
+ * "user:group" sets uid and gid
+ * ('unset' uid or gid retains the value it has on entry)
+ */
+void FAST_FUNC parse_chown_usergroup_or_die(struct bb_uidgid_t *u, char *user_group)
+{
+	char *group;
+
+	/* Check if there is a group name */
+	group = strchr(user_group, '.'); /* deprecated? */
+	if (!group)
+		group = strchr(user_group, ':');
+	else
+		*group = ':'; /* replace '.' with ':' */
+
+	/* Parse "user[:[group]]" */
+	if (!group) { /* "user" */
+		u->uid = get_ug_id(user_group, xuname2uid);
+	} else if (group == user_group) { /* ":group" */
+		u->gid = get_ug_id(group + 1, xgroup2gid);
+	} else {
+		if (!group[1]) /* "user:" */
+			*group = '\0';
+		xget_uidgid(u, user_group);
+	}
+}
+
+#if 0
+#include <stdio.h>
+int main()
+{
+	unsigned u;
+	struct bb_uidgid_t ug;
+	u = get_uidgid(&ug, "apache", 0);
+	printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+	ug.uid = ug.gid = 1111;
+	u = get_uidgid(&ug, "apache", 0);
+	printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+	ug.uid = ug.gid = 1111;
+	u = get_uidgid(&ug, "apache:users", 0);
+	printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+	ug.uid = ug.gid = 1111;
+	u = get_uidgid(&ug, "apache:users", 0);
+	printf("%u = %u:%u\n", u, ug.uid, ug.gid);
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/loginutils/Config.src b/busybox-1.19.3/loginutils/Config.src
new file mode 100644
index 0000000..0d7f50c
--- /dev/null
+++ b/busybox-1.19.3/loginutils/Config.src
@@ -0,0 +1,313 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Login/Password Management Utilities"
+
+INSERT
+
+config FEATURE_SHADOWPASSWDS
+	bool "Support for shadow passwords"
+	default y
+	help
+	  Build support for shadow password in /etc/shadow. This file is only
+	  readable by root and thus the encrypted passwords are no longer
+	  publicly readable.
+
+config USE_BB_PWD_GRP
+	bool "Use internal password and group functions rather than system functions"
+	default y
+	help
+	  If you leave this disabled, busybox will use the system's password
+	  and group functions. And if you are using the GNU C library
+	  (glibc), you will then need to install the /etc/nsswitch.conf
+	  configuration file and the required /lib/libnss_* libraries in
+	  order for the password and group functions to work. This generally
+	  makes your embedded system quite a bit larger.
+
+	  Enabling this option will cause busybox to directly access the
+	  system's /etc/password, /etc/group files (and your system will be
+	  smaller, and I will get fewer emails asking about how glibc NSS
+	  works). When this option is enabled, you will not be able to use
+	  PAM to access remote LDAP password servers and whatnot. And if you
+	  want hostname resolution to work with glibc, you still need the
+	  /lib/libnss_* libraries.
+
+	  If you need to use glibc's nsswitch.conf mechanism
+	  (e.g. if user/group database is NOT stored in /etc/passwd etc),
+	  you must NOT use this option.
+
+	  If you enable this option, it will add about 1.5k.
+
+config USE_BB_SHADOW
+	bool "Use internal shadow password functions"
+	default y
+	depends on USE_BB_PWD_GRP && FEATURE_SHADOWPASSWDS
+	help
+	  If you leave this disabled, busybox will use the system's shadow
+	  password handling functions. And if you are using the GNU C library
+	  (glibc), you will then need to install the /etc/nsswitch.conf
+	  configuration file and the required /lib/libnss_* libraries in
+	  order for the shadow password functions to work. This generally
+	  makes your embedded system quite a bit larger.
+
+	  Enabling this option will cause busybox to directly access the
+	  system's /etc/shadow file when handling shadow passwords. This
+	  makes your system smaller (and I will get fewer emails asking about
+	  how glibc NSS works). When this option is enabled, you will not be
+	  able to use PAM to access shadow passwords from remote LDAP
+	  password servers and whatnot.
+
+config USE_BB_CRYPT
+	bool "Use internal crypt functions"
+	default y
+	help
+	  Busybox has internal DES and MD5 crypt functions.
+	  They produce results which are identical to corresponding
+	  standard C library functions.
+
+	  If you leave this disabled, busybox will use the system's
+	  crypt functions. Most C libraries use large (~70k)
+	  static buffers there, and also combine them with more general
+	  DES encryption/decryption.
+
+	  For busybox, having large static buffers is undesirable,
+	  especially on NOMMU machines. Busybox also doesn't need
+	  DES encryption/decryption and can do with smaller code.
+
+	  If you enable this option, it will add about 4.8k of code
+	  if you are building dynamically linked executable.
+	  In static build, it makes code _smaller_ by about 1.2k,
+	  and likely many kilobytes less of bss.
+
+config USE_BB_CRYPT_SHA
+	bool "Enable SHA256/512 crypt functions"
+	default y
+	depends on USE_BB_CRYPT
+	help
+	  Enable this if you have passwords starting with "$5$" or "$6$"
+	  in your /etc/passwd or /etc/shadow files. These passwords
+	  are hashed using SHA256 and SHA512 algorithms. Support for them
+	  was added to glibc in 2008.
+	  With this option off, login will fail password check for any
+	  user which has password encrypted with these algorithms.
+
+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 FEATURE_CHECK_NAMES
+	bool "Enable sanity check on user/group names in adduser and addgroup"
+	default n
+	depends on ADDUSER || ADDGROUP
+	help
+	  Enable sanity check on user and group names in adduser and addgroup.
+	  To avoid problems, the user or group name should consist only of
+	  letters, digits, underscores, periods, at signs and dashes,
+	  and not start with a dash (as defined by IEEE Std 1003.1-2001).
+	  For compatibility with Samba machine accounts "$" is also supported
+	  at the end of the user or group name.
+
+config FIRST_SYSTEM_ID
+	int "First valid system uid or gid for adduser and addgroup"
+	depends on ADDUSER || ADDGROUP
+	range 0 64900
+	default 100
+	help
+	  First valid system uid or gid for adduser and addgroup
+
+config LAST_SYSTEM_ID
+	int "Last valid system uid or gid for adduser and addgroup"
+	depends on ADDUSER || ADDGROUP
+	range 0 64900
+	default 999
+	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.
+
+	  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_SYSLOG
+	help
+	  login is used when signing onto a system.
+
+	  Note that Busybox binary must be setuid root for this applet to
+	  work properly.
+
+config PAM
+	bool "Support for PAM (Pluggable Authentication Modules)"
+	default n
+	depends on LOGIN
+	help
+	  Use PAM in login(1) instead of direct access to password database.
+
+config LOGIN_SCRIPTS
+	bool "Support for login scripts"
+	depends on LOGIN
+	default y
+	help
+	  Enable this if you want login to execute $LOGIN_PRE_SUID_SCRIPT
+	  just prior to switching from root to logged-in user.
+
+config FEATURE_NOLOGIN
+	bool "Support for /etc/nologin"
+	default y
+	depends on LOGIN
+	help
+	  The file /etc/nologin is used by (some versions of) login(1).
+	  If it exists, non-root logins are prohibited.
+
+config FEATURE_SECURETTY
+	bool "Support for /etc/securetty"
+	default y
+	depends on LOGIN
+	help
+	  The file /etc/securetty is used by (some versions of) login(1).
+	  The file contains the device names of tty lines (one per line,
+	  without leading /dev/) on which root is allowed to login.
+
+config PASSWD
+	bool "passwd"
+	default y
+	select FEATURE_SYSLOG
+	help
+	  passwd changes passwords for user and group accounts. A normal user
+	  may only change the password for his/her own account, the super user
+	  may change the password for any account. The administrator of a group
+	  may change the password for the group.
+
+	  Note that Busybox binary must be setuid root for this applet to
+	  work properly.
+
+config FEATURE_PASSWD_WEAK_CHECK
+	bool "Check new passwords for weakness"
+	default y
+	depends on PASSWD
+	help
+	  With this option passwd will refuse new passwords which are "weak".
+
+config CRYPTPW
+	bool "cryptpw"
+	default y
+	help
+	  Encrypts the given password with the crypt(3) libc function
+	  using the given salt. Debian has this utility under mkpasswd
+	  name. Busybox provides mkpasswd as an alias for cryptpw.
+
+config CHPASSWD
+	bool "chpasswd"
+	default y
+	help
+	  Reads a file of user name and password pairs from standard input
+	  and uses this information to update a group of existing users.
+
+config SU
+	bool "su"
+	default y
+	select FEATURE_SYSLOG
+	help
+	  su is used to become another user during a login session.
+	  Invoked without a username, su defaults to becoming the super user.
+
+	  Note that Busybox binary must be setuid root for this applet to
+	  work properly.
+
+config FEATURE_SU_SYSLOG
+	bool "Enable su to write to syslog"
+	default y
+	depends on SU
+
+config FEATURE_SU_CHECKS_SHELLS
+	bool "Enable su to check user's shell to be listed in /etc/shells"
+	depends on SU
+	default y
+
+config SULOGIN
+	bool "sulogin"
+	default y
+	select FEATURE_SYSLOG
+	help
+	  sulogin is invoked when the system goes into single user
+	  mode (this is done through an entry in inittab).
+
+config VLOCK
+	bool "vlock"
+	default y
+	help
+	  Build the "vlock" applet which allows you to lock (virtual) terminals.
+
+	  Note that Busybox binary must be setuid root for this applet to
+	  work properly.
+
+endmenu
diff --git a/busybox-1.19.3/loginutils/Kbuild.src b/busybox-1.19.3/loginutils/Kbuild.src
new file mode 100644
index 0000000..ef416a7
--- /dev/null
+++ b/busybox-1.19.3/loginutils/Kbuild.src
@@ -0,0 +1,21 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_ADDGROUP)	+= addgroup.o
+lib-$(CONFIG_ADDUSER)	+= adduser.o
+lib-$(CONFIG_CRYPTPW)	+= cryptpw.o
+lib-$(CONFIG_CHPASSWD)	+= chpasswd.o
+lib-$(CONFIG_GETTY)	+= getty.o
+lib-$(CONFIG_LOGIN)	+= login.o
+lib-$(CONFIG_PASSWD)	+= passwd.o
+lib-$(CONFIG_SU)	+= su.o
+lib-$(CONFIG_SULOGIN)	+= sulogin.o
+lib-$(CONFIG_VLOCK)	+= vlock.o
+lib-$(CONFIG_DELUSER)	+= deluser.o
+lib-$(CONFIG_DELGROUP)	+= deluser.o
diff --git a/busybox-1.19.3/loginutils/add-remove-shell.c b/busybox-1.19.3/loginutils/add-remove-shell.c
new file mode 100644
index 0000000..9a14544
--- /dev/null
+++ b/busybox-1.19.3/loginutils/add-remove-shell.c
@@ -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_BIN, BB_SUID_DROP, add_shell   ))
+//applet:IF_REMOVE_SHELL(APPLET_ODDNAME(remove-shell, add_remove_shell, BB_DIR_USR_BIN, 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;
+}
diff --git a/busybox-1.19.3/loginutils/addgroup.c b/busybox-1.19.3/loginutils/addgroup.c
new file mode 100644
index 0000000..b37270f
--- /dev/null
+++ b/busybox-1.19.3/loginutils/addgroup.c
@@ -0,0 +1,182 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * addgroup - add groups to /etc/group and /etc/gshadow
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * 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 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
+#error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
+#endif
+
+#define OPT_GID                       (1 << 0)
+#define OPT_SYSTEM_ACCOUNT            (1 << 1)
+
+/* We assume GID_T_MAX == INT_MAX */
+static void xgroup_study(struct group *g)
+{
+	unsigned max = INT_MAX;
+
+	/* Make sure gr_name is unused */
+	if (getgrnam(g->gr_name)) {
+		bb_error_msg_and_die("%s '%s' in use", "group", g->gr_name);
+		/* these format strings are reused in adduser and addgroup */
+	}
+
+	/* if a specific gid is requested, the --system switch and */
+	/* min and max values are overridden, and the range of valid */
+	/* gid values is set to [0, INT_MAX] */
+	if (!(option_mask32 & OPT_GID)) {
+		if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
+			g->gr_gid = CONFIG_FIRST_SYSTEM_ID;
+			max = CONFIG_LAST_SYSTEM_ID;
+		} else {
+			g->gr_gid = CONFIG_LAST_SYSTEM_ID + 1;
+			max = 64999;
+		}
+	}
+	/* Check if the desired gid is free
+	 * or find the first free one */
+	while (1) {
+		if (!getgrgid(g->gr_gid)) {
+			return; /* found free group: return */
+		}
+		if (option_mask32 & OPT_GID) {
+			/* -g N, cannot pick gid other than N: error */
+			bb_error_msg_and_die("%s '%s' in use", "gid", itoa(g->gr_gid));
+			/* this format strings is reused in adduser and addgroup */
+		}
+		if (g->gr_gid == max) {
+			/* overflowed: error */
+			bb_error_msg_and_die("no %cids left", 'g');
+			/* this format string is reused in adduser and addgroup */
+		}
+		g->gr_gid++;
+	}
+}
+
+/* append a new user to the passwd file */
+static void new_group(char *group, gid_t gid)
+{
+	struct group gr;
+	char *p;
+
+	/* make sure gid and group haven't already been allocated */
+	gr.gr_gid = gid;
+	gr.gr_name = group;
+	xgroup_study(&gr);
+
+	/* add entry to group */
+	p = xasprintf("x:%u:", (unsigned) gr.gr_gid);
+	if (update_passwd(bb_path_group_file, group, p, NULL) < 0)
+		exit(EXIT_FAILURE);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(p);
+#if ENABLE_FEATURE_SHADOWPASSWDS
+	/* /etc/gshadow fields:
+	 * 1. Group name.
+	 * 2. Encrypted password.
+	 *    If set, non-members of the group can join the group
+	 *    by typing the password for that group using the newgrp command.
+	 *    If the value is of this field ! then no user is allowed
+	 *    to access the group using the newgrp command. A value of !!
+	 *    is treated the same as a value of ! only it indicates
+	 *    that a password has never been set before. If the value is null,
+	 *    only group members can log into the group.
+	 * 3. Group administrators (comma delimited list).
+	 *    Group members listed here can add or remove group members
+	 *    using the gpasswd command.
+	 * 4. Group members (comma delimited list).
+	 */
+	/* Ignore errors: if file is missing we assume admin doesn't want it */
+	update_passwd(bb_path_gshadow_file, group, "!::", NULL);
+#endif
+}
+
+#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS
+static const char addgroup_longopts[] ALIGN1 =
+		"gid\0"                 Required_argument "g"
+		"system\0"              No_argument       "S"
+		;
+#endif
+
+/*
+ * addgroup will take a login_name as its first parameter.
+ *
+ * gid can be customized via command-line parameters.
+ * If called with two non-option arguments, addgroup
+ * will add an existing user to an existing group.
+ */
+int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int addgroup_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opts;
+	unsigned gid = 0;
+
+	/* need to be root */
+	if (geteuid()) {
+		bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+	}
+#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS
+	applet_long_options = addgroup_longopts;
+#endif
+	/* Syntax:
+	 *  addgroup group
+	 *  addgroup -g num group
+	 *  addgroup user group
+	 * Check for min, max and missing args */
+	opt_complementary = "-1:?2:g+";
+	opts = getopt32(argv, "g:S", &gid);
+	/* move past the commandline options */
+	argv += optind;
+	//argc -= optind;
+
+#if ENABLE_FEATURE_ADDUSER_TO_GROUP
+	if (argv[1]) {
+		struct group *gr;
+
+		if (opts & OPT_GID) {
+			/* -g was there, but "addgroup -g num user group"
+			 * is a no-no */
+			bb_show_usage();
+		}
+
+		/* check if group and user exist */
+		xuname2uid(argv[0]); /* unknown user: exit */
+		gr = xgetgrnam(argv[1]); /* unknown group: exit */
+		/* check if user is already in this group */
+		for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) {
+			if (!strcmp(argv[0], *(gr->gr_mem))) {
+				/* user is already in group: do nothing */
+				return EXIT_SUCCESS;
+			}
+		}
+		if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) {
+			return EXIT_FAILURE;
+		}
+# if ENABLE_FEATURE_SHADOWPASSWDS
+		update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]);
+# endif
+	} else
+#endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */
+	{
+		die_if_bad_username(argv[0]);
+		new_group(argv[0], gid);
+	}
+	/* Reached only on success */
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/loginutils/adduser.c b/busybox-1.19.3/loginutils/adduser.c
new file mode 100644
index 0000000..1d082c8
--- /dev/null
+++ b/busybox-1.19.3/loginutils/adduser.c
@@ -0,0 +1,268 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * adduser - add users to /etc/passwd and /etc/shadow
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define adduser_trivial_usage
+//usage:       "[OPTIONS] USER"
+//usage:#define adduser_full_usage "\n\n"
+//usage:       "Add a user\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
+#error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
+#endif
+
+/* #define OPT_HOME           (1 << 0) */ /* unused */
+/* #define OPT_GECOS          (1 << 1) */ /* unused */
+#define OPT_SHELL          (1 << 2)
+#define OPT_GID            (1 << 3)
+#define OPT_DONT_SET_PASS  (1 << 4)
+#define OPT_SYSTEM_ACCOUNT (1 << 5)
+#define OPT_DONT_MAKE_HOME (1 << 6)
+#define OPT_UID            (1 << 7)
+
+/* We assume UID_T_MAX == INT_MAX */
+/* remix */
+/* recoded such that the uid may be passed in *p */
+static void passwd_study(struct passwd *p)
+{
+	int max = UINT_MAX;
+
+	if (getpwnam(p->pw_name)) {
+		bb_error_msg_and_die("%s '%s' in use", "user", p->pw_name);
+		/* this format string is reused in adduser and addgroup */
+	}
+
+	if (!(option_mask32 & OPT_UID)) {
+		if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
+			p->pw_uid = CONFIG_FIRST_SYSTEM_ID;
+			max = CONFIG_LAST_SYSTEM_ID;
+		} else {
+			p->pw_uid = CONFIG_LAST_SYSTEM_ID + 1;
+			max = 64999;
+		}
+	}
+	/* check for a free uid (and maybe gid) */
+	while (getpwuid(p->pw_uid) || (p->pw_gid == (gid_t)-1 && getgrgid(p->pw_uid))) {
+		if (option_mask32 & OPT_UID) {
+			/* -u N, cannot pick uid other than N: error */
+			bb_error_msg_and_die("%s '%s' in use", "uid", itoa(p->pw_uid));
+			/* this format string is reused in adduser and addgroup */
+		}
+		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++;
+	}
+
+	if (p->pw_gid == (gid_t)-1) {
+		p->pw_gid = p->pw_uid; /* new gid = uid */
+		if (getgrnam(p->pw_name)) {
+			bb_error_msg_and_die("%s '%s' in use", "group", p->pw_name);
+			/* this format string is reused in adduser and addgroup */
+		}
+	}
+}
+
+static void addgroup_wrapper(struct passwd *p, const char *group_name)
+{
+	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;
+	}
+
+	spawn_and_wait(argv);
+}
+
+static void passwd_wrapper(const char *login_name) NORETURN;
+
+static void passwd_wrapper(const char *login_name)
+{
+	BB_EXECLP("passwd", "passwd", "--", login_name, NULL);
+	bb_error_msg_and_die("can't execute passwd, you must set password manually");
+}
+
+#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
+static const char adduser_longopts[] ALIGN1 =
+		"home\0"                Required_argument "h"
+		"gecos\0"               Required_argument "g"
+		"shell\0"               Required_argument "s"
+		"ingroup\0"             Required_argument "G"
+		"disabled-password\0"   No_argument       "D"
+		"empty-password\0"      No_argument       "D"
+		"system\0"              No_argument       "S"
+		"no-create-home\0"      No_argument       "H"
+		"uid\0"                 Required_argument "u"
+		;
+#endif
+
+/*
+ * adduser will take a login_name as its first parameter.
+ * home, shell, gecos:
+ * can be customized via command-line parameters.
+ */
+int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int adduser_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct passwd pw;
+	const char *usegroup = NULL;
+	char *p;
+	unsigned opts;
+
+#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
+	applet_long_options = adduser_longopts;
+#endif
+
+	/* got root? */
+	if (geteuid()) {
+		bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+	}
+
+	pw.pw_gecos = (char *)"Linux User,,,";
+	/* 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 */
+	/* 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);
+	} else {
+		unsigned uid;
+		opts = getopt32(argv, "h:g:s:G:DSHu:", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup, &uid);
+		if (opts & OPT_UID) {
+			pw.pw_uid = uid;
+		}
+	}
+	argv += optind;
+
+	/* 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 */
+		pw.pw_dir = xasprintf("/home/%s", argv[0]);
+	}
+	pw.pw_passwd = (char *)"x";
+	if (opts & OPT_SYSTEM_ACCOUNT) {
+		if (!usegroup) {
+			usegroup = "nogroup";
+		}
+		if (!(opts & OPT_SHELL)) {
+			pw.pw_shell = (char *) "/bin/false";
+		}
+	}
+	pw.pw_gid = usegroup ? xgroup2gid(usegroup) : -1; /* exits on failure */
+
+	/* make sure everything is kosher and setup uid && maybe gid */
+	passwd_study(&pw);
+
+	p = xasprintf("x:%u:%u:%s:%s:%s",
+			(unsigned) pw.pw_uid, (unsigned) pw.pw_gid,
+			pw.pw_gecos, pw.pw_dir, pw.pw_shell);
+	if (update_passwd(bb_path_passwd_file, pw.pw_name, p, NULL) < 0) {
+		return EXIT_FAILURE;
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(p);
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+	/* /etc/shadow fields:
+	 * 1. username
+	 * 2. encrypted password
+	 * 3. last password change (unix date (unix time/24*60*60))
+	 * 4. minimum days required between password changes
+	 * 5. maximum days password is valid
+	 * 6. days before password is to expire that user is warned
+	 * 7. days after password expires that account is disabled
+	 * 8. unix date when login expires (i.e. when it may no longer be used)
+	 */
+	/* fields:     2 3  4 5     6 78 */
+	p = xasprintf("!:%u:0:99999:7:::", (unsigned)(time(NULL)) / (24*60*60));
+	/* ignore errors: if file is missing we suppose admin doesn't want it */
+	update_passwd(bb_path_shadow_file, pw.pw_name, p, NULL);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(p);
+#endif
+
+	/* add to group */
+	addgroup_wrapper(&pw, usegroup);
+
+	/* clear the umask for this process so it doesn't
+	 * screw up the permissions on the mkdir and chown. */
+	umask(0);
+	if (!(opts & OPT_DONT_MAKE_HOME)) {
+		/* set the owner and group so it is owned by the new user,
+		 * then fix up the permissions to 2755. Can't do it before
+		 * since chown will clear the setgid bit */
+		int mkdir_err = mkdir(pw.pw_dir, 0755);
+		if (mkdir_err == 0) {
+			/* New home. Copy /etc/skel to it */
+			const char *args[] = {
+				"chown",
+				"-R",
+				xasprintf("%u:%u", (int)pw.pw_uid, (int)pw.pw_gid),
+				pw.pw_dir,
+				NULL
+			};
+			/* Be silent on any errors (like: no /etc/skel) */
+			logmode = LOGMODE_NONE;
+			copy_file("/etc/skel", pw.pw_dir, FILEUTILS_RECUR);
+			logmode = LOGMODE_STDIO;
+			chown_main(4, (char**)args);
+		}
+		if ((mkdir_err != 0 && errno != EEXIST)
+		 || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid) != 0
+		 || chmod(pw.pw_dir, 02755) != 0 /* set setgid bit on homedir */
+		) {
+			bb_simple_perror_msg(pw.pw_dir);
+		}
+	}
+
+	if (!(opts & OPT_DONT_SET_PASS)) {
+		/* interactively set passwd */
+		passwd_wrapper(pw.pw_name);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/loginutils/chpasswd.c b/busybox-1.19.3/loginutils/chpasswd.c
new file mode 100644
index 0000000..b7df57e
--- /dev/null
+++ b/busybox-1.19.3/loginutils/chpasswd.c
@@ -0,0 +1,93 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chpasswd.c
+ *
+ * Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org>
+ * 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:	)
+
+#if ENABLE_LONG_OPTS
+static const char chpasswd_longopts[] ALIGN1 =
+	"encrypted\0" No_argument "e"
+	"md5\0"       No_argument "m"
+	;
+#endif
+
+#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;
+	int opt;
+
+	if (getuid() != 0)
+		bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+	opt_complementary = "m--e:e--m";
+	IF_LONG_OPTS(applet_long_options = chpasswd_longopts;)
+	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");
+		*pass++ = '\0';
+
+		xuname2uid(name); /* dies if there is no such user */
+
+		free_me = NULL;
+		if (!(opt & OPT_ENC)) {
+			char salt[sizeof("$N$XXXXXXXX")];
+
+			crypt_make_salt(salt, 1);
+			if (opt & OPT_MD5) {
+				salt[0] = '$';
+				salt[1] = '1';
+				salt[2] = '$';
+				crypt_make_salt(salt + 3, 4);
+			}
+			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) /* 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 */
+		logmode = LOGMODE_BOTH;
+		if (rc < 0)
+			bb_error_msg_and_die("an error occurred updating password for %s", name);
+		if (rc)
+			bb_info_msg("Password for '%s' changed", name);
+		logmode = LOGMODE_STDIO;
+		free(name);
+		free(free_me);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/loginutils/cryptpw.c b/busybox-1.19.3/loginutils/cryptpw.c
new file mode 100644
index 0000000..b244f55
--- /dev/null
+++ b/busybox-1.19.3/loginutils/cryptpw.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cryptpw.c - output a crypt(3)ed password to stdout.
+ *
+ * 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 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:
+
+NAME
+    mkpasswd - Overfeatured front end to crypt(3)
+SYNOPSIS
+    mkpasswd PASSWORD SALT
+...
+OPTIONS
+-S, --salt=STRING
+    Use the STRING as salt. It must not  contain  prefixes  such  as
+    $1$.
+-R, --rounds=NUMBER
+    Use NUMBER rounds. This argument is ignored if the method
+    choosen does not support variable rounds. For the OpenBSD Blowfish
+    method this is the logarithm of the number of rounds.
+-m, --method=TYPE
+    Compute the password using the TYPE method. If TYPE is 'help'
+    then the available methods are printed.
+-P, --password-fd=NUM
+    Read the password from file descriptor NUM instead of using getpass(3).
+    If the file descriptor is not connected to a tty then
+    no other message than the hashed password is printed on stdout.
+-s, --stdin
+    Like --password-fd=0.
+ENVIRONMENT
+    $MKPASSWD_OPTIONS
+    A list of options which will be evaluated before the ones
+    specified on the command line.
+BUGS
+    This programs suffers of a bad case of featuritis.
+    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Very true...
+
+cryptpw was in bbox before this gem, so we retain it, and alias mkpasswd
+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)
+{
+	char salt[MAX_PW_SALT_LEN];
+	char *salt_ptr;
+	const char *opt_m, *opt_S;
+	int fd;
+
+#if ENABLE_LONG_OPTS
+	static const char mkpasswd_longopts[] ALIGN1 =
+		"stdin\0"       No_argument       "s"
+		"password-fd\0" Required_argument "P"
+		"salt\0"        Required_argument "S"
+		"method\0"      Required_argument "m"
+	;
+	applet_long_options = mkpasswd_longopts;
+#endif
+	fd = STDIN_FILENO;
+	opt_m = "d";
+	opt_S = NULL;
+	/* at most two non-option arguments; -P NUM */
+	opt_complementary = "?2:P+";
+	getopt32(argv, "sP:S:m:a:", &fd, &opt_S, &opt_m, &opt_m);
+	argv += optind;
+
+	/* have no idea how to handle -s... */
+
+	if (argv[0] && !opt_S)
+		opt_S = argv[1];
+
+	salt_ptr = crypt_make_pw_salt(salt, opt_m);
+	if (opt_S)
+		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')
+			? bb_ask_stdin("Password: ")
+			: xmalloc_fgetline(stdin)
+		),
+		salt, 1));
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/loginutils/deluser.c b/busybox-1.19.3/loginutils/deluser.c
new file mode 100644
index 0000000..e39ac55
--- /dev/null
+++ b/busybox-1.19.3/loginutils/deluser.c
@@ -0,0 +1,115 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * deluser/delgroup implementation for busybox
+ *
+ * Copyright (C) 1999 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ *
+ */
+
+//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)
+{
+	/* 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() != 0)
+		bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+	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) {
+				pfile = sfile;
+				sfile = NULL;
+			}
+		} 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;
+	}
+	/* Reached only if number of command line args is wrong */
+	bb_show_usage();
+}
diff --git a/busybox-1.19.3/loginutils/getty.c b/busybox-1.19.3/loginutils/getty.c
new file mode 100644
index 0000000..6245665
--- /dev/null
+++ b/busybox-1.19.3/loginutils/getty.c
@@ -0,0 +1,657 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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.
+ *
+ * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
+ * - Added Native Language Support
+ *
+ * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
+ * - 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 source tree.
+ */
+
+#include "libbb.h"
+#include <syslog.h>
+#ifndef IUCLC
+# define IUCLC 0
+#endif
+
+#ifndef LOGIN_PROCESS
+# undef ENABLE_FEATURE_UTMP
+# undef ENABLE_FEATURE_WTMP
+# define ENABLE_FEATURE_UTMP 0
+# define ENABLE_FEATURE_WTMP 0
+#endif
+
+
+/* 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
+
+
+/*
+ * Things you may want to modify.
+ *
+ * You may disagree with the default line-editing etc. characters defined
+ * below. Note, however, that DEL cannot be used for interrupt generation
+ * and for line editing at the same time.
+ */
+#undef  _PATH_LOGIN
+#define _PATH_LOGIN "/bin/login"
+
+/* 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"
+
+/* Some shorthands for control characters */
+#define CTL(x)          ((x) ^ 0100)    /* Assumes ASCII dialect */
+#define BS              CTL('H')        /* back space */
+#define DEL             CTL('?')        /* delete */
+
+/* Defaults for line-editing etc. characters; you may want to change this */
+#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 (none) */
+
+/*
+ * 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 */
+
+struct globals {
+	unsigned timeout;               /* time-out period */
+	const char *login;              /* login program */
+	const char *fakehost;
+	const char *tty;                /* name of tty */
+	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 termios;         /* terminal mode bits */
+	char line_buf[128];
+};
+
+#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 a tty, prompt for a 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 */
+#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! */
+	if (value < 0) /* bad terminating char, overflow, etc */
+		return value;
+	return tty_value_to_baud(value);
+}
+
+/* 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) {
+		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 */
+		G.numspeed++;
+		if (G.numspeed > MAX_SPEED)
+			bb_error_msg_and_die("too many alternate speeds");
+	}
+	debug("exiting parse_speeds\n");
+}
+
+/* parse command-line arguments */
+static void parse_args(char **argv)
+{
+	char *ts;
+	int flags;
+
+	opt_complementary = "-2:t+"; /* at least 2 args; -t N */
+	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);
+	}
+	argv += optind;
+	debug("after getopt\n");
+
+	/* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
+	G.tty = argv[0];        /* tty name */
+	ts = argv[1];           /* baud rate(s) */
+	if (isdigit(argv[0][0])) {
+		/* A number first, assume it's a speed (BSD style) */
+		G.tty = ts;     /* tty name is in argv[1] */
+		ts = argv[0];   /* baud rate(s) */
+	}
+	parse_speeds(ts);
+	applet_name = xasprintf("getty: %s", G.tty);
+
+	if (argv[2])
+		xsetenv("TERM", argv[2]);
+
+	debug("exiting parse_args\n");
+}
+
+/* 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(G.tty)) {
+		if (G.tty[0] != '/')
+			G.tty = xasprintf("/dev/%s", G.tty); /* will leak it */
+
+		/* Open the tty as standard input */
+		debug("open(2)\n");
+		close(0);
+		xopen(G.tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */
+
+		/* Set proper protections and ownership */
+		fchown(0, 0, 0);        /* 0:0 */
+		fchmod(0, 0620);        /* crw--w---- */
+	} else {
+		/*
+		 * 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_RDONLY|O_WRONLY)) != O_RDWR)
+			bb_error_msg_and_die("stdin is not open for read/write");
+	}
+}
+
+static void set_termios(void)
+{
+	if (tcsetattr_stdin_TCSANOW(&G.termios) < 0)
+		bb_perror_msg_and_die("tcsetattr");
+}
+
+/* We manipulate termios this way:
+ * - first, we read existing termios settings
+ * - termios_init modifies some parts and sets it
+ * - auto_baud and/or BREAK processing can set different speed and set termios
+ * - termios_final again modifies some parts and sets termios before
+ *   execing login
+ */
+static void termios_init(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);
+	signal(SIGALRM, SIG_DFL); /* do not break -t TIMEOUT! */
+
+	/* 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.termios, speed);
+
+	/* Initial termios 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.
+	 */
+	/* Clear all bits except: */
+	G.termios.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
+		| CBAUD   /* (output) baud rate */
+#ifdef CBAUDEX
+		| CBAUDEX /* (output) baud rate */
+#endif
+#ifdef CIBAUD
+		| CIBAUD   /* input baud rate */
+#endif
+	);
+	/* Set: 8 bits; hang up (drop DTR) on last close; enable receive */
+	G.termios.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.termios.c_cflag |= CLOCAL;
+	}
+#ifdef CRTSCTS
+	if (option_mask32 & F_RTSCTS)
+		G.termios.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */
+#endif
+	G.termios.c_iflag = 0;
+	G.termios.c_lflag = 0;
+	/* non-raw output; add CR to each NL */
+	G.termios.c_oflag = OPOST | ONLCR;
+
+	G.termios.c_cc[VMIN] = 1; /* block reads if < 1 char is available */
+	G.termios.c_cc[VTIME] = 0; /* no timeout (reads block forever) */
+#ifdef __linux__
+	G.termios.c_line = 0;
+#endif
+
+	set_termios();
+
+	debug("term_io 2\n");
+}
+
+static void termios_final(void)
+{
+	/* software flow control on output (stop sending if XOFF is recvd);
+	 * and on input (send XOFF when buffer is full)
+	 */
+	G.termios.c_iflag |= IXON | IXOFF;
+	if (G.eol == '\r') {
+		G.termios.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.termios.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.termios.c_cc[VINTR] = DEF_INTR;
+	G.termios.c_cc[VQUIT] = DEF_QUIT;
+	G.termios.c_cc[VEOF] = DEF_EOF;
+	G.termios.c_cc[VEOL] = DEF_EOL;
+#ifdef VSWTC
+	G.termios.c_cc[VSWTC] = DEF_SWITCH;
+#endif
+#ifdef VSWTCH
+	G.termios.c_cc[VSWTCH] = DEF_SWITCH;
+#endif
+	G.termios.c_cc[VKILL] = DEF_KILL;
+	/* 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_termios();
+}
+
+/* extract baud rate from modem status message */
+static void auto_baud(void)
+{
+	int nread;
+
+	/*
+	 * This works only if the modem produces its status code AFTER raising
+	 * the DCD line, and if the computer is fast enough to set the proper
+	 * baud rate before the message has gone by. We expect a message of the
+	 * following format:
+	 *
+	 * <junk><number><junk>
+	 *
+	 * The number is interpreted as the baud rate of the incoming call. If the
+	 * modem does not tell us the baud rate within one second, we will keep
+	 * using the current baud rate. It is advisable to enable BREAK
+	 * processing (comma-separated list of baud rates) if the processing of
+	 * modem status messages is enabled.
+	 */
+
+	G.termios.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */
+	set_termios();
+
+	/*
+	 * 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, G.line_buf, sizeof(G.line_buf) - 1);
+	if (nread > 0) {
+		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)
+					cfsetspeed(&G.termios, speed);
+				break;
+			}
+		}
+	}
+
+	/* Restore terminal settings */
+	G.termios.c_cc[VMIN] = 1; /* restore to value set by termios_init */
+	set_termios();
+}
+
+/* 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;
+
+	/* Flush pending input (esp. after parsing or switching the baud rate) */
+	usleep(100*1000); /* 0.1 sec */
+	tcflush(STDIN_FILENO, TCIFLUSH);
+
+	/* Prompt for and read a login name */
+	G.line_buf[0] = '\0';
+	while (!G.line_buf[0]) {
+		/* Write issue file and prompt */
+#ifdef ISSUE
+		if (!(option_mask32 & F_NOISSUE))
+			print_login_issue(G.issue, G.tty);
+#endif
+		print_login_prompt();
+
+		/* Read name, watch for break, parity, erase, kill, end-of-line */
+		bp = G.line_buf;
+		G.eol = '\0';
+		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) {
+				if (errno == EINTR || errno == EIO)
+					exit(EXIT_SUCCESS);
+				bb_perror_msg_and_die(bb_msg_read_error);
+			}
+
+			/* BREAK. If we have speeds to try,
+			 * return NULL (will switch speeds and return here) */
+			if (c == '\0' && G.numspeed > 1)
+				return NULL;
+
+			/* Do erase, kill and end-of-line processing */
+			switch (c) {
+			case '\r':
+			case '\n':
+				*bp = '\0';
+				G.eol = c;
+				goto got_logname;
+			case BS:
+			case DEL:
+				G.termios.c_cc[VERASE] = c;
+				if (bp > G.line_buf) {
+					full_write(STDOUT_FILENO, "\010 \010", 3);
+					bp--;
+				}
+				break;
+			case CTL('U'):
+				while (bp > G.line_buf) {
+					full_write(STDOUT_FILENO, "\010 \010", 3);
+					bp--;
+				}
+				break;
+			case CTL('D'):
+				exit(EXIT_SUCCESS);
+			default:
+				if ((unsigned char)c < ' ') {
+					/* ignore garbage characters */
+				} 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;
+			}
+		} /* end of get char loop */
+ got_logname: ;
+	} /* while logname is empty */
+
+	return G.line_buf;
+}
+
+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 *logname;
+
+	INIT_G();
+	G.login = _PATH_LOGIN;    /* default login program */
+#ifdef ISSUE
+	G.issue = ISSUE;          /* default issue file */
+#endif
+	G.eol = '\r';
+
+	/* Parse command-line arguments */
+	parse_args(argv);
+
+	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 */
+	n = xopen(bb_dev_null, O_RDWR);
+	/* dup2(n, 0); - no, we need to handle "getty - 9600" too */
+	xdup2(n, 1);
+	xdup2(n, 2);
+	while (n > 2)
+		close(n--);
+
+	/* Logging. We want special flavor of error_msg_and_die */
+	die_sleep = 10;
+	msg_eol = "\r\n";
+	/* most likely will internally use fd #3 in CLOEXEC mode: */
+	openlog(applet_name, LOG_PID, LOG_AUTH);
+	logmode = LOGMODE_BOTH;
+
+#ifdef DEBUGGING
+	dbf = xfopen_for_write(DEBUGTERM);
+	for (n = 1; argv[n]; n++) {
+		debug(argv[n]);
+		debug("\n");
+	}
+#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();
+	ndelay_off(0);
+	debug("duping\n");
+	xdup2(0, 1);
+	xdup2(0, 2);
+
+	/*
+	 * The following ioctl will fail if stdin is not a tty, but also when
+	 * there is noise on the modem control lines. In the latter case, the
+	 * common course of action is (1) fix your cables (2) give the modem more
+	 * time to properly reset after hanging up. SunOS users can achieve (2)
+	 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
+	 * 5 seconds seems to be a good value.
+	 */
+	if (tcgetattr(STDIN_FILENO, &G.termios) < 0)
+		bb_perror_msg_and_die("tcgetattr");
+
+	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(STDIN_FILENO, TIOCSCTTY, (void*)1);
+	/* Make ourself a foreground process group within our session */
+	tcsetpgrp(STDIN_FILENO, pid);
+#endif
+
+	/* Update the utmp file. This tty is ours now! */
+	update_utmp(pid, LOGIN_PROCESS, G.tty, "LOGIN", G.fakehost);
+
+	/* Initialize the termios settings (raw mode, eight-bit, blocking i/o) */
+	debug("calling termios_init\n");
+	termios_init(G.speeds[0]);
+
+	/* Write the modem init string and DON'T flush the buffers */
+	if (option_mask32 & F_INITSTRING) {
+		debug("writing init string\n");
+		full_write1_str(G.initstring);
+	}
+
+	/* Optionally detect the baud rate from the modem status message */
+	debug("before autobaud\n");
+	if (option_mask32 & F_PARSE)
+		auto_baud();
+
+	/* Set the optional timer */
+	alarm(G.timeout); /* if 0, alarm is not set */
+//BUG: death by signal won't restore termios
+
+	/* Optionally wait for CR or LF before writing /etc/issue */
+	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);
+			if (ch == '\n' || ch == '\r')
+				break;
+		}
+	}
+
+	logname = NULL;
+	if (!(option_mask32 & F_NOPROMPT)) {
+		/* NB: termios_init already set line speed
+		 * to G.speeds[0] */
+		int baud_index = 0;
+
+		while (1) {
+			/* Read the login name */
+			debug("reading login name\n");
+			logname = get_logname();
+			if (logname)
+				break;
+			/* We are here only if G.numspeed > 1 */
+			baud_index = (baud_index + 1) % G.numspeed;
+			cfsetspeed(&G.termios, G.speeds[baud_index]);
+			set_termios();
+		}
+	}
+
+	/* Disable timer */
+	alarm(0);
+
+	/* Finalize the termios settings */
+	termios_final();
+
+	/* Now the newline character should be properly written */
+	full_write(STDOUT_FILENO, "\n", 1);
+
+	/* 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 */
+	/* With -n, logname == NULL, and login will ask for username instead */
+	BB_EXECLP(G.login, G.login, "--", logname, NULL);
+	bb_error_msg_and_die("can't execute '%s'", G.login);
+}
diff --git a/busybox-1.19.3/loginutils/login.c b/busybox-1.19.3/loginutils/login.c
new file mode 100644
index 0000000..2f7b9b2
--- /dev/null
+++ b/busybox-1.19.3/loginutils/login.c
@@ -0,0 +1,461 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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>
+#include <sys/resource.h>
+
+#if ENABLE_SELINUX
+# include <selinux/selinux.h>  /* for is_selinux_enabled()  */
+# include <selinux/get_context_list.h> /* for get_default_context() */
+# include <selinux/flask.h> /* for security class definitions  */
+#endif
+
+#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>
+static const struct pam_conv conv = {
+	misc_conv,
+	NULL
+};
+#endif
+
+enum {
+	TIMEOUT = 60,
+	EMPTY_USERNAME_COUNT = 10,
+	USERNAME_SIZE = 32,
+	TTYNAME_SIZE = 32,
+};
+
+static char* short_tty;
+
+#if ENABLE_FEATURE_NOLOGIN
+static void die_if_nologin(void)
+{
+	FILE *fp;
+	int c;
+	int empty = 1;
+
+	fp = fopen_for_read("/etc/nologin");
+	if (!fp) /* assuming it does not exist */
+		return;
+
+	while ((c = getc(fp)) != EOF) {
+		if (c == '\n')
+			bb_putchar('\r');
+		bb_putchar(c);
+		empty = 0;
+	}
+	if (empty)
+		puts("\r\nSystem closed for routine maintenance\r");
+
+	fclose(fp);
+	fflush_all();
+	/* Users say that they do need this prior to exit: */
+	tcdrain(STDOUT_FILENO);
+	exit(EXIT_FAILURE);
+}
+#else
+# define die_if_nologin() ((void)0)
+#endif
+
+#if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM
+static int check_securetty(void)
+{
+	char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */
+	parser_t *parser = config_open2("/etc/securetty", fopen_for_read);
+	while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) {
+		if (strcmp(buf, short_tty) == 0)
+			break;
+		buf = NULL;
+	}
+	config_close(parser);
+	/* buf != NULL here if config file was not found, empty
+	 * or line was found which equals short_tty */
+	return buf != NULL;
+}
+#else
+static ALWAYS_INLINE int check_securetty(void) { return 1; }
+#endif
+
+#if ENABLE_SELINUX
+static void initselinux(char *username, char *full_tty,
+						security_context_t *user_sid)
+{
+	security_context_t old_tty_sid, new_tty_sid;
+
+	if (!is_selinux_enabled())
+		return;
+
+	if (get_default_context(username, NULL, user_sid)) {
+		bb_error_msg_and_die("can't get SID for %s", username);
+	}
+	if (getfilecon(full_tty, &old_tty_sid) < 0) {
+		bb_perror_msg_and_die("getfilecon(%s) failed", full_tty);
+	}
+	if (security_compute_relabel(*user_sid, old_tty_sid,
+				SECCLASS_CHR_FILE, &new_tty_sid) != 0) {
+		bb_perror_msg_and_die("security_change_sid(%s) failed", full_tty);
+	}
+	if (setfilecon(full_tty, new_tty_sid) != 0) {
+		bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid);
+	}
+}
+#endif
+
+#if ENABLE_LOGIN_SCRIPTS
+static void run_login_script(struct passwd *pw, char *full_tty)
+{
+	char *t_argv[2];
+
+	t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT");
+	if (t_argv[0]) {
+		t_argv[1] = NULL;
+		xsetenv("LOGIN_TTY", full_tty);
+		xsetenv("LOGIN_USER", pw->pw_name);
+		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 */
+		unsetenv("LOGIN_TTY");
+		unsetenv("LOGIN_USER");
+		unsetenv("LOGIN_UID");
+		unsetenv("LOGIN_GID");
+		unsetenv("LOGIN_SHELL");
+	}
+}
+#else
+void run_login_script(struct passwd *pw, char *full_tty);
+#endif
+
+static void get_username_or_die(char *buf, int size_buf)
+{
+	int c, cntdown;
+
+	cntdown = EMPTY_USERNAME_COUNT;
+ prompt:
+	print_login_prompt();
+	/* skip whitespace */
+	do {
+		c = getchar();
+		if (c == EOF)
+			exit(EXIT_FAILURE);
+		if (c == '\n') {
+			if (!--cntdown)
+				exit(EXIT_FAILURE);
+			goto prompt;
+		}
+	} while (isspace(c)); /* maybe isblank? */
+
+	*buf++ = c;
+	if (!fgets(buf, size_buf-2, stdin))
+		exit(EXIT_FAILURE);
+	if (!strchr(buf, '\n'))
+		exit(EXIT_FAILURE);
+	while ((unsigned char)*buf > ' ')
+		buf++;
+	*buf = '\0';
+}
+
+static void motd(void)
+{
+	int fd;
+
+	fd = open(bb_path_motd_file, O_RDONLY);
+	if (fd >= 0) {
+		fflush_all();
+		bb_copyfd_eof(fd, STDOUT_FILENO);
+		close(fd);
+	}
+}
+
+static void alarm_handler(int sig UNUSED_PARAM)
+{
+	/* 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);
+	fflush_all();
+	/* unix API is brain damaged regarding O_NONBLOCK,
+	 * we should undo it, or else we can affect other processes */
+	ndelay_off(1);
+	_exit(EXIT_SUCCESS);
+}
+
+int login_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int login_main(int argc UNUSED_PARAM, char **argv)
+{
+	enum {
+		LOGIN_OPT_f = (1<<0),
+		LOGIN_OPT_h = (1<<1),
+		LOGIN_OPT_p = (1<<2),
+	};
+	char *fromhost;
+	char username[USERNAME_SIZE];
+	int run_by_root;
+	unsigned opt;
+	int count = 0;
+	struct passwd *pw;
+	char *opt_host = NULL;
+	char *opt_user = opt_user; /* for compiler */
+	char *full_tty;
+	IF_SELINUX(security_context_t user_sid = NULL;)
+#if ENABLE_PAM
+	int pamret;
+	pam_handle_t *pamh;
+	const char *pamuser;
+	const char *failed_msg;
+	struct passwd pwdstruct;
+	char pwdbuf[256];
+	char **pamenv;
+#endif
+
+	username[0] = '\0';
+	signal(SIGALRM, alarm_handler);
+	alarm(TIMEOUT);
+
+	/* More of suid paranoia if called by non-root: */
+	/* Clear dangerous stuff, set PATH */
+	run_by_root = !sanitize_env_if_suid();
+
+	/* Mandatory paranoia for suid applet:
+	 * ensure that fd# 0,1,2 are opened (at least to /dev/null)
+	 * and any extra open fd's are closed.
+	 * (The name of the function is misleading. Not daemonizing here.) */
+	bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL);
+
+	opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
+	if (opt & LOGIN_OPT_f) {
+		if (!run_by_root)
+			bb_error_msg_and_die("-f is for root only");
+		safe_strncpy(username, opt_user, sizeof(username));
+	}
+	argv += optind;
+	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 */
+	full_tty = xmalloc_ttyname(STDIN_FILENO);
+	if (!full_tty)
+		full_tty = xstrdup("UNKNOWN");
+	short_tty = skip_dev_pfx(full_tty);
+
+	if (opt_host) {
+		fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host);
+	} else {
+		fromhost = xasprintf(" on '%s'", short_tty);
+	}
+
+	/* Was breaking "login <username>" from shell command line: */
+	/*bb_setpgrp();*/
+
+	openlog(applet_name, LOG_PID | LOG_CONS, LOG_AUTH);
+
+	while (1) {
+		/* flush away any type-ahead (as getty does) */
+		tcflush(0, TCIFLUSH);
+
+		if (!username[0])
+			get_username_or_die(username, sizeof(username));
+
+#if ENABLE_PAM
+		pamret = pam_start("login", username, &conv, &pamh);
+		if (pamret != PAM_SUCCESS) {
+			failed_msg = "start";
+			goto pam_auth_failed;
+		}
+		/* set TTY (so things like securetty work) */
+		pamret = pam_set_item(pamh, PAM_TTY, short_tty);
+		if (pamret != PAM_SUCCESS) {
+			failed_msg = "set_item(TTY)";
+			goto pam_auth_failed;
+		}
+		/* 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;
+			}
+		}
+		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);
+		if (pamret != PAM_SUCCESS) {
+			failed_msg = "acct_mgmt";
+			goto pam_auth_failed;
+		}
+		/* read user back */
+		pamuser = NULL;
+		/* gcc: "dereferencing type-punned pointer breaks aliasing rules..."
+		 * thus we cast to (void*) */
+		if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) {
+			failed_msg = "get_item(USER)";
+			goto pam_auth_failed;
+		}
+		if (!pamuser || !pamuser[0])
+			goto auth_failed;
+		safe_strncpy(username, pamuser, sizeof(username));
+		/* Don't use "pw = getpwnam(username);",
+		 * PAM is said to be capable of destroying static storage
+		 * used by getpwnam(). We are using safe(r) function */
+		pw = NULL;
+		getpwnam_r(username, &pwdstruct, pwdbuf, sizeof(pwdbuf), &pw);
+		if (!pw)
+			goto auth_failed;
+		pamret = pam_open_session(pamh, 0);
+		if (pamret != PAM_SUCCESS) {
+			failed_msg = "open_session";
+			goto pam_auth_failed;
+		}
+		pamret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+		if (pamret != PAM_SUCCESS) {
+			failed_msg = "setcred";
+			goto pam_auth_failed;
+		}
+		break; /* success, continue login process */
+
+ pam_auth_failed:
+		/* syslog, because we don't want potential attacker
+		 * to know _why_ login failed */
+		syslog(LOG_WARNING, "pam_%s call failed: %s (%d)", failed_msg,
+					pam_strerror(pamh, pamret), pamret);
+		safe_strncpy(username, "UNKNOWN", sizeof(username));
+#else /* not PAM */
+		pw = getpwnam(username);
+		if (!pw) {
+			strcpy(username, "UNKNOWN");
+			goto fake_it;
+		}
+
+		if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
+			goto auth_failed;
+
+		if (opt & LOGIN_OPT_f)
+			break; /* -f USER: success without asking passwd */
+
+		if (pw->pw_uid == 0 && !check_securetty())
+			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))
+			break;
+#endif /* ENABLE_PAM */
+ auth_failed:
+		opt &= ~LOGIN_OPT_f;
+		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';
+	} /* while (1) */
+
+	alarm(0);
+	/* We can ignore /etc/nologin if we are logging in as root,
+	 * it doesn't matter whether we are run by root or not */
+	if (pw->pw_uid != 0)
+		die_if_nologin();
+
+	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); */
+	fchown(0, pw->pw_uid, pw->pw_gid);
+	fchmod(0, 0600);
+
+	update_utmp(getpid(), USER_PROCESS, short_tty, username, run_by_root ? opt_host : NULL);
+
+	/* We trust environment only if we run by root */
+	if (ENABLE_LOGIN_SCRIPTS && run_by_root)
+		run_login_script(pw, full_tty);
+
+	change_identity(pw);
+	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);)
+
+	// util-linux login also does:
+	// /* start new session */
+	// setsid();
+	// /* TIOCSCTTY: steal tty from other process group */
+	// if (ioctl(0, TIOCSCTTY, 1)) error_msg...
+	// BBox login used to do this (see above):
+	// bb_setpgrp();
+	// If this stuff is really needed, add it and explain why!
+
+	/* Set signals to defaults */
+	/* Non-ignored signals revert to SIG_DFL on exec anyway */
+	/*signal(SIGALRM, SIG_DFL);*/
+
+	/* Is this correct? This way user can ctrl-c out of /etc/profile,
+	 * potentially creating security breach (tested with bash 3.0).
+	 * But without this, bash 3.0 will not enable ctrl-c either.
+	 * Maybe bash is buggy?
+	 * Need to find out what standards say about /bin/login -
+	 * should we leave SIGINT etc enabled or disabled? */
+	signal(SIGINT, SIG_DFL);
+
+	/* Exec login shell with no additional parameters */
+	run_shell(pw->pw_shell, 1, NULL, NULL);
+
+	/* return EXIT_FAILURE; - not reached */
+}
diff --git a/busybox-1.19.3/loginutils/passwd.c b/busybox-1.19.3/loginutils/passwd.c
new file mode 100644
index 0000000..1cfafae
--- /dev/null
+++ b/busybox-1.19.3/loginutils/passwd.c
@@ -0,0 +1,225 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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>
+
+static void nuke_str(char *str)
+{
+	if (str) memset(str, 0, strlen(str));
+}
+
+static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo)
+{
+	char salt[MAX_PW_SALT_LEN];
+	char *orig = (char*)"";
+	char *newp = NULL;
+	char *cp = NULL;
+	char *ret = NULL; /* failure so far */
+
+	if (myuid != 0 && pw->pw_passwd[0]) {
+		char *encrypted;
+
+		orig = bb_ask_stdin("Old password: "); /* returns ptr to static */
+		if (!orig)
+			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(LOGIN_FAIL_DELAY);
+			puts("Incorrect password");
+			goto err_ret;
+		}
+		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 */
+	if (!newp)
+		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 != 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) != 0) {
+		puts("Passwords don't match");
+		goto err_ret;
+	}
+
+	crypt_make_pw_salt(salt, algo);
+
+	/* pw_encrypt returns malloced str */
+	ret = pw_encrypt(newp, salt, 1);
+	/* whee, success! */
+
+ 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;
+}
+
+int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int passwd_main(int argc UNUSED_PARAM, char **argv)
+{
+	enum {
+		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 = "d"; /* des */
+	const char *filename;
+	char *myname;
+	char *name;
+	char *newp;
+	struct passwd *pw;
+	uid_t myuid;
+	struct rlimit rlimit_fsize;
+	char c;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+	/* Using _r function to avoid pulling in static buffers */
+	struct spwd spw;
+	char buffer[256];
+#endif
+
+	logmode = LOGMODE_BOTH;
+	openlog(applet_name, 0, LOG_AUTH);
+	opt = getopt32(argv, "a:lud", &opt_a);
+	//argc -= optind;
+	argv += optind;
+
+	myuid = getuid();
+	/* -l, -u, -d require root priv and username argument */
+	if ((opt & OPT_lud) && (myuid != 0 || !argv[0]))
+		bb_show_usage();
+
+	/* Will complain and die if username not found */
+	myname = xstrdup(xuid2uname(myuid));
+	name = argv[0] ? argv[0] : myname;
+
+	pw = xgetpwnam(name);
+	if (myuid != 0 && pw->pw_uid != myuid) {
+		/* LOGMODE_BOTH */
+		bb_error_msg_and_die("%s can't change password for %s", myname, name);
+	}
+
+#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;
+		errno = 0;
+		if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0
+		 || !result /* no error, but no record found either */
+		 || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */
+		) {
+			if (errno != ENOENT) {
+				/* LOGMODE_BOTH */
+				bb_perror_msg("no record of %s in %s, using %s",
+					name, bb_path_shadow_file,
+					bb_path_passwd_file);
+			}
+			/* else: /etc/shadow does not exist,
+			 * apparently we are on a shadow-less system,
+			 * no surprise there */
+		} else {
+			pw->pw_passwd = result->sp_pwdp;
+		}
+	}
+#endif
+
+	/* Decide what the new password will be */
+	newp = NULL;
+	c = pw->pw_passwd[0] - '!';
+	if (!(opt & OPT_lud)) {
+		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_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 '!' */
+		newp = xasprintf("!%s", pw->pw_passwd);
+	} else if (opt & OPT_unlock) {
+		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 = (char*)"";
+	}
+
+	rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
+	setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
+	bb_signals(0
+		+ (1 << SIGHUP)
+		+ (1 << SIGINT)
+		+ (1 << SIGQUIT)
+		, SIG_IGN);
+	umask(077);
+	xsetuid(0);
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+	filename = bb_path_shadow_file;
+	rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
+	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;
+		rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
+	}
+	/* LOGMODE_BOTH */
+	if (rc < 0)
+		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); - 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);
+	return 0;
+}
diff --git a/busybox-1.19.3/loginutils/su.c b/busybox-1.19.3/loginutils/su.c
new file mode 100644
index 0000000..57ea738
--- /dev/null
+++ b/busybox-1.19.3/loginutils/su.c
@@ -0,0 +1,142 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini su implementation for busybox
+ *
+ * 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.  */
+static int restricted_shell(const char *shell)
+{
+	char *line;
+	int result = 1;
+
+	/*setusershell(); - getusershell does it itself*/
+	while ((line = getusershell()) != NULL) {
+		if (/* *line != '#' && */ strcmp(line, shell) == 0) {
+			result = 0;
+			break;
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		endusershell();
+	return result;
+}
+#endif
+
+#define SU_OPT_mp (3)
+#define SU_OPT_l  (4)
+
+int su_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int su_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned flags;
+	char *opt_shell = NULL;
+	char *opt_command = NULL;
+	const char *opt_username = "root";
+	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);
+	//argc -= optind;
+	argv += optind;
+
+	if (argv[0] && LONE_DASH(argv[0])) {
+		flags |= SU_OPT_l;
+		argv++;
+	}
+
+	/* get user if specified */
+	if (argv[0]) {
+		opt_username = argv[0];
+		argv++;
+	}
+
+	if (ENABLE_FEATURE_SU_SYSLOG) {
+		/* The utmp entry (via getlogin) is probably the best way to
+		 * identify the user, especially if someone su's from a su-shell.
+		 * But getlogin can fail -- usually due to lack of utmp entry.
+		 * in this case resort to getpwuid.  */
+#if ENABLE_FEATURE_UTMP
+		old_user = user_buf;
+		if (getlogin_r(user_buf, sizeof(user_buf)) != 0)
+#endif
+		{
+			pw = getpwuid(cur_uid);
+			old_user = pw ? xstrdup(pw->pw_name) : "";
+		}
+		tty = xmalloc_ttyname(2);
+		if (!tty) {
+			tty = "none";
+		}
+		openlog(applet_name, 0, LOG_AUTH);
+	}
+
+	pw = xgetpwnam(opt_username);
+
+	if (cur_uid == 0 || correct_password(pw)) {
+		if (ENABLE_FEATURE_SU_SYSLOG)
+			syslog(LOG_NOTICE, "%c %s %s:%s",
+				'+', tty, old_user, opt_username);
+	} else {
+		if (ENABLE_FEATURE_SU_SYSLOG)
+			syslog(LOG_NOTICE, "%c %s %s:%s",
+				'-', tty, old_user, opt_username);
+		bb_error_msg_and_die("incorrect password");
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) {
+		closelog();
+	}
+
+	if (!opt_shell && (flags & SU_OPT_mp)) {
+		/* -s SHELL is not given, but "preserve env" opt is */
+		opt_shell = getenv("SHELL");
+	}
+
+#if ENABLE_FEATURE_SU_CHECKS_SHELLS
+	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; /* 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. */
+#endif
+	if (!opt_shell)
+		opt_shell = pw->pw_shell;
+
+	change_identity(pw);
+	setup_environment(opt_shell,
+			((flags & SU_OPT_l) / SU_OPT_l * SETUP_ENV_CLEARENV)
+			+ (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV),
+			pw);
+	IF_SELINUX(set_current_security_context(NULL);)
+
+	/* Never returns */
+	run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
+
+	/* return EXIT_FAILURE; - not reached */
+}
diff --git a/busybox-1.19.3/loginutils/sulogin.c b/busybox-1.19.3/loginutils/sulogin.c
new file mode 100644
index 0000000..bd2b09e
--- /dev/null
+++ b/busybox-1.19.3/loginutils/sulogin.c
@@ -0,0 +1,118 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini sulogin implementation for busybox
+ *
+ * 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);
+
+	opt_complementary = "t+"; /* -t N */
+	getopt32(argv, "t:", &timeout);
+	argv += optind;
+
+	if (argv[0]) {
+		close(0);
+		close(1);
+		dup(xopen(argv[0], O_RDWR));
+		close(2);
+		dup(0);
+	}
+
+	/* Malicious use like "sulogin /dev/sda"? */
+	if (!isatty(0) || !isatty(1) || !isatty(2)) {
+		logmode = LOGMODE_SYSLOG;
+		bb_error_msg_and_die("not a tty");
+	}
+
+	/* Clear dangerous stuff, set PATH */
+	sanitize_env_if_suid();
+
+	pwd = getpwuid(0);
+	if (!pwd) {
+		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) {
+			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) {
+			break;
+		}
+		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");
+
+	IF_SELINUX(renew_current_security_context());
+
+	shell = getenv("SUSHELL");
+	if (!shell)
+		shell = getenv("sushell");
+	if (!shell)
+		shell = pwd->pw_shell;
+
+	/* Exec login shell with no additional parameters. Never returns. */
+	run_shell(shell, 1, NULL, NULL);
+
+ auth_error:
+	bb_error_msg_and_die("no password entry for root");
+}
diff --git a/busybox-1.19.3/loginutils/vlock.c b/busybox-1.19.3/loginutils/vlock.c
new file mode 100644
index 0000000..75af939
--- /dev/null
+++ b/busybox-1.19.3/loginutils/vlock.c
@@ -0,0 +1,119 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * vlock implementation for busybox
+ *
+ * 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 source tree.
+ */
+
+/* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the
+ * original vlock.  I snagged a bunch of his code to write this
+ * minimalistic vlock.
+ */
+/* Fixed by Erik Andersen to do passwords the tinylogin way...
+ * It now works with md5, sha1, etc passwords. */
+
+//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:
+	 * "no, kernel, we don't allow console switch away from us!" */
+	ioctl(STDIN_FILENO, VT_RELDISP, (unsigned long) !option_mask32);
+}
+
+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 passwd *pw;
+
+	pw = xgetpwuid(getuid());
+	opt_complementary = "=0"; /* no params! */
+	getopt32(argv, "a");
+
+	/* Ignore some signals so that we don't get killed by them */
+	bb_signals(0
+		+ (1 << SIGTSTP)
+		+ (1 << SIGTTIN)
+		+ (1 << SIGTTOU)
+		+ (1 << SIGHUP )
+		+ (1 << SIGCHLD) /* paranoia :) */
+		+ (1 << SIGQUIT)
+		+ (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);
+	signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt);
+	/* 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!" */
+	vtm.mode = VT_PROCESS;
+	vtm.relsig = SIGUSR1;
+	vtm.acqsig = SIGUSR2;
+	ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
+#endif
+
+	tcgetattr(STDIN_FILENO, &oterm);
+	term = oterm;
+	term.c_iflag &= ~BRKINT;
+	term.c_iflag |= IGNBRK;
+	term.c_lflag &= ~ISIG;
+	term.c_lflag &= ~(ECHO | ECHOCTL);
+	tcsetattr_stdin_TCSANOW(&term);
+
+	while (1) {
+		printf("Virtual console%s locked by %s.\n",
+				/* "s" if -a, else "": */ "s" + !option_mask32,
+				pw->pw_name
+		);
+		if (correct_password(pw)) {
+			break;
+		}
+		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);
+}
diff --git a/busybox-1.19.3/mailutils/Config.src b/busybox-1.19.3/mailutils/Config.src
new file mode 100644
index 0000000..2a9c5c0
--- /dev/null
+++ b/busybox-1.19.3/mailutils/Config.src
@@ -0,0 +1,55 @@
+menu "Mail Utilities"
+
+INSERT
+
+config MAKEMIME
+	bool "makemime"
+	default y
+	help
+	  Create MIME-formatted messages.
+
+config FEATURE_MIME_CHARSET
+	string "Default charset"
+	default "us-ascii"
+	depends on MAKEMIME || REFORMIME || SENDMAIL
+	help
+	  Default charset of the message.
+
+config POPMAILDIR
+	bool "popmaildir"
+	default y
+	help
+	  Simple yet powerful POP3 mail popper. Delivers content
+	  of remote mailboxes to local Maildir.
+
+config FEATURE_POPMAILDIR_DELIVERY
+	bool "Allow message filters and custom delivery program"
+	default y
+	depends on POPMAILDIR
+	help
+	  Allow to use a custom program to filter the content
+	  of the message before actual delivery (-F "prog [args...]").
+	  Allow to use a custom program for message actual delivery
+	  (-M "prog [args...]").
+
+config REFORMIME
+	bool "reformime"
+	default y
+	help
+	  Parse MIME-formatted messages.
+
+config FEATURE_REFORMIME_COMPAT
+	bool "Accept and ignore options other than -x and -X"
+	default y
+	depends on REFORMIME
+	help
+	  Accept (for compatibility only) and ignore options
+	  other than -x and -X.
+
+config SENDMAIL
+	bool "sendmail"
+	default y
+	help
+	  Barebones sendmail.
+
+endmenu
diff --git a/busybox-1.19.3/mailutils/Kbuild.src b/busybox-1.19.3/mailutils/Kbuild.src
new file mode 100644
index 0000000..6b4fb74
--- /dev/null
+++ b/busybox-1.19.3/mailutils/Kbuild.src
@@ -0,0 +1,9 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
diff --git a/busybox-1.19.3/mailutils/mail.c b/busybox-1.19.3/mailutils/mail.c
new file mode 100644
index 0000000..66c7947
--- /dev/null
+++ b/busybox-1.19.3/mailutils/mail.c
@@ -0,0 +1,180 @@
+/* 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.
+ */
+#include "libbb.h"
+#include "mail.h"
+
+static void kill_helper(void)
+{
+	if (G.helper_pid > 0) {
+		kill(G.helper_pid, SIGTERM);
+		G.helper_pid = 0;
+	}
+}
+
+// generic signal handler
+static void signal_handler(int signo)
+{
+#define err signo
+	if (SIGALRM == signo) {
+		kill_helper();
+		bb_error_msg_and_die("timed out");
+	}
+
+	// SIGCHLD. reap zombies
+	if (safe_waitpid(G.helper_pid, &err, WNOHANG) > 0) {
+		if (WIFSIGNALED(err))
+			bb_error_msg_and_die("helper killed by signal %u", WTERMSIG(err));
+		if (WIFEXITED(err)) {
+			G.helper_pid = 0;
+			if (WEXITSTATUS(err))
+				bb_error_msg_and_die("helper exited (%u)", WEXITSTATUS(err));
+		}
+	}
+#undef err
+}
+
+void FAST_FUNC launch_helper(const char **argv)
+{
+	// setup vanilla unidirectional pipes interchange
+	int i;
+	int pipes[4];
+
+	xpipe(pipes);
+	xpipe(pipes + 2);
+
+	// NB: handler must be installed before vfork
+	bb_signals(0
+		+ (1 << SIGCHLD)
+		+ (1 << SIGALRM)
+		, signal_handler);
+
+	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
+
+	if (!G.helper_pid) {
+		// child: try to execute connection helper
+		// NB: SIGCHLD & SIGALRM revert to SIG_DFL on exec
+		BB_EXECVP_or_die((char**)argv);
+	}
+
+	// parent
+	// check whether child is alive
+	//redundant:signal_handler(SIGCHLD);
+	// child seems OK -> parent goes on
+	atexit(kill_helper);
+}
+
+char* FAST_FUNC send_mail_command(const char *fmt, const char *param)
+{
+	char *msg;
+	if (timeout)
+		alarm(timeout);
+	msg = (char*)fmt;
+	if (fmt) {
+		msg = xasprintf(fmt, param);
+		if (verbose)
+			bb_error_msg("send:'%s'", msg);
+		printf("%s\r\n", msg);
+	}
+	fflush_all();
+	return msg;
+}
+
+// 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)
+{
+	// parse [user[:pass]@]host
+	// return host
+	char *s = strchr(url, '@');
+	*user = *pass = NULL;
+	if (s) {
+		*s++ = '\0';
+		*user = url;
+		url = s;
+		s = strchr(*user, ':');
+		if (s) {
+			*s++ = '\0';
+			*pass = s;
+		}
+	}
+	return url;
+}
+*/
+
+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 */
+		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 = src;
+	} else if (text) {
+		// though we do not call uuencode(NULL, NULL) explicitly
+		// still we do not want to break things suddenly
+		len = strlen(text);
+	} else
+		return;
+
+	while (1) {
+		size_t size;
+		if (fname) {
+			size = fread((char *)src_buf, 1, SRC_BUF_SIZE, fp);
+			if ((ssize_t)size < 0)
+				bb_perror_msg_and_die(bb_msg_read_error);
+		} else {
+			size = len;
+			if (len > SRC_BUF_SIZE)
+				size = SRC_BUF_SIZE;
+		}
+		if (!size)
+			break;
+		// encode the buffer we just read in
+		bb_uuencode(dst_buf, src_buf, size, bb_uuenc_tbl_base64);
+		if (fname) {
+			printf("%s\n", eol);
+		} else {
+			src_buf += size;
+			len -= size;
+		}
+		fwrite(dst_buf, 1, 4 * ((size + 2) / 3), stdout);
+	}
+	if (fname && NOT_LONE_DASH(fname))
+		fclose(fp);
+#undef src_buf
+}
+
+/*
+ * get username and password from a file descriptor
+ */
+void FAST_FUNC get_cred_or_die(int fd)
+{
+	if (isatty(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, /* 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");
+}
diff --git a/busybox-1.19.3/mailutils/mail.h b/busybox-1.19.3/mailutils/mail.h
new file mode 100644
index 0000000..d1d7830
--- /dev/null
+++ b/busybox-1.19.3/mailutils/mail.h
@@ -0,0 +1,44 @@
+/* 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 launch_helper(const char **argv) FAST_FUNC;
+void get_cred_or_die(int fd) FAST_FUNC;
+
+char *send_mail_command(const char *fmt, const char *param) FAST_FUNC;
+
+void encode_base64(char *fname, const char *text, const char *eol) FAST_FUNC;
diff --git a/busybox-1.19.3/mailutils/makemime.c b/busybox-1.19.3/mailutils/makemime.c
new file mode 100644
index 0000000..4dc53a3
--- /dev/null
+++ b/busybox-1.19.3/mailutils/makemime.c
@@ -0,0 +1,217 @@
+/* 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 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. Examples:"
+//usage:     "\n		\"From: user@host.org\", \"Date: `date -R`\""
+//usage:     "\n	-c CT	Content type. Default: text/plain"
+//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"
+
+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,         // 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:",
+		&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
+}
diff --git a/busybox-1.19.3/mailutils/popmaildir.c b/busybox-1.19.3/mailutils/popmaildir.c
new file mode 100644
index 0000000..6203033
--- /dev/null
+++ b/busybox-1.19.3/mailutils/popmaildir.c
@@ -0,0 +1,271 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * popmaildir: a simple yet powerful POP3 client
+ * Delivers contents of remote mailboxes to local Maildir
+ *
+ * Inspired by original utility by Nikola Vladov
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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)
+{
+	char *msg = send_mail_command(fmt, param);
+	char *answer = xmalloc_fgetline(stdin);
+	if (answer && '+' == answer[0]) {
+		free(msg);
+		if (timeout)
+			alarm(0);
+		if (ret) {
+			// skip "+OK "
+			memmove(answer, answer + 4, strlen(answer) - 4);
+			*ret = answer;
+		} else
+			free(answer);
+		return;
+	}
+	bb_error_msg_and_die("%s failed, reply was: %s", msg, answer);
+}
+
+static void pop3_check(const char *fmt, const char *param)
+{
+	pop3_checkr(fmt, param, NULL);
+}
+
+int popmaildir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int popmaildir_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *buf;
+	unsigned nmsg;
+	char *hostname;
+	pid_t pid;
+	const char *retr;
+#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
+	const char *delivery;
+#endif
+	unsigned opt_nlines = 0;
+
+	enum {
+		OPT_b = 1 << 0,		// -b binary mode. Ignored
+		OPT_d = 1 << 1,		// -d,-dd,-ddd debug. Ignored
+		OPT_m = 1 << 2,		// -m show used memory. Ignored
+		OPT_V = 1 << 3,		// -V version. Ignored
+		OPT_c = 1 << 4,		// -c use tcpclient. Ignored
+		OPT_a = 1 << 5,		// -a use APOP protocol
+		OPT_s = 1 << 6,		// -s skip authorization
+		OPT_T = 1 << 7,		// -T get messages with TOP instead with RETR
+		OPT_k = 1 << 8,		// -k keep retrieved messages on the server
+		OPT_t = 1 << 9,		// -t90 set timeout to 90 sec
+		OPT_R = 1 << 10,	// -R20000 remove old messages on the server >= 20000 bytes (requires -k). Ignored
+		OPT_Z = 1 << 11,	// -Z11-23 remove messages from 11 to 23 (dangerous). Ignored
+		OPT_L = 1 << 12,	// -L50000 not retrieve new messages >= 50000 bytes. Ignored
+		OPT_H = 1 << 13,	// -H30 type first 30 lines of a message; (-L12000 -H30). Ignored
+		OPT_M = 1 << 14,	// -M\"program arg1 arg2 ...\"; deliver by program. Treated like -F
+		OPT_F = 1 << 15,	// -F\"program arg1 arg2 ...\"; filter by program. Treated like -M
+	};
+
+	// init global variables
+	INIT_G();
+
+	// parse options
+	opt_complementary = "-1:dd:t+:R+:L+:H+";
+	opts = getopt32(argv,
+		"bdmVcasTkt:" "R:Z:L:H:" IF_FEATURE_POPMAILDIR_DELIVERY("M:F:"),
+		&timeout, NULL, NULL, NULL, &opt_nlines
+		IF_FEATURE_POPMAILDIR_DELIVERY(, &delivery, &delivery) // we treat -M and -F the same
+	);
+	//argc -= optind;
+	argv += optind;
+
+	// get auth info
+	if (!(opts & OPT_s))
+		get_cred_or_die(STDIN_FILENO);
+
+	// goto maildir
+	xchdir(*argv++);
+
+	// launch connect helper, if any
+	if (*argv)
+		launch_helper((const char **)argv);
+
+	// get server greeting
+	pop3_checkr(NULL, NULL, &buf);
+
+	// authenticate (if no -s given)
+	if (!(opts & OPT_s)) {
+		// server supports APOP and we want it?
+		if ('<' == buf[0] && (opts & OPT_a)) {
+			union { // save a bit of stack
+				md5_ctx_t ctx;
+				char hex[16 * 2 + 1];
+			} md5;
+			uint32_t res[16 / 4];
+
+			char *s = strchr(buf, '>');
+			if (s)
+				s[1] = '\0';
+			// get md5 sum of "<stamp>password" string
+			md5_begin(&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);
+			pop3_check("APOP %s", s);
+			free(s);
+			free(buf);
+		// server ignores APOP -> use simple text authentication
+		} else {
+			// USER
+			pop3_check("USER %s", G.user);
+			// PASS
+			pop3_check("PASS %s", G.pass);
+		}
+	}
+
+	// get mailbox statistics
+	pop3_checkr("STAT", NULL, &buf);
+
+	// prepare message filename suffix
+	hostname = safe_gethostname();
+	pid = getpid();
+
+	// get messages counter
+	// NOTE: we don't use xatou(buf) since buf is "nmsg nbytes"
+	// we only need nmsg and atoi is just exactly what we need
+	// if atoi fails to convert buf into number it returns 0
+	// in this case the following loop simply will not be executed
+	nmsg = atoi(buf);
+	free(buf);
+
+	// loop through messages
+	retr = (opts & OPT_T) ? xasprintf("TOP %%u %u", opt_nlines) : "RETR %u";
+	for (; nmsg; nmsg--) {
+
+		char *filename;
+		char *target;
+		char *answer;
+		FILE *fp;
+#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
+		int rc;
+#endif
+		// generate unique filename
+		filename  = xasprintf("tmp/%llu.%u.%s",
+			monotonic_us(), (unsigned)pid, hostname);
+
+		// retrieve message in ./tmp/ unless filter is specified
+		pop3_check(retr, (const char *)(ptrdiff_t)nmsg);
+
+#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
+		// delivery helper ordered? -> setup pipe
+		if (opts & (OPT_F|OPT_M)) {
+			// helper will have $FILENAME set to filename
+			xsetenv("FILENAME", filename);
+			fp = popen(delivery, "w");
+			unsetenv("FILENAME");
+			if (!fp) {
+				bb_perror_msg("delivery helper");
+				break;
+			}
+		} else
+#endif
+		// create and open file filename
+		fp = xfopen_for_write(filename);
+
+		// copy stdin to fp (either filename or delivery helper)
+		while ((answer = xmalloc_fgets_str(stdin, "\r\n")) != NULL) {
+			char *s = answer;
+			if ('.' == answer[0]) {
+				if ('.' == answer[1])
+					s++;
+				else if ('\r' == answer[1] && '\n' == answer[2] && '\0' == answer[3])
+					break;
+			}
+			//*strchrnul(s, '\r') = '\n';
+			fputs(s, fp);
+			free(answer);
+		}
+
+#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
+		// analyse delivery status
+		if (opts & (OPT_F|OPT_M)) {
+			rc = pclose(fp);
+			if (99 == rc) // 99 means bail out
+				break;
+//			if (rc) // !0 means skip to the next message
+				goto skip;
+//			// 0 means continue
+		} else {
+			// close filename
+			fclose(fp);
+		}
+#endif
+
+		// delete message from server
+		if (!(opts & OPT_k))
+			pop3_check("DELE %u", (const char*)(ptrdiff_t)nmsg);
+
+		// atomically move message to ./new/
+		target = xstrdup(filename);
+		strncpy(target, "new", 3);
+		// ... or just stop receiving on failure
+		if (rename_or_warn(filename, target))
+			break;
+		free(target);
+
+#if ENABLE_FEATURE_POPMAILDIR_DELIVERY
+ skip:
+#endif
+		free(filename);
+	}
+
+	// Bye
+	pop3_check("QUIT", NULL);
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(G.user);
+		free(G.pass);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/mailutils/reformime.c b/busybox-1.19.3/mailutils/reformime.c
new file mode 100644
index 0000000..5e28ef7
--- /dev/null
+++ b/busybox-1.19.3/mailutils/reformime.c
@@ -0,0 +1,279 @@
+/* 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 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);
+}
diff --git a/busybox-1.19.3/mailutils/sendmail.c b/busybox-1.19.3/mailutils/sendmail.c
new file mode 100644
index 0000000..e0aff20
--- /dev/null
+++ b/busybox-1.19.3/mailutils/sendmail.c
@@ -0,0 +1,342 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bare bones sendmail
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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 | -au<username> -ap<password>]"
+//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 | -au<username> -ap<password>]"
+//usage:     "\n	-S HOST[:PORT]	Server"
+//usage:     "\n	-au<username>	Username for AUTH LOGIN"
+//usage:     "\n	-ap<password>	Password for AUTH LOGIN"
+//usage:     "\n	-am<method>	Authentication method. Ignored. LOGIN is implied"
+//usage:     "\n"
+//usage:     "\nOther options are silently ignored; -oi -t is implied"
+//usage:	IF_MAKEMIME(
+//usage:     "\nUse makemime applet to create message with attachments"
+//usage:	)
+
+#include "libbb.h"
+#include "mail.h"
+
+// limit maximum allowed number of headers to prevent overflows.
+// 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;
+	char *msg = send_mail_command(fmt, param);
+	// read stdin
+	// 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) {
+		if (verbose)
+			bb_error_msg("recv:'%.*s' %d", (int)(strchrnul(answer, '\r') - answer), answer, verbose);
+		if (strlen(answer) <= 3 || '-' != answer[3])
+			break;
+		free(answer);
+	}
+	if (answer) {
+		int n = atoi(answer);
+		if (timeout)
+			alarm(0);
+		free(msg);
+		free(answer);
+		if (-1 == code || n == code)
+			return n;
+	}
+	bb_error_msg_and_die("%s failed", msg);
+}
+
+static int smtp_check(const char *fmt, int code)
+{
+	return smtp_checkp(fmt, NULL, code);
+}
+
+// strip argument of bad chars
+static char *sane_address(char *str)
+{
+	char *s = str;
+	char *p = s;
+	while (*s) {
+		if (isalnum(*s) || '_' == *s || '-' == *s || '.' == *s || '@' == *s) {
+			*p++ = *s;
+		}
+		s++;
+	}
+	*p = '\0';
+	return str;
+}
+
+static void rcptto(const char *s)
+{
+	// 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);
+}
+
+int sendmail_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sendmail_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *opt_connect = opt_connect;
+	char *opt_from;
+	char *s;
+	llist_t *list = NULL;
+	char *domain = sane_address(safe_getdomainname());
+	unsigned nheaders = 0;
+	int code;
+
+	enum {
+	//--- standard options
+		OPT_t = 1 << 0,         // read message for recipients, append them to those on cmdline
+		OPT_f = 1 << 1,         // sender address
+		OPT_o = 1 << 2,         // various options. -oi IMPLIED! others are IGNORED!
+		OPT_i = 1 << 3,         // IMPLIED!
+	//--- BB specific options
+		OPT_w = 1 << 4,         // network timeout
+		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
+	INIT_G();
+
+	// save initial stdin since body is piped!
+	xdup2(STDIN_FILENO, 3);
+	G.fp0 = xfdopen_for_read(3);
+
+	// parse options
+	// -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::v", &opt_from, NULL,
+			&timeout, &opt_connect, &opt_connect, &list, &verbose);
+	//argc -= optind;
+	argv += optind;
+
+	// process -a[upm]<token> options
+	if ((opts & OPT_a) && !list)
+		bb_show_usage();
+	while (list) {
+		char *a = (char *) llist_pop(&list);
+		if ('u' == a[0])
+			G.user = xstrdup(a+1);
+		if ('p' == a[0])
+			G.pass = xstrdup(a+1);
+		// N.B. we support only AUTH LOGIN so far
+		//if ('m' == a[0])
+		//	G.method = xstrdup(a+1);
+	}
+	// N.B. list == NULL here
+	//bb_info_msg("OPT[%x] AU[%s], AP[%s], AM[%s], ARGV[%s]", opts, au, ap, am, *argv);
+
+	// connect to server
+
+	// connection helper ordered? ->
+	if (opts & OPT_H) {
+		const char *args[] = { "sh", "-c", opt_connect, NULL };
+		// plug it in
+		launch_helper(args);
+	// vanilla connection
+	} else {
+		int fd;
+		// host[:port] not explicitly specified? -> use $SMTPHOST
+		// no $SMTPHOST ? -> use localhost
+		if (!(opts & OPT_S)) {
+			opt_connect = getenv("SMTPHOST");
+			if (!opt_connect)
+				opt_connect = (char *)"127.0.0.1";
+		}
+		// do connect
+		fd = create_and_connect_stream_or_die(opt_connect, 25);
+		// and make ourselves a simple IO filter
+		xmove_fd(fd, STDIN_FILENO);
+		xdup2(STDIN_FILENO, STDOUT_FILENO);
+	}
+	// 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);
+
+	// perform authentication
+	if (opts & OPT_a) {
+		smtp_check("AUTH LOGIN", 334);
+		// we must read credentials unless they are given via -a[up] options
+		if (!G.user || !G.pass)
+			get_cred_or_die(4);
+		encode_base64(NULL, G.user, NULL);
+		smtp_check("", 334);
+		encode_base64(NULL, G.pass, NULL);
+		smtp_check("", 235);
+	}
+
+	// set sender
+	// N.B. we have here a very loosely defined algotythm
+	// 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.
+	// 2) server can require AUTH ->
+	//	we must provide valid username and password along with a (possibly fake) reply address.
+	//	For the sake of security username and password are to be read either from console or from a secured file.
+	//	Since reading from console may defeat usability, the solution is either to read from a predefined
+	//	file descriptor (e.g. 4), or again from a secured file.
+
+	// got no sender address? -> use system username as a resort
+	// N.B. we marked -f as required option!
+	//if (!G.user) {
+	//	// N.B. IMHO getenv("USER") can be way easily spoofed!
+	//	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
+
+	// read recipients from message and add them to those given on cmdline.
+	// this means we scan stdin for To:, Cc:, Bcc: lines until an empty line
+	// and then use the rest of stdin as message body
+	code = 0; // set "analyze headers" mode
+	while ((s = xmalloc_fgetline(G.fp0)) != NULL) {
+ dump:
+		// put message lines doubling leading dots
+		if (code) {
+			// escape leading dots
+			// N.B. this feature is implied even if no -i (-oi) switch given
+			// N.B. we need to escape the leading dot regardless of
+			// whether it is single or not character on the line
+			if ('.' == s[0] /*&& '\0' == s[1] */)
+				printf(".");
+			// dump read line
+			send_r_n(s);
+			free(s);
+			continue;
+		}
+
+		// analyze headers
+		// To: or Cc: headers add recipients
+		if (0 == strncasecmp("To:", s, 3) || 0 == strncasecmp("Bcc:" + 1, s, 3)) {
+			rcptto(sane_address(s+3));
+			goto addheader;
+		// Bcc: header adds blind copy (hidden) recipient
+		} else if (0 == strncasecmp("Bcc:", s, 4)) {
+			rcptto(sane_address(s+4));
+			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)) {
+ 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 {
+ reenter:
+			// put recipients specified on cmdline
+			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));
+				argv++;
+			}
+			// enter "put message" mode
+			// N.B. DATA fails iff no recipients were accepted (or even provided)
+			// in this case just bail out gracefully
+			if (354 != smtp_check("DATA", -1))
+				goto bail;
+			// dump the headers
+			while (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) {
+				send_r_n("");
+				break;
+			}
+			// go dump message body
+			// N.B. "s" already contains the first non-header line, so pretend we read it from input
+			goto dump;
+		}
+	}
+	// odd case: we didn't stop "analyze headers" mode -> message body is empty. Reenter the loop
+	// N.B. after reenter code will be > 0
+	if (!code)
+		goto reenter;
+
+	// finalize the message
+	smtp_check(".", 250);
+ bail:
+	// ... and say goodbye
+	smtp_check("QUIT", 221);
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP)
+		fclose(G.fp0);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/Config.src b/busybox-1.19.3/miscutils/Config.src
new file mode 100644
index 0000000..b9fc196
--- /dev/null
+++ b/busybox-1.19.3/miscutils/Config.src
@@ -0,0 +1,613 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Miscellaneous Utilities"
+
+INSERT
+
+config ADJTIMEX
+	bool "adjtimex"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Adjtimex reads and optionally sets adjustment parameters for
+	  the Linux clock adjustment algorithm.
+
+config BBCONFIG
+	bool "bbconfig"
+	default n
+	help
+	  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.
+
+config FEATURE_BEEP_FREQ
+	int "default frequency"
+	range 0 2147483647
+	default 4000
+	depends on BEEP
+	help
+	  Frequency for default beep.
+
+config FEATURE_BEEP_LENGTH_MS
+	int "default length"
+	range 0 2147483647
+	default 30
+	depends on BEEP
+	help
+	  Length in ms for default beep.
+
+config CHAT
+	bool "chat"
+	default y
+	help
+	  Simple chat utility.
+
+config FEATURE_CHAT_NOFAIL
+	bool "Enable NOFAIL expect strings"
+	depends on CHAT
+	default y
+	help
+	  When enabled expect strings which are started with a dash trigger
+	  no-fail mode. That is when expectation is not met within timeout
+	  the script is not terminated but sends next SEND string and waits
+	  for next EXPECT string. This allows to compose far more flexible
+	  scripts.
+
+config FEATURE_CHAT_TTY_HIFI
+	bool "Force STDIN to be a TTY"
+	depends on CHAT
+	default n
+	help
+	  Original chat always treats STDIN as a TTY device and sets for it
+	  so-called raw mode. This option turns on such behaviour.
+
+config FEATURE_CHAT_IMPLICIT_CR
+	bool "Enable implicit Carriage Return"
+	depends on CHAT
+	default y
+	help
+	  When enabled make chat to terminate all SEND strings with a "\r"
+	  unless "\c" is met anywhere in the string.
+
+config FEATURE_CHAT_SWALLOW_OPTS
+	bool "Swallow options"
+	depends on CHAT
+	default y
+	help
+	  Busybox chat require no options. To make it not fail when used
+	  in place of original chat (which has a bunch of options) turn
+	  this on.
+
+config FEATURE_CHAT_SEND_ESCAPES
+	bool "Support weird SEND escapes"
+	depends on CHAT
+	default y
+	help
+	  Original chat uses some escape sequences in SEND arguments which
+	  are not sent to device but rather performs special actions.
+	  E.g. "\K" means to send a break sequence to device.
+	  "\d" delays execution for a second, "\p" -- for a 1/100 of second.
+	  Before turning this option on think twice: do you really need them?
+
+config FEATURE_CHAT_VAR_ABORT_LEN
+	bool "Support variable-length ABORT conditions"
+	depends on CHAT
+	default y
+	help
+	  Original chat uses fixed 50-bytes length ABORT conditions. Say N here.
+
+config FEATURE_CHAT_CLR_ABORT
+	bool "Support revoking of ABORT conditions"
+	depends on CHAT
+	default y
+	help
+	  Support CLR_ABORT directive.
+
+config CHRT
+	bool "chrt"
+	default y
+	help
+	  manipulate real-time attributes of a process.
+	  This requires sched_{g,s}etparam support in your libc.
+
+config CROND
+	bool "crond"
+	default y
+	select FEATURE_SYSLOG
+	help
+	  Crond is a background daemon that parses individual crontab
+	  files and executes commands on behalf of the users in question.
+	  This is a port of dcron from slackware. It uses files of the
+	  format /var/spool/cron/crontabs/<username> files, for example:
+	      $ cat /var/spool/cron/crontabs/root
+	      # Run daily cron jobs at 4:40 every day:
+	      40 4 * * * /etc/cron/daily > /dev/null 2>&1
+
+config FEATURE_CROND_D
+	bool "Support option -d to redirect output to stderr"
+	depends on CROND
+	default y
+	help
+	  -d sets loglevel to 0 (most verbose) and directs all output to stderr.
+
+config FEATURE_CROND_CALL_SENDMAIL
+	bool "Report command output via email (using sendmail)"
+	default y
+	depends on CROND
+	help
+	  Command output will be sent to corresponding user via email.
+
+config FEATURE_CROND_DIR
+	string "crond spool directory"
+	default "/var/spool/cron"
+	depends on CROND || CRONTAB
+	help
+	  Location of crond spool.
+
+config CRONTAB
+	bool "crontab"
+	default y
+	help
+	  Crontab manipulates the crontab for a particular user. Only
+	  the superuser may specify a different user and/or crontab directory.
+	  Note that Busybox binary must be setuid root for this applet to
+	  work properly.
+
+config DC
+	bool "dc"
+	default y
+	help
+	  Dc is a reverse-polish desk calculator which supports unlimited
+	  precision arithmetic.
+
+config FEATURE_DC_LIBM
+	bool "Enable power and exp functions (requires libm)"
+	default y
+	depends on DC
+	help
+	  Enable power and exp functions.
+	  NOTE: This will require libm to be present for linking.
+
+config DEVFSD
+	bool "devfsd (obsolete)"
+	default n
+	select PLATFORM_LINUX
+	select FEATURE_SYSLOG
+	help
+	  This is deprecated and should NOT be used anymore.
+	  Use linux >= 2.6 (optionally with hotplug) and mdev instead!
+	  See docs/mdev.txt for detailed instructions on how to use mdev
+	  instead.
+
+	  Provides compatibility with old device names on a devfs systems.
+	  You should set it to true if you have devfs enabled.
+	  The following keywords in devsfd.conf are supported:
+	  "CLEAR_CONFIG", "INCLUDE", "OPTIONAL_INCLUDE", "RESTORE",
+	  "PERMISSIONS", "EXECUTE", "COPY", "IGNORE",
+	  "MKOLDCOMPAT", "MKNEWCOMPAT","RMOLDCOMPAT", "RMNEWCOMPAT".
+
+	  But only if they are written UPPERCASE!!!!!!!!
+
+config DEVFSD_MODLOAD
+	bool "Adds support for MODLOAD keyword in devsfd.conf"
+	default y
+	depends on DEVFSD
+	help
+	  This actually doesn't work with busybox modutils but needs
+	  the external modutils.
+
+config DEVFSD_FG_NP
+	bool "Enables the -fg and -np options"
+	default y
+	depends on DEVFSD
+	help
+	  -fg  Run the daemon in the foreground.
+	  -np  Exit after parsing the configuration file.
+	       Do not poll for events.
+
+config DEVFSD_VERBOSE
+	bool "Increases logging (and size)"
+	default y
+	depends on DEVFSD
+	help
+	  Increases logging to stderr or syslog.
+
+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!
+
+	  For legacy systems -- if there is no way around devfsd -- this
+	  tells busybox to look for names like /dev/loop/0 instead of
+	  /dev/loop0. If your /dev directory has normal names instead of
+	  devfs names, you don't want this.
+
+config DEVMEM
+	bool "devmem"
+	default y
+	help
+	  devmem is a small program that reads and writes from physical
+	  memory using /dev/mem.
+
+config EJECT
+	bool "eject"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Used to eject cdroms. (defaults to /dev/cdrom)
+
+config FEATURE_EJECT_SCSI
+	bool "SCSI support"
+	default y
+	depends on EJECT
+	help
+	  Add the -s option to eject, this allows to eject SCSI-Devices and
+	  usb-storage devices.
+
+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.
+	  Usage:
+	  - use kernel option 'vga=xxx' or otherwise enable fb device.
+	  - put somewhere fbsplash.cfg file and an image in .ppm format.
+	  - $ setsid fbsplash [params] &
+	    -c: hide cursor
+	    -d /dev/fbN: framebuffer device (if not /dev/fb0)
+	    -s path_to_image_file (can be "-" for stdin)
+	    -i path_to_cfg_file (can be "-" for stdin)
+	    -f path_to_fifo (can be "-" for stdin)
+	  - if you want to run it only in presence of kernel parameter:
+	    grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params] &
+	  - commands for fifo:
+	    "NN" (ASCII decimal number) - percentage to show on progress bar
+	    "exit" - well you guessed it
+
+config FLASHCP
+	bool "flashcp"
+	default n  # doesn't build on Ubuntu 8.04
+	help
+	  The flashcp binary, inspired by mtd-utils as of git head 5eceb74f7.
+	  This utility is used to copy images into a MTD device.
+
+config FLASH_LOCK
+	bool "flash_lock"
+	default n  # doesn't build on Ubuntu 8.04
+	help
+	  The flash_lock binary from mtd-utils as of git head 5ec0c10d0. This
+	  utility locks part or all of the flash device.
+
+config FLASH_UNLOCK
+	bool "flash_unlock"
+	default n  # doesn't build on Ubuntu 8.04
+	help
+	  The flash_unlock binary from mtd-utils as of git head 5ec0c10d0. This
+	  utility unlocks part or all of the flash device.
+
+config FLASH_ERASEALL
+	bool "flash_eraseall"
+	default n  # doesn't build on Ubuntu 8.04
+	help
+	  The flash_eraseall binary from mtd-utils as of git head c4c6a59eb.
+	  This utility is used to erase the whole MTD device.
+
+config IONICE
+	bool "ionice"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Set/set program io scheduling class and priority
+	  Requires kernel >= 2.6.13
+
+config INOTIFYD
+	bool "inotifyd"
+	default n  # doesn't build on Knoppix 5
+	help
+	  Simple inotify daemon. Reports filesystem changes. Requires
+	  kernel >= 2.6.13
+
+config LAST
+	bool "last"
+	default y
+	depends on FEATURE_WTMP
+	help
+	  'last' displays a list of the last users that logged into the system.
+
+choice
+	prompt "Choose last implementation"
+	depends on LAST
+	default FEATURE_LAST_FANCY
+
+config FEATURE_LAST_SMALL
+	bool "small"
+	help
+	  This is a small version of last with just the basic set of
+	  features.
+
+config FEATURE_LAST_FANCY
+	bool "huge"
+	help
+	  'last' displays detailed information about the last users that
+	  logged into the system (mimics sysvinit last). +900 bytes.
+endchoice
+
+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
+	  FEATURE_HDPARM_GET_IDENTITY option)....
+
+config FEATURE_HDPARM_GET_IDENTITY
+	bool "Support obtaining detailed information directly from drives"
+	default y
+	depends on HDPARM
+	help
+	  Enables the -I and -i options to obtain detailed information
+	  directly from drives about their capabilities and supported ATA
+	  feature set. If no device name is specified, hdparm will read
+	  identify data from stdin. Enabling this option will add about 16k...
+
+config FEATURE_HDPARM_HDIO_SCAN_HWIF
+	bool "Register an IDE interface (DANGEROUS)"
+	default y
+	depends on HDPARM
+	help
+	  Enables the 'hdparm -R' option to register an IDE interface.
+	  This is dangerous stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+	bool "Un-register an IDE interface (DANGEROUS)"
+	default y
+	depends on HDPARM
+	help
+	  Enables the 'hdparm -U' option to un-register an IDE interface.
+	  This is dangerous stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_DRIVE_RESET
+	bool "Perform device reset (DANGEROUS)"
+	default y
+	depends on HDPARM
+	help
+	  Enables the 'hdparm -w' option to perform a device reset.
+	  This is dangerous stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+	bool "Tristate device for hotswap (DANGEROUS)"
+	default y
+	depends on HDPARM
+	help
+	  Enables the 'hdparm -x' option to tristate device for hotswap,
+	  and the '-b' option to get/set bus state. This is dangerous
+	  stuff, so you should probably say N.
+
+config FEATURE_HDPARM_HDIO_GETSET_DMA
+	bool "Get/set using_dma flag"
+	default y
+	depends on HDPARM
+	help
+	  Enables the 'hdparm -d' option to get/set using_dma flag.
+
+config MAKEDEVS
+	bool "makedevs"
+	default y
+	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.
+
+choice
+	prompt "Choose makedevs behaviour"
+	depends on MAKEDEVS
+	default FEATURE_MAKEDEVS_TABLE
+
+config FEATURE_MAKEDEVS_LEAF
+	bool "leaf"
+
+config FEATURE_MAKEDEVS_TABLE
+	bool "table"
+
+endchoice
+
+config MAN
+	bool "man"
+	default y
+	help
+	  Format and display manual pages.
+
+config MICROCOM
+	bool "microcom"
+	default y
+	help
+	  The poor man's minicom utility for chatting with serial port devices.
+
+config MOUNTPOINT
+	bool "mountpoint"
+	default y
+	help
+	  mountpoint checks if the directory is a mountpoint.
+
+config MT
+	bool "mt"
+	default y
+	help
+	  mt is used to control tape devices. You can use the mt utility
+	  to advance or rewind a tape past a specified number of archive
+	  files on the tape.
+
+config RAIDAUTORUN
+	bool "raidautorun"
+	default y
+	select PLATFORM_LINUX
+	help
+	  raidautorun tells the kernel md driver to
+	  search and start RAID arrays.
+
+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.
+
+	  This applet just calls the readahead(2) system call on each file.
+	  It is mainly useful in system startup scripts to preload files
+	  or executables before they are used. When used at the right time
+	  (in particular when a CPU bound process is running) it can
+	  significantly speed up system startup.
+
+	  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
+	select PLATFORM_LINUX
+	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.
+
+	  This applet uses utmp but does not rely on busybox supporing
+	  utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc.
+
+config RX
+	bool "rx"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Receive files using the Xmodem protocol.
+
+config SETSID
+	bool "setsid"
+	default y
+	help
+	  setsid runs a program in a new session
+
+config STRINGS
+	bool "strings"
+	default y
+	help
+	  strings prints the printable character sequences for each file
+	  specified.
+
+config TASKSET
+	bool "taskset"
+	default n  # doesn't build on some non-x86 targets (m68k)
+	help
+	  Retrieve or set a processes's CPU affinity.
+	  This requires sched_{g,s}etaffinity support in your libc.
+
+config FEATURE_TASKSET_FANCY
+	bool "Fancy output"
+	default y
+	depends on TASKSET
+	help
+	  Add code for fancy output. This merely silences a compiler-warning
+	  and adds about 135 Bytes. May be needed for machines with alot
+	  of CPUs.
+
+config TIME
+	bool "time"
+	default y
+	help
+	  The time command runs the specified program with the given arguments.
+	  When the command finishes, time writes a message to standard output
+	  giving timing statistics about this program run.
+
+config TIMEOUT
+	bool "timeout"
+	default y
+	help
+	  Runs a program and watches it. If it does not terminate in
+	  specified number of seconds, it is sent a signal.
+
+config TTYSIZE
+	bool "ttysize"
+	default y
+	help
+	  A replacement for "stty size". Unlike stty, can report only width,
+	  only height, or both, in any order. It also does not complain on
+	  error, but returns default 80x24.
+	  Usage in shell scripts: width=`ttysize w`.
+
+config VOLNAME
+	bool "volname"
+	default y
+	help
+	  Prints a CD-ROM volume name.
+
+config WALL
+	bool "wall"
+	default y
+	depends on FEATURE_UTMP
+	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
+	  and periodically writes a magic character to the device. If the
+	  watchdog applet ever fails to write the magic character within a
+	  certain amount of time, the watchdog device assumes the system has
+	  hung, and will cause the hardware to reboot.
+
+endmenu
diff --git a/busybox-1.19.3/miscutils/Kbuild.src b/busybox-1.19.3/miscutils/Kbuild.src
new file mode 100644
index 0000000..8c49864
--- /dev/null
+++ b/busybox-1.19.3/miscutils/Kbuild.src
@@ -0,0 +1,50 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_ADJTIMEX)    += adjtimex.o
+lib-$(CONFIG_BBCONFIG)    += bbconfig.o
+lib-$(CONFIG_BEEP)        += beep.o
+lib-$(CONFIG_CHAT)        += chat.o
+lib-$(CONFIG_CHRT)        += chrt.o
+lib-$(CONFIG_CROND)       += crond.o
+lib-$(CONFIG_CRONTAB)     += crontab.o
+lib-$(CONFIG_DC)          += dc.o
+lib-$(CONFIG_DEVFSD)      += devfsd.o
+lib-$(CONFIG_DEVMEM)      += devmem.o
+lib-$(CONFIG_EJECT)       += eject.o
+lib-$(CONFIG_FBSPLASH)    += fbsplash.o
+lib-$(CONFIG_FLASHCP)     += flashcp.o
+lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o
+lib-$(CONFIG_FLASH_LOCK)     += flash_lock_unlock.o
+lib-$(CONFIG_FLASH_UNLOCK)   += flash_lock_unlock.o
+lib-$(CONFIG_IONICE)      += ionice.o
+lib-$(CONFIG_HDPARM)      += hdparm.o
+lib-$(CONFIG_INOTIFYD)    += inotifyd.o
+lib-$(CONFIG_FEATURE_LAST_SMALL)+= last.o
+lib-$(CONFIG_FEATURE_LAST_FANCY)+= last_fancy.o
+lib-$(CONFIG_LESS)        += less.o
+lib-$(CONFIG_MAKEDEVS)    += makedevs.o
+lib-$(CONFIG_MAN)         += man.o
+lib-$(CONFIG_MICROCOM)    += microcom.o
+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
+lib-$(CONFIG_STRINGS)     += strings.o
+lib-$(CONFIG_TASKSET)     += taskset.o
+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
diff --git a/busybox-1.19.3/miscutils/adjtimex.c b/busybox-1.19.3/miscutils/adjtimex.c
new file mode 100644
index 0000000..c8816e9
--- /dev/null
+++ b/busybox-1.19.3/miscutils/adjtimex.c
@@ -0,0 +1,158 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * adjtimex.c - read, and possibly modify, the Linux kernel `timex' variables.
+ *
+ * Originally written: October 1997
+ * Last hack: March 2001
+ * Copyright 1997, 2000, 2001 Larry Doolittle <LRDoolittle@lbl.gov>
+ *
+ * busyboxed 20 March 2001, Larry Doolittle <ldoolitt@recycle.lbl.gov>
+ *
+ * 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"
+#ifdef __BIONIC__
+# include <linux/timex.h>
+#else
+# include <sys/timex.h>
+#endif
+
+static const uint16_t statlist_bit[] = {
+	STA_PLL,
+	STA_PPSFREQ,
+	STA_PPSTIME,
+	STA_FLL,
+	STA_INS,
+	STA_DEL,
+	STA_UNSYNC,
+	STA_FREQHOLD,
+	STA_PPSSIGNAL,
+	STA_PPSJITTER,
+	STA_PPSWANDER,
+	STA_PPSERROR,
+	STA_CLOCKERR,
+	0
+};
+static const char statlist_name[] =
+	"PLL"       "\0"
+	"PPSFREQ"   "\0"
+	"PPSTIME"   "\0"
+	"FFL"       "\0"
+	"INS"       "\0"
+	"DEL"       "\0"
+	"UNSYNC"    "\0"
+	"FREQHOLD"  "\0"
+	"PPSSIGNAL" "\0"
+	"PPSJITTER" "\0"
+	"PPSWANDER" "\0"
+	"PPSERROR"  "\0"
+	"CLOCKERR"
+;
+
+static const char ret_code_descript[] =
+	"clock synchronized" "\0"
+	"insert leap second" "\0"
+	"delete leap second" "\0"
+	"leap second in progress" "\0"
+	"leap second has occurred" "\0"
+	"clock not synchronized"
+;
+
+int adjtimex_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int adjtimex_main(int argc UNUSED_PARAM, char **argv)
+{
+	enum {
+		OPT_quiet = 0x1
+	};
+	unsigned opt;
+	char *opt_o, *opt_f, *opt_p, *opt_t;
+	struct timex txc;
+	int i, ret;
+	const char *descript;
+
+	opt_complementary = "=0"; /* no valid non-option parameters */
+	opt = getopt32(argv, "qo:f:p:t:",
+			&opt_o, &opt_f, &opt_p, &opt_t);
+	txc.modes = 0;
+	//if (opt & 0x1) // -q
+	if (opt & 0x2) { // -o
+		txc.offset = xatol(opt_o);
+		txc.modes |= ADJ_OFFSET_SINGLESHOT;
+	}
+	if (opt & 0x4) { // -f
+		txc.freq = xatol(opt_f);
+		txc.modes |= ADJ_FREQUENCY;
+	}
+	if (opt & 0x8) { // -p
+		txc.constant = xatol(opt_p);
+		txc.modes |= ADJ_TIMECONST;
+	}
+	if (opt & 0x10) { // -t
+		txc.tick = xatol(opt_t);
+		txc.modes |= ADJ_TICK;
+	}
+
+	ret = adjtimex(&txc);
+
+	if (ret < 0) {
+		bb_perror_nomsg_and_die();
+	}
+
+	if (!(opt & OPT_quiet)) {
+		int sep;
+		const char *name;
+
+		printf(
+			"    mode:         %d\n"
+			"-o  offset:       %ld\n"
+			"-f  frequency:    %ld\n"
+			"    maxerror:     %ld\n"
+			"    esterror:     %ld\n"
+			"    status:       %d (",
+		txc.modes, txc.offset, txc.freq, txc.maxerror,
+		txc.esterror, txc.status);
+
+		/* representative output of next code fragment:
+		   "PLL | PPSTIME" */
+		name = statlist_name;
+		sep = 0;
+		for (i = 0; statlist_bit[i]; i++) {
+			if (txc.status & statlist_bit[i]) {
+				if (sep)
+					fputs(" | ", stdout);
+				fputs(name, stdout);
+				sep = 1;
+			}
+			name += strlen(name) + 1;
+		}
+
+		descript = "error";
+		if (ret <= 5)
+			descript = nth_string(ret_code_descript, ret);
+		printf(")\n"
+			"-p  timeconstant: %ld\n"
+			"    precision:    %ld\n"
+			"    tolerance:    %ld\n"
+			"-t  tick:         %ld\n"
+			"    time.tv_sec:  %ld\n"
+			"    time.tv_usec: %ld\n"
+			"    return value: %d (%s)\n",
+		txc.constant,
+		txc.precision, txc.tolerance, txc.tick,
+		(long)txc.time.tv_sec, (long)txc.time.tv_usec, ret, descript);
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/miscutils/bbconfig.c b/busybox-1.19.3/miscutils/bbconfig.c
new file mode 100644
index 0000000..e8be813
--- /dev/null
+++ b/busybox-1.19.3/miscutils/bbconfig.c
@@ -0,0 +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 "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;
+}
diff --git a/busybox-1.19.3/miscutils/beep.c b/busybox-1.19.3/miscutils/beep.c
new file mode 100644
index 0000000..910e03e
--- /dev/null
+++ b/busybox-1.19.3/miscutils/beep.c
@@ -0,0 +1,127 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * beep implementation for busybox
+ *
+ * Copyright (C) 2009 Bernhard Reutner-Fischer
+ *
+ * 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>
+#ifndef CLOCK_TICK_RATE
+# define CLOCK_TICK_RATE 1193180
+#endif
+
+/* defaults */
+#ifndef CONFIG_FEATURE_BEEP_FREQ
+# define FREQ (4000)
+#else
+# define FREQ (CONFIG_FEATURE_BEEP_FREQ)
+#endif
+#ifndef CONFIG_FEATURE_BEEP_LENGTH_MS
+# define LENGTH (30)
+#else
+# define LENGTH (CONFIG_FEATURE_BEEP_LENGTH_MS)
+#endif
+#define DELAY (0)
+#define REPETITIONS (1)
+
+int beep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int beep_main(int argc, char **argv)
+{
+	int speaker = get_console_fd_or_die();
+	unsigned tickrate_div_freq = tickrate_div_freq; /* for compiler */
+	unsigned length = length;
+	unsigned delay = delay;
+	unsigned rep = rep;
+	int c;
+
+	c = 'n';
+	while (c != -1) {
+		if (c == 'n') {
+			tickrate_div_freq = CLOCK_TICK_RATE / FREQ;
+			length = LENGTH;
+			delay = DELAY;
+			rep = REPETITIONS;
+		}
+		c = getopt(argc, argv, "f:l:d:r:n");
+/* TODO: -s, -c:
+ * pipe stdin to stdout, but also beep after each line (-s) or char (-c)
+ */
+		switch (c) {
+		case 'f':
+/* TODO: what "-f 0" should do? */
+			tickrate_div_freq = (unsigned)CLOCK_TICK_RATE / xatou(optarg);
+			continue;
+		case 'l':
+			length = xatou(optarg);
+			continue;
+		case 'd':
+/* TODO:
+ * -d N, -D N
+ * specify a delay of N milliseconds between repetitions.
+ * -d specifies that this delay should only occur between beeps,
+ * that is, it should not occur after the last repetition.
+ * -D indicates that the delay should occur after every repetition
+ */
+			delay = xatou(optarg);
+			continue;
+		case 'r':
+			rep = xatou(optarg);
+			continue;
+		case 'n':
+		case -1:
+			break;
+		default:
+			bb_show_usage();
+		}
+		while (rep) {
+//bb_info_msg("rep[%d] freq=%d, length=%d, delay=%d", rep, freq, length, delay);
+			xioctl(speaker, KIOCSOUND, (void*)(uintptr_t)tickrate_div_freq);
+			usleep(1000 * length);
+			ioctl(speaker, KIOCSOUND, (void*)0);
+			if (--rep)
+				usleep(1000 * delay);
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(speaker);
+	return EXIT_SUCCESS;
+}
+/*
+ * so, e.g. Beethoven's 9th symphony "Ode an die Freude" would be
+ * something like:
+a=$((220*3))
+b=$((247*3))
+c=$((262*3))
+d=$((294*3))
+e=$((329*3))
+f=$((349*3))
+g=$((392*3))
+#./beep -f$d -l200 -r2 -n -f$e -l100 -d 10 -n -f$c -l400 -f$g -l200
+./beep -f$e -l200 -r2 \
+        -n -d 100 -f$f -l200 \
+        -n -f$g -l200 -r2 \
+        -n -f$f -l200 \
+        -n -f$e -l200 \
+        -n -f$d -l200 \
+        -n -f$c -l200 -r2 \
+        -n -f$d -l200 \
+        -n -f$e -l200 \
+        -n -f$e -l400 \
+        -n -f$d -l100 \
+        -n -f$d -l200 \
+*/
diff --git a/busybox-1.19.3/miscutils/chat.c b/busybox-1.19.3/miscutils/chat.c
new file mode 100644
index 0000000..ce994f8
--- /dev/null
+++ b/busybox-1.19.3/miscutils/chat.c
@@ -0,0 +1,445 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bare bones chat utility
+ * inspired by ppp's chat
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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
+// max length of "abort string",
+// i.e. device reply which causes termination
+#define MAX_ABORT_LEN 50
+
+// possible exit codes
+enum {
+	ERR_OK = 0,     // all's well
+	ERR_MEM,        // read too much while expecting
+	ERR_IO,         // signalled or I/O error
+	ERR_TIMEOUT,    // timed out while expecting
+	ERR_ABORT,      // first abort condition was met
+//	ERR_ABORT2,     // second abort condition was met
+//	...
+};
+
+// exit code
+#define exitcode bb_got_signal
+
+// trap for critical signals
+static void signal_handler(UNUSED_PARAM int signo)
+{
+	// report I/O error condition
+	exitcode = ERR_IO;
+}
+
+#if !ENABLE_FEATURE_CHAT_IMPLICIT_CR
+#define unescape(s, nocr) unescape(s)
+#endif
+static size_t unescape(char *s, int *nocr)
+{
+	char *start = s;
+	char *p = s;
+
+	while (*s) {
+		char c = *s;
+		// do we need special processing?
+		// standard escapes + \s for space and \N for \0
+		// \c inhibits terminating \r for commands and is noop for expects
+		if ('\\' == c) {
+			c = *++s;
+			if (c) {
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+				if ('c' == c) {
+					*nocr = 1;
+					goto next;
+				}
+#endif
+				if ('N' == c) {
+					c = '\0';
+				} else if ('s' == c) {
+					c = ' ';
+#if ENABLE_FEATURE_CHAT_NOFAIL
+				// unescape leading dash only
+				// TODO: and only for expect, not command string
+				} else if ('-' == c && (start + 1 == s)) {
+					//c = '-';
+#endif
+				} else {
+					c = bb_process_escape_sequence((const char **)&s);
+					s--;
+				}
+			}
+		// ^A becomes \001, ^B -- \002 and so on...
+		} else if ('^' == c) {
+			c = *++s-'@';
+		}
+		// put unescaped char
+		*p++ = c;
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+ next:
+#endif
+		// next char
+		s++;
+	}
+	*p = '\0';
+
+	return p - start;
+}
+
+int chat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chat_main(int argc UNUSED_PARAM, char **argv)
+{
+	int record_fd = -1;
+	bool echo = 0;
+	// collection of device replies which cause unconditional termination
+	llist_t *aborts = NULL;
+	// inactivity period
+	int timeout = DEFAULT_CHAT_TIMEOUT;
+	// maximum length of abort string
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+	size_t max_abort_len = 0;
+#else
+#define max_abort_len MAX_ABORT_LEN
+#endif
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+	struct termios tio0, tio;
+#endif
+	// directive names
+	enum {
+		DIR_HANGUP = 0,
+		DIR_ABORT,
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+		DIR_CLR_ABORT,
+#endif
+		DIR_TIMEOUT,
+		DIR_ECHO,
+		DIR_SAY,
+		DIR_RECORD,
+	};
+
+	// make x* functions fail with correct exitcode
+	xfunc_error_retval = ERR_IO;
+
+	// trap vanilla signals to prevent process from being killed suddenly
+	bb_signals(0
+		+ (1 << SIGHUP)
+		+ (1 << SIGINT)
+		+ (1 << SIGTERM)
+		+ (1 << SIGPIPE)
+		, signal_handler);
+
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+	tcgetattr(STDIN_FILENO, &tio);
+	tio0 = tio;
+	cfmakeraw(&tio);
+	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
+#endif
+
+#if ENABLE_FEATURE_CHAT_SWALLOW_OPTS
+	getopt32(argv, "vVsSE");
+	argv += optind;
+#else
+	argv++; // goto first arg
+#endif
+	// handle chat expect-send pairs
+	while (*argv) {
+		// directive given? process it
+		int key = index_in_strings(
+			"HANGUP\0" "ABORT\0"
+#if ENABLE_FEATURE_CHAT_CLR_ABORT
+			"CLR_ABORT\0"
+#endif
+			"TIMEOUT\0" "ECHO\0" "SAY\0" "RECORD\0"
+			, *argv
+		);
+		if (key >= 0) {
+			// cache directive value
+			char *arg = *++argv;
+			// OFF -> 0, anything else -> 1
+			bool onoff = (0 != strcmp("OFF", arg));
+			// process directive
+			if (DIR_HANGUP == key) {
+				// turn SIGHUP on/off
+				signal(SIGHUP, onoff ? signal_handler : SIG_IGN);
+			} else if (DIR_ABORT == key) {
+				// append the string to abort conditions
+#if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+				size_t len = strlen(arg);
+				if (len > max_abort_len)
+					max_abort_len = len;
+#endif
+				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
+				max_abort_len = 0;
+# 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) == 0) {
+						llist_unlink(&aborts, l);
+						continue;
+					}
+# if ENABLE_FEATURE_CHAT_VAR_ABORT_LEN
+					if (len > max_abort_len)
+						max_abort_len = len;
+# endif
+				}
+#endif
+			} else if (DIR_TIMEOUT == key) {
+				// set new timeout
+				// -1 means OFF
+				timeout = atoi(arg) * 1000;
+				// 0 means default
+				// >0 means value in msecs
+				if (!timeout)
+					timeout = DEFAULT_CHAT_TIMEOUT;
+			} else if (DIR_ECHO == key) {
+				// turn echo on/off
+				// N.B. echo means dumping device input/output to stderr
+				echo = onoff;
+			} else if (DIR_RECORD == key) {
+				// turn record on/off
+				// N.B. record means dumping device input to a file
+					// close previous record_fd
+				if (record_fd > 0)
+					close(record_fd);
+				// N.B. do we have to die here on open error?
+				record_fd = (onoff) ? xopen(arg, O_WRONLY|O_CREAT|O_TRUNC) : -1;
+			} else if (DIR_SAY == key) {
+				// just print argument verbatim
+				// TODO: should we use full_write() to avoid unistd/stdio conflict?
+				bb_error_msg("%s", arg);
+			}
+			// next, please!
+			argv++;
+		// ordinary expect-send pair!
+		} else {
+			//-----------------------
+			// do expect
+			//-----------------------
+			int expect_len;
+			size_t buf_len = 0;
+			size_t max_len = max_abort_len;
+
+			struct pollfd pfd;
+#if ENABLE_FEATURE_CHAT_NOFAIL
+			int nofail = 0;
+#endif
+			char *expect = *argv++;
+
+			// sanity check: shall we really expect something?
+			if (!expect)
+				goto expect_done;
+
+#if ENABLE_FEATURE_CHAT_NOFAIL
+			// if expect starts with -
+			if ('-' == *expect) {
+				// swallow -
+				expect++;
+				// and enter nofail mode
+				nofail++;
+			}
+#endif
+
+#ifdef ___TEST___BUF___ // test behaviour with a small buffer
+#	undef COMMON_BUFSIZE
+#	define COMMON_BUFSIZE 6
+#endif
+			// expand escape sequences in expect
+			expect_len = unescape(expect, &expect_len /*dummy*/);
+			if (expect_len > max_len)
+				max_len = expect_len;
+			// sanity check:
+			// we should expect more than nothing but not more than input buffer
+			// TODO: later we'll get rid of fixed-size buffer
+			if (!expect_len)
+				goto expect_done;
+			if (max_len >= COMMON_BUFSIZE) {
+				exitcode = ERR_MEM;
+				goto expect_done;
+			}
+
+			// get reply
+			pfd.fd = STDIN_FILENO;
+			pfd.events = POLLIN;
+			while (!exitcode
+			    && poll(&pfd, 1, timeout) > 0
+			    && (pfd.revents & POLLIN)
+			) {
+#define buf bb_common_bufsiz1
+				llist_t *l;
+				ssize_t delta;
+
+				// read next char from device
+				if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) {
+					// dump device input if RECORD fname
+					if (record_fd > 0) {
+						full_write(record_fd, buf+buf_len, 1);
+					}
+					// dump device input if ECHO ON
+					if (echo > 0) {
+//						if (buf[buf_len] < ' ') {
+//							full_write(STDERR_FILENO, "^", 1);
+//							buf[buf_len] += '@';
+//						}
+						full_write(STDERR_FILENO, buf+buf_len, 1);
+					}
+					buf_len++;
+					// move input frame if we've reached higher bound
+					if (buf_len > COMMON_BUFSIZE) {
+						memmove(buf, buf+buf_len-max_len, max_len);
+						buf_len = max_len;
+					}
+				}
+				// N.B. rule of thumb: values being looked for can
+				// be found only at the end of input buffer
+				// this allows to get rid of strstr() and memmem()
+
+				// TODO: make expect and abort strings processed uniformly
+				// abort condition is met? -> bail out
+				for (l = aborts, exitcode = ERR_ABORT; l; l = l->link, ++exitcode) {
+					size_t len = strlen(l->data);
+					delta = buf_len-len;
+					if (delta >= 0 && !memcmp(buf+delta, l->data, len))
+						goto expect_done;
+				}
+				exitcode = ERR_OK;
+
+				// expected reply received? -> goto next command
+				delta = buf_len - expect_len;
+				if (delta >= 0 && !memcmp(buf+delta, expect, expect_len))
+					goto expect_done;
+#undef buf
+			} /* while (have data) */
+
+			// device timed out or unexpected reply received
+			exitcode = ERR_TIMEOUT;
+ expect_done:
+#if ENABLE_FEATURE_CHAT_NOFAIL
+			// on success and when in nofail mode
+			// we should skip following subsend-subexpect pairs
+			if (nofail) {
+				if (!exitcode) {
+					// find last send before non-dashed expect
+					while (*argv && argv[1] && '-' == argv[1][0])
+						argv += 2;
+					// skip the pair
+					// N.B. do we really need this?!
+					if (!*argv++ || !*argv++)
+						break;
+				}
+				// nofail mode also clears all but IO errors (or signals)
+				if (ERR_IO != exitcode)
+					exitcode = ERR_OK;
+			}
+#endif
+			// bail out unless we expected successfully
+			if (exitcode)
+				break;
+
+			//-----------------------
+			// do send
+			//-----------------------
+			if (*argv) {
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+				int nocr = 0; // inhibit terminating command with \r
+#endif
+				char *loaded = NULL; // loaded command
+				size_t len;
+				char *buf = *argv++;
+
+				// if command starts with @
+				// load "real" command from file named after @
+				if ('@' == *buf) {
+					// skip the @ and any following white-space
+					trim(++buf);
+					buf = loaded = xmalloc_xopen_read_close(buf, NULL);
+				}
+				// expand escape sequences in command
+				len = unescape(buf, &nocr);
+
+				// send command
+				alarm(timeout);
+				pfd.fd = STDOUT_FILENO;
+				pfd.events = POLLOUT;
+				while (len && !exitcode
+				    && poll(&pfd, 1, -1) > 0
+				    && (pfd.revents & POLLOUT)
+				) {
+#if ENABLE_FEATURE_CHAT_SEND_ESCAPES
+					// "\\d" means 1 sec delay, "\\p" means 0.01 sec delay
+					// "\\K" means send BREAK
+					char c = *buf;
+					if ('\\' == c) {
+						c = *++buf;
+						if ('d' == c) {
+							sleep(1);
+							len--;
+							continue;
+						}
+						if ('p' == c) {
+							usleep(10000);
+							len--;
+							continue;
+						}
+						if ('K' == c) {
+							tcsendbreak(STDOUT_FILENO, 0);
+							len--;
+							continue;
+						}
+						buf--;
+					}
+					if (safe_write(STDOUT_FILENO, buf, 1) != 1)
+						break;
+					len--;
+					buf++;
+#else
+					len -= full_write(STDOUT_FILENO, buf, len);
+#endif
+				} /* while (can write) */
+				alarm(0);
+
+				// report I/O error if there still exists at least one non-sent char
+				if (len)
+					exitcode = ERR_IO;
+
+				// free loaded command (if any)
+				if (loaded)
+					free(loaded);
+#if ENABLE_FEATURE_CHAT_IMPLICIT_CR
+				// or terminate command with \r (if not inhibited)
+				else if (!nocr)
+					xwrite(STDOUT_FILENO, "\r", 1);
+#endif
+				// bail out unless we sent command successfully
+				if (exitcode)
+					break;
+			} /* if (*argv) */
+		}
+	} /* while (*argv) */
+
+#if ENABLE_FEATURE_CHAT_TTY_HIFI
+	tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
+#endif
+
+	return exitcode;
+}
diff --git a/busybox-1.19.3/miscutils/chrt.c b/busybox-1.19.3/miscutils/chrt.c
new file mode 100644
index 0000000..91b5397
--- /dev/null
+++ b/busybox-1.19.3/miscutils/chrt.c
@@ -0,0 +1,142 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 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;
+	char name[sizeof("SCHED_OTHER")];
+} policies[] = {
+	{SCHED_OTHER, "SCHED_OTHER"},
+	{SCHED_FIFO, "SCHED_FIFO"},
+	{SCHED_RR, "SCHED_RR"}
+};
+
+//TODO: add
+// -b, SCHED_BATCH
+// -i, SCHED_IDLE
+
+static void show_min_max(int pol)
+{
+	const char *fmt = "%s min/max priority\t: %u/%u\n";
+	int max, min;
+
+	max = sched_get_priority_max(pol);
+	min = sched_get_priority_min(pol);
+	if ((max|min) < 0)
+		fmt = "%s not supported\n";
+	printf(fmt, policies[pol].name, min, max);
+}
+
+#define OPT_m (1<<0)
+#define OPT_p (1<<1)
+#define OPT_r (1<<2)
+#define OPT_f (1<<3)
+#define OPT_o (1<<4)
+
+int chrt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chrt_main(int argc UNUSED_PARAM, char **argv)
+{
+	pid_t pid = 0;
+	unsigned opt;
+	struct sched_param sp;
+	char *pid_str;
+	char *priority = priority; /* for compiler */
+	const char *current_new;
+	int policy = SCHED_RR;
+
+	/* 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;
+
+	argv += optind;
+	if (!argv[0])
+		bb_show_usage();
+	if (opt & OPT_p) {
+		pid_str = *argv++;
+		if (*argv) { /* "-p <priority> <pid> [...]" */
+			priority = pid_str;
+			pid_str = *argv;
+		}
+		/* else "-p <pid>", and *argv == NULL */
+		pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
+	} else {
+		priority = *argv++;
+		if (!*argv)
+			bb_show_usage();
+	}
+
+	current_new = "current\0new";
+	if (opt & OPT_p) {
+		int pol;
+ print_rt_info:
+		pol = sched_getscheduler(pid);
+		if (pol < 0)
+			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", (int)pid);
+		printf("pid %d's %s scheduling priority: %d\n",
+				(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
+			 * for the second time (see goto below) */
+			return EXIT_SUCCESS;
+		}
+		*argv = NULL;
+		current_new += 8;
+	}
+
+	/* from the manpage of sched_getscheduler:
+	[...] sched_priority can have a value in the range 0 to 99.
+	[...] SCHED_OTHER or SCHED_BATCH must be assigned static priority 0.
+	[...] SCHED_FIFO or SCHED_RR can have static priority in 1..99 range.
+	*/
+	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', (int)pid);
+
+	if (!argv[0]) /* "-p <priority> <pid> [...]" */
+		goto print_rt_info;
+
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/miscutils/conspy.c b/busybox-1.19.3/miscutils/conspy.c
new file mode 100644
index 0000000..433c3e8
--- /dev/null
+++ b/busybox-1.19.3/miscutils/conspy.c
@@ -0,0 +1,549 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * A text-mode VNC like program for Linux virtual terminals.
+ *
+ * pascal.bellard@ads-lu.com
+ *
+ * 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 source tree.
+ */
+
+//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:	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 -nd NUM  screenshot of 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:#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:     "\n	-v	Don't send keystrokes to the console"
+//usage:     "\n	-c	Create missing devices in /dev"
+//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	-x COL	Starting column"
+//usage:     "\n	-y LINE	Starting line"
+
+#include "libbb.h"
+#include <sys/kd.h>
+
+
+#define ESC "\033"
+
+struct screen_info {
+	unsigned char lines, cols, cursor_x, cursor_y;
+};
+
+#define CHAR(x) (*(uint8_t*)(x))
+#define ATTR(x) (((uint8_t*)(x))[1])
+#define NEXT(x) ((x) += 2)
+#define DATA(x) (*(uint16_t*)(x))
+
+struct globals {
+	char* data;
+	int size;
+	int x, y;
+	int kbd_fd;
+	int ioerror_count;
+	int key_count;
+	int escape_count;
+	int nokeys;
+	int current;
+	int first_line_offset;
+	int last_attr;
+	// cached local tty parameters
+	unsigned width;
+	unsigned height;
+	unsigned col;
+	unsigned line;
+	smallint curoff; // unknown:0 cursor on:-1 cursor off:1
+	char attrbuf[sizeof(ESC"[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")];
+};
+
+#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)
+
+enum {
+	FLAG_v,  // view only
+	FLAG_c,  // create device if need
+	FLAG_s,  // session
+	FLAG_n,  // no colors
+	FLAG_d,  // dump screen
+	FLAG_f,  // follow cursor
+};
+#define FLAG(x) (1 << FLAG_##x)
+#define BW (option_mask32 & FLAG(n))
+
+static void clrscr(void)
+{
+	// Home, clear till end of screen
+	fputs(ESC"[1;1H" ESC"[J", stdout);
+	G.col = G.line = 0;
+}
+
+static void set_cursor(int state)
+{
+	if (G.curoff != state) {
+		G.curoff = state;
+		fputs(ESC"[?25", stdout);
+		bb_putchar("h?l"[1 + state]);
+	}
+}
+
+static void gotoxy(int col, int line)
+{
+	if (G.col != col || G.line != line) {
+		G.col = col;
+		G.line = line;
+		printf(ESC"[%u;%uH", line + 1, col + 1);
+	}
+}
+
+static void cleanup(int code)
+{
+	set_cursor(-1); // cursor on
+	tcsetattr(G.kbd_fd, TCSANOW, &G.term_orig);
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(G.kbd_fd);
+	}
+	// Reset attributes
+	if (!BW)
+		fputs(ESC"[0m", stdout);
+	bb_putchar('\n');
+	if (code > 1)
+		kill_myself_with_sig(code);
+	exit(code);
+}
+
+static void screen_read_close(void)
+{
+	unsigned i, j;
+	int vcsa_fd;
+	char *data;
+
+	// Close & re-open vcsa in case they have swapped virtual consoles
+	vcsa_fd = xopen(G.vcsa_name, O_RDONLY);
+	xread(vcsa_fd, &G.remote, 4);
+	i = G.remote.cols * 2;
+	G.first_line_offset = G.y * i;
+	i *= G.remote.lines;
+	if (G.data == NULL) {
+		G.size = i;
+		G.data = xzalloc(2 * i);
+	}
+	else if (G.size != i) {
+		cleanup(1);
+	}
+	data = G.data + G.current;
+	xread(vcsa_fd, data, G.size);
+	close(vcsa_fd);
+	for (i = 0; i < G.remote.lines; i++) {
+		for (j = 0; j < G.remote.cols; j++, NEXT(data)) {
+			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;
+		}
+	}
+}
+
+static void screen_char(char *data)
+{
+	if (!BW) {
+		uint8_t attr = ATTR(data);
+		//uint8_t attr = ATTR(data) >> 1; // for framebuffer console
+		uint8_t attr_diff = G.last_attr ^ attr;
+
+		if (attr_diff) {
+// Attribute layout for VGA compatible text videobuffer:
+// blinking text
+// |red bkgd
+// ||green bkgd
+// |||blue bkgd
+// vvvv
+// 00000000 <- lsb bit on the right
+//     bold text / text 8th bit
+//      red text
+//       green text
+//        blue text
+// TODO: apparently framebuffer-based console uses different layout
+// (bug? attempt to get 8th text bit in better position?)
+// red bkgd
+// |green bkgd
+// ||blue bkgd
+// vvv
+// 00000000 <- lsb bit on the right
+//    bold text
+//     red text
+//      green text
+//       blue text
+//        text 8th bit
+			// converting RGB color bit triad to BGR:
+			static const char color[8] = "04261537";
+			const uint8_t fg_mask = 0x07, bold_mask  = 0x08;
+			const uint8_t bg_mask = 0x70, blink_mask = 0x80;
+			char *ptr;
+
+			ptr = G.attrbuf + 2; // skip "ESC ["
+
+			// (G.last_attr & ~attr) has 1 only where
+			// G.last_attr has 1 but attr has 0.
+			// Here we check whether we have transition
+			// bold->non-bold or blink->non-blink:
+			if (G.last_attr < 0  // initial value
+			 || ((G.last_attr & ~attr) & (bold_mask | blink_mask)) != 0
+			) {
+				*ptr++ = '0'; // "reset all attrs"
+				*ptr++ = ';';
+				// must set fg & bg, maybe need to set bold or blink:
+				attr_diff = attr | ~(bold_mask | blink_mask);
+			}
+			G.last_attr = attr;
+			if (attr_diff & bold_mask) {
+				*ptr++ = '1';
+				*ptr++ = ';';
+			}
+			if (attr_diff & blink_mask) {
+				*ptr++ = '5';
+				*ptr++ = ';';
+			}
+			if (attr_diff & fg_mask) {
+				*ptr++ = '3';
+				*ptr++ = color[attr & fg_mask];
+				*ptr++ = ';';
+			}
+			if (attr_diff & bg_mask) {
+				*ptr++ = '4';
+				*ptr++ = color[(attr & bg_mask) >> 4];
+				*ptr++ = ';';
+			}
+			if (ptr != G.attrbuf + 2) {
+				ptr[-1] = 'm';
+				*ptr = '\0';
+				fputs(G.attrbuf, stdout);
+			}
+		}
+	}
+	putchar(CHAR(data));
+	G.col++;
+}
+
+static void screen_dump(void)
+{
+	int linefeed_cnt;
+	int line, col;
+	int linecnt = G.remote.lines - G.y;
+	char *data = G.data + G.current + G.first_line_offset;
+
+	linefeed_cnt = 0;
+	for (line = 0; line < linecnt && line < G.height; line++) {
+		int space_cnt = 0;
+		for (col = 0; col < G.remote.cols; col++, NEXT(data)) {
+			unsigned tty_col = col - G.x; // if will catch col < G.x too
+
+			if (tty_col >= G.width)
+				continue;
+			space_cnt++;
+			if (BW && CHAR(data) == ' ')
+				continue;
+			while (linefeed_cnt != 0) {
+				//bb_putchar('\r'); - tty driver does it for us
+				bb_putchar('\n');
+				linefeed_cnt--;
+			}
+			while (--space_cnt)
+				bb_putchar(' ');
+			screen_char(data);
+		}
+		linefeed_cnt++;
+	}
+}
+
+static void curmove(void)
+{
+	unsigned cx = G.remote.cursor_x - G.x;
+	unsigned cy = G.remote.cursor_y - G.y;
+	int cursor = 1;
+
+	if (cx < G.width && cy < G.height) {
+		gotoxy(cx, cy);
+		cursor = -1;
+	}
+	set_cursor(cursor);
+}
+
+static void create_cdev_if_doesnt_exist(const char* name, dev_t dev)
+{
+	int fd = open(name, O_RDONLY);
+	if (fd != -1)
+		close(fd);
+	else if (errno == ENOENT)
+		mknod(name, S_IFCHR | 0660, dev);
+}
+
+static NOINLINE void start_shell_in_child(const char* tty_name)
+{
+	int pid = xvfork();
+	if (pid == 0) {
+		struct termios termchild;
+		const char *shell = get_shell_name();
+
+		signal(SIGHUP, SIG_IGN);
+		// set tty as a controlling tty
+		setsid();
+		// make tty to be input, output, error
+		close(0);
+		xopen(tty_name, O_RDWR); // uses fd 0
+		xdup2(0, 1);
+		xdup2(0, 2);
+		ioctl(0, TIOCSCTTY, 1);
+		tcsetpgrp(0, getpid());
+		tcgetattr(0, &termchild);
+		termchild.c_lflag |= ECHO;
+		termchild.c_oflag |= ONLCR | XTABS;
+		termchild.c_iflag |= ICRNL;
+		termchild.c_iflag &= ~IXOFF;
+		tcsetattr_stdin_TCSANOW(&termchild);
+		execl(shell, shell, "-i", (char *) NULL);
+		bb_simple_perror_msg_and_die(shell);
+	}
+}
+
+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")];
+#define keybuf bb_common_bufsiz1
+	struct termios termbuf;
+	unsigned opts;
+	unsigned ttynum;
+	int poll_timeout_ms;
+#if ENABLE_LONG_OPTS
+	static const char getopt_longopts[] ALIGN1 =
+		"viewonly\0"     No_argument "v"
+		"createdevice\0" No_argument "c"
+		"session\0"      No_argument "s"
+		"nocolors\0"     No_argument "n"
+		"dump\0"         No_argument "d"
+		"follow\0"       No_argument "f"
+		;
+
+	applet_long_options = getopt_longopts;
+#endif
+	INIT_G();
+	strcpy(G.vcsa_name, "/dev/vcsa");
+
+	opt_complementary = "x+:y+"; // numeric params
+	opts = getopt32(argv, "vcsndfx: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(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));
+		create_cdev_if_doesnt_exist(G.vcsa_name, makedev(7, 128 + ttynum));
+	}
+	if ((opts & FLAG(s)) && ttynum) {
+		start_shell_in_child(tty_name);
+	}
+
+	screen_read_close();
+	if (opts & FLAG(d)) {
+		screen_dump();
+		bb_putchar('\n');
+		return 0;
+	}
+
+	bb_signals(BB_FATAL_SIGS, cleanup);
+
+	// All characters must be passed through to us unaltered
+	G.kbd_fd = xopen(CURRENT_TTY, O_RDONLY);
+	tcgetattr(G.kbd_fd, &G.term_orig);
+	termbuf = G.term_orig;
+	termbuf.c_iflag &= ~(BRKINT|INLCR|ICRNL|IXON|IXOFF|IUCLC|IXANY|IMAXBEL);
+	//termbuf.c_oflag &= ~(OPOST); - no, we still want \n -> \r\n
+	termbuf.c_lflag &= ~(ISIG|ICANON|ECHO);
+	termbuf.c_cc[VMIN] = 1;
+	termbuf.c_cc[VTIME] = 0;
+	tcsetattr(G.kbd_fd, TCSANOW, &termbuf);
+
+	poll_timeout_ms = 250;
+	while (1) {
+		struct pollfd pfd;
+		int bytes_read;
+		int i, j;
+		char *data, *old;
+
+		// in the first loop G.width = G.height = 0: refresh
+		i = G.width;
+		j = G.height;
+		get_terminal_width_height(G.kbd_fd, &G.width, &G.height);
+		if (option_mask32 & FLAG(f)) {
+			int nx = G.remote.cursor_x - G.width + 1;
+			int ny = G.remote.cursor_y - G.height + 1;
+
+			if (G.remote.cursor_x < G.x) {
+				G.x = G.remote.cursor_x;
+				i = 0; // force refresh
+			}
+			if (nx > G.x) {
+				G.x = nx;
+				i = 0; // force refresh
+			}
+			if (G.remote.cursor_y < G.y) {
+				G.y = G.remote.cursor_y;
+				i = 0; // force refresh
+			}
+			if (ny > G.y) {
+				G.y = ny;
+				i = 0; // force refresh
+			}
+		}
+
+		// Scan console data and redraw our tty where needed
+		old = G.data + G.current;
+		G.current = G.size - G.current;
+		data = G.data + G.current;
+		screen_read_close();
+		if (i != G.width || j != G.height) {
+			clrscr();
+			screen_dump();
+		} else {
+			// For each remote line
+			old += G.first_line_offset;
+			data += G.first_line_offset;
+			for (i = G.y; i < G.remote.lines; i++) {
+				char *first = NULL; // first char which needs updating
+				char *last = last;  // last char which needs updating
+				unsigned iy = i - G.y;
+
+				if (iy >= G.height)
+					break;
+				for (j = 0; j < G.remote.cols; j++, NEXT(old), NEXT(data)) {
+					unsigned jx = j - G.x; // if will catch j >= G.x too
+
+					if (jx < G.width && DATA(data) != DATA(old)) {
+						last = data;
+						if (!first) {
+							first = data;
+							gotoxy(jx, iy);
+						}
+					}
+				}
+				if (first) {
+					// Rewrite updated data on the local screen
+					for (; first <= last; NEXT(first))
+						screen_char(first);
+				}
+			}
+		}
+		curmove();
+
+		// Wait for local user keypresses
+		fflush_all();
+		pfd.fd = G.kbd_fd;
+		pfd.events = POLLIN;
+		bytes_read = 0;
+		switch (poll(&pfd, 1, poll_timeout_ms)) {
+			char *k;
+		case -1:
+			if (errno != EINTR)
+				cleanup(1);
+			break;
+		case 0:
+			if (++G.nokeys >= 4)
+				G.nokeys = G.escape_count = 0;
+			break;
+		default:
+			// Read the keys pressed
+			k = keybuf + G.key_count;
+			bytes_read = read(G.kbd_fd, k, sizeof(keybuf) - G.key_count);
+			if (bytes_read < 0)
+				cleanup(1);
+
+			// 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);
+			}
+		}
+		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.
+		G.key_count += bytes_read;
+		if (G.escape_count == 0) {
+			int handle, result;
+			long kbd_mode;
+
+			handle = xopen(tty_name, O_WRONLY);
+			result = ioctl(handle, KDGKBMODE, &kbd_mode);
+			if (result >= 0) {
+				char *p = keybuf;
+
+				if (kbd_mode != K_XLATE && kbd_mode != K_UNICODE) {
+					G.key_count = 0; // scan code mode
+				}
+				for (; G.key_count != 0; p++, G.key_count--) {
+					result = ioctl(handle, TIOCSTI, p);
+					if (result < 0) {
+						memmove(keybuf, p, G.key_count);
+						break;
+					}
+					// If there is an application on console which reacts
+					// to keypresses, we need to make our first sleep
+					// shorter to quickly redraw whatever it printed there.
+					poll_timeout_ms = 20;
+				}
+			}
+			// 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) */
+}
diff --git a/busybox-1.19.3/miscutils/crond.c b/busybox-1.19.3/miscutils/crond.c
new file mode 100644
index 0000000..a0b73c7
--- /dev/null
+++ b/busybox-1.19.3/miscutils/crond.c
@@ -0,0 +1,954 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * crond -d[#] -c <crondir> -f -b
+ *
+ * run as root, but NOT setuid root
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * (version 2.3.2)
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
+ *
+ * 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
+#else
+# define SETENV_LEAKS 1
+#endif
+
+
+#define TMPDIR          CONFIG_FEATURE_CROND_DIR
+#define CRONTABS        CONFIG_FEATURE_CROND_DIR "/crontabs"
+#ifndef SENDMAIL
+# define SENDMAIL       "sendmail"
+#endif
+#ifndef SENDMAIL_ARGS
+# define SENDMAIL_ARGS  "-ti"
+#endif
+#ifndef CRONUPDATE
+# define CRONUPDATE     "cron.update"
+#endif
+#ifndef MAXLINES
+# define MAXLINES       256  /* max lines in non-root crontabs */
+#endif
+
+
+typedef struct CronFile {
+	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_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_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 */
+} CronLine;
+
+
+#define DAEMON_UID 0
+
+
+enum {
+	OPT_l = (1 << 0),
+	OPT_L = (1 << 1),
+	OPT_f = (1 << 2),
+	OPT_b = (1 << 3),
+	OPT_S = (1 << 4),
+	OPT_c = (1 << 5),
+	OPT_d = (1 << 6) * ENABLE_FEATURE_CROND_D,
+};
+#if ENABLE_FEATURE_CROND_D
+# define DebugOpt (option_mask32 & OPT_d)
+#else
+# define DebugOpt 0
+#endif
+
+
+struct globals {
+	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 INIT_G() do { \
+	G.log_level = 8; \
+	G.crontab_dir_name = CRONTABS; \
+} while (0)
+
+
+/* 0 is the most verbose, default 8 */
+#define LVL5  "\x05"
+#define LVL7  "\x07"
+#define LVL8  "\x08"
+#define WARN9 "\x49"
+#define DIE9  "\xc9"
+/* level >= 20 is "error" */
+#define ERR20 "\x14"
+
+static void crondlog(const char *ctl, ...) __attribute__ ((format (printf, 1, 2)));
+static void crondlog(const char *ctl, ...)
+{
+	va_list va;
+	int level = (ctl[0] & 0x1f);
+
+	va_start(va, ctl);
+	if (level >= (int)G.log_level) {
+		/* Debug mode: all to (non-redirected) stderr, */
+		/* Syslog mode: all to syslog (logmode = LOGMODE_SYSLOG), */
+		if (!DebugOpt && G.log_filename) {
+			/* Otherwise (log to file): we reopen log file at every write: */
+			int logfd = open_or_warn(G.log_filename, O_WRONLY | O_CREAT | O_APPEND);
+			if (logfd >= 0)
+				xmove_fd(logfd, STDERR_FILENO);
+		}
+		/* When we log to syslog, level > 8 is logged at LOG_ERR
+		 * syslog level, level <= 8 is logged at LOG_INFO. */
+		if (level > 8) {
+			bb_verror_msg(ctl + 1, va, /* strerr: */ NULL);
+		} else {
+			char *msg = NULL;
+			vasprintf(&msg, ctl + 1, va);
+			bb_info_msg("%s: %s", applet_name, msg);
+			free(msg);
+		}
+	}
+	va_end(va);
+	if (ctl[0] & 0x80)
+		exit(20);
+}
+
+static const char DowAry[] ALIGN1 =
+	"sun""mon""tue""wed""thu""fri""sat"
+	/* "Sun""Mon""Tue""Wed""Thu""Fri""Sat" */
+;
+
+static const char MonAry[] ALIGN1 =
+	"jan""feb""mar""apr""may""jun""jul""aug""sep""oct""nov""dec"
+	/* "Jan""Feb""Mar""Apr""May""Jun""Jul""Aug""Sep""Oct""Nov""Dec" */
+;
+
+static void ParseField(char *user, char *ary, int modvalue, int off,
+				const char *names, char *ptr)
+/* 'names' is a pointer to a set of 3-char abbreviations */
+{
+	char *base = ptr;
+	int n1 = -1;
+	int n2 = -1;
+
+	// this can't happen due to config_read()
+	/*if (base == NULL)
+		return;*/
+
+	while (1) {
+		int skip = 0;
+
+		/* Handle numeric digit or symbol or '*' */
+		if (*ptr == '*') {
+			n1 = 0;  /* everything will be filled */
+			n2 = modvalue - 1;
+			skip = 1;
+			++ptr;
+		} else if (isdigit(*ptr)) {
+			char *endp;
+			if (n1 < 0) {
+				n1 = strtol(ptr, &endp, 10) + off;
+			} else {
+				n2 = strtol(ptr, &endp, 10) + off;
+			}
+			ptr = endp; /* gcc likes temp var for &endp */
+			skip = 1;
+		} else if (names) {
+			int i;
+
+			for (i = 0; names[i]; i += 3) {
+				/* was using strncmp before... */
+				if (strncasecmp(ptr, &names[i], 3) == 0) {
+					ptr += 3;
+					if (n1 < 0) {
+						n1 = i / 3;
+					} else {
+						n2 = i / 3;
+					}
+					skip = 1;
+					break;
+				}
+			}
+		}
+
+		/* handle optional range '-' */
+		if (skip == 0) {
+			goto err;
+		}
+		if (*ptr == '-' && n2 < 0) {
+			++ptr;
+			continue;
+		}
+
+		/*
+		 * collapse single-value ranges, handle skipmark, and fill
+		 * in the character array appropriately.
+		 */
+		if (n2 < 0) {
+			n2 = n1;
+		}
+		if (*ptr == '/') {
+			char *endp;
+			skip = strtol(ptr + 1, &endp, 10);
+			ptr = endp; /* gcc likes temp var for &endp */
+		}
+
+		/*
+		 * fill array, using a failsafe is the easiest way to prevent
+		 * an endless loop
+		 */
+		{
+			int s0 = 1;
+			int failsafe = 1024;
+
+			--n1;
+			do {
+				n1 = (n1 + 1) % modvalue;
+
+				if (--s0 == 0) {
+					ary[n1 % modvalue] = 1;
+					s0 = skip;
+				}
+				if (--failsafe == 0) {
+					goto err;
+				}
+			} while (n1 != n2);
+		}
+		if (*ptr != ',') {
+			break;
+		}
+		++ptr;
+		n1 = -1;
+		n2 = -1;
+	}
+
+	if (*ptr) {
+ err:
+		crondlog(WARN9 "user %s: parse error at %s", user, base);
+		return;
+	}
+
+	if (DebugOpt && (G.log_level <= 5)) { /* like LVL5 */
+		/* can't use crondlog, it inserts '\n' */
+		int i;
+		for (i = 0; i < modvalue; ++i)
+			fprintf(stderr, "%d", (unsigned char)ary[i]);
+		bb_putchar_stderr('\n');
+	}
+}
+
+static void FixDayDow(CronLine *line)
+{
+	unsigned i;
+	int weekUsed = 0;
+	int daysUsed = 0;
+
+	for (i = 0; i < ARRAY_SIZE(line->cl_Dow); ++i) {
+		if (line->cl_Dow[i] == 0) {
+			weekUsed = 1;
+			break;
+		}
+	}
+	for (i = 0; i < ARRAY_SIZE(line->cl_Days); ++i) {
+		if (line->cl_Days[i] == 0) {
+			daysUsed = 1;
+			break;
+		}
+	}
+	if (weekUsed != daysUsed) {
+		if (weekUsed)
+			memset(line->cl_Days, 0, sizeof(line->cl_Days));
+		else /* daysUsed */
+			memset(line->cl_Dow, 0, sizeof(line->cl_Dow));
+	}
+}
+
+/*
+ * 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;
+	int maxLines;
+	char *tokens[6];
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+	char *mailTo = NULL;
+#endif
+
+	delete_cronfile(fileName);
+
+	if (!getpwnam(fileName)) {
+		crondlog(LVL7 "ignoring file '%s' (no such user)", fileName);
+		return;
+	}
+
+	parser = config_open(fileName);
+	if (!parser)
+		return;
+
+	maxLines = (strcmp(fileName, "root") == 0) ? 65535 : MAXLINES;
+
+	if (fstat(fileno(parser->fp), &sbuf) == 0 && sbuf.st_uid == DAEMON_UID) {
+		CronFile *file = xzalloc(sizeof(CronFile));
+		CronLine **pline;
+		int n;
+
+		file->cf_username = xstrdup(fileName);
+		pline = &file->cf_lines;
+
+		while (1) {
+			CronLine *line;
+
+			if (!--maxLines)
+				break;
+			n = config_read(parser, tokens, 6, 1, "# \t", PARSE_NORMAL | PARSE_KEEP_COPY);
+			if (!n)
+				break;
+
+			if (DebugOpt)
+				crondlog(LVL5 "user:%s entry:%s", fileName, parser->data);
+
+			/* check if line is setting MAILTO= */
+			if (0 == strncmp(tokens[0], "MAILTO=", 7)) {
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+				free(mailTo);
+				mailTo = (tokens[0][7]) ? xstrdup(&tokens[0][7]) : NULL;
+#endif /* otherwise just ignore such lines */
+				continue;
+			}
+			/* check if a minimum of tokens is specified */
+			if (n < 6)
+				continue;
+			*pline = line = xzalloc(sizeof(*line));
+			/* parse date ranges */
+			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
+			 */
+			FixDayDow(line);
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+			/* copy mailto (can be NULL) */
+			line->cl_mailto = xstrdup(mailTo);
+#endif
+			/* copy command */
+			line->cl_cmd = xstrdup(tokens[5]);
+			if (DebugOpt) {
+				crondlog(LVL5 " command:%s", tokens[5]);
+			}
+			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 = G.cron_files;
+		G.cron_files = file;
+
+		if (maxLines == 0) {
+			crondlog(WARN9 "user %s: too many lines", fileName);
+		}
+	}
+	config_close(parser);
+}
+
+static void process_cron_update_file(void)
+{
+	FILE *fi;
+	char buf[256];
+
+	fi = fopen_for_read(CRONUPDATE);
+	if (fi != NULL) {
+		unlink(CRONUPDATE);
+		while (fgets(buf, sizeof(buf), fi) != NULL) {
+			/* use first word only */
+			skip_non_whitespace(buf)[0] = '\0';
+			load_crontab(buf);
+		}
+		fclose(fi);
+	}
+}
+
+static void rescan_crontab_dir(void)
+{
+	CronFile *file;
+
+	/* Delete all files until we only have ones with running jobs (or none) */
+ again:
+	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 */
+	unlink(CRONUPDATE);
+	/* 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;
+
+		if (!dir)
+			crondlog(DIE9 "chdir(%s)", "."); /* exits */
+		while ((den = readdir(dir)) != NULL) {
+			if (strchr(den->d_name, '.') != NULL) {
+				continue;
+			}
+			load_crontab(den->d_name);
+		}
+		closedir(dir);
+	}
+}
+
+#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 set_env_vars(struct passwd *pas)
+{
+#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 */
+}
+
+static void change_user(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 */
+		}
+	}
+}
+
+// TODO: sendmail should be _run-time_ option, not compile-time!
+#if ENABLE_FEATURE_CROND_CALL_SENDMAIL
+
+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;
+
+	/* prepare things before vfork */
+	pas = getpwnam(user);
+	if (!pas) {
+		crondlog(WARN9 "can't get uid for %s", user);
+		goto err;
+	}
+	set_env_vars(pas);
+
+	pid = vfork();
+	if (pid == 0) {
+		/* CHILD */
+		/* 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, shell_cmd ? 1 : 0);
+			dup2(1, 2);
+		}
+		/* crond 3.0pl1-100 puts tasks in separate process groups */
+		bb_setpgrp();
+		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);
+	}
+
+	if (pid < 0) {
+		/* FORK FAILED */
+		crondlog(ERR20 "can't vfork");
+ err:
+		pid = 0;
+	} /* else: PARENT, FORK SUCCESS */
+
+	/*
+	 * Close the mail file descriptor.. we can't just leave it open in
+	 * a structure, closing it later, because we might run out of descriptors
+	 */
+	if (mailFd >= 0) {
+		close(mailFd);
+	}
+	return pid;
+}
+
+static void start_one_job(const char *user, CronLine *line)
+{
+	char mailFile[128];
+	int mailFd = -1;
+
+	line->cl_pid = 0;
+	line->cl_empty_mail_size = 0;
+
+	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) {
+			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);
+		}
+	}
+
+	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);
+		}
+	}
+}
+
+/*
+ * process_finished_job - called when job terminates and when mail terminates
+ */
+static void process_finished_job(const char *user, CronLine *line)
+{
+	pid_t pid;
+	int mailFd;
+	char mailFile[128];
+	struct stat sbuf;
+
+	pid = line->cl_pid;
+	line->cl_pid = 0;
+	if (pid <= 0) {
+		/* No job */
+		return;
+	}
+	if (line->cl_empty_mail_size <= 0) {
+		/* End of job and no mail file, or end of sendmail job */
+		return;
+	}
+
+	/*
+	 * 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 != DAEMON_UID
+	 || sbuf.st_nlink != 0
+	 || sbuf.st_size == line->cl_empty_mail_size
+	 || !S_ISREG(sbuf.st_mode)
+	) {
+		close(mailFd);
+		return;
+	}
+	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 /* !ENABLE_FEATURE_CROND_CALL_SENDMAIL */
+
+static void start_one_job(const char *user, CronLine *line)
+{
+	struct passwd *pas;
+	pid_t pid;
+
+	pas = getpwnam(user);
+	if (!pas) {
+		crondlog(WARN9 "can't get uid for %s", user);
+		goto err;
+	}
+
+	/* Prepare things before vfork */
+	set_env_vars(pas);
+
+	/* Fork as the user in question and run program */
+	pid = vfork();
+	if (pid == 0) {
+		/* CHILD */
+		/* 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_cmd, (char *) NULL);
+		crondlog(ERR20 "can't execute '%s' for user %s", DEFAULT_SHELL, user);
+		_exit(EXIT_SUCCESS);
+	}
+	if (pid < 0) {
+		/* FORK FAILED */
+		crondlog(ERR20 "can't vfork");
+ err:
+		pid = 0;
+	}
+	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;
+}
+
+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("/var/run/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 */
+}
diff --git a/busybox-1.19.3/miscutils/crontab.c b/busybox-1.19.3/miscutils/crontab.c
new file mode 100644
index 0000000..4731d8d
--- /dev/null
+++ b/busybox-1.19.3/miscutils/crontab.c
@@ -0,0 +1,222 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * CRONTAB
+ *
+ * usually setuid root, -c option only works if getuid() == geteuid()
+ *
+ * Copyright 1994 Matthew Dillon (dillon@apollo.west.oic.com)
+ * Vladimir Oleynik <dzo@simtreas.ru> (C) 2002
+ *
+ * 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"
+#ifndef CRONUPDATE
+#define CRONUPDATE      "cron.update"
+#endif
+
+static void edit_file(const struct passwd *pas, const char *file)
+{
+	const char *ptr;
+	pid_t pid;
+
+	pid = xvfork();
+	if (pid) { /* parent */
+		wait4pid(pid);
+		return;
+	}
+
+	/* CHILD - change user and run editor */
+	/* initgroups, setgid, setuid */
+	change_identity(pas);
+	setup_environment(pas->pw_shell,
+			SETUP_ENV_CHANGEENV | SETUP_ENV_TO_TMP,
+			pas);
+	ptr = getenv("VISUAL");
+	if (!ptr) {
+		ptr = getenv("EDITOR");
+		if (!ptr)
+			ptr = "vi";
+	}
+
+	BB_EXECLP(ptr, ptr, file, NULL);
+	bb_perror_msg_and_die("can't execute '%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);
+}
+
+int crontab_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int crontab_main(int argc UNUSED_PARAM, char **argv)
+{
+	const struct passwd *pas;
+	const char *crontab_dir = CRONTABS;
+	char *tmp_fname;
+	char *new_fname;
+	char *user_name;  /* -u USER */
+	int fd;
+	int src_fd;
+	int opt_ler;
+
+	/* file [opts]     Replace crontab from file
+	 * - [opts]        Replace crontab from stdin
+	 * -u user         User
+	 * -c dir          Crontab directory
+	 * -l              List crontab for user
+	 * -e              Edit crontab for user
+	 * -r              Delete crontab for user
+	 * bbox also supports -d == -r, but most other crontab
+	 * implementations do not. Deprecated.
+	 */
+	enum {
+		OPT_u = (1 << 0),
+		OPT_c = (1 << 1),
+		OPT_l = (1 << 2),
+		OPT_e = (1 << 3),
+		OPT_r = (1 << 4),
+		OPT_ler = OPT_l + OPT_e + OPT_r,
+	};
+
+	opt_complementary = "?1:dr"; /* max one argument; -d implies -r */
+	opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir);
+	argv += optind;
+
+	if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */
+		/* Run by non-root */
+		if (opt_ler & (OPT_u|OPT_c))
+			bb_error_msg_and_die(bb_msg_you_must_be_root);
+	}
+
+	if (opt_ler & OPT_u) {
+		pas = xgetpwnam(user_name);
+	} else {
+		pas = xgetpwuid(getuid());
+	}
+
+#define user_name DONT_USE_ME_BEYOND_THIS_POINT
+
+	/* From now on, keep only -l, -e, -r bits */
+	opt_ler &= OPT_ler;
+	if ((opt_ler - 1) & opt_ler) /* more than one bit set? */
+		bb_show_usage();
+
+	/* Read replacement file under user's UID/GID/group vector */
+	src_fd = STDIN_FILENO;
+	if (!opt_ler) { /* Replace? */
+		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]);
+		}
+	}
+
+	/* cd to our crontab directory */
+	xchdir(crontab_dir);
+
+	tmp_fname = NULL;
+
+	/* Handle requested operation */
+	switch (opt_ler) {
+
+	default: /* case OPT_r: Delete */
+		unlink(pas->pw_name);
+		break;
+
+	case OPT_l: /* List */
+		{
+			char *args[2] = { pas->pw_name, NULL };
+			return bb_cat(args);
+			/* list exits,
+			 * the rest go play with cron update file */
+		}
+
+	case OPT_e: /* Edit */
+		tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid());
+		/* No O_EXCL: we don't want to be stuck if earlier crontabs
+		 * were killed, leaving stale temp file behind */
+		src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600);
+		fchown(src_fd, pas->pw_uid, pas->pw_gid);
+		fd = open(pas->pw_name, O_RDONLY);
+		if (fd >= 0) {
+			bb_copyfd_eof(fd, src_fd);
+			close(fd);
+			xlseek(src_fd, 0, SEEK_SET);
+		}
+		close_on_exec_on(src_fd); /* don't want editor to see this fd */
+		edit_file(pas, tmp_fname);
+		/* fall through */
+
+	case 0: /* Replace (no -l, -e, or -r were given) */
+		new_fname = xasprintf("%s.new", pas->pw_name);
+		fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600);
+		if (fd >= 0) {
+			bb_copyfd_eof(src_fd, fd);
+			close(fd);
+			xrename(new_fname, pas->pw_name);
+		} else {
+			bb_error_msg("can't create %s/%s",
+					crontab_dir, new_fname);
+		}
+		if (tmp_fname)
+			unlink(tmp_fname);
+		/*free(tmp_fname);*/
+		/*free(new_fname);*/
+
+	} /* switch */
+
+	/* Bump notification file.  Handle window where crond picks file up
+	 * before we can write our entry out.
+	 */
+	while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) {
+		struct stat st;
+
+		fdprintf(fd, "%s\n", pas->pw_name);
+		if (fstat(fd, &st) != 0 || st.st_nlink != 0) {
+			/*close(fd);*/
+			break;
+		}
+		/* st.st_nlink == 0:
+		 * file was deleted, maybe crond missed our notification */
+		close(fd);
+		/* loop */
+	}
+	if (fd < 0) {
+		bb_error_msg("can't append to %s/%s",
+				crontab_dir, CRONUPDATE);
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/miscutils/dc.c b/busybox-1.19.3/miscutils/dc.c
new file mode 100644
index 0000000..6903761
--- /dev/null
+++ b/busybox-1.19.3/miscutils/dc.c
@@ -0,0 +1,281 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include <math.h>
+
+//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, eor,\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 * 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 {
+	unsigned pointer;
+	unsigned base;
+	double stack[1];
+} FIX_ALIASING;
+enum { STACK_SIZE = (COMMON_BUFSIZE - offsetof(struct globals, stack)) / sizeof(double) };
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define pointer   (G.pointer   )
+#define base      (G.base      )
+#define stack     (G.stack     )
+#define INIT_G() do { \
+	base = 10; \
+} while (0)
+
+
+static void push(double a)
+{
+	if (pointer >= STACK_SIZE)
+		bb_error_msg_and_die("stack overflow");
+	stack[pointer++] = a;
+}
+
+static double pop(void)
+{
+	if (pointer == 0)
+		bb_error_msg_and_die("stack underflow");
+	return stack[--pointer];
+}
+
+static void add(void)
+{
+	push(pop() + pop());
+}
+
+static void sub(void)
+{
+	double subtrahend = pop();
+
+	push(pop() - subtrahend);
+}
+
+static void mul(void)
+{
+	push(pop() * pop());
+}
+
+#if ENABLE_FEATURE_DC_LIBM
+static void power(void)
+{
+	double topower = pop();
+
+	push(pow(pop(), topower));
+}
+#endif
+
+static void divide(void)
+{
+	double divisor = pop();
+
+	push(pop() / divisor);
+}
+
+static void mod(void)
+{
+	data_t d = pop();
+
+	push((data_t) pop() % d);
+}
+
+static void and(void)
+{
+	push((data_t) pop() & (data_t) pop());
+}
+
+static void or(void)
+{
+	push((data_t) pop() | (data_t) pop());
+}
+
+static void eor(void)
+{
+	push((data_t) pop() ^ (data_t) pop());
+}
+
+static void not(void)
+{
+	push(~(data_t) pop());
+}
+
+static void set_output_base(void)
+{
+	static const char bases[] ALIGN1 = { 2, 8, 10, 16, 0 };
+	unsigned b = (unsigned)pop();
+
+	base = *strchrnul(bases, b);
+	if (base == 0) {
+		bb_error_msg("error, base %u is not supported", b);
+		base = 10;
+	}
+}
+
+static void print_base(double print)
+{
+	data_t x, i;
+
+	x = (data_t) print;
+	if (base == 10) {
+		if (x == print) /* exactly representable as unsigned integer */
+			printf("%"DATA_FMT"u\n", x);
+		else
+			printf("%g\n", print);
+		return;
+	}
+
+	switch (base) {
+	case 16:
+		printf("%"DATA_FMT"x\n", x);
+		break;
+	case 8:
+		printf("%"DATA_FMT"o\n", x);
+		break;
+	default: /* base 2 */
+		i = MAXINT(data_t) - (MAXINT(data_t) >> 1);
+		/* i is 100000...00000 */
+		do {
+			if (x & i)
+				break;
+			i >>= 1;
+		} while (i > 1);
+		do {
+			bb_putchar('1' - !(x & i));
+			i >>= 1;
+		} while (i);
+		bb_putchar('\n');
+	}
+}
+
+static void print_stack_no_pop(void)
+{
+	unsigned i = pointer;
+	while (i)
+		print_base(stack[--i]);
+}
+
+static void print_no_pop(void)
+{
+	print_base(stack[pointer-1]);
+}
+
+struct op {
+	const char name[4];
+	void (*function) (void);
+};
+
+static const struct op operators[] = {
+	{"+",   add},
+	{"add", add},
+	{"-",   sub},
+	{"sub", sub},
+	{"*",   mul},
+	{"mul", mul},
+	{"/",   divide},
+	{"div", divide},
+#if ENABLE_FEATURE_DC_LIBM
+	{"**",  power},
+	{"exp", power},
+	{"pow", power},
+#endif
+	{"%",   mod},
+	{"mod", mod},
+	{"and", and},
+	{"or",  or},
+	{"not", not},
+	{"eor", eor},
+	{"xor", eor},
+	{"p", print_no_pop},
+	{"f", print_stack_no_pop},
+	{"o", set_output_base},
+	{ "", NULL }
+};
+
+static void stack_machine(const char *argument)
+{
+	char *endPointer;
+	double d;
+	const struct op *o = operators;
+
+	d = strtod(argument, &endPointer);
+
+	if (endPointer != argument && *endPointer == '\0') {
+		push(d);
+		return;
+	}
+
+	while (o->function) {
+		if (strcmp(o->name, argument) == 0) {
+			o->function();
+			return;
+		}
+		o++;
+	}
+	bb_error_msg_and_die("syntax error at '%s'", argument);
+}
+
+int dc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dc_main(int argc UNUSED_PARAM, char **argv)
+{
+	INIT_G();
+
+	argv++;
+	if (!argv[0]) {
+		/* take stuff from stdin if no args are given */
+		char *line;
+		char *cursor;
+		char *token;
+		while ((line = xmalloc_fgetline(stdin)) != NULL) {
+			cursor = line;
+			while (1) {
+				token = skip_whitespace(cursor);
+				if (*token == '\0')
+					break;
+				cursor = skip_non_whitespace(token);
+				if (*cursor != '\0')
+					*cursor++ = '\0';
+				stack_machine(token);
+			}
+			free(line);
+		}
+	} else {
+		// why? it breaks "dc -2 2 + p"
+		//if (argv[0][0] == '-')
+		//	bb_show_usage();
+		do {
+			stack_machine(*argv);
+		} while (*++argv);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/devfsd.c b/busybox-1.19.3/miscutils/devfsd.c
new file mode 100644
index 0000000..6493fe4
--- /dev/null
+++ b/busybox-1.19.3/miscutils/devfsd.c
@@ -0,0 +1,1809 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+	devfsd implementation for busybox
+
+	Copyright (C) 2003 by Tito Ragusa <farmatito@tiscali.it>
+
+	Busybox version is based on some previous work and ideas
+	Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+
+	devfsd.c
+
+	Main file for  devfsd  (devfs daemon for Linux).
+
+    Copyright (C) 1998-2002  Richard Gooch
+
+	devfsd.h
+
+    Header file for  devfsd  (devfs daemon for Linux).
+
+    Copyright (C) 1998-2000  Richard Gooch
+
+	compat_name.c
+
+    Compatibility name file for  devfsd  (build compatibility names).
+
+    Copyright (C) 1998-2002  Richard Gooch
+
+	expression.c
+
+    This code provides Borne Shell-like expression expansion.
+
+    Copyright (C) 1997-1999  Richard Gooch
+
+	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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Richard Gooch may be reached by email at  rgooch@atnf.csiro.au
+    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>
+
+#include <sys/un.h>
+#include <sys/sysmacros.h>
+
+/* Various defines taken from linux/major.h */
+#define IDE0_MAJOR	3
+#define IDE1_MAJOR	22
+#define IDE2_MAJOR	33
+#define IDE3_MAJOR	34
+#define IDE4_MAJOR	56
+#define IDE5_MAJOR	57
+#define IDE6_MAJOR	88
+#define IDE7_MAJOR	89
+#define IDE8_MAJOR	90
+#define IDE9_MAJOR	91
+
+
+/* Various defines taken from linux/devfs_fs.h */
+#define DEVFSD_PROTOCOL_REVISION_KERNEL  5
+#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)
+#define DEVFSDIOC_RELEASE_EVENT_QUEUE   _IOW(DEVFSD_IOCTL_BASE, 3, int)
+#define DEVFSDIOC_SET_CONFIG_DEBUG_MASK _IOW(DEVFSD_IOCTL_BASE, 4, int)
+#define DEVFSD_NOTIFY_REGISTERED    0
+#define DEVFSD_NOTIFY_UNREGISTERED  1
+#define DEVFSD_NOTIFY_ASYNC_OPEN    2
+#define DEVFSD_NOTIFY_CLOSE         3
+#define DEVFSD_NOTIFY_LOOKUP        4
+#define DEVFSD_NOTIFY_CHANGE        5
+#define DEVFSD_NOTIFY_CREATE        6
+#define DEVFSD_NOTIFY_DELETE        7
+#define DEVFS_PATHLEN               1024
+/*  Never change this otherwise the binary interface will change   */
+
+struct devfsd_notify_struct {
+	/*  Use native C types to ensure same types in kernel and user space     */
+	unsigned int type;           /*  DEVFSD_NOTIFY_* value                   */
+	unsigned int mode;           /*  Mode of the inode or device entry       */
+	unsigned int major;          /*  Major number of device entry            */
+	unsigned int minor;          /*  Minor number of device entry            */
+	unsigned int uid;            /*  Uid of process, inode or device entry   */
+	unsigned int gid;            /*  Gid of process, inode or device entry   */
+	unsigned int overrun_count;  /*  Number of lost events                   */
+	unsigned int namelen;        /*  Number of characters not including '\0' */
+	/*  The device name MUST come last                                       */
+	char devname[DEVFS_PATHLEN]; /*  This will be '\0' terminated            */
+};
+
+#define BUFFER_SIZE 16384
+#define DEVFSD_VERSION "1.3.25"
+#define CONFIG_FILE  "/etc/devfsd.conf"
+#define MODPROBE		"/sbin/modprobe"
+#define MODPROBE_SWITCH_1 "-k"
+#define MODPROBE_SWITCH_2 "-C"
+#define CONFIG_MODULES_DEVFS "/etc/modules.devfs"
+#define MAX_ARGS     (6 + 1)
+#define MAX_SUBEXPR  10
+#define STRING_LENGTH 255
+
+/* for get_uid_gid() */
+#define UID			0
+#define GID			1
+
+/* fork_and_execute() */
+# define DIE			1
+# define NO_DIE			0
+
+/* for dir_operation() */
+#define RESTORE		0
+#define SERVICE		1
+#define READ_CONFIG 2
+
+/*  Update only after changing code to reflect new protocol  */
+#define DEVFSD_PROTOCOL_REVISION_DAEMON  5
+
+/*  Compile-time check  */
+#if DEVFSD_PROTOCOL_REVISION_KERNEL != DEVFSD_PROTOCOL_REVISION_DAEMON
+#error protocol version mismatch. Update your kernel headers
+#endif
+
+#define AC_PERMISSIONS				0
+#define AC_MODLOAD					1
+#define AC_EXECUTE					2
+#define AC_MFUNCTION				3	/* not supported by busybox */
+#define AC_CFUNCTION				4	/* not supported by busybox */
+#define AC_COPY						5
+#define AC_IGNORE					6
+#define AC_MKOLDCOMPAT				7
+#define AC_MKNEWCOMPAT				8
+#define AC_RMOLDCOMPAT				9
+#define AC_RMNEWCOMPAT				10
+#define AC_RESTORE					11
+
+struct permissions_type {
+	mode_t mode;
+	uid_t uid;
+	gid_t gid;
+};
+
+struct execute_type {
+	char *argv[MAX_ARGS + 1];  /*  argv[0] must always be the programme  */
+};
+
+struct copy_type {
+	const char *source;
+	const char *destination;
+};
+
+struct action_type {
+	unsigned int what;
+	unsigned int when;
+};
+
+struct config_entry_struct {
+	struct action_type action;
+	regex_t preg;
+	union
+	{
+	struct permissions_type permissions;
+	struct execute_type execute;
+	struct copy_type copy;
+	}
+	u;
+	struct config_entry_struct *next;
+};
+
+struct get_variable_info {
+	const struct devfsd_notify_struct *info;
+	const char *devname;
+	char devpath[STRING_LENGTH];
+};
+
+static void dir_operation(int , const char * , int,  unsigned long*);
+static void service(struct stat statbuf, char *path);
+static int st_expr_expand(char *, unsigned, const char *, const char *(*)(const char *, void *), void *);
+static const char *get_old_name(const char *, unsigned, char *, unsigned, unsigned);
+static int mksymlink(const char *oldpath, const char *newpath);
+static void read_config_file(char *path, int optional, unsigned long *event_mask);
+static void process_config_line(const char *, unsigned long *);
+static int  do_servicing(int, unsigned long);
+static void service_name(const struct devfsd_notify_struct *);
+static void action_permissions(const struct devfsd_notify_struct *, const struct config_entry_struct *);
+static void action_execute(const struct devfsd_notify_struct *, const struct config_entry_struct *,
+							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);
+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);
+static int copy_inode(const char *, const struct stat *, mode_t, const char *, const struct stat *);
+static mode_t get_mode(const char *);
+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);
+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 *write_old_sd_name(char *, unsigned, unsigned, const char *);
+
+/* busybox functions */
+static int get_uid_gid(int flag, const char *string);
+static void safe_memcpy(char * dest, const char * src, int len);
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr);
+static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr);
+
+/* Structs and vars */
+static struct config_entry_struct *first_config = NULL;
+static struct config_entry_struct *last_config = NULL;
+static char *mount_point = NULL;
+static volatile int caught_signal = FALSE;
+static volatile int caught_sighup = FALSE;
+static struct initial_symlink_struct {
+	const char *dest;
+	const char *name;
+} initial_symlinks[] = {
+	{"/proc/self/fd", "fd"},
+	{"fd/0", "stdin"},
+	{"fd/1", "stdout"},
+	{"fd/2", "stderr"},
+	{NULL, NULL},
+};
+
+static struct event_type {
+	unsigned int type;        /*  The DEVFSD_NOTIFY_* value                  */
+	const char *config_name;  /*  The name used in the config file           */
+} event_types[] = {
+	{DEVFSD_NOTIFY_REGISTERED,   "REGISTER"},
+	{DEVFSD_NOTIFY_UNREGISTERED, "UNREGISTER"},
+	{DEVFSD_NOTIFY_ASYNC_OPEN,   "ASYNC_OPEN"},
+	{DEVFSD_NOTIFY_CLOSE,        "CLOSE"},
+	{DEVFSD_NOTIFY_LOOKUP,       "LOOKUP"},
+	{DEVFSD_NOTIFY_CHANGE,       "CHANGE"},
+	{DEVFSD_NOTIFY_CREATE,       "CREATE"},
+	{DEVFSD_NOTIFY_DELETE,       "DELETE"},
+	{0xffffffff,                 NULL}
+};
+
+/* Busybox messages */
+
+static const char bb_msg_proto_rev[] ALIGN1          = "protocol revision";
+static const char bb_msg_bad_config[] ALIGN1         = "bad %s config file: %s";
+static const char bb_msg_small_buffer[] ALIGN1       = "buffer too small";
+static const char bb_msg_variable_not_found[] ALIGN1 = "variable: %s not found";
+
+/* Busybox stuff */
+#if ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG
+#define info_logger(p, fmt, args...)                 bb_info_msg(fmt, ## args)
+#define msg_logger(p, fmt, args...)                  bb_error_msg(fmt, ## args)
+#define msg_logger_and_die(p, fmt, args...)          bb_error_msg_and_die(fmt, ## args)
+#define error_logger(p, fmt, args...)                bb_perror_msg(fmt, ## args)
+#define error_logger_and_die(p, fmt, args...)        bb_perror_msg_and_die(fmt, ## args)
+#else
+#define info_logger(p, fmt, args...)
+#define msg_logger(p, fmt, args...)
+#define msg_logger_and_die(p, fmt, args...)           exit(EXIT_FAILURE)
+#define error_logger(p, fmt, args...)
+#define error_logger_and_die(p, fmt, args...)         exit(EXIT_FAILURE)
+#endif
+
+static void safe_memcpy(char *dest, const char *src, int len)
+{
+	memcpy(dest , src, len);
+	dest[len] = '\0';
+}
+
+static unsigned int scan_dev_name_common(const char *d, unsigned int n, int addendum, const char *ptr)
+{
+	if (d[n - 4] == 'd' && d[n - 3] == 'i' && d[n - 2] == 's' && d[n - 1] == 'c')
+		return 2 + addendum;
+	if (d[n - 2] == 'c' && d[n - 1] == 'd')
+		return 3 + addendum;
+	if (ptr[0] == 'p' && ptr[1] == 'a' && ptr[2] == 'r' && ptr[3] == 't')
+		return 4 + addendum;
+	if (ptr[n - 2] == 'm' && ptr[n - 1] == 't')
+		return 5 + addendum;
+	return 0;
+}
+
+static unsigned int scan_dev_name(const char *d, unsigned int n, const char *ptr)
+{
+	if (d[0] == 's' && d[1] == 'c' && d[2] == 's' && d[3] == 'i' && d[4] == '/') {
+		if (d[n - 7] == 'g' && d[n - 6] == 'e' && d[n - 5] == 'n'
+			&& d[n - 4] == 'e' && d[n - 3] == 'r' && d[n - 2] == 'i' && d[n - 1] == 'c'
+		)
+			return 1;
+		return scan_dev_name_common(d, n, 0, ptr);
+	}
+	if (d[0] == 'i' && d[1] == 'd' && d[2] == 'e' && d[3] == '/'
+		&& d[4] == 'h' && d[5] == 'o' && d[6] == 's' && d[7] == 't'
+	)
+		return scan_dev_name_common(d, n, 4, ptr);
+	if (d[0] == 's' && d[1] == 'b' && d[2] == 'p' && d[3] == '/')
+		return 10;
+	if (d[0] == 'v' && d[1] == 'c' && d[2] == 'c' && d[3] == '/')
+		return 11;
+	if (d[0] == 'p' && d[1] == 't' && d[2] == 'y' && d[3] == '/')
+		return 12;
+	return 0;
+}
+
+/*  Public functions follow  */
+
+int devfsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int devfsd_main(int argc, char **argv)
+{
+	int print_version = FALSE;
+	int do_daemon = TRUE;
+	int no_polling = FALSE;
+	int do_scan;
+	int fd, proto_rev, count;
+	unsigned long event_mask = 0;
+	struct sigaction new_action;
+	struct initial_symlink_struct *curr;
+
+	if (argc < 2)
+		bb_show_usage();
+
+	for (count = 2; count < argc; ++count) {
+		if (argv[count][0] == '-') {
+			if (argv[count][1] == 'v' && !argv[count][2]) /* -v */
+				print_version = TRUE;
+			else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'f'
+			 && argv[count][2] == 'g' && !argv[count][3]) /* -fg */
+				do_daemon = FALSE;
+			else if (ENABLE_DEVFSD_FG_NP && argv[count][1] == 'n'
+			 && argv[count][2] == 'p' && !argv[count][3]) /* -np */
+				no_polling = TRUE;
+			else
+				bb_show_usage();
+		}
+	}
+
+	mount_point = bb_simplify_path(argv[1]);
+
+	xchdir(mount_point);
+
+	fd = xopen(".devfsd", O_RDONLY);
+	close_on_exec_on(fd);
+	xioctl(fd, DEVFSDIOC_GET_PROTO_REV, &proto_rev);
+
+	/*setup initial entries */
+	for (curr = initial_symlinks; curr->dest != NULL; ++curr)
+		symlink(curr->dest, curr->name);
+
+	/* NB: The check for CONFIG_FILE is done in read_config_file() */
+
+	if (print_version || (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)) {
+		printf("%s v%s\nDaemon %s:\t%d\nKernel-side %s:\t%d\n",
+				applet_name, DEVFSD_VERSION, bb_msg_proto_rev,
+				DEVFSD_PROTOCOL_REVISION_DAEMON, bb_msg_proto_rev, proto_rev);
+		if (DEVFSD_PROTOCOL_REVISION_DAEMON != proto_rev)
+			bb_error_msg_and_die("%s mismatch!", bb_msg_proto_rev);
+		exit(EXIT_SUCCESS); /* -v */
+	}
+	/*  Tell kernel we are special(i.e. we get to see hidden entries)  */
+	xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, 0);
+
+	/*  Set up SIGHUP and SIGUSR1 handlers  */
+	sigemptyset(&new_action.sa_mask);
+	new_action.sa_flags = 0;
+	new_action.sa_handler = signal_handler;
+	sigaction_set(SIGHUP, &new_action);
+	sigaction_set(SIGUSR1, &new_action);
+
+	printf("%s v%s started for %s\n", applet_name, DEVFSD_VERSION, mount_point);
+
+	/*  Set umask so that mknod(2), open(2) and mkdir(2) have complete control over permissions  */
+	umask(0);
+	read_config_file((char*)CONFIG_FILE, FALSE, &event_mask);
+	/*  Do the scan before forking, so that boot scripts see the finished product  */
+	dir_operation(SERVICE, mount_point, 0, NULL);
+
+	if (ENABLE_DEVFSD_FG_NP && no_polling)
+		exit(EXIT_SUCCESS);
+
+	if (ENABLE_DEVFSD_VERBOSE || ENABLE_DEBUG)
+		logmode = LOGMODE_BOTH;
+	else if (do_daemon == TRUE)
+		logmode = LOGMODE_SYSLOG;
+	/* This is the default */
+	/*else
+		logmode = LOGMODE_STDIO; */
+
+	if (do_daemon) {
+		/*  Release so that the child can grab it  */
+		xioctl(fd, DEVFSDIOC_RELEASE_EVENT_QUEUE, 0);
+		bb_daemonize_or_rexec(0, argv);
+	} else if (ENABLE_DEVFSD_FG_NP) {
+		setpgid(0, 0);  /*  Become process group leader                    */
+	}
+
+	while (TRUE) {
+		do_scan = do_servicing(fd, event_mask);
+
+		free_config();
+		read_config_file((char*)CONFIG_FILE, FALSE, &event_mask);
+		if (do_scan)
+			dir_operation(SERVICE, mount_point, 0, NULL);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) free(mount_point);
+}   /*  End Function main  */
+
+
+/*  Private functions follow  */
+
+static void read_config_file(char *path, int optional, unsigned long *event_mask)
+/*  [SUMMARY] Read a configuration database.
+    <path> The path to read the database from. If this is a directory, all
+    entries in that directory will be read(except hidden entries).
+    <optional> If TRUE, the routine will silently ignore a missing config file.
+    <event_mask> The event mask is written here. This is not initialised.
+    [RETURNS] Nothing.
+*/
+{
+	struct stat statbuf;
+	FILE *fp;
+	char buf[STRING_LENGTH];
+	char *line = NULL;
+	char *p;
+
+	if (stat(path, &statbuf) == 0) {
+		/* Don't read 0 length files: ignored */
+		/*if (statbuf.st_size == 0)
+				return;*/
+		if (S_ISDIR(statbuf.st_mode)) {
+			p = bb_simplify_path(path);
+			dir_operation(READ_CONFIG, p, 0, event_mask);
+			free(p);
+			return;
+		}
+		fp = fopen_for_read(path);
+		if (fp != NULL) {
+			while (fgets(buf, STRING_LENGTH, fp) != NULL) {
+				/*  Skip whitespace  */
+				line = buf;
+				line = skip_whitespace(line);
+				if (line[0] == '\0' || line[0] == '#')
+					continue;
+				process_config_line(line, event_mask);
+			}
+			fclose(fp);
+		} else {
+			goto read_config_file_err;
+		}
+	} else {
+read_config_file_err:
+		if (optional == 0 && errno == ENOENT)
+			error_logger_and_die(LOG_ERR, "read config file: %s", path);
+	}
+}   /*  End Function read_config_file   */
+
+static void process_config_line(const char *line, unsigned long *event_mask)
+/*  [SUMMARY] Process a line from a configuration file.
+    <line> The configuration line.
+    <event_mask> The event mask is written here. This is not initialised.
+    [RETURNS] Nothing.
+*/
+{
+	int  num_args, count;
+	struct config_entry_struct *new;
+	char p[MAX_ARGS][STRING_LENGTH];
+	char when[STRING_LENGTH], what[STRING_LENGTH];
+	char name[STRING_LENGTH];
+	const char *msg = "";
+	char *ptr;
+	int i;
+
+	/* !!!! Only Uppercase Keywords in devsfd.conf */
+	static const char options[] ALIGN1 =
+		"CLEAR_CONFIG\0""INCLUDE\0""OPTIONAL_INCLUDE\0"
+		"RESTORE\0""PERMISSIONS\0""MODLOAD\0""EXECUTE\0"
+		"COPY\0""IGNORE\0""MKOLDCOMPAT\0""MKNEWCOMPAT\0"
+		"RMOLDCOMPAT\0""RMNEWCOMPAT\0";
+
+	for (count = 0; count < MAX_ARGS; ++count)
+		p[count][0] = '\0';
+	num_args = sscanf(line, "%s %s %s %s %s %s %s %s %s %s",
+			when, name, what,
+			p[0], p[1], p[2], p[3], p[4], p[5], p[6]);
+
+	i = index_in_strings(options, when);
+
+	/* "CLEAR_CONFIG" */
+	if (i == 0) {
+		free_config();
+		*event_mask = 0;
+		return;
+	}
+
+	if (num_args < 2)
+		goto process_config_line_err;
+
+	/* "INCLUDE" & "OPTIONAL_INCLUDE" */
+	if (i == 1 || i == 2) {
+		st_expr_expand(name, STRING_LENGTH, name, get_variable, NULL);
+		info_logger(LOG_INFO, "%sinclude: %s", (toupper(when[0]) == 'I') ? "": "optional_", name);
+		read_config_file(name, (toupper(when[0]) == 'I') ? FALSE : TRUE, event_mask);
+		return;
+	}
+	/* "RESTORE" */
+	if (i == 3) {
+		dir_operation(RESTORE, name, strlen(name),NULL);
+		return;
+	}
+	if (num_args < 3)
+		goto process_config_line_err;
+
+	new = xzalloc(sizeof *new);
+
+	for (count = 0; event_types[count].config_name != NULL; ++count) {
+		if (strcasecmp(when, event_types[count].config_name) != 0)
+			continue;
+		new->action.when = event_types[count].type;
+		break;
+	}
+	if (event_types[count].config_name == NULL) {
+		msg = "WHEN in";
+		goto process_config_line_err;
+	}
+
+	i = index_in_strings(options, what);
+
+	switch (i) {
+		case 4:	/* "PERMISSIONS" */
+			new->action.what = AC_PERMISSIONS;
+			/*  Get user and group  */
+			ptr = strchr(p[0], '.');
+			if (ptr == NULL) {
+				msg = "UID.GID";
+				goto process_config_line_err; /*"missing '.' in UID.GID"*/
+			}
+
+			*ptr++ = '\0';
+			new->u.permissions.uid = get_uid_gid(UID, p[0]);
+			new->u.permissions.gid = get_uid_gid(GID, ptr);
+			/*  Get mode  */
+			new->u.permissions.mode = get_mode(p[1]);
+			break;
+		case 5:	/*  MODLOAD */
+			/*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)
+				new->action.what = AC_MODLOAD;
+			 break;
+		case 6: /* EXECUTE */
+			new->action.what = AC_EXECUTE;
+			num_args -= 3;
+
+			for (count = 0; count < num_args; ++count)
+				new->u.execute.argv[count] = xstrdup(p[count]);
+
+			new->u.execute.argv[num_args] = NULL;
+			break;
+		case 7: /* COPY */
+			new->action.what = AC_COPY;
+			num_args -= 3;
+			if (num_args != 2)
+				goto process_config_line_err; /* missing path and function in line */
+
+			new->u.copy.source = xstrdup(p[0]);
+			new->u.copy.destination = xstrdup(p[1]);
+			break;
+		case 8: /* IGNORE */
+		/* FALLTROUGH */
+		case 9: /* MKOLDCOMPAT */
+		/* FALLTROUGH */
+		case 10: /* MKNEWCOMPAT */
+		/* FALLTROUGH */
+		case 11:/* RMOLDCOMPAT */
+		/* FALLTROUGH */
+		case 12: /* RMNEWCOMPAT */
+		/*	AC_IGNORE					6
+			AC_MKOLDCOMPAT				7
+			AC_MKNEWCOMPAT				8
+			AC_RMOLDCOMPAT				9
+			AC_RMNEWCOMPAT				10*/
+			new->action.what = i - 2;
+			break;
+		default:
+			msg = "WHAT in";
+			goto process_config_line_err;
+		/*esac*/
+	} /* switch (i) */
+
+	xregcomp(&new->preg, name, REG_EXTENDED);
+
+	*event_mask |= 1 << new->action.when;
+	new->next = NULL;
+	if (first_config == NULL)
+		first_config = new;
+	else
+		last_config->next = new;
+	last_config = new;
+	return;
+
+ process_config_line_err:
+	msg_logger_and_die(LOG_ERR, bb_msg_bad_config, msg , line);
+}  /*  End Function process_config_line   */
+
+static int do_servicing(int fd, unsigned long event_mask)
+/*  [SUMMARY] Service devfs changes until a signal is received.
+    <fd> The open control file.
+    <event_mask> The event mask.
+    [RETURNS] TRUE if SIGHUP was caught, else FALSE.
+*/
+{
+	ssize_t bytes;
+	struct devfsd_notify_struct info;
+
+	/* (void*) cast is only in order to match prototype */
+	xioctl(fd, DEVFSDIOC_SET_EVENT_MASK, (void*)event_mask);
+	while (!caught_signal) {
+		errno = 0;
+		bytes = read(fd, (char *) &info, sizeof info);
+		if (caught_signal)
+			break;      /*  Must test for this first     */
+		if (errno == EINTR)
+			continue;  /*  Yes, the order is important  */
+		if (bytes < 1)
+			break;
+		service_name(&info);
+	}
+	if (caught_signal) {
+		int c_sighup = caught_sighup;
+
+		caught_signal = FALSE;
+		caught_sighup = FALSE;
+		return c_sighup;
+	}
+	msg_logger_and_die(LOG_ERR, "read error on control file");
+}   /*  End Function do_servicing  */
+
+static void service_name(const struct devfsd_notify_struct *info)
+/*  [SUMMARY] Service a single devfs change.
+    <info> The devfs change.
+    [RETURNS] Nothing.
+*/
+{
+	unsigned int n;
+	regmatch_t mbuf[MAX_SUBEXPR];
+	struct config_entry_struct *entry;
+
+	if (ENABLE_DEBUG && info->overrun_count > 0)
+		msg_logger(LOG_ERR, "lost %u events", info->overrun_count);
+
+	/*  Discard lookups on "/dev/log" and "/dev/initctl"  */
+	if (info->type == DEVFSD_NOTIFY_LOOKUP
+		&& ((info->devname[0] == 'l' && info->devname[1] == 'o'
+		&& info->devname[2] == 'g' && !info->devname[3])
+		|| (info->devname[0] == 'i' && info->devname[1] == 'n'
+		&& info->devname[2] == 'i' && info->devname[3] == 't'
+		&& info->devname[4] == 'c' && info->devname[5] == 't'
+		&& info->devname[6] == 'l' && !info->devname[7]))
+	)
+		return;
+
+	for (entry = first_config; entry != NULL; entry = entry->next) {
+		/*  First check if action matches the type, then check if name matches */
+		if (info->type != entry->action.when
+		|| regexec(&entry->preg, info->devname, MAX_SUBEXPR, mbuf, 0) != 0)
+			continue;
+		for (n = 0;(n < MAX_SUBEXPR) && (mbuf[n].rm_so != -1); ++n)
+			/* VOID */;
+
+		switch (entry->action.what) {
+			case AC_PERMISSIONS:
+				action_permissions(info, entry);
+				break;
+			case AC_MODLOAD:
+				if (ENABLE_DEVFSD_MODLOAD)
+					action_modload(info, entry);
+				break;
+			case AC_EXECUTE:
+				action_execute(info, entry, mbuf, n);
+				break;
+			case AC_COPY:
+				action_copy(info, entry, mbuf, n);
+				break;
+			case AC_IGNORE:
+				return;
+				/*break;*/
+			case AC_MKOLDCOMPAT:
+			case AC_MKNEWCOMPAT:
+			case AC_RMOLDCOMPAT:
+			case AC_RMNEWCOMPAT:
+				action_compat(info, entry->action.what);
+				break;
+			default:
+				msg_logger_and_die(LOG_ERR, "Unknown action");
+		}
+	}
+}   /*  End Function service_name  */
+
+static void action_permissions(const struct devfsd_notify_struct *info,
+				const struct config_entry_struct *entry)
+/*  [SUMMARY] Update permissions for a device entry.
+    <info> The devfs change.
+    <entry> The config file entry.
+    [RETURNS] Nothing.
+*/
+{
+	struct stat statbuf;
+
+	if (stat(info->devname, &statbuf) != 0
+	 || chmod(info->devname, (statbuf.st_mode & S_IFMT) | (entry->u.permissions.mode & ~S_IFMT)) != 0
+	 || chown(info->devname, entry->u.permissions.uid, entry->u.permissions.gid) != 0
+	)
+		error_logger(LOG_ERR, "Can't chmod or chown: %s", info->devname);
+}   /*  End Function action_permissions  */
+
+static void action_modload(const struct devfsd_notify_struct *info,
+			    const struct config_entry_struct *entry UNUSED_PARAM)
+/*  [SUMMARY] Load a module.
+    <info> The devfs change.
+    <entry> The config file entry.
+    [RETURNS] Nothing.
+*/
+{
+	char *argv[6];
+
+	argv[0] = (char*)MODPROBE;
+	argv[1] = (char*)MODPROBE_SWITCH_1; /* "-k" */
+	argv[2] = (char*)MODPROBE_SWITCH_2; /* "-C" */
+	argv[3] = (char*)CONFIG_MODULES_DEVFS;
+	argv[4] = concat_path_file("/dev", info->devname); /* device */
+	argv[5] = NULL;
+
+	spawn_and_wait(argv);
+	free(argv[4]);
+}  /*  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)
+/*  [SUMMARY] Execute a programme.
+    <info> The devfs change.
+    <entry> The config file entry.
+    <regexpr> The number of subexpression(start, end) offsets within the
+    device name.
+    <numexpr> The number of elements within <<regexpr>>.
+    [RETURNS] Nothing.
+*/
+{
+	unsigned int count;
+	struct get_variable_info gv_info;
+	char *argv[MAX_ARGS + 1];
+	char largv[MAX_ARGS + 1][STRING_LENGTH];
+
+	gv_info.info = info;
+	gv_info.devname = info->devname;
+	snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname);
+	for (count = 0; entry->u.execute.argv[count] != NULL; ++count) {
+		expand_expression(largv[count], STRING_LENGTH,
+				entry->u.execute.argv[count],
+				get_variable, &gv_info,
+				gv_info.devname, regexpr, numexpr);
+		argv[count] = largv[count];
+	}
+	argv[count] = NULL;
+	spawn_and_wait(argv);
+}   /*  End Function action_execute  */
+
+
+static void action_copy(const struct devfsd_notify_struct *info,
+			 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.
+    <regexpr> This list of subexpression(start, end) offsets within the
+    device name.
+    <numexpr> The number of elements in <<regexpr>>.
+    [RETURNS] Nothing.
+*/
+{
+	mode_t new_mode;
+	struct get_variable_info gv_info;
+	struct stat source_stat, dest_stat;
+	char source[STRING_LENGTH], destination[STRING_LENGTH];
+	int ret = 0;
+
+	dest_stat.st_mode = 0;
+
+	if ((info->type == DEVFSD_NOTIFY_CHANGE) && S_ISLNK(info->mode))
+		return;
+	gv_info.info = info;
+	gv_info.devname = info->devname;
+
+	snprintf(gv_info.devpath, sizeof(gv_info.devpath), "%s/%s", mount_point, info->devname);
+	expand_expression(source, STRING_LENGTH, entry->u.copy.source,
+				get_variable, &gv_info, gv_info.devname,
+				regexpr, numexpr);
+
+	expand_expression(destination, STRING_LENGTH, entry->u.copy.destination,
+				get_variable, &gv_info, gv_info.devname,
+				regexpr, numexpr);
+
+	if (!make_dir_tree(destination) || lstat(source, &source_stat) != 0)
+			return;
+	lstat(destination, &dest_stat);
+	new_mode = source_stat.st_mode & ~S_ISVTX;
+	if (info->type == DEVFSD_NOTIFY_CREATE)
+		new_mode |= S_ISVTX;
+	else if ((info->type == DEVFSD_NOTIFY_CHANGE) &&(dest_stat.st_mode & S_ISVTX))
+		new_mode |= S_ISVTX;
+	ret = copy_inode(destination, &dest_stat, new_mode, source, &source_stat);
+	if (ENABLE_DEBUG && ret && (errno != EEXIST))
+		error_logger(LOG_ERR, "copy_inode: %s to %s", source, destination);
+}   /*  End Function action_copy  */
+
+static void action_compat(const struct devfsd_notify_struct *info, unsigned int action)
+/*  [SUMMARY] Process a compatibility request.
+    <info> The devfs change.
+    <action> The action to take.
+    [RETURNS] Nothing.
+*/
+{
+	int ret;
+	const char *compat_name = NULL;
+	const char *dest_name = info->devname;
+	const char *ptr;
+	char compat_buf[STRING_LENGTH], dest_buf[STRING_LENGTH];
+	int mode, host, bus, target, lun;
+	unsigned int i;
+	char rewind_;
+	/* 1 to 5  "scsi/" , 6 to 9 "ide/host" */
+	static const char *const fmt[] = {
+		NULL ,
+		"sg/c%db%dt%du%d",		/* scsi/generic */
+		"sd/c%db%dt%du%d",		/* scsi/disc */
+		"sr/c%db%dt%du%d",		/* scsi/cd */
+		"sd/c%db%dt%du%dp%d",		/* scsi/part */
+		"st/c%db%dt%du%dm%d%c",		/* scsi/mt */
+		"ide/hd/c%db%dt%du%d",		/* ide/host/disc */
+		"ide/cd/c%db%dt%du%d",		/* ide/host/cd */
+		"ide/hd/c%db%dt%du%dp%d",	/* ide/host/part */
+		"ide/mt/c%db%dt%du%d%s",	/* ide/host/mt */
+		NULL
+	};
+
+	/*  First construct compatibility name  */
+	switch (action) {
+		case AC_MKOLDCOMPAT:
+		case AC_RMOLDCOMPAT:
+			compat_name = get_old_name(info->devname, info->namelen, compat_buf, info->major, info->minor);
+			break;
+		case AC_MKNEWCOMPAT:
+		case AC_RMNEWCOMPAT:
+			ptr = bb_basename(info->devname);
+			i = scan_dev_name(info->devname, info->namelen, ptr);
+
+			/* nothing found */
+			if (i == 0 || i > 9)
+				return;
+
+			sscanf(info->devname + ((i < 6) ? 5 : 4), "host%d/bus%d/target%d/lun%d/", &host, &bus, &target, &lun);
+			snprintf(dest_buf, sizeof(dest_buf), "../%s", info->devname + (( i > 5) ? 4 : 0));
+			dest_name = dest_buf;
+			compat_name = compat_buf;
+
+
+			/* 1 == scsi/generic  2 == scsi/disc 3 == scsi/cd 6 == ide/host/disc 7 == ide/host/cd */
+			if (i == 1 || i == 2 || i == 3 || i == 6 || i ==7)
+				sprintf(compat_buf, fmt[i], host, bus, target, lun);
+
+			/* 4 == scsi/part 8 == ide/host/part */
+			if (i == 4 || i == 8)
+				sprintf(compat_buf, fmt[i], host, bus, target, lun, atoi(ptr + 4));
+
+			/* 5 == scsi/mt */
+			if (i == 5) {
+				rewind_ = info->devname[info->namelen - 1];
+				if (rewind_ != 'n')
+					rewind_ = '\0';
+				mode=0;
+				if (ptr[2] ==  'l' /*108*/ || ptr[2] == 'm'/*109*/)
+					mode = ptr[2] - 107; /* 1 or 2 */
+				if (ptr[2] ==  'a')
+					mode = 3;
+				sprintf(compat_buf, fmt[i], host, bus, target, lun, mode, rewind_);
+			}
+
+			/* 9 == ide/host/mt */
+			if (i ==  9)
+				snprintf(compat_buf, sizeof(compat_buf), fmt[i], host, bus, target, lun, ptr + 2);
+		/* esac */
+	} /* switch (action) */
+
+	if (compat_name == NULL)
+		return;
+
+	/*  Now decide what to do with it  */
+	switch (action) {
+		case AC_MKOLDCOMPAT:
+		case AC_MKNEWCOMPAT:
+			mksymlink(dest_name, compat_name);
+			break;
+		case AC_RMOLDCOMPAT:
+		case AC_RMNEWCOMPAT:
+			ret = unlink(compat_name);
+			if (ENABLE_DEBUG && ret)
+				error_logger(LOG_ERR, "unlink: %s", compat_name);
+			break;
+		/*esac*/
+	} /* switch (action) */
+}   /*  End Function action_compat  */
+
+static void restore(char *spath, struct stat source_stat, int rootlen)
+{
+	char *dpath;
+	struct stat dest_stat;
+
+	dest_stat.st_mode = 0;
+	dpath = concat_path_file(mount_point, spath + rootlen);
+	lstat(dpath, &dest_stat);
+	free(dpath);
+	if (S_ISLNK(source_stat.st_mode) || (source_stat.st_mode & S_ISVTX))
+		copy_inode(dpath, &dest_stat, (source_stat.st_mode & ~S_ISVTX), spath, &source_stat);
+
+	if (S_ISDIR(source_stat.st_mode))
+		dir_operation(RESTORE, spath, rootlen, NULL);
+}
+
+
+static int copy_inode(const char *destpath, const struct stat *dest_stat,
+			mode_t new_mode,
+			const char *sourcepath, const struct stat *source_stat)
+/*  [SUMMARY] Copy an inode.
+    <destpath> The destination path. An existing inode may be deleted.
+    <dest_stat> The destination stat(2) information.
+    <new_mode> The desired new mode for the destination.
+    <sourcepath> The source path.
+    <source_stat> The source stat(2) information.
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+	int source_len, dest_len;
+	char source_link[STRING_LENGTH], dest_link[STRING_LENGTH];
+	int fd, val;
+	struct sockaddr_un un_addr;
+	char symlink_val[STRING_LENGTH];
+
+	if ((source_stat->st_mode & S_IFMT) ==(dest_stat->st_mode & S_IFMT)) {
+		/*  Same type  */
+		if (S_ISLNK(source_stat->st_mode)) {
+			source_len = readlink(sourcepath, source_link, STRING_LENGTH - 1);
+			if ((source_len < 0)
+			 || (dest_len = readlink(destpath, dest_link, STRING_LENGTH - 1)) < 0
+			)
+				return FALSE;
+			source_link[source_len]	= '\0';
+			dest_link[dest_len]	= '\0';
+			if ((source_len != dest_len) || (strcmp(source_link, dest_link) != 0)) {
+				unlink(destpath);
+				symlink(source_link, destpath);
+			}
+			return TRUE;
+		}   /*  Else not a symlink  */
+		chmod(destpath, new_mode & ~S_IFMT);
+		chown(destpath, source_stat->st_uid, source_stat->st_gid);
+		return TRUE;
+	}
+	/*  Different types: unlink and create  */
+	unlink(destpath);
+	switch (source_stat->st_mode & S_IFMT) {
+		case S_IFSOCK:
+			fd = socket(AF_UNIX, SOCK_STREAM, 0);
+			if (fd < 0)
+				break;
+			un_addr.sun_family = AF_UNIX;
+			snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s", destpath);
+			val = bind(fd, (struct sockaddr *) &un_addr, (int) sizeof un_addr);
+			close(fd);
+			if (val != 0 || chmod(destpath, new_mode & ~S_IFMT) != 0)
+				break;
+			goto do_chown;
+		case S_IFLNK:
+			val = readlink(sourcepath, symlink_val, STRING_LENGTH - 1);
+			if (val < 0)
+				break;
+			symlink_val[val] = '\0';
+			if (symlink(symlink_val, destpath) == 0)
+				return TRUE;
+			break;
+		case S_IFREG:
+			fd = open(destpath, O_RDONLY | O_CREAT, new_mode & ~S_IFMT);
+			if (fd < 0)
+				break;
+			close(fd);
+			if (chmod(destpath, new_mode & ~S_IFMT) != 0)
+				break;
+			goto do_chown;
+		case S_IFBLK:
+		case S_IFCHR:
+		case S_IFIFO:
+			if (mknod(destpath, new_mode, source_stat->st_rdev) != 0)
+				break;
+			goto do_chown;
+		case S_IFDIR:
+			if (mkdir(destpath, new_mode & ~S_IFMT) != 0)
+				break;
+do_chown:
+			if (chown(destpath, source_stat->st_uid, source_stat->st_gid) == 0)
+				return TRUE;
+		/*break;*/
+	}
+	return FALSE;
+}   /*  End Function copy_inode  */
+
+static void free_config(void)
+/*  [SUMMARY] Free the configuration information.
+    [RETURNS] Nothing.
+*/
+{
+	struct config_entry_struct *c_entry;
+	void *next;
+
+	for (c_entry = first_config; c_entry != NULL; c_entry = next) {
+		unsigned int count;
+
+		next = c_entry->next;
+		regfree(&c_entry->preg);
+		if (c_entry->action.what == AC_EXECUTE) {
+			for (count = 0; count < MAX_ARGS; ++count) {
+				if (c_entry->u.execute.argv[count] == NULL)
+					break;
+				free(c_entry->u.execute.argv[count]);
+			}
+		}
+		free(c_entry);
+	}
+	first_config = NULL;
+	last_config = NULL;
+}   /*  End Function free_config  */
+
+static int get_uid_gid(int flag, const char *string)
+/*  [SUMMARY] Convert a string to a UID or GID value.
+	<flag> "UID" or "GID".
+	<string> The string.
+    [RETURNS] The UID or GID value.
+*/
+{
+	struct passwd *pw_ent;
+	struct group *grp_ent;
+	static const char *msg;
+
+	if (ENABLE_DEVFSD_VERBOSE)
+		msg = "user";
+
+	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_logger(LOG_ERR, "unknown %s: %s, defaulting to %cid=0",  msg, string, msg[0]);
+	return 0;
+}/*  End Function get_uid_gid  */
+
+static mode_t get_mode(const char *string)
+/*  [SUMMARY] Convert a string to a mode value.
+    <string> The string.
+    [RETURNS] The mode value.
+*/
+{
+	mode_t mode;
+	int i;
+
+	if (isdigit(string[0]))
+		return strtoul(string, NULL, 8);
+	if (strlen(string) != 9)
+		msg_logger_and_die(LOG_ERR, "bad mode: %s", string);
+
+	mode = 0;
+	i = S_IRUSR;
+	while (i > 0) {
+		if (string[0] == 'r' || string[0] == 'w' || string[0] == 'x')
+			mode += i;
+		i = i / 2;
+		string++;
+	}
+	return mode;
+}   /*  End Function get_mode  */
+
+static void signal_handler(int sig)
+{
+	caught_signal = TRUE;
+	if (sig == SIGHUP)
+		caught_sighup = TRUE;
+
+	info_logger(LOG_INFO, "Caught signal %d", sig);
+}   /*  End Function signal_handler  */
+
+static const char *get_variable(const char *variable, void *info)
+{
+	static char sbuf[sizeof(int)*3 + 2]; /* sign and NUL */
+	static char *hostname;
+
+	struct get_variable_info *gv_info = info;
+	const char *field_names[] = {
+			"hostname", "mntpt", "devpath", "devname",
+			"uid", "gid", "mode", hostname, mount_point,
+			gv_info->devpath, gv_info->devname, NULL
+	};
+	int i;
+
+	if (!hostname)
+		hostname = safe_gethostname();
+	/* index_in_str_array returns i>=0  */
+	i = index_in_str_array(field_names, variable);
+
+	if (i > 6 || i < 0 || (i > 1 && gv_info == NULL))
+		return NULL;
+	if (i >= 0 && i <= 3)
+		return field_names[i + 7];
+
+	if (i == 4)
+		sprintf(sbuf, "%u", gv_info->info->uid);
+	else if (i == 5)
+		sprintf(sbuf, "%u", gv_info->info->gid);
+	else if (i == 6)
+		sprintf(sbuf, "%o", gv_info->info->mode);
+	return sbuf;
+}   /*  End Function get_variable  */
+
+static void service(struct stat statbuf, char *path)
+{
+	struct devfsd_notify_struct info;
+
+	memset(&info, 0, sizeof info);
+	info.type = DEVFSD_NOTIFY_REGISTERED;
+	info.mode = statbuf.st_mode;
+	info.major = major(statbuf.st_rdev);
+	info.minor = minor(statbuf.st_rdev);
+	info.uid = statbuf.st_uid;
+	info.gid = statbuf.st_gid;
+	snprintf(info.devname, sizeof(info.devname), "%s", path + strlen(mount_point) + 1);
+	info.namelen = strlen(info.devname);
+	service_name(&info);
+	if (S_ISDIR(statbuf.st_mode))
+		dir_operation(SERVICE, path, 0, NULL);
+}
+
+static void dir_operation(int type, const char * dir_name, int var, unsigned long *event_mask)
+/*  [SUMMARY] Scan a directory tree and generate register events on leaf nodes.
+	<flag> To choose which function to perform
+	<dp> The directory pointer. This is closed upon completion.
+    <dir_name> The name of the directory.
+	<rootlen> string length parameter.
+    [RETURNS] Nothing.
+*/
+{
+	struct stat statbuf;
+	DIR *dp;
+	struct dirent *de;
+	char *path;
+
+	dp = warn_opendir(dir_name);
+	if (dp == NULL)
+		return;
+
+	while ((de = readdir(dp)) != NULL) {
+
+		if (de->d_name && DOT_OR_DOTDOT(de->d_name))
+			continue;
+		path = concat_path_file(dir_name, de->d_name);
+		if (lstat(path, &statbuf) == 0) {
+			switch (type) {
+				case SERVICE:
+					service(statbuf, path);
+					break;
+				case RESTORE:
+					restore(path, statbuf, var);
+					break;
+				case READ_CONFIG:
+					read_config_file(path, var, event_mask);
+					break;
+			}
+		}
+		free(path);
+	}
+	closedir(dp);
+}   /*  End Function do_scan_and_service  */
+
+static int mksymlink(const char *oldpath, const char *newpath)
+/*  [SUMMARY] Create a symlink, creating intervening directories as required.
+    <oldpath> The string contained in the symlink.
+    <newpath> The name of the new symlink.
+    [RETURNS] 0 on success, else -1.
+*/
+{
+	if (!make_dir_tree(newpath))
+		return -1;
+
+	if (symlink(oldpath, newpath) != 0) {
+		if (errno != EEXIST)
+			return -1;
+	}
+	return 0;
+}   /*  End Function mksymlink  */
+
+
+static int make_dir_tree(const char *path)
+/*  [SUMMARY] Creating intervening directories for a path as required.
+    <path> The full pathname(including the leaf node).
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+	if (bb_make_directory(dirname((char *)path), -1, FILEUTILS_RECUR) == -1)
+		return FALSE;
+	return TRUE;
+} /*  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)
+/*  [SUMMARY] Expand environment variables and regular subexpressions in string.
+    <output> The output expanded expression is written here.
+    <length> The size of the output buffer.
+    <input> The input expression. This may equal <<output>>.
+    <get_variable> A function which will be used to get variable values. If
+    this returns NULL, the environment is searched instead. If this is NULL,
+    only the environment is searched.
+    <info> An arbitrary pointer passed to <<get_variable>>.
+    <devname> Device name; specifically, this is the string that contains all
+    of the regular subexpressions.
+    <ex> Array of start / end offsets into info->devname for each subexpression
+    <numexp> Number of regular subexpressions found in <<devname>>.
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+	char temp[STRING_LENGTH];
+
+	if (!st_expr_expand(temp, STRING_LENGTH, input, get_variable_func, info))
+		return FALSE;
+	expand_regexp(output, outsize, temp, devname, ex, numexp);
+	return TRUE;
+}   /*  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)
+/*  [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.
+    <input> The input expression. This may NOT equal <<output>>, because
+    supporting that would require yet another string-copy. However, it's not
+    hard to write a simple wrapper function to add this functionality for those
+    few cases that need it.
+    <devname> Device name; specifically, this is the string that contains all
+    of the regular subexpressions.
+    <ex> An array of start and end offsets into <<devname>>, one for each
+    subexpression
+    <numex> Number of subexpressions in the offset-array <<ex>>.
+    [RETURNS] Nothing.
+*/
+{
+	const char last_exp = '0' - 1 + numex;
+	int c = -1;
+
+	/*  Guarantee NULL termination by writing an explicit '\0' character into
+	the very last byte  */
+	if (outsize)
+		output[--outsize] = '\0';
+	/*  Copy the input string into the output buffer, replacing '\\' with '\'
+	and '\0' .. '\9' with subexpressions 0 .. 9, if they exist. Other \x
+	codes are deleted  */
+	while ((c != '\0') && (outsize != 0)) {
+		c = *input;
+		++input;
+		if (c == '\\') {
+			c = *input;
+			++input;
+			if (c != '\\') {
+				if ((c >= '0') && (c <= last_exp)) {
+					const regmatch_t *subexp = ex + (c - '0');
+					unsigned int sublen = subexp->rm_eo - subexp->rm_so;
+
+					/*  Range checking  */
+					if (sublen > outsize)
+						sublen = outsize;
+					strncpy(output, devname + subexp->rm_so, sublen);
+					output += sublen;
+					outsize -= sublen;
+				}
+				continue;
+			}
+		}
+		*output = c;
+		++output;
+		--outsize;
+	} /* while */
+}   /*  End Function expand_regexp  */
+
+
+/* from compat_name.c */
+
+struct translate_struct {
+	const char *match;    /*  The string to match to(up to length)                */
+	const char *format;   /*  Format of output, "%s" takes data past match string,
+			NULL is effectively "%s"(just more efficient)       */
+};
+
+static struct translate_struct translate_table[] =
+{
+	{"sound/",     NULL},
+	{"printers/",  "lp%s"},
+	{"v4l/",       NULL},
+	{"parports/",  "parport%s"},
+	{"fb/",        "fb%s"},
+	{"netlink/",   NULL},
+	{"loop/",      "loop%s"},
+	{"floppy/",    "fd%s"},
+	{"rd/",        "ram%s"},
+	{"md/",        "md%s"},         /*  Meta-devices                         */
+	{"vc/",        "tty%s"},
+	{"misc/",      NULL},
+	{"isdn/",      NULL},
+	{"pg/",        "pg%s"},         /*  Parallel port generic ATAPI interface*/
+	{"i2c/",       "i2c-%s"},
+	{"staliomem/", "staliomem%s"},  /*  Stallion serial driver control       */
+	{"tts/E",      "ttyE%s"},       /*  Stallion serial driver               */
+	{"cua/E",      "cue%s"},        /*  Stallion serial driver callout       */
+	{"tts/R",      "ttyR%s"},       /*  Rocketport serial driver             */
+	{"cua/R",      "cur%s"},        /*  Rocketport serial driver callout     */
+	{"ip2/",       "ip2%s"},        /*  Computone serial driver control      */
+	{"tts/F",      "ttyF%s"},       /*  Computone serial driver              */
+	{"cua/F",      "cuf%s"},        /*  Computone serial driver callout      */
+	{"tts/C",      "ttyC%s"},       /*  Cyclades serial driver               */
+	{"cua/C",      "cub%s"},        /*  Cyclades serial driver callout       */
+	{"tts/",       "ttyS%s"},       /*  Generic serial: must be after others */
+	{"cua/",       "cua%s"},        /*  Generic serial: must be after others */
+	{"input/js",   "js%s"},         /*  Joystick driver                      */
+	{NULL,         NULL}
+};
+
+const char *get_old_name(const char *devname, unsigned int namelen,
+			  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.
+    <buffer> A buffer that may be used. This should be at least 128 bytes long.
+    <major> The major number for the device.
+    <minor> The minor number for the device.
+    [RETURNS] A pointer to the old name if known, else NULL.
+*/
+{
+	const char *compat_name = NULL;
+	const char *ptr;
+	struct translate_struct *trans;
+	unsigned int i;
+	char mode;
+	int indexx;
+	const char *pty1;
+	const char *pty2;
+	size_t len;
+	/* 1 to 5  "scsi/" , 6 to 9 "ide/host", 10 sbp/, 11 vcc/, 12 pty/ */
+	static const char *const fmt[] = {
+		NULL ,
+		"sg%u",			/* scsi/generic */
+		NULL,			/* scsi/disc */
+		"sr%u",			/* scsi/cd */
+		NULL,			/* scsi/part */
+		"nst%u%c",		/* scsi/mt */
+		"hd%c"	,		/* ide/host/disc */
+		"hd%c"	,		/* ide/host/cd */
+		"hd%c%s",		/* ide/host/part */
+		"%sht%d",		/* ide/host/mt */
+		"sbpcd%u",		/* sbp/ */
+		"vcs%s",		/* vcc/ */
+		"%cty%c%c",		/* pty/ */
+		NULL
+	};
+
+	for (trans = translate_table; trans->match != NULL; ++trans) {
+		 len = strlen(trans->match);
+
+		if (strncmp(devname, trans->match, len) == 0) {
+			if (trans->format == NULL)
+				return devname + len;
+			sprintf(buffer, trans->format, devname + len);
+			return buffer;
+		}
+	}
+
+	ptr = bb_basename(devname);
+	i = scan_dev_name(devname, namelen, ptr);
+
+	if (i > 0 && i < 13)
+		compat_name = buffer;
+	else
+		return NULL;
+
+	/* 1 == scsi/generic, 3 == scsi/cd, 10 == sbp/ */
+	if (i == 1 || i == 3 || i == 10)
+		sprintf(buffer, fmt[i], minor);
+
+	/* 2 ==scsi/disc, 4 == scsi/part */
+	if (i == 2 || i == 4)
+		compat_name = write_old_sd_name(buffer, major, minor, ((i == 2) ? "" : (ptr + 4)));
+
+	/* 5 == scsi/mt */
+	if (i == 5) {
+		mode = ptr[2];
+		if (mode == 'n')
+			mode = '\0';
+		sprintf(buffer, fmt[i], minor & 0x1f, mode);
+		if (devname[namelen - 1] != 'n')
+			++compat_name;
+	}
+	/* 6 == ide/host/disc, 7 == ide/host/cd, 8 == ide/host/part */
+	if (i == 6 || i == 7 || i == 8)
+		/* last arg should be ignored for i == 6 or i== 7 */
+		sprintf(buffer, fmt[i] , get_old_ide_name(major, minor), ptr + 4);
+
+	/* 9 ==  ide/host/mt */
+	if (i == 9)
+		sprintf(buffer, fmt[i], ptr + 2, minor & 0x7f);
+
+	/*  11 == vcc/ */
+	if (i == 11) {
+		sprintf(buffer, fmt[i], devname + 4);
+		if (buffer[3] == '0')
+			buffer[3] = '\0';
+	}
+	/* 12 ==  pty/ */
+	if (i == 12) {
+		pty1 = "pqrstuvwxyzabcde";
+		pty2 = "0123456789abcdef";
+		indexx = atoi(devname + 5);
+		sprintf(buffer, fmt[i], (devname[4] == 'm') ? 'p' : 't', pty1[indexx >> 4], pty2[indexx & 0x0f]);
+	}
+	return compat_name;
+}   /*  End Function get_old_name  */
+
+static char get_old_ide_name(unsigned int major, unsigned int minor)
+/*  [SUMMARY] Get the old IDE name for a device.
+    <major> The major number for the device.
+    <minor> The minor number for the device.
+    [RETURNS] The drive letter.
+*/
+{
+	char letter = 'y';	/* 121 */
+	char c = 'a';		/*  97 */
+	int i = IDE0_MAJOR;
+
+	/* I hope it works like the previous code as it saves a few bytes. Tito ;P */
+	do {
+		if (i == IDE0_MAJOR || i == IDE1_MAJOR || i == IDE2_MAJOR
+		 || i == IDE3_MAJOR || i == IDE4_MAJOR || i == IDE5_MAJOR
+		 || i == IDE6_MAJOR || i == IDE7_MAJOR || i == IDE8_MAJOR
+		 || i == IDE9_MAJOR
+		) {
+			if ((unsigned int)i == major) {
+				letter = c;
+				break;
+			}
+			c += 2;
+		}
+		i++;
+	} while (i <= IDE9_MAJOR);
+
+	if (minor > 63)
+		++letter;
+	return letter;
+}   /*  End Function get_old_ide_name  */
+
+static char *write_old_sd_name(char *buffer,
+				unsigned int major, unsigned int minor,
+				const char *part)
+/*  [SUMMARY] Write the old SCSI disc name to a buffer.
+    <buffer> The buffer to write to.
+    <major> The major number for the device.
+    <minor> The minor number for the device.
+    <part> The partition string. Must be "" for a whole-disc entry.
+    [RETURNS] A pointer to the buffer on success, else NULL.
+*/
+{
+	unsigned int disc_index;
+
+	if (major == 8) {
+		sprintf(buffer, "sd%c%s", 'a' + (minor >> 4), part);
+		return buffer;
+	}
+	if ((major > 64) && (major < 72)) {
+		disc_index = ((major - 64) << 4) +(minor >> 4);
+		if (disc_index < 26)
+			sprintf(buffer, "sd%c%s", 'a' + disc_index, part);
+		else
+			sprintf(buffer, "sd%c%c%s", 'a' +(disc_index / 26) - 1, 'a' + disc_index % 26, part);
+		return buffer;
+	}
+	return NULL;
+}   /*  End Function write_old_sd_name  */
+
+
+/*  expression.c */
+
+/*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)
+/*  [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.
+    <input> The input expression. This may equal <<output>>.
+    <get_variable> A function which will be used to get variable values. If
+    this returns NULL, the environment is searched instead. If this is NULL,
+    only the environment is searched.
+    <info> An arbitrary pointer passed to <<get_variable>>.
+    [RETURNS] TRUE on success, else FALSE.
+*/
+{
+	char ch;
+	unsigned int len;
+	unsigned int out_pos = 0;
+	const char *env;
+	const char *ptr;
+	struct passwd *pwent;
+	char buffer[BUFFER_SIZE], tmp[STRING_LENGTH];
+
+	if (length > BUFFER_SIZE)
+		length = BUFFER_SIZE;
+	for (; TRUE; ++input) {
+		switch (ch = *input) {
+			case '$':
+				/*  Variable expansion  */
+				input = expand_variable(buffer, length, &out_pos, ++input, get_variable_func, info);
+				if (input == NULL)
+					return FALSE;
+				break;
+			case '~':
+				/*  Home directory expansion  */
+				ch = input[1];
+				if (isspace(ch) ||(ch == '/') ||(ch == '\0')) {
+					/* User's own home directory: leave separator for next time */
+					env = getenv("HOME");
+					if (env == NULL) {
+						info_logger(LOG_INFO, bb_msg_variable_not_found, "HOME");
+						return FALSE;
+					}
+					len = strlen(env);
+					if (len + out_pos >= length)
+						goto st_expr_expand_out;
+					memcpy(buffer + out_pos, env, len + 1);
+					out_pos += len;
+					continue;
+				}
+				/*  Someone else's home directory  */
+				for (ptr = ++input; !isspace(ch) && (ch != '/') && (ch != '\0'); ch = *++ptr)
+					/* VOID */;
+				len = ptr - input;
+				if (len >= sizeof tmp)
+					goto st_expr_expand_out;
+				safe_memcpy(tmp, input, len);
+				input = ptr - 1;
+				pwent = getpwnam(tmp);
+				if (pwent == NULL) {
+					info_logger(LOG_INFO, "no pwent for: %s", tmp);
+					return FALSE;
+				}
+				len = strlen(pwent->pw_dir);
+				if (len + out_pos >= length)
+					goto st_expr_expand_out;
+				memcpy(buffer + out_pos, pwent->pw_dir, len + 1);
+				out_pos += len;
+				break;
+			case '\0':
+			/* Falltrough */
+			default:
+				if (out_pos >= length)
+					goto st_expr_expand_out;
+				buffer[out_pos++] = ch;
+				if (ch == '\0') {
+					memcpy(output, buffer, out_pos);
+					return TRUE;
+				}
+				break;
+			/* esac */
+		}
+	}
+	return FALSE;
+st_expr_expand_out:
+	info_logger(LOG_INFO, bb_msg_small_buffer);
+	return FALSE;
+}   /*  End Function st_expr_expand  */
+
+
+/*  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)
+/*  [SUMMARY] Expand a variable.
+    <buffer> The buffer to write to.
+    <length> The length of the output buffer.
+    <out_pos> The current output position. This is updated.
+    <input> A pointer to the input character pointer.
+    <func> A function which will be used to get variable values. If this
+    returns NULL, the environment is searched instead. If this is NULL, only
+    the environment is searched.
+    <info> An arbitrary pointer passed to <<func>>.
+    <errfp> Diagnostic messages are written here.
+    [RETURNS] A pointer to the end of this subexpression on success, else NULL.
+*/
+{
+	char ch;
+	int len;
+	unsigned int open_braces;
+	const char *env, *ptr;
+	char tmp[STRING_LENGTH];
+
+	ch = input[0];
+	if (ch == '$') {
+		/*  Special case for "$$": PID  */
+		sprintf(tmp, "%d", (int) getpid());
+		len = strlen(tmp);
+		if (len + *out_pos >= length)
+			goto expand_variable_out;
+
+		memcpy(buffer + *out_pos, tmp, len + 1);
+		out_pos += len;
+		return input;
+	}
+	/*  Ordinary variable expansion, possibly in braces  */
+	if (ch != '{') {
+		/*  Simple variable expansion  */
+		for (ptr = input; isalnum(ch) || (ch == '_') || (ch == ':'); ch = *++ptr)
+			/* VOID */;
+		len = ptr - input;
+		if ((size_t)len >= sizeof tmp)
+			goto expand_variable_out;
+
+		safe_memcpy(tmp, input, len);
+		input = ptr - 1;
+		env = get_variable_v2(tmp, func, info);
+		if (env == NULL) {
+			info_logger(LOG_INFO, bb_msg_variable_not_found, tmp);
+			return NULL;
+		}
+		len = strlen(env);
+		if (len + *out_pos >= length)
+			goto expand_variable_out;
+
+		memcpy(buffer + *out_pos, env, len + 1);
+		*out_pos += len;
+		return input;
+	}
+	/*  Variable in braces: check for ':' tricks  */
+	ch = *++input;
+	for (ptr = input; isalnum(ch) || (ch == '_'); ch = *++ptr)
+		/* VOID */;
+	if (ch == '}') {
+		/*  Must be simple variable expansion with "${var}"  */
+		len = ptr - input;
+		if ((size_t)len >= sizeof tmp)
+			goto expand_variable_out;
+
+		safe_memcpy(tmp, input, len);
+		ptr = expand_variable(buffer, length, out_pos, tmp, func, info);
+		if (ptr == NULL)
+			return NULL;
+		return input + len;
+	}
+	if (ch != ':' || ptr[1] != '-') {
+		info_logger(LOG_INFO, "illegal char in var name");
+		return NULL;
+	}
+	/*  It's that handy "${var:-word}" expression. Check if var is defined  */
+	len = ptr - input;
+	if ((size_t)len >= sizeof tmp)
+		goto expand_variable_out;
+
+	safe_memcpy(tmp, input, len);
+	/*  Move input pointer to ':'  */
+	input = ptr;
+	/*  First skip to closing brace, taking note of nested expressions  */
+	ptr += 2;
+	ch = ptr[0];
+	for (open_braces = 1; open_braces > 0; ch = *++ptr) {
+		switch (ch) {
+			case '{':
+				++open_braces;
+				break;
+			case '}':
+				--open_braces;
+				break;
+			case '\0':
+				info_logger(LOG_INFO, "\"}\" not found in: %s", input);
+				return NULL;
+			default:
+				break;
+		}
+	}
+	--ptr;
+	/*  At this point ptr should point to closing brace of "${var:-word}"  */
+	env = get_variable_v2(tmp, func, info);
+	if (env != NULL) {
+		/*  Found environment variable, so skip the input to the closing brace
+			and return the variable  */
+		input = ptr;
+		len = strlen(env);
+		if (len + *out_pos >= length)
+			goto expand_variable_out;
+
+		memcpy(buffer + *out_pos, env, len + 1);
+		*out_pos += len;
+		return input;
+	}
+	/*  Environment variable was not found, so process word. Advance input
+	pointer to start of word in "${var:-word}"  */
+	input += 2;
+	len = ptr - input;
+	if ((size_t)len >= sizeof tmp)
+		goto expand_variable_out;
+
+	safe_memcpy(tmp, input, len);
+	input = ptr;
+	if (!st_expr_expand(tmp, STRING_LENGTH, tmp, func, info))
+		return NULL;
+	len = strlen(tmp);
+	if (len + *out_pos >= length)
+		goto expand_variable_out;
+
+	memcpy(buffer + *out_pos, tmp, len + 1);
+	*out_pos += len;
+	return input;
+expand_variable_out:
+	info_logger(LOG_INFO, bb_msg_small_buffer);
+	return NULL;
+}   /*  End Function expand_variable  */
+
+
+static const char *get_variable_v2(const char *variable,
+				  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
+    NULL, the environment is searched instead. If this is NULL, only the
+    environment is searched.
+    [RETURNS] The value of the variable on success, else NULL.
+*/
+{
+	const char *value;
+
+	if (func != NULL) {
+		value = (*func)(variable, info);
+		if (value != NULL)
+			return value;
+	}
+	return getenv(variable);
+}   /*  End Function get_variable  */
+
+/* END OF CODE */
diff --git a/busybox-1.19.3/miscutils/devmem.c b/busybox-1.19.3/miscutils/devmem.c
new file mode 100644
index 0000000..786a21b
--- /dev/null
+++ b/busybox-1.19.3/miscutils/devmem.c
@@ -0,0 +1,143 @@
+/*
+ * 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;
+int devmem_main(int argc UNUSED_PARAM, char **argv)
+{
+	void *map_base, *virt_addr;
+	uint64_t read_result;
+	uint64_t writeval = writeval; /* for compiler */
+	off_t target;
+	unsigned page_size, mapped_size, offset_in_page;
+	int fd;
+	unsigned width = 8 * sizeof(int);
+
+	/* devmem ADDRESS [WIDTH [VALUE]] */
+// TODO: options?
+// -r: read and output only the value in hex, with 0x prefix
+// -w: write only, no reads before or after, and no output
+// or make this behavior default?
+// Let's try this and see how users react.
+
+	/* ADDRESS */
+	if (!argv[1])
+		bb_show_usage();
+	errno = 0;
+	target = bb_strtoull(argv[1], NULL, 0); /* allows hex, oct etc */
+
+	/* WIDTH */
+	if (argv[2]) {
+		if (isdigit(argv[2][0]) || argv[2][1])
+			width = xatou(argv[2]);
+		else {
+			static const char bhwl[] ALIGN1 = "bhwl";
+			static const uint8_t sizes[] ALIGN1 = {
+				8 * sizeof(char),
+				8 * sizeof(short),
+				8 * sizeof(int),
+				8 * sizeof(long),
+				0 /* bad */
+			};
+			width = strchrnul(bhwl, (argv[2][0] | 0x20)) - bhwl;
+			width = sizes[width];
+		}
+		/* VALUE */
+		if (argv[3])
+			writeval = bb_strtoull(argv[3], NULL, 0);
+	} else { /* argv[2] == NULL */
+		/* make argv[3] to be a valid thing to fetch */
+		argv--;
+	}
+	if (errno)
+		bb_show_usage(); /* one of bb_strtouXX failed */
+
+	fd = xopen("/dev/mem", argv[3] ? (O_RDWR | O_SYNC) : (O_RDONLY | O_SYNC));
+	mapped_size = page_size = getpagesize();
+	offset_in_page = (unsigned)target & (page_size - 1);
+	if (offset_in_page + width > page_size) {
+		/* This access spans pages.
+		 * Must map two pages to make it possible: */
+		mapped_size *= 2;
+	}
+	map_base = mmap(NULL,
+			mapped_size,
+			argv[3] ? (PROT_READ | PROT_WRITE) : PROT_READ,
+			MAP_SHARED,
+			fd,
+			target & ~(off_t)(page_size - 1));
+	if (map_base == MAP_FAILED)
+		bb_perror_msg_and_die("mmap");
+
+//	printf("Memory mapped at address %p.\n", map_base);
+
+	virt_addr = (char*)map_base + offset_in_page;
+
+	if (!argv[3]) {
+		switch (width) {
+		case 8:
+			read_result = *(volatile uint8_t*)virt_addr;
+			break;
+		case 16:
+			read_result = *(volatile uint16_t*)virt_addr;
+			break;
+		case 32:
+			read_result = *(volatile uint32_t*)virt_addr;
+			break;
+		case 64:
+			read_result = *(volatile uint64_t*)virt_addr;
+			break;
+		default:
+			bb_error_msg_and_die("bad width");
+		}
+//		printf("Value at address 0x%"OFF_FMT"X (%p): 0x%llX\n",
+//			target, virt_addr,
+//			(unsigned long long)read_result);
+		/* Zero-padded output shows the width of access just done */
+		printf("0x%0*llX\n", (width >> 2), (unsigned long long)read_result);
+	} else {
+		switch (width) {
+		case 8:
+			*(volatile uint8_t*)virt_addr = writeval;
+//			read_result = *(volatile uint8_t*)virt_addr;
+			break;
+		case 16:
+			*(volatile uint16_t*)virt_addr = writeval;
+//			read_result = *(volatile uint16_t*)virt_addr;
+			break;
+		case 32:
+			*(volatile uint32_t*)virt_addr = writeval;
+//			read_result = *(volatile uint32_t*)virt_addr;
+			break;
+		case 64:
+			*(volatile uint64_t*)virt_addr = writeval;
+//			read_result = *(volatile uint64_t*)virt_addr;
+			break;
+		default:
+			bb_error_msg_and_die("bad width");
+		}
+//		printf("Written 0x%llX; readback 0x%llX\n",
+//				(unsigned long long)writeval,
+//				(unsigned long long)read_result);
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		if (munmap(map_base, mapped_size) == -1)
+			bb_perror_msg_and_die("munmap");
+		close(fd);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/eject.c b/busybox-1.19.3/miscutils/eject.c
new file mode 100644
index 0000000..a20e04b
--- /dev/null
+++ b/busybox-1.19.3/miscutils/eject.c
@@ -0,0 +1,128 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * eject implementation for busybox
+ *
+ * Copyright (C) 2004  Peter Willis <psyphreak@phreaker.net>
+ * Copyright (C) 2005  Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * This is a simple hack of eject based on something Erik posted in #uclibc.
+ * 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 */
+#include "fix_u32.h"
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+
+/* various defines swiped from linux/cdrom.h */
+#define CDROMCLOSETRAY            0x5319  /* pendant of CDROMEJECT  */
+#define CDROMEJECT                0x5309  /* Ejects the cdrom media */
+#define CDROM_DRIVE_STATUS        0x5326  /* Get tray position, etc. */
+/* drive status possibilities returned by CDROM_DRIVE_STATUS ioctl */
+#define CDS_TRAY_OPEN        2
+
+#define dev_fd 3
+
+/* Code taken from the original eject (http://eject.sourceforge.net/),
+ * refactored it a bit for busybox (ne-bb@nicoerfurth.de) */
+
+static void eject_scsi(const char *dev)
+{
+	static const char sg_commands[3][6] = {
+		{ ALLOW_MEDIUM_REMOVAL, 0, 0, 0, 0, 0 },
+		{ START_STOP, 0, 0, 0, 1, 0 },
+		{ START_STOP, 0, 0, 0, 2, 0 }
+	};
+
+	unsigned i;
+	unsigned char sense_buffer[32];
+	unsigned char inqBuff[2];
+	sg_io_hdr_t io_hdr;
+
+	if ((ioctl(dev_fd, SG_GET_VERSION_NUM, &i) < 0) || (i < 30000))
+		bb_error_msg_and_die("not a sg device or old sg driver");
+
+	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
+	io_hdr.interface_id = 'S';
+	io_hdr.cmd_len = 6;
+	io_hdr.mx_sb_len = sizeof(sense_buffer);
+	io_hdr.dxfer_direction = SG_DXFER_NONE;
+	/* io_hdr.dxfer_len = 0; */
+	io_hdr.dxferp = inqBuff;
+	io_hdr.sbp = sense_buffer;
+	io_hdr.timeout = 2000;
+
+	for (i = 0; i < 3; i++) {
+		io_hdr.cmdp = (void *)sg_commands[i];
+		ioctl_or_perror_and_die(dev_fd, SG_IO, (void *)&io_hdr, "%s", dev);
+	}
+
+	/* force kernel to reread partition table when new disc is inserted */
+	ioctl(dev_fd, BLKRRPART);
+}
+
+#define FLAG_CLOSE  1
+#define FLAG_SMART  2
+#define FLAG_SCSI   4
+
+static void eject_cdrom(unsigned flags, const char *dev)
+{
+	int cmd = CDROMEJECT;
+
+	if (flags & FLAG_CLOSE
+	 || ((flags & FLAG_SMART) && ioctl(dev_fd, CDROM_DRIVE_STATUS) == CDS_TRAY_OPEN)
+	) {
+		cmd = CDROMCLOSETRAY;
+	}
+
+	ioctl_or_perror_and_die(dev_fd, cmd, NULL, "%s", dev);
+}
+
+int eject_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int eject_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned flags;
+	const char *device;
+
+	opt_complementary = "?1:t--T:T--t";
+	flags = getopt32(argv, "tT" IF_FEATURE_EJECT_SCSI("s"));
+	device = argv[optind] ? argv[optind] : "/dev/cdrom";
+
+	/* We used to do "umount <device>" here, but it was buggy
+	   if something was mounted OVER cdrom and
+	   if cdrom is mounted many times.
+
+	   This works equally well (or better):
+	   #!/bin/sh
+	   umount /dev/cdrom
+	   eject /dev/cdrom
+	*/
+
+	xmove_fd(xopen_nonblocking(device), dev_fd);
+
+	if (ENABLE_FEATURE_EJECT_SCSI && (flags & FLAG_SCSI))
+		eject_scsi(device);
+	else
+		eject_cdrom(flags, device);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(dev_fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/fbsplash.c b/busybox-1.19.3/miscutils/fbsplash.c
new file mode 100644
index 0000000..51ba472
--- /dev/null
+++ b/busybox-1.19.3/miscutils/fbsplash.c
@@ -0,0 +1,454 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2008 Michele Sanges <michele.sanges@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Usage:
+ * - use kernel option 'vga=xxx' or otherwise enable framebuffer device.
+ * - put somewhere fbsplash.cfg file and an image in .ppm format.
+ * - run applet: $ setsid fbsplash [params] &
+ *      -c: hide cursor
+ *      -d /dev/fbN: framebuffer device (if not /dev/fb0)
+ *      -s path_to_image_file (can be "-" for stdin)
+ *      -i path_to_cfg_file
+ *      -f path_to_fifo (can be "-" for stdin)
+ * - if you want to run it only in presence of a kernel parameter
+ *   (for example fbsplash=on), use:
+ *   grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params]
+ * - commands for fifo:
+ *   "NN" (ASCII decimal number) - percentage to show on progress bar.
+ *   "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
+
+struct globals {
+#if DEBUG
+	bool bdebug_messages;	// enable/disable logging
+	FILE *logfile_fd;	// log file
+#endif
+	unsigned char *addr;	// pointer to framebuffer memory
+	unsigned ns[7];		// n-parameters
+	const char *image_filename;
+	struct fb_var_screeninfo scr_var;
+	struct fb_fix_screeninfo scr_fix;
+	unsigned bytes_per_pixel;
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+#define nbar_width	ns[0]	// progress bar width
+#define nbar_height	ns[1]	// progress bar height
+#define nbar_posx	ns[2]	// progress bar horizontal position
+#define nbar_posy	ns[3]	// progress bar vertical position
+#define nbar_colr	ns[4]	// progress bar color red component
+#define nbar_colg	ns[5]	// progress bar color green component
+#define nbar_colb	ns[6]	// progress bar color blue component
+
+#if DEBUG
+#define DEBUG_MESSAGE(strMessage, args...) \
+	if (G.bdebug_messages) { \
+		fprintf(G.logfile_fd, "[%s][%s] - %s\n", \
+		__FILE__, __FUNCTION__, strMessage);	\
+	}
+#else
+#define DEBUG_MESSAGE(...) ((void)0)
+#endif
+
+
+/**
+ * Open and initialize the framebuffer device
+ * \param *strfb_device pointer to framebuffer device
+ */
+static void fb_open(const char *strfb_device)
+{
+	int fbfd = xopen(strfb_device, O_RDWR);
+
+	// framebuffer properties
+	xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var);
+	xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix);
+
+	if (G.scr_var.bits_per_pixel < 16 || G.scr_var.bits_per_pixel > 32)
+		bb_error_msg_and_die("unsupported %u bpp", (int)G.scr_var.bits_per_pixel);
+	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 * G.bytes_per_pixel,
+			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);
+}
+
+
+/**
+ * Return pixel value of the passed RGB color
+ */
+static unsigned fb_pixel_value(unsigned r, unsigned g, unsigned b)
+{
+	if (G.bytes_per_pixel == 2) {
+		r >>= 3;  // 5-bit red
+		g >>= 2;  // 6-bit green
+		b >>= 3;  // 5-bit blue
+		return b + (g << 5) + (r << (5+6));
+	}
+	// 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 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;
+	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;
+
+	thispix = fb_pixel_value(nred, ngreen, nblue);
+
+	// horizontal lines
+	ptr1 = G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * G.bytes_per_pixel;
+	ptr2 = G.addr + ((G.nbar_posy + G.nbar_height - 1) * G.scr_var.xres + G.nbar_posx) * G.bytes_per_pixel;
+	cnt = G.nbar_width - 1;
+	do {
+		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 = G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * G.bytes_per_pixel;
+	ptr2 = G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx + G.nbar_width - 1) * G.bytes_per_pixel;
+	cnt = G.nbar_height - 1;
+	do {
+		fb_write_pixel(ptr1, thispix);
+		fb_write_pixel(ptr2, thispix);
+		ptr1 += G.scr_var.xres * G.bytes_per_pixel;
+		ptr2 += G.scr_var.xres * G.bytes_per_pixel;
+	} while (--cnt >= 0);
+}
+
+
+/**
+ * Draw filled rectangle on framebuffer
+ * \param nx1pos,ny1pos upper left position
+ * \param nx2pos,ny2pos down right position
+ * \param nred,ngreen,nblue rgb color
+ */
+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;
+	unsigned thispix;
+	unsigned char *ptr;
+
+	thispix = fb_pixel_value(nred, ngreen, nblue);
+
+	cnt1 = ny2pos - ny1pos;
+	nypos = ny1pos;
+	do {
+		ptr = G.addr + (nypos * G.scr_var.xres + nx1pos) * G.bytes_per_pixel;
+		cnt2 = nx2pos - nx1pos;
+		do {
+			fb_write_pixel(ptr, thispix);
+			ptr += G.bytes_per_pixel;
+		} while (--cnt2 >= 0);
+
+		nypos++;
+	} while (--cnt1 >= 0);
+}
+
+
+/**
+ * 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;
+
+	// 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)
+		return;
+	// NB: "width" of 1 actually makes rect with width of 2!
+	fb_drawrectangle();
+
+	// inner "empty" rectangle
+	left_x++;
+	top_y++;
+	width -= 2;
+	height -= 2;
+	if ((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);
+
+	if (percent > 0) {
+		// actual progress bar
+		width = width * percent / 100;
+		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;
+			fb_drawfullrectangle(
+					left_x, top_y, left_x + width, top_y,
+					gray_level, gray_level, gray_level);
+			top_y++;
+			i--;
+		}
+	}
+}
+
+
+/**
+ * Draw image from PPM file
+ */
+static void fb_drawimage(void)
+{
+	FILE *theme_file;
+	char *read_ptr;
+	unsigned char *pixline;
+	unsigned i, j, width, height, line_size;
+
+	if (LONE_DASH(G.image_filename)) {
+		theme_file = stdin;
+	} else {
+		int fd = open_zipped(G.image_filename);
+		if (fd < 0)
+			bb_simple_perror_msg_and_die(G.image_filename);
+		theme_file = xfdopen_for_read(fd);
+	}
+
+	/* Parse ppm header:
+	 * - Magic: two characters "P6".
+	 * - Whitespace (blanks, TABs, CRs, LFs).
+	 * - A width, formatted as ASCII characters in decimal.
+	 * - Whitespace.
+	 * - A height, ASCII decimal.
+	 * - Whitespace.
+	 * - The maximum color value, ASCII decimal, in 0..65535
+	 * - Newline or other single whitespace character.
+	 *   (we support newline only)
+	 * - A raster of Width * Height pixels in triplets of rgb
+	 *   in pure binary by 1 or 2 bytes. (we support only 1 byte)
+	 */
+#define concat_buf bb_common_bufsiz1
+	read_ptr = concat_buf;
+	while (1) {
+		int w, h, max_color_val;
+		int rem = concat_buf + sizeof(concat_buf) - read_ptr;
+		if (rem < 2
+		 || fgets(read_ptr, rem, theme_file) == NULL
+		) {
+			bb_error_msg_and_die("bad PPM file '%s'", G.image_filename);
+		}
+		read_ptr = strchrnul(read_ptr, '#');
+		*read_ptr = '\0'; /* ignore #comments */
+		if (sscanf(concat_buf, "P6 %u %u %u", &w, &h, &max_color_val) == 3
+		 && max_color_val <= 255
+		) {
+			width = w; /* w is on stack, width may be in register */
+			height = h;
+			break;
+		}
+	}
+
+	line_size = width*3;
+	pixline = xmalloc(line_size);
+
+	if (width > G.scr_var.xres)
+		width = G.scr_var.xres;
+	if (height > G.scr_var.yres)
+		height = G.scr_var.yres;
+	for (j = 0; j < height; j++) {
+		unsigned char *pixel;
+		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 = G.addr + j * G.scr_fix.line_length;
+		for (i = 0; i < width; i++) {
+			unsigned thispix = fb_pixel_value(pixel[0], pixel[1], pixel[2]);
+			fb_write_pixel(src, thispix);
+			src += G.bytes_per_pixel;
+			pixel += 3;
+		}
+	}
+	free(pixline);
+	fclose(theme_file);
+}
+
+
+/**
+ * Parse configuration file
+ * \param *cfg_filename name of the configuration file
+ */
+static void init(const char *cfg_filename)
+{
+	static const char param_names[] ALIGN1 =
+		"BAR_WIDTH\0" "BAR_HEIGHT\0"
+		"BAR_LEFT\0" "BAR_TOP\0"
+		"BAR_R\0" "BAR_G\0" "BAR_B\0"
+#if DEBUG
+		"DEBUG\0"
+#endif
+		;
+	char *token[2];
+	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_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]);
+		if (i >= 0 && i < 7)
+			G.ns[i] = val;
+#if DEBUG
+		if (i == 7) {
+			G.bdebug_messages = val;
+			if (G.bdebug_messages)
+				G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log");
+		}
+#endif
+	}
+	config_close(parser);
+}
+
+
+int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fbsplash_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *fb_device, *cfg_filename, *fifo_filename;
+	FILE *fp = fp; // for compiler
+	char *num_buf;
+	unsigned num;
+	bool bCursorOff;
+
+	INIT_G();
+
+	// parse command line options
+	fb_device = "/dev/fb0";
+	cfg_filename = NULL;
+	fifo_filename = NULL;
+	bCursorOff = 1 & getopt32(argv, "cs:d:i:f:",
+			&G.image_filename, &fb_device, &cfg_filename, &fifo_filename);
+
+	// parse configuration file
+	if (cfg_filename)
+		init(cfg_filename);
+
+	// We must have -s IMG
+	if (!G.image_filename)
+		bb_show_usage();
+
+	fb_open(fb_device);
+
+	if (fifo_filename && bCursorOff) {
+		// hide cursor (BEFORE any fb ops)
+		full_write(STDOUT_FILENO, "\033[?25l", 6);
+	}
+
+	fb_drawimage();
+
+	if (!fifo_filename)
+		return EXIT_SUCCESS;
+
+	fp = xfopen_stdin(fifo_filename);
+	if (fp != stdin) {
+		// For named pipes, we want to support this:
+		//  mkfifo cmd_pipe
+		//  fbsplash -f cmd_pipe .... &
+		//  ...
+		//  echo 33 >cmd_pipe
+		//  ...
+		//  echo 66 >cmd_pipe
+		// This means that we don't want fbsplash to get EOF
+		// when last writer closes input end.
+		// The simplest way is to open fifo for writing too
+		// and become an additional writer :)
+		open(fifo_filename, O_WRONLY); // errors are ignored
+	}
+
+	fb_drawprogressbar(0);
+	// Block on read, waiting for some input.
+	// Use of <stdio.h> style I/O allows to correctly
+	// handle a case when we have many buffered lines
+	// already in the pipe
+	while ((num_buf = xmalloc_fgetline(fp)) != NULL) {
+		if (strncmp(num_buf, "exit", 4) == 0) {
+			DEBUG_MESSAGE("exit");
+			break;
+		}
+		num = atoi(num_buf);
+		if (isdigit(num_buf[0]) && (num <= 100)) {
+#if DEBUG
+			DEBUG_MESSAGE(itoa(num));
+#endif
+			fb_drawprogressbar(num);
+		}
+		free(num_buf);
+	}
+
+	if (bCursorOff) // restore cursor
+		full_write(STDOUT_FILENO, "\033[?25h", 6);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/fbsplash.cfg b/busybox-1.19.3/miscutils/fbsplash.cfg
new file mode 100644
index 0000000..b6cf607
--- /dev/null
+++ b/busybox-1.19.3/miscutils/fbsplash.cfg
@@ -0,0 +1,9 @@
+# progress bar position
+BAR_LEFT=170
+BAR_TOP=300
+BAR_WIDTH=300
+BAR_HEIGHT=20
+# progress bar color
+BAR_R=80
+BAR_G=80
+BAR_B=130
diff --git a/busybox-1.19.3/miscutils/flash_eraseall.c b/busybox-1.19.3/miscutils/flash_eraseall.c
new file mode 100644
index 0000000..0598371
--- /dev/null
+++ b/busybox-1.19.3/miscutils/flash_eraseall.c
@@ -0,0 +1,206 @@
+/* vi: set sw=4 ts=4: */
+/* eraseall.c -- erase the whole of a MTD device
+ *
+ * Ported to busybox from mtd-utils.
+ *
+ * Copyright (C) 2000 Arcom Control System Ltd
+ *
+ * Renamed to flash_eraseall.c
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define flash_eraseall_trivial_usage
+//usage:       "[-jq] 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	-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)
+
+/* mtd/jffs2-user.h used to have this atrocity:
+extern int target_endian;
+
+#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
+#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
+
+#define cpu_to_je16(x) ((jint16_t){t16(x)})
+#define cpu_to_je32(x) ((jint32_t){t32(x)})
+#define cpu_to_jemode(x) ((jmode_t){t32(x)})
+
+#define je16_to_cpu(x) (t16((x).v16))
+#define je32_to_cpu(x) (t32((x).v32))
+#define jemode_to_cpu(x) (t32((x).m))
+
+but mtd/jffs2-user.h is gone now (at least 2.6.31.6 does not have it anymore)
+*/
+
+/* We always use native endianness */
+#undef cpu_to_je16
+#undef cpu_to_je32
+#define cpu_to_je16(v) ((jint16_t){(v)})
+#define cpu_to_je32(v) ((jint32_t){(v)})
+
+static void show_progress(mtd_info_t *meminfo, erase_info_t *erase)
+{
+	printf("\rErasing %u Kibyte @ %x - %2u%% complete.",
+		(unsigned)meminfo->erasesize / 1024,
+		erase->start,
+		(unsigned) ((unsigned long long) erase->start * 100 / meminfo->size)
+	);
+	fflush_all();
+}
+
+int flash_eraseall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct jffs2_unknown_node cleanmarker;
+	mtd_info_t meminfo;
+	int fd, clmpos, clmlen;
+	erase_info_t erase;
+	struct stat st;
+	unsigned int flags;
+	char *mtd_name;
+
+	opt_complementary = "=1";
+	flags = BBTEST | getopt32(argv, "jq");
+
+	mtd_name = argv[optind];
+	fd = xopen(mtd_name, O_RDWR);
+	fstat(fd, &st);
+	if (!S_ISCHR(st.st_mode))
+		bb_error_msg_and_die("%s: not a char device", mtd_name);
+
+	xioctl(fd, MEMGETINFO, &meminfo);
+	erase.length = meminfo.erasesize;
+	if (meminfo.type == MTD_NANDFLASH)
+		flags |= IS_NAND;
+
+	clmpos = 0;
+	clmlen = 8;
+	if (flags & OPTION_J) {
+		uint32_t *crc32_table;
+
+		crc32_table = crc32_filltable(NULL, 0);
+
+		cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+		if (!(flags & IS_NAND))
+			cleanmarker.totlen = cpu_to_je32(sizeof(struct jffs2_unknown_node));
+		else {
+			struct nand_oobinfo oobinfo;
+
+			xioctl(fd, MEMGETOOBSEL, &oobinfo);
+
+			/* Check for autoplacement */
+			if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
+				/* Get the position of the free bytes */
+				clmpos = oobinfo.oobfree[0][0];
+				clmlen = oobinfo.oobfree[0][1];
+				if (clmlen > 8)
+					clmlen = 8;
+				if (clmlen == 0)
+					bb_error_msg_and_die("autoplacement selected and no empty space in oob");
+			} else {
+				/* Legacy mode */
+				switch (meminfo.oobsize) {
+				case 8:
+					clmpos = 6;
+					clmlen = 2;
+					break;
+				case 16:
+					clmpos = 8;
+					/*clmlen = 8;*/
+					break;
+				case 64:
+					clmpos = 16;
+					/*clmlen = 8;*/
+					break;
+				}
+			}
+			cleanmarker.totlen = cpu_to_je32(8);
+		}
+
+		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 */
+	applet_name = xasprintf("\n%s: %s", applet_name, mtd_name);
+
+	for (erase.start = 0; erase.start < meminfo.size;
+	     erase.start += meminfo.erasesize) {
+		if (flags & BBTEST) {
+			int ret;
+			loff_t offset = erase.start;
+
+			ret = ioctl(fd, MEMGETBADBLOCK, &offset);
+			if (ret > 0) {
+				if (!(flags & OPTION_Q))
+					bb_info_msg("\nSkipping bad block at 0x%08x", erase.start);
+				continue;
+			}
+			if (ret < 0) {
+				/* Black block table is not available on certain flash
+				 * types e.g. NOR
+				 */
+				if (errno == EOPNOTSUPP) {
+					flags &= ~BBTEST;
+					if (flags & IS_NAND)
+						bb_error_msg_and_die("bad block check not available");
+				} else {
+					bb_perror_msg_and_die("MEMGETBADBLOCK error");
+				}
+			}
+		}
+
+		if (!(flags & OPTION_Q))
+			show_progress(&meminfo, &erase);
+
+		xioctl(fd, MEMERASE, &erase);
+
+		/* format for JFFS2 ? */
+		if (!(flags & OPTION_J))
+			continue;
+
+		/* write cleanmarker */
+		if (flags & IS_NAND) {
+			struct mtd_oob_buf oob;
+
+			oob.ptr = (unsigned char *) &cleanmarker;
+			oob.start = erase.start + clmpos;
+			oob.length = clmlen;
+			xioctl(fd, MEMWRITEOOB, &oob);
+		} else {
+			xlseek(fd, erase.start, SEEK_SET);
+			/* if (lseek(fd, erase.start, SEEK_SET) < 0) {
+				bb_perror_msg("MTD %s failure", "seek");
+				continue;
+			} */
+			xwrite(fd, &cleanmarker, sizeof(cleanmarker));
+			/* if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) {
+				bb_perror_msg("MTD %s failure", "write");
+				continue;
+			} */
+		}
+		if (!(flags & OPTION_Q))
+			printf(" Cleanmarker written at %x.", erase.start);
+	}
+	if (!(flags & OPTION_Q)) {
+		show_progress(&meminfo, &erase);
+		bb_putchar('\n');
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/flash_lock_unlock.c b/busybox-1.19.3/miscutils/flash_lock_unlock.c
new file mode 100644
index 0000000..1fefd95
--- /dev/null
+++ b/busybox-1.19.3/miscutils/flash_lock_unlock.c
@@ -0,0 +1,81 @@
+/* vi: set sw=4 ts=4: */
+/* Ported to busybox from mtd-utils.
+ *
+ * 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>
+
+int flash_lock_unlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int flash_lock_unlock_main(int argc UNUSED_PARAM, char **argv)
+{
+	/* note: fields in these structs are 32-bits.
+	 * apparently we can't win anything by using off_t
+	 * or long long's for offset and/or sectors vars. */
+	struct mtd_info_user info;
+	struct erase_info_user lock;
+	unsigned long offset;
+	long sectors;
+	int fd;
+
+#define do_lock (ENABLE_FLASH_LOCK && (!ENABLE_FLASH_UNLOCK || (applet_name[6] == 'l')))
+
+	if (!argv[1])
+		bb_show_usage();
+
+	/* parse offset and number of sectors to lock */
+	offset = 0;
+	sectors = -1;
+	if (do_lock) {
+		if (!argv[2] || !argv[3])
+			bb_show_usage();
+		offset = xstrtoul(argv[2], 0);
+		sectors = xstrtol(argv[3], 0);
+	}
+
+	fd = xopen(argv[1], O_RDWR);
+
+	xioctl(fd, MEMGETINFO, &info);
+
+	lock.start = 0;
+	lock.length = info.size;
+	if (do_lock) {
+		unsigned long size = info.size - info.erasesize;
+		if (offset > size) {
+			bb_error_msg_and_die("%lx is beyond device size %lx\n",
+					offset, size);
+		}
+
+		if (sectors == -1) {
+			sectors = info.size / info.erasesize;
+		} else {
+// isn't this useless?
+			unsigned long num = info.size / info.erasesize;
+			if (sectors > num) {
+				bb_error_msg_and_die("%ld are too many "
+						"sectors, device only has "
+						"%ld\n", sectors, num);
+			}
+		}
+
+		lock.start = offset;
+		lock.length = sectors * info.erasesize;
+		xioctl(fd, MEMLOCK, &lock);
+	} else {
+		xioctl(fd, MEMUNLOCK, &lock);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/flashcp.c b/busybox-1.19.3/miscutils/flashcp.c
new file mode 100644
index 0000000..790f9c0
--- /dev/null
+++ b/busybox-1.19.3/miscutils/flashcp.c
@@ -0,0 +1,157 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * busybox reimplementation of flashcp
+ *
+ * (C) 2009 Stefan Seyfried <seife@sphairon.com>
+ *
+ * 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>
+
+#define MTD_DEBUG 0
+
+#define OPT_v (1 << 0)
+
+#define BUFSIZE (8 * 1024)
+
+static void progress(int mode, uoff_t count, uoff_t total)
+{
+	uoff_t percent;
+
+	if (!option_mask32) //if (!(option_mask32 & OPT_v))
+		return;
+	percent = count * 100;
+	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"),
+		count, total, (unsigned)percent);
+	fflush_all();
+}
+
+static void progress_newline(void)
+{
+	if (!option_mask32) //if (!(option_mask32 & OPT_v))
+		return;
+	bb_putchar('\n');
+}
+
+int flashcp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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;
+//	const char *filename, *devicename;
+	RESERVE_CONFIG_UBUFFER(buf, BUFSIZE);
+	RESERVE_CONFIG_UBUFFER(buf2, BUFSIZE);
+
+	opt_complementary = "=2"; /* exactly 2 non-option args: file, dev */
+	opts = getopt32(argv, "v");
+	argv += optind;
+//	filename = *argv++;
+//	devicename = *argv;
+#define filename argv[0]
+#define devicename argv[1]
+
+	/* open input file and mtd device and do sanity checks */
+	fd_f = xopen(filename, O_RDONLY);
+	fstat(fd_f, &statb);
+	fd_d = xopen(devicename, O_SYNC | O_RDWR);
+#if !MTD_DEBUG
+	if (ioctl(fd_d, MEMGETINFO, &mtd) < 0) {
+		bb_error_msg_and_die("%s is not a MTD flash device", devicename);
+	}
+	if (statb.st_size > mtd.size) {
+		bb_error_msg_and_die("%s bigger than %s", filename, devicename);
+	}
+#else
+	mtd.erasesize = 64 * 1024;
+#endif
+
+	/* always erase a complete block */
+	erase_count = (uoff_t)(statb.st_size + mtd.erasesize - 1) / mtd.erasesize;
+	/* erase 1 block at a time to be able to give verbose output */
+	e.length = mtd.erasesize;
+#if 0
+/* (1) bloat
+ * (2) will it work for multi-gigabyte devices?
+ * (3) worse wrt error detection granularity
+ */
+	/* optimization: if not verbose, erase in one go */
+	if (!opts) { // if (!(opts & OPT_v))
+		e.length = mtd.erasesize * erase_count;
+		erase_count = 1;
+	}
+#endif
+	e.start = 0;
+	for (i = 1; i <= erase_count; i++) {
+		progress(0, i, erase_count);
+		errno = 0;
+#if !MTD_DEBUG
+		if (ioctl(fd_d, MEMERASE, &e) < 0) {
+			bb_perror_msg_and_die("erase error at 0x%llx on %s",
+				(long long)e.start, devicename);
+		}
+#else
+		usleep(100*1000);
+#endif
+		e.start += mtd.erasesize;
+	}
+	progress_newline();
+
+	/* doing this outer loop gives significantly smaller code
+	 * than doing two separate loops for writing and verifying */
+	for (i = 1; i <= 2; i++) {
+		uoff_t done;
+		unsigned count;
+
+		xlseek(fd_f, 0, SEEK_SET);
+		xlseek(fd_d, 0, SEEK_SET);
+		done = 0;
+		count = BUFSIZE;
+		while (1) {
+			uoff_t 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) {
+				int ret;
+				errno = 0;
+				ret = full_write(fd_d, buf, count);
+				if (ret != count) {
+					bb_perror_msg_and_die("write error at 0x%"OFF_FMT"x on %s, "
+						"write returned %d",
+						done, devicename, ret);
+				}
+			} else { /* i == 2 */
+				xread(fd_d, buf2, count);
+				if (memcmp(buf, buf2, count)) {
+					bb_error_msg_and_die("verification mismatch at 0x%"OFF_FMT"x", done);
+				}
+			}
+
+			done += count;
+		}
+
+		progress_newline();
+	}
+	/* we won't come here if there was an error */
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/hdparm.c b/busybox-1.19.3/miscutils/hdparm.c
new file mode 100644
index 0000000..f30e7de
--- /dev/null
+++ b/busybox-1.19.3/miscutils/hdparm.c
@@ -0,0 +1,2135 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * hdparm implementation for busybox
+ *
+ * Copyright (C) [2003] by [Matteo Croce] <3297627799@wind.it>
+ * Hacked by Tito <farmatito@tiscali.it> for size optimization.
+ *
+ * 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>
+#include <sys/mount.h>
+#if !defined(BLKGETSIZE64)
+# define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#endif
+
+/* device types */
+/* ------------ */
+#define NO_DEV                  0xffff
+#define ATA_DEV                 0x0000
+#define ATAPI_DEV               0x0001
+
+/* word definitions */
+/* ---------------- */
+#define GEN_CONFIG		0   /* general configuration */
+#define LCYLS			1   /* number of logical cylinders */
+#define CONFIG			2   /* specific configuration */
+#define LHEADS			3   /* number of logical heads */
+#define TRACK_BYTES		4   /* number of bytes/track (ATA-1) */
+#define SECT_BYTES		5   /* number of bytes/sector (ATA-1) */
+#define LSECTS			6   /* number of logical sectors/track */
+#define START_SERIAL            10  /* ASCII serial number */
+#define LENGTH_SERIAL           10  /* 10 words (20 bytes or characters) */
+#define BUF_TYPE		20  /* buffer type (ATA-1) */
+#define BUFFER__SIZE		21  /* buffer size (ATA-1) */
+#define RW_LONG			22  /* extra bytes in R/W LONG cmd ( < ATA-4)*/
+#define START_FW_REV            23  /* ASCII firmware revision */
+#define LENGTH_FW_REV		 4  /*  4 words (8 bytes or characters) */
+#define START_MODEL		27  /* ASCII model number */
+#define LENGTH_MODEL		20  /* 20 words (40 bytes or characters) */
+#define SECTOR_XFER_MAX		47  /* r/w multiple: max sectors xfered */
+#define DWORD_IO		48  /* can do double-word IO (ATA-1 only) */
+#define CAPAB_0			49  /* capabilities */
+#define CAPAB_1			50
+#define PIO_MODE		51  /* max PIO mode supported (obsolete)*/
+#define DMA_MODE		52  /* max Singleword DMA mode supported (obs)*/
+#define WHATS_VALID		53  /* what fields are valid */
+#define LCYLS_CUR		54  /* current logical cylinders */
+#define LHEADS_CUR		55  /* current logical heads */
+#define LSECTS_CUR		56  /* current logical sectors/track */
+#define CAPACITY_LSB		57  /* current capacity in sectors */
+#define CAPACITY_MSB		58
+#define SECTOR_XFER_CUR		59  /* r/w multiple: current sectors xfered */
+#define LBA_SECTS_LSB		60  /* LBA: total number of user */
+#define LBA_SECTS_MSB		61  /*      addressable sectors */
+#define SINGLE_DMA		62  /* singleword DMA modes */
+#define MULTI_DMA		63  /* multiword DMA modes */
+#define ADV_PIO_MODES		64  /* advanced PIO modes supported */
+				    /* multiword DMA xfer cycle time: */
+#define DMA_TIME_MIN		65  /*   - minimum */
+#define DMA_TIME_NORM		66  /*   - manufacturer's recommended */
+				    /* minimum PIO xfer cycle time: */
+#define PIO_NO_FLOW		67  /*   - without flow control */
+#define PIO_FLOW		68  /*   - with IORDY flow control */
+#define PKT_REL			71  /* typical #ns from PKT cmd to bus rel */
+#define SVC_NBSY		72  /* typical #ns from SERVICE cmd to !BSY */
+#define CDR_MAJOR		73  /* CD ROM: major version number */
+#define CDR_MINOR		74  /* CD ROM: minor version number */
+#define QUEUE_DEPTH		75  /* queue depth */
+#define MAJOR			80  /* major version number */
+#define MINOR			81  /* minor version number */
+#define CMDS_SUPP_0		82  /* command/feature set(s) supported */
+#define CMDS_SUPP_1		83
+#define CMDS_SUPP_2		84
+#define CMDS_EN_0		85  /* command/feature set(s) enabled */
+#define CMDS_EN_1		86
+#define CMDS_EN_2		87
+#define ULTRA_DMA		88  /* ultra DMA modes */
+				    /* time to complete security erase */
+#define ERASE_TIME		89  /*   - ordinary */
+#define ENH_ERASE_TIME		90  /*   - enhanced */
+#define ADV_PWR			91  /* current advanced power management level
+				       in low byte, 0x40 in high byte. */
+#define PSWD_CODE		92  /* master password revision code */
+#define HWRST_RSLT		93  /* hardware reset result */
+#define ACOUSTIC		94  /* acoustic mgmt values ( >= ATA-6) */
+#define LBA_LSB			100 /* LBA: maximum.  Currently only 48 */
+#define LBA_MID			101 /*      bits are used, but addr 103 */
+#define LBA_48_MSB		102 /*      has been reserved for LBA in */
+#define LBA_64_MSB		103 /*      the future. */
+#define RM_STAT			127 /* removable media status notification feature set support */
+#define SECU_STATUS		128 /* security status */
+#define CFA_PWR_MODE		160 /* CFA power mode 1 */
+#define START_MEDIA             176 /* media serial number */
+#define LENGTH_MEDIA            20  /* 20 words (40 bytes or characters)*/
+#define START_MANUF             196 /* media manufacturer I.D. */
+#define LENGTH_MANUF            10  /* 10 words (20 bytes or characters) */
+#define INTEGRITY		255 /* integrity word */
+
+/* bit definitions within the words */
+/* -------------------------------- */
+
+/* many words are considered valid if bit 15 is 0 and bit 14 is 1 */
+#define VALID			0xc000
+#define VALID_VAL		0x4000
+/* many words are considered invalid if they are either all-0 or all-1 */
+#define NOVAL_0			0x0000
+#define NOVAL_1			0xffff
+
+/* word 0: gen_config */
+#define NOT_ATA			0x8000
+#define NOT_ATAPI		0x4000	/* (check only if bit 15 == 1) */
+#define MEDIA_REMOVABLE		0x0080
+#define DRIVE_NOT_REMOVABLE	0x0040  /* bit obsoleted in ATA 6 */
+#define INCOMPLETE		0x0004
+#define CFA_SUPPORT_VAL		0x848a	/* 848a=CFA feature set support */
+#define DRQ_RESPONSE_TIME	0x0060
+#define DRQ_3MS_VAL		0x0000
+#define DRQ_INTR_VAL		0x0020
+#define DRQ_50US_VAL		0x0040
+#define PKT_SIZE_SUPPORTED	0x0003
+#define PKT_SIZE_12_VAL		0x0000
+#define PKT_SIZE_16_VAL		0x0001
+#define EQPT_TYPE		0x1f00
+#define SHIFT_EQPT		8
+
+#define CDROM 0x0005
+
+/* word 1: number of logical cylinders */
+#define LCYLS_MAX		0x3fff /* maximum allowable value */
+
+/* word 2: specific configuration
+ * (a) require SET FEATURES to spin-up
+ * (b) require spin-up to fully reply to IDENTIFY DEVICE
+ */
+#define STBY_NID_VAL		0x37c8  /*     (a) and     (b) */
+#define STBY_ID_VAL		0x738c	/*     (a) and not (b) */
+#define PWRD_NID_VAL		0x8c73	/* not (a) and     (b) */
+#define PWRD_ID_VAL		0xc837	/* not (a) and not (b) */
+
+/* words 47 & 59: sector_xfer_max & sector_xfer_cur */
+#define SECTOR_XFER		0x00ff  /* sectors xfered on r/w multiple cmds*/
+#define MULTIPLE_SETTING_VALID  0x0100  /* 1=multiple sector setting is valid */
+
+/* word 49: capabilities 0 */
+#define STD_STBY		0x2000  /* 1=standard values supported (ATA); 0=vendor specific values */
+#define IORDY_SUP		0x0800  /* 1=support; 0=may be supported */
+#define IORDY_OFF		0x0400  /* 1=may be disabled */
+#define LBA_SUP			0x0200  /* 1=Logical Block Address support */
+#define DMA_SUP			0x0100  /* 1=Direct Memory Access support */
+#define DMA_IL_SUP		0x8000  /* 1=interleaved DMA support (ATAPI) */
+#define CMD_Q_SUP		0x4000  /* 1=command queuing support (ATAPI) */
+#define OVLP_SUP		0x2000  /* 1=overlap operation support (ATAPI) */
+#define SWRST_REQ		0x1000  /* 1=ATA SW reset required (ATAPI, obsolete */
+
+/* word 50: capabilities 1 */
+#define MIN_STANDBY_TIMER	0x0001  /* 1=device specific standby timer value minimum */
+
+/* words 51 & 52: PIO & DMA cycle times */
+#define MODE			0xff00  /* the mode is in the MSBs */
+
+/* word 53: whats_valid */
+#define OK_W88			0x0004	/* the ultra_dma info is valid */
+#define OK_W64_70		0x0002  /* see above for word descriptions */
+#define OK_W54_58		0x0001  /* current cyl, head, sector, cap. info valid */
+
+/*word 63,88: dma_mode, ultra_dma_mode*/
+#define MODE_MAX		7	/* bit definitions force udma <=7 (when
+					 * udma >=8 comes out it'll have to be
+					 * defined in a new dma_mode word!) */
+
+/* word 64: PIO transfer modes */
+#define PIO_SUP			0x00ff  /* only bits 0 & 1 are used so far,  */
+#define PIO_MODE_MAX		8       /* but all 8 bits are defined        */
+
+/* word 75: queue_depth */
+#define DEPTH_BITS		0x001f  /* bits used for queue depth */
+
+/* words 80-81: version numbers */
+/* NOVAL_0 or  NOVAL_1 means device does not report version */
+
+/* word 81: minor version number */
+#define MINOR_MAX		0x22
+/* words 82-84: cmds/feats supported */
+#define CMDS_W82		0x77ff  /* word 82: defined command locations*/
+#define CMDS_W83		0x3fff  /* word 83: defined command locations*/
+#define CMDS_W84		0x002f  /* word 83: defined command locations*/
+#define SUPPORT_48_BIT		0x0400
+#define NUM_CMD_FEAT_STR	48
+
+/* words 85-87: cmds/feats enabled */
+/* use cmd_feat_str[] to display what commands and features have
+ * been enabled with words 85-87
+ */
+
+/* words 89, 90, SECU ERASE TIME */
+#define ERASE_BITS      0x00ff
+
+/* word 92: master password revision */
+/* NOVAL_0 or  NOVAL_1 means no support for master password revision */
+
+/* word 93: hw reset result */
+#define CBLID           0x2000  /* CBLID status */
+#define RST0            0x0001  /* 1=reset to device #0 */
+#define DEV_DET         0x0006  /* how device num determined */
+#define JUMPER_VAL      0x0002  /* device num determined by jumper */
+#define CSEL_VAL        0x0004  /* device num determined by CSEL_VAL */
+
+/* word 127: removable media status notification feature set support */
+#define RM_STAT_BITS    0x0003
+#define RM_STAT_SUP     0x0001
+
+/* word 128: security */
+#define SECU_ENABLED    0x0002
+#define SECU_LEVEL      0x0010
+#define NUM_SECU_STR    6
+
+/* word 160: CFA power mode */
+#define VALID_W160              0x8000  /* 1=word valid */
+#define PWR_MODE_REQ            0x2000  /* 1=CFA power mode req'd by some cmds*/
+#define PWR_MODE_OFF            0x1000  /* 1=CFA power moded disabled */
+#define MAX_AMPS                0x0fff  /* value = max current in ma */
+
+/* word 255: integrity */
+#define SIG                     0x00ff  /* signature location */
+#define SIG_VAL                 0x00a5  /* signature value */
+
+#define TIMING_BUF_MB           1
+#define TIMING_BUF_BYTES        (TIMING_BUF_MB * 1024 * 1024)
+
+#undef DO_FLUSHCACHE            /* under construction: force cache flush on -W0 */
+
+
+#define IS_GET 1
+#define IS_SET 2
+
+
+enum { fd = 3 };
+
+
+struct globals {
+	smallint get_identity, get_geom;
+	smallint do_flush;
+	smallint do_ctimings, do_timings;
+	smallint reread_partn;
+	smallint set_piomode, noisy_piomode;
+	smallint getset_readahead;
+	smallint getset_readonly;
+	smallint getset_unmask;
+	smallint getset_mult;
+#ifdef HDIO_GET_QDMA
+	smallint getset_dma_q;
+#endif
+	smallint getset_nowerr;
+	smallint getset_keep;
+	smallint getset_io32bit;
+	int piomode;
+	unsigned long Xreadahead;
+	unsigned long readonly;
+	unsigned long unmask;
+	unsigned long mult;
+#ifdef HDIO_SET_QDMA
+	unsigned long dma_q;
+#endif
+	unsigned long nowerr;
+	unsigned long keep;
+	unsigned long io32bit;
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+	unsigned long dma;
+	smallint getset_dma;
+#endif
+#ifdef HDIO_DRIVE_CMD
+	smallint set_xfermode, get_xfermode;
+	smallint getset_dkeep;
+	smallint getset_standby;
+	smallint getset_lookahead;
+	smallint getset_prefetch;
+	smallint getset_defects;
+	smallint getset_wcache;
+	smallint getset_doorlock;
+	smallint set_seagate;
+	smallint set_standbynow;
+	smallint set_sleepnow;
+	smallint get_powermode;
+	smallint getset_apmmode;
+	int xfermode_requested;
+	unsigned long dkeep;
+	unsigned long standby_requested; /* 0..255 */
+	unsigned long lookahead;
+	unsigned long prefetch;
+	unsigned long defects;
+	unsigned long wcache;
+	unsigned long doorlock;
+	unsigned long apmmode;
+#endif
+	IF_FEATURE_HDPARM_GET_IDENTITY(        smallint get_IDentity;)
+	IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(  smallint getset_busstate;)
+	IF_FEATURE_HDPARM_HDIO_DRIVE_RESET(    smallint perform_reset;)
+	IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(  smallint perform_tristate;)
+	IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(smallint unregister_hwif;)
+	IF_FEATURE_HDPARM_HDIO_SCAN_HWIF(      smallint scan_hwif;)
+	IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(  unsigned long busstate;)
+	IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(  unsigned long tristate;)
+	IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(unsigned long hwif;)
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+	unsigned long hwif_data;
+	unsigned long hwif_ctrl;
+	unsigned long hwif_irq;
+#endif
+#ifdef DO_FLUSHCACHE
+	unsigned char flushcache[4] = { WIN_FLUSHCACHE, 0, 0, 0 };
+#endif
+} 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 get_identity       (G.get_identity           )
+#define get_geom           (G.get_geom               )
+#define do_flush           (G.do_flush               )
+#define do_ctimings        (G.do_ctimings            )
+#define do_timings         (G.do_timings             )
+#define reread_partn       (G.reread_partn           )
+#define set_piomode        (G.set_piomode            )
+#define noisy_piomode      (G.noisy_piomode          )
+#define getset_readahead   (G.getset_readahead       )
+#define getset_readonly    (G.getset_readonly        )
+#define getset_unmask      (G.getset_unmask          )
+#define getset_mult        (G.getset_mult            )
+#define getset_dma_q       (G.getset_dma_q           )
+#define getset_nowerr      (G.getset_nowerr          )
+#define getset_keep        (G.getset_keep            )
+#define getset_io32bit     (G.getset_io32bit         )
+#define piomode            (G.piomode                )
+#define Xreadahead         (G.Xreadahead             )
+#define readonly           (G.readonly               )
+#define unmask             (G.unmask                 )
+#define mult               (G.mult                   )
+#define dma_q              (G.dma_q                  )
+#define nowerr             (G.nowerr                 )
+#define keep               (G.keep                   )
+#define io32bit            (G.io32bit                )
+#define dma                (G.dma                    )
+#define getset_dma         (G.getset_dma             )
+#define set_xfermode       (G.set_xfermode           )
+#define get_xfermode       (G.get_xfermode           )
+#define getset_dkeep       (G.getset_dkeep           )
+#define getset_standby     (G.getset_standby         )
+#define getset_lookahead   (G.getset_lookahead       )
+#define getset_prefetch    (G.getset_prefetch        )
+#define getset_defects     (G.getset_defects         )
+#define getset_wcache      (G.getset_wcache          )
+#define getset_doorlock    (G.getset_doorlock        )
+#define set_seagate        (G.set_seagate            )
+#define set_standbynow     (G.set_standbynow         )
+#define set_sleepnow       (G.set_sleepnow           )
+#define get_powermode      (G.get_powermode          )
+#define getset_apmmode     (G.getset_apmmode         )
+#define xfermode_requested (G.xfermode_requested     )
+#define dkeep              (G.dkeep                  )
+#define standby_requested  (G.standby_requested      )
+#define lookahead          (G.lookahead              )
+#define prefetch           (G.prefetch               )
+#define defects            (G.defects                )
+#define wcache             (G.wcache                 )
+#define doorlock           (G.doorlock               )
+#define apmmode            (G.apmmode                )
+#define get_IDentity       (G.get_IDentity           )
+#define getset_busstate    (G.getset_busstate        )
+#define perform_reset      (G.perform_reset          )
+#define perform_tristate   (G.perform_tristate       )
+#define unregister_hwif    (G.unregister_hwif        )
+#define scan_hwif          (G.scan_hwif              )
+#define busstate           (G.busstate               )
+#define tristate           (G.tristate               )
+#define hwif               (G.hwif                   )
+#define hwif_data          (G.hwif_data              )
+#define hwif_ctrl          (G.hwif_ctrl              )
+#define hwif_irq           (G.hwif_irq               )
+
+
+/* Busybox messages and functions */
+#if ENABLE_IOCTL_HEX2STR_ERROR
+static int ioctl_alt_func(/*int fd,*/ int cmd, unsigned char *args, int alt, const char *string)
+{
+	if (!ioctl(fd, cmd, args))
+		return 0;
+	args[0] = alt;
+	return bb_ioctl_or_warn(fd, cmd, args, string);
+}
+#define ioctl_alt_or_warn(cmd,args,alt) ioctl_alt_func(cmd,args,alt,#cmd)
+#else
+static int ioctl_alt_func(/*int fd,*/ int cmd, unsigned char *args, int alt)
+{
+	if (!ioctl(fd, cmd, args))
+		return 0;
+	args[0] = alt;
+	return bb_ioctl_or_warn(fd, cmd, args);
+}
+#define ioctl_alt_or_warn(cmd,args,alt) ioctl_alt_func(cmd,args,alt)
+#endif
+
+static void on_off(int value)
+{
+	puts(value ? " (on)" : " (off)");
+}
+
+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);
+		on_off(arg);
+	}
+}
+
+static void print_value_on_off(const char *str, unsigned long argp)
+{
+	printf(" %s\t= %2ld", str, argp);
+	on_off(argp != 0);
+}
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static void print_ascii(const char *p, int length)
+{
+#if BB_BIG_ENDIAN
+#define LE_ONLY(x)
+	enum { ofs = 0 };
+#else
+#define LE_ONLY(x) x
+	/* every 16bit word is big-endian (i.e. inverted) */
+	/* accessing bytes in 1,0, 3,2, 5,4... sequence */
+	int ofs = 1;
+#endif
+
+	length *= 2;
+	/* find first non-space & print it */
+	while (length && p[ofs] != ' ') {
+		p++;
+		LE_ONLY(ofs = -ofs;)
+		length--;
+	}
+	while (length && p[ofs]) {
+		bb_putchar(p[ofs]);
+		p++;
+		LE_ONLY(ofs = -ofs;)
+		length--;
+	}
+	bb_putchar('\n');
+#undef LE_ONLY
+}
+
+static void xprint_ascii(uint16_t *val, int i, const char *string, int n)
+{
+	if (val[i]) {
+		printf("\t%-20s", string);
+		print_ascii((void*)&val[i], n);
+	}
+}
+
+static uint8_t mode_loop(uint16_t mode_sup, uint16_t mode_sel, int cc, uint8_t *have_mode)
+{
+	uint16_t ii;
+	uint8_t err_dma = 0;
+
+	for (ii = 0; ii <= MODE_MAX; ii++) {
+		if (mode_sel & 0x0001) {
+			printf("*%cdma%u ", cc, ii);
+			if (*have_mode)
+				err_dma = 1;
+			*have_mode = 1;
+		} else if (mode_sup & 0x0001)
+			printf("%cdma%u ", cc, ii);
+
+		mode_sup >>= 1;
+		mode_sel >>= 1;
+	}
+	return err_dma;
+}
+
+static const char pkt_str[] ALIGN1 =
+	"Direct-access device" "\0"             /* word 0, bits 12-8 = 00 */
+	"Sequential-access device" "\0"         /* word 0, bits 12-8 = 01 */
+	"Printer" "\0"                          /* word 0, bits 12-8 = 02 */
+	"Processor" "\0"                        /* word 0, bits 12-8 = 03 */
+	"Write-once device" "\0"                /* word 0, bits 12-8 = 04 */
+	"CD-ROM" "\0"                           /* word 0, bits 12-8 = 05 */
+	"Scanner" "\0"                          /* word 0, bits 12-8 = 06 */
+	"Optical memory" "\0"                   /* word 0, bits 12-8 = 07 */
+	"Medium changer" "\0"                   /* word 0, bits 12-8 = 08 */
+	"Communications device" "\0"            /* word 0, bits 12-8 = 09 */
+	"ACS-IT8 device" "\0"                   /* word 0, bits 12-8 = 0a */
+	"ACS-IT8 device" "\0"                   /* word 0, bits 12-8 = 0b */
+	"Array controller" "\0"                 /* word 0, bits 12-8 = 0c */
+	"Enclosure services" "\0"               /* word 0, bits 12-8 = 0d */
+	"Reduced block command device" "\0"     /* word 0, bits 12-8 = 0e */
+	"Optical card reader/writer" "\0"       /* word 0, bits 12-8 = 0f */
+;
+
+static const char ata1_cfg_str[] ALIGN1 =       /* word 0 in ATA-1 mode */
+	"reserved" "\0"                         /* bit 0 */
+	"hard sectored" "\0"                    /* bit 1 */
+	"soft sectored" "\0"                    /* bit 2 */
+	"not MFM encoded " "\0"                 /* bit 3 */
+	"head switch time > 15us" "\0"          /* bit 4 */
+	"spindle motor control option" "\0"     /* bit 5 */
+	"fixed drive" "\0"                      /* bit 6 */
+	"removable drive" "\0"                  /* bit 7 */
+	"disk xfer rate <= 5Mbs" "\0"           /* bit 8 */
+	"disk xfer rate > 5Mbs, <= 10Mbs" "\0"  /* bit 9 */
+	"disk xfer rate > 5Mbs" "\0"            /* bit 10 */
+	"rotational speed tol." "\0"            /* bit 11 */
+	"data strobe offset option" "\0"        /* bit 12 */
+	"track offset option" "\0"              /* bit 13 */
+	"format speed tolerance gap reqd" "\0"  /* bit 14 */
+	"ATAPI"                                 /* bit 14 */
+;
+
+static const char minor_str[] ALIGN1 =
+	/* word 81 value: */
+	"Unspecified" "\0"                                  /* 0x0000 */
+	"ATA-1 X3T9.2 781D prior to rev.4" "\0"             /* 0x0001 */
+	"ATA-1 published, ANSI X3.221-1994" "\0"            /* 0x0002 */
+	"ATA-1 X3T9.2 781D rev.4" "\0"                      /* 0x0003 */
+	"ATA-2 published, ANSI X3.279-1996" "\0"            /* 0x0004 */
+	"ATA-2 X3T10 948D prior to rev.2k" "\0"             /* 0x0005 */
+	"ATA-3 X3T10 2008D rev.1" "\0"                      /* 0x0006 */
+	"ATA-2 X3T10 948D rev.2k" "\0"                      /* 0x0007 */
+	"ATA-3 X3T10 2008D rev.0" "\0"                      /* 0x0008 */
+	"ATA-2 X3T10 948D rev.3" "\0"                       /* 0x0009 */
+	"ATA-3 published, ANSI X3.298-199x" "\0"            /* 0x000a */
+	"ATA-3 X3T10 2008D rev.6" "\0"                      /* 0x000b */
+	"ATA-3 X3T13 2008D rev.7 and 7a" "\0"               /* 0x000c */
+	"ATA/ATAPI-4 X3T13 1153D rev.6" "\0"                /* 0x000d */
+	"ATA/ATAPI-4 T13 1153D rev.13" "\0"                 /* 0x000e */
+	"ATA/ATAPI-4 X3T13 1153D rev.7" "\0"                /* 0x000f */
+	"ATA/ATAPI-4 T13 1153D rev.18" "\0"                 /* 0x0010 */
+	"ATA/ATAPI-4 T13 1153D rev.15" "\0"                 /* 0x0011 */
+	"ATA/ATAPI-4 published, ANSI INCITS 317-1998" "\0"  /* 0x0012 */
+	"ATA/ATAPI-5 T13 1321D rev.3" "\0"                  /* 0x0013 */
+	"ATA/ATAPI-4 T13 1153D rev.14" "\0"                 /* 0x0014 */
+	"ATA/ATAPI-5 T13 1321D rev.1" "\0"                  /* 0x0015 */
+	"ATA/ATAPI-5 published, ANSI INCITS 340-2000" "\0"  /* 0x0016 */
+	"ATA/ATAPI-4 T13 1153D rev.17" "\0"                 /* 0x0017 */
+	"ATA/ATAPI-6 T13 1410D rev.0" "\0"                  /* 0x0018 */
+	"ATA/ATAPI-6 T13 1410D rev.3a" "\0"                 /* 0x0019 */
+	"ATA/ATAPI-7 T13 1532D rev.1" "\0"                  /* 0x001a */
+	"ATA/ATAPI-6 T13 1410D rev.2" "\0"                  /* 0x001b */
+	"ATA/ATAPI-6 T13 1410D rev.1" "\0"                  /* 0x001c */
+	"ATA/ATAPI-7 published, ANSI INCITS 397-2005" "\0"  /* 0x001d */
+	"ATA/ATAPI-7 T13 1532D rev.0" "\0"                  /* 0x001e */
+	"reserved" "\0"                                     /* 0x001f */
+	"reserved" "\0"                                     /* 0x0020 */
+	"ATA/ATAPI-7 T13 1532D rev.4a" "\0"                 /* 0x0021 */
+	"ATA/ATAPI-6 published, ANSI INCITS 361-2002" "\0"  /* 0x0022 */
+	"reserved"                                          /* 0x0023-0xfffe */
+;
+static const char actual_ver[MINOR_MAX + 2] ALIGN1 = {
+	   /* word 81 value: */
+	0, /* 0x0000 WARNING: actual_ver[] array */
+	1, /* 0x0001 WARNING: corresponds        */
+	1, /* 0x0002 WARNING: *exactly*          */
+	1, /* 0x0003 WARNING: to the ATA/        */
+	2, /* 0x0004 WARNING: ATAPI version      */
+	2, /* 0x0005 WARNING: listed in          */
+	3, /* 0x0006 WARNING: the                */
+	2, /* 0x0007 WARNING: minor_str          */
+	3, /* 0x0008 WARNING: array              */
+	2, /* 0x0009 WARNING: above.             */
+	3, /* 0x000a WARNING:                    */
+	3, /* 0x000b WARNING: If you change      */
+	3, /* 0x000c WARNING: that one,          */
+	4, /* 0x000d WARNING: change this one    */
+	4, /* 0x000e WARNING: too!!!             */
+	4, /* 0x000f */
+	4, /* 0x0010 */
+	4, /* 0x0011 */
+	4, /* 0x0012 */
+	5, /* 0x0013 */
+	4, /* 0x0014 */
+	5, /* 0x0015 */
+	5, /* 0x0016 */
+	4, /* 0x0017 */
+	6, /* 0x0018 */
+	6, /* 0x0019 */
+	7, /* 0x001a */
+	6, /* 0x001b */
+	6, /* 0x001c */
+	7, /* 0x001d */
+	7, /* 0x001e */
+	0, /* 0x001f */
+	0, /* 0x0020 */
+	7, /* 0x0021 */
+	6, /* 0x0022 */
+	0  /* 0x0023-0xfffe */
+};
+
+static const char cmd_feat_str[] ALIGN1 =
+	"" "\0"                                     /* word 82 bit 15: obsolete  */
+	"NOP cmd" "\0"                              /* word 82 bit 14 */
+	"READ BUFFER cmd" "\0"                      /* word 82 bit 13 */
+	"WRITE BUFFER cmd" "\0"                     /* word 82 bit 12 */
+	"" "\0"                                     /* word 82 bit 11: obsolete  */
+	"Host Protected Area feature set" "\0"      /* word 82 bit 10 */
+	"DEVICE RESET cmd" "\0"                     /* word 82 bit  9 */
+	"SERVICE interrupt" "\0"                    /* word 82 bit  8 */
+	"Release interrupt" "\0"                    /* word 82 bit  7 */
+	"Look-ahead" "\0"                           /* word 82 bit  6 */
+	"Write cache" "\0"                          /* word 82 bit  5 */
+	"PACKET command feature set" "\0"           /* word 82 bit  4 */
+	"Power Management feature set" "\0"         /* word 82 bit  3 */
+	"Removable Media feature set" "\0"          /* word 82 bit  2 */
+	"Security Mode feature set" "\0"            /* word 82 bit  1 */
+	"SMART feature set" "\0"                    /* word 82 bit  0 */
+	                                            /* -------------- */
+	"" "\0"                                     /* word 83 bit 15: !valid bit */
+	"" "\0"                                     /* word 83 bit 14:  valid bit */
+	"FLUSH CACHE EXT cmd" "\0"                  /* word 83 bit 13 */
+	"Mandatory FLUSH CACHE cmd " "\0"           /* word 83 bit 12 */
+	"Device Configuration Overlay feature set " "\0"
+	"48-bit Address feature set " "\0"          /* word 83 bit 10 */
+	"" "\0"
+	"SET MAX security extension" "\0"           /* word 83 bit  8 */
+	"Address Offset Reserved Area Boot" "\0"    /* word 83 bit  7 */
+	"SET FEATURES subcommand required to spinup after power up" "\0"
+	"Power-Up In Standby feature set" "\0"      /* word 83 bit  5 */
+	"Removable Media Status Notification feature set" "\0"
+	"Adv. Power Management feature set" "\0"    /* word 83 bit  3 */
+	"CFA feature set" "\0"                      /* word 83 bit  2 */
+	"READ/WRITE DMA QUEUED" "\0"                /* word 83 bit  1 */
+	"DOWNLOAD MICROCODE cmd" "\0"               /* word 83 bit  0 */
+	                                            /* -------------- */
+	"" "\0"                                     /* word 84 bit 15: !valid bit */
+	"" "\0"                                     /* word 84 bit 14:  valid bit */
+	"" "\0"                                     /* word 84 bit 13:  reserved */
+	"" "\0"                                     /* word 84 bit 12:  reserved */
+	"" "\0"                                     /* word 84 bit 11:  reserved */
+	"" "\0"                                     /* word 84 bit 10:  reserved */
+	"" "\0"                                     /* word 84 bit  9:  reserved */
+	"" "\0"                                     /* word 84 bit  8:  reserved */
+	"" "\0"                                     /* word 84 bit  7:  reserved */
+	"" "\0"                                     /* word 84 bit  6:  reserved */
+	"General Purpose Logging feature set" "\0"  /* word 84 bit  5 */
+	"" "\0"                                     /* word 84 bit  4:  reserved */
+	"Media Card Pass Through Command feature set " "\0"
+	"Media serial number " "\0"                 /* word 84 bit  2 */
+	"SMART self-test " "\0"                     /* word 84 bit  1 */
+	"SMART error logging "                      /* word 84 bit  0 */
+;
+
+static const char secu_str[] ALIGN1 =
+	"supported" "\0"                /* word 128, bit 0 */
+	"enabled" "\0"                  /* word 128, bit 1 */
+	"locked" "\0"                   /* word 128, bit 2 */
+	"frozen" "\0"                   /* word 128, bit 3 */
+	"expired: security count" "\0"  /* word 128, bit 4 */
+	"supported: enhanced erase"     /* word 128, bit 5 */
+;
+
+// Parse 512 byte disk identification block and print much crap.
+static void identify(uint16_t *val) NORETURN;
+static void identify(uint16_t *val)
+{
+	uint16_t ii, jj, kk;
+	uint16_t like_std = 1, std = 0, min_std = 0xffff;
+	uint16_t dev = NO_DEV, eqpt = NO_DEV;
+	uint8_t  have_mode = 0, err_dma = 0;
+	uint8_t  chksum = 0;
+	uint32_t ll, mm, nn, oo;
+	uint64_t bbbig; /* (:) */
+	const char *strng;
+#if BB_BIG_ENDIAN
+	uint16_t buf[256];
+
+	// Adjust for endianness
+	swab(val, buf, sizeof(buf));
+	val = buf;
+#endif
+	/* check if we recognize the device type */
+	bb_putchar('\n');
+	if (!(val[GEN_CONFIG] & NOT_ATA)) {
+		dev = ATA_DEV;
+		printf("ATA device, with ");
+	} else if (val[GEN_CONFIG]==CFA_SUPPORT_VAL) {
+		dev = ATA_DEV;
+		like_std = 4;
+		printf("CompactFlash ATA device, with ");
+	} else if (!(val[GEN_CONFIG] & NOT_ATAPI)) {
+		dev = ATAPI_DEV;
+		eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT;
+		printf("ATAPI %s, with ", eqpt <= 0xf ? nth_string(pkt_str, eqpt) : "unknown");
+		like_std = 3;
+	} else
+		/* "Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n" */
+		bb_error_msg_and_die("unknown device type");
+
+	printf("%sremovable media\n", !(val[GEN_CONFIG] & MEDIA_REMOVABLE) ? "non-" : "");
+	/* Info from the specific configuration word says whether or not the
+	 * ID command completed correctly.  It is only defined, however in
+	 * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior
+	 * standards.  Since the values allowed for this word are extremely
+	 * specific, it should be safe to check it now, even though we don't
+	 * know yet what standard this device is using.
+	 */
+	if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)
+	 || (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL)
+	) {
+		like_std = 5;
+		if ((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL))
+			printf("powers-up in standby; SET FEATURES subcmd spins-up.\n");
+		if (((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && (val[GEN_CONFIG] & INCOMPLETE))
+			printf("\n\tWARNING: ID response incomplete.\n\tFollowing data may be incorrect.\n\n");
+	}
+
+	/* output the model and serial numbers and the fw revision */
+	xprint_ascii(val, START_MODEL,  "Model Number:",        LENGTH_MODEL);
+	xprint_ascii(val, START_SERIAL, "Serial Number:",       LENGTH_SERIAL);
+	xprint_ascii(val, START_FW_REV, "Firmware Revision:",   LENGTH_FW_REV);
+	xprint_ascii(val, START_MEDIA,  "Media Serial Num:",    LENGTH_MEDIA);
+	xprint_ascii(val, START_MANUF,  "Media Manufacturer:",  LENGTH_MANUF);
+
+	/* major & minor standards version number (Note: these words were not
+	 * defined until ATA-3 & the CDROM std uses different words.) */
+	printf("Standards:");
+	if (eqpt != CDROM) {
+		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]));
+		}
+		/* looks like when they up-issue the std, they obsolete one;
+		 * thus, only the newest 4 issues need be supported. (That's
+		 * what "kk" and "min_std" are all about.) */
+		if (val[MAJOR] && (val[MAJOR] != NOVAL_1)) {
+			printf("\n\tSupported: ");
+			jj = val[MAJOR] << 1;
+			kk = like_std >4 ? like_std-4: 0;
+			for (ii = 14; (ii >0)&&(ii>kk); ii--) {
+				if (jj & 0x8000) {
+					printf("%u ", ii);
+					if (like_std < ii) {
+						like_std = ii;
+						kk = like_std >4 ? like_std-4: 0;
+					}
+					if (min_std > ii) min_std = ii;
+				}
+				jj <<= 1;
+			}
+			if (like_std < 3) like_std = 3;
+		}
+		/* Figure out what standard the device is using if it hasn't told
+		 * us.  If we know the std, check if the device is using any of
+		 * the words from the next level up.  It happens.
+		 */
+		if (like_std < std) like_std = std;
+
+		if (((std == 5) || (!std && (like_std < 6))) &&
+			((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+			((	val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) ||
+			(((	val[CMDS_SUPP_2] & VALID) == VALID_VAL) &&
+			(	val[CMDS_SUPP_2] & CMDS_W84) ) )
+		) {
+			like_std = 6;
+		} else if (((std == 4) || (!std && (like_std < 5))) &&
+			((((val[INTEGRITY]	& SIG) == SIG_VAL) && !chksum) ||
+			((	val[HWRST_RSLT] & VALID) == VALID_VAL) ||
+			(((	val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+			((	val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) )
+		{
+			like_std = 5;
+		} else if (((std == 3) || (!std && (like_std < 4))) &&
+				((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) &&
+				(((	val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) ||
+				((	val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) ||
+				((	val[CAPAB_1] & VALID) == VALID_VAL) ||
+				((	val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) ||
+				((	val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) )
+		) {
+			like_std = 4;
+		} else if (((std == 2) || (!std && (like_std < 3)))
+		 && ((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
+		) {
+			like_std = 3;
+		} else if (((std == 1) || (!std && (like_std < 2))) &&
+				((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) ||
+				(val[WHATS_VALID] & OK_W64_70)) )
+		{
+			like_std = 2;
+		}
+
+		if (!std)
+			printf("\n\tLikely used: %u\n", like_std);
+		else if (like_std > std)
+			printf("& some of %u\n", like_std);
+		else
+			bb_putchar('\n');
+	} else {
+		/* TBD: do CDROM stuff more thoroughly.  For now... */
+		kk = 0;
+		if (val[CDR_MINOR] == 9) {
+			kk = 1;
+			printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5");
+		}
+		if (val[CDR_MAJOR] && (val[CDR_MAJOR] !=NOVAL_1)) {
+			kk = 1;
+			printf("\n\tSupported: CD-ROM ATAPI");
+			jj = val[CDR_MAJOR] >> 1;
+			for (ii = 1; ii < 15; ii++) {
+				if (jj & 0x0001) printf("-%u ", ii);
+				jj >>= 1;
+			}
+		}
+		puts(kk ? "" : "\n\tLikely used CD-ROM ATAPI-1");
+		/* the cdrom stuff is more like ATA-2 than anything else, so: */
+		like_std = 2;
+	}
+
+	if (min_std == 0xffff)
+		min_std = like_std > 4 ? like_std - 3 : 1;
+
+	printf("Configuration:\n");
+	/* more info from the general configuration word */
+	if ((eqpt != CDROM) && (like_std == 1)) {
+		jj = val[GEN_CONFIG] >> 1;
+		for (ii = 1; ii < 15; ii++) {
+			if (jj & 0x0001)
+				printf("\t%s\n", nth_string(ata1_cfg_str, ii));
+			jj >>=1;
+		}
+	}
+	if (dev == ATAPI_DEV) {
+		if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) ==  DRQ_3MS_VAL)
+			strng = "3ms";
+		else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) ==  DRQ_INTR_VAL)
+			strng = "<=10ms with INTRQ";
+		else if ((val[GEN_CONFIG] & DRQ_RESPONSE_TIME) ==  DRQ_50US_VAL)
+			strng ="50us";
+		else
+			strng = "unknown";
+		printf("\tDRQ response: %s\n\tPacket size: ", strng); /* Data Request (DRQ) */
+
+		if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_12_VAL)
+			strng = "12 bytes";
+		else if ((val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) == PKT_SIZE_16_VAL)
+			strng = "16 bytes";
+		else
+			strng = "unknown";
+		puts(strng);
+	} else {
+		/* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */
+		ll = (uint32_t)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB];
+		mm = 0;
+		bbbig = 0;
+		if ((ll > 0x00FBFC10) && (!val[LCYLS]))
+			printf("\tCHS addressing not supported\n");
+		else {
+			jj = val[WHATS_VALID] & OK_W54_58;
+			printf("\tLogical\t\tmax\tcurrent\n"
+				"\tcylinders\t%u\t%u\n"
+				"\theads\t\t%u\t%u\n"
+				"\tsectors/track\t%u\t%u\n"
+				"\t--\n",
+				val[LCYLS],
+				jj ? val[LCYLS_CUR] : 0,
+				val[LHEADS],
+				jj ? val[LHEADS_CUR] : 0,
+				val[LSECTS],
+				jj ? val[LSECTS_CUR] : 0);
+
+			if ((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES]))
+				printf("\tbytes/track: %u\tbytes/sector: %u\n",
+					val[TRACK_BYTES], val[SECT_BYTES]);
+
+			if (jj) {
+				mm = (uint32_t)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB];
+				if (like_std < 3) {
+					/* check Endian of capacity bytes */
+					nn = val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR];
+					oo = (uint32_t)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB];
+					if (abs(mm - nn) > abs(oo - nn))
+						mm = oo;
+				}
+				printf("\tCHS current addressable sectors:%11u\n", mm);
+			}
+		}
+		/* LBA addressing */
+		printf("\tLBA    user addressable sectors:%11u\n", ll);
+		if (((val[CMDS_SUPP_1] & VALID) == VALID_VAL)
+		 && (val[CMDS_SUPP_1] & SUPPORT_48_BIT)
+		) {
+			bbbig = (uint64_t)val[LBA_64_MSB] << 48 |
+			        (uint64_t)val[LBA_48_MSB] << 32 |
+			        (uint64_t)val[LBA_MID] << 16 |
+					val[LBA_LSB];
+			printf("\tLBA48  user addressable sectors:%11"PRIu64"\n", bbbig);
+		}
+
+		if (!bbbig)
+			bbbig = (uint64_t)(ll>mm ? ll : mm); /* # 512 byte blocks */
+		printf("\tdevice size with M = 1024*1024: %11"PRIu64" MBytes\n", bbbig>>11);
+		bbbig = (bbbig << 9) / 1000000;
+		printf("\tdevice size with M = 1000*1000: %11"PRIu64" MBytes ", bbbig);
+
+		if (bbbig > 1000)
+			printf("(%"PRIu64" GB)\n", bbbig/1000);
+		else
+			bb_putchar('\n');
+	}
+
+	/* hw support of commands (capabilities) */
+	printf("Capabilities:\n\t");
+
+	if (dev == ATAPI_DEV) {
+		if (eqpt != CDROM && (val[CAPAB_0] & CMD_Q_SUP))
+			printf("Cmd queuing, ");
+		if (val[CAPAB_0] & OVLP_SUP)
+			printf("Cmd overlap, ");
+	}
+	if (val[CAPAB_0] & LBA_SUP) printf("LBA, ");
+
+	if (like_std != 1) {
+		printf("IORDY%s(can%s be disabled)\n",
+			!(val[CAPAB_0] & IORDY_SUP) ? "(may be)" : "",
+			(val[CAPAB_0] & IORDY_OFF) ? "" :"not");
+	} else
+		printf("no IORDY\n");
+
+	if ((like_std == 1) && val[BUF_TYPE]) {
+		printf("\tBuffer type: %04x: %s%s\n", val[BUF_TYPE],
+			(val[BUF_TYPE] < 2) ? "single port, single-sector" : "dual port, multi-sector",
+			(val[BUF_TYPE] > 2) ? " with read caching ability" : "");
+	}
+
+	if ((min_std == 1) && (val[BUFFER__SIZE] && (val[BUFFER__SIZE] != NOVAL_1))) {
+		printf("\tBuffer size: %.1fkB\n", (float)val[BUFFER__SIZE]/2);
+	}
+	if ((min_std < 4) && (val[RW_LONG])) {
+		printf("\tbytes avail on r/w long: %u\n", val[RW_LONG]);
+	}
+	if ((eqpt != CDROM) && (like_std > 3)) {
+		printf("\tQueue depth: %u\n", (val[QUEUE_DEPTH] & DEPTH_BITS) + 1);
+	}
+
+	if (dev == ATA_DEV) {
+		if (like_std == 1)
+			printf("\tCan%s perform double-word IO\n", (!val[DWORD_IO]) ? "not" : "");
+		else {
+			printf("\tStandby timer values: spec'd by %s",
+				(val[CAPAB_0] & STD_STBY) ? "standard" : "vendor");
+			if ((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL))
+				printf(", %s device specific minimum\n",
+					(val[CAPAB_1] & MIN_STANDBY_TIMER) ? "with" : "no");
+			else
+				bb_putchar('\n');
+		}
+		printf("\tR/W multiple sector transfer: ");
+		if ((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER))
+			printf("not supported\n");
+		else {
+			printf("Max = %u\tCurrent = ", val[SECTOR_XFER_MAX] & SECTOR_XFER);
+			if (val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID)
+				printf("%u\n", val[SECTOR_XFER_CUR] & SECTOR_XFER);
+			else
+				printf("?\n");
+		}
+		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. */
+			printf("\tAdvancedPM level: ");
+			if ((val[ADV_PWR] & 0xFF00) == 0x4000) {
+				uint8_t apm_level = val[ADV_PWR] & 0x00FF;
+				printf("%u (0x%x)\n", apm_level, apm_level);
+			}
+			else
+				printf("unknown setting (0x%04x)\n", val[ADV_PWR]);
+		}
+		if (like_std > 5 && val[ACOUSTIC]) {
+			printf("\tRecommended acoustic management value: %u, current value: %u\n",
+				(val[ACOUSTIC] >> 8) & 0x00ff,
+				val[ACOUSTIC] & 0x00ff);
+		}
+	} else {
+		 /* ATAPI */
+		if (eqpt != CDROM && (val[CAPAB_0] & SWRST_REQ))
+			printf("\tATA sw reset required\n");
+
+		if (val[PKT_REL] || val[SVC_NBSY]) {
+			printf("\tOverlap support:");
+			if (val[PKT_REL])
+				printf(" %uus to release bus.", val[PKT_REL]);
+			if (val[SVC_NBSY])
+				printf(" %uus to clear BSY after SERVICE cmd.",
+					val[SVC_NBSY]);
+			bb_putchar('\n');
+		}
+	}
+
+	/* DMA stuff. Check that only one DMA mode is selected. */
+	printf("\tDMA: ");
+	if (!(val[CAPAB_0] & DMA_SUP))
+		printf("not supported\n");
+	else {
+		if (val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA])
+			printf(" sdma%u\n", (val[DMA_MODE] & MODE) >> 8);
+		if (val[SINGLE_DMA]) {
+			jj = val[SINGLE_DMA];
+			kk = val[SINGLE_DMA] >> 8;
+			err_dma += mode_loop(jj, kk, 's', &have_mode);
+		}
+		if (val[MULTI_DMA]) {
+			jj = val[MULTI_DMA];
+			kk = val[MULTI_DMA] >> 8;
+			err_dma += mode_loop(jj, kk, 'm', &have_mode);
+		}
+		if ((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) {
+			jj = val[ULTRA_DMA];
+			kk = val[ULTRA_DMA] >> 8;
+			err_dma += mode_loop(jj, kk, 'u', &have_mode);
+		}
+		if (err_dma || !have_mode) printf("(?)");
+		bb_putchar('\n');
+
+		if ((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP))
+			printf("\t\tInterleaved DMA support\n");
+
+		if ((val[WHATS_VALID] & OK_W64_70)
+		 && (val[DMA_TIME_MIN] || val[DMA_TIME_NORM])
+		) {
+			printf("\t\tCycle time:");
+			if (val[DMA_TIME_MIN]) printf(" min=%uns", val[DMA_TIME_MIN]);
+			if (val[DMA_TIME_NORM]) printf(" recommended=%uns", val[DMA_TIME_NORM]);
+			bb_putchar('\n');
+		}
+	}
+
+	/* Programmed IO stuff */
+	printf("\tPIO: ");
+	/* If a drive supports mode n (e.g. 3), it also supports all modes less
+	 * than n (e.g. 3, 2, 1 and 0).  Print all the modes. */
+	if ((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP)) {
+		jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007;
+		for (ii = 0; ii <= PIO_MODE_MAX; ii++) {
+			if (jj & 0x0001) printf("pio%d ", ii);
+			jj >>=1;
+		}
+		bb_putchar('\n');
+	} else if (((min_std < 5) || (eqpt == CDROM)) && (val[PIO_MODE] & MODE)) {
+		for (ii = 0; ii <= val[PIO_MODE]>>8; ii++)
+			printf("pio%d ", ii);
+		bb_putchar('\n');
+	} else
+		puts("unknown");
+
+	if (val[WHATS_VALID] & OK_W64_70) {
+		if (val[PIO_NO_FLOW] || val[PIO_FLOW]) {
+			printf("\t\tCycle time:");
+			if (val[PIO_NO_FLOW])
+				printf(" no flow control=%uns", val[PIO_NO_FLOW]);
+			if (val[PIO_FLOW])
+				printf("  IORDY flow control=%uns", val[PIO_FLOW]);
+			bb_putchar('\n');
+		}
+	}
+
+	if ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) {
+		printf("Commands/features:\n"
+			"\tEnabled\tSupported:\n");
+		jj = val[CMDS_SUPP_0];
+		kk = val[CMDS_EN_0];
+		for (ii = 0; ii < NUM_CMD_FEAT_STR; ii++) {
+			const char *feat_str = nth_string(cmd_feat_str, ii);
+			if ((jj & 0x8000) && (*feat_str != '\0')) {
+				printf("\t%s\t%s\n", (kk & 0x8000) ? "   *" : "", feat_str);
+			}
+			jj <<= 1;
+			kk <<= 1;
+			if (ii % 16 == 15) {
+				jj = val[CMDS_SUPP_0+1+(ii/16)];
+				kk = val[CMDS_EN_0+1+(ii/16)];
+			}
+			if (ii == 31) {
+				if ((val[CMDS_SUPP_2] & VALID) != VALID_VAL)
+					ii +=16;
+			}
+		}
+	}
+	/* Removable Media Status Notification feature set */
+	if ((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP)
+		printf("\t%s supported\n", nth_string(cmd_feat_str, 27));
+
+	/* security */
+	if ((eqpt != CDROM) && (like_std > 3)
+	 && (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME])
+	) {
+		printf("Security:\n");
+		if (val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1))
+			printf("\tMaster password revision code = %u\n", val[PSWD_CODE]);
+		jj = val[SECU_STATUS];
+		if (jj) {
+			for (ii = 0; ii < NUM_SECU_STR; ii++) {
+				printf("\t%s\t%s\n",
+					(!(jj & 0x0001)) ? "not" : "",
+					nth_string(secu_str, ii));
+				jj >>=1;
+			}
+			if (val[SECU_STATUS] & SECU_ENABLED) {
+				printf("\tSecurity level %s\n",
+					(val[SECU_STATUS] & SECU_LEVEL) ? "maximum" : "high");
+			}
+		}
+		jj =  val[ERASE_TIME]     & ERASE_BITS;
+		kk =  val[ENH_ERASE_TIME] & ERASE_BITS;
+		if (jj || kk) {
+			bb_putchar('\t');
+			if (jj) printf("%umin for %sSECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1, "");
+			if (kk) printf("%umin for %sSECURITY ERASE UNIT. ", kk==ERASE_BITS ? 508 : kk<<1, "ENHANCED ");
+			bb_putchar('\n');
+		}
+	}
+
+	/* reset result */
+	jj = val[HWRST_RSLT];
+	if ((jj & VALID) == VALID_VAL) {
+		oo = (jj & RST0);
+		if (!oo)
+			jj >>= 8;
+		if ((jj & DEV_DET) == JUMPER_VAL)
+			strng = " determined by the jumper";
+		else if ((jj & DEV_DET) == CSEL_VAL)
+			strng = " determined by CSEL";
+		else
+			strng = "";
+		printf("HW reset results:\n"
+			"\tCBLID- %s Vih\n"
+			"\tDevice num = %i%s\n",
+			(val[HWRST_RSLT] & CBLID) ? "above" : "below",
+			!(oo), strng);
+	}
+
+	/* more stuff from std 5 */
+	if ((like_std > 4) && (eqpt != CDROM)) {
+		if (val[CFA_PWR_MODE] & VALID_W160) {
+			printf("CFA power mode 1:\n"
+				"\t%s%s\n",
+				(val[CFA_PWR_MODE] & PWR_MODE_OFF) ? "disabled" : "enabled",
+				(val[CFA_PWR_MODE] & PWR_MODE_REQ) ? " and required by some commands" : "");
+			if (val[CFA_PWR_MODE] & MAX_AMPS)
+				printf("\tMaximum current = %uma\n", val[CFA_PWR_MODE] & MAX_AMPS);
+		}
+		if ((val[INTEGRITY] & SIG) == SIG_VAL) {
+			printf("Checksum: %scorrect\n", chksum ? "in" : "");
+		}
+	}
+
+	exit(EXIT_SUCCESS);
+}
+#endif
+
+// Historically, if there was no HDIO_OBSOLETE_IDENTITY, then
+// then the HDIO_GET_IDENTITY only returned 142 bytes.
+// Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes,
+// and HDIO_GET_IDENTITY returns 512 bytes.  But the latest
+// 2.5.xx kernels no longer define HDIO_OBSOLETE_IDENTITY
+// (which they should, but they should just return -EINVAL).
+//
+// So.. we must now assume that HDIO_GET_IDENTITY returns 512 bytes.
+// On a really old system, it will not, and we will be confused.
+// Too bad, really.
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static const char cfg_str[] ALIGN1 =
+	"""\0"            "HardSect""\0"   "SoftSect""\0"  "NotMFM""\0"
+	"HdSw>15uSec""\0" "SpinMotCtl""\0" "Fixed""\0"     "Removeable""\0"
+	"DTR<=5Mbs""\0"   "DTR>5Mbs""\0"   "DTR>10Mbs""\0" "RotSpdTol>.5%""\0"
+	"dStbOff""\0"     "TrkOff""\0"     "FmtGapReq""\0" "nonMagnetic"
+;
+
+static const char BuffType[] ALIGN1 =
+	"unknown""\0"     "1Sect""\0"      "DualPort""\0"  "DualPortCache"
+;
+
+static NOINLINE void dump_identity(const struct hd_driveid *id)
+{
+	int i;
+	const unsigned short *id_regs = (const void*) id;
+
+	printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s\n Config={",
+				id->model, id->fw_rev, id->serial_no);
+	for (i = 0; i <= 15; i++) {
+		if (id->config & (1<<i))
+			printf(" %s", nth_string(cfg_str, i));
+	}
+	printf(" }\n RawCHS=%u/%u/%u, TrkSize=%u, SectSize=%u, ECCbytes=%u\n"
+		" BuffType=(%u) %s, BuffSize=%ukB, MaxMultSect=%u",
+		id->cyls, id->heads, id->sectors, id->track_bytes,
+		id->sector_bytes, id->ecc_bytes,
+		id->buf_type,
+		nth_string(BuffType, (id->buf_type > 3) ? 0 : id->buf_type),
+		id->buf_size/2, id->max_multsect);
+	if (id->max_multsect) {
+		printf(", MultSect=");
+		if (!(id->multsect_valid & 1))
+			printf("?%u?", id->multsect);
+		else if (id->multsect)
+			printf("%u", id->multsect);
+		else
+			printf("off");
+	}
+	bb_putchar('\n');
+
+	if (!(id->field_valid & 1))
+		printf(" (maybe):");
+
+	printf(" CurCHS=%u/%u/%u, CurSects=%lu, LBA=%s", id->cur_cyls, id->cur_heads,
+		id->cur_sectors,
+		(BB_BIG_ENDIAN) ?
+			(unsigned long)(id->cur_capacity0 << 16) | id->cur_capacity1 :
+			(unsigned long)(id->cur_capacity1 << 16) | id->cur_capacity0,
+			((id->capability&2) == 0) ? "no" : "yes");
+
+	if (id->capability & 2)
+		printf(", LBAsects=%u", id->lba_capacity);
+
+	printf("\n IORDY=%s",
+		(id->capability & 8)
+			? ((id->capability & 4) ? "on/off" : "yes")
+			: "no");
+
+	if (((id->capability & 8) || (id->field_valid & 2)) && (id->field_valid & 2))
+		printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy);
+
+	if ((id->capability & 1) && (id->field_valid & 2))
+		printf(", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time);
+
+	printf("\n PIO modes:  ");
+	if (id->tPIO <= 5) {
+		printf("pio0 ");
+		if (id->tPIO >= 1) printf("pio1 ");
+		if (id->tPIO >= 2) printf("pio2 ");
+	}
+	if (id->field_valid & 2) {
+		static const masks_labels_t pio_modes = {
+			.masks = { 1, 2, ~3 },
+			.labels = "pio3 \0""pio4 \0""pio? \0",
+		};
+		print_flags(&pio_modes, id->eide_pio_modes);
+	}
+	if (id->capability & 1) {
+		if (id->dma_1word | id->dma_mword) {
+			static const int dma_wmode_masks[] = { 0x100, 1, 0x200, 2, 0x400, 4, 0xf800, 0xf8 };
+			printf("\n DMA modes:  ");
+			print_flags_separated(dma_wmode_masks,
+				"*\0""sdma0 \0""*\0""sdma1 \0""*\0""sdma2 \0""*\0""sdma? \0",
+				id->dma_1word, NULL);
+			print_flags_separated(dma_wmode_masks,
+				"*\0""mdma0 \0""*\0""mdma1 \0""*\0""mdma2 \0""*\0""mdma? \0",
+				id->dma_mword, NULL);
+		}
+	}
+	if (((id->capability & 8) || (id->field_valid & 2)) && id->field_valid & 4) {
+		static const masks_labels_t ultra_modes1 = {
+			.masks = { 0x100, 0x001, 0x200, 0x002, 0x400, 0x004 },
+			.labels = "*\0""udma0 \0""*\0""udma1 \0""*\0""udma2 \0",
+		};
+
+		printf("\n UDMA modes: ");
+		print_flags(&ultra_modes1, id->dma_ultra);
+#ifdef __NEW_HD_DRIVE_ID
+		if (id->hw_config & 0x2000) {
+#else /* !__NEW_HD_DRIVE_ID */
+		if (id->word93 & 0x2000) {
+#endif /* __NEW_HD_DRIVE_ID */
+			static const masks_labels_t ultra_modes2 = {
+				.masks = { 0x0800, 0x0008, 0x1000, 0x0010,
+					0x2000, 0x0020, 0x4000, 0x0040,
+					0x8000, 0x0080 },
+				.labels = "*\0""udma3 \0""*\0""udma4 \0"
+					"*\0""udma5 \0""*\0""udma6 \0"
+					"*\0""udma7 \0"
+			};
+			print_flags(&ultra_modes2, id->dma_ultra);
+		}
+	}
+	printf("\n AdvancedPM=%s", (!(id_regs[83] & 8)) ? "no" : "yes");
+	if (id_regs[83] & 8) {
+		if (!(id_regs[86] & 8))
+			printf(": disabled (255)");
+		else if ((id_regs[91] & 0xFF00) != 0x4000)
+			printf(": unknown setting");
+		else
+			printf(": mode=0x%02X (%u)", id_regs[91] & 0xFF, id_regs[91] & 0xFF);
+	}
+	if (id_regs[82] & 0x20)
+		printf(" WriteCache=%s", (id_regs[85] & 0x20) ? "enabled" : "disabled");
+#ifdef __NEW_HD_DRIVE_ID
+	if ((id->minor_rev_num && id->minor_rev_num <= 31)
+	 || (id->major_rev_num && id->minor_rev_num <= 31)
+	) {
+		printf("\n Drive conforms to: %s: ",
+			(id->minor_rev_num <= 31) ? nth_string(minor_str, id->minor_rev_num) : "unknown");
+		if (id->major_rev_num != 0x0000 /* NOVAL_0 */
+		 && id->major_rev_num != 0xFFFF /* NOVAL_1 */
+		) {
+			for (i = 0; i <= 15; i++) {
+				if (id->major_rev_num & (1<<i))
+					printf(" ATA/ATAPI-%u", i);
+			}
+		}
+	}
+#endif /* __NEW_HD_DRIVE_ID */
+	printf("\n\n * current active mode\n\n");
+}
+#endif
+
+static void flush_buffer_cache(/*int fd*/ void)
+{
+	fsync(fd);				/* flush buffers */
+	ioctl_or_warn(fd, BLKFLSBUF, NULL); /* do it again, big time */
+#ifdef HDIO_DRIVE_CMD
+	sleep(1);
+	if (ioctl(fd, HDIO_DRIVE_CMD, NULL) && errno != EINVAL) {	/* await completion */
+		if (ENABLE_IOCTL_HEX2STR_ERROR) /* To be coherent with ioctl_or_warn */
+			bb_perror_msg("HDIO_DRIVE_CMD");
+		else
+			bb_perror_msg("ioctl %#x failed", HDIO_DRIVE_CMD);
+	}
+#endif
+}
+
+static void seek_to_zero(/*int fd*/ void)
+{
+	xlseek(fd, (off_t) 0, SEEK_SET);
+}
+
+static void read_big_block(/*int fd,*/ char *buf)
+{
+	int i;
+
+	xread(fd, buf, TIMING_BUF_BYTES);
+	/* access all sectors of buf to ensure the read fully completed */
+	for (i = 0; i < TIMING_BUF_BYTES; i += 512)
+		buf[i] &= 1;
+}
+
+static unsigned dev_size_mb(/*int fd*/ void)
+{
+	union {
+		unsigned long long blksize64;
+		unsigned blksize32;
+	} u;
+
+	if (0 == ioctl(fd, BLKGETSIZE64, &u.blksize64)) { // bytes
+		u.blksize64 /= (1024 * 1024);
+	} else {
+		xioctl(fd, BLKGETSIZE, &u.blksize32); // sectors
+		u.blksize64 = u.blksize32 / (2 * 1024);
+	}
+	if (u.blksize64 > UINT_MAX)
+		return UINT_MAX;
+	return u.blksize64;
+}
+
+static void print_timing(unsigned m, unsigned elapsed_us)
+{
+	unsigned sec = elapsed_us / 1000000;
+	unsigned hs = (elapsed_us % 1000000) / 10000;
+
+	printf("%5u MB in %u.%02u seconds = %u kB/s\n",
+		m, sec, hs,
+		/* "| 1" prevents div-by-0 */
+		(unsigned) ((unsigned long long)m * (1024 * 1000000) / (elapsed_us | 1))
+		// ~= (m * 1024) / (elapsed_us / 1000000)
+		// = kb / elapsed_sec
+	);
+}
+
+static void do_time(int cache /*,int fd*/)
+/* cache=1: time cache: repeatedly read N MB at offset 0
+ * cache=0: time device: linear read, starting at offset 0
+ */
+{
+	unsigned max_iterations, iterations;
+	unsigned start; /* doesn't need to be long long */
+	unsigned elapsed, elapsed2;
+	unsigned total_MB;
+	char *buf = xmalloc(TIMING_BUF_BYTES);
+
+	if (mlock(buf, TIMING_BUF_BYTES))
+		bb_perror_msg_and_die("mlock");
+
+	/* Clear out the device request queues & give them time to complete.
+	 * NB: *small* delay. User is expected to have a clue and to not run
+	 * heavy io in parallel with measurements. */
+	sync();
+	sleep(1);
+	if (cache) { /* Time cache */
+		seek_to_zero();
+		read_big_block(buf);
+		printf("Timing buffer-cache reads: ");
+	} else { /* Time device */
+		printf("Timing buffered disk reads:");
+	}
+	fflush_all();
+
+	/* Now do the timing */
+	iterations = 0;
+	/* Max time to run (small for cache, avoids getting
+	 * huge total_MB which can overlow unsigned type) */
+	elapsed2 = 510000; /* cache */
+	max_iterations = UINT_MAX;
+	if (!cache) {
+		elapsed2 = 3000000; /* not cache */
+		/* Don't want to read past the end! */
+		max_iterations = dev_size_mb() / TIMING_BUF_MB;
+	}
+	start = monotonic_us();
+	do {
+		if (cache)
+			seek_to_zero();
+		read_big_block(buf);
+		elapsed = (unsigned)monotonic_us() - start;
+		++iterations;
+	} while (elapsed < elapsed2 && iterations < max_iterations);
+	total_MB = iterations * TIMING_BUF_MB;
+	//printf(" elapsed:%u iterations:%u ", elapsed, iterations);
+	if (cache) {
+		/* Cache: remove lseek() and monotonic_us() overheads
+		 * from elapsed */
+		start = monotonic_us();
+		do {
+			seek_to_zero();
+			elapsed2 = (unsigned)monotonic_us() - start;
+		} while (--iterations);
+		//printf(" elapsed2:%u ", elapsed2);
+		elapsed -= elapsed2;
+		total_MB *= 2; // BUFCACHE_FACTOR (why?)
+		flush_buffer_cache();
+	}
+	print_timing(total_MB, elapsed);
+	munlock(buf, TIMING_BUF_BYTES);
+	free(buf);
+}
+
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+static void bus_state_value(unsigned value)
+{
+	if (value == BUSSTATE_ON)
+		on_off(1);
+	else if (value == BUSSTATE_OFF)
+		on_off(0);
+	else if (value == BUSSTATE_TRISTATE)
+		printf(" (tristate)\n");
+	else
+		printf(" (unknown: %d)\n", value);
+}
+#endif
+
+#ifdef HDIO_DRIVE_CMD
+static void interpret_standby(uint8_t standby)
+{
+	printf(" (");
+	if (standby == 0) {
+		printf("off");
+	} else if (standby <= 240 || standby == 252 || standby == 255) {
+		/* standby is in 5 sec units */
+		unsigned t = standby * 5;
+		printf("%u minutes %u seconds", t / 60, t % 60);
+	} else if (standby <= 251) {
+		unsigned t = (standby - 240); /* t is in 30 min units */;
+		printf("%u.%c hours", t / 2, (t & 1) ? '5' : '0');
+	}
+	if (standby == 253)
+		printf("vendor-specific");
+	if (standby == 254)
+		printf("reserved");
+	printf(")\n");
+}
+
+static const uint8_t xfermode_val[] ALIGN1 = {
+	 8,      9,     10,     11,     12,     13,     14,     15,
+	16,     17,     18,     19,     20,     21,     22,     23,
+	32,     33,     34,     35,     36,     37,     38,     39,
+	64,     65,     66,     67,     68,     69,     70,     71
+};
+/* NB: we save size by _not_ storing terninating NUL! */
+static const char xfermode_name[][5] ALIGN1 = {
+	"pio0", "pio1", "pio2", "pio3", "pio4", "pio5", "pio6", "pio7",
+	"sdma0","sdma1","sdma2","sdma3","sdma4","sdma5","sdma6","sdma7",
+	"mdma0","mdma1","mdma2","mdma3","mdma4","mdma5","mdma6","mdma7",
+	"udma0","udma1","udma2","udma3","udma4","udma5","udma6","udma7"
+};
+
+static int translate_xfermode(const char *name)
+{
+	int val;
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(xfermode_val); i++) {
+		if (!strncmp(name, xfermode_name[i], 5))
+			if (strlen(name) <= 5)
+				return xfermode_val[i];
+	}
+	/* Negative numbers are invalid and are caught later */
+	val = bb_strtoi(name, NULL, 10);
+	if (!errno)
+		return val;
+	return -1;
+}
+
+static void interpret_xfermode(unsigned xfermode)
+{
+	printf(" (");
+	if (xfermode == 0)
+		printf("default PIO mode");
+	else if (xfermode == 1)
+		printf("default PIO mode, disable IORDY");
+	else if (xfermode >= 8 && xfermode <= 15)
+		printf("PIO flow control mode%u", xfermode - 8);
+	else if (xfermode >= 16 && xfermode <= 23)
+		printf("singleword DMA mode%u", xfermode - 16);
+	else if (xfermode >= 32 && xfermode <= 39)
+		printf("multiword DMA mode%u", xfermode - 32);
+	else if (xfermode >= 64 && xfermode <= 71)
+		printf("UltraDMA mode%u", xfermode - 64);
+	else
+		printf("unknown");
+	printf(")\n");
+}
+#endif /* HDIO_DRIVE_CMD */
+
+static void print_flag(int flag, const char *s, unsigned long value)
+{
+	if (flag)
+		printf(" setting %s to %ld\n", s, value);
+}
+
+static void process_dev(char *devname)
+{
+	/*int fd;*/
+	long parm, multcount;
+#ifndef HDIO_DRIVE_CMD
+	int force_operation = 0;
+#endif
+	/* Please restore args[n] to these values after each ioctl
+	   except for args[2] */
+	unsigned char args[4] = { WIN_SETFEATURES, 0, 0, 0 };
+	const char *fmt = " %s\t= %2ld";
+
+	/*fd = xopen_nonblocking(devname);*/
+	xmove_fd(xopen_nonblocking(devname), fd);
+	printf("\n%s:\n", devname);
+
+	if (getset_readahead == IS_SET) {
+		print_flag(getset_readahead, "fs readahead", Xreadahead);
+		ioctl_or_warn(fd, BLKRASET, (int *)Xreadahead);
+	}
+#if ENABLE_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF
+	if (unregister_hwif) {
+		printf(" attempting to unregister hwif#%lu\n", hwif);
+		ioctl_or_warn(fd, HDIO_UNREGISTER_HWIF, (int *)(unsigned long)hwif);
+	}
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+	if (scan_hwif == IS_SET) {
+		printf(" attempting to scan hwif (0x%lx, 0x%lx, %lu)\n", hwif_data, hwif_ctrl, hwif_irq);
+		args[0] = hwif_data;
+		args[1] = hwif_ctrl;
+		args[2] = hwif_irq;
+		ioctl_or_warn(fd, HDIO_SCAN_HWIF, args);
+		args[0] = WIN_SETFEATURES;
+		args[1] = 0;
+	}
+#endif
+	if (set_piomode) {
+		if (noisy_piomode) {
+			printf(" attempting to ");
+			if (piomode == 255)
+				printf("auto-tune PIO mode\n");
+			else if (piomode < 100)
+				printf("set PIO mode to %d\n", piomode);
+			else if (piomode < 200)
+				printf("set MDMA mode to %d\n", (piomode-100));
+			else
+				printf("set UDMA mode to %d\n", (piomode-200));
+		}
+		ioctl_or_warn(fd, HDIO_SET_PIO_MODE, (int *)(unsigned long)piomode);
+	}
+	if (getset_io32bit == IS_SET) {
+		print_flag(getset_io32bit, "32-bit IO_support flag", io32bit);
+		ioctl_or_warn(fd, HDIO_SET_32BIT, (int *)io32bit);
+	}
+	if (getset_mult == IS_SET) {
+		print_flag(getset_mult, "multcount", mult);
+#ifdef HDIO_DRIVE_CMD
+		ioctl_or_warn(fd, HDIO_SET_MULTCOUNT, (void *)mult);
+#else
+		force_operation |= (!ioctl_or_warn(fd, HDIO_SET_MULTCOUNT, (void *)mult));
+#endif
+	}
+	if (getset_readonly == IS_SET) {
+		print_flag_on_off(getset_readonly, "readonly", readonly);
+		ioctl_or_warn(fd, BLKROSET, &readonly);
+	}
+	if (getset_unmask == IS_SET) {
+		print_flag_on_off(getset_unmask, "unmaskirq", unmask);
+		ioctl_or_warn(fd, HDIO_SET_UNMASKINTR, (int *)unmask);
+	}
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+	if (getset_dma == IS_SET) {
+		print_flag_on_off(getset_dma, "using_dma", dma);
+		ioctl_or_warn(fd, HDIO_SET_DMA, (int *)dma);
+	}
+#endif /* FEATURE_HDPARM_HDIO_GETSET_DMA */
+#ifdef HDIO_SET_QDMA
+	if (getset_dma_q == IS_SET) {
+		print_flag_on_off(getset_dma_q, "DMA queue_depth", dma_q);
+		ioctl_or_warn(fd, HDIO_SET_QDMA, (int *)dma_q);
+	}
+#endif
+	if (getset_nowerr == IS_SET) {
+		print_flag_on_off(getset_nowerr, "nowerr", nowerr);
+		ioctl_or_warn(fd, HDIO_SET_NOWERR, (int *)nowerr);
+	}
+	if (getset_keep == IS_SET) {
+		print_flag_on_off(getset_keep, "keep_settings", keep);
+		ioctl_or_warn(fd, HDIO_SET_KEEPSETTINGS, (int *)keep);
+	}
+#ifdef HDIO_DRIVE_CMD
+	if (getset_doorlock == IS_SET) {
+		args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK;
+		args[2] = 0;
+		print_flag_on_off(getset_doorlock, "drive doorlock", doorlock);
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+		args[0] = WIN_SETFEATURES;
+	}
+	if (getset_dkeep == IS_SET) {
+		/* lock/unlock the drive's "feature" settings */
+		print_flag_on_off(getset_dkeep, "drive keep features", dkeep);
+		args[2] = dkeep ? 0x66 : 0xcc;
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+	}
+	if (getset_defects == IS_SET) {
+		args[2] = defects ? 0x04 : 0x84;
+		print_flag(getset_defects, "drive defect-mgmt", defects);
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+	}
+	if (getset_prefetch == IS_SET) {
+		args[1] = prefetch;
+		args[2] = 0xab;
+		print_flag(getset_prefetch, "drive prefetch", prefetch);
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+		args[1] = 0;
+	}
+	if (set_xfermode) {
+		args[1] = xfermode_requested;
+		args[2] = 3;
+		print_flag(1, "xfermode", xfermode_requested);
+		interpret_xfermode(xfermode_requested);
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+		args[1] = 0;
+	}
+	if (getset_lookahead == IS_SET) {
+		args[2] = lookahead ? 0xaa : 0x55;
+		print_flag_on_off(getset_lookahead, "drive read-lookahead", lookahead);
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+	}
+	if (getset_apmmode == IS_SET) {
+		/* feature register */
+		args[2] = (apmmode == 255) ? 0x85 /* disable */ : 0x05 /* set */;
+		args[1] = apmmode; /* sector count register 1-255 */
+		printf(" setting APM level to %s 0x%02lX (%ld)\n",
+			(apmmode == 255) ? "disabled" : "",
+			apmmode, apmmode);
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+		args[1] = 0;
+	}
+	if (getset_wcache == IS_SET) {
+#ifdef DO_FLUSHCACHE
+#ifndef WIN_FLUSHCACHE
+#define WIN_FLUSHCACHE 0xe7
+#endif
+#endif /* DO_FLUSHCACHE */
+		args[2] = wcache ? 0x02 : 0x82;
+		print_flag_on_off(getset_wcache, "drive write-caching", wcache);
+#ifdef DO_FLUSHCACHE
+		if (!wcache)
+			ioctl_or_warn(fd, HDIO_DRIVE_CMD, &flushcache);
+#endif /* DO_FLUSHCACHE */
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+#ifdef DO_FLUSHCACHE
+		if (!wcache)
+			ioctl_or_warn(fd, HDIO_DRIVE_CMD, &flushcache);
+#endif /* DO_FLUSHCACHE */
+	}
+
+	/* In code below, we do not preserve args[0], but the rest
+	   is preserved, including args[2] */
+	args[2] = 0;
+
+	if (set_standbynow) {
+#ifndef WIN_STANDBYNOW1
+#define WIN_STANDBYNOW1 0xE0
+#endif
+#ifndef WIN_STANDBYNOW2
+#define WIN_STANDBYNOW2 0x94
+#endif
+		printf(" issuing standby command\n");
+		args[0] = WIN_STANDBYNOW1;
+		ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_STANDBYNOW2);
+	}
+	if (set_sleepnow) {
+#ifndef WIN_SLEEPNOW1
+#define WIN_SLEEPNOW1 0xE6
+#endif
+#ifndef WIN_SLEEPNOW2
+#define WIN_SLEEPNOW2 0x99
+#endif
+		printf(" issuing sleep command\n");
+		args[0] = WIN_SLEEPNOW1;
+		ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_SLEEPNOW2);
+	}
+	if (set_seagate) {
+		args[0] = 0xfb;
+		printf(" disabling Seagate auto powersaving mode\n");
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+	}
+	if (getset_standby == IS_SET) {
+		args[0] = WIN_SETIDLE1;
+		args[1] = standby_requested;
+		print_flag(1, "standby", standby_requested);
+		interpret_standby(standby_requested);
+		ioctl_or_warn(fd, HDIO_DRIVE_CMD, &args);
+		args[1] = 0;
+	}
+#else	/* HDIO_DRIVE_CMD */
+	if (force_operation) {
+		char buf[512];
+		flush_buffer_cache();
+		if (-1 == read(fd, buf, sizeof(buf)))
+			bb_perror_msg("read of 512 bytes failed");
+	}
+#endif  /* HDIO_DRIVE_CMD */
+	if (getset_mult || get_identity) {
+		multcount = -1;
+		if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount)) {
+			/* To be coherent with ioctl_or_warn. */
+			if (getset_mult && ENABLE_IOCTL_HEX2STR_ERROR)
+				bb_perror_msg("HDIO_GET_MULTCOUNT");
+			else
+				bb_perror_msg("ioctl %#x failed", HDIO_GET_MULTCOUNT);
+		} else if (getset_mult) {
+			printf(fmt, "multcount", multcount);
+			on_off(multcount != 0);
+		}
+	}
+	if (getset_io32bit) {
+		if (!ioctl_or_warn(fd, HDIO_GET_32BIT, &parm)) {
+			printf(" IO_support\t=%3ld (", parm);
+			if (parm == 0)
+				printf("default 16-bit)\n");
+			else if (parm == 2)
+				printf("16-bit)\n");
+			else if (parm == 1)
+				printf("32-bit)\n");
+			else if (parm == 3)
+				printf("32-bit w/sync)\n");
+			else if (parm == 8)
+				printf("Request-Queue-Bypass)\n");
+			else
+				printf("\?\?\?)\n");
+		}
+	}
+	if (getset_unmask) {
+		if (!ioctl_or_warn(fd, HDIO_GET_UNMASKINTR, &parm))
+			print_value_on_off("unmaskirq", parm);
+	}
+#if ENABLE_FEATURE_HDPARM_HDIO_GETSET_DMA
+	if (getset_dma) {
+		if (!ioctl_or_warn(fd, HDIO_GET_DMA, &parm)) {
+			printf(fmt, "using_dma", parm);
+			if (parm == 8)
+				printf(" (DMA-Assisted-PIO)\n");
+			else
+				on_off(parm != 0);
+		}
+	}
+#endif
+#ifdef HDIO_GET_QDMA
+	if (getset_dma_q) {
+		if (!ioctl_or_warn(fd, HDIO_GET_QDMA, &parm))
+			print_value_on_off("queue_depth", parm);
+	}
+#endif
+	if (getset_keep) {
+		if (!ioctl_or_warn(fd, HDIO_GET_KEEPSETTINGS, &parm))
+			print_value_on_off("keepsettings", parm);
+	}
+	if (getset_nowerr) {
+		if (!ioctl_or_warn(fd, HDIO_GET_NOWERR, &parm))
+			print_value_on_off("nowerr", parm);
+	}
+	if (getset_readonly) {
+		if (!ioctl_or_warn(fd, BLKROGET, &parm))
+			print_value_on_off("readonly", parm);
+	}
+	if (getset_readahead) {
+		if (!ioctl_or_warn(fd, BLKRAGET, &parm))
+			print_value_on_off("readahead", parm);
+	}
+	if (get_geom) {
+		if (!ioctl_or_warn(fd, BLKGETSIZE, &parm)) {
+			struct hd_geometry g;
+
+			if (!ioctl_or_warn(fd, HDIO_GETGEO, &g))
+				printf(" geometry\t= %u/%u/%u, sectors = %ld, start = %ld\n",
+					g.cylinders, g.heads, g.sectors, parm, g.start);
+		}
+	}
+#ifdef HDIO_DRIVE_CMD
+	if (get_powermode) {
+#ifndef WIN_CHECKPOWERMODE1
+#define WIN_CHECKPOWERMODE1 0xE5
+#endif
+#ifndef WIN_CHECKPOWERMODE2
+#define WIN_CHECKPOWERMODE2 0x98
+#endif
+		const char *state;
+
+		args[0] = WIN_CHECKPOWERMODE1;
+		if (ioctl_alt_or_warn(HDIO_DRIVE_CMD, args, WIN_CHECKPOWERMODE2)) {
+			if (errno != EIO || args[0] != 0 || args[1] != 0)
+				state = "unknown";
+			else
+				state = "sleeping";
+		} else
+			state = (args[2] == 255) ? "active/idle" : "standby";
+		args[1] = args[2] = 0;
+
+		printf(" drive state is:  %s\n", state);
+	}
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_DRIVE_RESET
+	if (perform_reset) {
+		ioctl_or_warn(fd, HDIO_DRIVE_RESET, NULL);
+	}
+#endif /* FEATURE_HDPARM_HDIO_DRIVE_RESET */
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+	if (perform_tristate) {
+		args[0] = 0;
+		args[1] = tristate;
+		ioctl_or_warn(fd, HDIO_TRISTATE_HWIF, &args);
+	}
+#endif /* FEATURE_HDPARM_HDIO_TRISTATE_HWIF */
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+	if (get_identity) {
+		struct hd_driveid id;
+
+		if (!ioctl(fd, HDIO_GET_IDENTITY, &id))	{
+			if (multcount != -1) {
+				id.multsect = multcount;
+				id.multsect_valid |= 1;
+			} else
+				id.multsect_valid &= ~1;
+			dump_identity(&id);
+		} else if (errno == -ENOMSG)
+			printf(" no identification info available\n");
+		else if (ENABLE_IOCTL_HEX2STR_ERROR)  /* To be coherent with ioctl_or_warn */
+			bb_perror_msg("HDIO_GET_IDENTITY");
+		else
+			bb_perror_msg("ioctl %#x failed", HDIO_GET_IDENTITY);
+	}
+
+	if (get_IDentity) {
+		unsigned char args1[4+512]; /* = { ... } will eat 0.5k of rodata! */
+
+		memset(args1, 0, sizeof(args1));
+		args1[0] = WIN_IDENTIFY;
+		args1[3] = 1;
+		if (!ioctl_alt_or_warn(HDIO_DRIVE_CMD, args1, WIN_PIDENTIFY))
+			identify((void *)(args1 + 4));
+	}
+#endif
+#if ENABLE_FEATURE_HDPARM_HDIO_TRISTATE_HWIF
+	if (getset_busstate == IS_SET) {
+		print_flag(1, "bus state", busstate);
+		bus_state_value(busstate);
+		ioctl_or_warn(fd, HDIO_SET_BUSSTATE, (int *)(unsigned long)busstate);
+	}
+	if (getset_busstate) {
+		if (!ioctl_or_warn(fd, HDIO_GET_BUSSTATE, &parm)) {
+			printf(fmt, "bus state", parm);
+			bus_state_value(parm);
+		}
+	}
+#endif
+	if (reread_partn)
+		ioctl_or_warn(fd, BLKRRPART, NULL);
+
+	if (do_ctimings)
+		do_time(1 /*,fd*/); /* time cache */
+	if (do_timings)
+		do_time(0 /*,fd*/); /* time device */
+	if (do_flush)
+		flush_buffer_cache();
+	close(fd);
+}
+
+#if ENABLE_FEATURE_HDPARM_GET_IDENTITY
+static int fromhex(unsigned char c)
+{
+	if (isdigit(c))
+		return (c - '0');
+	if (c >= 'a' && c <= 'f')
+		return (c - ('a' - 10));
+	bb_error_msg_and_die("bad char: '%c' 0x%02x", c, c);
+}
+
+static void identify_from_stdin(void) NORETURN;
+static void identify_from_stdin(void)
+{
+	uint16_t sbuf[256];
+	unsigned char buf[1280];
+	unsigned char *b = (unsigned char *)buf;
+	int i;
+
+	xread(STDIN_FILENO, buf, 1280);
+
+	// Convert the newline-separated hex data into an identify block.
+
+	for (i = 0; i < 256; i++) {
+		int j;
+		for (j = 0; j < 4; j++)
+			sbuf[i] = (sbuf[i] << 4) + fromhex(*(b++));
+	}
+
+	// Parse the data.
+
+	identify(sbuf);
+}
+#else
+void identify_from_stdin(void);
+#endif
+
+/* busybox specific stuff */
+static int parse_opts(unsigned long *value, int min, int max)
+{
+	if (optarg) {
+		*value = xatol_range(optarg, min, max);
+		return IS_SET;
+	}
+	return IS_GET;
+}
+static int parse_opts_0_max(unsigned long *value, int max)
+{
+	return parse_opts(value, 0, max);
+}
+static int parse_opts_0_1(unsigned long *value)
+{
+	return parse_opts(value, 0, 1);
+}
+static int parse_opts_0_INTMAX(unsigned long *value)
+{
+	return parse_opts(value, 0, INT_MAX);
+}
+
+static void parse_xfermode(int flag, smallint *get, smallint *set, int *value)
+{
+	if (flag) {
+		*get = IS_GET;
+		if (optarg) {
+			*value = translate_xfermode(optarg);
+			*set = (*value > -1);
+		}
+	}
+}
+
+/*------- getopt short options --------*/
+static const char hdparm_options[] ALIGN1 =
+	"gfu::n::p:r::m::c::k::a::B:tT"
+	IF_FEATURE_HDPARM_GET_IDENTITY("iI")
+	IF_FEATURE_HDPARM_HDIO_GETSET_DMA("d::")
+#ifdef HDIO_DRIVE_CMD
+	"S:D:P:X:K:A:L:W:CyYzZ"
+#endif
+	IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF("U:")
+#ifdef HDIO_GET_QDMA
+#ifdef HDIO_SET_QDMA
+	"Q:"
+#else
+	"Q"
+#endif
+#endif
+	IF_FEATURE_HDPARM_HDIO_DRIVE_RESET("w")
+	IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF("x::b:")
+	IF_FEATURE_HDPARM_HDIO_SCAN_HWIF("R:");
+/*-------------------------------------*/
+
+/* our main() routine: */
+int hdparm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hdparm_main(int argc, char **argv)
+{
+	int c;
+	int flagcount = 0;
+
+	while ((c = getopt(argc, argv, hdparm_options)) >= 0) {
+		flagcount++;
+		IF_FEATURE_HDPARM_GET_IDENTITY(get_IDentity |= (c == 'I'));
+		IF_FEATURE_HDPARM_GET_IDENTITY(get_identity |= (c == 'i'));
+		get_geom |= (c == 'g');
+		do_flush |= (c == 'f');
+		if (c == 'u') getset_unmask    = parse_opts_0_1(&unmask);
+	IF_FEATURE_HDPARM_HDIO_GETSET_DMA(
+		if (c == 'd') getset_dma       = parse_opts_0_max(&dma, 9);
+	)
+		if (c == 'n') getset_nowerr    = parse_opts_0_1(&nowerr);
+		parse_xfermode((c == 'p'), &noisy_piomode, &set_piomode, &piomode);
+		if (c == 'r') getset_readonly  = parse_opts_0_1(&readonly);
+		if (c == 'm') getset_mult      = parse_opts_0_INTMAX(&mult /*32*/);
+		if (c == 'c') getset_io32bit   = parse_opts_0_INTMAX(&io32bit /*8*/);
+		if (c == 'k') getset_keep      = parse_opts_0_1(&keep);
+		if (c == 'a') getset_readahead = parse_opts_0_INTMAX(&Xreadahead);
+		if (c == 'B') getset_apmmode   = parse_opts(&apmmode, 1, 255);
+		do_flush |= do_timings |= (c == 't');
+		do_flush |= do_ctimings |= (c == 'T');
+#ifdef HDIO_DRIVE_CMD
+		if (c == 'S') getset_standby  = parse_opts_0_max(&standby_requested, 255);
+		if (c == 'D') getset_defects  = parse_opts_0_INTMAX(&defects);
+		if (c == 'P') getset_prefetch = parse_opts_0_INTMAX(&prefetch);
+		parse_xfermode((c == 'X'), &get_xfermode, &set_xfermode, &xfermode_requested);
+		if (c == 'K') getset_dkeep     = parse_opts_0_1(&prefetch);
+		if (c == 'A') getset_lookahead = parse_opts_0_1(&lookahead);
+		if (c == 'L') getset_doorlock  = parse_opts_0_1(&doorlock);
+		if (c == 'W') getset_wcache    = parse_opts_0_1(&wcache);
+		get_powermode |= (c == 'C');
+		set_standbynow |= (c == 'y');
+		set_sleepnow |= (c == 'Y');
+		reread_partn |= (c == 'z');
+		set_seagate |= (c == 'Z');
+#endif
+		IF_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF(if (c == 'U') unregister_hwif = parse_opts_0_INTMAX(&hwif));
+#ifdef HDIO_GET_QDMA
+		if (c == 'Q') {
+			getset_dma_q = parse_opts_0_INTMAX(&dma_q);
+		}
+#endif
+		IF_FEATURE_HDPARM_HDIO_DRIVE_RESET(perform_reset = (c == 'r'));
+		IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'x') perform_tristate = parse_opts_0_1(&tristate));
+		IF_FEATURE_HDPARM_HDIO_TRISTATE_HWIF(if (c == 'b') getset_busstate = parse_opts_0_max(&busstate, 2));
+#if ENABLE_FEATURE_HDPARM_HDIO_SCAN_HWIF
+		if (c == 'R') {
+			scan_hwif = parse_opts_0_INTMAX(&hwif_data);
+			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;
+		}
+#endif
+	}
+	/* When no flags are given (flagcount = 0), -acdgkmnru is assumed. */
+	if (!flagcount) {
+		getset_mult = getset_io32bit = getset_unmask = getset_keep = getset_readonly = getset_readahead = get_geom = IS_GET;
+		IF_FEATURE_HDPARM_HDIO_GETSET_DMA(getset_dma = IS_GET);
+	}
+	argv += optind;
+
+	if (!*argv) {
+		if (ENABLE_FEATURE_HDPARM_GET_IDENTITY && !isatty(STDIN_FILENO))
+			identify_from_stdin(); /* EXIT */
+		bb_show_usage();
+	}
+
+	do {
+		process_dev(*argv++);
+	} while (*argv);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/inotifyd.c b/busybox-1.19.3/miscutils/inotifyd.c
new file mode 100644
index 0000000..b64e0ab
--- /dev/null
+++ b/busybox-1.19.3/miscutils/inotifyd.c
@@ -0,0 +1,203 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * simple inotify daemon
+ * reports filesystem changes via userspace agent
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * Use as follows:
+ * # inotifyd /user/space/agent dir/or/file/being/watched[:mask] ...
+ *
+ * When a filesystem event matching the specified mask is occured on specified file (or directory)
+ * a userspace agent is spawned and given the following parameters:
+ * $1. actual event(s)
+ * $2. file (or directory) name
+ * $3. name of subfile (if any), in case of watching a directory
+ *
+ * E.g. inotifyd ./dev-watcher /dev:n
+ *
+ * ./dev-watcher can be, say:
+ * #!/bin/sh
+ * echo "We have new device in here! Hello, $3!"
+ *
+ * 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:     "\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>
+
+static const char mask_names[] ALIGN1 =
+	"a"	// 0x00000001	File was accessed
+	"c"	// 0x00000002	File was modified
+	"e"	// 0x00000004	Metadata changed
+	"w"	// 0x00000008	Writable file was closed
+	"0"	// 0x00000010	Unwritable file closed
+	"r"	// 0x00000020	File was opened
+	"m"	// 0x00000040	File was moved from X
+	"y"	// 0x00000080	File was moved to Y
+	"n"	// 0x00000100	Subfile was created
+	"d"	// 0x00000200	Subfile was deleted
+	"D"	// 0x00000400	Self was deleted
+	"M"	// 0x00000800	Self was moved
+	"\0"	// 0x00001000   (unused)
+	// Kernel events, always reported:
+	"u"	// 0x00002000   Backing fs was unmounted
+	"o"	// 0x00004000   Event queued overflowed
+	"x"	// 0x00008000   File is no longer watched (usually deleted)
+;
+enum {
+	MASK_BITS = sizeof(mask_names) - 1
+};
+
+int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int inotifyd_main(int argc, char **argv)
+{
+	int n;
+	unsigned mask;
+	struct pollfd pfd;
+	char **watches; // names of files being watched
+	const char *args[5];
+
+	// sanity check: agent and at least one watch must be given
+	if (!argv[1] || !argv[2])
+		bb_show_usage();
+
+	argv++;
+	// inotify_add_watch will number watched files
+	// starting from 1, thus watches[0] is unimportant,
+	// and 1st file name is watches[1].
+	watches = argv;
+	args[0] = *argv;
+	args[4] = NULL;
+	argc -= 2; // number of files we watch
+
+	// open inotify
+	pfd.fd = inotify_init();
+	if (pfd.fd < 0)
+		bb_perror_msg_and_die("no kernel support");
+
+	// setup watches
+	while (*++argv) {
+		char *path = *argv;
+		char *masks = strchr(path, ':');
+
+		mask = 0x0fff; // assuming we want all non-kernel events
+		// if mask is specified ->
+		if (masks) {
+			*masks = '\0'; // split path and mask
+			// convert mask names to mask bitset
+			mask = 0;
+			while (*++masks) {
+				const char *found;
+				found = memchr(mask_names, *masks, MASK_BITS);
+				if (found)
+					mask |= (1 << (found - mask_names));
+			}
+		}
+		// add watch
+		n = inotify_add_watch(pfd.fd, path, mask);
+		if (n < 0)
+			bb_perror_msg_and_die("add watch (%s) failed", path);
+		//bb_error_msg("added %d [%s]:%4X", n, path, mask);
+	}
+
+	// setup signals
+	bb_signals(BB_FATAL_SIGS, record_signo);
+
+	// do watch
+	pfd.events = POLLIN;
+	while (1) {
+		int len;
+		void *buf;
+		struct inotify_event *ie;
+ again:
+		if (bb_got_signal)
+			break;
+		n = poll(&pfd, 1, -1);
+		// Signal interrupted us?
+		if (n < 0 && errno == EINTR)
+			goto again;
+		// Under Linux, above if() is not necessary.
+		// Non-fatal signals, e.g. SIGCHLD, when set to SIG_DFL,
+		// are not interrupting poll().
+		// Thus we can just break if n <= 0 (see below),
+		// because EINTR will happen only on SIGTERM et al.
+		// But this might be not true under other Unixes,
+		// and is generally way too subtle to depend on.
+		if (n <= 0) // strange error?
+			break;
+
+		// read out all pending events
+		// (NB: len must be int, not ssize_t or long!)
+		xioctl(pfd.fd, FIONREAD, &len);
+#define eventbuf bb_common_bufsiz1
+		ie = buf = (len <= sizeof(eventbuf)) ? eventbuf : xmalloc(len);
+		len = full_read(pfd.fd, buf, len);
+		// process events. N.B. events may vary in length
+		while (len > 0) {
+			int i;
+			// cache relevant events mask
+			unsigned m = ie->mask & ((1 << MASK_BITS) - 1);
+			if (m) {
+				char events[MASK_BITS + 1];
+				char *s = events;
+				for (i = 0; i < MASK_BITS; ++i, m >>= 1) {
+					if ((m & 1) && (mask_names[i] != '\0'))
+						*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);
+				// we are done if all files got final x event
+				if (ie->mask & 0x8000) {
+					if (--argc <= 0)
+						goto done;
+					inotify_rm_watch(pfd.fd, ie->wd);
+				}
+			}
+			// next event
+			i = sizeof(struct inotify_event) + ie->len;
+			len -= i;
+			ie = (void*)((char*)ie + i);
+		}
+		if (eventbuf != buf)
+			free(buf);
+	} // while (1)
+ done:
+	return bb_got_signal;
+}
diff --git a/busybox-1.19.3/miscutils/ionice.c b/busybox-1.19.3/miscutils/ionice.c
new file mode 100644
index 0000000..bd30060
--- /dev/null
+++ b/busybox-1.19.3/miscutils/ionice.c
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ionice implementation for busybox based on linux-utils-ng 2.14
+ *
+ * Copyright (C) 2008 by  <u173034@informatik.uni-oldenburg.de>
+ *
+ * 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"
+
+static int ioprio_set(int which, int who, int ioprio)
+{
+	return syscall(SYS_ioprio_set, which, who, ioprio);
+}
+
+static int ioprio_get(int which, int who)
+{
+	return syscall(SYS_ioprio_get, which, who);
+}
+
+enum {
+	IOPRIO_WHO_PROCESS = 1,
+	IOPRIO_WHO_PGRP,
+	IOPRIO_WHO_USER
+};
+
+enum {
+	IOPRIO_CLASS_NONE,
+	IOPRIO_CLASS_RT,
+	IOPRIO_CLASS_BE,
+	IOPRIO_CLASS_IDLE
+};
+
+static const char to_prio[] = "none\0realtime\0best-effort\0idle";
+
+#define IOPRIO_CLASS_SHIFT      13
+
+int ionice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ionice_main(int argc UNUSED_PARAM, char **argv)
+{
+	/* Defaults */
+	int ioclass = 0;
+	int pri = 0;
+	int pid = 0; /* affect own porcess */
+	int opt;
+	enum {
+		OPT_n = 1,
+		OPT_c = 2,
+		OPT_p = 4,
+	};
+
+	/* Numeric params */
+	opt_complementary = "n+:c+:p+";
+	/* '+': stop at first non-option */
+	opt = getopt32(argv, "+n:c:p:", &pri, &ioclass, &pid);
+	argv += optind;
+
+	if (opt & OPT_c) {
+		if (ioclass > 3)
+			bb_error_msg_and_die("bad class %d", ioclass);
+// Do we need this (compat?)?
+//		if (ioclass == IOPRIO_CLASS_NONE)
+//			ioclass = IOPRIO_CLASS_BE;
+//		if (ioclass == IOPRIO_CLASS_IDLE) {
+//			//if (opt & OPT_n)
+//			//	bb_error_msg("ignoring priority for idle class");
+//			pri = 7;
+//		}
+	}
+
+	if (!(opt & (OPT_n|OPT_c))) {
+		if (!(opt & OPT_p) && *argv)
+			pid = xatoi_positive(*argv);
+
+		pri = ioprio_get(IOPRIO_WHO_PROCESS, pid);
+		if (pri == -1)
+			bb_perror_msg_and_die("ioprio_%cet", 'g');
+
+		ioclass = (pri >> IOPRIO_CLASS_SHIFT) & 0x3;
+		pri &= 0xff;
+		printf((ioclass == IOPRIO_CLASS_IDLE) ? "%s\n" : "%s: prio %d\n",
+				nth_string(to_prio, ioclass), pri);
+	} else {
+//printf("pri=%d class=%d val=%x\n",
+//pri, ioclass, pri | (ioclass << IOPRIO_CLASS_SHIFT));
+		pri |= (ioclass << IOPRIO_CLASS_SHIFT);
+		if (ioprio_set(IOPRIO_WHO_PROCESS, pid, pri) == -1)
+			bb_perror_msg_and_die("ioprio_%cet", 's');
+		if (argv[0]) {
+			BB_EXECVP_or_die(argv);
+		}
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/last.c b/busybox-1.19.3/miscutils/last.c
new file mode 100644
index 0000000..d527803
--- /dev/null
+++ b/busybox-1.19.3/miscutils/last.c
@@ -0,0 +1,143 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * last implementation for busybox
+ *
+ * Copyright (C) 2003-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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"
+
+/* NB: ut_name and ut_user are the same field, use only one name (ut_user)
+ * to reduce confusion */
+
+#ifndef SHUTDOWN_TIME
+#  define SHUTDOWN_TIME 254
+#endif
+
+/* Grr... utmp char[] members do not have to be nul-terminated.
+ * Do what we can while still keeping this reasonably small.
+ * Note: We are assuming the ut_id[] size is fixed at 4. */
+
+#if defined UT_LINESIZE \
+	&& ((UT_LINESIZE != 32) || (UT_NAMESIZE != 32) || (UT_HOSTSIZE != 256))
+#error struct utmp member char[] size(s) have changed!
+#elif defined __UT_LINESIZE \
+	&& ((__UT_LINESIZE != 32) || (__UT_NAMESIZE != 64) || (__UT_HOSTSIZE != 256))
+#error struct utmp member char[] size(s) have changed!
+#endif
+
+#if EMPTY != 0 || RUN_LVL != 1 || BOOT_TIME != 2 || NEW_TIME != 3 || \
+	OLD_TIME != 4
+#error Values for the ut_type field of struct utmp changed
+#endif
+
+int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int last_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	struct utmp ut;
+	int n, file = STDIN_FILENO;
+	time_t t_tmp;
+	off_t pos;
+	static const char _ut_usr[] ALIGN1 =
+			"runlevel\0" "reboot\0" "shutdown\0";
+	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_SHUTDOWN_TIME = SHUTDOWN_TIME
+	};
+	enum {
+		_TILDE = EMPTY, /* 0 */
+		TYPE_NEW_TIME,  /* NEW_TIME, 3 */
+		TYPE_OLD_TIME   /* OLD_TIME, 4 */
+	};
+
+	if (argv[1]) {
+		bb_show_usage();
+	}
+	file = xopen(bb_path_wtmp_file, O_RDONLY);
+
+	printf("%-10s %-14s %-18s %-12.12s %s\n",
+	       "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);
+	while ((n = full_read(file, &ut, sizeof(ut))) > 0) {
+		if (n != sizeof(ut)) {
+			bb_perror_msg_and_die("short read");
+		}
+		n = index_in_strings(_ut_lin, ut.ut_line);
+		if (n == _TILDE) { /* '~' */
+#if 1
+/* do we really need to be cautious here? */
+			n = index_in_strings(_ut_usr, ut.ut_user);
+			if (++n > 0)
+				ut.ut_type = n != 3 ? n : SHUTDOWN_TIME;
+#else
+			if (strncmp(ut.ut_user, "shutdown", 8) == 0)
+				ut.ut_type = SHUTDOWN_TIME;
+			else if (strncmp(ut.ut_user, "reboot", 6) == 0)
+				ut.ut_type = BOOT_TIME;
+			else if (strncmp(ut.ut_user, "runlevel", 8) == 0)
+				ut.ut_type = RUN_LVL;
+#endif
+		} else {
+			if (ut.ut_user[0] == '\0' || strcmp(ut.ut_user, "LOGIN") == 0) {
+				/* Don't bother.  This means we can't find how long
+				 * someone was logged in for.  Oh well. */
+				goto next;
+			}
+			if (ut.ut_type != DEAD_PROCESS
+			 && ut.ut_user[0]
+			 && ut.ut_line[0]
+			) {
+				ut.ut_type = USER_PROCESS;
+			}
+			if (strcmp(ut.ut_user, "date") == 0) {
+				if (n == TYPE_OLD_TIME) { /* '|' */
+					ut.ut_type = OLD_TIME;
+				}
+				if (n == TYPE_NEW_TIME) { /* '{' */
+					ut.ut_type = NEW_TIME;
+				}
+			}
+		}
+
+		if (ut.ut_type != USER_PROCESS) {
+			switch (ut.ut_type) {
+				case OLD_TIME:
+				case NEW_TIME:
+				case RUN_LVL:
+				case SHUTDOWN_TIME:
+					goto next;
+				case BOOT_TIME:
+					strcpy(ut.ut_line, "system boot");
+			}
+		}
+		/* manpages say ut_tv.tv_sec *is* time_t,
+		 * 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);
+ next:
+		pos -= sizeof(ut);
+		if (pos <= 0)
+			break; /* done. */
+		xlseek(file, pos, SEEK_SET);
+	}
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/miscutils/last_fancy.c b/busybox-1.19.3/miscutils/last_fancy.c
new file mode 100644
index 0000000..dc09b65
--- /dev/null
+++ b/busybox-1.19.3/miscutils/last_fancy.c
@@ -0,0 +1,295 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * (sysvinit like) last implementation
+ *
+ * Copyright (C) 2008 by Patricia Muscalu <patricia.muscalu@axis.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+/* NB: ut_name and ut_user are the same field, use only one name (ut_user)
+ * to reduce confusion */
+
+#ifndef SHUTDOWN_TIME
+#  define SHUTDOWN_TIME 254
+#endif
+
+#define HEADER_FORMAT     "%-8.8s %-12.12s %-*.*s %-16.16s %-7.7s %s\n"
+#define HEADER_LINE       "USER", "TTY", \
+	INET_ADDRSTRLEN, INET_ADDRSTRLEN, "HOST", "LOGIN", "  TIME", ""
+#define HEADER_LINE_WIDE  "USER", "TTY", \
+	INET6_ADDRSTRLEN, INET6_ADDRSTRLEN, "HOST", "LOGIN", "  TIME", ""
+
+enum {
+	NORMAL,
+	LOGGED,
+	DOWN,
+	REBOOT,
+	CRASH,
+	GONE
+};
+
+enum {
+	LAST_OPT_W = (1 << 0),  /* -W wide            */
+	LAST_OPT_f = (1 << 1),  /* -f input file      */
+	LAST_OPT_H = (1 << 2),  /* -H header          */
+};
+
+#define show_wide (option_mask32 & LAST_OPT_W)
+
+static void show_entry(struct utmp *ut, int state, time_t dur_secs)
+{
+	unsigned days, hours, mins;
+	char duration[32];
+	char login_time[17];
+	char logout_time[8];
+	const char *logout_str;
+	const char *duration_str;
+	time_t tmp;
+
+	/* manpages say ut_tv.tv_sec *is* time_t,
+	 * but some systems have it wrong */
+	tmp = ut->ut_tv.tv_sec;
+	safe_strncpy(login_time, ctime(&tmp), 17);
+	snprintf(logout_time, 8, "- %s", ctime(&dur_secs) + 11);
+
+	dur_secs = MAX(dur_secs - (time_t)ut->ut_tv.tv_sec, (time_t)0);
+	/* unsigned int is easier to divide than time_t (which may be signed long) */
+	mins = dur_secs / 60;
+	days = mins / (24*60);
+	mins = mins % (24*60);
+	hours = mins / 60;
+	mins = mins % 60;
+
+//	if (days) {
+		sprintf(duration, "(%u+%02u:%02u)", days, hours, mins);
+//	} else {
+//		sprintf(duration, " (%02u:%02u)", hours, mins);
+//	}
+
+	logout_str = logout_time;
+	duration_str = duration;
+	switch (state) {
+	case NORMAL:
+		break;
+	case LOGGED:
+		logout_str = "  still";
+		duration_str = "logged in";
+		break;
+	case DOWN:
+		logout_str = "- down ";
+		break;
+	case REBOOT:
+		break;
+	case CRASH:
+		logout_str = "- crash";
+		break;
+	case GONE:
+		logout_str = "   gone";
+		duration_str = "- no logout";
+		break;
+	}
+
+	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);
+}
+
+static int get_ut_type(struct utmp *ut)
+{
+	if (ut->ut_line[0] == '~') {
+		if (strcmp(ut->ut_user, "shutdown") == 0) {
+			return SHUTDOWN_TIME;
+		}
+		if (strcmp(ut->ut_user, "reboot") == 0) {
+			return BOOT_TIME;
+		}
+		if (strcmp(ut->ut_user, "runlevel") == 0) {
+			return RUN_LVL;
+		}
+		return ut->ut_type;
+	}
+
+	if (ut->ut_user[0] == 0) {
+		return DEAD_PROCESS;
+	}
+
+	if ((ut->ut_type != DEAD_PROCESS)
+	 && (strcmp(ut->ut_user, "LOGIN") != 0)
+	 && ut->ut_user[0]
+	 && ut->ut_line[0]
+	) {
+		ut->ut_type = USER_PROCESS;
+	}
+
+	if (strcmp(ut->ut_user, "date") == 0) {
+		if (ut->ut_line[0] == '|') {
+			return OLD_TIME;
+		}
+		if (ut->ut_line[0] == '{') {
+			return NEW_TIME;
+		}
+	}
+	return ut->ut_type;
+}
+
+static int is_runlevel_shutdown(struct utmp *ut)
+{
+	if (((ut->ut_pid & 255) == '0') || ((ut->ut_pid & 255) == '6')) {
+		return 1;
+	}
+
+	return 0;
+}
+
+int last_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int last_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct utmp ut;
+	const char *filename = _PATH_WTMP;
+	llist_t *zlist;
+	off_t pos;
+	time_t start_time;
+	time_t boot_time;
+	time_t down_time;
+	int file;
+	smallint going_down;
+	smallint boot_down;
+
+	/*opt =*/ getopt32(argv, "Wf:" /* "H" */, &filename);
+#ifdef BUT_UTIL_LINUX_LAST_HAS_NO_SUCH_OPT
+	if (opt & LAST_OPT_H) {
+		/* Print header line */
+		if (opt & LAST_OPT_W) {
+			printf(HEADER_FORMAT, HEADER_LINE_WIDE);
+		} else {
+			printf(HEADER_FORMAT, HEADER_LINE);
+		}
+	}
+#endif
+
+	file = xopen(filename, O_RDONLY);
+	{
+		/* in case the file is empty... */
+		struct stat st;
+		fstat(file, &st);
+		start_time = st.st_ctime;
+	}
+
+	time(&down_time);
+	going_down = 0;
+	boot_down = NORMAL; /* 0 */
+	zlist = NULL;
+	boot_time = 0;
+	/* get file size, rounding down to last full record */
+	pos = xlseek(file, 0, SEEK_END) / sizeof(ut) * sizeof(ut);
+	for (;;) {
+		pos -= (off_t)sizeof(ut);
+		if (pos < 0) {
+			/* Beyond the beginning of the file boundary =>
+			 * the whole file has been read. */
+			break;
+		}
+		xlseek(file, pos, SEEK_SET);
+		xread(file, &ut, sizeof(ut));
+		/* rewritten by each record, eventially will have
+		 * first record's ut_tv.tv_sec: */
+		start_time = ut.ut_tv.tv_sec;
+
+		switch (get_ut_type(&ut)) {
+		case SHUTDOWN_TIME:
+			down_time = ut.ut_tv.tv_sec;
+			boot_down = DOWN;
+			going_down = 1;
+			break;
+		case RUN_LVL:
+			if (is_runlevel_shutdown(&ut)) {
+				down_time = ut.ut_tv.tv_sec;
+				going_down = 1;
+				boot_down = DOWN;
+			}
+			break;
+		case BOOT_TIME:
+			strcpy(ut.ut_line, "system boot");
+			show_entry(&ut, REBOOT, down_time);
+			boot_down = CRASH;
+			going_down = 1;
+			break;
+		case DEAD_PROCESS:
+			if (!ut.ut_line[0]) {
+				break;
+			}
+			/* add_entry */
+			llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
+			break;
+		case USER_PROCESS: {
+			int show;
+
+			if (!ut.ut_line[0]) {
+				break;
+			}
+			/* find_entry */
+			show = 1;
+			{
+				llist_t *el, *next;
+				for (el = zlist; el; el = next) {
+					struct utmp *up = (struct utmp *)el->data;
+					next = el->link;
+					if (strncmp(up->ut_line, ut.ut_line, UT_LINESIZE) == 0) {
+						if (show) {
+							show_entry(&ut, NORMAL, up->ut_tv.tv_sec);
+							show = 0;
+						}
+						llist_unlink(&zlist, el);
+						free(el->data);
+						free(el);
+					}
+				}
+			}
+
+			if (show) {
+				int state = boot_down;
+
+				if (boot_time == 0) {
+					state = LOGGED;
+					/* Check if the process is alive */
+					if ((ut.ut_pid > 0)
+					 && (kill(ut.ut_pid, 0) != 0)
+					 && (errno == ESRCH)) {
+						state = GONE;
+					}
+				}
+				show_entry(&ut, state, boot_time);
+			}
+			/* add_entry */
+			llist_add_to(&zlist, memcpy(xmalloc(sizeof(ut)), &ut, sizeof(ut)));
+			break;
+		}
+		}
+
+		if (going_down) {
+			boot_time = ut.ut_tv.tv_sec;
+			llist_free(zlist, free);
+			zlist = NULL;
+			going_down = 0;
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		llist_free(zlist, free);
+	}
+
+	printf("\nwtmp begins %s", ctime(&start_time));
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(file);
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/miscutils/less.c b/busybox-1.19.3/miscutils/less.c
new file mode 100644
index 0000000..045fd2d
--- /dev/null
+++ b/busybox-1.19.3/miscutils/less.c
@@ -0,0 +1,1917 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini less implementation for busybox
+ *
+ * Copyright (C) 2005 by Rob Sullivan <cogito.ergo.cogito@gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * TODO:
+ * - Add more regular expression support - search modifiers, certain matches, etc.
+ * - Add more complex bracket searching - currently, nested brackets are
+ *   not considered.
+ * - Add support for "F" as an input. This causes less to act in
+ *   a similar way to tail -f.
+ * - Allow horizontal scrolling.
+ *
+ * Notes:
+ * - the inp file pointer is used so that keyboard input works after
+ *   redirected input has been read from stdin
+ */
+
+//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 extra flags"
+//config:	default y
+//config:	depends on LESS
+//config:	help
+//config:	  The extra flags provided do the following:
+//config:
+//config:	  The -M flag enables a more sophisticated status line.
+//config:	  The -m flag enables a simpler status line with a percentage.
+//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:       "[-EMNmh~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:     "\n	-M,-m	Display status line with line numbers"
+//usage:     "\n		and percentage through the file"
+//usage:     "\n	-N	Prefix line number to each line"
+//usage:     "\n	-I	Ignore case in all searches"
+//usage:     "\n	-~	Suppress ~s displayed past the end of the file"
+
+#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   ESC"[7m"
+#define NORMAL      ESC"[0m"
+/* The escape code to home and clear to the end of screen */
+#define CLEAR       ESC"[H\033[J"
+/* The escape code to clear to the end of line */
+#define CLEAR_2_EOL ESC"[K"
+
+enum {
+/* Absolute max of lines eaten */
+	MAXLINES = CONFIG_FEATURE_LESS_MAXLINES,
+/* This many "after the end" lines we will show (at max) */
+	TILDES = 1,
+};
+
+/* Command line options */
+enum {
+	FLAG_E = 1 << 0,
+	FLAG_M = 1 << 1,
+	FLAG_m = 1 << 2,
+	FLAG_N = 1 << 3,
+	FLAG_TILDE = 1 << 4,
+	FLAG_I = 1 << 5,
+	FLAG_S = (1 << 6) * ENABLE_FEATURE_LESS_DASHCMD,
+/* hijack command line options variable for internal state vars */
+	LESS_STATE_MATCH_BACKWARDS = 1 << 15,
+};
+
+#if !ENABLE_FEATURE_LESS_REGEXP
+enum { pattern_valid = 0 };
+#endif
+
+struct globals {
+	int cur_fline; /* signed */
+	int kbd_fd;  /* fd to get input from */
+	int less_gets_pos;
+/* last position in last line, taking into account tabs */
+	size_t last_line_pos;
+	unsigned max_fline;
+	unsigned max_lineno; /* this one tracks linewrap */
+	unsigned max_displayed_line;
+	unsigned width;
+#if ENABLE_FEATURE_LESS_WINCH
+	unsigned winch_counter;
+#endif
+	ssize_t eof_error; /* eof if 0, error if < 0 */
+	ssize_t readpos;
+	ssize_t readeof; /* must be signed */
+	const char **buffer;
+	const char **flines;
+	const char *empty_line_marker;
+	unsigned num_files;
+	unsigned current_file;
+	char *filename;
+	char **files;
+#if ENABLE_FEATURE_LESS_MARKS
+	unsigned num_marks;
+	unsigned mark_lines[15][2];
+#endif
+#if ENABLE_FEATURE_LESS_REGEXP
+	unsigned *match_lines;
+	int match_pos; /* signed! */
+	int wanted_match; /* signed! */
+	int num_matches;
+	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];
+};
+#define G (*ptr_to_globals)
+#define cur_fline           (G.cur_fline         )
+#define kbd_fd              (G.kbd_fd            )
+#define less_gets_pos       (G.less_gets_pos     )
+#define last_line_pos       (G.last_line_pos     )
+#define max_fline           (G.max_fline         )
+#define max_lineno          (G.max_lineno        )
+#define max_displayed_line  (G.max_displayed_line)
+#define width               (G.width             )
+#define winch_counter       (G.winch_counter     )
+/* This one is 100% not cached by compiler on read access */
+#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter)
+#define eof_error           (G.eof_error         )
+#define readpos             (G.readpos           )
+#define readeof             (G.readeof           )
+#define buffer              (G.buffer            )
+#define flines              (G.flines            )
+#define empty_line_marker   (G.empty_line_marker )
+#define num_files           (G.num_files         )
+#define current_file        (G.current_file      )
+#define filename            (G.filename          )
+#define files               (G.files             )
+#define num_marks           (G.num_marks         )
+#define mark_lines          (G.mark_lines        )
+#if ENABLE_FEATURE_LESS_REGEXP
+#define match_lines         (G.match_lines       )
+#define match_pos           (G.match_pos         )
+#define num_matches         (G.num_matches       )
+#define wanted_match        (G.wanted_match      )
+#define pattern             (G.pattern           )
+#define pattern_valid       (G.pattern_valid     )
+#endif
+#define terminated          (G.terminated        )
+#define term_orig           (G.term_orig         )
+#define term_less           (G.term_less         )
+#define kbd_input           (G.kbd_input         )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	less_gets_pos = -1; \
+	empty_line_marker = "~"; \
+	num_files = 1; \
+	current_file = 1; \
+	eof_error = 1; \
+	terminated = 1; \
+	IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \
+} while (0)
+
+/* flines[] are lines read from stdin, each in malloc'ed buffer.
+ * Line numbers are stored as uint32_t prepended to each line.
+ * Pointer is adjusted so that flines[i] points directly past
+ * line number. Accesor: */
+#define MEMPTR(p) ((char*)(p) - 4)
+#define LINENO(p) (*(uint32_t*)((p) - 4))
+
+
+/* Reset terminal input to normal */
+static void set_tty_cooked(void)
+{
+	fflush_all();
+	tcsetattr(kbd_fd, TCSANOW, &term_orig);
+}
+
+/* Move the cursor to a position (x,y), where (0,0) is the
+   top-left corner of the console */
+static void move_cursor(int line, int row)
+{
+	printf(ESC"[%u;%uH", line, row);
+}
+
+static void clear_line(void)
+{
+	printf(ESC"[%u;0H" CLEAR_2_EOL, max_displayed_line + 2);
+}
+
+static void print_hilite(const char *str)
+{
+	printf(HIGHLIGHT"%s"NORMAL, str);
+}
+
+static void print_statusline(const char *str)
+{
+	clear_line();
+	printf(HIGHLIGHT"%.*s"NORMAL, width - 1, str);
+}
+
+/* Exit the program gracefully */
+static void less_exit(int code)
+{
+	set_tty_cooked();
+	clear_line();
+	if (code < 0)
+		kill_myself_with_sig(- code); /* does not return */
+	exit(code);
+}
+
+#if (ENABLE_FEATURE_LESS_DASHCMD && ENABLE_FEATURE_LESS_LINENUMS) \
+ || ENABLE_FEATURE_LESS_WINCH
+static void re_wrap(void)
+{
+	int w = width;
+	int new_line_pos;
+	int src_idx;
+	int dst_idx;
+	int new_cur_fline = 0;
+	uint32_t lineno;
+	char linebuf[w + 1];
+	const char **old_flines = flines;
+	const char *s;
+	char **new_flines = NULL;
+	char *d;
+
+	if (option_mask32 & FLAG_N)
+		w -= 8;
+
+	src_idx = 0;
+	dst_idx = 0;
+	s = old_flines[0];
+	lineno = LINENO(s);
+	d = linebuf;
+	new_line_pos = 0;
+	while (1) {
+		*d = *s;
+		if (*d != '\0') {
+			new_line_pos++;
+			if (*d == '\t') /* tab */
+				new_line_pos += 7;
+			s++;
+			d++;
+			if (new_line_pos >= w) {
+				int sz;
+				/* new line is full, create next one */
+				*d = '\0';
+ next_new:
+				sz = (d - linebuf) + 1; /* + 1: NUL */
+				d = ((char*)xmalloc(sz + 4)) + 4;
+				LINENO(d) = lineno;
+				memcpy(d, linebuf, sz);
+				new_flines = xrealloc_vector(new_flines, 8, dst_idx);
+				new_flines[dst_idx] = d;
+				dst_idx++;
+				if (new_line_pos < w) {
+					/* if we came here thru "goto next_new" */
+					if (src_idx > max_fline)
+						break;
+					lineno = LINENO(s);
+				}
+				d = linebuf;
+				new_line_pos = 0;
+			}
+			continue;
+		}
+		/* *d == NUL: old line ended, go to next old one */
+		free(MEMPTR(old_flines[src_idx]));
+		/* btw, convert cur_fline... */
+		if (cur_fline == src_idx)
+			new_cur_fline = dst_idx;
+		src_idx++;
+		/* no more lines? finish last new line (and exit the loop) */
+		if (src_idx > max_fline)
+			goto next_new;
+		s = old_flines[src_idx];
+		if (lineno != LINENO(s)) {
+			/* this is not a continuation line!
+			 * create next _new_ line too */
+			goto next_new;
+		}
+	}
+
+	free(old_flines);
+	flines = (const char **)new_flines;
+
+	max_fline = dst_idx - 1;
+	last_line_pos = new_line_pos;
+	cur_fline = new_cur_fline;
+	/* max_lineno is screen-size independent */
+#if ENABLE_FEATURE_LESS_REGEXP
+	pattern_valid = 0;
+#endif
+}
+#endif
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void fill_match_lines(unsigned pos);
+#else
+#define fill_match_lines(pos) ((void)0)
+#endif
+
+/* Devilishly complex routine.
+ *
+ * Has to deal with EOF and EPIPE on input,
+ * with line wrapping, with last line not ending in '\n'
+ * (possibly not ending YET!), with backspace and tabs.
+ * It reads input again if last time we got an EOF (thus supporting
+ * growing files) or EPIPE (watching output of slow process like make).
+ *
+ * Variables used:
+ * flines[] - array of lines already read. Linewrap may cause
+ *      one source file line to occupy several flines[n].
+ * flines[max_fline] - last line, possibly incomplete.
+ * terminated - 1 if flines[max_fline] is 'terminated'
+ *      (if there was '\n' [which isn't stored itself, we just remember
+ *      that it was seen])
+ * max_lineno - last line's number, this one doesn't increment
+ *      on line wrap, only on "real" new lines.
+ * readbuf[0..readeof-1] - small preliminary buffer.
+ * readbuf[readpos] - next character to add to current line.
+ * last_line_pos - screen line position of next char to be read
+ *      (takes into account tabs and backspaces)
+ * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error
+ */
+static void read_lines(void)
+{
+#define readbuf bb_common_bufsiz1
+	char *current_line, *p;
+	int w = width;
+	char last_terminated = terminated;
+#if ENABLE_FEATURE_LESS_REGEXP
+	unsigned old_max_fline = max_fline;
+	time_t last_time = 0;
+	unsigned seconds_p1 = 3; /* seconds_to_loop + 1 */
+#endif
+
+	if (option_mask32 & FLAG_N)
+		w -= 8;
+
+ IF_FEATURE_LESS_REGEXP(again0:)
+
+	p = current_line = ((char*)xmalloc(w + 4)) + 4;
+	max_fline += last_terminated;
+	if (!last_terminated) {
+		const char *cp = flines[max_fline];
+		strcpy(p, cp);
+		p += strlen(current_line);
+		free(MEMPTR(flines[max_fline]));
+		/* last_line_pos is still valid from previous read_lines() */
+	} else {
+		last_line_pos = 0;
+	}
+
+	while (1) { /* read lines until we reach cur_fline or wanted_match */
+		*p = '\0';
+		terminated = 0;
+		while (1) { /* read chars until we have a line */
+			char c;
+			/* if no unprocessed chars left, eat more */
+			if (readpos >= readeof) {
+				ndelay_on(0);
+				eof_error = safe_read(STDIN_FILENO, readbuf, sizeof(readbuf));
+				ndelay_off(0);
+				readpos = 0;
+				readeof = eof_error;
+				if (eof_error <= 0)
+					goto reached_eof;
+			}
+			c = readbuf[readpos];
+			/* backspace? [needed for manpages] */
+			/* <tab><bs> is (a) insane and */
+			/* (b) harder to do correctly, so we refuse to do it */
+			if (c == '\x8' && last_line_pos && p[-1] != '\t') {
+				readpos++; /* eat it */
+				last_line_pos--;
+			/* was buggy (p could end up <= current_line)... */
+				*--p = '\0';
+				continue;
+			}
+			{
+				size_t new_last_line_pos = last_line_pos + 1;
+				if (c == '\t') {
+					new_last_line_pos += 7;
+					new_last_line_pos &= (~7);
+				}
+				if ((int)new_last_line_pos >= w)
+					break;
+				last_line_pos = new_last_line_pos;
+			}
+			/* ok, we will eat this char */
+			readpos++;
+			if (c == '\n') {
+				terminated = 1;
+				last_line_pos = 0;
+				break;
+			}
+			/* NUL is substituted by '\n'! */
+			if (c == '\0') c = '\n';
+			*p++ = c;
+			*p = '\0';
+		} /* end of "read chars until we have a line" loop */
+		/* Corner case: linewrap with only "" wrapping to next line */
+		/* Looks ugly on screen, so we do not store this empty line */
+		if (!last_terminated && !current_line[0]) {
+			last_terminated = 1;
+			max_lineno++;
+			continue;
+		}
+ reached_eof:
+		last_terminated = terminated;
+		flines = xrealloc_vector(flines, 8, max_fline);
+
+		flines[max_fline] = (char*)xrealloc(MEMPTR(current_line), strlen(current_line) + 1 + 4) + 4;
+		LINENO(flines[max_fline]) = max_lineno;
+		if (terminated)
+			max_lineno++;
+
+		if (max_fline >= MAXLINES) {
+			eof_error = 0; /* Pretend we saw EOF */
+			break;
+		}
+		if (!(option_mask32 & FLAG_S)
+		  ? (max_fline > cur_fline + max_displayed_line)
+		  : (max_fline >= cur_fline
+		     && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
+		) {
+#if !ENABLE_FEATURE_LESS_REGEXP
+			break;
+#else
+			if (wanted_match >= num_matches) { /* goto_match called us */
+				fill_match_lines(old_max_fline);
+				old_max_fline = max_fline;
+			}
+			if (wanted_match < num_matches)
+				break;
+#endif
+		}
+		if (eof_error <= 0) {
+			if (eof_error < 0) {
+				if (errno == EAGAIN) {
+					/* not yet eof or error, reset flag (or else
+					 * we will hog CPU - select() will return
+					 * immediately */
+					eof_error = 1;
+				} else {
+					print_statusline(bb_msg_read_error);
+				}
+			}
+#if !ENABLE_FEATURE_LESS_REGEXP
+			break;
+#else
+			if (wanted_match < num_matches) {
+				break;
+			} else { /* goto_match called us */
+				time_t t = time(NULL);
+				if (t != last_time) {
+					last_time = t;
+					if (--seconds_p1 == 0)
+						break;
+				}
+				sched_yield();
+				goto again0; /* go loop again (max 2 seconds) */
+			}
+#endif
+		}
+		max_fline++;
+		current_line = ((char*)xmalloc(w + 4)) + 4;
+		p = current_line;
+		last_line_pos = 0;
+	} /* end of "read lines until we reach cur_fline" loop */
+	fill_match_lines(old_max_fline);
+#if ENABLE_FEATURE_LESS_REGEXP
+	/* prevent us from being stuck in search for a match */
+	wanted_match = -1;
+#endif
+#undef readbuf
+}
+
+#if ENABLE_FEATURE_LESS_FLAGS
+/* Interestingly, writing calc_percent as a function saves around 32 bytes
+ * on my build. */
+static int calc_percent(void)
+{
+	unsigned p = (100 * (cur_fline+max_displayed_line+1) + max_fline/2) / (max_fline+1);
+	return p <= 100 ? p : 100;
+}
+
+/* Print a status line if -M was specified */
+static void m_status_print(void)
+{
+	int percentage;
+
+	if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
+		return;
+
+	clear_line();
+	printf(HIGHLIGHT"%s", filename);
+	if (num_files > 1)
+		printf(" (file %i of %i)", current_file, num_files);
+	printf(" lines %i-%i/%i ",
+			cur_fline + 1, cur_fline + max_displayed_line + 1,
+			max_fline + 1);
+	if (cur_fline >= (int)(max_fline - max_displayed_line)) {
+		printf("(END)"NORMAL);
+		if (num_files > 1 && current_file != num_files)
+			printf(HIGHLIGHT" - next: %s"NORMAL, files[current_file]);
+		return;
+	}
+	percentage = calc_percent();
+	printf("%i%%"NORMAL, percentage);
+}
+#endif
+
+/* Print the status line */
+static void status_print(void)
+{
+	const char *p;
+
+	if (less_gets_pos >= 0) /* don't touch statusline while input is done! */
+		return;
+
+	/* Change the status if flags have been set */
+#if ENABLE_FEATURE_LESS_FLAGS
+	if (option_mask32 & (FLAG_M|FLAG_m)) {
+		m_status_print();
+		return;
+	}
+	/* No flags set */
+#endif
+
+	clear_line();
+	if (cur_fline && cur_fline < (int)(max_fline - max_displayed_line)) {
+		bb_putchar(':');
+		return;
+	}
+	p = "(END)";
+	if (!cur_fline)
+		p = filename;
+	if (num_files > 1) {
+		printf(HIGHLIGHT"%s (file %i of %i)"NORMAL,
+				p, current_file, num_files);
+		return;
+	}
+	print_hilite(p);
+}
+
+static void cap_cur_fline(int nlines)
+{
+	int diff;
+	if (cur_fline < 0)
+		cur_fline = 0;
+	if (cur_fline + max_displayed_line > max_fline + TILDES) {
+		cur_fline -= nlines;
+		if (cur_fline < 0)
+			cur_fline = 0;
+		diff = max_fline - (cur_fline + max_displayed_line) + TILDES;
+		/* As the number of lines requested was too large, we just move
+		 * to the end of the file */
+		if (diff > 0)
+			cur_fline += diff;
+	}
+}
+
+static const char controls[] ALIGN1 =
+	/* NUL: never encountered; TAB: not converted */
+	/**/"\x01\x02\x03\x04\x05\x06\x07\x08"  "\x0a\x0b\x0c\x0d\x0e\x0f"
+	"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+	"\x7f\x9b"; /* DEL and infamous Meta-ESC :( */
+static const char ctrlconv[] ALIGN1 =
+	/* why 40 instead of 4a below? - it is a replacement for '\n'.
+	 * '\n' is a former NUL - we subst it with @, not J */
+	"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x40\x4b\x4c\x4d\x4e\x4f"
+	"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f";
+
+static void lineno_str(char *nbuf9, const char *line)
+{
+	nbuf9[0] = '\0';
+	if (option_mask32 & FLAG_N) {
+		const char *fmt;
+		unsigned n;
+
+		if (line == empty_line_marker) {
+			memset(nbuf9, ' ', 8);
+			nbuf9[8] = '\0';
+			return;
+		}
+		/* Width of 7 preserves tab spacing in the text */
+		fmt = "%7u ";
+		n = LINENO(line) + 1;
+		if (n > 9999999) {
+			n %= 10000000;
+			fmt = "%07u ";
+		}
+		sprintf(nbuf9, fmt, n);
+	}
+}
+
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void print_found(const char *line)
+{
+	int match_status;
+	int eflags;
+	char *growline;
+	regmatch_t match_structs;
+
+	char buf[width];
+	char nbuf9[9];
+	const char *str = line;
+	char *p = buf;
+	size_t n;
+
+	while (*str) {
+		n = strcspn(str, controls);
+		if (n) {
+			if (!str[n]) break;
+			memcpy(p, str, n);
+			p += n;
+			str += n;
+		}
+		n = strspn(str, controls);
+		memset(p, '.', n);
+		p += n;
+		str += n;
+	}
+	strcpy(p, str);
+
+	/* 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[]) */
+	str = buf;
+	growline = NULL;
+	eflags = 0;
+	goto start;
+
+	while (match_status == 0) {
+		char *new = xasprintf("%s%.*s"HIGHLIGHT"%.*s"NORMAL,
+				growline ? growline : "",
+				(int)match_structs.rm_so, str,
+				(int)(match_structs.rm_eo - match_structs.rm_so),
+						str + match_structs.rm_so);
+		free(growline);
+		growline = new;
+		str += match_structs.rm_eo;
+		line += match_structs.rm_eo;
+		eflags = REG_NOTBOL;
+ start:
+		/* Most of the time doesn't find the regex, optimize for that */
+		match_status = regexec(&pattern, line, 1, &match_structs, eflags);
+		/* if even "" matches, treat it as "not a match" */
+		if (match_structs.rm_so >= match_structs.rm_eo)
+			match_status = 1;
+	}
+
+	lineno_str(nbuf9, line);
+	if (!growline) {
+		printf(CLEAR_2_EOL"%s%s\n", nbuf9, str);
+		return;
+	}
+	printf(CLEAR_2_EOL"%s%s%s\n", nbuf9, growline, str);
+	free(growline);
+}
+#else
+void print_found(const char *line);
+#endif
+
+static void print_ascii(const char *str)
+{
+	char buf[width];
+	char nbuf9[9];
+	char *p;
+	size_t n;
+
+	lineno_str(nbuf9, str);
+	printf(CLEAR_2_EOL"%s", nbuf9);
+
+	while (*str) {
+		n = strcspn(str, controls);
+		if (n) {
+			if (!str[n]) break;
+			printf("%.*s", (int) n, str);
+			str += n;
+		}
+		n = strspn(str, controls);
+		p = buf;
+		do {
+			if (*str == 0x7f)
+				*p++ = '?';
+			else if (*str == (char)0x9b)
+			/* VT100's CSI, aka Meta-ESC. Who's inventor? */
+			/* I want to know who committed this sin */
+				*p++ = '{';
+			else
+				*p++ = ctrlconv[(unsigned char)*str];
+			str++;
+		} while (--n);
+		*p = '\0';
+		print_hilite(buf);
+	}
+	puts(str);
+}
+
+/* Print the buffer */
+static void buffer_print(void)
+{
+	unsigned i;
+
+	move_cursor(0, 0);
+	for (i = 0; i <= max_displayed_line; i++)
+		if (pattern_valid)
+			print_found(buffer[i]);
+		else
+			print_ascii(buffer[i]);
+	status_print();
+}
+
+static void buffer_fill_and_print(void)
+{
+	unsigned i;
+#if ENABLE_FEATURE_LESS_DASHCMD
+	int fpos = cur_fline;
+
+	if (option_mask32 & FLAG_S) {
+		/* Go back to the beginning of this line */
+		while (fpos && LINENO(flines[fpos]) == LINENO(flines[fpos-1]))
+			fpos--;
+	}
+
+	i = 0;
+	while (i <= max_displayed_line && fpos <= max_fline) {
+		int lineno = LINENO(flines[fpos]);
+		buffer[i] = flines[fpos];
+		i++;
+		do {
+			fpos++;
+		} while ((fpos <= max_fline)
+		      && (option_mask32 & FLAG_S)
+		      && lineno == LINENO(flines[fpos])
+		);
+	}
+#else
+	for (i = 0; i <= max_displayed_line && cur_fline + i <= max_fline; i++) {
+		buffer[i] = flines[cur_fline + i];
+	}
+#endif
+	for (; i <= max_displayed_line; i++) {
+		buffer[i] = empty_line_marker;
+	}
+	buffer_print();
+}
+
+/* Move the buffer up and down in the file in order to scroll */
+static void buffer_down(int nlines)
+{
+	cur_fline += nlines;
+	read_lines();
+	cap_cur_fline(nlines);
+	buffer_fill_and_print();
+}
+
+static void buffer_up(int nlines)
+{
+	cur_fline -= nlines;
+	if (cur_fline < 0) cur_fline = 0;
+	read_lines();
+	buffer_fill_and_print();
+}
+
+static void buffer_line(int linenum)
+{
+	if (linenum < 0)
+		linenum = 0;
+	cur_fline = linenum;
+	read_lines();
+	if (linenum + max_displayed_line > max_fline)
+		linenum = max_fline - max_displayed_line + TILDES;
+	if (linenum < 0)
+		linenum = 0;
+	cur_fline = linenum;
+	buffer_fill_and_print();
+}
+
+static void open_file_and_read_lines(void)
+{
+	if (filename) {
+		xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
+	} else {
+		/* "less" with no arguments in argv[] */
+		/* For status line only */
+		filename = xstrdup(bb_msg_standard_input);
+	}
+	readpos = 0;
+	readeof = 0;
+	last_line_pos = 0;
+	terminated = 1;
+	read_lines();
+}
+
+/* Reinitialize everything for a new file - free the memory and start over */
+static void reinitialize(void)
+{
+	unsigned i;
+
+	if (flines) {
+		for (i = 0; i <= max_fline; i++)
+			free(MEMPTR(flines[i]));
+		free(flines);
+		flines = NULL;
+	}
+
+	max_fline = -1;
+	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 int64_t getch_nowait(void)
+{
+	int rd;
+	int64_t key64;
+	struct pollfd pfd[2];
+
+	pfd[0].fd = STDIN_FILENO;
+	pfd[0].events = POLLIN;
+	pfd[1].fd = kbd_fd;
+	pfd[1].events = POLLIN;
+ again:
+	tcsetattr(kbd_fd, TCSANOW, &term_less);
+	/* NB: select/poll returns whenever read will not block. Therefore:
+	 * if eof is reached, select/poll will return immediately
+	 * because read will immediately return 0 bytes.
+	 * Even if select/poll says that input is available, read CAN block
+	 * (switch fd into O_NONBLOCK'ed mode to avoid it)
+	 */
+	rd = 1;
+	/* Are we interested in stdin? */
+//TODO: reuse code for determining this
+	if (!(option_mask32 & FLAG_S)
+	   ? !(max_fline > cur_fline + max_displayed_line)
+	   : !(max_fline >= cur_fline
+	       && max_lineno > LINENO(flines[cur_fline]) + max_displayed_line)
+	) {
+		if (eof_error > 0) /* did NOT reach eof yet */
+			rd = 0; /* yes, we are interested in stdin */
+	}
+	/* Position cursor if line input is done */
+	if (less_gets_pos >= 0)
+		move_cursor(max_displayed_line + 2, less_gets_pos + 1);
+	fflush_all();
+
+	if (kbd_input[0] == 0) { /* if nothing is buffered */
+#if ENABLE_FEATURE_LESS_WINCH
+		while (1) {
+			int r;
+			/* NB: SIGWINCH interrupts poll() */
+			r = poll(pfd + rd, 2 - rd, -1);
+			if (/*r < 0 && errno == EINTR &&*/ winch_counter)
+				return '\\'; /* anything which has no defined function */
+			if (r) break;
+		}
+#else
+		safe_poll(pfd + rd, 2 - rd, -1);
+#endif
+	}
+
+	/* We have kbd_fd in O_NONBLOCK mode, read inside read_key()
+	 * would not block even if there is no input available */
+	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 */
+			read_lines();
+			buffer_fill_and_print();
+			goto again;
+		}
+		/* EOF/error (ssh session got killed etc) */
+		less_exit(0);
+	}
+	set_tty_cooked();
+	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 int64_t less_getch(int pos)
+{
+	int64_t key64;
+	int key;
+
+ again:
+	less_gets_pos = pos;
+	key = key64 = getch_nowait();
+	less_gets_pos = -1;
+
+	/* 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 key64;
+}
+
+static char* less_gets(int sz)
+{
+	int c;
+	unsigned i = 0;
+	char *result = xzalloc(1);
+
+	while (1) {
+		c = '\0';
+		less_gets_pos = sz + i;
+		c = getch_nowait();
+		if (c == 0x0d) {
+			result[i] = '\0';
+			less_gets_pos = -1;
+			return result;
+		}
+		if (c == 0x7f)
+			c = 8;
+		if (c == 8 && i) {
+			printf("\x8 \x8");
+			i--;
+		}
+		if (c < ' ') /* filters out KEYCODE_xxx too (<0) */
+			continue;
+		if (i >= width - sz - 1)
+			continue; /* len limit */
+		bb_putchar(c);
+		result[i++] = c;
+		result = xrealloc(result, i+1);
+	}
+}
+
+static void examine_file(void)
+{
+	char *new_fname;
+
+	print_statusline("Examine: ");
+	new_fname = less_gets(sizeof("Examine: ") - 1);
+	if (!new_fname[0]) {
+		status_print();
+ err:
+		free(new_fname);
+		return;
+	}
+	if (access(new_fname, R_OK) != 0) {
+		print_statusline("Cannot read this file");
+		goto err;
+	}
+	free(filename);
+	filename = new_fname;
+	/* files start by = argv. why we assume that argv is infinitely long??
+	files[num_files] = filename;
+	current_file = num_files + 1;
+	num_files++; */
+	files[0] = filename;
+	num_files = current_file = 1;
+	reinitialize();
+}
+
+/* This function changes the file currently being paged. direction can be one of the following:
+ * -1: go back one file
+ *  0: go to the first file
+ *  1: go forward one file */
+static void change_file(int direction)
+{
+	if (current_file != ((direction > 0) ? num_files : 1)) {
+		current_file = direction ? current_file + direction : 1;
+		free(filename);
+		filename = xstrdup(files[current_file - 1]);
+		reinitialize();
+	} else {
+		print_statusline(direction > 0 ? "No next file" : "No previous file");
+	}
+}
+
+static void remove_current_file(void)
+{
+	unsigned i;
+
+	if (num_files < 2)
+		return;
+
+	if (current_file != 1) {
+		change_file(-1);
+		for (i = 3; i <= num_files; i++)
+			files[i - 2] = files[i - 1];
+		num_files--;
+	} else {
+		change_file(1);
+		for (i = 2; i <= num_files; i++)
+			files[i - 2] = files[i - 1];
+		num_files--;
+		current_file--;
+	}
+}
+
+static void colon_process(void)
+{
+	int keypress;
+
+	/* Clear the current line and print a prompt */
+	print_statusline(" :");
+
+	keypress = less_getch(2);
+	switch (keypress) {
+	case 'd':
+		remove_current_file();
+		break;
+	case 'e':
+		examine_file();
+		break;
+#if ENABLE_FEATURE_LESS_FLAGS
+	case 'f':
+		m_status_print();
+		break;
+#endif
+	case 'n':
+		change_file(1);
+		break;
+	case 'p':
+		change_file(-1);
+		break;
+	case 'q':
+		less_exit(EXIT_SUCCESS);
+		break;
+	case 'x':
+		change_file(0);
+		break;
+	}
+}
+
+#if ENABLE_FEATURE_LESS_REGEXP
+static void normalize_match_pos(int match)
+{
+	if (match >= num_matches)
+		match = num_matches - 1;
+	if (match < 0)
+		match = 0;
+	match_pos = match;
+}
+
+static void goto_match(int match)
+{
+	if (!pattern_valid)
+		return;
+	if (match < 0)
+		match = 0;
+	/* Try to find next match if eof isn't reached yet */
+	if (match >= num_matches && eof_error > 0) {
+		wanted_match = match; /* "I want to read until I see N'th match" */
+		read_lines();
+	}
+	if (num_matches) {
+		normalize_match_pos(match);
+		buffer_line(match_lines[match_pos]);
+	} else {
+		print_statusline("No matches found");
+	}
+}
+
+static void fill_match_lines(unsigned pos)
+{
+	if (!pattern_valid)
+		return;
+	/* Run the regex on each line of the current file */
+	while (pos <= max_fline) {
+		/* If this line matches */
+		if (regexec(&pattern, flines[pos], 0, NULL, 0) == 0
+		/* and we didn't match it last time */
+		 && !(num_matches && match_lines[num_matches-1] == pos)
+		) {
+			match_lines = xrealloc_vector(match_lines, 4, num_matches);
+			match_lines[num_matches++] = pos;
+		}
+		pos++;
+	}
+}
+
+static void regex_process(void)
+{
+	char *uncomp_regex, *err;
+
+	/* Reset variables */
+	free(match_lines);
+	match_lines = NULL;
+	match_pos = 0;
+	num_matches = 0;
+	if (pattern_valid) {
+		regfree(&pattern);
+		pattern_valid = 0;
+	}
+
+	/* Get the uncompiled regular expression from the user */
+	clear_line();
+	bb_putchar((option_mask32 & LESS_STATE_MATCH_BACKWARDS) ? '?' : '/');
+	uncomp_regex = less_gets(1);
+	if (!uncomp_regex[0]) {
+		free(uncomp_regex);
+		buffer_print();
+		return;
+	}
+
+	/* Compile the regex and check for errors */
+	err = regcomp_or_errmsg(&pattern, uncomp_regex,
+				(option_mask32 & FLAG_I) ? REG_ICASE : 0);
+	free(uncomp_regex);
+	if (err) {
+		print_statusline(err);
+		free(err);
+		return;
+	}
+
+	pattern_valid = 1;
+	match_pos = 0;
+	fill_match_lines(0);
+	while (match_pos < num_matches) {
+		if ((int)match_lines[match_pos] > cur_fline)
+			break;
+		match_pos++;
+	}
+	if (option_mask32 & LESS_STATE_MATCH_BACKWARDS)
+		match_pos--;
+
+	/* It's possible that no matches are found yet.
+	 * goto_match() will read input looking for match,
+	 * if needed */
+	goto_match(match_pos);
+}
+#endif
+
+static void number_process(int first_digit)
+{
+	unsigned i;
+	int num;
+	int keypress;
+	char num_input[sizeof(int)*4]; /* more than enough */
+
+	num_input[0] = first_digit;
+
+	/* Clear the current line, print a prompt, and then print the digit */
+	clear_line();
+	printf(":%c", first_digit);
+
+	/* Receive input until a letter is given */
+	i = 1;
+	while (i < sizeof(num_input)-1) {
+		keypress = less_getch(i + 1);
+		if ((unsigned)keypress > 255 || !isdigit(num_input[i]))
+			break;
+		num_input[i] = keypress;
+		bb_putchar(keypress);
+		i++;
+	}
+
+	num_input[i] = '\0';
+	num = bb_strtou(num_input, NULL, 10);
+	/* on format error, num == -1 */
+	if (num < 1 || num > MAXLINES) {
+		buffer_print();
+		return;
+	}
+
+	/* We now know the number and the letter entered, so we process them */
+	switch (keypress) {
+	case KEYCODE_DOWN: case 'z': case 'd': case 'e': case ' ': case '\015':
+		buffer_down(num);
+		break;
+	case KEYCODE_UP: case 'b': case 'w': case 'y': case 'u':
+		buffer_up(num);
+		break;
+	case 'g': case '<': case 'G': case '>':
+		cur_fline = num + max_displayed_line;
+		read_lines();
+		buffer_line(num - 1);
+		break;
+	case 'p': case '%':
+		num = num * (max_fline / 100); /* + max_fline / 2; */
+		cur_fline = num + max_displayed_line;
+		read_lines();
+		buffer_line(num);
+		break;
+#if ENABLE_FEATURE_LESS_REGEXP
+	case 'n':
+		goto_match(match_pos + num);
+		break;
+	case '/':
+		option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
+		regex_process();
+		break;
+	case '?':
+		option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
+		regex_process();
+		break;
+#endif
+	}
+}
+
+#if ENABLE_FEATURE_LESS_DASHCMD
+static void flag_change(void)
+{
+	int keypress;
+
+	clear_line();
+	bb_putchar('-');
+	keypress = less_getch(1);
+
+	switch (keypress) {
+	case 'M':
+		option_mask32 ^= FLAG_M;
+		break;
+	case 'm':
+		option_mask32 ^= FLAG_m;
+		break;
+	case 'E':
+		option_mask32 ^= FLAG_E;
+		break;
+	case '~':
+		option_mask32 ^= FLAG_TILDE;
+		break;
+	case 'S':
+		option_mask32 ^= FLAG_S;
+		buffer_fill_and_print();
+		break;
+#if ENABLE_FEATURE_LESS_LINENUMS
+	case 'N':
+		option_mask32 ^= FLAG_N;
+		re_wrap();
+		buffer_fill_and_print();
+		break;
+#endif
+	}
+}
+
+#ifdef BLOAT
+static void show_flag_status(void)
+{
+	int keypress;
+	int flag_val;
+
+	clear_line();
+	bb_putchar('_');
+	keypress = less_getch(1);
+
+	switch (keypress) {
+	case 'M':
+		flag_val = option_mask32 & FLAG_M;
+		break;
+	case 'm':
+		flag_val = option_mask32 & FLAG_m;
+		break;
+	case '~':
+		flag_val = option_mask32 & FLAG_TILDE;
+		break;
+	case 'N':
+		flag_val = option_mask32 & FLAG_N;
+		break;
+	case 'E':
+		flag_val = option_mask32 & FLAG_E;
+		break;
+	default:
+		flag_val = 0;
+		break;
+	}
+
+	clear_line();
+	printf(HIGHLIGHT"The status of the flag is: %u"NORMAL, flag_val != 0);
+}
+#endif
+
+#endif /* ENABLE_FEATURE_LESS_DASHCMD */
+
+static void save_input_to_file(void)
+{
+	const char *msg = "";
+	char *current_line;
+	unsigned i;
+	FILE *fp;
+
+	print_statusline("Log file: ");
+	current_line = less_gets(sizeof("Log file: ")-1);
+	if (current_line[0]) {
+		fp = fopen_for_write(current_line);
+		if (!fp) {
+			msg = "Error opening log file";
+			goto ret;
+		}
+		for (i = 0; i <= max_fline; i++)
+			fprintf(fp, "%s\n", flines[i]);
+		fclose(fp);
+		msg = "Done";
+	}
+ ret:
+	print_statusline(msg);
+	free(current_line);
+}
+
+#if ENABLE_FEATURE_LESS_MARKS
+static void add_mark(void)
+{
+	int letter;
+
+	print_statusline("Mark: ");
+	letter = less_getch(sizeof("Mark: ") - 1);
+
+	if (isalpha(letter)) {
+		/* If we exceed 15 marks, start overwriting previous ones */
+		if (num_marks == 14)
+			num_marks = 0;
+
+		mark_lines[num_marks][0] = letter;
+		mark_lines[num_marks][1] = cur_fline;
+		num_marks++;
+	} else {
+		print_statusline("Invalid mark letter");
+	}
+}
+
+static void goto_mark(void)
+{
+	int letter;
+	int i;
+
+	print_statusline("Go to mark: ");
+	letter = less_getch(sizeof("Go to mark: ") - 1);
+	clear_line();
+
+	if (isalpha(letter)) {
+		for (i = 0; i <= num_marks; i++)
+			if (letter == mark_lines[i][0]) {
+				buffer_line(mark_lines[i][1]);
+				break;
+			}
+		if (num_marks == 14 && letter != mark_lines[14][0])
+			print_statusline("Mark not set");
+	} else
+		print_statusline("Invalid mark letter");
+}
+#endif
+
+#if ENABLE_FEATURE_LESS_BRACKETS
+static char opp_bracket(char bracket)
+{
+	switch (bracket) {
+		case '{': case '[': /* '}' == '{' + 2. Same for '[' */
+			bracket++;
+		case '(':           /* ')' == '(' + 1 */
+			bracket++;
+			break;
+		case '}': case ']':
+			bracket--;
+		case ')':
+			bracket--;
+			break;
+	};
+	return bracket;
+}
+
+static void match_right_bracket(char bracket)
+{
+	unsigned i;
+
+	if (strchr(flines[cur_fline], bracket) == NULL) {
+		print_statusline("No bracket in top line");
+		return;
+	}
+	bracket = opp_bracket(bracket);
+	for (i = cur_fline + 1; i < max_fline; i++) {
+		if (strchr(flines[i], bracket) != NULL) {
+			buffer_line(i);
+			return;
+		}
+	}
+	print_statusline("No matching bracket found");
+}
+
+static void match_left_bracket(char bracket)
+{
+	int i;
+
+	if (strchr(flines[cur_fline + max_displayed_line], bracket) == NULL) {
+		print_statusline("No bracket in bottom line");
+		return;
+	}
+
+	bracket = opp_bracket(bracket);
+	for (i = cur_fline + max_displayed_line; i >= 0; i--) {
+		if (strchr(flines[i], bracket) != NULL) {
+			buffer_line(i);
+			return;
+		}
+	}
+	print_statusline("No matching bracket found");
+}
+#endif  /* FEATURE_LESS_BRACKETS */
+
+static void keypress_process(int keypress)
+{
+	switch (keypress) {
+	case KEYCODE_DOWN: case 'e': case 'j': case 0x0d:
+		buffer_down(1);
+		break;
+	case KEYCODE_UP: case 'y': case 'k':
+		buffer_up(1);
+		break;
+	case KEYCODE_PAGEDOWN: case ' ': case 'z': case 'f':
+		buffer_down(max_displayed_line + 1);
+		break;
+	case KEYCODE_PAGEUP: case 'w': case 'b':
+		buffer_up(max_displayed_line + 1);
+		break;
+	case 'd':
+		buffer_down((max_displayed_line + 1) / 2);
+		break;
+	case 'u':
+		buffer_up((max_displayed_line + 1) / 2);
+		break;
+	case KEYCODE_HOME: case 'g': case 'p': case '<': case '%':
+		buffer_line(0);
+		break;
+	case KEYCODE_END: case 'G': case '>':
+		cur_fline = MAXLINES;
+		read_lines();
+		buffer_line(cur_fline);
+		break;
+	case 'q': case 'Q':
+		less_exit(EXIT_SUCCESS);
+		break;
+#if ENABLE_FEATURE_LESS_MARKS
+	case 'm':
+		add_mark();
+		buffer_print();
+		break;
+	case '\'':
+		goto_mark();
+		buffer_print();
+		break;
+#endif
+	case 'r': case 'R':
+		/* TODO: (1) also bind ^R, ^L to this?
+		 * (2) re-measure window size?
+		 */
+		buffer_print();
+		break;
+	/*case 'R':
+		full_repaint();
+		break;*/
+	case 's':
+		save_input_to_file();
+		break;
+	case 'E':
+		examine_file();
+		break;
+#if ENABLE_FEATURE_LESS_FLAGS
+	case '=':
+		m_status_print();
+		break;
+#endif
+#if ENABLE_FEATURE_LESS_REGEXP
+	case '/':
+		option_mask32 &= ~LESS_STATE_MATCH_BACKWARDS;
+		regex_process();
+		break;
+	case 'n':
+		goto_match(match_pos + 1);
+		break;
+	case 'N':
+		goto_match(match_pos - 1);
+		break;
+	case '?':
+		option_mask32 |= LESS_STATE_MATCH_BACKWARDS;
+		regex_process();
+		break;
+#endif
+#if ENABLE_FEATURE_LESS_DASHCMD
+	case '-':
+		flag_change();
+		buffer_print();
+		break;
+#ifdef BLOAT
+	case '_':
+		show_flag_status();
+		break;
+#endif
+#endif
+#if ENABLE_FEATURE_LESS_BRACKETS
+	case '{': case '(': case '[':
+		match_right_bracket(keypress);
+		break;
+	case '}': case ')': case ']':
+		match_left_bracket(keypress);
+		break;
+#endif
+	case ':':
+		colon_process();
+		break;
+	}
+
+	if (isdigit(keypress))
+		number_process(keypress);
+}
+
+static void sig_catcher(int sig)
+{
+	less_exit(- sig);
+}
+
+#if ENABLE_FEATURE_LESS_WINCH
+static void sigwinch_handler(int sig UNUSED_PARAM)
+{
+	winch_counter++;
+}
+#endif
+
+int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int less_main(int argc, char **argv)
+{
+	INIT_G();
+
+	/* TODO: -x: do not interpret backspace, -xx: tab also */
+	/* -xxx: newline also */
+	/* -w N: assume width N (-xxx -w 32: hex viewer of sorts) */
+	getopt32(argv, "EMmN~I" IF_FEATURE_LESS_DASHCMD("S"));
+	argc -= optind;
+	argv += optind;
+	num_files = argc;
+	files = argv;
+
+	/* Another popular pager, most, detects when stdout
+	 * is not a tty and turns into cat. This makes sense. */
+	if (!isatty(STDOUT_FILENO))
+		return bb_cat(argv);
+
+	if (!num_files) {
+		if (isatty(STDIN_FILENO)) {
+			/* Just "less"? No args and no redirection? */
+			bb_error_msg("missing filename");
+			bb_show_usage();
+		}
+	} else {
+		filename = xstrdup(files[0]);
+	}
+
+	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);
+
+	tcgetattr(kbd_fd, &term_orig);
+	term_less = term_orig;
+	term_less.c_lflag &= ~(ICANON | ECHO);
+	term_less.c_iflag &= ~(IXON | ICRNL);
+	/*term_less.c_oflag &= ~ONLCR;*/
+	term_less.c_cc[VMIN] = 1;
+	term_less.c_cc[VTIME] = 0;
+
+	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);
+	max_displayed_line -= 2;
+
+	/* We want to restore term_orig on exit */
+	bb_signals(BB_FATAL_SIGS, sig_catcher);
+#if ENABLE_FEATURE_LESS_WINCH
+	signal(SIGWINCH, sigwinch_handler);
+#endif
+
+	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--;
+			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;
+			if (max_displayed_line < 3)
+				max_displayed_line = 3;
+			max_displayed_line -= 2;
+			free(buffer);
+			buffer = xmalloc((max_displayed_line+1) * sizeof(char *));
+			/* Avoid re-wrap and/or redraw if we already know
+			 * we need to do it again. These ops are expensive */
+			if (WINCH_COUNTER)
+				goto again;
+			re_wrap();
+			if (WINCH_COUNTER)
+				goto again;
+			buffer_fill_and_print();
+			/* This took some time. Loop back and check,
+			 * were there another SIGWINCH? */
+		}
+		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);
+	}
+}
+
+/*
+Help text of less version 418 is below.
+If you are implementing something, keeping
+key and/or command line switch compatibility is a good idea:
+
+
+                   SUMMARY OF LESS COMMANDS
+
+      Commands marked with * may be preceded by a number, N.
+      Notes in parentheses indicate the behavior if N is given.
+  h  H                 Display this help.
+  q  :q  Q  :Q  ZZ     Exit.
+ ---------------------------------------------------------------------------
+                           MOVING
+  e  ^E  j  ^N  CR  *  Forward  one line   (or N lines).
+  y  ^Y  k  ^K  ^P  *  Backward one line   (or N lines).
+  f  ^F  ^V  SPACE  *  Forward  one window (or N lines).
+  b  ^B  ESC-v      *  Backward one window (or N lines).
+  z                 *  Forward  one window (and set window to N).
+  w                 *  Backward one window (and set window to N).
+  ESC-SPACE         *  Forward  one window, but don't stop at end-of-file.
+  d  ^D             *  Forward  one half-window (and set half-window to N).
+  u  ^U             *  Backward one half-window (and set half-window to N).
+  ESC-)  RightArrow *  Left  one half screen width (or N positions).
+  ESC-(  LeftArrow  *  Right one half screen width (or N positions).
+  F                    Forward forever; like "tail -f".
+  r  ^R  ^L            Repaint screen.
+  R                    Repaint screen, discarding buffered input.
+        ---------------------------------------------------
+        Default "window" is the screen height.
+        Default "half-window" is half of the screen height.
+ ---------------------------------------------------------------------------
+                          SEARCHING
+  /pattern          *  Search forward for (N-th) matching line.
+  ?pattern          *  Search backward for (N-th) matching line.
+  n                 *  Repeat previous search (for N-th occurrence).
+  N                 *  Repeat previous search in reverse direction.
+  ESC-n             *  Repeat previous search, spanning files.
+  ESC-N             *  Repeat previous search, reverse dir. & spanning files.
+  ESC-u                Undo (toggle) search highlighting.
+        ---------------------------------------------------
+        Search patterns may be modified by one or more of:
+        ^N or !  Search for NON-matching lines.
+        ^E or *  Search multiple files (pass thru END OF FILE).
+        ^F or @  Start search at FIRST file (for /) or last file (for ?).
+        ^K       Highlight matches, but don't move (KEEP position).
+        ^R       Don't use REGULAR EXPRESSIONS.
+ ---------------------------------------------------------------------------
+                           JUMPING
+  g  <  ESC-<       *  Go to first line in file (or line N).
+  G  >  ESC->       *  Go to last line in file (or line N).
+  p  %              *  Go to beginning of file (or N percent into file).
+  t                 *  Go to the (N-th) next tag.
+  T                 *  Go to the (N-th) previous tag.
+  {  (  [           *  Find close bracket } ) ].
+  }  )  ]           *  Find open bracket { ( [.
+  ESC-^F <c1> <c2>  *  Find close bracket <c2>.
+  ESC-^B <c1> <c2>  *  Find open bracket <c1>
+        ---------------------------------------------------
+        Each "find close bracket" command goes forward to the close bracket
+          matching the (N-th) open bracket in the top line.
+        Each "find open bracket" command goes backward to the open bracket
+          matching the (N-th) close bracket in the bottom line.
+  m<letter>            Mark the current position with <letter>.
+  '<letter>            Go to a previously marked position.
+  ''                   Go to the previous position.
+  ^X^X                 Same as '.
+        ---------------------------------------------------
+        A mark is any upper-case or lower-case letter.
+        Certain marks are predefined:
+             ^  means  beginning of the file
+             $  means  end of the file
+ ---------------------------------------------------------------------------
+                        CHANGING FILES
+  :e [file]            Examine a new file.
+  ^X^V                 Same as :e.
+  :n                *  Examine the (N-th) next file from the command line.
+  :p                *  Examine the (N-th) previous file from the command line.
+  :x                *  Examine the first (or N-th) file from the command line.
+  :d                   Delete the current file from the command line list.
+  =  ^G  :f            Print current file name.
+ ---------------------------------------------------------------------------
+                    MISCELLANEOUS COMMANDS
+  -<flag>              Toggle a command line option [see OPTIONS below].
+  --<name>             Toggle a command line option, by name.
+  _<flag>              Display the setting of a command line option.
+  __<name>             Display the setting of an option, by name.
+  +cmd                 Execute the less cmd each time a new file is examined.
+  !command             Execute the shell command with $SHELL.
+  |Xcommand            Pipe file between current pos & mark X to shell command.
+  v                    Edit the current file with $VISUAL or $EDITOR.
+  V                    Print version number of "less".
+ ---------------------------------------------------------------------------
+                           OPTIONS
+        Most options may be changed either on the command line,
+        or from within less by using the - or -- command.
+        Options may be given in one of two forms: either a single
+        character preceded by a -, or a name preceeded by --.
+  -?  ........  --help
+                  Display help (from command line).
+  -a  ........  --search-skip-screen
+                  Forward search skips current screen.
+  -b [N]  ....  --buffers=[N]
+                  Number of buffers.
+  -B  ........  --auto-buffers
+                  Don't automatically allocate buffers for pipes.
+  -c  ........  --clear-screen
+                  Repaint by clearing rather than scrolling.
+  -d  ........  --dumb
+                  Dumb terminal.
+  -D [xn.n]  .  --color=xn.n
+                  Set screen colors. (MS-DOS only)
+  -e  -E  ....  --quit-at-eof  --QUIT-AT-EOF
+                  Quit at end of file.
+  -f  ........  --force
+                  Force open non-regular files.
+  -F  ........  --quit-if-one-screen
+                  Quit if entire file fits on first screen.
+  -g  ........  --hilite-search
+                  Highlight only last match for searches.
+  -G  ........  --HILITE-SEARCH
+                  Don't highlight any matches for searches.
+  -h [N]  ....  --max-back-scroll=[N]
+                  Backward scroll limit.
+  -i  ........  --ignore-case
+                  Ignore case in searches that do not contain uppercase.
+  -I  ........  --IGNORE-CASE
+                  Ignore case in all searches.
+  -j [N]  ....  --jump-target=[N]
+                  Screen position of target lines.
+  -J  ........  --status-column
+                  Display a status column at left edge of screen.
+  -k [file]  .  --lesskey-file=[file]
+                  Use a lesskey file.
+  -L  ........  --no-lessopen
+                  Ignore the LESSOPEN environment variable.
+  -m  -M  ....  --long-prompt  --LONG-PROMPT
+                  Set prompt style.
+  -n  -N  ....  --line-numbers  --LINE-NUMBERS
+                  Don't use line numbers.
+  -o [file]  .  --log-file=[file]
+                  Copy to log file (standard input only).
+  -O [file]  .  --LOG-FILE=[file]
+                  Copy to log file (unconditionally overwrite).
+  -p [pattern]  --pattern=[pattern]
+                  Start at pattern (from command line).
+  -P [prompt]   --prompt=[prompt]
+                  Define new prompt.
+  -q  -Q  ....  --quiet  --QUIET  --silent --SILENT
+                  Quiet the terminal bell.
+  -r  -R  ....  --raw-control-chars  --RAW-CONTROL-CHARS
+                  Output "raw" control characters.
+  -s  ........  --squeeze-blank-lines
+                  Squeeze multiple blank lines.
+  -S  ........  --chop-long-lines
+                  Chop long lines.
+  -t [tag]  ..  --tag=[tag]
+                  Find a tag.
+  -T [tagsfile] --tag-file=[tagsfile]
+                  Use an alternate tags file.
+  -u  -U  ....  --underline-special  --UNDERLINE-SPECIAL
+                  Change handling of backspaces.
+  -V  ........  --version
+                  Display the version number of "less".
+  -w  ........  --hilite-unread
+                  Highlight first new line after forward-screen.
+  -W  ........  --HILITE-UNREAD
+                  Highlight first new line after any forward movement.
+  -x [N[,...]]  --tabs=[N[,...]]
+                  Set tab stops.
+  -X  ........  --no-init
+                  Don't use termcap init/deinit strings.
+                --no-keypad
+                  Don't use termcap keypad init/deinit strings.
+  -y [N]  ....  --max-forw-scroll=[N]
+                  Forward scroll limit.
+  -z [N]  ....  --window=[N]
+                  Set size of window.
+  -" [c[c]]  .  --quotes=[c[c]]
+                  Set shell quote characters.
+  -~  ........  --tilde
+                  Don't display tildes after end of file.
+  -# [N]  ....  --shift=[N]
+                  Horizontal scroll amount (0 = one half screen width)
+
+ ---------------------------------------------------------------------------
+                          LINE EDITING
+        These keys can be used to edit text being entered
+        on the "command line" at the bottom of the screen.
+ RightArrow                       ESC-l     Move cursor right one character.
+ LeftArrow                        ESC-h     Move cursor left one character.
+ CNTL-RightArrow  ESC-RightArrow  ESC-w     Move cursor right one word.
+ CNTL-LeftArrow   ESC-LeftArrow   ESC-b     Move cursor left one word.
+ HOME                             ESC-0     Move cursor to start of line.
+ END                              ESC-$     Move cursor to end of line.
+ BACKSPACE                                  Delete char to left of cursor.
+ DELETE                           ESC-x     Delete char under cursor.
+ CNTL-BACKSPACE   ESC-BACKSPACE             Delete word to left of cursor.
+ CNTL-DELETE      ESC-DELETE      ESC-X     Delete word under cursor.
+ CNTL-U           ESC (MS-DOS only)         Delete entire line.
+ UpArrow                          ESC-k     Retrieve previous command line.
+ DownArrow                        ESC-j     Retrieve next command line.
+ TAB                                        Complete filename & cycle.
+ SHIFT-TAB                        ESC-TAB   Complete filename & reverse cycle.
+ CNTL-L                                     Complete filename, list all.
+*/
diff --git a/busybox-1.19.3/miscutils/makedevs.c b/busybox-1.19.3/miscutils/makedevs.c
new file mode 100644
index 0000000..c945a13
--- /dev/null
+++ b/busybox-1.19.3/miscutils/makedevs.c
@@ -0,0 +1,269 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * public domain -- Dave 'Kill a Cop' Cinege <dcinege@psychosis.com>
+ *
+ * makedevs
+ * Make ranges of device files quickly.
+ * 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
+/*
+makedevs NAME TYPE MAJOR MINOR FIRST LAST [s]
+TYPEs:
+b       Block device
+c       Character device
+f       FIFO
+
+FIRST..LAST specify numbers appended to NAME.
+If 's' is the last argument, the base device is created as well.
+Examples:
+        makedevs /dev/ttyS c 4 66 2 63   ->  ttyS2-ttyS63
+        makedevs /dev/hda b 3 0 0 8 s    ->  hda,hda1-hda8
+*/
+int makedevs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int makedevs_main(int argc, char **argv)
+{
+	mode_t mode;
+	char *basedev, *type, *nodname, *buf;
+	int Smajor, Sminor, S, E;
+
+	if (argc < 7 || argv[1][0] == '-')
+		bb_show_usage();
+
+	basedev = argv[1];
+	buf = xasprintf("%s%u", argv[1], (unsigned)-1);
+	type = argv[2];
+	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;
+	switch (type[0]) {
+	case 'c':
+		mode |= S_IFCHR;
+		break;
+	case 'b':
+		mode |= S_IFBLK;
+		break;
+	case 'f':
+		mode |= S_IFIFO;
+		break;
+	default:
+		bb_show_usage();
+	}
+
+	while (S <= E) {
+		sprintf(buf, "%s%u", basedev, S);
+
+		/* if mode != S_IFCHR and != S_IFBLK,
+		 * third param in mknod() ignored */
+		if (mknod(nodname, mode, makedev(Smajor, Sminor)))
+			bb_perror_msg("can't create '%s'", nodname);
+
+		/*if (nodname == basedev)*/ /* ex. /dev/hda - to /dev/hda1 ... */
+			nodname = buf;
+		S++;
+		Sminor++;
+	}
+
+	return 0;
+}
+
+#elif ENABLE_FEATURE_MAKEDEVS_TABLE
+
+/* 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)
+{
+	parser_t *parser;
+	char *line = (char *)"-";
+	int ret = EXIT_SUCCESS;
+
+	opt_complementary = "=1"; /* exactly one param */
+	getopt32(argv, "d:", &line);
+	argv += optind;
+
+	xchdir(*argv); /* ensure root dir exists */
+
+	umask(0);
+
+	printf("rootdir=%s\ntable=", *argv);
+	if (NOT_LONE_DASH(line)) {
+		printf("'%s'\n", line);
+	} else {
+		puts("<stdin>");
+	}
+
+	parser = config_open(line);
+	while (config_read(parser, &line, 1, 1, "# \t", PARSE_NORMAL)) {
+		int linenum;
+		char type;
+		unsigned mode = 0755;
+		unsigned major = 0;
+		unsigned minor = 0;
+		unsigned count = 0;
+		unsigned increment = 0;
+		unsigned start = 0;
+		char name[41];
+		char user[41];
+		char group[41];
+		char *full_name = name;
+		uid_t uid;
+		gid_t gid;
+
+		linenum = parser->lineno;
+
+		if ((2 > sscanf(line, "%40s %c %o %40s %40s %u %u %u %u %u",
+					name, &type, &mode, user, group,
+					&major, &minor, &start, &increment, &count))
+		 || ((unsigned)(major | minor | start | count | increment) > 255)
+		) {
+			bb_error_msg("invalid line %d: '%s'", linenum, line);
+			ret = EXIT_FAILURE;
+			continue;
+		}
+
+		gid = (*group) ? get_ug_id(group, xgroup2gid) : getgid();
+		uid = (*user) ? get_ug_id(user, xuname2uid) : getuid();
+		/* We are already in the right root dir,
+		 * so make absolute paths relative */
+		if ('/' == *full_name)
+			full_name++;
+
+		if (type == 'd') {
+			bb_make_directory(full_name, mode | S_IFDIR, FILEUTILS_RECUR);
+			if (chown(full_name, uid, gid) == -1) {
+ chown_fail:
+				bb_perror_msg("line %d: can't chown %s", linenum, full_name);
+				ret = EXIT_FAILURE;
+				continue;
+			}
+			if (chmod(full_name, mode) < 0) {
+ chmod_fail:
+				bb_perror_msg("line %d: can't chmod %s", linenum, full_name);
+				ret = EXIT_FAILURE;
+				continue;
+			}
+		} else if (type == 'f') {
+			struct stat st;
+			if ((stat(full_name, &st) < 0 || !S_ISREG(st.st_mode))) {
+				bb_perror_msg("line %d: regular file '%s' does not exist", linenum, full_name);
+				ret = EXIT_FAILURE;
+				continue;
+			}
+			if (chown(full_name, uid, gid) < 0)
+				goto chown_fail;
+			if (chmod(full_name, mode) < 0)
+				goto chmod_fail;
+		} else {
+			dev_t rdev;
+			unsigned i;
+			char *full_name_inc;
+
+			if (type == 'p') {
+				mode |= S_IFIFO;
+			} else if (type == 'c') {
+				mode |= S_IFCHR;
+			} else if (type == 'b') {
+				mode |= S_IFBLK;
+			} else {
+				bb_error_msg("line %d: unsupported file type %c", linenum, type);
+				ret = EXIT_FAILURE;
+				continue;
+			}
+
+			full_name_inc = xmalloc(strlen(full_name) + sizeof(int)*3 + 2);
+			if (count)
+				count--;
+			for (i = start; i <= start + count; i++) {
+				sprintf(full_name_inc, count ? "%s%u" : "%s", full_name, i);
+				rdev = makedev(major, minor + (i - start) * increment);
+				if (mknod(full_name_inc, mode, rdev) < 0) {
+					bb_perror_msg("line %d: can't create node %s", linenum, full_name_inc);
+					ret = EXIT_FAILURE;
+				} else if (chown(full_name_inc, uid, gid) < 0) {
+					bb_perror_msg("line %d: can't chown %s", linenum, full_name_inc);
+					ret = EXIT_FAILURE;
+				} else if (chmod(full_name_inc, mode) < 0) {
+					bb_perror_msg("line %d: can't chmod %s", linenum, full_name_inc);
+					ret = EXIT_FAILURE;
+				}
+			}
+			free(full_name_inc);
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		config_close(parser);
+
+	return ret;
+}
+
+#else
+# error makedevs configuration error, either leaf or table must be selected
+#endif
diff --git a/busybox-1.19.3/miscutils/man.c b/busybox-1.19.3/miscutils/man.c
new file mode 100644
index 0000000..3bf7e84
--- /dev/null
+++ b/busybox-1.19.3/miscutils/man.c
@@ -0,0 +1,294 @@
+/* mini man implementation for busybox
+ * Copyright (C) 2008 Denys Vlasenko <vda.linux@googlemail.com>
+ * 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 {
+	OPT_a = 1, /* all */
+	OPT_w = 2, /* print path */
+};
+
+/* This is what I see on my desktop system being executed:
+
+(
+echo ".ll 12.4i"
+echo ".nr LL 12.4i"
+echo ".pl 1100i"
+gunzip -c '/usr/man/man1/bzip2.1.gz'
+echo ".\\\""
+echo ".pl \n(nlu+10"
+) | gtbl | nroff -Tlatin1 -mandoc | less
+
+*/
+
+#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)
+{
+	char *cmd;
+
+	/* Prevent man page link loops */
+	if (level > 10)
+		return 0;
+
+	if (access(man_filename, R_OK) != 0)
+		return 0;
+
+	if (option_mask32 & OPT_w) {
+		puts(man_filename);
+		return 1;
+	}
+
+	if (man) { /* man page, not cat page */
+		/* Is this a link to another manpage? */
+		/* The link has the following on the first line: */
+		/* ".so another_man_page" */
+
+		struct stat sb;
+		char *line;
+		char *linkname, *p;
+
+		/* On my system:
+		 * man1/genhostid.1.gz: 203 bytes - smallest real manpage
+		 * man2/path_resolution.2.gz: 114 bytes - largest link
+		 */
+		xstat(man_filename, &sb);
+		if (sb.st_size > 300) /* err on the safe side */
+			goto ordinary_manpage;
+
+		line = xmalloc_open_zipped_read_close(man_filename, NULL);
+		if (!line || strncmp(line, ".so ", 4) != 0) {
+			free(line);
+			goto ordinary_manpage;
+		}
+		/* Example: man2/path_resolution.2.gz contains
+		 * ".so man7/path_resolution.7\n<junk>"
+		 */
+		*strchrnul(line, '\n') = '\0';
+		linkname = skip_whitespace(&line[4]);
+
+		/* If link has no slashes, we just replace man page name.
+		 * If link has slashes (however many), we go back *once*.
+		 * ".so zzz/ggg/page.3" does NOT go back two levels. */
+		p = strrchr(man_filename, '/');
+		if (!p)
+			goto ordinary_manpage;
+		*p = '\0';
+		if (strchr(linkname, '/')) {
+			p = strrchr(man_filename, '/');
+			if (!p)
+				goto ordinary_manpage;
+			*p = '\0';
+		}
+
+		/* Links do not have .gz extensions, even if manpage
+		 * is compressed */
+		man_filename = xasprintf("%s/%s" Z_SUFFIX, man_filename, linkname);
+		free(line);
+		/* Note: we leak "new" man_filename string as well... */
+		if (show_manpage(pager, man_filename, man, level + 1))
+			return 1;
+		/* else: show the link, it's better than nothing */
+	}
+
+ ordinary_manpage:
+	close(STDIN_FILENO);
+	open_zipped(man_filename); /* guaranteed to use fd 0 (STDIN_FILENO) */
+	/* "2>&1" is added so that nroff errors are shown in pager too.
+	 * Otherwise it may show just empty screen */
+	cmd = xasprintf(
+		man ? "gtbl | nroff -Tlatin1 -mandoc 2>&1 | %s"
+		    : "%s",
+		pager);
+	system(cmd);
+	free(cmd);
+	return 1;
+}
+
+/* man_filename is of the form "/dir/dir/dir/name.s" Z_SUFFIX */
+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;
+#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))
+		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))
+		return 1;
+#endif
+
+#if ENABLE_FEATURE_SEAMLESS_LZMA || ENABLE_FEATURE_SEAMLESS_BZ2 || ENABLE_FEATURE_SEAMLESS_GZ
+	*strrchr(man_filename, '.') = '\0';
+#endif
+	if (run_pipe(pager, man_filename, man, level))
+		return 1;
+
+	return 0;
+}
+
+int man_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int man_main(int argc UNUSED_PARAM, char **argv)
+{
+	parser_t *parser;
+	const char *pager;
+	char **man_path_list;
+	char *sec_list;
+	char *cur_path, *cur_sect;
+	int count_mp, cur_mp;
+	int opt, not_found;
+	char *token[2];
+
+	opt_complementary = "-1"; /* at least one argument */
+	opt = getopt32(argv, "+aw");
+	argv += optind;
+
+	sec_list = xstrdup("1:2:3: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]));
+	man_path_list[0] = getenv("MANPATH");
+	if (!man_path_list[0]) /* default, may be overridden by /etc/man.conf */
+		man_path_list[0] = (char*)"/usr/man";
+	else
+		count_mp++;
+	pager = getenv("MANPAGER");
+	if (!pager) {
+		pager = getenv("PAGER");
+		if (!pager)
+			pager = "more";
+	}
+
+	/* 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("MANDATORY_MANPATH"+10, token[0]) == 0 /* "MANPATH"? */
+		 || strcmp("MANDATORY_MANPATH", token[0]) == 0
+		) {
+			char *path = token[1];
+			while (*path) {
+				char *next_path;
+				char **path_element;
+
+				next_path = strchr(path, ':');
+				if (next_path) {
+					*next_path = '\0';
+					if (next_path++ == path) /* "::"? */
+						goto next;
+				}
+				/* Do we already have path? */
+				path_element = man_path_list;
+				while (*path_element) {
+					if (strcmp(*path_element, path) == 0)
+						goto skip;
+					path_element++;
+				}
+				man_path_list = xrealloc_vector(man_path_list, 4, count_mp);
+				man_path_list[count_mp] = xstrdup(path);
+				count_mp++;
+				/* man_path_list is NULL terminated */
+				/*man_path_list[count_mp] = NULL; - xrealloc_vector did it */
+ skip:
+				if (!next_path)
+					break;
+ next:
+				path = next_path;
+			}
+		}
+		if (strcmp("MANSECT", token[0]) == 0) {
+			free(sec_list);
+			sec_list = xstrdup(token[1]);
+		}
+	}
+	config_close(parser);
+
+	not_found = 0;
+	do { /* for each argv[] */
+		int found = 0;
+		cur_mp = 0;
+
+		if (strchr(*argv, '/')) {
+			found = show_manpage(pager, *argv, /*man:*/ 1, 0);
+			goto check_found;
+		}
+		while ((cur_path = man_path_list[cur_mp++]) != NULL) {
+			/* for each MANPATH */
+			cur_sect = sec_list;
+			do { /* for each section */
+				char *next_sect = strchrnul(cur_sect, ':');
+				int sect_len = next_sect - cur_sect;
+				char *man_filename;
+				int cat0man1 = 0;
+
+				/* Search for cat, then man page */
+				while (cat0man1 < 2) {
+					int found_here;
+					man_filename = xasprintf("%s/%s%.*s/%s.%.*s" Z_SUFFIX,
+							cur_path,
+							"cat\0man" + (cat0man1 * 4),
+							sect_len, cur_sect,
+							*argv,
+							sect_len, cur_sect);
+					found_here = show_manpage(pager, man_filename, cat0man1, 0);
+					found |= found_here;
+					cat0man1 += found_here + 1;
+					free(man_filename);
+				}
+
+				if (found && !(opt & OPT_a))
+					goto next_arg;
+				cur_sect = next_sect;
+				while (*cur_sect == ':')
+					cur_sect++;
+			} while (*cur_sect);
+		}
+ check_found:
+		if (!found) {
+			bb_error_msg("no manual entry for '%s'", *argv);
+			not_found = 1;
+		}
+ next_arg:
+		argv++;
+	} while (*argv);
+
+	return not_found;
+}
diff --git a/busybox-1.19.3/miscutils/microcom.c b/busybox-1.19.3/miscutils/microcom.c
new file mode 100644
index 0000000..5e29a1a
--- /dev/null
+++ b/busybox-1.19.3/miscutils/microcom.c
@@ -0,0 +1,183 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bare bones 'talk to modem' program - similar to 'cu -l $device'
+ * inspired by mgetty's microcom
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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
+static void xget1(int fd, struct termios *t, struct termios *oldt)
+{
+	tcgetattr(fd, oldt);
+	*t = *oldt;
+	cfmakeraw(t);
+//	t->c_lflag &= ~(ISIG|ICANON|ECHO|IEXTEN);
+//	t->c_iflag &= ~(BRKINT|IXON|ICRNL);
+//	t->c_oflag &= ~(ONLCR);
+//	t->c_cc[VMIN]  = 1;
+//	t->c_cc[VTIME] = 0;
+}
+
+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;
+}
+
+int microcom_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int microcom_main(int argc UNUSED_PARAM, char **argv)
+{
+	int sfd;
+	int nfd;
+	struct pollfd pfd[2];
+	struct termios tio0, tiosfd, tio;
+	char *device_lock_file;
+	enum {
+		OPT_X = 1 << 0, // do not respect Ctrl-X, Ctrl-@
+		OPT_s = 1 << 1, // baudrate
+		OPT_d = 1 << 2, // wait for device response, ms
+		OPT_t = 1 << 3, // timeout, ms
+	};
+	speed_t speed = 9600;
+	int delay = -1;
+	int timeout = -1;
+	unsigned opts;
+
+	// fetch options
+	opt_complementary = "=1:s+:d+:t+"; // exactly one arg, numeric options
+	opts = getopt32(argv, "Xs:d:t:", &speed, &delay, &timeout);
+//	argc -= optind;
+	argv += optind;
+
+	// try to create lock file in /var/lock
+	device_lock_file = (char *)bb_basename(argv[0]);
+	device_lock_file = xasprintf("/var/lock/LCK..%s", device_lock_file);
+	sfd = open(device_lock_file, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0644);
+	if (sfd < 0) {
+		// device already locked -> bail out
+		if (errno == EEXIST)
+			bb_perror_msg_and_die("can't create '%s'", device_lock_file);
+		// can't create lock -> don't care
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(device_lock_file);
+		device_lock_file = NULL;
+	} else {
+		// %4d to make concurrent mgetty (if any) happy.
+		// Mgetty treats 4-bytes lock files as binary,
+		// not text, PID. Making 5+ char file. Brrr...
+		fdprintf(sfd, "%4d\n", getpid());
+		close(sfd);
+	}
+
+	// setup signals
+	bb_signals(0
+		+ (1 << SIGHUP)
+		+ (1 << SIGINT)
+		+ (1 << SIGTERM)
+		+ (1 << SIGPIPE)
+		, record_signo);
+
+	// error exit code if we fail to open the device
+	bb_got_signal = 1;
+
+	// open device
+	sfd = open_or_warn(argv[0], O_RDWR | O_NOCTTY | O_NONBLOCK);
+	if (sfd < 0)
+		goto done;
+	fcntl(sfd, F_SETFL, O_RDWR);
+
+	// put device to "raw mode"
+	xget1(sfd, &tio, &tiosfd);
+	// set device speed
+	cfsetspeed(&tio, tty_value_to_baud(speed));
+	if (xset1(sfd, &tio, argv[0]))
+		goto done;
+
+	// put stdin to "raw mode" (if stdin is a TTY),
+	// handle one character at a time
+	if (isatty(STDIN_FILENO)) {
+		xget1(STDIN_FILENO, &tio, &tio0);
+		if (xset1(STDIN_FILENO, &tio, "stdin"))
+			goto done;
+	}
+
+	// main loop: check with poll(), then read/write bytes across
+	pfd[0].fd = sfd;
+	pfd[0].events = POLLIN;
+	pfd[1].fd = STDIN_FILENO;
+	pfd[1].events = POLLIN;
+
+	bb_got_signal = 0;
+	nfd = 2;
+	// Not safe_poll: we want to exit on signal
+	while (!bb_got_signal && poll(pfd, nfd, timeout) > 0) {
+		if (nfd > 1 && pfd[1].revents) {
+			char c;
+			// read from stdin -> write to device
+			if (safe_read(STDIN_FILENO, &c, 1) < 1) {
+				// don't poll stdin anymore if we got EOF/error
+				nfd--;
+				goto skip_write;
+			}
+			// do we need special processing?
+			if (!(opts & OPT_X)) {
+				// ^@ sends Break
+				if (VINTR == c) {
+					tcsendbreak(sfd, 0);
+					goto skip_write;
+				}
+				// ^X exits
+				if (24 == c)
+					break;
+			}
+			write(sfd, &c, 1);
+			if (delay >= 0)
+				safe_poll(pfd, 1, delay);
+skip_write: ;
+		}
+		if (pfd[0].revents) {
+#define iobuf bb_common_bufsiz1
+			ssize_t len;
+			// read from device -> write to stdout
+			len = safe_read(sfd, iobuf, sizeof(iobuf));
+			if (len > 0)
+				full_write(STDOUT_FILENO, iobuf, len);
+			else {
+				// EOF/error -> bail out
+				bb_got_signal = SIGHUP;
+				break;
+			}
+		}
+	}
+
+	// restore device mode
+	tcsetattr(sfd, TCSAFLUSH, &tiosfd);
+
+	if (isatty(STDIN_FILENO))
+		tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0);
+
+done:
+	if (device_lock_file)
+		unlink(device_lock_file);
+
+	return bb_got_signal;
+}
diff --git a/busybox-1.19.3/miscutils/mountpoint.c b/busybox-1.19.3/miscutils/mountpoint.c
new file mode 100644
index 0000000..7041f7c
--- /dev/null
+++ b/busybox-1.19.3/miscutils/mountpoint.c
@@ -0,0 +1,96 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mountpoint implementation for busybox
+ *
+ * Copyright (C) 2005 Bernhard Reutner-Fischer
+ *
+ * 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;
+int mountpoint_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat st;
+	const char *msg;
+	char *arg;
+	int rc, opt;
+
+	opt_complementary = "=1"; /* must have one argument */
+	opt = getopt32(argv, "qdxn");
+#define OPT_q (1)
+#define OPT_d (2)
+#define OPT_x (4)
+#define OPT_n (8)
+	arg = argv[optind];
+	msg = "%s";
+
+	rc = (opt & OPT_x) ? stat(arg, &st) : lstat(arg, &st);
+	if (rc != 0)
+		goto err;
+
+	if (opt & OPT_x) {
+		if (S_ISBLK(st.st_mode)) {
+			printf("%u:%u\n", major(st.st_rdev),
+						minor(st.st_rdev));
+			return EXIT_SUCCESS;
+		}
+		errno = 0; /* make perror_msg work as error_msg */
+		msg = "%s: not a block device";
+		goto err;
+	}
+
+	errno = ENOTDIR;
+	if (S_ISDIR(st.st_mode)) {
+		dev_t st_dev = st.st_dev;
+		ino_t st_ino = st.st_ino;
+		char *p = xasprintf("%s/..", arg);
+
+		if (stat(p, &st) == 0) {
+			//int is_mnt = (st_dev != st.st_dev) || (st_dev == st.st_dev && st_ino == st.st_ino);
+			int is_not_mnt = (st_dev == st.st_dev) && (st_ino != st.st_ino);
+
+			if (opt & OPT_d)
+				printf("%u:%u\n", major(st_dev), minor(st_dev));
+			if (opt & OPT_n) {
+				const char *d = find_block_device(arg);
+				/* name is undefined, but device is mounted -> anonymous superblock! */
+				/* happens with btrfs */
+				if (!d) {
+					d = "UNKNOWN";
+					/* TODO: iterate /proc/mounts, or /proc/self/mountinfo
+					 * to find out the device name */
+				}
+				printf("%s %s\n", d, arg);
+			}
+			if (!(opt & (OPT_q | OPT_d | OPT_n)))
+				printf("%s is %sa mountpoint\n", arg, is_not_mnt ? "not " : "");
+			return is_not_mnt;
+		}
+		arg = p;
+		/* else: stat had set errno, just fall through */
+	}
+
+ err:
+	if (!(opt & OPT_q))
+		bb_perror_msg(msg, arg);
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/miscutils/mt.c b/busybox-1.19.3/miscutils/mt.c
new file mode 100644
index 0000000..20afd3a
--- /dev/null
+++ b/busybox-1.19.3/miscutils/mt.c
@@ -0,0 +1,152 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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>
+
+/* missing: eod/seod, stoptions, stwrthreshold, densities */
+static const short opcode_value[] = {
+	MTBSF,
+	MTBSFM,
+	MTBSR,
+	MTBSS,
+	MTCOMPRESSION,
+	MTEOM,
+	MTERASE,
+	MTFSF,
+	MTFSFM,
+	MTFSR,
+	MTFSS,
+	MTLOAD,
+	MTLOCK,
+	MTMKPART,
+	MTNOP,
+	MTOFFL,
+	MTOFFL,
+	MTRAS1,
+	MTRAS2,
+	MTRAS3,
+	MTRESET,
+	MTRETEN,
+	MTREW,
+	MTSEEK,
+	MTSETBLK,
+	MTSETDENSITY,
+	MTSETDRVBUFFER,
+	MTSETPART,
+	MTTELL,
+	MTWSM,
+	MTUNLOAD,
+	MTUNLOCK,
+	MTWEOF,
+	MTWEOF
+};
+
+static const char opcode_name[] ALIGN1 =
+	"bsf"             "\0"
+	"bsfm"            "\0"
+	"bsr"             "\0"
+	"bss"             "\0"
+	"datacompression" "\0"
+	"eom"             "\0"
+	"erase"           "\0"
+	"fsf"             "\0"
+	"fsfm"            "\0"
+	"fsr"             "\0"
+	"fss"             "\0"
+	"load"            "\0"
+	"lock"            "\0"
+	"mkpart"          "\0"
+	"nop"             "\0"
+	"offline"         "\0"
+	"rewoffline"      "\0"
+	"ras1"            "\0"
+	"ras2"            "\0"
+	"ras3"            "\0"
+	"reset"           "\0"
+	"retension"       "\0"
+	"rewind"          "\0"
+	"seek"            "\0"
+	"setblk"          "\0"
+	"setdensity"      "\0"
+	"drvbuffer"       "\0"
+	"setpart"         "\0"
+	"tell"            "\0"
+	"wset"            "\0"
+	"unload"          "\0"
+	"unlock"          "\0"
+	"eof"             "\0"
+	"weof"            "\0";
+
+int mt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mt_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *file = "/dev/tape";
+	struct mtop op;
+	struct mtpos position;
+	int fd, mode, idx;
+
+	if (!argv[1]) {
+		bb_show_usage();
+	}
+
+	if (strcmp(argv[1], "-f") == 0) {
+		if (!argv[2] || !argv[3])
+			bb_show_usage();
+		file = argv[2];
+		argv += 2;
+	}
+
+	idx = index_in_strings(opcode_name, argv[1]);
+
+	if (idx < 0)
+		bb_error_msg_and_die("unrecognized opcode %s", argv[1]);
+
+	op.mt_op = opcode_value[idx];
+	if (argv[2])
+		op.mt_count = xatoi_positive(argv[2]);
+	else
+		op.mt_count = 1;  /* One, not zero, right? */
+
+	switch (opcode_value[idx]) {
+		case MTWEOF:
+		case MTERASE:
+		case MTWSM:
+		case MTSETDRVBUFFER:
+			mode = O_WRONLY;
+			break;
+
+		default:
+			mode = O_RDONLY;
+			break;
+	}
+
+	fd = xopen(file, mode);
+
+	switch (opcode_value[idx]) {
+		case MTTELL:
+			ioctl_or_perror_and_die(fd, MTIOCPOS, &position, "%s", file);
+			printf("At block %d\n", (int) position.mt_blkno);
+			break;
+
+		default:
+			ioctl_or_perror_and_die(fd, MTIOCTOP, &op, "%s", file);
+			break;
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/nandwrite.c b/busybox-1.19.3/miscutils/nandwrite.c
new file mode 100644
index 0000000..562a342
--- /dev/null
+++ b/busybox-1.19.3/miscutils/nandwrite.c
@@ -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
+ */
+
+//applet:IF_NANDWRITE(APPLET(nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP))
+//applet:IF_NANDWRITE(APPLET_ODDNAME(nanddump, nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP, nanddump))
+
+//kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o
+//kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o
+
+//config:config NANDWRITE
+//config:	bool "nandwrite"
+//config:	default n
+//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 n
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Dump the content of raw NAND chip
+
+//usage:#define nandwrite_trivial_usage
+//usage:	"[-p] [-s ADDR] MTD_DEVICE [FILE]"
+//usage:#define nandwrite_full_usage "\n\n"
+//usage:	"Write to the specified 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] [-f FILE] MTD_DEVICE"
+//usage:#define nanddump_full_usage "\n\n"
+//usage:	"Dump the specified MTD device\n"
+//usage:     "\n	-o	Omit 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], O_RDWR);
+	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) {
+				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;
+}
diff --git a/busybox-1.19.3/miscutils/raidautorun.c b/busybox-1.19.3/miscutils/raidautorun.c
new file mode 100644
index 0000000..b72d890
--- /dev/null
+++ b/busybox-1.19.3/miscutils/raidautorun.c
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * raidautorun implementation for busybox
+ *
+ * Copyright (C) 2006 Bernhard Reutner-Fischer
+ *
+ * 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>
+#include <linux/raid/md_u.h>
+
+int raidautorun_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int raidautorun_main(int argc UNUSED_PARAM, char **argv)
+{
+	xioctl(xopen(single_argv(argv), O_RDONLY), RAID_AUTORUN, NULL);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/readahead.c b/busybox-1.19.3/miscutils/readahead.c
new file mode 100644
index 0000000..e22aaa4
--- /dev/null
+++ b/busybox-1.19.3/miscutils/readahead.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * readahead implementation for busybox
+ *
+ * Preloads the given files in RAM, to reduce access time.
+ * Does this by calling the readahead(2) system call.
+ *
+ * Copyright (C) 2006  Michael Opdenacker <michael@free-electrons.com>
+ *
+ * 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;
+int readahead_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval = EXIT_SUCCESS;
+
+	if (!argv[1]) {
+		bb_show_usage();
+	}
+
+	while (*++argv) {
+		int fd = open_or_warn(*argv, O_RDONLY);
+		if (fd >= 0) {
+			off_t len;
+			int r;
+
+			/* fdlength was reported to be unreliable - use seek */
+			len = xlseek(fd, 0, SEEK_END);
+			xlseek(fd, 0, SEEK_SET);
+			r = readahead(fd, 0, len);
+			close(fd);
+			if (r >= 0)
+				continue;
+		}
+		retval = EXIT_FAILURE;
+	}
+
+	return retval;
+}
diff --git a/busybox-1.19.3/miscutils/rfkill.c b/busybox-1.19.3/miscutils/rfkill.c
new file mode 100644
index 0000000..4671973
--- /dev/null
+++ b/busybox-1.19.3/miscutils/rfkill.c
@@ -0,0 +1,133 @@
+/* vi: set sw=4 ts=4: */
+/*
+* rfkill implementation for busybox
+*
+* Copyright (C) 2010  Malek Degachi <malek-degachi@laposte.net>
+*
+* Licensed under GPLv2 or later, see file LICENSE in this source tree.
+*/
+
+//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>
+
+enum {
+	OPT_b = (1 << 0), /* must be = 1 */
+	OPT_u = (1 << 1),
+	OPT_l = (1 << 2),
+};
+
+int rfkill_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rfkill_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct rfkill_event event;
+	const char *rf_name;
+	int rf_fd;
+	int mode;
+	int rf_type;
+	int rf_idx;
+	unsigned rf_opt = 0;
+
+	argv++;
+	/* Must have one or two params */
+	if (!argv[0] || (argv[1] && argv[2]))
+		bb_show_usage();
+
+	mode = O_RDWR | O_NONBLOCK;
+	rf_name = argv[1];
+	if (strcmp(argv[0], "list") == 0) {
+		rf_opt |= OPT_l;
+		mode = O_RDONLY | O_NONBLOCK;
+	} else if (strcmp(argv[0], "block") == 0 && rf_name) {
+		rf_opt |= OPT_b;
+	} else if (strcmp(argv[0], "unblock") == 0 && rf_name) {
+		rf_opt |= OPT_u;
+	} else
+		bb_show_usage();
+
+	rf_type = RFKILL_TYPE_ALL;
+	rf_idx = -1;
+	if (rf_name) {
+		static const char rfkill_types[] ALIGN1 = "all\0wlan\0bluetooth\0uwb\0wimax\0wwan\0gps\0fm\0";
+		if (strcmp(rf_name, "wifi") == 0)
+			rf_name = "wlan";
+		if (strcmp(rf_name, "ultrawideband") == 0)
+			rf_name = "uwb";
+		rf_type = index_in_strings(rfkill_types, rf_name);
+		if (rf_type < 0) {
+			rf_idx = xatoi_positive(rf_name);
+		}
+	}
+
+	rf_fd = device_open("/dev/rfkill", mode);
+	if (rf_fd < 0)
+		bb_perror_msg_and_die("/dev/rfkill");
+
+	if (rf_opt & OPT_l) {
+		while (full_read(rf_fd, &event, sizeof(event)) == RFKILL_EVENT_SIZE_V1) {
+			parser_t *parser;
+			char *tokens[2];
+			char rf_sysfs[sizeof("/sys/class/rfkill/rfkill%u/uevent") + sizeof(int)*3];
+			char *name, *type;
+
+			if (rf_type && rf_type != event.type && rf_idx < 0) {
+				continue;
+			}
+
+			if (rf_idx >= 0 && event.idx != rf_idx) {
+				continue;
+			}
+
+			name = NULL;
+			type = NULL;
+			sprintf(rf_sysfs, "/sys/class/rfkill/rfkill%u/uevent", event.idx);
+			parser = config_open2(rf_sysfs, fopen_for_read);
+			while (config_read(parser, tokens, 2, 2, "\n=", PARSE_NORMAL)) {
+				if (strcmp(tokens[0], "RFKILL_NAME") == 0) {
+					name = xstrdup(tokens[1]);
+					continue;
+				}
+				if (strcmp(tokens[0], "RFKILL_TYPE") == 0) {
+					type = xstrdup(tokens[1]);
+					continue;
+				}
+			}
+			config_close(parser);
+
+			printf("%u: %s: %s\n", event.idx, name, type);
+			printf("\tSoft blocked: %s\n", event.soft ? "yes" : "no");
+			printf("\tHard blocked: %s\n", event.hard ? "yes" : "no");
+			free(name);
+			free(type);
+		}
+	} else {
+		memset(&event, 0, sizeof(event));
+		if (rf_type >= 0) {
+			event.type = rf_type;
+			event.op = RFKILL_OP_CHANGE_ALL;
+		}
+
+		if (rf_idx >= 0) {
+			event.idx = rf_idx;
+			event.op = RFKILL_OP_CHANGE;
+		}
+
+		/* Note: OPT_b == 1 */
+		event.soft = (rf_opt & OPT_b);
+
+		xwrite(rf_fd, &event, sizeof(event));
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/runlevel.c b/busybox-1.19.3/miscutils/runlevel.c
new file mode 100644
index 0000000..9d38b79
--- /dev/null
+++ b/busybox-1.19.3/miscutils/runlevel.c
@@ -0,0 +1,54 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Prints out the previous and the current runlevel.
+ *
+ * Version: @(#)runlevel  1.20  16-Apr-1997  MvS
+ *
+ * This file is part of the sysvinit suite,
+ * Copyright 1991-1997 Miquel van Smoorenburg.
+ *
+ * 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"
+
+int runlevel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int runlevel_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct utmp *ut;
+	char prev;
+
+	if (argv[1]) utmpname(argv[1]);
+
+	setutent();
+	while ((ut = getutent()) != NULL) {
+		if (ut->ut_type == RUN_LVL) {
+			prev = ut->ut_pid / 256;
+			if (prev == 0) prev = 'N';
+			printf("%c %c\n", prev, ut->ut_pid % 256);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				endutent();
+			return 0;
+		}
+	}
+
+	puts("unknown");
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		endutent();
+	return 1;
+}
diff --git a/busybox-1.19.3/miscutils/rx.c b/busybox-1.19.3/miscutils/rx.c
new file mode 100644
index 0000000..e122577
--- /dev/null
+++ b/busybox-1.19.3/miscutils/rx.c
@@ -0,0 +1,275 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright:     Copyright (C) 2001, Hewlett-Packard Company
+ * Author:        Christopher Hoover <ch@hpl.hp.com>
+ * Description:   xmodem functionality for uploading of kernels
+ *                and the like
+ * Created at:    Thu Dec 20 01:58:08 PST 2001
+ *
+ * xmodem functionality for uploading of kernels and the like
+ *
+ * Copyright (C) 2001 Hewlett-Packard Laboratories
+ *
+ * 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
+#define STX 0x02
+#define EOT 0x04
+#define ACK 0x06
+#define NAK 0x15
+#define BS  0x08
+#define PAD 0x1A
+
+/*
+Cf:
+  http://www.textfiles.com/apple/xmodem
+  http://www.phys.washington.edu/~belonis/xmodem/docxmodem.txt
+  http://www.phys.washington.edu/~belonis/xmodem/docymodem.txt
+  http://www.phys.washington.edu/~belonis/xmodem/modmprot.col
+*/
+
+#define TIMEOUT 1
+#define TIMEOUT_LONG 10
+#define MAXERRORS 10
+
+#define read_fd  STDIN_FILENO
+#define write_fd STDOUT_FILENO
+
+static int read_byte(unsigned timeout)
+{
+	unsigned char buf;
+	int n;
+
+	alarm(timeout);
+	/* NOT safe_read! We want ALRM to interrupt us */
+	n = read(read_fd, &buf, 1);
+	alarm(0);
+	if (n == 1)
+		return buf;
+	return -1;
+}
+
+static int receive(/*int read_fd, */int file_fd)
+{
+	unsigned char blockBuf[1024];
+	unsigned blockLength = 0;
+	unsigned errors = 0;
+	unsigned wantBlockNo = 1;
+	unsigned length = 0;
+	int do_crc = 1;
+	char reply_char;
+	unsigned timeout = TIMEOUT_LONG;
+
+	/* Flush pending input */
+	tcflush(read_fd, TCIFLUSH);
+
+	/* Ask for CRC; if we get errors, we will go with checksum */
+	reply_char = 'C';
+	full_write(write_fd, &reply_char, 1);
+
+	for (;;) {
+		int blockBegin;
+		int blockNo, blockNoOnesCompl;
+		int cksum_or_crc;
+		int expected;
+		int i, j;
+
+		blockBegin = read_byte(timeout);
+		if (blockBegin < 0)
+			goto timeout;
+
+		/* If last block, remove padding */
+		if (blockBegin == EOT) {
+			/* Data blocks can be padded with ^Z characters */
+			/* This code tries to detect and remove them */
+			if (blockLength >= 3
+			 && blockBuf[blockLength - 1] == PAD
+			 && blockBuf[blockLength - 2] == PAD
+			 && blockBuf[blockLength - 3] == PAD
+			) {
+				while (blockLength
+			           && blockBuf[blockLength - 1] == PAD
+				) {
+					blockLength--;
+				}
+			}
+		}
+		/* 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;
+			}
+		}
+
+		timeout = TIMEOUT;
+		reply_char = NAK;
+
+		switch (blockBegin) {
+		case SOH:
+		case STX:
+			break;
+		case EOT:
+			reply_char = ACK;
+			full_write(write_fd, &reply_char, 1);
+			return length;
+		default:
+			goto error;
+		}
+
+		/* Block no */
+		blockNo = read_byte(TIMEOUT);
+		if (blockNo < 0)
+			goto timeout;
+
+		/* Block no, in one's complement form */
+		blockNoOnesCompl = read_byte(TIMEOUT);
+		if (blockNoOnesCompl < 0)
+			goto timeout;
+
+		if (blockNo != (255 - blockNoOnesCompl)) {
+			bb_error_msg("bad block ones compl");
+			goto error;
+		}
+
+		blockLength = (blockBegin == SOH) ? 128 : 1024;
+
+		for (i = 0; i < blockLength; i++) {
+			int cc = read_byte(TIMEOUT);
+			if (cc < 0)
+				goto timeout;
+			blockBuf[i] = cc;
+		}
+
+		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. */
+			goto next;
+		}
+		if (blockNo != (wantBlockNo & 0xff)) {
+			bb_error_msg("unexpected block no, 0x%08x, expecting 0x%08x", blockNo, wantBlockNo);
+			goto error;
+		}
+
+		expected = 0;
+		if (do_crc) {
+			for (i = 0; i < blockLength; i++) {
+				expected = expected ^ blockBuf[i] << 8;
+				for (j = 0; j < 8; j++) {
+					if (expected & 0x8000)
+						expected = (expected << 1) ^ 0x1021;
+					else
+						expected = (expected << 1);
+				}
+			}
+			expected &= 0xffff;
+		} else {
+			for (i = 0; i < blockLength; i++)
+				expected += blockBuf[i];
+			expected &= 0xff;
+		}
+		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);
+			goto error;
+		}
+
+		wantBlockNo++;
+		length += blockLength;
+ next:
+		errors = 0;
+		reply_char = ACK;
+		full_write(write_fd, &reply_char, 1);
+		continue;
+ error:
+ timeout:
+		errors++;
+		if (errors == MAXERRORS) {
+			/* Abort */
+
+			/* If were asking for crc, try again w/o crc */
+			if (reply_char == 'C') {
+				reply_char = NAK;
+				errors = 0;
+				do_crc = 0;
+				goto timeout;
+			}
+			bb_error_msg("too many errors; giving up");
+ fatal:
+			/* 5 CAN followed by 5 BS. Don't try too hard... */
+			safe_write(write_fd, "\030\030\030\030\030\010\010\010\010\010", 10);
+			return -1;
+		}
+
+		/* Flush pending input */
+		tcflush(read_fd, TCIFLUSH);
+
+		full_write(write_fd, &reply_char, 1);
+	} /* for (;;) */
+}
+
+static void sigalrm_handler(int UNUSED_PARAM signum)
+{
+}
+
+int rx_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rx_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct termios tty, orig_tty;
+	int termios_err;
+	int file_fd;
+	int n;
+
+	/* Disabled by vda:
+	 * why we can't receive from stdin? Why we *require*
+	 * controlling tty?? */
+	/*read_fd = xopen(CURRENT_TTY, O_RDWR);*/
+	file_fd = xopen(single_argv(argv), O_RDWR|O_CREAT|O_TRUNC);
+
+	termios_err = tcgetattr(read_fd, &tty);
+	if (termios_err == 0) {
+		orig_tty = tty;
+		cfmakeraw(&tty);
+		tcsetattr(read_fd, TCSAFLUSH, &tty);
+	}
+
+	/* No SA_RESTART: we want ALRM to interrupt read() */
+	signal_no_SA_RESTART_empty_mask(SIGALRM, sigalrm_handler);
+
+	n = receive(file_fd);
+
+	if (termios_err == 0)
+		tcsetattr(read_fd, TCSAFLUSH, &orig_tty);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(file_fd);
+	fflush_stdout_and_exit(n >= 0);
+}
diff --git a/busybox-1.19.3/miscutils/setserial.c b/busybox-1.19.3/miscutils/setserial.c
new file mode 100644
index 0000000..2a034e3
--- /dev/null
+++ b/busybox-1.19.3/miscutils/setserial.c
@@ -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, *baund_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"
+	"baund_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;
+}
diff --git a/busybox-1.19.3/miscutils/setsid.c b/busybox-1.19.3/miscutils/setsid.c
new file mode 100644
index 0000000..ad2c8a4
--- /dev/null
+++ b/busybox-1.19.3/miscutils/setsid.c
@@ -0,0 +1,56 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * setsid.c -- execute a command in a new session
+ * Rick Sladkey <jrs@world.std.com>
+ * In the public domain.
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * 2001-01-18 John Fremlin <vii@penguinpowered.com>
+ * - fork in case we are process group leader
+ *
+ * 2004-11-12 Paul Fox
+ * - 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;
+int setsid_main(int argc UNUSED_PARAM, char **argv)
+{
+	if (!argv[1])
+		bb_show_usage();
+
+	/* 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. */
+	if (setsid() < 0) {
+		pid_t pid = fork_or_rexec(argv);
+		if (pid != 0) {
+			/* parent */
+			/* TODO:
+			 * we can waitpid(pid, &status, 0) and then even
+			 * emulate exitcode, making the behavior consistent
+			 * in both forked and non forked cases.
+			 * However, the code is larger and upstream
+			 * does not do such trick.
+			 */
+			exit(EXIT_SUCCESS);
+		}
+
+		/* child */
+		/* now there should be no error: */
+		setsid();
+	}
+
+	argv++;
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/miscutils/strings.c b/busybox-1.19.3/miscutils/strings.c
new file mode 100644
index 0000000..9f50182
--- /dev/null
+++ b/busybox-1.19.3/miscutils/strings.c
@@ -0,0 +1,92 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * strings implementation for busybox
+ *
+ * Copyright 2003 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * 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
+
+int strings_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int strings_main(int argc UNUSED_PARAM, char **argv)
+{
+	int n, c, status = EXIT_SUCCESS;
+	unsigned count;
+	off_t offset;
+	FILE *file;
+	char *string;
+	const char *fmt = "%s: ";
+	const char *n_arg = "4";
+
+	getopt32(argv, "afon:", &n_arg);
+	/* -a is our default behaviour */
+	/*argc -= optind;*/
+	argv += optind;
+
+	n = xatou_range(n_arg, 1, INT_MAX);
+	string = xzalloc(n + 1);
+	n--;
+
+	if (!*argv) {
+		fmt = "{%s}: ";
+		*--argv = (char *)bb_msg_standard_input;
+	}
+
+	do {
+		file = fopen_or_warn_stdin(*argv);
+		if (!file) {
+			status = EXIT_FAILURE;
+			continue;
+		}
+		offset = 0;
+		count = 0;
+		do {
+			c = fgetc(file);
+			if (isprint_asciionly(c) || c == '\t') {
+				if (count > n) {
+					bb_putchar(c);
+				} else {
+					string[count] = c;
+					if (count == n) {
+						if (option_mask32 & PRINT_NAME) {
+							printf(fmt, *argv);
+						}
+						if (option_mask32 & PRINT_OFFSET) {
+							printf("%7"OFF_FMT"o ", offset - n);
+						}
+						fputs(string, stdout);
+					}
+					count++;
+				}
+			} else {
+				if (count > n) {
+					bb_putchar('\n');
+				}
+				count = 0;
+			}
+			offset++;
+		} while (c != EOF);
+		fclose_if_not_stdin(file);
+	} while (*++argv);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(string);
+
+	fflush_stdout_and_exit(status);
+}
diff --git a/busybox-1.19.3/miscutils/taskset.c b/busybox-1.19.3/miscutils/taskset.c
new file mode 100644
index 0000000..4a9e323
--- /dev/null
+++ b/busybox-1.19.3/miscutils/taskset.c
@@ -0,0 +1,153 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * taskset - retrieve or set a processes' CPU affinity
+ * Copyright (c) 2006 Bernhard Reutner-Fischer
+ *
+ * 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"
+
+#if ENABLE_FEATURE_TASKSET_FANCY
+#define TASKSET_PRINTF_MASK "%s"
+/* craft a string from the mask */
+static char *from_cpuset(cpu_set_t *mask)
+{
+	int i;
+	char *ret = NULL;
+	char *str = xzalloc((CPU_SETSIZE / 4) + 1); /* we will leak it */
+
+	for (i = CPU_SETSIZE - 4; i >= 0; i -= 4) {
+		int val = 0;
+		int off;
+		for (off = 0; off <= 3; ++off)
+			if (CPU_ISSET(i + off, mask))
+				val |= 1 << off;
+		if (!ret && val)
+			ret = str;
+		*str++ = bb_hexdigits_upcase[val] | 0x20;
+	}
+	return ret;
+}
+#else
+#define TASKSET_PRINTF_MASK "%llx"
+static unsigned long long from_cpuset(cpu_set_t *mask)
+{
+	struct BUG_CPU_SETSIZE_is_too_small {
+		char BUG_CPU_SETSIZE_is_too_small[
+			CPU_SETSIZE < sizeof(int) ? -1 : 1];
+	};
+	char *p = (void*)mask;
+
+	/* Take the least significant bits. Careful!
+	 * Consider both CPU_SETSIZE=4 and CPU_SETSIZE=1024 cases
+	 */
+#if BB_BIG_ENDIAN
+	/* For big endian, it means LAST bits */
+	if (CPU_SETSIZE < sizeof(long))
+		p += CPU_SETSIZE - sizeof(int);
+	else if (CPU_SETSIZE < sizeof(long long))
+		p += CPU_SETSIZE - sizeof(long);
+	else
+		p += CPU_SETSIZE - sizeof(long long);
+#endif
+	if (CPU_SETSIZE < sizeof(long))
+		return *(unsigned*)p;
+	if (CPU_SETSIZE < sizeof(long long))
+		return *(unsigned long*)p;
+	return *(unsigned long long*)p;
+}
+#endif
+
+
+int taskset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int taskset_main(int argc UNUSED_PARAM, char **argv)
+{
+	cpu_set_t mask;
+	pid_t pid = 0;
+	unsigned opt_p;
+	const char *current_new;
+	char *pid_str;
+	char *aff = aff; /* for compiler */
+
+	/* NB: we mimic util-linux's taskset: -p does not take
+	 * an argument, i.e., "-pN" is NOT valid, only "-p N"!
+	 * Indeed, util-linux-2.13-pre7 uses:
+	 * getopt_long(argc, argv, "+pchV", ...), not "...p:..." */
+
+	opt_complementary = "-1"; /* at least 1 arg */
+	opt_p = getopt32(argv, "+p");
+	argv += optind;
+
+	if (opt_p) {
+		pid_str = *argv++;
+		if (*argv) { /* "-p <aff> <pid> ...rest.is.ignored..." */
+			aff = pid_str;
+			pid_str = *argv; /* NB: *argv != NULL in this case */
+		}
+		/* else it was just "-p <pid>", and *argv == NULL */
+		pid = xatoul_range(pid_str, 1, ((unsigned)(pid_t)ULONG_MAX) >> 1);
+	} else {
+		aff = *argv++; /* <aff> <cmd...> */
+		if (!*argv)
+			bb_show_usage();
+	}
+
+	current_new = "current\0new";
+	if (opt_p) {
+ print_aff:
+		if (sched_getaffinity(pid, sizeof(mask), &mask) < 0)
+			bb_perror_msg_and_die("can't %cet pid %d's affinity", 'g', pid);
+		printf("pid %d's %s affinity mask: "TASKSET_PRINTF_MASK"\n",
+				pid, current_new, from_cpuset(&mask));
+		if (!*argv) {
+			/* Either it was just "-p <pid>",
+			 * or it was "-p <aff> <pid>" and we came here
+			 * for the second time (see goto below) */
+			return EXIT_SUCCESS;
+		}
+		*argv = NULL;
+		current_new += 8; /* "new" */
+	}
+
+	{ /* Affinity was specified, translate it into cpu_set_t */
+		unsigned i;
+		/* Do not allow zero mask: */
+		unsigned long long m = xstrtoull_range(aff, 0, 1, ULLONG_MAX);
+		enum { CNT_BIT = CPU_SETSIZE < sizeof(m)*8 ? CPU_SETSIZE : sizeof(m)*8 };
+
+		CPU_ZERO(&mask);
+		for (i = 0; i < CNT_BIT; i++) {
+			unsigned long long bit = (1ULL << i);
+			if (bit & m)
+				CPU_SET(i, &mask);
+		}
+	}
+
+	/* Set pid's or our own (pid==0) affinity */
+	if (sched_setaffinity(pid, sizeof(mask), &mask))
+		bb_perror_msg_and_die("can't %cet pid %d's affinity", 's', pid);
+
+	if (!argv[0]) /* "-p <aff> <pid> [...ignored...]" */
+		goto print_aff; /* print new affinity and exit */
+
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/miscutils/time.c b/busybox-1.19.3/miscutils/time.c
new file mode 100644
index 0000000..945f15f
--- /dev/null
+++ b/busybox-1.19.3/miscutils/time.c
@@ -0,0 +1,428 @@
+/* vi: set sw=4 ts=4: */
+/* 'time' utility to display resource usage of processes.
+   Copyright (C) 1990, 91, 92, 93, 96 Free Software Foundation, Inc.
+
+   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"
+
+/* Information on the resources used by a child process.  */
+typedef struct {
+	int waitstatus;
+	struct rusage ru;
+	unsigned elapsed_ms;	/* Wallclock time of process.  */
+} resource_t;
+
+/* msec = milliseconds = 1/1,000 (1*10e-3) second.
+   usec = microseconds = 1/1,000,000 (1*10e-6) second.  */
+
+#define UL unsigned long
+
+static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
+
+/* The output format for the -p option .*/
+static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
+
+/* Format string for printing all statistics verbosely.
+   Keep this output to 24 lines so users on terminals can see it all.*/
+static const char long_format[] ALIGN1 =
+	"\tCommand being timed: \"%C\"\n"
+	"\tUser time (seconds): %U\n"
+	"\tSystem time (seconds): %S\n"
+	"\tPercent of CPU this job got: %P\n"
+	"\tElapsed (wall clock) time (h:mm:ss or m:ss): %E\n"
+	"\tAverage shared text size (kbytes): %X\n"
+	"\tAverage unshared data size (kbytes): %D\n"
+	"\tAverage stack size (kbytes): %p\n"
+	"\tAverage total size (kbytes): %K\n"
+	"\tMaximum resident set size (kbytes): %M\n"
+	"\tAverage resident set size (kbytes): %t\n"
+	"\tMajor (requiring I/O) page faults: %F\n"
+	"\tMinor (reclaiming a frame) page faults: %R\n"
+	"\tVoluntary context switches: %w\n"
+	"\tInvoluntary context switches: %c\n"
+	"\tSwaps: %W\n"
+	"\tFile system inputs: %I\n"
+	"\tFile system outputs: %O\n"
+	"\tSocket messages sent: %s\n"
+	"\tSocket messages received: %r\n"
+	"\tSignals delivered: %k\n"
+	"\tPage size (bytes): %Z\n"
+	"\tExit status: %x";
+
+/* Wait for and fill in data on child process PID.
+   Return 0 on error, 1 if ok.  */
+/* pid_t is short on BSDI, so don't try to promote it.  */
+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. */
+	while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
+		if (caught == -1 && errno != EINTR) {
+			bb_perror_msg("wait");
+			return;
+		}
+	}
+	resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
+}
+
+static void printargv(char *const *argv)
+{
+	const char *fmt = " %s" + 1;
+	do {
+		printf(fmt, *argv);
+		fmt = " %s";
+	} while (*++argv);
+}
+
+/* Return the number of kilobytes corresponding to a number of pages PAGES.
+   (Actually, we use it to convert pages*ticks into kilobytes*ticks.)
+
+   Try to do arithmetic so that the risk of overflow errors is minimized.
+   This is funky since the pagesize could be less than 1K.
+   Note: Some machines express getrusage statistics in terms of K,
+   others in terms of pages.  */
+static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
+{
+	unsigned long tmp;
+
+	/* Conversion.  */
+	if (pages > (LONG_MAX / pagesize)) { /* Could overflow.  */
+		tmp = pages / 1024;     /* Smaller first, */
+		return tmp * pagesize;  /* then larger.  */
+	}
+	/* Could underflow.  */
+	tmp = pages * pagesize; /* Larger first, */
+	return tmp / 1024;      /* then smaller.  */
+}
+
+/* summarize: Report on the system use of a command.
+
+   Print the FMT argument except that `%' sequences
+   have special meaning, and `\n' and `\t' are translated into
+   newline and tab, respectively, and `\\' is translated into `\'.
+
+   The character following a `%' can be:
+   (* means the tcsh time builtin also recognizes it)
+   % == a literal `%'
+   C == command name and arguments
+*  D == average unshared data size in K (ru_idrss+ru_isrss)
+*  E == elapsed real (wall clock) time in [hour:]min:sec
+*  F == major page faults (required physical I/O) (ru_majflt)
+*  I == file system inputs (ru_inblock)
+*  K == average total mem usage (ru_idrss+ru_isrss+ru_ixrss)
+*  M == maximum resident set size in K (ru_maxrss)
+*  O == file system outputs (ru_oublock)
+*  P == percent of CPU this job got (total cpu time / elapsed time)
+*  R == minor page faults (reclaims; no physical I/O involved) (ru_minflt)
+*  S == system (kernel) time (seconds) (ru_stime)
+*  T == system time in [hour:]min:sec
+*  U == user time (seconds) (ru_utime)
+*  u == user time in [hour:]min:sec
+*  W == times swapped out (ru_nswap)
+*  X == average amount of shared text in K (ru_ixrss)
+   Z == page size
+*  c == involuntary context switches (ru_nivcsw)
+   e == elapsed real time in seconds
+*  k == signals delivered (ru_nsignals)
+   p == average unshared stack size in K (ru_isrss)
+*  r == socket messages received (ru_msgrcv)
+*  s == socket messages sent (ru_msgsnd)
+   t == average resident set size in K (ru_idrss)
+*  w == voluntary context switches (ru_nvcsw)
+   x == exit status of command
+
+   Various memory usages are found by converting from page-seconds
+   to kbytes by multiplying by the page size, dividing by 1024,
+   and dividing by elapsed real time.
+
+   FMT is the format string, interpreted as described above.
+   COMMAND is the command and args that are being summarized.
+   RESP is resource information on the command.  */
+
+#ifndef TICKS_PER_SEC
+#define TICKS_PER_SEC 100
+#endif
+
+static void summarize(const char *fmt, char **command, resource_t *resp)
+{
+	unsigned vv_ms;     /* Elapsed virtual (CPU) milliseconds */
+	unsigned cpu_ticks; /* Same, in "CPU ticks" */
+	unsigned pagesize = getpagesize();
+
+	/* Impossible: we do not use WUNTRACED flag in wait()...
+	if (WIFSTOPPED(resp->waitstatus))
+		printf("Command stopped by signal %u\n",
+				WSTOPSIG(resp->waitstatus));
+	else */
+	if (WIFSIGNALED(resp->waitstatus))
+		printf("Command terminated by signal %u\n",
+				WTERMSIG(resp->waitstatus));
+	else if (WIFEXITED(resp->waitstatus) && WEXITSTATUS(resp->waitstatus))
+		printf("Command exited with non-zero status %u\n",
+				WEXITSTATUS(resp->waitstatus));
+
+	vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
+	      + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
+
+#if (1000 / TICKS_PER_SEC) * TICKS_PER_SEC == 1000
+	/* 1000 is exactly divisible by TICKS_PER_SEC (typical) */
+	cpu_ticks = vv_ms / (1000 / TICKS_PER_SEC);
+#else
+	cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
+#endif
+	if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
+
+	while (*fmt) {
+		/* Handle leading literal part */
+		int n = strcspn(fmt, "%\\");
+		if (n) {
+			printf("%.*s", n, fmt);
+			fmt += n;
+			continue;
+		}
+
+		switch (*fmt) {
+#ifdef NOT_NEEDED
+		/* Handle literal char */
+		/* Usually we optimize for size, but there is a limit
+		 * for everything. With this we do a lot of 1-byte writes */
+		default:
+			bb_putchar(*fmt);
+			break;
+#endif
+
+		case '%':
+			switch (*++fmt) {
+#ifdef NOT_NEEDED_YET
+		/* Our format strings do not have these */
+		/* and we do not take format str from user */
+			default:
+				bb_putchar('%');
+				/*FALLTHROUGH*/
+			case '%':
+				if (!*fmt) goto ret;
+				bb_putchar(*fmt);
+				break;
+#endif
+			case 'C':	/* The command that got timed.  */
+				printargv(command);
+				break;
+			case 'D':	/* Average unshared data size.  */
+				printf("%lu",
+					(ptok(pagesize, (UL) resp->ru.ru_idrss) +
+					 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
+				break;
+			case 'E': {	/* Elapsed real (wall clock) time.  */
+				unsigned seconds = resp->elapsed_ms / 1000;
+				if (seconds >= 3600)	/* One hour -> h:m:s.  */
+					printf("%uh %um %02us",
+							seconds / 3600,
+							(seconds % 3600) / 60,
+							seconds % 60);
+				else
+					printf("%um %u.%02us",	/* -> m:s.  */
+							seconds / 60,
+							seconds % 60,
+							(unsigned)(resp->elapsed_ms / 10) % 100);
+				break;
+			}
+			case 'F':	/* Major page faults.  */
+				printf("%lu", resp->ru.ru_majflt);
+				break;
+			case 'I':	/* Inputs.  */
+				printf("%lu", resp->ru.ru_inblock);
+				break;
+			case 'K':	/* Average mem usage == data+stack+text.  */
+				printf("%lu",
+					(ptok(pagesize, (UL) resp->ru.ru_idrss) +
+					 ptok(pagesize, (UL) resp->ru.ru_isrss) +
+					 ptok(pagesize, (UL) resp->ru.ru_ixrss)) / cpu_ticks);
+				break;
+			case 'M':	/* Maximum resident set size.  */
+				printf("%lu", ptok(pagesize, (UL) resp->ru.ru_maxrss));
+				break;
+			case 'O':	/* Outputs.  */
+				printf("%lu", resp->ru.ru_oublock);
+				break;
+			case 'P':	/* Percent of CPU this job got.  */
+				/* % cpu is (total cpu time)/(elapsed time).  */
+				if (resp->elapsed_ms > 0)
+					printf("%u%%", (unsigned)(vv_ms * 100 / resp->elapsed_ms));
+				else
+					printf("?%%");
+				break;
+			case 'R':	/* Minor page faults (reclaims).  */
+				printf("%lu", resp->ru.ru_minflt);
+				break;
+			case 'S':	/* System time.  */
+				printf("%u.%02u",
+						(unsigned)resp->ru.ru_stime.tv_sec,
+						(unsigned)(resp->ru.ru_stime.tv_usec / 10000));
+				break;
+			case 'T':	/* System time.  */
+				if (resp->ru.ru_stime.tv_sec >= 3600) /* One hour -> h:m:s.  */
+					printf("%uh %um %02us",
+							(unsigned)(resp->ru.ru_stime.tv_sec / 3600),
+							(unsigned)(resp->ru.ru_stime.tv_sec % 3600) / 60,
+							(unsigned)(resp->ru.ru_stime.tv_sec % 60));
+				else
+					printf("%um %u.%02us",	/* -> m:s.  */
+							(unsigned)(resp->ru.ru_stime.tv_sec / 60),
+							(unsigned)(resp->ru.ru_stime.tv_sec % 60),
+							(unsigned)(resp->ru.ru_stime.tv_usec / 10000));
+				break;
+			case 'U':	/* User time.  */
+				printf("%u.%02u",
+						(unsigned)resp->ru.ru_utime.tv_sec,
+						(unsigned)(resp->ru.ru_utime.tv_usec / 10000));
+				break;
+			case 'u':	/* User time.  */
+				if (resp->ru.ru_utime.tv_sec >= 3600) /* One hour -> h:m:s.  */
+					printf("%uh %um %02us",
+							(unsigned)(resp->ru.ru_utime.tv_sec / 3600),
+							(unsigned)(resp->ru.ru_utime.tv_sec % 3600) / 60,
+							(unsigned)(resp->ru.ru_utime.tv_sec % 60));
+				else
+					printf("%um %u.%02us",	/* -> m:s.  */
+							(unsigned)(resp->ru.ru_utime.tv_sec / 60),
+							(unsigned)(resp->ru.ru_utime.tv_sec % 60),
+							(unsigned)(resp->ru.ru_utime.tv_usec / 10000));
+				break;
+			case 'W':	/* Times swapped out.  */
+				printf("%lu", resp->ru.ru_nswap);
+				break;
+			case 'X':	/* Average shared text size.  */
+				printf("%lu", ptok(pagesize, (UL) resp->ru.ru_ixrss) / cpu_ticks);
+				break;
+			case 'Z':	/* Page size.  */
+				printf("%u", pagesize);
+				break;
+			case 'c':	/* Involuntary context switches.  */
+				printf("%lu", resp->ru.ru_nivcsw);
+				break;
+			case 'e':	/* Elapsed real time in seconds.  */
+				printf("%u.%02u",
+						(unsigned)resp->elapsed_ms / 1000,
+						(unsigned)(resp->elapsed_ms / 10) % 100);
+				break;
+			case 'k':	/* Signals delivered.  */
+				printf("%lu", resp->ru.ru_nsignals);
+				break;
+			case 'p':	/* Average stack segment.  */
+				printf("%lu", ptok(pagesize, (UL) resp->ru.ru_isrss) / cpu_ticks);
+				break;
+			case 'r':	/* Incoming socket messages received.  */
+				printf("%lu", resp->ru.ru_msgrcv);
+				break;
+			case 's':	/* Outgoing socket messages sent.  */
+				printf("%lu", resp->ru.ru_msgsnd);
+				break;
+			case 't':	/* Average resident set size.  */
+				printf("%lu", ptok(pagesize, (UL) resp->ru.ru_idrss) / cpu_ticks);
+				break;
+			case 'w':	/* Voluntary context switches.  */
+				printf("%lu", resp->ru.ru_nvcsw);
+				break;
+			case 'x':	/* Exit status.  */
+				printf("%u", WEXITSTATUS(resp->waitstatus));
+				break;
+			}
+			break;
+
+#ifdef NOT_NEEDED_YET
+		case '\\':		/* Format escape.  */
+			switch (*++fmt) {
+			default:
+				bb_putchar('\\');
+				/*FALLTHROUGH*/
+			case '\\':
+				if (!*fmt) goto ret;
+				bb_putchar(*fmt);
+				break;
+			case 't':
+				bb_putchar('\t');
+				break;
+			case 'n':
+				bb_putchar('\n');
+				break;
+			}
+			break;
+#endif
+		}
+		++fmt;
+	}
+ /* ret: */
+	bb_putchar('\n');
+}
+
+/* Run command CMD and return statistics on it.
+   Put the statistics in *RESP.  */
+static void run_command(char *const *cmd, resource_t *resp)
+{
+	pid_t pid;
+	void (*interrupt_signal)(int);
+	void (*quit_signal)(int);
+
+	resp->elapsed_ms = monotonic_ms();
+	pid = xvfork();
+	if (pid == 0) {
+		/* Child */
+		BB_EXECVP_or_die((char**)cmd);
+	}
+
+	/* Have signals kill the child but not self (if possible).  */
+//TODO: just block all sigs? and reenable them in the very end in main?
+	interrupt_signal = signal(SIGINT, SIG_IGN);
+	quit_signal = signal(SIGQUIT, SIG_IGN);
+
+	resuse_end(pid, resp);
+
+	/* Re-enable signals.  */
+	signal(SIGINT, interrupt_signal);
+	signal(SIGQUIT, quit_signal);
+}
+
+int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int time_main(int argc UNUSED_PARAM, char **argv)
+{
+	resource_t res;
+	const char *output_format = default_format;
+	int opt;
+
+	opt_complementary = "-1"; /* at least one arg */
+	/* "+": stop on first non-option */
+	opt = getopt32(argv, "+vp");
+	argv += optind;
+	if (opt & 1)
+		output_format = long_format;
+	if (opt & 2)
+		output_format = posix_format;
+
+	run_command(argv, &res);
+
+	/* Cheat. printf's are shorter :) */
+	xdup2(STDERR_FILENO, STDOUT_FILENO);
+	summarize(output_format, argv, &res);
+
+	if (WIFSTOPPED(res.waitstatus))
+		return WSTOPSIG(res.waitstatus);
+	if (WIFSIGNALED(res.waitstatus))
+		return WTERMSIG(res.waitstatus);
+	if (WIFEXITED(res.waitstatus))
+		return WEXITSTATUS(res.waitstatus);
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/miscutils/timeout.c b/busybox-1.19.3/miscutils/timeout.c
new file mode 100644
index 0000000..9d56593
--- /dev/null
+++ b/busybox-1.19.3/miscutils/timeout.c
@@ -0,0 +1,118 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * COPYING NOTES
+ *
+ * timeout.c -- a timeout handler for shell commands
+ *
+ * Copyright (C) 2005-6, Roberto A. Foglietta <me@roberto.foglietta.name>
+ *
+ *   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 of the License.
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * REVISION NOTES:
+ * released 17-11-2005 by Roberto A. Foglietta
+ * talarm   04-12-2005 by Roberto A. Foglietta
+ * modified 05-12-2005 by Roberto A. Foglietta
+ * sizerdct 06-12-2005 by Roberto A. Foglietta
+ * splitszf 12-05-2006 by Roberto A. Foglietta
+ * 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;
+int timeout_main(int argc UNUSED_PARAM, char **argv)
+{
+	int signo;
+	int status;
+	int parent = 0;
+	int timeout = 10;
+	pid_t pid;
+#if !BB_MMU
+	char *sv1, *sv2;
+#endif
+	const char *opt_s = "TERM";
+
+	/* -p option is not documented, it is needed to support NOMMU. */
+
+	/* -t SECONDS; -p PARENT_PID */
+	opt_complementary = "t+" USE_FOR_NOMMU(":p+");
+	/* '+': stop at first non-option */
+	getopt32(argv, "+s:t:" USE_FOR_NOMMU("p:"), &opt_s, &timeout, &parent);
+	/*argv += optind; - no, wait for bb_daemonize_or_rexec! */
+	signo = get_signum(opt_s);
+	if (signo < 0)
+		bb_error_msg_and_die("unknown signal '%s'", opt_s);
+
+	/* We want to create a grandchild which will watch
+	 * and kill the grandparent. Other methods:
+	 * making parent watch child disrupts parent<->child link
+	 * (example: "tcpsvd 0.0.0.0 1234 timeout service_prog" -
+	 * it's better if service_prog is a child of tcpsvd!),
+	 * making child watch parent results in programs having
+	 * unexpected children. */
+
+	if (parent) /* we were re-execed, already grandchild */
+		goto grandchild;
+	if (!argv[optind]) /* no PROG? */
+		bb_show_usage();
+
+#if !BB_MMU
+	sv1 = argv[optind];
+	sv2 = argv[optind + 1];
+#endif
+	pid = xvfork();
+	if (pid == 0) {
+		/* Child: spawn grandchild and exit */
+		parent = getppid();
+#if !BB_MMU
+		argv[optind] = xasprintf("-p%u", parent);
+		argv[optind + 1] = NULL;
+#endif
+		/* NB: exits with nonzero on error: */
+		bb_daemonize_or_rexec(0, argv);
+		/* Here we are grandchild. Sleep, then kill grandparent */
+ grandchild:
+		/* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */
+		while (1) {
+			sleep(1);
+			if (--timeout <= 0)
+				break;
+			if (kill(parent, 0)) {
+				/* process is gone */
+				return EXIT_SUCCESS;
+			}
+		}
+		kill(parent, signo);
+		return EXIT_SUCCESS;
+	}
+
+	/* Parent */
+	wait(&status); /* wait for child to die */
+	/* Did intermediate [v]fork or exec fail? */
+	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+		return EXIT_FAILURE;
+	/* Ok, exec a program as requested */
+	argv += optind;
+#if !BB_MMU
+	argv[0] = sv1;
+	argv[1] = sv2;
+#endif
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/miscutils/ttysize.c b/busybox-1.19.3/miscutils/ttysize.c
new file mode 100644
index 0000000..d2d48d0
--- /dev/null
+++ b/busybox-1.19.3/miscutils/ttysize.c
@@ -0,0 +1,50 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Replacement for "stty size", which is awkward for shell script use.
+ * - Allows to request width, height, or both, in any order.
+ * - Does not complain on error, but returns width 80, height 24.
+ * - Size: less than 200 bytes
+ *
+ * Copyright (C) 2007 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * 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;
+int ttysize_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned w, h;
+	struct winsize wsz;
+
+	w = 80;
+	h = 24;
+	if (!ioctl(0, TIOCGWINSZ, &wsz)) {
+		w = wsz.ws_col;
+		h = wsz.ws_row;
+	}
+
+	if (!argv[1]) {
+		printf("%u %u", w, h);
+	} else {
+		const char *fmt, *arg;
+
+		fmt = "%u %u" + 3; /* "%u" */
+		while ((arg = *++argv) != NULL) {
+			char c = arg[0];
+			if (c == 'w')
+				printf(fmt, w);
+			if (c == 'h')
+				printf(fmt, h);
+			fmt = "%u %u" + 2; /* " %u" */
+		}
+	}
+	bb_putchar('\n');
+	return 0;
+}
diff --git a/busybox-1.19.3/miscutils/ubi_tools.c b/busybox-1.19.3/miscutils/ubi_tools.c
new file mode 100644
index 0000000..2a426db
--- /dev/null
+++ b/busybox-1.19.3/miscutils/ubi_tools.c
@@ -0,0 +1,272 @@
+/* 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"
+#include <mtd/ubi-user.h>
+
+#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)
+
+#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')
+
+//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"
+//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:     "\n	-d UBI_NUM	UBI device number"
+//usage:
+//usage:#define ubimkvol_trivial_usage
+//usage:       "UBI_DEVICE -N NAME -s SIZE"
+//usage:#define ubimkvol_full_usage "\n\n"
+//usage:       "Create UBI volume\n"
+//usage:     "\n	-a ALIGNMENT	Volume alignment (default 1)"
+//usage:     "\n	-n VOLID	Volume ID, if not specified, it"
+//usage:     "\n			will be assigned automatically"
+//usage:     "\n	-N NAME		Volume name"
+//usage:     "\n	-s SIZE		Size in bytes"
+//usage:     "\n	-t TYPE		Volume type (static|dynamic)"
+//usage:
+//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"
+//usage:
+//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 to resize"
+//usage:     "\n	-s SIZE		Size in bytes"
+//usage:
+//usage:#define ubiupdatevol_trivial_usage
+//usage:       "UBI_DEVICE [IMG_FILE]"
+//usage:#define ubiupdatevol_full_usage "\n\n"
+//usage:       "Update UBI volume\n"
+//usage:     "\n	-t	Truncate UBI volume"
+//usage:     "\n	-s SIZE	Bytes in input (if reading stdin)"
+
+
+int ubi_tools_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ubi_tools_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opts;
+	char *ubi_ctrl;
+	//struct stat st;
+	int fd;
+	int mtd_num;
+	int dev_num = UBI_DEV_NUM_AUTO;
+	int vol_id = UBI_VOL_NUM_AUTO;
+	char *vol_name = NULL;
+	int size_bytes;
+	int alignment = 1;
+	char *type = NULL;
+
+	opt_complementary = "-1:m+:d+:n+:s+:a+";
+	opts = getopt32(argv, "m:d:n:N:s:a:t::",
+			&mtd_num, &dev_num, &vol_id,
+			&vol_name, &size_bytes, &alignment, &type
+	);
+	ubi_ctrl = argv[optind];
+
+	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);
+
+	if (do_attach) {
+		struct ubi_attach_req req;
+		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
+	if (do_detach) {
+		if (!(opts & OPTION_D))
+			bb_error_msg_and_die("%s device not specified", "UBI");
+
+		xioctl(fd, UBI_IOCDET, &dev_num);
+	} else
+	if (do_mkvol) {
+		struct ubi_mkvol_req req;
+		int vol_name_len;
+		if (!(opts & OPTION_s))
+			bb_error_msg_and_die("%s size not specified", "UBI");
+		if (!(opts & OPTION_N))
+			bb_error_msg_and_die("%s name not specified", "UBI");
+		vol_name_len = strlen(vol_name);
+		if (vol_name_len > UBI_MAX_VOLUME_NAME)
+			bb_error_msg_and_die("%s volume name too long", "UBI");
+
+		memset(&req, 0, sizeof(req));
+		req.vol_id = vol_id;
+		if ((opts & OPTION_t) && type) {
+			if (type[0] == 's')
+				req.vol_type = UBI_STATIC_VOLUME;
+			else
+				req.vol_type = UBI_DYNAMIC_VOLUME;
+		} else {
+			req.vol_type = UBI_DYNAMIC_VOLUME;
+		}
+		req.alignment = alignment;
+		req.bytes = size_bytes;
+		strncpy(req.name, vol_name, UBI_MAX_VOLUME_NAME);
+		req.name_len = vol_name_len;
+
+		xioctl(fd, UBI_IOCMKVOL, &req);
+	} else
+	if (do_rmvol) {
+		if (!(opts & OPTION_n))
+			bb_error_msg_and_die("%s volume id not specified", "UBI");
+
+		xioctl(fd, UBI_IOCRMVOL, &vol_id);
+	} else
+	if (do_rsvol) {
+		struct ubi_rsvol_req req;
+		if (!(opts & OPTION_s))
+			bb_error_msg_and_die("%s size not specified", "UBI");
+		if (!(opts & OPTION_n))
+			bb_error_msg_and_die("%s volume id not specified", "UBI");
+
+		memset(&req, 0, sizeof(req));
+		req.bytes = size_bytes;
+		req.vol_id = vol_id;
+
+		xioctl(fd, UBI_IOCRSVOL, &req);
+	} else
+	if (do_update) {
+		long long bytes;
+
+		if (opts & OPTION_t) {
+			// truncate the volume by starting an update for size 0
+			bytes = 0;
+			xioctl(fd, UBI_IOCVOLUP, &bytes);
+		}
+		else {
+			struct stat st;
+			char buf[sizeof("/sys/class/ubi/ubi%d_%d/usable_eb_size") + 2 * sizeof(int)*3];
+			int input_fd;
+			unsigned ubinum, volnum;
+			unsigned leb_size;
+			ssize_t len;
+			char *input_data;
+
+			// Make assumption that device not 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("%s volume node not in correct format", "UBI");
+
+			sprintf(buf, "/sys/class/ubi/ubi%u_%u/usable_eb_size", ubinum, volnum);
+			if (open_read_close(buf, buf, sizeof(buf)) <= 0)
+				bb_error_msg_and_die("%s could not get LEB size", "UBI");
+			if (sscanf(buf, "%u", &leb_size) != 1)
+				bb_error_msg_and_die("%s could not get LEB size", "UBI");
+
+			if (opts & OPTION_s) {
+				input_fd = 0;
+			} else {
+				if (!argv[optind+1])
+					bb_show_usage();
+				xstat(argv[optind+1], &st);
+				size_bytes = st.st_size;
+				input_fd = xopen(argv[optind+1], O_RDONLY);
+			}
+
+			bytes = size_bytes;
+			xioctl(fd, UBI_IOCVOLUP, &bytes);
+
+			input_data = xmalloc(leb_size);
+			while ((len = full_read(input_fd, input_data, leb_size)) > 0) {
+				xwrite(fd, input_data, len);
+			}
+			if (len < 0)
+				bb_error_msg_and_die("%s volume update failed", "UBI");
+			if (ENABLE_FEATURE_CLEAN_UP)
+				close(input_fd);
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/volname.c b/busybox-1.19.3/miscutils/volname.c
new file mode 100644
index 0000000..b50e795
--- /dev/null
+++ b/busybox-1.19.3/miscutils/volname.c
@@ -0,0 +1,60 @@
+/*
+ * Reads and displays CD-ROM volume name
+ *
+ * Several people have asked how to read CD volume names so I wrote this
+ * small program to do it.
+ *
+ * usage: volname [<device-file>]
+ *
+ * Copyright (C) 2000-2001 Jeff Tranter (tranter@pobox.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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * 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;
+int volname_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+	char buffer[32];
+	const char *device;
+
+	device = "/dev/cdrom";
+	if (argv[1]) {
+		device = argv[1];
+		if (argv[2])
+			bb_show_usage();
+	}
+
+	fd = xopen(device, O_RDONLY);
+	xlseek(fd, 32808, SEEK_SET);
+	xread(fd, buffer, 32);
+	printf("%32.32s\n", buffer);
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(fd);
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/miscutils/wall.c b/busybox-1.19.3/miscutils/wall.c
new file mode 100644
index 0000000..762f53b
--- /dev/null
+++ b/busybox-1.19.3/miscutils/wall.c
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 source tree.
+ */
+
+//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"
+
+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;
+
+	msg = xmalloc_read(fd, NULL);
+	if (ENABLE_FEATURE_CLEAN_UP && argv[1])
+		close(fd);
+	setutent();
+	while ((ut = getutent()) != NULL) {
+		char *line;
+		if (ut->ut_type != USER_PROCESS)
+			continue;
+		line = concat_path_file("/dev", ut->ut_line);
+		xopen_xwrite_close(line, msg);
+		free(line);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		endutent();
+		free(msg);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/miscutils/watchdog.c b/busybox-1.19.3/miscutils/watchdog.c
new file mode 100644
index 0000000..ee28dc3
--- /dev/null
+++ b/busybox-1.19.3/miscutils/watchdog.c
@@ -0,0 +1,107 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini watchdog implementation for busybox
+ *
+ * Copyright (C) 2003  Paul Mundt <lethal@linux-sh.org>
+ * Copyright (C) 2006  Bernhard Reutner-Fischer <busybox@busybox.net>
+ * Copyright (C) 2008  Darius Augulis <augulis.darius@gmail.com>
+ *
+ * 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"
+
+#define OPT_FOREGROUND  (1 << 0)
+#define OPT_STIMER      (1 << 1)
+#define OPT_HTIMER      (1 << 2)
+
+static void watchdog_shutdown(int sig UNUSED_PARAM)
+{
+	static const char V = 'V';
+
+	write(3, &V, 1);  /* Magic, see watchdog-api.txt in kernel */
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(3);
+	_exit(EXIT_SUCCESS);
+}
+
+int watchdog_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int watchdog_main(int argc, char **argv)
+{
+	static const struct suffix_mult suffixes[] = {
+		{ "ms", 1 },
+		{ "", 1000 },
+		{ "", 0 }
+	};
+
+	unsigned opts;
+	unsigned stimer_duration; /* how often to restart */
+	unsigned htimer_duration = 60000; /* reboots after N ms if not restarted */
+	char *st_arg;
+	char *ht_arg;
+
+	opt_complementary = "=1"; /* must have exactly 1 argument */
+	opts = getopt32(argv, "Ft:T:", &st_arg, &ht_arg);
+
+	/* We need to daemonize *before* opening the watchdog as many drivers
+	 * will only allow one process at a time to do so.  Since daemonizing
+	 * is not perfect (child may run before parent finishes exiting), we
+	 * can't rely on parent exiting before us (let alone *cleanly* releasing
+	 * the watchdog fd -- something else that may not even be allowed).
+	 */
+	if (!(opts & OPT_FOREGROUND))
+		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
+
+	if (opts & OPT_HTIMER)
+		htimer_duration = xatou_sfx(ht_arg, suffixes);
+	stimer_duration = htimer_duration / 2;
+	if (opts & OPT_STIMER)
+		stimer_duration = xatou_sfx(st_arg, suffixes);
+
+	bb_signals(BB_FATAL_SIGS, watchdog_shutdown);
+
+	/* Use known fd # - avoid needing global 'int fd' */
+	xmove_fd(xopen(argv[argc - 1], O_WRONLY), 3);
+
+	/* WDIOC_SETTIMEOUT takes seconds, not milliseconds */
+	htimer_duration = htimer_duration / 1000;
+#ifndef WDIOC_SETTIMEOUT
+# error WDIOC_SETTIMEOUT is not defined, cannot compile watchdog applet
+#else
+# if defined WDIOC_SETOPTIONS && defined WDIOS_ENABLECARD
+	{
+		static const int enable = WDIOS_ENABLECARD;
+		ioctl_or_warn(3, WDIOC_SETOPTIONS, (void*) &enable);
+	}
+# endif
+	ioctl_or_warn(3, WDIOC_SETTIMEOUT, &htimer_duration);
+#endif
+
+#if 0
+	ioctl_or_warn(3, WDIOC_GETTIMEOUT, &htimer_duration);
+	printf("watchdog: SW timer is %dms, HW timer is %ds\n",
+		stimer_duration, htimer_duration * 1000);
+#endif
+
+	while (1) {
+		/*
+		 * Make sure we clear the counter before sleeping,
+		 * as the counter value is undefined at this point -- PFM
+		 */
+		write(3, "", 1); /* write zero byte */
+		usleep(stimer_duration * 1000L);
+	}
+	return EXIT_SUCCESS; /* - not reached, but gcc 4.2.1 is too dumb! */
+}
diff --git a/busybox-1.19.3/modutils/Config.src b/busybox-1.19.3/modutils/Config.src
new file mode 100644
index 0000000..449ac65
--- /dev/null
+++ b/busybox-1.19.3/modutils/Config.src
@@ -0,0 +1,263 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux Module Utilities"
+
+INSERT
+
+config MODPROBE_SMALL
+	bool "Simplified modutils"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Simplified modutils.
+
+	  With this option modprobe does not require modules.dep file
+	  and does not use /etc/modules.conf file.
+	  It scans module files in /lib/modules/`uname -r` and
+	  determines dependencies and module alias names on the fly.
+	  This may make module loading slower, most notably
+	  when one needs to load module by alias (this requires
+	  scanning through module _bodies_).
+
+	  At the first attempt to load a module by alias modprobe
+	  will try to generate modules.dep.bb file in order to speed up
+	  future loads by alias. Failure to do so (read-only /lib/modules,
+	  etc) is not reported, and future modprobes will be slow too.
+
+	  NB: modules.dep.bb file format is not compatible
+	  with modules.dep file as created/used by standard module tools.
+
+	  Additional module parameters can be stored in
+	  /etc/modules/$module_name files.
+
+	  Apart from modprobe, other utilities are also provided:
+	  - insmod is an alias to modprobe
+	  - rmmod is an alias to modprobe -r
+	  - depmod generates modules.dep.bb
+
+	  As of 2008-07, this code is experimental. It is 14kb smaller
+	  than "non-small" modutils.
+
+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.
+
+config FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
+	bool "Skip loading of already loaded modules"
+	default y
+	depends on MODPROBE_SMALL
+	help
+	  Check if the module is already loaded.
+
+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.
+
+config RMMOD
+	bool "rmmod"
+	default n
+	depends on !MODPROBE_SMALL
+	select PLATFORM_LINUX
+	help
+	  rmmod is used to unload specified modules from the kernel.
+
+config LSMOD
+	bool "lsmod"
+	default n
+	depends on !MODPROBE_SMALL
+	select PLATFORM_LINUX
+	help
+	  lsmod is used to display a list of loaded modules.
+
+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.
+	  Increases size somewhat.
+
+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.
+
+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
+	  blacklisted modules. This is useful if you want to prevent your
+	  hardware autodetection scripts to load modules like evdev, frame
+	  buffer drivers etc.
+
+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
+	  for modprobe.
+
+comment "Options common to multiple modutils"
+
+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
+	  to run ancient kernels.
+
+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,
+	  it does not work for compressed modules), module will be read
+	  (and unpacked if needed) into a memory block allocated by malloc.
+
+	  The only case when mmap works but malloc does not is when
+	  you are trying to load a big module on a very memory-constrained
+	  machine. Malloc will momentarily need 2x as much memory as mmap.
+
+	  Choosing N saves about 250 bytes of code (on 32-bit x86).
+
+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.
+
+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
+	  this feature, module symbols will always be added to the kernel symbol
+	  table for proper debugging support. If you are not interested in
+	  Oops messages from kernel modules, say N.
+
+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
+	  memory usage by preventing the need for two copies of the module
+	  being loaded into memory.
+
+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
+	  easier.
+	  If you don't plan to debug kernel modules, you
+	  don't need this option.
+
+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
+	  symbols load map.
+
+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
+	  support request.
+	  This option is required to support GPLONLY modules.
+
+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:
+	    alias pcmcia:m*c*f03fn*pfn*pa*pb*pc*pd* parport_cs
+
+	  and aliases for logical modules names e.g.:
+	    alias padlock_aes aes
+	    alias aes_i586 aes
+	    alias aes_generic aes
+
+	  Say Y if unsure.
+
+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:
+	    alias symbol:usb_sg_init usbcore
+
+	  Say Y if unsure.
+
+config DEFAULT_MODULES_DIR
+	string "Default directory containing modules"
+	default "/lib/modules"
+	depends on DEPMOD || MODPROBE || MODPROBE_SMALL || MODINFO
+	help
+	  Directory that contains kernel modules.
+	  Defaults to "/lib/modules"
+
+config DEFAULT_DEPMOD_FILE
+	string "Default name of modules.dep"
+	default "modules.dep"
+	depends on DEPMOD || MODPROBE || MODPROBE_SMALL || MODINFO
+	help
+	  Filename that contains kernel modules dependencies.
+	  Defaults to "modules.dep"
+
+endmenu
diff --git a/busybox-1.19.3/modutils/Kbuild.src b/busybox-1.19.3/modutils/Kbuild.src
new file mode 100644
index 0000000..1a7ac87
--- /dev/null
+++ b/busybox-1.19.3/modutils/Kbuild.src
@@ -0,0 +1,16 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_MODPROBE_SMALL)      += modprobe-small.o
+lib-$(CONFIG_DEPMOD)              += depmod.o modutils.o
+lib-$(CONFIG_INSMOD)              += insmod.o modutils.o
+lib-$(CONFIG_LSMOD)               += lsmod.o modutils.o
+lib-$(CONFIG_MODPROBE)            += modprobe.o modutils.o
+lib-$(CONFIG_RMMOD)               += rmmod.o modutils.o
+lib-$(CONFIG_FEATURE_2_4_MODULES) += modutils-24.o
diff --git a/busybox-1.19.3/modutils/depmod.c b/busybox-1.19.3/modutils/depmod.c
new file mode 100644
index 0000000..f6c0bf3
--- /dev/null
+++ b/busybox-1.19.3/modutils/depmod.c
@@ -0,0 +1,275 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * depmod - generate modules.dep
+ * Copyright (c) 2008 Bernhard Reutner-Fischer
+ * Copyrihgt (c) 2008 Timo Teras <timo.teras@iki.fi>
+ * Copyright (c) 2008 Vladimir Dronnikov
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//applet:IF_DEPMOD(APPLET(depmod, BB_DIR_SBIN, BB_SUID_DROP))
+
+//usage:#if !ENABLE_MODPROBE_SMALL
+//usage:#define depmod_trivial_usage NOUSAGE_STR
+//usage:#define depmod_full_usage ""
+//usage:#endif
+
+#include "libbb.h"
+#include "modutils.h"
+#include <sys/utsname.h> /* uname() */
+
+/*
+ * Theory of operation:
+ * - iterate over all modules and record their full path
+ * - iterate over all modules looking for "depends=" entries
+ *   for each depends, look through our list of full paths and emit if found
+ */
+
+typedef struct module_info {
+	struct module_info *next;
+	char *name, *modname;
+	llist_t *dependencies;
+	llist_t *aliases;
+	llist_t *symbols;
+	struct module_info *dnext, *dprev;
+} module_info;
+
+static int FAST_FUNC parse_module(const char *fname, struct stat *sb UNUSED_PARAM,
+				  void *data, int depth UNUSED_PARAM)
+{
+	char modname[MODULE_NAME_LEN];
+	module_info **first = (module_info **) data;
+	char *image, *ptr;
+	module_info *info;
+	/* Arbitrary. Was sb->st_size, but that breaks .gz etc */
+	size_t len = (64*1024*1024 - 4096);
+
+	if (strrstr(fname, ".ko") == NULL)
+		return TRUE;
+
+	image = xmalloc_open_zipped_read_close(fname, &len);
+	info = xzalloc(sizeof(*info));
+
+	info->next = *first;
+	*first = info;
+
+	info->dnext = info->dprev = info;
+	info->name = xstrdup(fname + 2); /* skip "./" */
+	info->modname = xstrdup(filename2modname(fname, modname));
+	for (ptr = image; ptr < image + len - 10; ptr++) {
+		if (strncmp(ptr, "depends=", 8) == 0) {
+			char *u;
+
+			ptr += 8;
+			for (u = ptr; *u; u++)
+				if (*u == '-')
+					*u = '_';
+			ptr += string_to_llist(ptr, &info->dependencies, ",");
+		} else if (ENABLE_FEATURE_MODUTILS_ALIAS
+		 && strncmp(ptr, "alias=", 6) == 0
+		) {
+			llist_add_to(&info->aliases, xstrdup(ptr + 6));
+			ptr += strlen(ptr);
+		} else if (ENABLE_FEATURE_MODUTILS_SYMBOLS
+		 && strncmp(ptr, "__ksymtab_", 10) == 0
+		) {
+			ptr += 10;
+			if (strncmp(ptr, "gpl", 3) == 0
+			 || strcmp(ptr, "strings") == 0
+			) {
+				continue;
+			}
+			llist_add_to(&info->symbols, xstrdup(ptr));
+			ptr += strlen(ptr);
+		}
+	}
+	free(image);
+
+	return TRUE;
+}
+
+static module_info *find_module(module_info *modules, const char *modname)
+{
+	module_info *m;
+
+	for (m = modules; m != NULL; m = m->next)
+		if (strcmp(m->modname, modname) == 0)
+			return m;
+	return NULL;
+}
+
+static void order_dep_list(module_info *modules, module_info *start,
+			   llist_t *add)
+{
+	module_info *m;
+	llist_t *n;
+
+	for (n = add; n != NULL; n = n->link) {
+		m = find_module(modules, n->data);
+		if (m == NULL)
+			continue;
+
+		/* unlink current entry */
+		m->dnext->dprev = m->dprev;
+		m->dprev->dnext = m->dnext;
+
+		/* and add it to tail */
+		m->dnext = start;
+		m->dprev = start->dprev;
+		start->dprev->dnext = m;
+		start->dprev = m;
+
+		/* recurse */
+		order_dep_list(modules, start, m->dependencies);
+	}
+}
+
+static void xfreopen_write(const char *file, FILE *f)
+{
+	if (freopen(file, "w", f) == NULL)
+		bb_perror_msg_and_die("can't open '%s'", file);
+}
+
+/* Usage:
+ * [-aAenv] [-C FILE or DIR] [-b BASE] [-F System.map] [VERSION] [MODFILES]...
+ *	-a --all
+ *		Probe all modules. Default if no MODFILES.
+ *	-A --quick
+ *		Check modules.dep's mtime against module files' mtimes.
+ *	-b --basedir BASE
+ *		Use $BASE/lib/modules/VERSION
+ *	-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
+ *		which are not supplied by other modules or kernel.
+ *	-F --filesyms System.map
+ *	-n --dry-run
+ *		Print modules.dep etc to standard output
+ *	-v --verbose
+ *		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: [-rn] [-b BASE] [VERSION] [MODFILES]...
+ * -aAeF are accepted but ignored. -vC are not accepted.
+ */
+enum {
+	//OPT_a = (1 << 0), /* All modules, ignore mods in argv */
+	//OPT_A = (1 << 1), /* Only emit .ko that are newer than modules.dep file */
+	OPT_b = (1 << 2), /* base directory when modules are in staging area */
+	//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_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;
+int depmod_main(int argc UNUSED_PARAM, char **argv)
+{
+	module_info *modules, *m, *dep;
+	const char *moddir_base = "/";
+	char *moddir, *version;
+	struct utsname uts;
+	int tmp;
+
+	getopt32(argv, "aAb:eF:nruqC:", &moddir_base, NULL, NULL);
+	argv += optind;
+
+	/* goto modules location */
+	xchdir(moddir_base);
+
+	/* If a version is provided, then that kernel version's module directory
+	 * is used, rather than the current kernel version (as returned by
+	 * "uname -r").  */
+	if (*argv && sscanf(*argv, "%u.%u.%u", &tmp, &tmp, &tmp) == 3) {
+		version = *argv++;
+	} else {
+		uname(&uts);
+		version = uts.release;
+	}
+	moddir = concat_path_file(&CONFIG_DEFAULT_MODULES_DIR[1], version);
+	xchdir(moddir);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(moddir);
+
+	/* Scan modules */
+	modules = NULL;
+	if (*argv) {
+		do {
+			parse_module(*argv, /*sb (unused):*/ NULL, &modules, 0);
+		} while (*++argv);
+	} else {
+		recursive_action(".", ACTION_RECURSE,
+				 parse_module, NULL, &modules, 0);
+	}
+
+	/* Generate dependency and alias files */
+	if (!(option_mask32 & OPT_n))
+		xfreopen_write(CONFIG_DEFAULT_DEPMOD_FILE, stdout);
+	for (m = modules; m != NULL; m = m->next) {
+		printf("%s:", m->name);
+
+		order_dep_list(modules, m, m->dependencies);
+		while (m->dnext != m) {
+			dep = m->dnext;
+			printf(" %s", dep->name);
+
+			/* unlink current entry */
+			dep->dnext->dprev = dep->dprev;
+			dep->dprev->dnext = dep->dnext;
+			dep->dnext = dep->dprev = dep;
+		}
+		bb_putchar('\n');
+	}
+
+#if ENABLE_FEATURE_MODUTILS_ALIAS
+	if (!(option_mask32 & OPT_n))
+		xfreopen_write("modules.alias", stdout);
+	for (m = modules; m != NULL; m = m->next) {
+		const char *fname = bb_basename(m->name);
+		int fnlen = strchrnul(fname, '.') - fname;
+		while (m->aliases) {
+			/* Last word can well be m->modname instead,
+			 * but depmod from module-init-tools 3.4
+			 * uses module basename, i.e., no s/-/_/g.
+			 * (pathname and .ko.* are still stripped)
+			 * Mimicking that... */
+			printf("alias %s %.*s\n",
+				(char*)llist_pop(&m->aliases),
+				fnlen, fname);
+		}
+	}
+#endif
+#if ENABLE_FEATURE_MODUTILS_SYMBOLS
+	if (!(option_mask32 & OPT_n))
+		xfreopen_write("modules.symbols", stdout);
+	for (m = modules; m != NULL; m = m->next) {
+		const char *fname = bb_basename(m->name);
+		int fnlen = strchrnul(fname, '.') - fname;
+		while (m->symbols) {
+			printf("alias symbol:%s %.*s\n",
+				(char*)llist_pop(&m->symbols),
+				fnlen, fname);
+		}
+	}
+#endif
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		while (modules) {
+			module_info *old = modules;
+			modules = modules->next;
+			free(old->name);
+			free(old->modname);
+			free(old);
+		}
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/modutils/depmod_process.sh b/busybox-1.19.3/modutils/depmod_process.sh
new file mode 100755
index 0000000..f99b091
--- /dev/null
+++ b/busybox-1.19.3/modutils/depmod_process.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Depmod output may be hard to diff.
+# This script sorts dependencies within "xx.ko: yy.ko zz.ko" lines,
+# and sorts all lines too.
+# Usage:
+#
+# [./busybox] depmod -n | ./depmod_process.sh | sort >OUTFILE
+#
+# and then you can diff OUTFILEs. Useful for comparing bbox depmod
+# with module-init-tools depmod and such.
+
+while read -r word rest; do
+    if ! test "${word/*:/}"; then
+	echo -n "$word "
+	echo "$rest" | xargs -n1 | sort | xargs
+    else
+	echo "$word $rest";
+    fi
+done
diff --git a/busybox-1.19.3/modutils/insmod.c b/busybox-1.19.3/modutils/insmod.c
new file mode 100644
index 0000000..887d9f2
--- /dev/null
+++ b/busybox-1.19.3/modutils/insmod.c
@@ -0,0 +1,66 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini insmod implementation for busybox
+ *
+ * Copyright (C) 2008 Timo Teras <timo.teras@iki.fi>
+ *
+ * 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:#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:#endif
+
+int insmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int insmod_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *filename;
+	int rc;
+
+	/* Compat note:
+	 * 2.6 style insmod has no options and required filename
+	 * (not module name - .ko can't be omitted).
+	 * 2.4 style insmod can take module name without .o
+	 * and performs module search in default directories
+	 * or in $MODPATH.
+	 */
+
+	IF_FEATURE_2_4_MODULES(
+		getopt32(argv, INSMOD_OPTS INSMOD_ARGS);
+		argv += optind - 1;
+	);
+
+	filename = *++argv;
+	if (!filename)
+		bb_show_usage();
+
+	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));
+
+	return rc;
+}
diff --git a/busybox-1.19.3/modutils/lsmod.c b/busybox-1.19.3/modutils/lsmod.c
new file mode 100644
index 0000000..3b3c166
--- /dev/null
+++ b/busybox-1.19.3/modutils/lsmod.c
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini lsmod implementation for busybox
+ *
+ * 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 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"
+
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+enum {
+	TAINT_PROPRIETORY_MODULE = (1 << 0),
+	TAINT_FORCED_MODULE      = (1 << 1),
+	TAINT_UNSAFE_SMP         = (1 << 2),
+};
+
+static void check_tainted(void)
+{
+	int tainted = 0;
+	char *buf = xmalloc_open_read_close("/proc/sys/kernel/tainted", NULL);
+	if (buf) {
+		tainted = atoi(buf);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(buf);
+	}
+
+	if (tainted) {
+		printf("    Tainted: %c%c%c\n",
+				tainted & TAINT_PROPRIETORY_MODULE      ? 'P' : 'G',
+				tainted & TAINT_FORCED_MODULE           ? 'F' : ' ',
+				tainted & TAINT_UNSAFE_SMP              ? 'S' : ' ');
+	} else {
+		puts("    Not tainted");
+	}
+}
+#else
+static void check_tainted(void) { putchar('\n'); }
+#endif
+
+int lsmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lsmod_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+#if ENABLE_FEATURE_LSMOD_PRETTY_2_6_OUTPUT
+	char *token[4];
+	parser_t *parser = config_open("/proc/modules");
+	init_unicode();
+
+	printf("%-24sSize  Used by", "Module");
+	check_tainted();
+
+	if (ENABLE_FEATURE_2_4_MODULES
+	 && get_linux_version_code() < KERNEL_VERSION(2,6,0)
+	) {
+		while (config_read(parser, token, 4, 3, "# \t", PARSE_NORMAL)) {
+			if (token[3] != NULL && token[3][0] == '[') {
+				token[3]++;
+				token[3][strlen(token[3])-1] = '\0';
+			} else
+				token[3] = (char *) "";
+# if ENABLE_UNICODE_SUPPORT
+			{
+				uni_stat_t uni_stat;
+				char *uni_name = unicode_conv_to_printable(&uni_stat, token[0]);
+				unsigned pad_len = (uni_stat.unicode_width > 19) ? 0 : 19 - uni_stat.unicode_width;
+				printf("%s%*s %8s %2s %s\n", uni_name, pad_len, "", token[1], token[2], token[3]);
+				free(uni_name);
+			}
+# else
+			printf("%-19s %8s %2s %s\n", token[0], token[1], token[2], token[3]);
+# endif
+		}
+	} else {
+		while (config_read(parser, token, 4, 4, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
+			// 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!
+			if (token[3][0])
+				token[3][strlen(token[3]) - 1] = '\0';
+# if ENABLE_UNICODE_SUPPORT
+			{
+				uni_stat_t uni_stat;
+				char *uni_name = unicode_conv_to_printable(&uni_stat, token[0]);
+				unsigned pad_len = (uni_stat.unicode_width > 19) ? 0 : 19 - uni_stat.unicode_width;
+				printf("%s%*s %8s %2s %s\n", uni_name, pad_len, "", token[1], token[2], token[3]);
+				free(uni_name);
+			}
+# else
+			printf("%-19s %8s %2s %s\n", token[0], token[1], token[2], token[3]);
+# endif
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		config_close(parser);
+#else
+	check_tainted();
+	xprint_and_close_file(xfopen_for_read("/proc/modules"));
+#endif
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/modutils/modinfo.c b/busybox-1.19.3/modutils/modinfo.c
new file mode 100644
index 0000000..410b6fb
--- /dev/null
+++ b/busybox-1.19.3/modutils/modinfo.c
@@ -0,0 +1,166 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * modinfo - retrieve module info
+ * Copyright (c) 2008 Pascal Bellard
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//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:	help
+//config:	  Show information about a Linux Kernel module
+
+#include <fnmatch.h>
+#include <sys/utsname.h> /* uname() */
+#include "libbb.h"
+#include "modutils.h"
+
+
+enum {
+	OPT_TAGS = (1 << 8) - 1,
+	OPT_F = (1 << 8), /* field name */
+	OPT_0 = (1 << 9),  /* \0 as separator */
+};
+
+struct modinfo_env {
+	char *field;
+	int tags;
+};
+
+static int display(const char *data, const char *pattern, int flag)
+{
+	if (flag) {
+		int n = printf("%s:", pattern);
+		while (n++ < 16)
+			bb_putchar(' ');
+	}
+	return printf("%s%c", data, (option_mask32 & OPT_0) ? '\0' : '\n');
+}
+
+static void modinfo(const char *path, const char *version,
+			const struct modinfo_env *env)
+{
+	static const char *const shortcuts[] = {
+		"filename",
+		"description",
+		"author",
+		"license",
+		"vermagic",
+		"parm",
+		"firmware",
+		"depends",
+	};
+	size_t len;
+	int j, length;
+	char *ptr, *the_module;
+	const char *field = env->field;
+	int tags = env->tags;
+
+	if (tags & 1) { /* filename */
+		display(path, shortcuts[0], 1 != tags);
+	}
+
+	len = MAXINT(ssize_t);
+	the_module = xmalloc_open_zipped_read_close(path, &len);
+	if (!the_module) {
+		if (path[0] == '/')
+			return;
+		/* Newer depmod puts relative paths in modules.dep */
+		path = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path);
+		the_module = xmalloc_open_zipped_read_close(path, &len);
+		free((char*)path);
+		if (!the_module)
+			return;
+	}
+
+	if (field)
+		tags |= OPT_F;
+	for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); 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) {
+			ptr = memchr(ptr, *pattern, len - (ptr - (char*)the_module));
+			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);
+			}
+			++ptr;
+		}
+	}
+	free(the_module);
+}
+
+//usage:#define modinfo_trivial_usage
+//usage:       "[-adlp0] [-F keyword] MODULE"
+//usage:#define modinfo_full_usage "\n\n"
+//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'"
+//usage:     "\n	-F keyword	Keyword to look for"
+//usage:     "\n	-0		Separate output with NULs"
+//usage:#define modinfo_example_usage
+//usage:       "$ modinfo -F vermagic loop\n"
+
+int modinfo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int modinfo_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct modinfo_env env;
+	char name[MODULE_NAME_LEN];
+	struct utsname uts;
+	parser_t *parser;
+	char *colon, *tokens[2];
+	unsigned opts;
+	unsigned i;
+
+	env.field = NULL;
+	opt_complementary = "-1"; /* minimum one param */
+	opts = getopt32(argv, "fdalvpF:0", &env.field);
+	env.tags = opts & OPT_TAGS ? opts & OPT_TAGS : OPT_TAGS;
+	argv += optind;
+
+	uname(&uts);
+	parser = config_open2(
+		xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, uts.release, CONFIG_DEFAULT_DEPMOD_FILE),
+		xfopen_for_read
+	);
+
+	while (config_read(parser, tokens, 2, 1, "# \t", PARSE_NORMAL)) {
+		colon = last_char_is(tokens[0], ':');
+		if (colon == NULL)
+			continue;
+		*colon = '\0';
+		filename2modname(tokens[0], name);
+		for (i = 0; argv[i]; i++) {
+			if (fnmatch(argv[i], name, 0) == 0) {
+				modinfo(tokens[0], uts.release, &env);
+				argv[i] = (char *) "";
+			}
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		config_close(parser);
+
+	for (i = 0; argv[i]; i++) {
+		if (argv[i][0]) {
+			modinfo(argv[i], uts.release, &env);
+		}
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/modutils/modprobe-small.c b/busybox-1.19.3/modutils/modprobe-small.c
new file mode 100644
index 0000000..f5b283b
--- /dev/null
+++ b/busybox-1.19.3/modutils/modprobe-small.c
@@ -0,0 +1,872 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * simplified modprobe
+ *
+ * Copyright (c) 2008 Vladimir Dronnikov
+ * Copyright (c) 2008 Bernhard Reutner-Fischer (initial depmod code)
+ *
+ * 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() */
+#include <fnmatch.h>
+
+extern int init_module(void *module, unsigned long len, const char *options);
+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);
+
+
+#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"
+
+enum {
+	OPT_q = (1 << 0), /* be quiet */
+	OPT_r = (1 << 1), /* module removal instead of loading */
+};
+
+typedef struct module_info {
+	char *pathname;
+	char *aliases;
+	char *deps;
+} module_info;
+
+/*
+ * GLOBALS
+ */
+struct globals {
+	module_info *modinfo;
+	char *module_load_options;
+	smallint dep_bb_seen;
+	smallint wrote_dep_bb_ok;
+	unsigned module_count;
+	int module_found_idx;
+	unsigned stringbuf_idx;
+	unsigned stringbuf_size;
+	char *stringbuf; /* some modules have lots of stuff */
+	/* for example, drivers/media/video/saa7134/saa7134.ko */
+	/* therefore having a fixed biggish buffer is not wise */
+};
+#define G (*ptr_to_globals)
+#define modinfo             (G.modinfo            )
+#define dep_bb_seen         (G.dep_bb_seen        )
+#define wrote_dep_bb_ok     (G.wrote_dep_bb_ok    )
+#define module_count        (G.module_count       )
+#define module_found_idx    (G.module_found_idx   )
+#define module_load_options (G.module_load_options)
+#define stringbuf_idx       (G.stringbuf_idx      )
+#define stringbuf_size      (G.stringbuf_size     )
+#define stringbuf           (G.stringbuf          )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+static void append(const char *s)
+{
+	unsigned len = strlen(s);
+	if (stringbuf_idx + len + 15 > stringbuf_size) {
+		stringbuf_size = stringbuf_idx + len + 127;
+		dbg2_error_msg("grow stringbuf to %u", stringbuf_size);
+		stringbuf = xrealloc(stringbuf, stringbuf_size);
+	}
+	memcpy(stringbuf + stringbuf_idx, s, len);
+	stringbuf_idx += len;
+}
+
+static void appendc(char c)
+{
+	/* We appendc() only after append(), + 15 trick in append()
+	 * makes it unnecessary to check for overflow here */
+	stringbuf[stringbuf_idx++] = c;
+}
+
+static void bksp(void)
+{
+	if (stringbuf_idx)
+		stringbuf_idx--;
+}
+
+static void reset_stringbuf(void)
+{
+	stringbuf_idx = 0;
+}
+
+static char* copy_stringbuf(void)
+{
+	char *copy = xzalloc(stringbuf_idx + 1); /* terminating NUL */
+	return memcpy(copy, stringbuf, stringbuf_idx);
+}
+
+static char* find_keyword(char *ptr, size_t len, const char *word)
+{
+	int wlen;
+
+	if (!ptr) /* happens if xmalloc_open_zipped_read_close cannot read it */
+		return NULL;
+
+	wlen = strlen(word);
+	len -= wlen - 1;
+	while ((ssize_t)len > 0) {
+		char *old = ptr;
+		/* search for the first char in word */
+		ptr = memchr(ptr, *word, len);
+		if (ptr == NULL) /* no occurance left, done */
+			break;
+		if (strncmp(ptr, word, wlen) == 0)
+			return ptr + wlen; /* found, return ptr past it */
+		++ptr;
+		len -= (ptr - old);
+	}
+	return NULL;
+}
+
+static void replace(char *s, char what, char with)
+{
+	while (*s) {
+		if (what == *s)
+			*s = with;
+		++s;
+	}
+}
+
+/* Take "word word", return malloced "word",NUL,"word",NUL,NUL */
+static char* str_2_list(const char *str)
+{
+	int len = strlen(str) + 1;
+	char *dst = xmalloc(len + 1);
+
+	dst[len] = '\0';
+	memcpy(dst, str, len);
+//TODO: protect against 2+ spaces: "word  word"
+	replace(dst, ' ', '\0');
+	return dst;
+}
+
+/* We use error numbers in a loose translation... */
+static const char *moderror(int err)
+{
+	switch (err) {
+	case ENOEXEC:
+		return "invalid module format";
+	case ENOENT:
+		return "unknown symbol in module or invalid parameter";
+	case ESRCH:
+		return "module has wrong symbol version";
+	case EINVAL: /* "invalid parameter" */
+		return "unknown symbol in module or invalid parameter"
+		+ sizeof("unknown symbol in module or");
+	default:
+		return strerror(err);
+	}
+}
+
+static int load_module(const char *fname, const char *options)
+{
+#if 1
+	int r;
+	size_t len = MAXINT(ssize_t);
+	char *module_image;
+	dbg1_error_msg("load_module('%s','%s')", fname, options);
+
+	module_image = xmalloc_open_zipped_read_close(fname, &len);
+	r = (!module_image || init_module(module_image, len, options ? options : "") != 0);
+	free(module_image);
+	dbg1_error_msg("load_module:%d", r);
+	return r; /* 0 = success */
+#else
+	/* For testing */
+	dbg1_error_msg("load_module('%s','%s')", fname, options);
+	return 1;
+#endif
+}
+
+static void parse_module(module_info *info, const char *pathname)
+{
+	char *module_image;
+	char *ptr;
+	size_t len;
+	size_t pos;
+	dbg1_error_msg("parse_module('%s')", 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" */
+	reset_stringbuf();
+	pos = 0;
+	while (1) {
+		ptr = find_keyword(module_image + pos, len - pos, "alias=");
+		if (!ptr) {
+			ptr = find_keyword(module_image + pos, len - pos, "__ksymtab_");
+			if (!ptr)
+				break;
+			/* DOCME: __ksymtab_gpl and __ksymtab_strings occur
+			 * in many modules. What do they mean? */
+			if (strcmp(ptr, "gpl") == 0 || strcmp(ptr, "strings") == 0)
+				goto skip;
+			dbg2_error_msg("alias:'symbol:%s'", ptr);
+			append("symbol:");
+		} else {
+			dbg2_error_msg("alias:'%s'", ptr);
+		}
+		append(ptr);
+		appendc(' ');
+ skip:
+		pos = (ptr - module_image);
+	}
+	bksp(); /* remove last ' ' */
+	info->aliases = copy_stringbuf();
+	replace(info->aliases, '-', '_');
+
+	/* "dependency1 depandency2" */
+	reset_stringbuf();
+	ptr = find_keyword(module_image, len, "depends=");
+	if (ptr && *ptr) {
+		replace(ptr, ',', ' ');
+		replace(ptr, '-', '_');
+		dbg2_error_msg("dep:'%s'", ptr);
+		append(ptr);
+	}
+	info->deps = copy_stringbuf();
+
+	free(module_image);
+}
+
+static int pathname_matches_modname(const char *pathname, const char *modname)
+{
+	const char *fname = bb_get_last_path_component_nostrip(pathname);
+	const char *suffix = strrstr(fname, ".ko");
+//TODO: can do without malloc?
+	char *name = xstrndup(fname, suffix - fname);
+	int r;
+	replace(name, '-', '_');
+	r = (strcmp(name, modname) == 0);
+	free(name);
+	return r;
+}
+
+static FAST_FUNC int fileAction(const char *pathname,
+		struct stat *sb UNUSED_PARAM,
+		void *modname_to_match,
+		int depth UNUSED_PARAM)
+{
+	int cur;
+	const char *fname;
+
+	pathname += 2; /* skip "./" */
+	fname = bb_get_last_path_component_nostrip(pathname);
+	if (!strrstr(fname, ".ko")) {
+		dbg1_error_msg("'%s' is not a module", pathname);
+		return TRUE; /* not a module, continue search */
+	}
+
+	cur = module_count++;
+	modinfo = xrealloc_vector(modinfo, 12, cur);
+	modinfo[cur].pathname = xstrdup(pathname);
+	/*modinfo[cur].aliases = NULL; - xrealloc_vector did it */
+	/*modinfo[cur+1].pathname = NULL;*/
+
+	if (!pathname_matches_modname(fname, modname_to_match)) {
+		dbg1_error_msg("'%s' module name doesn't match", pathname);
+		return TRUE; /* module name doesn't match, continue search */
+	}
+
+	dbg1_error_msg("'%s' module name matches", pathname);
+	module_found_idx = cur;
+	parse_module(&modinfo[cur], pathname);
+
+	if (!(option_mask32 & OPT_r)) {
+		if (load_module(pathname, module_load_options) == 0) {
+			/* Load was successful, there is nothing else to do.
+			 * This can happen ONLY for "top-level" module load,
+			 * not a dep, because deps dont do dirscan. */
+			exit(EXIT_SUCCESS);
+		}
+	}
+
+	return TRUE;
+}
+
+static int load_dep_bb(void)
+{
+	char *line;
+	FILE *fp = fopen_for_read(DEPFILE_BB);
+
+	if (!fp)
+		return 0;
+
+	dep_bb_seen = 1;
+	dbg1_error_msg("loading "DEPFILE_BB);
+
+	/* Why? There is a rare scenario: we did not find modprobe.dep.bb,
+	 * we scanned the dir and found no module by name, then we search
+	 * for alias (full scan), and we decided to generate modprobe.dep.bb.
+	 * But we see modprobe.dep.bb.new! Other modprobe is at work!
+	 * We wait and other modprobe renames it to modprobe.dep.bb.
+	 * Now we can use it.
+	 * But we already have modinfo[] filled, and "module_count = 0"
+	 * makes us start anew. Yes, we leak modinfo[].xxx pointers -
+	 * there is not much of data there anyway. */
+	module_count = 0;
+	memset(&modinfo[0], 0, sizeof(modinfo[0]));
+
+	while ((line = xmalloc_fgetline(fp)) != NULL) {
+		char* space;
+		char* linebuf;
+		int cur;
+
+		if (!line[0]) {
+			free(line);
+			continue;
+		}
+		space = strchrnul(line, ' ');
+		cur = module_count++;
+		modinfo = xrealloc_vector(modinfo, 12, cur);
+		/*modinfo[cur+1].pathname = NULL; - xrealloc_vector did it */
+		modinfo[cur].pathname = line; /* we take ownership of malloced block here */
+		if (*space)
+			*space++ = '\0';
+		modinfo[cur].aliases = space;
+		linebuf = xmalloc_fgetline(fp);
+		modinfo[cur].deps = linebuf ? linebuf : xzalloc(1);
+		if (modinfo[cur].deps[0]) {
+			/* deps are not "", so next line must be empty */
+			line = xmalloc_fgetline(fp);
+			/* Refuse to work with damaged config file */
+			if (line && line[0])
+				bb_error_msg_and_die("error in %s at '%s'", DEPFILE_BB, line);
+			free(line);
+		}
+	}
+	return 1;
+}
+
+static int start_dep_bb_writeout(void)
+{
+	int fd;
+
+	/* depmod -n: write result to stdout */
+	if (applet_name[0] == 'd' && (option_mask32 & 1))
+		return STDOUT_FILENO;
+
+	fd = open(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0644);
+	if (fd < 0) {
+		if (errno == EEXIST) {
+			int count = 5 * 20;
+			dbg1_error_msg(DEPFILE_BB".new exists, waiting for "DEPFILE_BB);
+			while (1) {
+				usleep(1000*1000 / 20);
+				if (load_dep_bb()) {
+					dbg1_error_msg(DEPFILE_BB" appeared");
+					return -2; /* magic number */
+				}
+				if (!--count)
+					break;
+			}
+			bb_error_msg("deleting stale %s", DEPFILE_BB".new");
+			fd = open_or_warn(DEPFILE_BB".new", O_WRONLY | O_CREAT | O_TRUNC);
+		}
+	}
+	dbg1_error_msg("opened "DEPFILE_BB".new:%d", fd);
+	return fd;
+}
+
+static void write_out_dep_bb(int fd)
+{
+	int i;
+	FILE *fp;
+
+	/* We want good error reporting. fdprintf is not good enough. */
+	fp = xfdopen_for_write(fd);
+	i = 0;
+	while (modinfo[i].pathname) {
+		fprintf(fp, "%s%s%s\n" "%s%s\n",
+			modinfo[i].pathname, modinfo[i].aliases[0] ? " " : "", modinfo[i].aliases,
+			modinfo[i].deps, modinfo[i].deps[0] ? "\n" : "");
+		i++;
+	}
+	/* Badly formatted depfile is a no-no. Be paranoid. */
+	errno = 0;
+	if (ferror(fp) | fclose(fp)) /* | instead of || is intended */
+		goto err;
+
+	if (fd == STDOUT_FILENO) /* it was depmod -n */
+		goto ok;
+
+	if (rename(DEPFILE_BB".new", DEPFILE_BB) != 0) {
+ err:
+		bb_perror_msg("can't create '%s'", DEPFILE_BB);
+		unlink(DEPFILE_BB".new");
+	} else {
+ ok:
+		wrote_dep_bb_ok = 1;
+		dbg1_error_msg("created "DEPFILE_BB);
+	}
+}
+
+static module_info* find_alias(const char *alias)
+{
+	int i;
+	int dep_bb_fd;
+	module_info *result;
+	dbg1_error_msg("find_alias('%s')", alias);
+
+ try_again:
+	/* First try to find by name (cheaper) */
+	i = 0;
+	while (modinfo[i].pathname) {
+		if (pathname_matches_modname(modinfo[i].pathname, alias)) {
+			dbg1_error_msg("found '%s' in module '%s'",
+					alias, modinfo[i].pathname);
+			if (!modinfo[i].aliases) {
+				parse_module(&modinfo[i], modinfo[i].pathname);
+			}
+			return &modinfo[i];
+		}
+		i++;
+	}
+
+	/* Ok, we definitely have to scan module bodies. This is a good
+	 * moment to generate modprobe.dep.bb, if it does not exist yet */
+	dep_bb_fd = dep_bb_seen ? -1 : start_dep_bb_writeout();
+	if (dep_bb_fd == -2) /* modprobe.dep.bb appeared? */
+		goto try_again;
+
+	/* Scan all module bodies, extract modinfo (it contains aliases) */
+	i = 0;
+	result = NULL;
+	while (modinfo[i].pathname) {
+		char *desc, *s;
+		if (!modinfo[i].aliases) {
+			parse_module(&modinfo[i], modinfo[i].pathname);
+		}
+		if (result) {
+			i++;
+			continue;
+		}
+		/* "alias1 symbol:sym1 alias2 symbol:sym2" */
+		desc = str_2_list(modinfo[i].aliases);
+		/* Does matching substring exist? */
+		for (s = desc; *s; s += strlen(s) + 1) {
+			/* Aliases in module bodies can be defined with
+			 * shell patterns. Example:
+			 * "pci:v000010DEd000000D9sv*sd*bc*sc*i*".
+			 * Plain strcmp() won't catch that */
+			if (fnmatch(s, alias, 0) == 0) {
+				dbg1_error_msg("found alias '%s' in module '%s'",
+						alias, modinfo[i].pathname);
+				result = &modinfo[i];
+				break;
+			}
+		}
+		free(desc);
+		if (result && dep_bb_fd < 0)
+			return result;
+		i++;
+	}
+
+	/* Create module.dep.bb if needed */
+	if (dep_bb_fd >= 0) {
+		write_out_dep_bb(dep_bb_fd);
+	}
+
+	dbg1_error_msg("find_alias '%s' returns %p", alias, result);
+	return result;
+}
+
+#if ENABLE_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
+// TODO: open only once, invent config_rewind()
+static int already_loaded(const char *name)
+{
+	int ret = 0;
+	char *s;
+	parser_t *parser = config_open2("/proc/modules", xfopen_for_read);
+	while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
+		if (strcmp(s, name) == 0) {
+			ret = 1;
+			break;
+		}
+	}
+	config_close(parser);
+	return ret;
+}
+#else
+#define already_loaded(name) is_rmmod
+#endif
+
+/*
+ * Given modules definition and module name (or alias, or symbol)
+ * load/remove the module respecting dependencies.
+ * NB: also called by depmod with bogus name "/",
+ * just in order to force modprobe.dep.bb creation.
+*/
+#if !ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
+#define process_module(a,b) process_module(a)
+#define cmdline_options ""
+#endif
+static void process_module(char *name, const char *cmdline_options)
+{
+	char *s, *deps, *options;
+	module_info *info;
+	int is_rmmod = (option_mask32 & OPT_r) != 0;
+	dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
+
+	replace(name, '-', '_');
+
+	dbg1_error_msg("already_loaded:%d is_rmmod:%d", already_loaded(name), is_rmmod);
+	if (already_loaded(name) != is_rmmod) {
+		dbg1_error_msg("nothing to do for '%s'", name);
+		return;
+	}
+
+	options = NULL;
+	if (!is_rmmod) {
+		char *opt_filename = xasprintf("/etc/modules/%s", name);
+		options = xmalloc_open_read_close(opt_filename, NULL);
+		if (options)
+			replace(options, '\n', ' ');
+#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
+		if (cmdline_options) {
+			/* NB: cmdline_options always have one leading ' '
+			 * (see main()), we remove it here */
+			char *op = xasprintf(options ? "%s %s" : "%s %s" + 3,
+						cmdline_options + 1, options);
+			free(options);
+			options = op;
+		}
+#endif
+		free(opt_filename);
+		module_load_options = options;
+		dbg1_error_msg("process_module('%s'): options:'%s'", name, options);
+	}
+
+	if (!module_count) {
+		/* Scan module directory. This is done only once.
+		 * It will attempt module load, and will exit(EXIT_SUCCESS)
+		 * on success. */
+		module_found_idx = -1;
+		recursive_action(".",
+			ACTION_RECURSE, /* flags */
+			fileAction, /* file action */
+			NULL, /* dir action */
+			name, /* user data */
+			0); /* depth */
+		dbg1_error_msg("dirscan complete");
+		/* Module was not found, or load failed, or is_rmmod */
+		if (module_found_idx >= 0) { /* module was found */
+			info = &modinfo[module_found_idx];
+		} else { /* search for alias, not a plain module name */
+			info = find_alias(name);
+		}
+	} else {
+		info = find_alias(name);
+	}
+
+// Problem here: there can be more than one module
+// for the given alias. For example,
+// "pci:v00008086d00007010sv00000000sd00000000bc01sc01i80" matches
+// ata_piix because it has an alias "pci:v00008086d00007010sv*sd*bc*sc*i*"
+// and ata_generic, it has an alias "alias=pci:v*d*sv*sd*bc01sc01i*"
+// Standard modprobe would load them both.
+// In this code, find_alias() returns only the first matching module.
+
+	/* rmmod? unload it by name */
+	if (is_rmmod) {
+		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 -
+		 * 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." */
+	}
+
+	if (!info) {
+		/* both dirscan and find_alias found nothing */
+		if (!is_rmmod && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
+			bb_error_msg("module '%s' not found", name);
+//TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
+		goto ret;
+	}
+
+	/* Iterate thru dependencies, trying to (un)load them */
+	deps = str_2_list(info->deps);
+	for (s = deps; *s; s += strlen(s) + 1) {
+		//if (strcmp(name, s) != 0) // N.B. do loops exist?
+		dbg1_error_msg("recurse on dep '%s'", s);
+		process_module(s, NULL);
+		dbg1_error_msg("recurse on dep '%s' done", s);
+	}
+	free(deps);
+
+	/* modprobe -> load it */
+	if (!is_rmmod) {
+		if (!options || strstr(options, "blacklist") == NULL) {
+			errno = 0;
+			if (load_module(info->pathname, options) != 0) {
+				if (EEXIST != errno) {
+					bb_error_msg("'%s': %s",
+						info->pathname,
+						moderror(errno));
+				} else {
+					dbg1_error_msg("'%s': %s",
+						info->pathname,
+						moderror(errno));
+				}
+			}
+		} else {
+			dbg1_error_msg("'%s': blacklisted", info->pathname);
+		}
+	}
+ ret:
+	free(options);
+//TODO: return load attempt result from process_module.
+//If dep didn't load ok, continuing makes little sense.
+}
+#undef cmdline_options
+
+
+/* For reference, module-init-tools v3.4 options:
+
+# insmod
+Usage: insmod filename [args]
+
+# rmmod --help
+Usage: rmmod [-fhswvV] modulename ...
+ -f (or --force) forces a module unload, and may crash your
+    machine. This requires the Forced Module Removal option
+    when the kernel was compiled.
+ -h (or --help) prints this help text
+ -s (or --syslog) says use syslog, not stderr
+ -v (or --verbose) enables more messages
+ -V (or --version) prints the version code
+ -w (or --wait) begins module removal even if it is used
+    and will stop new users from accessing the module (so it
+    should eventually fall to zero).
+
+# modprobe
+Usage: modprobe [-v] [-V] [-C config-file] [-n] [-i] [-q] [-b]
+    [-o <modname>] [ --dump-modversions ] <modname> [parameters...]
+modprobe -r [-n] [-i] [-v] <modulename> ...
+modprobe -l -t <dirname> [ -a <modulename> ...]
+
+# depmod --help
+depmod 3.4 -- part of module-init-tools
+depmod -[aA] [-n -e -v -q -V -r -u]
+      [-b basedirectory] [forced_version]
+depmod [-n -e -v -q -r -u] [-F kernelsyms] module1.ko module2.ko ...
+If no arguments (except options) are given, "depmod -a" is assumed.
+depmod will output a dependency list suitable for the modprobe utility.
+Options:
+    -a, --all           Probe all modules
+    -A, --quick         Only does the work if there's a new module
+    -n, --show          Write the dependency file on stdout only
+    -e, --errsyms       Report not supplied symbols
+    -V, --version       Print the release version
+    -v, --verbose       Enable verbose mode
+    -h, --help          Print this usage message
+The following options are useful for people managing distributions:
+    -b basedirectory
+    --basedir basedirectory
+                        Use an image of a module tree
+    -F kernelsyms
+    --filesyms kernelsyms
+                        Use the file instead of the current kernel symbols
+*/
+
+//usage:#if ENABLE_MODPROBE_SMALL
+
+//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:       "	-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
+
+int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int modprobe_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct utsname uts;
+	char applet0 = applet_name[0];
+	IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;)
+
+	/* are we lsmod? -> just dump /proc/modules */
+	if ('l' == applet0) {
+		xprint_and_close_file(xfopen_for_read("/proc/modules"));
+		return EXIT_SUCCESS;
+	}
+
+	INIT_G();
+
+	/* Prevent ugly corner cases with no modules at all */
+	modinfo = xzalloc(sizeof(modinfo[0]));
+
+	if ('i' != applet0) { /* not insmod */
+		/* Goto modules directory */
+		xchdir(CONFIG_DEFAULT_MODULES_DIR);
+	}
+	uname(&uts); /* never fails */
+
+	/* depmod? */
+	if ('d' == applet0) {
+		/* Supported:
+		 * -n: print result to stdout
+		 * -a: process all modules (default)
+		 * optional VERSION parameter
+		 * Ignored:
+		 * -A: do work only if a module is newer than depfile
+		 * -e: report any symbols which a module needs
+		 *  which are not supplied by other modules or the kernel
+		 * -F FILE: System.map (symbols for -e)
+		 * -q, -r, -u: noop?
+		 * Not supported:
+		 * -b BASEDIR: (TODO!) modules are in
+		 *  $BASEDIR/lib/modules/$VERSION
+		 * -v: human readable deps to stdout
+		 * -V: version (don't want to support it - people may depend
+		 *  on it as an indicator of "standard" depmod)
+		 * -h: help (well duh)
+		 * module1.o module2.o parameters (just ignored for now)
+		 */
+		getopt32(argv, "na" "AeF:qru" /* "b:vV", NULL */, NULL);
+		argv += optind;
+		/* if (argv[0] && argv[1]) bb_show_usage(); */
+		/* Goto $VERSION directory */
+		xchdir(argv[0] ? argv[0] : uts.release);
+		/* Force full module scan by asking to find a bogus module.
+		 * This will generate modules.dep.bb as a side effect. */
+		process_module((char*)"/", NULL);
+		return !wrote_dep_bb_ok;
+	}
+
+	/* insmod, modprobe, rmmod require at least one argument */
+	opt_complementary = "-1";
+	/* only -q (quiet) and -r (rmmod),
+	 * the rest are accepted and ignored (compat) */
+	getopt32(argv, "qrfsvw");
+	argv += optind;
+
+	/* are we rmmod? -> simulate modprobe -r */
+	if ('r' == applet0) {
+		option_mask32 |= OPT_r;
+	}
+
+	if ('i' != applet0) { /* not insmod */
+		/* Goto $VERSION directory */
+		xchdir(uts.release);
+	}
+
+#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
+	/* If not rmmod, parse possible module options given on command line.
+	 * insmod/modprobe takes one module name, the rest are parameters. */
+	options = NULL;
+	if ('r' != applet0) {
+		char **arg = argv;
+		while (*++arg) {
+			/* Enclose options in quotes */
+			char *s = options;
+			options = xasprintf("%s \"%s\"", s ? s : "", *arg);
+			free(s);
+			*arg = NULL;
+		}
+	}
+#else
+	if ('r' != applet0)
+		argv[1] = NULL;
+#endif
+
+	if ('i' == applet0) { /* insmod */
+		size_t len;
+		void *map;
+
+		len = MAXINT(ssize_t);
+		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
+		) {
+			bb_error_msg_and_die("can't insert '%s': %s",
+					*argv, moderror(errno));
+		}
+		return 0;
+	}
+
+	/* Try to load modprobe.dep.bb */
+	load_dep_bb();
+
+	/* Load/remove modules.
+	 * Only rmmod loops here, modprobe has only argv[0] */
+	do {
+		process_module(*argv, options);
+	} while (*++argv);
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/modutils/modprobe.c b/busybox-1.19.3/modutils/modprobe.c
new file mode 100644
index 0000000..c1a1828
--- /dev/null
+++ b/busybox-1.19.3/modutils/modprobe.c
@@ -0,0 +1,662 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Modprobe written from scratch for BusyBox
+ *
+ * Copyright (c) 2008 Timo Teras <timo.teras@iki.fi>
+ * Copyright (c) 2008 Vladimir Dronnikov
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//applet:IF_MODPROBE(APPLET(modprobe, BB_DIR_SBIN, BB_SUID_DROP))
+
+#include "libbb.h"
+#include "modutils.h"
+#include <sys/utsname.h>
+#include <fnmatch.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:	"loading). modprobe uses a configuration file to determine what option(s) to\n"
+//usage:	"pass each module it loads.\n"
+//usage:	"\n"
+//usage:	"The configuration file is searched (in this order):\n"
+//usage:	"\n"
+//usage:	"    /etc/modprobe.conf (2.6 only)\n"
+//usage:	"    /etc/modules.conf\n"
+//usage:	"    /etc/conf.modules (deprecated)\n"
+//usage:	"\n"
+//usage:	"They all have the same syntax (see below). If none is present, it is\n"
+//usage:	"_not_ an error; each loaded module is then expected to load without\n"
+//usage:	"options. Once a file is found, the others are tested for.\n"
+//usage:	"\n"
+//usage:	"/etc/modules.conf entry format:\n"
+//usage:	"\n"
+//usage:	"  alias <alias_name> <mod_name>\n"
+//usage:	"    Makes it possible to modprobe alias_name, when there is no such module.\n"
+//usage:	"    It makes sense if your mod_name is long, or you want a more representative\n"
+//usage:	"    name for that module (eg. 'scsi' in place of 'aha7xxx').\n"
+//usage:	"    This makes it also possible to use a different set of options (below) for\n"
+//usage:	"    the module and the alias.\n"
+//usage:	"    A module can be aliased more than once.\n"
+//usage:	"\n"
+//usage:	"  options <mod_name|alias_name> <symbol=value...>\n"
+//usage:	"    When loading module mod_name (or the module aliased by alias_name), pass\n"
+//usage:	"    the \"symbol=value\" pairs as option to that module.\n"
+//usage:	"\n"
+//usage:	"Sample /etc/modules.conf file:\n"
+//usage:	"\n"
+//usage:	"  options tulip irq=3\n"
+//usage:	"  alias tulip tulip2\n"
+//usage:	"  options tulip2 irq=4 io=0x308\n"
+//usage:	"\n"
+//usage:	"Other functionality offered by 'classic' modprobe is not available in\n"
+//usage:	"this implementation.\n"
+//usage:	"\n"
+//usage:	"If module options are present both in the config file, and on the command line,\n"
+//usage:	"then the options from the command line will be passed to the module _after_\n"
+//usage:	"the options from the config file. That way, you can have defaults in the config\n"
+//usage:	"file, and override them for a specific usage from the command line.\n"
+//usage:#define modprobe_example_usage
+//usage:       "(with the above /etc/modules.conf):\n\n"
+//usage:       "$ modprobe tulip\n"
+//usage:       "   will load the module 'tulip' with default option 'irq=3'\n\n"
+//usage:       "$ modprobe tulip irq=5\n"
+//usage:       "   will load the module 'tulip' with option 'irq=5', thus overriding the default\n\n"
+//usage:       "$ modprobe tulip2\n"
+//usage:       "   will load the module 'tulip' with default options 'irq=4 io=0x308',\n"
+//usage:       "   which are the default for alias 'tulip2'\n\n"
+//usage:       "$ modprobe tulip2 irq=8\n"
+//usage:       "   will load the module 'tulip' with default options 'irq=4 io=0x308 irq=8',\n"
+//usage:       "   which are the default for alias 'tulip2' overridden by the option 'irq=8'\n\n"
+//usage:       "   from the command line\n\n"
+//usage:       "$ modprobe tulip2 irq=2 io=0x210\n"
+//usage:       "   will load the module 'tulip' with default options 'irq=4 io=0x308 irq=4 io=0x210',\n"
+//usage:       "   which are the default for alias 'tulip2' overridden by the options 'irq=2 io=0x210'\n\n"
+//usage:       "   from the command line\n"
+//usage:
+//usage:#define modprobe_trivial_usage
+//usage:	"[-alrqvsD" IF_FEATURE_MODPROBE_BLACKLIST("b") "]"
+//usage:	" MODULE [symbol=value]..."
+//usage:#define modprobe_full_usage "\n\n"
+//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_OPTS  "alrD" IF_FEATURE_MODPROBE_BLACKLIST("b")
+/* -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:" IF_FEATURE_MODPROBE_BLACKLIST("b")
+//#define MODPROBE_COMPLEMENTARY "q-v:v-q:l--acr:a--lr:r--al"
+enum {
+	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"
+	// IF_FEATURE_MODPROBE_BLACKLIST(
+	// "use-blacklist\0" No_argument "b"
+	// )
+	;
+#endif
+
+#define MODULE_FLAG_LOADED              0x0001
+#define MODULE_FLAG_NEED_DEPS           0x0002
+/* "was seen in modules.dep": */
+#define MODULE_FLAG_FOUND_IN_MODDEP     0x0004
+#define MODULE_FLAG_BLACKLISTED         0x0008
+
+struct module_entry { /* I'll call it ME. */
+	unsigned flags;
+	char *modname; /* stripped of /path/, .ext and s/-/_/g */
+	const char *probed_name; /* verbatim as seen on cmdline */
+	char *options; /* options from config files */
+	llist_t *realnames; /* strings. if this module is an alias, */
+	/* real module name is one of these. */
+//Can there really be more than one? Example from real kernel?
+	llist_t *deps; /* strings. modules we depend on */
+};
+
+#define DB_HASH_SIZE 256
+
+struct globals {
+	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 (*ptr_to_globals)
+#define INIT_G() do { \
+        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+static int read_config(const char *path);
+
+static char *gather_options_str(char *opts, const char *append)
+{
+	/* Speed-optimized. We call gather_options_str many times. */
+	if (append) {
+		if (opts == NULL) {
+			opts = xstrdup(append);
+		} else {
+			int optlen = strlen(opts);
+			opts = xrealloc(opts, optlen + strlen(append) + 2);
+			sprintf(opts + optlen, " %s", 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);
+
+	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;
+	}
+	if (!create)
+		return NULL;
+
+	e = xzalloc(sizeof(*e));
+	e->modname = xstrdup(modname);
+	llist_add_to(&G.db[hash], e);
+
+	return e;
+}
+static ALWAYS_INLINE struct module_entry *get_or_add_modentry(const char *module)
+{
+	return helper_get_module(module, 1);
+}
+static ALWAYS_INLINE struct module_entry *get_modentry(const char *module)
+{
+	return helper_get_module(module, 0);
+}
+
+static void add_probe(const char *name)
+{
+	struct module_entry *m;
+
+	m = get_or_add_modentry(name);
+	if (!(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS))
+	 && (m->flags & MODULE_FLAG_LOADED)
+	) {
+		DBG("skipping %s, it is already loaded", name);
+		return;
+	}
+
+	DBG("queuing %s", name);
+	m->probed_name = name;
+	m->flags |= MODULE_FLAG_NEED_DEPS;
+	llist_add_to_end(&G.probes, m);
+	G.num_unresolved_deps++;
+	if (ENABLE_FEATURE_MODUTILS_SYMBOLS
+	 && strncmp(m->modname, "symbol:", 7) == 0
+	) {
+		G.need_symbols = 1;
+	}
+}
+
+static int FAST_FUNC config_file_action(const char *filename,
+					struct stat *statbuf UNUSED_PARAM,
+					void *userdata UNUSED_PARAM,
+					int depth UNUSED_PARAM)
+{
+	char *tokens[3];
+	parser_t *p;
+	struct module_entry *m;
+	int rc = TRUE;
+
+	if (bb_basename(filename)[0] == '.')
+		goto error;
+
+	p = config_open2(filename, fopen_for_read);
+	if (p == NULL) {
+		rc = FALSE;
+		goto error;
+	}
+
+	while (config_read(p, tokens, 3, 2, "# \t", PARSE_NORMAL)) {
+//Use index_in_strings?
+		if (strcmp(tokens[0], "alias") == 0) {
+			/* alias <wildcard> <modulename> */
+			llist_t *l;
+			char wildcard[MODULE_NAME_LEN];
+			char *rmod;
+
+			if (tokens[2] == NULL)
+				continue;
+			filename2modname(tokens[1], wildcard);
+
+			for (l = G.probes; l; l = l->link) {
+				m = (struct module_entry *) l->data;
+				if (fnmatch(wildcard, m->modname, 0) != 0)
+					continue;
+				rmod = filename2modname(tokens[2], NULL);
+				llist_add_to(&m->realnames, rmod);
+
+				if (m->flags & MODULE_FLAG_NEED_DEPS) {
+					m->flags &= ~MODULE_FLAG_NEED_DEPS;
+					G.num_unresolved_deps--;
+				}
+
+				m = get_or_add_modentry(rmod);
+				if (!(m->flags & MODULE_FLAG_NEED_DEPS)) {
+					m->flags |= MODULE_FLAG_NEED_DEPS;
+					G.num_unresolved_deps++;
+				}
+			}
+		} else if (strcmp(tokens[0], "options") == 0) {
+			/* options <modulename> <option...> */
+			if (tokens[2] == NULL)
+				continue;
+			m = get_or_add_modentry(tokens[1]);
+			m->options = gather_options_str(m->options, tokens[2]);
+		} else if (strcmp(tokens[0], "include") == 0) {
+			/* include <filename> */
+			read_config(tokens[1]);
+		} else if (ENABLE_FEATURE_MODPROBE_BLACKLIST
+		 && strcmp(tokens[0], "blacklist") == 0
+		) {
+			/* blacklist <modulename> */
+			get_or_add_modentry(tokens[1])->flags |= MODULE_FLAG_BLACKLISTED;
+		}
+	}
+	config_close(p);
+ error:
+	return rc;
+}
+
+static int read_config(const char *path)
+{
+	return recursive_action(path, ACTION_RECURSE | ACTION_QUIET,
+				config_file_action, NULL, NULL, 1);
+}
+
+static const char *humanly_readable_name(struct module_entry *m)
+{
+	/* probed_name may be NULL. modname always exists. */
+	return m->probed_name ? m->probed_name : m->modname;
+}
+
+static char *parse_and_add_kcmdline_module_options(char *options, const char *modulename)
+{
+	char *kcmdline_buf;
+	char *kcmdline;
+	char *kptr;
+	int len;
+
+	kcmdline_buf = xmalloc_open_read_close("/proc/cmdline", NULL);
+	if (!kcmdline_buf)
+		return options;
+
+	kcmdline = kcmdline_buf;
+	len = strlen(modulename);
+	while ((kptr = strsep(&kcmdline, "\n\t ")) != NULL) {
+		if (strncmp(modulename, kptr, len) != 0)
+			continue;
+		kptr += len;
+		if (*kptr != '.')
+			continue;
+		/* It is "modulename.xxxx" */
+		kptr++;
+		if (strchr(kptr, '=') != NULL) {
+			/* It is "modulename.opt=[val]" */
+			options = gather_options_str(options, kptr);
+		}
+	}
+	free(kcmdline_buf);
+
+	return options;
+}
+
+/* Return: similar to bb_init_module:
+ * 0 on success,
+ * -errno on open/read error,
+ * errno on init_module() error
+ */
+/* NB: INSMOD_OPT_SILENT bit suppresses ONLY non-existent modules,
+ * not deleted ones (those are still listed in modules.dep).
+ * module-init-tools version 3.4:
+ * # modprobe bogus
+ * FATAL: Module bogus not found. [exitcode 1]
+ * # modprobe -q bogus            [silent, exitcode still 1]
+ * but:
+ * # rm kernel/drivers/net/dummy.ko
+ * # modprobe -q dummy
+ * FATAL: Could not open '/lib/modules/xxx/kernel/drivers/net/dummy.ko': No such file or directory
+ * [exitcode 1]
+ */
+static int do_modprobe(struct module_entry *m)
+{
+	int rc, first;
+
+	if (!(m->flags & MODULE_FLAG_FOUND_IN_MODDEP)) {
+		if (!(option_mask32 & INSMOD_OPT_SILENT))
+			bb_error_msg("module %s not found in modules.dep",
+				humanly_readable_name(m));
+		return -ENOENT;
+	}
+	DBG("do_modprob'ing %s", m->modname);
+
+	if (!(option_mask32 & OPT_REMOVE))
+		m->deps = llist_rev(m->deps);
+
+	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 & OPT_REMOVE) {
+			/* modprobe -r */
+			if (m2->flags & MODULE_FLAG_LOADED) {
+				rc = bb_delete_module(m2->modname, O_EXCL);
+				if (rc) {
+					if (first) {
+						bb_error_msg("can't unload module %s: %s",
+							humanly_readable_name(m2),
+							moderror(rc));
+						break;
+					}
+				} else {
+					m2->flags &= ~MODULE_FLAG_LOADED;
+				}
+			}
+			/* do not error out if *deps* fail to unload */
+			first = 0;
+			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)
+			rc = 0;
+		free(options);
+		if (rc) {
+			bb_error_msg("can't load module %s (%s): %s",
+				humanly_readable_name(m2),
+				fn,
+				moderror(rc)
+			);
+			break;
+		}
+		m2->flags |= MODULE_FLAG_LOADED;
+	}
+
+	return rc;
+}
+
+static void load_modules_dep(void)
+{
+	struct module_entry *m;
+	char *colon, *tokens[2];
+	parser_t *p;
+
+	/* Modprobe does not work at all without modules.dep,
+	 * even if the full module name is given. Returning error here
+	 * was making us later confuse user with this message:
+	 * "module /full/path/to/existing/file/module.ko not found".
+	 * It's better to die immediately, with good message.
+	 * xfopen_for_read provides that. */
+	p = config_open2(CONFIG_DEFAULT_DEPMOD_FILE, xfopen_for_read);
+
+	while (G.num_unresolved_deps
+	 && config_read(p, tokens, 2, 1, "# \t", PARSE_NORMAL)
+	) {
+		colon = last_char_is(tokens[0], ':');
+		if (colon == NULL)
+			continue;
+		*colon = 0;
+
+		m = get_modentry(tokens[0]);
+		if (m == NULL)
+			continue;
+
+		/* Optimization... */
+		if ((m->flags & MODULE_FLAG_LOADED)
+		 && !(option_mask32 & (OPT_REMOVE | OPT_SHOW_DEPS))
+		) {
+			DBG("skip deps of %s, it's already loaded", tokens[0]);
+			continue;
+		}
+
+		m->flags |= MODULE_FLAG_FOUND_IN_MODDEP;
+		if ((m->flags & MODULE_FLAG_NEED_DEPS) && (m->deps == NULL)) {
+			G.num_unresolved_deps--;
+			llist_add_to(&m->deps, xstrdup(tokens[0]));
+			if (tokens[1])
+				string_to_llist(tokens[1], &m->deps, " \t");
+		} else
+			DBG("skipping dep line");
+	}
+	config_close(p);
+}
+
+int modprobe_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int modprobe_main(int argc UNUSED_PARAM, char **argv)
+{
+	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(&G.uts);
+	xchdir(G.uts.release);
+
+	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)
+				continue;
+			*colon = '\0';
+			filename2modname(tokens[0], name);
+			if (!argv[0])
+				puts(tokens[0]);
+			else {
+				for (i = 0; argv[i]; i++) {
+					if (fnmatch(argv[i], name, 0) == 0) {
+						puts(tokens[0]);
+					}
+				}
+			}
+		}
+		return EXIT_SUCCESS;
+	}
+
+	/* Yes, for some reason -l ignores -s... */
+	if (opt & INSMOD_OPT_SYSLOG)
+		logmode = LOGMODE_SYSLOG;
+
+	if (!argv[0]) {
+		if (opt & OPT_REMOVE) {
+			/* "modprobe -r" (w/o params).
+			 * "If name is NULL, all unused modules marked
+			 * autoclean will be removed".
+			 */
+			if (bb_delete_module(NULL, O_NONBLOCK | O_EXCL) != 0)
+				bb_perror_msg_and_die("rmmod");
+		}
+		return EXIT_SUCCESS;
+	}
+
+	/* Retrieve module names of already loaded modules */
+	{
+		char *s;
+		parser_t *parser = config_open2("/proc/modules", fopen_for_read);
+		while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY))
+			get_or_add_modentry(s)->flags |= MODULE_FLAG_LOADED;
+		config_close(parser);
+	}
+
+	if (opt & (OPT_INSERT_ALL | OPT_REMOVE)) {
+		/* Each argument is a module name */
+		do {
+			DBG("adding module %s", *argv);
+			add_probe(*argv++);
+		} while (*argv);
+	} else {
+		/* 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, /*quote_spaces:*/ 1);
+	}
+
+	/* Happens if all requested modules are already loaded */
+	if (G.probes == NULL)
+		return EXIT_SUCCESS;
+
+	read_config("/etc/modprobe.conf");
+	read_config("/etc/modprobe.d");
+	if (ENABLE_FEATURE_MODUTILS_SYMBOLS && G.need_symbols)
+		read_config("modules.symbols");
+	load_modules_dep();
+	if (ENABLE_FEATURE_MODUTILS_ALIAS && G.num_unresolved_deps) {
+		read_config("modules.alias");
+		load_modules_dep();
+	}
+
+	rc = 0;
+	while ((me = llist_pop(&G.probes)) != NULL) {
+		if (me->realnames == NULL) {
+			DBG("probing by module name");
+			/* This is not an alias. Literal names are blacklisted
+			 * only if '-b' is given.
+			 */
+			if (!(opt & OPT_BLACKLIST)
+			 || !(me->flags & MODULE_FLAG_BLACKLISTED)
+			) {
+				rc |= do_modprobe(me);
+			}
+			continue;
+		}
+
+		/* Probe all real names for the alias */
+		do {
+			char *realname = llist_pop(&me->realnames);
+			struct module_entry *m2;
+
+			DBG("probing alias %s by realname %s", me->modname, realname);
+			m2 = get_or_add_modentry(realname);
+			if (!(m2->flags & MODULE_FLAG_BLACKLISTED)
+			 && (!(m2->flags & MODULE_FLAG_LOADED)
+			    || (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
+//with alias name included, not just module name alias resolves to.
+				rc |= do_modprobe(m2);
+			}
+			free(realname);
+		} while (me->realnames != NULL);
+	}
+
+	return (rc != 0);
+}
diff --git a/busybox-1.19.3/modutils/modutils-24.c b/busybox-1.19.3/modutils/modutils-24.c
new file mode 100644
index 0000000..bbc54e3
--- /dev/null
+++ b/busybox-1.19.3/modutils/modutils-24.c
@@ -0,0 +1,3949 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini insmod implementation for busybox
+ *
+ * This version of insmod supports ARM, CRIS, H8/300, x86, ia64, x86_64,
+ * m68k, MIPS, PowerPC, S390, SH3/4/5, Sparc, v850e, and x86_64.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * and Ron Alder <alder@lineo.com>
+ *
+ * Rodney Radford <rradford@mindspring.com> 17-Aug-2004.
+ *   Added x86_64 support.
+ *
+ * Miles Bader <miles@gnu.org> added NEC V850E support.
+ *
+ * Modified by Bryan Rittmeyer <bryan@ixiacom.com> to support SH4
+ * and (theoretically) SH3. I have only tested SH4 in little endian mode.
+ *
+ * Modified by Alcove, Julien Gaulmin <julien.gaulmin@alcove.fr> and
+ * Nicolas Ferre <nicolas.ferre@alcove.fr> to support ARM7TDMI.  Only
+ * very minor changes required to also work with StrongArm and presumably
+ * all ARM based systems.
+ *
+ * Yoshinori Sato <ysato@users.sourceforge.jp> 19-May-2004.
+ *   added Renesas H8/300 support.
+ *
+ * Paul Mundt <lethal@linux-sh.org> 08-Aug-2003.
+ *   Integrated support for sh64 (SH-5), from preliminary modutils
+ *   patches from Benedict Gaster <benedict.gaster@superh.com>.
+ *   Currently limited to support for 32bit ABI.
+ *
+ * Magnus Damm <damm@opensource.se> 22-May-2002.
+ *   The plt and got code are now using the same structs.
+ *   Added generic linked list code to fully support PowerPC.
+ *   Replaced the mess in arch_apply_relocation() with architecture blocks.
+ *   The arch_create_got() function got cleaned up with architecture blocks.
+ *   These blocks should be easy maintain and sync with obj_xxx.c in modutils.
+ *
+ * Magnus Damm <damm@opensource.se> added PowerPC support 20-Feb-2001.
+ *   PowerPC specific code stolen from modutils-2.3.16,
+ *   written by Paul Mackerras, Copyright 1996, 1997 Linux International.
+ *   I've only tested the code on mpc8xx platforms in big-endian mode.
+ *   Did some cleanup and added USE_xxx_ENTRIES...
+ *
+ * Quinn Jensen <jensenq@lineo.com> added MIPS support 23-Feb-2001.
+ *   based on modutils-2.4.2
+ *   MIPS specific support for Elf loading and relocation.
+ *   Copyright 1996, 1997 Linux International.
+ *   Contributed by Ralf Baechle <ralf@gnu.ai.mit.edu>
+ *
+ * Based almost entirely on the Linux modutils-2.3.11 implementation.
+ *   Copyright 1996, 1997 Linux International.
+ *   New implementation contributed by Richard Henderson <rth@tamu.edu>
+ *   Based on original work by Bjorn Ekwall <bj0rn@blox.se>
+ *   Restructured (and partly rewritten) by:
+ *   Björn Ekwall <bj0rn@blox.se> February 1999
+ *
+ * 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 LOADBITS 0
+#else
+#define LOADBITS 1
+#endif
+
+/* Alpha */
+#if defined(__alpha__)
+#define MATCH_MACHINE(x) (x == EM_ALPHA)
+#define SHT_RELM       SHT_RELA
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#endif
+
+/* ARM support */
+#if defined(__arm__)
+#define MATCH_MACHINE(x) (x == EM_ARM)
+#define SHT_RELM	SHT_REL
+#define Elf32_RelM	Elf32_Rel
+#define ELFCLASSM	ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#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)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#endif
+
+/* CRIS */
+#if defined(__cris__)
+#define MATCH_MACHINE(x) (x == EM_CRIS)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#ifndef EM_CRIS
+#define EM_CRIS 76
+#define R_CRIS_NONE 0
+#define R_CRIS_32   3
+#endif
+#endif
+
+/* H8/300 */
+#if defined(__H8300H__) || defined(__H8300S__)
+#define MATCH_MACHINE(x) (x == EM_H8_300)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#define USE_SINGLE
+#define SYMBOL_PREFIX	"_"
+#endif
+
+/* PA-RISC / HP-PA */
+#if defined(__hppa__)
+#define MATCH_MACHINE(x) (x == EM_PARISC)
+#define SHT_RELM       SHT_RELA
+#if defined(__LP64__)
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#else
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+#endif
+
+/* x86 */
+#if defined(__i386__)
+#ifndef EM_486
+#define MATCH_MACHINE(x) (x == EM_386)
+#else
+#define MATCH_MACHINE(x) (x == EM_386 || x == EM_486)
+#endif
+#define SHT_RELM	SHT_REL
+#define Elf32_RelM	Elf32_Rel
+#define ELFCLASSM	ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+#endif
+
+/* IA64, aka Itanium */
+#if defined(__ia64__)
+#define MATCH_MACHINE(x) (x == EM_IA_64)
+#define SHT_RELM       SHT_RELA
+#define Elf64_RelM     Elf64_Rela
+#define ELFCLASSM      ELFCLASS64
+#endif
+
+/* m68k */
+#if defined(__mc68000__)
+#define MATCH_MACHINE(x) (x == EM_68K)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+#endif
+
+/* Microblaze */
+#if defined(__microblaze__)
+#define USE_SINGLE
+#include <linux/elf-em.h>
+#define MATCH_MACHINE(x) (x == EM_XILINX_MICROBLAZE)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#endif
+
+/* MIPS */
+#if defined(__mips__)
+#define MATCH_MACHINE(x) (x == EM_MIPS || x == EM_MIPS_RS3_LE)
+#define SHT_RELM	SHT_REL
+#define Elf32_RelM	Elf32_Rel
+#define ELFCLASSM	ELFCLASS32
+/* Account for ELF spec changes.  */
+#ifndef EM_MIPS_RS3_LE
+#ifdef EM_MIPS_RS4_BE
+#define EM_MIPS_RS3_LE	EM_MIPS_RS4_BE
+#else
+#define EM_MIPS_RS3_LE	10
+#endif
+#endif /* !EM_MIPS_RS3_LE */
+#define ARCHDATAM       "__dbe_table"
+#endif
+
+/* Nios II */
+#if defined(__nios2__)
+#define MATCH_MACHINE(x) (x == EM_ALTERA_NIOS2)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#endif
+
+/* PowerPC */
+#if defined(__powerpc64__)
+#define MATCH_MACHINE(x) (x == EM_PPC64)
+#define SHT_RELM	SHT_RELA
+#define Elf64_RelM	Elf64_Rela
+#define ELFCLASSM	ELFCLASS64
+#elif defined(__powerpc__)
+#define MATCH_MACHINE(x) (x == EM_PPC)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 16
+#define USE_PLT_LIST
+#define LIST_ARCHTYPE ElfW(Addr)
+#define USE_LIST
+#define ARCHDATAM       "__ftr_fixup"
+#endif
+
+/* S390 */
+#if defined(__s390__)
+#define MATCH_MACHINE(x) (x == EM_S390)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#endif
+
+/* SuperH */
+#if defined(__sh__)
+#define MATCH_MACHINE(x) (x == EM_SH)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 4
+#define USE_SINGLE
+/* the SH changes have only been tested in =little endian= mode */
+/* I'm not sure about big endian, so let's warn: */
+#if defined(__sh__) && BB_BIG_ENDIAN
+# error insmod.c may require changes for use on big endian SH
+#endif
+/* it may or may not work on the SH1/SH2... Error on those also */
+#if ((!(defined(__SH3__) || defined(__SH4__) || defined(__SH5__)))) && (defined(__sh__))
+#error insmod.c may require changes for SH1 or SH2 use
+#endif
+#endif
+
+/* Sparc */
+#if defined(__sparc__)
+#define MATCH_MACHINE(x) (x == EM_SPARC)
+#define SHT_RELM       SHT_RELA
+#define Elf32_RelM     Elf32_Rela
+#define ELFCLASSM      ELFCLASS32
+#endif
+
+/* v850e */
+#if defined(__v850e__)
+#define MATCH_MACHINE(x) ((x) == EM_V850 || (x) == EM_CYGNUS_V850)
+#define SHT_RELM	SHT_RELA
+#define Elf32_RelM	Elf32_Rela
+#define ELFCLASSM	ELFCLASS32
+#define USE_PLT_ENTRIES
+#define PLT_ENTRY_SIZE 8
+#define USE_SINGLE
+#ifndef EM_CYGNUS_V850	/* grumble */
+#define EM_CYGNUS_V850	0x9080
+#endif
+#define SYMBOL_PREFIX	"_"
+#endif
+
+/* X86_64  */
+#if defined(__x86_64__)
+#define MATCH_MACHINE(x) (x == EM_X86_64)
+#define SHT_RELM	SHT_RELA
+#define USE_GOT_ENTRIES
+#define GOT_ENTRY_SIZE 8
+#define USE_SINGLE
+#define Elf64_RelM	Elf64_Rela
+#define ELFCLASSM	ELFCLASS64
+#endif
+
+#ifndef SHT_RELM
+#error Sorry, but insmod.c does not yet support this architecture...
+#endif
+
+
+//----------------------------------------------------------------------------
+//--------modutils module.h, lines 45-242
+//----------------------------------------------------------------------------
+
+/* Definitions for the Linux module syscall interface.
+   Copyright 1996, 1997 Linux International.
+
+   Contributed by Richard Henderson <rth@tamu.edu>
+
+   This file is part of the Linux modutils.
+
+   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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+#ifndef MODUTILS_MODULE_H
+
+/*======================================================================*/
+/* For sizeof() which are related to the module platform and not to the
+   environment isnmod is running in, use sizeof_xx instead of sizeof(xx).  */
+
+#define tgt_sizeof_char		sizeof(char)
+#define tgt_sizeof_short	sizeof(short)
+#define tgt_sizeof_int		sizeof(int)
+#define tgt_sizeof_long		sizeof(long)
+#define tgt_sizeof_char_p	sizeof(char *)
+#define tgt_sizeof_void_p	sizeof(void *)
+#define tgt_long		long
+
+#if defined(__sparc__) && !defined(__sparc_v9__) && defined(ARCH_sparc64)
+#undef tgt_sizeof_long
+#undef tgt_sizeof_char_p
+#undef tgt_sizeof_void_p
+#undef tgt_long
+enum {
+	tgt_sizeof_long = 8,
+	tgt_sizeof_char_p = 8,
+	tgt_sizeof_void_p = 8
+};
+#define tgt_long		long long
+#endif
+
+/*======================================================================*/
+/* The structures used in Linux 2.1.  */
+
+/* Note: new_module_symbol does not use tgt_long intentionally */
+struct new_module_symbol {
+	unsigned long value;
+	unsigned long name;
+};
+
+struct new_module_persist;
+
+struct new_module_ref {
+	unsigned tgt_long dep;		/* kernel addresses */
+	unsigned tgt_long ref;
+	unsigned tgt_long next_ref;
+};
+
+struct new_module {
+	unsigned tgt_long size_of_struct;	/* == sizeof(module) */
+	unsigned tgt_long next;
+	unsigned tgt_long name;
+	unsigned tgt_long size;
+
+	tgt_long usecount;
+	unsigned tgt_long flags;		/* AUTOCLEAN et al */
+
+	unsigned nsyms;
+	unsigned ndeps;
+
+	unsigned tgt_long syms;
+	unsigned tgt_long deps;
+	unsigned tgt_long refs;
+	unsigned tgt_long init;
+	unsigned tgt_long cleanup;
+	unsigned tgt_long ex_table_start;
+	unsigned tgt_long ex_table_end;
+#ifdef __alpha__
+	unsigned tgt_long gp;
+#endif
+	/* Everything after here is extension.  */
+	unsigned tgt_long persist_start;
+	unsigned tgt_long persist_end;
+	unsigned tgt_long can_unload;
+	unsigned tgt_long runsize;
+	const char *kallsyms_start;     /* All symbols for kernel debugging */
+	const char *kallsyms_end;
+	const char *archdata_start;     /* arch specific data for module */
+	const char *archdata_end;
+	const char *kernel_data;        /* Reserved for kernel internal use */
+};
+
+#ifdef ARCHDATAM
+#define ARCHDATA_SEC_NAME ARCHDATAM
+#else
+#define ARCHDATA_SEC_NAME "__archdata"
+#endif
+#define KALLSYMS_SEC_NAME "__kallsyms"
+
+
+struct new_module_info {
+	unsigned long addr;
+	unsigned long size;
+	unsigned long flags;
+	long usecount;
+};
+
+/* Bits of module.flags.  */
+enum {
+	NEW_MOD_RUNNING = 1,
+	NEW_MOD_DELETED = 2,
+	NEW_MOD_AUTOCLEAN = 4,
+	NEW_MOD_VISITED = 8,
+	NEW_MOD_USED_ONCE = 16
+};
+
+int init_module(const char *name, const struct new_module *);
+int query_module(const char *name, int which, void *buf,
+		size_t bufsize, size_t *ret);
+
+/* Values for query_module's which.  */
+enum {
+	QM_MODULES = 1,
+	QM_DEPS = 2,
+	QM_REFS = 3,
+	QM_SYMBOLS = 4,
+	QM_INFO = 5
+};
+
+/*======================================================================*/
+/* The system calls unchanged between 2.0 and 2.1.  */
+
+unsigned long create_module(const char *, size_t);
+int delete_module(const char *module, unsigned int flags);
+
+
+#endif /* module.h */
+
+//----------------------------------------------------------------------------
+//--------end of modutils module.h
+//----------------------------------------------------------------------------
+
+
+
+//----------------------------------------------------------------------------
+//--------modutils obj.h, lines 253-462
+//----------------------------------------------------------------------------
+
+/* Elf object file loading and relocation routines.
+   Copyright 1996, 1997 Linux International.
+
+   Contributed by Richard Henderson <rth@tamu.edu>
+
+   This file is part of the Linux modutils.
+
+   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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+
+#ifndef MODUTILS_OBJ_H
+
+/* The relocatable object is manipulated using elfin types.  */
+
+#include <elf.h>
+#include <endian.h>
+
+#ifndef ElfW
+# if ELFCLASSM == ELFCLASS32
+#  define ElfW(x)  Elf32_ ## x
+#  define ELFW(x)  ELF32_ ## x
+# else
+#  define ElfW(x)  Elf64_ ## x
+#  define ELFW(x)  ELF64_ ## x
+# endif
+#endif
+
+/* For some reason this is missing from some ancient C libraries....  */
+#ifndef ELF32_ST_INFO
+# define ELF32_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
+#endif
+
+#ifndef ELF64_ST_INFO
+# define ELF64_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
+#endif
+
+#define ELF_ST_BIND(info) ELFW(ST_BIND)(info)
+#define ELF_ST_TYPE(info) ELFW(ST_TYPE)(info)
+#define ELF_ST_INFO(bind, type) ELFW(ST_INFO)(bind, type)
+#define ELF_R_TYPE(val) ELFW(R_TYPE)(val)
+#define ELF_R_SYM(val) ELFW(R_SYM)(val)
+
+struct obj_string_patch;
+struct obj_symbol_patch;
+
+struct obj_section {
+	ElfW(Shdr) header;
+	const char *name;
+	char *contents;
+	struct obj_section *load_next;
+	int idx;
+};
+
+struct obj_symbol {
+	struct obj_symbol *next;	/* hash table link */
+	const char *name;
+	unsigned long value;
+	unsigned long size;
+	int secidx;			/* the defining section index/module */
+	int info;
+	int ksymidx;			/* for export to the kernel symtab */
+	int referenced;		/* actually used in the link */
+};
+
+/* Hardcode the hash table size.  We shouldn't be needing so many
+   symbols that we begin to degrade performance, and we get a big win
+   by giving the compiler a constant divisor.  */
+
+#define HASH_BUCKETS  521
+
+struct obj_file {
+	ElfW(Ehdr) header;
+	ElfW(Addr) baseaddr;
+	struct obj_section **sections;
+	struct obj_section *load_order;
+	struct obj_section **load_order_search_start;
+	struct obj_string_patch *string_patches;
+	struct obj_symbol_patch *symbol_patches;
+	int (*symbol_cmp)(const char *, const char *); /* cant be FAST_FUNC */
+	unsigned long (*symbol_hash)(const char *) FAST_FUNC;
+	unsigned long local_symtab_size;
+	struct obj_symbol **local_symtab;
+	struct obj_symbol *symtab[HASH_BUCKETS];
+};
+
+enum obj_reloc {
+	obj_reloc_ok,
+	obj_reloc_overflow,
+	obj_reloc_dangerous,
+	obj_reloc_unhandled
+};
+
+struct obj_string_patch {
+	struct obj_string_patch *next;
+	int reloc_secidx;
+	ElfW(Addr) reloc_offset;
+	ElfW(Addr) string_offset;
+};
+
+struct obj_symbol_patch {
+	struct obj_symbol_patch *next;
+	int reloc_secidx;
+	ElfW(Addr) reloc_offset;
+	struct obj_symbol *sym;
+};
+
+
+/* Generic object manipulation routines.  */
+
+static unsigned long FAST_FUNC obj_elf_hash(const char *);
+
+static unsigned long obj_elf_hash_n(const char *, unsigned long len);
+
+static struct obj_symbol *obj_find_symbol(struct obj_file *f,
+		const char *name);
+
+static ElfW(Addr) obj_symbol_final_value(struct obj_file *f,
+		struct obj_symbol *sym);
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+static void obj_set_symbol_compare(struct obj_file *f,
+		int (*cmp)(const char *, const char *),
+		unsigned long (*hash)(const char *) FAST_FUNC);
+#endif
+
+static struct obj_section *obj_find_section(struct obj_file *f,
+		const char *name);
+
+static void obj_insert_section_load_order(struct obj_file *f,
+		struct obj_section *sec);
+
+static struct obj_section *obj_create_alloced_section(struct obj_file *f,
+		const char *name,
+		unsigned long align,
+		unsigned long size);
+
+static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
+		const char *name,
+		unsigned long align,
+		unsigned long size);
+
+static void *obj_extend_section(struct obj_section *sec, unsigned long more);
+
+static void obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+		const char *string);
+
+static void obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+		struct obj_symbol *sym);
+
+static void obj_check_undefineds(struct obj_file *f);
+
+static void obj_allocate_commons(struct obj_file *f);
+
+static unsigned long obj_load_size(struct obj_file *f);
+
+static int obj_relocate(struct obj_file *f, ElfW(Addr) base);
+
+#if !LOADBITS
+#define obj_load(image, image_size, loadprogbits) \
+	obj_load(image, image_size)
+#endif
+static struct obj_file *obj_load(char *image, size_t image_size, int loadprogbits);
+
+static int obj_create_image(struct obj_file *f, char *image);
+
+/* Architecture specific manipulation routines.  */
+
+static struct obj_file *arch_new_file(void);
+
+static struct obj_section *arch_new_section(void);
+
+static struct obj_symbol *arch_new_symbol(void);
+
+static enum obj_reloc arch_apply_relocation(struct obj_file *f,
+		struct obj_section *targsec,
+		/*struct obj_section *symsec,*/
+		struct obj_symbol *sym,
+		ElfW(RelM) *rel, ElfW(Addr) value);
+
+static void arch_create_got(struct obj_file *f);
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license);
+#endif
+#endif /* obj.h */
+//----------------------------------------------------------------------------
+//--------end of modutils obj.h
+//----------------------------------------------------------------------------
+
+
+/* SPFX is always a string, so it can be concatenated to string constants.  */
+#ifdef SYMBOL_PREFIX
+#define SPFX	SYMBOL_PREFIX
+#else
+#define SPFX	""
+#endif
+
+enum { STRVERSIONLEN = 64 };
+
+/*======================================================================*/
+
+#define flag_force_load (option_mask32 & INSMOD_OPT_FORCE)
+#define flag_autoclean (option_mask32 & INSMOD_OPT_KERNELD)
+#define flag_verbose (option_mask32 & INSMOD_OPT_VERBOSE)
+#define flag_quiet (option_mask32 & INSMOD_OPT_SILENT)
+#define flag_noexport (option_mask32 & INSMOD_OPT_NO_EXPORT)
+#define flag_print_load_map (option_mask32 & INSMOD_OPT_PRINT_MAP)
+
+/*======================================================================*/
+
+#if defined(USE_LIST)
+
+struct arch_list_entry {
+	struct arch_list_entry *next;
+	LIST_ARCHTYPE addend;
+	int offset;
+	int inited : 1;
+};
+
+#endif
+
+#if defined(USE_SINGLE)
+
+struct arch_single_entry {
+	int offset;
+	int inited : 1;
+	int allocated : 1;
+};
+
+#endif
+
+#if defined(__mips__)
+struct mips_hi16 {
+	struct mips_hi16 *next;
+	ElfW(Addr) *addr;
+	ElfW(Addr) value;
+};
+#endif
+
+struct arch_file {
+	struct obj_file root;
+#if defined(USE_PLT_ENTRIES)
+	struct obj_section *plt;
+#endif
+#if defined(USE_GOT_ENTRIES)
+	struct obj_section *got;
+#endif
+#if defined(__mips__)
+	struct mips_hi16 *mips_hi16_list;
+#endif
+};
+
+struct arch_symbol {
+	struct obj_symbol root;
+#if defined(USE_PLT_ENTRIES)
+#if defined(USE_PLT_LIST)
+	struct arch_list_entry *pltent;
+#else
+	struct arch_single_entry pltent;
+#endif
+#endif
+#if defined(USE_GOT_ENTRIES)
+	struct arch_single_entry gotent;
+#endif
+};
+
+
+struct external_module {
+	const char *name;
+	ElfW(Addr) addr;
+	int used;
+	size_t nsyms;
+	struct new_module_symbol *syms;
+};
+
+static struct new_module_symbol *ksyms;
+static size_t nksyms;
+
+static struct external_module *ext_modules;
+static int n_ext_modules;
+static int n_ext_modules_used;
+
+/*======================================================================*/
+
+
+static struct obj_file *arch_new_file(void)
+{
+	struct arch_file *f;
+	f = xzalloc(sizeof(*f));
+	return &f->root; /* it's a first member */
+}
+
+static struct obj_section *arch_new_section(void)
+{
+	return xzalloc(sizeof(struct obj_section));
+}
+
+static struct obj_symbol *arch_new_symbol(void)
+{
+	struct arch_symbol *sym;
+	sym = xzalloc(sizeof(*sym));
+	return &sym->root;
+}
+
+static enum obj_reloc
+arch_apply_relocation(struct obj_file *f,
+		struct obj_section *targsec,
+		/*struct obj_section *symsec,*/
+		struct obj_symbol *sym,
+		ElfW(RelM) *rel, ElfW(Addr) v)
+{
+#if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \
+ || defined(__sh__) || defined(__s390__) || defined(__x86_64__) \
+ || defined(__powerpc__) || defined(__mips__)
+	struct arch_file *ifile = (struct arch_file *) f;
+#endif
+	enum obj_reloc ret = obj_reloc_ok;
+	ElfW(Addr) *loc = (ElfW(Addr) *) (targsec->contents + rel->r_offset);
+#if defined(__arm__) || defined(__H8300H__) || defined(__H8300S__) \
+ || defined(__i386__) || defined(__mc68000__) || defined(__microblaze__) \
+ || defined(__mips__) || defined(__nios2__) || defined(__powerpc__) \
+ || defined(__s390__) || defined(__sh__) || defined(__x86_64__)
+	ElfW(Addr) dot = targsec->header.sh_addr + rel->r_offset;
+#endif
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+	struct arch_symbol *isym = (struct arch_symbol *) sym;
+#endif
+#if defined(__arm__) || defined(__i386__) || defined(__mc68000__) \
+ || defined(__sh__) || defined(__s390__)
+#if defined(USE_GOT_ENTRIES)
+	ElfW(Addr) got = ifile->got ? ifile->got->header.sh_addr : 0;
+#endif
+#endif
+#if defined(USE_PLT_ENTRIES)
+	ElfW(Addr) plt = ifile->plt ? ifile->plt->header.sh_addr : 0;
+	unsigned long *ip;
+# if defined(USE_PLT_LIST)
+	struct arch_list_entry *pe;
+# else
+	struct arch_single_entry *pe;
+# endif
+#endif
+
+	switch (ELF_R_TYPE(rel->r_info)) {
+
+#if defined(__arm__)
+
+		case R_ARM_NONE:
+			break;
+
+		case R_ARM_ABS32:
+			*loc += v;
+			break;
+
+		case R_ARM_GOT32:
+			goto bb_use_got;
+
+		case R_ARM_GOTPC:
+			/* relative reloc, always to _GLOBAL_OFFSET_TABLE_
+			 * (which is .got) similar to branch,
+			 * but is full 32 bits relative */
+
+			*loc += got - dot;
+			break;
+
+		case R_ARM_PC24:
+		case R_ARM_PLT32:
+			goto bb_use_plt;
+
+		case R_ARM_GOTOFF: /* address relative to the got */
+			*loc += v - got;
+			break;
+
+#elif defined(__cris__)
+
+		case R_CRIS_NONE:
+			break;
+
+		case R_CRIS_32:
+			/* CRIS keeps the relocation value in the r_addend field and
+			 * should not use whats in *loc at all
+			 */
+			*loc = v;
+			break;
+
+#elif defined(__H8300H__) || defined(__H8300S__)
+
+		case R_H8_DIR24R8:
+			loc = (ElfW(Addr) *)((ElfW(Addr))loc - 1);
+			*loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v);
+			break;
+		case R_H8_DIR24A8:
+			*loc += v;
+			break;
+		case R_H8_DIR32:
+		case R_H8_DIR32A16:
+			*loc += v;
+			break;
+		case R_H8_PCREL16:
+			v -= dot + 2;
+			if ((ElfW(Sword))v > 0x7fff
+			 || (ElfW(Sword))v < -(ElfW(Sword))0x8000
+			) {
+				ret = obj_reloc_overflow;
+			} else {
+				*(unsigned short *)loc = v;
+			}
+			break;
+		case R_H8_PCREL8:
+			v -= dot + 1;
+			if ((ElfW(Sword))v > 0x7f
+			 || (ElfW(Sword))v < -(ElfW(Sword))0x80
+			) {
+				ret = obj_reloc_overflow;
+			} else {
+				*(unsigned char *)loc = v;
+			}
+			break;
+
+#elif defined(__i386__)
+
+		case R_386_NONE:
+			break;
+
+		case R_386_32:
+			*loc += v;
+			break;
+
+		case R_386_PLT32:
+		case R_386_PC32:
+		case R_386_GOTOFF:
+			*loc += v - dot;
+			break;
+
+		case R_386_GLOB_DAT:
+		case R_386_JMP_SLOT:
+			*loc = v;
+			break;
+
+		case R_386_RELATIVE:
+			*loc += f->baseaddr;
+			break;
+
+		case R_386_GOTPC:
+			*loc += got - dot;
+			break;
+
+		case R_386_GOT32:
+			goto bb_use_got;
+			break;
+
+#elif defined(__microblaze__)
+		case R_MICROBLAZE_NONE:
+		case R_MICROBLAZE_64_NONE:
+		case R_MICROBLAZE_32_SYM_OP_SYM:
+		case R_MICROBLAZE_32_PCREL:
+			break;
+
+		case R_MICROBLAZE_64_PCREL: {
+			/* dot is the address of the current instruction.
+			 * v is the target symbol address.
+			 * So we need to extract the offset in the code,
+			 * adding v, then subtrating the current address
+			 * of this instruction.
+			 * Ex: "IMM 0xFFFE  bralid 0x0000" = "bralid 0xFFFE0000"
+			 */
+
+			/* Get split offset stored in code */
+			unsigned int temp = (loc[0] & 0xFFFF) << 16 |
+						(loc[1] & 0xFFFF);
+
+			/* Adjust relative offset. -4 adjustment required
+			 * because dot points to the IMM insn, but branch
+			 * is computed relative to the branch instruction itself.
+			 */
+			temp += v - dot - 4;
+
+			/* Store back into code */
+			loc[0] = (loc[0] & 0xFFFF0000) | temp >> 16;
+			loc[1] = (loc[1] & 0xFFFF0000) | (temp & 0xFFFF);
+
+			break;
+		}
+
+		case R_MICROBLAZE_32:
+			*loc += v;
+			break;
+
+		case R_MICROBLAZE_64: {
+			/* Get split pointer stored in code */
+			unsigned int temp1 = (loc[0] & 0xFFFF) << 16 |
+						(loc[1] & 0xFFFF);
+
+			/* Add reloc offset */
+			temp1+=v;
+
+			/* Store back into code */
+			loc[0] = (loc[0] & 0xFFFF0000) | temp1 >> 16;
+			loc[1] = (loc[1] & 0xFFFF0000) | (temp1 & 0xFFFF);
+
+			break;
+		}
+
+		case R_MICROBLAZE_32_PCREL_LO:
+		case R_MICROBLAZE_32_LO:
+		case R_MICROBLAZE_SRO32:
+		case R_MICROBLAZE_SRW32:
+			ret = obj_reloc_unhandled;
+			break;
+
+#elif defined(__mc68000__)
+
+		case R_68K_NONE:
+			break;
+
+		case R_68K_32:
+			*loc += v;
+			break;
+
+		case R_68K_8:
+			if (v > 0xff) {
+				ret = obj_reloc_overflow;
+			}
+			*(char *)loc = v;
+			break;
+
+		case R_68K_16:
+			if (v > 0xffff) {
+				ret = obj_reloc_overflow;
+			}
+			*(short *)loc = v;
+			break;
+
+		case R_68K_PC8:
+			v -= dot;
+			if ((ElfW(Sword))v > 0x7f
+			 || (ElfW(Sword))v < -(ElfW(Sword))0x80
+			) {
+				ret = obj_reloc_overflow;
+			}
+			*(char *)loc = v;
+			break;
+
+		case R_68K_PC16:
+			v -= dot;
+			if ((ElfW(Sword))v > 0x7fff
+			 || (ElfW(Sword))v < -(ElfW(Sword))0x8000
+			) {
+				ret = obj_reloc_overflow;
+			}
+			*(short *)loc = v;
+			break;
+
+		case R_68K_PC32:
+			*(int *)loc = v - dot;
+			break;
+
+		case R_68K_GLOB_DAT:
+		case R_68K_JMP_SLOT:
+			*loc = v;
+			break;
+
+		case R_68K_RELATIVE:
+			*(int *)loc += f->baseaddr;
+			break;
+
+		case R_68K_GOT32:
+			goto bb_use_got;
+
+# ifdef R_68K_GOTOFF
+		case R_68K_GOTOFF:
+			*loc += v - got;
+			break;
+# endif
+
+#elif defined(__mips__)
+
+		case R_MIPS_NONE:
+			break;
+
+		case R_MIPS_32:
+			*loc += v;
+			break;
+
+		case R_MIPS_26:
+			if (v % 4)
+				ret = obj_reloc_dangerous;
+			if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000))
+				ret = obj_reloc_overflow;
+			*loc =
+				(*loc & ~0x03ffffff) | ((*loc + (v >> 2)) &
+										0x03ffffff);
+			break;
+
+		case R_MIPS_HI16:
+			{
+				struct mips_hi16 *n;
+
+				/* We cannot relocate this one now because we don't know the value
+				   of the carry we need to add.  Save the information, and let LO16
+				   do the actual relocation.  */
+				n = xmalloc(sizeof *n);
+				n->addr = loc;
+				n->value = v;
+				n->next = ifile->mips_hi16_list;
+				ifile->mips_hi16_list = n;
+				break;
+			}
+
+		case R_MIPS_LO16:
+			{
+				unsigned long insnlo = *loc;
+				ElfW(Addr) val, vallo;
+
+				/* Sign extend the addend we extract from the lo insn.  */
+				vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000;
+
+				if (ifile->mips_hi16_list != NULL) {
+					struct mips_hi16 *l;
+
+					l = ifile->mips_hi16_list;
+					while (l != NULL) {
+						struct mips_hi16 *next;
+						unsigned long insn;
+
+						/* Do the HI16 relocation.  Note that we actually don't
+						   need to know anything about the LO16 itself, except where
+						   to find the low 16 bits of the addend needed by the LO16.  */
+						insn = *l->addr;
+						val =
+							((insn & 0xffff) << 16) +
+							vallo;
+						val += v;
+
+						/* Account for the sign extension that will happen in the
+						   low bits.  */
+						val =
+							((val >> 16) +
+							 ((val & 0x8000) !=
+							  0)) & 0xffff;
+
+						insn = (insn & ~0xffff) | val;
+						*l->addr = insn;
+
+						next = l->next;
+						free(l);
+						l = next;
+					}
+
+					ifile->mips_hi16_list = NULL;
+				}
+
+				/* Ok, we're done with the HI16 relocs.  Now deal with the LO16.  */
+				val = v + vallo;
+				insnlo = (insnlo & ~0xffff) | (val & 0xffff);
+				*loc = insnlo;
+				break;
+			}
+
+#elif defined(__nios2__)
+
+		case R_NIOS2_NONE:
+			break;
+
+		case R_NIOS2_BFD_RELOC_32:
+			*loc += v;
+			break;
+
+		case R_NIOS2_BFD_RELOC_16:
+			if (v > 0xffff) {
+				ret = obj_reloc_overflow;
+			}
+			*(short *)loc = v;
+			break;
+
+		case R_NIOS2_BFD_RELOC_8:
+			if (v > 0xff) {
+				ret = obj_reloc_overflow;
+			}
+			*(char *)loc = v;
+			break;
+
+		case R_NIOS2_S16:
+			{
+				Elf32_Addr word;
+
+				if ((Elf32_Sword)v > 0x7fff
+				 || (Elf32_Sword)v < -(Elf32_Sword)0x8000
+				) {
+					ret = obj_reloc_overflow;
+				}
+
+				word = *loc;
+				*loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+				       (word & 0x3f);
+			}
+			break;
+
+		case R_NIOS2_U16:
+			{
+				Elf32_Addr word;
+
+				if (v > 0xffff) {
+					ret = obj_reloc_overflow;
+				}
+
+				word = *loc;
+				*loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+				       (word & 0x3f);
+			}
+			break;
+
+		case R_NIOS2_PCREL16:
+			{
+				Elf32_Addr word;
+
+				v -= dot + 4;
+				if ((Elf32_Sword)v > 0x7fff
+				 || (Elf32_Sword)v < -(Elf32_Sword)0x8000
+				) {
+					ret = obj_reloc_overflow;
+				}
+
+				word = *loc;
+				*loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
+			}
+			break;
+
+		case R_NIOS2_GPREL:
+			{
+				Elf32_Addr word, gp;
+				/* get _gp */
+				gp = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "_gp"));
+				v -= gp;
+				if ((Elf32_Sword)v > 0x7fff
+				 || (Elf32_Sword)v < -(Elf32_Sword)0x8000
+				) {
+					ret = obj_reloc_overflow;
+				}
+
+				word = *loc;
+				*loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) | (word & 0x3f);
+			}
+			break;
+
+		case R_NIOS2_CALL26:
+			if (v & 3)
+				ret = obj_reloc_dangerous;
+			if ((v >> 28) != (dot >> 28))
+				ret = obj_reloc_overflow;
+			*loc = (*loc & 0x3f) | ((v >> 2) << 6);
+			break;
+
+		case R_NIOS2_IMM5:
+			{
+				Elf32_Addr word;
+
+				if (v > 0x1f) {
+					ret = obj_reloc_overflow;
+				}
+
+				word = *loc & ~0x7c0;
+				*loc = word | ((v & 0x1f) << 6);
+			}
+			break;
+
+		case R_NIOS2_IMM6:
+			{
+				Elf32_Addr word;
+
+				if (v > 0x3f) {
+					ret = obj_reloc_overflow;
+				}
+
+				word = *loc & ~0xfc0;
+				*loc = word | ((v & 0x3f) << 6);
+			}
+			break;
+
+		case R_NIOS2_IMM8:
+			{
+				Elf32_Addr word;
+
+				if (v > 0xff) {
+					ret = obj_reloc_overflow;
+				}
+
+				word = *loc & ~0x3fc0;
+				*loc = word | ((v & 0xff) << 6);
+			}
+			break;
+
+		case R_NIOS2_HI16:
+			{
+				Elf32_Addr word;
+
+				word = *loc;
+				*loc = ((((word >> 22) << 16) | ((v >>16) & 0xffff)) << 6) |
+				       (word & 0x3f);
+			}
+			break;
+
+		case R_NIOS2_LO16:
+			{
+				Elf32_Addr word;
+
+				word = *loc;
+				*loc = ((((word >> 22) << 16) | (v & 0xffff)) << 6) |
+				       (word & 0x3f);
+			}
+			break;
+
+		case R_NIOS2_HIADJ16:
+			{
+				Elf32_Addr word1, word2;
+
+				word1 = *loc;
+				word2 = ((v >> 16) + ((v >> 15) & 1)) & 0xffff;
+				*loc = ((((word1 >> 22) << 16) | word2) << 6) |
+				       (word1 & 0x3f);
+			}
+			break;
+
+#elif defined(__powerpc64__)
+		/* PPC64 needs a 2.6 kernel, 2.4 module relocation irrelevant */
+
+#elif defined(__powerpc__)
+
+		case R_PPC_ADDR16_HA:
+			*(unsigned short *)loc = (v + 0x8000) >> 16;
+			break;
+
+		case R_PPC_ADDR16_HI:
+			*(unsigned short *)loc = v >> 16;
+			break;
+
+		case R_PPC_ADDR16_LO:
+			*(unsigned short *)loc = v;
+			break;
+
+		case R_PPC_REL24:
+			goto bb_use_plt;
+
+		case R_PPC_REL32:
+			*loc = v - dot;
+			break;
+
+		case R_PPC_ADDR32:
+			*loc = v;
+			break;
+
+#elif defined(__s390__)
+
+		case R_390_32:
+			*(unsigned int *) loc += v;
+			break;
+		case R_390_16:
+			*(unsigned short *) loc += v;
+			break;
+		case R_390_8:
+			*(unsigned char *) loc += v;
+			break;
+
+		case R_390_PC32:
+			*(unsigned int *) loc += v - dot;
+			break;
+		case R_390_PC16DBL:
+			*(unsigned short *) loc += (v - dot) >> 1;
+			break;
+		case R_390_PC16:
+			*(unsigned short *) loc += v - dot;
+			break;
+
+		case R_390_PLT32:
+		case R_390_PLT16DBL:
+			/* find the plt entry and initialize it.  */
+			pe = (struct arch_single_entry *) &isym->pltent;
+			if (pe->inited == 0) {
+				ip = (unsigned long *)(ifile->plt->contents + pe->offset);
+				ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */
+				ip[1] = 0x100607f1;
+				if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+					ip[2] = v - 2;
+				else
+					ip[2] = v;
+				pe->inited = 1;
+			}
+
+			/* Insert relative distance to target.  */
+			v = plt + pe->offset - dot;
+			if (ELF_R_TYPE(rel->r_info) == R_390_PLT32)
+				*(unsigned int *) loc = (unsigned int) v;
+			else if (ELF_R_TYPE(rel->r_info) == R_390_PLT16DBL)
+				*(unsigned short *) loc = (unsigned short) ((v + 2) >> 1);
+			break;
+
+		case R_390_GLOB_DAT:
+		case R_390_JMP_SLOT:
+			*loc = v;
+			break;
+
+		case R_390_RELATIVE:
+			*loc += f->baseaddr;
+			break;
+
+		case R_390_GOTPC:
+			*(unsigned long *) loc += got - dot;
+			break;
+
+		case R_390_GOT12:
+		case R_390_GOT16:
+		case R_390_GOT32:
+			if (!isym->gotent.inited)
+			{
+				isym->gotent.inited = 1;
+				*(ElfW(Addr) *)(ifile->got->contents + isym->gotent.offset) = v;
+			}
+			if (ELF_R_TYPE(rel->r_info) == R_390_GOT12)
+				*(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff;
+			else if (ELF_R_TYPE(rel->r_info) == R_390_GOT16)
+				*(unsigned short *) loc += isym->gotent.offset;
+			else if (ELF_R_TYPE(rel->r_info) == R_390_GOT32)
+				*(unsigned int *) loc += isym->gotent.offset;
+			break;
+
+# ifndef R_390_GOTOFF32
+#  define R_390_GOTOFF32 R_390_GOTOFF
+# endif
+		case R_390_GOTOFF32:
+			*loc += v - got;
+			break;
+
+#elif defined(__sh__)
+
+		case R_SH_NONE:
+			break;
+
+		case R_SH_DIR32:
+			*loc += v;
+			break;
+
+		case R_SH_REL32:
+			*loc += v - dot;
+			break;
+
+		case R_SH_PLT32:
+			*loc = v - dot;
+			break;
+
+		case R_SH_GLOB_DAT:
+		case R_SH_JMP_SLOT:
+			*loc = v;
+			break;
+
+		case R_SH_RELATIVE:
+			*loc = f->baseaddr + rel->r_addend;
+			break;
+
+		case R_SH_GOTPC:
+			*loc = got - dot + rel->r_addend;
+			break;
+
+		case R_SH_GOT32:
+			goto bb_use_got;
+
+		case R_SH_GOTOFF:
+			*loc = v - got;
+			break;
+
+# if defined(__SH5__)
+		case R_SH_IMM_MEDLOW16:
+		case R_SH_IMM_LOW16:
+			{
+				ElfW(Addr) word;
+
+				if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16)
+					v >>= 16;
+
+				/*
+				 *  movi and shori have the format:
+				 *
+				 *  |  op  | imm  | reg | reserved |
+				 *   31..26 25..10 9.. 4 3   ..   0
+				 *
+				 * so we simply mask and or in imm.
+				 */
+				word = *loc & ~0x3fffc00;
+				word |= (v & 0xffff) << 10;
+
+				*loc = word;
+
+				break;
+			}
+
+		case R_SH_IMM_MEDLOW16_PCREL:
+		case R_SH_IMM_LOW16_PCREL:
+			{
+				ElfW(Addr) word;
+
+				word = *loc & ~0x3fffc00;
+
+				v -= dot;
+
+				if (ELF_R_TYPE(rel->r_info) == R_SH_IMM_MEDLOW16_PCREL)
+					v >>= 16;
+
+				word |= (v & 0xffff) << 10;
+
+				*loc = word;
+
+				break;
+			}
+# endif /* __SH5__ */
+
+#elif defined(__v850e__)
+
+		case R_V850_NONE:
+			break;
+
+		case R_V850_32:
+			/* We write two shorts instead of a long because even
+			   32-bit insns only need half-word alignment, but
+			   32-bit data needs to be long-word aligned.  */
+			v += ((unsigned short *)loc)[0];
+			v += ((unsigned short *)loc)[1] << 16;
+			((unsigned short *)loc)[0] = v & 0xffff;
+			((unsigned short *)loc)[1] = (v >> 16) & 0xffff;
+			break;
+
+		case R_V850_22_PCREL:
+			goto bb_use_plt;
+
+#elif defined(__x86_64__)
+
+		case R_X86_64_NONE:
+			break;
+
+		case R_X86_64_64:
+			*loc += v;
+			break;
+
+		case R_X86_64_32:
+			*(unsigned int *) loc += v;
+			if (v > 0xffffffff)
+			{
+				ret = obj_reloc_overflow; /* Kernel module compiled without -mcmodel=kernel. */
+				/* error("Possibly is module compiled without -mcmodel=kernel!"); */
+			}
+			break;
+
+		case R_X86_64_32S:
+			*(signed int *) loc += v;
+			break;
+
+		case R_X86_64_16:
+			*(unsigned short *) loc += v;
+			break;
+
+		case R_X86_64_8:
+			*(unsigned char *) loc += v;
+			break;
+
+		case R_X86_64_PC32:
+			*(unsigned int *) loc += v - dot;
+			break;
+
+		case R_X86_64_PC16:
+			*(unsigned short *) loc += v - dot;
+			break;
+
+		case R_X86_64_PC8:
+			*(unsigned char *) loc += v - dot;
+			break;
+
+		case R_X86_64_GLOB_DAT:
+		case R_X86_64_JUMP_SLOT:
+			*loc = v;
+			break;
+
+		case R_X86_64_RELATIVE:
+			*loc += f->baseaddr;
+			break;
+
+		case R_X86_64_GOT32:
+		case R_X86_64_GOTPCREL:
+			goto bb_use_got;
+# if 0
+			if (!isym->gotent.reloc_done)
+			{
+				isym->gotent.reloc_done = 1;
+				*(Elf64_Addr *)(ifile->got->contents + isym->gotent.offset) = v;
+			}
+			/* XXX are these really correct?  */
+			if (ELF64_R_TYPE(rel->r_info) == R_X86_64_GOTPCREL)
+				*(unsigned int *) loc += v + isym->gotent.offset;
+			else
+				*loc += isym->gotent.offset;
+			break;
+# endif
+
+#else
+# warning "no idea how to handle relocations on your arch"
+#endif
+
+		default:
+			printf("Warning: unhandled reloc %d\n", (int)ELF_R_TYPE(rel->r_info));
+			ret = obj_reloc_unhandled;
+			break;
+
+#if defined(USE_PLT_ENTRIES)
+
+bb_use_plt:
+
+			/* find the plt entry and initialize it if necessary */
+
+#if defined(USE_PLT_LIST)
+			for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend;)
+				pe = pe->next;
+#else
+			pe = &isym->pltent;
+#endif
+
+			if (! pe->inited) {
+				ip = (unsigned long *) (ifile->plt->contents + pe->offset);
+
+				/* generate some machine code */
+
+#if defined(__arm__)
+				ip[0] = 0xe51ff004;			/* ldr pc,[pc,#-4] */
+				ip[1] = v;				/* sym@ */
+#endif
+#if defined(__powerpc__)
+				ip[0] = 0x3d600000 + ((v + 0x8000) >> 16);  /* lis r11,sym@ha */
+				ip[1] = 0x396b0000 + (v & 0xffff);          /* addi r11,r11,sym@l */
+				ip[2] = 0x7d6903a6;			      /* mtctr r11 */
+				ip[3] = 0x4e800420;			      /* bctr */
+#endif
+#if defined(__v850e__)
+				/* We have to trash a register, so we assume that any control
+				   transfer more than 21-bits away must be a function call
+				   (so we can use a call-clobbered register).  */
+				ip[0] = 0x0621 + ((v & 0xffff) << 16);   /* mov sym, r1 ... */
+				ip[1] = ((v >> 16) & 0xffff) + 0x610000; /* ...; jmp r1 */
+#endif
+				pe->inited = 1;
+			}
+
+			/* relative distance to target */
+			v -= dot;
+			/* if the target is too far away.... */
+#if defined(__arm__) || defined(__powerpc__)
+			if ((int)v < -0x02000000 || (int)v >= 0x02000000)
+#elif defined(__v850e__)
+				if ((ElfW(Sword))v > 0x1fffff || (ElfW(Sword))v < (ElfW(Sword))-0x200000)
+#endif
+					/* go via the plt */
+					v = plt + pe->offset - dot;
+
+#if defined(__v850e__)
+			if (v & 1)
+#else
+				if (v & 3)
+#endif
+					ret = obj_reloc_dangerous;
+
+			/* merge the offset into the instruction. */
+#if defined(__arm__)
+			/* Convert to words. */
+			v >>= 2;
+
+			*loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff);
+#endif
+#if defined(__powerpc__)
+			*loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc);
+#endif
+#if defined(__v850e__)
+			/* We write two shorts instead of a long because even 32-bit insns
+			   only need half-word alignment, but the 32-bit data write needs
+			   to be long-word aligned.  */
+			((unsigned short *)loc)[0] =
+				(*(unsigned short *)loc & 0xffc0) /* opcode + reg */
+				| ((v >> 16) & 0x3f);             /* offs high part */
+			((unsigned short *)loc)[1] =
+				(v & 0xffff);                    /* offs low part */
+#endif
+			break;
+#endif /* USE_PLT_ENTRIES */
+
+#if defined(USE_GOT_ENTRIES)
+bb_use_got:
+
+			/* needs an entry in the .got: set it, once */
+			if (!isym->gotent.inited) {
+				isym->gotent.inited = 1;
+				*(ElfW(Addr) *) (ifile->got->contents + isym->gotent.offset) = v;
+			}
+			/* make the reloc with_respect_to_.got */
+#if defined(__sh__)
+			*loc += isym->gotent.offset + rel->r_addend;
+#elif defined(__i386__) || defined(__arm__) || defined(__mc68000__)
+			*loc += isym->gotent.offset;
+#endif
+			break;
+
+#endif /* USE_GOT_ENTRIES */
+	}
+
+	return ret;
+}
+
+
+#if defined(USE_LIST)
+
+static int arch_list_add(ElfW(RelM) *rel, struct arch_list_entry **list,
+			  int offset, int size)
+{
+	struct arch_list_entry *pe;
+
+	for (pe = *list; pe != NULL; pe = pe->next) {
+		if (pe->addend == rel->r_addend) {
+			break;
+		}
+	}
+
+	if (pe == NULL) {
+		pe = xzalloc(sizeof(struct arch_list_entry));
+		pe->next = *list;
+		pe->addend = rel->r_addend;
+		pe->offset = offset;
+		/*pe->inited = 0;*/
+		*list = pe;
+		return size;
+	}
+	return 0;
+}
+
+#endif
+
+#if defined(USE_SINGLE)
+
+static int arch_single_init(/*ElfW(RelM) *rel,*/ struct arch_single_entry *single,
+		int offset, int size)
+{
+	if (single->allocated == 0) {
+		single->allocated = 1;
+		single->offset = offset;
+		single->inited = 0;
+		return size;
+	}
+	return 0;
+}
+
+#endif
+
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+
+static struct obj_section *arch_xsect_init(struct obj_file *f, const char *name,
+		int offset, int size)
+{
+	struct obj_section *myrelsec = obj_find_section(f, name);
+
+	if (offset == 0) {
+		offset += size;
+	}
+
+	if (myrelsec) {
+		obj_extend_section(myrelsec, offset);
+	} else {
+		myrelsec = obj_create_alloced_section(f, name,
+				size, offset);
+	}
+
+	return myrelsec;
+}
+
+#endif
+
+static void arch_create_got(struct obj_file *f)
+{
+#if defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES)
+	struct arch_file *ifile = (struct arch_file *) f;
+	int i;
+#if defined(USE_GOT_ENTRIES)
+	int got_offset = 0, got_needed = 0, got_allocate;
+#endif
+#if defined(USE_PLT_ENTRIES)
+	int plt_offset = 0, plt_needed = 0, plt_allocate;
+#endif
+	struct obj_section *relsec, *symsec, *strsec;
+	ElfW(RelM) *rel, *relend;
+	ElfW(Sym) *symtab, *extsym;
+	const char *strtab, *name;
+	struct arch_symbol *intsym;
+
+	for (i = 0; i < f->header.e_shnum; ++i) {
+		relsec = f->sections[i];
+		if (relsec->header.sh_type != SHT_RELM)
+			continue;
+
+		symsec = f->sections[relsec->header.sh_link];
+		strsec = f->sections[symsec->header.sh_link];
+
+		rel = (ElfW(RelM) *) relsec->contents;
+		relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+		symtab = (ElfW(Sym) *) symsec->contents;
+		strtab = (const char *) strsec->contents;
+
+		for (; rel < relend; ++rel) {
+			extsym = &symtab[ELF_R_SYM(rel->r_info)];
+
+#if defined(USE_GOT_ENTRIES)
+			got_allocate = 0;
+#endif
+#if defined(USE_PLT_ENTRIES)
+			plt_allocate = 0;
+#endif
+
+			switch (ELF_R_TYPE(rel->r_info)) {
+#if defined(__arm__)
+			case R_ARM_PC24:
+			case R_ARM_PLT32:
+				plt_allocate = 1;
+				break;
+
+			case R_ARM_GOTOFF:
+			case R_ARM_GOTPC:
+				got_needed = 1;
+				continue;
+
+			case R_ARM_GOT32:
+				got_allocate = 1;
+				break;
+
+#elif defined(__i386__)
+			case R_386_GOTPC:
+			case R_386_GOTOFF:
+				got_needed = 1;
+				continue;
+
+			case R_386_GOT32:
+				got_allocate = 1;
+				break;
+
+#elif defined(__powerpc__)
+			case R_PPC_REL24:
+				plt_allocate = 1;
+				break;
+
+#elif defined(__mc68000__)
+			case R_68K_GOT32:
+				got_allocate = 1;
+				break;
+
+#ifdef R_68K_GOTOFF
+			case R_68K_GOTOFF:
+				got_needed = 1;
+				continue;
+#endif
+
+#elif defined(__sh__)
+			case R_SH_GOT32:
+				got_allocate = 1;
+				break;
+
+			case R_SH_GOTPC:
+			case R_SH_GOTOFF:
+				got_needed = 1;
+				continue;
+
+#elif defined(__v850e__)
+			case R_V850_22_PCREL:
+				plt_needed = 1;
+				break;
+
+#endif
+			default:
+				continue;
+			}
+
+			if (extsym->st_name != 0) {
+				name = strtab + extsym->st_name;
+			} else {
+				name = f->sections[extsym->st_shndx]->name;
+			}
+			intsym = (struct arch_symbol *) obj_find_symbol(f, name);
+#if defined(USE_GOT_ENTRIES)
+			if (got_allocate) {
+				got_offset += arch_single_init(
+						/*rel,*/ &intsym->gotent,
+						got_offset, GOT_ENTRY_SIZE);
+
+				got_needed = 1;
+			}
+#endif
+#if defined(USE_PLT_ENTRIES)
+			if (plt_allocate) {
+#if defined(USE_PLT_LIST)
+				plt_offset += arch_list_add(
+						rel, &intsym->pltent,
+						plt_offset, PLT_ENTRY_SIZE);
+#else
+				plt_offset += arch_single_init(
+						/*rel,*/ &intsym->pltent,
+						plt_offset, PLT_ENTRY_SIZE);
+#endif
+				plt_needed = 1;
+			}
+#endif
+		}
+	}
+
+#if defined(USE_GOT_ENTRIES)
+	if (got_needed) {
+		ifile->got = arch_xsect_init(f, ".got", got_offset,
+				GOT_ENTRY_SIZE);
+	}
+#endif
+
+#if defined(USE_PLT_ENTRIES)
+	if (plt_needed) {
+		ifile->plt = arch_xsect_init(f, ".plt", plt_offset,
+				PLT_ENTRY_SIZE);
+	}
+#endif
+
+#endif /* defined(USE_GOT_ENTRIES) || defined(USE_PLT_ENTRIES) */
+}
+
+/*======================================================================*/
+
+/* Standard ELF hash function.  */
+static unsigned long obj_elf_hash_n(const char *name, unsigned long n)
+{
+	unsigned long h = 0;
+	unsigned long g;
+	unsigned char ch;
+
+	while (n > 0) {
+		ch = *name++;
+		h = (h << 4) + ch;
+		g = (h & 0xf0000000);
+		if (g != 0) {
+			h ^= g >> 24;
+			h &= ~g;
+		}
+		n--;
+	}
+	return h;
+}
+
+static unsigned long FAST_FUNC obj_elf_hash(const char *name)
+{
+	return obj_elf_hash_n(name, strlen(name));
+}
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+/* String comparison for non-co-versioned kernel and module.  */
+
+static int ncv_strcmp(const char *a, const char *b)
+{
+	size_t alen = strlen(a), blen = strlen(b);
+
+	if (blen == alen + 10 && b[alen] == '_' && b[alen + 1] == 'R')
+		return strncmp(a, b, alen);
+	else if (alen == blen + 10 && a[blen] == '_' && a[blen + 1] == 'R')
+		return strncmp(a, b, blen);
+	else
+		return strcmp(a, b);
+}
+
+/* String hashing for non-co-versioned kernel and module.  Here
+   we are simply forced to drop the crc from the hash.  */
+
+static unsigned long FAST_FUNC ncv_symbol_hash(const char *str)
+{
+	size_t len = strlen(str);
+	if (len > 10 && str[len - 10] == '_' && str[len - 9] == 'R')
+		len -= 10;
+	return obj_elf_hash_n(str, len);
+}
+
+static void
+obj_set_symbol_compare(struct obj_file *f,
+		int (*cmp) (const char *, const char *),
+		unsigned long (*hash) (const char *) FAST_FUNC)
+{
+	if (cmp)
+		f->symbol_cmp = cmp;
+	if (hash) {
+		struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;
+		int i;
+
+		f->symbol_hash = hash;
+
+		memcpy(tmptab, f->symtab, sizeof(tmptab));
+		memset(f->symtab, 0, sizeof(f->symtab));
+
+		for (i = 0; i < HASH_BUCKETS; ++i) {
+			for (sym = tmptab[i]; sym; sym = next) {
+				unsigned long h = hash(sym->name) % HASH_BUCKETS;
+				next = sym->next;
+				sym->next = f->symtab[h];
+				f->symtab[h] = sym;
+			}
+		}
+	}
+}
+
+#endif /* FEATURE_INSMOD_VERSION_CHECKING */
+
+static struct obj_symbol *
+obj_add_symbol(struct obj_file *f, const char *name,
+		unsigned long symidx, int info,
+		int secidx, ElfW(Addr) value,
+		unsigned long size)
+{
+	struct obj_symbol *sym;
+	unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+	int n_type = ELF_ST_TYPE(info);
+	int n_binding = ELF_ST_BIND(info);
+
+	for (sym = f->symtab[hash]; sym; sym = sym->next) {
+		if (f->symbol_cmp(sym->name, name) == 0) {
+			int o_secidx = sym->secidx;
+			int o_info = sym->info;
+			int o_type = ELF_ST_TYPE(o_info);
+			int o_binding = ELF_ST_BIND(o_info);
+
+			/* A redefinition!  Is it legal?  */
+
+			if (secidx == SHN_UNDEF)
+				return sym;
+			else if (o_secidx == SHN_UNDEF)
+				goto found;
+			else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) {
+				/* Cope with local and global symbols of the same name
+				   in the same object file, as might have been created
+				   by ld -r.  The only reason locals are now seen at this
+				   level at all is so that we can do semi-sensible things
+				   with parameters.  */
+
+				struct obj_symbol *nsym, **p;
+
+				nsym = arch_new_symbol();
+				nsym->next = sym->next;
+				nsym->ksymidx = -1;
+
+				/* Excise the old (local) symbol from the hash chain.  */
+				for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
+					continue;
+				*p = sym = nsym;
+				goto found;
+			} else if (n_binding == STB_LOCAL) {
+				/* Another symbol of the same name has already been defined.
+				   Just add this to the local table.  */
+				sym = arch_new_symbol();
+				sym->next = NULL;
+				sym->ksymidx = -1;
+				f->local_symtab[symidx] = sym;
+				goto found;
+			} else if (n_binding == STB_WEAK)
+				return sym;
+			else if (o_binding == STB_WEAK)
+				goto found;
+			/* Don't unify COMMON symbols with object types the programmer
+			   doesn't expect.  */
+			else if (secidx == SHN_COMMON
+					&& (o_type == STT_NOTYPE || o_type == STT_OBJECT))
+				return sym;
+			else if (o_secidx == SHN_COMMON
+					&& (n_type == STT_NOTYPE || n_type == STT_OBJECT))
+				goto found;
+			else {
+				/* Don't report an error if the symbol is coming from
+				   the kernel or some external module.  */
+				if (secidx <= SHN_HIRESERVE)
+					bb_error_msg("%s multiply defined", name);
+				return sym;
+			}
+		}
+	}
+
+	/* Completely new symbol.  */
+	sym = arch_new_symbol();
+	sym->next = f->symtab[hash];
+	f->symtab[hash] = sym;
+	sym->ksymidx = -1;
+	if (ELF_ST_BIND(info) == STB_LOCAL && symidx != (unsigned long)(-1)) {
+		if (symidx >= f->local_symtab_size)
+			bb_error_msg("local symbol %s with index %ld exceeds local_symtab_size %ld",
+					name, (long) symidx, (long) f->local_symtab_size);
+		else
+			f->local_symtab[symidx] = sym;
+	}
+
+found:
+	sym->name = name;
+	sym->value = value;
+	sym->size = size;
+	sym->secidx = secidx;
+	sym->info = info;
+
+	return sym;
+}
+
+static struct obj_symbol *
+obj_find_symbol(struct obj_file *f, const char *name)
+{
+	struct obj_symbol *sym;
+	unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+
+	for (sym = f->symtab[hash]; sym; sym = sym->next)
+		if (f->symbol_cmp(sym->name, name) == 0)
+			return sym;
+	return NULL;
+}
+
+static ElfW(Addr) obj_symbol_final_value(struct obj_file * f, struct obj_symbol * sym)
+{
+	if (sym) {
+		if (sym->secidx >= SHN_LORESERVE)
+			return sym->value;
+		return sym->value + f->sections[sym->secidx]->header.sh_addr;
+	}
+	/* As a special case, a NULL sym has value zero.  */
+	return 0;
+}
+
+static struct obj_section *obj_find_section(struct obj_file *f, const char *name)
+{
+	int i, n = f->header.e_shnum;
+
+	for (i = 0; i < n; ++i)
+		if (strcmp(f->sections[i]->name, name) == 0)
+			return f->sections[i];
+	return NULL;
+}
+
+static int obj_load_order_prio(struct obj_section *a)
+{
+	unsigned long af, ac;
+
+	af = a->header.sh_flags;
+
+	ac = 0;
+	if (a->name[0] != '.' || strlen(a->name) != 10
+	 || strcmp(a->name + 5, ".init") != 0
+	) {
+		ac |= 32;
+	}
+	if (af & SHF_ALLOC)
+		ac |= 16;
+	if (!(af & SHF_WRITE))
+		ac |= 8;
+	if (af & SHF_EXECINSTR)
+		ac |= 4;
+	if (a->header.sh_type != SHT_NOBITS)
+		ac |= 2;
+
+	return ac;
+}
+
+static void
+obj_insert_section_load_order(struct obj_file *f, struct obj_section *sec)
+{
+	struct obj_section **p;
+	int prio = obj_load_order_prio(sec);
+	for (p = f->load_order_search_start; *p; p = &(*p)->load_next)
+		if (obj_load_order_prio(*p) < prio)
+			break;
+	sec->load_next = *p;
+	*p = sec;
+}
+
+static struct obj_section *helper_create_alloced_section(struct obj_file *f,
+		const char *name,
+		unsigned long align,
+		unsigned long size)
+{
+	int newidx = f->header.e_shnum++;
+	struct obj_section *sec;
+
+	f->sections = xrealloc_vector(f->sections, 2, newidx);
+	f->sections[newidx] = sec = arch_new_section();
+
+	sec->header.sh_type = SHT_PROGBITS;
+	sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+	sec->header.sh_size = size;
+	sec->header.sh_addralign = align;
+	sec->name = name;
+	sec->idx = newidx;
+	if (size)
+		sec->contents = xzalloc(size);
+
+	return sec;
+}
+
+static struct obj_section *obj_create_alloced_section(struct obj_file *f,
+		const char *name,
+		unsigned long align,
+		unsigned long size)
+{
+	struct obj_section *sec;
+
+	sec = helper_create_alloced_section(f, name, align, size);
+	obj_insert_section_load_order(f, sec);
+	return sec;
+}
+
+static struct obj_section *obj_create_alloced_section_first(struct obj_file *f,
+		const char *name,
+		unsigned long align,
+		unsigned long size)
+{
+	struct obj_section *sec;
+
+	sec = helper_create_alloced_section(f, name, align, size);
+	sec->load_next = f->load_order;
+	f->load_order = sec;
+	if (f->load_order_search_start == &f->load_order)
+		f->load_order_search_start = &sec->load_next;
+
+	return sec;
+}
+
+static void *obj_extend_section(struct obj_section *sec, unsigned long more)
+{
+	unsigned long oldsize = sec->header.sh_size;
+	if (more) {
+		sec->header.sh_size += more;
+		sec->contents = xrealloc(sec->contents, sec->header.sh_size);
+	}
+	return sec->contents + oldsize;
+}
+
+
+/* Conditionally add the symbols from the given symbol set to the
+   new module.  */
+
+static int add_symbols_from(struct obj_file *f,
+		int idx,
+		struct new_module_symbol *syms,
+		size_t nsyms)
+{
+	struct new_module_symbol *s;
+	size_t i;
+	int used = 0;
+#ifdef SYMBOL_PREFIX
+	char *name_buf = NULL;
+	size_t name_alloced_size = 0;
+#endif
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+	int gpl;
+
+	gpl = obj_gpl_license(f, NULL) == 0;
+#endif
+	for (i = 0, s = syms; i < nsyms; ++i, ++s) {
+		/* Only add symbols that are already marked external.
+		   If we override locals we may cause problems for
+		   argument initialization.  We will also create a false
+		   dependency on the module.  */
+		struct obj_symbol *sym;
+		char *name;
+
+		/* GPL licensed modules can use symbols exported with
+		 * EXPORT_SYMBOL_GPL, so ignore any GPLONLY_ prefix on the
+		 * exported names.  Non-GPL modules never see any GPLONLY_
+		 * symbols so they cannot fudge it by adding the prefix on
+		 * their references.
+		 */
+		if (strncmp((char *)s->name, "GPLONLY_", 8) == 0) {
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+			if (gpl)
+				s->name += 8;
+			else
+#endif
+				continue;
+		}
+		name = (char *)s->name;
+
+#ifdef SYMBOL_PREFIX
+		/* Prepend SYMBOL_PREFIX to the symbol's name (the
+		   kernel exports `C names', but module object files
+		   reference `linker names').  */
+		size_t extra = sizeof SYMBOL_PREFIX;
+		size_t name_size = strlen(name) + extra;
+		if (name_size > name_alloced_size) {
+			name_alloced_size = name_size * 2;
+			name_buf = alloca(name_alloced_size);
+		}
+		strcpy(name_buf, SYMBOL_PREFIX);
+		strcpy(name_buf + extra - 1, name);
+		name = name_buf;
+#endif
+
+		sym = obj_find_symbol(f, name);
+		if (sym && !(ELF_ST_BIND(sym->info) == STB_LOCAL)) {
+#ifdef SYMBOL_PREFIX
+			/* Put NAME_BUF into more permanent storage.  */
+			name = xmalloc(name_size);
+			strcpy(name, name_buf);
+#endif
+			sym = obj_add_symbol(f, name, -1,
+					ELF_ST_INFO(STB_GLOBAL,
+						STT_NOTYPE),
+					idx, s->value, 0);
+			/* Did our symbol just get installed?  If so, mark the
+			   module as "used".  */
+			if (sym->secidx == idx)
+				used = 1;
+		}
+	}
+
+	return used;
+}
+
+static void add_kernel_symbols(struct obj_file *f)
+{
+	struct external_module *m;
+	int i, nused = 0;
+
+	/* Add module symbols first.  */
+
+	for (i = 0, m = ext_modules; i < n_ext_modules; ++i, ++m) {
+		if (m->nsyms
+		 && add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->syms, m->nsyms)
+		) {
+			m->used = 1;
+			++nused;
+		}
+	}
+
+	n_ext_modules_used = nused;
+
+	/* And finally the symbols from the kernel proper.  */
+
+	if (nksyms)
+		add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms);
+}
+
+static char *get_modinfo_value(struct obj_file *f, const char *key)
+{
+	struct obj_section *sec;
+	char *p, *v, *n, *ep;
+	size_t klen = strlen(key);
+
+	sec = obj_find_section(f, ".modinfo");
+	if (sec == NULL)
+		return NULL;
+	p = sec->contents;
+	ep = p + sec->header.sh_size;
+	while (p < ep) {
+		v = strchr(p, '=');
+		n = strchr(p, '\0');
+		if (v) {
+			if (p + klen == v && strncmp(p, key, klen) == 0)
+				return v + 1;
+		} else {
+			if (p + klen == n && strcmp(p, key) == 0)
+				return n;
+		}
+		p = n + 1;
+	}
+
+	return NULL;
+}
+
+
+/*======================================================================*/
+/* Functions relating to module loading after 2.1.18.  */
+
+/* From Linux-2.6 sources */
+/* You can use " around spaces, but can't escape ". */
+/* Hyphens and underscores equivalent in parameter names. */
+static char *next_arg(char *args, char **param, char **val)
+{
+	unsigned int i, equals = 0;
+	int in_quote = 0, quoted = 0;
+	char *next;
+
+	if (*args == '"') {
+		args++;
+		in_quote = 1;
+		quoted = 1;
+	}
+
+	for (i = 0; args[i]; i++) {
+		if (args[i] == ' ' && !in_quote)
+			break;
+		if (equals == 0) {
+			if (args[i] == '=')
+				equals = i;
+		}
+		if (args[i] == '"')
+			in_quote = !in_quote;
+	}
+
+	*param = args;
+	if (!equals)
+		*val = NULL;
+	else {
+		args[equals] = '\0';
+		*val = args + equals + 1;
+
+		/* Don't include quotes in value. */
+		if (**val == '"') {
+			(*val)++;
+			if (args[i-1] == '"')
+				args[i-1] = '\0';
+		}
+		if (quoted && args[i-1] == '"')
+			args[i-1] = '\0';
+	}
+
+	if (args[i]) {
+		args[i] = '\0';
+		next = args + i + 1;
+	} else
+		next = args + i;
+
+	/* Chew up trailing spaces. */
+	return skip_whitespace(next);
+}
+
+static void
+new_process_module_arguments(struct obj_file *f, const char *options)
+{
+	char *xoptions, *pos;
+	char *param, *val;
+
+	xoptions = pos = xstrdup(skip_whitespace(options));
+	while (*pos) {
+		unsigned long charssize = 0;
+		char *tmp, *contents, *loc, *pinfo, *p;
+		struct obj_symbol *sym;
+		int min, max, n, len;
+
+		pos = next_arg(pos, &param, &val);
+
+		tmp = xasprintf("parm_%s", param);
+		pinfo = get_modinfo_value(f, tmp);
+		free(tmp);
+		if (pinfo == NULL)
+			bb_error_msg_and_die("invalid parameter %s", param);
+
+#ifdef SYMBOL_PREFIX
+		tmp = xasprintf(SYMBOL_PREFIX "%s", param);
+		sym = obj_find_symbol(f, tmp);
+		free(tmp);
+#else
+		sym = obj_find_symbol(f, param);
+#endif
+
+		/* Also check that the parameter was not resolved from the kernel.  */
+		if (sym == NULL || sym->secidx > SHN_HIRESERVE)
+			bb_error_msg_and_die("symbol for parameter %s not found", param);
+
+		/* Number of parameters */
+		min = max = 1;
+		if (isdigit(*pinfo)) {
+			min = max = strtoul(pinfo, &pinfo, 10);
+			if (*pinfo == '-')
+				max = strtoul(pinfo + 1, &pinfo, 10);
+		}
+
+		contents = f->sections[sym->secidx]->contents;
+		loc = contents + sym->value;
+
+		if (*pinfo == 'c') {
+			if (!isdigit(pinfo[1])) {
+				bb_error_msg_and_die("parameter type 'c' for %s must be followed by"
+						     " the maximum size", param);
+			}
+			charssize = strtoul(pinfo + 1, NULL, 10);
+		}
+
+		if (val == NULL) {
+			if (*pinfo != 'b')
+				bb_error_msg_and_die("argument expected for parameter %s", param);
+			val = (char *) "1";
+		}
+
+		/* Parse parameter values */
+		n = 0;
+		p = val;
+		while (*p) {
+			char sv_ch;
+			char *endp;
+
+			if (++n > max)
+				bb_error_msg_and_die("too many values for %s (max %d)", param, max);
+
+			switch (*pinfo) {
+			case 's':
+				len = strcspn(p, ",");
+				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, ",");
+				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);
+				p = endp; /* gcc likes temp var for &endp */
+				break;
+			case 'h':
+				*(short *) loc = strtoul(p, &endp, 0);
+				loc += tgt_sizeof_short;
+				p = endp;
+				break;
+			case 'i':
+				*(int *) loc = strtoul(p, &endp, 0);
+				loc += tgt_sizeof_int;
+				p = endp;
+				break;
+			case 'l':
+				*(long *) loc = strtoul(p, &endp, 0);
+				loc += tgt_sizeof_long;
+				p = endp;
+				break;
+			default:
+				bb_error_msg_and_die("unknown parameter type '%c' for %s",
+						     *pinfo, param);
+			}
+
+			p = skip_whitespace(p);
+			if (*p != ',')
+				break;
+			p = skip_whitespace(p + 1);
+		}
+
+		if (n < min)
+			bb_error_msg_and_die("parameter %s requires at least %d arguments", param, min);
+		if (*p != '\0')
+			bb_error_msg_and_die("invalid argument syntax for %s", param);
+	}
+
+	free(xoptions);
+}
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+static int new_is_module_checksummed(struct obj_file *f)
+{
+	const char *p = get_modinfo_value(f, "using_checksums");
+	if (p)
+		return xatoi(p);
+	return 0;
+}
+
+/* Get the module's kernel version in the canonical integer form.  */
+
+static int
+new_get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+	char *p, *q;
+	int a, b, c;
+
+	p = get_modinfo_value(f, "kernel_version");
+	if (p == NULL)
+		return -1;
+	safe_strncpy(str, p, STRVERSIONLEN);
+
+	a = strtoul(p, &p, 10);
+	if (*p != '.')
+		return -1;
+	b = strtoul(p + 1, &p, 10);
+	if (*p != '.')
+		return -1;
+	c = strtoul(p + 1, &q, 10);
+	if (p + 1 == q)
+		return -1;
+
+	return a << 16 | b << 8 | c;
+}
+
+#endif   /* FEATURE_INSMOD_VERSION_CHECKING */
+
+
+/* Fetch the loaded modules, and all currently exported symbols.  */
+
+static void new_get_kernel_symbols(void)
+{
+	char *module_names, *mn;
+	struct external_module *modules, *m;
+	struct new_module_symbol *syms, *s;
+	size_t ret, bufsize, nmod, nsyms, i, j;
+
+	/* Collect the loaded modules.  */
+
+	bufsize = 256;
+	module_names = xmalloc(bufsize);
+
+ retry_modules_load:
+	if (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) {
+		if (errno == ENOSPC && bufsize < ret) {
+			bufsize = ret;
+			module_names = xrealloc(module_names, bufsize);
+			goto retry_modules_load;
+		}
+		bb_perror_msg_and_die("QM_MODULES");
+	}
+
+	n_ext_modules = nmod = ret;
+
+	/* Collect the modules' symbols.  */
+
+	if (nmod) {
+		ext_modules = modules = xzalloc(nmod * sizeof(*modules));
+		for (i = 0, mn = module_names, m = modules;
+				i < nmod; ++i, ++m, mn += strlen(mn) + 1) {
+			struct new_module_info info;
+
+			if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) {
+				if (errno == ENOENT) {
+					/* The module was removed out from underneath us.  */
+					continue;
+				}
+				bb_perror_msg_and_die("query_module: QM_INFO: %s", mn);
+			}
+
+			bufsize = 1024;
+			syms = xmalloc(bufsize);
+ retry_mod_sym_load:
+			if (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) {
+				switch (errno) {
+					case ENOSPC:
+						bufsize = ret;
+						syms = xrealloc(syms, bufsize);
+						goto retry_mod_sym_load;
+					case ENOENT:
+						/* The module was removed out from underneath us.  */
+						continue;
+					default:
+						bb_perror_msg_and_die("query_module: QM_SYMBOLS: %s", mn);
+				}
+			}
+			nsyms = ret;
+
+			m->name = mn;
+			m->addr = info.addr;
+			m->nsyms = nsyms;
+			m->syms = syms;
+
+			for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+				s->name += (unsigned long) syms;
+			}
+		}
+	}
+
+	/* Collect the kernel's symbols.  */
+
+	bufsize = 16 * 1024;
+	syms = xmalloc(bufsize);
+ retry_kern_sym_load:
+	if (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) {
+		if (errno == ENOSPC && bufsize < ret) {
+			bufsize = ret;
+			syms = xrealloc(syms, bufsize);
+			goto retry_kern_sym_load;
+		}
+		bb_perror_msg_and_die("kernel: QM_SYMBOLS");
+	}
+	nksyms = nsyms = ret;
+	ksyms = syms;
+
+	for (j = 0, s = syms; j < nsyms; ++j, ++s) {
+		s->name += (unsigned long) syms;
+	}
+}
+
+
+/* Return the kernel symbol checksum version, or zero if not used.  */
+
+static int new_is_kernel_checksummed(void)
+{
+	struct new_module_symbol *s;
+	size_t i;
+
+	/* Using_Versions is not the first symbol, but it should be in there.  */
+
+	for (i = 0, s = ksyms; i < nksyms; ++i, ++s)
+		if (strcmp((char *) s->name, "Using_Versions") == 0)
+			return s->value;
+
+	return 0;
+}
+
+
+static void new_create_this_module(struct obj_file *f, const char *m_name)
+{
+	struct obj_section *sec;
+
+	sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long,
+			sizeof(struct new_module));
+	/* done by obj_create_alloced_section_first: */
+	/*memset(sec->contents, 0, sizeof(struct new_module));*/
+
+	obj_add_symbol(f, SPFX "__this_module", -1,
+			ELF_ST_INFO(STB_LOCAL, STT_OBJECT), sec->idx, 0,
+			sizeof(struct new_module));
+
+	obj_string_patch(f, sec->idx, offsetof(struct new_module, name),
+			m_name);
+}
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add an entry to the __ksymtab section, creating it if necessary */
+static void new_add_ksymtab(struct obj_file *f, struct obj_symbol *sym)
+{
+	struct obj_section *sec;
+	ElfW(Addr) ofs;
+
+	/* ensure __ksymtab is allocated, EXPORT_NOSYMBOLS creates a non-alloc section.
+	 * If __ksymtab is defined but not marked alloc, x out the first character
+	 * (no obj_delete routine) and create a new __ksymtab with the correct
+	 * characteristics.
+	 */
+	sec = obj_find_section(f, "__ksymtab");
+	if (sec && !(sec->header.sh_flags & SHF_ALLOC)) {
+		*((char *)(sec->name)) = 'x';	/* override const */
+		sec = NULL;
+	}
+	if (!sec)
+		sec = obj_create_alloced_section(f, "__ksymtab",
+				tgt_sizeof_void_p, 0);
+	if (!sec)
+		return;
+	sec->header.sh_flags |= SHF_ALLOC;
+	/* Empty section might be byte-aligned */
+	sec->header.sh_addralign = tgt_sizeof_void_p;
+	ofs = sec->header.sh_size;
+	obj_symbol_patch(f, sec->idx, ofs, sym);
+	obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p, sym->name);
+	obj_extend_section(sec, 2 * tgt_sizeof_char_p);
+}
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+static int new_create_module_ksymtab(struct obj_file *f)
+{
+	struct obj_section *sec;
+	int i;
+
+	/* We must always add the module references.  */
+
+	if (n_ext_modules_used) {
+		struct new_module_ref *dep;
+		struct obj_symbol *tm;
+
+		sec = obj_create_alloced_section(f, ".kmodtab", tgt_sizeof_void_p,
+				(sizeof(struct new_module_ref)
+				 * n_ext_modules_used));
+		if (!sec)
+			return 0;
+
+		tm = obj_find_symbol(f, SPFX "__this_module");
+		dep = (struct new_module_ref *) sec->contents;
+		for (i = 0; i < n_ext_modules; ++i)
+			if (ext_modules[i].used) {
+				dep->dep = ext_modules[i].addr;
+				obj_symbol_patch(f, sec->idx,
+						(char *) &dep->ref - sec->contents, tm);
+				dep->next_ref = 0;
+				++dep;
+			}
+	}
+
+	if (!flag_noexport && !obj_find_section(f, "__ksymtab")) {
+		size_t nsyms;
+		int *loaded;
+
+		sec = obj_create_alloced_section(f, "__ksymtab", tgt_sizeof_void_p, 0);
+
+		/* We don't want to export symbols residing in sections that
+		   aren't loaded.  There are a number of these created so that
+		   we make sure certain module options don't appear twice.  */
+		i = f->header.e_shnum;
+		loaded = alloca(sizeof(int) * i);
+		while (--i >= 0)
+			loaded[i] = (f->sections[i]->header.sh_flags & SHF_ALLOC) != 0;
+
+		for (nsyms = i = 0; i < HASH_BUCKETS; ++i) {
+			struct obj_symbol *sym;
+			for (sym = f->symtab[i]; sym; sym = sym->next) {
+				if (ELF_ST_BIND(sym->info) != STB_LOCAL
+				 && sym->secidx <= SHN_HIRESERVE
+				 && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx])
+				) {
+					ElfW(Addr) ofs = nsyms * 2 * tgt_sizeof_void_p;
+
+					obj_symbol_patch(f, sec->idx, ofs, sym);
+					obj_string_patch(f, sec->idx, ofs + tgt_sizeof_void_p,
+							sym->name);
+					nsyms++;
+				}
+			}
+		}
+
+		obj_extend_section(sec, nsyms * 2 * tgt_sizeof_char_p);
+	}
+
+	return 1;
+}
+
+
+static int
+new_init_module(const char *m_name, struct obj_file *f, unsigned long m_size)
+{
+	struct new_module *module;
+	struct obj_section *sec;
+	void *image;
+	int ret;
+	tgt_long m_addr;
+
+	sec = obj_find_section(f, ".this");
+	if (!sec || !sec->contents) {
+		bb_perror_msg_and_die("corrupt module %s?", m_name);
+	}
+	module = (struct new_module *) sec->contents;
+	m_addr = sec->header.sh_addr;
+
+	module->size_of_struct = sizeof(*module);
+	module->size = m_size;
+	module->flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0;
+
+	sec = obj_find_section(f, "__ksymtab");
+	if (sec && sec->header.sh_size) {
+		module->syms = sec->header.sh_addr;
+		module->nsyms = sec->header.sh_size / (2 * tgt_sizeof_char_p);
+	}
+
+	if (n_ext_modules_used) {
+		sec = obj_find_section(f, ".kmodtab");
+		module->deps = sec->header.sh_addr;
+		module->ndeps = n_ext_modules_used;
+	}
+
+	module->init = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "init_module"));
+	module->cleanup = obj_symbol_final_value(f, obj_find_symbol(f, SPFX "cleanup_module"));
+
+	sec = obj_find_section(f, "__ex_table");
+	if (sec) {
+		module->ex_table_start = sec->header.sh_addr;
+		module->ex_table_end = sec->header.sh_addr + sec->header.sh_size;
+	}
+
+	sec = obj_find_section(f, ".text.init");
+	if (sec) {
+		module->runsize = sec->header.sh_addr - m_addr;
+	}
+	sec = obj_find_section(f, ".data.init");
+	if (sec) {
+		if (!module->runsize
+		 || module->runsize > sec->header.sh_addr - m_addr
+		) {
+			module->runsize = sec->header.sh_addr - m_addr;
+		}
+	}
+	sec = obj_find_section(f, ARCHDATA_SEC_NAME);
+	if (sec && sec->header.sh_size) {
+		module->archdata_start = (void*)sec->header.sh_addr;
+		module->archdata_end = module->archdata_start + sec->header.sh_size;
+	}
+	sec = obj_find_section(f, KALLSYMS_SEC_NAME);
+	if (sec && sec->header.sh_size) {
+		module->kallsyms_start = (void*)sec->header.sh_addr;
+		module->kallsyms_end = module->kallsyms_start + sec->header.sh_size;
+	}
+
+	/* Whew!  All of the initialization is complete.  Collect the final
+	   module image and give it to the kernel.  */
+
+	image = xmalloc(m_size);
+	obj_create_image(f, image);
+
+	ret = init_module(m_name, (struct new_module *) image);
+	if (ret)
+		bb_perror_msg("init_module: %s", m_name);
+
+	free(image);
+
+	return ret == 0;
+}
+
+
+/*======================================================================*/
+
+static void
+obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+				 const char *string)
+{
+	struct obj_string_patch *p;
+	struct obj_section *strsec;
+	size_t len = strlen(string) + 1;
+	char *loc;
+
+	p = xzalloc(sizeof(*p));
+	p->next = f->string_patches;
+	p->reloc_secidx = secidx;
+	p->reloc_offset = offset;
+	f->string_patches = p;
+
+	strsec = obj_find_section(f, ".kstrtab");
+	if (strsec == NULL) {
+		strsec = obj_create_alloced_section(f, ".kstrtab", 1, len);
+		/*p->string_offset = 0;*/
+		loc = strsec->contents;
+	} else {
+		p->string_offset = strsec->header.sh_size;
+		loc = obj_extend_section(strsec, len);
+	}
+	memcpy(loc, string, len);
+}
+
+static void
+obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset,
+		struct obj_symbol *sym)
+{
+	struct obj_symbol_patch *p;
+
+	p = xmalloc(sizeof(*p));
+	p->next = f->symbol_patches;
+	p->reloc_secidx = secidx;
+	p->reloc_offset = offset;
+	p->sym = sym;
+	f->symbol_patches = p;
+}
+
+static void obj_check_undefineds(struct obj_file *f)
+{
+	unsigned i;
+
+	for (i = 0; i < HASH_BUCKETS; ++i) {
+		struct obj_symbol *sym;
+		for (sym = f->symtab[i]; sym; sym = sym->next) {
+			if (sym->secidx == SHN_UNDEF) {
+				if (ELF_ST_BIND(sym->info) == STB_WEAK) {
+					sym->secidx = SHN_ABS;
+					sym->value = 0;
+				} else {
+					if (!flag_quiet)
+						bb_error_msg_and_die("unresolved symbol %s", sym->name);
+				}
+			}
+		}
+	}
+}
+
+static void obj_allocate_commons(struct obj_file *f)
+{
+	struct common_entry {
+		struct common_entry *next;
+		struct obj_symbol *sym;
+	} *common_head = NULL;
+
+	unsigned long i;
+
+	for (i = 0; i < HASH_BUCKETS; ++i) {
+		struct obj_symbol *sym;
+		for (sym = f->symtab[i]; sym; sym = sym->next) {
+			if (sym->secidx == SHN_COMMON) {
+				/* Collect all COMMON symbols and sort them by size so as to
+				   minimize space wasted by alignment requirements.  */
+				struct common_entry **p, *n;
+				for (p = &common_head; *p; p = &(*p)->next)
+					if (sym->size <= (*p)->sym->size)
+						break;
+				n = alloca(sizeof(*n));
+				n->next = *p;
+				n->sym = sym;
+				*p = n;
+			}
+		}
+	}
+
+	for (i = 1; i < f->local_symtab_size; ++i) {
+		struct obj_symbol *sym = f->local_symtab[i];
+		if (sym && sym->secidx == SHN_COMMON) {
+			struct common_entry **p, *n;
+			for (p = &common_head; *p; p = &(*p)->next) {
+				if (sym == (*p)->sym)
+					break;
+				if (sym->size < (*p)->sym->size) {
+					n = alloca(sizeof(*n));
+					n->next = *p;
+					n->sym = sym;
+					*p = n;
+					break;
+				}
+			}
+		}
+	}
+
+	if (common_head) {
+		/* Find the bss section.  */
+		for (i = 0; i < f->header.e_shnum; ++i)
+			if (f->sections[i]->header.sh_type == SHT_NOBITS)
+				break;
+
+		/* If for some reason there hadn't been one, create one.  */
+		if (i == f->header.e_shnum) {
+			struct obj_section *sec;
+
+			f->header.e_shnum++;
+			f->sections = xrealloc_vector(f->sections, 2, i);
+			f->sections[i] = sec = arch_new_section();
+
+			sec->header.sh_type = SHT_PROGBITS;
+			sec->header.sh_flags = SHF_WRITE | SHF_ALLOC;
+			sec->name = ".bss";
+			sec->idx = i;
+		}
+
+		/* Allocate the COMMONS.  */
+		{
+			ElfW(Addr) bss_size = f->sections[i]->header.sh_size;
+			ElfW(Addr) max_align = f->sections[i]->header.sh_addralign;
+			struct common_entry *c;
+
+			for (c = common_head; c; c = c->next) {
+				ElfW(Addr) align = c->sym->value;
+
+				if (align > max_align)
+					max_align = align;
+				if (bss_size & (align - 1))
+					bss_size = (bss_size | (align - 1)) + 1;
+
+				c->sym->secidx = i;
+				c->sym->value = bss_size;
+
+				bss_size += c->sym->size;
+			}
+
+			f->sections[i]->header.sh_size = bss_size;
+			f->sections[i]->header.sh_addralign = max_align;
+		}
+	}
+
+	/* For the sake of patch relocation and parameter initialization,
+	   allocate zeroed data for NOBITS sections now.  Note that after
+	   this we cannot assume NOBITS are really empty.  */
+	for (i = 0; i < f->header.e_shnum; ++i) {
+		struct obj_section *s = f->sections[i];
+		if (s->header.sh_type == SHT_NOBITS) {
+			s->contents = NULL;
+			if (s->header.sh_size != 0)
+				s->contents = xzalloc(s->header.sh_size);
+			s->header.sh_type = SHT_PROGBITS;
+		}
+	}
+}
+
+static unsigned long obj_load_size(struct obj_file *f)
+{
+	unsigned long dot = 0;
+	struct obj_section *sec;
+
+	/* Finalize the positions of the sections relative to one another.  */
+
+	for (sec = f->load_order; sec; sec = sec->load_next) {
+		ElfW(Addr) align;
+
+		align = sec->header.sh_addralign;
+		if (align && (dot & (align - 1)))
+			dot = (dot | (align - 1)) + 1;
+
+		sec->header.sh_addr = dot;
+		dot += sec->header.sh_size;
+	}
+
+	return dot;
+}
+
+static int obj_relocate(struct obj_file *f, ElfW(Addr) base)
+{
+	int i, n = f->header.e_shnum;
+	int ret = 1;
+
+	/* Finalize the addresses of the sections.  */
+
+	f->baseaddr = base;
+	for (i = 0; i < n; ++i)
+		f->sections[i]->header.sh_addr += base;
+
+	/* And iterate over all of the relocations.  */
+
+	for (i = 0; i < n; ++i) {
+		struct obj_section *relsec, *symsec, *targsec, *strsec;
+		ElfW(RelM) * rel, *relend;
+		ElfW(Sym) * symtab;
+		const char *strtab;
+
+		relsec = f->sections[i];
+		if (relsec->header.sh_type != SHT_RELM)
+			continue;
+
+		symsec = f->sections[relsec->header.sh_link];
+		targsec = f->sections[relsec->header.sh_info];
+		strsec = f->sections[symsec->header.sh_link];
+
+		rel = (ElfW(RelM) *) relsec->contents;
+		relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM)));
+		symtab = (ElfW(Sym) *) symsec->contents;
+		strtab = (const char *) strsec->contents;
+
+		for (; rel < relend; ++rel) {
+			ElfW(Addr) value = 0;
+			struct obj_symbol *intsym = NULL;
+			unsigned long symndx;
+			ElfW(Sym) *extsym = NULL;
+			const char *errmsg;
+
+			/* Attempt to find a value to use for this relocation.  */
+
+			symndx = ELF_R_SYM(rel->r_info);
+			if (symndx) {
+				/* Note we've already checked for undefined symbols.  */
+
+				extsym = &symtab[symndx];
+				if (ELF_ST_BIND(extsym->st_info) == STB_LOCAL) {
+					/* Local symbols we look up in the local table to be sure
+					   we get the one that is really intended.  */
+					intsym = f->local_symtab[symndx];
+				} else {
+					/* Others we look up in the hash table.  */
+					const char *name;
+					if (extsym->st_name)
+						name = strtab + extsym->st_name;
+					else
+						name = f->sections[extsym->st_shndx]->name;
+					intsym = obj_find_symbol(f, name);
+				}
+
+				value = obj_symbol_final_value(f, intsym);
+				intsym->referenced = 1;
+			}
+#if SHT_RELM == SHT_RELA
+#if defined(__alpha__) && defined(AXP_BROKEN_GAS)
+			/* Work around a nasty GAS bug, that is fixed as of 2.7.0.9.  */
+			if (!extsym || !extsym->st_name
+			 || ELF_ST_BIND(extsym->st_info) != STB_LOCAL)
+#endif
+				value += rel->r_addend;
+#endif
+
+			/* Do it! */
+			switch (arch_apply_relocation
+					(f, targsec, /*symsec,*/ intsym, rel, value)
+			) {
+			case obj_reloc_ok:
+				break;
+
+			case obj_reloc_overflow:
+				errmsg = "Relocation overflow";
+				goto bad_reloc;
+			case obj_reloc_dangerous:
+				errmsg = "Dangerous relocation";
+				goto bad_reloc;
+			case obj_reloc_unhandled:
+				errmsg = "Unhandled relocation";
+bad_reloc:
+				if (extsym) {
+					bb_error_msg("%s of type %ld for %s", errmsg,
+							(long) ELF_R_TYPE(rel->r_info),
+							strtab + extsym->st_name);
+				} else {
+					bb_error_msg("%s of type %ld", errmsg,
+							(long) ELF_R_TYPE(rel->r_info));
+				}
+				ret = 0;
+				break;
+			}
+		}
+	}
+
+	/* Finally, take care of the patches.  */
+
+	if (f->string_patches) {
+		struct obj_string_patch *p;
+		struct obj_section *strsec;
+		ElfW(Addr) strsec_base;
+		strsec = obj_find_section(f, ".kstrtab");
+		strsec_base = strsec->header.sh_addr;
+
+		for (p = f->string_patches; p; p = p->next) {
+			struct obj_section *targsec = f->sections[p->reloc_secidx];
+			*(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+				= strsec_base + p->string_offset;
+		}
+	}
+
+	if (f->symbol_patches) {
+		struct obj_symbol_patch *p;
+
+		for (p = f->symbol_patches; p; p = p->next) {
+			struct obj_section *targsec = f->sections[p->reloc_secidx];
+			*(ElfW(Addr) *) (targsec->contents + p->reloc_offset)
+				= obj_symbol_final_value(f, p->sym);
+		}
+	}
+
+	return ret;
+}
+
+static int obj_create_image(struct obj_file *f, char *image)
+{
+	struct obj_section *sec;
+	ElfW(Addr) base = f->baseaddr;
+
+	for (sec = f->load_order; sec; sec = sec->load_next) {
+		char *secimg;
+
+		if (sec->contents == 0 || sec->header.sh_size == 0)
+			continue;
+
+		secimg = image + (sec->header.sh_addr - base);
+
+		/* Note that we allocated data for NOBITS sections earlier.  */
+		memcpy(secimg, sec->contents, sec->header.sh_size);
+	}
+
+	return 1;
+}
+
+/*======================================================================*/
+
+static struct obj_file *obj_load(char *image, size_t image_size, int loadprogbits)
+{
+	typedef uint32_t aliased_uint32_t FIX_ALIASING;
+#if BB_LITTLE_ENDIAN
+# define ELFMAG_U32 ((uint32_t)(ELFMAG0 + 0x100 * (ELFMAG1 + (0x100 * (ELFMAG2 + 0x100 * ELFMAG3)))))
+#else
+# define ELFMAG_U32 ((uint32_t)((((ELFMAG0 * 0x100) + ELFMAG1) * 0x100 + ELFMAG2) * 0x100 + ELFMAG3))
+#endif
+	struct obj_file *f;
+	ElfW(Shdr) * section_headers;
+	size_t shnum, i;
+	char *shstrtab;
+
+	/* Read the file header.  */
+
+	f = arch_new_file();
+	f->symbol_cmp = strcmp;
+	f->symbol_hash = obj_elf_hash;
+	f->load_order_search_start = &f->load_order;
+
+	if (image_size < sizeof(f->header))
+		bb_error_msg_and_die("error while loading ELF header");
+	memcpy(&f->header, image, sizeof(f->header));
+
+	if (*(aliased_uint32_t*)(&f->header.e_ident) != ELFMAG_U32) {
+		bb_error_msg_and_die("not an ELF file");
+	}
+	if (f->header.e_ident[EI_CLASS] != ELFCLASSM
+	 || f->header.e_ident[EI_DATA] != (BB_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB)
+	 || f->header.e_ident[EI_VERSION] != EV_CURRENT
+	 || !MATCH_MACHINE(f->header.e_machine)
+	) {
+		bb_error_msg_and_die("ELF file not for this architecture");
+	}
+	if (f->header.e_type != ET_REL) {
+		bb_error_msg_and_die("ELF file not a relocatable object");
+	}
+
+	/* Read the section headers.  */
+
+	if (f->header.e_shentsize != sizeof(ElfW(Shdr))) {
+		bb_error_msg_and_die("section header size mismatch: %lu != %lu",
+				(unsigned long) f->header.e_shentsize,
+				(unsigned long) sizeof(ElfW(Shdr)));
+	}
+
+	shnum = f->header.e_shnum;
+	/* Growth of ->sections vector will be done by
+	 * xrealloc_vector(..., 2, ...), therefore we must allocate
+	 * at least 2^2 = 4 extra elements here. */
+	f->sections = xzalloc(sizeof(f->sections[0]) * (shnum + 4));
+
+	section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);
+	if (image_size < f->header.e_shoff + sizeof(ElfW(Shdr)) * shnum)
+		bb_error_msg_and_die("error while loading section headers");
+	memcpy(section_headers, image + f->header.e_shoff, sizeof(ElfW(Shdr)) * shnum);
+
+	/* Read the section data.  */
+
+	for (i = 0; i < shnum; ++i) {
+		struct obj_section *sec;
+
+		f->sections[i] = sec = arch_new_section();
+
+		sec->header = section_headers[i];
+		sec->idx = i;
+
+		if (sec->header.sh_size) {
+			switch (sec->header.sh_type) {
+			case SHT_NULL:
+			case SHT_NOTE:
+			case SHT_NOBITS:
+				/* ignore */
+				break;
+			case SHT_PROGBITS:
+#if LOADBITS
+				if (!loadprogbits) {
+					sec->contents = NULL;
+					break;
+				}
+#endif
+			case SHT_SYMTAB:
+			case SHT_STRTAB:
+			case SHT_RELM:
+#if defined(__mips__)
+			case SHT_MIPS_DWARF:
+#endif
+				sec->contents = NULL;
+				if (sec->header.sh_size > 0) {
+					sec->contents = xmalloc(sec->header.sh_size);
+					if (image_size < (sec->header.sh_offset + sec->header.sh_size))
+						bb_error_msg_and_die("error while loading section data");
+					memcpy(sec->contents, image + sec->header.sh_offset, sec->header.sh_size);
+				}
+				break;
+#if SHT_RELM == SHT_REL
+			case SHT_RELA:
+				bb_error_msg_and_die("RELA relocations not supported on this architecture");
+#else
+			case SHT_REL:
+				bb_error_msg_and_die("REL relocations not supported on this architecture");
+#endif
+			default:
+				if (sec->header.sh_type >= SHT_LOPROC) {
+					/* Assume processor specific section types are debug
+					   info and can safely be ignored.  If this is ever not
+					   the case (Hello MIPS?), don't put ifdefs here but
+					   create an arch_load_proc_section().  */
+					break;
+				}
+
+				bb_error_msg_and_die("can't handle sections of type %ld",
+						(long) sec->header.sh_type);
+			}
+		}
+	}
+
+	/* Do what sort of interpretation as needed by each section.  */
+
+	shstrtab = f->sections[f->header.e_shstrndx]->contents;
+
+	for (i = 0; i < shnum; ++i) {
+		struct obj_section *sec = f->sections[i];
+		sec->name = shstrtab + sec->header.sh_name;
+	}
+
+	for (i = 0; i < shnum; ++i) {
+		struct obj_section *sec = f->sections[i];
+
+		/* .modinfo should be contents only but gcc has no attribute for that.
+		 * The kernel may have marked .modinfo as ALLOC, ignore this bit.
+		 */
+		if (strcmp(sec->name, ".modinfo") == 0)
+			sec->header.sh_flags &= ~SHF_ALLOC;
+
+		if (sec->header.sh_flags & SHF_ALLOC)
+			obj_insert_section_load_order(f, sec);
+
+		switch (sec->header.sh_type) {
+		case SHT_SYMTAB:
+			{
+				unsigned long nsym, j;
+				char *strtab;
+				ElfW(Sym) * sym;
+
+				if (sec->header.sh_entsize != sizeof(ElfW(Sym))) {
+					bb_error_msg_and_die("symbol size mismatch: %lu != %lu",
+							(unsigned long) sec->header.sh_entsize,
+							(unsigned long) sizeof(ElfW(Sym)));
+				}
+
+				nsym = sec->header.sh_size / sizeof(ElfW(Sym));
+				strtab = f->sections[sec->header.sh_link]->contents;
+				sym = (ElfW(Sym) *) sec->contents;
+
+				/* Allocate space for a table of local symbols.  */
+				j = f->local_symtab_size = sec->header.sh_info;
+				f->local_symtab = xzalloc(j * sizeof(struct obj_symbol *));
+
+				/* Insert all symbols into the hash table.  */
+				for (j = 1, ++sym; j < nsym; ++j, ++sym) {
+					ElfW(Addr) val = sym->st_value;
+					const char *name;
+					if (sym->st_name)
+						name = strtab + sym->st_name;
+					else if (sym->st_shndx < shnum)
+						name = f->sections[sym->st_shndx]->name;
+					else
+						continue;
+#if defined(__SH5__)
+					/*
+					 * For sh64 it is possible that the target of a branch
+					 * requires a mode switch (32 to 16 and back again).
+					 *
+					 * This is implied by the lsb being set in the target
+					 * address for SHmedia mode and clear for SHcompact.
+					 */
+					val |= sym->st_other & 4;
+#endif
+					obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx,
+							val, sym->st_size);
+				}
+			}
+			break;
+
+		case SHT_RELM:
+			if (sec->header.sh_entsize != sizeof(ElfW(RelM))) {
+				bb_error_msg_and_die("relocation entry size mismatch: %lu != %lu",
+						(unsigned long) sec->header.sh_entsize,
+						(unsigned long) sizeof(ElfW(RelM)));
+			}
+			break;
+			/* XXX  Relocation code from modutils-2.3.19 is not here.
+			 * Why?  That's about 20 lines of code from obj/obj_load.c,
+			 * which gets done in a second pass through the sections.
+			 * This BusyBox insmod does similar work in obj_relocate(). */
+		}
+	}
+
+	return f;
+}
+
+#if ENABLE_FEATURE_INSMOD_LOADINKMEM
+/*
+ * load the unloaded sections directly into the memory allocated by
+ * kernel for the module
+ */
+
+static int obj_load_progbits(char *image, size_t image_size, struct obj_file *f, char *imagebase)
+{
+	ElfW(Addr) base = f->baseaddr;
+	struct obj_section* sec;
+
+	for (sec = f->load_order; sec; sec = sec->load_next) {
+		/* section already loaded? */
+		if (sec->contents != NULL)
+			continue;
+		if (sec->header.sh_size == 0)
+			continue;
+		sec->contents = imagebase + (sec->header.sh_addr - base);
+		if (image_size < (sec->header.sh_offset + sec->header.sh_size)) {
+			bb_error_msg("error reading ELF section data");
+			return 0; /* need to delete half-loaded module! */
+		}
+		memcpy(sec->contents, image + sec->header.sh_offset, sec->header.sh_size);
+	}
+	return 1;
+}
+#endif
+
+static void hide_special_symbols(struct obj_file *f)
+{
+	static const char *const specials[] = {
+		SPFX "cleanup_module",
+		SPFX "init_module",
+		SPFX "kernel_version",
+		NULL
+	};
+
+	struct obj_symbol *sym;
+	const char *const *p;
+
+	for (p = specials; *p; ++p) {
+		sym = obj_find_symbol(f, *p);
+		if (sym != NULL)
+			sym->info = ELF_ST_INFO(STB_LOCAL, ELF_ST_TYPE(sym->info));
+	}
+}
+
+
+#if ENABLE_FEATURE_CHECK_TAINTED_MODULE
+static int obj_gpl_license(struct obj_file *f, const char **license)
+{
+	struct obj_section *sec;
+	/* This list must match *exactly* the list of allowable licenses in
+	 * linux/include/linux/module.h.  Checking for leading "GPL" will not
+	 * work, somebody will use "GPL sucks, this is proprietary".
+	 */
+	static const char *const gpl_licenses[] = {
+		"GPL",
+		"GPL v2",
+		"GPL and additional rights",
+		"Dual BSD/GPL",
+		"Dual MPL/GPL"
+	};
+
+	sec = obj_find_section(f, ".modinfo");
+	if (sec) {
+		const char *value, *ptr, *endptr;
+		ptr = sec->contents;
+		endptr = ptr + sec->header.sh_size;
+		while (ptr < endptr) {
+			value = strchr(ptr, '=');
+			if (value && strncmp(ptr, "license", value-ptr) == 0) {
+				unsigned i;
+				if (license)
+					*license = value+1;
+				for (i = 0; i < ARRAY_SIZE(gpl_licenses); ++i) {
+					if (strcmp(value+1, gpl_licenses[i]) == 0)
+						return 0;
+				}
+				return 2;
+			}
+			ptr = strchr(ptr, '\0');
+			if (ptr)
+				ptr++;
+			else
+				ptr = endptr;
+		}
+	}
+	return 1;
+}
+
+#define TAINT_FILENAME                  "/proc/sys/kernel/tainted"
+#define TAINT_PROPRIETORY_MODULE        (1 << 0)
+#define TAINT_FORCED_MODULE             (1 << 1)
+#define TAINT_UNSAFE_SMP                (1 << 2)
+#define TAINT_URL                       "http://www.tux.org/lkml/#export-tainted"
+
+static void set_tainted(int fd, const char *m_name,
+		int kernel_has_tainted, int taint,
+		const char *text1, const char *text2)
+{
+	static smallint printed_info;
+
+	char buf[80];
+	int oldval;
+
+	if (fd < 0 && !kernel_has_tainted)
+		return;		/* New modutils on old kernel */
+	printf("Warning: loading %s will taint the kernel: %s%s\n",
+			m_name, text1, text2);
+	if (!printed_info) {
+		printf("  See %s for information about tainted modules\n", TAINT_URL);
+		printed_info = 1;
+	}
+	if (fd >= 0) {
+		read(fd, buf, sizeof(buf)-1);
+		buf[sizeof(buf)-1] = '\0';
+		oldval = strtoul(buf, NULL, 10);
+		sprintf(buf, "%d\n", oldval | taint);
+		xwrite_str(fd, buf);
+	}
+}
+
+/* Check if loading this module will taint the kernel. */
+static void check_tainted_module(struct obj_file *f, const char *m_name)
+{
+	int fd, kernel_has_tainted;
+	const char *ptr;
+
+	kernel_has_tainted = 1;
+	fd = open(TAINT_FILENAME, O_RDWR);
+	if (fd < 0) {
+		if (errno == ENOENT)
+			kernel_has_tainted = 0;
+		else if (errno == EACCES)
+			kernel_has_tainted = 1;
+		else {
+			bb_simple_perror_msg(TAINT_FILENAME);
+			kernel_has_tainted = 0;
+		}
+	}
+
+	switch (obj_gpl_license(f, &ptr)) {
+		case 0:
+			break;
+		case 1:
+			set_tainted(fd, m_name, kernel_has_tainted, TAINT_PROPRIETORY_MODULE, "no license", "");
+			break;
+		default: /* case 2: */
+			/* The module has a non-GPL license so we pretend that the
+			 * kernel always has a taint flag to get a warning even on
+			 * kernels without the proc flag.
+			 */
+			set_tainted(fd, m_name, 1, TAINT_PROPRIETORY_MODULE, "non-GPL license - ", ptr);
+			break;
+	}
+
+	if (flag_force_load)
+		set_tainted(fd, m_name, 1, TAINT_FORCED_MODULE, "forced load", "");
+
+	if (fd >= 0)
+		close(fd);
+}
+#else /* !FEATURE_CHECK_TAINTED_MODULE */
+#define check_tainted_module(x, y) do { } while (0);
+#endif
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections.  this info is used by ksymoops to do better
+ * debugging.
+ */
+#if !ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+#define get_module_version(f, str) get_module_version(str)
+#endif
+static int
+get_module_version(struct obj_file *f, char str[STRVERSIONLEN])
+{
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+	return new_get_module_version(f, str);
+#else
+	strncpy(str, "???", sizeof(str));
+	return -1;
+#endif
+}
+
+/* add module source, timestamp, kernel version and a symbol for the
+ * start of some sections.  this info is used by ksymoops to do better
+ * debugging.
+ */
+static void
+add_ksymoops_symbols(struct obj_file *f, const char *filename,
+		const char *m_name)
+{
+	static const char symprefix[] ALIGN1 = "__insmod_";
+	static const char section_names[][8] = {
+		".text",
+		".rodata",
+		".data",
+		".bss",
+		".sbss"
+	};
+
+	struct obj_section *sec;
+	struct obj_symbol *sym;
+	char *name, *absolute_filename;
+	char str[STRVERSIONLEN];
+	unsigned i;
+	int lm_name, lfilename, use_ksymtab, version;
+	struct stat statbuf;
+
+	/* WARNING: was using realpath, but replaced by readlink to stop using
+	 * lots of stack. But here it seems to be able to cause problems? */
+	absolute_filename = xmalloc_readlink(filename);
+	if (!absolute_filename)
+		absolute_filename = xstrdup(filename);
+
+	lm_name = strlen(m_name);
+	lfilename = strlen(absolute_filename);
+
+	/* add to ksymtab if it already exists or there is no ksymtab and other symbols
+	 * are not to be exported.  otherwise leave ksymtab alone for now, the
+	 * "export all symbols" compatibility code will export these symbols later.
+	 */
+	use_ksymtab = obj_find_section(f, "__ksymtab") || flag_noexport;
+
+	sec = obj_find_section(f, ".this");
+	if (sec) {
+		/* tag the module header with the object name, last modified
+		 * timestamp and module version.  worst case for module version
+		 * is 0xffffff, decimal 16777215.  putting all three fields in
+		 * one symbol is less readable but saves kernel space.
+		 */
+		if (stat(absolute_filename, &statbuf) != 0)
+			statbuf.st_mtime = 0;
+		version = get_module_version(f, str);	/* -1 if not found */
+		name = xasprintf("%s%s_O%s_M%0*lX_V%d",
+				symprefix, m_name, absolute_filename,
+				(int)(2 * sizeof(statbuf.st_mtime)),
+				(long)statbuf.st_mtime,
+				version);
+		sym = obj_add_symbol(f, name, -1,
+				ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+				sec->idx, sec->header.sh_addr, 0);
+		if (use_ksymtab)
+			new_add_ksymtab(f, sym);
+	}
+	free(absolute_filename);
+#ifdef _NOT_SUPPORTED_
+	/* record where the persistent data is going, same address as previous symbol */
+	if (f->persist) {
+		name = xasprintf("%s%s_P%s",
+				symprefix, m_name, f->persist);
+		sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+				sec->idx, sec->header.sh_addr, 0);
+		if (use_ksymtab)
+			new_add_ksymtab(f, sym);
+	}
+#endif
+	/* tag the desired sections if size is non-zero */
+	for (i = 0; i < ARRAY_SIZE(section_names); ++i) {
+		sec = obj_find_section(f, section_names[i]);
+		if (sec && sec->header.sh_size) {
+			name = xasprintf("%s%s_S%s_L%ld",
+					symprefix, m_name, sec->name,
+					(long)sec->header.sh_size);
+			sym = obj_add_symbol(f, name, -1, ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE),
+					sec->idx, sec->header.sh_addr, 0);
+			if (use_ksymtab)
+				new_add_ksymtab(f, sym);
+		}
+	}
+}
+#endif /* FEATURE_INSMOD_KSYMOOPS_SYMBOLS */
+
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP
+static void print_load_map(struct obj_file *f)
+{
+	struct obj_section *sec;
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
+	struct obj_symbol **all, **p;
+	int i, nsyms;
+	char *loaded; /* array of booleans */
+	struct obj_symbol *sym;
+#endif
+	/* Report on the section layout.  */
+	printf("Sections:       Size      %-*s  Align\n",
+			(int) (2 * sizeof(void *)), "Address");
+
+	for (sec = f->load_order; sec; sec = sec->load_next) {
+		int a;
+		unsigned long tmp;
+
+		for (a = -1, tmp = sec->header.sh_addralign; tmp; ++a)
+			tmp >>= 1;
+		if (a == -1)
+			a = 0;
+
+		printf("%-15s %08lx  %0*lx  2**%d\n",
+				sec->name,
+				(long)sec->header.sh_size,
+				(int) (2 * sizeof(void *)),
+				(long)sec->header.sh_addr,
+				a);
+	}
+#if ENABLE_FEATURE_INSMOD_LOAD_MAP_FULL
+	/* Quick reference which section indices are loaded.  */
+	i = f->header.e_shnum;
+	loaded = alloca(i * sizeof(loaded[0]));
+	while (--i >= 0)
+		loaded[i] = ((f->sections[i]->header.sh_flags & SHF_ALLOC) != 0);
+
+	/* Collect the symbols we'll be listing.  */
+	for (nsyms = i = 0; i < HASH_BUCKETS; ++i)
+		for (sym = f->symtab[i]; sym; sym = sym->next)
+			if (sym->secidx <= SHN_HIRESERVE
+			 && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx])
+			) {
+				++nsyms;
+			}
+
+	all = alloca(nsyms * sizeof(all[0]));
+
+	for (i = 0, p = all; i < HASH_BUCKETS; ++i)
+		for (sym = f->symtab[i]; sym; sym = sym->next)
+			if (sym->secidx <= SHN_HIRESERVE
+			 && (sym->secidx >= SHN_LORESERVE || loaded[sym->secidx])
+			) {
+				*p++ = sym;
+			}
+
+	/* And list them.  */
+	printf("\nSymbols:\n");
+	for (p = all; p < all + nsyms; ++p) {
+		char type = '?';
+		unsigned long value;
+
+		sym = *p;
+		if (sym->secidx == SHN_ABS) {
+			type = 'A';
+			value = sym->value;
+		} else if (sym->secidx == SHN_UNDEF) {
+			type = 'U';
+			value = 0;
+		} else {
+			sec = f->sections[sym->secidx];
+
+			if (sec->header.sh_type == SHT_NOBITS)
+				type = 'B';
+			else if (sec->header.sh_flags & SHF_ALLOC) {
+				if (sec->header.sh_flags & SHF_EXECINSTR)
+					type = 'T';
+				else if (sec->header.sh_flags & SHF_WRITE)
+					type = 'D';
+				else
+					type = 'R';
+			}
+			value = sym->value + sec->header.sh_addr;
+		}
+
+		if (ELF_ST_BIND(sym->info) == STB_LOCAL)
+			type |= 0x20; /* tolower. safe for '?' too */
+
+		printf("%0*lx %c %s\n", (int) (2 * sizeof(void *)), value,
+				type, sym->name);
+	}
+#endif
+}
+#else /* !FEATURE_INSMOD_LOAD_MAP */
+static void print_load_map(struct obj_file *f UNUSED_PARAM)
+{
+}
+#endif
+
+int FAST_FUNC bb_init_module_24(const char *m_filename, const char *options)
+{
+	int k_crcs;
+	unsigned long m_size;
+	ElfW(Addr) m_addr;
+	struct obj_file *f;
+	int exit_status = EXIT_FAILURE;
+	char *m_name;
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+	int m_has_modinfo;
+#endif
+	char *image;
+	size_t image_size;
+	bool mmaped;
+
+	image_size = INT_MAX - 4095;
+	mmaped = 0;
+	image = try_to_mmap_module(m_filename, &image_size);
+	if (image) {
+		mmaped = 1;
+	} else {
+		/* Load module into memory and unzip if compressed */
+		image = xmalloc_open_zipped_read_close(m_filename, &image_size);
+		if (!image)
+			return EXIT_FAILURE;
+	}
+
+	m_name = xstrdup(bb_basename(m_filename));
+	/* "module.o[.gz]" -> "module" */
+	*strchrnul(m_name, '.') = '\0';
+
+	f = obj_load(image, image_size, LOADBITS);
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+	/* Version correspondence?  */
+	m_has_modinfo = (get_modinfo_value(f, "kernel_version") != NULL);
+	if (!flag_quiet) {
+		char m_strversion[STRVERSIONLEN];
+		struct utsname uts;
+
+		if (m_has_modinfo) {
+			int m_version = new_get_module_version(f, m_strversion);
+			if (m_version == -1) {
+				bb_error_msg_and_die("can't find the kernel version "
+					"the module was compiled for");
+			}
+		}
+
+		uname(&uts);
+		if (strncmp(uts.release, m_strversion, STRVERSIONLEN) != 0) {
+			bb_error_msg("%skernel-module version mismatch\n"
+				"\t%s was compiled for kernel version %s\n"
+				"\twhile this kernel is version %s",
+				flag_force_load ? "warning: " : "",
+				m_name, m_strversion, uts.release);
+			if (!flag_force_load)
+				goto out;
+		}
+	}
+#endif
+
+	if (query_module(NULL, 0, NULL, 0, NULL))
+		bb_error_msg_and_die("old (unsupported) kernel");
+	new_get_kernel_symbols();
+	k_crcs = new_is_kernel_checksummed();
+
+#if ENABLE_FEATURE_INSMOD_VERSION_CHECKING
+	{
+		int m_crcs = 0;
+		if (m_has_modinfo)
+			m_crcs = new_is_module_checksummed(f);
+		if (m_crcs != k_crcs)
+			obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash);
+	}
+#endif
+
+	/* Let the module know about the kernel symbols.  */
+	add_kernel_symbols(f);
+
+	/* Allocate common symbols, symbol tables, and string tables.  */
+	new_create_this_module(f, m_name);
+	obj_check_undefineds(f);
+	obj_allocate_commons(f);
+	check_tainted_module(f, m_name);
+
+	/* Done with the module name, on to the optional var=value arguments */
+	new_process_module_arguments(f, options);
+
+	arch_create_got(f);
+	hide_special_symbols(f);
+
+#if ENABLE_FEATURE_INSMOD_KSYMOOPS_SYMBOLS
+	add_ksymoops_symbols(f, m_filename, m_name);
+#endif
+
+	new_create_module_ksymtab(f);
+
+	/* Find current size of the module */
+	m_size = obj_load_size(f);
+
+	m_addr = create_module(m_name, m_size);
+	if (m_addr == (ElfW(Addr))(-1)) switch (errno) {
+	case EEXIST:
+		bb_error_msg_and_die("a module named %s already exists", m_name);
+	case ENOMEM:
+		bb_error_msg_and_die("can't allocate kernel memory for module; needed %lu bytes",
+				m_size);
+	default:
+		bb_perror_msg_and_die("create_module: %s", m_name);
+	}
+
+#if !LOADBITS
+	/*
+	 * the PROGBITS section was not loaded by the obj_load
+	 * now we can load them directly into the kernel memory
+	 */
+	if (!obj_load_progbits(image, image_size, f, (char*)m_addr)) {
+		delete_module(m_name, 0);
+		goto out;
+	}
+#endif
+
+	if (!obj_relocate(f, m_addr)) {
+		delete_module(m_name, 0);
+		goto out;
+	}
+
+	if (!new_init_module(m_name, f, m_size)) {
+		delete_module(m_name, 0);
+		goto out;
+	}
+
+	if (flag_print_load_map)
+		print_load_map(f);
+
+	exit_status = EXIT_SUCCESS;
+
+ out:
+	if (mmaped)
+		munmap(image, image_size);
+	else
+		free(image);
+	free(m_name);
+
+	return exit_status;
+}
diff --git a/busybox-1.19.3/modutils/modutils.c b/busybox-1.19.3/modutils/modutils.c
new file mode 100644
index 0000000..6187ca7
--- /dev/null
+++ b/busybox-1.19.3/modutils/modutils.c
@@ -0,0 +1,202 @@
+/*
+ * Common modutils related functions for busybox
+ *
+ * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "modutils.h"
+
+#ifdef __UCLIBC__
+extern int init_module(void *module, unsigned long len, const char *options);
+extern int delete_module(const char *module, unsigned int flags);
+#else
+# include <sys/syscall.h>
+# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
+# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
+#endif
+
+void FAST_FUNC replace(char *s, char what, char with)
+{
+	while (*s) {
+		if (what == *s)
+			*s = with;
+		++s;
+	}
+}
+
+char* FAST_FUNC replace_underscores(char *s)
+{
+	replace(s, '-', '_');
+	return s;
+}
+
+int FAST_FUNC string_to_llist(char *string, llist_t **llist, const char *delim)
+{
+	char *tok;
+	int len = 0;
+
+	while ((tok = strsep(&string, delim)) != NULL) {
+		if (tok[0] == '\0')
+			continue;
+		llist_add_to_end(llist, xstrdup(tok));
+		len += strlen(tok);
+	}
+	return len;
+}
+
+char* FAST_FUNC filename2modname(const char *filename, char *modname)
+{
+	int i;
+	char *from;
+
+	if (filename == NULL)
+		return NULL;
+	if (modname == NULL)
+		modname = xmalloc(MODULE_NAME_LEN);
+	from = bb_get_last_path_component_nostrip(filename);
+	for (i = 0; i < (MODULE_NAME_LEN-1) && from[i] != '\0' && from[i] != '.'; i++)
+		modname[i] = (from[i] == '-') ? '_' : from[i];
+	modname[i] = '\0';
+
+	return modname;
+}
+
+char* FAST_FUNC parse_cmdline_module_options(char **argv, int quote_spaces)
+{
+	char *options;
+	int optlen;
+
+	options = xzalloc(1);
+	optlen = 0;
+	while (*++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;
+}
+
+#if ENABLE_FEATURE_INSMOD_TRY_MMAP
+void* FAST_FUNC try_to_mmap_module(const char *filename, size_t *image_size_p)
+{
+	/* We have user reports of failure to load 3MB module
+	 * on a 16MB RAM machine. Apparently even a transient
+	 * memory spike to 6MB during module load
+	 * is too big for that system. */
+	void *image;
+	struct stat st;
+	int fd;
+
+	fd = xopen(filename, O_RDONLY);
+	fstat(fd, &st);
+	image = NULL;
+	/* st.st_size is off_t, we can't just pass it to mmap */
+	if (st.st_size <= *image_size_p) {
+		size_t image_size = st.st_size;
+		image = mmap(NULL, image_size, PROT_READ, MAP_PRIVATE, fd, 0);
+		if (image == MAP_FAILED) {
+			image = NULL;
+		} else if (*(uint32_t*)image != SWAP_BE32(0x7f454C46)) {
+			/* No ELF signature. Compressed module? */
+			munmap(image, image_size);
+			image = NULL;
+		} else {
+			/* Success. Report the size */
+			*image_size_p = image_size;
+		}
+	}
+	close(fd);
+	return image;
+}
+#endif
+
+/* Return:
+ * 0 on success,
+ * -errno on open/read error,
+ * errno on init_module() error
+ */
+int FAST_FUNC bb_init_module(const char *filename, const char *options)
+{
+	size_t image_size;
+	char *image;
+	int rc;
+	bool mmaped;
+
+	if (!options)
+		options = "";
+
+//TODO: audit bb_init_module_24 to match error code convention
+#if ENABLE_FEATURE_2_4_MODULES
+	if (get_linux_version_code() < KERNEL_VERSION(2,6,0))
+		return bb_init_module_24(filename, options);
+#endif
+
+	image_size = INT_MAX - 4095;
+	mmaped = 0;
+	image = try_to_mmap_module(filename, &image_size);
+	if (image) {
+		mmaped = 1;
+	} else {
+		errno = ENOMEM; /* may be changed by e.g. open errors below */
+		image = xmalloc_open_zipped_read_close(filename, &image_size);
+		if (!image)
+			return -errno;
+	}
+
+	errno = 0;
+	init_module(image, image_size, options);
+	rc = errno;
+	if (mmaped)
+		munmap(image, image_size);
+	else
+		free(image);
+	return rc;
+}
+
+int FAST_FUNC bb_delete_module(const char *module, unsigned int flags)
+{
+	errno = 0;
+	delete_module(module, flags);
+	return errno;
+}
+
+const char* FAST_FUNC moderror(int err)
+{
+	switch (err) {
+	case -1: /* btw: it's -EPERM */
+		return "no such module";
+	case ENOEXEC:
+		return "invalid module format";
+	case ENOENT:
+		return "unknown symbol in module, or unknown parameter";
+	case ESRCH:
+		return "module has wrong symbol version";
+	case ENOSYS:
+		return "kernel does not support requested operation";
+	}
+	if (err < 0) /* should always be */
+		err = -err;
+	return strerror(err);
+}
diff --git a/busybox-1.19.3/modutils/modutils.h b/busybox-1.19.3/modutils/modutils.h
new file mode 100644
index 0000000..5f059c7
--- /dev/null
+++ b/busybox-1.19.3/modutils/modutils.h
@@ -0,0 +1,75 @@
+/*
+ * Common modutils related functions for busybox
+ *
+ * Copyright (C) 2008 by Timo Teras <timo.teras@iki.fi>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#ifndef MODUTILS_H
+#define MODUTILS_H 1
+
+#include "libbb.h"
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* linux/include/linux/module.h has 64, but this is also used
+ * internally for the maximum alias name length, which can be quite long */
+#define MODULE_NAME_LEN 256
+
+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, int quote_spaces) FAST_FUNC;
+
+/* insmod for 2.4 and modprobe's options (insmod 2.6 has no options at all): */
+#define INSMOD_OPTS \
+	"vqs" \
+	IF_FEATURE_2_4_MODULES("Lfkx" IF_FEATURE_INSMOD_LOAD_MAP("m"))
+#define INSMOD_ARGS /* (was meant to support -o NAME) , NULL */
+
+enum {
+	INSMOD_OPT_VERBOSE      = (1 << 0),
+	INSMOD_OPT_SILENT       = (1 << 1),
+	INSMOD_OPT_SYSLOG       = (1 << 2),
+	//INSMOD_OPT_OUTPUTNAME = (1 << x) - not supported yet
+	INSMOD_OPT_LOCK         = (1 << 3) * ENABLE_FEATURE_2_4_MODULES,
+	INSMOD_OPT_FORCE        = (1 << 4) * ENABLE_FEATURE_2_4_MODULES,
+	INSMOD_OPT_KERNELD      = (1 << 5) * ENABLE_FEATURE_2_4_MODULES,
+	INSMOD_OPT_NO_EXPORT    = (1 << 6) * ENABLE_FEATURE_2_4_MODULES,
+	INSMOD_OPT_PRINT_MAP    = (1 << 7) * ENABLE_FEATURE_INSMOD_LOAD_MAP,
+	INSMOD_OPT_UNUSED       =
+		(INSMOD_OPT_PRINT_MAP ? INSMOD_OPT_PRINT_MAP
+		: INSMOD_OPT_NO_EXPORT ? INSMOD_OPT_NO_EXPORT
+		: INSMOD_OPT_SYSLOG
+		) << 1
+};
+
+#if ENABLE_FEATURE_INSMOD_TRY_MMAP
+void* FAST_FUNC try_to_mmap_module(const char *filename, size_t *image_size_p);
+#else
+# define try_to_mmap_module(filename, image_size) NULL
+#endif
+
+/* Return:
+ * 0 on success,
+ * -errno on open/read error,
+ * errno on init_module() error
+ */
+int FAST_FUNC bb_init_module(const char *module, const char *options);
+/* Return:
+ * 0 on success,
+ * errno on init_module() error
+ */
+int FAST_FUNC bb_delete_module(const char *module, unsigned int flags);
+/* Translates error return to a string */
+const char *moderror(int err) FAST_FUNC;
+
+#if ENABLE_FEATURE_2_4_MODULES
+int FAST_FUNC bb_init_module_24(const char *module, const char *options);
+#endif
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/modutils/rmmod.c b/busybox-1.19.3/modutils/rmmod.c
new file mode 100644
index 0000000..4a4a919
--- /dev/null
+++ b/busybox-1.19.3/modutils/rmmod.c
@@ -0,0 +1,67 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini rmmod implementation for busybox
+ *
+ * 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 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"
+
+int rmmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rmmod_main(int argc UNUSED_PARAM, char **argv)
+{
+	int n;
+	unsigned flags = O_NONBLOCK | O_EXCL;
+
+	/* Parse command line. */
+	n = getopt32(argv, "wfas"); // -s ignored
+	argv += optind;
+	if (n & 1)  // --wait
+		flags &= ~O_NONBLOCK;
+	if (n & 2)  // --force
+		flags |= O_TRUNC;
+	if (n & 4) {
+		/* Unload _all_ unused modules via NULL delete_module() call */
+		if (bb_delete_module(NULL, flags) != 0 && errno != EFAULT)
+			bb_perror_msg_and_die("rmmod");
+		return EXIT_SUCCESS;
+	}
+
+	if (!*argv)
+		bb_show_usage();
+
+	n = ENABLE_FEATURE_2_4_MODULES && get_linux_version_code() < KERNEL_VERSION(2,6,0);
+	while (*argv) {
+		char modname[MODULE_NAME_LEN];
+		const char *bname;
+
+		bname = bb_basename(*argv++);
+		if (n)
+			safe_strncpy(modname, bname, MODULE_NAME_LEN);
+		else
+			filename2modname(bname, modname);
+		if (bb_delete_module(modname, flags))
+			bb_error_msg_and_die("can't unload '%s': %s",
+					     modname, moderror(errno));
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/Config.src b/busybox-1.19.3/networking/Config.src
new file mode 100644
index 0000000..8aeba0e
--- /dev/null
+++ b/busybox-1.19.3/networking/Config.src
@@ -0,0 +1,989 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Networking Utilities"
+
+INSERT
+
+config FEATURE_IPV6
+	bool "Enable IPv6 support"
+	default y
+	help
+	  Enable IPv6 support in busybox.
+	  This adds IPv6 support in the networking applets.
+
+config FEATURE_UNIX_LOCAL
+	bool "Enable Unix domain socket support (usually not needed)"
+	default n
+	help
+	  Enable Unix domain socket support in all busybox networking
+	  applets.  Address of the form local:/path/to/unix/socket
+	  will be recognized.
+
+	  This extension is almost never used in real world usage.
+	  You most likely want to say N.
+
+config FEATURE_PREFER_IPV4_ADDRESS
+	bool "Prefer IPv4 addresses from DNS queries"
+	default y
+	depends on FEATURE_IPV6
+	help
+	  Use IPv4 address of network host if it has one.
+
+	  If this option is off, the first returned address will be used.
+	  This may cause problems when your DNS server is IPv6-capable and
+	  is returning IPv6 host addresses too. If IPv6 address
+	  precedes IPv4 one in DNS reply, busybox network applets
+	  (e.g. wget) will use IPv6 address. On an IPv6-incapable host
+	  or network applets will fail to connect to the host
+	  using IPv6 address.
+
+config VERBOSE_RESOLUTION_ERRORS
+	bool "Verbose resolution errors"
+	default n
+	help
+	  Enable if you are not satisfied with simplistic
+	  "can't resolve 'hostname.com'" and want to know more.
+	  This may increase size of your executable a bit.
+
+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.
+
+config FEATURE_BRCTL_FANCY
+	bool "Fancy options"
+	default y
+	depends on BRCTL
+	help
+	  Add support for extended option like:
+	    setageing, setfd, sethello, setmaxage,
+	    setpathcost, setportprio, setbridgeprio,
+	    stp
+	  This adds about 600 bytes.
+
+config FEATURE_BRCTL_SHOW
+	bool "Support show, showmac and showstp"
+	default y
+	depends on BRCTL && FEATURE_BRCTL_FANCY
+	help
+	  Add support for option which prints the current config:
+	    showmacs, showstp, show
+
+config DNSD
+	bool "dnsd"
+	default y
+	help
+	  Small and static DNS server daemon.
+
+config ETHER_WAKE
+	bool "ether-wake"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Send a magic packet to wake up sleeping machines.
+
+config FAKEIDENTD
+	bool "fakeidentd"
+	default y
+	select FEATURE_SYSLOG
+	help
+	  fakeidentd listens on the ident port and returns a predefined
+	  fake value on any query.
+
+config FTPD
+	bool "ftpd"
+	default y
+	help
+	  simple FTP daemon. You have to run it via inetd.
+
+config FEATURE_FTP_WRITE
+	bool "Enable upload commands"
+	default y
+	depends on FTPD
+	help
+	  Enable all kinds of FTP upload commands (-w option)
+
+config FEATURE_FTPD_ACCEPT_BROKEN_LIST
+	bool "Enable workaround for RFC-violating clients"
+	default y
+	depends on FTPD
+	help
+	  Some ftp clients (among them KDE's Konqueror) issue illegal
+	  "LIST -l" requests. This option works around such problems.
+	  It might prevent you from listing files starting with "-" and
+	  it increases the code size by ~40 bytes.
+	  Most other ftp servers seem to behave similar to this.
+
+config FTPGET
+	bool "ftpget"
+	default y
+	help
+	  Retrieve a remote file via FTP.
+
+config FTPPUT
+	bool "ftpput"
+	default y
+	help
+	  Store a remote file via FTP.
+
+config FEATURE_FTPGETPUT_LONG_OPTIONS
+	bool "Enable long options in ftpget/ftpput"
+	default y
+	depends on LONG_OPTS && (FTPGET || FTPPUT)
+	help
+	  Support long options for the ftpget/ftpput applet.
+
+config HOSTNAME
+	bool "hostname"
+	default y
+	help
+	  Show or set the system's host name.
+
+config HTTPD
+	bool "httpd"
+	default y
+	help
+	  Serve web pages via an HTTP server.
+
+config FEATURE_HTTPD_RANGES
+	bool "Support 'Ranges:' header"
+	default y
+	depends on HTTPD
+	help
+	  Makes httpd emit "Accept-Ranges: bytes" header and understand
+	  "Range: bytes=NNN-[MMM]" header. Allows for resuming interrupted
+	  downloads, seeking in multimedia players etc.
+
+config FEATURE_HTTPD_USE_SENDFILE
+	bool "Use sendfile system call"
+	default y
+	depends on HTTPD
+	help
+	  When enabled, httpd will use the kernel sendfile() function
+	  instead of read/write loop.
+
+config FEATURE_HTTPD_SETUID
+	bool "Enable -u <user> option"
+	default y
+	depends on HTTPD
+	help
+	  This option allows the server to run as a specific user
+	  rather than defaulting to the user that starts the server.
+	  Use of this option requires special privileges to change to a
+	  different user.
+
+config FEATURE_HTTPD_BASIC_AUTH
+	bool "Enable Basic http Authentication"
+	default y
+	depends on HTTPD
+	help
+	  Utilizes password settings from /etc/httpd.conf for basic
+	  authentication on a per url basis.
+
+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.
+
+config FEATURE_HTTPD_CGI
+	bool "Support Common Gateway Interface (CGI)"
+	default y
+	depends on HTTPD
+	help
+	  This option allows scripts and executables to be invoked
+	  when specific URLs are requested.
+
+config FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+	bool "Support for running scripts through an interpreter"
+	default y
+	depends on FEATURE_HTTPD_CGI
+	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:
+	  *.php:/path/to/your/php
+
+config FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+	bool "Set REMOTE_PORT environment variable for CGI"
+	default y
+	depends on FEATURE_HTTPD_CGI
+	help
+	  Use of this option can assist scripts in generating
+	  references that contain a unique port number.
+
+config FEATURE_HTTPD_ENCODE_URL_STR
+	bool "Enable -e option (useful for CGIs written as shell scripts)"
+	default y
+	depends on HTTPD
+	help
+	  This option allows html encoding of arbitrary strings for display
+	  by the browser. Output goes to stdout.
+	  For example, httpd -e "<Hello World>" produces
+	  "&#60Hello&#32World&#62".
+
+config FEATURE_HTTPD_ERROR_PAGES
+	bool "Support for custom error pages"
+	default y
+	depends on HTTPD
+	help
+	  This option allows you to define custom error pages in
+	  the configuration file instead of the default HTTP status
+	  error pages. For instance, if you add the line:
+	        E404:/path/e404.html
+	  in the config file, the server will respond the specified
+	  '/path/e404.html' file instead of the terse '404 NOT FOUND'
+	  message.
+
+config FEATURE_HTTPD_PROXY
+	bool "Support for reverse proxy"
+	default y
+	depends on HTTPD
+	help
+	  This option allows you to define URLs that will be forwarded
+	  to another HTTP server. To setup add the following line to the
+	  configuration file
+	        P:/url/:http://hostname[:port]/new/path/
+	  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.
+
+config FEATURE_IFCONFIG_STATUS
+	bool "Enable status reporting output (+7k)"
+	default y
+	depends on IFCONFIG
+	help
+	  If ifconfig is called with no arguments it will display the status
+	  of the currently active interfaces.
+
+config FEATURE_IFCONFIG_SLIP
+	bool "Enable slip-specific options \"keepalive\" and \"outfill\""
+	default y
+	depends on IFCONFIG
+	help
+	  Allow "keepalive" and "outfill" support for SLIP. If you're not
+	  planning on using serial lines, leave this unchecked.
+
+config FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+	bool "Enable options \"mem_start\", \"io_addr\", and \"irq\""
+	default y
+	depends on IFCONFIG
+	help
+	  Allow the start address for shared memory, start address for I/O,
+	  and/or the interrupt line used by the specified device.
+
+config FEATURE_IFCONFIG_HW
+	bool "Enable option \"hw\" (ether only)"
+	default y
+	depends on IFCONFIG
+	help
+	  Set the hardware address of this interface, if the device driver
+	  supports  this  operation. Currently, we only support the 'ether'
+	  class.
+
+config FEATURE_IFCONFIG_BROADCAST_PLUS
+	bool "Set the broadcast automatically"
+	default y
+	depends on IFCONFIG
+	help
+	  Setting this will make ifconfig attempt to find the broadcast
+	  automatically if the value '+' is used.
+
+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).
+
+config IFPLUGD
+	bool "ifplugd"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Network interface plug detection daemon.
+
+config IFUPDOWN
+	bool "ifupdown"
+	default y
+	help
+	  Activate or deactivate the specified interfaces. This applet makes
+	  use of either "ifconfig" and "route" or the "ip" command to actually
+	  configure network interfaces. Therefore, you will probably also want
+	  to enable either IFCONFIG and ROUTE, or enable
+	  FEATURE_IFUPDOWN_IP and the various IP options. Of
+	  course you could use non-busybox versions of these programs, so
+	  against my better judgement (since this will surely result in plenty
+	  of support questions on the mailing list), I do not force you to
+	  enable these additional options. It is up to you to supply either
+	  "ifconfig", "route" and "run-parts" or the "ip" command, either
+	  via busybox or via standalone utilities.
+
+config IFUPDOWN_IFSTATE_PATH
+	string "Absolute path to ifstate file"
+	default "/var/run/ifstate"
+	depends on IFUPDOWN
+	help
+	  ifupdown keeps state information in a file called ifstate.
+	  Typically it is located in /var/run/ifstate, however
+	  some distributions tend to put it in other places
+	  (debian, for example, uses /etc/network/run/ifstate).
+	  This config option defines location of ifstate.
+
+config FEATURE_IFUPDOWN_IP
+	bool "Use ip applet"
+	default y
+	depends on IFUPDOWN
+	help
+	  Use the iproute "ip" command to implement "ifup" and "ifdown", rather
+	  than the default of using the older 'ifconfig' and 'route' utilities.
+
+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
+	select FEATURE_IP_ROUTE
+	help
+	  Use the busybox iproute "ip" applet to implement "ifupdown".
+
+	  If left disabled, you must install the full-blown iproute2
+	  utility or the  "ifup" and "ifdown" applets will not work.
+
+config FEATURE_IFUPDOWN_IFCONFIG_BUILTIN
+	bool "Use busybox ifconfig and route applets"
+	default n
+	depends on IFUPDOWN && !FEATURE_IFUPDOWN_IP
+	select IFCONFIG
+	select ROUTE
+	help
+	  Use the busybox iproute "ifconfig" and "route" applets to
+	  implement the "ifup" and "ifdown" utilities.
+
+	  If left disabled, you must install the full-blown ifconfig
+	  and route utilities, or the  "ifup" and "ifdown" applets will not
+	  work.
+
+config FEATURE_IFUPDOWN_IPV4
+	bool "Support for IPv4"
+	default y
+	depends on IFUPDOWN
+	help
+	  If you want ifup/ifdown to talk IPv4, leave this on.
+
+config FEATURE_IFUPDOWN_IPV6
+	bool "Support for IPv6"
+	default y
+	depends on IFUPDOWN && FEATURE_IPV6
+	help
+	  If you need support for IPv6, turn this option on.
+
+### UNUSED
+###config FEATURE_IFUPDOWN_IPX
+###	bool "Support for IPX"
+###	default y
+###	depends on IFUPDOWN
+###	help
+###	  If this option is selected you can use busybox to work with IPX
+###	  networks.
+
+config FEATURE_IFUPDOWN_MAPPING
+	bool "Enable mapping support"
+	default y
+	depends on IFUPDOWN
+	help
+	  This enables support for the "mapping" stanza, unless you have
+	  a weird network setup you don't need it.
+
+config FEATURE_IFUPDOWN_EXTERNAL_DHCP
+	bool "Support for external dhcp clients"
+	default n
+	depends on IFUPDOWN
+	help
+	  This enables support for the external dhcp clients. Clients are
+	  tried in the following order: dhcpcd, dhclient, pump and udhcpc.
+	  Otherwise, if udhcpc applet is enabled, it is used.
+	  Otherwise, ifup/ifdown will have no support for DHCP.
+
+config INETD
+	bool "inetd"
+	default y
+	select FEATURE_SYSLOG
+	help
+	  Internet superserver daemon
+
+config FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+	bool "Support echo service"
+	default y
+	depends on INETD
+	help
+	  Echo received data internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+	bool "Support discard service"
+	default y
+	depends on INETD
+	help
+	  Internet /dev/null internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_TIME
+	bool "Support time service"
+	default y
+	depends on INETD
+	help
+	  Return 32 bit time since 1900 internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+	bool "Support daytime service"
+	default y
+	depends on INETD
+	help
+	  Return human-readable time internal inetd service
+
+config FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+	bool "Support chargen service"
+	default y
+	depends on INETD
+	help
+	  Familiar character generator internal inetd service
+
+config FEATURE_INETD_RPC
+	bool "Support RPC services"
+	default y
+	depends on INETD
+	select FEATURE_HAVE_RPC
+	help
+	  Support Sun-RPC based services
+
+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
+	  TCP/IP.
+
+config FEATURE_IP_ADDRESS
+	bool "ip address"
+	default y
+	depends on IP
+	help
+	  Address manipulation support for the "ip" applet.
+
+config FEATURE_IP_LINK
+	bool "ip link"
+	default y
+	depends on IP
+	help
+	  Configure network devices with "ip".
+
+config FEATURE_IP_ROUTE
+	bool "ip route"
+	default y
+	depends on IP
+	help
+	  Add support for routing table management to "ip".
+
+config FEATURE_IP_TUNNEL
+	bool "ip tunnel"
+	default y
+	depends on IP
+	help
+	  Add support for tunneling commands to "ip".
+
+config FEATURE_IP_RULE
+	bool "ip rule"
+	default y
+	depends on IP
+	help
+	  Add support for rule commands to "ip".
+
+config FEATURE_IP_SHORT_FORMS
+	bool "Support short forms of ip commands"
+	default y
+	depends on IP
+	help
+	  Also support short-form of ip <OBJECT> commands:
+	  ip addr   -> ipaddr
+	  ip link   -> iplink
+	  ip route  -> iproute
+	  ip tunnel -> iptunnel
+	  ip rule   -> iprule
+
+	  Say N unless you desparately need the short form of the ip
+	  object commands.
+
+config FEATURE_IP_RARE_PROTOCOLS
+	bool "Support displaying rarely used link types"
+	default n
+	depends on IP
+	help
+	  If you are not going to use links of type "frad", "econet",
+	  "bif" etc, you probably don't need to enable this.
+	  Ethernet, wireless, infrared, ppp/slip, ip tunnelling
+	  link types are supported without this option selected.
+
+config IPADDR
+	bool
+	default y
+	depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ADDRESS
+
+config IPLINK
+	bool
+	default y
+	depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_LINK
+
+config IPROUTE
+	bool
+	default y
+	depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_ROUTE
+
+config IPTUNNEL
+	bool
+	default y
+	depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_TUNNEL
+
+config IPRULE
+	bool
+	default y
+	depends on FEATURE_IP_SHORT_FORMS && FEATURE_IP_RULE
+
+config IPCALC
+	bool "ipcalc"
+	default y
+	help
+	  ipcalc takes an IP address and netmask and calculates the
+	  resulting broadcast, network, and host range.
+
+config FEATURE_IPCALC_FANCY
+	bool "Fancy IPCALC, more options, adds 1 kbyte"
+	default y
+	depends on IPCALC
+	help
+	  Adds the options hostname, prefix and silent to the output of
+	  "ipcalc".
+
+config FEATURE_IPCALC_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on IPCALC && LONG_OPTS
+	help
+	  Support long options for the ipcalc applet.
+
+config NETSTAT
+	bool "netstat"
+	default y
+	select PLATFORM_LINUX
+	help
+	  netstat prints information about the Linux networking subsystem.
+
+config FEATURE_NETSTAT_WIDE
+	bool "Enable wide netstat output"
+	default y
+	depends on NETSTAT
+	help
+	  Add support for wide columns. Useful when displaying IPv6 addresses
+	  (-W option).
+
+config FEATURE_NETSTAT_PRG
+	bool "Enable PID/Program name output"
+	default y
+	depends on NETSTAT
+	help
+	  Add support for -p flag to print out PID and program name.
+	  +700 bytes of code.
+
+config NSLOOKUP
+	bool "nslookup"
+	default y
+	help
+	  nslookup is a tool to query Internet name servers.
+
+config NTPD
+	bool "ntpd"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The NTP client/server daemon.
+
+config FEATURE_NTPD_SERVER
+	bool "Make ntpd usable as a NTP server"
+	default y
+	depends on NTPD
+	help
+	  Make ntpd usable as a NTP server. If you disable this option
+	  ntpd will be usable only as a NTP client.
+
+config PSCAN
+	bool "pscan"
+	default y
+	help
+	  Simple network port scanner.
+
+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.
+
+#config TC
+#	bool "tc"
+#	default y
+#	help
+#	  show / manipulate traffic control settings
+#
+#config FEATURE_TC_INGRESS
+#	def_bool n
+#	depends on TC
+
+config TCPSVD
+	bool "tcpsvd"
+	default y
+	help
+	  tcpsvd listens on a TCP port and runs a program for each new
+	  connection.
+
+config TELNET
+	bool "telnet"
+	default y
+	help
+	  Telnet is an interface to the TELNET protocol, but is also commonly
+	  used to test other simple protocols.
+
+config FEATURE_TELNET_TTYPE
+	bool "Pass TERM type to remote host"
+	default y
+	depends on TELNET
+	help
+	  Setting this option will forward the TERM environment variable to the
+	  remote host you are connecting to. This is useful to make sure that
+	  things like ANSI colors and other control sequences behave.
+
+config FEATURE_TELNET_AUTOLOGIN
+	bool "Pass USER type to remote host"
+	default y
+	depends on TELNET
+	help
+	  Setting this option will forward the USER environment variable to the
+	  remote host you are connecting to. This is useful when you need to
+	  log into a machine without telling the username (autologin). This
+	  option enables `-a' and `-l USER' arguments.
+
+config TELNETD
+	bool "telnetd"
+	default y
+	select FEATURE_SYSLOG
+	help
+	  A daemon for the TELNET protocol, allowing you to log onto the host
+	  running the daemon. Please keep in mind that the TELNET protocol
+	  sends passwords in plain text. If you can't afford the space for an
+	  SSH daemon and you trust your network, you may say 'y' here. As a
+	  more secure alternative, you should seriously consider installing the
+	  very small Dropbear SSH daemon instead:
+		http://matt.ucc.asn.au/dropbear/dropbear.html
+
+	  Note that for busybox telnetd to work you need several things:
+	  First of all, your kernel needs:
+		  UNIX98_PTYS=y
+		  DEVPTS_FS=y
+
+	  Next, you need a /dev/pts directory on your root filesystem:
+
+		  $ ls -ld /dev/pts
+		  drwxr-xr-x  2 root root 0 Sep 23 13:21 /dev/pts/
+
+	  Next you need the pseudo terminal master multiplexer /dev/ptmx:
+
+		  $ ls -la /dev/ptmx
+		  crw-rw-rw-  1 root tty 5, 2 Sep 23 13:55 /dev/ptmx
+
+	  Any /dev/ttyp[0-9]* files you may have can be removed.
+	  Next, you need to mount the devpts filesystem on /dev/pts using:
+
+		  mount -t devpts devpts /dev/pts
+
+	  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:
+
+		chown root.root /bin/busybox
+		chmod 4755 /bin/busybox
+
+	  with all that done, telnetd _should_ work....
+
+
+config FEATURE_TELNETD_STANDALONE
+	bool "Support standalone telnetd (not inetd only)"
+	default y
+	depends on TELNETD
+	help
+	  Selecting this will make telnetd able to run standalone.
+
+config FEATURE_TELNETD_INETD_WAIT
+	bool "Support -w SEC option (inetd wait mode)"
+	default y
+	depends on FEATURE_TELNETD_STANDALONE
+	help
+	  This option allows you to run telnetd in "inet wait" mode.
+	  Example inetd.conf line (note "wait", not usual "nowait"):
+
+	  telnet stream tcp wait root /bin/telnetd telnetd -w10
+
+	  In this example, inetd passes _listening_ socket_ as fd 0
+	  to telnetd when connection appears.
+	  telnetd will wait for connections until all existing
+	  connections are closed, and no new connections
+	  appear during 10 seconds. Then it exits, and inetd continues
+	  to listen for new connections.
+
+	  This option is rarely used. "tcp nowait" is much more usual
+	  way of running tcp services, including telnetd.
+	  You most probably want to say N here.
+
+config TFTP
+	bool "tftp"
+	default y
+	help
+	  This enables the Trivial File Transfer Protocol client program. TFTP
+	  is usually used for simple, small transfers such as a root image
+	  for a network-enabled bootloader.
+
+config TFTPD
+	bool "tftpd"
+	default y
+	help
+	  This enables the Trivial File Transfer Protocol server program.
+	  It expects that stdin is a datagram socket and a packet
+	  is already pending on it. It will exit after one transfer.
+	  In other words: it should be run from inetd in nowait mode,
+	  or from udpsvd. Example: "udpsvd -E 0 69 tftpd DIR"
+
+comment "Common options for tftp/tftpd"
+	depends on TFTP || TFTPD
+
+config FEATURE_TFTP_GET
+	bool "Enable 'tftp get' and/or tftpd upload code"
+	default y
+	depends on TFTP || TFTPD
+	help
+	  Add support for the GET command within the TFTP client. This allows
+	  a client to retrieve a file from a TFTP server.
+	  Also enable upload support in tftpd, if tftpd is selected.
+
+	  Note: this option does _not_ make tftpd capable of download
+	  (the usual operation people need from it)!
+
+config FEATURE_TFTP_PUT
+	bool "Enable 'tftp put' and/or tftpd download code"
+	default y
+	depends on TFTP || TFTPD
+	help
+	  Add support for the PUT command within the TFTP client. This allows
+	  a client to transfer a file to a TFTP server.
+	  Also enable download support in tftpd, if tftpd is selected.
+
+config FEATURE_TFTP_BLOCKSIZE
+	bool "Enable 'blksize' and 'tsize' protocol options"
+	default y
+	depends on TFTP || TFTPD
+	help
+	  Allow tftp to specify block size, and tftpd to understand
+	  "blksize" and "tsize" options.
+
+config FEATURE_TFTP_PROGRESS_BAR
+	bool "Enable tftp progress meter"
+	default y
+	depends on TFTP && FEATURE_TFTP_BLOCKSIZE
+	help
+	  Show progress bar.
+
+config TFTP_DEBUG
+	bool "Enable debug"
+	default n
+	depends on TFTP || TFTPD
+	help
+	  Make tftp[d] print debugging messages on stderr.
+	  This is useful if you are diagnosing a bug in tftp[d].
+
+config TRACEROUTE
+	bool "traceroute"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Utility to trace the route of IP packets.
+
+config TRACEROUTE6
+	bool "traceroute6"
+	default y
+	depends on FEATURE_IPV6 && TRACEROUTE
+	help
+	  Utility to trace the route of IPv6 packets.
+
+config FEATURE_TRACEROUTE_VERBOSE
+	bool "Enable verbose output"
+	default y
+	depends on TRACEROUTE
+	help
+	  Add some verbosity to traceroute. This includes among other things
+	  hostnames and ICMP response types.
+
+config FEATURE_TRACEROUTE_SOURCE_ROUTE
+	bool "Enable loose source route"
+	default n
+	depends on TRACEROUTE
+	help
+	  Add option to specify a loose source route gateway
+	  (8 maximum).
+
+config FEATURE_TRACEROUTE_USE_ICMP
+	bool "Use ICMP instead of UDP"
+	default n
+	depends on TRACEROUTE
+	help
+	  Add option -I to use ICMP ECHO instead of UDP datagrams.
+
+config TUNCTL
+	bool "tunctl"
+	default y
+	select PLATFORM_LINUX
+	help
+	  tunctl creates or deletes tun devices.
+
+config FEATURE_TUNCTL_UG
+	bool "Support owner:group assignment"
+	default y
+	depends on TUNCTL
+	help
+	  Allow to specify owner and group of newly created interface.
+	  340 bytes of pure bloat. Say no here.
+
+source networking/udhcp/Config.in
+
+config IFUPDOWN_UDHCPC_CMD_OPTIONS
+	string "ifup udhcpc command line options"
+	default "-R -n"
+	depends on IFUPDOWN && UDHCPC
+	help
+	  Command line options to pass to udhcpc from ifup.
+	  Intended to alter options not available in /etc/network/interfaces.
+	  (IE: --syslog --background etc...)
+
+config UDPSVD
+	bool "udpsvd"
+	default y
+	help
+	  udpsvd listens on an UDP port and runs a program for each new
+	  connection.
+
+config VCONFIG
+	bool "vconfig"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Creates, removes, and configures VLAN interfaces
+
+config WGET
+	bool "wget"
+	default y
+	help
+	  wget is a utility for non-interactive download of files from HTTP,
+	  HTTPS, and FTP servers.
+
+config FEATURE_WGET_STATUSBAR
+	bool "Enable a nifty process meter (+2k)"
+	default y
+	depends on WGET
+	help
+	  Enable the transfer progress bar for wget transfers.
+
+config FEATURE_WGET_AUTHENTICATION
+	bool "Enable HTTP authentication"
+	default y
+	depends on WGET
+	help
+	  Support authenticated HTTP transfers.
+
+config FEATURE_WGET_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on WGET && LONG_OPTS
+	help
+	  Support long options for the wget applet.
+
+config FEATURE_WGET_TIMEOUT
+	bool "Enable read timeout option -T SEC"
+	default y
+	depends on WGET
+	help
+	  Supports network read timeout for wget, so that wget will give
+	  up and timeout when reading network data, through the -T command
+	  line option.  Currently only network data read timeout is
+	  supported (i.e., timeout is not applied to the DNS nor TCP
+	  connection initialization).  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.
+	  It's a daemon that allocates and defends a dynamically assigned
+	  address on the 169.254/16 network, requiring no system administrator.
+
+	  See http://www.zeroconf.org for further details, and "zcip.script"
+	  in the busybox examples.
+
+endmenu
diff --git a/busybox-1.19.3/networking/Kbuild.src b/busybox-1.19.3/networking/Kbuild.src
new file mode 100644
index 0000000..944f27b
--- /dev/null
+++ b/busybox-1.19.3/networking/Kbuild.src
@@ -0,0 +1,48 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_ARP)          += arp.o interface.o
+lib-$(CONFIG_ARPING)       += arping.o
+lib-$(CONFIG_BRCTL)        += brctl.o
+lib-$(CONFIG_DNSD)         += dnsd.o
+lib-$(CONFIG_ETHER_WAKE)   += ether-wake.o
+lib-$(CONFIG_FAKEIDENTD)   += isrv_identd.o isrv.o
+lib-$(CONFIG_FTPD)         += ftpd.o
+lib-$(CONFIG_FTPGET)       += ftpgetput.o
+lib-$(CONFIG_FTPPUT)       += ftpgetput.o
+lib-$(CONFIG_HOSTNAME)     += hostname.o
+lib-$(CONFIG_HTTPD)        += httpd.o
+lib-$(CONFIG_IFCONFIG)     += ifconfig.o interface.o
+lib-$(CONFIG_IFENSLAVE)    += ifenslave.o interface.o
+lib-$(CONFIG_IFPLUGD)      += ifplugd.o
+lib-$(CONFIG_IFUPDOWN)     += ifupdown.o
+lib-$(CONFIG_INETD)        += inetd.o
+lib-$(CONFIG_IP)           += ip.o
+lib-$(CONFIG_IPCALC)       += ipcalc.o
+lib-$(CONFIG_NAMEIF)       += nameif.o
+lib-$(CONFIG_NC)           += nc.o
+lib-$(CONFIG_NETSTAT)      += netstat.o
+lib-$(CONFIG_NSLOOKUP)     += nslookup.o
+lib-$(CONFIG_NTPD)         += ntpd.o
+lib-$(CONFIG_PSCAN)        += pscan.o
+lib-$(CONFIG_ROUTE)        += route.o
+lib-$(CONFIG_SLATTACH)     += slattach.o
+lib-$(CONFIG_TC)           += tc.o
+lib-$(CONFIG_TELNET)       += telnet.o
+lib-$(CONFIG_TELNETD)      += telnetd.o
+lib-$(CONFIG_TFTP)         += tftp.o
+lib-$(CONFIG_TFTPD)        += tftp.o
+lib-$(CONFIG_TRACEROUTE)   += traceroute.o
+lib-$(CONFIG_TUNCTL)       += tunctl.o
+lib-$(CONFIG_VCONFIG)      += vconfig.o
+lib-$(CONFIG_WGET)         += wget.o
+lib-$(CONFIG_ZCIP)         += zcip.o
+
+lib-$(CONFIG_TCPSVD)       += tcpudp.o tcpudp_perhost.o
+lib-$(CONFIG_UDPSVD)       += tcpudp.o tcpudp_perhost.o
diff --git a/busybox-1.19.3/networking/arp.c b/busybox-1.19.3/networking/arp.c
new file mode 100644
index 0000000..696c402
--- /dev/null
+++ b/busybox-1.19.3/networking/arp.c
@@ -0,0 +1,533 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * arp.c - Manipulate the system ARP cache
+ *
+ * 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.
+ *
+ * Author: Fred N. van Kempen, <waltje at uwalt.nl.mugnet.org>
+ * Busybox port: Paul van Gool <pvangool at mimotech.com>
+ *
+ * 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	-s		Set new ARP entry"
+//usage:       "\n	-d		Delete a specified entry"
+//usage:       "\n	-v		Verbose"
+//usage:       "\n	-n		Don't resolve names"
+//usage:       "\n	-i IF		Network interface"
+//usage:       "\n	-D		Read <hwaddr> from given device"
+//usage:       "\n	-A,-p AF	Protocol family"
+//usage:       "\n	-H HWTYPE	Hardware address type"
+
+#include "libbb.h"
+#include "inet_common.h"
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/ether.h>
+#include <netpacket/packet.h>
+
+#define DEBUG 0
+
+#define DFLT_AF "inet"
+#define DFLT_HW "ether"
+
+enum {
+	ARP_OPT_A = (1 << 0),
+	ARP_OPT_p = (1 << 1),
+	ARP_OPT_H = (1 << 2),
+	ARP_OPT_t = (1 << 3),
+	ARP_OPT_i = (1 << 4),
+	ARP_OPT_a = (1 << 5),
+	ARP_OPT_d = (1 << 6),
+	ARP_OPT_n = (1 << 7), /* do not resolve addresses */
+	ARP_OPT_D = (1 << 8), /* HW-address is devicename */
+	ARP_OPT_s = (1 << 9),
+	ARP_OPT_v = (1 << 10) * DEBUG, /* debugging output flag */
+};
+
+enum {
+	sockfd = 3, /* active socket descriptor */
+};
+
+struct globals {
+	const struct aftype *ap; /* current address family */
+	const struct hwtype *hw; /* current hardware type */
+	const char *device;      /* current device */
+	smallint hw_set;         /* flag if hw-type was set (-H) */
+
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define ap         (G.ap        )
+#define hw         (G.hw        )
+#define device     (G.device    )
+#define hw_set     (G.hw_set    )
+#define INIT_G() do { \
+	device = ""; \
+} while (0)
+
+
+static const char options[] ALIGN1 =
+	"pub\0"
+	"priv\0"
+	"temp\0"
+	"trail\0"
+	"dontpub\0"
+	"auto\0"
+	"dev\0"
+	"netmask\0";
+
+/* Delete an entry from the ARP cache. */
+/* Called only from main, once */
+static int arp_del(char **args)
+{
+	char *host;
+	struct arpreq req;
+	struct sockaddr sa;
+	int flags = 0;
+	int err;
+
+	memset(&req, 0, sizeof(req));
+
+	/* Resolve the host name. */
+	host = *args;
+	if (ap->input(host, &sa) < 0) {
+		bb_herror_msg_and_die("%s", host);
+	}
+
+	/* If a host has more than one address, use the correct one! */
+	memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+	if (hw_set)
+		req.arp_ha.sa_family = hw->type;
+
+	req.arp_flags = ATF_PERM;
+	args++;
+	while (*args != NULL) {
+		switch (index_in_strings(options, *args)) {
+		case 0: /* "pub" */
+			flags |= 1;
+			args++;
+			break;
+		case 1: /* "priv" */
+			flags |= 2;
+			args++;
+			break;
+		case 2: /* "temp" */
+			req.arp_flags &= ~ATF_PERM;
+			args++;
+			break;
+		case 3: /* "trail" */
+			req.arp_flags |= ATF_USETRAILERS;
+			args++;
+			break;
+		case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+			req.arp_flags |= ATF_DONTPUB;
+#else
+			bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+			args++;
+			break;
+		case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+			req.arp_flags |= ATF_MAGIC;
+#else
+			bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+			args++;
+			break;
+		case 6: /* "dev" */
+			if (*++args == NULL)
+				bb_show_usage();
+			device = *args;
+			args++;
+			break;
+		case 7: /* "netmask" */
+			if (*++args == NULL)
+				bb_show_usage();
+			if (strcmp(*args, "255.255.255.255") != 0) {
+				host = *args;
+				if (ap->input(host, &sa) < 0) {
+					bb_herror_msg_and_die("%s", host);
+				}
+				memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+				req.arp_flags |= ATF_NETMASK;
+			}
+			args++;
+			break;
+		default:
+			bb_show_usage();
+			break;
+		}
+	}
+	if (flags == 0)
+		flags = 3;
+
+	strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+	err = -1;
+
+	/* Call the kernel. */
+	if (flags & 2) {
+		if (option_mask32 & ARP_OPT_v)
+			bb_error_msg("SIOCDARP(nopub)");
+		err = ioctl(sockfd, SIOCDARP, &req);
+		if (err < 0) {
+			if (errno == ENXIO) {
+				if (flags & 1)
+					goto nopub;
+				printf("No ARP entry for %s\n", host);
+				return -1;
+			}
+			bb_perror_msg_and_die("SIOCDARP(priv)");
+		}
+	}
+	if ((flags & 1) && err) {
+ nopub:
+		req.arp_flags |= ATF_PUBL;
+		if (option_mask32 & ARP_OPT_v)
+			bb_error_msg("SIOCDARP(pub)");
+		if (ioctl(sockfd, SIOCDARP, &req) < 0) {
+			if (errno == ENXIO) {
+				printf("No ARP entry for %s\n", host);
+				return -1;
+			}
+			bb_perror_msg_and_die("SIOCDARP(pub)");
+		}
+	}
+	return 0;
+}
+
+/* Get the hardware address to a specified interface name */
+static void arp_getdevhw(char *ifname, struct sockaddr *sa,
+						 const struct hwtype *hwt)
+{
+	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)) {
+		bb_error_msg_and_die("protocol type mismatch");
+	}
+	memcpy(sa, &(ifr.ifr_hwaddr), sizeof(struct sockaddr));
+
+	if (option_mask32 & ARP_OPT_v) {
+		xhw = get_hwntype(ifr.ifr_hwaddr.sa_family);
+		if (!xhw || !xhw->print) {
+			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));
+	}
+}
+
+/* Set an entry in the ARP cache. */
+/* Called only from main, once */
+static int arp_set(char **args)
+{
+	char *host;
+	struct arpreq req;
+	struct sockaddr sa;
+	int flags;
+
+	memset(&req, 0, sizeof(req));
+
+	host = *args++;
+	if (ap->input(host, &sa) < 0) {
+		bb_herror_msg_and_die("%s", host);
+	}
+	/* If a host has more than one address, use the correct one! */
+	memcpy(&req.arp_pa, &sa, sizeof(struct sockaddr));
+
+	/* Fetch the hardware address. */
+	if (*args == NULL) {
+		bb_error_msg_and_die("need hardware address");
+	}
+	if (option_mask32 & ARP_OPT_D) {
+		arp_getdevhw(*args++, &req.arp_ha, hw_set ? hw : NULL);
+	} else {
+		if (hw->input(*args++, &req.arp_ha) < 0) {
+			bb_error_msg_and_die("invalid hardware address");
+		}
+	}
+
+	/* Check out any modifiers. */
+	flags = ATF_PERM | ATF_COM;
+	while (*args != NULL) {
+		switch (index_in_strings(options, *args)) {
+		case 0: /* "pub" */
+			flags |= ATF_PUBL;
+			args++;
+			break;
+		case 1: /* "priv" */
+			flags &= ~ATF_PUBL;
+			args++;
+			break;
+		case 2: /* "temp" */
+			flags &= ~ATF_PERM;
+			args++;
+			break;
+		case 3: /* "trail" */
+			flags |= ATF_USETRAILERS;
+			args++;
+			break;
+		case 4: /* "dontpub" */
+#ifdef HAVE_ATF_DONTPUB
+			flags |= ATF_DONTPUB;
+#else
+			bb_error_msg("feature ATF_DONTPUB is not supported");
+#endif
+			args++;
+			break;
+		case 5: /* "auto" */
+#ifdef HAVE_ATF_MAGIC
+			flags |= ATF_MAGIC;
+#else
+			bb_error_msg("feature ATF_MAGIC is not supported");
+#endif
+			args++;
+			break;
+		case 6: /* "dev" */
+			if (*++args == NULL)
+				bb_show_usage();
+			device = *args;
+			args++;
+			break;
+		case 7: /* "netmask" */
+			if (*++args == NULL)
+				bb_show_usage();
+			if (strcmp(*args, "255.255.255.255") != 0) {
+				host = *args;
+				if (ap->input(host, &sa) < 0) {
+					bb_herror_msg_and_die("%s", host);
+				}
+				memcpy(&req.arp_netmask, &sa, sizeof(struct sockaddr));
+				flags |= ATF_NETMASK;
+			}
+			args++;
+			break;
+		default:
+			bb_show_usage();
+			break;
+		}
+	}
+
+	/* Fill in the remainder of the request. */
+	req.arp_flags = flags;
+
+	strncpy(req.arp_dev, device, sizeof(req.arp_dev));
+
+	/* Call the kernel. */
+	if (option_mask32 & ARP_OPT_v)
+		bb_error_msg("SIOCSARP()");
+	xioctl(sockfd, SIOCSARP, &req);
+	return 0;
+}
+
+
+/* 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)
+{
+	static const int arp_masks[] = {
+		ATF_PERM, ATF_PUBL,
+#ifdef HAVE_ATF_MAGIC
+		ATF_MAGIC,
+#endif
+#ifdef HAVE_ATF_DONTPUB
+		ATF_DONTPUB,
+#endif
+		ATF_USETRAILERS,
+	};
+	static const char arp_labels[] ALIGN1 = "PERM\0""PUP\0"
+#ifdef HAVE_ATF_MAGIC
+		"AUTO\0"
+#endif
+#ifdef HAVE_ATF_DONTPUB
+		"DONTPUB\0"
+#endif
+		"TRAIL\0"
+	;
+
+	const struct hwtype *xhw;
+
+	xhw = get_hwntype(type);
+	if (xhw == NULL)
+		xhw = get_hwtype(DFLT_HW);
+
+	printf("%s (%s) at ", name, ip);
+
+	if (!(arp_flags & ATF_COM)) {
+		if (arp_flags & ATF_PUBL)
+			printf("* ");
+		else
+			printf("<incomplete> ");
+	} else {
+		printf("%s [%s] ", hwa, xhw->name);
+	}
+
+	if (arp_flags & ATF_NETMASK)
+		printf("netmask %s ", mask);
+
+	print_flags_separated(arp_masks, arp_labels, arp_flags, " ");
+	printf(" on %s\n", dev);
+}
+
+/* Display the contents of the ARP cache in the kernel. */
+/* Called only from main, once */
+static int arp_show(char *name)
+{
+	const char *host;
+	const char *hostname;
+	FILE *fp;
+	struct sockaddr sa;
+	int type, flags;
+	int num;
+	unsigned entries = 0, shown = 0;
+	char ip[128];
+	char hwa[128];
+	char mask[128];
+	char line[128];
+	char dev[128];
+
+	host = NULL;
+	if (name != NULL) {
+		/* Resolve the host name. */
+		if (ap->input(name, &sa) < 0) {
+			bb_herror_msg_and_die("%s", name);
+		}
+		host = xstrdup(ap->sprint(&sa, 1));
+	}
+	fp = xfopen_for_read("/proc/net/arp");
+	/* Bypass header -- read one line */
+	fgets(line, sizeof(line), fp);
+
+	/* Read the ARP cache entries. */
+	while (fgets(line, sizeof(line), fp)) {
+
+		mask[0] = '-'; mask[1] = '\0';
+		dev[0] = '-'; dev[1] = '\0';
+		/* 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);
+		if (num < 4)
+			break;
+
+		entries++;
+		/* if the user specified hw-type differs, skip it */
+		if (hw_set && (type != hw->type))
+			continue;
+
+		/* if the user specified address differs, skip it */
+		if (host && strcmp(ip, host) != 0)
+			continue;
+
+		/* if the user specified device differs, skip it */
+		if (device[0] && strcmp(dev, device) != 0)
+			continue;
+
+		shown++;
+		/* This IS ugly but it works -be */
+		hostname = "?";
+		if (!(option_mask32 & ARP_OPT_n)) {
+			if (ap->input(ip, &sa) < 0)
+				hostname = ip;
+			else
+				hostname = ap->sprint(&sa, (option_mask32 & ARP_OPT_n) | 0x8000);
+			if (strcmp(hostname, ip) == 0)
+				hostname = "?";
+		}
+
+		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);
+
+	if (!shown) {
+		if (hw_set || host || device[0])
+			printf("No match found in %d entries\n", entries);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free((char*)host);
+		fclose(fp);
+	}
+	return 0;
+}
+
+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 *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");
+
+	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)
+			bb_error_msg_and_die("%s: unknown %s", protocol, "address family");
+	}
+	if (opts & (ARP_OPT_A | ARP_OPT_p)) {
+		hw = get_hwtype(hw_type);
+		if (hw == NULL)
+			bb_error_msg_and_die("%s: unknown %s", hw_type, "hardware type");
+		hw_set = 1;
+	}
+	//if (opts & ARP_OPT_i)... -i
+
+	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");
+	}
+
+	/* Now see what we have to do here... */
+	if (opts & (ARP_OPT_d | ARP_OPT_s)) {
+		if (argv[0] == NULL)
+			bb_error_msg_and_die("need host name");
+		if (opts & ARP_OPT_s)
+			return arp_set(argv);
+		return arp_del(argv);
+	}
+	//if (opts & ARP_OPT_a) - default
+	return arp_show(argv[0]);
+}
diff --git a/busybox-1.19.3/networking/arping.c b/busybox-1.19.3/networking/arping.c
new file mode 100644
index 0000000..a4421ed
--- /dev/null
+++ b/busybox-1.19.3/networking/arping.c
@@ -0,0 +1,425 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * 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>
+#include <netpacket/packet.h>
+
+#include "libbb.h"
+
+/* We don't expect to see 1000+ seconds delay, unsigned is enough */
+#define MONOTONIC_US() ((unsigned)monotonic_us())
+
+enum {
+	DAD = 1,
+	UNSOLICITED = 2,
+	ADVERT = 4,
+	QUIET = 8,
+	QUIT_ON_REPLY = 16,
+	BCAST_ONLY = 32,
+	UNICASTING = 64
+};
+
+struct globals {
+	struct in_addr src;
+	struct in_addr dst;
+	struct sockaddr_ll me;
+	struct sockaddr_ll he;
+	int sock_fd;
+
+	int count; // = -1;
+	unsigned last;
+	unsigned timeout_us;
+	unsigned start;
+
+	unsigned sent;
+	unsigned brd_sent;
+	unsigned received;
+	unsigned brd_recv;
+	unsigned req_recv;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define src        (G.src       )
+#define dst        (G.dst       )
+#define me         (G.me        )
+#define he         (G.he        )
+#define sock_fd    (G.sock_fd   )
+#define count      (G.count     )
+#define last       (G.last      )
+#define timeout_us (G.timeout_us)
+#define start      (G.start     )
+#define sent       (G.sent      )
+#define brd_sent   (G.brd_sent  )
+#define received   (G.received  )
+#define brd_recv   (G.brd_recv  )
+#define req_recv   (G.req_recv  )
+#define INIT_G() do { \
+	count = -1; \
+} while (0)
+
+// If GNUisms are not available...
+//static void *mempcpy(void *_dst, const void *_src, int n)
+//{
+//	memcpy(_dst, _src, n);
+//	return (char*)_dst + n;
+//}
+
+static int send_pack(struct in_addr *src_addr,
+			struct in_addr *dst_addr, struct sockaddr_ll *ME,
+			struct sockaddr_ll *HE)
+{
+	int err;
+	unsigned char buf[256];
+	struct arphdr *ah = (struct arphdr *) buf;
+	unsigned char *p = (unsigned char *) (ah + 1);
+
+	ah->ar_hrd = htons(ARPHRD_ETHER);
+	ah->ar_pro = htons(ETH_P_IP);
+	ah->ar_hln = ME->sll_halen;
+	ah->ar_pln = 4;
+	ah->ar_op = option_mask32 & ADVERT ? htons(ARPOP_REPLY) : htons(ARPOP_REQUEST);
+
+	p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
+	p = mempcpy(p, src_addr, 4);
+
+	if (option_mask32 & ADVERT)
+		p = mempcpy(p, &ME->sll_addr, ah->ar_hln);
+	else
+		p = mempcpy(p, &HE->sll_addr, ah->ar_hln);
+
+	p = mempcpy(p, dst_addr, 4);
+
+	err = sendto(sock_fd, buf, p - buf, 0, (struct sockaddr *) HE, sizeof(*HE));
+	if (err == p - buf) {
+		last = MONOTONIC_US();
+		sent++;
+		if (!(option_mask32 & UNICASTING))
+			brd_sent++;
+	}
+	return err;
+}
+
+static void finish(void) NORETURN;
+static void finish(void)
+{
+	if (!(option_mask32 & QUIET)) {
+		printf("Sent %u probe(s) (%u broadcast(s))\n"
+			"Received %u repl%s"
+			" (%u request(s), %u broadcast(s))\n",
+			sent, brd_sent,
+			received, (received == 1) ? "ies" : "y",
+			req_recv, brd_recv);
+	}
+	if (option_mask32 & DAD)
+		exit(!!received);
+	if (option_mask32 & UNSOLICITED)
+		exit(EXIT_SUCCESS);
+	exit(!received);
+}
+
+static void catcher(void)
+{
+	unsigned now;
+
+	now = MONOTONIC_US();
+	if (start == 0)
+		start = now;
+
+	if (count == 0 || (timeout_us && (now - start) > timeout_us))
+		finish();
+
+	/* count < 0 means "infinite count" */
+	if (count > 0)
+		count--;
+
+	if (last == 0 || (now - last) > 500000) {
+		send_pack(&src, &dst, &me, &he);
+		if (count == 0 && (option_mask32 & UNSOLICITED))
+			finish();
+	}
+	alarm(1);
+}
+
+static bool recv_pack(unsigned char *buf, int len, struct sockaddr_ll *FROM)
+{
+	struct arphdr *ah = (struct arphdr *) buf;
+	unsigned char *p = (unsigned char *) (ah + 1);
+	struct in_addr src_ip, dst_ip;
+	/* moves below assume in_addr is 4 bytes big, ensure that */
+	struct BUG_in_addr_must_be_4 {
+		char BUG_in_addr_must_be_4[
+			sizeof(struct in_addr) == 4 ? 1 : -1
+		];
+		char BUG_s_addr_must_be_4[
+			sizeof(src_ip.s_addr) == 4 ? 1 : -1
+		];
+	};
+
+	/* Filter out wild packets */
+	if (FROM->sll_pkttype != PACKET_HOST
+	 && FROM->sll_pkttype != PACKET_BROADCAST
+	 && FROM->sll_pkttype != PACKET_MULTICAST)
+		return false;
+
+	/* Only these types are recognized */
+	if (ah->ar_op != htons(ARPOP_REQUEST) && ah->ar_op != htons(ARPOP_REPLY))
+		return false;
+
+	/* ARPHRD check and this darned FDDI hack here :-( */
+	if (ah->ar_hrd != htons(FROM->sll_hatype)
+	 && (FROM->sll_hatype != ARPHRD_FDDI || ah->ar_hrd != htons(ARPHRD_ETHER)))
+		return false;
+
+	/* Protocol must be IP. */
+	if (ah->ar_pro != htons(ETH_P_IP)
+	 || (ah->ar_pln != 4)
+	 || (ah->ar_hln != me.sll_halen)
+	 || (len < (int)(sizeof(*ah) + 2 * (4 + ah->ar_hln))))
+		return false;
+
+	move_from_unaligned32(src_ip.s_addr, p + ah->ar_hln);
+	move_from_unaligned32(dst_ip.s_addr, p + ah->ar_hln + 4 + ah->ar_hln);
+
+	if (dst.s_addr != src_ip.s_addr)
+		return false;
+	if (!(option_mask32 & DAD)) {
+		if ((src.s_addr != dst_ip.s_addr)
+			|| (memcmp(p + ah->ar_hln + 4, &me.sll_addr, ah->ar_hln)))
+			return false;
+	} else {
+		/* DAD packet was:
+		   src_ip = 0 (or some src)
+		   src_hw = ME
+		   dst_ip = tested address
+		   dst_hw = <unspec>
+
+		   We fail, if receive request/reply with:
+		   src_ip = tested_address
+		   src_hw != ME
+		   if src_ip in request was not zero, check
+		   also that it matches to dst_ip, otherwise
+		   dst_ip/dst_hw do not matter.
+		 */
+		if ((memcmp(p, &me.sll_addr, me.sll_halen) == 0)
+		 || (src.s_addr && src.s_addr != dst_ip.s_addr))
+			return false;
+	}
+	if (!(option_mask32 & QUIET)) {
+		int s_printed = 0;
+
+		printf("%scast re%s from %s [%s]",
+			FROM->sll_pkttype == PACKET_HOST ? "Uni" : "Broad",
+			ah->ar_op == htons(ARPOP_REPLY) ? "ply" : "quest",
+			inet_ntoa(src_ip),
+			ether_ntoa((struct ether_addr *) p));
+		if (dst_ip.s_addr != src.s_addr) {
+			printf("for %s ", inet_ntoa(dst_ip));
+			s_printed = 1;
+		}
+		if (memcmp(p + ah->ar_hln + 4, me.sll_addr, ah->ar_hln)) {
+			if (!s_printed)
+				printf("for ");
+			printf("[%s]",
+				ether_ntoa((struct ether_addr *) p + ah->ar_hln + 4));
+		}
+
+		if (last) {
+			unsigned diff = MONOTONIC_US() - last;
+			printf(" %u.%03ums\n", diff / 1000, diff % 1000);
+		} else {
+			printf(" UNSOLICITED?\n");
+		}
+		fflush_all();
+	}
+	received++;
+	if (FROM->sll_pkttype != PACKET_HOST)
+		brd_recv++;
+	if (ah->ar_op == htons(ARPOP_REQUEST))
+		req_recv++;
+	if (option_mask32 & QUIT_ON_REPLY)
+		finish();
+	if (!(option_mask32 & BCAST_ONLY)) {
+		memcpy(he.sll_addr, p, me.sll_halen);
+		option_mask32 |= UNICASTING;
+	}
+	return true;
+}
+
+int arping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int arping_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *device = "eth0";
+	char *source = NULL;
+	char *target;
+	unsigned char *packet;
+	char *err_str;
+
+	INIT_G();
+
+	sock_fd = xsocket(AF_PACKET, SOCK_DGRAM, 0);
+
+	// Drop suid root privileges
+	// Need to remove SUID_NEVER from applets.h for this to work
+	//xsetuid(getuid());
+
+	err_str = xasprintf("interface %s %%s", device);
+	{
+		unsigned opt;
+		char *str_timeout;
+
+		/* Dad also sets quit_on_reply.
+		 * Advert also sets unsolicited.
+		 */
+		opt_complementary = "=1:Df:AU:c+";
+		opt = getopt32(argv, "DUAqfbc:w:I:s:",
+				&count, &str_timeout, &device, &source);
+		if (opt & 0x80) /* -w: timeout */
+			timeout_us = xatou_range(str_timeout, 0, INT_MAX/2000000) * 1000000 + 500000;
+		//if (opt & 0x200) /* -s: source */
+		option_mask32 &= 0x3f; /* set respective flags */
+	}
+
+	target = argv[optind];
+
+	xfunc_error_retval = 2;
+
+	{
+		struct ifreq ifr;
+
+		memset(&ifr, 0, sizeof(ifr));
+		strncpy_IFNAMSIZ(ifr.ifr_name, device);
+		/* We use ifr.ifr_name in error msg so that problem
+		 * with truncated name will be visible */
+		ioctl_or_perror_and_die(sock_fd, SIOCGIFINDEX, &ifr, err_str, "not found");
+		me.sll_ifindex = ifr.ifr_ifindex;
+
+		xioctl(sock_fd, SIOCGIFFLAGS, (char *) &ifr);
+
+		if (!(ifr.ifr_flags & IFF_UP)) {
+			bb_error_msg_and_die(err_str, "is down");
+		}
+		if (ifr.ifr_flags & (IFF_NOARP | IFF_LOOPBACK)) {
+			bb_error_msg(err_str, "is not ARPable");
+			return (option_mask32 & DAD ? 0 : 2);
+		}
+	}
+
+	/* if (!inet_aton(target, &dst)) - not needed */ {
+		len_and_sockaddr *lsa;
+		lsa = xhost_and_af2sockaddr(target, 0, AF_INET);
+		dst = lsa->u.sin.sin_addr;
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(lsa);
+	}
+
+	if (source && !inet_aton(source, &src)) {
+		bb_error_msg_and_die("invalid source address %s", source);
+	}
+
+	if ((option_mask32 & (DAD|UNSOLICITED)) == UNSOLICITED && src.s_addr == 0)
+		src = dst;
+
+	if (!(option_mask32 & DAD) || src.s_addr) {
+		struct sockaddr_in saddr;
+		int probe_fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+		setsockopt_bindtodevice(probe_fd, device);
+		memset(&saddr, 0, sizeof(saddr));
+		saddr.sin_family = AF_INET;
+		if (src.s_addr) {
+			/* Check that this is indeed our IP */
+			saddr.sin_addr = src;
+			xbind(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+		} else { /* !(option_mask32 & DAD) case */
+			/* Find IP address on this iface */
+			socklen_t alen = sizeof(saddr);
+
+			saddr.sin_port = htons(1025);
+			saddr.sin_addr = dst;
+
+			if (setsockopt(probe_fd, SOL_SOCKET, SO_DONTROUTE, &const_int_1, sizeof(const_int_1)) == -1)
+				bb_perror_msg("setsockopt(SO_DONTROUTE)");
+			xconnect(probe_fd, (struct sockaddr *) &saddr, sizeof(saddr));
+			getsockname(probe_fd, (struct sockaddr *) &saddr, &alen);
+			//never happens:
+			//if (getsockname(probe_fd, (struct sockaddr *) &saddr, &alen) == -1)
+			//	bb_perror_msg_and_die("getsockname");
+			if (saddr.sin_family != AF_INET)
+				bb_error_msg_and_die("no IP address configured");
+			src = saddr.sin_addr;
+		}
+		close(probe_fd);
+	}
+
+	me.sll_family = AF_PACKET;
+	//me.sll_ifindex = ifindex; - done before
+	me.sll_protocol = htons(ETH_P_ARP);
+	xbind(sock_fd, (struct sockaddr *) &me, sizeof(me));
+
+	{
+		socklen_t alen = sizeof(me);
+		getsockname(sock_fd, (struct sockaddr *) &me, &alen);
+		//never happens:
+		//if (getsockname(sock_fd, (struct sockaddr *) &me, &alen) == -1)
+		//	bb_perror_msg_and_die("getsockname");
+	}
+	if (me.sll_halen == 0) {
+		bb_error_msg(err_str, "is not ARPable (no ll address)");
+		return (option_mask32 & DAD ? 0 : 2);
+	}
+	he = me;
+	memset(he.sll_addr, -1, he.sll_halen);
+
+	if (!(option_mask32 & QUIET)) {
+		/* inet_ntoa uses static storage, can't use in same printf */
+		printf("ARPING to %s", inet_ntoa(dst));
+		printf(" from %s via %s\n", inet_ntoa(src), device);
+	}
+
+	signal_SA_RESTART_empty_mask(SIGINT,  (void (*)(int))finish);
+	signal_SA_RESTART_empty_mask(SIGALRM, (void (*)(int))catcher);
+
+	catcher();
+
+	packet = xmalloc(4096);
+	while (1) {
+		sigset_t sset, osset;
+		struct sockaddr_ll from;
+		socklen_t alen = sizeof(from);
+		int cc;
+
+		cc = recvfrom(sock_fd, packet, 4096, 0, (struct sockaddr *) &from, &alen);
+		if (cc < 0) {
+			bb_perror_msg("recvfrom");
+			continue;
+		}
+		sigemptyset(&sset);
+		sigaddset(&sset, SIGALRM);
+		sigaddset(&sset, SIGINT);
+		sigprocmask(SIG_BLOCK, &sset, &osset);
+		recv_pack(packet, cc, &from);
+		sigprocmask(SIG_SETMASK, &osset, NULL);
+	}
+}
diff --git a/busybox-1.19.3/networking/brctl.c b/busybox-1.19.3/networking/brctl.c
new file mode 100644
index 0000000..19f474f
--- /dev/null
+++ b/busybox-1.19.3/networking/brctl.c
@@ -0,0 +1,316 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Small implementation of brctl for busybox.
+ *
+ * Copyright (C) 2008 by Bernhard Reutner-Fischer
+ *
+ * Some helper functions from bridge-utils are
+ * Copyright (C) 2000 Lennert Buytenhek
+ *
+ * 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>
+
+#ifndef SIOCBRADDBR
+# define SIOCBRADDBR BRCTL_ADD_BRIDGE
+#endif
+#ifndef SIOCBRDELBR
+# define SIOCBRDELBR BRCTL_DEL_BRIDGE
+#endif
+#ifndef SIOCBRADDIF
+# define SIOCBRADDIF BRCTL_ADD_IF
+#endif
+#ifndef SIOCBRDELIF
+# define SIOCBRDELIF BRCTL_DEL_IF
+#endif
+
+
+/* Maximum number of ports supported per bridge interface.  */
+#ifndef MAX_PORTS
+# define MAX_PORTS 32
+#endif
+
+/* Use internal number parsing and not the "exact" conversion.  */
+/* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */
+#define BRCTL_USE_INTERNAL 1
+
+#if ENABLE_FEATURE_BRCTL_FANCY
+# 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,
+		const char *time_str)
+{
+	double secs;
+# if BRCTL_USE_INTERNAL
+	char *endptr;
+	secs = /*bb_*/strtod(time_str, &endptr);
+	if (endptr == time_str)
+# else
+	if (sscanf(time_str, "%lf", &secs) != 1)
+# endif
+		bb_error_msg_and_die(bb_msg_invalid_arg, time_str, "timespec");
+	tv->tv_sec = secs;
+	tv->tv_usec = 1000000 * (secs - tv->tv_sec);
+}
+
+static ALWAYS_INLINE unsigned long tv_to_jiffies(const struct timeval *tv)
+{
+	unsigned long long jif;
+
+	jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
+
+	return jif/10000;
+}
+# if 0
+static void jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
+{
+	unsigned long long tvusec;
+
+	tvusec = 10000ULL*jiffies;
+	tv->tv_sec = tvusec/1000000;
+	tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
+}
+# endif
+static unsigned long str_to_jiffies(const char *time_str)
+{
+	struct timeval tv;
+	strtotimeval(&tv, time_str);
+	return tv_to_jiffies(&tv);
+}
+
+static void arm_ioctl(unsigned long *args,
+		unsigned long arg0, unsigned long arg1, unsigned long arg2)
+{
+	args[0] = arg0;
+	args[1] = arg1;
+	args[2] = arg2;
+	args[3] = 0;
+}
+#endif
+
+
+int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int brctl_main(int argc UNUSED_PARAM, char **argv)
+{
+	static const char keywords[] ALIGN1 =
+		"addbr\0" "delbr\0" "addif\0" "delif\0"
+	IF_FEATURE_BRCTL_FANCY(
+		"stp\0"
+		"setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
+		"setpathcost\0" "setportprio\0" "setbridgeprio\0"
+	)
+	IF_FEATURE_BRCTL_SHOW("showmacs\0" "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
+		)
+		IF_FEATURE_BRCTL_SHOW(, ARG_showmacs, ARG_show)
+	};
+
+	int fd;
+	smallint key;
+	struct ifreq ifr;
+	char *br, *brif;
+
+	argv++;
+	while (*argv) {
+#if ENABLE_FEATURE_BRCTL_FANCY
+		int ifidx[MAX_PORTS];
+		unsigned long args[4];
+		ifr.ifr_data = (char *) &args;
+#endif
+
+		key = index_in_strings(keywords, *argv);
+		if (key == -1) /* no match found in keywords array, bail out. */
+			bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+		argv++;
+		fd = xsocket(AF_INET, SOCK_STREAM, 0);
+
+#if ENABLE_FEATURE_BRCTL_SHOW
+		if (key == ARG_show) { /* show */
+			char brname[IFNAMSIZ];
+			int bridx[MAX_PORTS];
+			int i, num;
+			arm_ioctl(args, BRCTL_GET_BRIDGES,
+						(unsigned long) bridx, MAX_PORTS);
+			num = xioctl(fd, SIOCGIFBR, args);
+			printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
+			for (i = 0; i < num; i++) {
+				char ifname[IFNAMSIZ];
+				int j, tabs;
+				struct __bridge_info bi;
+				unsigned char *x;
+
+				if (!if_indextoname(bridx[i], brname))
+					bb_perror_msg_and_die("can't get bridge name for index %d", i);
+				strncpy_IFNAMSIZ(ifr.ifr_name, brname);
+
+				arm_ioctl(args, BRCTL_GET_BRIDGE_INFO,
+							(unsigned long) &bi, 0);
+				xioctl(fd, SIOCDEVPRIVATE, &ifr);
+				printf("%s\t\t", brname);
+
+				/* print bridge id */
+				x = (unsigned char *) &bi.bridge_id;
+				for (j = 0; j < 8; j++) {
+					printf("%.2x", x[j]);
+					if (j == 1)
+						bb_putchar('.');
+				}
+				printf(bi.stp_enabled ? "\tyes" : "\tno");
+
+				/* print interface list */
+				arm_ioctl(args, BRCTL_GET_PORT_LIST,
+							(unsigned long) ifidx, MAX_PORTS);
+				xioctl(fd, SIOCDEVPRIVATE, &ifr);
+				tabs = 0;
+				for (j = 0; j < MAX_PORTS; j++) {
+					if (!ifidx[j])
+						continue;
+					if (!if_indextoname(ifidx[j], ifname))
+						bb_perror_msg_and_die("can't get interface name for index %d", j);
+					if (tabs)
+						printf("\t\t\t\t\t");
+					else
+						tabs = 1;
+					printf("\t\t%s\n", ifname);
+				}
+				if (!tabs)  /* bridge has no interfaces */
+					bb_putchar('\n');
+			}
+			goto done;
+		}
+#endif
+
+		if (!*argv) /* all but 'show' need at least one argument */
+			bb_show_usage();
+
+		br = *argv++;
+
+		if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
+			ioctl_or_perror_and_die(fd,
+					key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
+					br, "bridge %s", br);
+			goto done;
+		}
+
+		if (!*argv) /* all but 'addbr/delbr' need at least two arguments */
+			bb_show_usage();
+
+		strncpy_IFNAMSIZ(ifr.ifr_name, br);
+		if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
+			brif = *argv;
+			ifr.ifr_ifindex = if_nametoindex(brif);
+			if (!ifr.ifr_ifindex) {
+				bb_perror_msg_and_die("iface %s", brif);
+			}
+			ioctl_or_perror_and_die(fd,
+					key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
+					&ifr, "bridge %s", br);
+			goto done_next_argv;
+		}
+#if ENABLE_FEATURE_BRCTL_FANCY
+		if (key == ARG_stp) { /* stp */
+			static const char no_yes[] ALIGN1 =
+				"0\0" "off\0" "n\0" "no\0"   /* 0 .. 3 */
+				"1\0" "on\0"  "y\0" "yes\0"; /* 4 .. 7 */
+			int onoff = index_in_strings(no_yes, *argv);
+			if (onoff < 0)
+				bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+			onoff = (unsigned)onoff / 4;
+			arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0);
+			goto fire;
+		}
+		if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
+			static const uint8_t ops[] ALIGN1 = {
+				BRCTL_SET_AGEING_TIME,          /* ARG_setageing */
+				BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd     */
+				BRCTL_SET_BRIDGE_HELLO_TIME,    /* ARG_sethello  */
+				BRCTL_SET_BRIDGE_MAX_AGE        /* ARG_setmaxage */
+			};
+			arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0);
+			goto fire;
+		}
+		if (key == ARG_setpathcost
+		 || key == ARG_setportprio
+		 || key == ARG_setbridgeprio
+		) {
+			static const uint8_t ops[] ALIGN1 = {
+				BRCTL_SET_PATH_COST,      /* ARG_setpathcost   */
+				BRCTL_SET_PORT_PRIORITY,  /* ARG_setportprio   */
+				BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */
+			};
+			int port = -1;
+			unsigned arg1, arg2;
+
+			if (key != ARG_setbridgeprio) {
+				/* get portnum */
+				unsigned i;
+
+				port = if_nametoindex(*argv++);
+				if (!port)
+					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);
+				xioctl(fd, SIOCDEVPRIVATE, &ifr);
+				for (i = 0; i < MAX_PORTS; i++) {
+					if (ifidx[i] == port) {
+						port = i;
+						break;
+					}
+				}
+			}
+			arg1 = port;
+			arg2 = xatoi_positive(*argv);
+			if (key == ARG_setbridgeprio) {
+				arg1 = arg2;
+				arg2 = 0;
+			}
+			arm_ioctl(args, ops[key - ARG_setpathcost], arg1, arg2);
+		}
+ fire:
+		/* Execute the previously set command */
+		xioctl(fd, SIOCDEVPRIVATE, &ifr);
+#endif
+ done_next_argv:
+		argv++;
+ done:
+		close(fd);
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/dnsd.c b/busybox-1.19.3/networking/dnsd.c
new file mode 100644
index 0000000..fe98400
--- /dev/null
+++ b/busybox-1.19.3/networking/dnsd.c
@@ -0,0 +1,559 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini DNS server implementation for busybox
+ *
+ * Copyright (C) 2005 Roberto A. Foglietta (me@roberto.foglietta.name)
+ * 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 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.
+ * I also reused the input buffer for output and removed services he did not
+ * need.  [1] http://threading.2038bug.com/sheerdns/
+ *
+ * Some bugfix and minor changes was applied by Roberto A. Foglietta who made
+ * 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>
+
+//#define DEBUG 1
+#define DEBUG 0
+
+enum {
+	/* can tweak this */
+	DEFAULT_TTL = 120,
+
+	/* cannot get bigger packets than 512 per RFC1035. */
+	MAX_PACK_LEN = 512,
+	IP_STRING_LEN = sizeof(".xxx.xxx.xxx.xxx"),
+	MAX_NAME_LEN = IP_STRING_LEN - 1 + sizeof(".in-addr.arpa"),
+	REQ_A = 1,
+	REQ_PTR = 12,
+};
+
+/* the message from client and first part of response msg */
+struct dns_head {
+	uint16_t id;
+	uint16_t flags;
+	uint16_t nquer;
+	uint16_t nansw;
+	uint16_t nauth;
+	uint16_t nadd;
+};
+/* Structure used to access type and class fields.
+ * They are totally unaligned, but gcc 4.3.4 thinks that pointer of type uint16_t*
+ * is 16-bit aligned and replaces 16-bit memcpy (in move_from_unaligned16 macro)
+ * with aligned halfword access on arm920t!
+ * Oh well. Slapping PACKED everywhere seems to help: */
+struct type_and_class {
+	uint16_t type PACKED;
+	uint16_t class PACKED;
+} PACKED;
+/* element of known name, ip address and reversed ip address */
+struct dns_entry {
+	struct dns_entry *next;
+	uint32_t ip;
+	char rip[IP_STRING_LEN]; /* length decimal reversed IP */
+	char name[1];
+};
+
+#define OPT_verbose (option_mask32 & 1)
+#define OPT_silent  (option_mask32 & 2)
+
+
+/*
+ * Insert length of substrings instead of dots
+ */
+static void undot(char *rip)
+{
+	int i = 0;
+	int s = 0;
+
+	while (rip[i])
+		i++;
+	for (--i; i >= 0; i--) {
+		if (rip[i] == '.') {
+			rip[i] = s;
+			s = 0;
+		} else {
+			s++;
+		}
+	}
+}
+
+/*
+ * Read hostname/IP records from file
+ */
+static struct dns_entry *parse_conf_file(const char *fileconf)
+{
+	char *token[2];
+	parser_t *parser;
+	struct dns_entry *m, *conf_data;
+	struct dns_entry **nextp;
+
+	conf_data = NULL;
+	nextp = &conf_data;
+
+	parser = config_open(fileconf);
+	while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+		struct in_addr ip;
+		uint32_t v32;
+
+		if (inet_aton(token[1], &ip) == 0) {
+			bb_error_msg("error at line %u, skipping", parser->lineno);
+			continue;
+		}
+
+		if (OPT_verbose)
+			bb_error_msg("name:%s, ip:%s", token[0], token[1]);
+
+		/* sizeof(*m) includes 1 byte for m->name[0] */
+		m = xzalloc(sizeof(*m) + strlen(token[0]) + 1);
+		/*m->next = NULL;*/
+		*nextp = m;
+		nextp = &m->next;
+
+		m->name[0] = '.';
+		strcpy(m->name + 1, token[0]);
+		undot(m->name);
+		m->ip = ip.s_addr; /* in network order */
+		v32 = ntohl(m->ip);
+		/* inverted order */
+		sprintf(m->rip, ".%u.%u.%u.%u",
+			(uint8_t)(v32),
+			(uint8_t)(v32 >> 8),
+			(uint8_t)(v32 >> 16),
+			(v32 >> 24)
+		);
+		undot(m->rip);
+	}
+	config_close(parser);
+	return conf_data;
+}
+
+/*
+ * Look query up in dns records and return answer if found.
+ */
+static char *table_lookup(struct dns_entry *d,
+		uint16_t type,
+		char* query_string)
+{
+	while (d) {
+		unsigned len = d->name[0];
+		/* d->name[len] is the last (non NUL) char */
+#if DEBUG
+		char *p, *q;
+		q = query_string + 1;
+		p = d->name + 1;
+		fprintf(stderr, "%d/%d p:%s q:%s %d\n",
+			(int)strlen(p), len,
+			p, q, (int)strlen(q)
+		);
+#endif
+		if (type == htons(REQ_A)) {
+			/* search by host name */
+			if (len != 1 || d->name[1] != '*') {
+/* we are lax, hope no name component is ever >64 so that length
+ * (which will be represented as 'A','B'...) matches a lowercase letter.
+ * Actually, I think false matches are hard to construct.
+ * Example.
+ * [31] len is represented as '1', [65] as 'A', [65+32] as 'a'.
+ * [65]   <65 same chars>[31]<31 same chars>NUL
+ * [65+32]<65 same chars>1   <31 same chars>NUL
+ * This example seems to be the minimal case when false match occurs.
+ */
+				if (strcasecmp(d->name, query_string) != 0)
+					goto next;
+			}
+			return (char *)&d->ip;
+#if DEBUG
+			fprintf(stderr, "Found IP:%x\n", (int)d->ip);
+#endif
+			return 0;
+		}
+		/* search by IP-address */
+		if ((len != 1 || d->name[1] != '*')
+		/* we assume (do not check) that query_string
+		 * ends in ".in-addr.arpa" */
+		 && strncmp(d->rip, query_string, strlen(d->rip)) == 0
+		) {
+#if DEBUG
+			fprintf(stderr, "Found name:%s\n", d->name);
+#endif
+			return d->name;
+		}
+ next:
+		d = d->next;
+	}
+
+	return NULL;
+}
+
+/*
+ * Decode message and generate answer
+ */
+/* RFC 1035
+...
+Whenever an octet represents a numeric quantity, the left most bit
+in the diagram is the high order or most significant bit.
+That is, the bit labeled 0 is the most significant bit.
+...
+
+4.1.1. Header section format
+      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      ID                       |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |QR|   OPCODE  |AA|TC|RD|RA| 0  0  0|   RCODE   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    QDCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ANCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    NSCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                    ARCOUNT                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ID      16 bit random identifier assigned by querying peer.
+        Used to match query/response.
+QR      message is a query (0), or a response (1).
+OPCODE  0   standard query (QUERY)
+        1   inverse query (IQUERY)
+        2   server status request (STATUS)
+AA      Authoritative Answer - this bit is valid in responses.
+        Responding name server is an authority for the domain name
+        in question section. Answer section may have multiple owner names
+        because of aliases.  The AA bit corresponds to the name which matches
+        the query name, or the first owner name in the answer section.
+TC      TrunCation - this message was truncated.
+RD      Recursion Desired - this bit may be set in a query and
+        is copied into the response.  If RD is set, it directs
+        the name server to pursue the query recursively.
+        Recursive query support is optional.
+RA      Recursion Available - this be is set or cleared in a
+        response, and denotes whether recursive query support is
+        available in the name server.
+RCODE   Response code.
+        0   No error condition
+        1   Format error
+        2   Server failure - server was unable to process the query
+            due to a problem with the name server.
+        3   Name Error - meaningful only for responses from
+            an authoritative name server. The referenced domain name
+            does not exist.
+        4   Not Implemented.
+        5   Refused.
+QDCOUNT number of entries in the question section.
+ANCOUNT number of records in the answer section.
+NSCOUNT number of records in the authority records section.
+ARCOUNT number of records in the additional records section.
+
+4.1.2. Question section format
+
+The section contains QDCOUNT (usually 1) entries, each of this format:
+      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                     QNAME                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     QTYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     QCLASS                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+QNAME   a domain name represented as a sequence of labels, where
+        each label consists of a length octet followed by that
+        number of octets. The domain name terminates with the
+        zero length octet for the null label of the root. Note
+        that this field may be an odd number of octets; no
+        padding is used.
+QTYPE   a two octet type of the query.
+          1 a host address [REQ_A const]
+          2 an authoritative name server
+          3 a mail destination (Obsolete - use MX)
+          4 a mail forwarder (Obsolete - use MX)
+          5 the canonical name for an alias
+          6 marks the start of a zone of authority
+          7 a mailbox domain name (EXPERIMENTAL)
+          8 a mail group member (EXPERIMENTAL)
+          9 a mail rename domain name (EXPERIMENTAL)
+         10 a null RR (EXPERIMENTAL)
+         11 a well known service description
+         12 a domain name pointer [REQ_PTR const]
+         13 host information
+         14 mailbox or mail list information
+         15 mail exchange
+         16 text strings
+       0x1c IPv6?
+        252 a request for a transfer of an entire zone
+        253 a request for mailbox-related records (MB, MG or MR)
+        254 a request for mail agent RRs (Obsolete - see MX)
+        255 a request for all records
+QCLASS  a two octet code that specifies the class of the query.
+          1 the Internet
+        (others are historic only)
+        255 any class
+
+4.1.3. Resource Record format
+
+The answer, authority, and additional sections all share the same format:
+a variable number of resource records, where the number of records
+is specified in the corresponding count field in the header.
+Each resource record has this format:
+      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    /                                               /
+    /                      NAME                     /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TYPE                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                     CLASS                     |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                      TTL                      |
+    |                                               |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    |                   RDLENGTH                    |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
+    /                     RDATA                     /
+    /                                               /
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+NAME    a domain name to which this resource record pertains.
+TYPE    two octets containing one of the RR type codes.  This
+        field specifies the meaning of the data in the RDATA field.
+CLASS   two octets which specify the class of the data in the RDATA field.
+TTL     a 32 bit unsigned integer that specifies the time interval
+        (in seconds) that the record may be cached.
+RDLENGTH a 16 bit integer, length in octets of the RDATA field.
+RDATA   a variable length string of octets that describes the resource.
+        The format of this information varies according to the TYPE
+        and CLASS of the resource record.
+        If the TYPE is A and the CLASS is IN, it's a 4 octet IP address.
+
+4.1.4. Message compression
+
+In order to reduce the size of messages, domain names coan be compressed.
+An entire domain name or a list of labels at the end of a domain name
+is replaced with a pointer to a prior occurance of the same name.
+
+The pointer takes the form of a two octet sequence:
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    | 1  1|                OFFSET                   |
+    +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+The first two bits are ones.  This allows a pointer to be distinguished
+from a label, since the label must begin with two zero bits because
+labels are restricted to 63 octets or less.  The OFFSET field specifies
+an offset from the start of the message (i.e., the first octet
+of the ID field in the domain header).
+A zero offset specifies the first byte of the ID field, etc.
+Domain name in a message can be represented as either:
+   - a sequence of labels ending in a zero octet
+   - a pointer
+   - a sequence of labels ending with a pointer
+ */
+static int process_packet(struct dns_entry *conf_data,
+		uint32_t conf_ttl,
+		uint8_t *buf)
+{
+	struct dns_head *head;
+	struct type_and_class *unaligned_type_class;
+	const char *err_msg;
+	char *query_string;
+	char *answstr;
+	uint8_t *answb;
+	uint16_t outr_rlen;
+	uint16_t outr_flags;
+	uint16_t type;
+	uint16_t class;
+	int query_len;
+
+	head = (struct dns_head *)buf;
+	if (head->nquer == 0) {
+		bb_error_msg("packet has 0 queries, ignored");
+		return 0; /* don't reply */
+	}
+	if (head->flags & htons(0x8000)) { /* QR bit */
+		bb_error_msg("response packet, ignored");
+		return 0; /* don't reply */
+	}
+	/* QR = 1 "response", RCODE = 4 "Not Implemented" */
+	outr_flags = htons(0x8000 | 4);
+	err_msg = NULL;
+
+	/* start of query string */
+	query_string = (void *)(head + 1);
+	/* caller guarantees strlen is <= MAX_PACK_LEN */
+	query_len = strlen(query_string) + 1;
+	/* may be unaligned! */
+	unaligned_type_class = (void *)(query_string + query_len);
+	query_len += sizeof(*unaligned_type_class);
+	/* where to append answer block */
+	answb = (void *)(unaligned_type_class + 1);
+
+	/* OPCODE != 0 "standard query"? */
+	if ((head->flags & htons(0x7800)) != 0) {
+		err_msg = "opcode != 0";
+		goto empty_packet;
+	}
+	move_from_unaligned16(class, &unaligned_type_class->class);
+	if (class != htons(1)) { /* not class INET? */
+		err_msg = "class != 1";
+		goto empty_packet;
+	}
+	move_from_unaligned16(type, &unaligned_type_class->type);
+	if (type != htons(REQ_A) && type != htons(REQ_PTR)) {
+		/* we can't handle this query type */
+//TODO: happens all the time with REQ_AAAA (0x1c) requests - implement those?
+		err_msg = "type is !REQ_A and !REQ_PTR";
+		goto empty_packet;
+	}
+
+	/* look up the name */
+	answstr = table_lookup(conf_data, type, query_string);
+#if DEBUG
+	/* Shows lengths instead of dots, unusable for !DEBUG */
+	bb_error_msg("'%s'->'%s'", query_string, answstr);
+#endif
+	outr_rlen = 4;
+	if (answstr && type == htons(REQ_PTR)) {
+		/* returning a host name */
+		outr_rlen = strlen(answstr) + 1;
+	}
+	if (!answstr
+	 || (unsigned)(answb - buf) + query_len + 4 + 2 + outr_rlen > MAX_PACK_LEN
+	) {
+		/* QR = 1 "response"
+		 * AA = 1 "Authoritative Answer"
+		 * RCODE = 3 "Name Error" */
+		err_msg = "name is not found";
+		outr_flags = htons(0x8000 | 0x0400 | 3);
+		goto empty_packet;
+	}
+
+	/* Append answer Resource Record */
+	memcpy(answb, query_string, query_len); /* name, type, class */
+	answb += query_len;
+	move_to_unaligned32((uint32_t *)answb, htonl(conf_ttl));
+	answb += 4;
+	move_to_unaligned16((uint16_t *)answb, htons(outr_rlen));
+	answb += 2;
+	memcpy(answb, answstr, outr_rlen);
+	answb += outr_rlen;
+
+	/* QR = 1 "response",
+	 * AA = 1 "Authoritative Answer",
+	 * TODO: need to set RA bit 0x80? One user says nslookup complains
+	 * "Got recursion not available from SERVER, trying next server"
+	 * "** server can't find HOSTNAME"
+	 * RCODE = 0 "success"
+	 */
+	if (OPT_verbose)
+		bb_error_msg("returning positive reply");
+	outr_flags = htons(0x8000 | 0x0400 | 0);
+	/* we have one answer */
+	head->nansw = htons(1);
+
+ empty_packet:
+	if ((outr_flags & htons(0xf)) != 0) { /* not a positive response */
+		if (OPT_verbose) {
+			bb_error_msg("%s, %s",
+				err_msg,
+				OPT_silent ? "dropping query" : "sending error reply"
+			);
+		}
+		if (OPT_silent)
+			return 0;
+	}
+	head->flags |= outr_flags;
+	head->nauth = head->nadd = 0;
+	head->nquer = htons(1); // why???
+
+	return answb - buf;
+}
+
+int dnsd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dnsd_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *listen_interface = "0.0.0.0";
+	const char *fileconf = "/etc/dnsd.conf";
+	struct dns_entry *conf_data;
+	uint32_t conf_ttl = DEFAULT_TTL;
+	char *sttl, *sport;
+	len_and_sockaddr *lsa, *from, *to;
+	unsigned lsa_size;
+	int udps, opts;
+	uint16_t port = 53;
+	/* Ensure buf is 32bit aligned (we need 16bit, but 32bit can't hurt) */
+	uint8_t buf[MAX_PACK_LEN + 1] ALIGN4;
+
+	opts = getopt32(argv, "vsi:c:t:p:d", &listen_interface, &fileconf, &sttl, &sport);
+	//if (opts & (1 << 0)) // -v
+	//if (opts & (1 << 1)) // -s
+	//if (opts & (1 << 2)) // -i
+	//if (opts & (1 << 3)) // -c
+	if (opts & (1 << 4)) // -t
+		conf_ttl = xatou_range(sttl, 1, 0xffffffff);
+	if (opts & (1 << 5)) // -p
+		port = xatou_range(sport, 1, 0xffff);
+	if (opts & (1 << 6)) { // -d
+		bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+		openlog(applet_name, LOG_PID, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG;
+	}
+
+	conf_data = parse_conf_file(fileconf);
+
+	lsa = xdotted2sockaddr(listen_interface, port);
+	udps = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+	xbind(udps, &lsa->u.sa, lsa->len);
+	socket_want_pktinfo(udps); /* needed for recv_from_to to work */
+	lsa_size = LSA_LEN_SIZE + lsa->len;
+	from = xzalloc(lsa_size);
+	to = xzalloc(lsa_size);
+
+	{
+		char *p = xmalloc_sockaddr2dotted(&lsa->u.sa);
+		bb_error_msg("accepting UDP packets on %s", p);
+		free(p);
+	}
+
+	while (1) {
+		int r;
+		/* Try to get *DEST* address (to which of our addresses
+		 * this query was directed), and reply from the same address.
+		 * Or else we can exhibit usual UDP ugliness:
+		 * [ip1.multihomed.ip2] <=  query to ip1  <= peer
+		 * [ip1.multihomed.ip2] => reply from ip2 => peer (confused) */
+		memcpy(to, lsa, lsa_size);
+		r = recv_from_to(udps, buf, MAX_PACK_LEN + 1, 0, &from->u.sa, &to->u.sa, lsa->len);
+		if (r < 12 || r > MAX_PACK_LEN) {
+			bb_error_msg("packet size %d, ignored", r);
+			continue;
+		}
+		if (OPT_verbose)
+			bb_error_msg("got UDP packet");
+		buf[r] = '\0'; /* paranoia */
+		r = process_packet(conf_data, conf_ttl, buf);
+		if (r <= 0)
+			continue;
+		send_to_from(udps, buf, r, 0, &from->u.sa, &to->u.sa, lsa->len);
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/ether-wake.c b/busybox-1.19.3/networking/ether-wake.c
new file mode 100644
index 0000000..6a88279
--- /dev/null
+++ b/busybox-1.19.3/networking/ether-wake.c
@@ -0,0 +1,286 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ether-wake.c - Send a magic packet to wake up sleeping machines.
+ *
+ * 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>
+ *               Used version of ether-wake.c: v1.09 11/12/2003 Donald Becker, http://www.scyld.com/";
+ */
+
+/* full usage according Donald Becker
+ * usage: ether-wake [-i <ifname>] [-p aa:bb:cc:dd[:ee:ff]] 00:11:22:33:44:55\n"
+ *
+ *	This program generates and transmits a Wake-On-LAN (WOL)\n"
+ *	\"Magic Packet\", used for restarting machines that have been\n"
+ *	soft-powered-down (ACPI D3-warm state).\n"
+ *	It currently generates the standard AMD Magic Packet format, with\n"
+ *	an optional password appended.\n"
+ *
+ *	The single required parameter is the Ethernet MAC (station) address\n"
+ *	of the machine to wake or a host ID with known NSS 'ethers' entry.\n"
+ *	The MAC address may be found with the 'arp' program while the target\n"
+ *	machine is awake.\n"
+ *
+ *	Options:\n"
+ *		-b	Send wake-up packet to the broadcast address.\n"
+ *		-D	Increase the debug level.\n"
+ *		-i ifname	Use interface IFNAME instead of the default 'eth0'.\n"
+ *		-p <pw>		Append the four or six byte password PW to the packet.\n"
+ *					A password is only required for a few adapter types.\n"
+ *					The password may be specified in ethernet hex format\n"
+ *					or dotted decimal (Internet address)\n"
+ *		-p 00:22:44:66:88:aa\n"
+ *		-p 192.168.1.1\n";
+ *
+ *
+ *	This program generates and transmits a Wake-On-LAN (WOL) "Magic Packet",
+ *	used for restarting machines that have been soft-powered-down
+ *	(ACPI D3-warm state).  It currently generates the standard AMD Magic Packet
+ *	format, with an optional password appended.
+ *
+ *	This software may be used and distributed according to the terms
+ *	of the GNU Public License, incorporated herein by reference.
+ *	Contact the author for use under other terms.
+ *
+ *	This source file was originally part of the network tricks package, and
+ *	is now distributed to support the Scyld Beowulf system.
+ *	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
+ *
+ *   Notes:
+ *   On some systems dropping root capability allows the process to be
+ *   dumped, traced or debugged.
+ *   If someone traces this program, they get control of a raw socket.
+ *   Linux handles this safely, but beware when porting this program.
+ *
+ *   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]] 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		Send wake-up packet to the broadcast address"
+//usage:     "\n	-i iface	Interface to use (default eth0)"
+//usage:     "\n	-p pass		Append four or six byte password PW to the packet"
+
+#include "libbb.h"
+#include <netpacket/packet.h>
+#include <netinet/ether.h>
+#include <linux/if.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.
+ */
+#ifdef PF_PACKET
+# define whereto_t sockaddr_ll
+# define make_socket() xsocket(PF_PACKET, SOCK_RAW, 0)
+#else
+# define whereto_t sockaddr
+# define make_socket() xsocket(AF_INET, SOCK_PACKET, SOCK_PACKET)
+#endif
+
+#ifdef DEBUG
+# define bb_debug_msg(fmt, args...) fprintf(stderr, fmt, ## args)
+void bb_debug_dump_packet(unsigned char *outpack, int pktsize)
+{
+	int i;
+	printf("packet dump:\n");
+	for (i = 0; i < pktsize; ++i) {
+		printf("%2.2x ", outpack[i]);
+		if (i % 20 == 19) bb_putchar('\n');
+	}
+	printf("\n\n");
+}
+#else
+# define bb_debug_msg(fmt, args...)             ((void)0)
+# define bb_debug_dump_packet(outpack, pktsize) ((void)0)
+#endif
+
+/* Convert the host ID string to a MAC address.
+ * The string may be a:
+ *    Host name
+ *    IP address string
+ *    MAC address string
+*/
+static void get_dest_addr(const char *hostid, struct ether_addr *eaddr)
+{
+	struct ether_addr *eap;
+
+	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)
+	} else if (ether_hostton(hostid, eaddr) == 0) {
+		bb_debug_msg("Station address for hostname %s is %s\n\n", hostid, ether_ntoa(eaddr));
+#endif
+	} else {
+		bb_show_usage();
+	}
+}
+
+static int get_fill(unsigned char *pkt, struct ether_addr *eaddr, int broadcast)
+{
+	int i;
+	unsigned char *station_addr = eaddr->ether_addr_octet;
+
+	memset(pkt, 0xff, 6);
+	if (!broadcast)
+		memcpy(pkt, station_addr, 6);
+	pkt += 6;
+
+	memcpy(pkt, station_addr, 6); /* 6 */
+	pkt += 6;
+
+	*pkt++ = 0x08; /* 12 */ /* Or 0x0806 for ARP, 0x8035 for RARP */
+	*pkt++ = 0x42; /* 13 */
+
+	memset(pkt, 0xff, 6); /* 14 */
+
+	for (i = 0; i < 16; ++i) {
+		pkt += 6;
+		memcpy(pkt, station_addr, 6); /* 20,26,32,... */
+	}
+
+	return 20 + 16*6; /* length of packet */
+}
+
+static int get_wol_pw(const char *ethoptarg, unsigned char *wol_passwd)
+{
+	unsigned passwd[6];
+	int byte_cnt, i;
+
+	/* handle MAC format */
+	byte_cnt = sscanf(ethoptarg, "%2x:%2x:%2x:%2x:%2x:%2x",
+	                  &passwd[0], &passwd[1], &passwd[2],
+	                  &passwd[3], &passwd[4], &passwd[5]);
+	/* handle IP format */
+// FIXME: why < 4?? should it be < 6?
+	if (byte_cnt < 4)
+		byte_cnt = sscanf(ethoptarg, "%u.%u.%u.%u",
+		                  &passwd[0], &passwd[1], &passwd[2], &passwd[3]);
+	if (byte_cnt < 4) {
+		bb_error_msg("can't read Wake-On-LAN pass");
+		return 0;
+	}
+// TODO: check invalid numbers >255??
+	for (i = 0; i < byte_cnt; ++i)
+		wol_passwd[i] = passwd[i];
+
+	bb_debug_msg("password: %2.2x %2.2x %2.2x %2.2x (%d)\n\n",
+	             wol_passwd[0], wol_passwd[1], wol_passwd[2], wol_passwd[3],
+	             byte_cnt);
+
+	return byte_cnt;
+}
+
+int ether_wake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ether_wake_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *ifname = "eth0";
+	char *pass;
+	unsigned flags;
+	unsigned char wol_passwd[6];
+	int wol_passwd_sz = 0;
+	int s;  /* Raw socket */
+	int pktsize;
+	unsigned char outpack[1000];
+
+	struct ether_addr eaddr;
+	struct whereto_t whereto;  /* who to wake up */
+
+	/* handle misc user options */
+	opt_complementary = "=1";
+	flags = getopt32(argv, "bi:p:", &ifname, &pass);
+	if (flags & 4) /* -p */
+		wol_passwd_sz = get_wol_pw(pass, wol_passwd);
+	flags &= 1; /* we further interested only in -b [bcast] flag */
+
+	/* create the raw socket */
+	s = make_socket();
+
+	/* now that we have a raw socket we can drop root */
+	/* xsetuid(getuid()); - but save on code size... */
+
+	/* look up the dest mac address */
+	get_dest_addr(argv[optind], &eaddr);
+
+	/* fill out the header of the packet */
+	pktsize = get_fill(outpack, &eaddr, flags /* & 1 OPT_BROADCAST */);
+
+	bb_debug_dump_packet(outpack, pktsize);
+
+	/* Fill in the source address, if possible. */
+#ifdef __linux__
+	{
+		struct ifreq if_hwaddr;
+
+		strncpy_IFNAMSIZ(if_hwaddr.ifr_name, ifname);
+		ioctl_or_perror_and_die(s, SIOCGIFHWADDR, &if_hwaddr, "SIOCGIFHWADDR on %s failed", ifname);
+
+		memcpy(outpack+6, if_hwaddr.ifr_hwaddr.sa_data, 6);
+
+# ifdef DEBUG
+		{
+			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]);
+		}
+# endif
+	}
+#endif /* __linux__ */
+
+	bb_debug_dump_packet(outpack, pktsize);
+
+	/* append the password if specified */
+	if (wol_passwd_sz > 0) {
+		memcpy(outpack+pktsize, wol_passwd, wol_passwd_sz);
+		pktsize += wol_passwd_sz;
+	}
+
+	bb_debug_dump_packet(outpack, pktsize);
+
+	/* This is necessary for broadcasts to work */
+	if (flags /* & 1 OPT_BROADCAST */) {
+		if (setsockopt_broadcast(s) != 0)
+			bb_perror_msg("SO_BROADCAST");
+	}
+
+#if defined(PF_PACKET)
+	{
+		struct ifreq ifr;
+		strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+		xioctl(s, SIOCGIFINDEX, &ifr);
+		memset(&whereto, 0, sizeof(whereto));
+		whereto.sll_family = AF_PACKET;
+		whereto.sll_ifindex = ifr.ifr_ifindex;
+		/* The manual page incorrectly claims the address must be filled.
+		   We do so because the code may change to match the docs. */
+		whereto.sll_halen = ETH_ALEN;
+		memcpy(whereto.sll_addr, outpack, ETH_ALEN);
+	}
+#else
+	whereto.sa_family = 0;
+	strcpy(whereto.sa_data, ifname);
+#endif
+	xsendto(s, outpack, pktsize, (struct sockaddr *)&whereto, sizeof(whereto));
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(s);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/ftpd.c b/busybox-1.19.3/networking/ftpd.c
new file mode 100644
index 0000000..e38138c
--- /dev/null
+++ b/busybox-1.19.3/networking/ftpd.c
@@ -0,0 +1,1378 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Simple FTP daemon, based on vsftpd 2.0.7 (written by Chris Evans)
+ *
+ * Author: Adam Tkac <vonsch@gmail.com>
+ *
+ * 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>
+
+#define FTP_DATACONN            150
+#define FTP_NOOPOK              200
+#define FTP_TYPEOK              200
+#define FTP_PORTOK              200
+#define FTP_STRUOK              200
+#define FTP_MODEOK              200
+#define FTP_ALLOOK              202
+#define FTP_STATOK              211
+#define FTP_STATFILE_OK         213
+#define FTP_HELP                214
+#define FTP_SYSTOK              215
+#define FTP_GREET               220
+#define FTP_GOODBYE             221
+#define FTP_TRANSFEROK          226
+#define FTP_PASVOK              227
+/*#define FTP_EPRTOK              228*/
+#define FTP_EPSVOK              229
+#define FTP_LOGINOK             230
+#define FTP_CWDOK               250
+#define FTP_RMDIROK             250
+#define FTP_DELEOK              250
+#define FTP_RENAMEOK            250
+#define FTP_PWDOK               257
+#define FTP_MKDIROK             257
+#define FTP_GIVEPWORD           331
+#define FTP_RESTOK              350
+#define FTP_RNFROK              350
+#define FTP_TIMEOUT             421
+#define FTP_BADSENDCONN         425
+#define FTP_BADSENDNET          426
+#define FTP_BADSENDFILE         451
+#define FTP_BADCMD              500
+#define FTP_COMMANDNOTIMPL      502
+#define FTP_NEEDUSER            503
+#define FTP_NEEDRNFR            503
+#define FTP_BADSTRU             504
+#define FTP_BADMODE             504
+#define FTP_LOGINERR            530
+#define FTP_FILEFAIL            550
+#define FTP_NOPERM              550
+#define FTP_UPLOADFAIL          553
+
+#define STR1(s) #s
+#define STR(s) STR1(s)
+
+/* Convert a constant to 3-digit string, packed into uint32_t */
+enum {
+	/* Shift for Nth decimal digit */
+	SHIFT2  =  0 * BB_LITTLE_ENDIAN + 24 * BB_BIG_ENDIAN,
+	SHIFT1  =  8 * BB_LITTLE_ENDIAN + 16 * BB_BIG_ENDIAN,
+	SHIFT0  = 16 * BB_LITTLE_ENDIAN + 8 * BB_BIG_ENDIAN,
+	/* And for 4th position (space) */
+	SHIFTsp = 24 * BB_LITTLE_ENDIAN + 0 * BB_BIG_ENDIAN,
+};
+#define STRNUM32(s) (uint32_t)(0 \
+	| (('0' + ((s) / 1 % 10)) << SHIFT0) \
+	| (('0' + ((s) / 10 % 10)) << SHIFT1) \
+	| (('0' + ((s) / 100 % 10)) << SHIFT2) \
+)
+#define STRNUM32sp(s) (uint32_t)(0 \
+	| (' ' << SHIFTsp) \
+	| (('0' + ((s) / 1 % 10)) << SHIFT0) \
+	| (('0' + ((s) / 10 % 10)) << SHIFT1) \
+	| (('0' + ((s) / 100 % 10)) << SHIFT2) \
+)
+
+#define MSG_OK "Operation successful\r\n"
+#define MSG_ERR "Error\r\n"
+
+struct globals {
+	int pasv_listen_fd;
+#if !BB_MMU
+	int root_fd;
+#endif
+	int local_file_fd;
+	unsigned end_time;
+	unsigned timeout;
+	unsigned verbose;
+	off_t local_file_pos;
+	off_t restart_pos;
+	len_and_sockaddr *local_addr;
+	len_and_sockaddr *port_addr;
+	char *ftp_cmd;
+	char *ftp_arg;
+#if ENABLE_FEATURE_FTP_WRITE
+	char *rnfr_filename;
+#endif
+	/* We need these aligned to uint32_t */
+	char msg_ok [(sizeof("NNN " MSG_OK ) + 3) & 0xfffc];
+	char msg_err[(sizeof("NNN " MSG_ERR) + 3) & 0xfffc];
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	/* Moved to main */ \
+	/*strcpy(G.msg_ok  + 4, MSG_OK );*/ \
+	/*strcpy(G.msg_err + 4, MSG_ERR);*/ \
+} while (0)
+
+
+static char *
+escape_text(const char *prepend, const char *str, unsigned escapee)
+{
+	unsigned retlen, remainlen, chunklen;
+	char *ret, *found;
+	char append;
+
+	append = (char)escapee;
+	escapee >>= 8;
+
+	remainlen = strlen(str);
+	retlen = strlen(prepend);
+	ret = xmalloc(retlen + remainlen * 2 + 1 + 1);
+	strcpy(ret, prepend);
+
+	for (;;) {
+		found = strchrnul(str, escapee);
+		chunklen = found - str + 1;
+
+		/* Copy chunk up to and including escapee (or NUL) to ret */
+		memcpy(ret + retlen, str, chunklen);
+		retlen += chunklen;
+
+		if (*found == '\0') {
+			/* It wasn't escapee, it was NUL! */
+			ret[retlen - 1] = append; /* replace NUL */
+			ret[retlen] = '\0'; /* add NUL */
+			break;
+		}
+		ret[retlen++] = escapee; /* duplicate escapee */
+		str = found + 1;
+	}
+	return ret;
+}
+
+/* Returns strlen as a bonus */
+static unsigned
+replace_char(char *str, char from, char to)
+{
+	char *p = str;
+	while (*p) {
+		if (*p == from)
+			*p = to;
+		p++;
+	}
+	return p - str;
+}
+
+static void
+verbose_log(const char *str)
+{
+	bb_error_msg("%.*s", (int)strcspn(str, "\r\n"), str);
+}
+
+/* NB: status_str is char[4] packed into uint32_t */
+static void
+cmdio_write(uint32_t status_str, const char *str)
+{
+	char *response;
+	int len;
+
+	/* FTP uses telnet protocol for command link.
+	 * In telnet, 0xff is an escape char, and needs to be escaped: */
+	response = escape_text((char *) &status_str, str, (0xff << 8) + '\r');
+
+	/* FTP sends embedded LFs as NULs */
+	len = replace_char(response, '\n', '\0');
+
+	response[len++] = '\n'; /* tack on trailing '\n' */
+	xwrite(STDOUT_FILENO, response, len);
+	if (G.verbose > 1)
+		verbose_log(response);
+	free(response);
+}
+
+static void
+cmdio_write_ok(unsigned status)
+{
+	*(uint32_t *) G.msg_ok = status;
+	xwrite(STDOUT_FILENO, G.msg_ok, sizeof("NNN " MSG_OK) - 1);
+	if (G.verbose > 1)
+		verbose_log(G.msg_ok);
+}
+#define WRITE_OK(a) cmdio_write_ok(STRNUM32sp(a))
+
+/* TODO: output strerr(errno) if errno != 0? */
+static void
+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 > 0)
+		verbose_log(G.msg_err);
+}
+#define WRITE_ERR(a) cmdio_write_error(STRNUM32sp(a))
+
+static void
+cmdio_write_raw(const char *p_text)
+{
+	xwrite_str(STDOUT_FILENO, p_text);
+	if (G.verbose > 1)
+		verbose_log(p_text);
+}
+
+static void
+timeout_handler(int sig UNUSED_PARAM)
+{
+	off_t pos;
+	int sv_errno = errno;
+
+	if ((int)(monotonic_sec() - G.end_time) >= 0)
+		goto timed_out;
+
+	if (!G.local_file_fd)
+		goto timed_out;
+
+	pos = xlseek(G.local_file_fd, 0, SEEK_CUR);
+	if (pos == G.local_file_pos)
+		goto timed_out;
+	G.local_file_pos = pos;
+
+	alarm(G.timeout);
+	errno = sv_errno;
+	return;
+
+ timed_out:
+	cmdio_write_raw(STR(FTP_TIMEOUT)" Timeout\r\n");
+/* TODO: do we need to abort (as opposed to usual shutdown) data transfer? */
+	exit(1);
+}
+
+/* Simple commands */
+
+static void
+handle_pwd(void)
+{
+	char *cwd, *response;
+
+	cwd = xrealloc_getcwd_or_warn(NULL);
+	if (cwd == NULL)
+		cwd = xstrdup("");
+
+	/* We have to promote each " to "" */
+	response = escape_text(" \"", cwd, ('"' << 8) + '"');
+	free(cwd);
+	cmdio_write(STRNUM32(FTP_PWDOK), response);
+	free(response);
+}
+
+static void
+handle_cwd(void)
+{
+	if (!G.ftp_arg || chdir(G.ftp_arg) != 0) {
+		WRITE_ERR(FTP_FILEFAIL);
+		return;
+	}
+	WRITE_OK(FTP_CWDOK);
+}
+
+static void
+handle_cdup(void)
+{
+	G.ftp_arg = (char*)"..";
+	handle_cwd();
+}
+
+static void
+handle_stat(void)
+{
+	cmdio_write_raw(STR(FTP_STATOK)"-Server status:\r\n"
+			" TYPE: BINARY\r\n"
+			STR(FTP_STATOK)" Ok\r\n");
+}
+
+/* Examples of HELP and FEAT:
+# nc -vvv ftp.kernel.org 21
+ftp.kernel.org (130.239.17.4:21) open
+220 Welcome to ftp.kernel.org.
+FEAT
+211-Features:
+ EPRT
+ EPSV
+ MDTM
+ PASV
+ REST STREAM
+ SIZE
+ TVFS
+ UTF8
+211 End
+HELP
+214-The following commands are recognized.
+ ABOR ACCT ALLO APPE CDUP CWD  DELE EPRT EPSV FEAT HELP LIST MDTM MKD
+ MODE NLST NOOP OPTS PASS PASV PORT PWD  QUIT REIN REST RETR RMD  RNFR
+ RNTO SITE SIZE SMNT STAT STOR STOU STRU SYST TYPE USER XCUP XCWD XMKD
+ XPWD XRMD
+214 Help OK.
+*/
+static void
+handle_feat(unsigned status)
+{
+	cmdio_write(status, "-Features:");
+	cmdio_write_raw(" EPSV\r\n"
+			" PASV\r\n"
+			" REST STREAM\r\n"
+			" MDTM\r\n"
+			" SIZE\r\n");
+	cmdio_write(status, " Ok");
+}
+
+/* Download commands */
+
+static inline int
+port_active(void)
+{
+	return (G.port_addr != NULL);
+}
+
+static inline int
+pasv_active(void)
+{
+	return (G.pasv_listen_fd > STDOUT_FILENO);
+}
+
+static void
+port_pasv_cleanup(void)
+{
+	free(G.port_addr);
+	G.port_addr = NULL;
+	if (G.pasv_listen_fd > STDOUT_FILENO)
+		close(G.pasv_listen_fd);
+	G.pasv_listen_fd = -1;
+}
+
+/* On error, emits error code to the peer */
+static int
+ftpdataio_get_pasv_fd(void)
+{
+	int remote_fd;
+
+	remote_fd = accept(G.pasv_listen_fd, NULL, 0);
+
+	if (remote_fd < 0) {
+		WRITE_ERR(FTP_BADSENDCONN);
+		return remote_fd;
+	}
+
+	setsockopt(remote_fd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+	return remote_fd;
+}
+
+/* Clears port/pasv data.
+ * This means we dont waste resources, for example, keeping
+ * PASV listening socket open when it is no longer needed.
+ * On error, emits error code to the peer (or exits).
+ * On success, emits p_status_msg to the peer.
+ */
+static int
+get_remote_transfer_fd(const char *p_status_msg)
+{
+	int remote_fd;
+
+	if (pasv_active())
+		/* On error, emits error code to the peer */
+		remote_fd = ftpdataio_get_pasv_fd();
+	else
+		/* Exits on error */
+		remote_fd = xconnect_stream(G.port_addr);
+
+	port_pasv_cleanup();
+
+	if (remote_fd < 0)
+		return remote_fd;
+
+	cmdio_write(STRNUM32(FTP_DATACONN), p_status_msg);
+	return remote_fd;
+}
+
+/* If there were neither PASV nor PORT, emits error code to the peer */
+static int
+port_or_pasv_was_seen(void)
+{
+	if (!pasv_active() && !port_active()) {
+		cmdio_write_raw(STR(FTP_BADSENDCONN)" Use PORT/PASV first\r\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Exits on error */
+static unsigned
+bind_for_passive_mode(void)
+{
+	int fd;
+	unsigned port;
+
+	port_pasv_cleanup();
+
+	G.pasv_listen_fd = fd = xsocket(G.local_addr->u.sa.sa_family, SOCK_STREAM, 0);
+	setsockopt_reuseaddr(fd);
+
+	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);
+
+	port = get_nport(&G.local_addr->u.sa);
+	port = ntohs(port);
+	return port;
+}
+
+/* Exits on error */
+static void
+handle_pasv(void)
+{
+	unsigned port;
+	char *addr, *response;
+
+	port = bind_for_passive_mode();
+
+	if (G.local_addr->u.sa.sa_family == AF_INET)
+		addr = xmalloc_sockaddr2dotted_noport(&G.local_addr->u.sa);
+	else /* seen this in the wild done by other ftp servers: */
+		addr = xstrdup("0.0.0.0");
+	replace_char(addr, '.', ',');
+
+	response = xasprintf(STR(FTP_PASVOK)" PASV ok (%s,%u,%u)\r\n",
+			addr, (int)(port >> 8), (int)(port & 255));
+	free(addr);
+	cmdio_write_raw(response);
+	free(response);
+}
+
+/* Exits on error */
+static void
+handle_epsv(void)
+{
+	unsigned port;
+	char *response;
+
+	port = bind_for_passive_mode();
+	response = xasprintf(STR(FTP_EPSVOK)" EPSV ok (|||%u|)\r\n", port);
+	cmdio_write_raw(response);
+	free(response);
+}
+
+static void
+handle_port(void)
+{
+	unsigned port, port_hi;
+	char *raw, *comma;
+#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
+	socklen_t peer_ipv4_len;
+	struct sockaddr_in peer_ipv4;
+	struct in_addr port_ipv4_sin_addr;
+#endif
+
+	port_pasv_cleanup();
+
+	raw = G.ftp_arg;
+
+	/* PORT command format makes sense only over IPv4 */
+	if (!raw
+#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
+	 || G.local_addr->u.sa.sa_family != AF_INET
+#endif
+	) {
+ bail:
+		WRITE_ERR(FTP_BADCMD);
+		return;
+	}
+
+	comma = strrchr(raw, ',');
+	if (comma == NULL)
+		goto bail;
+	*comma = '\0';
+	port = bb_strtou(&comma[1], NULL, 10);
+	if (errno || port > 0xff)
+		goto bail;
+
+	comma = strrchr(raw, ',');
+	if (comma == NULL)
+		goto bail;
+	*comma = '\0';
+	port_hi = bb_strtou(&comma[1], NULL, 10);
+	if (errno || port_hi > 0xff)
+		goto bail;
+	port |= port_hi << 8;
+
+#ifdef WHY_BOTHER_WE_CAN_ASSUME_IP_MATCHES
+	replace_char(raw, ',', '.');
+
+	/* We are verifying that PORT's IP matches getpeername().
+	 * Otherwise peer can make us open data connections
+	 * to other hosts (security problem!)
+	 * This code would be too simplistic:
+	 * lsa = xdotted2sockaddr(raw, port);
+	 * if (lsa == NULL) goto bail;
+	 */
+	if (!inet_aton(raw, &port_ipv4_sin_addr))
+		goto bail;
+	peer_ipv4_len = sizeof(peer_ipv4);
+	if (getpeername(STDIN_FILENO, &peer_ipv4, &peer_ipv4_len) != 0)
+		goto bail;
+	if (memcmp(&port_ipv4_sin_addr, &peer_ipv4.sin_addr, sizeof(struct in_addr)) != 0)
+		goto bail;
+
+	G.port_addr = xdotted2sockaddr(raw, port);
+#else
+	G.port_addr = get_peer_lsa(STDIN_FILENO);
+	set_nport(&G.port_addr->u.sa, htons(port));
+#endif
+	WRITE_OK(FTP_PORTOK);
+}
+
+static void
+handle_rest(void)
+{
+	/* When ftp_arg == NULL simply restart from beginning */
+	G.restart_pos = G.ftp_arg ? xatoi_positive(G.ftp_arg) : 0;
+	WRITE_OK(FTP_RESTOK);
+}
+
+static void
+handle_retr(void)
+{
+	struct stat statbuf;
+	off_t bytes_transferred;
+	int remote_fd;
+	int local_file_fd;
+	off_t offset = G.restart_pos;
+	char *response;
+
+	G.restart_pos = 0;
+
+	if (!port_or_pasv_was_seen())
+		return; /* port_or_pasv_was_seen emitted error response */
+
+	/* O_NONBLOCK is useful if file happens to be a device node */
+	local_file_fd = G.ftp_arg ? open(G.ftp_arg, O_RDONLY | O_NONBLOCK) : -1;
+	if (local_file_fd < 0) {
+		WRITE_ERR(FTP_FILEFAIL);
+		return;
+	}
+
+	if (fstat(local_file_fd, &statbuf) != 0 || !S_ISREG(statbuf.st_mode)) {
+		/* Note - pretend open failed */
+		WRITE_ERR(FTP_FILEFAIL);
+		goto file_close_out;
+	}
+	G.local_file_fd = local_file_fd;
+
+	/* Now deactive O_NONBLOCK, otherwise we have a problem
+	 * on DMAPI filesystems such as XFS DMAPI.
+	 */
+	ndelay_off(local_file_fd);
+
+	/* Set the download offset (from REST) if any */
+	if (offset != 0)
+		xlseek(local_file_fd, offset, SEEK_SET);
+
+	response = xasprintf(
+		" Opening BINARY connection for %s (%"OFF_FMT"u bytes)",
+		G.ftp_arg, statbuf.st_size);
+	remote_fd = get_remote_transfer_fd(response);
+	free(response);
+	if (remote_fd < 0)
+		goto file_close_out;
+
+	bytes_transferred = bb_copyfd_eof(local_file_fd, remote_fd);
+	close(remote_fd);
+	if (bytes_transferred < 0)
+		WRITE_ERR(FTP_BADSENDFILE);
+	else
+		WRITE_OK(FTP_TRANSFEROK);
+
+ file_close_out:
+	close(local_file_fd);
+	G.local_file_fd = 0;
+}
+
+/* List commands */
+
+static int
+popen_ls(const char *opt)
+{
+	const char *argv[5];
+	struct fd_pair outfd;
+	pid_t pid;
+
+	argv[0] = "ftpd";
+	argv[1] = opt; /* "-l" or "-1" */
+#if BB_MMU
+	argv[2] = "--";
+#else
+	/* NOMMU ftpd ls helper chdirs to argv[2],
+	 * preventing peer from seeing real root. */
+	argv[2] = xrealloc_getcwd_or_warn(NULL);
+#endif
+	argv[3] = G.ftp_arg;
+	argv[4] = NULL;
+
+	/* Improve compatibility with non-RFC conforming FTP clients
+	 * which send e.g. "LIST -l", "LIST -la", "LIST -aL".
+	 * See https://bugs.kde.org/show_bug.cgi?id=195578 */
+	if (ENABLE_FEATURE_FTPD_ACCEPT_BROKEN_LIST
+	 && G.ftp_arg && G.ftp_arg[0] == '-'
+	) {
+		const char *tmp = strchr(G.ftp_arg, ' ');
+		if (tmp) /* skip the space */
+			tmp++;
+		argv[3] = tmp;
+	}
+
+	xpiped_pair(outfd);
+
+	/*fflush_all(); - so far we dont use stdio on output */
+	pid = BB_MMU ? xfork() : xvfork();
+	if (pid == 0) {
+		/* child */
+#if !BB_MMU
+		/* On NOMMU, we want to execute a child - copy of ourself.
+		 * In chroot we usually can't do it. Thus we chdir
+		 * out of the chroot back to original root,
+		 * and (see later below) execute bb_busybox_exec_path
+		 * relative to current directory */
+		if (fchdir(G.root_fd) != 0)
+			_exit(127);
+		/*close(G.root_fd); - close_on_exec_on() took care of this */
+#endif
+		/* NB: close _first_, then move fd! */
+		close(outfd.rd);
+		xmove_fd(outfd.wr, STDOUT_FILENO);
+		/* Opening /dev/null in chroot is hard.
+		 * Just making sure STDIN_FILENO is opened
+		 * to something harmless. Paranoia,
+		 * ls won't read it anyway */
+		close(STDIN_FILENO);
+		dup(STDOUT_FILENO); /* copy will become STDIN_FILENO */
+#if BB_MMU
+		/* memset(&G, 0, sizeof(G)); - ls_main does it */
+		exit(ls_main(ARRAY_SIZE(argv) - 1, (char**) argv));
+#else
+		/* + 1: we must use relative path here if in chroot.
+		 * For example, execv("/proc/self/exe") will fail, since
+		 * it looks for "/proc/self/exe" _relative to chroot!_ */
+		execv(bb_busybox_exec_path + 1, (char**) argv);
+		_exit(127);
+#endif
+	}
+
+	/* parent */
+	close(outfd.wr);
+#if !BB_MMU
+	free((char*)argv[2]);
+#endif
+	return outfd.rd;
+}
+
+enum {
+	USE_CTRL_CONN = 1,
+	LONG_LISTING = 2,
+};
+
+static void
+handle_dir_common(int opts)
+{
+	FILE *ls_fp;
+	char *line;
+	int ls_fd;
+
+	if (!(opts & USE_CTRL_CONN) && !port_or_pasv_was_seen())
+		return; /* port_or_pasv_was_seen emitted error response */
+
+	/* -n prevents user/groupname display,
+	 * which can be problematic in chroot */
+	ls_fd = popen_ls((opts & LONG_LISTING) ? "-l" : "-1");
+	ls_fp = xfdopen_for_read(ls_fd);
+
+	if (opts & USE_CTRL_CONN) {
+		/* STAT <filename> */
+		cmdio_write_raw(STR(FTP_STATFILE_OK)"-File status:\r\n");
+		while (1) {
+			line = xmalloc_fgetline(ls_fp);
+			if (!line)
+				break;
+			/* Hack: 0 results in no status at all */
+			/* Note: it's ok that we don't prepend space,
+			 * ftp.kernel.org doesn't do that too */
+			cmdio_write(0, line);
+			free(line);
+		}
+		WRITE_OK(FTP_STATFILE_OK);
+	} else {
+		/* LIST/NLST [<filename>] */
+		int remote_fd = get_remote_transfer_fd(" Directory listing");
+		if (remote_fd >= 0) {
+			while (1) {
+				line = xmalloc_fgetline(ls_fp);
+				if (!line)
+					break;
+				/* I've seen clients complaining when they
+				 * are fed with ls output with bare '\n'.
+				 * Pity... that would be much simpler.
+				 */
+/* TODO: need to s/LF/NUL/g here */
+				xwrite_str(remote_fd, line);
+				xwrite(remote_fd, "\r\n", 2);
+				free(line);
+			}
+		}
+		close(remote_fd);
+		WRITE_OK(FTP_TRANSFEROK);
+	}
+	fclose(ls_fp); /* closes ls_fd too */
+}
+static void
+handle_list(void)
+{
+	handle_dir_common(LONG_LISTING);
+}
+static void
+handle_nlst(void)
+{
+	/* NLST returns list of names, "\r\n" terminated without regard
+	 * to the current binary flag. Names may start with "/",
+	 * then they represent full names (we don't produce such names),
+	 * otherwise names are relative to current directory.
+	 * Embedded "\n" are replaced by NULs. This is safe since names
+	 * can never contain NUL.
+	 */
+	handle_dir_common(0);
+}
+static void
+handle_stat_file(void)
+{
+	handle_dir_common(LONG_LISTING + USE_CTRL_CONN);
+}
+
+/* This can be extended to handle MLST, as all info is available
+ * in struct stat for that:
+ * MLST file_name
+ * 250-Listing file_name
+ *  type=file;size=4161;modify=19970214165800; /dir/dir/file_name
+ * 250 End
+ * Nano-doc:
+ * MLST [<file or dir name, "." assumed if not given>]
+ * Returned name should be either the same as requested, or fully qualified.
+ * If there was no parameter, return "" or (preferred) fully-qualified name.
+ * Returned "facts" (case is not important):
+ *  size    - size in octets
+ *  modify  - last modification time
+ *  type    - entry type (file,dir,OS.unix=block)
+ *            (+ cdir and pdir types for MLSD)
+ *  unique  - unique id of file/directory (inode#)
+ *  perm    -
+ *      a: can be appended to (APPE)
+ *      d: can be deleted (RMD/DELE)
+ *      f: can be renamed (RNFR)
+ *      r: can be read (RETR)
+ *      w: can be written (STOR)
+ *      e: can CWD into this dir
+ *      l: this dir can be listed (dir only!)
+ *      c: can create files in this dir
+ *      m: can create dirs in this dir (MKD)
+ *      p: can delete files in this dir
+ *  UNIX.mode - unix file mode
+ */
+static void
+handle_size_or_mdtm(int need_size)
+{
+	struct stat statbuf;
+	struct tm broken_out;
+	char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3)
+		| sizeof("NNN YYYYMMDDhhmmss\r\n")
+	];
+
+	if (!G.ftp_arg
+	 || stat(G.ftp_arg, &statbuf) != 0
+	 || !S_ISREG(statbuf.st_mode)
+	) {
+		WRITE_ERR(FTP_FILEFAIL);
+		return;
+	}
+	if (need_size) {
+		sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size);
+	} else {
+		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_mday,
+			broken_out.tm_hour,
+			broken_out.tm_min,
+			broken_out.tm_sec);
+	}
+	cmdio_write_raw(buf);
+}
+
+/* Upload commands */
+
+#if ENABLE_FEATURE_FTP_WRITE
+static void
+handle_mkd(void)
+{
+	if (!G.ftp_arg || mkdir(G.ftp_arg, 0777) != 0) {
+		WRITE_ERR(FTP_FILEFAIL);
+		return;
+	}
+	WRITE_OK(FTP_MKDIROK);
+}
+
+static void
+handle_rmd(void)
+{
+	if (!G.ftp_arg || rmdir(G.ftp_arg) != 0) {
+		WRITE_ERR(FTP_FILEFAIL);
+		return;
+	}
+	WRITE_OK(FTP_RMDIROK);
+}
+
+static void
+handle_dele(void)
+{
+	if (!G.ftp_arg || unlink(G.ftp_arg) != 0) {
+		WRITE_ERR(FTP_FILEFAIL);
+		return;
+	}
+	WRITE_OK(FTP_DELEOK);
+}
+
+static void
+handle_rnfr(void)
+{
+	free(G.rnfr_filename);
+	G.rnfr_filename = xstrdup(G.ftp_arg);
+	WRITE_OK(FTP_RNFROK);
+}
+
+static void
+handle_rnto(void)
+{
+	int retval;
+
+	/* If we didn't get a RNFR, throw a wobbly */
+	if (G.rnfr_filename == NULL || G.ftp_arg == NULL) {
+		cmdio_write_raw(STR(FTP_NEEDRNFR)" Use RNFR first\r\n");
+		return;
+	}
+
+	retval = rename(G.rnfr_filename, G.ftp_arg);
+	free(G.rnfr_filename);
+	G.rnfr_filename = NULL;
+
+	if (retval) {
+		WRITE_ERR(FTP_FILEFAIL);
+		return;
+	}
+	WRITE_OK(FTP_RENAMEOK);
+}
+
+static void
+handle_upload_common(int is_append, int is_unique)
+{
+	struct stat statbuf;
+	char *tempname;
+	off_t bytes_transferred;
+	off_t offset;
+	int local_file_fd;
+	int remote_fd;
+
+	offset = G.restart_pos;
+	G.restart_pos = 0;
+
+	if (!port_or_pasv_was_seen())
+		return; /* port_or_pasv_was_seen emitted error response */
+
+	tempname = NULL;
+	local_file_fd = -1;
+	if (is_unique) {
+		tempname = xstrdup(" FILE: uniq.XXXXXX");
+		local_file_fd = mkstemp(tempname + 7);
+	} else if (G.ftp_arg) {
+		int flags = O_WRONLY | O_CREAT | O_TRUNC;
+		if (is_append)
+			flags = O_WRONLY | O_CREAT | O_APPEND;
+		if (offset)
+			flags = O_WRONLY | O_CREAT;
+		local_file_fd = open(G.ftp_arg, flags, 0666);
+	}
+
+	if (local_file_fd < 0
+	 || fstat(local_file_fd, &statbuf) != 0
+	 || !S_ISREG(statbuf.st_mode)
+	) {
+		WRITE_ERR(FTP_UPLOADFAIL);
+		if (local_file_fd >= 0)
+			goto close_local_and_bail;
+		return;
+	}
+	G.local_file_fd = local_file_fd;
+
+	if (offset)
+		xlseek(local_file_fd, offset, SEEK_SET);
+
+	remote_fd = get_remote_transfer_fd(tempname ? tempname : " Ok to send data");
+	free(tempname);
+
+	if (remote_fd < 0)
+		goto close_local_and_bail;
+
+	bytes_transferred = bb_copyfd_eof(remote_fd, local_file_fd);
+	close(remote_fd);
+	if (bytes_transferred < 0)
+		WRITE_ERR(FTP_BADSENDFILE);
+	else
+		WRITE_OK(FTP_TRANSFEROK);
+
+ close_local_and_bail:
+	close(local_file_fd);
+	G.local_file_fd = 0;
+}
+
+static void
+handle_stor(void)
+{
+	handle_upload_common(0, 0);
+}
+
+static void
+handle_appe(void)
+{
+	G.restart_pos = 0;
+	handle_upload_common(1, 0);
+}
+
+static void
+handle_stou(void)
+{
+	G.restart_pos = 0;
+	handle_upload_common(0, 1);
+}
+#endif /* ENABLE_FEATURE_FTP_WRITE */
+
+static uint32_t
+cmdio_get_cmd_and_arg(void)
+{
+	int len;
+	uint32_t cmdval;
+	char *cmd;
+
+	alarm(G.timeout);
+
+	free(G.ftp_cmd);
+	{
+		/* Paranoia. Peer may send 1 gigabyte long cmd... */
+		/* Using separate len_on_stk instead of len optimizes
+		 * code size (allows len to be in CPU register) */
+		size_t len_on_stk = 8 * 1024;
+		G.ftp_cmd = cmd = xmalloc_fgets_str_len(stdin, "\r\n", &len_on_stk);
+		if (!cmd)
+			exit(0);
+		len = len_on_stk;
+	}
+
+	/* De-escape telnet: 0xff,0xff => 0xff */
+	/* RFC959 says that ABOR, STAT, QUIT may be sent even during
+	 * data transfer, and may be preceded by telnet's "Interrupt Process"
+	 * code (two-byte sequence 255,244) and then by telnet "Synch" code
+	 * 255,242 (byte 242 is sent with TCP URG bit using send(MSG_OOB)
+	 * and may generate SIGURG on our side. See RFC854).
+	 * So far we don't support that (may install SIGURG handler if we'd want to),
+	 * but we need to at least remove 255,xxx pairs. lftp sends those. */
+	/* Then de-escape FTP: NUL => '\n' */
+	/* Testing for \xff:
+	 * Create file named '\xff': echo Hello >`echo -ne "\xff"`
+	 * Try to get it:            ftpget -v 127.0.0.1 Eff `echo -ne "\xff\xff"`
+	 * (need "\xff\xff" until ftpget applet is fixed to do escaping :)
+	 * Testing for embedded LF:
+	 * LF_HERE=`echo -ne "LF\nHERE"`
+	 * echo Hello >"$LF_HERE"
+	 * ftpget -v 127.0.0.1 LF_HERE "$LF_HERE"
+	 */
+	{
+		int dst, src;
+
+		/* Strip "\r\n" if it is there */
+		if (len != 0 && cmd[len - 1] == '\n') {
+			len--;
+			if (len != 0 && cmd[len - 1] == '\r')
+				len--;
+			cmd[len] = '\0';
+		}
+		src = strchrnul(cmd, 0xff) - cmd;
+		/* 99,99% there are neither NULs nor 255s and src == len */
+		if (src < len) {
+			dst = src;
+			do {
+				if ((unsigned char)(cmd[src]) == 255) {
+					src++;
+					/* 255,xxx - skip 255 */
+					if ((unsigned char)(cmd[src]) != 255) {
+						/* 255,!255 - skip both */
+						src++;
+						continue;
+					}
+					/* 255,255 - retain one 255 */
+				}
+				/* NUL => '\n' */
+				cmd[dst++] = cmd[src] ? cmd[src] : '\n';
+				src++;
+			} while (src < len);
+			cmd[dst] = '\0';
+		}
+	}
+
+	if (G.verbose > 1)
+		verbose_log(cmd);
+
+	G.ftp_arg = strchr(cmd, ' ');
+	if (G.ftp_arg != NULL)
+		*G.ftp_arg++ = '\0';
+
+	/* Uppercase and pack into uint32_t first word of the command */
+	cmdval = 0;
+	while (*cmd)
+		cmdval = (cmdval << 8) + ((unsigned char)*cmd++ & (unsigned char)~0x20);
+
+	return cmdval;
+}
+
+#define mk_const4(a,b,c,d) (((a * 0x100 + b) * 0x100 + c) * 0x100 + d)
+#define mk_const3(a,b,c)    ((a * 0x100 + b) * 0x100 + c)
+enum {
+	const_ALLO = mk_const4('A', 'L', 'L', 'O'),
+	const_APPE = mk_const4('A', 'P', 'P', 'E'),
+	const_CDUP = mk_const4('C', 'D', 'U', 'P'),
+	const_CWD  = mk_const3('C', 'W', 'D'),
+	const_DELE = mk_const4('D', 'E', 'L', 'E'),
+	const_EPSV = mk_const4('E', 'P', 'S', 'V'),
+	const_FEAT = mk_const4('F', 'E', 'A', 'T'),
+	const_HELP = mk_const4('H', 'E', 'L', 'P'),
+	const_LIST = mk_const4('L', 'I', 'S', 'T'),
+	const_MDTM = mk_const4('M', 'D', 'T', 'M'),
+	const_MKD  = mk_const3('M', 'K', 'D'),
+	const_MODE = mk_const4('M', 'O', 'D', 'E'),
+	const_NLST = mk_const4('N', 'L', 'S', 'T'),
+	const_NOOP = mk_const4('N', 'O', 'O', 'P'),
+	const_PASS = mk_const4('P', 'A', 'S', 'S'),
+	const_PASV = mk_const4('P', 'A', 'S', 'V'),
+	const_PORT = mk_const4('P', 'O', 'R', 'T'),
+	const_PWD  = mk_const3('P', 'W', 'D'),
+	const_QUIT = mk_const4('Q', 'U', 'I', 'T'),
+	const_REST = mk_const4('R', 'E', 'S', 'T'),
+	const_RETR = mk_const4('R', 'E', 'T', 'R'),
+	const_RMD  = mk_const3('R', 'M', 'D'),
+	const_RNFR = mk_const4('R', 'N', 'F', 'R'),
+	const_RNTO = mk_const4('R', 'N', 'T', 'O'),
+	const_SIZE = mk_const4('S', 'I', 'Z', 'E'),
+	const_STAT = mk_const4('S', 'T', 'A', 'T'),
+	const_STOR = mk_const4('S', 'T', 'O', 'R'),
+	const_STOU = mk_const4('S', 'T', 'O', 'U'),
+	const_STRU = mk_const4('S', 'T', 'R', 'U'),
+	const_SYST = mk_const4('S', 'Y', 'S', 'T'),
+	const_TYPE = mk_const4('T', 'Y', 'P', 'E'),
+	const_USER = mk_const4('U', 'S', 'E', 'R'),
+
+#if !BB_MMU
+	OPT_l = (1 << 0),
+	OPT_1 = (1 << 1),
+#endif
+	OPT_v = (1 << ((!BB_MMU) * 2 + 0)),
+	OPT_S = (1 << ((!BB_MMU) * 2 + 1)),
+	OPT_w = (1 << ((!BB_MMU) * 2 + 2)) * ENABLE_FEATURE_FTP_WRITE,
+};
+
+int ftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+#if !BB_MMU
+int ftpd_main(int argc, char **argv)
+#else
+int ftpd_main(int argc UNUSED_PARAM, char **argv)
+#endif
+{
+	unsigned abs_timeout;
+	unsigned verbose_S;
+	smallint opts;
+
+	INIT_G();
+
+	abs_timeout = 1 * 60 * 60;
+	verbose_S = 0;
+	G.timeout = 2 * 60;
+	opt_complementary = "t+:T+:vv:SS";
+#if BB_MMU
+	opts = getopt32(argv,   "vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
+#else
+	opts = getopt32(argv, "l1vS" IF_FEATURE_FTP_WRITE("w") "t:T:", &G.timeout, &abs_timeout, &G.verbose, &verbose_S);
+	if (opts & (OPT_l|OPT_1)) {
+		/* Our secret backdoor to ls */
+/* TODO: pass -n? It prevents user/group resolution, which may not work in chroot anyway */
+/* TODO: pass -A? It shows dot files */
+/* TODO: pass --group-directories-first? would be nice, but ls doesn't do that yet */
+		xchdir(argv[2]);
+		argv[2] = (char*)"--";
+		/* memset(&G, 0, sizeof(G)); - ls_main does it */
+		return ls_main(argc, argv);
+	}
+#endif
+	if (G.verbose < verbose_S)
+		G.verbose = verbose_S;
+	if (abs_timeout | G.timeout) {
+		if (abs_timeout == 0)
+			abs_timeout = INT_MAX;
+		G.end_time = monotonic_sec() + abs_timeout;
+		if (G.timeout > abs_timeout)
+			G.timeout = abs_timeout;
+	}
+	strcpy(G.msg_ok  + 4, MSG_OK );
+	strcpy(G.msg_err + 4, MSG_ERR);
+
+	G.local_addr = get_sock_lsa(STDIN_FILENO);
+	if (!G.local_addr) {
+		/* This is confusing:
+		 * bb_error_msg_and_die("stdin is not a socket");
+		 * Better: */
+		bb_show_usage();
+		/* Help text says that ftpd must be used as inetd service,
+		 * which is by far the most usual cause of get_sock_lsa
+		 * failure */
+	}
+
+	if (!(opts & OPT_v))
+		logmode = LOGMODE_NONE;
+	if (opts & OPT_S) {
+		/* LOG_NDELAY is needed since we may chroot later */
+		openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+		logmode |= LOGMODE_SYSLOG;
+	}
+	if (logmode)
+		applet_name = xasprintf("%s[%u]", applet_name, (int)getpid());
+
+#if !BB_MMU
+	G.root_fd = xopen("/", O_RDONLY | O_DIRECTORY);
+	close_on_exec_on(G.root_fd);
+#endif
+
+	if (argv[optind]) {
+		xchdir(argv[optind]);
+		chroot(".");
+	}
+
+	//umask(077); - admin can set umask before starting us
+
+	/* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
+	signal(SIGPIPE, SIG_IGN);
+
+	/* Set up options on the command socket (do we need these all? why?) */
+	setsockopt(STDIN_FILENO, IPPROTO_TCP, TCP_NODELAY, &const_int_1, sizeof(const_int_1));
+	setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+	/* Telnet protocol over command link may send "urgent" data,
+	 * we prefer it to be received in the "normal" data stream: */
+	setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &const_int_1, sizeof(const_int_1));
+
+	WRITE_OK(FTP_GREET);
+	signal(SIGALRM, timeout_handler);
+
+#ifdef IF_WE_WANT_TO_REQUIRE_LOGIN
+	{
+		smallint user_was_specified = 0;
+		while (1) {
+			uint32_t cmdval = cmdio_get_cmd_and_arg();
+
+			if (cmdval == const_USER) {
+				if (G.ftp_arg == NULL || strcasecmp(G.ftp_arg, "anonymous") != 0)
+					cmdio_write_raw(STR(FTP_LOGINERR)" Server is anonymous only\r\n");
+				else {
+					user_was_specified = 1;
+					cmdio_write_raw(STR(FTP_GIVEPWORD)" Please specify the password\r\n");
+				}
+			} else if (cmdval == const_PASS) {
+				if (user_was_specified)
+					break;
+				cmdio_write_raw(STR(FTP_NEEDUSER)" Login with USER\r\n");
+			} else if (cmdval == const_QUIT) {
+				WRITE_OK(FTP_GOODBYE);
+				return 0;
+			} else {
+				cmdio_write_raw(STR(FTP_LOGINERR)" Login with USER and PASS\r\n");
+			}
+		}
+	}
+	WRITE_OK(FTP_LOGINOK);
+#endif
+
+	/* RFC-959 Section 5.1
+	 * The following commands and options MUST be supported by every
+	 * server-FTP and user-FTP, except in cases where the underlying
+	 * file system or operating system does not allow or support
+	 * a particular command.
+	 * Type: ASCII Non-print, IMAGE, LOCAL 8
+	 * Mode: Stream
+	 * Structure: File, Record*
+	 * (Record structure is REQUIRED only for hosts whose file
+	 *  systems support record structure).
+	 * Commands:
+	 * USER, PASS, ACCT, [bbox: ACCT not supported]
+	 * PORT, PASV,
+	 * TYPE, MODE, STRU,
+	 * RETR, STOR, APPE,
+	 * RNFR, RNTO, DELE,
+	 * CWD,  CDUP, RMD,  MKD,  PWD,
+	 * LIST, NLST,
+	 * SYST, STAT,
+	 * HELP, NOOP, QUIT.
+	 */
+	/* ACCOUNT (ACCT)
+	 * "The argument field is a Telnet string identifying the user's account.
+	 * The command is not necessarily related to the USER command, as some
+	 * sites may require an account for login and others only for specific
+	 * access, such as storing files. In the latter case the command may
+	 * arrive at any time.
+	 * There are reply codes to differentiate these cases for the automation:
+	 * when account information is required for login, the response to
+	 * a successful PASSword command is reply code 332. On the other hand,
+	 * if account information is NOT required for login, the reply to
+	 * a successful PASSword command is 230; and if the account information
+	 * is needed for a command issued later in the dialogue, the server
+	 * should return a 332 or 532 reply depending on whether it stores
+	 * (pending receipt of the ACCounT command) or discards the command,
+	 * respectively."
+	 */
+
+	while (1) {
+		uint32_t cmdval = cmdio_get_cmd_and_arg();
+
+		if (cmdval == const_QUIT) {
+			WRITE_OK(FTP_GOODBYE);
+			return 0;
+		}
+		else if (cmdval == const_USER)
+			/* This would mean "ok, now give me PASS". */
+			/*WRITE_OK(FTP_GIVEPWORD);*/
+			/* vsftpd can be configured to not require that,
+			 * and this also saves one roundtrip:
+			 */
+			WRITE_OK(FTP_LOGINOK);
+		else if (cmdval == const_PASS)
+			WRITE_OK(FTP_LOGINOK);
+		else if (cmdval == const_NOOP)
+			WRITE_OK(FTP_NOOPOK);
+		else if (cmdval == const_TYPE)
+			WRITE_OK(FTP_TYPEOK);
+		else if (cmdval == const_STRU)
+			WRITE_OK(FTP_STRUOK);
+		else if (cmdval == const_MODE)
+			WRITE_OK(FTP_MODEOK);
+		else if (cmdval == const_ALLO)
+			WRITE_OK(FTP_ALLOOK);
+		else if (cmdval == const_SYST)
+			cmdio_write_raw(STR(FTP_SYSTOK)" UNIX Type: L8\r\n");
+		else if (cmdval == const_PWD)
+			handle_pwd();
+		else if (cmdval == const_CWD)
+			handle_cwd();
+		else if (cmdval == const_CDUP) /* cd .. */
+			handle_cdup();
+		/* HELP is nearly useless, but we can reuse FEAT for it */
+		/* lftp uses FEAT */
+		else if (cmdval == const_HELP || cmdval == const_FEAT)
+			handle_feat(cmdval == const_HELP
+					? STRNUM32(FTP_HELP)
+					: STRNUM32(FTP_STATOK)
+			);
+		else if (cmdval == const_LIST) /* ls -l */
+			handle_list();
+		else if (cmdval == const_NLST) /* "name list", bare ls */
+			handle_nlst();
+		/* SIZE is crucial for wget's download indicator etc */
+		/* Mozilla, lftp use MDTM (presumably for caching) */
+		else if (cmdval == const_SIZE || cmdval == const_MDTM)
+			handle_size_or_mdtm(cmdval == const_SIZE);
+		else if (cmdval == const_STAT) {
+			if (G.ftp_arg == NULL)
+				handle_stat();
+			else
+				handle_stat_file();
+		}
+		else if (cmdval == const_PASV)
+			handle_pasv();
+		else if (cmdval == const_EPSV)
+			handle_epsv();
+		else if (cmdval == const_RETR)
+			handle_retr();
+		else if (cmdval == const_PORT)
+			handle_port();
+		else if (cmdval == const_REST)
+			handle_rest();
+#if ENABLE_FEATURE_FTP_WRITE
+		else if (opts & OPT_w) {
+			if (cmdval == const_STOR)
+				handle_stor();
+			else if (cmdval == const_MKD)
+				handle_mkd();
+			else if (cmdval == const_RMD)
+				handle_rmd();
+			else if (cmdval == const_DELE)
+				handle_dele();
+			else if (cmdval == const_RNFR) /* "rename from" */
+				handle_rnfr();
+			else if (cmdval == const_RNTO) /* "rename to" */
+				handle_rnto();
+			else if (cmdval == const_APPE)
+				handle_appe();
+			else if (cmdval == const_STOU) /* "store unique" */
+				handle_stou();
+			else
+				goto bad_cmd;
+		}
+#endif
+#if 0
+		else if (cmdval == const_STOR
+		 || cmdval == const_MKD
+		 || cmdval == const_RMD
+		 || cmdval == const_DELE
+		 || cmdval == const_RNFR
+		 || cmdval == const_RNTO
+		 || cmdval == const_APPE
+		 || cmdval == const_STOU
+		) {
+			cmdio_write_raw(STR(FTP_NOPERM)" Permission denied\r\n");
+		}
+#endif
+		else {
+			/* Which unsupported commands were seen in the wild?
+			 * (doesn't necessarily mean "we must support them")
+			 * foo 1.2.3: XXXX - comment
+			 */
+#if ENABLE_FEATURE_FTP_WRITE
+ bad_cmd:
+#endif
+			cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n");
+		}
+	}
+}
diff --git a/busybox-1.19.3/networking/ftpgetput.c b/busybox-1.19.3/networking/ftpgetput.c
new file mode 100644
index 0000000..abdf94c
--- /dev/null
+++ b/busybox-1.19.3/networking/ftpgetput.c
@@ -0,0 +1,360 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ftpget
+ *
+ * Mini implementation of FTP to retrieve a remote file.
+ *
+ * Copyright (C) 2002 Jeff Angielski, The PTR Group <jeff@theptrgroup.com>
+ * Copyright (C) 2002 Glenn McGrath
+ *
+ * Based on wget.c by Chip Rosenthal Covad Communications
+ * <chip@laserlink.net>
+ *
+ * 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:       "Retrieve a remote 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	Username"
+//usage:     "\n	-p,--password	Password"
+//usage:     "\n	-P,--port	Port number"
+//usage:	)
+//usage:	IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
+//usage:     "\n	-c	Continue previous transfer"
+//usage:     "\n	-v	Verbose"
+//usage:     "\n	-u	Username"
+//usage:     "\n	-p	Password"
+//usage:     "\n	-P	Port number"
+//usage:	)
+//usage:
+//usage:#define ftpput_trivial_usage
+//usage:       "[OPTIONS] HOST [REMOTE_FILE] LOCAL_FILE"
+//usage:#define ftpput_full_usage "\n\n"
+//usage:       "Store a local file on a remote machine via FTP\n"
+//usage:	IF_FEATURE_FTPGETPUT_LONG_OPTIONS(
+//usage:     "\n	-v,--verbose	Verbose"
+//usage:     "\n	-u,--username	Username"
+//usage:     "\n	-p,--password	Password"
+//usage:     "\n	-P,--port	Port number"
+//usage:	)
+//usage:	IF_NOT_FEATURE_FTPGETPUT_LONG_OPTIONS(
+//usage:     "\n	-v	Verbose"
+//usage:     "\n	-u	Username"
+//usage:     "\n	-p	Password"
+//usage:     "\n	-P	Port number"
+//usage:	)
+
+#include "libbb.h"
+
+struct globals {
+	const char *user;
+	const char *password;
+	struct len_and_sockaddr *lsa;
+	FILE *control_stream;
+	int verbose_flag;
+	int do_continue;
+	char buf[4]; /* actually [BUFSZ] */
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+enum { BUFSZ = COMMON_BUFSIZE - offsetof(struct globals, buf) };
+struct BUG_G_too_big {
+	char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+};
+#define user           (G.user          )
+#define password       (G.password      )
+#define lsa            (G.lsa           )
+#define control_stream (G.control_stream)
+#define verbose_flag   (G.verbose_flag  )
+#define do_continue    (G.do_continue   )
+#define buf            (G.buf           )
+#define INIT_G() do { } while (0)
+
+
+static void ftp_die(const char *msg) NORETURN;
+static void ftp_die(const char *msg)
+{
+	char *cp = buf; /* buf holds peer's response */
+
+	/* Guard against garbage from remote server */
+	while (*cp >= ' ' && *cp < '\x7f')
+		cp++;
+	*cp = '\0';
+	bb_error_msg_and_die("unexpected server response%s%s: %s",
+			(msg ? " to " : ""), (msg ? msg : ""), buf);
+}
+
+static int ftpcmd(const char *s1, const char *s2)
+{
+	unsigned n;
+
+	if (verbose_flag) {
+		bb_error_msg("cmd %s %s", s1, s2);
+	}
+
+	if (s1) {
+		fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3),
+						s1, s2);
+		fflush(control_stream);
+	}
+
+	do {
+		strcpy(buf, "EOF"); /* for ftp_die */
+		if (fgets(buf, BUFSZ - 2, control_stream) == NULL) {
+			ftp_die(NULL);
+		}
+	} while (!isdigit(buf[0]) || buf[3] != ' ');
+
+	buf[3] = '\0';
+	n = xatou(buf);
+	buf[3] = ' ';
+	return n;
+}
+
+static void ftp_login(void)
+{
+	/* Connect to the command socket */
+	control_stream = fdopen(xconnect_stream(lsa), "r+");
+	if (control_stream == NULL) {
+		/* fdopen failed - extremely unlikely */
+		bb_perror_nomsg_and_die();
+	}
+
+	if (ftpcmd(NULL, NULL) != 220) {
+		ftp_die(NULL);
+	}
+
+	/*  Login to the server */
+	switch (ftpcmd("USER", user)) {
+	case 230:
+		break;
+	case 331:
+		if (ftpcmd("PASS", password) != 230) {
+			ftp_die("PASS");
+		}
+		break;
+	default:
+		ftp_die("USER");
+	}
+
+	ftpcmd("TYPE I", NULL);
+}
+
+static int xconnect_ftpdata(void)
+{
+	char *buf_ptr;
+	unsigned port_num;
+
+/*
+TODO: PASV command will not work for IPv6. RFC2428 describes
+IPv6-capable "extended PASV" - EPSV.
+
+"EPSV [protocol]" asks server to bind to and listen on a data port
+in specified protocol. Protocol is 1 for IPv4, 2 for IPv6.
+If not specified, defaults to "same as used for control connection".
+If server understood you, it should answer "229 <some text>(|||port|)"
+where "|" are literal pipe chars and "port" is ASCII decimal port#.
+
+There is also an IPv6-capable replacement for PORT (EPRT),
+but we don't need that.
+
+NB: PASV may still work for some servers even over IPv6.
+For example, vsftp happily answers
+"227 Entering Passive Mode (0,0,0,0,n,n)" and proceeds as usual.
+
+TODO2: need to stop ignoring IP address in PASV response.
+*/
+
+	if (ftpcmd("PASV", NULL) != 227) {
+		ftp_die("PASV");
+	}
+
+	/* Response is "NNN 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 */
+	buf_ptr = strrchr(buf, ')');
+	if (buf_ptr) *buf_ptr = '\0';
+
+	buf_ptr = strrchr(buf, ',');
+	*buf_ptr = '\0';
+	port_num = xatoul_range(buf_ptr + 1, 0, 255);
+
+	buf_ptr = strrchr(buf, ',');
+	*buf_ptr = '\0';
+	port_num += xatoul_range(buf_ptr + 1, 0, 255) * 256;
+
+	set_nport(&lsa->u.sa, htons(port_num));
+	return xconnect_stream(lsa);
+}
+
+static int pump_data_and_QUIT(int from, int to)
+{
+	/* copy the file */
+	if (bb_copyfd_eof(from, to) == -1) {
+		/* error msg is already printed by bb_copyfd_eof */
+		return EXIT_FAILURE;
+	}
+
+	/* close data connection */
+	close(from); /* don't know which one is that, so we close both */
+	close(to);
+
+	/* does server confirm that transfer is finished? */
+	if (ftpcmd(NULL, NULL) != 226) {
+		ftp_die(NULL);
+	}
+	ftpcmd("QUIT", NULL);
+
+	return EXIT_SUCCESS;
+}
+
+#if !ENABLE_FTPGET
+int ftp_receive(const char *local_path, char *server_path);
+#else
+static
+int ftp_receive(const char *local_path, char *server_path)
+{
+	int fd_data;
+	int fd_local = -1;
+	off_t beg_range = 0;
+
+	/* connect to the data socket */
+	fd_data = xconnect_ftpdata();
+
+	if (ftpcmd("SIZE", server_path) != 213) {
+		do_continue = 0;
+	}
+
+	if (LONE_DASH(local_path)) {
+		fd_local = STDOUT_FILENO;
+		do_continue = 0;
+	}
+
+	if (do_continue) {
+		struct stat sbuf;
+		/* lstat would be wrong here! */
+		if (stat(local_path, &sbuf) < 0) {
+			bb_perror_msg_and_die("stat");
+		}
+		if (sbuf.st_size > 0) {
+			beg_range = sbuf.st_size;
+		} else {
+			do_continue = 0;
+		}
+	}
+
+	if (do_continue) {
+		sprintf(buf, "REST %"OFF_FMT"u", beg_range);
+		if (ftpcmd(buf, NULL) != 350) {
+			do_continue = 0;
+		}
+	}
+
+	if (ftpcmd("RETR", server_path) > 150) {
+		ftp_die("RETR");
+	}
+
+	/* create local file _after_ we know that remote file exists */
+	if (fd_local == -1) {
+		fd_local = xopen(local_path,
+			do_continue ? (O_APPEND | O_WRONLY)
+			            : (O_CREAT | O_TRUNC | O_WRONLY)
+		);
+	}
+
+	return pump_data_and_QUIT(fd_data, fd_local);
+}
+#endif
+
+#if !ENABLE_FTPPUT
+int ftp_send(const char *server_path, char *local_path);
+#else
+static
+int ftp_send(const char *server_path, char *local_path)
+{
+	int fd_data;
+	int fd_local;
+	int response;
+
+	/* connect to the data socket */
+	fd_data = xconnect_ftpdata();
+
+	/* get the local file */
+	fd_local = STDIN_FILENO;
+	if (NOT_LONE_DASH(local_path))
+		fd_local = xopen(local_path, O_RDONLY);
+
+	response = ftpcmd("STOR", server_path);
+	switch (response) {
+	case 125:
+	case 150:
+		break;
+	default:
+		ftp_die("STOR");
+	}
+
+	return pump_data_and_QUIT(fd_local, fd_data);
+}
+#endif
+
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+static const char ftpgetput_longopts[] ALIGN1 =
+	"continue\0" Required_argument "c"
+	"verbose\0"  No_argument       "v"
+	"username\0" Required_argument "u"
+	"password\0" Required_argument "p"
+	"port\0"     Required_argument "P"
+	;
+#endif
+
+int ftpgetput_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ftpgetput_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *port = "ftp";
+	/* socket to ftp server */
+
+#if ENABLE_FTPPUT && !ENABLE_FTPGET
+# define ftp_action ftp_send
+#elif ENABLE_FTPGET && !ENABLE_FTPPUT
+# define ftp_action ftp_receive
+#else
+	int (*ftp_action)(const char *, char *) = ftp_send;
+
+	/* Check to see if the command is ftpget or ftput */
+	if (applet_name[3] == 'g') {
+		ftp_action = ftp_receive;
+	}
+#endif
+
+	INIT_G();
+	/* Set default values */
+	user = "anonymous";
+	password = "busybox@";
+
+	/*
+	 * Decipher the command line
+	 */
+#if ENABLE_FEATURE_FTPGETPUT_LONG_OPTIONS
+	applet_long_options = ftpgetput_longopts;
+#endif
+	opt_complementary = "-2:vv:cc"; /* must have 2 to 3 params; -v and -c count */
+	getopt32(argv, "cvu:p:P:", &user, &password, &port,
+					&verbose_flag, &do_continue);
+	argv += optind;
+
+	/* We want to do exactly _one_ DNS lookup, since some
+	 * sites (i.e. ftp.us.debian.org) use round-robin DNS
+	 * and we want to connect to only one IP... */
+	lsa = xhost2sockaddr(argv[0], bb_lookup_port(port, "tcp", 21));
+	if (verbose_flag) {
+		printf("Connecting to %s (%s)\n", argv[0],
+			xmalloc_sockaddr2dotted(&lsa->u.sa));
+	}
+
+	ftp_login();
+	return ftp_action(argv[1], argv[2] ? argv[2] : argv[1]);
+}
diff --git a/busybox-1.19.3/networking/hostname.c b/busybox-1.19.3/networking/hostname.c
new file mode 100644
index 0000000..5f66390
--- /dev/null
+++ b/busybox-1.19.3/networking/hostname.c
@@ -0,0 +1,176 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hostname implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * 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 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)
+{
+//	if (!s)
+//		return;
+	if (isfile) {
+		parser_t *parser = config_open2(s, xfopen_for_read);
+		while (config_read(parser, &s, 1, 1, "# \t", PARSE_NORMAL & ~PARSE_GREEDY)) {
+			do_sethostname(s, 0);
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			config_close(parser);
+	} else if (sethostname(s, strlen(s))) {
+//		if (errno == EPERM)
+//			bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+		bb_perror_msg_and_die("sethostname");
+	}
+}
+
+/* Manpage circa 2009:
+ *
+ * hostname [-v] [-a] [--alias] [-d] [--domain] [-f] [--fqdn] [--long]
+ *      [-i] [--ip-address] [-s] [--short] [-y] [--yp] [--nis]
+ *
+ * hostname [-v] [-F filename] [--file filename] / [hostname]
+ *
+ * domainname [-v] [-F filename] [--file filename]  / [name]
+ *  { bbox: not supported }
+ *
+ * nodename [-v] [-F filename] [--file filename] / [name]
+ *  { bbox: not supported }
+ *
+ * dnsdomainname [-v]
+ *  { bbox: supported: Linux kernel build needs this }
+ * nisdomainname [-v]
+ *  { bbox: not supported }
+ * ypdomainname [-v]
+ *  { bbox: not supported }
+ *
+ * -a, --alias
+ *  Display the alias name of the host (if used).
+ *  { bbox: not supported }
+ * -d, --domain
+ *  Display the name of the DNS domain. Don't use the command
+ *  domainname to get the DNS domain name because it will show the
+ *  NIS domain name and not the DNS domain name. Use dnsdomainname
+ *  instead.
+ * -f, --fqdn, --long
+ *  Display the FQDN (Fully Qualified Domain Name). A FQDN consists
+ *  of a short host name and the DNS domain name. Unless you are
+ *  using bind or NIS for host lookups you can change the FQDN and
+ *  the DNS domain name (which is part of the FQDN) in the
+ *  /etc/hosts file.
+ * -i, --ip-address
+ *  Display the IP address(es) of the host.
+ * -s, --short
+ *  Display the short host name. This is the host name cut at the
+ *  first dot.
+ * -v, --verbose
+ *  Be verbose and tell what's going on.
+ *  { bbox: supported but ignored }
+ * -y, --yp, --nis
+ *  Display the NIS domain name. If a parameter is given (or --file
+ *  name ) then root can also set a new NIS domain.
+ *  { bbox: not supported }
+ * -F, --file filename
+ *  Read the host name from the specified file. Comments (lines
+ *  starting with a `#') are ignored.
+ */
+int hostname_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hostname_main(int argc UNUSED_PARAM, char **argv)
+{
+	enum {
+		OPT_d = 0x1,
+		OPT_f = 0x2,
+		OPT_i = 0x4,
+		OPT_s = 0x8,
+		OPT_F = 0x10,
+		OPT_dfis = 0xf,
+	};
+
+	unsigned opts;
+	char *buf;
+	char *hostname_str;
+
+#if ENABLE_LONG_OPTS
+	applet_long_options =
+		"domain\0"     No_argument "d"
+		"fqdn\0"       No_argument "f"
+	//Enable if seen in active use in some distro:
+	//	"long\0"       No_argument "f"
+	//	"ip-address\0" No_argument "i"
+	//	"short\0"      No_argument "s"
+	//	"verbose\0"    No_argument "v"
+		"file\0"       No_argument "F"
+		;
+
+#endif
+	/* dnsdomainname from net-tools 1.60, hostname 1.100 (2001-04-14),
+	 * supports hostname's options too (not just -v as manpage says) */
+	opts = getopt32(argv, "dfisF:v", &hostname_str);
+	argv += optind;
+	buf = safe_gethostname();
+	if (applet_name[0] == 'd') /* dnsdomainname? */
+		opts = OPT_d;
+
+	if (opts & OPT_dfis) {
+		/* Cases when we need full hostname (or its part) */
+		struct hostent *hp;
+		char *p;
+
+		hp = xgethostbyname(buf);
+		p = strchrnul(hp->h_name, '.');
+		if (opts & OPT_f) {
+			puts(hp->h_name);
+		} else if (opts & OPT_s) {
+			*p = '\0';
+			puts(hp->h_name);
+		} else if (opts & OPT_d) {
+			if (*p)
+				puts(p + 1);
+		} else /*if (opts & OPT_i)*/ {
+			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));
+					h_addr_list++;
+				}
+				bb_putchar('\n');
+			}
+		}
+	} else if (opts & OPT_F) {
+		/* Set the hostname */
+		do_sethostname(hostname_str, 1);
+	} else if (argv[0]) {
+		/* Set the hostname */
+		do_sethostname(argv[0], 0);
+	} else {
+		/* Just print the current hostname */
+		puts(buf);
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(buf);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/httpd.c b/busybox-1.19.3/networking/httpd.c
new file mode 100644
index 0000000..ba5eeba
--- /dev/null
+++ b/busybox-1.19.3/networking/httpd.c
@@ -0,0 +1,2493 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * httpd implementation for busybox
+ *
+ * Copyright (C) 2002,2003 Glenn Engel <glenne@engel.org>
+ * Copyright (C) 2003-2006 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * 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
+ * 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 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"
+ * 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.
+ *
+ * httpd.conf has the following format:
+ *
+ * H:/serverroot     # define the server root. It will override -h
+ * A:172.20.         # Allow address from 172.20.0.0/16
+ * A:10.0.0.0/25     # Allow any address from 10.0.0.0-10.0.0.127
+ * A:10.0.0.0/255.255.255.128  # Allow any address that previous set
+ * A:127.0.0.1       # Allow local loopback connections
+ * D:*               # Deny from other IP connections
+ * E404:/path/e404.html # /path/e404.html is the 404 (not found) error page
+ * I:index.html      # Show index.html when a directory is requested
+ *
+ * P:/url:[http://]hostname[:port]/new/path
+ *                   # When /urlXXXXXX is requested, reverse proxy
+ *                   # it to http://hostname[:port]/new/pathXXXXXX
+ *
+ * /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/
+ * .au:audio/basic   # additional mime type for audio.au files
+ * *.php:/path/php   # run xxx.php through an interpreter
+ *
+ * A/D may be as a/d or allow/deny - only first char matters.
+ * Deny/Allow IP logic:
+ *  - Default is to allow all (Allow all (A:*) is a no-op).
+ *  - Deny rules take precedence over allow rules.
+ *  - "Deny all" rule (D:*) is applied last.
+ *
+ * Example:
+ *   1. Allow only specified addresses
+ *     A:172.20          # Allow any address that begins with 172.20.
+ *     A:10.10.          # Allow any address that begins with 10.10.
+ *     A:127.0.0.1       # Allow local loopback connections
+ *     D:*               # Deny from other IP connections
+ *
+ *   2. Only deny specified addresses
+ *     D:1.2.3.        # deny from 1.2.3.0 - 1.2.3.255
+ *     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 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
+ * affect the parent rules.
+ *
+ * Note that since the sub dir is parsed in the forked thread servicing the
+ * subdir http request, any merge is discarded when the process exits.  As a
+ * result, the subdir settings only have a lifetime of a single request.
+ *
+ * Custom error pages can contain an absolute path or be relative to
+ * 'home_httpd'. Error pages are to be static files (no CGI or script). Error
+ * page can only be defined in the root configuration file and are not taken
+ * into account in local (directories) config files.
+ *
+ * If -c is not set, an attempt will be made to open the default
+ * root configuration file.  If -c is set and the file is not found, the
+ * server exits with an error.
+ *
+ */
+ /* 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_FEATURE_HTTPD_USE_SENDFILE
+# include <sys/sendfile.h>
+#endif
+/* 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
+
+#define HEADER_READ_TIMEOUT 60
+
+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;
+} has_next_ptr;
+
+/* Must have "next" as a first member */
+typedef struct Htaccess {
+	struct Htaccess *next;
+	char *after_colon;
+	char before_colon[1];  /* really bigger, must be last */
+} Htaccess;
+
+/* Must have "next" as a first member */
+typedef struct Htaccess_IP {
+	struct Htaccess_IP *next;
+	unsigned ip;
+	unsigned mask;
+	int allow_deny;
+} Htaccess_IP;
+
+/* Must have "next" as a first member */
+typedef struct Htaccess_Proxy {
+	struct Htaccess_Proxy *next;
+	char *url_from;
+	char *host_port;
+	char *url_to;
+} Htaccess_Proxy;
+
+enum {
+	HTTP_OK = 200,
+	HTTP_PARTIAL_CONTENT = 206,
+	HTTP_MOVED_TEMPORARILY = 302,
+	HTTP_BAD_REQUEST = 400,       /* malformed syntax */
+	HTTP_UNAUTHORIZED = 401, /* authentication needed, respond with auth hdr */
+	HTTP_NOT_FOUND = 404,
+	HTTP_FORBIDDEN = 403,
+	HTTP_REQUEST_TIMEOUT = 408,
+	HTTP_NOT_IMPLEMENTED = 501,   /* used for unrecognized requests */
+	HTTP_INTERNAL_SERVER_ERROR = 500,
+	HTTP_CONTINUE = 100,
+#if 0   /* future use */
+	HTTP_SWITCHING_PROTOCOLS = 101,
+	HTTP_CREATED = 201,
+	HTTP_ACCEPTED = 202,
+	HTTP_NON_AUTHORITATIVE_INFO = 203,
+	HTTP_NO_CONTENT = 204,
+	HTTP_MULTIPLE_CHOICES = 300,
+	HTTP_MOVED_PERMANENTLY = 301,
+	HTTP_NOT_MODIFIED = 304,
+	HTTP_PAYMENT_REQUIRED = 402,
+	HTTP_BAD_GATEWAY = 502,
+	HTTP_SERVICE_UNAVAILABLE = 503, /* overload, maintenance */
+#endif
+};
+
+static const uint16_t http_response_type[] ALIGN2 = {
+	HTTP_OK,
+#if ENABLE_FEATURE_HTTPD_RANGES
+	HTTP_PARTIAL_CONTENT,
+#endif
+	HTTP_MOVED_TEMPORARILY,
+	HTTP_REQUEST_TIMEOUT,
+	HTTP_NOT_IMPLEMENTED,
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	HTTP_UNAUTHORIZED,
+#endif
+	HTTP_NOT_FOUND,
+	HTTP_BAD_REQUEST,
+	HTTP_FORBIDDEN,
+	HTTP_INTERNAL_SERVER_ERROR,
+#if 0   /* not implemented */
+	HTTP_CREATED,
+	HTTP_ACCEPTED,
+	HTTP_NO_CONTENT,
+	HTTP_MULTIPLE_CHOICES,
+	HTTP_MOVED_PERMANENTLY,
+	HTTP_NOT_MODIFIED,
+	HTTP_BAD_GATEWAY,
+	HTTP_SERVICE_UNAVAILABLE,
+#endif
+};
+
+static const struct {
+	const char *name;
+	const char *info;
+} http_response[ARRAY_SIZE(http_response_type)] = {
+	{ "OK", NULL },
+#if ENABLE_FEATURE_HTTPD_RANGES
+	{ "Partial Content", NULL },
+#endif
+	{ "Found", NULL },
+	{ "Request Timeout", "No request appeared within 60 seconds" },
+	{ "Not Implemented", "The requested method is not recognized" },
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	{ "Unauthorized", "" },
+#endif
+	{ "Not Found", "The requested URL was not found" },
+	{ "Bad Request", "Unsupported method" },
+	{ "Forbidden", ""  },
+	{ "Internal Server Error", "Internal Server Error" },
+#if 0   /* not implemented */
+	{ "Created" },
+	{ "Accepted" },
+	{ "No Content" },
+	{ "Multiple Choices" },
+	{ "Moved Permanently" },
+	{ "Not Modified" },
+	{ "Bad Gateway", "" },
+	{ "Service Unavailable", "" },
+#endif
+};
+
+struct globals {
+	int verbose;            /* must be int (used by getopt32) */
+	smallint flg_deny_all;
+
+	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;
+
+	const char *g_query;
+	const char *opt_c_configFile;
+	const char *home_httpd;
+	const char *index_page;
+
+	const char *found_mime_type;
+	const char *found_moved_temporarily;
+	Htaccess_IP *ip_a_d;    /* config allow/deny lines */
+
+	IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
+	IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
+	IF_FEATURE_HTTPD_CGI(char *referer;)
+	IF_FEATURE_HTTPD_CGI(char *user_agent;)
+	IF_FEATURE_HTTPD_CGI(char *host;)
+	IF_FEATURE_HTTPD_CGI(char *http_accept;)
+	IF_FEATURE_HTTPD_CGI(char *http_accept_language;)
+
+	off_t file_size;        /* -1 - unknown */
+#if ENABLE_FEATURE_HTTPD_RANGES
+	off_t range_start;
+	off_t range_end;
+	off_t range_len;
+#endif
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	Htaccess *g_auth;       /* config user:password lines */
+#endif
+	Htaccess *mime_a;       /* config mime types */
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+	Htaccess *script_i;     /* config script interpreters */
+#endif
+	char *iobuf;            /* [IOBUF_SIZE] */
+#define hdr_buf bb_common_bufsiz1
+	char *hdr_ptr;
+	int hdr_cnt;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+	const char *http_error_page[ARRAY_SIZE(http_response_type)];
+#endif
+#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          )
+#define flg_deny_all      (G.flg_deny_all     )
+#define rmt_ip            (G.rmt_ip           )
+#define bind_addr_or_port (G.bind_addr_or_port)
+#define g_query           (G.g_query          )
+#define opt_c_configFile  (G.opt_c_configFile )
+#define home_httpd        (G.home_httpd       )
+#define index_page        (G.index_page       )
+#define found_mime_type   (G.found_mime_type  )
+#define found_moved_temporarily (G.found_moved_temporarily)
+#define last_mod          (G.last_mod         )
+#define ip_a_d            (G.ip_a_d           )
+#define g_realm           (G.g_realm          )
+#define remoteuser        (G.remoteuser       )
+#define referer           (G.referer          )
+#define user_agent        (G.user_agent       )
+#define host              (G.host             )
+#define http_accept       (G.http_accept      )
+#define http_accept_language (G.http_accept_language)
+#define file_size         (G.file_size        )
+#if ENABLE_FEATURE_HTTPD_RANGES
+#define range_start       (G.range_start      )
+#define range_end         (G.range_end        )
+#define range_len         (G.range_len        )
+#else
+enum {
+	range_start = 0,
+	range_end = MAXINT(off_t) - 1,
+	range_len = MAXINT(off_t),
+};
+#endif
+#define rmt_ip_str        (G.rmt_ip_str       )
+#define g_auth            (G.g_auth           )
+#define mime_a            (G.mime_a           )
+#define script_i          (G.script_i         )
+#define iobuf             (G.iobuf            )
+#define hdr_ptr           (G.hdr_ptr          )
+#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";) \
+	bind_addr_or_port = "80"; \
+	index_page = index_html; \
+	file_size = -1; \
+} while (0)
+
+
+#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
+
+/* Prototypes */
+enum {
+	SEND_HEADERS     = (1 << 0),
+	SEND_BODY        = (1 << 1),
+	SEND_HEADERS_AND_BODY = SEND_HEADERS + SEND_BODY,
+};
+static void send_file_and_exit(const char *url, int what) NORETURN;
+
+static void free_llist(has_next_ptr **pptr)
+{
+	has_next_ptr *cur = *pptr;
+	while (cur) {
+		has_next_ptr *t = cur;
+		cur = cur->next;
+		free(t);
+	}
+	*pptr = NULL;
+}
+
+static ALWAYS_INLINE void free_Htaccess_list(Htaccess **pptr)
+{
+	free_llist((has_next_ptr**)pptr);
+}
+
+static ALWAYS_INLINE void free_Htaccess_IP_list(Htaccess_IP **pptr)
+{
+	free_llist((has_next_ptr**)pptr);
+}
+
+/* Returns presumed mask width in bits or < 0 on error.
+ * Updates strp, stores IP at provided pointer */
+static int scan_ip(const char **strp, unsigned *ipp, unsigned char endc)
+{
+	const char *p = *strp;
+	int auto_mask = 8;
+	unsigned ip = 0;
+	int j;
+
+	if (*p == '/')
+		return -auto_mask;
+
+	for (j = 0; j < 4; j++) {
+		unsigned octet;
+
+		if ((*p < '0' || *p > '9') && *p != '/' && *p)
+			return -auto_mask;
+		octet = 0;
+		while (*p >= '0' && *p <= '9') {
+			octet *= 10;
+			octet += *p - '0';
+			if (octet > 255)
+				return -auto_mask;
+			p++;
+		}
+		if (*p == '.')
+			p++;
+		if (*p != '/' && *p)
+			auto_mask += 8;
+		ip = (ip << 8) | octet;
+	}
+	if (*p) {
+		if (*p != endc)
+			return -auto_mask;
+		p++;
+		if (*p == '\0')
+			return -auto_mask;
+	}
+	*ipp = ip;
+	*strp = p;
+	return auto_mask;
+}
+
+/* Returns 0 on success. Stores IP and mask at provided pointers */
+static int scan_ip_mask(const char *str, unsigned *ipp, unsigned *maskp)
+{
+	int i;
+	unsigned mask;
+	char *p;
+
+	i = scan_ip(&str, ipp, '/');
+	if (i < 0)
+		return i;
+
+	if (*str) {
+		/* there is /xxx after dotted-IP address */
+		i = bb_strtou(str, &p, 10);
+		if (*p == '.') {
+			/* 'xxx' itself is dotted-IP mask, parse it */
+			/* (return 0 (success) only if it has N.N.N.N form) */
+			return scan_ip(&str, maskp, '\0') - 32;
+		}
+		if (*p)
+			return -1;
+	}
+
+	if (i > 32)
+		return -1;
+
+	if (sizeof(unsigned) == 4 && i == 32) {
+		/* mask >>= 32 below may not work */
+		mask = 0;
+	} else {
+		mask = 0xffffffff;
+		mask >>= i;
+	}
+	/* i == 0 -> *maskp = 0x00000000
+	 * i == 1 -> *maskp = 0x80000000
+	 * i == 4 -> *maskp = 0xf0000000
+	 * i == 31 -> *maskp = 0xfffffffe
+	 * i == 32 -> *maskp = 0xffffffff */
+	*maskp = (uint32_t)(~mask);
+	return 0;
+}
+
+/*
+ * Parse configuration file into in-memory linked list.
+ *
+ * Any previous IP rules are discarded.
+ * If the flag argument is not SUBDIR_PARSE then all /path and mime rules
+ * are also discarded.  That is, previous settings are retained if flag is
+ * SUBDIR_PARSE.
+ * Error pages are only parsed on the main config file.
+ *
+ * path   Path where to look for httpd.conf (without filename).
+ * flag   Type of the parse request.
+ */
+/* flag param: */
+enum {
+	FIRST_PARSE    = 0, /* path will be "/etc" */
+	SIGNALED_PARSE = 1, /* path will be "/etc" */
+	SUBDIR_PARSE   = 2, /* path will be derived from URL */
+};
+static void parse_conf(const char *path, int flag)
+{
+	/* internally used extra flag state */
+	enum { TRY_CURDIR_PARSE = 3 };
+
+	FILE *f;
+	const char *filename;
+	char buf[160];
+
+	/* discard old rules */
+	free_Htaccess_IP_list(&ip_a_d);
+	flg_deny_all = 0;
+	/* retain previous auth and mime config only for subdir parse */
+	if (flag != SUBDIR_PARSE) {
+		free_Htaccess_list(&mime_a);
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+		free_Htaccess_list(&g_auth);
+#endif
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+		free_Htaccess_list(&script_i);
+#endif
+	}
+
+	filename = opt_c_configFile;
+	if (flag == SUBDIR_PARSE || filename == NULL) {
+		filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2);
+		sprintf((char *)filename, "%s/%s", path, HTTPD_CONF);
+	}
+
+	while ((f = fopen_for_read(filename)) == NULL) {
+		if (flag >= SUBDIR_PARSE) { /* SUBDIR or TRY_CURDIR */
+			/* config file not found, no changes to config */
+			return;
+		}
+		if (flag == FIRST_PARSE) {
+			/* -c CONFFILE given, but CONFFILE doesn't exist? */
+			if (opt_c_configFile)
+				bb_simple_perror_msg_and_die(opt_c_configFile);
+			/* else: no -c, thus we looked at /etc/httpd.conf,
+			 * and it's not there. try ./httpd.conf: */
+		}
+		flag = TRY_CURDIR_PARSE;
+		filename = HTTPD_CONF;
+	}
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	/* in "/file:user:pass" lines, we prepend path in subdirs */
+	if (flag != SUBDIR_PARSE)
+		path = "";
+#endif
+	/* The lines can be:
+	 *
+	 * I:default_index_file
+	 * H:http_home
+	 * [AD]:IP[/mask]   # allow/deny, * for wildcard
+	 * Ennn:error.html  # error page for status nnn
+	 * P:/url:[http://]hostname[:port]/new/path # reverse proxy
+	 * .ext:mime/type   # mime type
+	 * *.php:/path/php  # run xxx.php through an interpreter
+	 * /file:user:pass  # username and password
+	 */
+	while (fgets(buf, sizeof(buf), f) != NULL) {
+		unsigned strlen_buf;
+		unsigned char ch;
+		char *after_colon;
+
+		{ /* remove all whitespace, and # comments */
+			char *p, *p0;
+
+			p0 = buf;
+			/* skip non-whitespace beginning. Often the whole line
+			 * is non-whitespace. We want this case to work fast,
+			 * without needless copying, therefore we don't merge
+			 * this operation into next while loop. */
+			while ((ch = *p0) != '\0' && ch != '\n' && ch != '#'
+			 && ch != ' ' && ch != '\t'
+			) {
+				p0++;
+			}
+			p = p0;
+			/* if we enter this loop, we have some whitespace.
+			 * discard it */
+			while (ch != '\0' && ch != '\n' && ch != '#') {
+				if (ch != ' ' && ch != '\t') {
+					*p++ = ch;
+				}
+				ch = *++p0;
+			}
+			*p = '\0';
+			strlen_buf = p - buf;
+			if (strlen_buf == 0)
+				continue; /* empty line */
+		}
+
+		after_colon = strchr(buf, ':');
+		/* strange line? */
+		if (after_colon == NULL || *++after_colon == '\0')
+			goto config_error;
+
+		ch = (buf[0] & ~0x20); /* toupper if it's a letter */
+
+		if (ch == 'I') {
+			if (index_page != index_html)
+				free((char*)index_page);
+			index_page = xstrdup(after_colon);
+			continue;
+		}
+
+		/* do not allow jumping around using H in subdir's configs */
+		if (flag == FIRST_PARSE && ch == 'H') {
+			home_httpd = xstrdup(after_colon);
+			xchdir(home_httpd);
+			continue;
+		}
+
+		if (ch == 'A' || ch == 'D') {
+			Htaccess_IP *pip;
+
+			if (*after_colon == '*') {
+				if (ch == 'D') {
+					/* memorize "deny all" */
+					flg_deny_all = 1;
+				}
+				/* skip assumed "A:*", it is a default anyway */
+				continue;
+			}
+			/* store "allow/deny IP/mask" line */
+			pip = xzalloc(sizeof(*pip));
+			if (scan_ip_mask(after_colon, &pip->ip, &pip->mask)) {
+				/* IP{/mask} syntax error detected, protect all */
+				ch = 'D';
+				pip->mask = 0;
+			}
+			pip->allow_deny = ch;
+			if (ch == 'D') {
+				/* Deny:from_IP - prepend */
+				pip->next = ip_a_d;
+				ip_a_d = pip;
+			} else {
+				/* A:from_IP - append (thus all D's precedes A's) */
+				Htaccess_IP *prev_IP = ip_a_d;
+				if (prev_IP == NULL) {
+					ip_a_d = pip;
+				} else {
+					while (prev_IP->next)
+						prev_IP = prev_IP->next;
+					prev_IP->next = pip;
+				}
+			}
+			continue;
+		}
+
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+		if (flag == FIRST_PARSE && ch == 'E') {
+			unsigned i;
+			int status = atoi(buf + 1); /* error status code */
+
+			if (status < HTTP_CONTINUE) {
+				goto config_error;
+			}
+			/* then error page; find matching status */
+			for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
+				if (http_response_type[i] == status) {
+					/* We chdir to home_httpd, thus no need to
+					 * concat_path_file(home_httpd, after_colon)
+					 * here */
+					http_error_page[i] = xstrdup(after_colon);
+					break;
+				}
+			}
+			continue;
+		}
+#endif
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+		if (flag == FIRST_PARSE && ch == 'P') {
+			/* P:/url:[http://]hostname[:port]/new/path */
+			char *url_from, *host_port, *url_to;
+			Htaccess_Proxy *proxy_entry;
+
+			url_from = after_colon;
+			host_port = strchr(after_colon, ':');
+			if (host_port == NULL) {
+				goto config_error;
+			}
+			*host_port++ = '\0';
+			if (strncmp(host_port, "http://", 7) == 0)
+				host_port += 7;
+			if (*host_port == '\0') {
+				goto config_error;
+			}
+			url_to = strchr(host_port, '/');
+			if (url_to == NULL) {
+				goto config_error;
+			}
+			*url_to = '\0';
+			proxy_entry = xzalloc(sizeof(*proxy_entry));
+			proxy_entry->url_from = xstrdup(url_from);
+			proxy_entry->host_port = xstrdup(host_port);
+			*url_to = '/';
+			proxy_entry->url_to = xstrdup(url_to);
+			proxy_entry->next = proxy;
+			proxy = proxy_entry;
+			continue;
+		}
+#endif
+		/* the rest of directives are non-alphabetic,
+		 * must avoid using "toupper'ed" ch */
+		ch = buf[0];
+
+		if (ch == '.' /* ".ext:mime/type" */
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+		 || (ch == '*' && buf[1] == '.') /* "*.php:/path/php" */
+#endif
+		) {
+			char *p;
+			Htaccess *cur;
+
+			cur = xzalloc(sizeof(*cur) /* includes space for NUL */ + strlen_buf);
+			strcpy(cur->before_colon, buf);
+			p = cur->before_colon + (after_colon - buf);
+			p[-1] = '\0';
+			cur->after_colon = p;
+			if (ch == '.') {
+				/* .mime line: prepend to mime_a list */
+				cur->next = mime_a;
+				mime_a = cur;
+			}
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+			else {
+				/* script interpreter line: prepend to script_i list */
+				cur->next = script_i;
+				script_i = cur;
+			}
+#endif
+			continue;
+		}
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+		if (ch == '/') { /* "/file:user:pass" */
+			char *p;
+			Htaccess *cur;
+			unsigned file_len;
+
+			/* note: path is "" unless we are in SUBDIR parse,
+			 * otherwise it does NOT start with "/" */
+			cur = xzalloc(sizeof(*cur) /* includes space for NUL */
+				+ 1 + strlen(path)
+				+ strlen_buf
+				);
+			/* form "/path/file" */
+			sprintf(cur->before_colon, "/%s%.*s",
+				path,
+				(int) (after_colon - buf - 1), /* includes "/", but not ":" */
+				buf);
+			/* canonicalize it */
+			p = bb_simplify_abs_path_inplace(cur->before_colon);
+			file_len = p - cur->before_colon;
+			/* add "user:pass" after NUL */
+			strcpy(++p, after_colon);
+			cur->after_colon = p;
+
+			/* insert cur into g_auth */
+			/* g_auth is sorted by decreased filename length */
+			{
+				Htaccess *auth, **authp;
+
+				authp = &g_auth;
+				while ((auth = *authp) != NULL) {
+					if (file_len >= strlen(auth->before_colon)) {
+						/* insert cur before auth */
+						cur->next = auth;
+						break;
+					}
+					authp = &auth->next;
+				}
+				*authp = cur;
+			}
+			continue;
+		}
+#endif /* BASIC_AUTH */
+
+		/* the line is not recognized */
+ config_error:
+		bb_error_msg("config error '%s' in '%s'", buf, filename);
+	 } /* while (fgets) */
+
+	 fclose(f);
+}
+
+#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+/*
+ * Given a string, html-encode special characters.
+ * This is used for the -e command line option to provide an easy way
+ * for scripts to encode result data without confusing browsers.  The
+ * returned string pointer is memory allocated by malloc().
+ *
+ * Returns a pointer to the encoded string (malloced).
+ */
+static char *encodeString(const char *string)
+{
+	/* take the simple route and encode everything */
+	/* could possibly scan once to get length.     */
+	int len = strlen(string);
+	char *out = xmalloc(len * 6 + 1);
+	char *p = out;
+	char ch;
+
+	while ((ch = *string++) != '\0') {
+		/* very simple check for what to encode */
+		if (isalnum(ch))
+			*p++ = ch;
+		else
+			p += sprintf(p, "&#%d;", (unsigned char) ch);
+	}
+	*p = '\0';
+	return out;
+}
+#endif
+
+/*
+ * 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;
+}
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+/*
+ * Decode a base64 data stream as per rfc1521.
+ * Note that the rfc states that non base64 chars are to be ignored.
+ * Since the decode always results in a shorter size than the input,
+ * it is OK to pass the input arg as an output arg.
+ * Parameter: a pointer to a base64 encoded string.
+ * Decoded data is stored in-place.
+ */
+static void decodeBase64(char *Data)
+{
+	const unsigned char *in = (const unsigned char *)Data;
+	/* The decoded size will be at most 3/4 the size of the encoded */
+	unsigned ch = 0;
+	int i = 0;
+
+	while (*in) {
+		int t = *in++;
+
+		if (t >= '0' && t <= '9')
+			t = t - '0' + 52;
+		else if (t >= 'A' && t <= 'Z')
+			t = t - 'A';
+		else if (t >= 'a' && t <= 'z')
+			t = t - 'a' + 26;
+		else if (t == '+')
+			t = 62;
+		else if (t == '/')
+			t = 63;
+		else if (t == '=')
+			t = 0;
+		else
+			continue;
+
+		ch = (ch << 6) | t;
+		i++;
+		if (i == 4) {
+			*Data++ = (char) (ch >> 16);
+			*Data++ = (char) (ch >> 8);
+			*Data++ = (char) ch;
+			i = 0;
+		}
+	}
+	*Data = '\0';
+}
+#endif
+
+/*
+ * Create a listen server socket on the designated port.
+ */
+static int openServer(void)
+{
+	unsigned n = bb_strtou(bind_addr_or_port, NULL, 10);
+	if (!errno && n && n <= 0xffff)
+		n = create_and_bind_stream_or_die(NULL, n);
+	else
+		n = create_and_bind_stream_or_die(bind_addr_or_port, 80);
+	xlisten(n, 9);
+	return n;
+}
+
+/*
+ * Log the connection closure and exit.
+ */
+static void log_and_exit(void) NORETURN;
+static void log_and_exit(void)
+{
+	/* Paranoia. IE said to be buggy. It may send some extra data
+	 * or be confused by us just exiting without SHUT_WR. Oh well. */
+	shutdown(1, SHUT_WR);
+	/* Why??
+	(this also messes up stdin when user runs httpd -i from terminal)
+	ndelay_on(0);
+	while (read(STDIN_FILENO, iobuf, IOBUF_SIZE) > 0)
+		continue;
+	*/
+
+	if (verbose > 2)
+		bb_error_msg("closed");
+	_exit(xfunc_error_retval);
+}
+
+/*
+ * Create and send HTTP response headers.
+ * The arguments are combined and sent as one write operation.  Note that
+ * IE will puke big-time if the headers are not sent in one packet and the
+ * second packet is delayed for any reason.
+ * responseNum - the result code to send.
+ */
+static void send_headers(int responseNum)
+{
+	static const char RFC1123FMT[] ALIGN1 = "%a, %d %b %Y %H:%M:%S GMT";
+
+	const char *responseString = "";
+	const char *infoString = NULL;
+	const char *mime_type;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+	const char *error_page = NULL;
+#endif
+	unsigned i;
+	time_t timer = time(NULL);
+	char tmp_str[80];
+	int len;
+
+	for (i = 0; i < ARRAY_SIZE(http_response_type); i++) {
+		if (http_response_type[i] == responseNum) {
+			responseString = http_response[i].name;
+			infoString = http_response[i].info;
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+			error_page = http_error_page[i];
+#endif
+			break;
+		}
+	}
+	/* error message is HTML */
+	mime_type = responseNum == HTTP_OK ?
+				found_mime_type : "text/html";
+
+	if (verbose)
+		bb_error_msg("response:%u", responseNum);
+
+	/* emit the current date */
+	strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&timer));
+	len = sprintf(iobuf,
+			"HTTP/1.0 %d %s\r\nContent-type: %s\r\n"
+			"Date: %s\r\nConnection: close\r\n",
+			responseNum, responseString, mime_type, tmp_str);
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	if (responseNum == HTTP_UNAUTHORIZED) {
+		len += sprintf(iobuf + len,
+				"WWW-Authenticate: Basic realm=\"%s\"\r\n",
+				g_realm);
+	}
+#endif
+	if (responseNum == HTTP_MOVED_TEMPORARILY) {
+		len += sprintf(iobuf + len, "Location: %s/%s%s\r\n",
+				found_moved_temporarily,
+				(g_query ? "?" : ""),
+				(g_query ? g_query : ""));
+	}
+
+#if ENABLE_FEATURE_HTTPD_ERROR_PAGES
+	if (error_page && access(error_page, R_OK) == 0) {
+		strcat(iobuf, "\r\n");
+		len += 2;
+
+		if (DEBUG)
+			fprintf(stderr, "headers: '%s'\n", iobuf);
+		full_write(STDOUT_FILENO, iobuf, len);
+		if (DEBUG)
+			fprintf(stderr, "writing error page: '%s'\n", error_page);
+		return send_file_and_exit(error_page, SEND_BODY);
+	}
+#endif
+
+	if (file_size != -1) {    /* file */
+		strftime(tmp_str, sizeof(tmp_str), RFC1123FMT, gmtime(&last_mod));
+#if ENABLE_FEATURE_HTTPD_RANGES
+		if (responseNum == HTTP_PARTIAL_CONTENT) {
+			len += sprintf(iobuf + len, "Content-Range: bytes %"OFF_FMT"u-%"OFF_FMT"u/%"OFF_FMT"u\r\n",
+					range_start,
+					range_end,
+					file_size);
+			file_size = range_end - range_start + 1;
+		}
+#endif
+		len += sprintf(iobuf + len,
+#if ENABLE_FEATURE_HTTPD_RANGES
+			"Accept-Ranges: bytes\r\n"
+#endif
+			"Last-Modified: %s\r\n%s %"OFF_FMT"u\r\n",
+				tmp_str,
+				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) {
+		len += sprintf(iobuf + len,
+				"<HTML><HEAD><TITLE>%d %s</TITLE></HEAD>\n"
+				"<BODY><H1>%d %s</H1>\n%s\n</BODY></HTML>\n",
+				responseNum, responseString,
+				responseNum, responseString, infoString);
+	}
+	if (DEBUG)
+		fprintf(stderr, "headers: '%s'\n", iobuf);
+	if (full_write(STDOUT_FILENO, iobuf, len) != len) {
+		if (verbose > 1)
+			bb_perror_msg("error");
+		log_and_exit();
+	}
+}
+
+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();
+}
+
+/*
+ * Read from the socket until '\n' or EOF. '\r' chars are removed.
+ * '\n' is replaced with NUL.
+ * Return number of characters read or 0 if nothing is read
+ * ('\r' and '\n' are not counted).
+ * Data is returned in iobuf.
+ */
+static int get_line(void)
+{
+	int count = 0;
+	char c;
+
+	alarm(HEADER_READ_TIMEOUT);
+	while (1) {
+		if (hdr_cnt <= 0) {
+			hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+			if (hdr_cnt <= 0)
+				break;
+			hdr_ptr = hdr_buf;
+		}
+		iobuf[count] = c = *hdr_ptr++;
+		hdr_cnt--;
+
+		if (c == '\r')
+			continue;
+		if (c == '\n') {
+			iobuf[count] = '\0';
+			break;
+		}
+		if (count < (IOBUF_SIZE - 1))      /* check overflow */
+			count++;
+	}
+	return count;
+}
+
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
+
+/* gcc 4.2.1 fares better with NOINLINE */
+static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len) NORETURN;
+static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post_len)
+{
+	enum { FROM_CGI = 1, TO_CGI = 2 }; /* indexes in pfd[] */
+	struct pollfd pfd[3];
+	int out_cnt; /* we buffer a bit of initial CGI output */
+	int count;
+
+	/* iobuf is used for CGI -> network data,
+	 * hdr_buf is for network -> CGI data (POSTDATA) */
+
+	/* If CGI dies, we still want to correctly finish reading its output
+	 * and send it to the peer. So please no SIGPIPEs! */
+	signal(SIGPIPE, SIG_IGN);
+
+	// We inconsistently handle a case when more POSTDATA from network
+	// is coming than we expected. We may give *some part* of that
+	// extra data to CGI.
+
+	//if (hdr_cnt > post_len) {
+	//	/* We got more POSTDATA from network than we expected */
+	//	hdr_cnt = post_len;
+	//}
+	post_len -= hdr_cnt;
+	/* post_len - number of POST bytes not yet read from network */
+
+	/* NB: breaking out of this loop jumps to log_and_exit() */
+	out_cnt = 0;
+	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;
+			} else {
+				/* post_len <= 0 && hdr_cnt <= 0:
+				 * no more POST data to CGI,
+				 * let CGI see EOF on CGI's stdin */
+				if (toCgi_wr != fromCgi_rd)
+					close(toCgi_wr);
+				toCgi_wr = 0;
+			}
+		}
+
+		/* Now wait on the set of sockets */
+		count = safe_poll(pfd, toCgi_wr ? TO_CGI+1 : FROM_CGI+1, -1);
+		if (count <= 0) {
+#if 0
+			if (safe_waitpid(pid, &status, WNOHANG) <= 0) {
+				/* Weird. CGI didn't exit and no fd's
+				 * are ready, yet poll returned?! */
+				continue;
+			}
+			if (DEBUG && WIFEXITED(status))
+				bb_error_msg("CGI exited, status=%d", WEXITSTATUS(status));
+			if (DEBUG && WIFSIGNALED(status))
+				bb_error_msg("CGI killed, signal=%d", WTERMSIG(status));
+#endif
+			break;
+		}
+
+		if (pfd[TO_CGI].revents) {
+			/* hdr_cnt > 0 here due to the way pfd[TO_CGI].events set */
+			/* 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
+			 *if (count < 0 && errno == EAGAIN) {
+			 *	...
+			 *} else */
+			if (count > 0) {
+				hdr_ptr += count;
+				hdr_cnt -= count;
+			} else {
+				/* EOF/broken pipe to CGI, stop piping POST data */
+				hdr_cnt = post_len = 0;
+			}
+		}
+
+		if (pfd[0].revents) {
+			/* post_len > 0 && hdr_cnt == 0 here */
+			/* We expect data, prev data portion is eaten by CGI
+			 * and there *is* data to read from the peer
+			 * (POSTDATA) */
+			//count = post_len > (int)sizeof(hdr_buf) ? (int)sizeof(hdr_buf) : post_len;
+			//count = safe_read(STDIN_FILENO, hdr_buf, count);
+			count = safe_read(STDIN_FILENO, hdr_buf, sizeof(hdr_buf));
+			if (count > 0) {
+				hdr_cnt = count;
+				hdr_ptr = hdr_buf;
+				post_len -= count;
+			} else {
+				/* no more POST data can be read */
+				post_len = 0;
+			}
+		}
+
+		if (pfd[FROM_CGI].revents) {
+			/* There is something to read from CGI */
+			char *rbuf = iobuf;
+
+			/* Are we still buffering CGI output? */
+			if (out_cnt >= 0) {
+				/* HTTP_200[] has single "\r\n" at the end.
+				 * According to http://hoohoo.ncsa.uiuc.edu/cgi/out.html,
+				 * CGI scripts MUST send their own header terminated by
+				 * empty line, then data. That's why we have only one
+				 * <cr><lf> pair here. We will output "200 OK" line
+				 * if needed, but CGI still has to provide blank line
+				 * between header and body */
+
+				/* Must use safe_read, not full_read, because
+				 * CGI may output a few first bytes and then wait
+				 * for POSTDATA without closing stdout.
+				 * With full_read we may wait here forever. */
+				count = safe_read(fromCgi_rd, rbuf + out_cnt, PIPE_BUF - 8);
+				if (count <= 0) {
+					/* eof (or error) and there was no "HTTP",
+					 * so write it, then write received data */
+					if (out_cnt) {
+						full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1);
+						full_write(STDOUT_FILENO, rbuf, out_cnt);
+					}
+					break; /* CGI stdout is closed, exiting */
+				}
+				out_cnt += count;
+				count = 0;
+				/* "Status" header format is: "Status: 302 Redirected\r\n" */
+				if (out_cnt >= 8 && memcmp(rbuf, "Status: ", 8) == 0) {
+					/* send "HTTP/1.0 " */
+					if (full_write(STDOUT_FILENO, HTTP_200, 9) != 9)
+						break;
+					rbuf += 8; /* skip "Status: " */
+					count = out_cnt - 8;
+					out_cnt = -1; /* buffering off */
+				} else if (out_cnt >= 4) {
+					/* Did CGI add "HTTP"? */
+					if (memcmp(rbuf, HTTP_200, 4) != 0) {
+						/* there is no "HTTP", do it ourself */
+						if (full_write(STDOUT_FILENO, HTTP_200, sizeof(HTTP_200)-1) != sizeof(HTTP_200)-1)
+							break;
+					}
+					/* Commented out:
+					if (!strstr(rbuf, "ontent-")) {
+						full_write(s, "Content-type: text/plain\r\n\r\n", 28);
+					}
+					 * Counter-example of valid CGI without Content-type:
+					 * echo -en "HTTP/1.0 302 Found\r\n"
+					 * echo -en "Location: http://www.busybox.net\r\n"
+					 * echo -en "\r\n"
+					 */
+					count = out_cnt;
+					out_cnt = -1; /* buffering off */
+				}
+			} else {
+				count = safe_read(fromCgi_rd, rbuf, PIPE_BUF);
+				if (count <= 0)
+					break;  /* eof (or error) */
+			}
+			if (full_write(STDOUT_FILENO, rbuf, count) != count)
+				break;
+			if (DEBUG)
+				fprintf(stderr, "cgi read %d bytes: '%.*s'\n", count, count, rbuf);
+		} /* if (pfd[FROM_CGI].revents) */
+	} /* while (1) */
+	log_and_exit();
+}
+#endif
+
+#if ENABLE_FEATURE_HTTPD_CGI
+
+static void setenv1(const char *name, const char *value)
+{
+	setenv(name, value ? value : "", 1);
+}
+
+/*
+ * Spawn CGI script, forward CGI's stdin/out <=> network
+ *
+ * Environment variables are set up and the script is invoked with pipes
+ * for stdin/stdout.  If a POST is being done the script is fed the POST
+ * data in addition to setting the QUERY_STRING variable (for GETs or POSTs).
+ *
+ * Parameters:
+ * const char *url              The requested URL (with leading /).
+ * 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 *request,
+		int post_len,
+		const char *cookie,
+		const char *content_type) NORETURN;
+static void send_cgi_and_exit(
+		const char *url,
+		const char *request,
+		int post_len,
+		const char *cookie,
+		const char *content_type)
+{
+	struct fd_pair fromCgi;  /* CGI -> httpd pipe */
+	struct fd_pair toCgi;    /* httpd -> CGI pipe */
+	char *script;
+	int pid;
+
+	/* Make a copy. NB: caller guarantees:
+	 * url[0] == '/', url[1] != '/' */
+	url = xstrdup(url);
+
+	/*
+	 * We are mucking with environment _first_ and then vfork/exec,
+	 * this allows us to use vfork safely. Parent doesn't care about
+	 * these environment changes anyway.
+	 */
+
+	/* Check for [dirs/]script.cgi/PATH_INFO */
+	script = (char*)url;
+	while ((script = strchr(script + 1, '/')) != NULL) {
+		*script = '\0';
+		if (!is_directory(url + 1, 1, NULL)) {
+			/* not directory, found script.cgi/PATH_INFO */
+			*script = '/';
+			break;
+		}
+		*script = '/'; /* is directory, find next '/' */
+	}
+	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));
+	} else {
+		setenv1("REQUEST_URI", url);
+	}
+	if (script != NULL)
+		*script = '\0';         /* cut off /PATH_INFO */
+
+	/* SCRIPT_FILENAME is required by PHP in CGI mode */
+	if (home_httpd[0] == '/') {
+		char *fullpath = concat_path_file(home_httpd, url);
+		setenv1("SCRIPT_FILENAME", fullpath);
+	}
+	/* set SCRIPT_NAME as full path: /cgi-bin/dirs/script.cgi */
+	setenv1("SCRIPT_NAME", url);
+	/* http://hoohoo.ncsa.uiuc.edu/cgi/env.html:
+	 * QUERY_STRING: The information which follows the ? in the URL
+	 * which referenced this script. This is the query information.
+	 * It should not be decoded in any fashion. This variable
+	 * should always be set when there is query information,
+	 * regardless of command line decoding. */
+	/* (Older versions of bbox seem to do some decoding) */
+	setenv1("QUERY_STRING", g_query);
+	putenv((char*)"SERVER_SOFTWARE=busybox httpd/"BB_VER);
+	putenv((char*)"SERVER_PROTOCOL=HTTP/1.0");
+	putenv((char*)"GATEWAY_INTERFACE=CGI/1.1");
+	/* Having _separate_ variables for IP and port defeats
+	 * the purpose of having socket abstraction. Which "port"
+	 * are you using on Unix domain socket?
+	 * IOW - REMOTE_PEER="1.2.3.4:56" makes much more sense.
+	 * Oh well... */
+	{
+		char *p = rmt_ip_str ? rmt_ip_str : (char*)"";
+		char *cp = strrchr(p, ':');
+		if (ENABLE_FEATURE_IPV6 && cp && strchr(cp, ']'))
+			cp = NULL;
+		if (cp) *cp = '\0'; /* delete :PORT */
+		setenv1("REMOTE_ADDR", p);
+		if (cp) {
+			*cp = ':';
+#if ENABLE_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV
+			setenv1("REMOTE_PORT", cp + 1);
+#endif
+		}
+	}
+	setenv1("HTTP_USER_AGENT", user_agent);
+	if (http_accept)
+		setenv1("HTTP_ACCEPT", http_accept);
+	if (http_accept_language)
+		setenv1("HTTP_ACCEPT_LANGUAGE", http_accept_language);
+	if (post_len)
+		putenv(xasprintf("CONTENT_LENGTH=%d", post_len));
+	if (cookie)
+		setenv1("HTTP_COOKIE", cookie);
+	if (content_type)
+		setenv1("CONTENT_TYPE", content_type);
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	if (remoteuser) {
+		setenv1("REMOTE_USER", remoteuser);
+		putenv((char*)"AUTH_TYPE=Basic");
+	}
+#endif
+	if (referer)
+		setenv1("HTTP_REFERER", referer);
+	setenv1("HTTP_HOST", host); /* set to "" if NULL */
+	/* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
+	 * just run "env SERVER_NAME=xyz httpd ..." instead */
+
+	xpiped_pair(fromCgi);
+	xpiped_pair(toCgi);
+
+	pid = vfork();
+	if (pid < 0) {
+		/* TODO: log perror? */
+		log_and_exit();
+	}
+
+	if (!pid) {
+		/* Child process */
+		char *argv[3];
+
+		xfunc_error_retval = 242;
+
+		/* NB: close _first_, then move fds! */
+		close(toCgi.wr);
+		close(fromCgi.rd);
+		xmove_fd(toCgi.rd, 0);  /* replace stdin with the pipe */
+		xmove_fd(fromCgi.wr, 1);  /* replace stdout with the pipe */
+		/* User seeing stderr output can be a security problem.
+		 * If CGI really wants that, it can always do dup itself. */
+		/* dup2(1, 2); */
+
+		/* Chdiring to script's dir */
+		script = strrchr(url, '/');
+		if (script != url) { /* paranoia */
+			*script = '\0';
+			if (chdir(url + 1) != 0) {
+				bb_perror_msg("chdir(%s)", url + 1);
+				goto error_execing_cgi;
+			}
+			// not needed: *script = '/';
+		}
+		script++;
+
+		/* set argv[0] to name without path */
+		argv[0] = script;
+		argv[1] = NULL;
+
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+		{
+			char *suffix = strrchr(script, '.');
+
+			if (suffix) {
+				Htaccess *cur;
+				for (cur = script_i; cur; cur = cur->next) {
+					if (strcmp(cur->before_colon + 1, suffix) == 0) {
+						/* found interpreter name */
+						argv[0] = cur->after_colon;
+						argv[1] = script;
+						argv[2] = NULL;
+						break;
+					}
+				}
+			}
+		}
+#endif
+		/* restore default signal dispositions for CGI process */
+		bb_signals(0
+			| (1 << SIGCHLD)
+			| (1 << SIGPIPE)
+			| (1 << SIGHUP)
+			, SIG_DFL);
+
+		/* _NOT_ execvp. We do not search PATH. argv[0] is a filename
+		 * without any dir components and will only match a file
+		 * in the current directory */
+		execv(argv[0], argv);
+		if (verbose)
+			bb_perror_msg("can't execute '%s'", argv[0]);
+ error_execing_cgi:
+		/* send to stdout
+		 * (we are CGI here, our stdout is pumped to the net) */
+		send_headers_and_exit(HTTP_NOT_FOUND);
+	} /* end child */
+
+	/* Parent process */
+
+	/* Restore variables possibly changed by child */
+	xfunc_error_retval = 0;
+
+	/* Pump data */
+	close(fromCgi.wr);
+	close(toCgi.rd);
+	cgi_io_loop_and_exit(fromCgi.rd, toCgi.wr, post_len);
+}
+
+#endif          /* FEATURE_HTTPD_CGI */
+
+/*
+ * Send a file response to a HTTP request, and exit
+ *
+ * Parameters:
+ * const char *url  The requested URL (with leading /).
+ * what             What to send (headers/body/both).
+ */
+static NOINLINE void send_file_and_exit(const char *url, int what)
+{
+	char *suffix;
+	int fd;
+	ssize_t count;
+
+	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);
+		/* Error pages are sent by using send_file_and_exit(SEND_BODY).
+		 * IOW: it is unsafe to call send_headers_and_exit
+		 * if what is SEND_BODY! Can recurse! */
+		if (what != SEND_BODY)
+			send_headers_and_exit(HTTP_NOT_FOUND);
+		log_and_exit();
+	}
+	/* If you want to know about EPIPE below
+	 * (happens if you abort downloads from local httpd): */
+	signal(SIGPIPE, SIG_IGN);
+
+	/* If not found, default is "application/octet-stream" */
+	found_mime_type = "application/octet-stream";
+	suffix = strrchr(url, '.');
+	if (suffix) {
+		static const char suffixTable[] ALIGN1 =
+			/* Shorter suffix must be first:
+			 * ".html.htm" will fail for ".htm"
+			 */
+			".txt.h.c.cc.cpp\0" "text/plain\0"
+			/* .htm line must be after .h line */
+			".htm.html\0" "text/html\0"
+			".jpg.jpeg\0" "image/jpeg\0"
+			".gif\0"      "image/gif\0"
+			".png\0"      "image/png\0"
+			/* .css line must be after .c line */
+			".css\0"      "text/css\0"
+			".wav\0"      "audio/wav\0"
+			".avi\0"      "video/x-msvideo\0"
+			".qt.mov\0"   "video/quicktime\0"
+			".mpe.mpeg\0" "video/mpeg\0"
+			".mid.midi\0" "audio/midi\0"
+			".mp3\0"      "audio/mpeg\0"
+#if 0  /* unpopular */
+			".au\0"       "audio/basic\0"
+			".pac\0"      "application/x-ns-proxy-autoconfig\0"
+			".vrml.wrl\0" "model/vrml\0"
+#endif
+			/* compiler adds another "\0" here */
+		;
+		Htaccess *cur;
+
+		/* Examine built-in table */
+		const char *table = suffixTable;
+		const char *table_next;
+		for (; *table; table = table_next) {
+			const char *try_suffix;
+			const char *mime_type;
+			mime_type  = table + strlen(table) + 1;
+			table_next = mime_type + strlen(mime_type) + 1;
+			try_suffix = strstr(table, suffix);
+			if (!try_suffix)
+				continue;
+			try_suffix += strlen(suffix);
+			if (*try_suffix == '\0' || *try_suffix == '.') {
+				found_mime_type = mime_type;
+				break;
+			}
+			/* Example: strstr(table, ".av") != NULL, but it
+			 * does not match ".avi" after all and we end up here.
+			 * The table is arranged so that in this case we know
+			 * that it can't match anything in the following lines,
+			 * and we stop the search: */
+			break;
+		}
+		/* ...then user's table */
+		for (cur = mime_a; cur; cur = cur->next) {
+			if (strcmp(cur->before_colon, suffix) == 0) {
+				found_mime_type = cur->after_colon;
+				break;
+			}
+		}
+	}
+
+	if (DEBUG)
+		bb_error_msg("sending file '%s' content-type: %s",
+			url, found_mime_type);
+
+#if ENABLE_FEATURE_HTTPD_RANGES
+	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 = 0;
+	}
+	range_len = MAXINT(off_t);
+	if (range_start) {
+		if (!range_end) {
+			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;
+		} else {
+			range_len = range_end - range_start + 1;
+			send_headers(HTTP_PARTIAL_CONTENT);
+			what = SEND_BODY;
+		}
+	}
+#endif
+	if (what & SEND_HEADERS)
+		send_headers(HTTP_OK);
+#if ENABLE_FEATURE_HTTPD_USE_SENDFILE
+	{
+		off_t offset = range_start;
+		while (1) {
+			/* sz is rounded down to 64k */
+			ssize_t sz = MAXINT(ssize_t) - 0xffff;
+			IF_FEATURE_HTTPD_RANGES(if (sz > range_len) sz = range_len;)
+			count = sendfile(STDOUT_FILENO, fd, &offset, sz);
+			if (count < 0) {
+				if (offset == range_start)
+					break; /* fall back to read/write loop */
+				goto fin;
+			}
+			IF_FEATURE_HTTPD_RANGES(range_len -= sz;)
+			if (count == 0 || range_len == 0)
+				log_and_exit();
+		}
+	}
+#endif
+	while ((count = safe_read(fd, iobuf, IOBUF_SIZE)) > 0) {
+		ssize_t n;
+		IF_FEATURE_HTTPD_RANGES(if (count > range_len) count = range_len;)
+		n = full_write(STDOUT_FILENO, iobuf, count);
+		if (count != n)
+			break;
+		IF_FEATURE_HTTPD_RANGES(range_len -= count;)
+		if (range_len == 0)
+			break;
+	}
+	if (count < 0) {
+ IF_FEATURE_HTTPD_USE_SENDFILE(fin:)
+		if (verbose > 1)
+			bb_perror_msg("error");
+	}
+	log_and_exit();
+}
+
+static int checkPermIP(void)
+{
+	Htaccess_IP *cur;
+
+	for (cur = ip_a_d; cur; cur = cur->next) {
+#if DEBUG
+		fprintf(stderr,
+			"checkPermIP: '%s' ? '%u.%u.%u.%u/%u.%u.%u.%u'\n",
+			rmt_ip_str,
+			(unsigned char)(cur->ip >> 24),
+			(unsigned char)(cur->ip >> 16),
+			(unsigned char)(cur->ip >> 8),
+			(unsigned char)(cur->ip),
+			(unsigned char)(cur->mask >> 24),
+			(unsigned char)(cur->mask >> 16),
+			(unsigned char)(cur->mask >> 8),
+			(unsigned char)(cur->mask)
+		);
+#endif
+		if ((rmt_ip & cur->mask) == cur->ip)
+			return (cur->allow_deny == 'A'); /* A -> 1 */
+	}
+
+	return !flg_deny_all; /* depends on whether we saw "D:*" */
+}
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+/*
+ * Config file entries are of the form "/<path>:<user>:<passwd>".
+ * If config file has no prefix match for path, access is allowed.
+ *
+ * path                 The file path
+ * user_and_passwd      "user:passwd" to validate
+ *
+ * Returns 1 if user_and_passwd is OK.
+ */
+static int check_user_passwd(const char *path, const char *user_and_passwd)
+{
+	Htaccess *cur;
+	const char *prev = NULL;
+
+	for (cur = g_auth; cur; cur = cur->next) {
+		const char *dir_prefix;
+		size_t len;
+
+		dir_prefix = cur->before_colon;
+
+		/* WHY? */
+		/* If already saw a match, don't accept other different matches */
+		if (prev && strcmp(prev, dir_prefix) != 0)
+			continue;
+
+		if (DEBUG)
+			fprintf(stderr, "checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
+
+		/* If it's not a prefix match, continue searching */
+		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'))
+		) {
+			continue;
+		}
+
+		/* Path match found */
+		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 *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;
+				}
+
+				encrypted = pw_encrypt(
+					user_and_passwd + user_len_p1 /* cleartext pwd from user */,
+					md5_passwd /*salt */, 1 /* cleanup */);
+				r = strcmp(encrypted, md5_passwd);
+				free(encrypted);
+				if (r == 0)
+					goto set_remoteuser_var; /* Ok */
+				continue;
+			}
+		}
+
+		/* Comparing plaintext "user:pass" in one go */
+		if (strcmp(cur->after_colon, user_and_passwd) == 0) {
+ set_remoteuser_var:
+			remoteuser = xstrndup(user_and_passwd,
+					strchrnul(user_and_passwd, ':') - user_and_passwd);
+			return 1; /* Ok */
+		}
+	} /* for */
+
+	/* 0(bad) if prev is set: matches were found but passwd was wrong */
+	return (prev == NULL);
+}
+#endif  /* FEATURE_HTTPD_BASIC_AUTH */
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+static Htaccess_Proxy *find_proxy_entry(const char *url)
+{
+	Htaccess_Proxy *p;
+	for (p = proxy; p; p = p->next) {
+		if (strncmp(url, p->url_from, strlen(p->url_from)) == 0)
+			return p;
+	}
+	return NULL;
+}
+#endif
+
+/*
+ * Handle timeouts
+ */
+static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
+static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
+{
+	send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
+}
+
+/*
+ * Handle an incoming http request and exit.
+ */
+static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) NORETURN;
+static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
+{
+	static const char request_GET[] ALIGN1 = "GET";
+	struct stat sb;
+	char *urlcopy;
+	char *urlp;
+	char *tptr;
+#if ENABLE_FEATURE_HTTPD_CGI
+	static const char request_HEAD[] ALIGN1 = "HEAD";
+	const char *prequest;
+	char *cookie = NULL;
+	char *content_type = NULL;
+	unsigned long length = 0;
+#elif ENABLE_FEATURE_HTTPD_PROXY
+#define prequest request_GET
+	unsigned long length = 0;
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	smallint authorized = -1;
+#endif
+	smallint ip_allowed;
+	char http_major_version;
+#if ENABLE_FEATURE_HTTPD_PROXY
+	char http_minor_version;
+	char *header_buf = header_buf; /* for gcc */
+	char *header_ptr = header_ptr;
+	Htaccess_Proxy *proxy_entry;
+#endif
+
+	/* Allocation of iobuf is postponed until now
+	 * (IOW, server process doesn't need to waste 8k) */
+	iobuf = xmalloc(IOBUF_SIZE);
+
+	rmt_ip = 0;
+	if (fromAddr->u.sa.sa_family == AF_INET) {
+		rmt_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
+	}
+#if ENABLE_FEATURE_IPV6
+	if (fromAddr->u.sa.sa_family == AF_INET6
+	 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
+	 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
+	 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
+		rmt_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
+#endif
+	if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
+		/* NB: can be NULL (user runs httpd -i by hand?) */
+		rmt_ip_str = xmalloc_sockaddr2dotted(&fromAddr->u.sa);
+	}
+	if (verbose) {
+		/* this trick makes -v logging much simpler */
+		if (rmt_ip_str)
+			applet_name = rmt_ip_str;
+		if (verbose > 2)
+			bb_error_msg("connected");
+	}
+
+	/* Install timeout handler. get_line() needs it. */
+	signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
+
+	if (!get_line()) /* EOF or error or empty line */
+		send_headers_and_exit(HTTP_BAD_REQUEST);
+
+	/* Determine type of request (GET/POST) */
+	urlp = strpbrk(iobuf, " \t");
+	if (urlp == NULL)
+		send_headers_and_exit(HTTP_BAD_REQUEST);
+	*urlp++ = '\0';
+#if ENABLE_FEATURE_HTTPD_CGI
+	prequest = request_GET;
+	if (strcasecmp(iobuf, prequest) != 0) {
+		prequest = request_HEAD;
+		if (strcasecmp(iobuf, prequest) != 0) {
+			prequest = "POST";
+			if (strcasecmp(iobuf, prequest) != 0)
+				send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
+		}
+	}
+#else
+	if (strcasecmp(iobuf, request_GET) != 0)
+		send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
+#endif
+	urlp = skip_whitespace(urlp);
+	if (urlp[0] != '/')
+		send_headers_and_exit(HTTP_BAD_REQUEST);
+
+	/* Find end of URL and parse HTTP version, if any */
+	http_major_version = '0';
+	IF_FEATURE_HTTPD_PROXY(http_minor_version = '0';)
+	tptr = strchrnul(urlp, ' ');
+	/* Is it " HTTP/"? */
+	if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) {
+		http_major_version = tptr[6];
+		IF_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];)
+	}
+	*tptr = '\0';
+
+	/* Copy URL from after "GET "/"POST " to stack-allocated char[] */
+	urlcopy = alloca((tptr - urlp) + 2 + strlen(index_page));
+	/*if (urlcopy == NULL)
+	 *	send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/
+	strcpy(urlcopy, urlp);
+	/* NB: urlcopy ptr is never changed after this */
+
+	/* Extract url args if present */
+	g_query = NULL;
+	tptr = strchr(urlcopy, '?');
+	if (tptr) {
+		*tptr++ = '\0';
+		g_query = tptr;
+	}
+
+	/* Decode URL escape sequences */
+	tptr = decodeString(urlcopy, 0);
+	if (tptr == NULL)
+		send_headers_and_exit(HTTP_BAD_REQUEST);
+	if (tptr == urlcopy + 1) {
+		/* '/' or NUL is encoded */
+		send_headers_and_exit(HTTP_NOT_FOUND);
+	}
+
+	/* Canonicalize path */
+	/* Algorithm stolen from libbb bb_simplify_path(),
+	 * but don't strdup, retain trailing slash, protect root */
+	urlp = tptr = urlcopy;
+	do {
+		if (*urlp == '/') {
+			/* skip duplicate (or initial) slash */
+			if (*tptr == '/') {
+				continue;
+			}
+			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 */
+						send_headers_and_exit(HTTP_BAD_REQUEST);
+					while (*--urlp != '/') /* omit previous dir */;
+						continue;
+				}
+			}
+		}
+		*++urlp = *tptr;
+	} while (*++tptr);
+	*++urlp = '\0';       /* terminate after last character */
+
+	/* If URL is a directory, add '/' */
+	if (urlp[-1] != '/') {
+		if (is_directory(urlcopy + 1, 1, NULL)) {
+			found_moved_temporarily = urlcopy;
+		}
+	}
+
+	/* Log it */
+	if (verbose > 1)
+		bb_error_msg("url:%s", urlcopy);
+
+	tptr = urlcopy;
+	ip_allowed = checkPermIP();
+	while (ip_allowed && (tptr = strchr(tptr + 1, '/')) != NULL) {
+		/* have path1/path2 */
+		*tptr = '\0';
+		if (is_directory(urlcopy + 1, 1, NULL)) {
+			/* may have subdir config */
+			parse_conf(urlcopy + 1, SUBDIR_PARSE);
+			ip_allowed = checkPermIP();
+		}
+		*tptr = '/';
+	}
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+	proxy_entry = find_proxy_entry(urlcopy);
+	if (proxy_entry)
+		header_buf = header_ptr = xmalloc(IOBUF_SIZE);
+#endif
+
+	if (http_major_version >= '0') {
+		/* Request was with "... HTTP/nXXX", and n >= 0 */
+
+		/* Read until blank line */
+		while (1) {
+			if (!get_line())
+				break; /* EOF or error or empty line */
+			if (DEBUG)
+				bb_error_msg("header: '%s'", iobuf);
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+			/* We need 2 more bytes for yet another "\r\n" -
+			 * see near fdprintf(proxy_fd...) further below */
+			if (proxy_entry && (header_ptr - header_buf) < IOBUF_SIZE - 2) {
+				int len = strlen(iobuf);
+				if (len > IOBUF_SIZE - (header_ptr - header_buf) - 4)
+					len = IOBUF_SIZE - (header_ptr - header_buf) - 4;
+				memcpy(header_ptr, iobuf, len);
+				header_ptr += len;
+				header_ptr[0] = '\r';
+				header_ptr[1] = '\n';
+				header_ptr += 2;
+			}
+#endif
+
+#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
+			/* Try and do our best to parse more lines */
+			if ((STRNCASECMP(iobuf, "Content-length:") == 0)) {
+				/* extra read only for POST */
+				if (prequest != request_GET
+# if ENABLE_FEATURE_HTTPD_CGI
+				 && prequest != request_HEAD
+# endif
+				) {
+					tptr = skip_whitespace(iobuf + sizeof("Content-length:") - 1);
+					if (!tptr[0])
+						send_headers_and_exit(HTTP_BAD_REQUEST);
+					/* not using strtoul: it ignores leading minus! */
+					length = bb_strtou(tptr, NULL, 10);
+					/* length is "ulong", but we need to pass it to int later */
+					if (errno || length > INT_MAX)
+						send_headers_and_exit(HTTP_BAD_REQUEST);
+				}
+			}
+#endif
+#if ENABLE_FEATURE_HTTPD_CGI
+			else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
+				cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
+			} else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
+				content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));
+			} else if (STRNCASECMP(iobuf, "Referer:") == 0) {
+				referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
+			} else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
+				user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
+			} else if (STRNCASECMP(iobuf, "Host:") == 0) {
+				host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1));
+			} else if (STRNCASECMP(iobuf, "Accept:") == 0) {
+				http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));
+			} else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {
+				http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1));
+			}
+#endif
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+			if (STRNCASECMP(iobuf, "Authorization:") == 0) {
+				/* We only allow Basic credentials.
+				 * It shows up as "Authorization: Basic <user>:<passwd>" where
+				 * "<user>:<passwd>" is base64 encoded.
+				 */
+				tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1);
+				if (STRNCASECMP(tptr, "Basic") != 0)
+					continue;
+				tptr += sizeof("Basic")-1;
+				/* decodeBase64() skips whitespace itself */
+				decodeBase64(tptr);
+				authorized = check_user_passwd(urlcopy, tptr);
+			}
+#endif
+#if ENABLE_FEATURE_HTTPD_RANGES
+			if (STRNCASECMP(iobuf, "Range:") == 0) {
+				/* We know only bytes=NNN-[MMM] */
+				char *s = skip_whitespace(iobuf + sizeof("Range:")-1);
+				if (strncmp(s, "bytes=", 6) == 0) {
+					s += sizeof("bytes=")-1;
+					range_start = BB_STRTOOFF(s, &s, 10);
+					if (s[0] != '-' || range_start < 0) {
+						range_start = 0;
+					} else if (s[1]) {
+						range_end = BB_STRTOOFF(s+1, NULL, 10);
+						if (errno || range_end < range_start)
+							range_start = 0;
+					}
+				}
+			}
+#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 */
+	}
+
+	/* We are done reading headers, disable peer timeout */
+	alarm(0);
+
+	if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0 || !ip_allowed) {
+		/* protect listing [/path]/httpd.conf or IP deny */
+		send_headers_and_exit(HTTP_FORBIDDEN);
+	}
+
+#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
+	/* Case: no "Authorization:" was seen, but page does require passwd.
+	 * Check that with dummy user:pass */
+	if (authorized < 0)
+		authorized = check_user_passwd(urlcopy, ":");
+	if (!authorized)
+		send_headers_and_exit(HTTP_UNAUTHORIZED);
+#endif
+
+	if (found_moved_temporarily) {
+		send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
+	}
+
+#if ENABLE_FEATURE_HTTPD_PROXY
+	if (proxy_entry != NULL) {
+		int proxy_fd;
+		len_and_sockaddr *lsa;
+
+		proxy_fd = socket(AF_INET, SOCK_STREAM, 0);
+		if (proxy_fd < 0)
+			send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+		lsa = host2sockaddr(proxy_entry->host_port, 80);
+		if (lsa == NULL)
+			send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+		if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
+			send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+		fdprintf(proxy_fd, "%s %s%s%s%s HTTP/%c.%c\r\n",
+				prequest, /* GET or POST */
+				proxy_entry->url_to, /* url part 1 */
+				urlcopy + strlen(proxy_entry->url_from), /* url part 2 */
+				(g_query ? "?" : ""), /* "?" (maybe) */
+				(g_query ? g_query : ""), /* query string (maybe) */
+				http_major_version, http_minor_version);
+		header_ptr[0] = '\r';
+		header_ptr[1] = '\n';
+		header_ptr += 2;
+		write(proxy_fd, header_buf, header_ptr - header_buf);
+		free(header_buf); /* on the order of 8k, free it */
+		cgi_io_loop_and_exit(proxy_fd, proxy_fd, length);
+	}
+#endif
+
+	tptr = urlcopy + 1;      /* skip first '/' */
+
+#if ENABLE_FEATURE_HTTPD_CGI
+	if (strncmp(tptr, "cgi-bin/", 8) == 0) {
+		if (tptr[8] == '\0') {
+			/* protect listing "cgi-bin/" */
+			send_headers_and_exit(HTTP_FORBIDDEN);
+		}
+		send_cgi_and_exit(urlcopy, prequest, length, cookie, content_type);
+	}
+#endif
+
+	if (urlp[-1] == '/')
+		strcpy(urlp, index_page);
+	if (stat(tptr, &sb) == 0) {
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+		char *suffix = strrchr(tptr, '.');
+		if (suffix) {
+			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);
+				}
+			}
+		}
+#endif
+		file_size = sb.st_size;
+		last_mod = sb.st_mtime;
+	}
+#if ENABLE_FEATURE_HTTPD_CGI
+	else if (urlp[-1] == '/') {
+		/* 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);
+		}
+	}
+	/* else fall through to send_file, it errors out if open fails: */
+
+	if (prequest != request_GET && prequest != request_HEAD) {
+		/* POST for files does not make sense */
+		send_headers_and_exit(HTTP_NOT_IMPLEMENTED);
+	}
+	send_file_and_exit(tptr,
+		(prequest != request_HEAD ? SEND_HEADERS_AND_BODY : SEND_HEADERS)
+	);
+#else
+	send_file_and_exit(tptr, SEND_HEADERS_AND_BODY);
+#endif
+}
+
+/*
+ * The main http server function.
+ * Given a socket, listen for new connections and farm out
+ * the processing as a [v]forked process.
+ * Never returns.
+ */
+#if BB_MMU
+static void mini_httpd(int server_socket) NORETURN;
+static void mini_httpd(int server_socket)
+{
+	/* NB: it's best to not use xfuncs in this loop before fork().
+	 * Otherwise server may die on transient errors (temporary
+	 * out-of-memory condition, etc), which is Bad(tm).
+	 * Try to do any dangerous calls after fork.
+	 */
+	while (1) {
+		int n;
+		len_and_sockaddr fromAddr;
+
+		/* 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));
+
+		if (fork() == 0) {
+			/* child */
+			/* Do not reload config on HUP */
+			signal(SIGHUP, SIG_IGN);
+			close(server_socket);
+			xmove_fd(n, 0);
+			xdup2(0, 1);
+
+			handle_incoming_and_exit(&fromAddr);
+		}
+		/* parent, or fork failed */
+		close(n);
+	} /* while (1) */
+	/* never reached */
+}
+#else
+static void mini_httpd_nommu(int server_socket, int argc, char **argv) NORETURN;
+static void mini_httpd_nommu(int server_socket, int argc, char **argv)
+{
+	char *argv_copy[argc + 2];
+
+	argv_copy[0] = argv[0];
+	argv_copy[1] = (char*)"-i";
+	memcpy(&argv_copy[2], &argv[1], argc * sizeof(argv[0]));
+
+	/* NB: it's best to not use xfuncs in this loop before vfork().
+	 * Otherwise server may die on transient errors (temporary
+	 * out-of-memory condition, etc), which is Bad(tm).
+	 * Try to do any dangerous calls after fork.
+	 */
+	while (1) {
+		int n;
+		len_and_sockaddr fromAddr;
+
+		/* 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));
+
+		if (vfork() == 0) {
+			/* child */
+			/* Do not reload config on HUP */
+			signal(SIGHUP, SIG_IGN);
+			close(server_socket);
+			xmove_fd(n, 0);
+			xdup2(0, 1);
+
+			/* Run a copy of ourself in inetd mode */
+			re_exec(argv_copy);
+		}
+		/* parent, or vfork failed */
+		close(n);
+	} /* while (1) */
+	/* never reached */
+}
+#endif
+
+/*
+ * Process a HTTP connection on stdin/out.
+ * Never returns.
+ */
+static void mini_httpd_inetd(void) NORETURN;
+static void mini_httpd_inetd(void)
+{
+	len_and_sockaddr fromAddr;
+
+	memset(&fromAddr, 0, sizeof(fromAddr));
+	fromAddr.len = LSA_SIZEOF_SA;
+	/* NB: can fail if user runs it by hand and types in http cmds */
+	getpeername(0, &fromAddr.u.sa, &fromAddr.len);
+	handle_incoming_and_exit(&fromAddr);
+}
+
+static void sighup_handler(int sig UNUSED_PARAM)
+{
+	parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE);
+}
+
+enum {
+	c_opt_config_file = 0,
+	d_opt_decode_url,
+	h_opt_home_httpd,
+	IF_FEATURE_HTTPD_ENCODE_URL_STR(e_opt_encode_url,)
+	IF_FEATURE_HTTPD_BASIC_AUTH(    r_opt_realm     ,)
+	IF_FEATURE_HTTPD_AUTH_MD5(      m_opt_md5       ,)
+	IF_FEATURE_HTTPD_SETUID(        u_opt_setuid    ,)
+	p_opt_port      ,
+	p_opt_inetd     ,
+	p_opt_foreground,
+	p_opt_verbose   ,
+	OPT_CONFIG_FILE = 1 << c_opt_config_file,
+	OPT_DECODE_URL  = 1 << d_opt_decode_url,
+	OPT_HOME_HTTPD  = 1 << h_opt_home_httpd,
+	OPT_ENCODE_URL  = IF_FEATURE_HTTPD_ENCODE_URL_STR((1 << e_opt_encode_url)) + 0,
+	OPT_REALM       = IF_FEATURE_HTTPD_BASIC_AUTH(    (1 << r_opt_realm     )) + 0,
+	OPT_MD5         = IF_FEATURE_HTTPD_AUTH_MD5(      (1 << m_opt_md5       )) + 0,
+	OPT_SETUID      = IF_FEATURE_HTTPD_SETUID(        (1 << u_opt_setuid    )) + 0,
+	OPT_PORT        = 1 << p_opt_port,
+	OPT_INETD       = 1 << p_opt_inetd,
+	OPT_FOREGROUND  = 1 << p_opt_foreground,
+	OPT_VERBOSE     = 1 << p_opt_verbose,
+};
+
+
+int httpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int httpd_main(int argc UNUSED_PARAM, char **argv)
+{
+	int server_socket = server_socket; /* for gcc */
+	unsigned opt;
+	char *url_for_decode;
+	IF_FEATURE_HTTPD_ENCODE_URL_STR(const char *url_for_encode;)
+	IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
+	IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
+	IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
+
+	INIT_G();
+
+#if ENABLE_LOCALE_SUPPORT
+	/* Undo busybox.c: we want to speak English in http (dates etc) */
+	setlocale(LC_TIME, "C");
+#endif
+
+	home_httpd = xrealloc_getcwd_or_warn(NULL);
+	/* -v counts, -i implies -f */
+	opt_complementary = "vv:if";
+	/* We do not "absolutize" path given by -h (home) opt.
+	 * If user gives relative path in -h,
+	 * $SCRIPT_FILENAME will not be set. */
+	opt = getopt32(argv, "c:d:h:"
+			IF_FEATURE_HTTPD_ENCODE_URL_STR("e:")
+			IF_FEATURE_HTTPD_BASIC_AUTH("r:")
+			IF_FEATURE_HTTPD_AUTH_MD5("m:")
+			IF_FEATURE_HTTPD_SETUID("u:")
+			"p:ifv",
+			&opt_c_configFile, &url_for_decode, &home_httpd
+			IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
+			IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
+			IF_FEATURE_HTTPD_AUTH_MD5(, &pass)
+			IF_FEATURE_HTTPD_SETUID(, &s_ugid)
+			, &bind_addr_or_port
+			, &verbose
+		);
+	if (opt & OPT_DECODE_URL) {
+		fputs(decodeString(url_for_decode, 1), stdout);
+		return 0;
+	}
+#if ENABLE_FEATURE_HTTPD_ENCODE_URL_STR
+	if (opt & OPT_ENCODE_URL) {
+		fputs(encodeString(url_for_encode), stdout);
+		return 0;
+	}
+#endif
+#if ENABLE_FEATURE_HTTPD_AUTH_MD5
+	if (opt & OPT_MD5) {
+		char salt[sizeof("$1$XXXXXXXX")];
+		salt[0] = '$';
+		salt[1] = '1';
+		salt[2] = '$';
+		crypt_make_salt(salt + 3, 4);
+		puts(pw_encrypt(pass, salt, 1));
+		return 0;
+	}
+#endif
+#if ENABLE_FEATURE_HTTPD_SETUID
+	if (opt & OPT_SETUID) {
+		xget_uidgid(&ugid, s_ugid);
+	}
+#endif
+
+#if !BB_MMU
+	if (!(opt & OPT_FOREGROUND)) {
+		bb_daemonize_or_rexec(0, argv); /* don't change current directory */
+	}
+#endif
+
+	xchdir(home_httpd);
+	if (!(opt & OPT_INETD)) {
+		signal(SIGCHLD, SIG_IGN);
+		server_socket = openServer();
+#if ENABLE_FEATURE_HTTPD_SETUID
+		/* drop privileges */
+		if (opt & OPT_SETUID) {
+			if (ugid.gid != (gid_t)-1) {
+				if (setgroups(1, &ugid.gid) == -1)
+					bb_perror_msg_and_die("setgroups");
+				xsetgid(ugid.gid);
+			}
+			xsetuid(ugid.uid);
+		}
+#endif
+	}
+
+#if 0
+	/* User can do it himself: 'env - PATH="$PATH" httpd'
+	 * We don't do it because we don't want to screw users
+	 * which want to do
+	 * 'env - VAR1=val1 VAR2=val2 httpd'
+	 * and have VAR1 and VAR2 values visible in their CGIs.
+	 * Besides, it is also smaller. */
+	{
+		char *p = getenv("PATH");
+		/* env strings themself are not freed, no need to xstrdup(p): */
+		clearenv();
+		if (p)
+			putenv(p - 5);
+//		if (!(opt & OPT_INETD))
+//			setenv_long("SERVER_PORT", ???);
+	}
+#endif
+
+	parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
+	if (!(opt & OPT_INETD))
+		signal(SIGHUP, sighup_handler);
+
+	xfunc_error_retval = 0;
+	if (opt & OPT_INETD)
+		mini_httpd_inetd();
+#if BB_MMU
+	if (!(opt & OPT_FOREGROUND))
+		bb_daemonize(0); /* don't change current directory */
+	mini_httpd(server_socket); /* never returns */
+#else
+	mini_httpd_nommu(server_socket, argc, argv); /* never returns */
+#endif
+	/* return 0; */
+}
diff --git a/busybox-1.19.3/networking/httpd_indexcgi.c b/busybox-1.19.3/networking/httpd_indexcgi.c
new file mode 100644
index 0000000..7e0225e
--- /dev/null
+++ b/busybox-1.19.3/networking/httpd_indexcgi.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * This program is a CGI application. It outputs directory index page.
+ * Put it into cgi-bin/index.cgi and chmod 0755.
+ */
+
+/* Build a-la
+i486-linux-uclibc-gcc \
+-static -static-libgcc \
+-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \
+-Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \
+-Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \
+-Wmissing-prototypes -Wmissing-declarations \
+-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \
+-ffunction-sections -fdata-sections -fno-guess-branch-probability \
+-funsigned-char \
+-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \
+-march=i386 -mpreferred-stack-boundary=2 \
+-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \
+httpd_indexcgi.c -o index.cgi
+*/
+
+/* We don't use printf, as it pulls in >12 kb of code from uclibc (i386). */
+/* Currently malloc machinery is the biggest part of libc we pull in. */
+/* We have only one realloc and one strdup, any idea how to do without? */
+
+/* Size (i386, static uclibc, approximate):
+ *   text    data     bss     dec     hex filename
+ *  13036      44    3052   16132    3f04 index.cgi
+ *   2576       4    2048    4628    1214 index.cgi.o
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <time.h>
+
+/* Appearance of the table is controlled by style sheet *ONLY*,
+ * formatting code uses <TAG class=CLASS> to apply style
+ * to elements. Edit stylesheet to your liking and recompile. */
+
+#define STYLE_STR \
+"<style>"                                               "\n"\
+"table {"                                               "\n"\
+  "width:100%;"                                         "\n"\
+  "background-color:#fff5ee;"                           "\n"\
+  "border-width:1px;" /* 1px 1px 1px 1px; */            "\n"\
+  "border-spacing:2px;"                                 "\n"\
+  "border-style:solid;" /* solid solid solid solid; */  "\n"\
+  "border-color:black;" /* black black black black; */  "\n"\
+  "border-collapse:collapse;"                           "\n"\
+"}"                                                     "\n"\
+"th {"                                                  "\n"\
+  "border-width:1px;" /* 1px 1px 1px 1px; */            "\n"\
+  "padding:1px;" /* 1px 1px 1px 1px; */                 "\n"\
+  "border-style:solid;" /* solid solid solid solid; */  "\n"\
+  "border-color:black;" /* black black black black; */  "\n"\
+"}"                                                     "\n"\
+"td {"                                                  "\n"\
+             /* top right bottom left */                    \
+  "border-width:0px 1px 0px 1px;"                       "\n"\
+  "padding:1px;" /* 1px 1px 1px 1px; */                 "\n"\
+  "border-style:solid;" /* solid solid solid solid; */  "\n"\
+  "border-color:black;" /* black black black black; */  "\n"\
+  "white-space:nowrap;"                                 "\n"\
+"}"                                                     "\n"\
+"tr.hdr { background-color:#eee5de; }"                  "\n"\
+"tr.o { background-color:#ffffff; }"                    "\n"\
+/* tr.e { ... } - for even rows (currently none) */         \
+"tr.foot { background-color:#eee5de; }"                 "\n"\
+"th.cnt { text-align:left; }"                           "\n"\
+"th.sz { text-align:right; }"                           "\n"\
+"th.dt { text-align:right; }"                           "\n"\
+"td.sz { text-align:right; }"                           "\n"\
+"td.dt { text-align:right; }"                           "\n"\
+"col.nm { width:98%; }"                                 "\n"\
+"col.sz { width:1%; }"                                  "\n"\
+"col.dt { width:1%; }"                                  "\n"\
+"</style>"                                              "\n"\
+
+typedef struct dir_list_t {
+	char  *dl_name;
+	mode_t dl_mode;
+	off_t  dl_size;
+	time_t dl_mtime;
+} dir_list_t;
+
+static int compare_dl(dir_list_t *a, dir_list_t *b)
+{
+	/* ".." is 'less than' any other dir entry */
+	if (strcmp(a->dl_name, "..") == 0) {
+		return -1;
+	}
+	if (strcmp(b->dl_name, "..") == 0) {
+		return 1;
+	}
+	if (S_ISDIR(a->dl_mode) != S_ISDIR(b->dl_mode)) {
+		/* 1 if b is a dir (and thus a is 'after' b, a > b),
+		 * else -1 (a < b) */
+		return (S_ISDIR(b->dl_mode) != 0) ? 1 : -1;
+	}
+	return strcmp(a->dl_name, b->dl_name);
+}
+
+static char buffer[2*1024 > sizeof(STYLE_STR) ? 2*1024 : sizeof(STYLE_STR)];
+static char *dst = buffer;
+enum {
+	BUFFER_SIZE = sizeof(buffer),
+	HEADROOM = 64,
+};
+
+/* After this call, you have at least size + HEADROOM bytes available
+ * ahead of dst */
+static void guarantee(int size)
+{
+	if (buffer + (BUFFER_SIZE-HEADROOM) - dst >= size)
+		return;
+	write(STDOUT_FILENO, buffer, dst - buffer);
+	dst = buffer;
+}
+
+/* NB: formatters do not store terminating NUL! */
+
+/* HEADROOM bytes are available after dst after this call */
+static void fmt_str(/*char *dst,*/ const char *src)
+{
+	unsigned len = strlen(src);
+	guarantee(len);
+	memcpy(dst, src, len);
+	dst += len;
+}
+
+/* HEADROOM bytes after dst are available after this call */
+static void fmt_url(/*char *dst,*/ const char *name)
+{
+	while (*name) {
+		unsigned c = *name++;
+		guarantee(3);
+		*dst = c;
+		if ((c - '0') > 9 /* not a digit */
+		 && ((c|0x20) - 'a') > ('z' - 'a') /* not A-Z or a-z */
+		 && !strchr("._-+@", c)
+		) {
+			*dst++ = '%';
+			*dst++ = "0123456789ABCDEF"[c >> 4];
+			*dst = "0123456789ABCDEF"[c & 0xf];
+		}
+		dst++;
+	}
+}
+
+/* HEADROOM bytes are available after dst after this call */
+static void fmt_html(/*char *dst,*/ const char *name)
+{
+	while (*name) {
+		char c = *name++;
+		if (c == '<')
+			fmt_str("&lt;");
+		else if (c == '>')
+			fmt_str("&gt;");
+		else if (c == '&') {
+			fmt_str("&amp;");
+		} else {
+			guarantee(1);
+			*dst++ = c;
+			continue;
+		}
+	}
+}
+
+/* HEADROOM bytes are available after dst after this call */
+static void fmt_ull(/*char *dst,*/ unsigned long long n)
+{
+	char buf[sizeof(n)*3 + 2];
+	char *p;
+
+	p = buf + sizeof(buf) - 1;
+	*p = '\0';
+	do {
+		*--p = (n % 10) + '0';
+		n /= 10;
+	} while (n);
+	fmt_str(/*dst,*/ p);
+}
+
+/* Does not call guarantee - eats into headroom instead */
+static void fmt_02u(/*char *dst,*/ unsigned n)
+{
+	/* n %= 100; - not needed, callers don't pass big n */
+	dst[0] = (n / 10) + '0';
+	dst[1] = (n % 10) + '0';
+	dst += 2;
+}
+
+/* Does not call guarantee - eats into headroom instead */
+static void fmt_04u(/*char *dst,*/ unsigned n)
+{
+	/* n %= 10000; - not needed, callers don't pass big n */
+	fmt_02u(n / 100);
+	fmt_02u(n % 100);
+}
+
+int main(int argc, char *argv[])
+{
+	dir_list_t *dir_list;
+	dir_list_t *cdir;
+	unsigned dir_list_count;
+	unsigned count_dirs;
+	unsigned count_files;
+	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
+	) {
+		return 1;
+	}
+
+	if (chdir("..")
+	 || (QUERY_STRING[1] && chdir(QUERY_STRING + 1))
+	) {
+		return 1;
+	}
+
+	dirp = opendir(".");
+	if (!dirp)
+		return 1;
+	dir_list = NULL;
+	dir_list_count = 0;
+	while (1) {
+		struct dirent *dp;
+		struct stat sb;
+
+		dp = readdir(dirp);
+		if (!dp)
+			break;
+		if (dp->d_name[0] == '.' && !dp->d_name[1])
+			continue;
+		if (stat(dp->d_name, &sb) != 0)
+			continue;
+		dir_list = realloc(dir_list, (dir_list_count + 1) * sizeof(dir_list[0]));
+		dir_list[dir_list_count].dl_name = strdup(dp->d_name);
+		dir_list[dir_list_count].dl_mode = sb.st_mode;
+		dir_list[dir_list_count].dl_size = sb.st_size;
+		dir_list[dir_list_count].dl_mtime = sb.st_mtime;
+		dir_list_count++;
+	}
+	closedir(dirp);
+
+	qsort(dir_list, dir_list_count, sizeof(dir_list[0]), (void*)compare_dl);
+
+	fmt_str(
+		"" /* Additional headers (currently none) */
+		"\r\n" /* Mandatory empty line after headers */
+		"<html><head><title>Index of ");
+	/* Guard against directories with &, > etc */
+	fmt_html(QUERY_STRING);
+	fmt_str(
+		"</title>\n"
+		STYLE_STR
+		"</head>" "\n"
+		"<body>" "\n"
+		"<h1>Index of ");
+	fmt_html(QUERY_STRING);
+	fmt_str(
+		"</h1>" "\n"
+		"<table>" "\n"
+		"<col class=nm><col class=sz><col class=dt>" "\n"
+		"<tr class=hdr><th class=cnt>Name<th class=sz>Size<th class=dt>Last modified" "\n");
+
+	odd = 0;
+	count_dirs = 0;
+	count_files = 0;
+	size_total = 0;
+	cdir = dir_list;
+	while (dir_list_count--) {
+		struct tm *ptm;
+
+		if (S_ISDIR(cdir->dl_mode)) {
+			count_dirs++;
+		} else if (S_ISREG(cdir->dl_mode)) {
+			count_files++;
+			size_total += cdir->dl_size;
+		} else
+			goto next;
+
+		fmt_str("<tr class=");
+		*dst++ = (odd ? 'o' : 'e');
+		fmt_str("><td class=nm><a href='");
+		fmt_url(cdir->dl_name); /* %20 etc */
+		if (S_ISDIR(cdir->dl_mode))
+			*dst++ = '/';
+		fmt_str("'>");
+		fmt_html(cdir->dl_name); /* &lt; etc */
+		if (S_ISDIR(cdir->dl_mode))
+			*dst++ = '/';
+		fmt_str("</a><td class=sz>");
+		if (S_ISREG(cdir->dl_mode))
+			fmt_ull(cdir->dl_size);
+		fmt_str("<td class=dt>");
+		ptm = gmtime(&cdir->dl_mtime);
+		fmt_04u(1900 + ptm->tm_year); *dst++ = '-';
+		fmt_02u(ptm->tm_mon + 1); *dst++ = '-';
+		fmt_02u(ptm->tm_mday); *dst++ = ' ';
+		fmt_02u(ptm->tm_hour); *dst++ = ':';
+		fmt_02u(ptm->tm_min); *dst++ = ':';
+		fmt_02u(ptm->tm_sec);
+		*dst++ = '\n';
+
+		odd = 1 - odd;
+ next:
+		cdir++;
+	}
+
+	fmt_str("<tr class=foot><th class=cnt>Files: ");
+	fmt_ull(count_files);
+	/* count_dirs - 1: we don't want to count ".." */
+	fmt_str(", directories: ");
+	fmt_ull(count_dirs - 1);
+	fmt_str("<th class=sz>");
+	fmt_ull(size_total);
+	fmt_str("<th class=dt>\n");
+	/* "</table></body></html>" - why bother? */
+	guarantee(BUFFER_SIZE * 2); /* flush */
+
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/httpd_post_upload.txt b/busybox-1.19.3/networking/httpd_post_upload.txt
new file mode 100644
index 0000000..9d504f4
--- /dev/null
+++ b/busybox-1.19.3/networking/httpd_post_upload.txt
@@ -0,0 +1,65 @@
+POST upload example:
+
+post_upload.htm
+===============
+<html>
+<body>
+<form action=/cgi-bin/post_upload.cgi method=post enctype=multipart/form-data>
+File to upload: <input type=file name=file1> <input type=submit>
+</form>
+
+
+post_upload.cgi
+===============
+#!/bin/sh
+
+# POST upload format:
+# -----------------------------29995809218093749221856446032^M
+# Content-Disposition: form-data; name="file1"; filename="..."^M
+# Content-Type: application/octet-stream^M
+# ^M    <--------- headers end with empty line
+# file contents
+# file contents
+# file contents
+# ^M    <--------- extra empty line
+# -----------------------------29995809218093749221856446032--^M
+
+file=/tmp/$$-$RANDOM
+
+CR=`printf '\r'`
+
+# CGI output must start with at least empty line (or headers)
+printf '\r\n'
+
+IFS="$CR"
+read -r delim_line
+IFS=""
+
+while read -r line; do
+    test x"$line" = x"" && break
+    test x"$line" = x"$CR" && break
+done
+
+cat >"$file"
+
+# We need to delete the tail of "\r\ndelim_line--\r\n"
+tail_len=$((${#delim_line} + 6))
+
+# Get and check file size
+filesize=`stat -c"%s" "$file"`
+test "$filesize" -lt "$tail_len" && exit 1
+
+# Check that tail is correct
+dd if="$file" skip=$((filesize - tail_len)) bs=1 count=1000 >"$file.tail" 2>/dev/null
+printf "\r\n%s--\r\n" "$delim_line" >"$file.tail.expected"
+if ! diff -q "$file.tail" "$file.tail.expected" >/dev/null; then
+    printf "<html>\n<body>\nMalformed file upload"
+    exit 1
+fi
+rm "$file.tail"
+rm "$file.tail.expected"
+
+# Truncate the file
+dd of="$file" seek=$((filesize - tail_len)) bs=1 count=0 >/dev/null 2>/dev/null
+
+printf "<html>\n<body>\nFile upload has been accepted"
diff --git a/busybox-1.19.3/networking/httpd_ssi.c b/busybox-1.19.3/networking/httpd_ssi.c
new file mode 100644
index 0000000..cfe64eb
--- /dev/null
+++ b/busybox-1.19.3/networking/httpd_ssi.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2009 Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * This program is a CGI application. It processes server-side includes:
+ * <!--#include file="file.html" -->
+ *
+ * Usage: put these lines in httpd.conf:
+ *
+ * *.html:/bin/httpd_ssi
+ * *.htm:/bin/httpd_ssi
+ */
+
+/* Build a-la
+i486-linux-uclibc-gcc \
+-static -static-libgcc \
+-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 \
+-Wall -Wshadow -Wwrite-strings -Wundef -Wstrict-prototypes -Werror \
+-Wold-style-definition -Wdeclaration-after-statement -Wno-pointer-sign \
+-Wmissing-prototypes -Wmissing-declarations \
+-Os -fno-builtin-strlen -finline-limit=0 -fomit-frame-pointer \
+-ffunction-sections -fdata-sections -fno-guess-branch-probability \
+-funsigned-char \
+-falign-functions=1 -falign-jumps=1 -falign-labels=1 -falign-loops=1 \
+-march=i386 -mpreferred-stack-boundary=2 \
+-Wl,-Map -Wl,link.map -Wl,--warn-common -Wl,--sort-common -Wl,--gc-sections \
+httpd_ssi.c -o httpd_ssi
+*/
+
+/* Size (i386, static uclibc, approximate):
+ * text    data     bss     dec     hex filename
+ * 9487     160   68552   78199   13177 httpd_ssi
+ *
+ * Note: it wouldn't be too hard to get rid of stdio and strdup,
+ * (especially that fgets() mangles NULs...)
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <time.h>
+
+static char* skip_whitespace(char *s)
+{
+	while (*s == ' ' || *s == '\t') ++s;
+
+	return s;
+}
+
+static char line[64 * 1024];
+
+static void process_includes(const char *filename)
+{
+	int curdir_fd;
+	char *end;
+	FILE *fp = fopen(filename, "r");
+	if (!fp)
+		exit(1);
+
+	/* Ensure that nested includes are relative:
+	 * if we include a/1.htm and it includes b/2.htm,
+	 * we need to include a/b/2.htm, not b/2.htm
+	 */
+	curdir_fd = -1;
+	end = strrchr(filename, '/');
+	if (end) {
+		curdir_fd = open(".", O_RDONLY);
+		/* *end = '\0' would mishandle "/file.htm" */
+		end[1] = '\0';
+		chdir(filename);
+	}
+
+#define INCLUDE "<!--#include"
+	while (fgets(line, sizeof(line), fp)) {
+		unsigned preceding_len;
+		char *include_directive;
+
+		include_directive = strstr(line, INCLUDE);
+		if (!include_directive) {
+			fputs(line, stdout);
+			continue;
+		}
+		preceding_len = include_directive - line;
+		if (memchr(line, '\"', preceding_len)
+		 || memchr(line, '\'', preceding_len)
+		) {
+			/* INCLUDE string may be inside "str" or 'str',
+			 * ignore it */
+			fputs(line, stdout);
+			continue;
+		}
+		/* Small bug: we accept #includefile="file" too */
+		include_directive = skip_whitespace(include_directive + sizeof(INCLUDE)-1);
+		if (strncmp(include_directive, "file=\"", 6) != 0) {
+			/* "<!--#include virtual=..."? - not supported */
+			fputs(line, stdout);
+			continue;
+		}
+		include_directive += 6; /* now it points to file name */
+		end = strchr(include_directive, '\"');
+		if (!end) {
+			fputs(line, stdout);
+			continue;
+		}
+		/* We checked that this is a valid include directive */
+
+		/* Print everything before directive */
+		if (preceding_len) {
+			line[preceding_len] = '\0';
+			fputs(line, stdout);
+		}
+		/* Save everything after directive */
+		*end++ = '\0';
+		end = strchr(end, '>');
+		if (end)
+			end = strdup(end + 1);
+
+		/* FIXME:
+		 * (1) are relative paths with /../ etc ok?
+		 * (2) what to do with absolute paths?
+		 * are they relative to doc root or to real root?
+		 */
+		process_includes(include_directive);
+
+		/* Print everything after directive */
+	        if (end) {
+			fputs(end, stdout);
+			free(end);
+		}
+	}
+	if (curdir_fd >= 0)
+		fchdir(curdir_fd);
+	fclose(fp);
+}
+
+int main(int argc, char *argv[])
+{
+	if (!argv[1])
+		return 1;
+
+	/* Seen from busybox.net's Apache:
+	 * HTTP/1.1 200 OK
+	 * Date: Thu, 10 Sep 2009 18:23:28 GMT
+	 * Server: Apache
+	 * Accept-Ranges: bytes
+	 * Connection: close
+	 * Content-Type: text/html
+	 */
+	fputs(
+		/* "Date: Thu, 10 Sep 2009 18:23:28 GMT\r\n" */
+		/* "Server: Apache\r\n" */
+		/* "Accept-Ranges: bytes\r\n" - do we really accept bytes?! */
+		"Connection: close\r\n"
+		"Content-Type: text/html\r\n"
+		"\r\n",
+		stdout
+	);
+	process_includes(argv[1]);
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/ifconfig.c b/busybox-1.19.3/networking/ifconfig.c
new file mode 100644
index 0000000..b6604f5
--- /dev/null
+++ b/busybox-1.19.3/networking/ifconfig.c
@@ -0,0 +1,554 @@
+/* vi: set sw=4 ts=4: */
+/* ifconfig
+ *
+ * Similar to the standard Unix ifconfig, but with only the necessary
+ * parts for AF_INET, and without any printing of if info (for now).
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ *
+ * 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 source tree.
+ */
+
+/*
+ * Heavily modified by Manuel Novoa III       Mar 6, 2001
+ *
+ * From initial port to busybox, removed most of the redundancy by
+ * converting to a table-driven approach.  Added several (optional)
+ * args missing from initial port.
+ *
+ * Still missing:  media, tunnel.
+ *
+ * 2002-04-20
+ * 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>
+#ifdef HAVE_NET_ETHERNET_H
+# include <net/ethernet.h>
+#endif
+
+#if ENABLE_FEATURE_IFCONFIG_SLIP
+# include <net/if_slip.h>
+#endif
+
+/* I don't know if this is needed for busybox or not.  Anyone? */
+#define QUESTIONABLE_ALIAS_CASE
+
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+# define SIOCSIFTXQLEN      0x8943
+# define SIOCGIFTXQLEN      0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+# define ifr_qlen        ifr_ifru.ifru_mtu
+#endif
+
+#ifndef IFF_DYNAMIC
+# define IFF_DYNAMIC     0x8000	/* dialup device with changing addresses */
+#endif
+
+#if ENABLE_FEATURE_IPV6
+struct in6_ifreq {
+	struct in6_addr ifr6_addr;
+	uint32_t ifr6_prefixlen;
+	int ifr6_ifindex;
+};
+#endif
+
+/*
+ * Here are the bit masks for the "flags" member of struct options below.
+ * N_ signifies no arg prefix; M_ signifies arg prefixed by '-'.
+ * CLR clears the flag; SET sets the flag; ARG signifies (optional) arg.
+ */
+#define N_CLR            0x01
+#define M_CLR            0x02
+#define N_SET            0x04
+#define M_SET            0x08
+#define N_ARG            0x10
+#define M_ARG            0x20
+
+#define M_MASK           (M_CLR | M_SET | M_ARG)
+#define N_MASK           (N_CLR | N_SET | N_ARG)
+#define SET_MASK         (N_SET | M_SET)
+#define CLR_MASK         (N_CLR | M_CLR)
+#define SET_CLR_MASK     (SET_MASK | CLR_MASK)
+#define ARG_MASK         (M_ARG | N_ARG)
+
+/*
+ * Here are the bit masks for the "arg_flags" member of struct options below.
+ */
+
+/*
+ * cast type:
+ *   00 int
+ *   01 char *
+ *   02 HOST_COPY in_ether
+ *   03 HOST_COPY INET_resolve
+ */
+#define A_CAST_TYPE      0x03
+/*
+ * map type:
+ *   00 not a map type (mem_start, io_addr, irq)
+ *   04 memstart (unsigned long)
+ *   08 io_addr  (unsigned short)
+ *   0C irq      (unsigned char)
+ */
+#define A_MAP_TYPE       0x0C
+#define A_ARG_REQ        0x10	/* Set if an arg is required. */
+#define A_NETMASK        0x20	/* Set if netmask (check for multiple sets). */
+#define A_SET_AFTER      0x40	/* Set a flag at the end. */
+#define A_COLON_CHK      0x80	/* Is this needed?  See below. */
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+#define A_HOSTNAME      0x100	/* Set if it is ip addr. */
+#define A_BROADCAST     0x200	/* Set if it is broadcast addr. */
+#else
+#define A_HOSTNAME          0
+#define A_BROADCAST         0
+#endif
+
+/*
+ * These defines are for dealing with the A_CAST_TYPE field.
+ */
+#define A_CAST_CHAR_PTR  0x01
+#define A_CAST_RESOLVE   0x01
+#define A_CAST_HOST_COPY 0x02
+#define A_CAST_HOST_COPY_IN_ETHER    A_CAST_HOST_COPY
+#define A_CAST_HOST_COPY_RESOLVE     (A_CAST_HOST_COPY | A_CAST_RESOLVE)
+
+/*
+ * These defines are for dealing with the A_MAP_TYPE field.
+ */
+#define A_MAP_ULONG      0x04	/* memstart */
+#define A_MAP_USHORT     0x08	/* io_addr */
+#define A_MAP_UCHAR      0x0C	/* irq */
+
+/*
+ * Define the bit masks signifying which operations to perform for each arg.
+ */
+
+#define ARG_METRIC       (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MTU          (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_TXQUEUELEN   (A_ARG_REQ /*| A_CAST_INT*/)
+#define ARG_MEM_START    (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IO_ADDR      (A_ARG_REQ | A_MAP_ULONG)
+#define ARG_IRQ          (A_ARG_REQ | A_MAP_UCHAR)
+#define ARG_DSTADDR      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE)
+#define ARG_NETMASK      (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_NETMASK)
+#define ARG_BROADCAST    (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_BROADCAST)
+#define ARG_HW           (A_ARG_REQ | A_CAST_HOST_COPY_IN_ETHER)
+#define ARG_POINTOPOINT  (A_ARG_REQ | A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER)
+#define ARG_KEEPALIVE    (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_OUTFILL      (A_ARG_REQ | A_CAST_CHAR_PTR)
+#define ARG_HOSTNAME     (A_CAST_HOST_COPY_RESOLVE | A_SET_AFTER | A_COLON_CHK | A_HOSTNAME)
+#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;
+	unsigned short ifr_offset;
+};
+
+struct options {
+	const char *name;
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+	const unsigned int flags:6;
+	const unsigned int arg_flags:10;
+#else
+	const unsigned char flags;
+	const unsigned char arg_flags;
+#endif
+	const unsigned short selector;
+};
+
+#define ifreq_offsetof(x)  offsetof(struct ifreq, x)
+
+static const struct arg1opt Arg1Opt[] = {
+	{ "SIFMETRIC",  SIOCSIFMETRIC,  ifreq_offsetof(ifr_metric) },
+	{ "SIFMTU",     SIOCSIFMTU,     ifreq_offsetof(ifr_mtu) },
+	{ "SIFTXQLEN",  SIOCSIFTXQLEN,  ifreq_offsetof(ifr_qlen) },
+	{ "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) },
+	{ "SIFNETMASK", SIOCSIFNETMASK, ifreq_offsetof(ifr_netmask) },
+	{ "SIFBRDADDR", SIOCSIFBRDADDR, ifreq_offsetof(ifr_broadaddr) },
+#if ENABLE_FEATURE_IFCONFIG_HW
+	{ "SIFHWADDR",  SIOCSIFHWADDR,  ifreq_offsetof(ifr_hwaddr) },
+#endif
+	{ "SIFDSTADDR", SIOCSIFDSTADDR, ifreq_offsetof(ifr_dstaddr) },
+#ifdef SIOCSKEEPALIVE
+	{ "SKEEPALIVE", SIOCSKEEPALIVE, ifreq_offsetof(ifr_data) },
+#endif
+#ifdef SIOCSOUTFILL
+	{ "SOUTFILL",   SIOCSOUTFILL,   ifreq_offsetof(ifr_data) },
+#endif
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+	{ "SIFMAP",     SIOCSIFMAP,     ifreq_offsetof(ifr_map.mem_start) },
+	{ "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
+	{ "SIFADDR",    SIOCSIFADDR,    ifreq_offsetof(ifr_addr) },
+};
+
+static const struct options OptArray[] = {
+	{ "metric",      N_ARG,         ARG_METRIC,      0 },
+	{ "mtu",         N_ARG,         ARG_MTU,         0 },
+	{ "txqueuelen",  N_ARG,         ARG_TXQUEUELEN,  0 },
+	{ "dstaddr",     N_ARG,         ARG_DSTADDR,     0 },
+	{ "netmask",     N_ARG,         ARG_NETMASK,     0 },
+	{ "broadcast",   N_ARG | M_CLR, ARG_BROADCAST,   IFF_BROADCAST },
+#if ENABLE_FEATURE_IFCONFIG_HW
+	{ "hw",          N_ARG,         ARG_HW,          0 },
+#endif
+	{ "pointopoint", N_ARG | M_CLR, ARG_POINTOPOINT, IFF_POINTOPOINT },
+#ifdef SIOCSKEEPALIVE
+	{ "keepalive",   N_ARG,         ARG_KEEPALIVE,   0 },
+#endif
+#ifdef SIOCSOUTFILL
+	{ "outfill",     N_ARG,         ARG_OUTFILL,     0 },
+#endif
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+	{ "mem_start",   N_ARG,         ARG_MEM_START,   0 },
+	{ "io_addr",     N_ARG,         ARG_IO_ADDR,     0 },
+	{ "irq",         N_ARG,         ARG_IRQ,         0 },
+#endif
+#if ENABLE_FEATURE_IPV6
+	{ "add",         N_ARG,         ARG_ADD_DEL,     0 },
+	{ "del",         N_ARG,         ARG_ADD_DEL,     0 },
+#endif
+	{ "arp",         N_CLR | M_SET, 0,               IFF_NOARP },
+	{ "trailers",    N_CLR | M_SET, 0,               IFF_NOTRAILERS },
+	{ "promisc",     N_SET | M_CLR, 0,               IFF_PROMISC },
+	{ "multicast",   N_SET | M_CLR, 0,               IFF_MULTICAST },
+	{ "allmulti",    N_SET | M_CLR, 0,               IFF_ALLMULTI },
+	{ "dynamic",     N_SET | M_CLR, 0,               IFF_DYNAMIC },
+	{ "up",          N_SET,         0,               (IFF_UP | IFF_RUNNING) },
+	{ "down",        N_CLR,         0,               IFF_UP },
+	{ 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)
+{
+	struct ifreq ifr;
+	struct sockaddr_in sai;
+#if ENABLE_FEATURE_IFCONFIG_HW
+	struct sockaddr sa;
+#endif
+	const struct arg1opt *a1op;
+	const struct options *op;
+	int sockfd;			/* socket fd we use to manipulate stuff with */
+	int selector;
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+	unsigned int mask;
+	unsigned int did_flags;
+	unsigned int sai_hostname, sai_netmask;
+#else
+	unsigned char mask;
+	unsigned char did_flags;
+#endif
+	char *p;
+	/*char host[128];*/
+	const char *host = NULL; /* make gcc happy */
+
+	did_flags = 0;
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+	sai_hostname = 0;
+	sai_netmask = 0;
+#endif
+
+	/* skip argv[0] */
+	++argv;
+
+#if ENABLE_FEATURE_IFCONFIG_STATUS
+	if (argv[0] && (argv[0][0] == '-' && argv[0][1] == 'a' && !argv[0][2])) {
+		interface_opt_a = 1;
+		++argv;
+	}
+#endif
+
+	if (!argv[0] || !argv[1]) { /* one or no args */
+#if ENABLE_FEATURE_IFCONFIG_STATUS
+		return display_interfaces(argv[0] /* can be NULL */);
+#else
+		bb_error_msg_and_die("no support for status display");
+#endif
+	}
+
+	/* Create a channel to the NET kernel. */
+	sockfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+	/* get interface name */
+	strncpy_IFNAMSIZ(ifr.ifr_name, *argv);
+
+	/* Process the remaining arguments. */
+	while (*++argv != (char *) NULL) {
+		p = *argv;
+		mask = N_MASK;
+		if (*p == '-') {	/* If the arg starts with '-'... */
+			++p;		/*    advance past it and */
+			mask = M_MASK;	/*    set the appropriate mask. */
+		}
+		for (op = OptArray; op->name; op++) {	/* Find table entry. */
+			if (strcmp(p, op->name) == 0) {	/* If name matches... */
+				mask &= op->flags;
+				if (mask)	/* set the mask and go. */
+					goto FOUND_ARG;
+				/* If we get here, there was a valid arg with an */
+				/* invalid '-' prefix. */
+				bb_error_msg_and_die("bad: '%s'", p-1);
+			}
+		}
+
+		/* We fell through, so treat as possible hostname. */
+		a1op = Arg1Opt + ARRAY_SIZE(Arg1Opt) - 1;
+		mask = op->arg_flags;
+		goto HOSTNAME;
+
+ FOUND_ARG:
+		if (mask & ARG_MASK) {
+			mask = op->arg_flags;
+			a1op = Arg1Opt + (op - OptArray);
+			if (mask & A_NETMASK & did_flags)
+				bb_show_usage();
+			if (*++argv == NULL) {
+				if (mask & A_ARG_REQ)
+					bb_show_usage();
+				--argv;
+				mask &= A_SET_AFTER;	/* just for broadcast */
+			} else {	/* got an arg so process it */
+ HOSTNAME:
+				did_flags |= (mask & (A_NETMASK|A_HOSTNAME));
+				if (mask & A_CAST_HOST_COPY) {
+#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
+						sai.sin_family = AF_INET;
+						sai.sin_port = 0;
+						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)
+						 && (did_flags & (A_NETMASK|A_HOSTNAME)) == (A_NETMASK|A_HOSTNAME)
+						) {
+							/* + is special, meaning broadcast is derived. */
+							sai.sin_addr.s_addr = (~sai_netmask) | (sai_hostname & sai_netmask);
+						}
+#endif
+						else {
+							len_and_sockaddr *lsa;
+							if (strcmp(host, "inet") == 0)
+								continue; /* compat stuff */
+							lsa = xhost2sockaddr(host, 0);
+#if ENABLE_FEATURE_IPV6
+							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);
+								ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+								ifr6.ifr6_prefixlen = prefix_len;
+								ioctl_or_perror_and_die(sockfd6, a1op->selector, &ifr6, "SIOC%s", a1op->name);
+								if (ENABLE_FEATURE_CLEAN_UP)
+									free(lsa);
+								continue;
+							}
+#endif
+							sai.sin_addr = lsa->u.sin.sin_addr;
+							if (ENABLE_FEATURE_CLEAN_UP)
+								free(lsa);
+						}
+#if ENABLE_FEATURE_IFCONFIG_BROADCAST_PLUS
+						if (mask & A_HOSTNAME)
+							sai_hostname = sai.sin_addr.s_addr;
+						if (mask & A_NETMASK)
+							sai_netmask = sai.sin_addr.s_addr;
+#endif
+						p = (char *) &sai;
+#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"
+								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));
+				} else {
+					/* FIXME: error check?? */
+					unsigned long i = strtoul(*argv, NULL, 0);
+					p = ((char *)&ifr) + a1op->ifr_offset;
+#if ENABLE_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ
+					if (mask & A_MAP_TYPE) {
+						xioctl(sockfd, SIOCGIFMAP, &ifr);
+						if ((mask & A_MAP_UCHAR) == A_MAP_UCHAR)
+							*((unsigned char *) p) = i;
+						else if (mask & A_MAP_USHORT)
+							*((unsigned short *) p) = i;
+						else
+							*((unsigned long *) p) = i;
+					} else
+#endif
+					if (mask & A_CAST_CHAR_PTR)
+						*((caddr_t *) p) = (caddr_t) i;
+					else	/* A_CAST_INT */
+						*((int *) p) = i;
+				}
+
+				ioctl_or_perror_and_die(sockfd, a1op->selector, &ifr, "SIOC%s", a1op->name);
+#ifdef QUESTIONABLE_ALIAS_CASE
+				if (mask & A_COLON_CHK) {
+					/*
+					 * Don't do the set_flag() if the address is an alias with
+					 * a '-' at the end, since it's deleted already! - Roman
+					 *
+					 * Should really use regex.h here, not sure though how well
+					 * it'll go with the cross-platform support etc.
+					 */
+					char *ptr;
+					short int found_colon = 0;
+					for (ptr = ifr.ifr_name; *ptr; ptr++)
+						if (*ptr == ':')
+							found_colon++;
+					if (found_colon && ptr[-1] == '-')
+						continue;
+				}
+#endif
+			}
+			if (!(mask & A_SET_AFTER))
+				continue;
+			mask = N_SET;
+		}
+
+		xioctl(sockfd, SIOCGIFFLAGS, &ifr);
+		selector = op->selector;
+		if (mask & SET_MASK)
+			ifr.ifr_flags |= selector;
+		else
+			ifr.ifr_flags &= ~selector;
+		xioctl(sockfd, SIOCSIFFLAGS, &ifr);
+	} /* while () */
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		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
diff --git a/busybox-1.19.3/networking/ifenslave.c b/busybox-1.19.3/networking/ifenslave.c
new file mode 100644
index 0000000..ae7719f
--- /dev/null
+++ b/busybox-1.19.3/networking/ifenslave.c
@@ -0,0 +1,617 @@
+/* Mode: C;
+ *
+ * Mini ifenslave implementation for busybox
+ * Copyright (C) 2005 by Marc Leeman <marc.leeman@barco.com>
+ *
+ * ifenslave.c: Configure network interfaces for parallel routing.
+ *
+ *      This program controls the Linux implementation of running multiple
+ *      network interfaces in parallel.
+ *
+ * Author:      Donald Becker <becker@cesdis.gsfc.nasa.gov>
+ *              Copyright 1994-1996 Donald Becker
+ *
+ *              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.
+ *
+ *      The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ *      Center of Excellence in Space Data and Information Sciences
+ *         Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+ *
+ *  Changes :
+ *    - 2000/10/02 Willy Tarreau <willy at meta-x.org> :
+ *       - few fixes. Master's MAC address is now correctly taken from
+ *         the first device when not previously set ;
+ *       - detach support : call BOND_RELEASE to detach an enslaved interface.
+ *       - give a mini-howto from command-line help : # ifenslave -h
+ *
+ *    - 2001/02/16 Chad N. Tindel <ctindel at ieee dot org> :
+ *       - Master is now brought down before setting the MAC address.  In
+ *         the 2.4 kernel you can't change the MAC address while the device is
+ *         up because you get EBUSY.
+ *
+ *    - 2001/09/13 Takao Indoh <indou dot takao at jp dot fujitsu dot com>
+ *       - Added the ability to change the active interface on a mode 1 bond
+ *         at runtime.
+ *
+ *    - 2001/10/23 Chad N. Tindel <ctindel at ieee dot org> :
+ *       - No longer set the MAC address of the master.  The bond device will
+ *         take care of this itself
+ *       - Try the SIOC*** versions of the bonding ioctls before using the
+ *         old versions
+ *    - 2002/02/18 Erik Habbinga <erik_habbinga @ hp dot com> :
+ *       - ifr2.ifr_flags was not initialized in the hwaddr_notset case,
+ *         SIOCGIFFLAGS now called before hwaddr_notset test
+ *
+ *    - 2002/10/31 Tony Cureington <tony.cureington * hp_com> :
+ *       - If the master does not have a hardware address when the first slave
+ *         is enslaved, the master is assigned the hardware address of that
+ *         slave - there is a comment in bonding.c stating "ifenslave takes
+ *         care of this now." This corrects the problem of slaves having
+ *         different hardware addresses in active-backup mode when
+ *         multiple interfaces are specified on a single ifenslave command
+ *         (ifenslave bond0 eth0 eth1).
+ *
+ *    - 2003/03/18 - Tsippy Mendelson <tsippy.mendelson at intel dot com> and
+ *                   Shmulik Hen <shmulik.hen at intel dot com>
+ *       - Moved setting the slave's mac address and openning it, from
+ *         the application to the driver. This enables support of modes
+ *         that need to use the unique mac address of each slave.
+ *         The driver also takes care of closing the slave and restoring its
+ *         original mac address upon release.
+ *         In addition, block possibility of enslaving before the master is up.
+ *         This prevents putting the system in an undefined state.
+ *
+ *    - 2003/05/01 - Amir Noam <amir.noam at intel dot com>
+ *       - Added ABI version control to restore compatibility between
+ *         new/old ifenslave and new/old bonding.
+ *       - Prevent adding an adapter that is already a slave.
+ *         Fixes the problem of stalling the transmission and leaving
+ *         the slave in a down state.
+ *
+ *    - 2003/05/01 - Shmulik Hen <shmulik.hen at intel dot com>
+ *       - Prevent enslaving if the bond device is down.
+ *         Fixes the problem of leaving the system in unstable state and
+ *         halting when trying to remove the module.
+ *       - Close socket on all abnormal exists.
+ *       - Add versioning scheme that follows that of the bonding driver.
+ *         current version is 1.0.0 as a base line.
+ *
+ *    - 2003/05/22 - Jay Vosburgh <fubar at us dot ibm dot com>
+ *       - ifenslave -c was broken; it's now fixed
+ *       - Fixed problem with routes vanishing from master during enslave
+ *         processing.
+ *
+ *    - 2003/05/27 - Amir Noam <amir.noam at intel dot com>
+ *       - Fix backward compatibility issues:
+ *         For drivers not using ABI versions, slave was set down while
+ *         it should be left up before enslaving.
+ *         Also, master was not set down and the default set_mac_address()
+ *         would fail and generate an error message in the system log.
+ *       - For opt_c: slave should not be set to the master's setting
+ *         while it is running. It was already set during enslave. To
+ *         simplify things, it is now handeled separately.
+ *
+ *    - 2003/12/01 - Shmulik Hen <shmulik.hen at intel dot com>
+ *       - Code cleanup and style changes
+ *         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 */
+#include <linux/if.h>
+//#include <net/if_arp.h> - not needed?
+#include <linux/if_bonding.h>
+#include <linux/sockios.h>
+#include "fix_u32.h" /* hack, so we may include kernel's ethtool.h */
+#include <linux/ethtool.h>
+#ifndef BOND_ABI_VERSION
+# define BOND_ABI_VERSION 2
+#endif
+#ifndef IFNAMSIZ
+# define IFNAMSIZ 16
+#endif
+
+
+struct dev_data {
+	struct ifreq mtu, flags, hwaddr;
+};
+
+
+enum { skfd = 3 };      /* AF_INET socket for ioctl() calls. */
+struct globals {
+	unsigned abi_ver;       /* userland - kernel ABI version */
+	smallint hwaddr_set;    /* Master's hwaddr is set */
+	struct dev_data master;
+	struct dev_data slave;
+};
+#define G (*ptr_to_globals)
+#define abi_ver    (G.abi_ver   )
+#define hwaddr_set (G.hwaddr_set)
+#define master     (G.master    )
+#define slave      (G.slave     )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+/* NOINLINEs are placed where it results in smaller code (gcc 4.3.1) */
+
+static int ioctl_on_skfd(unsigned request, struct ifreq *ifr)
+{
+	return ioctl(skfd, request, ifr);
+}
+
+static int set_ifrname_and_do_ioctl(unsigned request, struct ifreq *ifr, const char *ifname)
+{
+	strncpy_IFNAMSIZ(ifr->ifr_name, ifname);
+	return ioctl_on_skfd(request, ifr);
+}
+
+static int get_if_settings(char *ifname, struct dev_data *dd)
+{
+	int res;
+
+	res = set_ifrname_and_do_ioctl(SIOCGIFMTU, &dd->mtu, ifname);
+	res |= set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &dd->flags, ifname);
+	res |= set_ifrname_and_do_ioctl(SIOCGIFHWADDR, &dd->hwaddr, ifname);
+
+	return res;
+}
+
+static int get_slave_flags(char *slave_ifname)
+{
+	return set_ifrname_and_do_ioctl(SIOCGIFFLAGS, &slave.flags, slave_ifname);
+}
+
+static int set_hwaddr(char *ifname, struct sockaddr *hwaddr)
+{
+	struct ifreq ifr;
+
+	memcpy(&(ifr.ifr_hwaddr), hwaddr, sizeof(*hwaddr));
+	return set_ifrname_and_do_ioctl(SIOCSIFHWADDR, &ifr, ifname);
+}
+
+static int set_mtu(char *ifname, int mtu)
+{
+	struct ifreq ifr;
+
+	ifr.ifr_mtu = mtu;
+	return set_ifrname_and_do_ioctl(SIOCSIFMTU, &ifr, ifname);
+}
+
+static int set_if_flags(char *ifname, int flags)
+{
+	struct ifreq ifr;
+
+	ifr.ifr_flags = flags;
+	return set_ifrname_and_do_ioctl(SIOCSIFFLAGS, &ifr, ifname);
+}
+
+static int set_if_up(char *ifname, int flags)
+{
+	int res = set_if_flags(ifname, flags | IFF_UP);
+	if (res)
+		bb_perror_msg("%s: can't up", ifname);
+	return res;
+}
+
+static int set_if_down(char *ifname, int flags)
+{
+	int res = set_if_flags(ifname, flags & ~IFF_UP);
+	if (res)
+		bb_perror_msg("%s: can't down", ifname);
+	return res;
+}
+
+static int clear_if_addr(char *ifname)
+{
+	struct ifreq ifr;
+
+	ifr.ifr_addr.sa_family = AF_INET;
+	memset(ifr.ifr_addr.sa_data, 0, sizeof(ifr.ifr_addr.sa_data));
+	return set_ifrname_and_do_ioctl(SIOCSIFADDR, &ifr, ifname);
+}
+
+static int set_if_addr(char *master_ifname, char *slave_ifname)
+{
+#if (SIOCGIFADDR | SIOCSIFADDR \
+  | SIOCGIFDSTADDR | SIOCSIFDSTADDR \
+  | SIOCGIFBRDADDR | SIOCSIFBRDADDR \
+  | SIOCGIFNETMASK | SIOCSIFNETMASK) <= 0xffff
+#define INT uint16_t
+#else
+#define INT int
+#endif
+	static const struct {
+		INT g_ioctl;
+		INT s_ioctl;
+	} ifra[] = {
+		{ SIOCGIFADDR,    SIOCSIFADDR    },
+		{ SIOCGIFDSTADDR, SIOCSIFDSTADDR },
+		{ SIOCGIFBRDADDR, SIOCSIFBRDADDR },
+		{ SIOCGIFNETMASK, SIOCSIFNETMASK },
+	};
+
+	struct ifreq ifr;
+	int res;
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(ifra); i++) {
+		res = set_ifrname_and_do_ioctl(ifra[i].g_ioctl, &ifr, master_ifname);
+		if (res < 0) {
+			ifr.ifr_addr.sa_family = AF_INET;
+			memset(ifr.ifr_addr.sa_data, 0,
+			       sizeof(ifr.ifr_addr.sa_data));
+		}
+
+		res = set_ifrname_and_do_ioctl(ifra[i].s_ioctl, &ifr, slave_ifname);
+		if (res < 0)
+			return res;
+	}
+
+	return 0;
+}
+
+static void change_active(char *master_ifname, char *slave_ifname)
+{
+	struct ifreq ifr;
+
+	if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
+		bb_error_msg_and_die("%s is not a slave", slave_ifname);
+	}
+
+	strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
+	if (set_ifrname_and_do_ioctl(SIOCBONDCHANGEACTIVE, &ifr, master_ifname)
+	 && ioctl_on_skfd(BOND_CHANGE_ACTIVE_OLD, &ifr)
+	) {
+		bb_perror_msg_and_die(
+			"master %s, slave %s: can't "
+			"change active",
+			master_ifname, slave_ifname);
+	}
+}
+
+static NOINLINE int enslave(char *master_ifname, char *slave_ifname)
+{
+	struct ifreq ifr;
+	int res;
+
+	if (slave.flags.ifr_flags & IFF_SLAVE) {
+		bb_error_msg(
+			"%s is already a slave",
+			slave_ifname);
+		return 1;
+	}
+
+	res = set_if_down(slave_ifname, slave.flags.ifr_flags);
+	if (res)
+		return res;
+
+	if (abi_ver < 2) {
+		/* Older bonding versions would panic if the slave has no IP
+		 * address, so get the IP setting from the master.
+		 */
+		res = set_if_addr(master_ifname, slave_ifname);
+		if (res) {
+			bb_perror_msg("%s: can't set address", slave_ifname);
+			return res;
+		}
+	} else {
+		res = clear_if_addr(slave_ifname);
+		if (res) {
+			bb_perror_msg("%s: can't clear address", slave_ifname);
+			return res;
+		}
+	}
+
+	if (master.mtu.ifr_mtu != slave.mtu.ifr_mtu) {
+		res = set_mtu(slave_ifname, master.mtu.ifr_mtu);
+		if (res) {
+			bb_perror_msg("%s: can't set MTU", slave_ifname);
+			return res;
+		}
+	}
+
+	if (hwaddr_set) {
+		/* Master already has an hwaddr
+		 * so set it's hwaddr to the slave
+		 */
+		if (abi_ver < 1) {
+			/* The driver is using an old ABI, so
+			 * the application sets the slave's
+			 * hwaddr
+			 */
+			if (set_hwaddr(slave_ifname, &(master.hwaddr.ifr_hwaddr))) {
+				bb_perror_msg("%s: can't set hw address",
+						slave_ifname);
+				goto undo_mtu;
+			}
+
+			/* For old ABI the application needs to bring the
+			 * slave back up
+			 */
+			if (set_if_up(slave_ifname, slave.flags.ifr_flags))
+				goto undo_slave_mac;
+		}
+		/* The driver is using a new ABI,
+		 * so the driver takes care of setting
+		 * the slave's hwaddr and bringing
+		 * it up again
+		 */
+	} else {
+		/* No hwaddr for master yet, so
+		 * set the slave's hwaddr to it
+		 */
+		if (abi_ver < 1) {
+			/* For old ABI, the master needs to be
+			 * down before setting it's hwaddr
+			 */
+			if (set_if_down(master_ifname, master.flags.ifr_flags))
+				goto undo_mtu;
+		}
+
+		if (set_hwaddr(master_ifname, &(slave.hwaddr.ifr_hwaddr))) {
+			bb_error_msg("%s: can't set hw address",
+				master_ifname);
+			goto undo_mtu;
+		}
+
+		if (abi_ver < 1) {
+			/* For old ABI, bring the master
+			 * back up
+			 */
+			if (set_if_up(master_ifname, master.flags.ifr_flags))
+				goto undo_master_mac;
+		}
+
+		hwaddr_set = 1;
+	}
+
+	/* Do the real thing */
+	strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
+	if (set_ifrname_and_do_ioctl(SIOCBONDENSLAVE, &ifr, master_ifname)
+	 && ioctl_on_skfd(BOND_ENSLAVE_OLD, &ifr)
+	) {
+		goto undo_master_mac;
+	}
+
+	return 0;
+
+/* rollback (best effort) */
+ undo_master_mac:
+	set_hwaddr(master_ifname, &(master.hwaddr.ifr_hwaddr));
+	hwaddr_set = 0;
+	goto undo_mtu;
+
+ undo_slave_mac:
+	set_hwaddr(slave_ifname, &(slave.hwaddr.ifr_hwaddr));
+ undo_mtu:
+	set_mtu(slave_ifname, slave.mtu.ifr_mtu);
+	return 1;
+}
+
+static int release(char *master_ifname, char *slave_ifname)
+{
+	struct ifreq ifr;
+	int res = 0;
+
+	if (!(slave.flags.ifr_flags & IFF_SLAVE)) {
+		bb_error_msg("%s is not a slave", slave_ifname);
+		return 1;
+	}
+
+	strncpy_IFNAMSIZ(ifr.ifr_slave, slave_ifname);
+	if (set_ifrname_and_do_ioctl(SIOCBONDRELEASE, &ifr, master_ifname) < 0
+	 && ioctl_on_skfd(BOND_RELEASE_OLD, &ifr) < 0
+	) {
+		return 1;
+	}
+	if (abi_ver < 1) {
+		/* The driver is using an old ABI, so we'll set the interface
+		 * down to avoid any conflicts due to same MAC/IP
+		 */
+		res = set_if_down(slave_ifname, slave.flags.ifr_flags);
+	}
+
+	/* set to default mtu */
+	set_mtu(slave_ifname, 1500);
+
+	return res;
+}
+
+static NOINLINE void get_drv_info(char *master_ifname)
+{
+	struct ifreq ifr;
+	struct ethtool_drvinfo info;
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_data = (caddr_t)&info;
+	info.cmd = ETHTOOL_GDRVINFO;
+	/* both fields are 32 bytes long (long enough) */
+	strcpy(info.driver, "ifenslave");
+	strcpy(info.fw_version, utoa(BOND_ABI_VERSION));
+	if (set_ifrname_and_do_ioctl(SIOCETHTOOL, &ifr, master_ifname) < 0) {
+		if (errno == EOPNOTSUPP)
+			return;
+		bb_perror_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
+	}
+
+	abi_ver = bb_strtou(info.fw_version, NULL, 0);
+	if (errno)
+		bb_error_msg_and_die("%s: SIOCETHTOOL error", master_ifname);
+}
+
+int ifenslave_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ifenslave_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *master_ifname, *slave_ifname;
+	int rv;
+	int res;
+	unsigned opt;
+	enum {
+		OPT_c = (1 << 0),
+		OPT_d = (1 << 1),
+		OPT_f = (1 << 2),
+	};
+#if ENABLE_LONG_OPTS
+	static const char ifenslave_longopts[] ALIGN1 =
+		"change-active\0"  No_argument "c"
+		"detach\0"         No_argument "d"
+		"force\0"          No_argument "f"
+		/* "all-interfaces\0" No_argument "a" */
+		;
+
+	applet_long_options = ifenslave_longopts;
+#endif
+	INIT_G();
+
+	opt = getopt32(argv, "cdfa");
+	argv += optind;
+	if (opt & (opt-1)) /* Only one option can be given */
+		bb_show_usage();
+
+	master_ifname = *argv++;
+
+	/* No interface names - show all interfaces. */
+	if (!master_ifname) {
+		display_interfaces(NULL);
+		return EXIT_SUCCESS;
+	}
+
+	/* Open a basic socket */
+	xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), skfd);
+
+	/* Exchange abi version with bonding module */
+	get_drv_info(master_ifname);
+
+	slave_ifname = *argv++;
+	if (!slave_ifname) {
+		if (opt & (OPT_d|OPT_c)) {
+			/* --change or --detach, and no slaves given -
+			 * show all interfaces. */
+			display_interfaces(slave_ifname /* == NULL */);
+			return 2; /* why 2? */
+		}
+		/* A single arg means show the
+		 * configuration for this interface
+		 */
+		display_interfaces(master_ifname);
+		return EXIT_SUCCESS;
+	}
+
+	if (get_if_settings(master_ifname, &master)) {
+		/* Probably a good reason not to go on */
+		bb_perror_msg_and_die("%s: can't get settings", master_ifname);
+	}
+
+	/* Check if master is indeed a master;
+	 * if not then fail any operation
+	 */
+	if (!(master.flags.ifr_flags & IFF_MASTER))
+		bb_error_msg_and_die("%s is not a master", master_ifname);
+
+	/* Check if master is up; if not then fail any operation */
+	if (!(master.flags.ifr_flags & IFF_UP))
+		bb_error_msg_and_die("%s is not up", master_ifname);
+
+#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)) {
+		/* The family '1' is ARPHRD_ETHER for ethernet. */
+		if (master.hwaddr.ifr_hwaddr.sa_family != 1) {
+			bb_error_msg_and_die(
+				"%s is not ethernet-like (-f overrides)",
+				master_ifname);
+		}
+	}
+#endif
+
+	/* Accepts only one slave */
+	if (opt & OPT_c) {
+		/* Change active slave */
+		if (get_slave_flags(slave_ifname)) {
+			bb_perror_msg_and_die(
+				"%s: can't get flags", slave_ifname);
+		}
+		change_active(master_ifname, slave_ifname);
+		return EXIT_SUCCESS;
+	}
+
+	/* Accepts multiple slaves */
+	res = 0;
+	do {
+		if (opt & OPT_d) {
+			/* Detach a slave interface from the master */
+			rv = get_slave_flags(slave_ifname);
+			if (rv) {
+				/* Can't work with this slave, */
+				/* remember the error and skip it */
+				bb_perror_msg(
+					"skipping %s: can't get flags",
+					slave_ifname);
+				res = rv;
+				continue;
+			}
+			rv = release(master_ifname, slave_ifname);
+			if (rv) {
+				bb_perror_msg("can't release %s from %s",
+					slave_ifname, master_ifname);
+				res = rv;
+			}
+		} else {
+			/* Attach a slave interface to the master */
+			rv = get_if_settings(slave_ifname, &slave);
+			if (rv) {
+				/* Can't work with this slave, */
+				/* remember the error and skip it */
+				bb_perror_msg(
+					"skipping %s: can't get settings",
+					slave_ifname);
+				res = rv;
+				continue;
+			}
+			rv = enslave(master_ifname, slave_ifname);
+			if (rv) {
+				bb_perror_msg("can't enslave %s to %s",
+					slave_ifname, master_ifname);
+				res = rv;
+			}
+		}
+	} while ((slave_ifname = *argv++) != NULL);
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(skfd);
+	}
+
+	return res;
+}
diff --git a/busybox-1.19.3/networking/ifplugd.c b/busybox-1.19.3/networking/ifplugd.c
new file mode 100644
index 0000000..d8358cd
--- /dev/null
+++ b/busybox-1.19.3/networking/ifplugd.c
@@ -0,0 +1,739 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ifplugd for busybox, based on ifplugd 0.28 (written by Lennart Poettering).
+ *
+ * Copyright (C) 2009 Maksym Kryzhanovskyy <xmaks@email.cz>
+ *
+ * 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 script on daemon startup"
+//usage:     "\n	-q		Don't run script on daemon quit"
+//usage:     "\n	-l		Run script on startup even if no cable is detected"
+//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>
+#ifdef HAVE_NET_ETHERNET_H
+# include <net/ethernet.h>
+#endif
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+#include <syslog.h>
+
+#define __user
+#include <linux/wireless.h>
+
+/*
+From initial port to busybox, removed most of the redundancy by
+converting implementation of a polymorphic interface to the strict
+functional style. The main role is run a script when link state
+changed, other activities like audio signal or detailed reports
+are on the script itself.
+
+One questionable point of the design is netlink usage:
+
+We have 1 second timeout by default to poll the link status,
+it is short enough so that there are no real benefits in
+using netlink to get "instantaneous" interface creation/deletion
+notifications. We can check for interface existence by just
+doing some fast ioctl using its name.
+
+Netlink code then can be just dropped (1k or more?)
+*/
+
+
+#define IFPLUGD_ENV_PREVIOUS "IFPLUGD_PREVIOUS"
+#define IFPLUGD_ENV_CURRENT "IFPLUGD_CURRENT"
+
+enum {
+	FLAG_NO_AUTO			= 1 <<  0, // -a, Do not enable interface automatically
+	FLAG_NO_DAEMON			= 1 <<  1, // -n, Do not daemonize
+	FLAG_NO_SYSLOG			= 1 <<  2, // -s, Do not use syslog, use stderr instead
+	FLAG_IGNORE_FAIL		= 1 <<  3, // -f, Ignore detection failure, retry instead (failure is treated as DOWN)
+	FLAG_IGNORE_FAIL_POSITIVE	= 1 <<  4, // -F, Ignore detection failure, retry instead (failure is treated as UP)
+	FLAG_IFACE			= 1 <<  5, // -i, Specify ethernet interface
+	FLAG_RUN			= 1 <<  6, // -r, Specify program to execute
+	FLAG_IGNORE_RETVAL		= 1 <<  7, // -I, Don't exit on nonzero return value of program executed
+	FLAG_POLL_TIME			= 1 <<  8, // -t, Specify poll time in seconds
+	FLAG_DELAY_UP			= 1 <<  9, // -u, Specify delay for configuring interface
+	FLAG_DELAY_DOWN			= 1 << 10, // -d, Specify delay for deconfiguring interface
+	FLAG_API_MODE			= 1 << 11, // -m, Force API mode (mii, priv, ethtool, wlan, auto)
+	FLAG_NO_STARTUP			= 1 << 12, // -p, Don't run script on daemon startup
+	FLAG_NO_SHUTDOWN		= 1 << 13, // -q, Don't run script on daemon quit
+	FLAG_INITIAL_DOWN		= 1 << 14, // -l, Run "down" script on startup if no cable is detected
+	FLAG_EXTRA_ARG			= 1 << 15, // -x, Specify an extra argument for action script
+	FLAG_MONITOR			= 1 << 16, // -M, Use interface monitoring
+#if ENABLE_FEATURE_PIDFILE
+	FLAG_KILL			= 1 << 17, // -k, Kill a running daemon
+#endif
+};
+#if ENABLE_FEATURE_PIDFILE
+# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:Mk"
+#else
+# define OPTION_STR "+ansfFi:r:It:u:d:m:pqlx:M"
+#endif
+
+enum { // interface status
+	IFSTATUS_ERR = -1,
+	IFSTATUS_DOWN = 0,
+	IFSTATUS_UP = 1,
+};
+
+enum { // constant fds
+	ioctl_fd = 3,
+	netlink_fd = 4,
+};
+
+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;
+	unsigned delay_up;
+	unsigned delay_down;
+
+	const char *iface;
+	const char *api_mode;
+	const char *script_name;
+	const char *extra_arg;
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	G.iface_last_status = -1; \
+	G.iface_exists   = 1; \
+	G.poll_time      = 1; \
+	G.delay_down     = 5; \
+	G.iface          = "eth0"; \
+	G.api_mode       = "a"; \
+	G.script_name    = "/etc/ifplugd/ifplugd.action"; \
+} while (0)
+
+
+/* Utility routines */
+
+static void set_ifreq_to_ifname(struct ifreq *ifreq)
+{
+	memset(ifreq, 0, sizeof(struct ifreq));
+	strncpy_IFNAMSIZ(ifreq->ifr_name, G.iface);
+}
+
+static int network_ioctl(int request, void* data, const char *errmsg)
+{
+	int r = ioctl(ioctl_fd, request, data);
+	if (r < 0 && errmsg)
+		bb_perror_msg("%s failed", errmsg);
+	return r;
+}
+
+/* Link detection routines and table */
+
+static smallint detect_link_mii(void)
+{
+	/* char buffer instead of bona-fide struct avoids aliasing warning */
+	char buf[sizeof(struct ifreq)];
+	struct ifreq *const ifreq = (void *)buf;
+
+	struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
+
+	set_ifreq_to_ifname(ifreq);
+
+	if (network_ioctl(SIOCGMIIPHY, ifreq, "SIOCGMIIPHY") < 0) {
+		return IFSTATUS_ERR;
+	}
+
+	mii->reg_num = 1;
+
+	if (network_ioctl(SIOCGMIIREG, ifreq, "SIOCGMIIREG") < 0) {
+		return IFSTATUS_ERR;
+	}
+
+	return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
+}
+
+static smallint detect_link_priv(void)
+{
+	/* char buffer instead of bona-fide struct avoids aliasing warning */
+	char buf[sizeof(struct ifreq)];
+	struct ifreq *const ifreq = (void *)buf;
+
+	struct mii_ioctl_data *mii = (void *)&ifreq->ifr_data;
+
+	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) {
+		return IFSTATUS_ERR;
+	}
+
+	return (mii->val_out & 0x0004) ? IFSTATUS_UP : IFSTATUS_DOWN;
+}
+
+static smallint detect_link_ethtool(void)
+{
+	struct ifreq ifreq;
+	struct ethtool_value edata;
+
+	set_ifreq_to_ifname(&ifreq);
+
+	edata.cmd = ETHTOOL_GLINK;
+	ifreq.ifr_data = (void*) &edata;
+
+	if (network_ioctl(SIOCETHTOOL, &ifreq, "ETHTOOL_GLINK") < 0) {
+		return IFSTATUS_ERR;
+	}
+
+	return edata.data ? IFSTATUS_UP : IFSTATUS_DOWN;
+}
+
+static smallint detect_link_iff(void)
+{
+	struct ifreq ifreq;
+
+	set_ifreq_to_ifname(&ifreq);
+
+	if (network_ioctl(SIOCGIFFLAGS, &ifreq, "SIOCGIFFLAGS") < 0) {
+		return IFSTATUS_ERR;
+	}
+
+	/* If IFF_UP is not set (interface is down), IFF_RUNNING is never set
+	 * regardless of link status. Simply continue to report last status -
+	 * no point in reporting spurious link downs if interface is disabled
+	 * by admin. When/if it will be brought up,
+	 * we'll report real link status.
+	 */
+	if (!(ifreq.ifr_flags & IFF_UP) && G.iface_last_status != IFSTATUS_ERR)
+		return G.iface_last_status;
+
+	return (ifreq.ifr_flags & IFF_RUNNING) ? IFSTATUS_UP : IFSTATUS_DOWN;
+}
+
+static smallint detect_link_wlan(void)
+{
+	int i;
+	struct iwreq iwrequest;
+	uint8_t mac[ETH_ALEN];
+
+	memset(&iwrequest, 0, sizeof(iwrequest));
+	strncpy_IFNAMSIZ(iwrequest.ifr_ifrn.ifrn_name, G.iface);
+
+	if (network_ioctl(SIOCGIWAP, &iwrequest, "SIOCGIWAP") < 0) {
+		return IFSTATUS_ERR;
+	}
+
+	memcpy(mac, &iwrequest.u.ap_addr.sa_data, ETH_ALEN);
+
+	if (mac[0] == 0xFF || mac[0] == 0x44 || mac[0] == 0x00) {
+		for (i = 1; i < ETH_ALEN; ++i) {
+			if (mac[i] != mac[0])
+				return IFSTATUS_UP;
+		}
+		return IFSTATUS_DOWN;
+	}
+
+	return IFSTATUS_UP;
+}
+
+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)
+{
+	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.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
+	if (G.api_mode[0] == 'a')
+		G.api_method_num = API_AUTO;
+}
+
+static smallint detect_link(void)
+{
+	smallint status;
+
+	if (!G.iface_exists)
+		return (option_mask32 & FLAG_MONITOR) ? IFSTATUS_DOWN : IFSTATUS_ERR;
+
+	/* Some drivers can't detect link status when the interface is down.
+	 * I imagine detect_link_iff() is the most vulnerable.
+	 * That's why -a "noauto" in an option, not a hardwired behavior.
+	 */
+	if (!(option_mask32 & FLAG_NO_AUTO))
+		up_iface();
+
+	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;
+		else if (option_mask32 & FLAG_IGNORE_FAIL_POSITIVE)
+			status = IFSTATUS_UP;
+		else if (G.api_mode[0] == 'a')
+			bb_error_msg("can't detect link status");
+	}
+
+	if (status != G.iface_last_status) {
+		G.iface_prev_status = G.iface_last_status;
+		G.iface_last_status = status;
+	}
+
+	return status;
+}
+
+static NOINLINE int check_existence_through_netlink(void)
+{
+	int iface_len;
+	char replybuf[1024];
+
+	iface_len = strlen(G.iface);
+	while (1) {
+		struct nlmsghdr *mhdr;
+		ssize_t bytes;
+
+		bytes = recv(netlink_fd, &replybuf, sizeof(replybuf), MSG_DONTWAIT);
+		if (bytes < 0) {
+			if (errno == EAGAIN)
+				return G.iface_exists;
+			if (errno == EINTR)
+				continue;
+
+			bb_perror_msg("netlink: recv");
+			return -1;
+		}
+
+		mhdr = (struct nlmsghdr*)replybuf;
+		while (bytes > 0) {
+			if (!NLMSG_OK(mhdr, bytes)) {
+				bb_error_msg("netlink packet too small or truncated");
+				return -1;
+			}
+
+			if (mhdr->nlmsg_type == RTM_NEWLINK || mhdr->nlmsg_type == RTM_DELLINK) {
+				struct rtattr *attr;
+				int attr_len;
+
+				if (mhdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct ifinfomsg))) {
+					bb_error_msg("netlink packet too small or truncated");
+					return -1;
+				}
+
+				attr = IFLA_RTA(NLMSG_DATA(mhdr));
+				attr_len = IFLA_PAYLOAD(mhdr);
+
+				while (RTA_OK(attr, attr_len)) {
+					if (attr->rta_type == IFLA_IFNAME) {
+						int len = RTA_PAYLOAD(attr);
+						if (len > IFNAMSIZ)
+							len = IFNAMSIZ;
+						if (iface_len <= len
+						 && strncmp(G.iface, RTA_DATA(attr), len) == 0
+						) {
+							G.iface_exists = (mhdr->nlmsg_type == RTM_NEWLINK);
+						}
+					}
+					attr = RTA_NEXT(attr, attr_len);
+				}
+			}
+
+			mhdr = NLMSG_NEXT(mhdr, bytes);
+		}
+	}
+
+	return G.iface_exists;
+}
+
+#if ENABLE_FEATURE_PIDFILE
+static NOINLINE pid_t read_pid(const char *filename)
+{
+	int len;
+	char buf[128];
+
+	len = open_read_close(filename, buf, 127);
+	if (len > 0) {
+		buf[len] = '\0';
+		/* returns ULONG_MAX on error => -1 */
+		return bb_strtoul(buf, NULL, 10);
+	}
+	return 0;
+}
+#endif
+
+int ifplugd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ifplugd_main(int argc UNUSED_PARAM, char **argv)
+{
+	int iface_status;
+	int delay_time;
+	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;
+#endif
+
+	INIT_G();
+
+	opt_complementary = "t+:u+:d+";
+	opts = getopt32(argv, OPTION_STR,
+		&G.iface, &G.script_name, &G.poll_time, &G.delay_up,
+		&G.delay_down, &G.api_mode, &G.extra_arg);
+	G.poll_time *= 1000;
+
+	applet_name = xasprintf("ifplugd(%s)", G.iface);
+
+#if ENABLE_FEATURE_PIDFILE
+	pidfile_name = xasprintf(_PATH_VARRUN"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);
+		return EXIT_SUCCESS;
+	}
+
+	if (pid_from_pidfile > 0 && kill(pid_from_pidfile, 0) == 0)
+		bb_error_msg_and_die("daemon already running");
+#endif
+
+	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) {
+		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);
+
+	/* this can't be moved before socket creation */
+	if (!(opts & FLAG_NO_SYSLOG)) {
+		openlog(applet_name, 0, LOG_DAEMON);
+		logmode |= LOGMODE_SYSLOG;
+	}
+
+	bb_signals(0
+		| (1 << SIGINT )
+		| (1 << SIGTERM)
+		| (1 << SIGQUIT)
+		| (1 << SIGHUP ) /* why we ignore it? */
+		/* | (1 << SIGCHLD) - run_script does not use it anymore */
+		, record_signo);
+
+	bb_error_msg("started: %s", bb_banner);
+
+	if (opts & FLAG_MONITOR) {
+		struct ifreq ifrequest;
+		set_ifreq_to_ifname(&ifrequest);
+		G.iface_exists = (network_ioctl(SIOCGIFINDEX, &ifrequest, NULL) == 0);
+	}
+
+	if (G.iface_exists)
+		maybe_up_new_iface();
+
+	iface_status = detect_link();
+	if (iface_status == IFSTATUS_ERR)
+		goto exiting;
+	iface_status_str = strstatus(iface_status);
+
+	if (opts & FLAG_MONITOR) {
+		bb_error_msg("interface %s",
+			G.iface_exists ? "exists"
+			: "doesn't exist, waiting");
+	}
+	/* else we assume it always exists, but don't mislead user
+	 * by potentially lying that it really exists */
+
+	if (G.iface_exists) {
+		bb_error_msg("link is %s", iface_status_str);
+	}
+
+	if ((!(opts & FLAG_NO_STARTUP)
+	     && iface_status == IFSTATUS_UP
+	    )
+	 || (opts & FLAG_INITIAL_DOWN)
+	) {
+		if (run_script(iface_status_str) != 0)
+			goto exiting;
+	}
+
+	/* Main loop */
+	netlink_pollfd[0].fd = netlink_fd;
+	netlink_pollfd[0].events = POLLIN;
+	delay_time = 0;
+	while (1) {
+		int iface_status_old;
+		int iface_exists_old;
+
+		switch (bb_got_signal) {
+		case SIGINT:
+		case SIGTERM:
+			bb_got_signal = 0;
+			goto cleanup;
+		case SIGQUIT:
+			bb_got_signal = 0;
+			goto exiting;
+		default:
+			bb_got_signal = 0;
+			break;
+		}
+
+		if (poll(netlink_pollfd,
+				(opts & FLAG_MONITOR) ? 1 : 0,
+				G.poll_time
+			) < 0
+		) {
+			if (errno == EINTR)
+				continue;
+			bb_perror_msg("poll");
+			goto exiting;
+		}
+
+		iface_status_old = iface_status;
+		iface_exists_old = G.iface_exists;
+
+		if ((opts & FLAG_MONITOR)
+		 && (netlink_pollfd[0].revents & POLLIN)
+		) {
+			G.iface_exists = check_existence_through_netlink();
+			if (G.iface_exists < 0) /* error */
+				goto exiting;
+			if (iface_exists_old != G.iface_exists) {
+				bb_error_msg("interface %sappeared",
+						G.iface_exists ? "" : "dis");
+				if (G.iface_exists)
+					maybe_up_new_iface();
+			}
+		}
+
+		/* note: if !G.iface_exists, returns DOWN */
+		iface_status = detect_link();
+		if (iface_status == IFSTATUS_ERR) {
+			if (!(opts & FLAG_MONITOR))
+				goto exiting;
+			iface_status = IFSTATUS_DOWN;
+		}
+		iface_status_str = strstatus(iface_status);
+
+		if (iface_status_old != iface_status) {
+			bb_error_msg("link is %s", iface_status_str);
+
+			if (delay_time) {
+				/* link restored its old status before
+				 * we run script. don't run the script: */
+				delay_time = 0;
+			} else {
+				delay_time = monotonic_sec();
+				if (iface_status == IFSTATUS_UP)
+					delay_time += G.delay_up;
+				if (iface_status == IFSTATUS_DOWN)
+					delay_time += G.delay_down;
+				if (delay_time == 0)
+					delay_time++;
+			}
+		}
+
+		if (delay_time && (int)(monotonic_sec() - delay_time) >= 0) {
+			delay_time = 0;
+			if (run_script(iface_status_str) != 0)
+				goto exiting;
+		}
+	} /* while (1) */
+
+ cleanup:
+	if (!(opts & FLAG_NO_SHUTDOWN)
+	 && (iface_status == IFSTATUS_UP
+	     || (iface_status == IFSTATUS_DOWN && delay_time)
+	    )
+	) {
+		setenv(IFPLUGD_ENV_PREVIOUS, strstatus(iface_status), 1);
+		setenv(IFPLUGD_ENV_CURRENT, strstatus(-1), 1);
+		run_script("down\0up"); /* reusing string */
+	}
+
+ exiting:
+	remove_pidfile(pidfile_name);
+	bb_error_msg_and_die("exiting");
+}
diff --git a/busybox-1.19.3/networking/ifupdown.c b/busybox-1.19.3/networking/ifupdown.c
new file mode 100644
index 0000000..3820330
--- /dev/null
+++ b/busybox-1.19.3/networking/ifupdown.c
@@ -0,0 +1,1352 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  ifupdown for busybox
+ *  Copyright (c) 2002 Glenn McGrath
+ *  Copyright (c) 2003-2004 Erik Andersen <andersen@codepoet.org>
+ *
+ *  Based on ifupdown v 0.6.4 by Anthony Towns
+ *  Copyright (c) 1999 Anthony Towns <aj@azure.humbug.org.au>
+ *
+ *  Changes to upstream version
+ *  Remove checks for kernel version, assume kernel version 2.2.0 or better.
+ *  Lines in the interfaces file cannot wrap.
+ *  To adhere to the FHS, the default state file is /var/run/ifstate
+ *  (defined via CONFIG_IFUPDOWN_IFSTATE_PATH) and can be overridden by build
+ *  configuration.
+ *
+ * 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
+#endif
+
+#define UDHCPC_CMD_OPTIONS CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS
+
+#define debug_noise(args...) /*fprintf(stderr, args)*/
+
+/* Forward declaration */
+struct interface_defn_t;
+
+typedef int execfn(char *command);
+
+struct method_t {
+	const char *name;
+	int (*up)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
+	int (*down)(struct interface_defn_t *ifd, execfn *e) FAST_FUNC;
+};
+
+struct address_family_t {
+	const char *name;
+	int n_methods;
+	const struct method_t *method;
+};
+
+struct mapping_defn_t {
+	struct mapping_defn_t *next;
+
+	int max_matches;
+	int n_matches;
+	char **match;
+
+	char *script;
+
+	int max_mappings;
+	int n_mappings;
+	char **mapping;
+};
+
+struct variable_t {
+	char *name;
+	char *value;
+};
+
+struct interface_defn_t {
+	const struct address_family_t *address_family;
+	const struct method_t *method;
+
+	char *iface;
+	int max_options;
+	int n_options;
+	struct variable_t *option;
+};
+
+struct interfaces_file_t {
+	llist_t *autointerfaces;
+	llist_t *ifaces;
+	struct mapping_defn_t *mappings;
+};
+
+
+#define OPTION_STR "anvf" IF_FEATURE_IFUPDOWN_MAPPING("m") "i:"
+enum {
+	OPT_do_all      = 0x1,
+	OPT_no_act      = 0x2,
+	OPT_verbose     = 0x4,
+	OPT_force       = 0x8,
+	OPT_no_mappings = 0x10,
+};
+#define DO_ALL      (option_mask32 & OPT_do_all)
+#define NO_ACT      (option_mask32 & OPT_no_act)
+#define VERBOSE     (option_mask32 & OPT_verbose)
+#define FORCE       (option_mask32 & OPT_force)
+#define NO_MAPPINGS (option_mask32 & OPT_no_mappings)
+
+
+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)
+
+
+#if ENABLE_FEATURE_IFUPDOWN_IPV4 || ENABLE_FEATURE_IFUPDOWN_IPV6
+
+static void addstr(char **bufp, const char *str, size_t str_length)
+{
+	/* xasprintf trick will be smaller, but we are often
+	 * called with str_length == 1 - don't want to have
+	 * THAT much of malloc/freeing! */
+	char *buf = *bufp;
+	int len = (buf ? strlen(buf) : 0);
+	str_length++;
+	buf = xrealloc(buf, len + str_length);
+	/* copies at most str_length-1 chars! */
+	safe_strncpy(buf + len, str, str_length);
+	*bufp = buf;
+}
+
+static int strncmpz(const char *l, const char *r, size_t llen)
+{
+	int i = strncmp(l, r, llen);
+
+	if (i == 0)
+		return - (unsigned char)r[llen];
+	return i;
+}
+
+static char *get_var(const char *id, size_t idlen, struct interface_defn_t *ifd)
+{
+	int i;
+
+	if (strncmpz(id, "iface", idlen) == 0) {
+		// ubuntu's ifup doesn't do this:
+		//static char *label_buf;
+		//char *result;
+		//free(label_buf);
+		//label_buf = xstrdup(ifd->iface);
+		// Remove virtual iface suffix
+		//result = strchrnul(label_buf, ':');
+		//*result = '\0';
+		//return label_buf;
+
+		return ifd->iface;
+	}
+	if (strncmpz(id, "label", idlen) == 0) {
+		return ifd->iface;
+	}
+	for (i = 0; i < ifd->n_options; i++) {
+		if (strncmpz(id, ifd->option[i].name, idlen) == 0) {
+			return ifd->option[i].value;
+		}
+	}
+	return NULL;
+}
+
+# if ENABLE_FEATURE_IFUPDOWN_IP
+static int count_netmask_bits(const char *dotted_quad)
+{
+//	int result;
+//	unsigned a, b, c, d;
+//	/* Found a netmask...  Check if it is dotted quad */
+//	if (sscanf(dotted_quad, "%u.%u.%u.%u", &a, &b, &c, &d) != 4)
+//		return -1;
+//	if ((a|b|c|d) >> 8)
+//		return -1; /* one of numbers is >= 256 */
+//	d |= (a << 24) | (b << 16) | (c << 8); /* IP */
+//	d = ~d; /* 11110000 -> 00001111 */
+
+	/* Shorter version */
+	int result;
+	struct in_addr ip;
+	unsigned d;
+
+	if (inet_aton(dotted_quad, &ip) == 0)
+		return -1; /* malformed dotted IP */
+	d = ntohl(ip.s_addr); /* IP in host order */
+	d = ~d; /* 11110000 -> 00001111 */
+	if (d & (d+1)) /* check that it is in 00001111 form */
+		return -1; /* no it is not */
+	result = 32;
+	while (d) {
+		d >>= 1;
+		result--;
+	}
+	return result;
+}
+# 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 };
+	int opt_depth = 1;
+	char *result = NULL;
+
+	while (*command) {
+		switch (*command) {
+		default:
+			addstr(&result, command, 1);
+			command++;
+			break;
+		case '\\':
+			if (command[1]) {
+				addstr(&result, command + 1, 1);
+				command += 2;
+			} else {
+				addstr(&result, command, 1);
+				command++;
+			}
+			break;
+		case '[':
+			if (command[1] == '[' && opt_depth < MAX_OPT_DEPTH) {
+				old_pos[opt_depth] = result ? strlen(result) : 0;
+				okay[opt_depth] = 1;
+				opt_depth++;
+				command += 2;
+			} else {
+				addstr(&result, "[", 1);
+				command++;
+			}
+			break;
+		case ']':
+			if (command[1] == ']' && opt_depth > 1) {
+				opt_depth--;
+				if (!okay[opt_depth]) {
+					result[old_pos[opt_depth]] = '\0';
+				}
+				command += 2;
+			} else {
+				addstr(&result, "]", 1);
+				command++;
+			}
+			break;
+		case '%':
+			{
+				char *nextpercent;
+				char *varvalue;
+
+				command++;
+				nextpercent = strchr(command, '%');
+				if (!nextpercent) {
+					errno = EUNBALPER;
+					free(result);
+					return NULL;
+				}
+
+				varvalue = get_var(command, nextpercent - command, ifd);
+
+				if (varvalue) {
+# 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
+					addstr(&result, varvalue, strlen(varvalue));
+				} else {
+# 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) {
+						unsigned res;
+						varvalue = get_var("netmask", 7, ifd);
+						if (varvalue) {
+							res = count_netmask_bits(varvalue);
+							if (res > 0) {
+								const char *argument = utoa(res);
+								addstr(&result, argument, strlen(argument));
+								command = nextpercent + 1;
+								break;
+							}
+						}
+					}
+# endif
+					okay[opt_depth - 1] = 0;
+				}
+
+				command = nextpercent + 1;
+			}
+			break;
+		}
+	}
+
+	if (opt_depth > 1) {
+		errno = EUNBALBRACK;
+		free(result);
+		return NULL;
+	}
+
+	if (!okay[0]) {
+		errno = EUNDEFVAR;
+		free(result);
+		return NULL;
+	}
+
+	return result;
+}
+
+/* execute() returns 1 for success and 0 for failure */
+static int execute(const char *command, struct interface_defn_t *ifd, execfn *exec)
+{
+	char *out;
+	int ret;
+
+	out = parse(command, ifd);
+	if (!out) {
+		/* parse error? */
+		return 0;
+	}
+	/* out == "": parsed ok but not all needed variables known, skip */
+	ret = out[0] ? (*exec)(out) : 1;
+
+	free(out);
+	if (ret != 1) {
+		return 0;
+	}
+	return 1;
+}
+
+#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
+	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
+	return execute("ifconfig %iface% add ::1", ifd, exec);
+# endif
+}
+
+static int FAST_FUNC loopback_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+# if ENABLE_FEATURE_IFUPDOWN_IP
+	return execute("ip link set %iface% down", ifd, exec);
+# else
+	return execute("ifconfig %iface% del ::1", ifd, exec);
+# 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
+	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("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
+	return ((result == 3) ? 3 : 0);
+}
+
+static int FAST_FUNC static_down6(struct interface_defn_t *ifd, execfn *exec)
+{
+# if ENABLE_FEATURE_IFUPDOWN_IP
+	return execute("ip link set %iface% down", ifd, exec);
+# else
+	return execute("ifconfig %iface% down", ifd, exec);
+# endif
+}
+
+# if ENABLE_FEATURE_IFUPDOWN_IP
+static int FAST_FUNC v4tunnel_up(struct interface_defn_t *ifd, execfn *exec)
+{
+	int result;
+	result = execute("ip tunnel add %iface% mode sit remote "
+			"%endpoint%[[ local %local%]][[ ttl %ttl%]]", ifd, exec);
+	result += execute("ip link set %iface% up", ifd, exec);
+	result += execute("ip addr add %address%/%netmask% dev %iface%", ifd, exec);
+	result += execute("[[ip route add ::/0 via %gateway%]]", ifd, exec);
+	return ((result == 4) ? 4 : 0);
+}
+
+static int FAST_FUNC v4tunnel_down(struct interface_defn_t * ifd, execfn * exec)
+{
+	return execute("ip tunnel del %iface%", ifd, exec);
+}
+# endif
+
+static const struct method_t methods6[] = {
+# 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 = {
+	"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
+	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
+	return execute("ifconfig %iface% 127.0.0.1 up", ifd, exec);
+# endif
+}
+
+static int FAST_FUNC loopback_down(struct interface_defn_t *ifd, execfn *exec)
+{
+# 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
+	return execute("ifconfig %iface% 127.0.0.1 down", ifd, exec);
+# endif
+}
+
+static int FAST_FUNC static_up(struct interface_defn_t *ifd, execfn *exec)
+{
+	int result;
+# 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);
+	return ((result == 3) ? 3 : 0);
+# 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",
+				ifd, exec);
+	result += execute("ifconfig %iface% %address% netmask %netmask%"
+				"[[ broadcast %broadcast%]][[ pointopoint %pointopoint%]] ",
+				ifd, exec);
+	result += execute("[[route add default gw %gateway% %iface%]]", ifd, exec);
+	return ((result == 3) ? 3 : 0);
+# endif
+}
+
+static int FAST_FUNC static_down(struct interface_defn_t *ifd, execfn *exec)
+{
+	int result;
+# if ENABLE_FEATURE_IFUPDOWN_IP
+	result = execute("ip addr flush dev %iface%", ifd, exec);
+	result += execute("ip link set %iface% down", ifd, exec);
+# 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
+	return ((result == 2) ? 2 : 0);
+}
+
+# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+struct dhcp_client_t {
+	const char *name;
+	const char *startcmd;
+	const char *stopcmd;
+};
+
+static const struct dhcp_client_t ext_dhcp_clients[] = {
+	{ "dhcpcd",
+		"dhcpcd[[ -h %hostname%]][[ -i %vendor%]][[ -I %client%]][[ -l %leasetime%]] %iface%",
+		"dhcpcd -k %iface%",
+	},
+	{ "dhclient",
+		"dhclient -pf /var/run/dhclient.%iface%.pid %iface%",
+		"kill -9 `cat /var/run/dhclient.%iface%.pid` 2>/dev/null",
+	},
+	{ "pump",
+		"pump -i %iface%[[ -h %hostname%]][[ -l %leasehours%]]",
+		"pump -i %iface% -k",
+	},
+	{ "udhcpc",
+		"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 /* FEATURE_IFUPDOWN_EXTERNAL_DHCPC */
+
+# 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
+	/* 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
+	/* needed if we have hwaddress on dhcp iface */
+	if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
+		return 0;
+#  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);
+	}
+	bb_error_msg("no dhcp clients found");
+	return 0;
+}
+# elif ENABLE_UDHCPC
+static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+#  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
+	/* needed if we have hwaddress on dhcp iface */
+	if (!execute("ifconfig %iface%[[ hw %hwaddress%]] up", ifd, exec))
+		return 0;
+#  endif
+	return execute("udhcpc " UDHCPC_CMD_OPTIONS " -p /var/run/udhcpc.%iface%.pid "
+			"-i %iface%[[ -H %hostname%]][[ -c %client%]][[ -s %script%]][[ %udhcpc_opts%]]",
+			ifd, exec);
+}
+# else
+static int FAST_FUNC dhcp_up(struct interface_defn_t *ifd UNUSED_PARAM,
+		execfn *exec UNUSED_PARAM)
+{
+	return 0; /* no dhcp support */
+}
+# endif
+
+# if ENABLE_FEATURE_IFUPDOWN_EXTERNAL_DHCP
+static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+	int result = 0;
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(ext_dhcp_clients); i++) {
+		if (exists_execable(ext_dhcp_clients[i].name)) {
+			result = execute(ext_dhcp_clients[i].stopcmd, ifd, exec);
+			if (result)
+				break;
+		}
+	}
+
+	if (!result)
+		bb_error_msg("warning: no dhcp clients found and stopped");
+
+	/* Sleep a bit, otherwise static_down tries to bring down interface too soon,
+	   and it may come back up because udhcpc is still shutting down */
+	usleep(100000);
+	result += static_down(ifd, exec);
+	return ((result == 3) ? 3 : 0);
+}
+# elif ENABLE_UDHCPC
+static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+	int result;
+	result = execute(
+		"test -f /var/run/udhcpc.%iface%.pid && "
+		"kill `cat /var/run/udhcpc.%iface%.pid` 2>/dev/null",
+		ifd, exec);
+	/* Also bring the hardware interface down since
+	   killing the dhcp client alone doesn't do it.
+	   This enables consecutive ifup->ifdown->ifup */
+	/* Sleep a bit, otherwise static_down tries to bring down interface too soon,
+	   and it may come back up because udhcpc is still shutting down */
+	usleep(100000);
+	result += static_down(ifd, exec);
+	return ((result == 3) ? 3 : 0);
+}
+# else
+static int FAST_FUNC dhcp_down(struct interface_defn_t *ifd UNUSED_PARAM,
+		execfn *exec UNUSED_PARAM)
+{
+	return 0; /* no dhcp support */
+}
+# endif
+
+static int FAST_FUNC manual_up_down(struct interface_defn_t *ifd UNUSED_PARAM, execfn *exec UNUSED_PARAM)
+{
+	return 1;
+}
+
+static int FAST_FUNC bootp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+	return execute("bootpc[[ --bootfile %bootfile%]] --dev %iface%"
+			"[[ --server %server%]][[ --hwaddr %hwaddr%]]"
+			" --returniffail --serverbcast", ifd, exec);
+}
+
+static int FAST_FUNC ppp_up(struct interface_defn_t *ifd, execfn *exec)
+{
+	return execute("pon[[ %provider%]]", ifd, exec);
+}
+
+static int FAST_FUNC ppp_down(struct interface_defn_t *ifd, execfn *exec)
+{
+	return execute("poff[[ %provider%]]", ifd, exec);
+}
+
+static int FAST_FUNC wvdial_up(struct interface_defn_t *ifd, execfn *exec)
+{
+	return execute("start-stop-daemon --start -x wvdial "
+		"-p /var/run/wvdial.%iface% -b -m --[[ %provider%]]", ifd, exec);
+}
+
+static int FAST_FUNC wvdial_down(struct interface_defn_t *ifd, execfn *exec)
+{
+	return execute("start-stop-daemon --stop -x wvdial "
+			"-p /var/run/wvdial.%iface% -s 2", ifd, exec);
+}
+
+static const struct method_t methods[] = {
+	{ "manual"  , manual_up_down, manual_up_down, },
+	{ "wvdial"  , wvdial_up     , wvdial_down   , },
+	{ "ppp"     , ppp_up        , ppp_down      , },
+	{ "static"  , static_up     , static_down   , },
+	{ "bootp"   , bootp_up      , static_down   , },
+	{ "dhcp"    , dhcp_up       , dhcp_down     , },
+	{ "loopback", loopback_up   , loopback_down , },
+};
+
+static const struct address_family_t addr_inet = {
+	"inet",
+	ARRAY_SIZE(methods),
+	methods
+};
+
+#endif  /* FEATURE_IFUPDOWN_IPV4 */
+
+
+/* Returns pointer to the next word, or NULL.
+ * In 1st case, advances *buf to the word after this one.
+ */
+static char *next_word(char **buf)
+{
+	unsigned length;
+	char *word;
+
+	/* Skip over leading whitespace */
+	word = skip_whitespace(*buf);
+
+	/* Stop on EOL */
+	if (*word == '\0')
+		return NULL;
+
+	/* Find the length of this word (can't be 0) */
+	length = strcspn(word, " \t\n");
+
+	/* Unless we are already at NUL, store NUL and advance */
+	if (word[length] != '\0')
+		word[length++] = '\0';
+
+	*buf = skip_whitespace(word + length);
+
+	return word;
+}
+
+static const struct address_family_t *get_address_family(const struct address_family_t *const af[], char *name)
+{
+	int i;
+
+	if (!name)
+		return NULL;
+
+	for (i = 0; af[i]; i++) {
+		if (strcmp(af[i]->name, name) == 0) {
+			return af[i];
+		}
+	}
+	return NULL;
+}
+
+static const struct method_t *get_method(const struct address_family_t *af, char *name)
+{
+	int i;
+
+	if (!name)
+		return NULL;
+	/* TODO: use index_in_str_array() */
+	for (i = 0; i < af->n_methods; i++) {
+		if (strcmp(af->method[i].name, name) == 0) {
+			return &af->method[i];
+		}
+	}
+	return NULL;
+}
+
+static struct interfaces_file_t *read_interfaces(const char *filename)
+{
+	/* Let's try to be compatible.
+	 *
+	 * "man 5 interfaces" says:
+	 * Lines starting with "#" are ignored. Note that end-of-line
+	 * comments are NOT supported, comments must be on a line of their own.
+	 * A line may be extended across multiple lines by making
+	 * the last character a backslash.
+	 *
+	 * Seen elsewhere in example config file:
+	 * A first non-blank "#" character makes the rest of the line
+	 * 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.
+	 */
+#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));
+	f = xfopen_for_read(filename);
+
+	while ((buf = xmalloc_fgetline(f)) != NULL) {
+#if ENABLE_DESKTOP
+		/* Trailing "\" concatenates lines */
+		char *p;
+		while ((p = last_char_is(buf, '\\')) != NULL) {
+			*p = '\0';
+			rest_of_line = xmalloc_fgetline(f);
+			if (!rest_of_line)
+				break;
+			p = xasprintf("%s%s", buf, rest_of_line);
+			free(buf);
+			free(rest_of_line);
+			buf = p;
+		}
+#endif
+		rest_of_line = buf;
+		first_word = next_word(&rest_of_line);
+		if (!first_word || *first_word == '#') {
+			free(buf);
+			continue; /* blank/comment line */
+		}
+
+		if (strcmp(first_word, "mapping") == 0) {
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+			currmap = xzalloc(sizeof(*currmap));
+
+			while ((first_word = next_word(&rest_of_line)) != NULL) {
+				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;*/
+			{
+				struct mapping_defn_t **where = &defn->mappings;
+				while (*where != NULL) {
+					where = &(*where)->next;
+				}
+				*where = currmap;
+				/*currmap->next = NULL;*/
+			}
+			debug_noise("Added mapping\n");
+#endif
+			currently_processing = MAPPING;
+		} else if (strcmp(first_word, "iface") == 0) {
+			static const struct address_family_t *const addr_fams[] = {
+#if ENABLE_FEATURE_IFUPDOWN_IPV4
+				&addr_inet,
+#endif
+#if ENABLE_FEATURE_IFUPDOWN_IPV6
+				&addr_inet6,
+#endif
+				NULL
+			};
+			char *iface_name;
+			char *address_family_name;
+			char *method_name;
+			llist_t *iface_list;
+
+			currif = xzalloc(sizeof(*currif));
+			iface_name = next_word(&rest_of_line);
+			address_family_name = next_word(&rest_of_line);
+			method_name = next_word(&rest_of_line);
+
+			if (method_name == NULL)
+				bb_error_msg_and_die("too few parameters for line \"%s\"", buf);
+
+			/* ship any trailing whitespace */
+			rest_of_line = skip_whitespace(rest_of_line);
+
+			if (rest_of_line[0] != '\0' /* && rest_of_line[0] != '#' */)
+				bb_error_msg_and_die("too many parameters \"%s\"", buf);
+
+			currif->iface = xstrdup(iface_name);
+
+			currif->address_family = get_address_family(addr_fams, address_family_name);
+			if (!currif->address_family)
+				bb_error_msg_and_die("unknown address type \"%s\"", address_family_name);
+
+			currif->method = get_method(currif->address_family, method_name);
+			if (!currif->method)
+				bb_error_msg_and_die("unknown method \"%s\"", method_name);
+
+			for (iface_list = defn->ifaces; iface_list; iface_list = iface_list->link) {
+				struct interface_defn_t *tmp = (struct interface_defn_t *) iface_list->data;
+				if ((strcmp(tmp->iface, currif->iface) == 0)
+				 && (tmp->address_family == currif->address_family)
+				) {
+					bb_error_msg_and_die("duplicate interface \"%s\"", tmp->iface);
+				}
+			}
+			llist_add_to_end(&(defn->ifaces), (char*)currif);
+
+			debug_noise("iface %s %s %s\n", currif->iface, address_family_name, method_name);
+			currently_processing = IFACE;
+		} else if (strcmp(first_word, "auto") == 0) {
+			while ((first_word = next_word(&rest_of_line)) != NULL) {
+
+				/* Check the interface isnt already listed */
+				if (llist_find_str(defn->autointerfaces, first_word)) {
+					bb_perror_msg_and_die("interface declared auto twice \"%s\"", buf);
+				}
+
+				/* Add the interface to the list */
+				llist_add_to_end(&(defn->autointerfaces), xstrdup(first_word));
+				debug_noise("\nauto %s\n", first_word);
+			}
+			currently_processing = NONE;
+		} 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
+				) {
+					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[currif->n_options].name = xstrdup(first_word);
+				currif->option[currif->n_options].value = xstrdup(rest_of_line);
+				currif->n_options++;
+				break;
+			case MAPPING:
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+				if (strcmp(first_word, "script") == 0) {
+					if (currmap->script != NULL)
+						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[currmap->n_mappings] = xstrdup(next_word(&rest_of_line));
+					currmap->n_mappings++;
+				} else {
+					bb_error_msg_and_die("misplaced option \"%s\"", buf);
+				}
+#endif
+				break;
+			case NONE:
+			default:
+				bb_error_msg_and_die("misplaced option \"%s\"", buf);
+			}
+		}
+		free(buf);
+	} /* while (fgets) */
+
+	if (ferror(f) != 0) {
+		/* ferror does NOT set errno! */
+		bb_error_msg_and_die("%s: I/O error", filename);
+	}
+	fclose(f);
+
+	return defn;
+}
+
+static char *setlocalenv(const char *format, const char *name, const char *value)
+{
+	char *result;
+	char *dst;
+	char *src;
+	char c;
+
+	result = xasprintf(format, name, value);
+
+	for (dst = src = result; (c = *src) != '=' && c; src++) {
+		if (c == '-')
+			c = '_';
+		if (c >= 'a' && c <= 'z')
+			c -= ('a' - 'A');
+		if (isalnum(c) || c == '_')
+			*dst++ = c;
+	}
+	overlapping_strcpy(dst, src);
+
+	return result;
+}
+
+static void set_environ(struct interface_defn_t *iface, const char *mode)
+{
+	int i;
+	char **pp;
+
+	if (G.my_environ != NULL) {
+		for (pp = G.my_environ; *pp; pp++) {
+			free(*pp);
+		}
+		free(G.my_environ);
+	}
+
+	/* note: last element will stay NULL: */
+	G.my_environ = xzalloc(sizeof(char *) * (iface->n_options + 6));
+	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
+		) {
+			continue;
+		}
+		*pp++ = setlocalenv("IF_%s=%s", iface->option[i].name, iface->option[i].value);
+	}
+
+	*pp++ = setlocalenv("%s=%s", "IFACE", iface->iface);
+	*pp++ = setlocalenv("%s=%s", "ADDRFAM", iface->address_family->name);
+	*pp++ = setlocalenv("%s=%s", "METHOD", iface->method->name);
+	*pp++ = setlocalenv("%s=%s", "MODE", mode);
+	if (G.startup_PATH)
+		*pp++ = setlocalenv("%s=%s", "PATH", G.startup_PATH);
+}
+
+static int doit(char *str)
+{
+	if (option_mask32 & (OPT_no_act|OPT_verbose)) {
+		puts(str);
+	}
+	if (!(option_mask32 & OPT_no_act)) {
+		pid_t child;
+		int status;
+
+		fflush_all();
+		child = vfork();
+		if (child < 0) /* failure */
+			return 0;
+		if (child == 0) { /* child */
+			execle(G.shell, G.shell, "-c", str, (char *) NULL, G.my_environ);
+			_exit(127);
+		}
+		safe_waitpid(child, &status, 0);
+		if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static int execute_all(struct interface_defn_t *ifd, const char *opt)
+{
+	int i;
+	char *buf;
+	for (i = 0; i < ifd->n_options; i++) {
+		if (strcmp(ifd->option[i].name, opt) == 0) {
+			if (!doit(ifd->option[i].value)) {
+				return 0;
+			}
+		}
+	}
+
+	buf = xasprintf("run-parts /etc/network/if-%s.d", opt);
+	/* heh, we don't bother free'ing it */
+	return doit(buf);
+}
+
+static int check(char *str)
+{
+	return str != NULL;
+}
+
+static int iface_up(struct interface_defn_t *iface)
+{
+	if (!iface->method->up(iface, check)) return -1;
+	set_environ(iface, "start");
+	if (!execute_all(iface, "pre-up")) return 0;
+	if (!iface->method->up(iface, doit)) return 0;
+	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 (!execute_all(iface, "down")) return 0;
+	if (!iface->method->down(iface, doit)) return 0;
+	if (!execute_all(iface, "post-down")) return 0;
+	return 1;
+}
+
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+static int popen2(FILE **in, FILE **out, char *command, char *param)
+{
+	char *argv[3] = { command, param, NULL };
+	struct fd_pair infd, outfd;
+	pid_t pid;
+
+	xpiped_pair(infd);
+	xpiped_pair(outfd);
+
+	fflush_all();
+	pid = xvfork();
+
+	if (pid == 0) {
+		/* Child */
+		/* NB: close _first_, then move fds! */
+		close(infd.wr);
+		close(outfd.rd);
+		xmove_fd(infd.rd, 0);
+		xmove_fd(outfd.wr, 1);
+		BB_EXECVP_or_die(argv);
+	}
+	/* parent */
+	close(infd.rd);
+	close(outfd.wr);
+	*in = xfdopen_for_write(infd.wr);
+	*out = xfdopen_for_read(outfd.rd);
+	return pid;
+}
+
+static char *run_mapping(char *physical, struct mapping_defn_t *map)
+{
+	FILE *in, *out;
+	int i, status;
+	pid_t pid;
+
+	char *logical = xstrdup(physical);
+
+	/* Run the mapping script. Never fails. */
+	pid = popen2(&in, &out, map->script, physical);
+
+	/* Write mappings to stdin of mapping script. */
+	for (i = 0; i < map->n_mappings; i++) {
+		fprintf(in, "%s\n", map->mapping[i]);
+	}
+	fclose(in);
+	safe_waitpid(pid, &status, 0);
+
+	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+		/* If the mapping script exited successfully, try to
+		 * grab a line of output and use that as the name of the
+		 * logical interface. */
+		char *new_logical = xmalloc_fgetline(out);
+
+		if (new_logical) {
+			/* If we are able to read a line of output from the script,
+			 * remove any trailing whitespace and use this value
+			 * as the name of the logical interface. */
+			char *pch = new_logical + strlen(new_logical) - 1;
+
+			while (pch >= new_logical && isspace(*pch))
+				*(pch--) = '\0';
+
+			free(logical);
+			logical = new_logical;
+		}
+	}
+
+	fclose(out);
+
+	return logical;
+}
+#endif /* FEATURE_IFUPDOWN_MAPPING */
+
+static llist_t *find_iface_state(llist_t *state_list, const char *iface)
+{
+	unsigned iface_len = strlen(iface);
+	llist_t *search = state_list;
+
+	while (search) {
+		if ((strncmp(search->data, iface, iface_len) == 0)
+		 && (search->data[iface_len] == '=')
+		) {
+			return search;
+		}
+		search = search->link;
+	}
+	return NULL;
+}
+
+/* read the previous state from the state file */
+static llist_t *read_iface_state(void)
+{
+	llist_t *state_list = NULL;
+	FILE *state_fp = fopen_for_read(CONFIG_IFUPDOWN_IFSTATE_PATH);
+
+	if (state_fp) {
+		char *start, *end_ptr;
+		while ((start = xmalloc_fgets(state_fp)) != NULL) {
+			/* We should only need to check for a single character */
+			end_ptr = start + strcspn(start, " \t\n");
+			*end_ptr = '\0';
+			llist_add_to(&state_list, start);
+		}
+		fclose(state_fp);
+	}
+	return state_list;
+}
+
+
+int ifupdown_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ifupdown_main(int argc UNUSED_PARAM, char **argv)
+{
+	int (*cmds)(struct interface_defn_t *);
+	struct interfaces_file_t *defn;
+	llist_t *target_list = NULL;
+	const char *interfaces = "/etc/network/interfaces";
+	bool any_failures = 0;
+
+	INIT_G();
+
+	G.startup_PATH = getenv("PATH");
+	G.shell = xstrdup(get_shell_name());
+
+	cmds = iface_down;
+	if (applet_name[2] == 'u') {
+		/* ifup command */
+		cmds = iface_up;
+	}
+
+	getopt32(argv, OPTION_STR, &interfaces);
+	argv += optind;
+	if (argv[0]) {
+		if (DO_ALL) bb_show_usage();
+	} else {
+		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);
+
+	/* Create a list of interfaces to work on */
+	if (DO_ALL) {
+		target_list = defn->autointerfaces;
+	} else {
+		llist_add_to_end(&target_list, argv[0]);
+	}
+
+	/* Update the interfaces */
+	while (target_list) {
+		llist_t *iface_list;
+		struct interface_defn_t *currif;
+		char *iface;
+		char *liface;
+		char *pch;
+		bool okay = 0;
+		int cmds_ret;
+
+		iface = xstrdup(target_list->data);
+		target_list = target_list->link;
+
+		pch = strchr(iface, '=');
+		if (pch) {
+			*pch = '\0';
+			liface = xstrdup(pch + 1);
+		} else {
+			liface = xstrdup(iface);
+		}
+
+		if (!FORCE) {
+			llist_t *state_list = read_iface_state();
+			const llist_t *iface_state = find_iface_state(state_list, iface);
+
+			if (cmds == iface_up) {
+				/* ifup */
+				if (iface_state) {
+					bb_error_msg("interface %s already configured", iface);
+					goto next;
+				}
+			} else {
+				/* ifdown */
+				if (!iface_state) {
+					bb_error_msg("interface %s not configured", iface);
+					goto next;
+				}
+			}
+			llist_free(state_list, free);
+		}
+
+#if ENABLE_FEATURE_IFUPDOWN_MAPPING
+		if ((cmds == iface_up) && !NO_MAPPINGS) {
+			struct mapping_defn_t *currmap;
+
+			for (currmap = defn->mappings; currmap; currmap = currmap->next) {
+				int i;
+				for (i = 0; i < currmap->n_matches; i++) {
+					if (fnmatch(currmap->match[i], liface, 0) != 0)
+						continue;
+					if (VERBOSE) {
+						printf("Running mapping script %s on %s\n", currmap->script, liface);
+					}
+					liface = run_mapping(iface, currmap);
+					break;
+				}
+			}
+		}
+#endif
+
+		iface_list = defn->ifaces;
+		while (iface_list) {
+			currif = (struct interface_defn_t *) iface_list->data;
+			if (strcmp(liface, currif->iface) == 0) {
+				char *oldiface = currif->iface;
+
+				okay = 1;
+				currif->iface = iface;
+
+				debug_noise("\nConfiguring interface %s (%s)\n", liface, currif->address_family->name);
+
+				/* Call the cmds function pointer, does either iface_up() or iface_down() */
+				cmds_ret = cmds(currif);
+				if (cmds_ret == -1) {
+					bb_error_msg("don't seem to have all the variables for %s/%s",
+							liface, currif->address_family->name);
+					any_failures = 1;
+				} else if (cmds_ret == 0) {
+					any_failures = 1;
+				}
+
+				currif->iface = oldiface;
+			}
+			iface_list = iface_list->link;
+		}
+		if (VERBOSE) {
+			bb_putchar('\n');
+		}
+
+		if (!okay && !FORCE) {
+			bb_error_msg("ignoring unknown interface %s", liface);
+			any_failures = 1;
+		} else if (!NO_ACT) {
+			/* update the state file */
+			FILE *state_fp;
+			llist_t *state;
+			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) {
+					llist_add_to_end(&state_list, newiface);
+				} else {
+					free(iface_state->data);
+					iface_state->data = newiface;
+				}
+			} else {
+				/* Remove an interface from state_list */
+				llist_unlink(&state_list, iface_state);
+				free(llist_pop(&iface_state));
+			}
+
+			/* Actually write the new state */
+			state_fp = xfopen_for_write(CONFIG_IFUPDOWN_IFSTATE_PATH);
+			state = state_list;
+			while (state) {
+				if (state->data) {
+					fprintf(state_fp, "%s\n", state->data);
+				}
+				state = state->link;
+			}
+			fclose(state_fp);
+			llist_free(state_list, free);
+		}
+ next:
+		free(iface);
+		free(liface);
+	}
+
+	return any_failures;
+}
diff --git a/busybox-1.19.3/networking/inetd.c b/busybox-1.19.3/networking/inetd.c
new file mode 100644
index 0000000..05ad8c7
--- /dev/null
+++ b/busybox-1.19.3/networking/inetd.c
@@ -0,0 +1,1635 @@
+/* vi: set sw=4 ts=4: */
+/*      $Slackware: inetd.c 1.79s 2001/02/06 13:18:00 volkerdi Exp $    */
+/*      $OpenBSD: inetd.c,v 1.79 2001/01/30 08:30:57 deraadt Exp $      */
+/*      $NetBSD: inetd.c,v 1.11 1996/02/22 11:14:41 mycroft Exp $       */
+/* Busybox port by Vladimir Oleynik (C) 2001-2005 <dzo@simtreas.ru>     */
+/* IPv6 support, many bug fixes by Denys Vlasenko (c) 2008 */
+/*
+ * Copyright (c) 1983,1991 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
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by the University of
+ *      California, Berkeley and its contributors.
+ * 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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+/* Inetd - Internet super-server
+ *
+ * This program invokes configured services when a connection
+ * from a peer is established or a datagram arrives.
+ * Connection-oriented services are invoked each time a
+ * connection is made, by creating a process.  This process
+ * is passed the connection as file descriptor 0 and is
+ * expected to do a getpeername to find out peer's host
+ * and port.
+ * Datagram oriented services are invoked when a datagram
+ * arrives; a process is created and passed a pending message
+ * on file descriptor 0. peer's address can be obtained
+ * using recvfrom.
+ *
+ * Inetd uses a configuration file which is read at startup
+ * and, possibly, at some later time in response to a hangup signal.
+ * The configuration file is "free format" with fields given in the
+ * order shown below.  Continuation lines for an entry must begin with
+ * a space or tab.  All fields must be present in each entry.
+ *
+ *      service_name                    must be in /etc/services
+ *      socket_type                     stream/dgram/raw/rdm/seqpacket
+ *      protocol                        must be in /etc/protocols
+ *                                      (usually "tcp" or "udp")
+ *      wait/nowait[.max]               single-threaded/multi-threaded, max #
+ *      user[.group] or user[:group]    user/group to run daemon as
+ *      server_program                  full path name
+ *      server_program_arguments        maximum of MAXARGS (20)
+ *
+ * For RPC services
+ *      service_name/version            must be in /etc/rpc
+ *      socket_type                     stream/dgram/raw/rdm/seqpacket
+ *      rpc/protocol                    "rpc/tcp" etc
+ *      wait/nowait[.max]               single-threaded/multi-threaded
+ *      user[.group] or user[:group]    user to run daemon as
+ *      server_program                  full path name
+ *      server_program_arguments        maximum of MAXARGS (20)
+ *
+ * For non-RPC services, the "service name" can be of the form
+ * hostaddress:servicename, in which case the hostaddress is used
+ * as the host portion of the address to listen on.  If hostaddress
+ * consists of a single '*' character, INADDR_ANY is used.
+ *
+ * A line can also consist of just
+ *      hostaddress:
+ * where hostaddress is as in the preceding paragraph.  Such a line must
+ * have no further fields; the specified hostaddress is remembered and
+ * used for all further lines that have no hostaddress specified,
+ * until the next such line (or EOF).  (This is why * is provided to
+ * allow explicit specification of INADDR_ANY.)  A line
+ *      *:
+ * is implicitly in effect at the beginning of the file.
+ *
+ * The hostaddress specifier may (and often will) contain dots;
+ * the service name must not.
+ *
+ * For RPC services, host-address specifiers are accepted and will
+ * work to some extent; however, because of limitations in the
+ * portmapper interface, it will not work to try to give more than
+ * one line for any given RPC service, even if the host-address
+ * specifiers are different.
+ *
+ * Comment lines are indicated by a '#' in column 1.
+ */
+
+/* inetd rules for passing file descriptors to children
+ * (http://www.freebsd.org/cgi/man.cgi?query=inetd):
+ *
+ * The wait/nowait entry specifies whether the server that is invoked by
+ * inetd will take over the socket associated with the service access point,
+ * and thus whether inetd should wait for the server to exit before listen-
+ * ing for new service requests.  Datagram servers must use "wait", as
+ * they are always invoked with the original datagram socket bound to the
+ * specified service address.  These servers must read at least one datagram
+ * from the socket before exiting.  If a datagram server connects to its
+ * peer, freeing the socket so inetd can receive further messages on the
+ * socket, it is said to be a "multi-threaded" server; it should read one
+ * datagram from the socket and create a new socket connected to the peer.
+ * It should fork, and the parent should then exit to allow inetd to check
+ * for new service requests to spawn new servers.  Datagram servers which
+ * process all incoming datagrams on a socket and eventually time out are
+ * said to be "single-threaded".  The comsat(8), biff(1) and talkd(8)
+ * utilities are both examples of the latter type of datagram server.  The
+ * tftpd(8) utility is an example of a multi-threaded datagram server.
+ *
+ * Servers using stream sockets generally are multi-threaded and use the
+ * "nowait" entry. Connection requests for these services are accepted by
+ * inetd, and the server is given only the newly-accepted socket connected
+ * to a client of the service.  Most stream-based services operate in this
+ * manner.  Stream-based servers that use "wait" are started with the lis-
+ * tening service socket, and must accept at least one connection request
+ * before exiting.  Such a server would normally accept and process incoming
+ * connection requests until a timeout.
+ */
+
+/* Despite of above doc saying that dgram services must use "wait",
+ * "udp nowait" servers are implemented in busyboxed inetd.
+ * IPv6 addresses are also implemented. However, they may look ugly -
+ * ":::service..." means "address '::' (IPv6 wildcard addr)":"service"...
+ * You have to put "tcp6"/"udp6" in protocol field to select IPv6.
+ */
+
+/* Here's the scoop concerning the user[:group] feature:
+ * 1) group is not specified:
+ *      a) user = root: NO setuid() or setgid() is done
+ *      b) other:       initgroups(name, primary group)
+ *                      setgid(primary group as found in passwd)
+ *                      setuid()
+ * 2) group is specified:
+ *      a) user = root: setgid(specified group)
+ *                      NO initgroups()
+ *                      NO setuid()
+ *      b) other:       initgroups(name, specified group)
+ *                      setgid(specified group)
+ *                      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/un.h>
+
+#include "libbb.h"
+
+#if ENABLE_FEATURE_INETD_RPC
+# 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
+/* stream version of chargen is forking but not execing,
+ * can't do that (easily) on NOMMU */
+#undef  ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+#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 */
+
+// TODO: explain, or get rid of setrlimit games
+
+#ifndef RLIMIT_NOFILE
+#define RLIMIT_NOFILE   RLIMIT_OFILE
+#endif
+
+#ifndef OPEN_MAX
+#define OPEN_MAX        64
+#endif
+
+/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */
+#define FD_MARGIN       8
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO    \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME    \
+ || ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+# define INETD_BUILTINS_ENABLED
+#endif
+
+typedef struct servtab_t {
+	/* The most frequently referenced one: */
+	int se_fd;                            /* open descriptor */
+	/* NB: 'biggest fields last' saves on code size (~250 bytes) */
+	/* [addr:]service socktype proto wait user[:group] prog [args] */
+	char *se_local_hostname;              /* addr to listen on */
+	char *se_service;                     /* "80" or "www" or "mount/2[-3]" */
+	/* socktype is in se_socktype */      /* "stream" "dgram" "raw" "rdm" "seqpacket" */
+	char *se_proto;                       /* "unix" or "[rpc/]tcp[6]" */
+#if ENABLE_FEATURE_INETD_RPC
+	int se_rpcprog;                       /* rpc program number */
+	int se_rpcver_lo;                     /* rpc program lowest version */
+	int se_rpcver_hi;                     /* rpc program highest version */
+#define is_rpc_service(sep)       ((sep)->se_rpcver_lo != 0)
+#else
+#define is_rpc_service(sep)       0
+#endif
+	pid_t se_wait;                        /* 0:"nowait", 1:"wait", >1:"wait" */
+	                                      /* and waiting for this pid */
+	socktype_t se_socktype;               /* SOCK_STREAM/DGRAM/RDM/... */
+	family_t se_family;                   /* AF_UNIX/INET[6] */
+	/* se_proto_no is used by RPC code only... hmm */
+	smallint se_proto_no;                 /* IPPROTO_TCP/UDP, n/a for AF_UNIX */
+	smallint se_checked;                  /* looked at during merge */
+	unsigned se_max;                      /* allowed instances per minute */
+	unsigned se_count;                    /* number started since se_time */
+	unsigned se_time;                     /* when we started counting */
+	char *se_user;                        /* user name to run as */
+	char *se_group;                       /* group name to run as, can be NULL */
+#ifdef INETD_BUILTINS_ENABLED
+	const struct builtin *se_builtin;     /* if built-in, description */
+#endif
+	struct servtab_t *se_next;
+	len_and_sockaddr *se_lsa;
+	char *se_program;                     /* server program */
+#define MAXARGV 20
+	char *se_argv[MAXARGV + 1];           /* program arguments */
+} servtab_t;
+
+#ifdef INETD_BUILTINS_ENABLED
+/* Echo received data */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+static void FAST_FUNC echo_stream(int, servtab_t *);
+static void FAST_FUNC echo_dg(int, servtab_t *);
+#endif
+/* Internet /dev/null */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+static void FAST_FUNC discard_stream(int, servtab_t *);
+static void FAST_FUNC discard_dg(int, servtab_t *);
+#endif
+/* Return 32 bit time since 1900 */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+static void FAST_FUNC machtime_stream(int, servtab_t *);
+static void FAST_FUNC machtime_dg(int, servtab_t *);
+#endif
+/* Return human-readable time */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+static void FAST_FUNC daytime_stream(int, servtab_t *);
+static void FAST_FUNC daytime_dg(int, servtab_t *);
+#endif
+/* Familiar character generator */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+static void FAST_FUNC chargen_stream(int, servtab_t *);
+static void FAST_FUNC chargen_dg(int, servtab_t *);
+#endif
+
+struct builtin {
+	/* NB: not necessarily NUL terminated */
+	char bi_service7[7];      /* internally provided service name */
+	uint8_t bi_fork;          /* 1 if stream fn should run in child */
+	void (*bi_stream_fn)(int, servtab_t *) FAST_FUNC;
+	void (*bi_dgram_fn)(int, servtab_t *) FAST_FUNC;
+};
+
+static const struct builtin builtins[] = {
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+	{ "echo", 1, echo_stream, echo_dg },
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+	{ "discard", 1, discard_stream, discard_dg },
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+	{ "chargen", 1, chargen_stream, chargen_dg },
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+	{ "time", 0, machtime_stream, machtime_dg },
+#endif
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+	{ "daytime", 0, daytime_stream, daytime_dg },
+#endif
+};
+#endif /* INETD_BUILTINS_ENABLED */
+
+struct globals {
+	rlim_t rlim_ofile_cur;
+	struct rlimit rlim_ofile;
+	servtab_t *serv_list;
+	int global_queuelen;
+	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;
+	unsigned max_concurrency;
+	smallint alarm_armed;
+	uid_t real_uid; /* user ID who ran us */
+	const char *config_filename;
+	parser_t *parser;
+	char *default_local_hostname;
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+	char *end_ring;
+	char *ring_pos;
+	char ring[128];
+#endif
+	fd_set allsock;
+	/* Used in next_line(), and as scratch read buffer */
+	char line[256];          /* _at least_ 256, see LINE_SIZE */
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+enum { LINE_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line) };
+struct BUG_G_too_big {
+	char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1];
+};
+#define rlim_ofile_cur  (G.rlim_ofile_cur )
+#define rlim_ofile      (G.rlim_ofile     )
+#define serv_list       (G.serv_list      )
+#define global_queuelen (G.global_queuelen)
+#define maxsock         (G.maxsock        )
+#define prev_maxsock    (G.prev_maxsock   )
+#define max_concurrency (G.max_concurrency)
+#define alarm_armed     (G.alarm_armed    )
+#define real_uid        (G.real_uid       )
+#define config_filename (G.config_filename)
+#define parser          (G.parser         )
+#define default_local_hostname (G.default_local_hostname)
+#define first_ps_byte   (G.first_ps_byte  )
+#define last_ps_byte    (G.last_ps_byte   )
+#define end_ring        (G.end_ring       )
+#define ring_pos        (G.ring_pos       )
+#define ring            (G.ring           )
+#define allsock         (G.allsock        )
+#define line            (G.line           )
+#define INIT_G() do { \
+	rlim_ofile_cur = OPEN_MAX; \
+	global_queuelen = 128; \
+	config_filename = "/etc/inetd.conf"; \
+} while (0)
+
+static void maybe_close(int fd)
+{
+	if (fd >= 0)
+		close(fd);
+}
+
+// TODO: move to libbb?
+static len_and_sockaddr *xzalloc_lsa(int family)
+{
+	len_and_sockaddr *lsa;
+	int sz;
+
+	sz = sizeof(struct sockaddr_in);
+	if (family == AF_UNIX)
+		sz = sizeof(struct sockaddr_un);
+#if ENABLE_FEATURE_IPV6
+	if (family == AF_INET6)
+		sz = sizeof(struct sockaddr_in6);
+#endif
+	lsa = xzalloc(LSA_LEN_SIZE + sz);
+	lsa->len = sz;
+	lsa->u.sa.sa_family = family;
+	return lsa;
+}
+
+static void rearm_alarm(void)
+{
+	if (!alarm_armed) {
+		alarm_armed = 1;
+		alarm(RETRYTIME);
+	}
+}
+
+static void block_CHLD_HUP_ALRM(sigset_t *m)
+{
+	sigemptyset(m);
+	sigaddset(m, SIGCHLD);
+	sigaddset(m, SIGHUP);
+	sigaddset(m, SIGALRM);
+	sigprocmask(SIG_BLOCK, m, m); /* old sigmask is stored in m */
+}
+
+static void restore_sigmask(sigset_t *m)
+{
+	sigprocmask(SIG_SETMASK, m, NULL);
+}
+
+#if ENABLE_FEATURE_INETD_RPC
+static void register_rpc(servtab_t *sep)
+{
+	int n;
+	struct sockaddr_in ir_sin;
+	socklen_t size;
+
+	size = sizeof(ir_sin);
+	if (getsockname(sep->se_fd, (struct sockaddr *) &ir_sin, &size) < 0) {
+		bb_perror_msg("getsockname");
+		return;
+	}
+
+	for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
+		pmap_unset(sep->se_rpcprog, n);
+		if (!pmap_set(sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port)))
+			bb_perror_msg("%s %s: pmap_set(%u,%u,%u,%u)",
+				sep->se_service, sep->se_proto,
+				sep->se_rpcprog, n, sep->se_proto_no, ntohs(ir_sin.sin_port));
+	}
+}
+
+static void unregister_rpc(servtab_t *sep)
+{
+	int n;
+
+	for (n = sep->se_rpcver_lo; n <= sep->se_rpcver_hi; n++) {
+		if (!pmap_unset(sep->se_rpcprog, n))
+			bb_perror_msg("pmap_unset(%u,%u)", sep->se_rpcprog, n);
+	}
+}
+#endif /* FEATURE_INETD_RPC */
+
+static void bump_nofile(void)
+{
+	enum { FD_CHUNK = 32 };
+	struct rlimit rl;
+
+	/* Never fails under Linux (except if you pass it bad arguments) */
+	getrlimit(RLIMIT_NOFILE, &rl);
+	rl.rlim_cur = MIN(rl.rlim_max, rl.rlim_cur + FD_CHUNK);
+	rl.rlim_cur = MIN(FD_SETSIZE, rl.rlim_cur + FD_CHUNK);
+	if (rl.rlim_cur <= rlim_ofile_cur) {
+		bb_error_msg("can't extend file limit, max = %d",
+						(int) rl.rlim_cur);
+		return;
+	}
+
+	if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+		bb_perror_msg("setrlimit");
+		return;
+	}
+
+	rlim_ofile_cur = rl.rlim_cur;
+}
+
+static void remove_fd_from_set(int fd)
+{
+	if (fd >= 0) {
+		FD_CLR(fd, &allsock);
+		maxsock = -1;
+	}
+}
+
+static void add_fd_to_set(int fd)
+{
+	if (fd >= 0) {
+		FD_SET(fd, &allsock);
+		if (maxsock >= 0 && fd > maxsock) {
+			prev_maxsock = maxsock = fd;
+			if ((rlim_t)fd > rlim_ofile_cur - FD_MARGIN)
+				bump_nofile();
+		}
+	}
+}
+
+static void recalculate_maxsock(void)
+{
+	int fd = 0;
+
+	/* We may have no services, in this case maxsock should still be >= 0
+	 * (code elsewhere is not happy with maxsock == -1) */
+	maxsock = 0;
+	while (fd <= prev_maxsock) {
+		if (FD_ISSET(fd, &allsock))
+			maxsock = fd;
+		fd++;
+	}
+	prev_maxsock = maxsock;
+	if ((rlim_t)maxsock > rlim_ofile_cur - FD_MARGIN)
+		bump_nofile();
+}
+
+static void prepare_socket_fd(servtab_t *sep)
+{
+	int r, fd;
+
+	fd = socket(sep->se_family, sep->se_socktype, 0);
+	if (fd < 0) {
+		bb_perror_msg("socket");
+		return;
+	}
+	setsockopt_reuseaddr(fd);
+
+#if ENABLE_FEATURE_INETD_RPC
+	if (is_rpc_service(sep)) {
+		struct passwd *pwd;
+
+		/* zero out the port for all RPC services; let bind()
+		 * find one. */
+		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. */
+		if (real_uid == 0 && sep->se_family == AF_INET
+		 && (pwd = getpwnam(sep->se_user)) != NULL
+		 && pwd->pw_uid == 0
+		) {
+			r = bindresvport(fd, &sep->se_lsa->u.sin);
+		} else {
+			r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
+		}
+		if (r == 0) {
+			int saveerrno = errno;
+			/* update lsa with port# */
+			getsockname(fd, &sep->se_lsa->u.sa, &sep->se_lsa->len);
+			errno = saveerrno;
+		}
+	} else
+#endif
+	{
+		if (sep->se_family == AF_UNIX) {
+			struct sockaddr_un *sun;
+			sun = (struct sockaddr_un*)&(sep->se_lsa->u.sa);
+			unlink(sun->sun_path);
+		}
+		r = bind(fd, &sep->se_lsa->u.sa, sep->se_lsa->len);
+	}
+	if (r < 0) {
+		bb_perror_msg("%s/%s: bind",
+				sep->se_service, sep->se_proto);
+		close(fd);
+		rearm_alarm();
+		return;
+	}
+	if (sep->se_socktype == SOCK_STREAM)
+		listen(fd, global_queuelen);
+
+	add_fd_to_set(fd);
+	sep->se_fd = fd;
+}
+
+static int reopen_config_file(void)
+{
+	free(default_local_hostname);
+	default_local_hostname = xstrdup("*");
+	if (parser != NULL)
+		config_close(parser);
+	parser = config_open(config_filename);
+	return (parser != NULL);
+}
+
+static void close_config_file(void)
+{
+	if (parser) {
+		config_close(parser);
+		parser = NULL;
+	}
+}
+
+static void free_servtab_strings(servtab_t *cp)
+{
+	int i;
+
+	free(cp->se_local_hostname);
+	free(cp->se_service);
+	free(cp->se_proto);
+	free(cp->se_user);
+	free(cp->se_group);
+	free(cp->se_lsa); /* not a string in fact */
+	free(cp->se_program);
+	for (i = 0; i < MAXARGV; i++)
+		free(cp->se_argv[i]);
+}
+
+static servtab_t *new_servtab(void)
+{
+	servtab_t *newtab = xzalloc(sizeof(servtab_t));
+	newtab->se_fd = -1; /* paranoia */
+	return newtab;
+}
+
+static servtab_t *dup_servtab(servtab_t *sep)
+{
+	servtab_t *newtab;
+	int argc;
+
+	newtab = new_servtab();
+	*newtab = *sep; /* struct copy */
+	/* deep-copying strings */
+	newtab->se_service = xstrdup(newtab->se_service);
+	newtab->se_proto = xstrdup(newtab->se_proto);
+	newtab->se_user = xstrdup(newtab->se_user);
+	newtab->se_group = xstrdup(newtab->se_group);
+	newtab->se_program = xstrdup(newtab->se_program);
+	for (argc = 0; argc <= MAXARGV; argc++)
+		newtab->se_argv[argc] = xstrdup(newtab->se_argv[argc]);
+	/* NB: se_fd, se_hostaddr and se_next are always
+	 * overwrittend by callers, so we don't bother resetting them
+	 * to NULL/0/-1 etc */
+
+	return newtab;
+}
+
+/* gcc generates much more code if this is inlined */
+static servtab_t *parse_one_line(void)
+{
+	int argc;
+	char *token[6+MAXARGV];
+	char *p, *arg;
+	char *hostdelim;
+	servtab_t *sep;
+	servtab_t *nsep;
+ new:
+	sep = new_servtab();
+ more:
+	argc = config_read(parser, token, 6+MAXARGV, 1, "# \t", PARSE_NORMAL);
+	if (!argc) {
+		free(sep);
+		return NULL;
+	}
+
+	/* [host:]service socktype proto wait user[:group] prog [args] */
+	/* Check for "host:...." line */
+	arg = token[0];
+	hostdelim = strrchr(arg, ':');
+	if (hostdelim) {
+		*hostdelim = '\0';
+		sep->se_local_hostname = xstrdup(arg);
+		arg = hostdelim + 1;
+		if (*arg == '\0' && argc == 1) {
+			/* Line has just "host:", change the
+			 * default host for the following lines. */
+			free(default_local_hostname);
+			default_local_hostname = sep->se_local_hostname;
+			goto more;
+		}
+	} else
+		sep->se_local_hostname = xstrdup(default_local_hostname);
+
+	/* service socktype proto wait user[:group] prog [args] */
+	sep->se_service = xstrdup(arg);
+
+	/* socktype proto wait user[:group] prog [args] */
+	if (argc < 6) {
+ parse_err:
+		bb_error_msg("parse error on line %u, line is ignored",
+				parser->lineno);
+		free_servtab_strings(sep);
+		/* Just "goto more" can make sep to carry over e.g.
+		 * "rpc"-ness (by having se_rpcver_lo != 0).
+		 * We will be more paranoid: */
+		free(sep);
+		goto new;
+	}
+
+	{
+		static const int8_t SOCK_xxx[] ALIGN1 = {
+			-1,
+			SOCK_STREAM, SOCK_DGRAM, SOCK_RDM,
+			SOCK_SEQPACKET, SOCK_RAW
+		};
+		sep->se_socktype = SOCK_xxx[1 + index_in_strings(
+			"stream""\0" "dgram""\0" "rdm""\0"
+			"seqpacket""\0" "raw""\0"
+			, token[1])];
+	}
+
+	/* {unix,[rpc/]{tcp,udp}[6]} wait user[:group] prog [args] */
+	sep->se_proto = arg = xstrdup(token[2]);
+	if (strcmp(arg, "unix") == 0) {
+		sep->se_family = AF_UNIX;
+	} else {
+		char *six;
+		sep->se_family = AF_INET;
+		six = last_char_is(arg, '6');
+		if (six) {
+#if ENABLE_FEATURE_IPV6
+			*six = '\0';
+			sep->se_family = AF_INET6;
+#else
+			bb_error_msg("%s: no support for IPv6", sep->se_proto);
+			goto parse_err;
+#endif
+		}
+		if (strncmp(arg, "rpc/", 4) == 0) {
+#if ENABLE_FEATURE_INETD_RPC
+			unsigned n;
+			arg += 4;
+			p = strchr(sep->se_service, '/');
+			if (p == NULL) {
+				bb_error_msg("no rpc version: '%s'", sep->se_service);
+				goto parse_err;
+			}
+			*p++ = '\0';
+			n = bb_strtou(p, &p, 10);
+			if (n > INT_MAX) {
+ bad_ver_spec:
+				bb_error_msg("bad rpc version");
+				goto parse_err;
+			}
+			sep->se_rpcver_lo = sep->se_rpcver_hi = n;
+			if (*p == '-') {
+				p++;
+				n = bb_strtou(p, &p, 10);
+				if (n > INT_MAX || (int)n < sep->se_rpcver_lo)
+					goto bad_ver_spec;
+				sep->se_rpcver_hi = n;
+			}
+			if (*p != '\0')
+				goto bad_ver_spec;
+#else
+			bb_error_msg("no support for rpc services");
+			goto parse_err;
+#endif
+		}
+		/* we don't really need getprotobyname()! */
+		if (strcmp(arg, "tcp") == 0)
+			sep->se_proto_no = IPPROTO_TCP; /* = 6 */
+		if (strcmp(arg, "udp") == 0)
+			sep->se_proto_no = IPPROTO_UDP; /* = 17 */
+		if (six)
+			*six = '6';
+		if (!sep->se_proto_no) /* not tcp/udp?? */
+			goto parse_err;
+	}
+
+	/* [no]wait[.max] user[:group] prog [args] */
+	arg = token[3];
+	sep->se_max = max_concurrency;
+	p = strchr(arg, '.');
+	if (p) {
+		*p++ = '\0';
+		sep->se_max = bb_strtou(p, NULL, 10);
+		if (errno)
+			goto parse_err;
+	}
+	sep->se_wait = (arg[0] != 'n' || arg[1] != 'o');
+	if (!sep->se_wait) /* "no" seen */
+		arg += 2;
+	if (strcmp(arg, "wait") != 0)
+		goto parse_err;
+
+	/* user[:group] prog [args] */
+	sep->se_user = xstrdup(token[4]);
+	arg = strchr(sep->se_user, '.');
+	if (arg == NULL)
+		arg = strchr(sep->se_user, ':');
+	if (arg) {
+		*arg++ = '\0';
+		sep->se_group = xstrdup(arg);
+	}
+
+	/* prog [args] */
+	sep->se_program = xstrdup(token[5]);
+#ifdef INETD_BUILTINS_ENABLED
+	if (strcmp(sep->se_program, "internal") == 0
+	 && strlen(sep->se_service) <= 7
+	 && (sep->se_socktype == SOCK_STREAM
+	     || sep->se_socktype == SOCK_DGRAM)
+	) {
+		unsigned i;
+		for (i = 0; i < ARRAY_SIZE(builtins); i++)
+			if (strncmp(builtins[i].bi_service7, sep->se_service, 7) == 0)
+				goto found_bi;
+		bb_error_msg("unknown internal service %s", sep->se_service);
+		goto parse_err;
+ found_bi:
+		sep->se_builtin = &builtins[i];
+		/* stream builtins must be "nowait", dgram must be "wait" */
+		if (sep->se_wait != (sep->se_socktype == SOCK_DGRAM))
+			goto parse_err;
+	}
+#endif
+	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) {
+		if (sep->se_proto_no == IPPROTO_UDP)
+			goto parse_err;
+	}
+	if (sep->se_socktype == SOCK_DGRAM) {
+		if (sep->se_proto_no == IPPROTO_TCP)
+			goto parse_err;
+	}
+
+//	bb_info_msg(
+//		"ENTRY[%s][%s][%s][%d][%d][%d][%d][%d][%s][%s][%s]",
+//		sep->se_local_hostname, sep->se_service, sep->se_proto, sep->se_wait, sep->se_proto_no,
+//		sep->se_max, sep->se_count, sep->se_time, sep->se_user, sep->se_group, sep->se_program);
+
+	/* check if the hostname specifier is a comma separated list
+	 * of hostnames. we'll make new entries for each address. */
+	while ((hostdelim = strrchr(sep->se_local_hostname, ',')) != NULL) {
+		nsep = dup_servtab(sep);
+		/* NUL terminate the hostname field of the existing entry,
+		 * and make a dup for the new entry. */
+		*hostdelim++ = '\0';
+		nsep->se_local_hostname = xstrdup(hostdelim);
+		nsep->se_next = sep->se_next;
+		sep->se_next = nsep;
+	}
+
+	/* was doing it here: */
+	/* DNS resolution, create copies for each IP address */
+	/* IPv6-ization destroyed it :( */
+
+	return sep;
+}
+
+static servtab_t *insert_in_servlist(servtab_t *cp)
+{
+	servtab_t *sep;
+	sigset_t omask;
+
+	sep = new_servtab();
+	*sep = *cp; /* struct copy */
+	sep->se_fd = -1;
+#if ENABLE_FEATURE_INETD_RPC
+	sep->se_rpcprog = -1;
+#endif
+	block_CHLD_HUP_ALRM(&omask);
+	sep->se_next = serv_list;
+	serv_list = sep;
+	restore_sigmask(&omask);
+	return sep;
+}
+
+static int same_serv_addr_proto(servtab_t *old, servtab_t *new)
+{
+	if (strcmp(old->se_local_hostname, new->se_local_hostname) != 0)
+		return 0;
+	if (strcmp(old->se_service, new->se_service) != 0)
+		return 0;
+	if (strcmp(old->se_proto, new->se_proto) != 0)
+		return 0;
+	return 1;
+}
+
+static void reread_config_file(int sig UNUSED_PARAM)
+{
+	servtab_t *sep, *cp, **sepp;
+	len_and_sockaddr *lsa;
+	sigset_t omask;
+	unsigned n;
+	uint16_t port;
+	int save_errno = errno;
+
+	if (!reopen_config_file())
+		goto ret;
+	for (sep = serv_list; sep; sep = sep->se_next)
+		sep->se_checked = 0;
+
+	goto first_line;
+	while (1) {
+		if (cp == NULL) {
+ first_line:
+			cp = parse_one_line();
+			if (cp == NULL)
+				break;
+		}
+		for (sep = serv_list; sep; sep = sep->se_next)
+			if (same_serv_addr_proto(sep, cp))
+				goto equal_servtab;
+		/* not an "equal" servtab */
+		sep = insert_in_servlist(cp);
+		goto after_check;
+ equal_servtab:
+		{
+			int i;
+
+			block_CHLD_HUP_ALRM(&omask);
+#if ENABLE_FEATURE_INETD_RPC
+			if (is_rpc_service(sep))
+				unregister_rpc(sep);
+			sep->se_rpcver_lo = cp->se_rpcver_lo;
+			sep->se_rpcver_hi = cp->se_rpcver_hi;
+#endif
+			if (cp->se_wait == 0) {
+				/* New config says "nowait". If old one
+				 * was "wait", we currently may be waiting
+				 * for a child (and not accepting connects).
+				 * Stop waiting, start listening again.
+				 * (if it's not true, this op is harmless) */
+				add_fd_to_set(sep->se_fd);
+			}
+			sep->se_wait = cp->se_wait;
+			sep->se_max = cp->se_max;
+			/* string fields need more love - we don't want to leak them */
+#define SWAP(type, a, b) do { type c = (type)a; a = (type)b; b = (type)c; } while (0)
+			SWAP(char*, sep->se_user, cp->se_user);
+			SWAP(char*, sep->se_group, cp->se_group);
+			SWAP(char*, sep->se_program, cp->se_program);
+			for (i = 0; i < MAXARGV; i++)
+				SWAP(char*, sep->se_argv[i], cp->se_argv[i]);
+#undef SWAP
+			restore_sigmask(&omask);
+			free_servtab_strings(cp);
+		}
+ after_check:
+		/* cp->string_fields are consumed by insert_in_servlist()
+		 * or freed at this point, cp itself is not yet freed. */
+		sep->se_checked = 1;
+
+		/* create new len_and_sockaddr */
+		switch (sep->se_family) {
+			struct sockaddr_un *sun;
+		case AF_UNIX:
+			lsa = xzalloc_lsa(AF_UNIX);
+			sun = (struct sockaddr_un*)&lsa->u.sa;
+			safe_strncpy(sun->sun_path, sep->se_service, sizeof(sun->sun_path));
+			break;
+
+		default: /* case AF_INET, case AF_INET6 */
+			n = bb_strtou(sep->se_service, NULL, 10);
+#if ENABLE_FEATURE_INETD_RPC
+			if (is_rpc_service(sep)) {
+				sep->se_rpcprog = n;
+				if (errno) { /* se_service is not numeric */
+					struct rpcent *rp = getrpcbyname(sep->se_service);
+					if (rp == NULL) {
+						bb_error_msg("%s: unknown rpc service", sep->se_service);
+						goto next_cp;
+					}
+					sep->se_rpcprog = rp->r_number;
+				}
+				if (sep->se_fd == -1)
+					prepare_socket_fd(sep);
+				if (sep->se_fd != -1)
+					register_rpc(sep);
+				goto next_cp;
+			}
+#endif
+			/* what port to listen on? */
+			port = htons(n);
+			if (errno || n > 0xffff) { /* se_service is not numeric */
+				char protoname[4];
+				struct servent *sp;
+				/* can result only in "tcp" or "udp": */
+				safe_strncpy(protoname, sep->se_proto, 4);
+				sp = getservbyname(sep->se_service, protoname);
+				if (sp == NULL) {
+					bb_error_msg("%s/%s: unknown service",
+							sep->se_service, sep->se_proto);
+					goto next_cp;
+				}
+				port = sp->s_port;
+			}
+			if (LONE_CHAR(sep->se_local_hostname, '*')) {
+				lsa = xzalloc_lsa(sep->se_family);
+				set_nport(&lsa->u.sa, port);
+			} else {
+				lsa = host_and_af2sockaddr(sep->se_local_hostname,
+						ntohs(port), sep->se_family);
+				if (!lsa) {
+					bb_error_msg("%s/%s: unknown host '%s'",
+						sep->se_service, sep->se_proto,
+						sep->se_local_hostname);
+					goto next_cp;
+				}
+			}
+			break;
+		} /* end of "switch (sep->se_family)" */
+
+		/* did lsa change? Then close/open */
+		if (sep->se_lsa == NULL
+		 || lsa->len != sep->se_lsa->len
+		 || memcmp(&lsa->u.sa, &sep->se_lsa->u.sa, lsa->len) != 0
+		) {
+			remove_fd_from_set(sep->se_fd);
+			maybe_close(sep->se_fd);
+			free(sep->se_lsa);
+			sep->se_lsa = lsa;
+			sep->se_fd = -1;
+		} else {
+			free(lsa);
+		}
+		if (sep->se_fd == -1)
+			prepare_socket_fd(sep);
+ next_cp:
+		sep = cp->se_next;
+		free(cp);
+		cp = sep;
+	} /* end of "while (1) parse lines" */
+	close_config_file();
+
+	/* Purge anything not looked at above - these are stale entries,
+	 * new config file doesnt have them. */
+	block_CHLD_HUP_ALRM(&omask);
+	sepp = &serv_list;
+	while ((sep = *sepp)) {
+		if (sep->se_checked) {
+			sepp = &sep->se_next;
+			continue;
+		}
+		*sepp = sep->se_next;
+		remove_fd_from_set(sep->se_fd);
+		maybe_close(sep->se_fd);
+#if ENABLE_FEATURE_INETD_RPC
+		if (is_rpc_service(sep))
+			unregister_rpc(sep);
+#endif
+		if (sep->se_family == AF_UNIX)
+			unlink(sep->se_service);
+		free_servtab_strings(sep);
+		free(sep);
+	}
+	restore_sigmask(&omask);
+ ret:
+	errno = save_errno;
+}
+
+static void reap_child(int sig UNUSED_PARAM)
+{
+	pid_t pid;
+	int status;
+	servtab_t *sep;
+	int save_errno = errno;
+
+	for (;;) {
+		pid = wait_any_nohang(&status);
+		if (pid <= 0)
+			break;
+		for (sep = serv_list; sep; sep = sep->se_next) {
+			if (sep->se_wait != pid)
+				continue;
+			/* One of our "wait" services */
+			if (WIFEXITED(status) && WEXITSTATUS(status))
+				bb_error_msg("%s: exit status %u",
+						sep->se_program, WEXITSTATUS(status));
+			else if (WIFSIGNALED(status))
+				bb_error_msg("%s: exit signal %u",
+						sep->se_program, WTERMSIG(status));
+			sep->se_wait = 1;
+			add_fd_to_set(sep->se_fd);
+			break;
+		}
+	}
+	errno = save_errno;
+}
+
+static void retry_network_setup(int sig UNUSED_PARAM)
+{
+	int save_errno = errno;
+	servtab_t *sep;
+
+	alarm_armed = 0;
+	for (sep = serv_list; sep; sep = sep->se_next) {
+		if (sep->se_fd == -1) {
+			prepare_socket_fd(sep);
+#if ENABLE_FEATURE_INETD_RPC
+			if (sep->se_fd != -1 && is_rpc_service(sep))
+				register_rpc(sep);
+#endif
+		}
+	}
+	errno = save_errno;
+}
+
+static void clean_up_and_exit(int sig UNUSED_PARAM)
+{
+	servtab_t *sep;
+
+	/* XXX signal race walking sep list */
+	for (sep = serv_list; sep; sep = sep->se_next) {
+		if (sep->se_fd == -1)
+			continue;
+
+		switch (sep->se_family) {
+		case AF_UNIX:
+			unlink(sep->se_service);
+			break;
+		default: /* case AF_INET, AF_INET6 */
+#if ENABLE_FEATURE_INETD_RPC
+			if (sep->se_wait == 1 && is_rpc_service(sep))
+				unregister_rpc(sep);   /* XXX signal race */
+#endif
+			break;
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			close(sep->se_fd);
+	}
+	remove_pidfile(_PATH_INETDPID);
+	exit(EXIT_SUCCESS);
+}
+
+int inetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int inetd_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct sigaction sa, saved_pipe_handler;
+	servtab_t *sep, *sep2;
+	struct passwd *pwd;
+	struct group *grp = grp; /* for compiler */
+	int opt;
+	pid_t pid;
+	sigset_t omask;
+
+	INIT_G();
+
+	real_uid = getuid();
+	if (real_uid != 0) /* run by non-root user */
+		config_filename = NULL;
+
+	opt_complementary = "R+:q+"; /* -q N, -R N */
+	opt = getopt32(argv, "R:feq:", &max_concurrency, &global_queuelen);
+	argv += optind;
+	//argc -= optind;
+	if (argv[0])
+		config_filename = argv[0];
+	if (config_filename == NULL)
+		bb_error_msg_and_die("non-root must specify config file");
+	if (!(opt & 2))
+		bb_daemonize_or_rexec(0, argv - optind);
+	else
+		bb_sanitize_stdio();
+	if (!(opt & 4)) {
+		/* LOG_NDELAY: connect to syslog daemon NOW.
+		 * Otherwise, we may open syslog socket
+		 * in vforked child, making opened fds and syslog()
+		 * internal state inconsistent.
+		 * This was observed to leak file descriptors. */
+		openlog(applet_name, LOG_PID | LOG_NDELAY, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG;
+	}
+
+	if (real_uid == 0) {
+		/* run by root, ensure groups vector gets trashed */
+		gid_t gid = getgid();
+		setgroups(1, &gid);
+	}
+
+	write_pidfile(_PATH_INETDPID);
+
+	/* never fails under Linux (except if you pass it bad arguments) */
+	getrlimit(RLIMIT_NOFILE, &rlim_ofile);
+	rlim_ofile_cur = rlim_ofile.rlim_cur;
+	if (rlim_ofile_cur == RLIM_INFINITY)    /* ! */
+		rlim_ofile_cur = OPEN_MAX;
+
+	memset(&sa, 0, sizeof(sa));
+	/*sigemptyset(&sa.sa_mask); - memset did it */
+	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;
+	sigaction_set(SIGINT, &sa);
+	sa.sa_handler = SIG_IGN;
+	sigaction(SIGPIPE, &sa, &saved_pipe_handler);
+
+	reread_config_file(SIGHUP); /* load config from file */
+
+	for (;;) {
+		int ready_fd_cnt;
+		int ctrl, accepted_fd, new_udp_fd;
+		fd_set readable;
+
+		if (maxsock < 0)
+			recalculate_maxsock();
+
+		readable = allsock; /* struct copy */
+		/* if there are no fds to wait on, we will block
+		 * until signal wakes us up (maxsock == 0, but readable
+		 * never contains fds 0 and 1...) */
+		ready_fd_cnt = select(maxsock + 1, &readable, NULL, NULL, NULL);
+		if (ready_fd_cnt < 0) {
+			if (errno != EINTR) {
+				bb_perror_msg("select");
+				sleep(1);
+			}
+			continue;
+		}
+
+		for (sep = serv_list; ready_fd_cnt && sep; sep = sep->se_next) {
+			if (sep->se_fd == -1 || !FD_ISSET(sep->se_fd, &readable))
+				continue;
+
+			ready_fd_cnt--;
+			ctrl = sep->se_fd;
+			accepted_fd = -1;
+			new_udp_fd = -1;
+			if (!sep->se_wait) {
+				if (sep->se_socktype == SOCK_STREAM) {
+					ctrl = accepted_fd = accept(sep->se_fd, NULL, NULL);
+					if (ctrl < 0) {
+						if (errno != EINTR)
+							bb_perror_msg("accept (for %s)", sep->se_service);
+						continue;
+					}
+				}
+				/* "nowait" udp */
+				if (sep->se_socktype == SOCK_DGRAM
+				 && sep->se_family != AF_UNIX
+				) {
+/* How udp "nowait" works:
+ * child peeks at (received and buffered by kernel) UDP packet,
+ * performs connect() on the socket so that it is linked only
+ * to this peer. But this also affects parent, because descriptors
+ * are shared after fork() a-la dup(). When parent performs
+ * select(), it will see this descriptor connected to the peer (!)
+ * and still readable, will act on it and mess things up
+ * (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);
+					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,
+					 * 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) {
+						close(new_udp_fd);
+						goto udp_err;
+					}
+				}
+			}
+
+			block_CHLD_HUP_ALRM(&omask);
+			pid = 0;
+#ifdef INETD_BUILTINS_ENABLED
+			/* do we need to fork? */
+			if (sep->se_builtin == NULL
+			 || (sep->se_socktype == SOCK_STREAM
+			     && sep->se_builtin->bi_fork))
+#endif
+			{
+				if (sep->se_max != 0) {
+					if (++sep->se_count == 1)
+						sep->se_time = monotonic_sec();
+					else if (sep->se_count >= sep->se_max) {
+						unsigned now = monotonic_sec();
+						/* did we accumulate se_max connects too quickly? */
+						if (now - sep->se_time <= CNT_INTERVAL) {
+							bb_error_msg("%s/%s: too many connections, pausing",
+									sep->se_service, sep->se_proto);
+							remove_fd_from_set(sep->se_fd);
+							close(sep->se_fd);
+							sep->se_fd = -1;
+							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 */
+						}
+						sep->se_count = 0;
+					}
+				}
+				/* on NOMMU, streamed chargen
+				 * builtin wouldn't work, but it is
+				 * not allowed on NOMMU (ifdefed out) */
+#ifdef INETD_BUILTINS_ENABLED
+				if (BB_MMU && sep->se_builtin)
+					pid = fork();
+				else
+#endif
+					pid = vfork();
+
+				if (pid < 0) { /* fork error */
+					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 didn't fork */
+
+			if (pid > 0) { /* parent */
+				if (sep->se_wait) {
+					/* wait: we passed socket to child,
+					 * will wait for child to terminate */
+					sep->se_wait = pid;
+					remove_fd_from_set(sep->se_fd);
+				}
+				if (new_udp_fd >= 0) {
+					/* udp nowait: child connected the socket,
+					 * we created and will use new, unconnected one */
+					xmove_fd(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 */
+#ifdef INETD_BUILTINS_ENABLED
+			if (sep->se_builtin) {
+				if (pid) { /* "pid" is -1: we did vfork */
+					close(sep->se_fd); /* listening socket */
+					logmode = LOGMODE_NONE; /* make xwrite etc silent */
+				}
+				restore_sigmask(&omask);
+				if (sep->se_socktype == SOCK_STREAM)
+					sep->se_builtin->bi_stream_fn(ctrl, sep);
+				else
+					sep->se_builtin->bi_dgram_fn(ctrl, sep);
+				if (pid) /* we did vfork */
+					_exit(EXIT_FAILURE);
+				maybe_close(accepted_fd);
+				continue; /* -> check next fd in fd set */
+			}
+#endif
+			/* child */
+			setsid();
+			/* "nowait" udp */
+			if (new_udp_fd >= 0) {
+				len_and_sockaddr *lsa;
+				int r;
+
+				close(new_udp_fd);
+				lsa = xzalloc_lsa(sep->se_family);
+				/* peek at the packet and remember peer addr */
+				r = recvfrom(ctrl, NULL, 0, MSG_PEEK|MSG_DONTWAIT,
+					&lsa->u.sa, &lsa->len);
+				if (r < 0)
+					goto do_exit1;
+				/* make this socket "connected" to peer addr:
+				 * only packets from this peer will be recv'ed,
+				 * and bare write()/send() will work on it */
+				connect(ctrl, &lsa->u.sa, lsa->len);
+				free(lsa);
+			}
+			/* prepare env and exec program */
+			pwd = getpwnam(sep->se_user);
+			if (pwd == NULL) {
+				bb_error_msg("%s: no such %s", sep->se_user, "user");
+				goto do_exit1;
+			}
+			if (sep->se_group && (grp = getgrnam(sep->se_group)) == NULL) {
+				bb_error_msg("%s: no such %s", sep->se_group, "group");
+				goto do_exit1;
+			}
+			if (real_uid != 0 && real_uid != pwd->pw_uid) {
+				/* a user running private inetd */
+				bb_error_msg("non-root must run services as himself");
+				goto do_exit1;
+			}
+			if (pwd->pw_uid) {
+				if (sep->se_group)
+					pwd->pw_gid = grp->gr_gid;
+				/* initgroups, setgid, setuid: */
+				change_identity(pwd);
+			} else if (sep->se_group) {
+				xsetgid(grp->gr_gid);
+				setgroups(1, &grp->gr_gid);
+			}
+			if (rlim_ofile.rlim_cur != rlim_ofile_cur)
+				if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0)
+					bb_perror_msg("setrlimit");
+
+			/* closelog(); - WRONG. we are after vfork,
+			 * this may confuse syslog() internal state.
+			 * Let's hope libc sets syslog fd to CLOEXEC...
+			 */
+			xmove_fd(ctrl, STDIN_FILENO);
+			xdup2(STDIN_FILENO, STDOUT_FILENO);
+			/* 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 (!) */
+			if (!sep->se_wait) /* only for usual "tcp nowait" */
+				xdup2(STDIN_FILENO, STDERR_FILENO);
+			/* NB: among others, this loop closes listening sockets
+			 * for nowait stream children */
+			for (sep2 = serv_list; sep2; sep2 = sep2->se_next)
+				if (sep2->se_fd != ctrl)
+					maybe_close(sep2->se_fd);
+			sigaction_set(SIGPIPE, &saved_pipe_handler);
+			restore_sigmask(&omask);
+			BB_EXECVP(sep->se_program, sep->se_argv);
+			bb_perror_msg("can't execute '%s'", sep->se_program);
+ do_exit1:
+			/* eat packet in udp case */
+			if (sep->se_socktype != SOCK_STREAM)
+				recv(0, line, LINE_SIZE, MSG_DONTWAIT);
+			_exit(EXIT_FAILURE);
+		} /* for (sep = servtab...) */
+	} /* for (;;) */
+}
+
+#if !BB_MMU
+static const char *const cat_args[] = { "cat", NULL };
+#endif
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_ECHO
+/* Echo service -- echo data back. */
+/* ARGSUSED */
+static void FAST_FUNC echo_stream(int s, servtab_t *sep UNUSED_PARAM)
+{
+#if BB_MMU
+	while (1) {
+		ssize_t sz = safe_read(s, line, LINE_SIZE);
+		if (sz <= 0)
+			break;
+		xwrite(s, line, sz);
+	}
+#else
+	/* We are after vfork here! */
+	/* move network socket to stdin/stdout */
+	xmove_fd(s, STDIN_FILENO);
+	xdup2(STDIN_FILENO, STDOUT_FILENO);
+	/* no error messages please... */
+	close(STDERR_FILENO);
+	xopen(bb_dev_null, O_WRONLY);
+	BB_EXECVP("cat", (char**)cat_args);
+	/* on failure we return to main, which does exit(EXIT_FAILURE) */
+#endif
+}
+static void FAST_FUNC echo_dg(int s, servtab_t *sep)
+{
+	enum { BUFSIZE = 12*1024 }; /* for jumbo sized packets! :) */
+	char *buf = xmalloc(BUFSIZE); /* too big for stack */
+	int sz;
+	len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
+
+	lsa->len = sep->se_lsa->len;
+	/* dgram builtins are non-forking - DONT BLOCK! */
+	sz = recvfrom(s, buf, BUFSIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len);
+	if (sz > 0)
+		sendto(s, buf, sz, 0, &lsa->u.sa, lsa->len);
+	free(buf);
+}
+#endif  /* FEATURE_INETD_SUPPORT_BUILTIN_ECHO */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD
+/* Discard service -- ignore data. */
+/* ARGSUSED */
+static void FAST_FUNC discard_stream(int s, servtab_t *sep UNUSED_PARAM)
+{
+#if BB_MMU
+	while (safe_read(s, line, LINE_SIZE) > 0)
+		continue;
+#else
+	/* We are after vfork here! */
+	/* move network socket to stdin */
+	xmove_fd(s, STDIN_FILENO);
+	/* discard output */
+	close(STDOUT_FILENO);
+	xopen(bb_dev_null, O_WRONLY);
+	/* no error messages please... */
+	xdup2(STDOUT_FILENO, STDERR_FILENO);
+	BB_EXECVP("cat", (char**)cat_args);
+	/* on failure we return to main, which does exit(EXIT_FAILURE) */
+#endif
+}
+/* ARGSUSED */
+static void FAST_FUNC discard_dg(int s, servtab_t *sep UNUSED_PARAM)
+{
+	/* dgram builtins are non-forking - DONT BLOCK! */
+	recv(s, line, LINE_SIZE, MSG_DONTWAIT);
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DISCARD */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN
+#define LINESIZ 72
+static void init_ring(void)
+{
+	int i;
+
+	end_ring = ring;
+	for (i = ' '; i < 127; i++)
+		*end_ring++ = i;
+}
+/* Character generator. MMU arches only. */
+/* ARGSUSED */
+static void FAST_FUNC chargen_stream(int s, servtab_t *sep UNUSED_PARAM)
+{
+	char *rs;
+	int len;
+	char text[LINESIZ + 2];
+
+	if (!end_ring) {
+		init_ring();
+		rs = ring;
+	}
+
+	text[LINESIZ] = '\r';
+	text[LINESIZ + 1] = '\n';
+	rs = ring;
+	for (;;) {
+		len = end_ring - rs;
+		if (len >= LINESIZ)
+			memmove(text, rs, LINESIZ);
+		else {
+			memmove(text, rs, len);
+			memmove(text + len, ring, LINESIZ - len);
+		}
+		if (++rs == end_ring)
+			rs = ring;
+		xwrite(s, text, sizeof(text));
+	}
+}
+/* ARGSUSED */
+static void FAST_FUNC chargen_dg(int s, servtab_t *sep)
+{
+	int len;
+	char text[LINESIZ + 2];
+	len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
+
+	/* Eat UDP packet which started it all */
+	/* dgram builtins are non-forking - DONT BLOCK! */
+	lsa->len = sep->se_lsa->len;
+	if (recvfrom(s, text, sizeof(text), MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
+		return;
+
+	if (!end_ring) {
+		init_ring();
+		ring_pos = ring;
+	}
+
+	len = end_ring - ring_pos;
+	if (len >= LINESIZ)
+		memmove(text, ring_pos, LINESIZ);
+	else {
+		memmove(text, ring_pos, len);
+		memmove(text + len, ring, LINESIZ - len);
+	}
+	if (++ring_pos == end_ring)
+		ring_pos = ring;
+	text[LINESIZ] = '\r';
+	text[LINESIZ + 1] = '\n';
+	sendto(s, text, sizeof(text), 0, &lsa->u.sa, lsa->len);
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_TIME
+/*
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900.  Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+static uint32_t machtime(void)
+{
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+	return htonl((uint32_t)(tv.tv_sec + 2208988800));
+}
+/* ARGSUSED */
+static void FAST_FUNC machtime_stream(int s, servtab_t *sep UNUSED_PARAM)
+{
+	uint32_t result;
+
+	result = machtime();
+	full_write(s, &result, sizeof(result));
+}
+static void FAST_FUNC machtime_dg(int s, servtab_t *sep)
+{
+	uint32_t result;
+	len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
+
+	lsa->len = sep->se_lsa->len;
+	if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
+		return;
+
+	result = machtime();
+	sendto(s, &result, sizeof(result), 0, &lsa->u.sa, lsa->len);
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_TIME */
+
+
+#if ENABLE_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME
+/* Return human-readable time of day */
+/* ARGSUSED */
+static void FAST_FUNC daytime_stream(int s, servtab_t *sep UNUSED_PARAM)
+{
+	time_t t;
+
+	t = time(NULL);
+	fdprintf(s, "%.24s\r\n", ctime(&t));
+}
+static void FAST_FUNC daytime_dg(int s, servtab_t *sep)
+{
+	time_t t;
+	len_and_sockaddr *lsa = alloca(LSA_LEN_SIZE + sep->se_lsa->len);
+
+	lsa->len = sep->se_lsa->len;
+	if (recvfrom(s, line, LINE_SIZE, MSG_DONTWAIT, &lsa->u.sa, &lsa->len) < 0)
+		return;
+
+	t = time(NULL);
+	sprintf(line, "%.24s\r\n", ctime(&t));
+	sendto(s, line, strlen(line), 0, &lsa->u.sa, lsa->len);
+}
+#endif /* FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME */
diff --git a/busybox-1.19.3/networking/interface.c b/busybox-1.19.3/networking/interface.c
new file mode 100644
index 0000000..79c322e
--- /dev/null
+++ b/busybox-1.19.3/networking/interface.c
@@ -0,0 +1,1231 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * stolen from net-tools-1.59 and stripped down for busybox by
+ *			Erik Andersen <andersen@codepoet.org>
+ *
+ * Heavily modified by Manuel Novoa III       Mar 12, 2001
+ *
+ * Added print_bytes_scaled function to reduce code size.
+ * Added some (potentially) missing defines.
+ * Improved display support for -a and for a named interface.
+ *
+ * -----------------------------------------------------------
+ *
+ * ifconfig   This file contains an implementation of the command
+ *              that either displays or sets the characteristics of
+ *              one or more of the system's networking interfaces.
+ *
+ *
+ * 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 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
+ *			(default AF was wrong)
+ */
+
+#include "libbb.h"
+#include "inet_common.h"
+#include <net/if.h>
+#include <net/if_arp.h>
+#ifdef HAVE_NET_ETHERNET_H
+# include <net/ethernet.h>
+#endif
+
+#if ENABLE_FEATURE_HWIB
+/* #include <linux/if_infiniband.h> */
+# undef INFINIBAND_ALEN
+# define INFINIBAND_ALEN 20
+#endif
+
+#if ENABLE_FEATURE_IPV6
+# define HAVE_AFINET6 1
+#else
+# undef HAVE_AFINET6
+#endif
+
+#define _PATH_PROCNET_DEV               "/proc/net/dev"
+#define _PATH_PROCNET_IFINET6           "/proc/net/if_inet6"
+
+#ifdef HAVE_AFINET6
+# ifndef _LINUX_IN6_H
+/*
+ * This is from linux/include/net/ipv6.h
+ */
+struct in6_ifreq {
+	struct in6_addr ifr6_addr;
+	uint32_t ifr6_prefixlen;
+	unsigned int ifr6_ifindex;
+};
+# endif
+#endif /* HAVE_AFINET6 */
+
+/* Defines for glibc2.0 users. */
+#ifndef SIOCSIFTXQLEN
+# define SIOCSIFTXQLEN      0x8943
+# define SIOCGIFTXQLEN      0x8942
+#endif
+
+/* ifr_qlen is ifru_ivalue, but it isn't present in 2.0 kernel headers */
+#ifndef ifr_qlen
+# define ifr_qlen        ifr_ifru.ifru_mtu
+#endif
+
+#ifndef HAVE_TXQUEUELEN
+# define HAVE_TXQUEUELEN 1
+#endif
+
+#ifndef IFF_DYNAMIC
+# define IFF_DYNAMIC     0x8000 /* dialup device with changing addresses */
+#endif
+
+/* Display an Internet socket address. */
+static const char* FAST_FUNC INET_sprint(struct sockaddr *sap, int numeric)
+{
+	static char *buff; /* defaults to NULL */
+
+	free(buff);
+	if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+		return "[NONE SET]";
+	buff = INET_rresolve((struct sockaddr_in *) sap, numeric, 0xffffff00);
+	return buff;
+}
+
+#ifdef UNUSED_AND_BUGGY
+static int INET_getsock(char *bufp, struct sockaddr *sap)
+{
+	char *sp = bufp, *bp;
+	unsigned int i;
+	unsigned val;
+	struct sockaddr_in *sock_in;
+
+	sock_in = (struct sockaddr_in *) sap;
+	sock_in->sin_family = AF_INET;
+	sock_in->sin_port = 0;
+
+	val = 0;
+	bp = (char *) &val;
+	for (i = 0; i < sizeof(sock_in->sin_addr.s_addr); i++) {
+		*sp = toupper(*sp);
+
+		if ((unsigned)(*sp - 'A') <= 5)
+			bp[i] |= (int) (*sp - ('A' - 10));
+		else if (isdigit(*sp))
+			bp[i] |= (int) (*sp - '0');
+		else
+			return -1;
+
+		bp[i] <<= 4;
+		sp++;
+		*sp = toupper(*sp);
+
+		if ((unsigned)(*sp - 'A') <= 5)
+			bp[i] |= (int) (*sp - ('A' - 10));
+		else if (isdigit(*sp))
+			bp[i] |= (int) (*sp - '0');
+		else
+			return -1;
+
+		sp++;
+	}
+	sock_in->sin_addr.s_addr = htonl(val);
+
+	return (sp - bufp);
+}
+#endif
+
+static int FAST_FUNC INET_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+	return INET_resolve(bufp, (struct sockaddr_in *) sap, 0);
+/*
+	switch (type) {
+	case 1:
+		return (INET_getsock(bufp, sap));
+	case 256:
+		return (INET_resolve(bufp, (struct sockaddr_in *) sap, 1));
+	default:
+		return (INET_resolve(bufp, (struct sockaddr_in *) sap, 0));
+	}
+*/
+}
+
+static const struct aftype inet_aftype = {
+	.name   = "inet",
+	.title  = "DARPA Internet",
+	.af     = AF_INET,
+	.alen   = 4,
+	.sprint = INET_sprint,
+	.input  = INET_input,
+};
+
+#ifdef HAVE_AFINET6
+
+/* Display an Internet socket address. */
+/* dirty! struct sockaddr usually doesn't suffer for inet6 addresses, fst. */
+static const char* FAST_FUNC INET6_sprint(struct sockaddr *sap, int numeric)
+{
+	static char *buff;
+
+	free(buff);
+	if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+		return "[NONE SET]";
+	buff = INET6_rresolve((struct sockaddr_in6 *) sap, numeric);
+	return buff;
+}
+
+#ifdef UNUSED
+static int INET6_getsock(char *bufp, struct sockaddr *sap)
+{
+	struct sockaddr_in6 *sin6;
+
+	sin6 = (struct sockaddr_in6 *) sap;
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_port = 0;
+
+	if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0)
+		return -1;
+
+	return 16;			/* ?;) */
+}
+#endif
+
+static int FAST_FUNC INET6_input(/*int type,*/ const char *bufp, struct sockaddr *sap)
+{
+	return INET6_resolve(bufp, (struct sockaddr_in6 *) sap);
+/*
+	switch (type) {
+	case 1:
+		return (INET6_getsock(bufp, sap));
+	default:
+		return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap));
+	}
+*/
+}
+
+static const struct aftype inet6_aftype = {
+	.name   = "inet6",
+	.title  = "IPv6",
+	.af     = AF_INET6,
+	.alen   = sizeof(struct in6_addr),
+	.sprint = INET6_sprint,
+	.input  = INET6_input,
+};
+
+#endif /* HAVE_AFINET6 */
+
+/* Display an UNSPEC address. */
+static char* FAST_FUNC UNSPEC_print(unsigned char *ptr)
+{
+	static char *buff;
+
+	char *pos;
+	unsigned int i;
+
+	if (!buff)
+		buff = xmalloc(sizeof(struct sockaddr) * 3 + 1);
+	pos = buff;
+	for (i = 0; i < sizeof(struct sockaddr); i++) {
+		/* careful -- not every libc's sprintf returns # bytes written */
+		sprintf(pos, "%02X-", (*ptr++ & 0377));
+		pos += 3;
+	}
+	/* Erase trailing "-".  Works as long as sizeof(struct sockaddr) != 0 */
+	*--pos = '\0';
+	return buff;
+}
+
+/* Display an UNSPEC socket address. */
+static const char* FAST_FUNC UNSPEC_sprint(struct sockaddr *sap, int numeric UNUSED_PARAM)
+{
+	if (sap->sa_family == 0xFFFF || sap->sa_family == 0)
+		return "[NONE SET]";
+	return UNSPEC_print((unsigned char *)sap->sa_data);
+}
+
+static const struct aftype unspec_aftype = {
+	.name   = "unspec",
+	.title  = "UNSPEC",
+	.af     = AF_UNSPEC,
+	.alen   = 0,
+	.print  = UNSPEC_print,
+	.sprint = UNSPEC_sprint,
+};
+
+static const struct aftype *const aftypes[] = {
+	&inet_aftype,
+#ifdef HAVE_AFINET6
+	&inet6_aftype,
+#endif
+	&unspec_aftype,
+	NULL
+};
+
+/* Check our protocol family table for this family. */
+const struct aftype* FAST_FUNC get_aftype(const char *name)
+{
+	const struct aftype *const *afp;
+
+	afp = aftypes;
+	while (*afp != NULL) {
+		if (!strcmp((*afp)->name, name))
+			return (*afp);
+		afp++;
+	}
+	return NULL;
+}
+
+/* Check our protocol family table for this family. */
+static const struct aftype *get_afntype(int af)
+{
+	const struct aftype *const *afp;
+
+	afp = aftypes;
+	while (*afp != NULL) {
+		if ((*afp)->af == af)
+			return *afp;
+		afp++;
+	}
+	return NULL;
+}
+
+struct user_net_device_stats {
+	unsigned long long rx_packets;	/* total packets received       */
+	unsigned long long tx_packets;	/* total packets transmitted    */
+	unsigned long long rx_bytes;	/* total bytes received         */
+	unsigned long long tx_bytes;	/* total bytes transmitted      */
+	unsigned long rx_errors;	/* bad packets received         */
+	unsigned long tx_errors;	/* packet transmit problems     */
+	unsigned long rx_dropped;	/* no space in linux buffers    */
+	unsigned long tx_dropped;	/* no space available in linux  */
+	unsigned long rx_multicast;	/* multicast packets received   */
+	unsigned long rx_compressed;
+	unsigned long tx_compressed;
+	unsigned long collisions;
+
+	/* detailed rx_errors: */
+	unsigned long rx_length_errors;
+	unsigned long rx_over_errors;	/* receiver ring buff overflow  */
+	unsigned long rx_crc_errors;	/* recved pkt with crc error    */
+	unsigned long rx_frame_errors;	/* recv'd frame alignment error */
+	unsigned long rx_fifo_errors;	/* recv'r fifo overrun          */
+	unsigned long rx_missed_errors;	/* receiver missed packet     */
+	/* detailed tx_errors */
+	unsigned long tx_aborted_errors;
+	unsigned long tx_carrier_errors;
+	unsigned long tx_fifo_errors;
+	unsigned long tx_heartbeat_errors;
+	unsigned long tx_window_errors;
+};
+
+struct interface {
+	struct interface *next, *prev;
+	char name[IFNAMSIZ];                    /* interface name        */
+	short type;                             /* if type               */
+	short flags;                            /* various flags         */
+	int metric;                             /* routing metric        */
+	int mtu;                                /* MTU value             */
+	int tx_queue_len;                       /* transmit queue length */
+	struct ifmap map;                       /* hardware setup        */
+	struct sockaddr addr;                   /* IP address            */
+	struct sockaddr dstaddr;                /* P-P IP address        */
+	struct sockaddr broadaddr;              /* IP broadcast address  */
+	struct sockaddr netmask;                /* IP network mask       */
+	int has_ip;
+	char hwaddr[32];                        /* HW address            */
+	int statistics_valid;
+	struct user_net_device_stats stats;     /* statistics            */
+	int keepalive;                          /* keepalive value for SLIP */
+	int outfill;                            /* outfill value for SLIP */
+};
+
+
+smallint interface_opt_a;	/* show all interfaces */
+
+static struct interface *int_list, *int_last;
+
+
+#if 0
+/* like strcmp(), but knows about numbers */
+except that the freshly added calls to xatoul() brf on ethernet aliases with
+uClibc with e.g.: ife->name='lo'  name='eth0:1'
+static int nstrcmp(const char *a, const char *b)
+{
+	const char *a_ptr = a;
+	const char *b_ptr = b;
+
+	while (*a == *b) {
+		if (*a == '\0') {
+			return 0;
+		}
+		if (!isdigit(*a) && isdigit(*(a+1))) {
+			a_ptr = a+1;
+			b_ptr = b+1;
+		}
+		a++;
+		b++;
+	}
+
+	if (isdigit(*a) && isdigit(*b)) {
+		return xatoul(a_ptr) > xatoul(b_ptr) ? 1 : -1;
+	}
+	return *a - *b;
+}
+#endif
+
+static struct interface *add_interface(char *name)
+{
+	struct interface *ife, **nextp, *new;
+
+	for (ife = int_last; ife; ife = ife->prev) {
+		int n = /*n*/strcmp(ife->name, name);
+
+		if (n == 0)
+			return ife;
+		if (n < 0)
+			break;
+	}
+
+	new = xzalloc(sizeof(*new));
+	strncpy_IFNAMSIZ(new->name, name);
+	nextp = ife ? &ife->next : &int_list;
+	new->prev = ife;
+	new->next = *nextp;
+	if (new->next)
+		new->next->prev = new;
+	else
+		int_last = new;
+	*nextp = new;
+	return new;
+}
+
+static char *get_name(char *name, char *p)
+{
+	/* Extract <name> from nul-terminated p where p matches
+	 * <name>: after leading whitespace.
+	 * If match is not made, set name empty and return unchanged p
+	 */
+	char *nameend;
+	char *namestart = skip_whitespace(p);
+
+	nameend = namestart;
+	while (*nameend && *nameend != ':' && !isspace(*nameend))
+		nameend++;
+	if (*nameend == ':') {
+		if ((nameend - namestart) < IFNAMSIZ) {
+			memcpy(name, namestart, nameend - namestart);
+			name[nameend - namestart] = '\0';
+			p = nameend;
+		} else {
+			/* Interface name too large */
+			name[0] = '\0';
+		}
+	} else {
+		/* trailing ':' not found - return empty */
+		name[0] = '\0';
+	}
+	return p + 1;
+}
+
+/* If scanf supports size qualifiers for %n conversions, then we can
+ * use a modified fmt that simply stores the position in the fields
+ * having no associated fields in the proc string.  Of course, we need
+ * to zero them again when we're done.  But that is smaller than the
+ * old approach of multiple scanf occurrences with large numbers of
+ * args. */
+
+/* static const char *const ss_fmt[] = { */
+/*	"%lln%llu%lu%lu%lu%lu%ln%ln%lln%llu%lu%lu%lu%lu%lu", */
+/*	"%llu%llu%lu%lu%lu%lu%ln%ln%llu%llu%lu%lu%lu%lu%lu", */
+/*	"%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu" */
+/* }; */
+
+	/* Lie about the size of the int pointed to for %n. */
+#if INT_MAX == LONG_MAX
+static const char *const ss_fmt[] = {
+	"%n%llu%u%u%u%u%n%n%n%llu%u%u%u%u%u",
+	"%llu%llu%u%u%u%u%n%n%llu%llu%u%u%u%u%u",
+	"%llu%llu%u%u%u%u%u%u%llu%llu%u%u%u%u%u%u"
+};
+#else
+static const char *const ss_fmt[] = {
+	"%n%llu%lu%lu%lu%lu%n%n%n%llu%lu%lu%lu%lu%lu",
+	"%llu%llu%lu%lu%lu%lu%n%n%llu%llu%lu%lu%lu%lu%lu",
+	"%llu%llu%lu%lu%lu%lu%lu%lu%llu%llu%lu%lu%lu%lu%lu%lu"
+};
+
+#endif
+
+static void get_dev_fields(char *bp, struct interface *ife, int procnetdev_vsn)
+{
+	memset(&ife->stats, 0, sizeof(struct user_net_device_stats));
+
+	sscanf(bp, ss_fmt[procnetdev_vsn],
+		   &ife->stats.rx_bytes, /* missing for 0 */
+		   &ife->stats.rx_packets,
+		   &ife->stats.rx_errors,
+		   &ife->stats.rx_dropped,
+		   &ife->stats.rx_fifo_errors,
+		   &ife->stats.rx_frame_errors,
+		   &ife->stats.rx_compressed, /* missing for <= 1 */
+		   &ife->stats.rx_multicast, /* missing for <= 1 */
+		   &ife->stats.tx_bytes, /* missing for 0 */
+		   &ife->stats.tx_packets,
+		   &ife->stats.tx_errors,
+		   &ife->stats.tx_dropped,
+		   &ife->stats.tx_fifo_errors,
+		   &ife->stats.collisions,
+		   &ife->stats.tx_carrier_errors,
+		   &ife->stats.tx_compressed /* missing for <= 1 */
+		   );
+
+	if (procnetdev_vsn <= 1) {
+		if (procnetdev_vsn == 0) {
+			ife->stats.rx_bytes = 0;
+			ife->stats.tx_bytes = 0;
+		}
+		ife->stats.rx_multicast = 0;
+		ife->stats.rx_compressed = 0;
+		ife->stats.tx_compressed = 0;
+	}
+}
+
+static int procnetdev_version(char *buf)
+{
+	if (strstr(buf, "compressed"))
+		return 2;
+	if (strstr(buf, "bytes"))
+		return 1;
+	return 0;
+}
+
+static int if_readconf(void)
+{
+	int numreqs = 30;
+	struct ifconf ifc;
+	struct ifreq *ifr;
+	int n, err = -1;
+	int skfd;
+
+	ifc.ifc_buf = NULL;
+
+	/* SIOCGIFCONF currently seems to only work properly on AF_INET sockets
+	   (as of 2.1.128) */
+	skfd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (skfd < 0) {
+		bb_perror_msg("error: no inet socket available");
+		return -1;
+	}
+
+	for (;;) {
+		ifc.ifc_len = sizeof(struct ifreq) * numreqs;
+		ifc.ifc_buf = xrealloc(ifc.ifc_buf, ifc.ifc_len);
+
+		if (ioctl_or_warn(skfd, SIOCGIFCONF, &ifc) < 0) {
+			goto out;
+		}
+		if (ifc.ifc_len == (int)(sizeof(struct ifreq) * numreqs)) {
+			/* assume it overflowed and try again */
+			numreqs += 10;
+			continue;
+		}
+		break;
+	}
+
+	ifr = ifc.ifc_req;
+	for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq)) {
+		add_interface(ifr->ifr_name);
+		ifr++;
+	}
+	err = 0;
+
+ out:
+	close(skfd);
+	free(ifc.ifc_buf);
+	return err;
+}
+
+static int if_readlist_proc(char *target)
+{
+	static smallint proc_read;
+
+	FILE *fh;
+	char buf[512];
+	struct interface *ife;
+	int err, procnetdev_vsn;
+
+	if (proc_read)
+		return 0;
+	if (!target)
+		proc_read = 1;
+
+	fh = fopen_or_warn(_PATH_PROCNET_DEV, "r");
+	if (!fh) {
+		return if_readconf();
+	}
+	fgets(buf, sizeof buf, fh);	/* eat line */
+	fgets(buf, sizeof buf, fh);
+
+	procnetdev_vsn = procnetdev_version(buf);
+
+	err = 0;
+	while (fgets(buf, sizeof buf, fh)) {
+		char *s, name[128];
+
+		s = get_name(name, buf);
+		ife = add_interface(name);
+		get_dev_fields(s, ife, procnetdev_vsn);
+		ife->statistics_valid = 1;
+		if (target && !strcmp(target, name))
+			break;
+	}
+	if (ferror(fh)) {
+		bb_perror_msg(_PATH_PROCNET_DEV);
+		err = -1;
+		proc_read = 0;
+	}
+	fclose(fh);
+	return err;
+}
+
+static int if_readlist(void)
+{
+	int err = if_readlist_proc(NULL);
+	/* Needed in order to get ethN:M aliases */
+	if (!err)
+		err = if_readconf();
+	return err;
+}
+
+/* Fetch the interface configuration from the kernel. */
+static int if_fetch(struct interface *ife)
+{
+	struct ifreq ifr;
+	char *ifname = ife->name;
+	int skfd;
+
+	skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+	if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) {
+		close(skfd);
+		return -1;
+	}
+	ife->flags = ifr.ifr_flags;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+	memset(ife->hwaddr, 0, 32);
+	if (ioctl(skfd, SIOCGIFHWADDR, &ifr) >= 0)
+		memcpy(ife->hwaddr, ifr.ifr_hwaddr.sa_data, 8);
+
+	ife->type = ifr.ifr_hwaddr.sa_family;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+	ife->metric = 0;
+	if (ioctl(skfd, SIOCGIFMETRIC, &ifr) >= 0)
+		ife->metric = ifr.ifr_metric;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+	ife->mtu = 0;
+	if (ioctl(skfd, SIOCGIFMTU, &ifr) >= 0)
+		ife->mtu = ifr.ifr_mtu;
+
+	memset(&ife->map, 0, sizeof(struct ifmap));
+#ifdef SIOCGIFMAP
+	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+	if (ioctl(skfd, SIOCGIFMAP, &ifr) == 0)
+		ife->map = ifr.ifr_map;
+#endif
+
+#ifdef HAVE_TXQUEUELEN
+	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+	ife->tx_queue_len = -1;	/* unknown value */
+	if (ioctl(skfd, SIOCGIFTXQLEN, &ifr) >= 0)
+		ife->tx_queue_len = ifr.ifr_qlen;
+#else
+	ife->tx_queue_len = -1;	/* unknown value */
+#endif
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+	ifr.ifr_addr.sa_family = AF_INET;
+	memset(&ife->addr, 0, sizeof(struct sockaddr));
+	if (ioctl(skfd, SIOCGIFADDR, &ifr) == 0) {
+		ife->has_ip = 1;
+		ife->addr = ifr.ifr_addr;
+		strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+		memset(&ife->dstaddr, 0, sizeof(struct sockaddr));
+		if (ioctl(skfd, SIOCGIFDSTADDR, &ifr) >= 0)
+			ife->dstaddr = ifr.ifr_dstaddr;
+
+		strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+		memset(&ife->broadaddr, 0, sizeof(struct sockaddr));
+		if (ioctl(skfd, SIOCGIFBRDADDR, &ifr) >= 0)
+			ife->broadaddr = ifr.ifr_broadaddr;
+
+		strncpy_IFNAMSIZ(ifr.ifr_name, ifname);
+		memset(&ife->netmask, 0, sizeof(struct sockaddr));
+		if (ioctl(skfd, SIOCGIFNETMASK, &ifr) >= 0)
+			ife->netmask = ifr.ifr_netmask;
+	}
+
+	close(skfd);
+	return 0;
+}
+
+static int do_if_fetch(struct interface *ife)
+{
+	if (if_fetch(ife) < 0) {
+		const char *errmsg;
+
+		if (errno == ENODEV) {
+			/* Give better error message for this case. */
+			errmsg = "Device not found";
+		} else {
+			errmsg = strerror(errno);
+		}
+		bb_error_msg("%s: error fetching interface information: %s",
+				ife->name, errmsg);
+		return -1;
+	}
+	return 0;
+}
+
+static const struct hwtype unspec_hwtype = {
+	.name =		"unspec",
+	.title =	"UNSPEC",
+	.type =		-1,
+	.print =	UNSPEC_print
+};
+
+static const struct hwtype loop_hwtype = {
+	.name =		"loop",
+	.title =	"Local Loopback",
+	.type =		ARPHRD_LOOPBACK
+};
+
+/* Display an Ethernet address in readable format. */
+static char* FAST_FUNC ether_print(unsigned char *ptr)
+{
+	static char *buff;
+
+	free(buff);
+	buff = xasprintf("%02X:%02X:%02X:%02X:%02X:%02X",
+			 (ptr[0] & 0377), (ptr[1] & 0377), (ptr[2] & 0377),
+			 (ptr[3] & 0377), (ptr[4] & 0377), (ptr[5] & 0377)
+		);
+	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
+};
+
+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",
+	.type =		ARPHRD_PPP
+};
+
+#if ENABLE_FEATURE_IPV6
+static const struct hwtype sit_hwtype = {
+	.name =			"sit",
+	.title =		"IPv6-in-IPv4",
+	.type =			ARPHRD_SIT,
+	.print =		UNSPEC_print,
+	.suppress_null_addr =	1
+};
+#endif
+#if ENABLE_FEATURE_HWIB
+static const struct hwtype ib_hwtype = {
+	.name  = "infiniband",
+	.title = "InfiniBand",
+	.type  = ARPHRD_INFINIBAND,
+	.alen  = INFINIBAND_ALEN,
+	.print = UNSPEC_print,
+	.input = in_ib,
+};
+#endif
+
+
+static const struct hwtype *const hwtypes[] = {
+	&loop_hwtype,
+	&ether_hwtype,
+	&ppp_hwtype,
+	&unspec_hwtype,
+#if ENABLE_FEATURE_IPV6
+	&sit_hwtype,
+#endif
+#if ENABLE_FEATURE_HWIB
+	&ib_hwtype,
+#endif
+	NULL
+};
+
+#ifdef IFF_PORTSEL
+static const char *const if_port_text[] = {
+	/* Keep in step with <linux/netdevice.h> */
+	"unknown",
+	"10base2",
+	"10baseT",
+	"AUI",
+	"100baseT",
+	"100baseTX",
+	"100baseFX",
+	NULL
+};
+#endif
+
+/* Check our hardware type table for this type. */
+const struct hwtype* FAST_FUNC get_hwtype(const char *name)
+{
+	const struct hwtype *const *hwp;
+
+	hwp = hwtypes;
+	while (*hwp != NULL) {
+		if (!strcmp((*hwp)->name, name))
+			return (*hwp);
+		hwp++;
+	}
+	return NULL;
+}
+
+/* Check our hardware type table for this type. */
+const struct hwtype* FAST_FUNC get_hwntype(int type)
+{
+	const struct hwtype *const *hwp;
+
+	hwp = hwtypes;
+	while (*hwp != NULL) {
+		if ((*hwp)->type == type)
+			return *hwp;
+		hwp++;
+	}
+	return NULL;
+}
+
+/* return 1 if address is all zeros */
+static int hw_null_address(const struct hwtype *hw, void *ap)
+{
+	int i;
+	unsigned char *address = (unsigned char *) ap;
+
+	for (i = 0; i < hw->alen; i++)
+		if (address[i])
+			return 0;
+	return 1;
+}
+
+static const char TRext[] ALIGN1 = "\0\0\0Ki\0Mi\0Gi\0Ti";
+
+static void print_bytes_scaled(unsigned long long ull, const char *end)
+{
+	unsigned long long int_part;
+	const char *ext;
+	unsigned int frac_part;
+	int i;
+
+	frac_part = 0;
+	ext = TRext;
+	int_part = ull;
+	i = 4;
+	do {
+		if (int_part >= 1024) {
+			frac_part = ((((unsigned int) int_part) & (1024-1)) * 10) / 1024;
+			int_part /= 1024;
+			ext += 3;	/* KiB, MiB, GiB, TiB */
+		}
+		--i;
+	} while (i);
+
+	printf("X bytes:%llu (%llu.%u %sB)%s", ull, int_part, frac_part, ext, end);
+}
+
+
+#ifdef HAVE_AFINET6
+#define IPV6_ADDR_ANY           0x0000U
+
+#define IPV6_ADDR_UNICAST       0x0001U
+#define IPV6_ADDR_MULTICAST     0x0002U
+#define IPV6_ADDR_ANYCAST       0x0004U
+
+#define IPV6_ADDR_LOOPBACK      0x0010U
+#define IPV6_ADDR_LINKLOCAL     0x0020U
+#define IPV6_ADDR_SITELOCAL     0x0040U
+
+#define IPV6_ADDR_COMPATv4      0x0080U
+
+#define IPV6_ADDR_SCOPE_MASK    0x00f0U
+
+#define IPV6_ADDR_MAPPED        0x1000U
+#define IPV6_ADDR_RESERVED      0x2000U	/* reserved address space */
+
+
+static void ife_print6(struct interface *ptr)
+{
+	FILE *f;
+	char addr6[40], devname[20];
+	struct sockaddr_in6 sap;
+	int plen, scope, dad_status, if_idx;
+	char addr6p[8][5];
+
+	f = fopen_for_read(_PATH_PROCNET_IFINET6);
+	if (f == NULL)
+		return;
+
+	while (fscanf
+		   (f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
+			addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4],
+			addr6p[5], addr6p[6], addr6p[7], &if_idx, &plen, &scope,
+			&dad_status, devname) != EOF
+	) {
+		if (!strcmp(devname, ptr->name)) {
+			sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
+					addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+					addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+			inet_pton(AF_INET6, addr6,
+					  (struct sockaddr *) &sap.sin6_addr);
+			sap.sin6_family = AF_INET6;
+			printf("          inet6 addr: %s/%d",
+				   INET6_sprint((struct sockaddr *) &sap, 1),
+				   plen);
+			printf(" Scope:");
+			switch (scope & IPV6_ADDR_SCOPE_MASK) {
+			case 0:
+				puts("Global");
+				break;
+			case IPV6_ADDR_LINKLOCAL:
+				puts("Link");
+				break;
+			case IPV6_ADDR_SITELOCAL:
+				puts("Site");
+				break;
+			case IPV6_ADDR_COMPATv4:
+				puts("Compat");
+				break;
+			case IPV6_ADDR_LOOPBACK:
+				puts("Host");
+				break;
+			default:
+				puts("Unknown");
+			}
+		}
+	}
+	fclose(f);
+}
+#else
+#define ife_print6(a) ((void)0)
+#endif
+
+static void ife_print(struct interface *ptr)
+{
+	const struct aftype *ap;
+	const struct hwtype *hw;
+	int hf;
+	int can_compress = 0;
+
+	ap = get_afntype(ptr->addr.sa_family);
+	if (ap == NULL)
+		ap = get_afntype(0);
+
+	hf = ptr->type;
+
+	if (hf == ARPHRD_CSLIP || hf == ARPHRD_CSLIP6)
+		can_compress = 1;
+
+	hw = get_hwntype(hf);
+	if (hw == NULL)
+		hw = get_hwntype(-1);
+
+	printf("%-9s Link encap:%s  ", ptr->name, hw->title);
+	/* For some hardware types (eg Ash, ATM) we don't print the
+	   hardware address if it's null.  */
+	if (hw->print != NULL
+	 && !(hw_null_address(hw, ptr->hwaddr) && hw->suppress_null_addr)
+	) {
+		printf("HWaddr %s  ", hw->print((unsigned char *)ptr->hwaddr));
+	}
+#ifdef IFF_PORTSEL
+	if (ptr->flags & IFF_PORTSEL) {
+		printf("Media:%s", if_port_text[ptr->map.port] /* [0] */);
+		if (ptr->flags & IFF_AUTOMEDIA)
+			printf("(auto)");
+	}
+#endif
+	bb_putchar('\n');
+
+	if (ptr->has_ip) {
+		printf("          %s addr:%s ", ap->name,
+			   ap->sprint(&ptr->addr, 1));
+		if (ptr->flags & IFF_POINTOPOINT) {
+			printf(" P-t-P:%s ", ap->sprint(&ptr->dstaddr, 1));
+		}
+		if (ptr->flags & IFF_BROADCAST) {
+			printf(" Bcast:%s ", ap->sprint(&ptr->broadaddr, 1));
+		}
+		printf(" Mask:%s\n", ap->sprint(&ptr->netmask, 1));
+	}
+
+	ife_print6(ptr);
+
+	printf("          ");
+	/* DONT FORGET TO ADD THE FLAGS IN ife_print_short, too */
+
+	if (ptr->flags == 0) {
+		printf("[NO FLAGS] ");
+	} else {
+		static const char ife_print_flags_strs[] ALIGN1 =
+			"UP\0"
+			"BROADCAST\0"
+			"DEBUG\0"
+			"LOOPBACK\0"
+			"POINTOPOINT\0"
+			"NOTRAILERS\0"
+			"RUNNING\0"
+			"NOARP\0"
+			"PROMISC\0"
+			"ALLMULTI\0"
+			"SLAVE\0"
+			"MASTER\0"
+			"MULTICAST\0"
+#ifdef HAVE_DYNAMIC
+			"DYNAMIC\0"
+#endif
+			;
+		static const unsigned short ife_print_flags_mask[] ALIGN2 = {
+			IFF_UP,
+			IFF_BROADCAST,
+			IFF_DEBUG,
+			IFF_LOOPBACK,
+			IFF_POINTOPOINT,
+			IFF_NOTRAILERS,
+			IFF_RUNNING,
+			IFF_NOARP,
+			IFF_PROMISC,
+			IFF_ALLMULTI,
+			IFF_SLAVE,
+			IFF_MASTER,
+			IFF_MULTICAST
+#ifdef HAVE_DYNAMIC
+			,IFF_DYNAMIC
+#endif
+		};
+		const unsigned short *mask = ife_print_flags_mask;
+		const char *str = ife_print_flags_strs;
+		do {
+			if (ptr->flags & *mask) {
+				printf("%s ", str);
+			}
+			mask++;
+			str += strlen(str) + 1;
+		} while (*str);
+	}
+
+	/* DONT FORGET TO ADD THE FLAGS IN ife_print_short */
+	printf(" MTU:%d  Metric:%d", ptr->mtu, ptr->metric ? ptr->metric : 1);
+#ifdef SIOCSKEEPALIVE
+	if (ptr->outfill || ptr->keepalive)
+		printf("  Outfill:%d  Keepalive:%d", ptr->outfill, ptr->keepalive);
+#endif
+	bb_putchar('\n');
+
+	/* If needed, display the interface statistics. */
+
+	if (ptr->statistics_valid) {
+		/* XXX: statistics are currently only printed for the primary address,
+		 *      not for the aliases, although strictly speaking they're shared
+		 *      by all addresses.
+		 */
+		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);
+		if (can_compress)
+			printf("             compressed:%lu\n",
+				   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);
+		printf("          collisions:%lu ", ptr->stats.collisions);
+		if (can_compress)
+			printf("compressed:%lu ", ptr->stats.tx_compressed);
+		if (ptr->tx_queue_len != -1)
+			printf("txqueuelen:%d ", ptr->tx_queue_len);
+		printf("\n          R");
+		print_bytes_scaled(ptr->stats.rx_bytes, "  T");
+		print_bytes_scaled(ptr->stats.tx_bytes, "\n");
+	}
+
+	if (ptr->map.irq || ptr->map.mem_start
+	 || ptr->map.dma || ptr->map.base_addr
+	) {
+		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 */
+			printf("Base address:0x%lx ",
+				   (unsigned long) ptr->map.base_addr);
+		if (ptr->map.mem_start) {
+			printf("Memory:%lx-%lx ", ptr->map.mem_start,
+				   ptr->map.mem_end);
+		}
+		if (ptr->map.dma)
+			printf("DMA chan:%x ", ptr->map.dma);
+		bb_putchar('\n');
+	}
+	bb_putchar('\n');
+}
+
+static int do_if_print(struct interface *ife) /*, int *opt_a)*/
+{
+	int res;
+
+	res = do_if_fetch(ife);
+	if (res >= 0) {
+		if ((ife->flags & IFF_UP) || interface_opt_a)
+			ife_print(ife);
+	}
+	return res;
+}
+
+static struct interface *lookup_interface(char *name)
+{
+	struct interface *ife = NULL;
+
+	if (if_readlist_proc(name) < 0)
+		return NULL;
+	ife = add_interface(name);
+	return ife;
+}
+
+#ifdef UNUSED
+static int for_all_interfaces(int (*doit) (struct interface *, void *),
+							  void *cookie)
+{
+	struct interface *ife;
+
+	if (!int_list && (if_readlist() < 0))
+		return -1;
+	for (ife = int_list; ife; ife = ife->next) {
+		int err = doit(ife, cookie);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+#endif
+
+/* for ipv4 add/del modes */
+static int if_print(char *ifname)
+{
+	struct interface *ife;
+	int res;
+
+	if (!ifname) {
+		/*res = for_all_interfaces(do_if_print, &interface_opt_a);*/
+		if (!int_list && (if_readlist() < 0))
+			return -1;
+		for (ife = int_list; ife; ife = ife->next) {
+			int err = do_if_print(ife); /*, &interface_opt_a);*/
+			if (err)
+				return err;
+		}
+		return 0;
+	}
+	ife = lookup_interface(ifname);
+	res = do_if_fetch(ife);
+	if (res >= 0)
+		ife_print(ife);
+	return res;
+}
+
+#if ENABLE_FEATURE_HWIB
+/* Input an Infiniband address and convert to binary. */
+int FAST_FUNC in_ib(const char *bufp, struct sockaddr *sap)
+{
+	sap->sa_family = ib_hwtype.type;
+//TODO: error check?
+	hex2bin((char*)sap->sa_data, bufp, INFINIBAND_ALEN);
+# ifdef HWIB_DEBUG
+	fprintf(stderr, "in_ib(%s): %s\n", bufp, UNSPEC_print(sap->sa_data));
+# endif
+	return 0;
+}
+#endif
+
+int FAST_FUNC display_interfaces(char *ifname)
+{
+	int status;
+
+	status = if_print(ifname);
+
+	return (status < 0); /* status < 0 == 1 -- error */
+}
diff --git a/busybox-1.19.3/networking/ip.c b/busybox-1.19.3/networking/ip.c
new file mode 100644
index 0000000..fb2f5e2
--- /dev/null
+++ b/busybox-1.19.3/networking/ip.c
@@ -0,0 +1,178 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+ * 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 | monitor } 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 | monitor } ROUTE\n"
+//usage:       "			SELECTOR := [root PREFIX] [match PREFIX] [proto RTPROTO]\n"
+//usage:       "			ROUTE := [TYPE] PREFIX [tos TOS] [proto RTPROTO]\n"
+//usage:       "				[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"
+#include "libiproute/ip_common.h"
+
+#if ENABLE_FEATURE_IP_ADDRESS \
+ || ENABLE_FEATURE_IP_ROUTE \
+ || ENABLE_FEATURE_IP_LINK \
+ || ENABLE_FEATURE_IP_TUNNEL \
+ || ENABLE_FEATURE_IP_RULE
+
+static int FAST_FUNC ip_print_help(char **argv UNUSED_PARAM)
+{
+	bb_show_usage();
+}
+
+typedef int FAST_FUNC (*ip_func_ptr_t)(char**);
+
+static int ip_do(ip_func_ptr_t ip_func, char **argv)
+{
+	argv = ip_parse_common_args(argv + 1);
+	return ip_func(argv);
+}
+
+#if ENABLE_FEATURE_IP_ADDRESS
+int ipaddr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipaddr_main(int argc UNUSED_PARAM, char **argv)
+{
+	return ip_do(do_ipaddr, argv);
+}
+#endif
+#if ENABLE_FEATURE_IP_LINK
+int iplink_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iplink_main(int argc UNUSED_PARAM, char **argv)
+{
+	return ip_do(do_iplink, argv);
+}
+#endif
+#if ENABLE_FEATURE_IP_ROUTE
+int iproute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iproute_main(int argc UNUSED_PARAM, char **argv)
+{
+	return ip_do(do_iproute, argv);
+}
+#endif
+#if ENABLE_FEATURE_IP_RULE
+int iprule_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iprule_main(int argc UNUSED_PARAM, char **argv)
+{
+	return ip_do(do_iprule, argv);
+}
+#endif
+#if ENABLE_FEATURE_IP_TUNNEL
+int iptunnel_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int iptunnel_main(int argc UNUSED_PARAM, char **argv)
+{
+	return ip_do(do_iptunnel, argv);
+}
+#endif
+
+
+int ip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ip_main(int argc UNUSED_PARAM, char **argv)
+{
+	static const char keywords[] ALIGN1 =
+		IF_FEATURE_IP_ADDRESS("address\0")
+		IF_FEATURE_IP_ROUTE("route\0")
+		IF_FEATURE_IP_ROUTE("r\0")
+		IF_FEATURE_IP_LINK("link\0")
+		IF_FEATURE_IP_TUNNEL("tunnel\0")
+		IF_FEATURE_IP_TUNNEL("tunl\0")
+		IF_FEATURE_IP_RULE("rule\0")
+		;
+	static const ip_func_ptr_t ip_func_ptrs[] = {
+		ip_print_help,
+		IF_FEATURE_IP_ADDRESS(do_ipaddr,)
+		IF_FEATURE_IP_ROUTE(do_iproute,)
+		IF_FEATURE_IP_ROUTE(do_iproute,)
+		IF_FEATURE_IP_LINK(do_iplink,)
+		IF_FEATURE_IP_TUNNEL(do_iptunnel,)
+		IF_FEATURE_IP_TUNNEL(do_iptunnel,)
+		IF_FEATURE_IP_RULE(do_iprule,)
+	};
+	ip_func_ptr_t ip_func;
+	int key;
+
+	argv = ip_parse_common_args(argv + 1);
+	key = *argv ? index_in_substrings(keywords, *argv++) : -1;
+	ip_func = ip_func_ptrs[key + 1];
+
+	return ip_func(argv);
+}
+
+#endif /* any of ENABLE_FEATURE_IP_xxx is 1 */
diff --git a/busybox-1.19.3/networking/ipcalc.c b/busybox-1.19.3/networking/ipcalc.c
new file mode 100644
index 0000000..3c8b8bf
--- /dev/null
+++ b/busybox-1.19.3/networking/ipcalc.c
@@ -0,0 +1,213 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ipcalc implementation for busybox
+ *
+ * By Jordan Crouse <jordan@cosmicpenguin.net>
+ *    Stephan Linz  <linz@li-pro.net>
+ *
+ * This is a complete reimplementation of the ipcalc program
+ * 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 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)
+
+static unsigned long get_netmask(unsigned long ipaddr)
+{
+	ipaddr = htonl(ipaddr);
+
+	if ((ipaddr & 0xC0000000) == 0xC0000000)
+		return CLASS_C_NETMASK;
+	else if ((ipaddr & 0x80000000) == 0x80000000)
+		return CLASS_B_NETMASK;
+	else if ((ipaddr & 0x80000000) == 0)
+		return CLASS_A_NETMASK;
+	else
+		return 0;
+}
+
+#if ENABLE_FEATURE_IPCALC_FANCY
+static int get_prefix(unsigned long netmask)
+{
+	unsigned long msk = 0x80000000;
+	int ret = 0;
+
+	netmask = htonl(netmask);
+	while (msk) {
+		if (netmask & msk)
+			ret++;
+		msk >>= 1;
+	}
+	return ret;
+}
+#else
+int get_prefix(unsigned long netmask);
+#endif
+
+
+#define NETMASK   0x01
+#define BROADCAST 0x02
+#define NETWORK   0x04
+#define NETPREFIX 0x08
+#define HOSTNAME  0x10
+#define SILENT    0x20
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+	static const char ipcalc_longopts[] ALIGN1 =
+		"netmask\0"   No_argument "m" // netmask from IP (assuming complete class A, B, or C network)
+		"broadcast\0" No_argument "b" // broadcast from IP [netmask]
+		"network\0"   No_argument "n" // network from IP [netmask]
+# if ENABLE_FEATURE_IPCALC_FANCY
+		"prefix\0"    No_argument "p" // prefix from IP[/prefix] [netmask]
+		"hostname\0"  No_argument "h" // hostname from IP
+		"silent\0"    No_argument "s" // don’t ever display error messages
+# endif
+		;
+#endif
+
+int ipcalc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipcalc_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	bool have_netmask = 0;
+	struct in_addr s_netmask, s_broadcast, s_network, s_ipaddr;
+	/* struct in_addr { in_addr_t s_addr; }  and  in_addr_t
+	 * (which in turn is just a typedef to uint32_t)
+	 * are essentially the same type. A few macros for less verbosity: */
+#define netmask   (s_netmask.s_addr)
+#define broadcast (s_broadcast.s_addr)
+#define network   (s_network.s_addr)
+#define ipaddr    (s_ipaddr.s_addr)
+	char *ipstr;
+
+#if ENABLE_FEATURE_IPCALC_LONG_OPTIONS
+	applet_long_options = ipcalc_longopts;
+#endif
+	opt_complementary = "-1:?2"; /* minimum 1 arg, maximum 2 args */
+	opt = getopt32(argv, "mbn" IF_FEATURE_IPCALC_FANCY("phs"));
+	argv += optind;
+	if (opt & SILENT)
+		logmode = LOGMODE_NONE; /* suppress error_msg() output */
+	opt &= ~SILENT;
+	if (!(opt & (BROADCAST | NETWORK | NETPREFIX))) {
+		/* if no options at all or
+		 * (no broadcast,network,prefix) and (two args)... */
+		if (!opt || argv[1])
+			bb_show_usage();
+	}
+
+	ipstr = argv[0];
+	if (ENABLE_FEATURE_IPCALC_FANCY) {
+		unsigned long netprefix = 0;
+		char *prefixstr;
+
+		prefixstr = ipstr;
+
+		while (*prefixstr) {
+			if (*prefixstr == '/') {
+				*prefixstr++ = '\0';
+				if (*prefixstr) {
+					unsigned msk;
+					netprefix = xatoul_range(prefixstr, 0, 32);
+					netmask = 0;
+					msk = 0x80000000;
+					while (netprefix > 0) {
+						netmask |= msk;
+						msk >>= 1;
+						netprefix--;
+					}
+					netmask = htonl(netmask);
+					/* Even if it was 0, we will signify that we have a netmask. This allows */
+					/* for specification of default routes, etc which have a 0 netmask/prefix */
+					have_netmask = 1;
+				}
+				break;
+			}
+			prefixstr++;
+		}
+	}
+
+	if (inet_aton(ipstr, &s_ipaddr) == 0) {
+		bb_error_msg_and_die("bad IP address: %s", argv[0]);
+	}
+
+	if (argv[1]) {
+		if (ENABLE_FEATURE_IPCALC_FANCY && have_netmask) {
+			bb_error_msg_and_die("use prefix or netmask, not both");
+		}
+		if (inet_aton(argv[1], &s_netmask) == 0) {
+			bb_error_msg_and_die("bad netmask: %s", argv[1]);
+		}
+	} else {
+		/* JHC - If the netmask wasn't provided then calculate it */
+		if (!ENABLE_FEATURE_IPCALC_FANCY || !have_netmask)
+			netmask = get_netmask(ipaddr);
+	}
+
+	if (opt & NETMASK) {
+		printf("NETMASK=%s\n", inet_ntoa(s_netmask));
+	}
+
+	if (opt & BROADCAST) {
+		broadcast = (ipaddr & netmask) | ~netmask;
+		printf("BROADCAST=%s\n", inet_ntoa(s_broadcast));
+	}
+
+	if (opt & NETWORK) {
+		network = ipaddr & netmask;
+		printf("NETWORK=%s\n", inet_ntoa(s_network));
+	}
+
+	if (ENABLE_FEATURE_IPCALC_FANCY) {
+		if (opt & NETPREFIX) {
+			printf("PREFIX=%i\n", get_prefix(netmask));
+		}
+
+		if (opt & HOSTNAME) {
+			struct hostent *hostinfo;
+
+			hostinfo = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET);
+			if (!hostinfo) {
+				bb_herror_msg_and_die("can't find hostname for %s", argv[0]);
+			}
+			str_tolower(hostinfo->h_name);
+
+			printf("HOSTNAME=%s\n", hostinfo->h_name);
+		}
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/isrv.c b/busybox-1.19.3/networking/isrv.c
new file mode 100644
index 0000000..1c6491e
--- /dev/null
+++ b/busybox-1.19.3/networking/isrv.c
@@ -0,0 +1,338 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "isrv.h"
+
+#define DEBUG 0
+
+#if DEBUG
+#define DPRINTF(args...) bb_error_msg(args)
+#else
+#define DPRINTF(args...) ((void)0)
+#endif
+
+/* Helpers */
+
+/* Opaque structure */
+
+struct isrv_state_t {
+	short  *fd2peer; /* one per registered fd */
+	void  **param_tbl; /* one per registered peer */
+	/* one per registered peer; doesn't exist if !timeout */
+	time_t *timeo_tbl;
+	int   (*new_peer)(isrv_state_t *state, int fd);
+	time_t  curtime;
+	int     timeout;
+	int     fd_count;
+	int     peer_count;
+	int     wr_count;
+	fd_set  rd;
+	fd_set  wr;
+};
+#define FD2PEER    (state->fd2peer)
+#define PARAM_TBL  (state->param_tbl)
+#define TIMEO_TBL  (state->timeo_tbl)
+#define CURTIME    (state->curtime)
+#define TIMEOUT    (state->timeout)
+#define FD_COUNT   (state->fd_count)
+#define PEER_COUNT (state->peer_count)
+#define WR_COUNT   (state->wr_count)
+
+/* callback */
+void isrv_want_rd(isrv_state_t *state, int fd)
+{
+	FD_SET(fd, &state->rd);
+}
+
+/* callback */
+void isrv_want_wr(isrv_state_t *state, int fd)
+{
+	if (!FD_ISSET(fd, &state->wr)) {
+		WR_COUNT++;
+		FD_SET(fd, &state->wr);
+	}
+}
+
+/* callback */
+void isrv_dont_want_rd(isrv_state_t *state, int fd)
+{
+	FD_CLR(fd, &state->rd);
+}
+
+/* callback */
+void isrv_dont_want_wr(isrv_state_t *state, int fd)
+{
+	if (FD_ISSET(fd, &state->wr)) {
+		WR_COUNT--;
+		FD_CLR(fd, &state->wr);
+	}
+}
+
+/* callback */
+int isrv_register_fd(isrv_state_t *state, int peer, int fd)
+{
+	int n;
+
+	DPRINTF("register_fd(peer:%d,fd:%d)", peer, fd);
+
+	if (FD_COUNT >= FD_SETSIZE) return -1;
+	if (FD_COUNT <= fd) {
+		n = FD_COUNT;
+		FD_COUNT = fd + 1;
+
+		DPRINTF("register_fd: FD_COUNT %d", FD_COUNT);
+
+		FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+		while (n < fd) FD2PEER[n++] = -1;
+	}
+
+	DPRINTF("register_fd: FD2PEER[%d] = %d", fd, peer);
+
+	FD2PEER[fd] = peer;
+	return 0;
+}
+
+/* callback */
+void isrv_close_fd(isrv_state_t *state, int fd)
+{
+	DPRINTF("close_fd(%d)", fd);
+
+	close(fd);
+	isrv_dont_want_rd(state, fd);
+	if (WR_COUNT) isrv_dont_want_wr(state, fd);
+
+	FD2PEER[fd] = -1;
+	if (fd == FD_COUNT-1) {
+		do fd--; while (fd >= 0 && FD2PEER[fd] == -1);
+		FD_COUNT = fd + 1;
+
+		DPRINTF("close_fd: FD_COUNT %d", FD_COUNT);
+
+		FD2PEER = xrealloc(FD2PEER, FD_COUNT * sizeof(FD2PEER[0]));
+	}
+}
+
+/* callback */
+int isrv_register_peer(isrv_state_t *state, void *param)
+{
+	int n;
+
+	if (PEER_COUNT >= FD_SETSIZE) return -1;
+	n = PEER_COUNT++;
+
+	DPRINTF("register_peer: PEER_COUNT %d", PEER_COUNT);
+
+	PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+	PARAM_TBL[n] = param;
+	if (TIMEOUT) {
+		TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+		TIMEO_TBL[n] = CURTIME;
+	}
+	return n;
+}
+
+static void remove_peer(isrv_state_t *state, int peer)
+{
+	int movesize;
+	int fd;
+
+	DPRINTF("remove_peer(%d)", peer);
+
+	fd = FD_COUNT - 1;
+	while (fd >= 0) {
+		if (FD2PEER[fd] == peer) {
+			isrv_close_fd(state, fd);
+			fd--;
+			continue;
+		}
+		if (FD2PEER[fd] > peer)
+			FD2PEER[fd]--;
+		fd--;
+	}
+
+	PEER_COUNT--;
+	DPRINTF("remove_peer: PEER_COUNT %d", PEER_COUNT);
+
+	movesize = (PEER_COUNT - peer) * sizeof(void*);
+	if (movesize > 0) {
+		memcpy(&PARAM_TBL[peer], &PARAM_TBL[peer+1], movesize);
+		if (TIMEOUT)
+			memcpy(&TIMEO_TBL[peer], &TIMEO_TBL[peer+1], movesize);
+	}
+	PARAM_TBL = xrealloc(PARAM_TBL, PEER_COUNT * sizeof(PARAM_TBL[0]));
+	if (TIMEOUT)
+		TIMEO_TBL = xrealloc(TIMEO_TBL, PEER_COUNT * sizeof(TIMEO_TBL[0]));
+}
+
+static void handle_accept(isrv_state_t *state, int fd)
+{
+	int n, newfd;
+
+	/* suppress gcc warning "cast from ptr to int of different size" */
+	fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]) | O_NONBLOCK);
+	newfd = accept(fd, NULL, 0);
+	fcntl(fd, F_SETFL, (int)(ptrdiff_t)(PARAM_TBL[0]));
+	if (newfd < 0) {
+		if (errno == EAGAIN) return;
+		/* Most probably someone gave us wrong fd type
+		 * (for example, non-socket). Don't want
+		 * to loop forever. */
+		bb_perror_msg_and_die("accept");
+	}
+
+	DPRINTF("new_peer(%d)", newfd);
+	n = state->new_peer(state, newfd);
+	if (n)
+		remove_peer(state, n); /* unsuccesful peer start */
+}
+
+void BUG_sizeof_fd_set_is_strange(void);
+static void handle_fd_set(isrv_state_t *state, fd_set *fds, int (*h)(int, void **))
+{
+	enum { LONG_CNT = sizeof(fd_set) / sizeof(long) };
+	int fds_pos;
+	int fd, peer;
+	/* need to know value at _the beginning_ of this routine */
+	int fd_cnt = FD_COUNT;
+
+	if (LONG_CNT * sizeof(long) != sizeof(fd_set))
+		BUG_sizeof_fd_set_is_strange();
+
+	fds_pos = 0;
+	while (1) {
+		/* Find next nonzero bit */
+		while (fds_pos < LONG_CNT) {
+			if (((long*)fds)[fds_pos] == 0) {
+				fds_pos++;
+				continue;
+			}
+			/* Found non-zero word */
+			fd = fds_pos * sizeof(long)*8; /* word# -> bit# */
+			while (1) {
+				if (FD_ISSET(fd, fds)) {
+					FD_CLR(fd, fds);
+					goto found_fd;
+				}
+				fd++;
+			}
+		}
+		break; /* all words are zero */
+ found_fd:
+		if (fd >= fd_cnt) { /* paranoia */
+			DPRINTF("handle_fd_set: fd > fd_cnt?? (%d > %d)",
+					fd, fd_cnt);
+			break;
+		}
+		DPRINTF("handle_fd_set: fd %d is active", fd);
+		peer = FD2PEER[fd];
+		if (peer < 0)
+			continue; /* peer is already gone */
+		if (peer == 0) {
+			handle_accept(state, fd);
+			continue;
+		}
+		DPRINTF("h(fd:%d)", fd);
+		if (h(fd, &PARAM_TBL[peer])) {
+			/* this peer is gone */
+			remove_peer(state, peer);
+		} else if (TIMEOUT) {
+			TIMEO_TBL[peer] = monotonic_sec();
+		}
+	}
+}
+
+static void handle_timeout(isrv_state_t *state, int (*do_timeout)(void **))
+{
+	int n, peer;
+	peer = PEER_COUNT-1;
+	/* peer 0 is not checked */
+	while (peer > 0) {
+		DPRINTF("peer %d: time diff %d", peer,
+				(int)(CURTIME - TIMEO_TBL[peer]));
+		if ((CURTIME - TIMEO_TBL[peer]) >= TIMEOUT) {
+			DPRINTF("peer %d: do_timeout()", peer);
+			n = do_timeout(&PARAM_TBL[peer]);
+			if (n)
+				remove_peer(state, peer);
+		}
+		peer--;
+	}
+}
+
+/* Driver */
+void isrv_run(
+	int listen_fd,
+	int (*new_peer)(isrv_state_t *state, int fd),
+	int (*do_rd)(int fd, void **),
+	int (*do_wr)(int fd, void **),
+	int (*do_timeout)(void **),
+	int timeout,
+	int linger_timeout)
+{
+	isrv_state_t *state = xzalloc(sizeof(*state));
+	state->new_peer = new_peer;
+	state->timeout  = timeout;
+
+	/* register "peer" #0 - it will accept new connections */
+	isrv_register_peer(state, NULL);
+	isrv_register_fd(state, /*peer:*/ 0, listen_fd);
+	isrv_want_rd(state, listen_fd);
+	/* remember flags to make blocking<->nonblocking switch faster */
+	/* (suppress gcc warning "cast from ptr to int of different size") */
+	PARAM_TBL[0] = (void*)(ptrdiff_t)(fcntl(listen_fd, F_GETFL));
+
+	while (1) {
+		struct timeval tv;
+		fd_set rd;
+		fd_set wr;
+		fd_set *wrp = NULL;
+		int n;
+
+		tv.tv_sec = timeout;
+		if (PEER_COUNT <= 1)
+			tv.tv_sec = linger_timeout;
+		tv.tv_usec = 0;
+		rd = state->rd;
+		if (WR_COUNT) {
+			wr = state->wr;
+			wrp = &wr;
+		}
+
+		DPRINTF("run: select(FD_COUNT:%d,timeout:%d)...",
+				FD_COUNT, (int)tv.tv_sec);
+		n = select(FD_COUNT, &rd, wrp, NULL, tv.tv_sec ? &tv : NULL);
+		DPRINTF("run: ...select:%d", n);
+
+		if (n < 0) {
+			if (errno != EINTR)
+				bb_perror_msg("select");
+			continue;
+		}
+
+		if (n == 0 && linger_timeout && PEER_COUNT <= 1)
+			break;
+
+		if (timeout) {
+			time_t t = monotonic_sec();
+			if (t != CURTIME) {
+				CURTIME = t;
+				handle_timeout(state, do_timeout);
+			}
+		}
+		if (n > 0) {
+			handle_fd_set(state, &rd, do_rd);
+			if (wrp)
+				handle_fd_set(state, wrp, do_wr);
+		}
+	}
+	DPRINTF("run: bailout");
+	/* NB: accept socket is not closed. Caller is to decide what to do */
+}
diff --git a/busybox-1.19.3/networking/isrv.h b/busybox-1.19.3/networking/isrv.h
new file mode 100644
index 0000000..4c7e01d
--- /dev/null
+++ b/busybox-1.19.3/networking/isrv.h
@@ -0,0 +1,37 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Generic non-forking server infrastructure.
+ * Intended to make writing telnetd-type servers easier.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* opaque structure */
+struct isrv_state_t;
+typedef struct isrv_state_t isrv_state_t;
+
+/* callbacks */
+void isrv_want_rd(isrv_state_t *state, int fd);
+void isrv_want_wr(isrv_state_t *state, int fd);
+void isrv_dont_want_rd(isrv_state_t *state, int fd);
+void isrv_dont_want_wr(isrv_state_t *state, int fd);
+int isrv_register_fd(isrv_state_t *state, int peer, int fd);
+void isrv_close_fd(isrv_state_t *state, int fd);
+int isrv_register_peer(isrv_state_t *state, void *param);
+
+/* driver */
+void isrv_run(
+	int listen_fd,
+	int (*new_peer)(isrv_state_t *state, int fd),
+	int (*do_rd)(int fd, void **),
+	int (*do_wr)(int fd, void **),
+	int (*do_timeout)(void **),
+	int timeout,
+	int linger_timeout
+);
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/busybox-1.19.3/networking/isrv_identd.c b/busybox-1.19.3/networking/isrv_identd.c
new file mode 100644
index 0000000..a41405c
--- /dev/null
+++ b/busybox-1.19.3/networking/isrv_identd.c
@@ -0,0 +1,157 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Fake identd server.
+ *
+ * Copyright (C) 2007 Denys Vlasenko
+ *
+ * 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"
+
+enum { TIMEOUT = 20 };
+
+typedef struct identd_buf_t {
+	int pos;
+	int fd_flag;
+	char buf[64 - 2*sizeof(int)];
+} identd_buf_t;
+
+#define bogouser bb_common_bufsiz1
+
+static int new_peer(isrv_state_t *state, int fd)
+{
+	int peer;
+	identd_buf_t *buf = xzalloc(sizeof(*buf));
+
+	peer = isrv_register_peer(state, buf);
+	if (peer < 0)
+		return 0; /* failure */
+	if (isrv_register_fd(state, peer, fd) < 0)
+		return peer; /* failure, unregister peer */
+
+	buf->fd_flag = fcntl(fd, F_GETFL) | O_NONBLOCK;
+	isrv_want_rd(state, fd);
+	return 0;
+}
+
+static int do_rd(int fd, void **paramp)
+{
+	identd_buf_t *buf = *paramp;
+	char *cur, *p;
+	int retval = 0; /* session is ok (so far) */
+	int sz;
+
+	cur = buf->buf + buf->pos;
+
+	if (buf->fd_flag & O_NONBLOCK)
+		fcntl(fd, F_SETFL, buf->fd_flag);
+	sz = safe_read(fd, cur, sizeof(buf->buf) - buf->pos);
+
+	if (sz < 0) {
+		if (errno != EAGAIN)
+			goto term; /* terminate this session if !EAGAIN */
+		goto ok;
+	}
+
+	buf->pos += sz;
+	buf->buf[buf->pos] = '\0';
+	p = strpbrk(cur, "\r\n");
+	if (p)
+		*p = '\0';
+	if (!p && sz && buf->pos <= (int)sizeof(buf->buf))
+		goto ok;
+	/* Terminate session. If we are in server mode, then
+	 * fd is still in nonblocking mode - we never block here */
+	if (fd == 0) fd++; /* inetd mode? then write to fd 1 */
+	fdprintf(fd, "%s : USERID : UNIX : %s\r\n", buf->buf, bogouser);
+ term:
+	free(buf);
+	retval = 1; /* terminate */
+ ok:
+	if (buf->fd_flag & O_NONBLOCK)
+		fcntl(fd, F_SETFL, buf->fd_flag & ~O_NONBLOCK);
+	return retval;
+}
+
+static int do_timeout(void **paramp UNUSED_PARAM)
+{
+	return 1; /* terminate session */
+}
+
+static void inetd_mode(void)
+{
+	identd_buf_t *buf = xzalloc(sizeof(*buf));
+	/* buf->pos = 0; - xzalloc did it */
+	/* We do NOT want nonblocking I/O here! */
+	/* buf->fd_flag = 0; - xzalloc did it */
+	do
+		alarm(TIMEOUT);
+	while (do_rd(0, (void*)&buf) == 0);
+}
+
+int fakeidentd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fakeidentd_main(int argc UNUSED_PARAM, char **argv)
+{
+	enum {
+		OPT_foreground = 0x1,
+		OPT_inetd      = 0x2,
+		OPT_inetdwait  = 0x4,
+		OPT_fiw        = 0x7,
+		OPT_bindaddr   = 0x8,
+	};
+
+	const char *bind_address = NULL;
+	unsigned opt;
+	int fd;
+
+	opt = getopt32(argv, "fiwb:", &bind_address);
+	strcpy(bogouser, "nobody");
+	if (argv[optind])
+		strncpy(bogouser, argv[optind], sizeof(bogouser));
+
+	/* Daemonize if no -f and no -i and no -w */
+	if (!(opt & OPT_fiw))
+		bb_daemonize_or_rexec(0, argv);
+
+	/* Where to log in inetd modes? "Classic" inetd
+	 * probably has its stderr /dev/null'ed (we need log to syslog?),
+	 * but daemontools-like utilities usually expect that children
+	 * log to stderr. I like daemontools more. Go their way.
+	 * (Or maybe we need yet another option "log to syslog") */
+	if (!(opt & OPT_fiw) /* || (opt & OPT_syslog) */) {
+		openlog(applet_name, LOG_PID, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG;
+	}
+
+	if (opt & OPT_inetd) {
+		inetd_mode();
+		return 0;
+	}
+
+	/* Ignore closed connections when writing */
+	signal(SIGPIPE, SIG_IGN);
+
+	fd = 0;
+	if (!(opt & OPT_inetdwait)) {
+		fd = create_and_bind_stream_or_die(bind_address,
+				bb_lookup_port("identd", "tcp", 113));
+		xlisten(fd, 5);
+	}
+
+	isrv_run(fd, new_peer, do_rd, /*do_wr:*/ NULL, do_timeout,
+			TIMEOUT, (opt & OPT_inetdwait) ? TIMEOUT : 0);
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/libiproute/Kbuild.src b/busybox-1.19.3/networking/libiproute/Kbuild.src
new file mode 100644
index 0000000..7c78f3c
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/Kbuild.src
@@ -0,0 +1,66 @@
+# 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:=
+
+INSERT
+
+lib-$(CONFIG_SLATTACH) += \
+	utils.o
+
+lib-$(CONFIG_IP) += \
+	ip_parse_common_args.o \
+	libnetlink.o \
+	ll_addr.o \
+	ll_map.o \
+	ll_proto.o \
+	ll_types.o \
+	rt_names.o \
+	rtm_map.o \
+	utils.o
+
+lib-$(CONFIG_FEATURE_IP_ADDRESS) += \
+	ip_parse_common_args.o \
+	ipaddress.o \
+	libnetlink.o \
+	ll_addr.o \
+	ll_map.o \
+	ll_types.o \
+	rt_names.o \
+	utils.o
+
+lib-$(CONFIG_FEATURE_IP_LINK) += \
+	ip_parse_common_args.o \
+	ipaddress.o \
+	iplink.o \
+	libnetlink.o \
+	ll_addr.o \
+	ll_map.o \
+	ll_types.o \
+	rt_names.o \
+	utils.o
+
+lib-$(CONFIG_FEATURE_IP_ROUTE) += \
+	ip_parse_common_args.o \
+	iproute.o \
+	libnetlink.o \
+	ll_map.o \
+	rt_names.o \
+	rtm_map.o \
+	utils.o
+
+lib-$(CONFIG_FEATURE_IP_TUNNEL) += \
+	ip_parse_common_args.o \
+	iptunnel.o \
+	rt_names.o \
+	utils.o
+
+lib-$(CONFIG_FEATURE_IP_RULE) += \
+	ip_parse_common_args.o \
+	iprule.o \
+	rt_names.o \
+	utils.o
diff --git a/busybox-1.19.3/networking/libiproute/ip_common.h b/busybox-1.19.3/networking/libiproute/ip_common.h
new file mode 100644
index 0000000..30c7e59
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ip_common.h
@@ -0,0 +1,36 @@
+/* vi: set sw=4 ts=4: */
+#ifndef IP_COMMON_H
+#define IP_COMMON_H 1
+
+#include "libbb.h"
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#if !defined IFA_RTA
+#include <linux/if_addr.h>
+#endif
+#if !defined IFLA_RTA
+#include <linux/if_link.h>
+#endif
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+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);
+
+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
+
+#endif
diff --git a/busybox-1.19.3/networking/libiproute/ip_parse_common_args.c b/busybox-1.19.3/networking/libiproute/ip_parse_common_args.c
new file mode 100644
index 0000000..59c759b
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ip_parse_common_args.c
@@ -0,0 +1,81 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+ */
+
+#include "ip_common.h"  /* #include "libbb.h" is inside */
+#include "utils.h"
+
+family_t preferred_family = AF_UNSPEC;
+smallint oneline;
+char _SL_;
+
+char** FAST_FUNC ip_parse_common_args(char **argv)
+{
+	static const char ip_common_commands[] ALIGN1 =
+		"oneline" "\0"
+		"family" "\0"
+		"4" "\0"
+		"6" "\0"
+		"0" "\0"
+		;
+	enum {
+		ARG_oneline,
+		ARG_family,
+		ARG_IPv4,
+		ARG_IPv6,
+		ARG_packet,
+	};
+	static const family_t af_numbers[] = { AF_INET, AF_INET6, AF_PACKET };
+	int arg;
+
+	while (*argv) {
+		char *opt = *argv;
+
+		if (opt[0] != '-')
+			break;
+		opt++;
+		if (opt[0] == '-') {
+			opt++;
+			if (!opt[0]) { /* "--" */
+				argv++;
+				break;
+			}
+		}
+		arg = index_in_substrings(ip_common_commands, opt);
+		if (arg < 0)
+			bb_show_usage();
+		if (arg == ARG_oneline) {
+			oneline = 1;
+			argv++;
+			continue;
+		}
+		if (arg == ARG_family) {
+			static const char families[] ALIGN1 =
+				"inet" "\0" "inet6" "\0" "link" "\0";
+			argv++;
+			if (!*argv)
+				bb_show_usage();
+			arg = index_in_strings(families, *argv);
+			if (arg < 0)
+				invarg(*argv, "protocol family");
+			/* now arg == 0, 1 or 2 */
+		} else {
+			arg -= ARG_IPv4;
+			/* now arg == 0, 1 or 2 */
+		}
+		preferred_family = af_numbers[arg];
+		argv++;
+	}
+	_SL_ = oneline ? '\\' : '\n';
+	return argv;
+}
diff --git a/busybox-1.19.3/networking/libiproute/ipaddress.c b/busybox-1.19.3/networking/libiproute/ipaddress.c
new file mode 100644
index 0000000..b3748e8
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ipaddress.c
@@ -0,0 +1,766 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Changes:
+ * Laszlo Valko <valko@linux.karinthy.hu> 990223: address label must be zero terminated
+ */
+
+#include <fnmatch.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "ip_common.h"  /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+#ifndef IFF_LOWER_UP
+/* from linux/if.h */
+#define IFF_LOWER_UP  0x10000  /* driver signals L1 up */
+#endif
+
+struct filter_t {
+	char *label;
+	char *flushb;
+	struct rtnl_handle *rth;
+	int scope, scopemask;
+	int flags, flagmask;
+	int flushp;
+	int flushe;
+	int ifindex;
+	family_t family;
+	smallint showqueue;
+	smallint oneline;
+	smallint up;
+	smallint flushed;
+	inet_prefix pfx;
+} FIX_ALIASING;
+typedef struct filter_t filter_t;
+
+#define G_filter (*(filter_t*)&bb_common_bufsiz1)
+
+
+static void print_link_flags(unsigned flags, unsigned mdown)
+{
+	static const int flag_masks[] = {
+		IFF_LOOPBACK, IFF_BROADCAST, IFF_POINTOPOINT,
+		IFF_MULTICAST, IFF_NOARP, IFF_UP, IFF_LOWER_UP };
+	static const char flag_labels[] ALIGN1 =
+		"LOOPBACK\0""BROADCAST\0""POINTOPOINT\0"
+		"MULTICAST\0""NOARP\0""UP\0""LOWER_UP\0";
+
+	bb_putchar('<');
+	if (flags & IFF_UP && !(flags & IFF_RUNNING))
+		printf("NO-CARRIER,");
+	flags &= ~IFF_RUNNING;
+#if 0
+	_PF(ALLMULTI);
+	_PF(PROMISC);
+	_PF(MASTER);
+	_PF(SLAVE);
+	_PF(DEBUG);
+	_PF(DYNAMIC);
+	_PF(AUTOMEDIA);
+	_PF(PORTSEL);
+	_PF(NOTRAILERS);
+#endif
+	flags = print_flags_separated(flag_masks, flag_labels, flags, ",");
+	if (flags)
+		printf("%x", flags);
+	if (mdown)
+		printf(",M-DOWN");
+	printf("> ");
+}
+
+static void print_queuelen(char *name)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(AF_INET, SOCK_STREAM, 0);
+	if (s < 0)
+		return;
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy_IFNAMSIZ(ifr.ifr_name, name);
+	if (ioctl_or_warn(s, SIOCGIFTXQLEN, &ifr) < 0) {
+		close(s);
+		return;
+	}
+	close(s);
+
+	if (ifr.ifr_qlen)
+		printf("qlen %d", ifr.ifr_qlen);
+}
+
+static NOINLINE int print_linkinfo(const struct nlmsghdr *n)
+{
+	struct ifinfomsg *ifi = NLMSG_DATA(n);
+	struct rtattr *tb[IFLA_MAX+1];
+	int len = n->nlmsg_len;
+
+	if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+		return 0;
+
+	len -= NLMSG_LENGTH(sizeof(*ifi));
+	if (len < 0)
+		return -1;
+
+	if (G_filter.ifindex && ifi->ifi_index != G_filter.ifindex)
+		return 0;
+	if (G_filter.up && !(ifi->ifi_flags & IFF_UP))
+		return 0;
+
+	memset(tb, 0, sizeof(tb));
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+	if (tb[IFLA_IFNAME] == NULL) {
+		bb_error_msg("nil ifname");
+		return -1;
+	}
+	if (G_filter.label
+	 && (!G_filter.family || G_filter.family == AF_PACKET)
+	 && fnmatch(G_filter.label, RTA_DATA(tb[IFLA_IFNAME]), 0)
+	) {
+		return 0;
+	}
+
+	if (n->nlmsg_type == RTM_DELLINK)
+		printf("Deleted ");
+
+	printf("%d: %s", ifi->ifi_index,
+		/*tb[IFLA_IFNAME] ? (char*)RTA_DATA(tb[IFLA_IFNAME]) : "<nil>" - we checked tb[IFLA_IFNAME] above*/
+		(char*)RTA_DATA(tb[IFLA_IFNAME])
+	);
+
+	{
+		unsigned m_flag = 0;
+		if (tb[IFLA_LINK]) {
+			SPRINT_BUF(b1);
+			int iflink = *(int*)RTA_DATA(tb[IFLA_LINK]);
+			if (iflink == 0)
+				printf("@NONE: ");
+			else {
+				printf("@%s: ", ll_idx_n2a(iflink, b1));
+				m_flag = ll_index_to_flags(iflink);
+				m_flag = !(m_flag & IFF_UP);
+			}
+		} else {
+			printf(": ");
+		}
+		print_link_flags(ifi->ifi_flags, m_flag);
+	}
+
+	if (tb[IFLA_MTU])
+		printf("mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU]));
+	if (tb[IFLA_QDISC])
+		printf("qdisc %s ", (char*)RTA_DATA(tb[IFLA_QDISC]));
+#ifdef IFLA_MASTER
+	if (tb[IFLA_MASTER]) {
+		SPRINT_BUF(b1);
+		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"
+			"TESTING\0""DORMANT\0""UP\0";
+		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]));
+
+	if (!G_filter.family || G_filter.family == AF_PACKET) {
+		SPRINT_BUF(b1);
+		printf("%c    link/%s ", _SL_, ll_type_n2a(ifi->ifi_type, b1));
+
+		if (tb[IFLA_ADDRESS]) {
+			fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_ADDRESS]),
+						      RTA_PAYLOAD(tb[IFLA_ADDRESS]),
+						      ifi->ifi_type,
+						      b1, sizeof(b1)), stdout);
+		}
+		if (tb[IFLA_BROADCAST]) {
+			if (ifi->ifi_flags & IFF_POINTOPOINT)
+				printf(" peer ");
+			else
+				printf(" brd ");
+			fputs(ll_addr_n2a(RTA_DATA(tb[IFLA_BROADCAST]),
+						      RTA_PAYLOAD(tb[IFLA_BROADCAST]),
+						      ifi->ifi_type,
+						      b1, sizeof(b1)), stdout);
+		}
+	}
+	bb_putchar('\n');
+	/*fflush_all();*/
+	return 0;
+}
+
+static int flush_update(void)
+{
+	if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
+		bb_perror_msg("can't send flush request");
+		return -1;
+	}
+	G_filter.flushp = 0;
+	return 0;
+}
+
+static int FAST_FUNC print_addrinfo(const struct sockaddr_nl *who UNUSED_PARAM,
+		struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+	struct ifaddrmsg *ifa = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr * rta_tb[IFA_MAX+1];
+	char abuf[256];
+	SPRINT_BUF(b1);
+
+	if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
+		return 0;
+	len -= NLMSG_LENGTH(sizeof(*ifa));
+	if (len < 0) {
+		bb_error_msg("wrong nlmsg len %d", len);
+		return -1;
+	}
+
+	if (G_filter.flushb && n->nlmsg_type != RTM_NEWADDR)
+		return 0;
+
+	memset(rta_tb, 0, sizeof(rta_tb));
+	parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
+
+	if (!rta_tb[IFA_LOCAL])
+		rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
+	if (!rta_tb[IFA_ADDRESS])
+		rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
+
+	if (G_filter.ifindex && G_filter.ifindex != ifa->ifa_index)
+		return 0;
+	if ((G_filter.scope ^ ifa->ifa_scope) & G_filter.scopemask)
+		return 0;
+	if ((G_filter.flags ^ ifa->ifa_flags) & G_filter.flagmask)
+		return 0;
+	if (G_filter.label) {
+		const char *label;
+		if (rta_tb[IFA_LABEL])
+			label = RTA_DATA(rta_tb[IFA_LABEL]);
+		else
+			label = ll_idx_n2a(ifa->ifa_index, b1);
+		if (fnmatch(G_filter.label, label, 0) != 0)
+			return 0;
+	}
+	if (G_filter.pfx.family) {
+		if (rta_tb[IFA_LOCAL]) {
+			inet_prefix dst;
+			memset(&dst, 0, sizeof(dst));
+			dst.family = ifa->ifa_family;
+			memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]), RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
+			if (inet_addr_match(&dst, &G_filter.pfx, G_filter.pfx.bitlen))
+				return 0;
+		}
+	}
+
+	if (G_filter.flushb) {
+		struct nlmsghdr *fn;
+		if (NLMSG_ALIGN(G_filter.flushp) + n->nlmsg_len > G_filter.flushe) {
+			if (flush_update())
+				return -1;
+		}
+		fn = (struct nlmsghdr*)(G_filter.flushb + NLMSG_ALIGN(G_filter.flushp));
+		memcpy(fn, n, n->nlmsg_len);
+		fn->nlmsg_type = RTM_DELADDR;
+		fn->nlmsg_flags = NLM_F_REQUEST;
+		fn->nlmsg_seq = ++G_filter.rth->seq;
+		G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
+		G_filter.flushed = 1;
+		return 0;
+	}
+
+	if (n->nlmsg_type == RTM_DELADDR)
+		printf("Deleted ");
+
+	if (G_filter.oneline)
+		printf("%u: %s", ifa->ifa_index, ll_index_to_name(ifa->ifa_index));
+	if (ifa->ifa_family == AF_INET)
+		printf("    inet ");
+	else if (ifa->ifa_family == AF_INET6)
+		printf("    inet6 ");
+	else
+		printf("    family %d ", ifa->ifa_family);
+
+	if (rta_tb[IFA_LOCAL]) {
+		fputs(rt_addr_n2a(ifa->ifa_family,
+					      RTA_DATA(rta_tb[IFA_LOCAL]),
+					      abuf, sizeof(abuf)), stdout);
+
+		if (rta_tb[IFA_ADDRESS] == NULL
+		 || memcmp(RTA_DATA(rta_tb[IFA_ADDRESS]), RTA_DATA(rta_tb[IFA_LOCAL]), 4) == 0
+		) {
+			printf("/%d ", ifa->ifa_prefixlen);
+		} else {
+			printf(" peer %s/%d ",
+				rt_addr_n2a(ifa->ifa_family,
+					    RTA_DATA(rta_tb[IFA_ADDRESS]),
+					    abuf, sizeof(abuf)),
+				ifa->ifa_prefixlen);
+		}
+	}
+
+	if (rta_tb[IFA_BROADCAST]) {
+		printf("brd %s ",
+			rt_addr_n2a(ifa->ifa_family,
+				    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)));
+	}
+	printf("scope %s ", rtnl_rtscope_n2a(ifa->ifa_scope, b1));
+	if (ifa->ifa_flags & IFA_F_SECONDARY) {
+		ifa->ifa_flags &= ~IFA_F_SECONDARY;
+		printf("secondary ");
+	}
+	if (ifa->ifa_flags & IFA_F_TENTATIVE) {
+		ifa->ifa_flags &= ~IFA_F_TENTATIVE;
+		printf("tentative ");
+	}
+	if (ifa->ifa_flags & IFA_F_DEPRECATED) {
+		ifa->ifa_flags &= ~IFA_F_DEPRECATED;
+		printf("deprecated ");
+	}
+	if (!(ifa->ifa_flags & IFA_F_PERMANENT)) {
+		printf("dynamic ");
+	} else
+		ifa->ifa_flags &= ~IFA_F_PERMANENT;
+	if (ifa->ifa_flags)
+		printf("flags %02x ", ifa->ifa_flags);
+	if (rta_tb[IFA_LABEL])
+		fputs((char*)RTA_DATA(rta_tb[IFA_LABEL]), stdout);
+	if (rta_tb[IFA_CACHEINFO]) {
+		struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+		char buf[128];
+		bb_putchar(_SL_);
+		if (ci->ifa_valid == 0xFFFFFFFFU)
+			sprintf(buf, "valid_lft forever");
+		else
+			sprintf(buf, "valid_lft %dsec", ci->ifa_valid);
+		if (ci->ifa_prefered == 0xFFFFFFFFU)
+			sprintf(buf+strlen(buf), " preferred_lft forever");
+		else
+			sprintf(buf+strlen(buf), " preferred_lft %dsec", ci->ifa_prefered);
+		printf("       %s", buf);
+	}
+	bb_putchar('\n');
+	/*fflush_all();*/
+	return 0;
+}
+
+
+struct nlmsg_list {
+	struct nlmsg_list *next;
+	struct nlmsghdr   h;
+};
+
+static int print_selected_addrinfo(int ifindex, struct nlmsg_list *ainfo)
+{
+	for (; ainfo; ainfo = ainfo->next) {
+		struct nlmsghdr *n = &ainfo->h;
+		struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+		if (n->nlmsg_type != RTM_NEWADDR)
+			continue;
+		if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifa)))
+			return -1;
+		if (ifa->ifa_index != ifindex
+		 || (G_filter.family && G_filter.family != ifa->ifa_family)
+		) {
+			continue;
+		}
+		print_addrinfo(NULL, n, NULL);
+	}
+	return 0;
+}
+
+
+static int FAST_FUNC store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
+{
+	struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
+	struct nlmsg_list *h;
+	struct nlmsg_list **lp;
+
+	h = xzalloc(n->nlmsg_len + sizeof(void*));
+
+	memcpy(&h->h, n, n->nlmsg_len);
+	/*h->next = NULL; - xzalloc did it */
+
+	for (lp = linfo; *lp; lp = &(*lp)->next)
+		continue;
+	*lp = h;
+
+	ll_remember_index(who, n, NULL);
+	return 0;
+}
+
+static void ipaddr_reset_filter(int _oneline)
+{
+	memset(&G_filter, 0, sizeof(G_filter));
+	G_filter.oneline = _oneline;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+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";
+
+	struct nlmsg_list *linfo = NULL;
+	struct nlmsg_list *ainfo = NULL;
+	struct nlmsg_list *l;
+	struct rtnl_handle rth;
+	char *filter_dev = NULL;
+	int no_link = 0;
+
+	ipaddr_reset_filter(oneline);
+	G_filter.showqueue = 1;
+
+	if (G_filter.family == AF_UNSPEC)
+		G_filter.family = preferred_family;
+
+	if (flush) {
+		if (!*argv) {
+			bb_error_msg_and_die(bb_msg_requires_arg, "flush");
+		}
+		if (G_filter.family == AF_PACKET) {
+			bb_error_msg_and_die("can't flush link addresses");
+		}
+	}
+
+	while (*argv) {
+		const smalluint key = index_in_strings(option, *argv);
+		if (key == 0) { /* to */
+			NEXT_ARG();
+			get_prefix(&G_filter.pfx, *argv, G_filter.family);
+			if (G_filter.family == AF_UNSPEC) {
+				G_filter.family = G_filter.pfx.family;
+			}
+		} else if (key == 1) { /* scope */
+			uint32_t scope = 0;
+			NEXT_ARG();
+			G_filter.scopemask = -1;
+			if (rtnl_rtscope_a2n(&scope, *argv)) {
+				if (strcmp(*argv, "all") != 0) {
+					invarg(*argv, "scope");
+				}
+				scope = RT_SCOPE_NOWHERE;
+				G_filter.scopemask = 0;
+			}
+			G_filter.scope = scope;
+		} else if (key == 2) { /* up */
+			G_filter.up = 1;
+		} else if (key == 3) { /* label */
+			NEXT_ARG();
+			G_filter.label = *argv;
+		} else {
+			if (key == 4) /* dev */
+				NEXT_ARG();
+			if (filter_dev)
+				duparg2("dev", *argv);
+			filter_dev = *argv;
+		}
+		argv++;
+	}
+
+	xrtnl_open(&rth);
+
+	xrtnl_wilddump_request(&rth, preferred_family, RTM_GETLINK);
+	xrtnl_dump_filter(&rth, store_nlmsg, &linfo);
+
+	if (filter_dev) {
+		G_filter.ifindex = xll_name_to_index(filter_dev);
+	}
+
+	if (flush) {
+		char flushb[4096-512];
+
+		G_filter.flushb = flushb;
+		G_filter.flushp = 0;
+		G_filter.flushe = sizeof(flushb);
+		G_filter.rth = &rth;
+
+		for (;;) {
+			xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETADDR);
+			G_filter.flushed = 0;
+			xrtnl_dump_filter(&rth, print_addrinfo, NULL);
+			if (G_filter.flushed == 0) {
+				return 0;
+			}
+			if (flush_update() < 0) {
+				return 1;
+			}
+		}
+	}
+
+	if (G_filter.family != AF_PACKET) {
+		xrtnl_wilddump_request(&rth, G_filter.family, RTM_GETADDR);
+		xrtnl_dump_filter(&rth, store_nlmsg, &ainfo);
+	}
+
+
+	if (G_filter.family && G_filter.family != AF_PACKET) {
+		struct nlmsg_list **lp;
+		lp = &linfo;
+
+		if (G_filter.oneline)
+			no_link = 1;
+
+		while ((l = *lp) != NULL) {
+			int ok = 0;
+			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+			struct nlmsg_list *a;
+
+			for (a = ainfo; a; a = a->next) {
+				struct nlmsghdr *n = &a->h;
+				struct ifaddrmsg *ifa = NLMSG_DATA(n);
+
+				if (ifa->ifa_index != ifi->ifi_index
+				 || (G_filter.family && G_filter.family != ifa->ifa_family)
+				) {
+					continue;
+				}
+				if ((G_filter.scope ^ ifa->ifa_scope) & G_filter.scopemask)
+					continue;
+				if ((G_filter.flags ^ ifa->ifa_flags) & G_filter.flagmask)
+					continue;
+				if (G_filter.pfx.family || G_filter.label) {
+					struct rtattr *tb[IFA_MAX+1];
+					memset(tb, 0, sizeof(tb));
+					parse_rtattr(tb, IFA_MAX, IFA_RTA(ifa), IFA_PAYLOAD(n));
+					if (!tb[IFA_LOCAL])
+						tb[IFA_LOCAL] = tb[IFA_ADDRESS];
+
+					if (G_filter.pfx.family && tb[IFA_LOCAL]) {
+						inet_prefix dst;
+						memset(&dst, 0, sizeof(dst));
+						dst.family = ifa->ifa_family;
+						memcpy(&dst.data, RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_LOCAL]));
+						if (inet_addr_match(&dst, &G_filter.pfx, G_filter.pfx.bitlen))
+							continue;
+					}
+					if (G_filter.label) {
+						SPRINT_BUF(b1);
+						const char *label;
+						if (tb[IFA_LABEL])
+							label = RTA_DATA(tb[IFA_LABEL]);
+						else
+							label = ll_idx_n2a(ifa->ifa_index, b1);
+						if (fnmatch(G_filter.label, label, 0) != 0)
+							continue;
+					}
+				}
+
+				ok = 1;
+				break;
+			}
+			if (!ok)
+				*lp = l->next;
+			else
+				lp = &l->next;
+		}
+	}
+
+	for (l = linfo; l; l = l->next) {
+		if (no_link || print_linkinfo(&l->h) == 0) {
+			struct ifinfomsg *ifi = NLMSG_DATA(&l->h);
+			if (G_filter.family != AF_PACKET)
+				print_selected_addrinfo(ifi->ifi_index, ainfo);
+		}
+	}
+
+	return 0;
+}
+
+static int default_scope(inet_prefix *lcl)
+{
+	if (lcl->family == AF_INET) {
+		if (lcl->bytelen >= 1 && *(uint8_t*)&lcl->data == 127)
+			return RT_SCOPE_HOST;
+	}
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int ipaddr_modify(int cmd, char **argv)
+{
+	static const char option[] ALIGN1 =
+		"peer\0""remote\0""broadcast\0""brd\0"
+		"anycast\0""scope\0""dev\0""label\0""local\0";
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr  n;
+		struct ifaddrmsg ifa;
+		char             buf[256];
+	} req;
+	char *d = NULL;
+	char *l = NULL;
+	inet_prefix lcl;
+	inet_prefix peer;
+	int local_len = 0;
+	int peer_len = 0;
+	int brd_len = 0;
+	int any_len = 0;
+	bool scoped = 0;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = cmd;
+	req.ifa.ifa_family = preferred_family;
+
+	while (*argv) {
+		const smalluint arg = index_in_strings(option, *argv);
+		if (arg <= 1) { /* peer, remote */
+			NEXT_ARG();
+
+			if (peer_len) {
+				duparg("peer", *argv);
+			}
+			get_prefix(&peer, *argv, req.ifa.ifa_family);
+			peer_len = peer.bytelen;
+			if (req.ifa.ifa_family == AF_UNSPEC) {
+				req.ifa.ifa_family = peer.family;
+			}
+			addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &peer.data, peer.bytelen);
+			req.ifa.ifa_prefixlen = peer.bitlen;
+		} else if (arg <= 3) { /* broadcast, brd */
+			inet_prefix addr;
+			NEXT_ARG();
+			if (brd_len) {
+				duparg("broadcast", *argv);
+			}
+			if (LONE_CHAR(*argv, '+')) {
+				brd_len = -1;
+			} else if (LONE_DASH(*argv)) {
+				brd_len = -2;
+			} else {
+				get_addr(&addr, *argv, req.ifa.ifa_family);
+				if (req.ifa.ifa_family == AF_UNSPEC)
+					req.ifa.ifa_family = addr.family;
+				addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &addr.data, addr.bytelen);
+				brd_len = addr.bytelen;
+			}
+		} else if (arg == 4) { /* anycast */
+			inet_prefix addr;
+			NEXT_ARG();
+			if (any_len) {
+				duparg("anycast", *argv);
+			}
+			get_addr(&addr, *argv, req.ifa.ifa_family);
+			if (req.ifa.ifa_family == AF_UNSPEC) {
+				req.ifa.ifa_family = addr.family;
+			}
+			addattr_l(&req.n, sizeof(req), IFA_ANYCAST, &addr.data, addr.bytelen);
+			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();
+			if (local_len) {
+				duparg2("local", *argv);
+			}
+			get_prefix(&lcl, *argv, req.ifa.ifa_family);
+			if (req.ifa.ifa_family == AF_UNSPEC) {
+				req.ifa.ifa_family = lcl.family;
+			}
+			addattr_l(&req.n, sizeof(req), IFA_LOCAL, &lcl.data, lcl.bytelen);
+			local_len = lcl.bytelen;
+		}
+		argv++;
+	}
+
+	if (!d) {
+		/* There was no "dev IFACE", but we need that */
+		bb_error_msg_and_die("need \"dev IFACE\"");
+	}
+	if (l && strncmp(d, l, strlen(d)) != 0) {
+		bb_error_msg_and_die("\"dev\" (%s) must match \"label\" (%s)", d, l);
+	}
+
+	if (peer_len == 0 && local_len && cmd != RTM_DELADDR) {
+		peer = lcl;
+		addattr_l(&req.n, sizeof(req), IFA_ADDRESS, &lcl.data, lcl.bytelen);
+	}
+	if (req.ifa.ifa_prefixlen == 0)
+		req.ifa.ifa_prefixlen = lcl.bitlen;
+
+	if (brd_len < 0 && cmd != RTM_DELADDR) {
+		inet_prefix brd;
+		int i;
+		if (req.ifa.ifa_family != AF_INET) {
+			bb_error_msg_and_die("broadcast can be set only for IPv4 addresses");
+		}
+		brd = peer;
+		if (brd.bitlen <= 30) {
+			for (i=31; i>=brd.bitlen; i--) {
+				if (brd_len == -1)
+					brd.data[0] |= htonl(1<<(31-i));
+				else
+					brd.data[0] &= ~htonl(1<<(31-i));
+			}
+			addattr_l(&req.n, sizeof(req), IFA_BROADCAST, &brd.data, brd.bytelen);
+			brd_len = brd.bytelen;
+		}
+	}
+	if (!scoped && cmd != RTM_DELADDR)
+		req.ifa.ifa_scope = default_scope(&lcl);
+
+	xrtnl_open(&rth);
+
+	ll_init_map(&rth);
+
+	req.ifa.ifa_index = xll_name_to_index(d);
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+		return 2;
+
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+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;
+	if (*argv) {
+		cmd = index_in_substrings(commands, *argv);
+		if (cmd > 5)
+			bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+		argv++;
+		if (cmd <= 1)
+			return ipaddr_modify((cmd == 0) ? RTM_NEWADDR : RTM_DELADDR, argv);
+	}
+	/* 2 == list, 3 == show, 4 == lst */
+	return ipaddr_list_or_flush(argv, cmd == 5);
+}
diff --git a/busybox-1.19.3/networking/libiproute/iplink.c b/busybox-1.19.3/networking/libiproute/iplink.c
new file mode 100644
index 0000000..bad2017
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/iplink.c
@@ -0,0 +1,384 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include <net/if.h>
+#include <net/if_packet.h>
+#include <netpacket/packet.h>
+#include <netinet/if_ether.h>
+
+#include "ip_common.h"  /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+#ifndef IFLA_LINKINFO
+# define IFLA_LINKINFO 18
+# define IFLA_INFO_KIND 1
+#endif
+
+/* taken from linux/sockios.h */
+#define SIOCSIFNAME  0x8923  /* set interface name */
+
+/* Exits on error */
+static int get_ctl_fd(void)
+{
+	int fd;
+
+	fd = socket(PF_INET, SOCK_DGRAM, 0);
+	if (fd >= 0)
+		return fd;
+	fd = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (fd >= 0)
+		return fd;
+	return xsocket(PF_INET6, SOCK_DGRAM, 0);
+}
+
+/* Exits on error */
+static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
+{
+	struct ifreq ifr;
+	int fd;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+	fd = get_ctl_fd();
+	xioctl(fd, SIOCGIFFLAGS, &ifr);
+	if ((ifr.ifr_flags ^ flags) & mask) {
+		ifr.ifr_flags &= ~mask;
+		ifr.ifr_flags |= mask & flags;
+		xioctl(fd, SIOCSIFFLAGS, &ifr);
+	}
+	close(fd);
+}
+
+/* Exits on error */
+static void do_changename(char *dev, char *newdev)
+{
+	struct ifreq ifr;
+	int fd;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+	strncpy_IFNAMSIZ(ifr.ifr_newname, newdev);
+	fd = get_ctl_fd();
+	xioctl(fd, SIOCSIFNAME, &ifr);
+	close(fd);
+}
+
+/* Exits on error */
+static void set_qlen(char *dev, int qlen)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = get_ctl_fd();
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+	ifr.ifr_qlen = qlen;
+	xioctl(s, SIOCSIFTXQLEN, &ifr);
+	close(s);
+}
+
+/* Exits on error */
+static void set_mtu(char *dev, int mtu)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = get_ctl_fd();
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+	ifr.ifr_mtu = mtu;
+	xioctl(s, SIOCSIFMTU, &ifr);
+	close(s);
+}
+
+/* Exits on error */
+static int get_address(char *dev, int *htype)
+{
+	struct ifreq ifr;
+	struct sockaddr_ll me;
+	socklen_t alen;
+	int s;
+
+	s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
+
+	memset(&ifr, 0, sizeof(ifr));
+	strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+	xioctl(s, SIOCGIFINDEX, &ifr);
+
+	memset(&me, 0, sizeof(me));
+	me.sll_family = AF_PACKET;
+	me.sll_ifindex = ifr.ifr_ifindex;
+	me.sll_protocol = htons(ETH_P_LOOP);
+	xbind(s, (struct sockaddr*)&me, sizeof(me));
+	alen = sizeof(me);
+	getsockname(s, (struct sockaddr*)&me, &alen);
+	//never happens:
+	//if (getsockname(s, (struct sockaddr*)&me, &alen) == -1)
+	//	bb_perror_msg_and_die("getsockname");
+	close(s);
+	*htype = me.sll_hatype;
+	return me.sll_halen;
+}
+
+/* Exits on error */
+static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
+{
+	int alen;
+
+	memset(ifr, 0, sizeof(*ifr));
+	strncpy_IFNAMSIZ(ifr->ifr_name, dev);
+	ifr->ifr_hwaddr.sa_family = hatype;
+
+	alen = hatype == 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/;
+	alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), alen, lla);
+	if (alen < 0)
+		exit(EXIT_FAILURE);
+	if (alen != halen) {
+		bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
+	}
+}
+
+/* Exits on error */
+static void set_address(struct ifreq *ifr, int brd)
+{
+	int s;
+
+	s = get_ctl_fd();
+	if (brd)
+		xioctl(s, SIOCSIFHWBROADCAST, ifr);
+	else
+		xioctl(s, SIOCSIFHWADDR, ifr);
+	close(s);
+}
+
+
+static void die_must_be_on_off(const char *msg) NORETURN;
+static void die_must_be_on_off(const char *msg)
+{
+	bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg);
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_set(char **argv)
+{
+	char *dev = NULL;
+	uint32_t mask = 0;
+	uint32_t flags = 0;
+	int qlen = -1;
+	int mtu = -1;
+	char *newaddr = NULL;
+	char *newbrd = NULL;
+	struct ifreq ifr0, ifr1;
+	char *newname = NULL;
+	int htype, halen;
+	static const char keywords[] ALIGN1 =
+		"up\0""down\0""name\0""mtu\0""qlen\0""multicast\0"
+		"arp\0""address\0""dev\0";
+	enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast,
+		ARG_arp, ARG_addr, ARG_dev };
+	static const char str_on_off[] ALIGN1 = "on\0""off\0";
+	enum { PARM_on = 0, PARM_off };
+	smalluint key;
+
+	while (*argv) {
+		/* substring search ensures that e.g. "addr" and "address"
+		 * are both accepted */
+		key = index_in_substrings(keywords, *argv);
+		if (key == ARG_up) {
+			mask |= IFF_UP;
+			flags |= IFF_UP;
+		} else if (key == ARG_down) {
+			mask |= IFF_UP;
+			flags &= ~IFF_UP;
+		} else if (key == ARG_name) {
+			NEXT_ARG();
+			newname = *argv;
+		} else if (key == ARG_mtu) {
+			NEXT_ARG();
+			if (mtu != -1)
+				duparg("mtu", *argv);
+			mtu = get_unsigned(*argv, "mtu");
+		} else if (key == ARG_qlen) {
+			NEXT_ARG();
+			if (qlen != -1)
+				duparg("qlen", *argv);
+			qlen = get_unsigned(*argv, "qlen");
+		} else if (key == ARG_addr) {
+			NEXT_ARG();
+			newaddr = *argv;
+		} else if (key >= ARG_dev) {
+			if (key == ARG_dev) {
+				NEXT_ARG();
+			}
+			if (dev)
+				duparg2("dev", *argv);
+			dev = *argv;
+		} else {
+			int param;
+			NEXT_ARG();
+			param = index_in_strings(str_on_off, *argv);
+			if (key == ARG_multicast) {
+				if (param < 0)
+					die_must_be_on_off("multicast");
+				mask |= IFF_MULTICAST;
+				if (param == PARM_on)
+					flags |= IFF_MULTICAST;
+				else
+					flags &= ~IFF_MULTICAST;
+			} else if (key == ARG_arp) {
+				if (param < 0)
+					die_must_be_on_off("arp");
+				mask |= IFF_NOARP;
+				if (param == PARM_on)
+					flags &= ~IFF_NOARP;
+				else
+					flags |= IFF_NOARP;
+			}
+		}
+		argv++;
+	}
+
+	if (!dev) {
+		bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
+	}
+
+	if (newaddr || newbrd) {
+		halen = get_address(dev, &htype);
+		if (newaddr) {
+			parse_address(dev, htype, halen, newaddr, &ifr0);
+			set_address(&ifr0, 0);
+		}
+		if (newbrd) {
+			parse_address(dev, htype, halen, newbrd, &ifr1);
+			set_address(&ifr1, 1);
+		}
+	}
+
+	if (newname && strcmp(dev, newname)) {
+		do_changename(dev, newname);
+		dev = newname;
+	}
+	if (qlen != -1) {
+		set_qlen(dev, qlen);
+	}
+	if (mtu != -1) {
+		set_mtu(dev, mtu);
+	}
+	if (mask)
+		do_chflags(dev, flags, mask);
+	return 0;
+}
+
+static int ipaddr_list_link(char **argv)
+{
+	preferred_family = AF_PACKET;
+	return ipaddr_list_or_flush(argv, 0);
+}
+
+#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 const char keywords[] ALIGN1 =
+		"link\0""name\0""type\0""dev\0";
+	enum {
+		ARG_link,
+		ARG_name,
+		ARG_type,
+		ARG_dev,
+	};
+	struct rtnl_handle rth;
+	struct {
+		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;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.n.nlmsg_type = rtm;
+	req.i.ifi_family = preferred_family;
+	if (rtm == RTM_NEWLINK)
+		req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+
+	while (*argv) {
+		arg = index_in_substrings(keywords, *argv);
+		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)
+					duparg(*argv, "dev");
+				NEXT_ARG();
+			}
+			dev_str = *argv;
+		}
+		argv++;
+	}
+	xrtnl_open(&rth);
+	ll_init_map(&rth);
+	if (type_str) {
+		struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
+
+		addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
+		addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
+				strlen(type_str));
+		linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
+	}
+	if (rtm != RTM_NEWLINK) {
+		if (!dev_str)
+			return 1; /* Need a device to delete */
+		req.i.ifi_index = xll_name_to_index(dev_str);
+	} else {
+		if (!name_str)
+			name_str = dev_str;
+		if (link_str) {
+			int idx = xll_name_to_index(link_str);
+			addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
+		}
+	}
+	if (name_str) {
+		const size_t name_len = strlen(name_str) + 1;
+		if (name_len < 2 || name_len > IFNAMSIZ)
+			invarg(name_str, "name");
+		addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
+	}
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+		return 2;
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+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);
+		argv++;
+		if (key <= 1) /* add/delete */
+			return do_change(argv, key ? RTM_DELLINK : RTM_NEWLINK);
+		else if (key == 2) /* set */
+			return do_set(argv);
+	}
+	/* show, lst, list */
+	return ipaddr_list_link(argv);
+}
diff --git a/busybox-1.19.3/networking/libiproute/iproute.c b/busybox-1.19.3/networking/libiproute/iproute.c
new file mode 100644
index 0000000..f8a67d9
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/iproute.c
@@ -0,0 +1,946 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+ * Kunihiro Ishiguro <kunihiro@zebra.org> 001102: rtnh_ifindex was not initialized
+ */
+
+#include "ip_common.h"  /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+#ifndef RTAX_RTTVAR
+#define RTAX_RTTVAR RTAX_HOPS
+#endif
+
+
+struct filter_t {
+	int tb;
+	smallint flushed;
+	char *flushb;
+	int flushp;
+	int flushe;
+	struct rtnl_handle *rth;
+	//int protocol, protocolmask; - write-only fields?!
+	//int scope, scopemask; - unused
+	//int type; - read-only
+	//int typemask; - unused
+	//int tos, tosmask; - unused
+	int iif;
+	int oif;
+	//int realm, realmmask; - unused
+	//inet_prefix rprefsrc; - read-only
+	inet_prefix rvia;
+	inet_prefix rdst;
+	inet_prefix mdst;
+	inet_prefix rsrc;
+	inet_prefix msrc;
+} FIX_ALIASING;
+typedef struct filter_t filter_t;
+
+#define G_filter (*(filter_t*)&bb_common_bufsiz1)
+
+static int flush_update(void)
+{
+	if (rtnl_send(G_filter.rth, G_filter.flushb, G_filter.flushp) < 0) {
+		bb_perror_msg("can't send flush request");
+		return -1;
+	}
+	G_filter.flushp = 0;
+	return 0;
+}
+
+static unsigned get_hz(void)
+{
+	static unsigned hz_internal;
+	FILE *fp;
+
+	if (hz_internal)
+		return hz_internal;
+
+	fp = fopen_for_read("/proc/net/psched");
+	if (fp) {
+		unsigned nom, denom;
+
+		if (fscanf(fp, "%*08x%*08x%08x%08x", &nom, &denom) == 2)
+			if (nom == 1000000)
+				hz_internal = denom;
+		fclose(fp);
+	}
+	if (!hz_internal)
+		hz_internal = sysconf(_SC_CLK_TCK);
+	return hz_internal;
+}
+
+static int FAST_FUNC print_route(const struct sockaddr_nl *who UNUSED_PARAM,
+		struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+	struct rtmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	struct rtattr *tb[RTA_MAX+1];
+	char abuf[256];
+	inet_prefix dst;
+	inet_prefix src;
+	int host_len = -1;
+	SPRINT_BUF(b1);
+
+	if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) {
+		fprintf(stderr, "Not a route: %08x %08x %08x\n",
+			n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags);
+		return 0;
+	}
+	if (G_filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
+		return 0;
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0)
+		bb_error_msg_and_die("wrong nlmsg len %d", len);
+
+	if (r->rtm_family == AF_INET6)
+		host_len = 128;
+	else if (r->rtm_family == AF_INET)
+		host_len = 32;
+
+	if (r->rtm_family == AF_INET6) {
+		if (G_filter.tb) {
+			if (G_filter.tb < 0) {
+				if (!(r->rtm_flags & RTM_F_CLONED)) {
+					return 0;
+				}
+			} else {
+				if (r->rtm_flags & RTM_F_CLONED) {
+					return 0;
+				}
+				if (G_filter.tb == RT_TABLE_LOCAL) {
+					if (r->rtm_type != RTN_LOCAL) {
+						return 0;
+					}
+				} else if (G_filter.tb == RT_TABLE_MAIN) {
+					if (r->rtm_type == RTN_LOCAL) {
+						return 0;
+					}
+				} else {
+					return 0;
+				}
+			}
+		}
+	} else {
+		if (G_filter.tb > 0 && G_filter.tb != r->rtm_table) {
+			return 0;
+		}
+	}
+	if (G_filter.rdst.family
+	 && (r->rtm_family != G_filter.rdst.family || G_filter.rdst.bitlen > r->rtm_dst_len)
+	) {
+		return 0;
+	}
+	if (G_filter.mdst.family
+	 && (r->rtm_family != G_filter.mdst.family
+	    || (G_filter.mdst.bitlen >= 0 && G_filter.mdst.bitlen < r->rtm_dst_len)
+	    )
+	) {
+		return 0;
+	}
+	if (G_filter.rsrc.family
+	 && (r->rtm_family != G_filter.rsrc.family || G_filter.rsrc.bitlen > r->rtm_src_len)
+	) {
+		return 0;
+	}
+	if (G_filter.msrc.family
+	 && (r->rtm_family != G_filter.msrc.family
+	    || (G_filter.msrc.bitlen >= 0 && G_filter.msrc.bitlen < r->rtm_src_len)
+	    )
+	) {
+		return 0;
+	}
+
+	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)
+	) {
+		return 0;
+	}
+	if (G_filter.mdst.family
+	 && G_filter.mdst.bitlen >= 0
+	 && inet_addr_match(&dst, &G_filter.mdst, r->rtm_dst_len)
+	) {
+		return 0;
+	}
+	if (G_filter.rsrc.family
+	 && inet_addr_match(&src, &G_filter.rsrc, G_filter.rsrc.bitlen)
+	) {
+		return 0;
+	}
+	if (G_filter.msrc.family && G_filter.msrc.bitlen >= 0
+	 && inet_addr_match(&src, &G_filter.msrc, r->rtm_src_len)
+	) {
+		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 = (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;
+		fn->nlmsg_seq = ++G_filter.rth->seq;
+		G_filter.flushp = (((char*)fn) + n->nlmsg_len) - G_filter.flushb;
+		G_filter.flushed = 1;
+		return 0;
+	}
+
+	/* We are printing routes */
+
+	if (n->nlmsg_type == RTM_DELROUTE) {
+		printf("Deleted ");
+	}
+	if (r->rtm_type != RTN_UNICAST /* && !G_filter.type - always 0 */) {
+		printf("%s ", rtnl_rtntype_n2a(r->rtm_type, b1));
+	}
+
+	if (tb[RTA_DST]) {
+		if (r->rtm_dst_len != host_len) {
+			printf("%s/%u ", rt_addr_n2a(r->rtm_family,
+						RTA_DATA(tb[RTA_DST]),
+						abuf, sizeof(abuf)),
+					r->rtm_dst_len
+					);
+		} else {
+			printf("%s ", format_host(r->rtm_family,
+						RTA_PAYLOAD(tb[RTA_DST]),
+						RTA_DATA(tb[RTA_DST]),
+						abuf, sizeof(abuf))
+					);
+		}
+	} else if (r->rtm_dst_len) {
+		printf("0/%d ", r->rtm_dst_len);
+	} else {
+		printf("default ");
+	}
+	if (tb[RTA_SRC]) {
+		if (r->rtm_src_len != host_len) {
+			printf("from %s/%u ", rt_addr_n2a(r->rtm_family,
+						RTA_DATA(tb[RTA_SRC]),
+						abuf, sizeof(abuf)),
+					r->rtm_src_len
+					);
+		} else {
+			printf("from %s ", format_host(r->rtm_family,
+						RTA_PAYLOAD(tb[RTA_SRC]),
+						RTA_DATA(tb[RTA_SRC]),
+						abuf, sizeof(abuf))
+					);
+		}
+	} else if (r->rtm_src_len) {
+		printf("from 0/%u ", r->rtm_src_len);
+	}
+	if (tb[RTA_GATEWAY] && G_filter.rvia.bitlen != host_len) {
+		printf("via %s ", format_host(r->rtm_family,
+					RTA_PAYLOAD(tb[RTA_GATEWAY]),
+					RTA_DATA(tb[RTA_GATEWAY]),
+					abuf, sizeof(abuf)));
+	}
+	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.
+		 */
+		printf(" src %s ", rt_addr_n2a(r->rtm_family,
+					RTA_DATA(tb[RTA_PREFSRC]),
+					abuf, sizeof(abuf)));
+	}
+	if (tb[RTA_PRIORITY]) {
+		printf(" metric %d ", *(uint32_t*)RTA_DATA(tb[RTA_PRIORITY]));
+	}
+	if (r->rtm_family == AF_INET6) {
+		struct rta_cacheinfo *ci = NULL;
+		if (tb[RTA_CACHEINFO]) {
+			ci = RTA_DATA(tb[RTA_CACHEINFO]);
+		}
+		if ((r->rtm_flags & RTM_F_CLONED) || (ci && ci->rta_expires)) {
+			if (r->rtm_flags & RTM_F_CLONED) {
+				printf("%c    cache ", _SL_);
+			}
+			if (ci->rta_expires) {
+				printf(" expires %dsec", ci->rta_expires / get_hz());
+			}
+			if (ci->rta_error != 0) {
+				printf(" error %d", ci->rta_error);
+			}
+		} else if (ci) {
+			if (ci->rta_error != 0)
+				printf(" error %d", ci->rta_error);
+		}
+	}
+	if (tb[RTA_IIF] && G_filter.iif == 0) {
+		printf(" iif %s", ll_index_to_name(*(int*)RTA_DATA(tb[RTA_IIF])));
+	}
+	bb_putchar('\n');
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_modify(int cmd, unsigned flags, char **argv)
+{
+	static const char keywords[] ALIGN1 =
+		"src\0""via\0""mtu\0""lock\0""protocol\0"IF_FEATURE_IP_RULE("table\0")
+		"dev\0""oif\0""to\0""metric\0";
+	enum {
+		ARG_src,
+		ARG_via,
+		ARG_mtu, PARM_lock,
+		ARG_protocol,
+IF_FEATURE_IP_RULE(ARG_table,)
+		ARG_dev,
+		ARG_oif,
+		ARG_to,
+		ARG_metric,
+	};
+	enum {
+		gw_ok = 1 << 0,
+		dst_ok = 1 << 1,
+		proto_ok = 1 << 2,
+		type_ok = 1 << 3
+	};
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr n;
+		struct rtmsg    r;
+		char            buf[1024];
+	} req;
+	char mxbuf[256];
+	struct rtattr * mxrta = (void*)mxbuf;
+	unsigned mxlock = 0;
+	char *d = NULL;
+	smalluint ok = 0;
+	int arg;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST | flags;
+	req.n.nlmsg_type = cmd;
+	req.r.rtm_family = preferred_family;
+	if (RT_TABLE_MAIN) /* if it is zero, memset already did it */
+		req.r.rtm_table = RT_TABLE_MAIN;
+	if (RT_SCOPE_NOWHERE)
+		req.r.rtm_scope = RT_SCOPE_NOWHERE;
+
+	if (cmd != RTM_DELROUTE) {
+		req.r.rtm_protocol = RTPROT_BOOT;
+		req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+		req.r.rtm_type = RTN_UNICAST;
+	}
+
+	mxrta->rta_type = RTA_METRICS;
+	mxrta->rta_len = RTA_LENGTH(0);
+
+	while (*argv) {
+		arg = index_in_substrings(keywords, *argv);
+		if (arg == ARG_src) {
+			inet_prefix addr;
+			NEXT_ARG();
+			get_addr(&addr, *argv, req.r.rtm_family);
+			if (req.r.rtm_family == AF_UNSPEC)
+				req.r.rtm_family = addr.family;
+			addattr_l(&req.n, sizeof(req), RTA_PREFSRC, &addr.data, addr.bytelen);
+		} else if (arg == ARG_via) {
+			inet_prefix addr;
+			ok |= gw_ok;
+			NEXT_ARG();
+			get_addr(&addr, *argv, req.r.rtm_family);
+			if (req.r.rtm_family == AF_UNSPEC) {
+				req.r.rtm_family = addr.family;
+			}
+			addattr_l(&req.n, sizeof(req), RTA_GATEWAY, &addr.data, addr.bytelen);
+		} else if (arg == ARG_mtu) {
+			unsigned mtu;
+			NEXT_ARG();
+			if (index_in_strings(keywords, *argv) == PARM_lock) {
+				mxlock |= (1 << RTAX_MTU);
+				NEXT_ARG();
+			}
+			mtu = get_unsigned(*argv, "mtu");
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_MTU, mtu);
+		} else if (arg == ARG_protocol) {
+			uint32_t prot;
+			NEXT_ARG();
+			if (rtnl_rtprot_a2n(&prot, *argv))
+				invarg(*argv, "protocol");
+			req.r.rtm_protocol = prot;
+			ok |= proto_ok;
+#if ENABLE_FEATURE_IP_RULE
+		} else if (arg == ARG_table) {
+			uint32_t tid;
+			NEXT_ARG();
+			if (rtnl_rttable_a2n(&tid, *argv))
+				invarg(*argv, "table");
+			req.r.rtm_table = tid;
+#endif
+		} else if (arg == ARG_dev || arg == ARG_oif) {
+			NEXT_ARG();
+			d = *argv;
+		} else if (arg == ARG_metric) {
+			uint32_t metric;
+			NEXT_ARG();
+			metric = get_u32(*argv, "metric");
+			addattr32(&req.n, sizeof(req), RTA_PRIORITY, metric);
+		} else {
+			int type;
+			inet_prefix dst;
+
+			if (arg == ARG_to) {
+				NEXT_ARG();
+			}
+			if ((**argv < '0' || **argv > '9')
+			 && rtnl_rtntype_a2n(&type, *argv) == 0
+			) {
+				NEXT_ARG();
+				req.r.rtm_type = type;
+				ok |= type_ok;
+			}
+
+			if (ok & dst_ok) {
+				duparg2("to", *argv);
+			}
+			get_prefix(&dst, *argv, req.r.rtm_family);
+			if (req.r.rtm_family == AF_UNSPEC) {
+				req.r.rtm_family = dst.family;
+			}
+			req.r.rtm_dst_len = dst.bitlen;
+			ok |= dst_ok;
+			if (dst.bytelen) {
+				addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+			}
+		}
+		argv++;
+	}
+
+	xrtnl_open(&rth);
+
+	if (d)  {
+		int idx;
+
+		ll_init_map(&rth);
+
+		if (d) {
+			idx = xll_name_to_index(d);
+			addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+		}
+	}
+
+	if (mxrta->rta_len > RTA_LENGTH(0)) {
+		if (mxlock) {
+			rta_addattr32(mxrta, sizeof(mxbuf), RTAX_LOCK, mxlock);
+		}
+		addattr_l(&req.n, sizeof(req), RTA_METRICS, RTA_DATA(mxrta), RTA_PAYLOAD(mxrta));
+	}
+
+	if (req.r.rtm_type == RTN_LOCAL || req.r.rtm_type == RTN_NAT)
+		req.r.rtm_scope = RT_SCOPE_HOST;
+	else
+	if (req.r.rtm_type == RTN_BROADCAST
+	 || req.r.rtm_type == RTN_MULTICAST
+	 || req.r.rtm_type == RTN_ANYCAST
+	) {
+		req.r.rtm_scope = RT_SCOPE_LINK;
+	}
+	else if (req.r.rtm_type == RTN_UNICAST || req.r.rtm_type == RTN_UNSPEC) {
+		if (cmd == RTM_DELROUTE)
+			req.r.rtm_scope = RT_SCOPE_NOWHERE;
+		else if (!(ok & gw_ok))
+			req.r.rtm_scope = RT_SCOPE_LINK;
+	}
+
+	if (req.r.rtm_family == AF_UNSPEC) {
+		req.r.rtm_family = AF_INET;
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) {
+		return 2;
+	}
+
+	return 0;
+}
+
+static int rtnl_rtcache_request(struct rtnl_handle *rth, int family)
+{
+	struct {
+		struct nlmsghdr nlh;
+		struct rtmsg rtm;
+	} req;
+	struct sockaddr_nl nladdr;
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	memset(&req, 0, sizeof(req));
+	nladdr.nl_family = AF_NETLINK;
+
+	req.nlh.nlmsg_len = sizeof(req);
+	if (RTM_GETROUTE)
+		req.nlh.nlmsg_type = RTM_GETROUTE;
+	if (NLM_F_ROOT | NLM_F_REQUEST)
+		req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST;
+	/*req.nlh.nlmsg_pid = 0; - memset did it already */
+	req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+	req.rtm.rtm_family = family;
+	if (RTM_F_CLONED)
+		req.rtm.rtm_flags = RTM_F_CLONED;
+
+	return xsendto(rth->fd, (void*)&req, sizeof(req), (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+static void iproute_flush_cache(void)
+{
+	static const char fn[] ALIGN1 = "/proc/sys/net/ipv4/route/flush";
+	int flush_fd = open_or_warn(fn, O_WRONLY);
+
+	if (flush_fd < 0) {
+		return;
+	}
+
+	if (write(flush_fd, "-1", 2) < 2) {
+		bb_perror_msg("can't flush routing cache");
+		return;
+	}
+	close(flush_fd);
+}
+
+static void iproute_reset_filter(void)
+{
+	memset(&G_filter, 0, sizeof(G_filter));
+	G_filter.mdst.bitlen = -1;
+	G_filter.msrc.bitlen = -1;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_list_or_flush(char **argv, int flush)
+{
+	int do_ipv6 = preferred_family;
+	struct rtnl_handle rth;
+	char *id = NULL;
+	char *od = NULL;
+	static const char keywords[] ALIGN1 =
+		/* "ip route list/flush" parameters: */
+		"protocol\0" "dev\0"   "oif\0"   "iif\0"
+		"via\0"      "table\0" "cache\0"
+		"from\0"     "to\0"
+		/* and possible further keywords */
+		"all\0"
+		"root\0"
+		"match\0"
+		"exact\0"
+		"main\0"
+		;
+	enum {
+		KW_proto, KW_dev,   KW_oif,  KW_iif,
+		KW_via,   KW_table, KW_cache,
+		KW_from,  KW_to,
+		/* */
+		KW_all,
+		KW_root,
+		KW_match,
+		KW_exact,
+		KW_main,
+	};
+	int arg, parm;
+
+	iproute_reset_filter();
+	G_filter.tb = RT_TABLE_MAIN;
+
+	if (flush && !*argv)
+		bb_error_msg_and_die(bb_msg_requires_arg, "\"ip route flush\"");
+
+	while (*argv) {
+		arg = index_in_substrings(keywords, *argv);
+		if (arg == KW_proto) {
+			uint32_t prot = 0;
+			NEXT_ARG();
+			//G_filter.protocolmask = -1;
+			if (rtnl_rtprot_a2n(&prot, *argv)) {
+				if (index_in_strings(keywords, *argv) != KW_all)
+					invarg(*argv, "protocol");
+				prot = 0;
+				//G_filter.protocolmask = 0;
+			}
+			//G_filter.protocol = prot;
+		} else if (arg == KW_dev || arg == KW_oif) {
+			NEXT_ARG();
+			od = *argv;
+		} else if (arg == KW_iif) {
+			NEXT_ARG();
+			id = *argv;
+		} else if (arg == KW_via) {
+			NEXT_ARG();
+			get_prefix(&G_filter.rvia, *argv, do_ipv6);
+		} else if (arg == KW_table) { /* table all/cache/main */
+			NEXT_ARG();
+			parm = index_in_substrings(keywords, *argv);
+			if (parm == KW_cache)
+				G_filter.tb = -1;
+			else if (parm == KW_all)
+				G_filter.tb = 0;
+			else if (parm != KW_main) {
+#if ENABLE_FEATURE_IP_RULE
+				uint32_t tid;
+				if (rtnl_rttable_a2n(&tid, *argv))
+					invarg(*argv, "table");
+				G_filter.tb = tid;
+#else
+				invarg(*argv, "table");
+#endif
+			}
+		} else if (arg == KW_cache) {
+			/* The command 'ip route flush cache' is used by OpenSWAN.
+			 * Assuming it's a synonym for 'ip route flush table cache' */
+			G_filter.tb = -1;
+		} else if (arg == KW_from) {
+			NEXT_ARG();
+			parm = index_in_substrings(keywords, *argv);
+			if (parm == KW_root) {
+				NEXT_ARG();
+				get_prefix(&G_filter.rsrc, *argv, do_ipv6);
+			} else if (parm == KW_match) {
+				NEXT_ARG();
+				get_prefix(&G_filter.msrc, *argv, do_ipv6);
+			} else {
+				if (parm == KW_exact)
+					NEXT_ARG();
+				get_prefix(&G_filter.msrc, *argv, do_ipv6);
+				G_filter.rsrc = G_filter.msrc;
+			}
+		} else { /* "to" is the default parameter */
+			if (arg == KW_to) {
+				NEXT_ARG();
+				arg = index_in_substrings(keywords, *argv);
+			}
+			/* parm = arg; - would be more plausible, but we reuse 'arg' here */
+			if (arg == KW_root) {
+				NEXT_ARG();
+				get_prefix(&G_filter.rdst, *argv, do_ipv6);
+			} else if (arg == KW_match) {
+				NEXT_ARG();
+				get_prefix(&G_filter.mdst, *argv, do_ipv6);
+			} else { /* "to exact" is the default */
+				if (arg == KW_exact)
+					NEXT_ARG();
+				get_prefix(&G_filter.mdst, *argv, do_ipv6);
+				G_filter.rdst = G_filter.mdst;
+			}
+		}
+		argv++;
+	}
+
+	if (do_ipv6 == AF_UNSPEC && G_filter.tb) {
+		do_ipv6 = AF_INET;
+	}
+
+	xrtnl_open(&rth);
+	ll_init_map(&rth);
+
+	if (id || od)  {
+		int idx;
+
+		if (id) {
+			idx = xll_name_to_index(id);
+			G_filter.iif = idx;
+		}
+		if (od) {
+			idx = xll_name_to_index(od);
+			G_filter.oif = idx;
+		}
+	}
+
+	if (flush) {
+		char flushb[4096-512];
+
+		if (G_filter.tb == -1) { /* "flush table cache" */
+			if (do_ipv6 != AF_INET6)
+				iproute_flush_cache();
+			if (do_ipv6 == AF_INET)
+				return 0;
+		}
+
+		G_filter.flushb = flushb;
+		G_filter.flushp = 0;
+		G_filter.flushe = sizeof(flushb);
+		G_filter.rth = &rth;
+
+		for (;;) {
+			xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
+			G_filter.flushed = 0;
+			xrtnl_dump_filter(&rth, print_route, NULL);
+			if (G_filter.flushed == 0)
+				return 0;
+			if (flush_update())
+				return 1;
+		}
+	}
+
+	if (G_filter.tb != -1) {
+		xrtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE);
+	} else if (rtnl_rtcache_request(&rth, do_ipv6) < 0) {
+		bb_perror_msg_and_die("can't send dump request");
+	}
+	xrtnl_dump_filter(&rth, print_route, NULL);
+
+	return 0;
+}
+
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iproute_get(char **argv)
+{
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr n;
+		struct rtmsg    r;
+		char            buf[1024];
+	} req;
+	char *idev = NULL;
+	char *odev = NULL;
+	bool connected = 0;
+	bool from_ok = 0;
+	static const char options[] ALIGN1 =
+		"from\0""iif\0""oif\0""dev\0""notify\0""connected\0""to\0";
+
+	memset(&req, 0, sizeof(req));
+
+	iproute_reset_filter();
+
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	if (NLM_F_REQUEST)
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+	if (RTM_GETROUTE)
+		req.n.nlmsg_type = RTM_GETROUTE;
+	req.r.rtm_family = preferred_family;
+	/*req.r.rtm_table = 0; - memset did this already */
+	/*req.r.rtm_protocol = 0;*/
+	/*req.r.rtm_scope = 0;*/
+	/*req.r.rtm_type = 0;*/
+	/*req.r.rtm_src_len = 0;*/
+	/*req.r.rtm_dst_len = 0;*/
+	/*req.r.rtm_tos = 0;*/
+
+	while (*argv) {
+		switch (index_in_strings(options, *argv)) {
+			case 0: /* from */
+			{
+				inet_prefix addr;
+				NEXT_ARG();
+				from_ok = 1;
+				get_prefix(&addr, *argv, req.r.rtm_family);
+				if (req.r.rtm_family == AF_UNSPEC) {
+					req.r.rtm_family = addr.family;
+				}
+				if (addr.bytelen) {
+					addattr_l(&req.n, sizeof(req), RTA_SRC, &addr.data, addr.bytelen);
+				}
+				req.r.rtm_src_len = addr.bitlen;
+				break;
+			}
+			case 1: /* iif */
+				NEXT_ARG();
+				idev = *argv;
+				break;
+			case 2: /* oif */
+			case 3: /* dev */
+				NEXT_ARG();
+				odev = *argv;
+				break;
+			case 4: /* notify */
+				req.r.rtm_flags |= RTM_F_NOTIFY;
+				break;
+			case 5: /* connected */
+				connected = 1;
+				break;
+			case 6: /* to */
+				NEXT_ARG();
+			default:
+			{
+				inet_prefix addr;
+				get_prefix(&addr, *argv, req.r.rtm_family);
+				if (req.r.rtm_family == AF_UNSPEC) {
+					req.r.rtm_family = addr.family;
+				}
+				if (addr.bytelen) {
+					addattr_l(&req.n, sizeof(req), RTA_DST, &addr.data, addr.bytelen);
+				}
+				req.r.rtm_dst_len = addr.bitlen;
+			}
+		}
+		argv++;
+	}
+
+	if (req.r.rtm_dst_len == 0) {
+		bb_error_msg_and_die("need at least destination address");
+	}
+
+	xrtnl_open(&rth);
+
+	ll_init_map(&rth);
+
+	if (idev || odev)  {
+		int idx;
+
+		if (idev) {
+			idx = xll_name_to_index(idev);
+			addattr32(&req.n, sizeof(req), RTA_IIF, idx);
+		}
+		if (odev) {
+			idx = xll_name_to_index(odev);
+			addattr32(&req.n, sizeof(req), RTA_OIF, idx);
+		}
+	}
+
+	if (req.r.rtm_family == AF_UNSPEC) {
+		req.r.rtm_family = AF_INET;
+	}
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+		return 2;
+	}
+
+	if (connected && !from_ok) {
+		struct rtmsg *r = NLMSG_DATA(&req.n);
+		int len = req.n.nlmsg_len;
+		struct rtattr * tb[RTA_MAX+1];
+
+		print_route(NULL, &req.n, NULL);
+
+		if (req.n.nlmsg_type != RTM_NEWROUTE) {
+			bb_error_msg_and_die("not a route?");
+		}
+		len -= NLMSG_LENGTH(sizeof(*r));
+		if (len < 0) {
+			bb_error_msg_and_die("wrong len %d", len);
+		}
+
+		memset(tb, 0, sizeof(tb));
+		parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+		if (tb[RTA_PREFSRC]) {
+			tb[RTA_PREFSRC]->rta_type = RTA_SRC;
+			r->rtm_src_len = 8*RTA_PAYLOAD(tb[RTA_PREFSRC]);
+		} else if (!tb[RTA_SRC]) {
+			bb_error_msg_and_die("can't connect the route");
+		}
+		if (!odev && tb[RTA_OIF]) {
+			tb[RTA_OIF]->rta_type = 0;
+		}
+		if (tb[RTA_GATEWAY]) {
+			tb[RTA_GATEWAY]->rta_type = 0;
+		}
+		if (!idev && tb[RTA_IIF]) {
+			tb[RTA_IIF]->rta_type = 0;
+		}
+		req.n.nlmsg_flags = NLM_F_REQUEST;
+		req.n.nlmsg_type = RTM_GETROUTE;
+
+		if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+			return 2;
+		}
+	}
+	print_route(NULL, &req.n, NULL);
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+int FAST_FUNC do_iproute(char **argv)
+{
+	static const char ip_route_commands[] ALIGN1 =
+	/*0-3*/	"add\0""append\0""change\0""chg\0"
+	/*4-7*/	"delete\0""get\0""list\0""show\0"
+	/*8..*/	"prepend\0""replace\0""test\0""flush\0";
+	int command_num;
+	unsigned flags = 0;
+	int cmd = RTM_NEWROUTE;
+
+	if (!*argv)
+		return iproute_list_or_flush(argv, 0);
+
+	/* "Standard" 'ip r a' treats 'a' as 'add', not 'append' */
+	/* It probably means that it is using "first match" rule */
+	command_num = index_in_substrings(ip_route_commands, *argv);
+
+	switch (command_num) {
+		case 0: /* add */
+			flags = NLM_F_CREATE|NLM_F_EXCL;
+			break;
+		case 1: /* append */
+			flags = NLM_F_CREATE|NLM_F_APPEND;
+			break;
+		case 2: /* change */
+		case 3: /* chg */
+			flags = NLM_F_REPLACE;
+			break;
+		case 4: /* delete */
+			cmd = RTM_DELROUTE;
+			break;
+		case 5: /* get */
+			return iproute_get(argv+1);
+		case 6: /* list */
+		case 7: /* show */
+			return iproute_list_or_flush(argv+1, 0);
+		case 8: /* prepend */
+			flags = NLM_F_CREATE;
+			break;
+		case 9: /* replace */
+			flags = NLM_F_CREATE|NLM_F_REPLACE;
+			break;
+		case 10: /* test */
+			flags = NLM_F_EXCL;
+			break;
+		case 11: /* flush */
+			return iproute_list_or_flush(argv+1, 1);
+		default:
+			bb_error_msg_and_die("unknown command %s", *argv);
+	}
+
+	return iproute_modify(cmd, flags, argv+1);
+}
diff --git a/busybox-1.19.3/networking/libiproute/iprule.c b/busybox-1.19.3/networking/libiproute/iprule.c
new file mode 100644
index 0000000..dd3265c
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/iprule.c
@@ -0,0 +1,317 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+ * initially integrated into busybox by Bernhard Reutner-Fischer
+ */
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+
+#include "ip_common.h"  /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+/*
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip rule [ list | add | del ] SELECTOR ACTION\n");
+	fprintf(stderr, "SELECTOR := [ from PREFIX ] [ to PREFIX ] [ tos TOS ] [ fwmark FWMARK ]\n");
+	fprintf(stderr, "            [ dev STRING ] [ pref NUMBER ]\n");
+	fprintf(stderr, "ACTION := [ table TABLE_ID ] [ nat ADDRESS ]\n");
+	fprintf(stderr, "          [ prohibit | reject | unreachable ]\n");
+	fprintf(stderr, "          [ realms [SRCREALM/]DSTREALM ]\n");
+	fprintf(stderr, "TABLE_ID := [ local | main | default | NUMBER ]\n");
+	exit(-1);
+}
+*/
+
+static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
+					struct nlmsghdr *n, void *arg UNUSED_PARAM)
+{
+	struct rtmsg *r = NLMSG_DATA(n);
+	int len = n->nlmsg_len;
+	int host_len = -1;
+	struct rtattr * tb[RTA_MAX+1];
+	char abuf[256];
+	SPRINT_BUF(b1);
+
+	if (n->nlmsg_type != RTM_NEWRULE)
+		return 0;
+
+	len -= NLMSG_LENGTH(sizeof(*r));
+	if (len < 0)
+		return -1;
+
+	memset(tb, 0, sizeof(tb));
+	parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+
+	if (r->rtm_family == AF_INET)
+		host_len = 32;
+	else if (r->rtm_family == AF_INET6)
+		host_len = 128;
+/*	else if (r->rtm_family == AF_DECnet)
+		host_len = 16;
+	else if (r->rtm_family == AF_IPX)
+		host_len = 80;
+*/
+	printf("%u:\t", tb[RTA_PRIORITY] ?
+					*(unsigned*)RTA_DATA(tb[RTA_PRIORITY])
+					: 0);
+	printf("from ");
+	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)),
+				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);
+		}
+	} else if (r->rtm_src_len) {
+		printf("0/%d", r->rtm_src_len);
+	} else {
+		printf("all");
+	}
+	bb_putchar(' ');
+
+	if (tb[RTA_DST]) {
+		if (r->rtm_dst_len != host_len) {
+			printf("to %s/%u ", rt_addr_n2a(r->rtm_family,
+							 RTA_DATA(tb[RTA_DST]),
+							 abuf, sizeof(abuf)),
+				r->rtm_dst_len
+				);
+		} else {
+			printf("to %s ", format_host(r->rtm_family,
+						       RTA_PAYLOAD(tb[RTA_DST]),
+						       RTA_DATA(tb[RTA_DST]),
+						       abuf, sizeof(abuf)));
+		}
+	} else if (r->rtm_dst_len) {
+		printf("to 0/%d ", r->rtm_dst_len);
+	}
+
+	if (r->rtm_tos) {
+		printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1));
+	}
+	if (tb[RTA_PROTOINFO]) {
+		printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
+	}
+
+	if (tb[RTA_IIF]) {
+		printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
+	}
+
+	if (r->rtm_table)
+		printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1));
+
+	if (tb[RTA_FLOW]) {
+		uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
+		uint32_t from = to>>16;
+		to &= 0xFFFF;
+		if (from) {
+			printf("realms %s/",
+				rtnl_rtrealm_n2a(from, b1));
+		}
+		printf("%s ",
+			rtnl_rtrealm_n2a(to, b1));
+	}
+
+	if (r->rtm_type == RTN_NAT) {
+		if (tb[RTA_GATEWAY]) {
+			printf("map-to %s ",
+				format_host(r->rtm_family,
+					    RTA_PAYLOAD(tb[RTA_GATEWAY]),
+					    RTA_DATA(tb[RTA_GATEWAY]),
+					    abuf, sizeof(abuf)));
+		} else
+			printf("masquerade");
+	} else if (r->rtm_type != RTN_UNICAST)
+		fputs(rtnl_rtntype_n2a(r->rtm_type, b1), stdout);
+
+	bb_putchar('\n');
+	/*fflush_all();*/
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_list(char **argv)
+{
+	struct rtnl_handle rth;
+	int af = preferred_family;
+
+	if (af == AF_UNSPEC)
+		af = AF_INET;
+
+	if (*argv) {
+		//bb_error_msg("\"rule show\" needs no arguments");
+		bb_warn_ignoring_args(*argv);
+		return -1;
+	}
+
+	xrtnl_open(&rth);
+
+	xrtnl_wilddump_request(&rth, af, RTM_GETRULE);
+	xrtnl_dump_filter(&rth, print_rule, NULL);
+
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_modify(int cmd, char **argv)
+{
+	static const char keywords[] ALIGN1 =
+		"from\0""to\0""preference\0""order\0""priority\0"
+		"tos\0""fwmark\0""realms\0""table\0""lookup\0""dev\0"
+		"iif\0""nat\0""map-to\0""type\0""help\0";
+	enum {
+		ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
+		ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup, ARG_dev,
+		ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help
+	};
+	bool table_ok = 0;
+	struct rtnl_handle rth;
+	struct {
+		struct nlmsghdr n;
+		struct rtmsg    r;
+		char            buf[1024];
+	} req;
+	smalluint key;
+
+	memset(&req, 0, sizeof(req));
+
+	req.n.nlmsg_type = cmd;
+	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+	req.n.nlmsg_flags = NLM_F_REQUEST;
+	req.r.rtm_family = preferred_family;
+	req.r.rtm_protocol = RTPROT_BOOT;
+	req.r.rtm_scope = RT_SCOPE_UNIVERSE;
+	req.r.rtm_table = 0;
+	req.r.rtm_type = RTN_UNSPEC;
+
+	if (cmd == RTM_NEWRULE) {
+		req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+		req.r.rtm_type = RTN_UNICAST;
+	}
+
+	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);
+		if (key == ARG_from) {
+			inet_prefix dst;
+			NEXT_ARG();
+			get_prefix(&dst, *argv, req.r.rtm_family);
+			req.r.rtm_src_len = dst.bitlen;
+			addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
+		} else if (key == ARG_to) {
+			inet_prefix dst;
+			NEXT_ARG();
+			get_prefix(&dst, *argv, req.r.rtm_family);
+			req.r.rtm_dst_len = dst.bitlen;
+			addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
+		} else if (key == ARG_preference ||
+			   key == ARG_order ||
+			   key == ARG_priority
+		) {
+			uint32_t pref;
+			NEXT_ARG();
+			pref = get_u32(*argv, "preference");
+			addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
+		} else if (key == ARG_tos) {
+			uint32_t tos;
+			NEXT_ARG();
+			if (rtnl_dsfield_a2n(&tos, *argv))
+				invarg(*argv, "TOS");
+			req.r.rtm_tos = tos;
+		} else if (key == ARG_fwmark) {
+			uint32_t fwmark;
+			NEXT_ARG();
+			fwmark = get_u32(*argv, "fwmark");
+			addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
+		} else if (key == ARG_realms) {
+			uint32_t realm;
+			NEXT_ARG();
+			if (get_rt_realms(&realm, *argv))
+				invarg(*argv, "realms");
+			addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
+		} else if (key == ARG_table ||
+			   key == ARG_lookup
+		) {
+			uint32_t tid;
+			NEXT_ARG();
+			if (rtnl_rttable_a2n(&tid, *argv))
+				invarg(*argv, "table ID");
+			req.r.rtm_table = tid;
+			table_ok = 1;
+		} else if (key == ARG_dev ||
+			   key == ARG_iif
+		) {
+			NEXT_ARG();
+			addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
+		} else if (key == ARG_nat ||
+			   key == ARG_map_to
+		) {
+			NEXT_ARG();
+			addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
+			req.r.rtm_type = RTN_NAT;
+		} else {
+			int type;
+
+			if (key == ARG_type) {
+				NEXT_ARG();
+			}
+			if (key == ARG_help)
+				bb_show_usage();
+			if (rtnl_rtntype_a2n(&type, *argv))
+				invarg(*argv, "type");
+			req.r.rtm_type = type;
+		}
+		argv++;
+	}
+
+	if (req.r.rtm_family == AF_UNSPEC)
+		req.r.rtm_family = AF_INET;
+
+	if (!table_ok && cmd == RTM_NEWRULE)
+		req.r.rtm_table = RT_TABLE_MAIN;
+
+	xrtnl_open(&rth);
+
+	if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+		return 2;
+
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+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);
+		argv++;
+		if (cmd < 2)
+			return iprule_modify((cmd == 0) ? RTM_NEWRULE : RTM_DELRULE, argv);
+	}
+	return iprule_list(argv);
+}
diff --git a/busybox-1.19.3/networking/libiproute/iptunnel.c b/busybox-1.19.3/networking/libiproute/iptunnel.c
new file mode 100644
index 0000000..5942fea
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/iptunnel.c
@@ -0,0 +1,576 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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> 980930: do not allow key for ipip/sit
+ * Phil Karn <karn@ka9q.ampr.org>       990408: "pmtudisc" flag
+ */
+
+#include <netinet/ip.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <asm/types.h>
+
+#ifndef __constant_htons
+#define __constant_htons htons
+#endif
+
+// FYI: #define SIOCDEVPRIVATE 0x89F0
+
+/* From linux/if_tunnel.h. #including it proved troublesome
+ * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
+#define SIOCGETTUNNEL   (SIOCDEVPRIVATE + 0)
+#define SIOCADDTUNNEL   (SIOCDEVPRIVATE + 1)
+#define SIOCDELTUNNEL   (SIOCDEVPRIVATE + 2)
+#define SIOCCHGTUNNEL   (SIOCDEVPRIVATE + 3)
+//#define SIOCGETPRL      (SIOCDEVPRIVATE + 4)
+//#define SIOCADDPRL      (SIOCDEVPRIVATE + 5)
+//#define SIOCDELPRL      (SIOCDEVPRIVATE + 6)
+//#define SIOCCHGPRL      (SIOCDEVPRIVATE + 7)
+#define GRE_CSUM        __constant_htons(0x8000)
+//#define GRE_ROUTING     __constant_htons(0x4000)
+#define GRE_KEY         __constant_htons(0x2000)
+#define GRE_SEQ         __constant_htons(0x1000)
+//#define GRE_STRICT      __constant_htons(0x0800)
+//#define GRE_REC         __constant_htons(0x0700)
+//#define GRE_FLAGS       __constant_htons(0x00F8)
+//#define GRE_VERSION     __constant_htons(0x0007)
+struct ip_tunnel_parm {
+	char            name[IFNAMSIZ];
+	int             link;
+	uint16_t        i_flags;
+	uint16_t        o_flags;
+	uint32_t        i_key;
+	uint32_t        o_key;
+	struct iphdr    iph;
+};
+/* SIT-mode i_flags */
+//#define SIT_ISATAP 0x0001
+//struct ip_tunnel_prl {
+//	uint32_t          addr;
+//	uint16_t          flags;
+//	uint16_t          __reserved;
+//	uint32_t          datalen;
+//	uint32_t          __reserved2;
+//	/* data follows */
+//};
+///* PRL flags */
+//#define PRL_DEFAULT 0x0001
+
+#include "ip_common.h"  /* #include "libbb.h" is inside */
+#include "rt_names.h"
+#include "utils.h"
+
+
+/* Dies on error */
+static int do_ioctl_get_ifindex(char *dev)
+{
+	struct ifreq ifr;
+	int fd;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+	fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+	xioctl(fd, SIOCGIFINDEX, &ifr);
+	close(fd);
+	return ifr.ifr_ifindex;
+}
+
+static int do_ioctl_get_iftype(char *dev)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, dev);
+	fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+	err = ioctl_or_warn(fd, SIOCGIFHWADDR, &ifr);
+	close(fd);
+	return err ? -1 : ifr.ifr_addr.sa_family;
+}
+
+static char *do_ioctl_get_ifname(int idx)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	ifr.ifr_ifindex = idx;
+	fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+	err = ioctl_or_warn(fd, SIOCGIFNAME, &ifr);
+	close(fd);
+	return err ? NULL : xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name));
+}
+
+static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
+{
+	struct ifreq ifr;
+	int fd;
+	int err;
+
+	strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
+	ifr.ifr_ifru.ifru_data = (void*)p;
+	fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+	err = ioctl_or_warn(fd, SIOCGETTUNNEL, &ifr);
+	close(fd);
+	return err;
+}
+
+/* Dies on error, otherwise returns 0 */
+static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
+{
+	struct ifreq ifr;
+	int fd;
+
+	if (cmd == SIOCCHGTUNNEL && p->name[0]) {
+		strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
+	} else {
+		strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
+	}
+	ifr.ifr_ifru.ifru_data = (void*)p;
+	fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+#if ENABLE_IOCTL_HEX2STR_ERROR
+	/* #define magic will turn ioctl# into string */
+	if (cmd == SIOCCHGTUNNEL)
+		xioctl(fd, SIOCCHGTUNNEL, &ifr);
+	else
+		xioctl(fd, SIOCADDTUNNEL, &ifr);
+#else
+	xioctl(fd, cmd, &ifr);
+#endif
+	close(fd);
+	return 0;
+}
+
+/* Dies on error, otherwise returns 0 */
+static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
+{
+	struct ifreq ifr;
+	int fd;
+
+	if (p->name[0]) {
+		strncpy_IFNAMSIZ(ifr.ifr_name, p->name);
+	} else {
+		strncpy_IFNAMSIZ(ifr.ifr_name, basedev);
+	}
+	ifr.ifr_ifru.ifru_data = (void*)p;
+	fd = xsocket(AF_INET, SOCK_DGRAM, 0);
+	xioctl(fd, SIOCDELTUNNEL, &ifr);
+	close(fd);
+	return 0;
+}
+
+/* Dies on error */
+static void parse_args(char **argv, int cmd, struct ip_tunnel_parm *p)
+{
+	static const char keywords[] ALIGN1 =
+		"mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
+		"key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
+		"csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
+		"remote\0""any\0""local\0""dev\0"
+		"ttl\0""inherit\0""tos\0""dsfield\0"
+		"name\0";
+	enum {
+		ARG_mode, ARG_ipip, ARG_ip_ip, ARG_gre, ARG_gre_ip, ARG_sit, ARG_ip6_ip,
+		ARG_key, ARG_ikey, ARG_okey, ARG_seq, ARG_iseq, ARG_oseq,
+		ARG_csum, ARG_icsum, ARG_ocsum, ARG_nopmtudisc, ARG_pmtudisc,
+		ARG_remote, ARG_any, ARG_local, ARG_dev,
+		ARG_ttl, ARG_inherit, ARG_tos, ARG_dsfield,
+		ARG_name
+	};
+	int count = 0;
+	char medium[IFNAMSIZ];
+	int key;
+
+	memset(p, 0, sizeof(*p));
+	medium[0] = '\0';
+
+	p->iph.version = 4;
+	p->iph.ihl = 5;
+#ifndef IP_DF
+#define IP_DF 0x4000  /* Flag: "Don't Fragment" */
+#endif
+	p->iph.frag_off = htons(IP_DF);
+
+	while (*argv) {
+		key = index_in_strings(keywords, *argv);
+		if (key == ARG_mode) {
+			NEXT_ARG();
+			key = index_in_strings(keywords, *argv);
+			if (key == ARG_ipip ||
+			    key == ARG_ip_ip
+			) {
+				if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
+					bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
+				}
+				p->iph.protocol = IPPROTO_IPIP;
+			} else if (key == ARG_gre ||
+				   key == ARG_gre_ip
+			) {
+				if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
+					bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
+				}
+				p->iph.protocol = IPPROTO_GRE;
+			} else if (key == ARG_sit ||
+				   key == ARG_ip6_ip
+			) {
+				if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
+					bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
+				}
+				p->iph.protocol = IPPROTO_IPV6;
+			} else {
+				bb_error_msg_and_die("%s tunnel mode", "can't guess");
+			}
+		} else if (key == ARG_key) {
+			unsigned uval;
+			NEXT_ARG();
+			p->i_flags |= GRE_KEY;
+			p->o_flags |= GRE_KEY;
+			if (strchr(*argv, '.'))
+				p->i_key = p->o_key = get_addr32(*argv);
+			else {
+				uval = get_unsigned(*argv, "key");
+				p->i_key = p->o_key = htonl(uval);
+			}
+		} else if (key == ARG_ikey) {
+			unsigned uval;
+			NEXT_ARG();
+			p->i_flags |= GRE_KEY;
+			if (strchr(*argv, '.'))
+				p->o_key = get_addr32(*argv);
+			else {
+				uval = get_unsigned(*argv, "ikey");
+				p->i_key = htonl(uval);
+			}
+		} else if (key == ARG_okey) {
+			unsigned uval;
+			NEXT_ARG();
+			p->o_flags |= GRE_KEY;
+			if (strchr(*argv, '.'))
+				p->o_key = get_addr32(*argv);
+			else {
+				uval = get_unsigned(*argv, "okey");
+				p->o_key = htonl(uval);
+			}
+		} else if (key == ARG_seq) {
+			p->i_flags |= GRE_SEQ;
+			p->o_flags |= GRE_SEQ;
+		} else if (key == ARG_iseq) {
+			p->i_flags |= GRE_SEQ;
+		} else if (key == ARG_oseq) {
+			p->o_flags |= GRE_SEQ;
+		} else if (key == ARG_csum) {
+			p->i_flags |= GRE_CSUM;
+			p->o_flags |= GRE_CSUM;
+		} else if (key == ARG_icsum) {
+			p->i_flags |= GRE_CSUM;
+		} else if (key == ARG_ocsum) {
+			p->o_flags |= GRE_CSUM;
+		} else if (key == ARG_nopmtudisc) {
+			p->iph.frag_off = 0;
+		} else if (key == ARG_pmtudisc) {
+			p->iph.frag_off = htons(IP_DF);
+		} else if (key == ARG_remote) {
+			NEXT_ARG();
+			key = index_in_strings(keywords, *argv);
+			if (key != ARG_any)
+				p->iph.daddr = get_addr32(*argv);
+		} else if (key == ARG_local) {
+			NEXT_ARG();
+			key = index_in_strings(keywords, *argv);
+			if (key != ARG_any)
+				p->iph.saddr = get_addr32(*argv);
+		} else if (key == ARG_dev) {
+			NEXT_ARG();
+			strncpy_IFNAMSIZ(medium, *argv);
+		} else if (key == ARG_ttl) {
+			unsigned uval;
+			NEXT_ARG();
+			key = index_in_strings(keywords, *argv);
+			if (key != ARG_inherit) {
+				uval = get_unsigned(*argv, "TTL");
+				if (uval > 255)
+					invarg(*argv, "TTL must be <=255");
+				p->iph.ttl = uval;
+			}
+		} else if (key == ARG_tos ||
+			   key == ARG_dsfield
+		) {
+			uint32_t uval;
+			NEXT_ARG();
+			key = index_in_strings(keywords, *argv);
+			if (key != ARG_inherit) {
+				if (rtnl_dsfield_a2n(&uval, *argv))
+					invarg(*argv, "TOS");
+				p->iph.tos = uval;
+			} else
+				p->iph.tos = 1;
+		} else {
+			if (key == ARG_name) {
+				NEXT_ARG();
+			}
+			if (p->name[0])
+				duparg2("name", *argv);
+			strncpy_IFNAMSIZ(p->name, *argv);
+			if (cmd == SIOCCHGTUNNEL && count == 0) {
+				struct ip_tunnel_parm old_p;
+				memset(&old_p, 0, sizeof(old_p));
+				if (do_get_ioctl(*argv, &old_p))
+					exit(EXIT_FAILURE);
+				*p = old_p;
+			}
+		}
+		count++;
+		argv++;
+	}
+
+	if (p->iph.protocol == 0) {
+		if (memcmp(p->name, "gre", 3) == 0)
+			p->iph.protocol = IPPROTO_GRE;
+		else if (memcmp(p->name, "ipip", 4) == 0)
+			p->iph.protocol = IPPROTO_IPIP;
+		else if (memcmp(p->name, "sit", 3) == 0)
+			p->iph.protocol = IPPROTO_IPV6;
+	}
+
+	if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
+		if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
+			bb_error_msg_and_die("keys are not allowed with ipip and sit");
+		}
+	}
+
+	if (medium[0]) {
+		p->link = do_ioctl_get_ifindex(medium);
+	}
+
+	if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+		p->i_key = p->iph.daddr;
+		p->i_flags |= GRE_KEY;
+	}
+	if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
+		p->o_key = p->iph.daddr;
+		p->o_flags |= GRE_KEY;
+	}
+	if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
+		bb_error_msg_and_die("broadcast tunnel requires a source address");
+	}
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_add(int cmd, char **argv)
+{
+	struct ip_tunnel_parm p;
+
+	parse_args(argv, cmd, &p);
+
+	if (p.iph.ttl && p.iph.frag_off == 0) {
+		bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
+	}
+
+	switch (p.iph.protocol) {
+	case IPPROTO_IPIP:
+		return do_add_ioctl(cmd, "tunl0", &p);
+	case IPPROTO_GRE:
+		return do_add_ioctl(cmd, "gre0", &p);
+	case IPPROTO_IPV6:
+		return do_add_ioctl(cmd, "sit0", &p);
+	default:
+		bb_error_msg_and_die("can't determine tunnel mode (ipip, gre or sit)");
+	}
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_del(char **argv)
+{
+	struct ip_tunnel_parm p;
+
+	parse_args(argv, SIOCDELTUNNEL, &p);
+
+	switch (p.iph.protocol) {
+	case IPPROTO_IPIP:
+		return do_del_ioctl("tunl0", &p);
+	case IPPROTO_GRE:
+		return do_del_ioctl("gre0", &p);
+	case IPPROTO_IPV6:
+		return do_del_ioctl("sit0", &p);
+	default:
+		return do_del_ioctl(p.name, &p);
+	}
+}
+
+static void print_tunnel(struct ip_tunnel_parm *p)
+{
+	char s1[256];
+	char s2[256];
+	char s3[64];
+	char s4[64];
+
+	format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
+	format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
+	inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
+	inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
+
+	printf("%s: %s/ip  remote %s  local %s ",
+	       p->name,
+	       p->iph.protocol == IPPROTO_IPIP ? "ip" :
+	       (p->iph.protocol == IPPROTO_GRE ? "gre" :
+		(p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
+	       p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
+	if (p->link) {
+		char *n = do_ioctl_get_ifname(p->link);
+		if (n) {
+			printf(" dev %s ", n);
+			free(n);
+		}
+	}
+	if (p->iph.ttl)
+		printf(" ttl %d ", p->iph.ttl);
+	else
+		printf(" ttl inherit ");
+	if (p->iph.tos) {
+		SPRINT_BUF(b1);
+		printf(" tos");
+		if (p->iph.tos & 1)
+			printf(" inherit");
+		if (p->iph.tos & ~1)
+			printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
+			       rtnl_dsfield_n2a(p->iph.tos & ~1, b1));
+	}
+	if (!(p->iph.frag_off & htons(IP_DF)))
+		printf(" nopmtudisc");
+
+	if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
+		printf(" key %s", s3);
+	else if ((p->i_flags | p->o_flags) & GRE_KEY) {
+		if (p->i_flags & GRE_KEY)
+			printf(" ikey %s ", s3);
+		if (p->o_flags & GRE_KEY)
+			printf(" okey %s ", s4);
+	}
+
+	if (p->i_flags & GRE_SEQ)
+		printf("%c  Drop packets out of sequence.\n", _SL_);
+	if (p->i_flags & GRE_CSUM)
+		printf("%c  Checksum in received packet is required.", _SL_);
+	if (p->o_flags & GRE_SEQ)
+		printf("%c  Sequence packets on output.", _SL_);
+	if (p->o_flags & GRE_CSUM)
+		printf("%c  Checksum output packets.", _SL_);
+}
+
+static void do_tunnels_list(struct ip_tunnel_parm *p)
+{
+	char name[IFNAMSIZ];
+	unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
+		rx_fifo, rx_frame,
+		tx_bytes, tx_packets, tx_errs, tx_drops,
+		tx_fifo, tx_colls, tx_carrier, rx_multi;
+	int type;
+	struct ip_tunnel_parm p1;
+	char buf[512];
+	FILE *fp = fopen_or_warn("/proc/net/dev", "r");
+
+	if (fp == NULL) {
+		return;
+	}
+	/* skip headers */
+	fgets(buf, sizeof(buf), fp);
+	fgets(buf, sizeof(buf), fp);
+
+	while (fgets(buf, sizeof(buf), fp) != NULL) {
+		char *ptr;
+
+		/*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
+		ptr = strchr(buf, ':');
+		if (ptr == NULL ||
+		    (*ptr++ = 0, sscanf(buf, "%s", name) != 1)
+		) {
+			bb_error_msg("wrong format of /proc/net/dev");
+			return;
+		}
+		if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
+			   &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
+			   &rx_fifo, &rx_frame, &rx_multi,
+			   &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
+			   &tx_fifo, &tx_colls, &tx_carrier) != 14)
+			continue;
+		if (p->name[0] && strcmp(p->name, name))
+			continue;
+		type = do_ioctl_get_iftype(name);
+		if (type == -1) {
+			bb_error_msg("can't get type of [%s]", name);
+			continue;
+		}
+		if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
+			continue;
+		memset(&p1, 0, sizeof(p1));
+		if (do_get_ioctl(name, &p1))
+			continue;
+		if ((p->link && p1.link != p->link) ||
+		    (p->name[0] && strcmp(p1.name, p->name)) ||
+		    (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
+		    (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
+		    (p->i_key && p1.i_key != p->i_key)
+		) {
+			continue;
+		}
+		print_tunnel(&p1);
+		bb_putchar('\n');
+	}
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_show(char **argv)
+{
+	int err;
+	struct ip_tunnel_parm p;
+
+	parse_args(argv, SIOCGETTUNNEL, &p);
+
+	switch (p.iph.protocol) {
+	case IPPROTO_IPIP:
+		err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
+		break;
+	case IPPROTO_GRE:
+		err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
+		break;
+	case IPPROTO_IPV6:
+		err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
+		break;
+	default:
+		do_tunnels_list(&p);
+		return 0;
+	}
+	if (err)
+		return -1;
+
+	print_tunnel(&p);
+	bb_putchar('\n');
+	return 0;
+}
+
+/* Return value becomes exitcode. It's okay to not return at all */
+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);
+		argv++;
+		if (key == ARG_add)
+			return do_add(SIOCADDTUNNEL, argv);
+		if (key == ARG_change)
+			return do_add(SIOCCHGTUNNEL, argv);
+		if (key == ARG_del)
+			return do_del(argv);
+	}
+	return do_show(argv);
+}
diff --git a/busybox-1.19.3/networking/libiproute/libnetlink.c b/busybox-1.19.3/networking/libiproute/libnetlink.c
new file mode 100644
index 0000000..c7533a4
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/libnetlink.c
@@ -0,0 +1,401 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 <sys/uio.h>
+
+#include "libbb.h"
+#include "libnetlink.h"
+
+void FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
+{
+	socklen_t addr_len;
+
+	memset(rth, 0, sizeof(*rth));
+	rth->fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	rth->local.nl_family = AF_NETLINK;
+	/*rth->local.nl_groups = subscriptions;*/
+
+	xbind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local));
+	addr_len = sizeof(rth->local);
+	getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len);
+
+/* too much paranoia
+	if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0)
+		bb_perror_msg_and_die("getsockname");
+	if (addr_len != sizeof(rth->local))
+		bb_error_msg_and_die("wrong address length %d", addr_len);
+	if (rth->local.nl_family != AF_NETLINK)
+		bb_error_msg_and_die("wrong address family %d", rth->local.nl_family);
+*/
+	rth->seq = time(NULL);
+}
+
+int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+	struct {
+		struct nlmsghdr nlh;
+		struct rtgenmsg g;
+	} req;
+
+	req.nlh.nlmsg_len = sizeof(req);
+	req.nlh.nlmsg_type = type;
+	req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	req.nlh.nlmsg_pid = 0;
+	req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+	req.g.rtgen_family = family;
+
+	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;
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+
+	return xsendto(rth->fd, buf, len, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+	struct nlmsghdr nlh;
+	struct sockaddr_nl nladdr;
+	struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
+	struct msghdr msg = {
+		(void*)&nladdr, sizeof(nladdr),
+		iov,  2,
+		NULL, 0,
+		0
+	};
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+
+	nlh.nlmsg_len = NLMSG_LENGTH(len);
+	nlh.nlmsg_type = type;
+	nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+	nlh.nlmsg_pid = 0;
+	nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+	return sendmsg(rth->fd, &msg, 0);
+}
+
+static int rtnl_dump_filter(struct rtnl_handle *rth,
+		int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *n, void *) FAST_FUNC,
+		void *arg1/*,
+		int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
+		void *arg2*/)
+{
+	int retval = -1;
+	char *buf = xmalloc(8*1024); /* avoid big stack buffer */
+	struct sockaddr_nl nladdr;
+	struct iovec iov = { buf, 8*1024 };
+
+	while (1) {
+		int status;
+		struct nlmsghdr *h;
+
+		struct msghdr msg = {
+			(void*)&nladdr, sizeof(nladdr),
+			&iov, 1,
+			NULL, 0,
+			0
+		};
+
+		status = recvmsg(rth->fd, &msg, 0);
+
+		if (status < 0) {
+			if (errno == EINTR)
+				continue;
+			bb_perror_msg("OVERRUN");
+			continue;
+		}
+		if (status == 0) {
+			bb_error_msg("EOF on netlink");
+			goto ret;
+		}
+		if (msg.msg_namelen != sizeof(nladdr)) {
+			bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+		}
+
+		h = (struct nlmsghdr*)buf;
+		while (NLMSG_OK(h, status)) {
+			int err;
+
+			if (nladdr.nl_pid != 0 ||
+			    h->nlmsg_pid != rth->local.nl_pid ||
+			    h->nlmsg_seq != rth->dump
+			) {
+//				if (junk) {
+//					err = junk(&nladdr, h, arg2);
+//					if (err < 0) {
+//						retval = err;
+//						goto ret;
+//					}
+//				}
+				goto skip_it;
+			}
+
+			if (h->nlmsg_type == NLMSG_DONE) {
+				goto ret_0;
+			}
+			if (h->nlmsg_type == NLMSG_ERROR) {
+				struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
+				if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+					bb_error_msg("ERROR truncated");
+				} else {
+					errno = -l_err->error;
+					bb_perror_msg("RTNETLINK answers");
+				}
+				goto ret;
+			}
+			err = filter(&nladdr, h, arg1);
+			if (err < 0) {
+				retval = err;
+				goto ret;
+			}
+
+ skip_it:
+			h = NLMSG_NEXT(h, status);
+		}
+		if (msg.msg_flags & MSG_TRUNC) {
+			bb_error_msg("message truncated");
+			continue;
+		}
+		if (status) {
+			bb_error_msg_and_die("remnant of size %d!", status);
+		}
+	} /* while (1) */
+ ret_0:
+	retval++; /* = 0 */
+ ret:
+	free(buf);
+	return retval;
+}
+
+int FAST_FUNC xrtnl_dump_filter(struct rtnl_handle *rth,
+		int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *, void *) FAST_FUNC,
+		void *arg1)
+{
+	int ret = rtnl_dump_filter(rth, filter, arg1/*, NULL, NULL*/);
+	if (ret < 0)
+		bb_error_msg_and_die("dump terminated");
+	return ret;
+}
+
+int FAST_FUNC rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
+		pid_t peer, unsigned groups,
+		struct nlmsghdr *answer,
+		int (*junk)(struct sockaddr_nl *, struct nlmsghdr *, void *),
+		void *jarg)
+{
+/* bbox doesn't use parameters no. 3, 4, 6, 7, they are stubbed out */
+#define peer   0
+#define groups 0
+#define junk   NULL
+#define jarg   NULL
+	int retval = -1;
+	int status;
+	unsigned seq;
+	struct nlmsghdr *h;
+	struct sockaddr_nl nladdr;
+	struct iovec iov = { (void*)n, n->nlmsg_len };
+	char   *buf = xmalloc(8*1024); /* avoid big stack buffer */
+	struct msghdr msg = {
+		(void*)&nladdr, sizeof(nladdr),
+		&iov, 1,
+		NULL, 0,
+		0
+	};
+
+	memset(&nladdr, 0, sizeof(nladdr));
+	nladdr.nl_family = AF_NETLINK;
+//	nladdr.nl_pid = peer;
+//	nladdr.nl_groups = groups;
+
+	n->nlmsg_seq = seq = ++rtnl->seq;
+	if (answer == NULL) {
+		n->nlmsg_flags |= NLM_F_ACK;
+	}
+	status = sendmsg(rtnl->fd, &msg, 0);
+
+	if (status < 0) {
+		bb_perror_msg("can't talk to rtnetlink");
+		goto ret;
+	}
+
+	iov.iov_base = buf;
+
+	while (1) {
+		iov.iov_len = 8*1024;
+		status = recvmsg(rtnl->fd, &msg, 0);
+
+		if (status < 0) {
+			if (errno == EINTR) {
+				continue;
+			}
+			bb_perror_msg("OVERRUN");
+			continue;
+		}
+		if (status == 0) {
+			bb_error_msg("EOF on netlink");
+			goto ret;
+		}
+		if (msg.msg_namelen != sizeof(nladdr)) {
+			bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
+		}
+		for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+//			int l_err;
+			int len = h->nlmsg_len;
+			int l = len - sizeof(*h);
+
+			if (l < 0 || len > status) {
+				if (msg.msg_flags & MSG_TRUNC) {
+					bb_error_msg("truncated message");
+					goto ret;
+				}
+				bb_error_msg_and_die("malformed message: len=%d!", len);
+			}
+
+			if (nladdr.nl_pid != peer ||
+			    h->nlmsg_pid != rtnl->local.nl_pid ||
+			    h->nlmsg_seq != seq
+			) {
+//				if (junk) {
+//					l_err = junk(&nladdr, h, jarg);
+//					if (l_err < 0) {
+//						retval = l_err;
+//						goto ret;
+//					}
+//				}
+				continue;
+			}
+
+			if (h->nlmsg_type == NLMSG_ERROR) {
+				struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+				if (l < (int)sizeof(struct nlmsgerr)) {
+					bb_error_msg("ERROR truncated");
+				} else {
+					errno = - err->error;
+					if (errno == 0) {
+						if (answer) {
+							memcpy(answer, h, h->nlmsg_len);
+						}
+						goto ret_0;
+					}
+					bb_perror_msg("RTNETLINK answers");
+				}
+				goto ret;
+			}
+			if (answer) {
+				memcpy(answer, h, h->nlmsg_len);
+				goto ret_0;
+			}
+
+			bb_error_msg("unexpected reply!");
+
+			status -= NLMSG_ALIGN(len);
+			h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+		}
+		if (msg.msg_flags & MSG_TRUNC) {
+			bb_error_msg("message truncated");
+			continue;
+		}
+		if (status) {
+			bb_error_msg_and_die("remnant of size %d!", status);
+		}
+	} /* while (1) */
+ ret_0:
+	retval++; /* = 0 */
+ ret:
+	free(buf);
+	return retval;
+}
+
+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) {
+		return -1;
+	}
+	rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
+	rta->rta_type = type;
+	rta->rta_len = len;
+	move_to_unaligned32(RTA_DATA(rta), data);
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+	return 0;
+}
+
+int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
+{
+	int len = RTA_LENGTH(alen);
+	struct rtattr *rta;
+
+	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;
+	memcpy(RTA_DATA(rta), data, alen);
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+	return 0;
+}
+
+int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data)
+{
+	int len = RTA_LENGTH(4);
+	struct rtattr *subrta;
+
+	if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+		return -1;
+	}
+	subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+	subrta->rta_type = type;
+	subrta->rta_len = len;
+	move_to_unaligned32(RTA_DATA(subrta), data);
+	rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+	return 0;
+}
+
+int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
+{
+	struct rtattr *subrta;
+	int len = RTA_LENGTH(alen);
+
+	if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+		return -1;
+	}
+	subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+	subrta->rta_type = type;
+	subrta->rta_len = len;
+	memcpy(RTA_DATA(subrta), data, alen);
+	rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+	return 0;
+}
+
+
+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);
+	}
+	if (len) {
+		bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
+	}
+}
diff --git a/busybox-1.19.3/networking/libiproute/libnetlink.h b/busybox-1.19.3/networking/libiproute/libnetlink.h
new file mode 100644
index 0000000..51bee2d
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/libnetlink.h
@@ -0,0 +1,49 @@
+/* vi: set sw=4 ts=4: */
+#ifndef LIBNETLINK_H
+#define LIBNETLINK_H 1
+
+#include <linux/types.h>
+/* We need linux/types.h because older kernels use __u32 etc
+ * in linux/[rt]netlink.h. 2.6.19 seems to be ok, though */
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+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;
+};
+
+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,
+		int (*filter)(const struct sockaddr_nl*, struct nlmsghdr *n, void*) FAST_FUNC,
+		void *arg1) FAST_FUNC;
+
+/* bbox doesn't use parameters no. 3, 4, 6, 7, stub them out */
+#define rtnl_talk(rtnl, n, peer, groups, answer, junk, jarg) \
+	rtnl_talk(rtnl, n, answer)
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+		unsigned groups, struct nlmsghdr *answer,
+		int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
+		void *jarg) FAST_FUNC;
+
+extern int rtnl_send(struct rtnl_handle *rth, char *buf, int) FAST_FUNC;
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) FAST_FUNC;
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) FAST_FUNC;
+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 void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/libiproute/ll_addr.c b/busybox-1.19.3/networking/libiproute/ll_addr.c
new file mode 100644
index 0000000..33a54ea
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ll_addr.c
@@ -0,0 +1,78 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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_arp.h>
+
+#include "libbb.h"
+#include "rt_names.h"
+#include "utils.h"
+
+
+const char* FAST_FUNC ll_addr_n2a(unsigned char *addr, int alen, int type, char *buf, int blen)
+{
+	int i;
+	int l;
+
+	if (alen == 4
+	 && (type == ARPHRD_TUNNEL || type == ARPHRD_SIT || type == ARPHRD_IPGRE)
+	) {
+		return inet_ntop(AF_INET, addr, buf, blen);
+	}
+	l = 0;
+	for (i = 0; i < alen; i++) {
+		if (i == 0) {
+			snprintf(buf + l, blen, ":%02x"+1, addr[i]);
+			blen -= 2;
+			l += 2;
+		} else {
+			snprintf(buf + l, blen, ":%02x", addr[i]);
+			blen -= 3;
+			l += 3;
+		}
+	}
+	return buf;
+}
+
+int FAST_FUNC ll_addr_a2n(unsigned char *lladdr, int len, char *arg)
+{
+	int i;
+
+	if (strchr(arg, '.')) {
+		inet_prefix pfx;
+		if (get_addr_1(&pfx, arg, AF_INET)) {
+			bb_error_msg("\"%s\" is invalid lladdr", arg);
+			return -1;
+		}
+		if (len < 4) {
+			return -1;
+		}
+		memcpy(lladdr, pfx.data, 4);
+		return 4;
+	}
+
+	for (i = 0; i < len; i++) {
+		int temp;
+		char *cp = strchr(arg, ':');
+		if (cp) {
+			*cp = 0;
+			cp++;
+		}
+		if (sscanf(arg, "%x", &temp) != 1 || (temp < 0 || temp > 255)) {
+			bb_error_msg("\"%s\" is invalid lladdr", arg);
+			return -1;
+		}
+		lladdr[i] = temp;
+		if (!cp) {
+			break;
+		}
+		arg = cp;
+	}
+	return i+1;
+}
diff --git a/busybox-1.19.3/networking/libiproute/ll_map.c b/busybox-1.19.3/networking/libiproute/ll_map.c
new file mode 100644
index 0000000..27cd90f
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ll_map.c
@@ -0,0 +1,202 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 "libbb.h"
+#include "libnetlink.h"
+#include "ll_map.h"
+
+struct idxmap {
+	struct idxmap *next;
+	int            index;
+	int            type;
+	int            alen;
+	unsigned       flags;
+	unsigned char  addr[8];
+	char           name[16];
+};
+
+static struct idxmap **idxmap; /* treat as *idxmap[16] */
+
+static struct idxmap *find_by_index(int idx)
+{
+	struct idxmap *im;
+
+	if (idxmap)
+		for (im = idxmap[idx & 0xF]; im; im = im->next)
+			if (im->index == idx)
+				return im;
+	return NULL;
+}
+
+int FAST_FUNC ll_remember_index(const struct sockaddr_nl *who UNUSED_PARAM,
+		struct nlmsghdr *n,
+		void *arg UNUSED_PARAM)
+{
+	int h;
+	struct ifinfomsg *ifi = NLMSG_DATA(n);
+	struct idxmap *im, **imp;
+	struct rtattr *tb[IFLA_MAX+1];
+
+	if (n->nlmsg_type != RTM_NEWLINK)
+		return 0;
+
+	if (n->nlmsg_len < NLMSG_LENGTH(sizeof(ifi)))
+		return -1;
+
+	memset(tb, 0, sizeof(tb));
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+	if (tb[IFLA_IFNAME] == NULL)
+		return 0;
+
+	if (!idxmap)
+		idxmap = xzalloc(sizeof(idxmap[0]) * 16);
+
+	h = ifi->ifi_index & 0xF;
+	for (imp = &idxmap[h]; (im = *imp) != NULL; imp = &im->next)
+		if (im->index == ifi->ifi_index)
+			goto found;
+
+	im = xmalloc(sizeof(*im));
+	im->next = *imp;
+	im->index = ifi->ifi_index;
+	*imp = im;
+ found:
+	im->type = ifi->ifi_type;
+	im->flags = ifi->ifi_flags;
+	if (tb[IFLA_ADDRESS]) {
+		int alen;
+		im->alen = alen = RTA_PAYLOAD(tb[IFLA_ADDRESS]);
+		if (alen > (int)sizeof(im->addr))
+			alen = sizeof(im->addr);
+		memcpy(im->addr, RTA_DATA(tb[IFLA_ADDRESS]), alen);
+	} else {
+		im->alen = 0;
+		memset(im->addr, 0, sizeof(im->addr));
+	}
+	strcpy(im->name, RTA_DATA(tb[IFLA_IFNAME]));
+	return 0;
+}
+
+const char FAST_FUNC *ll_idx_n2a(int idx, char *buf)
+{
+	struct idxmap *im;
+
+	if (idx == 0)
+		return "*";
+	im = find_by_index(idx);
+	if (im)
+		return im->name;
+	snprintf(buf, 16, "if%d", idx);
+	return buf;
+}
+
+
+const char FAST_FUNC *ll_index_to_name(int idx)
+{
+	static char nbuf[16];
+
+	return ll_idx_n2a(idx, nbuf);
+}
+
+#ifdef UNUSED
+int ll_index_to_type(int idx)
+{
+	struct idxmap *im;
+
+	if (idx == 0)
+		return -1;
+	im = find_by_index(idx);
+	if (im)
+		return im->type;
+	return -1;
+}
+#endif
+
+unsigned FAST_FUNC ll_index_to_flags(int idx)
+{
+	struct idxmap *im;
+
+	if (idx == 0)
+		return 0;
+	im = find_by_index(idx);
+	if (im)
+		return im->flags;
+	return 0;
+}
+
+int FAST_FUNC xll_name_to_index(const char *name)
+{
+	int ret = 0;
+	int sock_fd;
+
+/* caching is not warranted - no users which repeatedly call it */
+#ifdef UNUSED
+	static char ncache[16];
+	static int icache;
+
+	struct idxmap *im;
+	int i;
+
+	if (name == NULL)
+		goto out;
+	if (icache && strcmp(name, ncache) == 0) {
+		ret = icache;
+		goto out;
+	}
+	if (idxmap) {
+		for (i = 0; i < 16; i++) {
+			for (im = idxmap[i]; im; im = im->next) {
+				if (strcmp(im->name, name) == 0) {
+					icache = im->index;
+					strcpy(ncache, name);
+					ret = im->index;
+					goto out;
+				}
+			}
+		}
+	}
+	/* We have not found the interface in our cache, but the kernel
+	 * may still know about it. One reason is that we may be using
+	 * module on-demand loading, which means that the kernel will
+	 * load the module and make the interface exist only when
+	 * we explicitely request it (check for dev_load() in net/core/dev.c).
+	 * I can think of other similar scenario, but they are less common...
+	 * Jean II */
+#endif
+
+	sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
+	if (sock_fd >= 0) {
+		struct ifreq ifr;
+		int tmp;
+
+		strncpy_IFNAMSIZ(ifr.ifr_name, name);
+		ifr.ifr_ifindex = -1;
+		tmp = ioctl(sock_fd, SIOCGIFINDEX, &ifr);
+		close(sock_fd);
+		if (tmp >= 0)
+			/* In theory, we should redump the interface list
+			 * to update our cache, this is left as an exercise
+			 * to the reader... Jean II */
+			ret = ifr.ifr_ifindex;
+	}
+/* out:*/
+	if (ret <= 0)
+		bb_error_msg_and_die("can't find device '%s'", name);
+	return ret;
+}
+
+int FAST_FUNC ll_init_map(struct rtnl_handle *rth)
+{
+	xrtnl_wilddump_request(rth, AF_UNSPEC, RTM_GETLINK);
+	xrtnl_dump_filter(rth, ll_remember_index, NULL);
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/libiproute/ll_map.h b/busybox-1.19.3/networking/libiproute/ll_map.h
new file mode 100644
index 0000000..c5d3834
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ll_map.h
@@ -0,0 +1,17 @@
+/* vi: set sw=4 ts=4: */
+#ifndef LL_MAP_H
+#define LL_MAP_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+int ll_remember_index(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) FAST_FUNC;
+int ll_init_map(struct rtnl_handle *rth) FAST_FUNC;
+int xll_name_to_index(const char *name) FAST_FUNC;
+const char *ll_index_to_name(int idx) FAST_FUNC;
+const char *ll_idx_n2a(int idx, char *buf) FAST_FUNC;
+/* int ll_index_to_type(int idx); */
+unsigned ll_index_to_flags(int idx) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/libiproute/ll_proto.c b/busybox-1.19.3/networking/libiproute/ll_proto.c
new file mode 100644
index 0000000..7aac836
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ll_proto.c
@@ -0,0 +1,122 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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"
+#include "rt_names.h"
+#include "utils.h"
+
+#include <netinet/if_ether.h>
+
+#if !ENABLE_WERROR
+#warning de-bloat
+#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[] = {
+__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
+
+__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
+
+{ 0x8100, "802.1Q" },
+{ ETH_P_IP, "ipv4" },
+};
+#undef __PF
+
+
+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;
+	}
+	snprintf(buf, len, "[%d]", 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;
+		 }
+	}
+	i = bb_strtou(buf, NULL, 0);
+	if (errno || i > 0xffff)
+		return -1;
+ good:
+	*id = htons(i);
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/libiproute/ll_types.c b/busybox-1.19.3/networking/libiproute/ll_types.c
new file mode 100644
index 0000000..bb42e26
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/ll_types.c
@@ -0,0 +1,204 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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> /* linux/if_arp.h needs it on some systems */
+#include <arpa/inet.h>
+#include <linux/if_arp.h>
+
+#include "libbb.h"
+#include "rt_names.h"
+
+const char* FAST_FUNC ll_type_n2a(int type, char *buf)
+{
+	static const char arphrd_name[] =
+	/* 0,                  */ "generic" "\0"
+	/* ARPHRD_LOOPBACK,    */ "loopback" "\0"
+	/* ARPHRD_ETHER,       */ "ether" "\0"
+#ifdef ARPHRD_INFINIBAND
+	/* ARPHRD_INFINIBAND,  */ "infiniband" "\0"
+#endif
+#ifdef ARPHRD_IEEE802_TR
+	/* ARPHRD_IEEE802,     */ "ieee802" "\0"
+	/* ARPHRD_IEEE802_TR,  */ "tr" "\0"
+#else
+	/* ARPHRD_IEEE802,     */ "tr" "\0"
+#endif
+#ifdef ARPHRD_IEEE80211
+	/* ARPHRD_IEEE80211,   */ "ieee802.11" "\0"
+#endif
+#ifdef ARPHRD_IEEE1394
+	/* ARPHRD_IEEE1394,    */ "ieee1394" "\0"
+#endif
+	/* ARPHRD_IRDA,        */ "irda" "\0"
+	/* ARPHRD_SLIP,        */ "slip" "\0"
+	/* ARPHRD_CSLIP,       */ "cslip" "\0"
+	/* ARPHRD_SLIP6,       */ "slip6" "\0"
+	/* ARPHRD_CSLIP6,      */ "cslip6" "\0"
+	/* ARPHRD_PPP,         */ "ppp" "\0"
+	/* ARPHRD_TUNNEL,      */ "ipip" "\0"
+	/* ARPHRD_TUNNEL6,     */ "tunnel6" "\0"
+	/* ARPHRD_SIT,         */ "sit" "\0"
+	/* ARPHRD_IPGRE,       */ "gre" "\0"
+#ifdef ARPHRD_VOID
+	/* ARPHRD_VOID,        */ "void" "\0"
+#endif
+
+#if ENABLE_FEATURE_IP_RARE_PROTOCOLS
+	/* ARPHRD_EETHER,      */ "eether" "\0"
+	/* ARPHRD_AX25,        */ "ax25" "\0"
+	/* ARPHRD_PRONET,      */ "pronet" "\0"
+	/* ARPHRD_CHAOS,       */ "chaos" "\0"
+	/* ARPHRD_ARCNET,      */ "arcnet" "\0"
+	/* ARPHRD_APPLETLK,    */ "atalk" "\0"
+	/* ARPHRD_DLCI,        */ "dlci" "\0"
+#ifdef ARPHRD_ATM
+	/* ARPHRD_ATM,         */ "atm" "\0"
+#endif
+	/* ARPHRD_METRICOM,    */ "metricom" "\0"
+	/* ARPHRD_RSRVD,       */ "rsrvd" "\0"
+	/* ARPHRD_ADAPT,       */ "adapt" "\0"
+	/* ARPHRD_ROSE,        */ "rose" "\0"
+	/* ARPHRD_X25,         */ "x25" "\0"
+#ifdef ARPHRD_HWX25
+	/* ARPHRD_HWX25,       */ "hwx25" "\0"
+#endif
+	/* ARPHRD_HDLC,        */ "hdlc" "\0"
+	/* ARPHRD_LAPB,        */ "lapb" "\0"
+#ifdef ARPHRD_DDCMP
+	/* ARPHRD_DDCMP,       */ "ddcmp" "\0"
+	/* ARPHRD_RAWHDLC,     */ "rawhdlc" "\0"
+#endif
+	/* ARPHRD_FRAD,        */ "frad" "\0"
+	/* ARPHRD_SKIP,        */ "skip" "\0"
+	/* ARPHRD_LOCALTLK,    */ "ltalk" "\0"
+	/* ARPHRD_FDDI,        */ "fddi" "\0"
+	/* ARPHRD_BIF,         */ "bif" "\0"
+	/* ARPHRD_IPDDP,       */ "ip/ddp" "\0"
+	/* ARPHRD_PIMREG,      */ "pimreg" "\0"
+	/* ARPHRD_HIPPI,       */ "hippi" "\0"
+	/* ARPHRD_ASH,         */ "ash" "\0"
+	/* ARPHRD_ECONET,      */ "econet" "\0"
+	/* ARPHRD_FCPP,        */ "fcpp" "\0"
+	/* ARPHRD_FCAL,        */ "fcal" "\0"
+	/* ARPHRD_FCPL,        */ "fcpl" "\0"
+	/* ARPHRD_FCFABRIC,    */ "fcfb0" "\0"
+	/* ARPHRD_FCFABRIC+1,  */ "fcfb1" "\0"
+	/* ARPHRD_FCFABRIC+2,  */ "fcfb2" "\0"
+	/* ARPHRD_FCFABRIC+3,  */ "fcfb3" "\0"
+	/* ARPHRD_FCFABRIC+4,  */ "fcfb4" "\0"
+	/* ARPHRD_FCFABRIC+5,  */ "fcfb5" "\0"
+	/* ARPHRD_FCFABRIC+6,  */ "fcfb6" "\0"
+	/* ARPHRD_FCFABRIC+7,  */ "fcfb7" "\0"
+	/* ARPHRD_FCFABRIC+8,  */ "fcfb8" "\0"
+	/* ARPHRD_FCFABRIC+9,  */ "fcfb9" "\0"
+	/* ARPHRD_FCFABRIC+10, */ "fcfb10" "\0"
+	/* ARPHRD_FCFABRIC+11, */ "fcfb11" "\0"
+	/* ARPHRD_FCFABRIC+12, */ "fcfb12" "\0"
+#endif /* FEATURE_IP_RARE_PROTOCOLS */
+	;
+
+	/* Keep these arrays in sync! */
+
+	static const uint16_t arphrd_type[] = {
+	0,                  /* "generic" "\0" */
+	ARPHRD_LOOPBACK,    /* "loopback" "\0" */
+	ARPHRD_ETHER,       /* "ether" "\0" */
+#ifdef ARPHRD_INFINIBAND
+	ARPHRD_INFINIBAND,  /* "infiniband" "\0" */
+#endif
+#ifdef ARPHRD_IEEE802_TR
+	ARPHRD_IEEE802,     /* "ieee802" "\0" */
+	ARPHRD_IEEE802_TR,  /* "tr" "\0" */
+#else
+	ARPHRD_IEEE802,     /* "tr" "\0" */
+#endif
+#ifdef ARPHRD_IEEE80211
+	ARPHRD_IEEE80211,   /* "ieee802.11" "\0" */
+#endif
+#ifdef ARPHRD_IEEE1394
+	ARPHRD_IEEE1394,    /* "ieee1394" "\0" */
+#endif
+	ARPHRD_IRDA,        /* "irda" "\0" */
+	ARPHRD_SLIP,        /* "slip" "\0" */
+	ARPHRD_CSLIP,       /* "cslip" "\0" */
+	ARPHRD_SLIP6,       /* "slip6" "\0" */
+	ARPHRD_CSLIP6,      /* "cslip6" "\0" */
+	ARPHRD_PPP,         /* "ppp" "\0" */
+	ARPHRD_TUNNEL,      /* "ipip" "\0" */
+	ARPHRD_TUNNEL6,     /* "tunnel6" "\0" */
+	ARPHRD_SIT,         /* "sit" "\0" */
+	ARPHRD_IPGRE,       /* "gre" "\0" */
+#ifdef ARPHRD_VOID
+	ARPHRD_VOID,        /* "void" "\0" */
+#endif
+
+#if ENABLE_FEATURE_IP_RARE_PROTOCOLS
+	ARPHRD_EETHER,      /* "eether" "\0" */
+	ARPHRD_AX25,        /* "ax25" "\0" */
+	ARPHRD_PRONET,      /* "pronet" "\0" */
+	ARPHRD_CHAOS,       /* "chaos" "\0" */
+	ARPHRD_ARCNET,      /* "arcnet" "\0" */
+	ARPHRD_APPLETLK,    /* "atalk" "\0" */
+	ARPHRD_DLCI,        /* "dlci" "\0" */
+#ifdef ARPHRD_ATM
+	ARPHRD_ATM,         /* "atm" "\0" */
+#endif
+	ARPHRD_METRICOM,    /* "metricom" "\0" */
+	ARPHRD_RSRVD,       /* "rsrvd" "\0" */
+	ARPHRD_ADAPT,       /* "adapt" "\0" */
+	ARPHRD_ROSE,        /* "rose" "\0" */
+	ARPHRD_X25,         /* "x25" "\0" */
+#ifdef ARPHRD_HWX25
+	ARPHRD_HWX25,       /* "hwx25" "\0" */
+#endif
+	ARPHRD_HDLC,        /* "hdlc" "\0" */
+	ARPHRD_LAPB,        /* "lapb" "\0" */
+#ifdef ARPHRD_DDCMP
+	ARPHRD_DDCMP,       /* "ddcmp" "\0" */
+	ARPHRD_RAWHDLC,     /* "rawhdlc" "\0" */
+#endif
+	ARPHRD_FRAD,        /* "frad" "\0" */
+	ARPHRD_SKIP,        /* "skip" "\0" */
+	ARPHRD_LOCALTLK,    /* "ltalk" "\0" */
+	ARPHRD_FDDI,        /* "fddi" "\0" */
+	ARPHRD_BIF,         /* "bif" "\0" */
+	ARPHRD_IPDDP,       /* "ip/ddp" "\0" */
+	ARPHRD_PIMREG,      /* "pimreg" "\0" */
+	ARPHRD_HIPPI,       /* "hippi" "\0" */
+	ARPHRD_ASH,         /* "ash" "\0" */
+	ARPHRD_ECONET,      /* "econet" "\0" */
+	ARPHRD_FCPP,        /* "fcpp" "\0" */
+	ARPHRD_FCAL,        /* "fcal" "\0" */
+	ARPHRD_FCPL,        /* "fcpl" "\0" */
+	ARPHRD_FCFABRIC,    /* "fcfb0" "\0" */
+	ARPHRD_FCFABRIC+1,  /* "fcfb1" "\0" */
+	ARPHRD_FCFABRIC+2,  /* "fcfb2" "\0" */
+	ARPHRD_FCFABRIC+3,  /* "fcfb3" "\0" */
+	ARPHRD_FCFABRIC+4,  /* "fcfb4" "\0" */
+	ARPHRD_FCFABRIC+5,  /* "fcfb5" "\0" */
+	ARPHRD_FCFABRIC+6,  /* "fcfb6" "\0" */
+	ARPHRD_FCFABRIC+7,  /* "fcfb7" "\0" */
+	ARPHRD_FCFABRIC+8,  /* "fcfb8" "\0" */
+	ARPHRD_FCFABRIC+9,  /* "fcfb9" "\0" */
+	ARPHRD_FCFABRIC+10, /* "fcfb10" "\0" */
+	ARPHRD_FCFABRIC+11, /* "fcfb11" "\0" */
+	ARPHRD_FCFABRIC+12, /* "fcfb12" "\0" */
+#endif /* FEATURE_IP_RARE_PROTOCOLS */
+	};
+
+	unsigned i;
+	const char *aname = arphrd_name;
+	for (i = 0; i < ARRAY_SIZE(arphrd_type); i++) {
+		if (arphrd_type[i] == type)
+			return aname;
+		aname += strlen(aname) + 1;
+	}
+	sprintf(buf, "[%d]", type);
+	return buf;
+}
diff --git a/busybox-1.19.3/networking/libiproute/rt_names.c b/busybox-1.19.3/networking/libiproute/rt_names.c
new file mode 100644
index 0000000..c474ab9
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/rt_names.c
@@ -0,0 +1,256 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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"
+#include "rt_names.h"
+
+typedef struct rtnl_tab_t {
+	const char *cached_str;
+	unsigned cached_result;
+	const char *tab[256];
+} rtnl_tab_t;
+
+static void rtnl_tab_initialize(const char *file, const char **tab)
+{
+	char *token[2];
+	parser_t *parser = config_open2(file, fopen_for_read);
+
+	while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+		unsigned id = bb_strtou(token[0], NULL, 0);
+		if (id > 256) {
+			bb_error_msg("database %s is corrupted at line %d",
+				file, parser->lineno);
+			break;
+		}
+		tab[id] = xstrdup(token[1]);
+	}
+	config_close(parser);
+}
+
+static int rtnl_a2n(rtnl_tab_t *tab, uint32_t *id, const char *arg, int base)
+{
+	unsigned i;
+
+	if (tab->cached_str && strcmp(tab->cached_str, arg) == 0) {
+		*id = tab->cached_result;
+		return 0;
+	}
+
+	for (i = 0; i < 256; i++) {
+		if (tab->tab[i]
+		 && strcmp(tab->tab[i], arg) == 0
+		) {
+			tab->cached_str = tab->tab[i];
+			tab->cached_result = i;
+			*id = i;
+			return 0;
+		}
+	}
+
+	i = bb_strtou(arg, NULL, base);
+	if (i > 255)
+		return -1;
+	*id = i;
+	return 0;
+}
+
+
+static rtnl_tab_t *rtnl_rtprot_tab;
+
+static void rtnl_rtprot_initialize(void)
+{
+	static const char *const init_tab[] = {
+		"none",
+		"redirect",
+		"kernel",
+		"boot",
+		"static",
+		NULL,
+		NULL,
+		NULL,
+		"gated",
+		"ra",
+		"mrt",
+		"zebra",
+		"bird",
+	};
+
+	if (rtnl_rtprot_tab)
+		return;
+	rtnl_rtprot_tab = xzalloc(sizeof(*rtnl_rtprot_tab));
+	memcpy(rtnl_rtprot_tab->tab, init_tab, sizeof(init_tab));
+	rtnl_tab_initialize("/etc/iproute2/rt_protos", rtnl_rtprot_tab->tab);
+}
+
+const char* FAST_FUNC rtnl_rtprot_n2a(int id, char *buf)
+{
+	if (id < 0 || id >= 256) {
+		sprintf(buf, "%d", id);
+		return buf;
+	}
+
+	rtnl_rtprot_initialize();
+
+	if (rtnl_rtprot_tab->tab[id])
+		return rtnl_rtprot_tab->tab[id];
+	/* buf is SPRINT_BSIZE big */
+	sprintf(buf, "%d", id);
+	return buf;
+}
+
+int FAST_FUNC rtnl_rtprot_a2n(uint32_t *id, char *arg)
+{
+	rtnl_rtprot_initialize();
+	return rtnl_a2n(rtnl_rtprot_tab, id, arg, 0);
+}
+
+
+static rtnl_tab_t *rtnl_rtscope_tab;
+
+static void rtnl_rtscope_initialize(void)
+{
+	if (rtnl_rtscope_tab)
+		return;
+	rtnl_rtscope_tab = xzalloc(sizeof(*rtnl_rtscope_tab));
+	rtnl_rtscope_tab->tab[0] = "global";
+	rtnl_rtscope_tab->tab[255] = "nowhere";
+	rtnl_rtscope_tab->tab[254] = "host";
+	rtnl_rtscope_tab->tab[253] = "link";
+	rtnl_rtscope_tab->tab[200] = "site";
+	rtnl_tab_initialize("/etc/iproute2/rt_scopes", rtnl_rtscope_tab->tab);
+}
+
+const char* FAST_FUNC rtnl_rtscope_n2a(int id, char *buf)
+{
+	if (id < 0 || id >= 256) {
+		sprintf(buf, "%d", id);
+		return buf;
+	}
+
+	rtnl_rtscope_initialize();
+
+	if (rtnl_rtscope_tab->tab[id])
+		return rtnl_rtscope_tab->tab[id];
+	/* buf is SPRINT_BSIZE big */
+	sprintf(buf, "%d", id);
+	return buf;
+}
+
+int FAST_FUNC rtnl_rtscope_a2n(uint32_t *id, char *arg)
+{
+	rtnl_rtscope_initialize();
+	return rtnl_a2n(rtnl_rtscope_tab, id, arg, 0);
+}
+
+
+static rtnl_tab_t *rtnl_rtrealm_tab;
+
+static void rtnl_rtrealm_initialize(void)
+{
+	if (rtnl_rtrealm_tab) return;
+	rtnl_rtrealm_tab = xzalloc(sizeof(*rtnl_rtrealm_tab));
+	rtnl_rtrealm_tab->tab[0] = "unknown";
+	rtnl_tab_initialize("/etc/iproute2/rt_realms", rtnl_rtrealm_tab->tab);
+}
+
+int FAST_FUNC rtnl_rtrealm_a2n(uint32_t *id, char *arg)
+{
+	rtnl_rtrealm_initialize();
+	return rtnl_a2n(rtnl_rtrealm_tab, id, arg, 0);
+}
+
+#if ENABLE_FEATURE_IP_RULE
+const char* FAST_FUNC rtnl_rtrealm_n2a(int id, char *buf)
+{
+	if (id < 0 || id >= 256) {
+		sprintf(buf, "%d", id);
+		return buf;
+	}
+
+	rtnl_rtrealm_initialize();
+
+	if (rtnl_rtrealm_tab->tab[id])
+		return rtnl_rtrealm_tab->tab[id];
+	/* buf is SPRINT_BSIZE big */
+	sprintf(buf, "%d", id);
+	return buf;
+}
+#endif
+
+
+static rtnl_tab_t *rtnl_rtdsfield_tab;
+
+static void rtnl_rtdsfield_initialize(void)
+{
+	if (rtnl_rtdsfield_tab) return;
+	rtnl_rtdsfield_tab = xzalloc(sizeof(*rtnl_rtdsfield_tab));
+	rtnl_rtdsfield_tab->tab[0] = "0";
+	rtnl_tab_initialize("/etc/iproute2/rt_dsfield", rtnl_rtdsfield_tab->tab);
+}
+
+const char* FAST_FUNC rtnl_dsfield_n2a(int id, char *buf)
+{
+	if (id < 0 || id >= 256) {
+		sprintf(buf, "%d", id);
+		return buf;
+	}
+
+	rtnl_rtdsfield_initialize();
+
+	if (rtnl_rtdsfield_tab->tab[id])
+		return rtnl_rtdsfield_tab->tab[id];
+	/* buf is SPRINT_BSIZE big */
+	sprintf(buf, "0x%02x", id);
+	return buf;
+}
+
+int FAST_FUNC rtnl_dsfield_a2n(uint32_t *id, char *arg)
+{
+	rtnl_rtdsfield_initialize();
+	return rtnl_a2n(rtnl_rtdsfield_tab, id, arg, 16);
+}
+
+
+#if ENABLE_FEATURE_IP_RULE
+static rtnl_tab_t *rtnl_rttable_tab;
+
+static void rtnl_rttable_initialize(void)
+{
+	if (rtnl_rtdsfield_tab) return;
+	rtnl_rttable_tab = xzalloc(sizeof(*rtnl_rttable_tab));
+	rtnl_rttable_tab->tab[0] = "unspec";
+	rtnl_rttable_tab->tab[255] = "local";
+	rtnl_rttable_tab->tab[254] = "main";
+	rtnl_rttable_tab->tab[253] = "default";
+	rtnl_tab_initialize("/etc/iproute2/rt_tables", rtnl_rttable_tab->tab);
+}
+
+const char* FAST_FUNC rtnl_rttable_n2a(int id, char *buf)
+{
+	if (id < 0 || id >= 256) {
+		sprintf(buf, "%d", id);
+		return buf;
+	}
+
+	rtnl_rttable_initialize();
+
+	if (rtnl_rttable_tab->tab[id])
+		return rtnl_rttable_tab->tab[id];
+	/* buf is SPRINT_BSIZE big */
+	sprintf(buf, "%d", id);
+	return buf;
+}
+
+int FAST_FUNC rtnl_rttable_a2n(uint32_t *id, char *arg)
+{
+	rtnl_rttable_initialize();
+	return rtnl_a2n(rtnl_rttable_tab, id, arg, 0);
+}
+
+#endif
diff --git a/busybox-1.19.3/networking/libiproute/rt_names.h b/busybox-1.19.3/networking/libiproute/rt_names.h
new file mode 100644
index 0000000..e73aa85
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/rt_names.h
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+#ifndef RT_NAMES_H
+#define RT_NAMES_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* buf is SPRINT_BSIZE big */
+extern const char* rtnl_rtprot_n2a(int id, char *buf) FAST_FUNC;
+extern const char* rtnl_rtscope_n2a(int id, char *buf) FAST_FUNC;
+extern const char* rtnl_rtrealm_n2a(int id, char *buf) FAST_FUNC;
+extern const char* rtnl_dsfield_n2a(int id, char *buf) FAST_FUNC;
+extern const char* rtnl_rttable_n2a(int id, char *buf) FAST_FUNC;
+extern int rtnl_rtprot_a2n(uint32_t *id, char *arg) FAST_FUNC;
+extern int rtnl_rtscope_a2n(uint32_t *id, char *arg) FAST_FUNC;
+extern int rtnl_rtrealm_a2n(uint32_t *id, char *arg) FAST_FUNC;
+extern int rtnl_dsfield_a2n(uint32_t *id, char *arg) FAST_FUNC;
+extern int rtnl_rttable_a2n(uint32_t *id, char *arg) FAST_FUNC;
+
+extern const char* ll_type_n2a(int type, char *buf) FAST_FUNC;
+
+extern const char* ll_addr_n2a(unsigned char *addr, int alen, int type,
+				char *buf, int blen) FAST_FUNC;
+extern int ll_addr_a2n(unsigned char *lladdr, int len, char *arg) FAST_FUNC;
+
+extern const char* ll_proto_n2a(unsigned short id, char *buf, int len) FAST_FUNC;
+extern int ll_proto_a2n(unsigned short *id, char *buf) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/libiproute/rtm_map.c b/busybox-1.19.3/networking/libiproute/rtm_map.c
new file mode 100644
index 0000000..3bab53b
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/rtm_map.c
@@ -0,0 +1,116 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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"
+#include "rt_names.h"
+#include "utils.h"
+
+const char* FAST_FUNC rtnl_rtntype_n2a(int id, char *buf)
+{
+	switch (id) {
+	case RTN_UNSPEC:
+		return "none";
+	case RTN_UNICAST:
+		return "unicast";
+	case RTN_LOCAL:
+		return "local";
+	case RTN_BROADCAST:
+		return "broadcast";
+	case RTN_ANYCAST:
+		return "anycast";
+	case RTN_MULTICAST:
+		return "multicast";
+	case RTN_BLACKHOLE:
+		return "blackhole";
+	case RTN_UNREACHABLE:
+		return "unreachable";
+	case RTN_PROHIBIT:
+		return "prohibit";
+	case RTN_THROW:
+		return "throw";
+	case RTN_NAT:
+		return "nat";
+	case RTN_XRESOLVE:
+		return "xresolve";
+	default:
+		/* buf is SPRINT_BSIZE big */
+		sprintf(buf, "%d", id);
+		return buf;
+	}
+}
+
+
+int FAST_FUNC rtnl_rtntype_a2n(int *id, char *arg)
+{
+	static const char keywords[] ALIGN1 =
+		"local\0""nat\0""broadcast\0""brd\0""anycast\0"
+		"multicast\0""prohibit\0""unreachable\0""blackhole\0"
+		"xresolve\0""unicast\0""throw\0";
+	enum {
+		ARG_local = 1, ARG_nat, ARG_broadcast, ARG_brd, ARG_anycast,
+		ARG_multicast, ARG_prohibit, ARG_unreachable, ARG_blackhole,
+		ARG_xresolve, ARG_unicast, ARG_throw
+	};
+	const smalluint key = index_in_substrings(keywords, arg) + 1;
+	char *end;
+	unsigned long res;
+
+	if (key == ARG_local)
+		res = RTN_LOCAL;
+	else if (key == ARG_nat)
+		res = RTN_NAT;
+	else if (key == ARG_broadcast || key == ARG_brd)
+		res = RTN_BROADCAST;
+	else if (key == ARG_anycast)
+		res = RTN_ANYCAST;
+	else if (key == ARG_multicast)
+		res = RTN_MULTICAST;
+	else if (key == ARG_prohibit)
+		res = RTN_PROHIBIT;
+	else if (key == ARG_unreachable)
+		res = RTN_UNREACHABLE;
+	else if (key == ARG_blackhole)
+		res = RTN_BLACKHOLE;
+	else if (key == ARG_xresolve)
+		res = RTN_XRESOLVE;
+	else if (key == ARG_unicast)
+		res = RTN_UNICAST;
+	else if (key == ARG_throw)
+		res = RTN_THROW;
+	else {
+		res = strtoul(arg, &end, 0);
+		if (end == arg || *end || res > 255)
+			return -1;
+	}
+	*id = res;
+	return 0;
+}
+
+int FAST_FUNC get_rt_realms(uint32_t *realms, char *arg)
+{
+	uint32_t realm = 0;
+	char *p = strchr(arg, '/');
+
+	*realms = 0;
+	if (p) {
+		*p = 0;
+		if (rtnl_rtrealm_a2n(realms, arg)) {
+			*p = '/';
+			return -1;
+		}
+		*realms <<= 16;
+		*p = '/';
+		arg = p+1;
+	}
+	if (*arg && rtnl_rtrealm_a2n(&realm, arg))
+		return -1;
+	*realms |= realm;
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/libiproute/rtm_map.h b/busybox-1.19.3/networking/libiproute/rtm_map.h
new file mode 100644
index 0000000..4377bd5
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/rtm_map.h
@@ -0,0 +1,14 @@
+/* vi: set sw=4 ts=4: */
+#ifndef RTM_MAP_H
+#define RTM_MAP_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+const char *rtnl_rtntype_n2a(int id, char *buf) FAST_FUNC;
+int rtnl_rtntype_a2n(int *id, char *arg) FAST_FUNC;
+
+int get_rt_realms(uint32_t *realms, char *arg) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/libiproute/utils.c b/busybox-1.19.3/networking/libiproute/utils.c
new file mode 100644
index 0000000..d0fe306
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/utils.c
@@ -0,0 +1,296 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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
+ */
+
+#include "libbb.h"
+#include "utils.h"
+#include "inet_common.h"
+
+unsigned get_unsigned(char *arg, const char *errmsg)
+{
+	unsigned long res;
+	char *ptr;
+
+	if (*arg) {
+		res = strtoul(arg, &ptr, 0);
+//FIXME: "" will be accepted too, is it correct?!
+		if (!*ptr && res <= UINT_MAX) {
+			return res;
+		}
+	}
+	invarg(arg, errmsg); /* does not return */
+}
+
+uint32_t get_u32(char *arg, const char *errmsg)
+{
+	unsigned long res;
+	char *ptr;
+
+	if (*arg) {
+		res = strtoul(arg, &ptr, 0);
+//FIXME: "" will be accepted too, is it correct?!
+		if (!*ptr && res <= 0xFFFFFFFFUL) {
+			return res;
+		}
+	}
+	invarg(arg, errmsg); /* does not return */
+}
+
+uint16_t get_u16(char *arg, const char *errmsg)
+{
+	unsigned long res;
+	char *ptr;
+
+	if (*arg) {
+		res = strtoul(arg, &ptr, 0);
+//FIXME: "" will be accepted too, is it correct?!
+		if (!*ptr && res <= 0xFFFF) {
+			return res;
+		}
+	}
+	invarg(arg, errmsg); /* does not return */
+}
+
+int get_addr_1(inet_prefix *addr, char *name, int family)
+{
+	memset(addr, 0, sizeof(*addr));
+
+	if (strcmp(name, "default") == 0
+	 || strcmp(name, "all") == 0
+	 || strcmp(name, "any") == 0
+	) {
+		addr->family = family;
+		addr->bytelen = (family == AF_INET6 ? 16 : 4);
+		addr->bitlen = -1;
+		return 0;
+	}
+
+	if (strchr(name, ':')) {
+		addr->family = AF_INET6;
+		if (family != AF_UNSPEC && family != AF_INET6)
+			return -1;
+		if (inet_pton(AF_INET6, name, addr->data) <= 0)
+			return -1;
+		addr->bytelen = 16;
+		addr->bitlen = -1;
+		return 0;
+	}
+
+	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 void get_prefix_1(inet_prefix *dst, char *arg, int family)
+{
+	char *slash;
+
+	memset(dst, 0, sizeof(*dst));
+
+	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;
+	}
+
+	slash = strchr(arg, '/');
+	if (slash)
+		*slash = '\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) != 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))
+					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;
+ 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)
+{
+	if (family == AF_PACKET) {
+		bb_error_msg_and_die("\"%s\" may be inet %s, but it is not allowed in this context", arg, "address");
+	}
+	if (get_addr_1(dst, arg, family)) {
+		bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "inet", "address", arg);
+	}
+	return 0;
+}
+
+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");
+	}
+	get_prefix_1(dst, arg, family);
+}
+
+uint32_t get_addr32(char *name)
+{
+	inet_prefix addr;
+
+	if (get_addr_1(&addr, name, AF_INET)) {
+		bb_error_msg_and_die("an %s %s is expected rather than \"%s\"", "IP", "address", name);
+	}
+	return addr.data[0];
+}
+
+void incomplete_command(void)
+{
+	bb_error_msg_and_die("command line is not complete, try option \"help\"");
+}
+
+void invarg(const char *arg, const char *opt)
+{
+	bb_error_msg_and_die(bb_msg_invalid_arg, arg, opt);
+}
+
+void duparg(const char *key, const char *arg)
+{
+	bb_error_msg_and_die("duplicate \"%s\": \"%s\" is the second value", key, arg);
+}
+
+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(const inet_prefix *a, const inet_prefix *b, int bits)
+{
+	const uint32_t *a1 = a->data;
+	const uint32_t *a2 = b->data;
+	int words = bits >> 5;
+
+	bits &= 0x1f;
+
+	if (words)
+		if (memcmp(a1, a2, words << 2))
+			return -1;
+
+	if (bits) {
+		uint32_t w1, w2;
+		uint32_t mask;
+
+		w1 = a1[words];
+		w2 = a2[words];
+
+		mask = htonl((0xffffffff) << (0x20 - bits));
+
+		if ((w1 ^ w2) & mask)
+			return 1;
+	}
+
+	return 0;
+}
+
+const char *rt_addr_n2a(int af,
+		void *addr, char *buf, int buflen)
+{
+	switch (af) {
+	case AF_INET:
+	case AF_INET6:
+		return inet_ntop(af, addr, buf, buflen);
+	default:
+		return "???";
+	}
+}
+
+#ifdef RESOLVE_HOSTNAMES
+const char *format_host(int af, int len, void *addr, char *buf, int buflen)
+{
+	if (resolve_hosts) {
+		struct hostent *h_ent;
+
+		if (len <= 0) {
+			switch (af) {
+			case AF_INET:
+				len = 4;
+				break;
+			case AF_INET6:
+				len = 16;
+				break;
+			default:;
+			}
+		}
+		if (len > 0) {
+			h_ent = gethostbyaddr(addr, len, af);
+			if (h_ent != NULL) {
+				safe_strncpy(buf, h_ent->h_name, buflen);
+				return buf;
+			}
+		}
+	}
+	return rt_addr_n2a(af, addr, buf, buflen);
+}
+#endif
diff --git a/busybox-1.19.3/networking/libiproute/utils.h b/busybox-1.19.3/networking/libiproute/utils.h
new file mode 100644
index 0000000..5fb4a86
--- /dev/null
+++ b/busybox-1.19.3/networking/libiproute/utils.h
@@ -0,0 +1,90 @@
+/* vi: set sw=4 ts=4: */
+#ifndef UTILS_H
+#define UTILS_H 1
+
+#include "libnetlink.h"
+#include "ll_map.h"
+#include "rtm_map.h"
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+extern family_t preferred_family;
+extern smallint show_stats;    /* UNUSED */
+extern smallint show_details;  /* UNUSED */
+extern smallint show_raw;      /* UNUSED */
+extern smallint resolve_hosts; /* UNUSED */
+extern smallint oneline;
+extern char _SL_;
+
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP  50
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH  51
+#endif
+
+#define SPRINT_BSIZE 64
+#define SPRINT_BUF(x)  char x[SPRINT_BSIZE]
+
+extern void incomplete_command(void) NORETURN;
+
+#define NEXT_ARG() do { if (!*++argv) incomplete_command(); } while (0)
+
+typedef struct {
+	uint8_t family;
+	uint8_t bytelen;
+	int16_t bitlen;
+	uint32_t data[4];
+} inet_prefix;
+
+#define PREFIXLEN_SPECIFIED 1
+
+#define DN_MAXADDL 20
+#ifndef AF_DECnet
+#define AF_DECnet 12
+#endif
+
+struct dn_naddr {
+	unsigned short a_len;
+	unsigned char  a_addr[DN_MAXADDL];
+};
+
+#define IPX_NODE_LEN 6
+
+struct ipx_addr {
+	uint32_t ipx_net;
+	uint8_t  ipx_node[IPX_NODE_LEN];
+};
+
+extern uint32_t get_addr32(char *name);
+extern int get_addr_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 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);
+extern uint16_t get_u16(char *arg, const char *errmsg);
+
+extern const char *rt_addr_n2a(int af, void *addr, char *buf, int buflen);
+#ifdef RESOLVE_HOSTNAMES
+extern const char *format_host(int af, int len, void *addr, char *buf, int buflen);
+#else
+#define format_host(af, len, addr, buf, buflen) \
+	rt_addr_n2a(af, addr, buf, buflen)
+#endif
+
+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(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);
+
+const char *ipx_ntop(int af, const void *addr, char *str, size_t len);
+int ipx_pton(int af, const char *src, void *addr);
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/nameif.c b/busybox-1.19.3/networking/nameif.c
new file mode 100644
index 0000000..5d7e8f9
--- /dev/null
+++ b/busybox-1.19.3/networking/nameif.c
@@ -0,0 +1,324 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * nameif.c - Naming Interfaces based on MAC address for busybox.
+ *
+ * Written 2000 by Andi Kleen.
+ * Busybox port 2002 by Nick Fedchik <nick@fedchik.org.ua>
+ *			Glenn McGrath
+ * Extended matching support 2008 by Nico Erfurth <masta@perlgolf.de>
+ *
+ * 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>
+#include <netinet/ether.h>
+#include <linux/sockios.h>
+
+#ifndef IFNAMSIZ
+#define IFNAMSIZ 16
+#endif
+
+/* Taken from linux/sockios.h */
+#define SIOCSIFNAME  0x8923  /* set interface name */
+
+/* Octets in one Ethernet addr, from <linux/if_ether.h> */
+#define ETH_ALEN     6
+
+#ifndef ifr_newname
+#define ifr_newname ifr_ifru.ifru_slave
+#endif
+
+typedef struct ethtable_s {
+	struct ethtable_s *next;
+	struct ethtable_s *prev;
+	char *ifname;
+	struct ether_addr *mac;
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+	char *bus_info;
+	char *driver;
+	int32_t phy_address;
+#endif
+} ethtable_t;
+
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+/* Cut'n'paste from ethtool.h */
+#define ETHTOOL_BUSINFO_LEN 32
+/* these strings are set to whatever the driver author decides... */
+struct ethtool_drvinfo {
+	uint32_t cmd;
+	char  driver[32]; /* driver short name, "tulip", "eepro100" */
+	char  version[32];  /* driver version string */
+	char  fw_version[32]; /* firmware version string, if applicable */
+	char  bus_info[ETHTOOL_BUSINFO_LEN];  /* Bus info for this IF. */
+	/* For PCI devices, use pci_dev->slot_name. */
+	char  reserved1[32];
+	char  reserved2[16];
+	uint32_t n_stats;  /* number of u64's from ETHTOOL_GSTATS */
+	uint32_t testinfo_len;
+	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
+
+
+static void nameif_parse_selector(ethtable_t *ch, char *selector)
+{
+	struct ether_addr *lmac;
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+	int found_selector = 0;
+
+	while (*selector) {
+		char *next;
+#endif
+		selector = skip_whitespace(selector);
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+		ch->phy_address = -1;
+		if (*selector == '\0')
+			break;
+		/* Search for the end .... */
+		next = skip_non_whitespace(selector);
+		if (*next)
+			*next++ = '\0';
+		/* Check for selectors, mac= is assumed */
+		if (strncmp(selector, "bus=", 4) == 0) {
+			ch->bus_info = xstrdup(selector + 4);
+			found_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);
+			ch->mac = ether_aton_r(selector + (strncmp(selector, "mac=", 4) != 0 ? 0 : 4), lmac);
+			if (ch->mac == NULL)
+				bb_error_msg_and_die("can't parse %s", selector);
+#if  ENABLE_FEATURE_NAMEIF_EXTENDED
+			found_selector++;
+		};
+		selector = next;
+	}
+	if (found_selector == 0)
+		bb_error_msg_and_die("no selectors found for %s", ch->ifname);
+#endif
+}
+
+static void prepend_new_eth_table(ethtable_t **clist, char *ifname, char *selector)
+{
+	ethtable_t *ch;
+	if (strlen(ifname) >= IFNAMSIZ)
+		bb_error_msg_and_die("interface name '%s' too long", ifname);
+	ch = xzalloc(sizeof(*ch));
+	ch->ifname = xstrdup(ifname);
+	nameif_parse_selector(ch, selector);
+	ch->next = *clist;
+	if (*clist)
+		(*clist)->prev = ch;
+	*clist = ch;
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void delete_eth_table(ethtable_t *ch)
+{
+	free(ch->ifname);
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+	free(ch->bus_info);
+	free(ch->driver);
+#endif
+	free(ch->mac);
+	free(ch);
+};
+#else
+void delete_eth_table(ethtable_t *ch);
+#endif
+
+int nameif_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nameif_main(int argc UNUSED_PARAM, char **argv)
+{
+	ethtable_t *clist = NULL;
+	const char *fname = "/etc/mactab";
+	int ctl_sk;
+	ethtable_t *ch;
+	parser_t *parser;
+	char *token[2];
+
+	if (1 & getopt32(argv, "sc:", &fname)) {
+		openlog(applet_name, 0, LOG_LOCAL0);
+		/* Why not just "="? I assume logging to stderr
+		 * can't hurt. 2>/dev/null if you don't like it: */
+		logmode |= LOGMODE_SYSLOG;
+	}
+	argv += optind;
+
+	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))
+			prepend_new_eth_table(&clist, token[0], token[1]);
+		config_close(parser);
+	}
+
+	ctl_sk = xsocket(PF_INET, SOCK_DGRAM, 0);
+	parser = config_open2("/proc/net/dev", xfopen_for_read);
+
+	while (clist && config_read(parser, token, 2, 2, "\0: \t", PARSE_NORMAL)) {
+		struct ifreq ifr;
+#if  ENABLE_FEATURE_NAMEIF_EXTENDED
+		struct ethtool_drvinfo drvinfo;
+		struct ethtool_cmd eth_settings;
+#endif
+		if (parser->lineno <= 2)
+			continue; /* Skip the first two lines */
+
+		/* Find the current interface name and copy it to ifr.ifr_name */
+		memset(&ifr, 0, sizeof(struct ifreq));
+		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(drvinfo));
+		drvinfo.cmd = ETHTOOL_GDRVINFO;
+		ifr.ifr_data = (caddr_t) &drvinfo;
+		/* Get driver and businfo first, so we have it in drvinfo */
+		ioctl(ctl_sk, SIOCETHTOOL, &ifr);
+#endif
+		ioctl(ctl_sk, SIOCGIFHWADDR, &ifr);
+
+		/* Search the list for a matching device */
+		for (ch = clist; ch; ch = ch->next) {
+#if ENABLE_FEATURE_NAMEIF_EXTENDED
+			if (ch->bus_info && strcmp(ch->bus_info, drvinfo.bus_info) != 0)
+				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;
+		}
+		/* Nothing found for current interface */
+		if (!ch)
+			continue;
+
+		if (strcmp(ifr.ifr_name, ch->ifname) != 0) {
+			strcpy(ifr.ifr_newname, ch->ifname);
+			ioctl_or_perror_and_die(ctl_sk, SIOCSIFNAME, &ifr,
+					"can't change ifname %s to %s",
+					ifr.ifr_name, ch->ifname);
+		}
+		/* Remove list entry of renamed interface */
+		if (ch->prev != NULL)
+			ch->prev->next = ch->next;
+		else
+			clist = ch->next;
+		if (ch->next != NULL)
+			ch->next->prev = ch->prev;
+		if (ENABLE_FEATURE_CLEAN_UP)
+			delete_eth_table(ch);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		for (ch = clist; ch; ch = ch->next)
+			delete_eth_table(ch);
+		config_close(parser);
+	};
+
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/nbd-client.c b/busybox-1.19.3/networking/nbd-client.c
new file mode 100644
index 0000000..cadda52
--- /dev/null
+++ b/busybox-1.19.3/networking/nbd-client.c
@@ -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;
+}
diff --git a/busybox-1.19.3/networking/nc.c b/busybox-1.19.3/networking/nc.c
new file mode 100644
index 0000000..1b32e3a
--- /dev/null
+++ b/busybox-1.19.3/networking/nc.c
@@ -0,0 +1,279 @@
+/* vi: set sw=4 ts=4: */
+/* nc: mini-netcat - built from the ground up for LRP
+ *
+ * Copyright (C) 1998, 1999  Charles P. Wright
+ * Copyright (C) 1998  Dave Cinege
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+
+//config:config NC
+//config:	bool "nc"
+//config:	default y
+//config:	help
+//config:	  A simple Unix utility which reads and writes data across network
+//config:	  connections.
+//config:
+//config:config NC_SERVER
+//config:	bool "Netcat server options (-l)"
+//config:	default y
+//config:	depends on NC
+//config:	help
+//config:	  Allow netcat to act as a server.
+//config:
+//config:config NC_EXTRA
+//config:	bool "Netcat extensions (-eiw and filename)"
+//config:	default y
+//config:	depends on NC
+//config:	help
+//config:	  Add -e (support for executing the rest of the command line after
+//config:	  making or receiving a successful connection), -i (delay interval for
+//config:	  lines sent), -w (timeout for initial connection).
+//config:
+//config:config NC_110_COMPAT
+//config:	bool "Netcat 1.10 compatibility (+2.5k)"
+//config:	default n  # off specially for Rob
+//config:	depends on NC
+//config:	help
+//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.
+
+#if ENABLE_NC_110_COMPAT
+# include "nc_bloaty.c"
+#else
+
+//usage:#if !ENABLE_NC_110_COMPAT
+//usage:
+//usage:#if ENABLE_NC_SERVER || ENABLE_NC_EXTRA
+//usage:#define NC_OPTIONS_STR "\n"
+//usage:#else
+//usage:#define NC_OPTIONS_STR
+//usage:#endif
+//usage:
+//usage:#define nc_trivial_usage
+//usage:	IF_NC_EXTRA("[-iN] [-wN] ")IF_NC_SERVER("[-l] [-p PORT] ")
+//usage:       "["IF_NC_EXTRA("-f FILE|")"IPADDR PORT]"IF_NC_EXTRA(" [-e PROG]")
+//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	-p PORT	Local port"
+//usage:	)
+//usage:     "\n	-w SEC	Timeout for connect"
+//usage:     "\n	-i SEC	Delay interval for lines sent"
+//usage:     "\n	-f FILE	Use file (ala /dev/ttyS0) instead of network"
+//usage:	)
+//usage:
+//usage:#define nc_notes_usage ""
+//usage:	IF_NC_EXTRA(
+//usage:       "To use netcat as a terminal emulator on a serial port:\n\n"
+//usage:       "$ stty 115200 -F /dev/ttyS0\n"
+//usage:       "$ stty raw -echo -ctlecho && nc -f /dev/ttyS0\n"
+//usage:	)
+//usage:
+//usage:#define nc_example_usage
+//usage:       "$ nc foobar.somedomain.com 25\n"
+//usage:       "220 foobar ESMTP Exim 3.12 #1 Sat, 15 Apr 2000 00:03:02 -0600\n"
+//usage:       "help\n"
+//usage:       "214-Commands supported:\n"
+//usage:       "214-    HELO EHLO MAIL RCPT DATA AUTH\n"
+//usage:       "214     NOOP QUIT RSET HELP\n"
+//usage:       "quit\n"
+//usage:       "221 foobar closing connection\n"
+//usage:
+//usage:#endif
+
+/* Lots of small differences in features
+ * when compared to "standard" nc
+ */
+
+static void timeout(int signum UNUSED_PARAM)
+{
+	bb_error_msg_and_die("timed out");
+}
+
+int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nc_main(int argc, char **argv)
+{
+	/* sfd sits _here_ only because of "repeat" option (-l -l). */
+	int sfd = sfd; /* for gcc */
+	int cfd = 0;
+	unsigned lport = 0;
+	IF_NOT_NC_SERVER(const) unsigned do_listen = 0;
+	IF_NOT_NC_EXTRA (const) unsigned wsecs = 0;
+	IF_NOT_NC_EXTRA (const) unsigned delay = 0;
+	IF_NOT_NC_EXTRA (const int execparam = 0;)
+	IF_NC_EXTRA     (char **execparam = NULL;)
+	fd_set readfds, testfds;
+	int opt; /* must be signed (getopt returns -1) */
+
+	if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
+		/* 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 (ENABLE_NC_SERVER && opt == 'l')
+				IF_NC_SERVER(do_listen++);
+			else if (ENABLE_NC_SERVER && opt == 'p')
+				IF_NC_SERVER(lport = bb_lookup_port(optarg, "tcp", 0));
+			else if (ENABLE_NC_EXTRA && opt == 'w')
+				IF_NC_EXTRA( wsecs = xatou(optarg));
+			else if (ENABLE_NC_EXTRA && opt == 'i')
+				IF_NC_EXTRA( delay = xatou(optarg));
+			else if (ENABLE_NC_EXTRA && opt == 'f')
+				IF_NC_EXTRA( cfd = xopen(optarg, O_RDWR));
+			else if (ENABLE_NC_EXTRA && opt == 'e' && optind <= argc) {
+				/* We cannot just 'break'. We should let getopt finish.
+				** Or else we won't be able to find where
+				** 'host' and 'port' params are
+				** (think "nc -w 60 host port -e PROG"). */
+				IF_NC_EXTRA(
+					char **p;
+					// +2: one for progname (optarg) and one for NULL
+					execparam = xzalloc(sizeof(char*) * (argc - optind + 2));
+					p = execparam;
+					*p++ = optarg;
+					while (optind < argc) {
+						*p++ = argv[optind++];
+					}
+				)
+				/* optind points to argv[arvc] (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
+				** which is to be used ...  */
+			} else bb_show_usage();
+		}
+		argv += optind; /* ... here! */
+		argc -= optind;
+		// -l and -f don't mix
+		if (do_listen && cfd) bb_show_usage();
+		// File mode needs need zero arguments, listen mode needs zero or one,
+		// client mode needs one or two
+		if (cfd) {
+			if (argc) bb_show_usage();
+		} else if (do_listen) {
+			if (argc > 1) bb_show_usage();
+		} else {
+			if (!argc || argc > 2) bb_show_usage();
+		}
+	} else {
+		if (argc != 3) bb_show_usage();
+		argc--;
+		argv++;
+	}
+
+	if (wsecs) {
+		signal(SIGALRM, timeout);
+		alarm(wsecs);
+	}
+
+	if (!cfd) {
+		if (do_listen) {
+			sfd = create_and_bind_stream_or_die(argv[0], lport);
+			xlisten(sfd, do_listen); /* can be > 1 */
+#if 0  /* nc-1.10 does not do this (without -v) */
+			/* If we didn't specify a port number,
+			 * query and print it after listen() */
+			if (!lport) {
+				len_and_sockaddr lsa;
+				lsa.len = LSA_SIZEOF_SA;
+				getsockname(sfd, &lsa.u.sa, &lsa.len);
+				lport = get_nport(&lsa.u.sa);
+				fdprintf(2, "%d\n", ntohs(lport));
+			}
+#endif
+			close_on_exec_on(sfd);
+ accept_again:
+			cfd = accept(sfd, NULL, 0);
+			if (cfd < 0)
+				bb_perror_msg_and_die("accept");
+			if (!execparam)
+				close(sfd);
+		} else {
+			cfd = create_and_connect_stream_or_die(argv[0],
+				argv[1] ? bb_lookup_port(argv[1], "tcp", 0) : 0);
+		}
+	}
+
+	if (wsecs) {
+		alarm(0);
+		/* Non-ignored signals revert to SIG_DFL on exec anyway */
+		/*signal(SIGALRM, SIG_DFL);*/
+	}
+
+	/* -e given? */
+	if (execparam) {
+		pid_t pid;
+		/* With more than one -l, repeatedly act as server */
+		if (do_listen > 1 && (pid = xvfork()) != 0) {
+			/* parent */
+			/* prevent zombies */
+			signal(SIGCHLD, SIG_IGN);
+			close(cfd);
+			goto accept_again;
+		}
+		/* child, or main thread if only one -l */
+		xmove_fd(cfd, 0);
+		xdup2(0, 1);
+		xdup2(0, 2);
+		IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
+		/* Don't print stuff or it will go over the wire... */
+		_exit(127);
+	}
+
+	/* Select loop copying stdin to cfd, and cfd to stdout */
+
+	FD_ZERO(&readfds);
+	FD_SET(cfd, &readfds);
+	FD_SET(STDIN_FILENO, &readfds);
+
+	for (;;) {
+		int fd;
+		int ofd;
+		int nread;
+
+		testfds = readfds;
+
+		if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0)
+			bb_perror_msg_and_die("select");
+
+#define iobuf bb_common_bufsiz1
+		fd = STDIN_FILENO;
+		while (1) {
+			if (FD_ISSET(fd, &testfds)) {
+				nread = safe_read(fd, iobuf, sizeof(iobuf));
+				if (fd == cfd) {
+					if (nread < 1)
+						exit(EXIT_SUCCESS);
+					ofd = STDOUT_FILENO;
+				} else {
+					if (nread < 1) {
+						/* Close outgoing half-connection so they get EOF,
+						 * but leave incoming alone so we can see response */
+						shutdown(cfd, 1);
+						FD_CLR(STDIN_FILENO, &readfds);
+					}
+					ofd = cfd;
+				}
+				xwrite(ofd, iobuf, nread);
+				if (delay > 0)
+					sleep(delay);
+			}
+			if (fd == cfd)
+				break;
+			fd = cfd;
+		}
+	}
+}
+#endif
diff --git a/busybox-1.19.3/networking/nc_bloaty.c b/busybox-1.19.3/networking/nc_bloaty.c
new file mode 100644
index 0000000..1daad13
--- /dev/null
+++ b/busybox-1.19.3/networking/nc_bloaty.c
@@ -0,0 +1,887 @@
+/* Based on netcat 1.10 RELEASE 960320 written by hobbit@avian.org.
+ * Released into public domain by the author.
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* Author's comments from nc 1.10:
+ * =====================
+ * Netcat is entirely my own creation, although plenty of other code was used as
+ * examples.  It is freely given away to the Internet community in the hope that
+ * it will be useful, with no restrictions except giving credit where it is due.
+ * No GPLs, Berkeley copyrights or any of that nonsense.  The author assumes NO
+ * responsibility for how anyone uses it.  If netcat makes you rich somehow and
+ * you're feeling generous, mail me a check.  If you are affiliated in any way
+ * with Microsoft Network, get a life.  Always ski in control.  Comments,
+ * questions, and patches to hobbit@avian.org.
+ * ...
+ * Netcat and the associated package is a product of Avian Research, and is freely
+ * available in full source form with no restrictions save an obligation to give
+ * credit where due.
+ * ...
+ * A damn useful little "backend" utility begun 950915 or thereabouts,
+ * as *Hobbit*'s first real stab at some sockets programming.  Something that
+ * should have and indeed may have existed ten years ago, but never became a
+ * standard Unix utility.  IMHO, "nc" could take its place right next to cat,
+ * cp, rm, mv, dd, ls, and all those other cryptic and Unix-like things.
+ * =====================
+ *
+ * Much of author's comments are still retained in the code.
+ *
+ * Functionality removed (rationale):
+ * - miltiple-port ranges, randomized port scanning (use nmap)
+ * - telnet support (use telnet)
+ * - source routing
+ * - multiple DNS checks
+ * Functionalty which is different from nc 1.10:
+ * - PROG in '-e PROG' can have ARGS (and options).
+ *   Because of this -e option must be last.
+//TODO: remove -e incompatibility?
+ * - we don't redirect stderr to the network socket for the -e PROG.
+ *   (PROG can do it itself if needed, but sometimes it is NOT wanted!)
+ * - numeric addresses are printed in (), not [] (IPv6 looks better),
+ *   port numbers are inside (): (1.2.3.4:5678)
+ * - network read errors are reported on verbose levels > 1
+ *   (nc 1.10 treats them as EOF)
+ * - 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.
+ */
+
+/* done in nc.c: #include "libbb.h" */
+
+//usage:#if ENABLE_NC_110_COMPAT
+//usage:
+//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:	)
+//usage:#define nc_full_usage "\n\n"
+//usage:       "	-e PROG	Run PROG after connect (must be last)"
+//usage:	IF_NC_SERVER(
+//usage:     "\n	-l	Listen mode, for inbound connects"
+//usage:	)
+//usage:     "\n	-p PORT	Local port"
+//usage:     "\n	-s ADDR	Local address"
+//usage:     "\n	-w SEC	Timeout for connects and final net reads"
+//usage:	IF_NC_EXTRA(
+//usage:     "\n	-i SEC	Delay interval for lines sent" /* ", ports scanned" */
+//usage:	)
+//usage:     "\n	-n	Don't do DNS resolution"
+//usage:     "\n	-u	UDP mode"
+//usage:     "\n	-v	Verbose"
+//usage:	IF_NC_EXTRA(
+//usage:     "\n	-o FILE	Hex dump traffic"
+//usage:     "\n	-z	Zero-I/O mode (scanning)"
+//usage:	)
+//usage:#endif
+
+/*   "\n	-r		Randomize local and remote ports" */
+/*   "\n	-g gateway	Source-routing hop point[s], up to 8" */
+/*   "\n	-G num		Source-routing pointer: 4, 8, 12, ..." */
+/*   "\nport numbers can be individual or ranges: lo-hi [inclusive]" */
+
+/* -e PROG can take ARGS too: "nc ... -e ls -l", but we don't document it
+ * in help text: nc 1.10 does not allow that. We don't want to entice
+ * users to use this incompatibility */
+
+enum {
+	SLEAZE_PORT = 31337,               /* for UDP-scan RTT trick, change if ya want */
+	BIGSIZ = 8192,                     /* big buffers */
+
+	netfd = 3,
+	ofd = 4,
+};
+
+struct globals {
+	/* global cmd flags: */
+	unsigned o_verbose;
+	unsigned o_wait;
+#if ENABLE_NC_EXTRA
+	unsigned o_interval;
+#endif
+
+	/*int netfd;*/
+	/*int ofd;*/                     /* hexdump output fd */
+#if ENABLE_LFS
+#define SENT_N_RECV_M "sent %llu, rcvd %llu\n"
+	unsigned long long wrote_out;          /* total stdout bytes */
+	unsigned long long wrote_net;          /* total net bytes */
+#else
+#define SENT_N_RECV_M "sent %u, rcvd %u\n"
+	unsigned wrote_out;          /* total stdout bytes */
+	unsigned wrote_net;          /* total net bytes */
+#endif
+	/* 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)
+	 3 - local address after connect??/recv/accept (IP and port are nonzero) */
+	struct len_and_sockaddr *ouraddr;
+	/* themaddr is NULL if no peer hostname[:port] specified on command line */
+	struct len_and_sockaddr *themaddr;
+	/* remend is set after connect/recv/accept to the actual ip:port of peer */
+	struct len_and_sockaddr remend;
+
+	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];
+};
+
+#define G (*ptr_to_globals)
+#define wrote_out  (G.wrote_out )
+#define wrote_net  (G.wrote_net )
+#define ouraddr    (G.ouraddr   )
+#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 )
+#define o_wait     (G.o_wait    )
+#if ENABLE_NC_EXTRA
+#define o_interval (G.o_interval)
+#else
+#define o_interval 0
+#endif
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+
+/* 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,
+};
+
+#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)
+#else
+#define o_ofile   0
+#define o_zero    0
+#endif
+
+/* Debug: squirt whatever message and sleep a bit so we can see it go by. */
+/* Beware: writes to stdOUT... */
+#if 0
+#define Debug(...) do { printf(__VA_ARGS__); printf("\n"); fflush_all(); sleep(1); } while (0)
+#else
+#define Debug(...) do { } while (0)
+#endif
+
+#define holler_error(...)  do { if (o_verbose) bb_error_msg(__VA_ARGS__); } while (0)
+#define holler_perror(...) do { if (o_verbose) bb_perror_msg(__VA_ARGS__); } while (0)
+
+/* catch: no-brainer interrupt handler */
+static void catch(int sig)
+{
+	if (o_verbose > 1)                /* normally we don't care */
+		fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
+	fprintf(stderr, "punt!\n");
+	kill_myself_with_sig(sig);
+}
+
+/* unarm  */
+static void unarm(void)
+{
+	signal(SIGALRM, SIG_IGN);
+	alarm(0);
+}
+
+/* timeout and other signal handling cruft */
+static void tmtravel(int sig UNUSED_PARAM)
+{
+	unarm();
+	longjmp(jbuf, 1);
+}
+
+/* arm: set the timer.  */
+static void arm(unsigned secs)
+{
+	signal(SIGALRM, tmtravel);
+	alarm(secs);
+}
+
+/* findline:
+ find the next newline in a buffer; return inclusive size of that "line",
+ or the entire buffer size, so the caller knows how much to then write().
+ Not distinguishing \n vs \r\n for the nonce; it just works as is... */
+static unsigned findline(char *buf, unsigned siz)
+{
+	char * p;
+	int x;
+	if (!buf)                        /* various sanity checks... */
+		return 0;
+	if (siz > BIGSIZ)
+		return 0;
+	x = siz;
+	for (p = buf; x > 0; x--) {
+		if (*p == '\n') {
+			x = (int) (p - buf);
+			x++;                        /* 'sokay if it points just past the end! */
+Debug("findline returning %d", x);
+			return x;
+		}
+		p++;
+	} /* for */
+Debug("findline returning whole thing: %d", siz);
+	return siz;
+} /* findline */
+
+/* doexec:
+ fiddle all the file descriptors around, and hand off to another prog.  Sort
+ of like a one-off "poor man's inetd".  This is the only section of code
+ that would be security-critical, which is why it's ifdefed out by default.
+ Use at your own hairy risk; if you leave shells lying around behind open
+ listening ports you deserve to lose!! */
+static int doexec(char **proggie) NORETURN;
+static int doexec(char **proggie)
+{
+	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 */
+	BB_EXECVP_or_die(proggie);
+}
+
+/* connect_w_timeout:
+ return an fd for one of
+ an open outbound TCP connection, a UDP stub-socket thingie, or
+ an unconnected TCP or UDP socket to listen on.
+ Examines various global o_blah flags to figure out what to do.
+ lad can be NULL, then socket is not bound to any local ip[:port] */
+static int connect_w_timeout(int fd)
+{
+	int rr;
+
+	/* wrap connect inside a timer, and hit it */
+	arm(o_wait);
+	if (setjmp(jbuf) == 0) {
+		rr = connect(fd, &themaddr->u.sa, themaddr->len);
+		unarm();
+	} else { /* setjmp: connect failed... */
+		rr = -1;
+		errno = ETIMEDOUT; /* fake it */
+	}
+	return rr;
+}
+
+/* dolisten:
+ listens for
+ 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)
+{
+	int rr;
+
+	if (!o_udpmode)
+		xlisten(netfd, 1); /* TCP: gotta listen() before we can get */
+
+	/* Various things that follow temporarily trash bigbuf_net, which might contain
+	 a copy of any recvfrom()ed packet, but we'll read() another copy later. */
+
+	/* I can't believe I have to do all this to get my own goddamn bound address
+	 and port number.  It should just get filled in during bind() or something.
+	 All this is only useful if we didn't say -p for listening, since if we
+	 said -p we *know* what port we're listening on.  At any rate we won't bother
+	 with it all unless we wanted to see it, although listening quietly on a
+	 random unknown port is probably not very useful without "netstat". */
+	if (o_verbose) {
+		char *addr;
+		getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
+		//if (rr < 0)
+		//	bb_perror_msg_and_die("getsockname after bind");
+		addr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
+		fprintf(stderr, "listening on %s ...\n", addr);
+		free(addr);
+	}
+
+	if (o_udpmode) {
+		/* UDP is a speeeeecial case -- we have to do I/O *and* get the calling
+		 party's particulars all at once, listen() and accept() don't apply.
+		 At least in the BSD universe, however, recvfrom/PEEK is enough to tell
+		 us something came in, and we can set things up so straight read/write
+		 actually does work after all.  Yow.  YMMV on strange platforms!  */
+
+		/* I'm not completely clear on how this works -- BSD seems to make UDP
+		 just magically work in a connect()ed context, but we'll undoubtedly run
+		 into systems this deal doesn't work on.  For now, we apparently have to
+		 issue a connect() on our just-tickled socket so we can write() back.
+		 Again, why the fuck doesn't it just get filled in and taken care of?!
+		 This hack is anything but optimal.  Basically, if you want your listener
+		 to also be able to send data back, you need this connect() line, which
+		 also has the side effect that now anything from a different source or even a
+		 different port on the other end won't show up and will cause ICMP errors.
+		 I guess that's what they meant by "connect".
+		 Let's try to remember what the "U" is *really* for, eh? */
+
+		/* If peer address is specified, connect to it */
+		remend.len = LSA_SIZEOF_SA;
+		if (themaddr) {
+			remend = *themaddr;
+			xconnect(netfd, &themaddr->u.sa, themaddr->len);
+		}
+		/* peek first packet and remember peer addr */
+		arm(o_wait);                /* might as well timeout this, too */
+		if (setjmp(jbuf) == 0) {       /* do timeout for initial connect */
+			/* (*ouraddr) is prefilled with "default" address */
+			/* and here we block... */
+			rr = recv_from_to(netfd, NULL, 0, MSG_PEEK, /*was bigbuf_net, BIGSIZ*/
+				&remend.u.sa, &ouraddr->u.sa, ouraddr->len);
+			if (rr < 0)
+				bb_perror_msg_and_die("recvfrom");
+			unarm();
+		} else
+			bb_error_msg_and_die("timeout");
+/* Now we learned *to which IP* peer has connected, and we want to anchor
+our socket on it, so that our outbound packets will have correct local IP.
+Unfortunately, bind() on already bound socket will fail now (EINVAL):
+	xbind(netfd, &ouraddr->u.sa, ouraddr->len);
+Need to read the packet, save data, close this socket and
+create new one, and bind() it. TODO */
+		if (!themaddr)
+			xconnect(netfd, &remend.u.sa, ouraddr->len);
+	} else {
+		/* TCP */
+		arm(o_wait); /* wrap this in a timer, too; 0 = forever */
+		if (setjmp(jbuf) == 0) {
+ again:
+			remend.len = LSA_SIZEOF_SA;
+			rr = accept(netfd, &remend.u.sa, &remend.len);
+			if (rr < 0)
+				bb_perror_msg_and_die("accept");
+			if (themaddr) {
+				int sv_port, port, r;
+
+				sv_port = get_nport(&remend.u.sa); /* save */
+				port = get_nport(&themaddr->u.sa);
+				if (port == 0) {
+					/* "nc -nl -p LPORT RHOST" (w/o RPORT!):
+					 * we should accept any 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.u.sa, sv_port); /* restore */
+				if (r != 0) {
+					/* nc 1.10 bails out instead, and its error message
+					 * is not suppressed by o_verbose */
+					if (o_verbose) {
+						char *remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
+						bb_error_msg("connect from wrong ip/port %s ignored", remaddr);
+						free(remaddr);
+					}
+					close(rr);
+					goto again;
+				}
+			}
+			unarm();
+		} else
+			bb_error_msg_and_die("timeout");
+		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
+		 offer different services via different alias addresses, such as the
+		 "virtual web site" hack. */
+		getsockname(netfd, &ouraddr->u.sa, &ouraddr->len);
+		//if (rr < 0)
+		//	bb_perror_msg_and_die("getsockname after accept");
+	}
+
+	if (o_verbose) {
+		char *lcladdr, *remaddr, *remhostname;
+
+#if ENABLE_NC_EXTRA && defined(IP_OPTIONS)
+	/* If we can, look for any IP options.  Useful for testing the receiving end of
+	 such things, and is a good exercise in dealing with it.  We do this before
+	 the connect message, to ensure that the connect msg is uniformly the LAST
+	 thing to emerge after all the intervening crud.  Doesn't work for UDP on
+	 any machines I've tested, but feel free to surprise me. */
+		char optbuf[40];
+		socklen_t x = sizeof(optbuf);
+
+		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';
+			fprintf(stderr, "IP options: %s\n", bigbuf_net);
+		}
+#endif
+
+	/* now check out who it is.  We don't care about mismatched DNS names here,
+	 but any ADDR and PORT we specified had better fucking well match the caller.
+	 Converting from addr to inet_ntoa and back again is a bit of a kludge, but
+	 gethostpoop wants a string and there's much gnarlier code out there already,
+	 so I don't feel bad.
+	 The *real* question is why BFD sockets wasn't designed to allow listens for
+	 connections *from* specific hosts/ports, instead of requiring the caller to
+	 accept the connection and then reject undesireable ones by closing.
+	 In other words, we need a TCP MSG_PEEK. */
+	/* bbox: removed most of it */
+		lcladdr = xmalloc_sockaddr2dotted(&ouraddr->u.sa);
+		remaddr = xmalloc_sockaddr2dotted(&remend.u.sa);
+		remhostname = o_nflag ? remaddr : xmalloc_sockaddr2host(&remend.u.sa);
+		fprintf(stderr, "connect to %s from %s (%s)\n",
+				lcladdr, remhostname, remaddr);
+		free(lcladdr);
+		free(remaddr);
+		if (!o_nflag)
+			free(remhostname);
+	}
+}
+
+/* udptest:
+ fire a couple of packets at a UDP target port, just to see if it's really
+ there.  On BSD kernels, ICMP host/port-unreachable errors get delivered to
+ our socket as ECONNREFUSED write errors.  On SV kernels, we lose; we'll have
+ to collect and analyze raw ICMP ourselves a la satan's probe_udp_ports
+ backend.  Guess where one could swipe the appropriate code from...
+
+ Use the time delay between writes if given, otherwise use the "tcp ping"
+ trick for getting the RTT.  [I got that idea from pluvius, and warped it.]
+ Return either the original fd, or clean up and return -1. */
+#if ENABLE_NC_EXTRA
+static int udptest(void)
+{
+	int rr;
+
+	rr = write(netfd, bigbuf_in, 1);
+	if (rr != 1)
+		bb_perror_msg("udptest first write");
+
+	if (o_wait)
+		sleep(o_wait); // can be interrupted! while (t) nanosleep(&t)?
+	else {
+	/* use the tcp-ping trick: try connecting to a normally refused port, which
+	 causes us to block for the time that SYN gets there and RST gets back.
+	 Not completely reliable, but it *does* mostly work. */
+	/* Set a temporary connect timeout, so packet filtration doesnt cause
+	 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->u.sa, htons(SLEAZE_PORT));
+		connect_w_timeout(rr);
+		/* don't need to restore themaddr's port, it's not used anymore */
+		close(rr);
+		o_wait = 0; /* restore */
+	}
+
+	rr = write(netfd, bigbuf_in, 1);
+	return (rr != 1); /* if rr == 1, return 0 (success) */
+}
+#else
+int udptest(void);
+#endif
+
+/* oprint:
+ Hexdump bytes shoveled either way to a running logfile, in the format:
+ D offset       -  - - - --- 16 bytes --- - - -  -     # .... ascii .....
+ where "which" sets the direction indicator, D:
+ 0 -- sent to network, or ">"
+ 1 -- rcvd and printed to stdout, or "<"
+ and "buf" and "n" are data-block and length.  If the current block generates
+ a partial line, so be it; we *want* that lockstep indication of who sent
+ what when.  Adapted from dgaudet's original example -- but must be ripping
+ *fast*, since we don't want to be too disk-bound... */
+#if ENABLE_NC_EXTRA
+static void oprint(int direction, unsigned char *p, unsigned bc)
+{
+	unsigned obc;           /* current "global" offset */
+	unsigned x;
+	unsigned char *op;      /* out hexdump ptr */
+	unsigned char *ap;      /* out asc-dump ptr */
+	unsigned char stage[100];
+
+	if (bc == 0)
+		return;
+
+	obc = wrote_net; /* use the globals! */
+	if (direction == '<')
+		obc = wrote_out;
+	stage[0] = direction;
+	stage[59] = '#'; /* preload separator */
+	stage[60] = ' ';
+
+	do {    /* for chunk-o-data ... */
+		x = 16;
+		if (bc < 16) {
+			/* memset(&stage[bc*3 + 11], ' ', 16*3 - bc*3); */
+			memset(&stage[11], ' ', 16*3);
+			x = bc;
+		}
+		sprintf((char *)&stage[1], " %8.8x ", obc);  /* xxx: still slow? */
+		bc -= x;          /* fix current count */
+		obc += x;         /* fix current offset */
+		op = &stage[11];  /* where hex starts */
+		ap = &stage[61];  /* where ascii starts */
+
+		do {  /* for line of dump, however long ... */
+			*op++ = 0x20 | bb_hexdigits_upcase[*p >> 4];
+			*op++ = 0x20 | bb_hexdigits_upcase[*p & 0x0f];
+			*op++ = ' ';
+			if ((*p > 31) && (*p < 127))
+				*ap = *p;   /* printing */
+			else
+				*ap = '.';  /* nonprinting, loose def */
+			ap++;
+			p++;
+		} while (--x);
+		*ap++ = '\n';  /* finish the line */
+		xwrite(ofd, stage, ap - stage);
+	} while (bc);
+}
+#else
+void oprint(int direction, unsigned char *p, unsigned bc);
+#endif
+
+/* readwrite:
+ handle stdin/stdout/network I/O.  Bwahaha!! -- the select loop from hell.
+ In this instance, return what might become our exit status. */
+static int readwrite(void)
+{
+	int rr;
+	char *zp = zp; /* gcc */  /* stdin buf ptr */
+	char *np = np;            /* net-in buf ptr */
+	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 */
+
+	/* 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 */
+	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 */
+		}
+		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.  */
+		if (o_wait) {
+			struct timeval tmp_timer;
+			tmp_timer.tv_sec = o_wait;
+			tmp_timer.tv_usec = 0;
+		/* highest possible fd is netfd (3) */
+			rr = select(netfd+1, &ding2, NULL, NULL, &tmp_timer);
+		} else
+			rr = select(netfd+1, &ding2, NULL, NULL, NULL);
+		if (rr < 0 && errno != EINTR) {                /* might have gotten ^Zed, etc */
+			holler_perror("select");
+			close(netfd);
+			return 1;
+		}
+	/* 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))
+				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! */
+			}
+		} /* select timeout */
+	/* xxx: should we check the exception fds too?  The read fds seem to give
+	 us the right info, and none of the examples I found bothered. */
+
+	/* Ding!!  Something arrived, go check all the incoming hoppers, net first */
+		if (FD_ISSET(netfd, &ding2)) {                /* net: ding! */
+			rr = read(netfd, bigbuf_net, BIGSIZ);
+			if (rr <= 0) {
+				if (rr < 0 && o_verbose > 1) {
+					/* nc 1.10 doesn't do this */
+					bb_perror_msg("net read");
+				}
+				FD_CLR(netfd, &ding1);                /* net closed, we'll finish up... */
+				rzleft = 0;                        /* can't write anymore: broken pipe */
+			} else {
+				rnleft = rr;
+				np = bigbuf_net;
+			}
+Debug("got %d from the net, errno %d", rr, errno);
+		} /* net:ding */
+
+	/* if we're in "slowly" mode there's probably still stuff in the stdin
+	 buffer, so don't read unless we really need MORE INPUT!  MORE INPUT! */
+		if (rzleft)
+			goto shovel;
+
+	/* okay, suck more stdin */
+		if (FD_ISSET(STDIN_FILENO, &ding2)) {                /* stdin: ding! */
+			rr = read(STDIN_FILENO, bigbuf_in, BIGSIZ);
+	/* 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?)
+			} else {
+				rzleft = rr;
+				zp = bigbuf_in;
+			}
+		} /* stdin:ding */
+ shovel:
+	/* now that we've dingdonged all our thingdings, send off the results.
+	 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 */
+			}
+Debug("wrote %d to stdout, errno %d", rr, errno);
+		} /* rnleft */
+		if (rzleft) {
+			if (o_interval)                        /* in "slowly" mode ?? */
+				rr = findline(zp, rzleft);
+			else
+				rr = rzleft;
+			rr = write(netfd, zp, rr);        /* one line, or the whole buffer */
+			if (rr > 0) {
+				if (o_ofile) /* log what got sent */
+					oprint('>', (unsigned char *)zp, rr);
+				zp += rr;
+				rzleft -= rr;
+				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 */
+			wretry--;                        /* none left, and get another load */
+			goto shovel;
+		}
+	} /* while ding1:netfd is 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
+	 blocking reads and writes and my own manual "last ditch" efforts to read
+	 the net again after a timeout.  I haven't seen any screwups yet, but it's
+	 not like my test network is particularly busy... */
+	close(netfd);
+	return 0;
+} /* readwrite */
+
+/* main: now we pull it all together... */
+int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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 **proggie;
+	int x;
+	unsigned o_lport = 0;
+
+	INIT_G();
+
+	/* catch a signal or two for cleanup */
+	bb_signals(0
+		+ (1 << SIGINT)
+		+ (1 << SIGQUIT)
+		+ (1 << SIGTERM)
+		, catch);
+	/* and suppress others... */
+	bb_signals(0
+#ifdef SIGURG
+		+ (1 << SIGURG)
+#endif
+		+ (1 << SIGPIPE) /* important! */
+		, SIG_IGN);
+
+	proggie = argv;
+	while (*++proggie) {
+		if (strcmp(*proggie, "-e") == 0) {
+			*proggie = NULL;
+			proggie++;
+			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")
+			IF_NC_EXTRA("i:o:z"),
+			&str_p, &str_s, &o_wait
+			IF_NC_EXTRA(, &str_i, &str_o), &o_verbose);
+	argv += optind;
+#if ENABLE_NC_EXTRA
+	if (option_mask32 & OPT_i) /* line-interval time */
+		o_interval = xatou_range(str_i, 1, 0xffff);
+#endif
+	//if (option_mask32 & OPT_l) /* listen mode */
+	//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 */
+		o_lport = bb_lookup_port(str_p, o_udpmode ? "udp" : "tcp", 0);
+		if (!o_lport)
+			bb_error_msg_and_die("bad local port '%s'", str_p);
+	}
+	//if (option_mask32 & OPT_r) /* randomize various things */
+	//if (option_mask32 & OPT_u) /* use UDP */
+	//if (option_mask32 & OPT_v) /* verbose */
+	//if (option_mask32 & OPT_w) /* wait time */
+	//if (option_mask32 & OPT_z) /* little or no data xfer */
+
+	/* We manage our fd's so that they are never 0,1,2 */
+	/*bb_sanitize_stdio(); - not needed */
+
+	if (argv[0]) {
+		themaddr = xhost2sockaddr(argv[0],
+			argv[1]
+			? bb_lookup_port(argv[1], o_udpmode ? "udp" : "tcp", 0)
+			: 0);
+	}
+
+	/* create & bind network socket */
+	x = (o_udpmode ? SOCK_DGRAM : SOCK_STREAM);
+	if (option_mask32 & OPT_s) { /* local address */
+		/* if o_lport is still 0, then we will use random port */
+		ouraddr = xhost2sockaddr(str_s, o_lport);
+#ifdef BLOAT
+		/* prevent spurious "UDP listen needs !0 port" */
+		o_lport = get_nport(ouraddr);
+		o_lport = ntohs(o_lport);
+#endif
+		x = xsocket(ouraddr->u.sa.sa_family, x, 0);
+	} else {
+		/* We try IPv6, then IPv4, unless addr family is
+		 * implicitly set by way of remote addr/port spec */
+		x = xsocket_type(&ouraddr,
+				(themaddr ? themaddr->u.sa.sa_family : AF_UNSPEC),
+				x);
+		if (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
+	 || ouraddr->u.sa.sa_family != AF_UNIX
+	) {
+		xbind(netfd, &ouraddr->u.sa, ouraddr->len);
+	}
+#if 0
+	setsockopt(netfd, SOL_SOCKET, SO_RCVBUF, &o_rcvbuf, sizeof o_rcvbuf);
+	setsockopt(netfd, SOL_SOCKET, SO_SNDBUF, &o_sndbuf, sizeof o_sndbuf);
+#endif
+
+#ifdef BLOAT
+	if (OPT_l && (option_mask32 & (OPT_u|OPT_l)) == (OPT_u|OPT_l)) {
+		/* apparently UDP can listen ON "port 0",
+		 but that's not useful */
+		if (!o_lport)
+			bb_error_msg_and_die("UDP listen needs nonzero -p port");
+	}
+#endif
+
+	FD_SET(STDIN_FILENO, &ding1);                        /* stdin *is* initially open */
+	if (proggie) {
+		close(0); /* won't need stdin */
+		option_mask32 &= ~OPT_o; /* -o with -e is meaningless! */
+	}
+#if ENABLE_NC_EXTRA
+	if (o_ofile)
+		xmove_fd(xopen(str_o, O_WRONLY|O_CREAT|O_TRUNC), ofd);
+#endif
+
+	if (o_listen) {
+		dolisten();
+		/* 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_show_usage();
+
+		remend = *themaddr;
+		if (o_verbose)
+			themdotted = xmalloc_sockaddr2dotted(&themaddr->u.sa);
+
+		x = connect_w_timeout(netfd);
+		if (o_zero && x == 0 && o_udpmode)        /* if UDP scanning... */
+			x = udptest();
+		if (x == 0) {                        /* Yow, are we OPEN YET?! */
+			if (o_verbose)
+				fprintf(stderr, "%s (%s) open\n", argv[0], themdotted);
+			if (proggie)                        /* exec is valid for outbound, too */
+				doexec(proggie);
+			if (!o_zero)
+				x = readwrite();
+		} else { /* connect or udptest wasn't successful */
+			x = 1;                                /* exit status */
+			/* if we're scanning at a "one -v" verbosity level, don't print refusals.
+			 Give it another -v if you want to see everything. */
+			if (o_verbose > 1 || (o_verbose && errno != ECONNREFUSED))
+				bb_perror_msg("%s (%s)", argv[0], themdotted);
+		}
+	}
+	if (o_verbose > 1)                /* normally we don't care */
+		fprintf(stderr, SENT_N_RECV_M, wrote_net, wrote_out);
+	return x;
+}
diff --git a/busybox-1.19.3/networking/netstat.c b/busybox-1.19.3/networking/netstat.c
new file mode 100644
index 0000000..9c23957
--- /dev/null
+++ b/busybox-1.19.3/networking/netstat.c
@@ -0,0 +1,746 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini netstat implementation(s) for busybox
+ * based in part on the netstat implementation from net-tools.
+ *
+ * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
+ *
+ * 2002-04-20
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ *
+ * 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 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 {
+	OPT_sock_listen = 1 << 0, // l
+	OPT_sock_all    = 1 << 1, // a
+	OPT_extended    = 1 << 2, // e
+	OPT_noresolve   = 1 << 3, // n
+	OPT_sock_tcp    = 1 << 4, // t
+	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
+};
+
+#define NETSTAT_CONNECTED 0x01
+#define NETSTAT_LISTENING 0x02
+#define NETSTAT_NUMERIC   0x04
+/* Must match getopt32 option string */
+#define NETSTAT_TCP       0x10
+#define NETSTAT_UDP       0x20
+#define NETSTAT_RAW       0x40
+#define NETSTAT_UNIX      0x80
+#define NETSTAT_ALLPROTO  (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
+
+
+enum {
+	TCP_ESTABLISHED = 1,
+	TCP_SYN_SENT,
+	TCP_SYN_RECV,
+	TCP_FIN_WAIT1,
+	TCP_FIN_WAIT2,
+	TCP_TIME_WAIT,
+	TCP_CLOSE,
+	TCP_CLOSE_WAIT,
+	TCP_LAST_ACK,
+	TCP_LISTEN,
+	TCP_CLOSING, /* now a valid state */
+};
+
+static const char *const tcp_state[] = {
+	"",
+	"ESTABLISHED",
+	"SYN_SENT",
+	"SYN_RECV",
+	"FIN_WAIT1",
+	"FIN_WAIT2",
+	"TIME_WAIT",
+	"CLOSE",
+	"CLOSE_WAIT",
+	"LAST_ACK",
+	"LISTEN",
+	"CLOSING"
+};
+
+typedef enum {
+	SS_FREE = 0,     /* not allocated                */
+	SS_UNCONNECTED,  /* unconnected to any socket    */
+	SS_CONNECTING,   /* in process of connecting     */
+	SS_CONNECTED,    /* connected to socket          */
+	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            */
+
+#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 ADDR_WIDE                51  /* INET6_ADDRSTRLEN + 5 for the port number */
+#if ENABLE_FEATURE_NETSTAT_WIDE
+# define FMT_NET_CONN_DATA       "%s   %6ld %6ld %-*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   %6ld %6ld %-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"
+/* PROGNAME_WIDTH chars: 12345678901234567890 */
+#define PROGNAME_BANNER "PID/Program name    "
+
+struct prg_node {
+	struct prg_node *next;
+	long inode;
+	char name[PROGNAME_WIDTH];
+};
+
+#define PRG_HASH_SIZE 211
+
+struct globals {
+	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 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; \
+} while (0)
+
+
+#if ENABLE_FEATURE_NETSTAT_PRG
+
+/* Deliberately truncating long to unsigned *int* */
+#define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE)
+
+static void prg_cache_add(long inode, char *name)
+{
+	unsigned hi = PRG_HASHIT(inode);
+	struct prg_node **pnp, *pn;
+
+	prg_cache_loaded = 2;
+	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 */
+			return;
+		}
+	}
+	*pnp = xzalloc(sizeof(struct prg_node));
+	pn = *pnp;
+	pn->inode = inode;
+	safe_strncpy(pn->name, name, PROGNAME_WIDTH);
+}
+
+static const char *prg_cache_get(long inode)
+{
+	unsigned hi = PRG_HASHIT(inode);
+	struct prg_node *pn;
+
+	for (pn = prg_hash[hi]; pn; pn = pn->next)
+		if (pn->inode == inode)
+			return pn->name;
+	return "-";
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void prg_cache_clear(void)
+{
+	struct prg_node **pnp, *pn;
+
+	for (pnp = prg_hash; pnp < prg_hash + PRG_HASH_SIZE; pnp++) {
+		while ((pn = *pnp) != NULL) {
+			*pnp = pn->next;
+			free(pn);
+		}
+	}
+}
+#else
+#define prg_cache_clear() ((void)0)
+#endif
+
+static long extract_socket_inode(const char *lname)
+{
+	long inode = -1;
+
+	if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) {
+		/* "socket:[12345]", extract the "12345" as inode */
+		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_strtoul(lname + sizeof("[0000]:")-1, NULL, 0);
+		if (errno) /* not NUL terminated? */
+			inode = -1;
+	}
+
+#if 0 /* bb_strtol returns all-ones bit pattern on ERANGE anyway */
+	if (errno == ERANGE)
+		inode = -1;
+#endif
+	return inode;
+}
+
+static int FAST_FUNC add_to_prg_cache_if_socket(const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *pid_slash_progname,
+		int depth UNUSED_PARAM)
+{
+	char *linkname;
+	long inode;
+
+	linkname = xmalloc_readlink(fileName);
+	if (linkname != NULL) {
+		inode = extract_socket_inode(linkname);
+		free(linkname);
+		if (inode >= 0)
+			prg_cache_add(inode, (char *)pid_slash_progname);
+	}
+	return TRUE;
+}
+
+static int FAST_FUNC dir_act(const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth)
+{
+	const char *pid;
+	char *pid_slash_progname;
+	char proc_pid_fname[sizeof("/proc/%u/cmdline") + sizeof(long)*3];
+	char cmdline_buf[512];
+	int n, len;
+
+	if (depth == 0) /* "/proc" itself */
+		return TRUE; /* continue looking one level below /proc */
+
+	pid = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
+	if (!isdigit(pid[0])) /* skip /proc entries which aren't processes */
+		return SKIP;
+
+	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[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)
+{
+	int load_ok;
+
+	prg_cache_loaded = 1;
+	load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET,
+				NULL, dir_act, NULL, 0);
+	if (load_ok)
+		return;
+
+	if (prg_cache_loaded == 1)
+		bb_error_msg("can't scan /proc - are you root?");
+	else
+		bb_error_msg("showing only processes with your user ID");
+}
+
+#else
+
+#define prg_cache_clear()       ((void)0)
+
+#endif //ENABLE_FEATURE_NETSTAT_PRG
+
+
+#if ENABLE_FEATURE_IPV6
+static void build_ipv6_addr(char* local_addr, struct sockaddr_in6* localaddr)
+{
+	char addr6[INET6_ADDRSTRLEN];
+	struct in6_addr in6;
+
+	sscanf(local_addr, "%08X%08X%08X%08X",
+		   &in6.s6_addr32[0], &in6.s6_addr32[1],
+		   &in6.s6_addr32[2], &in6.s6_addr32[3]);
+	inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
+	inet_pton(AF_INET6, addr6, &localaddr->sin6_addr);
+
+	localaddr->sin6_family = AF_INET6;
+}
+#endif
+
+static void build_ipv4_addr(char* local_addr, struct sockaddr_in* localaddr)
+{
+	sscanf(local_addr, "%X", &localaddr->sin_addr.s_addr);
+	localaddr->sin_family = AF_INET;
+}
+
+static const char *get_sname(int port, const char *proto, int numeric)
+{
+	if (!port)
+		return "*";
+	if (!numeric) {
+		struct servent *se = getservbyport(port, proto);
+		if (se)
+			return se->s_name;
+	}
+	/* hummm, we may return static buffer here!! */
+	return itoa(ntohs(port));
+}
+
+static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric)
+{
+	char *host, *host_port;
+
+	/* Code which used "*" for INADDR_ANY is removed: it's ambiguous
+	 * in IPv6, while "0.0.0.0" is not. */
+
+	host = numeric ? xmalloc_sockaddr2dotted_noport(addr)
+	               : xmalloc_sockaddr2host_noport(addr);
+
+	host_port = xasprintf("%s:%s", host, get_sname(htons(port), proto, numeric));
+	free(host);
+	return host_port;
+}
+
+struct inet_params {
+	int local_port, rem_port, state, uid;
+	union {
+		struct sockaddr     sa;
+		struct sockaddr_in  sin;
+#if ENABLE_FEATURE_IPV6
+		struct sockaddr_in6 sin6;
+#endif
+	} localaddr, remaddr;
+	unsigned long rxq, txq, inode;
+};
+
+static int scan_inet_proc_line(struct inet_params *param, char *line)
+{
+	int num;
+	/* 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: %32[0-9A-Fa-f]:%X "
+			"%32[0-9A-Fa-f]:%X %X "
+			"%lX:%lX %*X:%*X "
+			"%*X %d %*d %ld ",
+			local_addr, &param->local_port,
+			rem_addr, &param->rem_port, &param->state,
+			&param->txq, &param->rxq,
+			&param->uid, &param->inode);
+	if (num < 9) {
+		return 1; /* error */
+	}
+
+	if (strlen(local_addr) > 8) {
+#if ENABLE_FEATURE_IPV6
+		build_ipv6_addr(local_addr, &param->localaddr.sin6);
+		build_ipv6_addr(rem_addr, &param->remaddr.sin6);
+#endif
+	} else {
+		build_ipv4_addr(local_addr, &param->localaddr.sin);
+		build_ipv4_addr(rem_addr, &param->remaddr.sin);
+	}
+	return 0;
+}
+
+static void print_inet_line(struct inet_params *param,
+		const char *state_str, const char *proto, int is_connected)
+{
+	if ((is_connected && (flags & NETSTAT_CONNECTED))
+	 || (!is_connected && (flags & NETSTAT_LISTENING))
+	) {
+		char *l = ip_port_str(
+				&param->localaddr.sa, param->local_port,
+				proto, flags & NETSTAT_NUMERIC);
+		char *r = ip_port_str(
+				&param->remaddr.sa, param->rem_port,
+				proto, flags & NETSTAT_NUMERIC);
+		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));
+#endif
+		bb_putchar('\n');
+		free(l);
+		free(r);
+	}
+}
+
+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;
+
+	print_inet_line(&param, tcp_state[param.state], "tcp", param.rem_port);
+	return 0;
+}
+
+#if ENABLE_FEATURE_IPV6
+# define NOT_NULL_ADDR(A) ( \
+	( (A.sa.sa_family == AF_INET6) \
+	  && (A.sin6.sin6_addr.s6_addr32[0] | A.sin6.sin6_addr.s6_addr32[1] | \
+	      A.sin6.sin6_addr.s6_addr32[2] | A.sin6.sin6_addr.s6_addr32[3])  \
+	) || ( \
+	  (A.sa.sa_family == AF_INET) \
+	  && A.sin.sin_addr.s_addr != 0 \
+	) \
+)
+#else
+# define NOT_NULL_ADDR(A) (A.sin.sin_addr.s_addr)
+#endif
+
+static int FAST_FUNC udp_do_one(char *line)
+{
+	int have_remaddr;
+	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;
+
+	state_str = "UNKNOWN";
+	switch (param.state) {
+	case TCP_ESTABLISHED:
+		state_str = "ESTABLISHED";
+		break;
+	case TCP_CLOSE:
+		state_str = "";
+		break;
+	}
+
+	have_remaddr = NOT_NULL_ADDR(param.remaddr);
+	print_inet_line(&param, state_str, "udp", have_remaddr);
+	return 0;
+}
+
+static int FAST_FUNC raw_do_one(char *line)
+{
+	int have_remaddr;
+	struct inet_params param;
+
+	if (scan_inet_proc_line(&param, line))
+		return 1;
+
+	have_remaddr = NOT_NULL_ADDR(param.remaddr);
+	print_inet_line(&param, itoa(param.state), "raw", have_remaddr);
+	return 0;
+}
+
+static int FAST_FUNC unix_do_one(char *line)
+{
+	unsigned long refcnt, proto, unix_flags;
+	unsigned long inode;
+	int type, state;
+	int num, path_ofs;
+	const char *ss_proto, *ss_state, *ss_type;
+	char ss_flags[32];
+
+	/* 2.6.15 may report lines like "... @/tmp/fam-user-^@^@^@^@^@^@^@..."
+	 * Other users report long lines filled by NUL bytes.
+	 * (those ^@ are NUL bytes too). We see them as empty lines. */
+	if (!line[0])
+		return 0;
+
+	path_ofs = 0; /* paranoia */
+	num = sscanf(line, "%*p: %lX %lX %lX %X %X %lu %n",
+			&refcnt, &proto, &unix_flags, &type, &state, &inode, &path_ofs);
+	if (num < 6) {
+		return 1; /* error */
+	}
+	if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
+		if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
+			if (!(flags & NETSTAT_LISTENING))
+				return 0;
+		} else {
+			if (!(flags & NETSTAT_CONNECTED))
+				return 0;
+		}
+	}
+
+	switch (proto) {
+		case 0:
+			ss_proto = "unix";
+			break;
+		default:
+			ss_proto = "??";
+	}
+
+	switch (type) {
+		case SOCK_STREAM:
+			ss_type = "STREAM";
+			break;
+		case SOCK_DGRAM:
+			ss_type = "DGRAM";
+			break;
+		case SOCK_RAW:
+			ss_type = "RAW";
+			break;
+		case SOCK_RDM:
+			ss_type = "RDM";
+			break;
+		case SOCK_SEQPACKET:
+			ss_type = "SEQPACKET";
+			break;
+		default:
+			ss_type = "UNKNOWN";
+	}
+
+	switch (state) {
+		case SS_FREE:
+			ss_state = "FREE";
+			break;
+		case SS_UNCONNECTED:
+			/*
+			 * Unconnected sockets may be listening
+			 * for something.
+			 */
+			if (unix_flags & SO_ACCEPTCON) {
+				ss_state = "LISTENING";
+			} else {
+				ss_state = "";
+			}
+			break;
+		case SS_CONNECTING:
+			ss_state = "CONNECTING";
+			break;
+		case SS_CONNECTED:
+			ss_state = "CONNECTED";
+			break;
+		case SS_DISCONNECTING:
+			ss_state = "DISCONNECTING";
+			break;
+		default:
+			ss_state = "UNKNOWN";
+	}
+
+	strcpy(ss_flags, "[ ");
+	if (unix_flags & SO_ACCEPTCON)
+		strcat(ss_flags, "ACC ");
+	if (unix_flags & SO_WAITDATA)
+		strcat(ss_flags, "W ");
+	if (unix_flags & SO_NOSPACE)
+		strcat(ss_flags, "N ");
+	strcat(ss_flags, "]");
+
+	printf("%-5s %-6ld %-11s %-10s %-13s %6lu ",
+		ss_proto, refcnt, ss_flags, ss_type, ss_state, inode
+		);
+
+#if ENABLE_FEATURE_NETSTAT_PRG
+	if (option_mask32 & OPT_prg)
+		printf("%-"PROGNAME_WIDTH_STR"s", prg_cache_get(inode));
+#endif
+
+	/* TODO: currently we stop at first NUL byte. Is it a problem? */
+	line += path_ofs;
+	*strchrnul(line, '\n') = '\0';
+	while (*line)
+		fputc_printable(*line++, stdout);
+	bb_putchar('\n');
+	return 0;
+}
+
+static void do_info(const char *file, int FAST_FUNC (*proc)(char *))
+{
+	int lnr;
+	FILE *procinfo;
+	char *buffer;
+
+	/* _stdin is just to save "r" param */
+	procinfo = fopen_or_warn_stdin(file);
+	if (procinfo == NULL) {
+		return;
+	}
+	lnr = 0;
+	/* Why xmalloc_fgets_str? because it doesn't stop on NULs */
+	while ((buffer = xmalloc_fgets_str(procinfo, "\n")) != NULL) {
+		/* line 0 is skipped */
+		if (lnr && proc(buffer))
+			bb_error_msg("%s: bogus data on line %d", file, lnr + 1);
+		lnr++;
+		free(buffer);
+	}
+	fclose(procinfo);
+}
+
+int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int netstat_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+
+	INIT_G();
+
+	/* Option string must match NETSTAT_xxx constants */
+	opt = getopt32(argv, NETSTAT_OPTS);
+	if (opt & OPT_sock_listen) { // -l
+		flags &= ~NETSTAT_CONNECTED;
+		flags |= NETSTAT_LISTENING;
+	}
+	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;
+	}
+#endif
+#if ENABLE_FEATURE_NETSTAT_WIDE
+	G.addr_width = ADDR_NORMAL_WIDTH;
+	if (opt & OPT_wide) { // -W
+		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
+
+	opt &= NETSTAT_ALLPROTO;
+	if (opt) {
+		flags &= ~NETSTAT_ALLPROTO;
+		flags |= opt;
+	}
+	if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
+		printf("Active Internet connections "); /* xxx */
+
+		if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
+			printf("(servers and established)");
+		else if (flags & NETSTAT_LISTENING)
+			printf("(only servers)");
+		else
+			printf("(w/o servers)");
+		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);
+#if ENABLE_FEATURE_IPV6
+		do_info("/proc/net/tcp6", tcp_do_one);
+#endif
+	}
+	if (flags & NETSTAT_UDP) {
+		do_info("/proc/net/udp", udp_do_one);
+#if ENABLE_FEATURE_IPV6
+		do_info("/proc/net/udp6", udp_do_one);
+#endif
+	}
+	if (flags & NETSTAT_RAW) {
+		do_info("/proc/net/raw", raw_do_one);
+#if ENABLE_FEATURE_IPV6
+		do_info("/proc/net/raw6", raw_do_one);
+#endif
+	}
+	if (flags & NETSTAT_UNIX) {
+		printf("Active UNIX domain sockets ");
+		if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
+			printf("(servers and established)");
+		else if (flags & NETSTAT_LISTENING)
+			printf("(only servers)");
+		else
+			printf("(w/o servers)");
+		printf("\nProto RefCnt Flags       Type       State         I-Node %sPath\n", progname_banner);
+		do_info("/proc/net/unix", unix_do_one);
+	}
+	prg_cache_clear();
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/nslookup.c b/busybox-1.19.3/networking/nslookup.c
new file mode 100644
index 0000000..f4fd407
--- /dev/null
+++ b/busybox-1.19.3/networking/nslookup.c
@@ -0,0 +1,189 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini nslookup implementation for busybox
+ *
+ * Copyright (C) 1999,2000 by Lineo, inc. and John Beppu
+ * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ *
+ * 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 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"
+
+/*
+ * I'm only implementing non-interactive mode;
+ * I totally forgot nslookup even had an interactive mode.
+ *
+ * This applet is the only user of res_init(). Without it,
+ * you may avoid pulling in _res global from libc.
+ */
+
+/* Examples of 'standard' nslookup output
+ * $ nslookup yahoo.com
+ * Server:         128.193.0.10
+ * Address:        128.193.0.10#53
+ *
+ * Non-authoritative answer:
+ * Name:   yahoo.com
+ * Address: 216.109.112.135
+ * Name:   yahoo.com
+ * Address: 66.94.234.13
+ *
+ * $ nslookup 204.152.191.37
+ * Server:         128.193.4.20
+ * Address:        128.193.4.20#53
+ *
+ * Non-authoritative answer:
+ * 37.191.152.204.in-addr.arpa     canonical name = 37.32-27.191.152.204.in-addr.arpa.
+ * 37.32-27.191.152.204.in-addr.arpa       name = zeus-pub2.kernel.org.
+ *
+ * Authoritative answers can be found from:
+ * 32-27.191.152.204.in-addr.arpa  nameserver = ns1.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa  nameserver = ns2.kernel.org.
+ * 32-27.191.152.204.in-addr.arpa  nameserver = ns3.kernel.org.
+ * ns1.kernel.org  internet address = 140.211.167.34
+ * ns2.kernel.org  internet address = 204.152.191.4
+ * ns3.kernel.org  internet address = 204.152.191.36
+ */
+
+static int print_host(const char *hostname, const char *header)
+{
+	/* We can't use xhost2sockaddr() - we want to get ALL addresses,
+	 * not just one */
+	struct addrinfo *result = NULL;
+	int rc;
+	struct addrinfo hint;
+
+	memset(&hint, 0 , sizeof(hint));
+	/* hint.ai_family = AF_UNSPEC; - zero anyway */
+	/* Needed. Or else we will get each address thrice (or more)
+	 * for each possible socket type (tcp,udp,raw...): */
+	hint.ai_socktype = SOCK_STREAM;
+	// hint.ai_flags = AI_CANONNAME;
+	rc = getaddrinfo(hostname, NULL /*service*/, &hint, &result);
+
+	if (rc == 0) {
+		struct addrinfo *cur = result;
+		unsigned cnt = 0;
+
+		printf("%-10s %s\n", header, hostname);
+		// puts(cur->ai_canonname); ?
+		while (cur) {
+			char *dotted, *revhost;
+			dotted = xmalloc_sockaddr2dotted_noport(cur->ai_addr);
+			revhost = xmalloc_sockaddr2hostonly_noport(cur->ai_addr);
+
+			printf("Address %u: %s%c", ++cnt, dotted, revhost ? ' ' : '\n');
+			if (revhost) {
+				puts(revhost);
+				if (ENABLE_FEATURE_CLEAN_UP)
+					free(revhost);
+			}
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(dotted);
+			cur = cur->ai_next;
+		}
+	} else {
+#if ENABLE_VERBOSE_RESOLUTION_ERRORS
+		bb_error_msg("can't resolve '%s': %s", hostname, gai_strerror(rc));
+#else
+		bb_error_msg("can't resolve '%s'", hostname);
+#endif
+	}
+	if (ENABLE_FEATURE_CLEAN_UP && result)
+		freeaddrinfo(result);
+	return (rc != 0);
+}
+
+/* lookup the default nameserver and display it */
+static void server_print(void)
+{
+	char *server;
+	struct sockaddr *sa;
+
+#if ENABLE_FEATURE_IPV6
+	sa = (struct sockaddr*)_res._u._ext.nsaddrs[0];
+	if (!sa)
+#endif
+		sa = (struct sockaddr*)&_res.nsaddr_list[0];
+	server = xmalloc_sockaddr2dotted_noport(sa);
+
+	print_host(server, "Server:");
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(server);
+	bb_putchar('\n');
+}
+
+/* alter the global _res nameserver structure to use
+   an explicit dns server instead of what is in /etc/resolv.conf */
+static void set_default_dns(const char *server)
+{
+	len_and_sockaddr *lsa;
+
+	/* NB: this works even with, say, "[::1]:5353"! :) */
+	lsa = xhost2sockaddr(server, 53);
+
+	if (lsa->u.sa.sa_family == AF_INET) {
+		_res.nscount = 1;
+		/* struct copy */
+		_res.nsaddr_list[0] = lsa->u.sin;
+	}
+#if ENABLE_FEATURE_IPV6
+	/* Hoped libc can cope with IPv4 address there too.
+	 * No such luck, glibc 2.4 segfaults even with IPv6,
+	 * maybe I misunderstand how to make glibc use IPv6 addr?
+	 * (uclibc 0.9.31+ should work) */
+	if (lsa->u.sa.sa_family == AF_INET6) {
+		// glibc neither SEGVs nor sends any dgrams with this
+		// (strace shows no socket ops):
+		//_res.nscount = 0;
+		_res._u._ext.nscount = 1;
+		/* store a pointer to part of malloc'ed lsa */
+		_res._u._ext.nsaddrs[0] = &lsa->u.sin6;
+		/* must not free(lsa)! */
+	}
+#endif
+}
+
+int nslookup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nslookup_main(int argc, char **argv)
+{
+	/* We allow 1 or 2 arguments.
+	 * The first is the name to be looked up and the second is an
+	 * optional DNS server with which to do the lookup.
+	 * More than 3 arguments is an error to follow the pattern of the
+	 * standard nslookup */
+	if (!argv[1] || argv[1][0] == '-' || argc > 3)
+		bb_show_usage();
+
+	/* initialize DNS structure _res used in printing the default
+	 * name server and in the explicit name server option feature. */
+	res_init();
+	/* rfc2133 says this enables IPv6 lookups */
+	/* (but it also says "may be enabled in /etc/resolv.conf") */
+	/*_res.options |= RES_USE_INET6;*/
+
+	if (argv[2])
+		set_default_dns(argv[2]);
+
+	server_print();
+	return print_host(argv[1], "Name:");
+}
diff --git a/busybox-1.19.3/networking/ntpd.c b/busybox-1.19.3/networking/ntpd.c
new file mode 100644
index 0000000..206af00
--- /dev/null
+++ b/busybox-1.19.3/networking/ntpd.c
@@ -0,0 +1,2292 @@
+/*
+ * NTP client/server, based on OpenNTPD 3.9p1
+ *
+ * Author: Adam Tkac <vonsch@gmail.com>
+ *
+ * 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
+ * copyright notice:
+ *
+ ***********************************************************************
+ *                                                                     *
+ * Copyright (c) University of Delaware 1992-2009                      *
+ *                                                                     *
+ * Permission to use, copy, modify, and distribute this software and   *
+ * its documentation for any purpose with or without fee is hereby     *
+ * granted, provided that the above copyright notice appears in all    *
+ * copies and that both the copyright notice and this permission       *
+ * notice appear in supporting documentation, and that the name        *
+ * University of Delaware not be used in advertising or publicity      *
+ * pertaining to distribution of the software without specific,        *
+ * written prior permission. The University of Delaware makes no       *
+ * representations about the suitability this software for any         *
+ * purpose. It is provided "as is" without express or implied          *
+ * warranty.                                                           *
+ *                                                                     *
+ ***********************************************************************
+ */
+
+//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/timex.h>
+#ifndef IPTOS_LOWDELAY
+# define IPTOS_LOWDELAY 0x10
+#endif
+#ifndef IP_PKTINFO
+# error "Sorry, your kernel has to support IP_PKTINFO"
+#endif
+
+
+/* Verbosity control (max level of -dddd options accepted).
+ * max 5 is very talkative (and bloated). 2 is non-bloated,
+ * production level setting.
+ */
+#define MAX_VERBOSE     2
+
+
+/* High-level description of the algorithm:
+ *
+ * We start running with very small poll_exp, BURSTPOLL,
+ * 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:
+ * we collect new datapoints but ignore them for WATCH_THRESHOLD
+ * seconds. After WATCH_THRESHOLD seconds we look at accumulated
+ * offset and estimate frequency drift.
+ *
+ * (frequency measurement step seems to not be strictly needed,
+ * it is conditionally disabled with USING_INITIAL_FREQ_ESTIMATION
+ * define set to 0)
+ *
+ * After this, we enter "steady state": we collect a datapoint,
+ * we select the best peer, if this datapoint is not a new one
+ * (IOW: if this datapoint isn't for selected peer), sleep
+ * and collect another one; otherwise, use its offset to update
+ * frequency drift, if offset is somewhat large, reduce poll_exp,
+ * otherwise increase poll_exp.
+ *
+ * If offset is larger than STEP_THRESHOLD, which shouldn't normally
+ * happen, we assume that something "bad" happened (computer
+ * was hibernated, someone set totally wrong date, etc),
+ * then the time is stepped, all datapoints are discarded,
+ * and we go back to steady state.
+ */
+
+#define RETRY_INTERVAL  5       /* on error, retry in N secs */
+#define RESPONSE_INTERVAL 15    /* wait for reply up to N secs */
+#define INITIAL_SAMPLES 4       /* how many samples do we want for init */
+
+/* Clock discipline parameters and constants */
+
+/* Step threshold (sec). std ntpd uses 0.128.
+ * Using exact power of 2 (1/8) results in smaller code */
+#define STEP_THRESHOLD  0.125
+#define WATCH_THRESHOLD 128     /* stepout threshold (sec). std ntpd uses 900 (11 mins (!)) */
+/* NB: set WATCH_THRESHOLD to ~60 when debugging to save time) */
+//UNUSED: #define PANIC_THRESHOLD 1000    /* panic threshold (sec) */
+
+#define FREQ_TOLERANCE  0.000015 /* frequency tolerance (15 PPM) */
+#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) */
+#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)
+#define MINDISP         0.01    /* minimum dispersion (sec) */
+#define MAXDISP         16      /* maximum dispersion (sec) */
+#define MAXSTRAT        16      /* maximum stratum (infinity metric) */
+#define MAXDIST         1       /* distance threshold (sec) */
+#define MIN_SELECTED    1       /* minimum intersection survivors */
+#define MIN_CLUSTERED   3       /* minimum cluster survivors */
+
+#define MAXDRIFT        0.000500 /* frequency drift we can correct (500 PPM) */
+
+/* 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 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)
+ */
+#define POLLADJ_LIMIT   36
+/* If offset < POLLADJ_GATE * discipline_jitter, then we can increase
+ * poll interval (we think we can't improve timekeeping
+ * by staying at smaller poll).
+ */
+#define POLLADJ_GATE    4
+/* Compromise Allan intercept (sec). doc uses 1500, std ntpd uses 512 */
+#define ALLAN           512
+/* PLL loop gain */
+#define PLL             65536
+/* FLL loop gain [why it depends on MAXPOLL??] */
+#define FLL             (MAXPOLL + 1)
+/* Parameter averaging constant */
+#define AVG             4
+
+
+enum {
+	NTP_VERSION     = 4,
+	NTP_MAXSTRATUM  = 15,
+
+	NTP_DIGESTSIZE     = 16,
+	NTP_MSGSIZE_NOAUTH = 48,
+	NTP_MSGSIZE        = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE),
+
+	/* Status Masks */
+	MODE_MASK       = (7 << 0),
+	VERSION_MASK    = (7 << 3),
+	VERSION_SHIFT   = 3,
+	LI_MASK         = (3 << 6),
+
+	/* Leap Second Codes (high order two bits of m_status) */
+	LI_NOWARNING    = (0 << 6),    /* no warning */
+	LI_PLUSSEC      = (1 << 6),    /* add a second (61 seconds) */
+	LI_MINUSSEC     = (2 << 6),    /* minus a second (59 seconds) */
+	LI_ALARM        = (3 << 6),    /* alarm condition */
+
+	/* Mode values */
+	MODE_RES0       = 0,    /* reserved */
+	MODE_SYM_ACT    = 1,    /* symmetric active */
+	MODE_SYM_PAS    = 2,    /* symmetric passive */
+	MODE_CLIENT     = 3,    /* client */
+	MODE_SERVER     = 4,    /* server */
+	MODE_BROADCAST  = 5,    /* broadcast */
+	MODE_RES1       = 6,    /* reserved for NTP control message */
+	MODE_RES2       = 7,    /* reserved for private use */
+};
+
+//TODO: better base selection
+#define OFFSET_1900_1970 2208988800UL  /* 1970 - 1900 in seconds */
+
+#define NUM_DATAPOINTS  8
+
+typedef struct {
+	uint32_t int_partl;
+	uint32_t fractionl;
+} l_fixedpt_t;
+
+typedef struct {
+	uint16_t int_parts;
+	uint16_t fractions;
+} s_fixedpt_t;
+
+typedef struct {
+	uint8_t     m_status;     /* status of local clock and leap info */
+	uint8_t     m_stratum;
+	uint8_t     m_ppoll;      /* poll value */
+	int8_t      m_precision_exp;
+	s_fixedpt_t m_rootdelay;
+	s_fixedpt_t m_rootdisp;
+	uint32_t    m_refid;
+	l_fixedpt_t m_reftime;
+	l_fixedpt_t m_orgtime;
+	l_fixedpt_t m_rectime;
+	l_fixedpt_t m_xmttime;
+	uint32_t    m_keyid;
+	uint8_t     m_digest[NTP_DIGESTSIZE];
+} msg_t;
+
+typedef struct {
+	double d_recv_time;
+	double d_offset;
+	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;
+	double           next_action_time;
+	double           p_xmttime;
+	double           lastpkt_recv_time;
+	double           lastpkt_delay;
+	double           lastpkt_rootdelay;
+	double           lastpkt_rootdisp;
+	/* produced by filter algorithm: */
+	double           filter_offset;
+	double           filter_dispersion;
+	double           filter_jitter;
+	datapoint_t      filter_datapoint[NUM_DATAPOINTS];
+	/* last sent packet: */
+	msg_t            p_xmt_msg;
+} peer_t;
+
+
+#define USING_KERNEL_PLL_LOOP          1
+#define USING_INITIAL_FREQ_ESTIMATION  0
+
+enum {
+	OPT_n = (1 << 0),
+	OPT_q = (1 << 1),
+	OPT_N = (1 << 2),
+	OPT_x = (1 << 3),
+	/* Insert new options above this line. */
+	/* Non-compat options: */
+	OPT_w = (1 << 4),
+	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 << 8),
+};
+
+struct globals {
+	double   cur_time;
+	/* total round trip delay to currently selected reference clock */
+	double   rootdelay;
+	/* reference timestamp: time when the system clock was last set or corrected */
+	double   reftime;
+	/* total dispersion to currently selected reference clock */
+	double   rootdisp;
+
+	double   last_script_run;
+	char     *script_name;
+	llist_t  *ntp_peers;
+#if ENABLE_FEATURE_NTPD_SERVER
+	int      listen_fd;
+#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
+	 */
+	uint32_t refid;
+	uint8_t  ntp_status;
+	/* precision is defined as the larger of the resolution and time to
+	 * read the clock, in log2 units.  For instance, the precision of a
+	 * 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.
+	 *
+	 * 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.
+	 * (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.
+	 * 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.
+	 *
+	 * -6 is 1/64 sec, -7 is 1/128 sec and so on.
+	 */
+#define G_precision_exp  -8
+#define G_precision_sec  (1.0 / (1 << (- G_precision_exp)))
+	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_FREQ    3       /* initial frequency */
+#define STATE_SYNC      4       /* clock synchronized (normal operation) */
+	uint8_t  discipline_state;      // doc calls it c.state
+	uint8_t  poll_exp;              // s.poll
+	int      polladj_count;         // c.count
+	long     kernel_freq_drift;
+	peer_t   *last_update_peer;
+	double   last_update_offset;    // c.last
+	double   last_update_recv_time; // s.t
+	double   discipline_jitter;     // c.jitter
+	//double   cluster_offset;        // s.offset
+	//double   cluster_jitter;        // s.jitter
+#if !USING_KERNEL_PLL_LOOP
+	double   discipline_freq_drift; // c.freq
+	/* Maybe conditionally calculate wander? it's used only for logging */
+	double   discipline_wander;     // c.wander
+#endif
+};
+#define G (*ptr_to_globals)
+
+static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY;
+
+
+#define VERB1 if (MAX_VERBOSE && G.verbose)
+#define VERB2 if (MAX_VERBOSE >= 2 && G.verbose >= 2)
+#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)
+
+
+static double LOG2D(int a)
+{
+	if (a < 0)
+		return 1.0 / (1UL << -a);
+	return 1UL << a;
+}
+static ALWAYS_INLINE double SQUARE(double x)
+{
+	return x * x;
+}
+static ALWAYS_INLINE double MAXD(double a, double b)
+{
+	if (a > b)
+		return a;
+	return b;
+}
+static ALWAYS_INLINE double MIND(double a, double b)
+{
+	if (a < b)
+		return a;
+	return b;
+}
+static NOINLINE double my_SQRT(double X)
+{
+	union {
+		float   f;
+		int32_t i;
+	} v;
+	double invsqrt;
+	double Xhalf = X * 0.5;
+
+	/* Fast and good approximation to 1/sqrt(X), black magic */
+	v.f = X;
+	/*v.i = 0x5f3759df - (v.i >> 1);*/
+	v.i = 0x5f375a86 - (v.i >> 1); /* - this constant is slightly better */
+	invsqrt = v.f; /* better than 0.2% accuracy */
+
+	/* Refining it using Newton's method: x1 = x0 - f(x0)/f'(x0)
+	 * f(x) = 1/(x*x) - X  (f==0 when x = 1/sqrt(X))
+	 * f'(x) = -2/(x*x*x)
+	 * f(x)/f'(x) = (X - 1/(x*x)) / (2/(x*x*x)) = X*x*x*x/2 - x/2
+	 * x1 = x0 - (X*x0*x0*x0/2 - x0/2) = 1.5*x0 - X*x0*x0*x0/2 = x0*(1.5 - (X/2)*x0*x0)
+	 */
+	invsqrt = invsqrt * (1.5 - Xhalf * invsqrt * invsqrt); /* ~0.05% accuracy */
+	/* invsqrt = invsqrt * (1.5 - Xhalf * invsqrt * invsqrt); 2nd iter: ~0.0001% accuracy */
+	/* With 4 iterations, more than half results will be exact,
+	 * at 6th iterations result stabilizes with about 72% results exact.
+	 * We are well satisfied with 0.05% accuracy.
+	 */
+
+	return X * invsqrt; /* X * 1/sqrt(X) ~= sqrt(X) */
+}
+static ALWAYS_INLINE double SQRT(double X)
+{
+	/* If this arch doesn't use IEEE 754 floats, fall back to using libm */
+	if (sizeof(float) != 4)
+		return sqrt(X);
+
+	/* This avoids needing libm, saves about 0.5k on x86-32 */
+	return my_SQRT(X);
+}
+
+static double
+gettime1900d(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL); /* never fails */
+	G.cur_time = tv.tv_sec + (1.0e-6 * tv.tv_usec) + OFFSET_1900_1970;
+	return G.cur_time;
+}
+
+static void
+d_to_tv(double d, struct timeval *tv)
+{
+	tv->tv_sec = (long)d;
+	tv->tv_usec = (d - tv->tv_sec) * 1000000;
+}
+
+static double
+lfp_to_d(l_fixedpt_t lfp)
+{
+	double ret;
+	lfp.int_partl = ntohl(lfp.int_partl);
+	lfp.fractionl = ntohl(lfp.fractionl);
+	ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX);
+	return ret;
+}
+static double
+sfp_to_d(s_fixedpt_t sfp)
+{
+	double ret;
+	sfp.int_parts = ntohs(sfp.int_parts);
+	sfp.fractions = ntohs(sfp.fractions);
+	ret = (double)sfp.int_parts + ((double)sfp.fractions / USHRT_MAX);
+	return ret;
+}
+#if ENABLE_FEATURE_NTPD_SERVER
+static l_fixedpt_t
+d_to_lfp(double d)
+{
+	l_fixedpt_t lfp;
+	lfp.int_partl = (uint32_t)d;
+	lfp.fractionl = (uint32_t)((d - lfp.int_partl) * UINT_MAX);
+	lfp.int_partl = htonl(lfp.int_partl);
+	lfp.fractionl = htonl(lfp.fractionl);
+	return lfp;
+}
+static s_fixedpt_t
+d_to_sfp(double d)
+{
+	s_fixedpt_t sfp;
+	sfp.int_parts = (uint16_t)d;
+	sfp.fractions = (uint16_t)((d - sfp.int_parts) * USHRT_MAX);
+	sfp.int_parts = htons(sfp.int_parts);
+	sfp.fractions = htons(sfp.fractions);
+	return sfp;
+}
+#endif
+
+static double
+dispersion(const datapoint_t *dp)
+{
+	return dp->d_dispersion + FREQ_TOLERANCE * (G.cur_time - dp->d_recv_time);
+}
+
+static double
+root_distance(peer_t *p)
+{
+	/* The root synchronization distance is the maximum error due to
+	 * all causes of the local clock relative to the primary server.
+	 * It is defined as half the total delay plus total dispersion
+	 * plus peer jitter.
+	 */
+	return MAXD(MINDISP, p->lastpkt_rootdelay + p->lastpkt_delay) / 2
+		+ p->lastpkt_rootdisp
+		+ p->filter_dispersion
+		+ FREQ_TOLERANCE * (G.cur_time - p->lastpkt_recv_time)
+		+ p->filter_jitter;
+}
+
+static void
+set_next(peer_t *p, unsigned t)
+{
+	p->next_action_time = G.cur_time + t;
+}
+
+/*
+ * Peer clock filter and its helpers
+ */
+static void
+filter_datapoints(peer_t *p)
+{
+	int i, idx;
+	int got_newest;
+	double minoff, maxoff, wavg, sum, 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;
+	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;
+	}
+
+	idx = p->datapoint_idx; /* most recent datapoint */
+	/* Average offset:
+	 * Drop two outliers and take weighted average of the rest:
+	 * most_recent/2 + older1/4 + older2/8 ... + older5/32 + older6/32
+	 * we use older6/32, not older6/64 since sum of weights should be 1:
+	 * 1/2 + 1/4 + 1/8 + 1/16 + 1/32 + 1/32 = 1
+	 */
+	wavg = 0;
+	w = 0.5;
+	/*                     n-1
+	 *                     ---    dispersion(i)
+	 * filter_dispersion =  \     -------------
+	 *                      /       (i+1)
+	 *                     ---     2
+	 *                     i=0
+	 */
+	got_newest = 0;
+	sum = 0;
+	for (i = 0; i < NUM_DATAPOINTS; i++) {
+		VERB4 {
+			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)
+					? " (outlier by offset)" : ""
+			);
+		}
+
+		sum += dispersion(&p->filter_datapoint[idx]) / (2 << i);
+
+		if (minoff == p->filter_datapoint[idx].d_offset) {
+			minoff -= 1; /* so that we don't match it ever again */
+		} else
+		if (maxoff == p->filter_datapoint[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;
+			if (!got_newest) {
+				got_newest = 1;
+				newest_off = oldest_off;
+				newest_age = oldest_age;
+			}
+			x = oldest_off * w;
+			wavg += x;
+			w /= 2;
+		}
+
+		idx = (idx - 1) & (NUM_DATAPOINTS - 1);
+	}
+	p->filter_dispersion = sum;
+	wavg += x; /* add another older6/64 to form older6/32 */
+	/* Fix systematic underestimation with large poll intervals.
+	 * Imagine that we still have a bit of uncorrected drift,
+	 * and poll interval is big (say, 100 sec). Offsets form a progression:
+	 * 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 - 0.7 is most recent.
+	 * The algorithm above drops 0.0 and 0.7 as outliers,
+	 * and then we have this estimation, ~25% off from 0.7:
+	 * 0.1/32 + 0.2/32 + 0.3/16 + 0.4/8 + 0.5/4 + 0.6/2 = 0.503125
+	 */
+	x = oldest_age - newest_age;
+	if (x != 0) {
+		x = newest_age / x; /* in above example, 100 / (600 - 100) */
+		if (x < 1) { /* paranoia check */
+			x = (newest_off - oldest_off) * x; /* 0.5 * 100/500 = 0.1 */
+			wavg += x;
+		}
+	}
+	p->filter_offset = wavg;
+
+	/*                  +-----                 -----+ ^ 1/2
+	 *                  |       n-1                 |
+	 *                  |       ---                 |
+	 *                  |  1    \                2  |
+	 * filter_jitter =  | --- * /  (avg-offset_j)   |
+	 *                  |  n    ---                 |
+	 *                  |       j=0                 |
+	 *                  +-----                 -----+
+	 * where n is the number of valid datapoints in the filter (n > 1);
+	 * if filter_jitter < precision then filter_jitter = precision
+	 */
+	sum = 0;
+	for (i = 0; i < NUM_DATAPOINTS; i++) {
+		sum += SQUARE(wavg - p->filter_datapoint[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,
+			p->filter_dispersion,
+			p->filter_jitter);
+}
+
+static void
+reset_peer_stats(peer_t *p, double offset)
+{
+	int i;
+	bool small_ofs = fabs(offset) < 16 * STEP_THRESHOLD;
+
+	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;
+			}
+		} 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;
+		}
+	}
+	if (small_ofs) {
+		p->lastpkt_recv_time += offset;
+	} else {
+		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);
+}
+
+static void
+add_peers(char *s)
+{
+	peer_t *p;
+
+	p = xzalloc(sizeof(*p));
+	p->p_lsa = xhost2sockaddr(s, 123);
+	p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa);
+	p->p_fd = -1;
+	p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3);
+	p->next_action_time = G.cur_time; /* = set_next(p, 0); */
+	reset_peer_stats(p, 16 * STEP_THRESHOLD);
+
+	llist_add_to(&G.ntp_peers, p);
+	G.peer_cnt++;
+}
+
+static int
+do_sendto(int fd,
+		const struct sockaddr *from, const struct sockaddr *to, socklen_t addrlen,
+		msg_t *msg, ssize_t len)
+{
+	ssize_t ret;
+
+	errno = 0;
+	if (!from) {
+		ret = sendto(fd, msg, len, MSG_DONTWAIT, to, addrlen);
+	} else {
+		ret = send_to_from(fd, msg, len, MSG_DONTWAIT, to, from, addrlen);
+	}
+	if (ret != len) {
+		bb_perror_msg("send failed");
+		return -1;
+	}
+	return 0;
+}
+
+static void
+send_query_to_peer(peer_t *p)
+{
+	/* Why do we need to bind()?
+	 * See what happens when we don't bind:
+	 *
+	 * socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
+	 * setsockopt(3, SOL_IP, IP_TOS, [16], 4) = 0
+	 * gettimeofday({1259071266, 327885}, NULL) = 0
+	 * sendto(3, "xxx", 48, MSG_DONTWAIT, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("10.34.32.125")}, 16) = 48
+	 * ^^^ we sent it from some source port picked by kernel.
+	 * time(NULL)              = 1259071266
+	 * write(2, "ntpd: entering poll 15 secs\n", 28) = 28
+	 * poll([{fd=3, events=POLLIN}], 1, 15000) = 1 ([{fd=3, revents=POLLIN}])
+	 * recv(3, "yyy", 68, MSG_DONTWAIT) = 48
+	 * ^^^ this recv will receive packets to any local port!
+	 *
+	 * Uncomment this and use strace to see it in action:
+	 */
+#define PROBE_LOCAL_ADDR /* { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(p->query.fd, &lsa.u.sa, &lsa.len); } */
+
+	if (p->p_fd == -1) {
+		int fd, family;
+		len_and_sockaddr *local_lsa;
+
+		family = p->p_lsa->u.sa.sa_family;
+		p->p_fd = fd = xsocket_type(&local_lsa, family, SOCK_DGRAM);
+		/* local_lsa has "null" address and port 0 now.
+		 * bind() ensures we have a *particular port* selected by kernel
+		 * and remembered in p->p_fd, thus later recv(p->p_fd)
+		 * receives only packets sent to this port.
+		 */
+		PROBE_LOCAL_ADDR
+		xbind(fd, &local_lsa->u.sa, local_lsa->len);
+		PROBE_LOCAL_ADDR
+#if ENABLE_FEATURE_IPV6
+		if (family == AF_INET)
+#endif
+			setsockopt(fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
+		free(local_lsa);
+	}
+
+	/*
+	 * Send out a random 64-bit number as our transmit time.  The NTP
+	 * server will copy said number into the originate field on the
+	 * response that it sends us.  This is totally legal per the SNTP spec.
+	 *
+	 * The impact of this is two fold: we no longer send out the current
+	 * system time for the world to see (which may aid an attacker), and
+	 * it gives us a (not very secure) way of knowing that we're not
+	 * getting spoofed by an attacker that can't capture our traffic
+	 * but can spoof packets from the NTP server we're communicating with.
+	 *
+	 * Save the real transmit timestamp locally.
+	 */
+	p->p_xmt_msg.m_xmttime.int_partl = random();
+	p->p_xmt_msg.m_xmttime.fractionl = random();
+	p->p_xmttime = gettime1900d();
+
+	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;
+		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
+ * 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.
+ * Script should be ready to deal with this.
+ */
+static void run_script(const char *action, double offset)
+{
+	char *argv[3];
+	char *env1, *env2, *env3, *env4;
+
+	if (!G.script_name)
+		return;
+
+	argv[0] = (char*) G.script_name;
+	argv[1] = (char*) action;
+	argv[2] = NULL;
+
+	VERB1 bb_error_msg("executing '%s %s'", G.script_name, action);
+
+	env1 = xasprintf("%s=%u", "stratum", G.stratum);
+	putenv(env1);
+	env2 = xasprintf("%s=%ld", "freq_drift_ppm", G.kernel_freq_drift);
+	putenv(env2);
+	env3 = xasprintf("%s=%u", "poll_interval", 1 << G.poll_exp);
+	putenv(env3);
+	env4 = xasprintf("%s=%f", "offset", offset);
+	putenv(env4);
+	/* Other items of potential interest: selected peer,
+	 * rootdelay, reftime, rootdisp, refid, ntp_status,
+	 * last_update_offset, last_update_recv_time, discipline_jitter,
+	 * how many peers have reachable_bits = 0?
+	 */
+
+	/* Don't want to wait: it may run hwclock --systohc, and that
+	 * may take some time (seconds): */
+	/*spawn_and_wait(argv);*/
+	spawn(argv);
+
+	unsetenv("stratum");
+	unsetenv("freq_drift_ppm");
+	unsetenv("poll_interval");
+	unsetenv("offset");
+	free(env1);
+	free(env2);
+	free(env3);
+	free(env4);
+
+	G.last_script_run = G.cur_time;
+}
+
+static NOINLINE void
+step_time(double offset)
+{
+	llist_t *item;
+	double dtime;
+	struct timeval tv;
+	char buf[80];
+	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)
+		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);
+
+	/* Correct various fields which contain time-relative values: */
+
+	/* 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",
+		//	offset, pp->next_action_time, pp->next_action_time + offset);
+		pp->next_action_time += offset;
+	}
+	/* Globals: */
+	G.cur_time += offset;
+	G.last_update_recv_time += offset;
+	G.last_script_run += offset;
+}
+
+
+/*
+ * Selection and clustering, and their helpers
+ */
+typedef struct {
+	peer_t *p;
+	int    type;
+	double edge;
+	double opt_rd; /* optimization */
+} point_t;
+static int
+compare_point_edge(const void *aa, const void *bb)
+{
+	const point_t *a = aa;
+	const point_t *b = bb;
+	if (a->edge < b->edge) {
+		return -1;
+	}
+	return (a->edge > b->edge);
+}
+typedef struct {
+	peer_t *p;
+	double metric;
+} survivor_t;
+static int
+compare_survivor_metric(const void *aa, const void *bb)
+{
+	const survivor_t *a = aa;
+	const survivor_t *b = bb;
+	if (a->metric < b->metric) {
+		return -1;
+	}
+	return (a->metric > b->metric);
+}
+static int
+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);
+		return 0;
+	}
+#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);
+		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);
+		return 0;
+	}
+//TODO
+//	/* Do we have a loop? */
+//	if (p->refid == p->dstaddr || p->refid == s.refid)
+//		return 0;
+	return 1;
+}
+static peer_t*
+select_and_cluster(void)
+{
+	peer_t     *p;
+	llist_t    *item;
+	int        i, j;
+	int        size = 3 * G.peer_cnt;
+	/* for selection algorithm */
+	point_t    point[size];
+	unsigned   num_points, num_candidates;
+	double     low, high;
+	unsigned   num_falsetickers;
+	/* for cluster algorithm */
+	survivor_t survivor[size];
+	unsigned   num_survivors;
+
+	/* Selection */
+
+	num_points = 0;
+	item = G.ntp_peers;
+	if (G.initial_poll_complete) while (item != NULL) {
+		double rd, offset;
+
+		p = (peer_t *) item->data;
+		rd = root_distance(p);
+		offset = p->filter_offset;
+		if (!fit(p, rd)) {
+			item = item->link;
+			continue;
+		}
+
+		VERB4 bb_error_msg("interval: [%f %f %f] %s",
+				offset - rd,
+				offset,
+				offset + rd,
+				p->p_dotted
+		);
+		point[num_points].p = p;
+		point[num_points].type = -1;
+		point[num_points].edge = offset - rd;
+		point[num_points].opt_rd = rd;
+		num_points++;
+		point[num_points].p = p;
+		point[num_points].type = 0;
+		point[num_points].edge = offset;
+		point[num_points].opt_rd = rd;
+		num_points++;
+		point[num_points].p = p;
+		point[num_points].type = 1;
+		point[num_points].edge = offset + rd;
+		point[num_points].opt_rd = rd;
+		num_points++;
+		item = item->link;
+	}
+	num_candidates = num_points / 3;
+	if (num_candidates == 0) {
+		VERB3 bb_error_msg("no valid datapoints, no peer selected");
+		return NULL;
+	}
+//TODO: sorting does not seem to be done in reference code
+	qsort(point, num_points, sizeof(point[0]), compare_point_edge);
+
+	/* Start with the assumption that there are no falsetickers.
+	 * Attempt to find a nonempty intersection interval containing
+	 * the midpoints of all truechimers.
+	 * If a nonempty interval cannot be found, increase the number
+	 * of assumed falsetickers by one and try again.
+	 * If a nonempty interval is found and the number of falsetickers
+	 * is less than the number of truechimers, a majority has been found
+	 * and the midpoint of each truechimer represents
+	 * the candidates available to the cluster algorithm.
+	 */
+	num_falsetickers = 0;
+	while (1) {
+		int c;
+		unsigned num_midpoints = 0;
+
+		low = 1 << 9;
+		high = - (1 << 9);
+		c = 0;
+		for (i = 0; i < num_points; i++) {
+			/* We want to do:
+			 * if (point[i].type == -1) c++;
+			 * if (point[i].type == 1) c--;
+			 * and it's simpler to do it this way:
+			 */
+			c -= point[i].type;
+			if (c >= num_candidates - num_falsetickers) {
+				/* If it was c++ and it got big enough... */
+				low = point[i].edge;
+				break;
+			}
+			if (point[i].type == 0)
+				num_midpoints++;
+		}
+		c = 0;
+		for (i = num_points-1; i >= 0; i--) {
+			c += point[i].type;
+			if (c >= num_candidates - num_falsetickers) {
+				high = point[i].edge;
+				break;
+			}
+			if (point[i].type == 0)
+				num_midpoints++;
+		}
+		/* If the number of midpoints is greater than the number
+		 * of allowed falsetickers, the intersection contains at
+		 * least one truechimer with no midpoint - bad.
+		 * Also, interval should be nonempty.
+		 */
+		if (num_midpoints <= num_falsetickers && low < high)
+			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);
+			return NULL;
+		}
+	}
+	VERB3 bb_error_msg("selected interval: [%f, %f]; candidates:%d falsetickers:%d",
+			low, high, num_candidates, num_falsetickers);
+
+	/* Clustering */
+
+	/* Construct a list of survivors (p, metric)
+	 * from the chime list, where metric is dominated
+	 * first by stratum and then by root distance.
+	 * All other things being equal, this is the order of preference.
+	 */
+	num_survivors = 0;
+	for (i = 0; i < num_points; i++) {
+		if (point[i].edge < low || point[i].edge > high)
+			continue;
+		p = point[i].p;
+		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",
+			num_survivors, survivor[num_survivors].metric, p->p_dotted);
+		num_survivors++;
+	}
+	/* There must be at least MIN_SELECTED survivors to satisfy the
+	 * correctness assertions. Ordinarily, the Byzantine criteria
+	 * require four survivors, but for the demonstration here, one
+	 * is acceptable.
+	 */
+	if (num_survivors < MIN_SELECTED) {
+		VERB3 bb_error_msg("num_survivors %d < %d, no peer selected",
+				num_survivors, MIN_SELECTED);
+		return NULL;
+	}
+
+//looks like this is ONLY used by the fact that later we pick survivor[0].
+//we can avoid sorting then, just find the minimum once!
+	qsort(survivor, num_survivors, sizeof(survivor[0]), compare_survivor_metric);
+
+	/* For each association p in turn, calculate the selection
+	 * jitter p->sjitter as the square root of the sum of squares
+	 * (p->offset - q->offset) over all q associations. The idea is
+	 * to repeatedly discard the survivor with maximum selection
+	 * jitter until a termination condition is met.
+	 */
+	while (1) {
+		unsigned max_idx = max_idx;
+		double max_selection_jitter = max_selection_jitter;
+		double min_jitter = min_jitter;
+
+		if (num_survivors <= MIN_CLUSTERED) {
+			VERB3 bb_error_msg("num_survivors %d <= %d, not discarding more",
+					num_survivors, MIN_CLUSTERED);
+			break;
+		}
+
+		/* To make sure a few survivors are left
+		 * for the clustering algorithm to chew on,
+		 * we stop if the number of survivors
+		 * is less than or equal to MIN_CLUSTERED (3).
+		 */
+		for (i = 0; i < num_survivors; i++) {
+			double selection_jitter_sq;
+
+			p = survivor[i].p;
+			if (i == 0 || p->filter_jitter < min_jitter)
+				min_jitter = p->filter_jitter;
+
+			selection_jitter_sq = 0;
+			for (j = 0; j < num_survivors; j++) {
+				peer_t *q = survivor[j].p;
+				selection_jitter_sq += SQUARE(p->filter_offset - q->filter_offset);
+			}
+			if (i == 0 || selection_jitter_sq > max_selection_jitter) {
+				max_selection_jitter = selection_jitter_sq;
+				max_idx = i;
+			}
+			VERB5 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",
+				max_idx, max_selection_jitter, min_jitter);
+
+		/* If the maximum selection jitter is less than the
+		 * minimum peer jitter, then tossing out more survivors
+		 * will not lower the minimum peer jitter, so we might
+		 * 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",
+					max_selection_jitter, min_jitter, num_survivors);
+			break;
+		}
+
+		/* Delete survivor[max_idx] from the list
+		 * and go around again.
+		 */
+		VERB5 bb_error_msg("dropping survivor %d", max_idx);
+		num_survivors--;
+		while (max_idx < num_survivors) {
+			survivor[max_idx] = survivor[max_idx + 1];
+			max_idx++;
+		}
+	}
+
+	if (0) {
+		/* Combine the offsets of the clustering algorithm survivors
+		 * using a weighted average with weight determined by the root
+		 * distance. Compute the selection jitter as the weighted RMS
+		 * difference between the first survivor and the remaining
+		 * survivors. In some cases the inherent clock jitter can be
+		 * reduced by not using this algorithm, especially when frequent
+		 * clockhopping is involved. bbox: thus we don't do it.
+		 */
+		double x, y, z, w;
+		y = z = w = 0;
+		for (i = 0; i < num_survivors; i++) {
+			p = survivor[i].p;
+			x = root_distance(p);
+			y += 1 / x;
+			z += p->filter_offset / x;
+			w += SQUARE(p->filter_offset - survivor[0].p->filter_offset) / x;
+		}
+		//G.cluster_offset = z / y;
+		//G.cluster_jitter = SQRT(w / y);
+	}
+
+	/* Pick the best clock. If the old system peer is on the list
+	 * and at the same stratum as the first survivor on the list,
+	 * then don't do a clock hop. Otherwise, select the first
+	 * survivor on the list as the new system peer.
+	 */
+	p = survivor[0].p;
+	if (G.last_update_peer
+	 && G.last_update_peer->lastpkt_stratum <= p->lastpkt_stratum
+	) {
+		/* 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");
+				p = G.last_update_peer;
+				goto keep_old;
+			}
+		}
+	}
+	G.last_update_peer = p;
+ keep_old:
+	VERB3 bb_error_msg("selected peer %s filter_offset:%f age:%f",
+			p->p_dotted,
+			p->filter_offset,
+			G.cur_time - p->lastpkt_recv_time
+	);
+	return p;
+}
+
+
+/*
+ * Local clock discipline and its helpers
+ */
+static void
+set_new_values(int disc_state, double offset, double recv_time)
+{
+	/* Enter new state and set state variables. Note we use the 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",
+			disc_state, offset, recv_time);
+	G.discipline_state = disc_state;
+	G.last_update_offset = offset;
+	G.last_update_recv_time = recv_time;
+}
+/* Return: -1: decrease poll interval, 0: leave as is, 1: increase */
+static NOINLINE int
+update_local_clock(peer_t *p)
+{
+	int rc;
+	struct timex tmx;
+	/* Note: can use G.cluster_offset instead: */
+	double offset = p->filter_offset;
+	double recv_time = p->lastpkt_recv_time;
+	double abs_offset;
+#if !USING_KERNEL_PLL_LOOP
+	double freq_drift;
+#endif
+	double since_last_update;
+	double etemp, dtemp;
+
+	abs_offset = fabs(offset);
+
+#if 0
+	/* If needed, -S script can do it by looking at $offset
+	 * env var and killing parent */
+	/* If the offset is too large, give up and go home */
+	if (abs_offset > PANIC_THRESHOLD) {
+		bb_error_msg_and_die("offset %f far too big, exiting", offset);
+	}
+#endif
+
+	/* If this is an old update, for instance as the result
+	 * of a system peer change, avoid it. We never use
+	 * 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);
+		return 0; /* "leave poll interval as is" */
+	}
+
+	/* Clock state machine transition function. This is where the
+	 * action is and defines how the system reacts to large time
+	 * and frequency errors.
+	 */
+	since_last_update = recv_time - G.reftime;
+#if !USING_KERNEL_PLL_LOOP
+	freq_drift = 0;
+#endif
+#if USING_INITIAL_FREQ_ESTIMATION
+	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",
+					WATCH_THRESHOLD - since_last_update);
+			return 0; /* "leave poll interval as is" */
+		}
+# if !USING_KERNEL_PLL_LOOP
+		freq_drift = (offset - G.last_update_offset) / since_last_update;
+# endif
+	}
+#endif
+
+	/* There are two main regimes: when the
+	 * offset exceeds the step threshold and when it does not.
+	 */
+	if (abs_offset > STEP_THRESHOLD) {
+		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);
+			G.discipline_state = STATE_SPIK;
+			return -1; /* "decrease poll interval" */
+
+		case STATE_SPIK:
+			/* 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);
+				return -1; /* "decrease poll interval" */
+			}
+			/* fall through: we need to step */
+		} /* switch */
+
+		/* Step the time and clamp down the poll interval.
+		 *
+		 * In NSET state an initial frequency correction is
+		 * not available, usually because the frequency file has
+		 * not yet been written. Since the time is outside the
+		 * capture range, the clock is stepped. The frequency
+		 * will be set directly following the stepout interval.
+		 *
+		 * In FSET state the initial frequency has been set
+		 * from the frequency file. Since the time is outside
+		 * the capture range, the clock is stepped immediately,
+		 * rather than after the stepout interval. Guys get
+		 * nervous if it takes 17 minutes to set the clock for
+		 * the first time.
+		 *
+		 * In SPIK state the stepout threshold has expired and
+		 * the phase is still above the step threshold. Note
+		 * that a single spike greater than the step threshold
+		 * is always suppressed, even at the longer poll
+		 * intervals.
+		 */
+		VERB3 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. */
+			exit(0);
+		}
+
+		G.polladj_count = 0;
+		G.poll_exp = MINPOLL;
+		G.stratum = MAXSTRAT;
+
+		run_script("step", 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);
+
+	} 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);
+			G.polladj_count = 0;
+			G.poll_exp = MINPOLL;
+		}
+
+		/* Compute the clock jitter as the RMS of exponentially
+		 * 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));
+		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:
+			if (option_mask32 & OPT_q) {
+				/* We were only asked to set time once.
+				 * The clock is precise enough, no need to step.
+				 */
+				exit(0);
+			}
+#if USING_INITIAL_FREQ_ESTIMATION
+			/* This is the first update received and the frequency
+			 * has not been initialized. The first thing to do
+			 * is directly measure the oscillator frequency.
+			 */
+			set_new_values(STATE_FREQ, offset, recv_time);
+#else
+			set_new_values(STATE_SYNC, offset, recv_time);
+#endif
+			VERB3 bb_error_msg("transitioning to FREQ, datapoint ignored");
+			return 0; /* "leave poll interval as is" */
+
+#if 0 /* this is dead code for now */
+		case STATE_FSET:
+			/* This is the first update and the frequency
+			 * has been initialized. Adjust the phase, but
+			 * don't adjust the frequency until the next update.
+			 */
+			set_new_values(STATE_SYNC, offset, recv_time);
+			/* freq_drift remains 0 */
+			break;
+#endif
+
+#if USING_INITIAL_FREQ_ESTIMATION
+		case STATE_FREQ:
+			/* since_last_update >= WATCH_THRESHOLD, we waited enough.
+			 * Correct the phase and frequency and switch to SYNC state.
+			 * freq_drift was already estimated (see code above)
+			 */
+			set_new_values(STATE_SYNC, offset, recv_time);
+			break;
+#endif
+
+		default:
+#if !USING_KERNEL_PLL_LOOP
+			/* Compute freq_drift due to PLL and FLL contributions.
+			 *
+			 * The FLL and PLL frequency gain constants
+			 * depend on the poll interval and Allan
+			 * intercept. The FLL is not used below one-half
+			 * the Allan intercept. Above that the loop gain
+			 * increases in steps to 1 / AVG.
+			 */
+			if ((1 << G.poll_exp) > ALLAN / 2) {
+				etemp = FLL - G.poll_exp;
+				if (etemp < AVG)
+					etemp = AVG;
+				freq_drift += (offset - G.last_update_offset) / (MAXD(since_last_update, ALLAN) * etemp);
+			}
+			/* For the PLL the integration interval
+			 * (numerator) is the minimum of the update
+			 * interval and poll interval. This allows
+			 * oversampling, but not undersampling.
+			 */
+			etemp = MIND(since_last_update, (1 << G.poll_exp));
+			dtemp = (4 * PLL) << G.poll_exp;
+			freq_drift += offset * etemp / SQUARE(dtemp);
+#endif
+			set_new_values(STATE_SYNC, offset, recv_time);
+			break;
+		}
+		if (G.stratum != p->lastpkt_stratum + 1) {
+			G.stratum = p->lastpkt_stratum + 1;
+			run_script("stratum", offset);
+		}
+	}
+
+	G.reftime = G.cur_time;
+	G.ntp_status = p->lastpkt_status;
+	G.refid = p->lastpkt_refid;
+	G.rootdelay = p->lastpkt_rootdelay + p->lastpkt_delay;
+	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);
+
+	/* 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
+	 * to values suitable for adjtimex.
+	 */
+#if !USING_KERNEL_PLL_LOOP
+	/* Calculate the new frequency drift and frequency stability (wander).
+	 * Compute the clock wander as the RMS of exponentially weighted
+	 * frequency differences. This is not used directly, but can,
+	 * along with the jitter, be a highly useful monitoring and
+	 * debugging tool.
+	 */
+	dtemp = G.discipline_freq_drift + freq_drift;
+	G.discipline_freq_drift = MAXD(MIND(MAXDRIFT, dtemp), -MAXDRIFT);
+	etemp = SQUARE(G.discipline_wander);
+	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",
+			G.discipline_freq_drift,
+			(long)(G.discipline_freq_drift * 65536e6),
+			freq_drift,
+			G.discipline_wander);
+#endif
+	VERB3 {
+		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);
+	}
+
+	memset(&tmx, 0, sizeof(tmx));
+#if 0
+//doesn't work, offset remains 0 (!) in kernel:
+//ntpd:  set adjtimex freq:1786097 tmx.offset:77487
+//ntpd: prev adjtimex freq:1786097 tmx.offset:0
+//ntpd:  cur adjtimex freq:1786097 tmx.offset:0
+	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.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);
+	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
+	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);
+
+	return 1; /* "ok to increase poll interval" */
+}
+
+
+/*
+ * We've got a new reply packet from a peer, process it
+ * (helpers first)
+ */
+static unsigned
+retry_interval(void)
+{
+	/* Local problem, want to retry soon */
+	unsigned interval, r;
+	interval = RETRY_INTERVAL;
+	r = random();
+	interval += r % (unsigned)(RETRY_INTERVAL / 4);
+	VERB3 bb_error_msg("chose retry interval:%u", interval);
+	return interval;
+}
+static unsigned
+poll_interval(int exponent)
+{
+	unsigned interval, r;
+	exponent = G.poll_exp + exponent;
+	if (exponent < 0)
+		exponent = 0;
+	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);
+	return interval;
+}
+static NOINLINE void
+recv_and_process_peer_pkt(peer_t *p)
+{
+	int         rc;
+	ssize_t     size;
+	msg_t       msg;
+	double      T1, T2, T3, T4;
+	unsigned    interval;
+	datapoint_t *datapoint;
+	peer_t      *q;
+
+	/* 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?
+	 */
+	size = recv(p->p_fd, &msg, sizeof(msg), MSG_DONTWAIT);
+	if (size == -1) {
+		bb_perror_msg("recv(%s) error", p->p_dotted);
+		if (errno == EHOSTUNREACH || errno == EHOSTDOWN
+		 || errno == ENETUNREACH || errno == ENETDOWN
+		 || errno == ECONNREFUSED || errno == EADDRNOTAVAIL
+		 || errno == EAGAIN
+		) {
+//TODO: always do this?
+			interval = retry_interval();
+			goto set_next_and_close_sock;
+		}
+		xfunc_die();
+	}
+
+	if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
+		bb_error_msg("malformed packet received from %s", p->p_dotted);
+		goto bail;
+	}
+
+	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;
+	}
+
+	if ((msg.m_status & LI_ALARM) == LI_ALARM
+	 || msg.m_stratum == 0
+	 || msg.m_stratum > NTP_MAXSTRATUM
+	) {
+// 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;
+	}
+
+//	/* Verify valid root distance */
+//	if (msg.m_rootdelay / 2 + msg.m_rootdisp >= MAXDISP || p->lastpkt_reftime > msg.m_xmt)
+//		return;                 /* invalid header values */
+
+	p->lastpkt_status = msg.m_status;
+	p->lastpkt_stratum = msg.m_stratum;
+	p->lastpkt_rootdelay = sfp_to_d(msg.m_rootdelay);
+	p->lastpkt_rootdisp = sfp_to_d(msg.m_rootdisp);
+	p->lastpkt_refid = msg.m_refid;
+
+	/*
+	 * From RFC 2030 (with a correction to the delay math):
+	 *
+	 * Timestamp Name          ID   When Generated
+	 * ------------------------------------------------------------
+	 * Originate Timestamp     T1   time request sent by client
+	 * Receive Timestamp       T2   time request received by server
+	 * Transmit Timestamp      T3   time reply sent by server
+	 * Destination Timestamp   T4   time reply received by client
+	 *
+	 * The roundtrip delay and local clock offset are defined as
+	 *
+	 * delay = (T4 - T1) - (T3 - T2); offset = ((T2 - T1) + (T3 - T4)) / 2
+	 */
+	T1 = p->p_xmttime;
+	T2 = lfp_to_d(msg.m_rectime);
+	T3 = lfp_to_d(msg.m_xmttime);
+	T4 = G.cur_time;
+
+	p->lastpkt_recv_time = T4;
+
+	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.
+	 */
+	p->lastpkt_delay = (T4 - T1) - (T3 - T2);
+	if (p->lastpkt_delay < G_precision_sec)
+		p->lastpkt_delay = G_precision_sec;
+	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;
+		}
+	}
+
+	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",
+			p->p_dotted,
+			p->reachable_bits,
+			datapoint->d_offset,
+			p->lastpkt_delay,
+			p->lastpkt_status,
+			p->lastpkt_stratum,
+			p->lastpkt_refid,
+			p->lastpkt_rootdelay
+			/* not shown: m_ppoll, m_precision_exp, m_rootdisp,
+			 * m_reftime, m_orgtime, m_rectime, m_xmttime
+			 */
+		);
+	}
+
+	/* Muck with statictics and update the clock */
+	filter_datapoints(p);
+	q = select_and_cluster();
+	rc = -1;
+	if (q) {
+		rc = 0;
+		if (!(option_mask32 & OPT_w)) {
+			rc = update_local_clock(q);
+			/* If drift is dangerously large, immediately
+			 * drop poll interval one step down.
+			 */
+			if (fabs(q->filter_offset) >= POLLDOWN_OFFSET) {
+				VERB3 bb_error_msg("offset:%f > POLLDOWN_OFFSET", q->filter_offset);
+				goto poll_down;
+			}
+		}
+	}
+	/* else: no peer selected, rc = -1: we want to poll more often */
+
+	if (rc != 0) {
+		/* Adjust the poll interval by comparing the current offset
+		 * with the clock jitter. If the offset is less than
+		 * the clock jitter times a constant, then the averaging interval
+		 * 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) {
+			/* was += G.poll_exp but it is a bit
+			 * too optimistic for my taste at high poll_exp's */
+			G.polladj_count += MINPOLL;
+			if (G.polladj_count > POLLADJ_LIMIT) {
+				G.polladj_count = 0;
+				if (G.poll_exp < MAXPOLL) {
+					G.poll_exp++;
+					VERB3 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);
+			}
+		} else {
+			G.polladj_count -= G.poll_exp * 2;
+			if (G.polladj_count < -POLLADJ_LIMIT || G.poll_exp >= BIGPOLL) {
+ poll_down:
+				G.polladj_count = 0;
+				if (G.poll_exp > MINPOLL) {
+					llist_t *item;
+
+					G.poll_exp--;
+					/* Correct p->next_action_time in each peer
+					 * which waits for sending, so that they send earlier.
+					 * Old pp->next_action_time are on the order
+					 * of t + (1 << old_poll_exp) + small_random,
+					 * we simply need to subtract ~half of that.
+					 */
+					for (item = G.ntp_peers; item != NULL; item = item->link) {
+						peer_t *pp = (peer_t *) item->data;
+						if (pp->p_fd < 0)
+							pp->next_action_time -= (1 << G.poll_exp);
+					}
+					VERB3 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);
+			}
+		}
+	}
+
+	/* Decide when to send new query for this peer */
+	interval = poll_interval(0);
+
+ set_next_and_close_sock:
+	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
+static NOINLINE void
+recv_and_process_client_pkt(void /*int fd*/)
+{
+	ssize_t          size;
+	//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);
+	from = xzalloc(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) {
+			if (errno == EAGAIN)
+				goto bail;
+			bb_perror_msg_and_die("recv");
+		}
+		addr = xmalloc_sockaddr2dotted_noport(from);
+		bb_error_msg("malformed packet received from %s: size %u", addr, (int)size);
+		free(addr);
+		goto bail;
+	}
+
+	query_status = msg.m_status;
+	query_xmttime = msg.m_xmttime;
+
+	/* Build a reply packet */
+	memset(&msg, 0, sizeof(msg));
+	msg.m_status = G.stratum < MAXSTRAT ? G.ntp_status : LI_ALARM;
+	msg.m_status |= (query_status & VERSION_MASK);
+	msg.m_status |= ((query_status & MODE_MASK) == MODE_CLIENT) ?
+			 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 */
+	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,
+		/*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len,
+		&msg, size);
+
+ bail:
+	free(to);
+	free(from);
+}
+#endif
+
+/* Upstream ntpd's options:
+ *
+ * -4   Force DNS resolution of host names to the IPv4 namespace.
+ * -6   Force DNS resolution of host names to the IPv6 namespace.
+ * -a   Require cryptographic authentication for broadcast client,
+ *      multicast client and symmetric passive associations.
+ *      This is the default.
+ * -A   Do not require cryptographic authentication for broadcast client,
+ *      multicast client and symmetric passive associations.
+ *      This is almost never a good idea.
+ * -b   Enable the client to synchronize to broadcast servers.
+ * -c conffile
+ *      Specify the name and path of the configuration file,
+ *      default /etc/ntp.conf
+ * -d   Specify debugging mode. This option may occur more than once,
+ *      with each occurrence indicating greater detail of display.
+ * -D level
+ *      Specify debugging level directly.
+ * -f driftfile
+ *      Specify the name and path of the frequency file.
+ *      This is the same operation as the "driftfile FILE"
+ *      configuration command.
+ * -g   Normally, ntpd exits with a message to the system log
+ *      if the offset exceeds the panic threshold, which is 1000 s
+ *      by default. This option allows the time to be set to any value
+ *      without restriction; however, this can happen only once.
+ *      If the threshold is exceeded after that, ntpd will exit
+ *      with a message to the system log. This option can be used
+ *      with the -q and -x options. See the tinker command for other options.
+ * -i jaildir
+ *      Chroot the server to the directory jaildir. This option also implies
+ *      that the server attempts to drop root privileges at startup
+ *      (otherwise, chroot gives very little additional security).
+ *      You may need to also specify a -u option.
+ * -k keyfile
+ *      Specify the name and path of the symmetric key file,
+ *      default /etc/ntp/keys. This is the same operation
+ *      as the "keys FILE" configuration command.
+ * -l logfile
+ *      Specify the name and path of the log file. The default
+ *      is the system log file. This is the same operation as
+ *      the "logfile FILE" configuration command.
+ * -L   Do not listen to virtual IPs. The default is to listen.
+ * -n   Don't fork.
+ * -N   To the extent permitted by the operating system,
+ *      run the ntpd at the highest priority.
+ * -p pidfile
+ *      Specify the name and path of the file used to record the ntpd
+ *      process ID. This is the same operation as the "pidfile FILE"
+ *      configuration command.
+ * -P priority
+ *      To the extent permitted by the operating system,
+ *      run the ntpd at the specified priority.
+ * -q   Exit the ntpd just after the first time the clock is set.
+ *      This behavior mimics that of the ntpdate program, which is
+ *      to be retired. The -g and -x options can be used with this option.
+ *      Note: The kernel time discipline is disabled with this option.
+ * -r broadcastdelay
+ *      Specify the default propagation delay from the broadcast/multicast
+ *      server to this client. This is necessary only if the delay
+ *      cannot be computed automatically by the protocol.
+ * -s statsdir
+ *      Specify the directory path for files created by the statistics
+ *      facility. This is the same operation as the "statsdir DIR"
+ *      configuration command.
+ * -t key
+ *      Add a key number to the trusted key list. This option can occur
+ *      more than once.
+ * -u user[:group]
+ *      Specify a user, and optionally a group, to switch to.
+ * -v variable
+ * -V variable
+ *      Add a system variable listed by default.
+ * -x   Normally, the time is slewed if the offset is less than the step
+ *      threshold, which is 128 ms by default, and stepped if above
+ *      the threshold. This option sets the threshold to 600 s, which is
+ *      well within the accuracy window to set the clock manually.
+ *      Note: since the slew rate of typical Unix kernels is limited
+ *      to 0.5 ms/s, each second of adjustment requires an amortization
+ *      interval of 2000 s. Thus, an adjustment as much as 600 s
+ *      will take almost 14 days to complete. This option can be used
+ *      with the -g and -q options. See the tinker command for other options.
+ *      Note: The kernel time discipline is disabled with this option.
+ */
+
+/* By doing init in a separate function we decrease stack usage
+ * in main loop.
+ */
+static NOINLINE void ntp_init(char **argv)
+{
+	unsigned opts;
+	llist_t *peers;
+
+	srandom(getpid());
+
+	if (getuid())
+		bb_error_msg_and_die(bb_msg_you_must_be_root);
+
+	/* Set some globals */
+	G.stratum = MAXSTRAT;
+	if (BURSTPOLL != 0)
+		G.poll_exp = BURSTPOLL; /* speeds up initial sync */
+	G.last_script_run = G.reftime = G.last_update_recv_time = gettime1900d(); /* sets G.cur_time too */
+
+	/* Parse options */
+	peers = NULL;
+	opt_complementary = "dd:p::wn"; /* d: counter; p: list; -w implies -n */
+	opts = getopt32(argv,
+			"nqNx" /* compat */
+			"wp:S:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
+			"d" /* compat */
+			"46aAbgL", /* compat, ignored */
+			&peers, &G.script_name, &G.verbose);
+	if (!(opts & (OPT_p|OPT_l)))
+		bb_show_usage();
+//	if (opts & OPT_x) /* disable stepping, only slew is allowed */
+//		G.time_was_stepped = 1;
+	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;
+	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));
+	}
+#endif
+	/* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
+	if (opts & OPT_N)
+		setpriority(PRIO_PROCESS, 0, -15);
+
+	/* 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;
+int ntpd_main(int argc UNUSED_PARAM, char **argv)
+{
+#undef G
+	struct globals G;
+	struct pollfd *pfd;
+	peer_t **idx2peer;
+	unsigned cnt;
+
+	memset(&G, 0, sizeof(G));
+	SET_PTR_TO_GLOBALS(&G);
+
+	ntp_init(argv);
+
+	/* If ENABLE_FEATURE_NTPD_SERVER, + 1 for listen_fd: */
+	cnt = G.peer_cnt + ENABLE_FEATURE_NTPD_SERVER;
+	idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt);
+	pfd = xzalloc(sizeof(pfd[0]) * cnt);
+
+	/* 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_SAMPLES packets,
+	 * since last reply does not come back instantaneously.
+	 */
+	cnt = G.peer_cnt * (INITIAL_SAMPLES + 1);
+
+	while (!bb_got_signal) {
+		llist_t *item;
+		unsigned i, j;
+		int nfds, timeout;
+		double nextaction;
+
+		/* Nothing between here and poll() blocks for any significant time */
+
+		nextaction = G.cur_time + 3600;
+
+		i = 0;
+#if ENABLE_FEATURE_NTPD_SERVER
+		if (G.listen_fd != -1) {
+			pfd[0].fd = G.listen_fd;
+			pfd[0].events = POLLIN;
+			i++;
+		}
+#endif
+		/* Pass over peer list, send requests, time out on receives */
+		for (item = G.ntp_peers; item != NULL; item = item->link) {
+			peer_t *p = (peer_t *) item->data;
+
+			if (p->next_action_time <= G.cur_time) {
+				if (p->p_fd == -1) {
+					/* Time to send new req */
+					if (--cnt == 0) {
+						G.initial_poll_complete = 1;
+					}
+					send_query_to_peer(p);
+				} else {
+					/* Timed out waiting for reply */
+					close(p->p_fd);
+					p->p_fd = -1;
+					timeout = poll_interval(-2); /* -2: try a bit sooner */
+					bb_error_msg("timed out waiting for %s, reach 0x%02x, next query in %us",
+							p->p_dotted, p->reachable_bits, timeout);
+					set_next(p, timeout);
+				}
+			}
+
+			if (p->next_action_time < nextaction)
+				nextaction = p->next_action_time;
+
+			if (p->p_fd >= 0) {
+				/* Wait for reply from this peer */
+				pfd[i].fd = p->p_fd;
+				pfd[i].events = POLLIN;
+				idx2peer[i] = p;
+				i++;
+			}
+		}
+
+		timeout = nextaction - G.cur_time;
+		if (timeout < 0)
+			timeout = 0;
+		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);
+		nfds = poll(pfd, i, timeout * 1000);
+		gettime1900d(); /* sets G.cur_time */
+		if (nfds <= 0) {
+			if (G.script_name && 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;
+		}
+
+		/* Process any received packets */
+		j = 0;
+#if ENABLE_FEATURE_NTPD_SERVER
+		if (G.listen_fd != -1) {
+			if (pfd[0].revents /* & (POLLIN|POLLERR)*/) {
+				nfds--;
+				recv_and_process_client_pkt(/*G.listen_fd*/);
+				gettime1900d(); /* sets G.cur_time */
+			}
+			j = 1;
+		}
+#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 */
+			}
+		}
+	} /* while (!bb_got_signal) */
+
+	kill_myself_with_sig(bb_got_signal);
+}
+
+
+
+
+
+
+/*** openntpd-4.6 uses only adjtime, not adjtimex ***/
+
+/*** ntp-4.2.6/ntpd/ntp_loopfilter.c - adjtimex usage ***/
+
+#if 0
+static double
+direct_freq(double fp_offset)
+{
+#ifdef KERNEL_PLL
+	/*
+	 * If the kernel is enabled, we need the residual offset to
+	 * calculate the frequency correction.
+	 */
+	if (pll_control && kern_enable) {
+		memset(&ntv, 0, sizeof(ntv));
+		ntp_adjtime(&ntv);
+#ifdef STA_NANO
+		clock_offset = ntv.offset / 1e9;
+#else /* STA_NANO */
+		clock_offset = ntv.offset / 1e6;
+#endif /* STA_NANO */
+		drift_comp = FREQTOD(ntv.freq);
+	}
+#endif /* KERNEL_PLL */
+	set_freq((fp_offset - clock_offset) / (current_time - clock_epoch) + drift_comp);
+	wander_resid = 0;
+	return drift_comp;
+}
+
+static void
+set_freq(double freq) /* frequency update */
+{
+	char tbuf[80];
+
+	drift_comp = freq;
+
+#ifdef KERNEL_PLL
+	/*
+	 * If the kernel is enabled, update the kernel frequency.
+	 */
+	if (pll_control && kern_enable) {
+		memset(&ntv, 0, sizeof(ntv));
+		ntv.modes = MOD_FREQUENCY;
+		ntv.freq = DTOFREQ(drift_comp);
+		ntp_adjtime(&ntv);
+		snprintf(tbuf, sizeof(tbuf), "kernel %.3f PPM", drift_comp * 1e6);
+		report_event(EVNT_FSET, NULL, tbuf);
+	} else {
+		snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", drift_comp * 1e6);
+		report_event(EVNT_FSET, NULL, tbuf);
+	}
+#else /* KERNEL_PLL */
+	snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", drift_comp * 1e6);
+	report_event(EVNT_FSET, NULL, tbuf);
+#endif /* KERNEL_PLL */
+}
+
+...
+...
+...
+
+#ifdef KERNEL_PLL
+	/*
+	 * This code segment works when clock adjustments are made using
+	 * precision time kernel support and the ntp_adjtime() system
+	 * call. This support is available in Solaris 2.6 and later,
+	 * Digital Unix 4.0 and later, FreeBSD, Linux and specially
+	 * modified kernels for HP-UX 9 and Ultrix 4. In the case of the
+	 * DECstation 5000/240 and Alpha AXP, additional kernel
+	 * modifications provide a true microsecond clock and nanosecond
+	 * clock, respectively.
+	 *
+	 * Important note: The kernel discipline is used only if the
+	 * step threshold is less than 0.5 s, as anything higher can
+	 * lead to overflow problems. This might occur if some misguided
+	 * lad set the step threshold to something ridiculous.
+	 */
+	if (pll_control && kern_enable) {
+
+#define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | MOD_STATUS | MOD_TIMECONST)
+
+		/*
+		 * We initialize the structure for the ntp_adjtime()
+		 * system call. We have to convert everything to
+		 * microseconds or nanoseconds first. Do not update the
+		 * system variables if the ext_enable flag is set. In
+		 * this case, the external clock driver will update the
+		 * variables, which will be read later by the local
+		 * clock driver. Afterwards, remember the time and
+		 * frequency offsets for jitter and stability values and
+		 * to update the frequency file.
+		 */
+		memset(&ntv,  0, sizeof(ntv));
+		if (ext_enable) {
+			ntv.modes = MOD_STATUS;
+		} else {
+#ifdef STA_NANO
+			ntv.modes = MOD_BITS | MOD_NANO;
+#else /* STA_NANO */
+			ntv.modes = MOD_BITS;
+#endif /* STA_NANO */
+			if (clock_offset < 0)
+				dtemp = -.5;
+			else
+				dtemp = .5;
+#ifdef STA_NANO
+			ntv.offset = (int32)(clock_offset * 1e9 + dtemp);
+			ntv.constant = sys_poll;
+#else /* STA_NANO */
+			ntv.offset = (int32)(clock_offset * 1e6 + dtemp);
+			ntv.constant = sys_poll - 4;
+#endif /* STA_NANO */
+			ntv.esterror = (u_int32)(clock_jitter * 1e6);
+			ntv.maxerror = (u_int32)((sys_rootdelay / 2 + sys_rootdisp) * 1e6);
+			ntv.status = STA_PLL;
+
+			/*
+			 * Enable/disable the PPS if requested.
+			 */
+			if (pps_enable) {
+				if (!(pll_status & STA_PPSTIME))
+					report_event(EVNT_KERN,
+					    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);
+			}
+			if (sys_leap == LEAP_ADDSECOND)
+				ntv.status |= STA_INS;
+			else if (sys_leap == LEAP_DELSECOND)
+				ntv.status |= STA_DEL;
+		}
+
+		/*
+		 * Pass the stuff to the kernel. If it squeals, turn off
+		 * the pps. In any case, fetch the kernel offset,
+		 * frequency and jitter.
+		 */
+		if (ntp_adjtime(&ntv) == TIME_ERROR) {
+			if (!(ntv.status & STA_PPSSIGNAL))
+				report_event(EVNT_KERN, NULL,
+				    "PPS no signal");
+		}
+		pll_status = ntv.status;
+#ifdef STA_NANO
+		clock_offset = ntv.offset / 1e9;
+#else /* STA_NANO */
+		clock_offset = ntv.offset / 1e6;
+#endif /* STA_NANO */
+		clock_frequency = FREQTOD(ntv.freq);
+
+		/*
+		 * If the kernel PPS is lit, monitor its performance.
+		 */
+		if (ntv.status & STA_PPSTIME) {
+#ifdef STA_NANO
+			clock_jitter = ntv.jitter / 1e9;
+#else /* STA_NANO */
+			clock_jitter = ntv.jitter / 1e6;
+#endif /* STA_NANO */
+		}
+
+#if defined(STA_NANO) && NTP_API == 4
+		/*
+		 * If the TAI changes, update the kernel TAI.
+		 */
+		if (loop_tai != sys_tai) {
+			loop_tai = sys_tai;
+			ntv.modes = MOD_TAI;
+			ntv.constant = sys_tai;
+			ntp_adjtime(&ntv);
+		}
+#endif /* STA_NANO */
+	}
+#endif /* KERNEL_PLL */
+#endif
diff --git a/busybox-1.19.3/networking/ntpd_simple.c b/busybox-1.19.3/networking/ntpd_simple.c
new file mode 100644
index 0000000..4ad44e4
--- /dev/null
+++ b/busybox-1.19.3/networking/ntpd_simple.c
@@ -0,0 +1,1007 @@
+/*
+ * NTP client/server, based on OpenNTPD 3.9p1
+ *
+ * Author: Adam Tkac <vonsch@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include <netinet/ip.h> /* For IPTOS_LOWDELAY definition */
+#ifndef IPTOS_LOWDELAY
+# define IPTOS_LOWDELAY 0x10
+#endif
+#ifndef IP_PKTINFO
+# error "Sorry, your kernel has to support IP_PKTINFO"
+#endif
+
+
+/* Sync to peers every N secs */
+#define INTERVAL_QUERY_NORMAL    30
+#define INTERVAL_QUERY_PATHETIC  60
+#define INTERVAL_QUERY_AGRESSIVE  5
+
+/* Bad if *less than* TRUSTLEVEL_BADPEER */
+#define TRUSTLEVEL_BADPEER        6
+#define TRUSTLEVEL_PATHETIC       2
+#define TRUSTLEVEL_AGRESSIVE      8
+#define TRUSTLEVEL_MAX           10
+
+#define QSCALE_OFF_MIN         0.05
+#define QSCALE_OFF_MAX         0.50
+
+/* Single query might take N secs max */
+#define QUERYTIME_MAX            15
+/* Min offset for settime at start. "man ntpd" says it's 128 ms */
+#define STEPTIME_MIN_OFFSET   0.128
+
+typedef struct {
+	uint32_t int_partl;
+	uint32_t fractionl;
+} l_fixedpt_t;
+
+typedef struct {
+	uint16_t int_parts;
+	uint16_t fractions;
+} s_fixedpt_t;
+
+enum {
+	NTP_DIGESTSIZE     = 16,
+	NTP_MSGSIZE_NOAUTH = 48,
+	NTP_MSGSIZE        = (NTP_MSGSIZE_NOAUTH + 4 + NTP_DIGESTSIZE),
+};
+
+typedef struct {
+	uint8_t     m_status;     /* status of local clock and leap info */
+	uint8_t     m_stratum;    /* stratum level */
+	uint8_t     m_ppoll;      /* poll value */
+	int8_t      m_precision_exp;
+	s_fixedpt_t m_rootdelay;
+	s_fixedpt_t m_dispersion;
+	uint32_t    m_refid;
+	l_fixedpt_t m_reftime;
+	l_fixedpt_t m_orgtime;
+	l_fixedpt_t m_rectime;
+	l_fixedpt_t m_xmttime;
+	uint32_t    m_keyid;
+	uint8_t     m_digest[NTP_DIGESTSIZE];
+} msg_t;
+
+enum {
+	NTP_VERSION     = 4,
+	NTP_MAXSTRATUM  = 15,
+
+	/* Status Masks */
+	MODE_MASK       = (7 << 0),
+	VERSION_MASK    = (7 << 3),
+	VERSION_SHIFT   = 3,
+	LI_MASK         = (3 << 6),
+
+	/* Leap Second Codes (high order two bits of m_status) */
+	LI_NOWARNING    = (0 << 6),    /* no warning */
+	LI_PLUSSEC      = (1 << 6),    /* add a second (61 seconds) */
+	LI_MINUSSEC     = (2 << 6),    /* minus a second (59 seconds) */
+	LI_ALARM        = (3 << 6),    /* alarm condition */
+
+	/* Mode values */
+	MODE_RES0       = 0,    /* reserved */
+	MODE_SYM_ACT    = 1,    /* symmetric active */
+	MODE_SYM_PAS    = 2,    /* symmetric passive */
+	MODE_CLIENT     = 3,    /* client */
+	MODE_SERVER     = 4,    /* server */
+	MODE_BROADCAST  = 5,    /* broadcast */
+	MODE_RES1       = 6,    /* reserved for NTP control message */
+	MODE_RES2       = 7,    /* reserved for private use */
+};
+
+#define OFFSET_1900_1970 2208988800UL  /* 1970 - 1900 in seconds */
+
+typedef struct {
+	double   d_offset;
+	double   d_delay;
+	//UNUSED: double d_error;
+	time_t   d_rcv_time;
+	uint32_t d_refid4;
+	uint8_t  d_leap;
+	uint8_t  d_stratum;
+	uint8_t  d_good;
+} datapoint_t;
+
+#define NUM_DATAPOINTS  8
+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): */
+	time_t           next_action_time;
+	int              p_fd;
+	uint8_t          p_datapoint_idx;
+	uint8_t          p_trustlevel;
+	double           p_xmttime;
+	datapoint_t      update;
+	datapoint_t      p_datapoint[NUM_DATAPOINTS];
+	msg_t            p_xmt_msg;
+} peer_t;
+
+enum {
+	OPT_n = (1 << 0),
+	OPT_q = (1 << 1),
+	OPT_N = (1 << 2),
+	OPT_x = (1 << 3),
+	/* Insert new options above this line. */
+	/* Non-compat options: */
+	OPT_p = (1 << 4),
+	OPT_l = (1 << 5) * ENABLE_FEATURE_NTPD_SERVER,
+};
+
+
+struct globals {
+	/* total round trip delay to currently selected reference clock */
+	double   rootdelay;
+	/* reference timestamp: time when the system clock was last set or corrected */
+	double   reftime;
+	llist_t  *ntp_peers;
+#if ENABLE_FEATURE_NTPD_SERVER
+	int      listen_fd;
+#endif
+	unsigned verbose;
+	unsigned peer_cnt;
+	unsigned scale;
+	uint32_t refid;
+	uint32_t refid4;
+	uint8_t  synced;
+	uint8_t  leap;
+#define G_precision_exp -6
+//	int8_t   precision_exp;
+	uint8_t  stratum;
+	uint8_t  time_was_stepped;
+	uint8_t  first_adj_done;
+};
+#define G (*ptr_to_globals)
+
+static const int const_IPTOS_LOWDELAY = IPTOS_LOWDELAY;
+
+
+static void
+set_next(peer_t *p, unsigned t)
+{
+	p->next_action_time = time(NULL) + t;
+}
+
+static void
+add_peers(char *s)
+{
+	peer_t *p;
+
+	p = xzalloc(sizeof(*p));
+	p->p_lsa = xhost2sockaddr(s, 123);
+	p->p_dotted = xmalloc_sockaddr2dotted_noport(&p->p_lsa->u.sa);
+	p->p_fd = -1;
+	p->p_xmt_msg.m_status = MODE_CLIENT | (NTP_VERSION << 3);
+	p->p_trustlevel = TRUSTLEVEL_PATHETIC;
+	p->next_action_time = time(NULL); /* = set_next(p, 0); */
+
+	llist_add_to(&G.ntp_peers, p);
+	G.peer_cnt++;
+}
+
+static double
+gettime1900d(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL); /* never fails */
+	return (tv.tv_sec + 1.0e-6 * tv.tv_usec + OFFSET_1900_1970);
+}
+
+static void
+d_to_tv(double d, struct timeval *tv)
+{
+	tv->tv_sec = (long)d;
+	tv->tv_usec = (d - tv->tv_sec) * 1000000;
+}
+
+static double
+lfp_to_d(l_fixedpt_t lfp)
+{
+	double ret;
+	lfp.int_partl = ntohl(lfp.int_partl);
+	lfp.fractionl = ntohl(lfp.fractionl);
+	ret = (double)lfp.int_partl + ((double)lfp.fractionl / UINT_MAX);
+	return ret;
+}
+
+#if 0 //UNUSED
+static double
+sfp_to_d(s_fixedpt_t sfp)
+{
+	double ret;
+	sfp.int_parts = ntohs(sfp.int_parts);
+	sfp.fractions = ntohs(sfp.fractions);
+	ret = (double)sfp.int_parts + ((double)sfp.fractions / USHRT_MAX);
+	return ret;
+}
+#endif
+
+#if ENABLE_FEATURE_NTPD_SERVER
+static l_fixedpt_t
+d_to_lfp(double d)
+{
+	l_fixedpt_t lfp;
+	lfp.int_partl = (uint32_t)d;
+	lfp.fractionl = (uint32_t)((d - lfp.int_partl) * UINT_MAX);
+	lfp.int_partl = htonl(lfp.int_partl);
+	lfp.fractionl = htonl(lfp.fractionl);
+	return lfp;
+}
+
+static s_fixedpt_t
+d_to_sfp(double d)
+{
+	s_fixedpt_t sfp;
+	sfp.int_parts = (uint16_t)d;
+	sfp.fractions = (uint16_t)((d - sfp.int_parts) * USHRT_MAX);
+	sfp.int_parts = htons(sfp.int_parts);
+	sfp.fractions = htons(sfp.fractions);
+	return sfp;
+}
+#endif
+
+static unsigned
+error_interval(void)
+{
+	unsigned interval, r;
+	interval = INTERVAL_QUERY_PATHETIC * QSCALE_OFF_MAX / QSCALE_OFF_MIN;
+	r = (unsigned)random() % (unsigned)(interval / 10);
+	return (interval + r);
+}
+
+static int
+do_sendto(int fd,
+		const struct sockaddr *from, const struct sockaddr *to, socklen_t addrlen,
+		msg_t *msg, ssize_t len)
+{
+	ssize_t ret;
+
+	errno = 0;
+	if (!from) {
+		ret = sendto(fd, msg, len, MSG_DONTWAIT, to, addrlen);
+	} else {
+		ret = send_to_from(fd, msg, len, MSG_DONTWAIT, to, from, addrlen);
+	}
+	if (ret != len) {
+		bb_perror_msg("send failed");
+		return -1;
+	}
+	return 0;
+}
+
+static int
+send_query_to_peer(peer_t *p)
+{
+	// Why do we need to bind()?
+	// See what happens when we don't bind:
+	//
+	// socket(PF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
+	// setsockopt(3, SOL_IP, IP_TOS, [16], 4) = 0
+	// gettimeofday({1259071266, 327885}, NULL) = 0
+	// sendto(3, "xxx", 48, MSG_DONTWAIT, {sa_family=AF_INET, sin_port=htons(123), sin_addr=inet_addr("10.34.32.125")}, 16) = 48
+	// ^^^ we sent it from some source port picked by kernel.
+	// time(NULL)              = 1259071266
+	// write(2, "ntpd: entering poll 15 secs\n", 28) = 28
+	// poll([{fd=3, events=POLLIN}], 1, 15000) = 1 ([{fd=3, revents=POLLIN}])
+	// recv(3, "yyy", 68, MSG_DONTWAIT) = 48
+	// ^^^ this recv will receive packets to any local port!
+	//
+	// Uncomment this and use strace to see it in action:
+#define PROBE_LOCAL_ADDR // { len_and_sockaddr lsa; lsa.len = LSA_SIZEOF_SA; getsockname(p->query.fd, &lsa.u.sa, &lsa.len); }
+
+	if (p->p_fd == -1) {
+		int fd, family;
+		len_and_sockaddr *local_lsa;
+
+		family = p->p_lsa->u.sa.sa_family;
+		p->p_fd = fd = xsocket_type(&local_lsa, family, SOCK_DGRAM);
+		/* local_lsa has "null" address and port 0 now.
+		 * bind() ensures we have a *particular port* selected by kernel
+		 * and remembered in p->p_fd, thus later recv(p->p_fd)
+		 * receives only packets sent to this port.
+		 */
+		PROBE_LOCAL_ADDR
+		xbind(fd, &local_lsa->u.sa, local_lsa->len);
+		PROBE_LOCAL_ADDR
+#if ENABLE_FEATURE_IPV6
+		if (family == AF_INET)
+#endif
+			setsockopt(fd, IPPROTO_IP, IP_TOS, &const_IPTOS_LOWDELAY, sizeof(const_IPTOS_LOWDELAY));
+		free(local_lsa);
+	}
+
+	/*
+	 * Send out a random 64-bit number as our transmit time.  The NTP
+	 * server will copy said number into the originate field on the
+	 * response that it sends us.  This is totally legal per the SNTP spec.
+	 *
+	 * The impact of this is two fold: we no longer send out the current
+	 * system time for the world to see (which may aid an attacker), and
+	 * it gives us a (not very secure) way of knowing that we're not
+	 * getting spoofed by an attacker that can't capture our traffic
+	 * but can spoof packets from the NTP server we're communicating with.
+	 *
+	 * Save the real transmit timestamp locally.
+	 */
+	p->p_xmt_msg.m_xmttime.int_partl = random();
+	p->p_xmt_msg.m_xmttime.fractionl = random();
+	p->p_xmttime = gettime1900d();
+
+	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;
+		set_next(p, INTERVAL_QUERY_PATHETIC);
+		return -1;
+	}
+
+	if (G.verbose)
+		bb_error_msg("sent query to %s", p->p_dotted);
+	set_next(p, QUERYTIME_MAX);
+
+	return 0;
+}
+
+
+/* Time is stepped only once, when the first packet from a peer is received.
+ */
+static void
+step_time_once(double offset)
+{
+	double dtime;
+	llist_t *item;
+	struct timeval tv;
+	char buf[80];
+	time_t tval;
+
+	if (G.time_was_stepped)
+		goto bail;
+	G.time_was_stepped = 1;
+
+	/* if the offset is small, don't step, slew (later) */
+	if (offset < STEPTIME_MIN_OFFSET && offset > -STEPTIME_MIN_OFFSET)
+		goto bail;
+
+	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)
+		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);
+
+	for (item = G.ntp_peers; item != NULL; item = item->link) {
+		peer_t *p = (peer_t *) item->data;
+		p->next_action_time -= (time_t)offset;
+	}
+
+ bail:
+	if (option_mask32 & OPT_q)
+		exit(0);
+}
+
+
+/* Time is periodically slewed when we collect enough
+ * good data points.
+ */
+static int
+compare_offsets(const void *aa, const void *bb)
+{
+	const peer_t *const *a = aa;
+	const peer_t *const *b = bb;
+	if ((*a)->update.d_offset < (*b)->update.d_offset)
+		return -1;
+	return ((*a)->update.d_offset > (*b)->update.d_offset);
+}
+static unsigned
+updated_scale(double offset)
+{
+	if (offset < 0)
+		offset = -offset;
+	if (offset > QSCALE_OFF_MAX)
+		return 1;
+	if (offset < QSCALE_OFF_MIN)
+		return QSCALE_OFF_MAX / QSCALE_OFF_MIN;
+	return QSCALE_OFF_MAX / offset;
+}
+static void
+slew_time(void)
+{
+	llist_t *item;
+	double offset_median;
+	struct timeval tv;
+
+	{
+		peer_t **peers = xzalloc(sizeof(peers[0]) * G.peer_cnt);
+		unsigned goodpeer_cnt = 0;
+		unsigned middle;
+
+		for (item = G.ntp_peers; item != NULL; item = item->link) {
+			peer_t *p = (peer_t *) item->data;
+			if (p->p_trustlevel < TRUSTLEVEL_BADPEER)
+				continue;
+			if (!p->update.d_good) {
+				free(peers);
+				return;
+			}
+			peers[goodpeer_cnt++] = p;
+		}
+
+		if (goodpeer_cnt == 0) {
+			free(peers);
+			goto clear_good;
+		}
+
+		qsort(peers, goodpeer_cnt, sizeof(peers[0]), compare_offsets);
+
+		middle = goodpeer_cnt / 2;
+		if (middle != 0 && (goodpeer_cnt & 1) == 0) {
+			offset_median = (peers[middle-1]->update.d_offset + peers[middle]->update.d_offset) / 2;
+			G.rootdelay = (peers[middle-1]->update.d_delay + peers[middle]->update.d_delay) / 2;
+			G.stratum = 1 + MAX(peers[middle-1]->update.d_stratum, peers[middle]->update.d_stratum);
+		} else {
+			offset_median = peers[middle]->update.d_offset;
+			G.rootdelay = peers[middle]->update.d_delay;
+			G.stratum = 1 + peers[middle]->update.d_stratum;
+		}
+		G.leap = peers[middle]->update.d_leap;
+		G.refid4 = peers[middle]->update.d_refid4;
+		G.refid =
+#if ENABLE_FEATURE_IPV6
+			peers[middle]->p_lsa->u.sa.sa_family != AF_INET ?
+				G.refid4 :
+#endif
+				peers[middle]->p_lsa->u.sin.sin_addr.s_addr;
+		free(peers);
+	}
+//TODO: if (offset_median > BIG) step_time(offset_median)?
+
+	G.scale = updated_scale(offset_median);
+
+	bb_error_msg("adjusting clock by %fs, our stratum is %u, time scale %u",
+			offset_median, G.stratum, G.scale);
+
+	errno = 0;
+	d_to_tv(offset_median, &tv);
+	if (adjtime(&tv, &tv) == -1)
+		bb_perror_msg_and_die("adjtime failed");
+	if (G.verbose >= 2)
+		bb_error_msg("old adjust: %d.%06u", (int)tv.tv_sec, (unsigned)tv.tv_usec);
+
+	if (G.first_adj_done) {
+		uint8_t synced = (tv.tv_sec == 0 && tv.tv_usec == 0);
+		if (synced != G.synced) {
+			G.synced = synced;
+			bb_error_msg("clock is %ssynced", synced ? "" : "un");
+		}
+	}
+	G.first_adj_done = 1;
+
+	G.reftime = gettime1900d();
+
+ clear_good:
+	for (item = G.ntp_peers; item != NULL; item = item->link) {
+		peer_t *p = (peer_t *) item->data;
+		p->update.d_good = 0;
+	}
+}
+
+static void
+update_peer_data(peer_t *p)
+{
+	/* Clock filter.
+	 * Find the datapoint with the lowest delay.
+	 * Use that as the peer update.
+	 * Invalidate it and all older ones.
+	 */
+	int i;
+	int best = -1;
+	int good = 0;
+
+	for (i = 0; i < NUM_DATAPOINTS; i++) {
+		if (p->p_datapoint[i].d_good) {
+			good++;
+			if (best < 0 || p->p_datapoint[i].d_delay < p->p_datapoint[best].d_delay)
+				best = i;
+		}
+	}
+
+	if (good < 8) //FIXME: was it meant to be NUM_DATAPOINTS, not 8?
+		return;
+
+	p->update = p->p_datapoint[best]; /* struct copy */
+	slew_time();
+
+	for (i = 0; i < NUM_DATAPOINTS; i++)
+		if (p->p_datapoint[i].d_rcv_time <= p->p_datapoint[best].d_rcv_time)
+			p->p_datapoint[i].d_good = 0;
+}
+
+static unsigned
+scale_interval(unsigned requested)
+{
+	unsigned interval, r;
+	interval = requested * G.scale;
+	r = (unsigned)random() % (unsigned)(MAX(5, interval / 10));
+	return (interval + r);
+}
+static void
+recv_and_process_peer_pkt(peer_t *p)
+{
+	ssize_t     size;
+	msg_t       msg;
+	double      T1, T2, T3, T4;
+	unsigned    interval;
+	datapoint_t *datapoint;
+
+	/* 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?
+	 */
+	size = recv(p->p_fd, &msg, sizeof(msg), MSG_DONTWAIT);
+	if (size == -1) {
+		bb_perror_msg("recv(%s) error", p->p_dotted);
+		if (errno == EHOSTUNREACH || errno == EHOSTDOWN
+		 || errno == ENETUNREACH || errno == ENETDOWN
+		 || errno == ECONNREFUSED || errno == EADDRNOTAVAIL
+		 || errno == EAGAIN
+		) {
+//TODO: always do this?
+			set_next(p, error_interval());
+			goto close_sock;
+		}
+		xfunc_die();
+	}
+
+	if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
+		bb_error_msg("malformed packet received from %s", p->p_dotted);
+		goto bail;
+	}
+
+	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;
+	}
+
+	if ((msg.m_status & LI_ALARM) == LI_ALARM
+	 || msg.m_stratum == 0
+	 || msg.m_stratum > NTP_MAXSTRATUM
+	) {
+// 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 = error_interval();
+		bb_error_msg("reply from %s: not synced, next query in %us", p->p_dotted, interval);
+		goto close_sock;
+	}
+
+	/*
+	 * From RFC 2030 (with a correction to the delay math):
+	 *
+	 * Timestamp Name          ID   When Generated
+	 * ------------------------------------------------------------
+	 * Originate Timestamp     T1   time request sent by client
+	 * Receive Timestamp       T2   time request received by server
+	 * Transmit Timestamp      T3   time reply sent by server
+	 * Destination Timestamp   T4   time reply received by client
+	 *
+	 * The roundtrip delay and local clock offset are defined as
+	 *
+	 * delay = (T4 - T1) - (T3 - T2); offset = ((T2 - T1) + (T3 - T4)) / 2
+	 */
+	T1 = p->p_xmttime;
+	T2 = lfp_to_d(msg.m_rectime);
+	T3 = lfp_to_d(msg.m_xmttime);
+	T4 = gettime1900d();
+
+	datapoint = &p->p_datapoint[p->p_datapoint_idx];
+
+	datapoint->d_offset = ((T2 - T1) + (T3 - T4)) / 2;
+	datapoint->d_delay = (T4 - T1) - (T3 - T2);
+	if (datapoint->d_delay < 0) {
+		bb_error_msg("reply from %s: negative delay %f", p->p_dotted, datapoint->d_delay);
+		interval = error_interval();
+		set_next(p, interval);
+		goto close_sock;
+	}
+	//UNUSED: datapoint->d_error = (T2 - T1) - (T3 - T4);
+	datapoint->d_rcv_time = (time_t)(T4 - OFFSET_1900_1970); /* = time(NULL); */
+	datapoint->d_good = 1;
+
+	datapoint->d_leap = (msg.m_status & LI_MASK);
+	//UNUSED: datapoint->o_precision = msg.m_precision_exp;
+	//UNUSED: datapoint->o_rootdelay = sfp_to_d(msg.m_rootdelay);
+	//UNUSED: datapoint->o_rootdispersion = sfp_to_d(msg.m_dispersion);
+	//UNUSED: datapoint->d_refid = ntohl(msg.m_refid);
+	datapoint->d_refid4 = msg.m_xmttime.fractionl;
+	//UNUSED: datapoint->o_reftime = lfp_to_d(msg.m_reftime);
+	//UNUSED: datapoint->o_poll = msg.m_ppoll;
+	datapoint->d_stratum = msg.m_stratum;
+
+	if (p->p_trustlevel < TRUSTLEVEL_PATHETIC)
+		interval = scale_interval(INTERVAL_QUERY_PATHETIC);
+	else if (p->p_trustlevel < TRUSTLEVEL_AGRESSIVE)
+		interval = scale_interval(INTERVAL_QUERY_AGRESSIVE);
+	else
+		interval = scale_interval(INTERVAL_QUERY_NORMAL);
+
+	set_next(p, interval);
+
+	/* Every received reply which we do not discard increases trust */
+	if (p->p_trustlevel < TRUSTLEVEL_MAX) {
+		p->p_trustlevel++;
+		if (p->p_trustlevel == TRUSTLEVEL_BADPEER)
+			bb_error_msg("peer %s now valid", p->p_dotted);
+	}
+
+	if (G.verbose)
+		bb_error_msg("reply from %s: offset %f delay %f, next query in %us", p->p_dotted,
+			datapoint->d_offset, datapoint->d_delay, interval);
+
+	update_peer_data(p);
+//TODO: do it after all peers had a chance to return at least one reply?
+	step_time_once(datapoint->d_offset);
+
+	p->p_datapoint_idx++;
+	if (p->p_datapoint_idx >= NUM_DATAPOINTS)
+		p->p_datapoint_idx = 0;
+
+ close_sock:
+	/* 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
+static void
+recv_and_process_client_pkt(void /*int fd*/)
+{
+	ssize_t          size;
+	uint8_t          version;
+	double           rectime;
+	len_and_sockaddr *to;
+	struct sockaddr  *from;
+	msg_t            msg;
+	uint8_t          query_status;
+	uint8_t          query_ppoll;
+	l_fixedpt_t      query_xmttime;
+
+	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);
+	if (size != NTP_MSGSIZE_NOAUTH && size != NTP_MSGSIZE) {
+		char *addr;
+		if (size < 0) {
+			if (errno == EAGAIN)
+				goto bail;
+			bb_perror_msg_and_die("recv");
+		}
+		addr = xmalloc_sockaddr2dotted_noport(from);
+		bb_error_msg("malformed packet received from %s: size %u", addr, (int)size);
+		free(addr);
+		goto bail;
+	}
+
+	query_status = msg.m_status;
+	query_ppoll = msg.m_ppoll;
+	query_xmttime = msg.m_xmttime;
+
+	/* Build a reply packet */
+	memset(&msg, 0, sizeof(msg));
+	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;
+	msg.m_stratum = G.stratum;
+	msg.m_ppoll = query_ppoll;
+	msg.m_precision_exp = G_precision_exp;
+	rectime = gettime1900d();
+	msg.m_xmttime = msg.m_rectime = d_to_lfp(rectime);
+	msg.m_reftime = d_to_lfp(G.reftime);
+	//msg.m_xmttime = d_to_lfp(gettime1900d()); // = msg.m_rectime
+	msg.m_orgtime = query_xmttime;
+	msg.m_rootdelay = d_to_sfp(G.rootdelay);
+	version = (query_status & VERSION_MASK); /* ... >> VERSION_SHIFT - done below instead */
+	msg.m_refid = (version > (3 << VERSION_SHIFT)) ? G.refid4 : G.refid;
+
+	/* We reply from the local address packet was sent to,
+	 * this makes to/from look swapped here: */
+	do_sendto(G.listen_fd,
+		/*from:*/ &to->u.sa, /*to:*/ from, /*addrlen:*/ to->len,
+		&msg, size);
+
+ bail:
+	free(to);
+	free(from);
+}
+#endif
+
+/* Upstream ntpd's options:
+ *
+ * -4   Force DNS resolution of host names to the IPv4 namespace.
+ * -6   Force DNS resolution of host names to the IPv6 namespace.
+ * -a   Require cryptographic authentication for broadcast client,
+ *      multicast client and symmetric passive associations.
+ *      This is the default.
+ * -A   Do not require cryptographic authentication for broadcast client,
+ *      multicast client and symmetric passive associations.
+ *      This is almost never a good idea.
+ * -b   Enable the client to synchronize to broadcast servers.
+ * -c conffile
+ *      Specify the name and path of the configuration file,
+ *      default /etc/ntp.conf
+ * -d   Specify debugging mode. This option may occur more than once,
+ *      with each occurrence indicating greater detail of display.
+ * -D level
+ *      Specify debugging level directly.
+ * -f driftfile
+ *      Specify the name and path of the frequency file.
+ *      This is the same operation as the "driftfile FILE"
+ *      configuration command.
+ * -g   Normally, ntpd exits with a message to the system log
+ *      if the offset exceeds the panic threshold, which is 1000 s
+ *      by default. This option allows the time to be set to any value
+ *      without restriction; however, this can happen only once.
+ *      If the threshold is exceeded after that, ntpd will exit
+ *      with a message to the system log. This option can be used
+ *      with the -q and -x options. See the tinker command for other options.
+ * -i jaildir
+ *      Chroot the server to the directory jaildir. This option also implies
+ *      that the server attempts to drop root privileges at startup
+ *      (otherwise, chroot gives very little additional security).
+ *      You may need to also specify a -u option.
+ * -k keyfile
+ *      Specify the name and path of the symmetric key file,
+ *      default /etc/ntp/keys. This is the same operation
+ *      as the "keys FILE" configuration command.
+ * -l logfile
+ *      Specify the name and path of the log file. The default
+ *      is the system log file. This is the same operation as
+ *      the "logfile FILE" configuration command.
+ * -L   Do not listen to virtual IPs. The default is to listen.
+ * -n   Don't fork.
+ * -N   To the extent permitted by the operating system,
+ *      run the ntpd at the highest priority.
+ * -p pidfile
+ *      Specify the name and path of the file used to record the ntpd
+ *      process ID. This is the same operation as the "pidfile FILE"
+ *      configuration command.
+ * -P priority
+ *      To the extent permitted by the operating system,
+ *      run the ntpd at the specified priority.
+ * -q   Exit the ntpd just after the first time the clock is set.
+ *      This behavior mimics that of the ntpdate program, which is
+ *      to be retired. The -g and -x options can be used with this option.
+ *      Note: The kernel time discipline is disabled with this option.
+ * -r broadcastdelay
+ *      Specify the default propagation delay from the broadcast/multicast
+ *      server to this client. This is necessary only if the delay
+ *      cannot be computed automatically by the protocol.
+ * -s statsdir
+ *      Specify the directory path for files created by the statistics
+ *      facility. This is the same operation as the "statsdir DIR"
+ *      configuration command.
+ * -t key
+ *      Add a key number to the trusted key list. This option can occur
+ *      more than once.
+ * -u user[:group]
+ *      Specify a user, and optionally a group, to switch to.
+ * -v variable
+ * -V variable
+ *      Add a system variable listed by default.
+ * -x   Normally, the time is slewed if the offset is less than the step
+ *      threshold, which is 128 ms by default, and stepped if above
+ *      the threshold. This option sets the threshold to 600 s, which is
+ *      well within the accuracy window to set the clock manually.
+ *      Note: since the slew rate of typical Unix kernels is limited
+ *      to 0.5 ms/s, each second of adjustment requires an amortization
+ *      interval of 2000 s. Thus, an adjustment as much as 600 s
+ *      will take almost 14 days to complete. This option can be used
+ *      with the -g and -q options. See the tinker command for other options.
+ *      Note: The kernel time discipline is disabled with this option.
+ */
+
+/* By doing init in a separate function we decrease stack usage
+ * in main loop.
+ */
+static NOINLINE void ntp_init(char **argv)
+{
+	unsigned opts;
+	llist_t *peers;
+
+	srandom(getpid());
+
+	if (getuid())
+		bb_error_msg_and_die(bb_msg_you_must_be_root);
+
+	peers = NULL;
+	opt_complementary = "dd:p::"; /* d: counter, p: list */
+	opts = getopt32(argv,
+			"nqNx" /* compat */
+			"p:"IF_FEATURE_NTPD_SERVER("l") /* NOT compat */
+			"d" /* compat */
+			"46aAbgL", /* compat, ignored */
+			&peers, &G.verbose);
+	if (!(opts & (OPT_p|OPT_l)))
+		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 (!(opts & OPT_n)) {
+		bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO, argv);
+		logmode = LOGMODE_NONE;
+	}
+#if ENABLE_FEATURE_NTPD_SERVER
+	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));
+	}
+#endif
+	/* I hesitate to set -20 prio. -15 should be high enough for timekeeping */
+	if (opts & OPT_N)
+		setpriority(PRIO_PROCESS, 0, -15);
+
+	/* Set some globals */
+#if 0
+	/* With constant b = 100, G.precision_exp is also constant -6.
+	 * Uncomment this and you'll see */
+	{
+		int prec = 0;
+		int b;
+# if 0
+		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;
+		tp.tv_nsec = 10000000;
+		b = 1000000000 / tp.tv_nsec;  /* convert to Hz */
+# else
+		b = 100; /* b = 1000000000/10000000 = 100 */
+# endif
+		while (b > 1)
+			prec--, b >>= 1;
+		//G.precision_exp = prec;
+		bb_error_msg("G.precision_exp:%d", prec); /* -6 */
+	}
+#endif
+	G.scale = 1;
+
+	bb_signals((1 << SIGTERM) | (1 << SIGINT), record_signo);
+	bb_signals((1 << SIGPIPE) | (1 << SIGHUP), SIG_IGN);
+}
+
+int ntpd_main(int argc UNUSED_PARAM, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ntpd_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct globals g;
+	struct pollfd *pfd;
+	peer_t **idx2peer;
+
+	memset(&g, 0, sizeof(g));
+	SET_PTR_TO_GLOBALS(&g);
+
+	ntp_init(argv);
+
+	{
+		/* if ENABLE_FEATURE_NTPD_SERVER, + 1 for listen_fd: */
+		unsigned cnt = g.peer_cnt + ENABLE_FEATURE_NTPD_SERVER;
+		idx2peer = xzalloc(sizeof(idx2peer[0]) * cnt);
+		pfd = xzalloc(sizeof(pfd[0]) * cnt);
+	}
+
+	while (!bb_got_signal) {
+		llist_t *item;
+		unsigned i, j;
+		unsigned sent_cnt, trial_cnt;
+		int nfds, timeout;
+		time_t cur_time, nextaction;
+
+		/* Nothing between here and poll() blocks for any significant time */
+
+		cur_time = time(NULL);
+		nextaction = cur_time + 3600;
+
+		i = 0;
+#if ENABLE_FEATURE_NTPD_SERVER
+		if (g.listen_fd != -1) {
+			pfd[0].fd = g.listen_fd;
+			pfd[0].events = POLLIN;
+			i++;
+		}
+#endif
+		/* Pass over peer list, send requests, time out on receives */
+		sent_cnt = trial_cnt = 0;
+		for (item = g.ntp_peers; item != NULL; item = item->link) {
+			peer_t *p = (peer_t *) item->data;
+
+			/* Overflow-safe "if (p->next_action_time <= cur_time) ..." */
+			if ((int)(cur_time - p->next_action_time) >= 0) {
+				if (p->p_fd == -1) {
+					/* Time to send new req */
+					trial_cnt++;
+					if (send_query_to_peer(p) == 0)
+						sent_cnt++;
+				} else {
+					/* Timed out waiting for reply */
+					close(p->p_fd);
+					p->p_fd = -1;
+					timeout = error_interval();
+					bb_error_msg("timed out waiting for %s, "
+							"next query in %us", p->p_dotted, timeout);
+					if (p->p_trustlevel >= TRUSTLEVEL_BADPEER) {
+						p->p_trustlevel /= 2;
+						if (p->p_trustlevel < TRUSTLEVEL_BADPEER)
+							bb_error_msg("peer %s now invalid", p->p_dotted);
+					}
+					set_next(p, timeout);
+				}
+			}
+
+			if (p->next_action_time < nextaction)
+				nextaction = p->next_action_time;
+
+			if (p->p_fd >= 0) {
+				/* Wait for reply from this peer */
+				pfd[i].fd = p->p_fd;
+				pfd[i].events = POLLIN;
+				idx2peer[i] = p;
+				i++;
+			}
+		}
+
+		if ((trial_cnt > 0 && sent_cnt == 0) || g.peer_cnt == 0)
+			step_time_once(0); /* no good peers, don't wait */
+
+		timeout = nextaction - cur_time;
+		if (timeout < 1)
+			timeout = 1;
+
+		/* Here we may block */
+		if (g.verbose >= 2)
+			bb_error_msg("poll %us, sockets:%u", timeout, i);
+		nfds = poll(pfd, i, timeout * 1000);
+		if (nfds <= 0)
+			continue;
+
+		/* Process any received packets */
+		j = 0;
+#if ENABLE_FEATURE_NTPD_SERVER
+		if (g.listen_fd != -1) {
+			if (pfd[0].revents /* & (POLLIN|POLLERR)*/) {
+				nfds--;
+				recv_and_process_client_pkt(/*g.listen_fd*/);
+			}
+			j = 1;
+		}
+#endif
+		for (; nfds != 0 && j < i; j++) {
+			if (pfd[j].revents /* & (POLLIN|POLLERR)*/) {
+				nfds--;
+				recv_and_process_peer_pkt(idx2peer[j]);
+			}
+		}
+	} /* while (!bb_got_signal) */
+
+	kill_myself_with_sig(bb_got_signal);
+}
diff --git a/busybox-1.19.3/networking/ping.c b/busybox-1.19.3/networking/ping.c
new file mode 100644
index 0000000..efd4f21
--- /dev/null
+++ b/busybox-1.19.3/networking/ping.c
@@ -0,0 +1,946 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ping implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * Adapted from the ping in netkit-base 0.10:
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+/* from ping6.c:
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * This version of ping is adapted from the ping in netkit-base 0.10,
+ * which is:
+ *
+ * Original copyright notice is retained at the end of this file.
+ *
+ * This version is an adaptation of ping.c from busybox.
+ * The code was modified by Bart Visscher <magick@linux-fan.com>
+ */
+
+#include <net/if.h>
+#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:     "\n	-4,-6		Force IP or IPv6 name resolution"
+//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 - !!?
+ * What a fuck is going on with libc? Is it a glibc joke? */
+# ifdef IPV6_2292HOPLIMIT
+#  undef IPV6_HOPLIMIT
+#  define IPV6_HOPLIMIT IPV6_2292HOPLIMIT
+# endif
+#endif
+
+enum {
+	DEFDATALEN = 56,
+	MAXIPLEN = 60,
+	MAXICMPLEN = 76,
+	MAX_DUP_CHK = (8 * 128),
+	MAXWAIT = 10,
+	PINGINTERVAL = 1, /* 1 second */
+};
+
+/* Common routines */
+
+static int in_cksum(unsigned short *buf, int sz)
+{
+	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;
+	}
+
+	sum = (sum >> 16) + (sum & 0xFFFF);
+	sum += (sum >> 16);
+	ans = ~sum;
+	return ans;
+}
+
+#if !ENABLE_FEATURE_FANCY_PING
+
+/* Simple version */
+
+struct globals {
+	char *hostname;
+	char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { } while (0)
+
+static void noresp(int ign UNUSED_PARAM)
+{
+	printf("No response from %s\n", G.hostname);
+	exit(EXIT_FAILURE);
+}
+
+static void ping4(len_and_sockaddr *lsa)
+{
+	struct icmp *pkt;
+	int pingsock, c;
+
+	pingsock = create_icmp_socket();
+
+	pkt = (struct icmp *) G.packet;
+	memset(pkt, 0, sizeof(G.packet));
+	pkt->icmp_type = ICMP_ECHO;
+	pkt->icmp_cksum = in_cksum((unsigned short *) pkt, sizeof(G.packet));
+
+	xsendto(pingsock, G.packet, DEFDATALEN + ICMP_MINLEN, &lsa->u.sa, lsa->len);
+
+	/* listen for replies */
+	while (1) {
+		struct sockaddr_in from;
+		socklen_t fromlen = sizeof(from);
+
+		c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
+				(struct sockaddr *) &from, &fromlen);
+		if (c < 0) {
+			if (errno != EINTR)
+				bb_perror_msg("recvfrom");
+			continue;
+		}
+		if (c >= 76) {			/* ip + icmp */
+			struct iphdr *iphdr = (struct iphdr *) G.packet;
+
+			pkt = (struct icmp *) (G.packet + (iphdr->ihl << 2));	/* skip ip hdr */
+			if (pkt->icmp_type == ICMP_ECHOREPLY)
+				break;
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(pingsock);
+}
+
+#if ENABLE_PING6
+static void ping6(len_and_sockaddr *lsa)
+{
+	struct icmp6_hdr *pkt;
+	int pingsock, c;
+	int sockopt;
+
+	pingsock = create_icmp6_socket();
+
+	pkt = (struct icmp6_hdr *) G.packet;
+	memset(pkt, 0, sizeof(G.packet));
+	pkt->icmp6_type = ICMP6_ECHO_REQUEST;
+
+	sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+	setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+
+	xsendto(pingsock, G.packet, DEFDATALEN + sizeof(struct icmp6_hdr), &lsa->u.sa, lsa->len);
+
+	/* listen for replies */
+	while (1) {
+		struct sockaddr_in6 from;
+		socklen_t fromlen = sizeof(from);
+
+		c = recvfrom(pingsock, G.packet, sizeof(G.packet), 0,
+				(struct sockaddr *) &from, &fromlen);
+		if (c < 0) {
+			if (errno != EINTR)
+				bb_perror_msg("recvfrom");
+			continue;
+		}
+		if (c >= ICMP_MINLEN) {			/* icmp6_hdr */
+			pkt = (struct icmp6_hdr *) G.packet;
+			if (pkt->icmp6_type == ICMP6_ECHO_REPLY)
+				break;
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(pingsock);
+}
+#endif
+
+#if !ENABLE_PING6
+# define common_ping_main(af, argv) common_ping_main(argv)
+#endif
+static int common_ping_main(sa_family_t af, char **argv)
+{
+	len_and_sockaddr *lsa;
+
+	INIT_G();
+
+#if ENABLE_PING6
+	while ((++argv)[0] && argv[0][0] == '-') {
+		if (argv[0][1] == '4') {
+			af = AF_INET;
+			continue;
+		}
+		if (argv[0][1] == '6') {
+			af = AF_INET6;
+			continue;
+		}
+		bb_show_usage();
+	}
+#else
+	argv++;
+#endif
+
+	G.hostname = *argv;
+	if (!G.hostname)
+		bb_show_usage();
+
+#if ENABLE_PING6
+	lsa = xhost_and_af2sockaddr(G.hostname, 0, af);
+#else
+	lsa = xhost_and_af2sockaddr(G.hostname, 0, AF_INET);
+#endif
+	/* Set timer _after_ DNS resolution */
+	signal(SIGALRM, noresp);
+	alarm(5); /* give the host 5000ms to respond */
+
+#if ENABLE_PING6
+	if (lsa->u.sa.sa_family == AF_INET6)
+		ping6(lsa);
+	else
+#endif
+		ping4(lsa);
+	printf("%s is alive!\n", G.hostname);
+	return EXIT_SUCCESS;
+}
+
+
+#else /* FEATURE_FANCY_PING */
+
+
+/* Full(er) version */
+
+#define OPT_STRING ("qvc:s:t:w:W:I:4" IF_PING6("6"))
+enum {
+	OPT_QUIET = 1 << 0,
+	OPT_VERBOSE = 1 << 1,
+	OPT_c = 1 << 2,
+	OPT_s = 1 << 3,
+	OPT_t = 1 << 4,
+	OPT_w = 1 << 5,
+	OPT_W = 1 << 6,
+	OPT_I = 1 << 7,
+	OPT_IPV4 = 1 << 8,
+	OPT_IPV6 = (1 << 9) * 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 */
+	unsigned long long tsum; /* in us, sum of all times */
+	unsigned deadline;
+	unsigned timeout;
+	unsigned total_secs;
+	unsigned sizeof_rcv_packet;
+	char *rcv_packet; /* [datalen + MAXIPLEN + MAXICMPLEN] */
+	void *snd_packet; /* [datalen + ipv4/ipv6_const] */
+	const char *hostname;
+	const char *dotted;
+	union {
+		struct sockaddr sa;
+		struct sockaddr_in sin;
+#if ENABLE_PING6
+		struct sockaddr_in6 sin6;
+#endif
+	} pingaddr;
+	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        )
+#define tsum         (G.tsum        )
+#define deadline     (G.deadline    )
+#define timeout      (G.timeout     )
+#define total_secs   (G.total_secs  )
+#define hostname     (G.hostname    )
+#define dotted       (G.dotted      )
+#define pingaddr     (G.pingaddr    )
+#define rcvd_tbl     (G.rcvd_tbl    )
+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))
+
+/**************************************************************************/
+
+static void print_stats_and_exit(int junk) NORETURN;
+static void print_stats_and_exit(int junk UNUSED_PARAM)
+{
+	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);
+	if (tmin != UINT_MAX) {
+		unsigned tavg = tsum / (nreceived + 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));
+}
+
+static void sendping_tail(void (*sp)(int), int size_pkt)
+{
+	int sz;
+
+	CLR((uint16_t)ntransmitted % MAX_DUP_CHK);
+	ntransmitted++;
+
+	size_pkt += datalen;
+
+	/* sizeof(pingaddr) can be larger than real sa size, but I think
+	 * it doesn't matter */
+	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) {
+		/* Didn't send all pings yet - schedule next in 1s */
+		signal(SIGALRM, sp);
+		if (deadline) {
+			total_secs += PINGINTERVAL;
+			if (total_secs >= deadline)
+				signal(SIGALRM, print_stats_and_exit);
+		}
+		alarm(PINGINTERVAL);
+	} else { /* -c NN, and all NN are sent (and no deadline) */
+		/* Wait for the last ping to come back.
+		 * -W timeout: wait for a response in seconds.
+		 * Affects only timeout in absense of any responses,
+		 * otherwise ping waits for two RTTs. */
+		unsigned expire = timeout;
+
+		if (nreceived) {
+			/* approx. 2*tmax, in seconds (2 RTT) */
+			expire = tmax / (512*1024);
+			if (expire == 0)
+				expire = 1;
+		}
+		signal(SIGALRM, print_stats_and_exit);
+		alarm(expire);
+	}
+}
+
+static void sendping4(int junk UNUSED_PARAM)
+{
+	struct icmp *pkt = G.snd_packet;
+
+	//memset(pkt, 0, datalen + ICMP_MINLEN + 4); - G.snd_packet was xzalloced
+	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_id = myid;
+
+	/* If datalen < 4, we store timestamp _past_ the packet,
+	 * but it's ok - we allocated 4 extra bytes in xzalloc() just in case.
+	 */
+	/*if (datalen >= 4)*/
+		/* 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);
+
+	sendping_tail(sendping4, ICMP_MINLEN);
+}
+#if ENABLE_PING6
+static void sendping6(int junk UNUSED_PARAM)
+{
+	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_id = myid;
+
+	/*if (datalen >= 4)*/
+		*(uint32_t*)(&pkt->icmp6_data8[4]) = monotonic_us();
+
+	//TODO? pkt->icmp_cksum = in_cksum(...);
+
+	sendping_tail(sendping6, sizeof(struct icmp6_hdr));
+}
+#endif
+
+static const char *icmp_type_name(int id)
+{
+	switch (id) {
+	case ICMP_ECHOREPLY:      return "Echo Reply";
+	case ICMP_DEST_UNREACH:   return "Destination Unreachable";
+	case ICMP_SOURCE_QUENCH:  return "Source Quench";
+	case ICMP_REDIRECT:       return "Redirect (change route)";
+	case ICMP_ECHO:           return "Echo Request";
+	case ICMP_TIME_EXCEEDED:  return "Time Exceeded";
+	case ICMP_PARAMETERPROB:  return "Parameter Problem";
+	case ICMP_TIMESTAMP:      return "Timestamp Request";
+	case ICMP_TIMESTAMPREPLY: return "Timestamp Reply";
+	case ICMP_INFO_REQUEST:   return "Information Request";
+	case ICMP_INFO_REPLY:     return "Information Reply";
+	case ICMP_ADDRESS:        return "Address Mask Request";
+	case ICMP_ADDRESSREPLY:   return "Address Mask Reply";
+	default:                  return "unknown ICMP type";
+	}
+}
+#if ENABLE_PING6
+/* RFC3542 changed some definitions from RFC2292 for no good reason, whee!
+ * the newer 3542 uses a MLD_ prefix where as 2292 uses ICMP6_ prefix */
+#ifndef MLD_LISTENER_QUERY
+# define MLD_LISTENER_QUERY ICMP6_MEMBERSHIP_QUERY
+#endif
+#ifndef MLD_LISTENER_REPORT
+# define MLD_LISTENER_REPORT ICMP6_MEMBERSHIP_REPORT
+#endif
+#ifndef MLD_LISTENER_REDUCTION
+# define MLD_LISTENER_REDUCTION ICMP6_MEMBERSHIP_REDUCTION
+#endif
+static const char *icmp6_type_name(int id)
+{
+	switch (id) {
+	case ICMP6_DST_UNREACH:      return "Destination Unreachable";
+	case ICMP6_PACKET_TOO_BIG:   return "Packet too big";
+	case ICMP6_TIME_EXCEEDED:    return "Time Exceeded";
+	case ICMP6_PARAM_PROB:       return "Parameter Problem";
+	case ICMP6_ECHO_REPLY:       return "Echo Reply";
+	case ICMP6_ECHO_REQUEST:     return "Echo Request";
+	case MLD_LISTENER_QUERY:     return "Listener Query";
+	case MLD_LISTENER_REPORT:    return "Listener Report";
+	case MLD_LISTENER_REDUCTION: return "Listener Reduction";
+	default:                     return "unknown ICMP type";
+	}
+}
+#endif
+
+static void unpack_tail(int sz, uint32_t *tp,
+		const char *from_str,
+		uint16_t recv_seq, int ttl)
+{
+	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) */
+		triptime = (int32_t) ((uint32_t)monotonic_us() - *tp);
+		tsum += triptime;
+		if (triptime < tmin)
+			tmin = triptime;
+		if (triptime > tmax)
+			tmax = triptime;
+	}
+
+	if (TST(recv_seq % MAX_DUP_CHK)) {
+		++nrepeats;
+		--nreceived;
+	} else {
+		SET(recv_seq % MAX_DUP_CHK);
+		dupmsg += 7;
+	}
+
+	if (option_mask32 & OPT_QUIET)
+		return;
+
+	printf("%d bytes from %s: seq=%u ttl=%d", sz,
+		from_str, recv_seq, ttl);
+	if (tp)
+		printf(" time=%u.%03u ms", triptime / 1000, triptime % 1000);
+	puts(dupmsg);
+	fflush_all();
+}
+static void unpack4(char *buf, int sz, struct sockaddr_in *from)
+{
+	struct icmp *icmppkt;
+	struct iphdr *iphdr;
+	int hlen;
+
+	/* discard if too short */
+	if (sz < (datalen + ICMP_MINLEN))
+		return;
+
+	/* check IP header */
+	iphdr = (struct iphdr *) buf;
+	hlen = iphdr->ihl << 2;
+	sz -= hlen;
+	icmppkt = (struct icmp *) (buf + hlen);
+	if (icmppkt->icmp_id != myid)
+		return;				/* not our ping */
+
+	if (icmppkt->icmp_type == ICMP_ECHOREPLY) {
+		uint16_t recv_seq = ntohs(icmppkt->icmp_seq);
+		uint32_t *tp = NULL;
+
+		if (sz >= ICMP_MINLEN + sizeof(uint32_t))
+			tp = (uint32_t *) icmppkt->icmp_data;
+		unpack_tail(sz, tp,
+			inet_ntoa(*(struct in_addr *) &from->sin_addr.s_addr),
+			recv_seq, iphdr->ttl);
+	} else if (icmppkt->icmp_type != ICMP_ECHO) {
+		bb_error_msg("warning: got ICMP %d (%s)",
+				icmppkt->icmp_type,
+				icmp_type_name(icmppkt->icmp_type));
+	}
+}
+#if ENABLE_PING6
+static void unpack6(char *packet, int sz, /*struct sockaddr_in6 *from,*/ int hoplimit)
+{
+	struct icmp6_hdr *icmppkt;
+	char buf[INET6_ADDRSTRLEN];
+
+	/* discard if too short */
+	if (sz < (datalen + sizeof(struct icmp6_hdr)))
+		return;
+
+	icmppkt = (struct icmp6_hdr *) packet;
+	if (icmppkt->icmp6_id != myid)
+		return;				/* not our ping */
+
+	if (icmppkt->icmp6_type == ICMP6_ECHO_REPLY) {
+		uint16_t recv_seq = ntohs(icmppkt->icmp6_seq);
+		uint32_t *tp = NULL;
+
+		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,
+					buf, sizeof(buf)),
+			recv_seq, hoplimit);
+	} else if (icmppkt->icmp6_type != ICMP6_ECHO_REQUEST) {
+		bb_error_msg("warning: got ICMP %d (%s)",
+				icmppkt->icmp6_type,
+				icmp6_type_name(icmppkt->icmp6_type));
+	}
+}
+#endif
+
+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,
+				&source_lsa->u.sa, source_lsa->len))
+			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);
+
+	/* set recv buf (needed if we can get lots of responses: flood ping,
+	 * broadcast ping etc) */
+	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 ... */
+	sendping4(0);
+
+	/* listen for replies */
+	while (1) {
+		struct sockaddr_in from;
+		socklen_t fromlen = (socklen_t) sizeof(from);
+		int c;
+
+		c = recvfrom(pingsock, G.rcv_packet, G.sizeof_rcv_packet, 0,
+				(struct sockaddr *) &from, &fromlen);
+		if (c < 0) {
+			if (errno != EINTR)
+				bb_perror_msg("recvfrom");
+			continue;
+		}
+		unpack4(G.rcv_packet, c, &from);
+		if (pingcount && nreceived >= pingcount)
+			break;
+	}
+}
+#if ENABLE_PING6
+extern int BUG_bad_offsetof_icmp6_cksum(void);
+static void ping6(len_and_sockaddr *lsa)
+{
+	int sockopt;
+	struct msghdr msg;
+	struct sockaddr_in6 from;
+	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
+	{
+		struct icmp6_filter filt;
+		if (!(option_mask32 & OPT_VERBOSE)) {
+			ICMP6_FILTER_SETBLOCKALL(&filt);
+			ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filt);
+		} else {
+			ICMP6_FILTER_SETPASSALL(&filt);
+		}
+		if (setsockopt(pingsock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+					   sizeof(filt)) < 0)
+			bb_error_msg_and_die("setsockopt(ICMP6_FILTER)");
+	}
+#endif /*ICMP6_FILTER*/
+
+	/* enable broadcast pings */
+	setsockopt_broadcast(pingsock);
+
+	/* set recv buf (needed if we can get lots of responses: flood ping,
+	 * broadcast ping etc) */
+	sockopt = (datalen * 2) + 7 * 1024; /* giving it a bit of extra room */
+	setsockopt(pingsock, SOL_SOCKET, SO_RCVBUF, &sockopt, sizeof(sockopt));
+
+	sockopt = offsetof(struct icmp6_hdr, icmp6_cksum);
+	if (offsetof(struct icmp6_hdr, icmp6_cksum) != 2)
+		BUG_bad_offsetof_icmp6_cksum();
+	setsockopt(pingsock, SOL_RAW, IPV6_CHECKSUM, &sockopt, sizeof(sockopt));
+
+	/* request ttl info to be returned in ancillary data */
+	setsockopt(pingsock, SOL_IPV6, IPV6_HOPLIMIT, &const_int_1, sizeof(const_int_1));
+
+	if (if_index)
+		pingaddr.sin6.sin6_scope_id = if_index;
+
+	signal(SIGINT, print_stats_and_exit);
+
+	/* start the ping's going ... */
+	sendping6(0);
+
+	/* listen for replies */
+	msg.msg_name = &from;
+	msg.msg_namelen = sizeof(from);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+	msg.msg_control = control_buf;
+	iov.iov_base = G.rcv_packet;
+	iov.iov_len = G.sizeof_rcv_packet;
+	while (1) {
+		int c;
+		struct cmsghdr *mp;
+		int hoplimit = -1;
+		msg.msg_controllen = sizeof(control_buf);
+
+		c = recvmsg(pingsock, &msg, 0);
+		if (c < 0) {
+			if (errno != EINTR)
+				bb_perror_msg("recvfrom");
+			continue;
+		}
+		for (mp = CMSG_FIRSTHDR(&msg); mp; mp = CMSG_NXTHDR(&msg, mp)) {
+			if (mp->cmsg_level == SOL_IPV6
+			 && mp->cmsg_type == IPV6_HOPLIMIT
+			 /* don't check len - we trust the kernel: */
+			 /* && mp->cmsg_len >= CMSG_LEN(sizeof(int)) */
+			) {
+				/*hoplimit = *(int*)CMSG_DATA(mp); - unaligned access */
+				move_from_unaligned_int(hoplimit, CMSG_DATA(mp));
+			}
+		}
+		unpack6(G.rcv_packet, c, /*&from,*/ hoplimit);
+		if (pingcount && nreceived >= pingcount)
+			break;
+	}
+}
+#endif
+
+static void ping(len_and_sockaddr *lsa)
+{
+	printf("PING %s (%s)", hostname, dotted);
+	if (source_lsa) {
+		printf(" from %s",
+			xmalloc_sockaddr2dotted_noport(&source_lsa->u.sa));
+	}
+	printf(": %d data bytes\n", datalen);
+
+	G.sizeof_rcv_packet = datalen + MAXIPLEN + MAXICMPLEN;
+	G.rcv_packet = xzalloc(G.sizeof_rcv_packet);
+#if ENABLE_PING6
+	if (lsa->u.sa.sa_family == AF_INET6) {
+		/* +4 reserves a place for timestamp, which may end up sitting
+		 * _after_ packet. Saves one if() - see sendping4/6() */
+		G.snd_packet = xzalloc(datalen + sizeof(struct icmp6_hdr) + 4);
+		ping6(lsa);
+	} else
+#endif
+	{
+		G.snd_packet = xzalloc(datalen + ICMP_MINLEN + 4);
+		ping4(lsa);
+	}
+}
+
+static int common_ping_main(int opt, char **argv)
+{
+	len_and_sockaddr *lsa;
+	char *str_s;
+
+	INIT_G();
+
+	/* 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
+		if_index = if_nametoindex(str_I);
+		if (!if_index) {
+			/* TODO: I'm not sure it takes IPv6 unless in [XX:XX..] format */
+			source_lsa = xdotted2sockaddr(str_I, 0);
+			str_I = NULL; /* don't try to bind to device later */
+		}
+	}
+	myid = (uint16_t) getpid();
+	hostname = argv[optind];
+#if ENABLE_PING6
+	{
+		sa_family_t af = AF_UNSPEC;
+		if (opt & OPT_IPV4)
+			af = AF_INET;
+		if (opt & OPT_IPV6)
+			af = AF_INET6;
+		lsa = xhost_and_af2sockaddr(hostname, 0, af);
+	}
+#else
+	lsa = xhost_and_af2sockaddr(hostname, 0, AF_INET);
+#endif
+
+	if (source_lsa && source_lsa->u.sa.sa_family != lsa->u.sa.sa_family)
+		/* leaking it here... */
+		source_lsa = NULL;
+
+	dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+	ping(lsa);
+	print_stats_and_exit(EXIT_SUCCESS);
+	/*return EXIT_SUCCESS;*/
+}
+#endif /* FEATURE_FANCY_PING */
+
+
+int ping_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ping_main(int argc UNUSED_PARAM, char **argv)
+{
+#if !ENABLE_FEATURE_FANCY_PING
+	return common_ping_main(AF_UNSPEC, argv);
+#else
+	return common_ping_main(0, argv);
+#endif
+}
+
+#if ENABLE_PING6
+int ping6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ping6_main(int argc UNUSED_PARAM, char **argv)
+{
+# if !ENABLE_FEATURE_FANCY_PING
+	return common_ping_main(AF_INET6, argv);
+# else
+	return common_ping_main(OPT_IPV6, argv);
+# endif
+}
+#endif
+
+/* from ping6.c:
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Mike Muuss.
+ *
+ * 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. <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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/networking/pscan.c b/busybox-1.19.3/networking/pscan.c
new file mode 100644
index 0000000..28005ad
--- /dev/null
+++ b/busybox-1.19.3/networking/pscan.c
@@ -0,0 +1,165 @@
+/*
+ * Pscan is a mini port scanner implementation for busybox
+ *
+ * Copyright 2007 Tito Ragusa <farmatito@tiscali.it>
+ *
+ * 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 */
+#ifdef DEBUG_PSCAN
+#define DMSG(...) bb_error_msg(__VA_ARGS__)
+#define DERR(...) bb_perror_msg(__VA_ARGS__)
+#else
+#define DMSG(...) ((void)0)
+#define DERR(...) ((void)0)
+#endif
+
+static const char *port_name(unsigned port)
+{
+	struct servent *server;
+
+	server = getservbyport(htons(port), NULL);
+	if (server)
+		return server->s_name;
+	return "unknown";
+}
+
+/* We don't expect to see 1000+ seconds delay, unsigned is enough */
+#define MONOTONIC_US() ((unsigned)monotonic_us())
+
+int pscan_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pscan_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *opt_max_port = "1024";      /* -P: default max port */
+	const char *opt_min_port = "1";         /* -p: default min port */
+	const char *opt_timeout = "5000";       /* -t: default timeout in msec */
+	/* We estimate rtt and wait rtt*4 before concluding that port is
+	 * totally blocked. min rtt of 5 ms may be too low if you are
+	 * scanning an Internet host behind saturated/traffic shaped link.
+	 * Rule of thumb: with min_rtt of N msec, scanning 1000 ports
+	 * will take N seconds at absolute minimum */
+	const char *opt_min_rtt = "5";          /* -T: default min rtt in msec */
+	const char *result_str;
+	len_and_sockaddr *lsap;
+	int s;
+	unsigned opt;
+	unsigned port, max_port, nports;
+	unsigned closed_ports = 0;
+	unsigned open_ports = 0;
+	/* all in usec */
+	unsigned timeout;
+	unsigned min_rtt;
+	unsigned rtt_4;
+	unsigned start, diff;
+
+	opt_complementary = "=1"; /* exactly one non-option */
+	opt = getopt32(argv, "cbp:P:t:T:", &opt_min_port, &opt_max_port, &opt_timeout, &opt_min_rtt);
+	argv += optind;
+	max_port = xatou_range(opt_max_port, 1, 65535);
+	port = xatou_range(opt_min_port, 1, max_port);
+	nports = max_port - port + 1;
+	min_rtt = xatou_range(opt_min_rtt, 1, INT_MAX/1000 / 4) * 1000;
+	timeout = xatou_range(opt_timeout, 1, INT_MAX/1000 / 4) * 1000;
+	/* Initial rtt is BIG: */
+	rtt_4 = timeout;
+
+	DMSG("min_rtt %u timeout %u", min_rtt, timeout);
+
+	lsap = xhost2sockaddr(*argv, port);
+	printf("Scanning %s ports %u to %u\n Port\tProto\tState\tService\n",
+			*argv, port, max_port);
+
+	for (; port <= max_port; port++) {
+		DMSG("rtt %u", rtt_4);
+
+		/* The SOCK_STREAM socket type is implemented on the TCP/IP protocol. */
+		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 */
+		ndelay_on(s);
+
+		DMSG("connect to port %u", port);
+		result_str = NULL;
+		start = MONOTONIC_US();
+		if (connect(s, &lsap->u.sa, lsap->len) == 0) {
+			/* Unlikely, for me even localhost fails :) */
+			DMSG("connect succeeded");
+			goto open;
+		}
+		/* Check for untypical errors... */
+		if (errno != EAGAIN && errno != EINPROGRESS
+		 && errno != ECONNREFUSED
+		) {
+			bb_perror_nomsg_and_die();
+		}
+
+		diff = 0;
+		while (1) {
+			if (errno == ECONNREFUSED) {
+				if (opt & 1) /* -c: show closed too */
+					result_str = "closed";
+				closed_ports++;
+				break;
+			}
+			DERR("port %u errno %d @%u", port, errno, diff);
+
+			if (diff > rtt_4) {
+				if (opt & 2) /* -b: show blocked too */
+					result_str = "blocked";
+				break;
+			}
+			/* Can sleep (much) longer than specified delay.
+			 * We check rtt BEFORE we usleep, otherwise
+			 * on localhost we'll have no writes done (!)
+			 * before we exceed (rather small) rtt */
+			usleep(rtt_4/8);
+ open:
+			diff = MONOTONIC_US() - start;
+			DMSG("write to port %u @%u", port, diff - start);
+			if (write(s, " ", 1) >= 0) { /* We were able to write to the socket */
+				open_ports++;
+				result_str = "open";
+				break;
+			}
+		}
+		DMSG("out of loop @%u", diff);
+		if (result_str)
+			printf("%5u" "\t" "tcp" "\t" "%s" "\t" "%s" "\n",
+					port, result_str, port_name(port));
+
+		/* Estimate new rtt - we don't want to wait entire timeout
+		 * for each port. *4 allows for rise in net delay.
+		 * We increase rtt quickly (rtt_4*4), decrease slowly
+		 * (diff is at least rtt_4/8, *4 == rtt_4/2)
+		 * because we don't want to accidentally miss ports. */
+		rtt_4 = diff * 4;
+		if (rtt_4 < min_rtt)
+			rtt_4 = min_rtt;
+		if (rtt_4 > timeout)
+			rtt_4 = timeout;
+		/* Clean up */
+		close(s);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) free(lsap);
+
+	printf("%d closed, %d open, %d timed out (or blocked) ports\n",
+					closed_ports,
+					open_ports,
+					nports - (closed_ports + open_ports));
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/route.c b/busybox-1.19.3/networking/route.c
new file mode 100644
index 0000000..b7b5a02
--- /dev/null
+++ b/busybox-1.19.3/networking/route.c
@@ -0,0 +1,712 @@
+/* vi: set sw=4 ts=4: */
+/* route
+ *
+ * Similar to the standard Unix route, but with only the necessary
+ * parts for AF_INET and AF_INET6
+ *
+ * Bjorn Wesen, Axis Communications AB
+ *
+ * Author of the original route:
+ *              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 source tree.
+ *
+ *
+ * displayroute() code added by Vladimir N. Oleynik <dzo@simtreas.ru>
+ * adjustments by Larry Doolittle  <LRDoolittle@lbl.gov>
+ *
+ * IPV6 support added by Bart Visscher <magick@linux-fan.com>
+ */
+
+/* 2004/03/09  Manuel Novoa III <mjn3@codepoet.org>
+ *
+ * Rewritten to fix several bugs, add additional error checking, and
+ * 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>
+
+#include "libbb.h"
+#include "inet_common.h"
+
+
+#ifndef RTF_UP
+/* Keep this in sync with /usr/src/linux/include/linux/route.h */
+#define RTF_UP          0x0001	/* route usable                 */
+#define RTF_GATEWAY     0x0002	/* destination is a gateway     */
+#define RTF_HOST        0x0004	/* host entry (net otherwise)   */
+#define RTF_REINSTATE   0x0008	/* reinstate route after tmout  */
+#define RTF_DYNAMIC     0x0010	/* created dyn. (by redirect)   */
+#define RTF_MODIFIED    0x0020	/* modified dyn. (by redirect)  */
+#define RTF_MTU         0x0040	/* specific MTU for this route  */
+#ifndef RTF_MSS
+#define RTF_MSS         RTF_MTU	/* Compatibility :-(            */
+#endif
+#define RTF_WINDOW      0x0080	/* per route window clamping    */
+#define RTF_IRTT        0x0100	/* Initial round trip time      */
+#define RTF_REJECT      0x0200	/* Reject route                 */
+#endif
+
+#if defined(SIOCADDRTOLD) || defined(RTF_IRTT)	/* route */
+#define HAVE_NEW_ADDRT 1
+#endif
+
+#if HAVE_NEW_ADDRT
+#define mask_in_addr(x) (((struct sockaddr_in *)&((x).rt_genmask))->sin_addr.s_addr)
+#define full_mask(x) (x)
+#else
+#define mask_in_addr(x) ((x).rt_genmask)
+#define full_mask(x) (((struct sockaddr_in *)&(x))->sin_addr.s_addr)
+#endif
+
+/* The RTACTION entries must agree with tbl_verb[] below! */
+#define RTACTION_ADD 1
+#define RTACTION_DEL 2
+
+/* For the various tbl_*[] arrays, the 1st byte is the offset to
+ * the next entry and the 2nd byte is return value. */
+
+#define NET_FLAG  1
+#define HOST_FLAG 2
+
+/* We remap '-' to '#' to avoid problems with getopt. */
+static const char tbl_hash_net_host[] ALIGN1 =
+	"\007\001#net\0"
+/*	"\010\002#host\0" */
+	"\007\002#host"				/* Since last, we can save a byte. */
+;
+
+#define KW_TAKES_ARG            020
+#define KW_SETS_FLAG            040
+
+#define KW_IPVx_METRIC          020
+#define KW_IPVx_NETMASK         021
+#define KW_IPVx_GATEWAY         022
+#define KW_IPVx_MSS             023
+#define KW_IPVx_WINDOW          024
+#define KW_IPVx_IRTT            025
+#define KW_IPVx_DEVICE          026
+
+#define KW_IPVx_FLAG_ONLY       040
+#define KW_IPVx_REJECT          040
+#define KW_IPVx_MOD             041
+#define KW_IPVx_DYN             042
+#define KW_IPVx_REINSTATE       043
+
+static const char tbl_ipvx[] ALIGN1 =
+	/* 020 is the "takes an arg" bit */
+#if HAVE_NEW_ADDRT
+	"\011\020metric\0"
+#endif
+	"\012\021netmask\0"
+	"\005\022gw\0"
+	"\012\022gateway\0"
+	"\006\023mss\0"
+	"\011\024window\0"
+#ifdef RTF_IRTT
+	"\007\025irtt\0"
+#endif
+	"\006\026dev\0"
+	"\011\026device\0"
+	/* 040 is the "sets a flag" bit - MUST match flags_ipvx[] values below. */
+#ifdef RTF_REJECT
+	"\011\040reject\0"
+#endif
+	"\006\041mod\0"
+	"\006\042dyn\0"
+/*	"\014\043reinstate\0" */
+	"\013\043reinstate"			/* Since last, we can save a byte. */
+;
+
+static const int flags_ipvx[] = { /* MUST match tbl_ipvx[] values above. */
+#ifdef RTF_REJECT
+	RTF_REJECT,
+#endif
+	RTF_MODIFIED,
+	RTF_DYNAMIC,
+	RTF_REINSTATE
+};
+
+static int kw_lookup(const char *kwtbl, char ***pargs)
+{
+	if (**pargs) {
+		do {
+			if (strcmp(kwtbl+2, **pargs) == 0) { /* Found a match. */
+				*pargs += 1;
+				if (kwtbl[1] & KW_TAKES_ARG) {
+					if (!**pargs) {	/* No more args! */
+						bb_show_usage();
+					}
+					*pargs += 1; /* Calling routine will use args[-1]. */
+				}
+				return kwtbl[1];
+			}
+			kwtbl += *kwtbl;
+		} while (*kwtbl);
+	}
+	return 0;
+}
+
+/* Add or delete a route, depending on action. */
+
+static NOINLINE void INET_setroute(int action, char **args)
+{
+	/* 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;
+
+	/* Grab the -net or -host options.  Remember they were transformed. */
+	xflag = kw_lookup(tbl_hash_net_host, &args);
+
+	/* If we did grab -net or -host, make sure we still have an arg left. */
+	if (*args == NULL) {
+		bb_show_usage();
+	}
+
+	/* Clean out the RTREQ structure. */
+	memset(rt, 0, sizeof(*rt));
+
+	{
+		const char *target = *args++;
+		char *prefix;
+
+		/* recognize x.x.x.x/mask format. */
+		prefix = strchr(target, '/');
+		if (prefix) {
+			int prefix_len;
+
+			prefix_len = xatoul_range(prefix+1, 0, 32);
+			mask_in_addr(*rt) = htonl( ~(0xffffffffUL >> prefix_len));
+			*prefix = '\0';
+#if HAVE_NEW_ADDRT
+			rt->rt_genmask.sa_family = AF_INET;
+#endif
+		} else {
+			/* Default netmask. */
+			netmask = "default";
+		}
+		/* Prefer hostname lookup is -host flag (xflag==1) was given. */
+		isnet = INET_resolve(target, (struct sockaddr_in *) &rt->rt_dst,
+							 (xflag & HOST_FLAG));
+		if (isnet < 0) {
+			bb_error_msg_and_die("resolving %s", target);
+		}
+		if (prefix) {
+			/* do not destroy prefix for process args */
+			*prefix = '/';
+		}
+	}
+
+	if (xflag) {		/* Reinit isnet if -net or -host was specified. */
+		isnet = (xflag & NET_FLAG);
+	}
+
+	/* Fill in the other fields. */
+	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];
+			continue;
+		}
+
+#if HAVE_NEW_ADDRT
+		if (k == KW_IPVx_METRIC) {
+			rt->rt_metric = xatoul(args_m1) + 1;
+			continue;
+		}
+#endif
+
+		if (k == KW_IPVx_NETMASK) {
+			struct sockaddr mask;
+
+			if (mask_in_addr(*rt)) {
+				bb_show_usage();
+			}
+
+			netmask = args_m1;
+			isnet = INET_resolve(netmask, (struct sockaddr_in *) &mask, 0);
+			if (isnet < 0) {
+				bb_error_msg_and_die("resolving %s", netmask);
+			}
+			rt->rt_genmask = full_mask(mask);
+			continue;
+		}
+
+		if (k == KW_IPVx_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;
+
+			if (isnet) {
+				if (isnet < 0) {
+					bb_error_msg_and_die("resolving %s", args_m1);
+				}
+				bb_error_msg_and_die("gateway %s is a NETWORK", args_m1);
+			}
+			continue;
+		}
+
+		if (k == KW_IPVx_MSS) {	/* Check valid MSS bounds. */
+			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);
+			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 */
+#if 0					/* FIXME: do we need to check anything of this? */
+			if (rt->rt_irtt < 1 || rt->rt_irtt > (120 * HZ)) {
+				bb_error_msg_and_die("bad irtt");
+			}
+#endif
+			continue;
+		}
+#endif
+
+		/* 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))) {
+			/* Don't use args_m1 here since args may have changed! */
+			rt->rt_dev = args[-1];
+			continue;
+		}
+
+		/* Nothing matched. */
+		bb_show_usage();
+	}
+
+#ifdef RTF_REJECT
+	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);
+
+		mask = ~ntohl(mask);
+		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)) {
+			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;
+	}
+
+	/* Create a socket to the INET kernel. */
+	skfd = xsocket(AF_INET, SOCK_DGRAM, 0);
+
+	if (action == RTACTION_ADD)
+		xioctl(skfd, SIOCADDRT, rt);
+	else
+		xioctl(skfd, SIOCDELRT, rt);
+
+	if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
+}
+
+#if ENABLE_FEATURE_IPV6
+
+static NOINLINE void INET6_setroute(int action, char **args)
+{
+	struct sockaddr_in6 sa6;
+	struct in6_rtmsg rt;
+	int prefix_len, skfd;
+	const char *devname;
+
+		/* We know args isn't NULL from the check in route_main. */
+		const char *target = *args++;
+
+		if (strcmp(target, "default") == 0) {
+			prefix_len = 0;
+			memset(&sa6, 0, sizeof(sa6));
+		} else {
+			char *cp;
+			cp = strchr(target, '/'); /* Yes... const to non is ok. */
+			if (cp) {
+				*cp = '\0';
+				prefix_len = xatoul_range(cp + 1, 0, 128);
+			} else {
+				prefix_len = 128;
+			}
+			if (INET6_resolve(target, (struct sockaddr_in6 *) &sa6) < 0) {
+				bb_error_msg_and_die("resolving %s", target);
+			}
+		}
+
+	/* Clean out the RTREQ structure. */
+	memset(&rt, 0, sizeof(rt));
+
+	memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr));
+
+	/* Fill in the other fields. */
+	rt.rtmsg_dst_len = prefix_len;
+	rt.rtmsg_flags = ((prefix_len == 128) ? (RTF_UP|RTF_HOST) : RTF_UP);
+	rt.rtmsg_metric = 1;
+
+	devname = NULL;
+
+	while (*args) {
+		int k = kw_lookup(tbl_ipvx, &args);
+		const char *args_m1 = args[-1];
+
+		if ((k == KW_IPVx_MOD) || (k == KW_IPVx_DYN)) {
+			rt.rtmsg_flags |= flags_ipvx[k & 3];
+			continue;
+		}
+
+		if (k == KW_IPVx_METRIC) {
+			rt.rtmsg_metric = xatoul(args_m1);
+			continue;
+		}
+
+		if (k == KW_IPVx_GATEWAY) {
+			if (rt.rtmsg_flags & RTF_GATEWAY) {
+				bb_show_usage();
+			}
+
+			if (INET6_resolve(args_m1, (struct sockaddr_in6 *) &sa6) < 0) {
+				bb_error_msg_and_die("resolving %s", args_m1);
+			}
+			memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr,
+				   sizeof(struct in6_addr));
+			rt.rtmsg_flags |= RTF_GATEWAY;
+			continue;
+		}
+
+		/* Device is special in that it can be the last arg specified
+		 * and doesn't requre the dev/device keyword in that case. */
+		if (!devname && ((k == KW_IPVx_DEVICE) || (!k && !*++args))) {
+			/* Don't use args_m1 here since args may have changed! */
+			devname = args[-1];
+			continue;
+		}
+
+		/* Nothing matched. */
+		bb_show_usage();
+	}
+
+	/* Create a socket to the INET6 kernel. */
+	skfd = xsocket(AF_INET6, SOCK_DGRAM, 0);
+
+	rt.rtmsg_ifindex = 0;
+
+	if (devname) {
+		struct ifreq ifr;
+		memset(&ifr, 0, sizeof(ifr));
+		strncpy_IFNAMSIZ(ifr.ifr_name, devname);
+		xioctl(skfd, SIOGIFINDEX, &ifr);
+		rt.rtmsg_ifindex = ifr.ifr_ifindex;
+	}
+
+	/* Tell the kernel to accept this route. */
+	if (action == RTACTION_ADD)
+		xioctl(skfd, SIOCADDRT, &rt);
+	else
+		xioctl(skfd, SIOCDELRT, &rt);
+
+	if (ENABLE_FEATURE_CLEAN_UP) close(skfd);
+}
+#endif
+
+static const unsigned flagvals[] = { /* Must agree with flagchars[]. */
+	RTF_GATEWAY,
+	RTF_HOST,
+	RTF_REINSTATE,
+	RTF_DYNAMIC,
+	RTF_MODIFIED,
+#if ENABLE_FEATURE_IPV6
+	RTF_DEFAULT,
+	RTF_ADDRCONF,
+	RTF_CACHE
+#endif
+};
+
+#define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
+#define IPV6_MASK (RTF_GATEWAY|RTF_HOST|RTF_DEFAULT|RTF_ADDRCONF|RTF_CACHE)
+
+/* Must agree with flagvals[]. */
+static const char flagchars[] ALIGN1 =
+	"GHRDM"
+#if ENABLE_FEATURE_IPV6
+	"DAC"
+#endif
+;
+
+static void set_flags(char *flagstr, int flags)
+{
+	int i;
+
+	*flagstr++ = 'U';
+
+	for (i = 0; (*flagstr = flagchars[i]) != 0; i++) {
+		if (flags & flagvals[i]) {
+			++flagstr;
+		}
+	}
+}
+
+/* also used in netstat */
+void FAST_FUNC bb_displayroutes(int noresolve, int netstatfmt)
+{
+	char devname[64], flags[16], *sdest, *sgw;
+	unsigned long d, g, m;
+	int flgs, ref, use, metric, mtu, win, ir;
+	struct sockaddr_in s_addr;
+	struct in_addr mask;
+
+	FILE *fp = xfopen_for_read("/proc/net/route");
+
+	printf("Kernel IP routing table\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. */
+	}
+	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);
+		if (r != 11) {
+			if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+				break;
+			}
+ ERROR:
+			bb_error_msg_and_die("fscanf");
+		}
+
+		if (!(flgs & RTF_UP)) { /* Skip interfaces that are down. */
+			continue;
+		}
+
+		set_flags(flags, (flgs & IPV4_MASK));
+#ifdef RTF_REJECT
+		if (flgs & RTF_REJECT) {
+			flags[0] = '!';
+		}
+#endif
+
+		memset(&s_addr, 0, sizeof(struct sockaddr_in));
+		s_addr.sin_family = AF_INET;
+		s_addr.sin_addr.s_addr = d;
+		sdest = INET_rresolve(&s_addr, (noresolve | 0x8000), m); /* 'default' instead of '*' */
+		s_addr.sin_addr.s_addr = g;
+		sgw = INET_rresolve(&s_addr, (noresolve | 0x4000), m); /* Host instead of net */
+		mask.s_addr = m;
+		/* "%15.15s" truncates hostnames, do we really want that? */
+		printf("%-15.15s %-15.15s %-16s%-6s", sdest, sgw, inet_ntoa(mask), flags);
+		free(sdest);
+		free(sgw);
+		if (netstatfmt) {
+			printf("%5d %-5d %6d %s\n", mtu, win, ir, devname);
+		} else {
+			printf("%-6d %-2d %7d %s\n", metric, ref, use, devname);
+		}
+	}
+	fclose(fp);
+}
+
+#if ENABLE_FEATURE_IPV6
+
+static void INET6_displayroutes(void)
+{
+	char addr6[128], *naddr6;
+	/* In addr6x, we store both 40-byte ':'-delimited ipv6 addresses.
+	 * We read the non-delimited strings into the tail of the buffer
+	 * using fscanf and then modify the buffer by shifting forward
+	 * while inserting ':'s and the nul terminator for the first string.
+	 * Hence the strings are at addr6x and addr6x+40.  This generates
+	 * _much_ less code than the previous (upstream) approach. */
+	char addr6x[80];
+	char iface[16], flags[16];
+	int iflags, metric, refcnt, use, prefix_len, slen;
+	struct sockaddr_in6 snaddr6;
+
+	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");
+
+	while (1) {
+		int r;
+		r = fscanf(fp, "%32s%x%*s%x%32s%x%x%x%x%s\n",
+				addr6x+14, &prefix_len, &slen, addr6x+40+7,
+				&metric, &use, &refcnt, &iflags, iface);
+		if (r != 9) {
+			if ((r < 0) && feof(fp)) { /* EOF with no (nonspace) chars read. */
+				break;
+			}
+ ERROR:
+			bb_error_msg_and_die("fscanf");
+		}
+
+		/* Do the addr6x shift-and-insert changes to ':'-delimit addresses.
+		 * For now, always do this to validate the proc route format, even
+		 * if the interface is down. */
+		{
+			int i = 0;
+			char *p = addr6x+14;
+
+			do {
+				if (!*p) {
+					if (i == 40) { /* nul terminator for 1st address? */
+						addr6x[39] = 0;	/* Fixup... need 0 instead of ':'. */
+						++p;	/* Skip and continue. */
+						continue;
+					}
+					goto ERROR;
+				}
+				addr6x[i++] = *p++;
+				if (!((i+1) % 5)) {
+					addr6x[i++] = ':';
+				}
+			} while (i < 40+28+7);
+		}
+
+		if (!(iflags & RTF_UP)) { /* Skip interfaces that are down. */
+			continue;
+		}
+
+		set_flags(flags, (iflags & IPV6_MASK));
+
+		r = 0;
+		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. */
+						   );
+
+			if (!r) {			/* 1st pass */
+				snprintf(addr6, sizeof(addr6), "%s/%d", naddr6, prefix_len);
+				r += 40;
+				free(naddr6);
+			} else {			/* 2nd pass */
+				/* Print the info. */
+				printf("%-43s %-39s %-5s %-6d %-2d %7d %-8s\n",
+						addr6, naddr6, flags, metric, refcnt, use, iface);
+				free(naddr6);
+				break;
+			}
+		}
+	}
+	fclose(fp);
+}
+
+#endif
+
+#define ROUTE_OPT_A     0x01
+#define ROUTE_OPT_n     0x02
+#define ROUTE_OPT_e     0x04
+#define ROUTE_OPT_INET6 0x08 /* Not an actual option. See below. */
+
+/* 1st byte is offset to next entry offset.  2nd byte is return value. */
+/* 2nd byte matches RTACTION_* code */
+static const char tbl_verb[] ALIGN1 =
+	"\006\001add\0"
+	"\006\002del\0"
+/*	"\011\002delete\0" */
+	"\010\002delete"  /* Since it's last, we can save a byte. */
+;
+
+int route_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int route_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	int what;
+	char *family;
+	char **p;
+
+	/* First, remap '-net' and '-host' to avoid getopt problems. */
+	p = argv;
+	while (*++p) {
+		if (strcmp(*p, "-net") == 0 || strcmp(*p, "-host") == 0) {
+			p[0][0] = '#';
+		}
+	}
+
+	opt = getopt32(argv, "A:ne", &family);
+
+	if ((opt & ROUTE_OPT_A) && strcmp(family, "inet") != 0) {
+#if ENABLE_FEATURE_IPV6
+		if (strcmp(family, "inet6") == 0) {
+			opt |= ROUTE_OPT_INET6;	/* Set flag for ipv6. */
+		} else
+#endif
+		bb_show_usage();
+	}
+
+	argv += optind;
+
+	/* No more args means display the routing table. */
+	if (!*argv) {
+		int noresolve = (opt & ROUTE_OPT_n) ? 0x0fff : 0;
+#if ENABLE_FEATURE_IPV6
+		if (opt & ROUTE_OPT_INET6)
+			INET6_displayroutes();
+		else
+#endif
+			bb_displayroutes(noresolve, opt & ROUTE_OPT_e);
+
+		fflush_stdout_and_exit(EXIT_SUCCESS);
+	}
+
+	/* Check verb.  At the moment, must be add, del, or delete. */
+	what = kw_lookup(tbl_verb, &argv);
+	if (!what || !*argv) {		/* Unknown verb or no more args. */
+		bb_show_usage();
+	}
+
+#if ENABLE_FEATURE_IPV6
+	if (opt & ROUTE_OPT_INET6)
+		INET6_setroute(what, argv);
+	else
+#endif
+		INET_setroute(what, argv);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/slattach.c b/busybox-1.19.3/networking/slattach.c
new file mode 100644
index 0000000..a500da6
--- /dev/null
+++ b/busybox-1.19.3/networking/slattach.c
@@ -0,0 +1,258 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Stripped down version of net-tools for busybox.
+ *
+ * Author: Ignacio Garcia Perez (iggarpe at gmail dot com)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * There are some differences from the standard net-tools slattach:
+ *
+ * - The -l option is not supported.
+ *
+ * - 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() */
+
+struct globals {
+	int handle;
+	int saved_disc;
+	struct termios saved_state;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define handle       (G.handle      )
+#define saved_disc   (G.saved_disc  )
+#define saved_state  (G.saved_state )
+#define INIT_G() do { } while (0)
+
+
+/*
+ * Save tty state and line discipline
+ *
+ * It is fine here to bail out on errors, since we haven modified anything yet
+ */
+static void save_state(void)
+{
+	/* Save line status */
+	if (tcgetattr(handle, &saved_state) < 0)
+		bb_perror_msg_and_die("get state");
+
+	/* Save line discipline */
+	xioctl(handle, TIOCGETD, &saved_disc);
+}
+
+static int set_termios_state_or_warn(struct termios *state)
+{
+	int ret;
+
+	ret = tcsetattr(handle, TCSANOW, state);
+	if (ret < 0) {
+		bb_perror_msg("set state");
+		return 1; /* used as exitcode */
+	}
+	return 0;
+}
+
+/*
+ * Restore state and line discipline for ALL managed ttys
+ *
+ * Restoring ALL managed ttys is the only way to have a single
+ * hangup delay.
+ *
+ * Go on after errors: we want to restore as many controlled ttys
+ * as possible.
+ */
+static void restore_state_and_exit(int exitcode) NORETURN;
+static void restore_state_and_exit(int exitcode)
+{
+	struct termios state;
+
+	/* Restore line discipline */
+	if (ioctl_or_warn(handle, TIOCSETD, &saved_disc) < 0) {
+		exitcode = 1;
+	}
+
+	/* Hangup */
+	memcpy(&state, &saved_state, sizeof(state));
+	cfsetispeed(&state, B0);
+	cfsetospeed(&state, B0);
+	if (set_termios_state_or_warn(&state))
+		exitcode = 1;
+	sleep(1);
+
+	/* Restore line status */
+	if (set_termios_state_or_warn(&saved_state))
+		exit(EXIT_FAILURE);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(handle);
+
+	exit(exitcode);
+}
+
+/*
+ * Set tty state, line discipline and encapsulation
+ */
+static void set_state(struct termios *state, int encap)
+{
+	int disc;
+
+	/* Set line status */
+	if (set_termios_state_or_warn(state))
+		goto bad;
+	/* Set line discliple (N_SLIP always) */
+	disc = N_SLIP;
+	if (ioctl_or_warn(handle, TIOCSETD, &disc) < 0) {
+		goto bad;
+	}
+
+	/* Set encapsulation (SLIP, CSLIP, etc) */
+	if (ioctl_or_warn(handle, SIOCSIFENCAP, &encap) < 0) {
+ bad:
+		restore_state_and_exit(EXIT_FAILURE);
+	}
+}
+
+static void sig_handler(int signo UNUSED_PARAM)
+{
+	restore_state_and_exit(EXIT_SUCCESS);
+}
+
+int slattach_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int slattach_main(int argc UNUSED_PARAM, char **argv)
+{
+	/* Line discipline code table */
+	static const char proto_names[] ALIGN1 =
+		"slip\0"        /* 0 */
+		"cslip\0"       /* 1 */
+		"slip6\0"       /* 2 */
+		"cslip6\0"      /* 3 */
+		"adaptive\0"    /* 8 */
+		;
+
+	int i, encap, opt;
+	struct termios state;
+	const char *proto = "cslip";
+	const char *extcmd;   /* Command to execute after hangup */
+	const char *baud_str;
+	int baud_code = -1;   /* Line baud rate (system code) */
+
+	enum {
+		OPT_p_proto  = 1 << 0,
+		OPT_s_baud   = 1 << 1,
+		OPT_c_extcmd = 1 << 2,
+		OPT_e_quit   = 1 << 3,
+		OPT_h_watch  = 1 << 4,
+		OPT_m_nonraw = 1 << 5,
+		OPT_L_local  = 1 << 6,
+		OPT_F_noflow = 1 << 7
+	};
+
+	INIT_G();
+
+	/* Parse command line options */
+	opt = getopt32(argv, "p:s:c:ehmLF", &proto, &baud_str, &extcmd);
+	/*argc -= optind;*/
+	argv += optind;
+
+	if (!*argv)
+		bb_show_usage();
+
+	encap = index_in_strings(proto_names, proto);
+
+	if (encap < 0)
+		invarg(proto, "protocol");
+	if (encap > 3)
+		encap = 8;
+
+	/* We want to know if the baud rate is valid before we start touching the ttys */
+	if (opt & OPT_s_baud) {
+		baud_code = tty_value_to_baud(xatoi(baud_str));
+		if (baud_code < 0)
+			invarg(baud_str, "baud rate");
+	}
+
+	/* Trap signals in order to restore tty states upon exit */
+	if (!(opt & OPT_e_quit)) {
+		bb_signals(0
+			+ (1 << SIGHUP)
+			+ (1 << SIGINT)
+			+ (1 << SIGQUIT)
+			+ (1 << SIGTERM)
+			, sig_handler);
+	}
+
+	/* Open tty */
+	handle = open(*argv, O_RDWR | O_NDELAY);
+	if (handle < 0) {
+		char *buf = concat_path_file("/dev", *argv);
+		handle = xopen(buf, O_RDWR | O_NDELAY);
+		/* maybe if (ENABLE_FEATURE_CLEAN_UP) ?? */
+		free(buf);
+	}
+
+	/* Save current tty state */
+	save_state();
+
+	/* Configure tty */
+	memcpy(&state, &saved_state, sizeof(state));
+	if (!(opt & OPT_m_nonraw)) { /* raw not suppressed */
+		memset(&state.c_cc, 0, sizeof(state.c_cc));
+		state.c_cc[VMIN] = 1;
+		state.c_iflag = IGNBRK | IGNPAR;
+		state.c_oflag = 0;
+		state.c_lflag = 0;
+		state.c_cflag = CS8 | HUPCL | CREAD
+		              | ((opt & OPT_L_local) ? CLOCAL : 0)
+		              | ((opt & OPT_F_noflow) ? 0 : CRTSCTS);
+		cfsetispeed(&state, cfgetispeed(&saved_state));
+		cfsetospeed(&state, cfgetospeed(&saved_state));
+	}
+
+	if (opt & OPT_s_baud) {
+		cfsetispeed(&state, baud_code);
+		cfsetospeed(&state, baud_code);
+	}
+
+	set_state(&state, encap);
+
+	/* Exit now if option -e was passed */
+	if (opt & OPT_e_quit)
+		return 0;
+
+	/* If we're not requested to watch, just keep descriptor open
+	 * until we are killed */
+	if (!(opt & OPT_h_watch))
+		while (1)
+			sleep(24*60*60);
+
+	/* Watch line for hangup */
+	while (1) {
+		if (ioctl(handle, TIOCMGET, &i) < 0 || !(i & TIOCM_CAR))
+			goto no_carrier;
+		sleep(15);
+	}
+
+ no_carrier:
+
+	/* Execute command on hangup */
+	if (opt & OPT_c_extcmd)
+		system(extcmd);
+
+	/* Restore states and exit */
+	restore_state_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/networking/tc.c b/busybox-1.19.3/networking/tc.c
new file mode 100644
index 0000000..1574353
--- /dev/null
+++ b/busybox-1.19.3/networking/tc.c
@@ -0,0 +1,564 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * 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"
+#include "libiproute/ip_common.h"
+#include "libiproute/rt_names.h"
+#include <linux/pkt_sched.h> /* for the TC_H_* macros */
+
+#define parse_rtattr_nested(tb, max, rta) \
+	(parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+/* nullifies tb on error */
+#define __parse_rtattr_nested_compat(tb, max, rta, len) \
+	({if ((RTA_PAYLOAD(rta) >= len) && \
+		 (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr))) { \
+			rta = RTA_DATA(rta) + RTA_ALIGN(len); \
+			parse_rtattr_nested(tb, max, rta); \
+	  } else \
+			memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); \
+	})
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+	({data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+	__parse_rtattr_nested_compat(tb, max, rta, len); })
+
+#define show_details (0) /* not implemented. Does anyone need it? */
+#define use_iec (0) /* not currently documented in the upstream manpage */
+
+
+struct globals {
+	int filter_ifindex;
+	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)
+#define INIT_G() do { } while (0)
+
+/* Allocates a buffer containing the name of a class id.
+ * The caller must free the returned memory.  */
+static char* print_tc_classid(uint32_t cid)
+{
+#if 0 /* IMPOSSIBLE */
+	if (cid == TC_H_ROOT)
+		return xasprintf("root");
+	else
+#endif
+	if (cid == TC_H_UNSPEC)
+		return xasprintf("none");
+	else if (TC_H_MAJ(cid) == 0)
+		return xasprintf(":%x", TC_H_MIN(cid));
+	else if (TC_H_MIN(cid) == 0)
+		return xasprintf("%x:", TC_H_MAJ(cid)>>16);
+	else
+		return xasprintf("%x:%x", TC_H_MAJ(cid)>>16, TC_H_MIN(cid));
+}
+
+/* Get a qdisc handle.  Return 0 on success, !0 otherwise.  */
+static int get_qdisc_handle(uint32_t *h, const char *str) {
+	uint32_t maj;
+	char *p;
+
+	maj = TC_H_UNSPEC;
+	if (!strcmp(str, "none"))
+		goto ok;
+	maj = strtoul(str, &p, 16);
+	if (p == str)
+		return 1;
+	maj <<= 16;
+	if (*p != ':' && *p != '\0')
+		return 1;
+ ok:
+	*h = maj;
+	return 0;
+}
+
+/* Get class ID.  Return 0 on success, !0 otherwise.  */
+static int get_tc_classid(uint32_t *h, const char *str) {
+	uint32_t maj, min;
+	char *p;
+
+	maj = TC_H_ROOT;
+	if (!strcmp(str, "root"))
+		goto ok;
+	maj = TC_H_UNSPEC;
+	if (!strcmp(str, "none"))
+		goto ok;
+	maj = strtoul(str, &p, 16);
+	if (p == str) {
+		if (*p != ':')
+			return 1;
+		maj = 0;
+	}
+	if (*p == ':') {
+		if (maj >= (1<<16))
+			return 1;
+		maj <<= 16;
+		str = p + 1;
+		min = strtoul(str, &p, 16);
+//FIXME: check for "" too?
+		if (*p != '\0' || min >= (1<<16))
+			return 1;
+		maj |= min;
+	} else if (*p != 0)
+		return 1;
+ ok:
+	*h = maj;
+	return 0;
+}
+
+static void print_rate(char *buf, int len, uint32_t rate)
+{
+	double tmp = (double)rate*8;
+
+	if (use_iec) {
+		if (tmp >= 1000.0*1024.0*1024.0)
+			snprintf(buf, len, "%.0fMibit", tmp/1024.0*1024.0);
+		else if (tmp >= 1000.0*1024)
+			snprintf(buf, len, "%.0fKibit", tmp/1024);
+		else
+			snprintf(buf, len, "%.0fbit", tmp);
+	} else {
+		if (tmp >= 1000.0*1000000.0)
+			snprintf(buf, len, "%.0fMbit", tmp/1000000.0);
+		else if (tmp >= 1000.0 * 1000.0)
+			snprintf(buf, len, "%.0fKbit", tmp/1000.0);
+		else
+			snprintf(buf, len, "%.0fbit",  tmp);
+	}
+}
+
+/* This is "pfifo_fast".  */
+static int prio_parse_opt(int argc, char **argv, struct nlmsghdr *n)
+{
+	return 0;
+}
+static int prio_print_opt(struct rtattr *opt)
+{
+	int i;
+	struct tc_prio_qopt *qopt;
+	struct rtattr *tb[TCA_PRIO_MAX+1];
+
+	if (opt == NULL)
+		return 0;
+	parse_rtattr_nested_compat(tb, TCA_PRIO_MAX, opt, qopt, sizeof(*qopt));
+	if (tb == NULL)
+		return 0;
+	printf("bands %u priomap ", qopt->bands);
+	for (i=0; i<=TC_PRIO_MAX; i++)
+		printf(" %d", qopt->priomap[i]);
+
+	if (tb[TCA_PRIO_MQ])
+		printf(" multiqueue: o%s ",
+		    *(unsigned char *)RTA_DATA(tb[TCA_PRIO_MQ]) ? "n" : "ff");
+
+	return 0;
+}
+
+/* Class Based Queue */
+static int cbq_parse_opt(int argc, char **argv, struct nlmsghdr *n)
+{
+	return 0;
+}
+static int cbq_print_opt(struct rtattr *opt)
+{
+	struct rtattr *tb[TCA_CBQ_MAX+1];
+	struct tc_ratespec *r = NULL;
+	struct tc_cbq_lssopt *lss = NULL;
+	struct tc_cbq_wrropt *wrr = NULL;
+	struct tc_cbq_fopt *fopt = NULL;
+	struct tc_cbq_ovl *ovl = NULL;
+	const char *const error = "CBQ: too short %s opt";
+	char buf[64];
+
+	if (opt == NULL)
+		goto done;
+	parse_rtattr_nested(tb, TCA_CBQ_MAX, opt);
+
+	if (tb[TCA_CBQ_RATE]) {
+		if (RTA_PAYLOAD(tb[TCA_CBQ_RATE]) < sizeof(*r))
+			bb_error_msg(error, "rate");
+		else
+			r = RTA_DATA(tb[TCA_CBQ_RATE]);
+	}
+	if (tb[TCA_CBQ_LSSOPT]) {
+		if (RTA_PAYLOAD(tb[TCA_CBQ_LSSOPT]) < sizeof(*lss))
+			bb_error_msg(error, "lss");
+		else
+			lss = RTA_DATA(tb[TCA_CBQ_LSSOPT]);
+	}
+	if (tb[TCA_CBQ_WRROPT]) {
+		if (RTA_PAYLOAD(tb[TCA_CBQ_WRROPT]) < sizeof(*wrr))
+			bb_error_msg(error, "wrr");
+		else
+			wrr = RTA_DATA(tb[TCA_CBQ_WRROPT]);
+	}
+	if (tb[TCA_CBQ_FOPT]) {
+		if (RTA_PAYLOAD(tb[TCA_CBQ_FOPT]) < sizeof(*fopt))
+			bb_error_msg(error, "fopt");
+		else
+			fopt = RTA_DATA(tb[TCA_CBQ_FOPT]);
+	}
+	if (tb[TCA_CBQ_OVL_STRATEGY]) {
+		if (RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]) < sizeof(*ovl))
+			bb_error_msg("CBQ: too short overlimit strategy %u/%u",
+				(unsigned) RTA_PAYLOAD(tb[TCA_CBQ_OVL_STRATEGY]),
+				(unsigned) sizeof(*ovl));
+		else
+			ovl = RTA_DATA(tb[TCA_CBQ_OVL_STRATEGY]);
+	}
+
+	if (r) {
+		print_rate(buf, sizeof(buf), r->rate);
+		printf("rate %s ", buf);
+		if (show_details) {
+			printf("cell %ub ", 1<<r->cell_log);
+			if (r->mpu)
+				printf("mpu %ub ", r->mpu);
+			if (r->overhead)
+				printf("overhead %ub ", r->overhead);
+		}
+	}
+	if (lss && lss->flags) {
+		bool comma = false;
+		bb_putchar('(');
+		if (lss->flags&TCF_CBQ_LSS_BOUNDED) {
+			printf("bounded");
+			comma = true;
+		}
+		if (lss->flags&TCF_CBQ_LSS_ISOLATED) {
+			if (comma)
+				bb_putchar(',');
+			printf("isolated");
+		}
+		printf(") ");
+	}
+	if (wrr) {
+		if (wrr->priority != TC_CBQ_MAXPRIO)
+			printf("prio %u", wrr->priority);
+		else
+			printf("prio no-transmit");
+		if (show_details) {
+			printf("/%u ", wrr->cpriority);
+			if (wrr->weight != 1) {
+				print_rate(buf, sizeof(buf), wrr->weight);
+				printf("weight %s ", buf);
+			}
+			if (wrr->allot)
+				printf("allot %ub ", wrr->allot);
+		}
+	}
+ done:
+	return 0;
+}
+
+static int print_qdisc(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];
+	char *name;
+
+	if (hdr->nlmsg_type != RTM_NEWQDISC && hdr->nlmsg_type != RTM_DELQDISC) {
+		/* bb_error_msg("not a qdisc"); */
+		return 0; /* ??? mimic upstream; should perhaps return -1 */
+	}
+	len -= NLMSG_LENGTH(sizeof(*msg));
+	if (len < 0) {
+		/* bb_error_msg("wrong len %d", len); */
+		return -1;
+	}
+	/* not the desired interface? */
+	if (filter_ifindex && filter_ifindex != msg->tcm_ifindex)
+		return 0;
+	memset (tb, 0, sizeof(tb));
+	parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len);
+	if (tb[TCA_KIND] == NULL) {
+		/* bb_error_msg("%s: NULL kind", "qdisc"); */
+		return -1;
+	}
+	if (hdr->nlmsg_type == RTM_DELQDISC)
+		printf("deleted ");
+	name = (char*)RTA_DATA(tb[TCA_KIND]);
+	printf("qdisc %s %x: ", name, msg->tcm_handle>>16);
+	if (filter_ifindex == 0)
+		printf("dev %s ", ll_index_to_name(msg->tcm_ifindex));
+	if (msg->tcm_parent == TC_H_ROOT)
+		printf("root ");
+	else if (msg->tcm_parent) {
+		char *classid = print_tc_classid(msg->tcm_parent);
+		printf("parent %s ", classid);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(classid);
+	}
+	if (msg->tcm_info != 1)
+		printf("refcnt %d ", msg->tcm_info);
+	if (tb[TCA_OPTIONS]) {
+		static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0";
+		int qqq = index_in_strings(_q_, name);
+		if (qqq == 0) { /* pfifo_fast aka prio */
+			prio_print_opt(tb[TCA_OPTIONS]);
+		} else if (qqq == 1) { /* class based queuing */
+			cbq_print_opt(tb[TCA_OPTIONS]);
+		} else
+			bb_error_msg("unknown %s", name);
+	}
+	bb_putchar('\n');
+	return 0;
+}
+
+static int print_class(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];
+	char *name, *classid;
+
+	/*XXX Eventually factor out common code */
+
+	if (hdr->nlmsg_type != RTM_NEWTCLASS && hdr->nlmsg_type != RTM_DELTCLASS) {
+		/* bb_error_msg("not a class"); */
+		return 0; /* ??? mimic upstream; should perhaps return -1 */
+	}
+	len -= NLMSG_LENGTH(sizeof(*msg));
+	if (len < 0) {
+		/* bb_error_msg("wrong len %d", len); */
+		return -1;
+	}
+	/* not the desired interface? */
+	if (filter_qdisc && TC_H_MAJ(msg->tcm_handle^filter_qdisc))
+		return 0;
+	memset (tb, 0, sizeof(tb));
+	parse_rtattr(tb, TCA_MAX, TCA_RTA(msg), len);
+	if (tb[TCA_KIND] == NULL) {
+		/* bb_error_msg("%s: NULL kind", "class"); */
+		return -1;
+	}
+	if (hdr->nlmsg_type == RTM_DELTCLASS)
+		printf("deleted ");
+
+	name = (char*)RTA_DATA(tb[TCA_KIND]);
+	classid = !msg->tcm_handle ? NULL : print_tc_classid(
+				filter_qdisc ? TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
+	printf ("class %s %s", name, classid);
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(classid);
+
+	if (filter_ifindex == 0)
+		printf("dev %s ", ll_index_to_name(msg->tcm_ifindex));
+	if (msg->tcm_parent == TC_H_ROOT)
+		printf("root ");
+	else if (msg->tcm_parent) {
+		classid = print_tc_classid(filter_qdisc ?
+								   TC_H_MIN(msg->tcm_parent) : msg->tcm_parent);
+		printf("parent %s ", classid);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(classid);
+	}
+	if (msg->tcm_info)
+		printf("leaf %x ", msg->tcm_info >> 16);
+	/* Do that get_qdisc_kind(RTA_DATA(tb[TCA_KIND])).  */
+	if (tb[TCA_OPTIONS]) {
+		static const char _q_[] ALIGN1 = "pfifo_fast\0""cbq\0";
+		int qqq = index_in_strings(_q_, name);
+		if (qqq == 0) { /* pfifo_fast aka prio */
+			/* nothing. */ /*prio_print_opt(tb[TCA_OPTIONS]);*/
+		} else if (qqq == 1) { /* class based queuing */
+			/* cbq_print_copt() is identical to cbq_print_opt(). */
+			cbq_print_opt(tb[TCA_OPTIONS]);
+		} else
+			bb_error_msg("unknown %s", name);
+	}
+	bb_putchar('\n');
+
+	return 0;
+}
+
+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;
+}
+
+int tc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tc_main(int argc UNUSED_PARAM, char **argv)
+{
+	static const char objects[] ALIGN1 =
+		"qdisc\0""class\0""filter\0"
+		;
+	enum { OBJ_qdisc = 0, OBJ_class, OBJ_filter };
+	static const char commands[] ALIGN1 =
+		"add\0""delete\0""change\0"
+		"link\0" /* only qdisc */
+		"replace\0"
+		"show\0""list\0"
+		;
+	static const char args[] ALIGN1 =
+		"dev\0" /* qdisc, class, filter */
+		"root\0" /* class, filter */
+		"parent\0" /* class, filter */
+		"qdisc\0" /* class */
+		"handle\0" /* change: qdisc, class(classid) list: filter */
+		"classid\0" /* change: for class use "handle" */
+		"preference\0""priority\0""protocol\0" /* filter */
+		;
+	enum { CMD_add = 0, CMD_del, CMD_change, CMD_link, CMD_replace, CMD_show };
+	enum { ARG_dev = 0, ARG_root, ARG_parent, ARG_qdisc,
+			ARG_handle, ARG_classid, ARG_pref, ARG_prio, ARG_proto};
+	struct rtnl_handle rth;
+	struct tcmsg msg;
+	int ret, obj, cmd, arg;
+	char *dev = NULL;
+
+	INIT_G();
+
+	if (!*++argv)
+		bb_show_usage();
+	xrtnl_open(&rth);
+	ret = EXIT_SUCCESS;
+
+	obj = index_in_substrings(objects, *argv++);
+
+	if (obj < OBJ_qdisc)
+		bb_show_usage();
+	if (!*argv)
+		cmd = CMD_show; /* list is the default */
+	else {
+		cmd = index_in_substrings(commands, *argv);
+		if (cmd < 0)
+			bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
+		argv++;
+	}
+	memset(&msg, 0, sizeof(msg));
+	msg.tcm_family = AF_UNSPEC;
+	ll_init_map(&rth);
+	while (*argv) {
+		arg = index_in_substrings(args, *argv);
+		if (arg == ARG_dev) {
+			NEXT_ARG();
+			if (dev)
+				duparg2("dev", *argv);
+			dev = *argv++;
+			msg.tcm_ifindex = xll_name_to_index(dev);
+			if (cmd >= CMD_show)
+				filter_ifindex = msg.tcm_ifindex;
+		} else
+		if ((arg == ARG_qdisc && obj == OBJ_class && cmd >= CMD_show)
+		 || (arg == ARG_handle && obj == OBJ_qdisc && cmd == CMD_change)
+		) {
+			NEXT_ARG();
+			/* We don't care about duparg2("qdisc handle",*argv) for now */
+			if (get_qdisc_handle(&filter_qdisc, *argv))
+				invarg(*argv, "qdisc");
+		} else
+		if (obj != OBJ_qdisc
+		 && (arg == ARG_root
+		    || arg == ARG_parent
+		    || (obj == OBJ_filter && arg >= ARG_pref)
+		    )
+		) {
+			/* nothing */
+		} else {
+			invarg(*argv, "command");
+		}
+		NEXT_ARG();
+		if (arg == ARG_root) {
+			if (msg.tcm_parent)
+				duparg("parent", *argv);
+			msg.tcm_parent = TC_H_ROOT;
+			if (obj == OBJ_filter)
+				filter_parent = TC_H_ROOT;
+		} else if (arg == ARG_parent) {
+			uint32_t handle;
+			if (msg.tcm_parent)
+				duparg(*argv, "parent");
+			if (get_tc_classid(&handle, *argv))
+				invarg(*argv, "parent");
+			msg.tcm_parent = handle;
+			if (obj == OBJ_filter)
+				filter_parent = handle;
+		} else if (arg == ARG_handle) { /* filter::list */
+			if (msg.tcm_handle)
+				duparg(*argv, "handle");
+			/* reject LONG_MIN || LONG_MAX */
+			/* TODO: for fw
+			   if ((slash = strchr(handle, '/')) != NULL)
+				   *slash = '\0';
+			 */
+			msg.tcm_handle = get_u32(*argv, "handle");
+			/* 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)
+				duparg(*argv, "priority");
+			filter_prio = get_u32(*argv, "priority");
+		} else if (arg == ARG_proto) { /* filter::list */
+			uint16_t tmp;
+			if (filter_proto)
+				duparg(*argv, "protocol");
+			if (ll_proto_a2n(&tmp, *argv))
+				invarg(*argv, "protocol");
+			filter_proto = tmp;
+		}
+	}
+	if (cmd >= CMD_show) { /* show or list */
+		if (obj == OBJ_filter)
+			msg.tcm_info = TC_H_MAKE(filter_prio<<16, filter_proto);
+		if (rtnl_dump_request(&rth, obj == OBJ_qdisc ? RTM_GETQDISC :
+						obj == OBJ_class ? RTM_GETTCLASS : RTM_GETTFILTER,
+						&msg, sizeof(msg)) < 0)
+			bb_simple_perror_msg_and_die("can't send dump request");
+
+		xrtnl_dump_filter(&rth, obj == OBJ_qdisc ? print_qdisc :
+						obj == OBJ_class ? print_class : print_filter,
+						NULL);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		rtnl_close(&rth);
+	}
+	return ret;
+}
diff --git a/busybox-1.19.3/networking/tcpudp.c b/busybox-1.19.3/networking/tcpudp.c
new file mode 100644
index 0000000..3df6a98
--- /dev/null
+++ b/busybox-1.19.3/networking/tcpudp.c
@@ -0,0 +1,646 @@
+/* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
+ * which are released into public domain by the author.
+ * Homepage: http://smarden.sunsite.dk/ipsvd/
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* Based on ipsvd-0.12.1. This tcpsvd accepts all options
+ * which are supported by one from ipsvd-0.12.1, but not all are
+ * functional. See help text at the end of this file for details.
+ *
+ * Code inside "#ifdef SSLSVD" is for sslsvd and is currently unused.
+ *
+ * Busybox version exports TCPLOCALADDR instead of
+ * TCPLOCALIP + TCPLOCALPORT pair. ADDR more closely matches reality
+ * (which is "struct sockaddr_XXX". Port is not a separate entity,
+ * it's just a part of (AF_INET[6]) sockaddr!).
+ *
+ * TCPORIGDSTADDR is Busybox-specific addition.
+ *
+ * udp server is hacked up by reusing TCP code. It has the following
+ * limitation inherent in Unix DGRAM sockets implementation:
+ * - local IP address is retrieved (using recvmsg voodoo) but
+ *   child's socket is not bound to it (bind cannot be called on
+ *   already bound socket). Thus it still can emit outgoing packets
+ *   with wrong source IP...
+ * - 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"
+
+#ifdef SSLSVD
+#include "matrixSsl.h"
+#include "ssl_io.h"
+#endif
+
+struct globals {
+	unsigned verbose;
+	unsigned max_per_host;
+	unsigned cur_per_host;
+	unsigned cnum;
+	unsigned cmax;
+	char **env_cur;
+	char *env_var[1]; /* actually bigger */
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define verbose      (G.verbose     )
+#define max_per_host (G.max_per_host)
+#define cur_per_host (G.cur_per_host)
+#define cnum         (G.cnum        )
+#define cmax         (G.cmax        )
+#define env_cur      (G.env_cur     )
+#define env_var      (G.env_var     )
+#define INIT_G() do { \
+	cmax = 30; \
+	env_cur = &env_var[0]; \
+} while (0)
+
+
+/* We have to be careful about leaking memory in repeated setenv's */
+static void xsetenv_plain(const char *n, const char *v)
+{
+	char *var = xasprintf("%s=%s", n, v);
+	*env_cur++ = var;
+	putenv(var);
+}
+
+static void xsetenv_proto(const char *proto, const char *n, const char *v)
+{
+	char *var = xasprintf("%s%s=%s", proto, n, v);
+	*env_cur++ = var;
+	putenv(var);
+}
+
+static void undo_xsetenv(void)
+{
+	char **pp = env_cur = &env_var[0];
+	while (*pp) {
+		char *var = *pp;
+		bb_unsetenv_and_free(var);
+		*pp++ = NULL;
+	}
+}
+
+static void sig_term_handler(int sig)
+{
+	if (verbose)
+		bb_error_msg("got signal %u, exit", sig);
+	kill_myself_with_sig(sig);
+}
+
+/* Little bloated, but tries to give accurate info how child exited.
+ * Makes easier to spot segfaulting children etc... */
+static void print_waitstat(unsigned pid, int wstat)
+{
+	unsigned e = 0;
+	const char *cause = "?exit";
+
+	if (WIFEXITED(wstat)) {
+		cause++;
+		e = WEXITSTATUS(wstat);
+	} else if (WIFSIGNALED(wstat)) {
+		cause = "signal";
+		e = WTERMSIG(wstat);
+	}
+	bb_error_msg("end %d %s %d", pid, cause, e);
+}
+
+/* Must match getopt32 in main! */
+enum {
+	OPT_c = (1 << 0),
+	OPT_C = (1 << 1),
+	OPT_i = (1 << 2),
+	OPT_x = (1 << 3),
+	OPT_u = (1 << 4),
+	OPT_l = (1 << 5),
+	OPT_E = (1 << 6),
+	OPT_b = (1 << 7),
+	OPT_h = (1 << 8),
+	OPT_p = (1 << 9),
+	OPT_t = (1 << 10),
+	OPT_v = (1 << 11),
+	OPT_V = (1 << 12),
+	OPT_U = (1 << 13), /* from here: sslsvd only */
+	OPT_slash = (1 << 14),
+	OPT_Z = (1 << 15),
+	OPT_K = (1 << 16),
+};
+
+static void connection_status(void)
+{
+	/* "only 1 client max" desn't need this */
+	if (cmax > 1)
+		bb_error_msg("status %u/%u", cnum, cmax);
+}
+
+static void sig_child_handler(int sig UNUSED_PARAM)
+{
+	int wstat;
+	pid_t pid;
+
+	while ((pid = wait_any_nohang(&wstat)) > 0) {
+		if (max_per_host)
+			ipsvd_perhost_remove(pid);
+		if (cnum)
+			cnum--;
+		if (verbose)
+			print_waitstat(pid, wstat);
+	}
+	if (verbose)
+		connection_status();
+}
+
+int tcpudpsvd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tcpudpsvd_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *str_C, *str_t;
+	char *user;
+	struct hcc *hccp;
+	const char *instructs;
+	char *msg_per_host = NULL;
+	unsigned len_per_host = len_per_host; /* gcc */
+#ifndef SSLSVD
+	struct bb_uidgid_t ugid;
+#endif
+	bool tcp;
+	uint16_t local_port;
+	char *preset_local_hostname = NULL;
+	char *remote_hostname = remote_hostname; /* for compiler */
+	char *remote_addr = remote_addr; /* for compiler */
+	len_and_sockaddr *lsa;
+	len_and_sockaddr local, remote;
+	socklen_t sa_len;
+	int pid;
+	int sock;
+	int conn;
+	unsigned backlog = 20;
+	unsigned opts;
+
+	INIT_G();
+
+	tcp = (applet_name[0] == 't');
+
+	/* 3+ args, -i at most once, -p implies -h, -v is counter, -b N, -c N */
+	opt_complementary = "-3:i--i:ph:vv:b+:c+";
+#ifdef SSLSVD
+	opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:vU:/:Z:K:",
+		&cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
+		&backlog, &str_t, &ssluser, &root, &cert, &key, &verbose
+	);
+#else
+	/* "+": stop on first non-option */
+	opts = getopt32(argv, "+c:C:i:x:u:l:Eb:hpt:v",
+		&cmax, &str_C, &instructs, &instructs, &user, &preset_local_hostname,
+		&backlog, &str_t, &verbose
+	);
+#endif
+	if (opts & OPT_C) { /* -C n[:message] */
+		max_per_host = bb_strtou(str_C, &str_C, 10);
+		if (str_C[0]) {
+			if (str_C[0] != ':')
+				bb_show_usage();
+			msg_per_host = str_C + 1;
+			len_per_host = strlen(msg_per_host);
+		}
+	}
+	if (max_per_host > cmax)
+		max_per_host = cmax;
+	if (opts & OPT_u) {
+		xget_uidgid(&ugid, user);
+	}
+#ifdef SSLSVD
+	if (opts & OPT_U) ssluser = optarg;
+	if (opts & OPT_slash) root = optarg;
+	if (opts & OPT_Z) cert = optarg;
+	if (opts & OPT_K) key = optarg;
+#endif
+	argv += optind;
+	if (!argv[0][0] || LONE_CHAR(argv[0], '0'))
+		argv[0] = (char*)"0.0.0.0";
+
+	/* Per-IP flood protection is not thought-out for UDP */
+	if (!tcp)
+		max_per_host = 0;
+
+	bb_sanitize_stdio(); /* fd# 0,1,2 must be opened */
+
+#ifdef SSLSVD
+	sslser = user;
+	client = 0;
+	if ((getuid() == 0) && !(opts & OPT_u)) {
+		xfunc_exitcode = 100;
+		bb_error_msg_and_die(bb_msg_you_must_be_root);
+	}
+	if (opts & OPT_u)
+		if (!uidgid_get(&sslugid, ssluser, 1)) {
+			if (errno) {
+				bb_perror_msg_and_die("can't get user/group: %s", ssluser);
+			}
+			bb_error_msg_and_die("unknown user/group %s", ssluser);
+		}
+	if (!cert) cert = "./cert.pem";
+	if (!key) key = cert;
+	if (matrixSslOpen() < 0)
+		fatal("can't initialize ssl");
+	if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
+		if (client)
+			fatal("can't read cert, key, or ca file");
+		fatal("can't read cert or key file");
+	}
+	if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0)
+		fatal("can't create ssl session");
+#endif
+
+	sig_block(SIGCHLD);
+	signal(SIGCHLD, sig_child_handler);
+	bb_signals(BB_FATAL_SIGS, sig_term_handler);
+	signal(SIGPIPE, SIG_IGN);
+
+	if (max_per_host)
+		ipsvd_perhost_init(cmax);
+
+	local_port = bb_lookup_port(argv[1], tcp ? "tcp" : "udp", 0);
+	lsa = xhost2sockaddr(argv[0], local_port);
+	argv += 2;
+
+	sock = xsocket(lsa->u.sa.sa_family, tcp ? SOCK_STREAM : SOCK_DGRAM, 0);
+	setsockopt_reuseaddr(sock);
+	sa_len = lsa->len; /* I presume sockaddr len stays the same */
+	xbind(sock, &lsa->u.sa, sa_len);
+	if (tcp) {
+		xlisten(sock, backlog);
+		close_on_exec_on(sock);
+	} else { /* udp: needed for recv_from_to to work: */
+		socket_want_pktinfo(sock);
+	}
+	/* ndelay_off(sock); - it is the default I think? */
+
+#ifndef SSLSVD
+	if (opts & OPT_u) {
+		/* drop permissions */
+		xsetgid(ugid.gid);
+		xsetuid(ugid.uid);
+	}
+#endif
+
+	if (verbose) {
+		char *addr = xmalloc_sockaddr2dotted(&lsa->u.sa);
+		if (opts & OPT_u)
+			bb_error_msg("listening on %s, starting, uid %u, gid %u", addr,
+				(unsigned)ugid.uid, (unsigned)ugid.gid);
+		else
+			bb_error_msg("listening on %s, starting", addr);
+		free(addr);
+	}
+
+	/* Main accept() loop */
+
+ again:
+	hccp = NULL;
+
+	while (cnum >= cmax)
+		wait_for_any_sig(); /* expecting SIGCHLD */
+
+	/* Accept a connection to fd #0 */
+ again1:
+	close(0);
+ again2:
+	sig_unblock(SIGCHLD);
+	local.len = remote.len = sa_len;
+	if (tcp) {
+		conn = accept(sock, &remote.u.sa, &remote.len);
+	} else {
+		/* In case recv_from_to won't be able to recover local addr.
+		 * Also sets port - recv_from_to is unable to do it. */
+		local = *lsa;
+		conn = recv_from_to(sock, NULL, 0, MSG_PEEK,
+				&remote.u.sa, &local.u.sa, sa_len);
+	}
+	sig_block(SIGCHLD);
+	if (conn < 0) {
+		if (errno != EINTR)
+			bb_perror_msg(tcp ? "accept" : "recv");
+		goto again2;
+	}
+	xmove_fd(tcp ? conn : sock, 0);
+
+	if (max_per_host) {
+		/* Drop connection immediately if cur_per_host > max_per_host
+		 * (minimizing load under SYN flood) */
+		remote_addr = xmalloc_sockaddr2dotted_noport(&remote.u.sa);
+		cur_per_host = ipsvd_perhost_add(remote_addr, max_per_host, &hccp);
+		if (cur_per_host > max_per_host) {
+			/* ipsvd_perhost_add detected that max is exceeded
+			 * (and did not store ip in connection table) */
+			free(remote_addr);
+			if (msg_per_host) {
+				/* don't block or test for errors */
+				send(0, msg_per_host, len_per_host, MSG_DONTWAIT);
+			}
+			goto again1;
+		}
+		/* NB: remote_addr is not leaked, it is stored in conn table */
+	}
+
+	if (!tcp) {
+		/* Voodoo magic: making udp sockets each receive its own
+		 * packets is not trivial, and I still not sure
+		 * I do it 100% right.
+		 * 1) we have to do it before fork()
+		 * 2) order is important - is it right now? */
+
+		/* Open new non-connected UDP socket for further clients... */
+		sock = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+		setsockopt_reuseaddr(sock);
+		/* Make plain write/send work for old socket by supplying default
+		 * destination address. This also restricts incoming packets
+		 * to ones coming from this remote IP. */
+		xconnect(0, &remote.u.sa, sa_len);
+	/* hole? at this point we have no wildcard udp socket...
+	 * can this cause clients to get "port unreachable" icmp?
+	 * Yup, time window is very small, but it exists (is it?) */
+		/* ..."open new socket", continued */
+		xbind(sock, &lsa->u.sa, sa_len);
+		socket_want_pktinfo(sock);
+
+		/* Doesn't work:
+		 * we cannot replace fd #0 - we will lose pending packet
+		 * which is already buffered for us! And we cannot use fd #1
+		 * instead - it will "intercept" all following packets, but child
+		 * does not expect data coming *from fd #1*! */
+#if 0
+		/* Make it so that local addr is fixed to localp->u.sa
+		 * and we don't accidentally accept packets to other local IPs. */
+		/* NB: we possibly bind to the _very_ same_ address & port as the one
+		 * 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->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);
+#endif
+	}
+
+	pid = vfork();
+	if (pid == -1) {
+		bb_perror_msg("vfork");
+		goto again;
+	}
+
+	if (pid != 0) {
+		/* Parent */
+		cnum++;
+		if (verbose)
+			connection_status();
+		if (hccp)
+			hccp->pid = pid;
+		/* clean up changes done by vforked child */
+		undo_xsetenv();
+		goto again;
+	}
+
+	/* Child: prepare env, log, and exec prog */
+
+	{ /* vfork alert! every xmalloc in this block should be freed! */
+		char *local_hostname = local_hostname; /* for compiler */
+		char *local_addr = NULL;
+		char *free_me0 = NULL;
+		char *free_me1 = NULL;
+		char *free_me2 = NULL;
+
+		if (verbose || !(opts & OPT_E)) {
+			if (!max_per_host) /* remote_addr is not yet known */
+				free_me0 = remote_addr = xmalloc_sockaddr2dotted(&remote.u.sa);
+			if (opts & OPT_h) {
+				free_me1 = remote_hostname = xmalloc_sockaddr2host_noport(&remote.u.sa);
+				if (!remote_hostname) {
+					bb_error_msg("can't look up hostname for %s", remote_addr);
+					remote_hostname = remote_addr;
+				}
+			}
+			/* Find out local IP peer connected to.
+			 * Errors ignored (I'm not paranoid enough to imagine kernel
+			 * which doesn't know local IP). */
+			if (tcp)
+				getsockname(0, &local.u.sa, &local.len);
+			/* else: for UDP it is done earlier by parent */
+			local_addr = xmalloc_sockaddr2dotted(&local.u.sa);
+			if (opts & OPT_h) {
+				local_hostname = preset_local_hostname;
+				if (!local_hostname) {
+					free_me2 = local_hostname = xmalloc_sockaddr2host_noport(&local.u.sa);
+					if (!local_hostname)
+						bb_error_msg_and_die("can't look up hostname for %s", local_addr);
+				}
+				/* else: local_hostname is not NULL, but is NOT malloced! */
+			}
+		}
+		if (verbose) {
+			pid = getpid();
+			if (max_per_host) {
+				bb_error_msg("concurrency %s %u/%u",
+					remote_addr,
+					cur_per_host, max_per_host);
+			}
+			bb_error_msg((opts & OPT_h)
+				? "start %u %s-%s (%s-%s)"
+				: "start %u %s-%s",
+				pid,
+				local_addr, remote_addr,
+				local_hostname, remote_hostname);
+		}
+
+		if (!(opts & OPT_E)) {
+			/* 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
+			 * to know where it originally tried to connect */
+			if (tcp && getsockopt(0, SOL_IP, SO_ORIGINAL_DST, &local.u.sa, &local.len) == 0) {
+				char *addr = xmalloc_sockaddr2dotted(&local.u.sa);
+				xsetenv_plain("TCPORIGDSTADDR", addr);
+				free(addr);
+			}
+#endif
+			xsetenv_plain("PROTO", proto);
+			xsetenv_proto(proto, "LOCALADDR", local_addr);
+			xsetenv_proto(proto, "REMOTEADDR", remote_addr);
+			if (opts & OPT_h) {
+				xsetenv_proto(proto, "LOCALHOST", local_hostname);
+				xsetenv_proto(proto, "REMOTEHOST", remote_hostname);
+			}
+			//compat? xsetenv_proto(proto, "REMOTEINFO", "");
+			/* additional */
+			if (cur_per_host > 0) /* can not be true for udp */
+				xsetenv_plain("TCPCONCURRENCY", utoa(cur_per_host));
+		}
+		free(local_addr);
+		free(free_me0);
+		free(free_me1);
+		free(free_me2);
+	}
+
+	xdup2(0, 1);
+
+	signal(SIGPIPE, SIG_DFL); /* this one was SIG_IGNed */
+	/* Non-ignored signals revert to SIG_DFL on exec anyway */
+	/*signal(SIGCHLD, SIG_DFL);*/
+	sig_unblock(SIGCHLD);
+
+#ifdef SSLSVD
+	strcpy(id, utoa(pid));
+	ssl_io(0, argv);
+	bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+#else
+	BB_EXECVP_or_die(argv);
+#endif
+}
+
+/*
+tcpsvd [-hpEvv] [-c n] [-C n:msg] [-b n] [-u user] [-l name]
+	[-i dir|-x cdb] [ -t sec] host port prog
+
+tcpsvd creates a TCP/IP socket, binds it to the address host:port,
+and listens on the socket for incoming connections.
+
+On each incoming connection, tcpsvd conditionally runs a program,
+with standard input reading from the socket, and standard output
+writing to the socket, to handle this connection. tcpsvd keeps
+listening on the socket for new connections, and can handle
+multiple connections simultaneously.
+
+tcpsvd optionally checks for special instructions depending
+on the IP address or hostname of the client that initiated
+the connection, see ipsvd-instruct(5).
+
+host
+    host either is a hostname, or a dotted-decimal IP address,
+    or 0. If host is 0, tcpsvd accepts connections to any local
+    IP address.
+    * busybox accepts IPv6 addresses and host:port pairs too
+      In this case second parameter is ignored
+port
+    tcpsvd accepts connections to host:port. port may be a name
+    from /etc/services or a number.
+prog
+    prog consists of one or more arguments. For each connection,
+    tcpsvd normally runs prog, with file descriptor 0 reading from
+    the network, and file descriptor 1 writing to the network.
+    By default it also sets up TCP-related environment variables,
+    see tcp-environ(5)
+-i dir
+    read instructions for handling new connections from the instructions
+    directory dir. See ipsvd-instruct(5) for details.
+    * ignored by busyboxed version
+-x cdb
+    read instructions for handling new connections from the constant database
+    cdb. The constant database normally is created from an instructions
+    directory by running ipsvd-cdb(8).
+    * ignored by busyboxed version
+-t sec
+    timeout. This option only takes effect if the -i option is given.
+    While checking the instructions directory, check the time of last access
+    of the file that matches the clients address or hostname if any, discard
+    and remove the file if it wasn't accessed within the last sec seconds;
+    tcpsvd does not discard or remove a file if the user's write permission
+    is not set, for those files the timeout is disabled. Default is 0,
+    which means that the timeout is disabled.
+    * ignored by busyboxed version
+-l name
+    local hostname. Do not look up the local hostname in DNS, but use name
+    as hostname. This option must be set if tcpsvd listens on port 53
+    to avoid loops.
+-u user[:group]
+    drop permissions. Switch user ID to user's UID, and group ID to user's
+    primary GID after creating and binding to the socket. If user is followed
+    by a colon and a group name, the group ID is switched to the GID of group
+    instead. All supplementary groups are removed.
+-c n
+    concurrency. Handle up to n connections simultaneously. Default is 30.
+    If there are n connections active, tcpsvd defers acceptance of a new
+    connection until an active connection is closed.
+-C n[:msg]
+    per host concurrency. Allow only up to n connections from the same IP
+    address simultaneously. If there are n active connections from one IP
+    address, new incoming connections from this IP address are closed
+    immediately. If n is followed by :msg, the message msg is written
+    to the client if possible, before closing the connection. By default
+    msg is empty. See ipsvd-instruct(5) for supported escape sequences in msg.
+
+    For each accepted connection, the current per host concurrency is
+    available through the environment variable TCPCONCURRENCY. n and msg
+    can be overwritten by ipsvd(7) instructions, see ipsvd-instruct(5).
+    By default tcpsvd doesn't keep track of connections.
+-h
+    Look up the client's hostname in DNS.
+-p
+    paranoid. After looking up the client's hostname in DNS, look up the IP
+    addresses in DNS for that hostname, and forget about the hostname
+    if none of the addresses match the client's IP address. You should
+    set this option if you use hostname based instructions. The -p option
+    implies the -h option.
+    * ignored by busyboxed version
+-b n
+    backlog. Allow a backlog of approximately n TCP SYNs. On some systems n
+    is silently limited. Default is 20.
+-E
+    no special environment. Do not set up TCP-related environment variables.
+-v
+    verbose. Print verbose messsages to standard output.
+-vv
+    more verbose. Print more verbose messages to standard output.
+    * no difference between -v and -vv in busyboxed version
+*/
diff --git a/busybox-1.19.3/networking/tcpudp_perhost.c b/busybox-1.19.3/networking/tcpudp_perhost.c
new file mode 100644
index 0000000..1054108
--- /dev/null
+++ b/busybox-1.19.3/networking/tcpudp_perhost.c
@@ -0,0 +1,65 @@
+/* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
+ * which are released into public domain by the author.
+ * Homepage: http://smarden.sunsite.dk/ipsvd/
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#include "tcpudp_perhost.h"
+
+static struct hcc *cc;
+static unsigned cclen;
+
+/* to be optimized */
+
+void ipsvd_perhost_init(unsigned c)
+{
+//	free(cc);
+	cc = xzalloc(c * sizeof(*cc));
+	cclen = c;
+}
+
+unsigned ipsvd_perhost_add(char *ip, unsigned maxconn, struct hcc **hccpp)
+{
+	unsigned i;
+	unsigned conn = 1;
+	int freepos = -1;
+
+	for (i = 0; i < cclen; ++i) {
+		if (!cc[i].ip) {
+			freepos = i;
+			continue;
+		}
+		if (strcmp(cc[i].ip, ip) == 0) {
+			conn++;
+			continue;
+		}
+	}
+	if (freepos == -1) return 0;
+	if (conn <= maxconn) {
+		cc[freepos].ip = ip;
+		*hccpp = &cc[freepos];
+	}
+	return conn;
+}
+
+void ipsvd_perhost_remove(int pid)
+{
+	unsigned i;
+	for (i = 0; i < cclen; ++i) {
+		if (cc[i].pid == pid) {
+			free(cc[i].ip);
+			cc[i].ip = NULL;
+			cc[i].pid = 0;
+			return;
+		}
+	}
+}
+
+//void ipsvd_perhost_free(void)
+//{
+//	free(cc);
+//}
diff --git a/busybox-1.19.3/networking/tcpudp_perhost.h b/busybox-1.19.3/networking/tcpudp_perhost.h
new file mode 100644
index 0000000..3e57576
--- /dev/null
+++ b/busybox-1.19.3/networking/tcpudp_perhost.h
@@ -0,0 +1,33 @@
+/* Based on ipsvd utilities written by Gerrit Pape <pape@smarden.org>
+ * which are released into public domain by the author.
+ * Homepage: http://smarden.sunsite.dk/ipsvd/
+ *
+ * Copyright (C) 2007 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+struct hcc {
+	char *ip;
+	int pid;
+};
+
+void ipsvd_perhost_init(unsigned);
+
+/* Returns number of already opened connects to this ips, including this one.
+ * ip should be a malloc'ed ptr.
+ * If return value is <= maxconn, ip is inserted into the table
+ * and pointer to table entry if stored in *hccpp
+ * (useful for storing pid later).
+ * Else ip is NOT inserted (you must take care of it - free() etc) */
+unsigned ipsvd_perhost_add(char *ip, unsigned maxconn, struct hcc **hccpp);
+
+/* Finds and frees element with pid */
+void ipsvd_perhost_remove(int pid);
+
+//unsigned ipsvd_perhost_setpid(int pid);
+//void ipsvd_perhost_free(void);
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/busybox-1.19.3/networking/telnet.c b/busybox-1.19.3/networking/telnet.c
new file mode 100644
index 0000000..e8e51dc
--- /dev/null
+++ b/busybox-1.19.3/networking/telnet.c
@@ -0,0 +1,671 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * telnet implementation for busybox
+ *
+ * Author: Tomi Ollila <too@iki.fi>
+ * Copyright (C) 1994-2000 by Tomi Ollila
+ *
+ * Created: Thu Apr  7 13:29:41 1994 too
+ * Last modified: Fri Jun  9 14:34:24 2000 too
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * HISTORY
+ * Revision 3.1  1994/04/17  11:31:54  too
+ * initial revision
+ * Modified 2000/06/13 for inclusion into BusyBox by Erik Andersen <andersen@codepoet.org>
+ * Modified 2001/05/07 to add ability to pass TTYPE to remote host by Jim McQuillan
+ * <jam@ltsp.org>
+ * Modified 2004/02/11 to add ability to pass the USER variable to remote host
+ * by Fernando Silveira <swrh@gmx.net>
+ *
+ */
+
+//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)
+#else
+# define TRACE(x, y)
+#endif
+
+enum {
+	DATABUFSIZE = 128,
+	IACBUFSIZE  = 128,
+
+	CHM_TRY = 0,
+	CHM_ON = 1,
+	CHM_OFF = 2,
+
+	UF_ECHO = 0x01,
+	UF_SGA = 0x02,
+
+	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;
+
+enum { netfd = 3 };
+
+struct globals {
+	int	iaclen; /* could even use byte, but it's a loss on x86 */
+	byte	telstate; /* telnet negotiation state from network input */
+	byte	telwish;  /* DO, DONT, WILL, WONT */
+	byte    charmode;
+	byte    telflags;
+	byte	do_termios;
+#if ENABLE_FEATURE_TELNET_TTYPE
+	char	*ttype;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+	const char *autologin;
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+	unsigned win_width, win_height;
+#endif
+	/* same buffer used both for network and console read/write */
+	char    buf[DATABUFSIZE];
+	/* buffer to handle telnet negotiations */
+	char    iacbuf[IACBUFSIZE];
+	struct termios termios_def;
+	struct termios termios_raw;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	struct G_sizecheck { \
+		char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
+	}; \
+} while (0)
+
+
+static void rawmode(void);
+static void cookmode(void);
+static void do_linemode(void);
+static void will_charmode(void);
+static void telopt(byte c);
+static void subneg(byte c);
+
+static void iac_flush(void)
+{
+	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)
+{
+	cookmode();
+	exit(ev);
+}
+
+static void con_escape(void)
+{
+	char b;
+
+	if (bb_got_signal) /* came from line mode... go raw */
+		rawmode();
+
+	write_str(1, "\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"
+			" e	exit telnet\r\n");
+
+	if (read(STDIN_FILENO, &b, 1) <= 0)
+		doexit(EXIT_FAILURE);
+
+	switch (b) {
+	case 'l':
+		if (!bb_got_signal) {
+			do_linemode();
+			goto ret;
+		}
+		break;
+	case 'c':
+		if (bb_got_signal) {
+			will_charmode();
+			goto ret;
+		}
+		break;
+	case 'z':
+		cookmode();
+		kill(0, SIGTSTP);
+		rawmode();
+		break;
+	case 'e':
+		doexit(EXIT_SUCCESS);
+	}
+
+	write_str(1, "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.
+	 */
+	byte outbuf[2 * DATABUFSIZE];
+	byte *p = (byte*)G.buf;
+	int j = 0;
+
+	for (; len > 0; len--, p++) {
+		byte c = *p;
+		if (c == 0x1d) {
+			con_escape();
+			return;
+		}
+		outbuf[j++] = c;
+		if (c == IAC)
+			outbuf[j++] = c; /* IAC -> IAC IAC */
+		else if (c == '\r')
+			outbuf[j++] = '\0'; /* CR -> CR NUL */
+	}
+	if (j > 0)
+		full_write(netfd, outbuf, j);
+}
+
+static void handle_net_input(int len)
+{
+	int i;
+	int cstart = 0;
+
+	for (i = 0; i < len; i++) {
+		byte c = G.buf[i];
+
+		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_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: /* Prev char was IAC */
+			if (c == IAC) { /* IAC IAC -> one IAC */
+				G.buf[cstart++] = c;
+				G.telstate = TS_COPY;
+				break;
+			}
+			/* else */
+			switch (c) {
+			case SB:
+				G.telstate = TS_SUB1;
+				break;
+			case DO:
+			case DONT:
+			case WILL:
+			case WONT:
+				G.telwish = c;
+				G.telstate = TS_OPT;
+				break;
+			/* DATA MARK must be added later */
+			default:
+				G.telstate = TS_COPY;
+			}
+			break;
+
+		case TS_OPT: /* Prev chars were IAC WILL/WONT/DO/DONT */
+			telopt(c);
+			G.telstate = TS_COPY;
+			break;
+
+		case TS_SUB1: /* Subnegotiation */
+		case TS_SUB2: /* Subnegotiation */
+			subneg(c); /* can change G.telstate */
+			break;
+		}
+	}
+
+	if (G.telstate != TS_NORMAL) {
+		/* We had some IACs, or CR */
+		if (G.iaclen)
+			iac_flush();
+		if (G.telstate == TS_COPY) /* we aren't in the middle of IAC */
+			G.telstate = TS_NORMAL;
+		len = cstart;
+	}
+
+	if (len)
+		full_write(STDOUT_FILENO, G.buf, len);
+}
+
+static void put_iac(int c)
+{
+	G.iacbuf[G.iaclen++] = c;
+}
+
+static void put_iac2(byte wwdd, byte c)
+{
+	if (G.iaclen + 3 > IACBUFSIZE)
+		iac_flush();
+
+	put_iac(IAC);
+	put_iac(wwdd);
+	put_iac(c);
+}
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+static void put_iac_subopt(byte c, char *str)
+{
+	int len = strlen(str) + 6;   // ( 2 + 1 + 1 + strlen + 2 )
+
+	if (G.iaclen + len > IACBUFSIZE)
+		iac_flush();
+
+	put_iac(IAC);
+	put_iac(SB);
+	put_iac(c);
+	put_iac(0);
+
+	while (*str)
+		put_iac(*str++);
+
+	put_iac(IAC);
+	put_iac(SE);
+}
+#endif
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void put_iac_subopt_autologin(void)
+{
+	int len = strlen(G.autologin) + 6;	// (2 + 1 + 1 + strlen + 2)
+	const char *p = "USER";
+
+	if (G.iaclen + len > IACBUFSIZE)
+		iac_flush();
+
+	put_iac(IAC);
+	put_iac(SB);
+	put_iac(TELOPT_NEW_ENVIRON);
+	put_iac(TELQUAL_IS);
+	put_iac(NEW_ENV_VAR);
+
+	while (*p)
+		put_iac(*p++);
+
+	put_iac(NEW_ENV_VALUE);
+
+	p = G.autologin;
+	while (*p)
+		put_iac(*p++);
+
+	put_iac(IAC);
+	put_iac(SE);
+}
+#endif
+
+#if ENABLE_FEATURE_AUTOWIDTH
+static void put_iac_naws(byte c, int x, int y)
+{
+	if (G.iaclen + 9 > IACBUFSIZE)
+		iac_flush();
+
+	put_iac(IAC);
+	put_iac(SB);
+	put_iac(c);
+
+	put_iac((x >> 8) & 0xff);
+	put_iac(x & 0xff);
+	put_iac((y >> 8) & 0xff);
+	put_iac(y & 0xff);
+
+	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);
+			rawmode();
+		}
+	} else {
+		if (G.charmode != CHM_OFF) {
+			G.charmode = CHM_OFF;
+			printf("\r\nEntering line mode%s'^C'.\r\n", escapecharis);
+			cookmode();
+		}
+	}
+}
+
+static void will_charmode(void)
+{
+	G.charmode = CHM_TRY;
+	G.telflags |= (UF_ECHO | UF_SGA);
+	setConMode();
+
+	put_iac2(DO, TELOPT_ECHO);
+	put_iac2(DO, TELOPT_SGA);
+	iac_flush();
+}
+
+static void do_linemode(void)
+{
+	G.charmode = CHM_TRY;
+	G.telflags &= ~(UF_ECHO | UF_SGA);
+	setConMode();
+
+	put_iac2(DONT, TELOPT_ECHO);
+	put_iac2(DONT, TELOPT_SGA);
+	iac_flush();
+}
+
+static void to_notsup(char c)
+{
+	if (G.telwish == WILL)
+		put_iac2(DONT, c);
+	else if (G.telwish == DO)
+		put_iac2(WONT, c);
+}
+
+static void to_echo(void)
+{
+	/* if server requests ECHO, don't agree */
+	if (G.telwish == DO) {
+		put_iac2(WONT, TELOPT_ECHO);
+		return;
+	}
+	if (G.telwish == DONT)
+		return;
+
+	if (G.telflags & UF_ECHO) {
+		if (G.telwish == WILL)
+			return;
+	} else if (G.telwish == WONT)
+		return;
+
+	if (G.charmode != CHM_OFF)
+		G.telflags ^= UF_ECHO;
+
+	if (G.telflags & UF_ECHO)
+		put_iac2(DO, TELOPT_ECHO);
+	else
+		put_iac2(DONT, TELOPT_ECHO);
+
+	setConMode();
+	full_write1_str("\r\n");  /* sudden modec */
+}
+
+static void to_sga(void)
+{
+	/* daemon always sends will/wont, client do/dont */
+
+	if (G.telflags & UF_SGA) {
+		if (G.telwish == WILL)
+			return;
+	} else if (G.telwish == WONT)
+		return;
+
+	G.telflags ^= UF_SGA; /* toggle */
+	if (G.telflags & UF_SGA)
+		put_iac2(DO, TELOPT_SGA);
+	else
+		put_iac2(DONT, TELOPT_SGA);
+}
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+static void to_ttype(void)
+{
+	/* Tell server we will (or won't) do TTYPE */
+	if (G.ttype)
+		put_iac2(WILL, TELOPT_TTYPE);
+	else
+		put_iac2(WONT, TELOPT_TTYPE);
+}
+#endif
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+static void to_new_environ(void)
+{
+	/* Tell server we will (or will not) do AUTOLOGIN */
+	if (G.autologin)
+		put_iac2(WILL, TELOPT_NEW_ENVIRON);
+	else
+		put_iac2(WONT, TELOPT_NEW_ENVIRON);
+}
+#endif
+
+#if ENABLE_FEATURE_AUTOWIDTH
+static void to_naws(void)
+{
+	/* Tell server we will do NAWS */
+	put_iac2(WILL, TELOPT_NAWS);
+}
+#endif
+
+static void telopt(byte c)
+{
+	switch (c) {
+	case TELOPT_ECHO:
+		to_echo(); break;
+	case TELOPT_SGA:
+		to_sga(); break;
+#if ENABLE_FEATURE_TELNET_TTYPE
+	case TELOPT_TTYPE:
+		to_ttype(); break;
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+	case TELOPT_NEW_ENVIRON:
+		to_new_environ(); break;
+#endif
+#if ENABLE_FEATURE_AUTOWIDTH
+	case TELOPT_NAWS:
+		to_naws();
+		put_iac_naws(c, G.win_width, G.win_height);
+		break;
+#endif
+	default:
+		to_notsup(c);
+		break;
+	}
+}
+
+/* subnegotiation -- ignore all (except TTYPE,NAWS) */
+static void subneg(byte c)
+{
+	switch (G.telstate) {
+	case TS_SUB1:
+		if (c == IAC)
+			G.telstate = TS_SUB2;
+#if ENABLE_FEATURE_TELNET_TTYPE
+		else
+		if (c == TELOPT_TTYPE && G.ttype)
+			put_iac_subopt(TELOPT_TTYPE, G.ttype);
+#endif
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+		else
+		if (c == TELOPT_NEW_ENVIRON && G.autologin)
+			put_iac_subopt_autologin();
+#endif
+		break;
+	case TS_SUB2:
+		if (c == SE) {
+			G.telstate = TS_COPY;
+			return;
+		}
+		G.telstate = TS_SUB1;
+		break;
+	}
+}
+
+static void rawmode(void)
+{
+	if (G.do_termios)
+		tcsetattr(0, TCSADRAIN, &G.termios_raw);
+}
+
+static void cookmode(void)
+{
+	if (G.do_termios)
+		tcsetattr(0, TCSADRAIN, &G.termios_def);
+}
+
+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;
+	struct pollfd ufds[2];
+
+	INIT_G();
+
+#if ENABLE_FEATURE_AUTOWIDTH
+	get_terminal_width_height(0, &G.win_width, &G.win_height);
+#endif
+
+#if ENABLE_FEATURE_TELNET_TTYPE
+	G.ttype = getenv("TERM");
+#endif
+
+	if (tcgetattr(0, &G.termios_def) >= 0) {
+		G.do_termios = 1;
+		G.termios_raw = G.termios_def;
+		cfmakeraw(&G.termios_raw);
+	}
+
+#if ENABLE_FEATURE_TELNET_AUTOLOGIN
+	if (1 & getopt32(argv, "al:", &G.autologin))
+		G.autologin = getenv("USER");
+	argv += optind;
+#else
+	argv++;
+#endif
+	if (!*argv)
+		bb_show_usage();
+	host = *argv++;
+	port = bb_lookup_port(*argv ? *argv++ : "telnet", "tcp", 23);
+	if (*argv) /* extra params?? */
+		bb_show_usage();
+
+	xmove_fd(create_and_connect_stream_or_die(host, port), netfd);
+
+	setsockopt(netfd, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+
+	signal(SIGINT, record_signo);
+
+	ufds[0].fd = STDIN_FILENO;
+	ufds[0].events = POLLIN;
+	ufds[1].fd = netfd;
+	ufds[1].events = POLLIN;
+
+	while (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);
+			continue;
+		}
+
+// FIXME: reads can block. Need full bidirectional buffering.
+
+		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) */
+}
diff --git a/busybox-1.19.3/networking/telnetd.c b/busybox-1.19.3/networking/telnetd.c
new file mode 100644
index 0000000..33020f1
--- /dev/null
+++ b/busybox-1.19.3/networking/telnetd.c
@@ -0,0 +1,747 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Simple telnet server
+ * Bjorn Wesen, Axis Communications AB (bjornw@axis.com)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * ---------------------------------------------------------------------------
+ * (C) Copyright 2000, Axis Communications AB, LUND, SWEDEN
+ ****************************************************************************
+ *
+ * The telnetd manpage says it all:
+ *
+ * Telnetd operates by allocating a pseudo-terminal device (see pty(4)) for
+ * a client, then creating a login process which has the slave side of the
+ * pseudo-terminal as stdin, stdout, and stderr. Telnetd manipulates the
+ * master side of the pseudo-terminal, implementing the telnet protocol and
+ * passing characters between the remote client and the login process.
+ *
+ * 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"
+#include <syslog.h>
+
+#if DEBUG
+# define TELCMDS
+# define TELOPTS
+#endif
+#include <arpa/telnet.h>
+
+
+struct tsession {
+	struct tsession *next;
+	pid_t shell_pid;
+	int sockfd_read;
+	int sockfd_write;
+	int ptyfd;
+
+	/* two circular buffers */
+	/*char *buf1, *buf2;*/
+/*#define TS_BUF1(ts) ts->buf1*/
+/*#define TS_BUF2(ts) TS_BUF2(ts)*/
+#define TS_BUF1(ts) ((unsigned char*)(ts + 1))
+#define TS_BUF2(ts) (((unsigned char*)(ts + 1)) + BUFSIZE)
+	int rdidx1, wridx1, size1;
+	int rdidx2, wridx2, size2;
+};
+
+/* Two buffers are directly after tsession in malloced memory.
+ * Make whole thing fit in 4k */
+enum { BUFSIZE = (4 * 1024 - sizeof(struct tsession)) / 2 };
+
+
+/* Globals */
+struct globals {
+	struct tsession *sessions;
+	const char *loginpath;
+	const char *issuefile;
+	int maxfd;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define INIT_G() do { \
+	G.loginpath = "/bin/login"; \
+	G.issuefile = "/etc/issue.net"; \
+} while (0)
+
+
+/*
+   Remove all IAC's from buf1 (received IACs are ignored and must be removed
+   so as to not be interpreted by the terminal).  Make an uninterrupted
+   string of characters fit for the terminal.  Do this by packing
+   all characters meant for the terminal sequentially towards the end of buf.
+
+   Return a pointer to the beginning of the characters meant for the terminal
+   and make *num_totty the number of characters that should be sent to
+   the terminal.
+
+   Note - if an IAC (3 byte quantity) starts before (bf + len) but extends
+   past (bf + len) then that IAC will be left unprocessed and *processed
+   will be less than len.
+
+   CR-LF ->'s CR mapping is also done here, for convenience.
+
+   NB: may fail to remove iacs which wrap around buffer!
+ */
+static unsigned char *
+remove_iacs(struct tsession *ts, int *pnum_totty)
+{
+	unsigned char *ptr0 = TS_BUF1(ts) + ts->wridx1;
+	unsigned char *ptr = ptr0;
+	unsigned char *totty = ptr;
+	unsigned char *end = ptr + MIN(BUFSIZE - ts->wridx1, ts->size1);
+	int num_totty;
+
+	while (ptr < end) {
+		if (*ptr != IAC) {
+			char c = *ptr;
+
+			*totty++ = c;
+			ptr++;
+			/* We map \r\n ==> \r for pragmatic reasons.
+			 * Many client implementations send \r\n when
+			 * the user hits the CarriageReturn key.
+			 */
+			if (c == '\r' && ptr < end && (*ptr == '\n' || *ptr == '\0'))
+				ptr++;
+			continue;
+		}
+
+		if ((ptr+1) >= end)
+			break;
+		if (ptr[1] == NOP) { /* Ignore? (putty keepalive, etc.) */
+			ptr += 2;
+			continue;
+		}
+		if (ptr[1] == IAC) { /* Literal IAC? (emacs M-DEL) */
+			*totty++ = ptr[1];
+			ptr += 2;
+			continue;
+		}
+
+		/*
+		 * TELOPT_NAWS support!
+		 */
+		if ((ptr+2) >= end) {
+			/* Only the beginning of the IAC is in the
+			buffer we were asked to process, we can't
+			process this char */
+			break;
+		}
+		/*
+		 * IAC -> SB -> TELOPT_NAWS -> 4-byte -> IAC -> SE
+		 */
+		if (ptr[1] == SB && ptr[2] == TELOPT_NAWS) {
+			struct winsize ws;
+			if ((ptr+8) >= end)
+				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);
+			ptr += 9;
+			continue;
+		}
+		/* skip 3-byte IAC non-SB cmd */
+#if DEBUG
+		fprintf(stderr, "Ignoring IAC %s,%s\n",
+				TELCMD(ptr[1]), TELOPT(ptr[2]));
+#endif
+		ptr += 3;
+	}
+
+	num_totty = totty - ptr0;
+	*pnum_totty = num_totty;
+	/* The difference between ptr and totty is number of iacs
+	   we removed from the stream. Adjust buf1 accordingly */
+	if ((ptr - totty) == 0) /* 99.999% of cases */
+		return ptr0;
+	ts->wridx1 += ptr - totty;
+	ts->size1 -= ptr - totty;
+	/* Move chars meant for the terminal towards the end of the buffer */
+	return memmove(ptr - num_totty, ptr0, num_totty);
+}
+
+/*
+ * Converting single IAC into double on output
+ */
+static size_t iac_safe_write(int fd, const char *buf, size_t count)
+{
+	const char *IACptr;
+	size_t wr, rc, total;
+
+	total = 0;
+	while (1) {
+		if (count == 0)
+			return total;
+		if (*buf == (char)IAC) {
+			static const char IACIAC[] ALIGN1 = { IAC, IAC };
+			rc = safe_write(fd, IACIAC, 2);
+			if (rc != 2)
+				break;
+			buf++;
+			total++;
+			count--;
+			continue;
+		}
+		/* count != 0, *buf != IAC */
+		IACptr = memchr(buf, IAC, count);
+		wr = count;
+		if (IACptr)
+			wr = IACptr - buf;
+		rc = safe_write(fd, buf, wr);
+		if (rc != wr)
+			break;
+		buf += rc;
+		total += rc;
+		count -= rc;
+	}
+	/* here: rc - result of last short write */
+	if ((ssize_t)rc < 0) { /* error? */
+		if (total == 0)
+			return rc;
+		rc = 0;
+	}
+	return total + rc;
+}
+
+/* Must match getopt32 string */
+enum {
+	OPT_WATCHCHILD = (1 << 2), /* -K */
+	OPT_INETD      = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */
+	OPT_PORT       = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p PORT */
+	OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */
+	OPT_SYSLOG     = (1 << 7) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -S */
+	OPT_WAIT       = (1 << 8) * ENABLE_FEATURE_TELNETD_INETD_WAIT, /* -w SEC */
+};
+
+static struct tsession *
+make_new_session(
+		IF_FEATURE_TELNETD_STANDALONE(int sock)
+		IF_NOT_FEATURE_TELNETD_STANDALONE(void)
+) {
+#if !ENABLE_FEATURE_TELNETD_STANDALONE
+	enum { sock = 0 };
+#endif
+	const char *login_argv[2];
+	struct termios termbuf;
+	int fd, pid;
+	char tty_name[GETPTY_BUFSIZE];
+	struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
+
+	/*ts->buf1 = (char *)(ts + 1);*/
+	/*ts->buf2 = ts->buf1 + BUFSIZE;*/
+
+	/* Got a new connection, set up a tty */
+	fd = xgetpty(tty_name);
+	if (fd > G.maxfd)
+		G.maxfd = fd;
+	ts->ptyfd = fd;
+	ndelay_on(fd);
+	close_on_exec_on(fd);
+
+	/* SO_KEEPALIVE by popular demand */
+	setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &const_int_1, sizeof(const_int_1));
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+	ts->sockfd_read = sock;
+	ndelay_on(sock);
+	if (sock == 0) { /* We are called with fd 0 - we are in inetd mode */
+		sock++; /* so use fd 1 for output */
+		ndelay_on(sock);
+	}
+	ts->sockfd_write = sock;
+	if (sock > G.maxfd)
+		G.maxfd = sock;
+#else
+	/* ts->sockfd_read = 0; - done by xzalloc */
+	ts->sockfd_write = 1;
+	ndelay_on(0);
+	ndelay_on(1);
+#endif
+
+	/* Make the telnet client understand we will echo characters so it
+	 * should not do it locally. We don't tell the client to run linemode,
+	 * because we want to handle line editing and tab completion and other
+	 * stuff that requires char-by-char support. */
+	{
+		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,*/
+			IAC, WILL, TELOPT_ECHO,
+			IAC, WILL, TELOPT_SGA
+		};
+		/* This confuses iac_safe_write(), it will try to duplicate
+		 * each IAC... */
+		//memcpy(TS_BUF2(ts), iacs_to_send, sizeof(iacs_to_send));
+		//ts->rdidx2 = sizeof(iacs_to_send);
+		//ts->size2 = sizeof(iacs_to_send);
+		/* So just stuff it into TCP stream! (no error check...) */
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+		safe_write(sock, iacs_to_send, sizeof(iacs_to_send));
+#else
+		safe_write(1, iacs_to_send, sizeof(iacs_to_send));
+#endif
+		/*ts->rdidx2 = 0; - xzalloc did it */
+		/*ts->size2 = 0;*/
+	}
+
+	fflush_all();
+	pid = vfork(); /* NOMMU-friendly */
+	if (pid < 0) {
+		free(ts);
+		close(fd);
+		/* sock will be closed by caller */
+		bb_perror_msg("vfork");
+		return NULL;
+	}
+	if (pid > 0) {
+		/* Parent */
+		ts->shell_pid = pid;
+		return ts;
+	}
+
+	/* Child */
+	/* Careful - we are after vfork! */
+
+	/* 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;
+		if (lsa) {
+			hostname = xmalloc_sockaddr2dotted(&lsa->u.sa);
+			free(lsa);
+		}
+		write_new_utmp(pid, LOGIN_PROCESS, tty_name, /*username:*/ "LOGIN", hostname);
+		free(hostname);
+	}
+
+	/* Make new session and process group */
+	setsid();
+
+	/* Open the child's side of the tty */
+	/* NB: setsid() disconnects from any previous ctty's. Therefore
+	 * we must open child's side of the tty AFTER setsid! */
+	close(0);
+	xopen(tty_name, O_RDWR); /* becomes our ctty */
+	xdup2(0, 1);
+	xdup2(0, 2);
+	tcsetpgrp(0, pid); /* switch this tty's process group to us */
+
+	/* The pseudo-terminal allocated to the client is configured to operate
+	 * in cooked mode, and with XTABS CRMOD enabled (see tty(4)) */
+	tcgetattr(0, &termbuf);
+	termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
+	termbuf.c_oflag |= ONLCR | XTABS;
+	termbuf.c_iflag |= ICRNL;
+	termbuf.c_iflag &= ~IXOFF;
+	/*termbuf.c_lflag &= ~ICANON;*/
+	tcsetattr_stdin_TCSANOW(&termbuf);
+
+	/* Uses FILE-based I/O to stdout, but does fflush_all(),
+	 * so should be safe with vfork.
+	 * I fear, though, that some users will have ridiculously big
+	 * issue files, and they may block writing to fd 1,
+	 * (parent is supposed to read it, but parent waits
+	 * for vforked child to exec!) */
+	print_login_issue(G.issuefile, tty_name);
+
+	/* Exec shell / login / whatever */
+	login_argv[0] = G.loginpath;
+	login_argv[1] = NULL;
+	/* exec busybox applet (if PREFER_APPLETS=y), if that fails,
+	 * exec external program.
+	 * NB: sock is either 0 or has CLOEXEC set on it.
+	 * fd has CLOEXEC set on it too. These two fds will be closed here.
+	 */
+	BB_EXECVP(G.loginpath, (char **)login_argv);
+	/* _exit is safer with vfork, and we shouldn't send message
+	 * to remote clients anyway */
+	_exit(EXIT_FAILURE); /*bb_perror_msg_and_die("execv %s", G.loginpath);*/
+}
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+
+static void
+free_session(struct tsession *ts)
+{
+	struct tsession *t;
+
+	if (option_mask32 & OPT_INETD)
+		exit(EXIT_SUCCESS);
+
+	/* Unlink this telnet session from the session list */
+	t = G.sessions;
+	if (t == ts)
+		G.sessions = ts->next;
+	else {
+		while (t->next != ts)
+			t = t->next;
+		t->next = ts->next;
+	}
+
+#if 0
+	/* It was said that "normal" telnetd just closes ptyfd,
+	 * doesn't send SIGKILL. When we close ptyfd,
+	 * kernel sends SIGHUP to processes having slave side opened. */
+	kill(ts->shell_pid, SIGKILL);
+	waitpid(ts->shell_pid, NULL, 0);
+#endif
+	close(ts->ptyfd);
+	close(ts->sockfd_read);
+	/* We do not need to close(ts->sockfd_write), it's the same
+	 * as sockfd_read unless we are in inetd mode. But in inetd mode
+	 * we do not reach this */
+	free(ts);
+
+	/* Scan all sessions and find new maxfd */
+	G.maxfd = 0;
+	ts = G.sessions;
+	while (ts) {
+		if (G.maxfd < ts->ptyfd)
+			G.maxfd = ts->ptyfd;
+		if (G.maxfd < ts->sockfd_read)
+			G.maxfd = ts->sockfd_read;
+#if 0
+		/* Again, sockfd_write == sockfd_read here */
+		if (G.maxfd < ts->sockfd_write)
+			G.maxfd = ts->sockfd_write;
+#endif
+		ts = ts->next;
+	}
+}
+
+#else /* !FEATURE_TELNETD_STANDALONE */
+
+/* Used in main() only, thus "return 0" actually is exit(EXIT_SUCCESS). */
+#define free_session(ts) return 0
+
+#endif
+
+static void handle_sigchld(int sig UNUSED_PARAM)
+{
+	pid_t pid;
+	struct tsession *ts;
+	int save_errno = errno;
+
+	/* Looping: more than one child may have exited */
+	while (1) {
+		pid = wait_any_nohang(NULL);
+		if (pid <= 0)
+			break;
+		ts = G.sessions;
+		while (ts) {
+			if (ts->shell_pid == pid) {
+				ts->shell_pid = -1;
+// man utmp:
+// When init(8) finds that a process has exited, it locates its utmp entry
+// by ut_pid, sets ut_type to DEAD_PROCESS, and clears ut_user, ut_host
+// and ut_time with null bytes.
+// [same applies to other processes which maintain utmp entries, like telnetd]
+//
+// We do not bother actually clearing fields:
+// it might be interesting to know who was logged in and from where
+				update_utmp(pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
+				break;
+			}
+			ts = ts->next;
+		}
+	}
+
+	errno = save_errno;
+}
+
+int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int telnetd_main(int argc UNUSED_PARAM, char **argv)
+{
+	fd_set rdfdset, wrfdset;
+	unsigned opt;
+	int count;
+	struct tsession *ts;
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+#define IS_INETD (opt & OPT_INETD)
+	int master_fd = master_fd; /* for compiler */
+	int sec_linger = sec_linger;
+	char *opt_bindaddr = NULL;
+	char *opt_portnbr;
+#else
+	enum {
+		IS_INETD = 1,
+		master_fd = -1,
+	};
+#endif
+	INIT_G();
+
+	/* -w NUM, and implies -F. -w and -i don't mix */
+	IF_FEATURE_TELNETD_INETD_WAIT(opt_complementary = "wF:w+:i--w:w--i";)
+	/* Even if !STANDALONE, we accept (and ignore) -i, thus people
+	 * don't need to guess whether it's ok to pass -i to us */
+	opt = getopt32(argv, "f:l:Ki"
+			IF_FEATURE_TELNETD_STANDALONE("p:b:F")
+			IF_FEATURE_TELNETD_INETD_WAIT("Sw:"),
+			&G.issuefile, &G.loginpath
+			IF_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)
+			IF_FEATURE_TELNETD_INETD_WAIT(, &sec_linger)
+	);
+	if (!IS_INETD /*&& !re_execed*/) {
+		/* inform that we start in standalone mode?
+		 * May be useful when people forget to give -i */
+		/*bb_error_msg("listening for connections");*/
+		if (!(opt & OPT_FOREGROUND)) {
+			/* DAEMON_CHDIR_ROOT was giving inconsistent
+			 * behavior with/without -F, -i */
+			bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
+		}
+	}
+	/* Redirect log to syslog early, if needed */
+	if (IS_INETD || (opt & OPT_SYSLOG) || !(opt & OPT_FOREGROUND)) {
+		openlog(applet_name, LOG_PID, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG;
+	}
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+	if (IS_INETD) {
+		G.sessions = make_new_session(0);
+		if (!G.sessions) /* pty opening or vfork problem, exit */
+			return 1; /* make_new_session printed error message */
+	} else {
+		master_fd = 0;
+		if (!(opt & OPT_WAIT)) {
+			unsigned portnbr = 23;
+			if (opt & OPT_PORT)
+				portnbr = xatou16(opt_portnbr);
+			master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
+			xlisten(master_fd, 1);
+		}
+		close_on_exec_on(master_fd);
+	}
+#else
+	G.sessions = make_new_session();
+	if (!G.sessions) /* pty opening or vfork problem, exit */
+		return 1; /* make_new_session printed error message */
+#endif
+
+	/* We don't want to die if just one session is broken */
+	signal(SIGPIPE, SIG_IGN);
+
+	if (opt & OPT_WATCHCHILD)
+		signal(SIGCHLD, handle_sigchld);
+	else /* prevent dead children from becoming zombies */
+		signal(SIGCHLD, SIG_IGN);
+
+/*
+   This is how the buffers are used. The arrows indicate data flow.
+
+   +-------+     wridx1++     +------+     rdidx1++     +----------+
+   |       | <--------------  | buf1 | <--------------  |          |
+   |       |     size1--      +------+     size1++      |          |
+   |  pty  |                                            |  socket  |
+   |       |     rdidx2++     +------+     wridx2++     |          |
+   |       |  --------------> | buf2 |  --------------> |          |
+   +-------+     size2++      +------+     size2--      +----------+
+
+   size1: "how many bytes are buffered for pty between rdidx1 and wridx1?"
+   size2: "how many bytes are buffered for socket between rdidx2 and wridx2?"
+
+   Each session has got two buffers. Buffers are circular. If sizeN == 0,
+   buffer is empty. If sizeN == BUFSIZE, buffer is full. In both these cases
+   rdidxN == wridxN.
+*/
+ again:
+	FD_ZERO(&rdfdset);
+	FD_ZERO(&wrfdset);
+
+	/* Select on the master socket, all telnet sockets and their
+	 * ptys if there is room in their session buffers.
+	 * NB: scalability problem: we recalculate entire bitmap
+	 * before each select. Can be a problem with 500+ connections. */
+	ts = G.sessions;
+	while (ts) {
+		struct tsession *next = ts->next; /* in case we free ts */
+		if (ts->shell_pid == -1) {
+			/* Child died and we detected that */
+			free_session(ts);
+		} else {
+			if (ts->size1 > 0)       /* can write to pty */
+				FD_SET(ts->ptyfd, &wrfdset);
+			if (ts->size1 < BUFSIZE) /* can read from socket */
+				FD_SET(ts->sockfd_read, &rdfdset);
+			if (ts->size2 > 0)       /* can write to socket */
+				FD_SET(ts->sockfd_write, &wrfdset);
+			if (ts->size2 < BUFSIZE) /* can read from pty */
+				FD_SET(ts->ptyfd, &rdfdset);
+		}
+		ts = next;
+	}
+	if (!IS_INETD) {
+		FD_SET(master_fd, &rdfdset);
+		/* This is needed because free_session() does not
+		 * take master_fd into account when it finds new
+		 * maxfd among remaining fd's */
+		if (master_fd > G.maxfd)
+			G.maxfd = master_fd;
+	}
+
+	{
+		struct timeval *tv_ptr = NULL;
+#if ENABLE_FEATURE_TELNETD_INETD_WAIT
+		struct timeval tv;
+		if ((opt & OPT_WAIT) && !G.sessions) {
+			tv.tv_sec = sec_linger;
+			tv.tv_usec = 0;
+			tv_ptr = &tv;
+		}
+#endif
+		count = select(G.maxfd + 1, &rdfdset, &wrfdset, NULL, tv_ptr);
+	}
+	if (count == 0) /* "telnetd -w SEC" timed out */
+		return 0;
+	if (count < 0)
+		goto again; /* EINTR or ENOMEM */
+
+#if ENABLE_FEATURE_TELNETD_STANDALONE
+	/* Check for and accept new sessions */
+	if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
+		int fd;
+		struct tsession *new_ts;
+
+		fd = accept(master_fd, NULL, NULL);
+		if (fd < 0)
+			goto again;
+		close_on_exec_on(fd);
+
+		/* Create a new session and link it into active list */
+		new_ts = make_new_session(fd);
+		if (new_ts) {
+			new_ts->next = G.sessions;
+			G.sessions = new_ts;
+		} else {
+			close(fd);
+		}
+	}
+#endif
+
+	/* Then check for data tunneling */
+	ts = G.sessions;
+	while (ts) { /* For all sessions... */
+		struct tsession *next = ts->next; /* in case we free ts */
+
+		if (/*ts->size1 &&*/ FD_ISSET(ts->ptyfd, &wrfdset)) {
+			int num_totty;
+			unsigned char *ptr;
+			/* Write to pty from buffer 1 */
+			ptr = remove_iacs(ts, &num_totty);
+			count = safe_write(ts->ptyfd, ptr, num_totty);
+			if (count < 0) {
+				if (errno == EAGAIN)
+					goto skip1;
+				goto kill_session;
+			}
+			ts->size1 -= count;
+			ts->wridx1 += count;
+			if (ts->wridx1 >= BUFSIZE) /* actually == BUFSIZE */
+				ts->wridx1 = 0;
+		}
+ skip1:
+		if (/*ts->size2 &&*/ FD_ISSET(ts->sockfd_write, &wrfdset)) {
+			/* Write to socket from buffer 2 */
+			count = MIN(BUFSIZE - ts->wridx2, ts->size2);
+			count = iac_safe_write(ts->sockfd_write, (void*)(TS_BUF2(ts) + ts->wridx2), count);
+			if (count < 0) {
+				if (errno == EAGAIN)
+					goto skip2;
+				goto kill_session;
+			}
+			ts->size2 -= count;
+			ts->wridx2 += count;
+			if (ts->wridx2 >= BUFSIZE) /* actually == BUFSIZE */
+				ts->wridx2 = 0;
+		}
+ skip2:
+		/* Should not be needed, but... remove_iacs is actually buggy
+		 * (it cannot process iacs which wrap around buffer's end)!
+		 * Since properly fixing it requires writing bigger code,
+		 * we rely instead on this code making it virtually impossible
+		 * to have wrapped iac (people don't type at 2k/second).
+		 * It also allows for bigger reads in common case. */
+		if (ts->size1 == 0) {
+			ts->rdidx1 = 0;
+			ts->wridx1 = 0;
+		}
+		if (ts->size2 == 0) {
+			ts->rdidx2 = 0;
+			ts->wridx2 = 0;
+		}
+
+		if (/*ts->size1 < BUFSIZE &&*/ FD_ISSET(ts->sockfd_read, &rdfdset)) {
+			/* Read from socket to buffer 1 */
+			count = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
+			count = safe_read(ts->sockfd_read, TS_BUF1(ts) + ts->rdidx1, count);
+			if (count <= 0) {
+				if (count < 0 && errno == EAGAIN)
+					goto skip3;
+				goto kill_session;
+			}
+			/* Ignore trailing NUL if it is there */
+			if (!TS_BUF1(ts)[ts->rdidx1 + count - 1]) {
+				--count;
+			}
+			ts->size1 += count;
+			ts->rdidx1 += count;
+			if (ts->rdidx1 >= BUFSIZE) /* actually == BUFSIZE */
+				ts->rdidx1 = 0;
+		}
+ skip3:
+		if (/*ts->size2 < BUFSIZE &&*/ FD_ISSET(ts->ptyfd, &rdfdset)) {
+			/* Read from pty to buffer 2 */
+			count = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
+			count = safe_read(ts->ptyfd, TS_BUF2(ts) + ts->rdidx2, count);
+			if (count <= 0) {
+				if (count < 0 && errno == EAGAIN)
+					goto skip4;
+				goto kill_session;
+			}
+			ts->size2 += count;
+			ts->rdidx2 += count;
+			if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
+				ts->rdidx2 = 0;
+		}
+ skip4:
+		ts = next;
+		continue;
+ kill_session:
+		if (ts->shell_pid > 0)
+			update_utmp(ts->shell_pid, DEAD_PROCESS, /*tty_name:*/ NULL, /*username:*/ NULL, /*hostname:*/ NULL);
+		free_session(ts);
+		ts = next;
+	}
+
+	goto again;
+}
diff --git a/busybox-1.19.3/networking/telnetd.ctrlSQ.patch b/busybox-1.19.3/networking/telnetd.ctrlSQ.patch
new file mode 100644
index 0000000..7060e1c
--- /dev/null
+++ b/busybox-1.19.3/networking/telnetd.ctrlSQ.patch
@@ -0,0 +1,175 @@
+From: "Doug Graham" <dgraham@nortel.com>
+Date: 2009-01-22 07:20
+
+Hello,
+
+Busybox's telnetd does not disable local (client-side) flow control
+properly.  It does not put the pty into packet mode and then notify the
+client whenever flow control is disabled by an application running under
+its control.  The result is that ^S/^Q are not passed through to the
+application, which is painful when the application is an emacs variant.
+
+I suppose that support for this might be considered bloat, but the
+included patch only adds about 200 bytes of text to x86 busybox and 300
+bytes to mipsel busybox.  Please consider applying.
+
+=============================
+
+NB: the patch doesn't work as-is because we now have iac_safe_write()
+which quotes IACs on output.
+
+=============================
+Docs:
+
+The following ioctl(2) calls apply only to pseudo terminals:
+
+TIOCSTOP Stops output to a terminal (e.g. like typing ^S). Takes no parameter.
+
+TIOCSTART Restarts output (stopped by TIOCSTOP or by typing ^S). Takes no parameter.
+
+TIOCPKT         Enable/disable packet mode. When applied to the master side of a pseudo terminal, each
+subsequent read(2) from the terminal will return data written on the slave part of the pseudo terminal preceded by a
+zero byte (symbolically defined as TIOCPKT_DATA), or a single byte reflecting control status information.
+In the latter case, the byte is an inclusive-or of zero or more of the bits:
+
+TIOCPKT_FLUSHREAD     whenever the read queue for the terminal is flushed.
+TIOCPKT_FLUSHWRITE    whenever the write queue for the terminal is flushed.
+TIOCPKT_STOP    whenever output to the terminal is stopped a la ^S.
+TIOCPKT_START   whenever output to the terminal is restarted.
+TIOCPKT_DOSTOP  whenever t_stopc is ^S and t_startc is ^Q.
+TIOCPKT_NOSTOP  whenever the start and stop characters are not ^S/^Q.
+
+While this mode is in use, the presence of control status information to be read from the master side may be detected
+by a select(2) for exceptional conditions.
+
+This mode is used by rlogin(1) and rlogind(8) to implement a remote-echoed, locally ^S/^Q flow-controlled remote login
+with proper back-flushing of output; it can be used by other similar programs.
+
+TIOCUCNTL       Enable/disable a mode that allows a small number of simple user ioctl(2) commands to be passed through
+the pseudo-terminal, using a protocol similar to that of TIOCPKT. The TIOCUCNTL and TIOCPKT modes are mutually
+exclusive. This mode is enabled from the master side of a pseudo terminal. Each subsequent read(2) from the master side
+will return data written on the slave part of the pseudo terminal preceded by a zero byte, or a single byte reflecting a
+user control operation on the slave side. A user control command consists of a special ioctl(2) operation with no data;
+the command is given as UIOCCMD (n), where n is a number in the range 1-255. The operation value n will be received as
+a single byte on the next read(2) from the master side. The ioctl(2) UIOCCMD (0) is a no-op that may be used to probe
+for the existence of this facility. As with TIOCPKT mode, command operations may be detected with a select(2) for
+exceptional conditions.
+
+--- busybox-1.13.2/networking/telnetd.c	2009/01/21 20:02:39	1.1
++++ busybox-1.13.2/networking/telnetd.c	2009/01/22 00:35:28
+@@ -38,6 +38,9 @@
+ 	int sockfd_read, sockfd_write, ptyfd;
+ 	int shell_pid;
+ 
++#ifdef TIOCPKT
++	int flowstate;
++#endif
+ 	/* two circular buffers */
+ 	/*char *buf1, *buf2;*/
+ /*#define TS_BUF1 ts->buf1*/
+@@ -170,6 +173,9 @@
+ 	int fd, pid;
+ 	char tty_name[GETPTY_BUFSIZE];
+ 	struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
++#ifdef TIOCPKT
++	int on = 1;
++#endif
+ 
+ 	/*ts->buf1 = (char *)(ts + 1);*/
+ 	/*ts->buf2 = ts->buf1 + BUFSIZE;*/
+@@ -180,6 +186,10 @@
+ 		maxfd = fd;
+ 	ts->ptyfd = fd;
+ 	ndelay_on(fd);
++#ifdef TIOCPKT
++	ioctl(fd, TIOCPKT, &on);
++	ts->flowstate = TIOCPKT_DOSTOP;
++#endif
+ #if ENABLE_FEATURE_TELNETD_STANDALONE
+ 	ts->sockfd_read = sock;
+ 	/* SO_KEEPALIVE by popular demand */
+@@ -385,6 +395,16 @@
+ 		portnbr = 23,
+ 	};
+ #endif
++#ifdef TIOCPKT
++	int control;
++	static const char lflow_on[] =
++	    {IAC, SB, TELOPT_LFLOW, LFLOW_ON, IAC, SE};
++	static const char lflow_off[] =
++	    {IAC, SB, TELOPT_LFLOW, LFLOW_OFF, IAC, SE};
++# define RESERVED sizeof(lflow_on)
++#else
++# define RESERVED 0
++#endif
+ 	/* Even if !STANDALONE, we accept (and ignore) -i, thus people
+ 	 * don't need to guess whether it's ok to pass -i to us */
+ 	opt = getopt32(argv, "f:l:Ki" IF_FEATURE_TELNETD_STANDALONE("p:b:F"),
+@@ -475,7 +495,7 @@
+ 				FD_SET(ts->sockfd_read, &rdfdset);
+ 			if (ts->size2 > 0)       /* can write to socket */
+ 				FD_SET(ts->sockfd_write, &wrfdset);
+-			if (ts->size2 < BUFSIZE) /* can read from pty */
++			if (ts->size2 < (BUFSIZE - RESERVED)) /* can read from pty */
+ 				FD_SET(ts->ptyfd, &rdfdset);
+ 		}
+ 		ts = next;
+@@ -593,6 +613,52 @@
+ 					goto skip4;
+ 				goto kill_session;
+ 			}
++#ifdef TIOCPKT
++			control = TS_BUF2[ts->rdidx2];
++			if (--count > 0 && control == TIOCPKT_DATA) {
++				/*
++				 * If we are in packet mode, and we have
++				 * just read a chunk of actual data from
++				 * the pty, then there is the TIOCPKT_DATA
++				 * byte (zero) that we have got to remove
++				 * somehow.  If there were no chars in
++				 * TS_BUF2 before we did this read, then
++				 * we can optimize by just advancing wridx2.
++				 * Otherwise we have to copy the new data down
++				 * to close the gap (Could use readv() instead).
++				 */
++				if (ts->size2 == 0)
++					ts->wridx2++;
++				else {
++					memmove(TS_BUF2 + ts->rdidx2,
++						TS_BUF2 + ts->rdidx2 + 1, count);
++				}
++			}
++
++			/*
++			 * If the flow control state changed, notify
++			 * the client.  If "control" is not TIOCPKT_DATA,
++			 * then there are no data bytes to worry about.
++			 */
++			if ((control & (TIOCPKT_DOSTOP|TIOCPKT_NOSTOP)) != 0
++			 && ts->flowstate != (control & TIOCPKT_DOSTOP)) {
++				const char *p = ts->flowstate ? lflow_off : lflow_on;
++
++				/*
++				 * We know we have enough free slots available
++				 * (see RESERVED) but they are not necessarily
++				 * contiguous; we may have to wrap.
++				 */
++				for (count = sizeof(lflow_on); count > 0; count--) {
++					TS_BUF2[ts->rdidx2++] = *p++;
++					if (ts->rdidx2 >= BUFSIZE)
++						ts->rdidx2 = 0;
++					ts->size2++;
++				}
++
++				ts->flowstate = control & TIOCPKT_DOSTOP;
++			}
++#endif /* TIOCPKT */
+ 			ts->size2 += count;
+ 			ts->rdidx2 += count;
+ 			if (ts->rdidx2 >= BUFSIZE) /* actually == BUFSIZE */
+
+--Doug
+_______________________________________________
+busybox mailing list
+busybox@busybox.net
+http://lists.busybox.net/mailman/listinfo/busybox
diff --git a/busybox-1.19.3/networking/tftp.c b/busybox-1.19.3/networking/tftp.c
new file mode 100644
index 0000000..043b879
--- /dev/null
+++ b/busybox-1.19.3/networking/tftp.c
@@ -0,0 +1,878 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * A simple tftp client/server for busybox.
+ * Tries to follow RFC1350.
+ * Only "octet" mode supported.
+ * Optional blocksize negotiation (RFC2347 + RFC2348)
+ *
+ * Copyright (C) 2001 Magnus Damm <damm@opensource.se>
+ *
+ * Parts of the code based on:
+ *
+ * atftp:  Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca>
+ *                        and Remi Lefebvre <remi@debian.org>
+ *
+ * utftp:  Copyright (C) 1999 Uwe Ohse <uwe@ohse.de>
+ *
+ * tftpd added by Denys Vlasenko & Vladimir Dronnikov
+ *
+ * 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
+
+#define TFTP_BLKSIZE_DEFAULT       512  /* according to RFC 1350, don't change */
+#define TFTP_BLKSIZE_DEFAULT_STR "512"
+/* Was 50 ms but users asked to bump it up a bit */
+#define TFTP_TIMEOUT_MS            100
+#define TFTP_MAXTIMEOUT_MS        2000
+#define TFTP_NUM_RETRIES            12  /* number of backed-off retries */
+
+/* opcodes we support */
+#define TFTP_RRQ   1
+#define TFTP_WRQ   2
+#define TFTP_DATA  3
+#define TFTP_ACK   4
+#define TFTP_ERROR 5
+#define TFTP_OACK  6
+
+/* error codes sent over network (we use only 0, 1, 3 and 8) */
+/* generic (error message is included in the packet) */
+#define ERR_UNSPEC   0
+#define ERR_NOFILE   1
+#define ERR_ACCESS   2
+/* disk full or allocation exceeded */
+#define ERR_WRITE    3
+#define ERR_OP       4
+#define ERR_BAD_ID   5
+#define ERR_EXIST    6
+#define ERR_BAD_USER 7
+#define ERR_BAD_OPT  8
+
+/* masks coming from getopt32 */
+enum {
+	TFTP_OPT_GET = (1 << 0),
+	TFTP_OPT_PUT = (1 << 1),
+	/* pseudo option: if set, it's tftpd */
+	TFTPD_OPT = (1 << 7) * ENABLE_TFTPD,
+	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
+#define IF_GETPUT(...)
+#define CMD_GET(cmd) 1
+#define CMD_PUT(cmd) 0
+#elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT
+#define IF_GETPUT(...)
+#define CMD_GET(cmd) 0
+#define CMD_PUT(cmd) 1
+#else
+#define IF_GETPUT(...) __VA_ARGS__
+#define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET)
+#define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT)
+#endif
+/* NB: in the code below
+ * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive
+ */
+
+
+struct globals {
+	/* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */
+	uint8_t error_pkt[4 + 32];
+	char *user_opt;
+	/* used in tftpd_main(), a bit big for stack: */
+	char block_buf[TFTP_BLKSIZE_DEFAULT];
+#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
+	off_t pos;
+	off_t size;
+	const char *file;
+	bb_progress_t pmt;
+#endif
+} 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 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)
+
+#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
+static void tftp_progress_update(void)
+{
+	bb_progress_update(&G.pmt, 0, G.pos, G.size);
+}
+static void tftp_progress_init(void)
+{
+	bb_progress_init(&G.pmt, G.file);
+	tftp_progress_update();
+}
+static void tftp_progress_done(void)
+{
+	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)
+# define tftp_progress_done() ((void)0)
+#endif
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+
+static int tftp_blksize_check(const char *blksize_str, int maxsize)
+{
+	/* Check if the blksize is valid:
+	 * RFC2348 says between 8 and 65464,
+	 * but our implementation makes it impossible
+	 * to use blksizes smaller than 22 octets. */
+	unsigned blksize = bb_strtou(blksize_str, NULL, 10);
+	if (errno
+	 || (blksize < 24) || (blksize > maxsize)
+	) {
+		bb_error_msg("bad blocksize '%s'", blksize_str);
+		return -1;
+	}
+# if ENABLE_TFTP_DEBUG
+	bb_error_msg("using blksize %u", blksize);
+# endif
+	return blksize;
+}
+
+static char *tftp_get_option(const char *option, char *buf, int len)
+{
+	int opt_val = 0;
+	int opt_found = 0;
+	int k;
+
+	/* buf points to:
+	 * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */
+
+	while (len > 0) {
+		/* Make sure options are terminated correctly */
+		for (k = 0; k < len; k++) {
+			if (buf[k] == '\0') {
+				goto nul_found;
+			}
+		}
+		return NULL;
+ nul_found:
+		if (opt_val == 0) { /* it's "name" part */
+			if (strcasecmp(buf, option) == 0) {
+				opt_found = 1;
+			}
+		} else if (opt_found) {
+			return buf;
+		}
+
+		k++;
+		buf += k;
+		len -= k;
+		opt_val ^= 1;
+	}
+
+	return NULL;
+}
+
+#endif
+
+static int tftp_protocol(
+		/* NULL if tftp, !NULL if tftpd: */
+		len_and_sockaddr *our_lsa,
+		len_and_sockaddr *peer_lsa,
+		const char *local_file
+		IF_TFTP(, const char *remote_file)
+#if !ENABLE_TFTP
+# define remote_file NULL
+#endif
+		/* 1 for tftp; 1/0 for tftpd depending whether client asked about it: */
+		IF_FEATURE_TFTP_BLOCKSIZE(, int want_transfer_size)
+		IF_FEATURE_TFTP_BLOCKSIZE(, int blksize))
+{
+#if !ENABLE_FEATURE_TFTP_BLOCKSIZE
+	enum { blksize = TFTP_BLKSIZE_DEFAULT };
+#endif
+
+	struct pollfd pfd[1];
+#define socket_fd (pfd[0].fd)
+	int len;
+	int send_len;
+	IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;)
+	smallint finished = 0;
+	uint16_t opcode;
+	uint16_t block_nr;
+	uint16_t recv_blk;
+	int open_mode, local_fd;
+	int retries, waittime_ms;
+	int io_bufsize = blksize + 4;
+	char *cp;
+	/* Can't use RESERVE_CONFIG_BUFFER here since the allocation
+	 * size varies meaning BUFFERS_GO_ON_STACK would fail.
+	 *
+	 * We must keep the transmit and receive buffers separate
+	 * in case we rcv a garbage pkt - we need to rexmit the last pkt.
+	 */
+	char *xbuf = xmalloc(io_bufsize);
+	char *rbuf = xmalloc(io_bufsize);
+
+	socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+	setsockopt_reuseaddr(socket_fd);
+
+	if (!ENABLE_TFTP || our_lsa) { /* tftpd */
+		/* Create a socket which is:
+		 * 1. bound to IP:port peer sent 1st datagram to,
+		 * 2. connected to peer's IP:port
+		 * This way we will answer from the IP:port peer
+		 * expects, will not get any other packets on
+		 * the socket, and also plain read/write will work. */
+		xbind(socket_fd, &our_lsa->u.sa, our_lsa->len);
+		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])
+			goto send_err_pkt;
+
+		if (user_opt) {
+			struct passwd *pw = xgetpwnam(user_opt);
+			change_identity(pw); /* initgroups, setgid, setuid */
+		}
+	}
+
+	/* Prepare open mode */
+	if (CMD_PUT(option_mask32)) {
+		open_mode = O_RDONLY;
+	} else {
+		open_mode = O_WRONLY | O_TRUNC | O_CREAT;
+#if ENABLE_TFTPD
+		if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) {
+			/* tftpd without -c */
+			open_mode = O_WRONLY | O_TRUNC;
+		}
+#endif
+	}
+
+	/* Examples of network traffic.
+	 * Note two cases when ACKs with block# of 0 are sent.
+	 *
+	 * Download without options:
+	 * tftp -> "\0\1FILENAME\0octet\0"
+	 *         "\0\3\0\1FILEDATA..." <- tftpd
+	 * tftp -> "\0\4\0\1"
+	 * ...
+	 * Download with option of blksize 16384:
+	 * tftp -> "\0\1FILENAME\0octet\0blksize\00016384\0"
+	 *         "\0\6blksize\00016384\0" <- tftpd
+	 * tftp -> "\0\4\0\0"
+	 *         "\0\3\0\1FILEDATA..." <- tftpd
+	 * tftp -> "\0\4\0\1"
+	 * ...
+	 * Upload without options:
+	 * tftp -> "\0\2FILENAME\0octet\0"
+	 *         "\0\4\0\0" <- tftpd
+	 * tftp -> "\0\3\0\1FILEDATA..."
+	 *         "\0\4\0\1" <- tftpd
+	 * ...
+	 * Upload with option of blksize 16384:
+	 * tftp -> "\0\2FILENAME\0octet\0blksize\00016384\0"
+	 *         "\0\6blksize\00016384\0" <- tftpd
+	 * tftp -> "\0\3\0\1FILEDATA..."
+	 *         "\0\4\0\1" <- tftpd
+	 * ...
+	 */
+	block_nr = 1;
+	cp = xbuf + 2;
+
+	if (!ENABLE_TFTP || our_lsa) { /* tftpd */
+		/* 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");
+			goto send_err_pkt;
+		}
+/* gcc 4.3.1 would NOT optimize it out as it should! */
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+		if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) {
+			/* Create and send OACK packet. */
+			/* For the download case, block_nr is still 1 -
+			 * we expect 1st ACK from peer to be for (block_nr-1),
+			 * that is, for "block 0" which is our OACK pkt */
+			opcode = TFTP_OACK;
+			goto add_blksize_opt;
+		}
+#endif
+		if (CMD_GET(option_mask32)) {
+			/* It's upload and we don't send OACK.
+			 * We must ACK 1st packet (with filename)
+			 * as if it is "block 0" */
+			block_nr = 0;
+		}
+
+	} else { /* tftp */
+		/* Open file (must be after changing user) */
+		local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO;
+		if (NOT_LONE_DASH(local_file))
+			local_fd = xopen(local_file, open_mode);
+/* Removing #if, or using if() statement instead of #if may lead to
+ * "warning: null argument where non-null required": */
+#if ENABLE_TFTP
+		/* tftp */
+
+		/* We can't (and don't really need to) bind the socket:
+		 * we don't know from which local IP datagrams will be sent,
+		 * but kernel will pick the same IP every time (unless routing
+		 * table is changed), thus peer will see dgrams consistently
+		 * coming from the same IP.
+		 * We would like to connect the socket, but since peer's
+		 * UDP code can be less perfect than ours, _peer's_ IP:port
+		 * in replies may differ from IP:port we used to send
+		 * our first packet. We can connect() only when we get
+		 * first reply. */
+
+		/* build opcode */
+		opcode = TFTP_WRQ;
+		if (CMD_GET(option_mask32)) {
+			opcode = TFTP_RRQ;
+		}
+		/* add filename and mode */
+		/* fill in packet if the filename fits into xbuf */
+		len = strlen(remote_file) + 1;
+		if (2 + len + sizeof("octet") >= io_bufsize) {
+			bb_error_msg("remote filename is too long");
+			goto ret;
+		}
+		strcpy(cp, remote_file);
+		cp += len;
+		/* add "mode" part of the packet */
+		strcpy(cp, "octet");
+		cp += sizeof("octet");
+
+# if ENABLE_FEATURE_TFTP_BLOCKSIZE
+		if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size)
+			goto send_pkt;
+
+		/* Need to add option to pkt */
+		if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) {
+			bb_error_msg("remote filename is too long");
+			goto ret;
+		}
+		expect_OACK = 1;
+# endif
+#endif /* ENABLE_TFTP */
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+ add_blksize_opt:
+		if (blksize != TFTP_BLKSIZE_DEFAULT) {
+			/* add "blksize", <nul>, blksize, <nul> */
+			strcpy(cp, "blksize");
+			cp += sizeof("blksize");
+			cp += snprintf(cp, 6, "%d", blksize) + 1;
+		}
+		if (want_transfer_size) {
+			/* add "tsize", <nul>, size, <nul> (see RFC2349) */
+			/* if tftp and downloading, we send "0" (since we opened local_fd with O_TRUNC)
+			 * and this makes server to send "tsize" option with the size */
+			/* if tftp and uploading, we send file size (maybe dont, to not confuse old servers???) */
+			/* if tftpd and downloading, we are answering to client's request */
+			/* if tftpd and uploading: !want_transfer_size, this code is not executed */
+			struct stat st;
+			strcpy(cp, "tsize");
+			cp += sizeof("tsize");
+			st.st_size = 0;
+			fstat(local_fd, &st);
+			cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1;
+# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
+			/* Save for progress bar. If 0 (tftp downloading),
+			 * we look at server's reply later */
+			G.size = st.st_size;
+			if (remote_file && st.st_size)
+				tftp_progress_init();
+# endif
+		}
+#endif
+		/* First packet is built, so skip packet generation */
+		goto send_pkt;
+	}
+
+	/* Using mostly goto's - continue/break will be less clear
+	 * in where we actually jump to */
+	while (1) {
+		/* Build ACK or DATA */
+		cp = xbuf + 2;
+		*((uint16_t*)cp) = htons(block_nr);
+		cp += 2;
+		block_nr++;
+		opcode = TFTP_ACK;
+		if (CMD_PUT(option_mask32)) {
+			opcode = TFTP_DATA;
+			len = full_read(local_fd, cp, blksize);
+			if (len < 0) {
+				goto send_read_err_pkt;
+			}
+			if (len != blksize) {
+				finished = 1;
+			}
+			cp += len;
+			IF_FEATURE_TFTP_PROGRESS_BAR(G.pos += len;)
+		}
+ send_pkt:
+		/* Send packet */
+		*((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */
+		send_len = cp - xbuf;
+		/* NB: send_len value is preserved in code below
+		 * for potential resend */
+
+		retries = TFTP_NUM_RETRIES;  /* re-initialize */
+		waittime_ms = TFTP_TIMEOUT_MS;
+
+ send_again:
+#if ENABLE_TFTP_DEBUG
+		fprintf(stderr, "sending %u bytes\n", send_len);
+		for (cp = xbuf; cp < &xbuf[send_len]; cp++)
+			fprintf(stderr, "%02x ", (unsigned char) *cp);
+		fprintf(stderr, "\n");
+#endif
+		xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len);
+
+#if ENABLE_FEATURE_TFTP_PROGRESS_BAR
+		if (is_bb_progress_inited(&G.pmt))
+			tftp_progress_update();
+#endif
+		/* Was it final ACK? then exit */
+		if (finished && (opcode == TFTP_ACK))
+			goto ret;
+
+ recv_again:
+		/* Receive packet */
+		/*pfd[0].fd = socket_fd;*/
+		pfd[0].events = POLLIN;
+		switch (safe_poll(pfd, 1, waittime_ms)) {
+		default:
+			/*bb_perror_msg("poll"); - done in safe_poll */
+			goto ret;
+		case 0:
+			retries--;
+			if (retries == 0) {
+				tftp_progress_done();
+				bb_error_msg("timeout");
+				goto ret; /* no err packet sent */
+			}
+
+			/* exponential backoff with limit */
+			waittime_ms += waittime_ms/2;
+			if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
+				waittime_ms = TFTP_MAXTIMEOUT_MS;
+			}
+
+			goto send_again; /* resend last sent pkt */
+		case 1:
+			if (!our_lsa) {
+				/* tftp (not tftpd!) receiving 1st packet */
+				our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */
+				len = recvfrom(socket_fd, rbuf, io_bufsize, 0,
+						&peer_lsa->u.sa, &peer_lsa->len);
+				/* Our first dgram went to port 69
+				 * but reply may come from different one.
+				 * Remember and use this new port (and IP) */
+				if (len >= 0)
+					xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len);
+			} else {
+				/* tftpd, or not the very first packet:
+				 * socket is connect()ed, can just read from it. */
+				/* Don't full_read()!
+				 * This is not TCP, one read == one pkt! */
+				len = safe_read(socket_fd, rbuf, io_bufsize);
+			}
+			if (len < 0) {
+				goto send_read_err_pkt;
+			}
+			if (len < 4) { /* too small? */
+				goto recv_again;
+			}
+		}
+
+		/* Process recv'ed packet */
+		opcode = ntohs( ((uint16_t*)rbuf)[0] );
+		recv_blk = ntohs( ((uint16_t*)rbuf)[1] );
+#if ENABLE_TFTP_DEBUG
+		fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk);
+#endif
+		if (opcode == TFTP_ERROR) {
+			static const char errcode_str[] ALIGN1 =
+				"\0"
+				"file not found\0"
+				"access violation\0"
+				"disk full\0"
+				"bad operation\0"
+				"unknown transfer id\0"
+				"file already exists\0"
+				"no such user\0"
+				"bad option";
+
+			const char *msg = "";
+
+			if (len > 4 && rbuf[4] != '\0') {
+				msg = &rbuf[4];
+				rbuf[io_bufsize - 1] = '\0'; /* paranoia */
+			} else if (recv_blk <= 8) {
+				msg = nth_string(errcode_str, recv_blk);
+			}
+			bb_error_msg("server error: (%u) %s", recv_blk, msg);
+			goto ret;
+		}
+
+#if ENABLE_FEATURE_TFTP_BLOCKSIZE
+		if (expect_OACK) {
+			expect_OACK = 0;
+			if (opcode == TFTP_OACK) {
+				/* server seems to support options */
+				char *res;
+
+				res = tftp_get_option("blksize", &rbuf[2], len - 2);
+				if (res) {
+					blksize = tftp_blksize_check(res, blksize);
+					if (blksize < 0) {
+						error_pkt_reason = ERR_BAD_OPT;
+						goto send_err_pkt;
+					}
+					io_bufsize = blksize + 4;
+				}
+# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
+				if (remote_file && G.size == 0) { /* if we don't know it yet */
+					res = tftp_get_option("tsize", &rbuf[2], len - 2);
+					if (res) {
+						G.size = bb_strtoull(res, NULL, 10);
+						if (G.size)
+							tftp_progress_init();
+					}
+				}
+# endif
+				if (CMD_GET(option_mask32)) {
+					/* We'll send ACK for OACK,
+					 * such ACK has "block no" of 0 */
+					block_nr = 0;
+				}
+				continue;
+			}
+			/* rfc2347:
+			 * "An option not acknowledged by the server
+			 * must be ignored by the client and server
+			 * as if it were never requested." */
+			if (blksize != TFTP_BLKSIZE_DEFAULT)
+				bb_error_msg("falling back to blocksize "TFTP_BLKSIZE_DEFAULT_STR);
+			blksize = TFTP_BLKSIZE_DEFAULT;
+			io_bufsize = TFTP_BLKSIZE_DEFAULT + 4;
+		}
+#endif
+		/* block_nr is already advanced to next block# we expect
+		 * to get / block# we are about to send next time */
+
+		if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) {
+			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;
+					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 */
+#if 0
+			if (recv_blk == (block_nr - 1)) {
+				/* Server lost our TFTP_ACK.  Resend it */
+				block_nr = recv_blk;
+				continue;
+			}
+#endif
+		}
+
+		if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) {
+			/* did peer ACK our last DATA pkt? */
+			if (recv_blk == (uint16_t) (block_nr - 1)) {
+				if (finished)
+					goto ret;
+				continue; /* send next block */
+			}
+		}
+		/* Awww... recv'd packet is not recognized! */
+		goto recv_again;
+		/* why recv_again? - rfc1123 says:
+		 * "The sender (i.e., the side originating the DATA packets)
+		 *  must never resend the current DATA packet on receipt
+		 *  of a duplicate ACK".
+		 * DATA pkts are resent ONLY on timeout.
+		 * Thus "goto send_again" will ba a bad mistake above.
+		 * See:
+		 * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome
+		 */
+	} /* end of "while (1)" */
+ ret:
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(local_fd);
+		close(socket_fd);
+		free(xbuf);
+		free(rbuf);
+	}
+	return finished == 0; /* returns 1 on failure */
+
+ send_read_err_pkt:
+	strcpy((char*)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),
+			&peer_lsa->u.sa, peer_lsa->len);
+	return EXIT_FAILURE;
+#undef remote_file
+}
+
+#if ENABLE_TFTP
+
+int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tftp_main(int argc UNUSED_PARAM, char **argv)
+{
+	len_and_sockaddr *peer_lsa;
+	const char *local_file = NULL;
+	const char *remote_file = NULL;
+# if ENABLE_FEATURE_TFTP_BLOCKSIZE
+	const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR;
+	int blksize;
+# endif
+	int result;
+	int port;
+	IF_GETPUT(int opt;)
+
+	INIT_G();
+
+	/* -p or -g is mandatory, and they are mutually exclusive */
+	opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:")
+			IF_GETPUT("g--p:p--g:");
+
+	IF_GETPUT(opt =) getopt32(argv,
+			IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p")
+				"l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:"),
+			&local_file, &remote_file
+			IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str));
+	argv += optind;
+
+# if ENABLE_FEATURE_TFTP_BLOCKSIZE
+	/* Check if the blksize is valid:
+	 * RFC2348 says between 8 and 65464 */
+	blksize = tftp_blksize_check(blksize_str, 65564);
+	if (blksize < 0) {
+		//bb_error_msg("bad block size");
+		return EXIT_FAILURE;
+	}
+# endif
+
+	if (remote_file) {
+		if (!local_file) {
+			const char *slash = strrchr(remote_file, '/');
+			local_file = slash ? slash + 1 : remote_file;
+		}
+	} else {
+		remote_file = local_file;
+	}
+
+	/* Error if filename or host is not known */
+	if (!remote_file || !argv[0])
+		bb_show_usage();
+
+	port = bb_lookup_port(argv[1], "udp", 69);
+	peer_lsa = xhost2sockaddr(argv[0], port);
+
+# if ENABLE_TFTP_DEBUG
+	fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n",
+			xmalloc_sockaddr2dotted(&peer_lsa->u.sa),
+			remote_file, local_file);
+# endif
+
+# if ENABLE_FEATURE_TFTP_PROGRESS_BAR
+	G.file = remote_file;
+# endif
+	result = tftp_protocol(
+		NULL /*our_lsa*/, peer_lsa,
+		local_file, remote_file
+		IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */)
+		IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
+	);
+	tftp_progress_done();
+
+	if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) {
+		unlink(local_file);
+	}
+	return result;
+}
+
+#endif /* ENABLE_TFTP */
+
+#if ENABLE_TFTPD
+int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tftpd_main(int argc UNUSED_PARAM, char **argv)
+{
+	len_and_sockaddr *our_lsa;
+	len_and_sockaddr *peer_lsa;
+	char *local_file, *mode;
+	const char *error_msg;
+	int opt, result, opcode;
+	IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;)
+	IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;)
+
+	INIT_G();
+
+	our_lsa = get_sock_lsa(STDIN_FILENO);
+	if (!our_lsa) {
+		/* This is confusing:
+		 *bb_error_msg_and_die("stdin is not a socket");
+		 * Better: */
+		bb_show_usage();
+		/* Help text says that tftpd must be used as inetd service,
+		 * which is by far the most usual cause of get_sock_lsa
+		 * failure */
+	}
+	peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len);
+	peer_lsa->len = our_lsa->len;
+
+	/* Shifting to not collide with TFTP_OPTs */
+	opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:l", &user_opt) << 8);
+	argv += optind;
+	if (opt & TFTPD_OPT_l) {
+		openlog(applet_name, LOG_PID, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG;
+	}
+	if (argv[0])
+		xchdir(argv[0]);
+
+	result = recv_from_to(STDIN_FILENO, block_buf, sizeof(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'
+	 || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */
+	     IF_GETPUT(&&)
+	     IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */
+	    )
+	) {
+		goto err;
+	}
+	local_file = 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;
+	/* RFC 1350 says mode string is case independent */
+	if (mode >= 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;
+		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;
+					/* will just send error pkt */
+					goto do_proto;
+				}
+			}
+			if (opcode != TFTP_WRQ /* download? */
+			/* did client ask us about file size? */
+			 && tftp_get_option("tsize", opt_str, opt_len)
+			) {
+				want_transfer_size = 1;
+			}
+		}
+	}
+# endif
+
+	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;*/
+			error_msg = bb_msg_write_error;
+			goto err;
+		}
+		IF_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */
+	} else {
+		IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */
+	}
+
+	/* NB: if error_pkt_str or error_pkt_reason is set up,
+	 * tftp_protocol() just sends one error pkt and returns */
+
+ do_proto:
+	close(STDIN_FILENO); /* close old, possibly wildcard socket */
+	/* tftp_protocol() will create new one, bound to particular local IP */
+	result = tftp_protocol(
+		our_lsa, peer_lsa,
+		local_file IF_TFTP(, NULL /*remote_file*/)
+		IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size)
+		IF_FEATURE_TFTP_BLOCKSIZE(, blksize)
+	);
+
+	return result;
+ err:
+	strcpy((char*)error_pkt_str, error_msg);
+	goto do_proto;
+}
+
+#endif /* ENABLE_TFTPD */
+
+#endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */
diff --git a/busybox-1.19.3/networking/traceroute.c b/busybox-1.19.3/networking/traceroute.c
new file mode 100644
index 0000000..c321035
--- /dev/null
+++ b/busybox-1.19.3/networking/traceroute.c
@@ -0,0 +1,1266 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (c) 1988, 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Busybox port by Vladimir Oleynik (C) 2005 <dzo@simtreas.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ *	traceroute6
+ *
+ *      Modified for NRL 4.4BSD IPv6 release.
+ *      07/31/96 bgp
+ *
+ *	Modified for Linux IPv6 by Pedro Roque <roque@di.fc.ul.pt>
+ *	31/07/1996
+ *
+ *	As ICMP error messages for IPv6 now include more than 8 bytes
+ *	UDP datagrams are now sent via an UDP socket instead of magic
+ *	RAW socket tricks.
+ *
+ *	Converted to busybox applet by Leonid Lisovskiy <lly@sf.net>
+ *	2009-11-16
+ */
+
+/*
+ * traceroute host  - trace the route ip packets follow going to "host".
+ *
+ * Attempt to trace the route an ip packet would follow to some
+ * internet host.  We find out intermediate hops by launching probe
+ * packets with a small ttl (time to live) then listening for an
+ * icmp "time exceeded" reply from a gateway.  We start our probes
+ * with a ttl of one and increase by one until we get an icmp "port
+ * unreachable" (which means we got to "host") or hit a max (which
+ * defaults to 30 hops & can be changed with the -m flag).  Three
+ * probes (change with -q flag) are sent at each ttl setting and a
+ * line is printed showing the ttl, address of the gateway and
+ * round trip time of each probe.  If the probe answers come from
+ * different gateways, the address of each responding system will
+ * be printed.  If there is no response within a 5 sec. timeout
+ * interval (changed with the -w flag), a "*" is printed for that
+ * probe.
+ *
+ * Probe packets are UDP format.  We don't want the destination
+ * host to process them so the destination port is set to an
+ * unlikely value (if some clod on the destination is using that
+ * value, it can be changed with the -p flag).
+ *
+ * A sample use might be:
+ *
+ *     [yak 71]% traceroute nis.nsf.net.
+ *     traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
+ *      1  helios.ee.lbl.gov (128.3.112.1)  19 ms  19 ms  0 ms
+ *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
+ *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  39 ms  19 ms
+ *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  39 ms
+ *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  39 ms  39 ms  39 ms
+ *      6  128.32.197.4 (128.32.197.4)  40 ms  59 ms  59 ms
+ *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  59 ms
+ *      8  129.140.70.13 (129.140.70.13)  99 ms  99 ms  80 ms
+ *      9  129.140.71.6 (129.140.71.6)  139 ms  239 ms  319 ms
+ *     10  129.140.81.7 (129.140.81.7)  220 ms  199 ms  199 ms
+ *     11  nic.merit.edu (35.1.1.48)  239 ms  239 ms  239 ms
+ *
+ * Note that lines 2 & 3 are the same.  This is due to a buggy
+ * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
+ * packets with a zero ttl.
+ *
+ * A more interesting example is:
+ *
+ *     [yak 72]% traceroute allspice.lcs.mit.edu.
+ *     traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
+ *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
+ *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  19 ms  19 ms
+ *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  19 ms
+ *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  19 ms  39 ms  39 ms
+ *      5  ccn-nerif22.Berkeley.EDU (128.32.168.22)  20 ms  39 ms  39 ms
+ *      6  128.32.197.4 (128.32.197.4)  59 ms  119 ms  39 ms
+ *      7  131.119.2.5 (131.119.2.5)  59 ms  59 ms  39 ms
+ *      8  129.140.70.13 (129.140.70.13)  80 ms  79 ms  99 ms
+ *      9  129.140.71.6 (129.140.71.6)  139 ms  139 ms  159 ms
+ *     10  129.140.81.7 (129.140.81.7)  199 ms  180 ms  300 ms
+ *     11  129.140.72.17 (129.140.72.17)  300 ms  239 ms  239 ms
+ *     12  * * *
+ *     13  128.121.54.72 (128.121.54.72)  259 ms  499 ms  279 ms
+ *     14  * * *
+ *     15  * * *
+ *     16  * * *
+ *     17  * * *
+ *     18  ALLSPICE.LCS.MIT.EDU (18.26.0.115)  339 ms  279 ms  279 ms
+ *
+ * (I start to see why I'm having so much trouble with mail to
+ * MIT.)  Note that the gateways 12, 14, 15, 16 & 17 hops away
+ * either don't send ICMP "time exceeded" messages or send them
+ * with a ttl too small to reach us.  14 - 17 are running the
+ * MIT C Gateway code that doesn't send "time exceeded"s.  God
+ * only knows what's going on with 12.
+ *
+ * The silent gateway 12 in the above may be the result of a bug in
+ * the 4.[23]BSD network code (and its derivatives):  4.x (x <= 3)
+ * sends an unreachable message using whatever ttl remains in the
+ * original datagram.  Since, for gateways, the remaining ttl is
+ * zero, the icmp "time exceeded" is guaranteed to not make it back
+ * to us.  The behavior of this bug is slightly more interesting
+ * when it appears on the destination system:
+ *
+ *      1  helios.ee.lbl.gov (128.3.112.1)  0 ms  0 ms  0 ms
+ *      2  lilac-dmc.Berkeley.EDU (128.32.216.1)  39 ms  19 ms  39 ms
+ *      3  lilac-dmc.Berkeley.EDU (128.32.216.1)  19 ms  39 ms  19 ms
+ *      4  ccngw-ner-cc.Berkeley.EDU (128.32.136.23)  39 ms  40 ms  19 ms
+ *      5  ccn-nerif35.Berkeley.EDU (128.32.168.35)  39 ms  39 ms  39 ms
+ *      6  csgw.Berkeley.EDU (128.32.133.254)  39 ms  59 ms  39 ms
+ *      7  * * *
+ *      8  * * *
+ *      9  * * *
+ *     10  * * *
+ *     11  * * *
+ *     12  * * *
+ *     13  rip.Berkeley.EDU (128.32.131.22)  59 ms !  39 ms !  39 ms !
+ *
+ * Notice that there are 12 "gateways" (13 is the final
+ * destination) and exactly the last half of them are "missing".
+ * What's really happening is that rip (a Sun-3 running Sun OS3.5)
+ * is using the ttl from our arriving datagram as the ttl in its
+ * icmp reply.  So, the reply will time out on the return path
+ * (with no notice sent to anyone since icmp's aren't sent for
+ * icmp's) until we probe with a ttl that's at least twice the path
+ * length.  I.e., rip is really only 7 hops away.  A reply that
+ * returns with a ttl of 1 is a clue this problem exists.
+ * Traceroute prints a "!" after the time if the ttl is <= 1.
+ * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
+ * non-standard (HPUX) software, expect to see this problem
+ * frequently and/or take care picking the target host of your
+ * probes.
+ *
+ * Other possible annotations after the time are !H, !N, !P (got a host,
+ * network or protocol unreachable, respectively), !S or !F (source
+ * route failed or fragmentation needed -- neither of these should
+ * ever occur and the associated gateway is busted if you see one).  If
+ * almost all the probes result in some kind of unreachable, traceroute
+ * will give up and exit.
+ *
+ * Notes
+ * -----
+ * This program must be run by root or be setuid.  (I suggest that
+ * you *don't* make it setuid -- casual use could result in a lot
+ * of unnecessary traffic on our poor, congested nets.)
+ *
+ * This program requires a kernel mod that does not appear in any
+ * system available from Berkeley:  A raw ip socket using proto
+ * IPPROTO_RAW must interpret the data sent as an ip datagram (as
+ * opposed to data to be wrapped in a ip datagram).  See the README
+ * file that came with the source to this program for a description
+ * of the mods I made to /sys/netinet/raw_ip.c.  Your mileage may
+ * vary.  But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
+ * MODIFIED TO RUN THIS PROGRAM.
+ *
+ * The udp port usage may appear bizarre (well, ok, it is bizarre).
+ * The problem is that an icmp message only contains 8 bytes of
+ * data from the original datagram.  8 bytes is the size of a udp
+ * header so, if we want to associate replies with the original
+ * datagram, the necessary information must be encoded into the
+ * udp header (the ip id could be used but there's no way to
+ * interlock with the kernel's assignment of ip id's and, anyway,
+ * it would have taken a lot more kernel hacking to allow this
+ * code to set the ip id).  So, to allow two or more users to
+ * use traceroute simultaneously, we use this task's pid as the
+ * source port (the high bit is set to move the port number out
+ * of the "likely" range).  To keep track of which probe is being
+ * replied to (so times and/or hop counts don't get confused by a
+ * reply that was delayed in transit), we increment the destination
+ * port number before each probe.
+ *
+ * Don't use this as a coding example.  I was trying to find a
+ * routing problem and this code sort-of popped out after 48 hours
+ * without sleep.  I was amazed it ever compiled, much less ran.
+ *
+ * I stole the idea for this program from Steve Deering.  Since
+ * the first release, I've learned that had I attended the right
+ * IETF working group meetings, I also could have stolen it from Guy
+ * Almes or Matt Mathis.  I don't know (or care) who came up with
+ * the idea first.  I envy the originators' perspicacity and I'm
+ * glad they didn't keep the idea a secret.
+ *
+ * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
+ * enhancements to the original distribution.
+ *
+ * I've hacked up a round-trip-route version of this that works by
+ * sending a loose-source-routed udp datagram through the destination
+ * back to yourself.  Unfortunately, SO many gateways botch source
+ * routing, the thing is almost worthless.  Maybe one day...
+ *
+ *  -- Van Jacobson (van@ee.lbl.gov)
+ *     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! */
+/* probably ok to remove altogether */
+//#undef CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#define CONFIG_FEATURE_TRACEROUTE_VERBOSE
+//#undef CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#define CONFIG_FEATURE_TRACEROUTE_SOURCE_ROUTE
+//#undef CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+//#define CONFIG_FEATURE_TRACEROUTE_USE_ICMP
+
+
+#include <net/if.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#if ENABLE_FEATURE_IPV6
+# include <netinet/ip6.h>
+# include <netinet/icmp6.h>
+# ifndef SOL_IPV6
+#  define SOL_IPV6 IPPROTO_IPV6
+# endif
+#endif
+
+#include "libbb.h"
+#include "inet_common.h"
+
+#ifndef IPPROTO_ICMP
+# define IPPROTO_ICMP 1
+#endif
+#ifndef IPPROTO_IP
+# define IPPROTO_IP 0
+#endif
+
+
+#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 */
+	OPT_TTL_FLAG     = (1 << 2),    /* l */
+	OPT_ADDR_NUM     = (1 << 3),    /* n */
+	OPT_BYPASS_ROUTE = (1 << 4),    /* r */
+	OPT_DEBUG        = (1 << 5),    /* d */
+	OPT_VERBOSE      = (1 << 6) * ENABLE_FEATURE_TRACEROUTE_VERBOSE, /* v */
+	OPT_IP_CHKSUM    = (1 << 7),    /* x */
+	OPT_TOS          = (1 << 8),    /* t */
+	OPT_DEVICE       = (1 << 9),    /* i */
+	OPT_MAX_TTL      = (1 << 10),   /* m */
+	OPT_PORT         = (1 << 11),   /* p */
+	OPT_NPROBES      = (1 << 12),   /* q */
+	OPT_SOURCE       = (1 << 13),   /* s */
+	OPT_WAITTIME     = (1 << 14),   /* w */
+	OPT_PAUSE_MS     = (1 << 15),   /* z */
+	OPT_FIRST_TTL    = (1 << 16),   /* f */
+	OPT_SOURCE_ROUTE = (1 << 17) * ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE, /* g */
+	OPT_IPV4         = (1 << (17+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)),   /* 4 */
+	OPT_IPV6         = (1 << (18+ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE)) * ENABLE_TRACEROUTE6, /* 6 */
+};
+#define verbose (option_mask32 & OPT_VERBOSE)
+
+enum {
+	SIZEOF_ICMP_HDR = 8,
+	rcvsock = 3, /* receive (icmp) socket file descriptor */
+	sndsock = 4, /* send (udp/icmp) socket file descriptor */
+};
+
+/* Data section of the probe packet */
+struct outdata_t {
+	unsigned char seq;             /* sequence number of this packet */
+	unsigned char ttl;             /* ttl packet left with */
+// UNUSED. Retaining to have the same packet size.
+	struct timeval tv_UNUSED PACKED; /* time packet left */
+};
+
+#if ENABLE_TRACEROUTE6
+struct outdata6_t {
+	uint32_t ident6;
+	uint32_t seq6;
+	struct timeval tv_UNUSED PACKED; /* time packet left */
+};
+#endif
+
+struct globals {
+	struct ip *outip;
+	struct outdata_t *outdata;
+	len_and_sockaddr *dest_lsa;
+	int packlen;                    /* total length of packet */
+	int pmtu;                       /* Path MTU Discovery (RFC1191) */
+	uint32_t ident;
+	uint16_t port; // 32768 + 666;  /* start udp dest port # for probe packets */
+	int waittime; // 5;             /* time to wait for response (in seconds) */
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+	int optlen;                     /* length of ip options */
+#else
+#define optlen 0
+#endif
+	unsigned char recv_pkt[512];    /* last inbound (icmp) packet */
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+	/* Maximum number of gateways (include room for one noop) */
+#define NGATEWAYS ((int)((MAX_IPOPTLEN - IPOPT_MINOFF - 1) / sizeof(uint32_t)))
+	/* loose source route gateway list (including room for final destination) */
+	uint32_t gwlist[NGATEWAYS + 1];
+#endif
+};
+
+#define G (*ptr_to_globals)
+#define outip     (G.outip    )
+#define outdata   (G.outdata  )
+#define dest_lsa  (G.dest_lsa )
+#define packlen   (G.packlen  )
+#define pmtu      (G.pmtu     )
+#define ident     (G.ident    )
+#define port      (G.port     )
+#define waittime  (G.waittime )
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+# define optlen   (G.optlen   )
+#endif
+#define recv_pkt  (G.recv_pkt )
+#define gwlist    (G.gwlist   )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	port = 32768 + 666; \
+	waittime = 5; \
+} while (0)
+
+#define outicmp ((struct icmp *)(outip + 1))
+#define outudp  ((struct udphdr *)(outip + 1))
+
+
+/* libbb candidate? tftp uses this idiom too */
+static len_and_sockaddr* dup_sockaddr(const len_and_sockaddr *lsa)
+{
+	len_and_sockaddr *new_lsa = xzalloc(LSA_LEN_SIZE + lsa->len);
+	memcpy(new_lsa, lsa, LSA_LEN_SIZE + lsa->len);
+	return new_lsa;
+}
+
+
+static int
+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 (*left_ms >= 0 && safe_poll(pfd, 1, *left_ms) > 0) {
+		unsigned t;
+
+		read_len = recv_from_to(rcvsock,
+				recv_pkt, sizeof(recv_pkt),
+				/*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)
+{
+	int len, res;
+	void *out;
+
+	/* Payload */
+#if ENABLE_TRACEROUTE6
+	if (dest_lsa->u.sa.sa_family == AF_INET6) {
+		struct outdata6_t *pkt = (struct outdata6_t *) outip;
+		pkt->ident6 = htonl(ident);
+		pkt->seq6   = htonl(seq);
+		/*gettimeofday(&pkt->tv, &tz);*/
+	} else
+#endif
+	{
+		outdata->seq = seq;
+		outdata->ttl = ttl;
+// UNUSED: was storing gettimeofday's result there, but never ever checked it
+		/*memcpy(&outdata->tv, tp, sizeof(outdata->tv));*/
+
+		if (option_mask32 & OPT_USE_ICMP) {
+			outicmp->icmp_seq = htons(seq);
+
+			/* Always calculate checksum for icmp packets */
+			outicmp->icmp_cksum = 0;
+			outicmp->icmp_cksum = in_cksum((uint16_t *)outicmp,
+						packlen - (sizeof(*outip) + optlen));
+			if (outicmp->icmp_cksum == 0)
+				outicmp->icmp_cksum = 0xffff;
+		}
+	}
+
+//BUG! verbose is (x & OPT_VERBOSE), not a counter!
+#if 0 //ENABLE_FEATURE_TRACEROUTE_VERBOSE
+	/* XXX undocumented debugging hack */
+	if (verbose > 1) {
+		const uint16_t *sp;
+		int nshorts, i;
+
+		sp = (uint16_t *)outip;
+		nshorts = (unsigned)packlen / sizeof(uint16_t);
+		i = 0;
+		printf("[ %d bytes", packlen);
+		while (--nshorts >= 0) {
+			if ((i++ % 8) == 0)
+				printf("\n\t");
+			printf(" %04x", ntohs(*sp));
+			sp++;
+		}
+		if (packlen & 1) {
+			if ((i % 8) == 0)
+				printf("\n\t");
+			printf(" %02x", *(unsigned char *)sp);
+		}
+		printf("]\n");
+	}
+#endif
+
+#if ENABLE_TRACEROUTE6
+	if (dest_lsa->u.sa.sa_family == AF_INET6) {
+		res = setsockopt(sndsock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
+		if (res < 0)
+			bb_perror_msg_and_die("setsockopt UNICAST_HOPS %d", ttl);
+		out = outip;
+		len = packlen;
+	} else
+#endif
+	{
+#if defined IP_TTL
+		res = setsockopt(sndsock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
+		if (res < 0)
+			bb_perror_msg_and_die("setsockopt ttl %d", ttl);
+#endif
+		out = outicmp;
+		len = packlen - sizeof(*outip);
+		if (!(option_mask32 & OPT_USE_ICMP)) {
+			out = outdata;
+			len -= sizeof(*outudp);
+			set_nport(&dest_lsa->u.sa, htons(port + seq));
+		}
+	}
+
+	res = xsendto(sndsock, out, len, &dest_lsa->u.sa, dest_lsa->len);
+	if (res != len)
+		bb_info_msg("sent %d octets, ret=%d", len, res);
+}
+
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+static const char *
+pr_type(unsigned char t)
+{
+	static const char *const ttab[] = {
+	"Echo Reply",   "ICMP 1",       "ICMP 2",       "Dest Unreachable",
+	"Source Quench", "Redirect",    "ICMP 6",       "ICMP 7",
+	"Echo",         "Router Advert", "Router Solicit", "Time Exceeded",
+	"Param Problem", "Timestamp",   "Timestamp Reply", "Info Request",
+	"Info Reply",   "Mask Request", "Mask Reply"
+	};
+# if ENABLE_TRACEROUTE6
+	static const char *const ttab6[] = {
+[0]	"Error", "Dest Unreachable", "Packet Too Big", "Time Exceeded",
+[4]	"Param Problem",
+[8]	"Echo Request", "Echo Reply", "Membership Query", "Membership Report",
+[12]	"Membership Reduction", "Router Solicit", "Router Advert", "Neighbor Solicit",
+[16]	"Neighbor Advert", "Redirect",
+	};
+
+	if (dest_lsa->u.sa.sa_family == AF_INET6) {
+		if (t < 5)
+			return ttab6[t];
+		if (t < 128 || t > ND_REDIRECT)
+			return "OUT-OF-RANGE";
+		return ttab6[(t & 63) + 8];
+	}
+# endif
+	if (t >= ARRAY_SIZE(ttab))
+		return "OUT-OF-RANGE";
+
+	return ttab[t];
+}
+#endif
+
+#if !ENABLE_FEATURE_TRACEROUTE_VERBOSE
+#define packet4_ok(read_len, from, seq) \
+	packet4_ok(read_len, seq)
+#endif
+static int
+packet4_ok(int read_len, const struct sockaddr_in *from, int seq)
+{
+	const struct icmp *icp;
+	unsigned char type, code;
+	int hlen;
+	const struct ip *ip;
+
+	ip = (struct ip *) recv_pkt;
+	hlen = ip->ip_hl << 2;
+	if (read_len < hlen + ICMP_MINLEN) {
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+		if (verbose)
+			printf("packet too short (%d bytes) from %s\n", read_len,
+				inet_ntoa(from->sin_addr));
+#endif
+		return 0;
+	}
+	read_len -= hlen;
+	icp = (struct icmp *)(recv_pkt + hlen);
+	type = icp->icmp_type;
+	code = icp->icmp_code;
+	/* Path MTU Discovery (RFC1191) */
+	pmtu = 0;
+	if (code == ICMP_UNREACH_NEEDFRAG)
+		pmtu = ntohs(icp->icmp_nextmtu);
+
+	if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS)
+	 || type == ICMP_UNREACH
+	 || type == ICMP_ECHOREPLY
+	) {
+		const struct ip *hip;
+		const struct udphdr *up;
+
+		hip = &icp->icmp_ip;
+		hlen = hip->ip_hl << 2;
+		if (option_mask32 & OPT_USE_ICMP) {
+			struct icmp *hicmp;
+
+			/* XXX */
+			if (type == ICMP_ECHOREPLY
+			 && icp->icmp_id == htons(ident)
+			 && icp->icmp_seq == htons(seq)
+			) {
+				return ICMP_UNREACH_PORT+1;
+			}
+
+			hicmp = (struct icmp *)((unsigned char *)hip + hlen);
+			if (hlen + SIZEOF_ICMP_HDR <= read_len
+			 && hip->ip_p == IPPROTO_ICMP
+			 && hicmp->icmp_id == htons(ident)
+			 && hicmp->icmp_seq == htons(seq)
+			) {
+				return (type == ICMP_TIMXCEED ? -1 : code + 1);
+			}
+		} else {
+			up = (struct udphdr *)((char *)hip + hlen);
+			if (hlen + 12 <= read_len
+			 && hip->ip_p == IPPROTO_UDP
+// Off: since we do not form the entire IP packet,
+// but defer it to kernel, we can't set source port,
+// and thus can't check it here in the reply
+			/* && up->source == htons(ident) */
+			 && up->dest == htons(port + seq)
+			) {
+				return (type == ICMP_TIMXCEED ? -1 : code + 1);
+			}
+		}
+	}
+#if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+	if (verbose) {
+		int i;
+		uint32_t *lp = (uint32_t *)&icp->icmp_ip;
+
+		printf("\n%d bytes from %s to "
+		       "%s: icmp type %d (%s) code %d\n",
+			read_len, inet_ntoa(from->sin_addr),
+			inet_ntoa(ip->ip_dst),
+			type, pr_type(type), icp->icmp_code);
+		for (i = 4; i < read_len; i += sizeof(*lp))
+			printf("%2d: x%8.8x\n", i, *lp++);
+	}
+#endif
+	return 0;
+}
+
+#if ENABLE_TRACEROUTE6
+# if !ENABLE_FEATURE_TRACEROUTE_VERBOSE
+#define packet_ok(read_len, from_lsa, to, seq) \
+	packet_ok(read_len, from_lsa, seq)
+# endif
+static int
+packet_ok(int read_len, len_and_sockaddr *from_lsa,
+			struct sockaddr *to,
+			int seq)
+{
+	const struct icmp6_hdr *icp;
+	unsigned char type, code;
+
+	if (from_lsa->u.sa.sa_family == AF_INET)
+		return packet4_ok(read_len, &from_lsa->u.sin, seq);
+
+	icp = (struct icmp6_hdr *) recv_pkt;
+
+	type = icp->icmp6_type;
+	code = icp->icmp6_code;
+
+	if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT)
+	 || type == ICMP6_DST_UNREACH
+	) {
+		struct ip6_hdr *hip;
+		struct udphdr *up;
+		int nexthdr;
+
+		hip = (struct ip6_hdr *)(icp + 1);
+		up  = (struct udphdr *) (hip + 1);
+		nexthdr = hip->ip6_nxt;
+
+		if (nexthdr == IPPROTO_FRAGMENT) {
+			nexthdr = *(unsigned char*)up;
+			up++;
+		}
+		if (nexthdr == IPPROTO_UDP) {
+			struct outdata6_t *pkt;
+
+			pkt = (struct outdata6_t *) (up + 1);
+
+			if (ntohl(pkt->ident6) == ident
+			 && ntohl(pkt->seq6) == seq
+			) {
+				return (type == ICMP6_TIME_EXCEEDED ? -1 : (code<<8)+1);
+			}
+		}
+	}
+
+# if ENABLE_FEATURE_TRACEROUTE_VERBOSE
+	if (verbose) {
+		unsigned char *p;
+		char pa1[MAXHOSTNAMELEN];
+		char pa2[MAXHOSTNAMELEN];
+		int i;
+
+		p = (unsigned char *) (icp + 1);
+
+		printf("\n%d bytes from %s to "
+		       "%s: icmp type %d (%s) code %d\n",
+			read_len,
+			inet_ntop(AF_INET6, &from_lsa->u.sin6.sin6_addr, pa1, sizeof(pa1)),
+			inet_ntop(AF_INET6, &((struct sockaddr_in6*)to)->sin6_addr, pa2, sizeof(pa2)),
+			type, pr_type(type), icp->icmp6_code);
+
+		read_len -= sizeof(struct icmp6_hdr);
+		for (i = 0; i < read_len; i++) {
+			if (i % 16 == 0)
+				printf("%04x:", i);
+			if (i % 4 == 0)
+				bb_putchar(' ');
+			printf("%02x", p[i]);
+			if ((i % 16 == 15) && (i + 1 < read_len))
+				bb_putchar('\n');
+		}
+		bb_putchar('\n');
+	}
+# endif
+
+	return 0;
+}
+#else /* !ENABLE_TRACEROUTE6 */
+static ALWAYS_INLINE int
+packet_ok(int read_len,
+		len_and_sockaddr *from_lsa IF_NOT_FEATURE_TRACEROUTE_VERBOSE(UNUSED_PARAM),
+		struct sockaddr *to UNUSED_PARAM,
+		int seq)
+{
+	return packet4_ok(read_len, &from_lsa->u.sin, seq);
+}
+#endif
+
+/*
+ * Construct an Internet address representation.
+ * If the -n flag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+static void
+print_inetname(const struct sockaddr *from)
+{
+	char *ina = xmalloc_sockaddr2dotted_noport(from);
+
+	if (option_mask32 & OPT_ADDR_NUM) {
+		printf("  %s", ina);
+	} else {
+		char *n = NULL;
+
+		if (from->sa_family != AF_INET
+		 || ((struct sockaddr_in*)from)->sin_addr.s_addr != INADDR_ANY
+		) {
+			/* Try to reverse resolve if it is not 0.0.0.0 */
+			n = xmalloc_sockaddr2host_noport((struct sockaddr*)from);
+		}
+		printf("  %s (%s)", (n ? n : ina), ina);
+		free(n);
+	}
+	free(ina);
+}
+
+static void
+print(int read_len, const struct sockaddr *from, const struct sockaddr *to)
+{
+	print_inetname(from);
+
+	if (verbose) {
+		char *ina = xmalloc_sockaddr2dotted_noport(to);
+#if ENABLE_TRACEROUTE6
+		if (to->sa_family == AF_INET6) {
+			read_len -= sizeof(struct ip6_hdr);
+		} else
+#endif
+		{
+			struct ip *ip4packet = (struct ip*)recv_pkt;
+			read_len -= ip4packet->ip_hl << 2;
+		}
+		printf(" %d bytes to %s", read_len, ina);
+		free(ina);
+	}
+}
+
+static void
+print_delta_ms(unsigned t1p, unsigned t2p)
+{
+	unsigned tt = t2p - t1p;
+	printf("  %u.%03u ms", tt / 1000, tt % 1000);
+}
+
+/*
+ * Usage: [-dFIlnrvx] [-g gateway] [-i iface] [-f first_ttl]
+ * [-m max_ttl] [ -p port] [-q nqueries] [-s src_addr] [-t tos]
+ * [-w waittime] [-z pausemsecs] host [packetlen]"
+ */
+static int
+common_traceroute_main(int op, char **argv)
+{
+	int minpacket;
+	int tos = 0;
+	int max_ttl = 30;
+	int nprobes = 3;
+	int first_ttl = 1;
+	unsigned pausemsecs = 0;
+	char *source;
+	char *device;
+	char *tos_str;
+	char *max_ttl_str;
+	char *port_str;
+	char *nprobes_str;
+	char *waittime_str;
+	char *pausemsecs_str;
+	char *first_ttl_str;
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+	llist_t *source_route_list = NULL;
+	int lsrr = 0;
+#endif
+#if ENABLE_TRACEROUTE6
+	sa_family_t af;
+#else
+	enum { af = AF_INET };
+#endif
+	int ttl;
+	int seq;
+	len_and_sockaddr *from_lsa;
+	struct sockaddr *lastaddr;
+	struct sockaddr *to;
+
+	INIT_G();
+
+	/* minimum 1 arg */
+	opt_complementary = "-1:x-x" IF_FEATURE_TRACEROUTE_SOURCE_ROUTE(":g::");
+	op |= getopt32(argv, OPT_STRING
+		, &tos_str, &device, &max_ttl_str, &port_str, &nprobes_str
+		, &source, &waittime_str, &pausemsecs_str, &first_ttl_str
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+		, &source_route_list
+#endif
+	);
+	argv += optind;
+
+#if 0 /* IGNORED */
+	if (op & OPT_IP_CHKSUM)
+		bb_error_msg("warning: ip checksums disabled");
+#endif
+	if (op & OPT_TOS)
+		tos = xatou_range(tos_str, 0, 255);
+	if (op & OPT_MAX_TTL)
+		max_ttl = xatou_range(max_ttl_str, 1, 255);
+	if (op & OPT_PORT)
+		port = xatou16(port_str);
+	if (op & OPT_NPROBES)
+		nprobes = xatou_range(nprobes_str, 1, INT_MAX);
+	if (op & OPT_SOURCE) {
+		/*
+		 * set the ip source address of the outbound
+		 * probe (e.g., on a multi-homed host).
+		 */
+		if (getuid() != 0)
+			bb_error_msg_and_die(bb_msg_you_must_be_root);
+	}
+	if (op & OPT_WAITTIME)
+		waittime = xatou_range(waittime_str, 1, 24 * 60 * 60);
+	if (op & OPT_PAUSE_MS)
+		pausemsecs = xatou_range(pausemsecs_str, 0, 60 * 60 * 1000);
+	if (op & OPT_FIRST_TTL)
+		first_ttl = xatou_range(first_ttl_str, 1, max_ttl);
+
+#if ENABLE_FEATURE_TRACEROUTE_SOURCE_ROUTE
+	if (source_route_list) {
+		while (source_route_list) {
+			len_and_sockaddr *lsa;
+
+			if (lsrr >= NGATEWAYS)
+				bb_error_msg_and_die("no more than %d gateways", NGATEWAYS);
+			lsa = xhost_and_af2sockaddr(llist_pop(&source_route_list), 0, AF_INET);
+			gwlist[lsrr] = lsa->u.sin.sin_addr.s_addr;
+			free(lsa);
+			++lsrr;
+		}
+		optlen = (lsrr + 1) * sizeof(gwlist[0]);
+	}
+#endif
+
+	/* Process destination and optional packet size */
+	minpacket = sizeof(*outip) + SIZEOF_ICMP_HDR + sizeof(*outdata) + optlen;
+	if (!(op & OPT_USE_ICMP))
+		minpacket += sizeof(*outudp) - SIZEOF_ICMP_HDR;
+#if ENABLE_TRACEROUTE6
+	af = AF_UNSPEC;
+	if (op & OPT_IPV4)
+		af = AF_INET;
+	if (op & OPT_IPV6)
+		af = AF_INET6;
+	dest_lsa = xhost_and_af2sockaddr(argv[0], port, af);
+	af = dest_lsa->u.sa.sa_family;
+	if (af == AF_INET6)
+		minpacket = sizeof(struct outdata6_t);
+#else
+	dest_lsa = xhost2sockaddr(argv[0], port);
+#endif
+	packlen = minpacket;
+	if (argv[1])
+		packlen = xatoul_range(argv[1], minpacket, 32 * 1024);
+
+	/* Ensure the socket fds won't be 0, 1 or 2 */
+	bb_sanitize_stdio();
+
+#if ENABLE_TRACEROUTE6
+	if (af == AF_INET6) {
+		xmove_fd(xsocket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6), rcvsock);
+# ifdef IPV6_RECVPKTINFO
+		setsockopt(rcvsock, SOL_IPV6, IPV6_RECVPKTINFO,
+				&const_int_1, sizeof(const_int_1));
+		setsockopt(rcvsock, SOL_IPV6, IPV6_2292PKTINFO,
+				&const_int_1, sizeof(const_int_1));
+# else
+		setsockopt(rcvsock, SOL_IPV6, IPV6_PKTINFO,
+				&const_int_1, sizeof(const_int_1));
+# endif
+	} else
+#endif
+	{
+		xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), rcvsock);
+	}
+
+#if TRACEROUTE_SO_DEBUG
+	if (op & OPT_DEBUG)
+		setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
+				&const_int_1, sizeof(const_int_1));
+#endif
+	if (op & OPT_BYPASS_ROUTE)
+		setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
+				&const_int_1, sizeof(const_int_1));
+
+#if ENABLE_TRACEROUTE6
+	if (af == AF_INET6) {
+		static const int two = 2;
+		if (setsockopt(rcvsock, SOL_RAW, IPV6_CHECKSUM, &two, sizeof(two)) < 0)
+			bb_perror_msg_and_die("setsockopt RAW_CHECKSUM");
+		xmove_fd(xsocket(af, SOCK_DGRAM, 0), sndsock);
+	} else
+#endif
+	{
+		if (op & OPT_USE_ICMP)
+			xmove_fd(xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP), sndsock);
+		else
+			xmove_fd(xsocket(AF_INET, SOCK_DGRAM, 0), sndsock);
+#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;
+			++lsrr;
+
+			/* force 4 byte alignment */
+			optlist[0] = IPOPT_NOP;
+			/* loose source route option */
+			optlist[1] = IPOPT_LSRR;
+			size = lsrr * sizeof(gwlist[0]);
+			optlist[2] = size + 3;
+			/* pointer to LSRR addresses */
+			optlist[3] = IPOPT_MINOFF;
+			memcpy(optlist + 4, gwlist, size);
+
+			if (setsockopt(sndsock, IPPROTO_IP, IP_OPTIONS,
+					(char *)optlist, size + sizeof(gwlist[0])) < 0) {
+				bb_perror_msg_and_die("IP_OPTIONS");
+			}
+		}
+#endif
+	}
+
+#ifdef SO_SNDBUF
+	if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, &packlen, sizeof(packlen)) < 0) {
+		bb_perror_msg_and_die("SO_SNDBUF");
+	}
+#endif
+#ifdef IP_TOS
+	if ((op & OPT_TOS) && setsockopt(sndsock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) {
+		bb_perror_msg_and_die("setsockopt tos %d", tos);
+	}
+#endif
+#ifdef IP_DONTFRAG
+	if (op & OPT_DONT_FRAGMNT)
+		setsockopt(sndsock, IPPROTO_IP, IP_DONTFRAG,
+				&const_int_1, sizeof(const_int_1));
+#endif
+#if TRACEROUTE_SO_DEBUG
+	if (op & OPT_DEBUG)
+		setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+				&const_int_1, sizeof(const_int_1));
+#endif
+	if (op & OPT_BYPASS_ROUTE)
+		setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+				&const_int_1, sizeof(const_int_1));
+
+	outip = xzalloc(packlen);
+
+	ident = getpid();
+
+	if (af == AF_INET) {
+		if (op & OPT_USE_ICMP) {
+			ident |= 0x8000;
+			outicmp->icmp_type = ICMP_ECHO;
+			outicmp->icmp_id = htons(ident);
+			outdata = (struct outdata_t *)((char *)outicmp + SIZEOF_ICMP_HDR);
+		} else {
+			outdata = (struct outdata_t *)(outudp + 1);
+		}
+	}
+
+	if (op & OPT_DEVICE) /* hmm, do we need error check? */
+		setsockopt_bindtodevice(sndsock, device);
+
+	if (op & OPT_SOURCE) {
+#if ENABLE_TRACEROUTE6
+// TODO: need xdotted_and_af2sockaddr?
+		len_and_sockaddr *source_lsa = xhost_and_af2sockaddr(source, 0, af);
+#else
+		len_and_sockaddr *source_lsa = xdotted2sockaddr(source, 0);
+#endif
+		/* Ping4 does this (why?) */
+		if (af == AF_INET)
+			if (setsockopt(sndsock, IPPROTO_IP, IP_MULTICAST_IF,
+					&source_lsa->u.sa, source_lsa->len))
+				bb_error_msg_and_die("can't set multicast source interface");
+//TODO: we can query source port we bound to,
+// and check it in replies... if we care enough
+		xbind(sndsock, &source_lsa->u.sa, source_lsa->len);
+		free(source_lsa);
+	}
+#if ENABLE_TRACEROUTE6
+	else if (af == AF_INET6) {
+//TODO: why we don't do it for IPv4?
+		len_and_sockaddr *source_lsa;
+
+		int probe_fd = xsocket(af, SOCK_DGRAM, 0);
+		if (op & OPT_DEVICE)
+			setsockopt_bindtodevice(probe_fd, device);
+		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->u.sa, htons(port));
+
+		/* read IP and port */
+		source_lsa = get_sock_lsa(probe_fd);
+		if (source_lsa == NULL)
+			bb_error_msg_and_die("can't get probe addr");
+
+		close(probe_fd);
+
+		/* bind our sockets to this IP (but not port) */
+		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);
+
+		free(source_lsa);
+	}
+#endif
+
+	/* Revert to non-privileged user after opening sockets */
+	xsetgid(getgid());
+	xsetuid(getuid());
+
+	printf("traceroute to %s (%s)", argv[0],
+			xmalloc_sockaddr2dotted_noport(&dest_lsa->u.sa));
+	if (op & OPT_SOURCE)
+		printf(" from %s", source);
+	printf(", %d hops max, %d byte packets\n", max_ttl, packlen);
+
+	from_lsa = dup_sockaddr(dest_lsa);
+	lastaddr = xzalloc(dest_lsa->len);
+	to = xzalloc(dest_lsa->len);
+	seq = 0;
+	for (ttl = first_ttl; ttl <= max_ttl; ++ttl) {
+		int probe;
+		int unreachable = 0; /* counter */
+		int gotlastaddr = 0; /* flags */
+		int got_there = 0;
+
+		printf("%2d", ttl);
+		for (probe = 0; probe < nprobes; ++probe) {
+			int read_len;
+			unsigned t1;
+			unsigned t2;
+			int left_ms;
+			struct ip *ip;
+
+			fflush_all();
+			if (probe != 0 && pausemsecs > 0)
+				usleep(pausemsecs * 1000);
+
+			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 */
+
+				if (read_len < 0)
+					continue;
+				icmp_code = packet_ok(read_len, from_lsa, to, seq);
+				/* Skip short packet */
+				if (icmp_code == 0)
+					continue;
+
+				if (!gotlastaddr
+				 || (memcmp(lastaddr, &from_lsa->u.sa, from_lsa->len) != 0)
+				) {
+					print(read_len, &from_lsa->u.sa, to);
+					memcpy(lastaddr, &from_lsa->u.sa, from_lsa->len);
+					gotlastaddr = 1;
+				}
+
+				print_delta_ms(t1, t2);
+				ip = (struct ip *)recv_pkt;
+
+				if (from_lsa->u.sa.sa_family == AF_INET)
+					if (op & OPT_TTL_FLAG)
+						printf(" (%d)", ip->ip_ttl);
+
+				/* time exceeded in transit */
+				if (icmp_code == -1)
+					break;
+				icmp_code--;
+				switch (icmp_code) {
+#if ENABLE_TRACEROUTE6
+				case ICMP6_DST_UNREACH_NOPORT << 8:
+					got_there = 1;
+					break;
+#endif
+				case ICMP_UNREACH_PORT:
+					if (ip->ip_ttl <= 1)
+						printf(" !");
+					got_there = 1;
+					break;
+
+				case ICMP_UNREACH_NET:
+#if ENABLE_TRACEROUTE6 && (ICMP6_DST_UNREACH_NOROUTE != ICMP_UNREACH_NET)
+				case ICMP6_DST_UNREACH_NOROUTE << 8:
+#endif
+					printf(" !N");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_HOST:
+#if ENABLE_TRACEROUTE6
+				case ICMP6_DST_UNREACH_ADDR << 8:
+#endif
+					printf(" !H");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_PROTOCOL:
+					printf(" !P");
+					got_there = 1;
+					break;
+				case ICMP_UNREACH_NEEDFRAG:
+					printf(" !F-%d", pmtu);
+					++unreachable;
+					break;
+				case ICMP_UNREACH_SRCFAIL:
+#if ENABLE_TRACEROUTE6
+				case ICMP6_DST_UNREACH_ADMIN << 8:
+#endif
+					printf(" !S");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_FILTER_PROHIB:
+				case ICMP_UNREACH_NET_PROHIB:   /* misuse */
+					printf(" !A");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_HOST_PROHIB:
+					printf(" !C");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_HOST_PRECEDENCE:
+					printf(" !V");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+					printf(" !C");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_NET_UNKNOWN:
+				case ICMP_UNREACH_HOST_UNKNOWN:
+					printf(" !U");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_ISOLATED:
+					printf(" !I");
+					++unreachable;
+					break;
+				case ICMP_UNREACH_TOSNET:
+				case ICMP_UNREACH_TOSHOST:
+					printf(" !T");
+					++unreachable;
+					break;
+				default:
+					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)
+		) {
+			break;
+		}
+	}
+
+	return 0;
+}
+
+int traceroute_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int traceroute_main(int argc UNUSED_PARAM, char **argv)
+{
+	return common_traceroute_main(0, argv);
+}
+
+#if ENABLE_TRACEROUTE6
+int traceroute6_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int traceroute6_main(int argc UNUSED_PARAM, char **argv)
+{
+	return common_traceroute_main(OPT_IPV6, argv);
+}
+#endif
diff --git a/busybox-1.19.3/networking/tunctl.c b/busybox-1.19.3/networking/tunctl.c
new file mode 100644
index 0000000..3a0870e
--- /dev/null
+++ b/busybox-1.19.3/networking/tunctl.c
@@ -0,0 +1,157 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tun devices controller
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Original code:
+ *      Jeff Dike
+ *
+ * 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>
+#include "libbb.h"
+
+/* TUNSETGROUP appeared in 2.6.23 */
+#ifndef TUNSETGROUP
+#define TUNSETGROUP _IOW('T', 206, int)
+#endif
+
+#define IOCTL(a, b, c) ioctl_or_perror_and_die(a, b, c, NULL)
+
+#if 1
+
+int tunctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tunctl_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct ifreq ifr;
+	int fd;
+	const char *opt_name = "tap%d";
+	const char *opt_device = "/dev/net/tun";
+#if ENABLE_FEATURE_TUNCTL_UG
+	const char *opt_user, *opt_group;
+	long user = -1, group = -1;
+#endif
+	unsigned opts;
+
+	enum {
+		OPT_f = 1 << 0, // control device name (/dev/net/tun)
+		OPT_t = 1 << 1, // create named interface
+		OPT_d = 1 << 2, // delete named interface
+#if ENABLE_FEATURE_TUNCTL_UG
+		OPT_u = 1 << 3, // set new interface owner
+		OPT_g = 1 << 4, // set new interface group
+		OPT_b = 1 << 5, // brief output
+#endif
+	};
+
+	opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d
+	opts = getopt32(argv, "f:t:d:" IF_FEATURE_TUNCTL_UG("u:g:b"),
+			&opt_device, &opt_name, &opt_name
+			IF_FEATURE_TUNCTL_UG(, &opt_user, &opt_group));
+
+	// select device
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	strncpy_IFNAMSIZ(ifr.ifr_name, opt_name);
+
+	// open device
+	fd = xopen(opt_device, O_RDWR);
+	IOCTL(fd, TUNSETIFF, (void *)&ifr);
+
+	// delete?
+	if (opts & OPT_d) {
+		IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)0);
+		bb_info_msg("Set '%s' %spersistent", ifr.ifr_name, "non");
+		return EXIT_SUCCESS;
+	}
+
+	// create
+#if ENABLE_FEATURE_TUNCTL_UG
+	if (opts & OPT_g) {
+		group = xgroup2gid(opt_group);
+		IOCTL(fd, TUNSETGROUP, (void *)(uintptr_t)group);
+	} else
+		user = geteuid();
+	if (opts & OPT_u)
+		user = xuname2uid(opt_user);
+	IOCTL(fd, TUNSETOWNER, (void *)(uintptr_t)user);
+#endif
+	IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)1);
+
+	// show info
+#if ENABLE_FEATURE_TUNCTL_UG
+	if (opts & OPT_b) {
+		puts(ifr.ifr_name);
+	} else {
+		printf("Set '%s' %spersistent", ifr.ifr_name, "");
+		printf(" and owned by uid %ld", user);
+		if (group != -1)
+			printf(" gid %ld", group);
+		bb_putchar('\n');
+	}
+#else
+	puts(ifr.ifr_name);
+#endif
+	return EXIT_SUCCESS;
+}
+
+#else
+
+/* -210 bytes: */
+
+int tunctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int tunctl_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct ifreq ifr;
+	int fd;
+	const char *opt_name = "tap%d";
+	const char *opt_device = "/dev/net/tun";
+	unsigned opts;
+
+	enum {
+		OPT_f = 1 << 0, // control device name (/dev/net/tun)
+		OPT_t = 1 << 1, // create named interface
+		OPT_d = 1 << 2, // delete named interface
+	};
+
+	opt_complementary = "=0:t--d:d--t"; // no arguments; t ^ d
+	opts = getopt32(argv, "f:t:d:u:g:b", // u, g, b accepted and ignored
+			&opt_device, &opt_name, &opt_name, NULL, NULL);
+
+	// set interface name
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+	strncpy_IFNAMSIZ(ifr.ifr_name, opt_name);
+
+	// open device
+	fd = xopen(opt_device, O_RDWR);
+	IOCTL(fd, TUNSETIFF, (void *)&ifr);
+
+	// create or delete interface
+	IOCTL(fd, TUNSETPERSIST, (void *)(uintptr_t)(0 == (opts & OPT_d)));
+
+	return EXIT_SUCCESS;
+}
+
+#endif
diff --git a/busybox-1.19.3/networking/udhcp/Config.src b/busybox-1.19.3/networking/udhcp/Config.src
new file mode 100644
index 0000000..6bfa398
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/Config.src
@@ -0,0 +1,154 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+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.
+
+config DHCPRELAY
+	bool "dhcprelay"
+	default y
+	depends on UDHCPD
+	help
+	  dhcprelay listens for dhcp requests on one or more interfaces
+	  and forwards these requests to a different interface or dhcp
+	  server.
+
+config DUMPLEASES
+	bool "Lease display utility (dumpleases)"
+	default y
+	depends on UDHCPD
+	help
+	  dumpleases displays the leases written out by the udhcpd server.
+	  Lease times are stored in the file by time remaining in lease, or
+	  by the absolute time that it expires in seconds from epoch.
+
+config FEATURE_UDHCPD_WRITE_LEASES_EARLY
+	bool "Rewrite the lease file at every new acknowledge"
+	default y
+	depends on UDHCPD
+	help
+	  If selected, udhcpd will write a new file with leases every
+	  time a new lease has been accepted, thus eliminating the need
+	  to send SIGUSR1 for the initial writing or updating. Any timed
+	  rewriting remains undisturbed.
+
+config FEATURE_UDHCPD_BASE_IP_ON_MAC
+	bool "Select IP address based on client MAC"
+	default n
+	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"
+	default "/var/lib/misc/udhcpd.leases"
+	depends on UDHCPD
+	help
+	  udhcpd stores addresses in a lease file. This is the absolute path
+	  of the file. Normally it is safe to leave it untouched.
+
+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.
+
+	  The udhcp client negotiates a lease with the DHCP server and
+	  runs a script when a lease is obtained or lost.
+
+config FEATURE_UDHCPC_ARPING
+	bool "Verify that the offered address is free, using ARP ping"
+	default y
+	depends on UDHCPC
+	help
+	  If selected, udhcpc will send ARP probes and make sure
+	  the offered address is really not in use by anyone. The client
+	  will DHCPDECLINE the offer if the address is in use,
+	  and restart the discover process.
+
+config FEATURE_UDHCP_PORT
+	bool "Enable '-P port' option for udhcpd and udhcpc"
+	default n
+	depends on UDHCPD || UDHCPC
+	help
+	  At the cost of ~300 bytes, enables -P port option.
+	  This feature is typically not needed.
+
+config UDHCP_DEBUG
+	int "Maximum verbosity level for udhcp applets (0..9)"
+	default 9
+	range 0 9
+	depends on UDHCPD || UDHCPC || DHCPRELAY
+	help
+	  Verbosity can be increased with multiple -v options.
+	  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.
+
+config FEATURE_UDHCP_RFC3397
+	bool "Support for RFC3397 domain search (experimental)"
+	default y
+	depends on UDHCPD || UDHCPC
+	help
+	  If selected, both client and server will support passing of domain
+	  search lists via option 119, specified in RFC 3397,
+	  and SIP servers option 120, specified in RFC 3361.
+
+config FEATURE_UDHCP_8021Q
+	bool "Support 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"
+	depends on UDHCPC
+	help
+	  This script is called after udhcpc receives an answer. See
+	  examples/udhcp for a working example. Normally it is safe
+	  to leave this untouched.
+
+config UDHCPC_SLACK_FOR_BUGGY_SERVERS
+	int "DHCP options slack buffer size"
+	default 80
+	range 0 924
+	depends on UDHCPD || UDHCPC
+	help
+	  Some buggy DHCP servers send DHCP offer packets with option
+	  field larger than we expect (which might also be considered a
+	  buffer overflow attempt). These packets are normally discarded.
+	  If circumstances beyond your control force you to support such
+	  servers, this may help. The upper limit (924) makes dhcpc accept
+	  even 1500 byte packets (maximum-sized ethernet packets).
+
+	  This option does not make dhcp[cd] emit non-standard
+	  sized packets.
+
+	  Known buggy DHCP servers:
+	  3Com OfficeConnect Remote 812 ADSL Router:
+	    seems to confuse maximum allowed UDP packet size with
+	    maximum size of entire IP packet, and sends packets which are
+	    28 bytes too large.
+	  Seednet (ISP) VDSL: sends packets 2 bytes too large.
diff --git a/busybox-1.19.3/networking/udhcp/Kbuild.src b/busybox-1.19.3/networking/udhcp/Kbuild.src
new file mode 100644
index 0000000..b8767ba
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/Kbuild.src
@@ -0,0 +1,21 @@
+# 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:=
+
+INSERT
+
+lib-$(CONFIG_UDHCPC)     += common.o packet.o signalpipe.o socket.o
+lib-$(CONFIG_UDHCPD)     += common.o packet.o signalpipe.o socket.o
+
+lib-$(CONFIG_UDHCPC)     += dhcpc.o
+lib-$(CONFIG_UDHCPD)     += dhcpd.o arpping.o files.o leases.o static_leases.o
+lib-$(CONFIG_DUMPLEASES) += dumpleases.o
+lib-$(CONFIG_DHCPRELAY)  += dhcprelay.o
+
+lib-$(CONFIG_FEATURE_UDHCPC_ARPING) += arpping.o
+lib-$(CONFIG_FEATURE_UDHCP_RFC3397) += domain_codec.o
diff --git a/busybox-1.19.3/networking/udhcp/arpping.c b/busybox-1.19.3/networking/udhcp/arpping.c
new file mode 100644
index 0000000..ff63478
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/arpping.c
@@ -0,0 +1,128 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mostly stolen from: dhcpcd - DHCP client daemon
+ * by Yoichi Hariguchi <yoichi@fore.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+
+#include "common.h"
+#include "dhcpd.h"
+
+struct arpMsg {
+	/* Ethernet header */
+	uint8_t  h_dest[6];     /* 00 destination ether addr */
+	uint8_t  h_source[6];   /* 06 source ether addr */
+	uint16_t h_proto;       /* 0c packet type ID field */
+
+	/* ARP packet */
+	uint16_t htype;         /* 0e hardware type (must be ARPHRD_ETHER) */
+	uint16_t ptype;         /* 10 protocol type (must be ETH_P_IP) */
+	uint8_t  hlen;          /* 12 hardware address length (must be 6) */
+	uint8_t  plen;          /* 13 protocol address length (must be 4) */
+	uint16_t operation;     /* 14 ARP opcode */
+	uint8_t  sHaddr[6];     /* 16 sender's hardware address */
+	uint8_t  sInaddr[4];    /* 1c sender's IP address */
+	uint8_t  tHaddr[6];     /* 20 target's hardware address */
+	uint8_t  tInaddr[4];    /* 26 target's IP address */
+	uint8_t  pad[18];       /* 2a pad for min. ethernet payload (60 bytes) */
+} PACKED;
+
+enum {
+	ARP_MSG_SIZE = 0x2a
+};
+
+/* Returns 1 if no reply received */
+int FAST_FUNC arpping(uint32_t test_nip,
+		const uint8_t *safe_mac,
+		uint32_t from_ip,
+		uint8_t *from_mac,
+		const char *interface)
+{
+	int timeout_ms;
+	struct pollfd pfd[1];
+#define s (pfd[0].fd)           /* socket */
+	int rv = 1;             /* "no reply received" yet */
+	struct sockaddr addr;   /* for interface name */
+	struct arpMsg arp;
+
+	s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ARP));
+	if (s == -1) {
+		bb_perror_msg(bb_msg_can_not_create_raw_socket);
+		return -1;
+	}
+
+	if (setsockopt_broadcast(s) == -1) {
+		bb_perror_msg("can't enable bcast on raw socket");
+		goto ret;
+	}
+
+	/* send arp request */
+	memset(&arp, 0, sizeof(arp));
+	memset(arp.h_dest, 0xff, 6);                    /* MAC DA */
+	memcpy(arp.h_source, from_mac, 6);              /* MAC SA */
+	arp.h_proto = htons(ETH_P_ARP);                 /* protocol type (Ethernet) */
+	arp.htype = htons(ARPHRD_ETHER);                /* hardware type */
+	arp.ptype = htons(ETH_P_IP);                    /* protocol type (ARP message) */
+	arp.hlen = 6;                                   /* hardware address length */
+	arp.plen = 4;                                   /* protocol address length */
+	arp.operation = htons(ARPOP_REQUEST);           /* ARP op code */
+	memcpy(arp.sHaddr, from_mac, 6);                /* source hardware address */
+	memcpy(arp.sInaddr, &from_ip, sizeof(from_ip)); /* source IP address */
+	/* tHaddr is zero-filled */                     /* target hardware address */
+	memcpy(arp.tInaddr, &test_nip, sizeof(test_nip));/* target IP address */
+
+	memset(&addr, 0, sizeof(addr));
+	safe_strncpy(addr.sa_data, interface, sizeof(addr.sa_data));
+	if (sendto(s, &arp, sizeof(arp), 0, &addr, sizeof(addr)) < 0) {
+		// TODO: error message? caller didn't expect us to fail,
+		// just returning 1 "no reply received" misleads it.
+		goto ret;
+	}
+
+	/* wait for arp reply, and check it */
+	timeout_ms = 2000;
+	do {
+		typedef uint32_t aliased_uint32_t FIX_ALIASING;
+		int r;
+		unsigned prevTime = monotonic_ms();
+
+		pfd[0].events = POLLIN;
+		r = safe_poll(pfd, 1, timeout_ms);
+		if (r < 0)
+			break;
+		if (r) {
+			r = safe_read(s, &arp, sizeof(arp));
+			if (r < 0)
+				break;
+
+			//log3("sHaddr %02x:%02x:%02x:%02x:%02x:%02x",
+			//	arp.sHaddr[0], arp.sHaddr[1], arp.sHaddr[2],
+			//	arp.sHaddr[3], arp.sHaddr[4], arp.sHaddr[5]);
+
+			if (r >= ARP_MSG_SIZE
+			 && arp.operation == htons(ARPOP_REPLY)
+			 /* don't check it: Linux doesn't return proper tHaddr (fixed in 2.6.24?) */
+			 /* && memcmp(arp.tHaddr, from_mac, 6) == 0 */
+			 && *(aliased_uint32_t*)arp.sInaddr == test_nip
+			) {
+				/* if ARP source MAC matches safe_mac
+				 * (which is client's MAC), then it's not a conflict
+				 * (client simply already has this IP and replies to ARPs!)
+				 */
+				if (!safe_mac || memcmp(safe_mac, arp.sHaddr, 6) != 0)
+					rv = 0;
+				//else log2("sHaddr == safe_mac");
+				break;
+			}
+		}
+		timeout_ms -= (unsigned)monotonic_ms() - prevTime;
+	} while (timeout_ms > 0);
+
+ ret:
+	close(s);
+	log1("%srp reply received for this address", rv ? "No a" : "A");
+	return rv;
+}
diff --git a/busybox-1.19.3/networking/udhcp/common.c b/busybox-1.19.3/networking/udhcp/common.c
new file mode 100644
index 0000000..70e3461
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/common.c
@@ -0,0 +1,535 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "common.h"
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+unsigned dhcp_verbose;
+#endif
+
+const uint8_t MAC_BCAST_ADDR[6] ALIGN2 = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/* Supported options are easily added here.
+ * See RFC2132 for more options.
+ * OPTION_REQ: these options are requested by udhcpc (unless -o).
+ */
+const struct dhcp_optflag dhcp_optflags[] = {
+	/* flags                                    code */
+	{ OPTION_IP                   | OPTION_REQ, 0x01 }, /* DHCP_SUBNET        */
+	{ OPTION_S32                              , 0x02 }, /* DHCP_TIME_OFFSET   */
+	{ OPTION_IP | OPTION_LIST     | OPTION_REQ, 0x03 }, /* DHCP_ROUTER        */
+//	{ OPTION_IP | OPTION_LIST                 , 0x04 }, /* DHCP_TIME_SERVER   */
+//	{ OPTION_IP | OPTION_LIST                 , 0x05 }, /* DHCP_NAME_SERVER   */
+	{ OPTION_IP | OPTION_LIST     | OPTION_REQ, 0x06 }, /* DHCP_DNS_SERVER    */
+//	{ 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_U16                              , 0x0d }, /* DHCP_BOOT_SIZE     */
+	{ OPTION_STRING               | 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           */
+	{ OPTION_IP                   | OPTION_REQ, 0x1c }, /* DHCP_BROADCAST     */
+	{ OPTION_IP_PAIR | OPTION_LIST            , 0x21 }, /* DHCP_ROUTES        */
+	{ OPTION_STRING                           , 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   */
+	{ OPTION_U32                              , 0x33 }, /* DHCP_LEASE_TIME    */
+	{ 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                           , 0x43 }, /* DHCP_BOOT_FILE     */
+//TODO: not a string, but a set of LASCII strings:
+//	{ OPTION_STRING                           , 0x4D }, /* DHCP_USER_CLASS    */
+#if ENABLE_FEATURE_UDHCP_RFC3397
+	{ OPTION_DNS_STRING | OPTION_LIST         , 0x77 }, /* DHCP_DOMAIN_SEARCH */
+	{ OPTION_SIP_SERVERS                      , 0x78 }, /* DHCP_SIP_SERVERS   */
+#endif
+	{ OPTION_STATIC_ROUTES                    , 0x79 }, /* DHCP_STATIC_ROUTES */
+#if ENABLE_FEATURE_UDHCP_8021Q
+	{ OPTION_U16                              , 0x84 }, /* DHCP_VLAN_ID       */
+	{ OPTION_U8                               , 0x85 }, /* DHCP_VLAN_PRIORITY */
+#endif
+	{ OPTION_STATIC_ROUTES                    , 0xf9 }, /* DHCP_MS_STATIC_ROUTES */
+	{ OPTION_STRING                           , 0xfc }, /* DHCP_WPAD          */
+
+	/* Options below have no match in dhcp_option_strings[],
+	 * are not passed to dhcpc scripts, and cannot be specified
+	 * with "option XXX YYY" syntax in dhcpd config file.
+	 * These entries are only used internally by udhcp[cd]
+	 * to correctly encode options into packets.
+	 */
+
+	{ OPTION_IP                               , 0x32 }, /* DHCP_REQUESTED_IP  */
+	{ OPTION_U8                               , 0x35 }, /* DHCP_MESSAGE_TYPE  */
+	{ OPTION_U16                              , 0x39 }, /* DHCP_MAX_SIZE      */
+//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 */
+};
+
+/* Used for converting options from incoming packets to env variables
+ * for udhcpc stript, and for setting options for udhcpd via
+ * "opt OPTION_NAME OPTION_VALUE" directives in udhcpd.conf file.
+ */
+/* Must match dhcp_optflags[] order */
+const char dhcp_option_strings[] ALIGN1 =
+	"subnet" "\0"      /* DHCP_SUBNET         */
+	"timezone" "\0"    /* DHCP_TIME_OFFSET    */
+	"router" "\0"      /* DHCP_ROUTER         */
+//	"timesrv" "\0"     /* DHCP_TIME_SERVER    */
+//	"namesrv" "\0"     /* DHCP_NAME_SERVER    */
+	"dns" "\0"         /* DHCP_DNS_SERVER     */
+//	"logsrv" "\0"      /* DHCP_LOG_SERVER     */
+//	"cookiesrv" "\0"   /* DHCP_COOKIE_SERVER  */
+	"lprsrv" "\0"      /* DHCP_LPR_SERVER     */
+	"hostname" "\0"    /* DHCP_HOST_NAME      */
+	"bootsize" "\0"    /* DHCP_BOOT_SIZE      */
+	"domain" "\0"      /* DHCP_DOMAIN_NAME    */
+	"swapsrv" "\0"     /* DHCP_SWAP_SERVER    */
+	"rootpath" "\0"    /* DHCP_ROOT_PATH      */
+	"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     */
+	"wins" "\0"        /* DHCP_WINS_SERVER    */
+	"lease" "\0"       /* DHCP_LEASE_TIME     */
+	"serverid" "\0"    /* DHCP_SERVER_ID      */
+	"message" "\0"     /* DHCP_ERR_MESSAGE    */
+	"tftp" "\0"        /* DHCP_TFTP_SERVER_NAME */
+	"bootfile" "\0"    /* DHCP_BOOT_FILE      */
+//	"userclass" "\0"   /* DHCP_USER_CLASS     */
+#if ENABLE_FEATURE_UDHCP_RFC3397
+	"search" "\0"      /* DHCP_DOMAIN_SEARCH  */
+// doesn't work in udhcpd.conf since OPTION_SIP_SERVERS
+// 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
+	"msstaticroutes""\0"/* DHCP_MS_STATIC_ROUTES */
+	"wpad" "\0"        /* DHCP_WPAD           */
+	;
+
+/* Lengths of the option types in binary form.
+ * Used by:
+ * udhcp_str2optset: to determine how many bytes to allocate.
+ * xmalloc_optname_optval: to estimate string length
+ * from binary option length: (option[LEN] / dhcp_option_lengths[opt_type])
+ * is the number of elements, multiply in by one element's string width
+ * (len_of_option_as_string[opt_type]) and you know how wide string you need.
+ */
+const uint8_t dhcp_option_lengths[] ALIGN1 = {
+	[OPTION_IP] =      4,
+	[OPTION_IP_PAIR] = 8,
+//	[OPTION_BOOLEAN] = 1,
+	[OPTION_STRING] =  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,
+#endif
+	[OPTION_U8] =      1,
+	[OPTION_U16] =     2,
+//	[OPTION_S16] =     2,
+	[OPTION_U32] =     4,
+	[OPTION_S32] =     4,
+	/* Just like OPTION_STRING, we use minimum length here */
+	[OPTION_STATIC_ROUTES] = 5,
+};
+
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+static void log_option(const char *pfx, const uint8_t *opt)
+{
+	if (dhcp_verbose >= 2) {
+		char buf[256 * 2 + 2];
+		*bin2hex(buf, (void*) (opt + OPT_DATA), opt[OPT_LEN]) = '\0';
+		bb_info_msg("%s: 0x%02x %s", pfx, opt[OPT_CODE], buf);
+	}
+}
+#else
+# define log_option(pfx, opt) ((void)0)
+#endif
+
+unsigned FAST_FUNC udhcp_option_idx(const char *name)
+{
+	int n = index_in_strings(dhcp_option_strings, name);
+	if (n >= 0)
+		return n;
+
+	{
+		char buf[sizeof(dhcp_option_strings)];
+		char *d = buf;
+		const char *s = dhcp_option_strings;
+		while (s < dhcp_option_strings + sizeof(dhcp_option_strings) - 2) {
+			*d++ = (*s == '\0' ? ' ' : *s);
+			s++;
+		}
+		*d = '\0';
+		bb_error_msg_and_die("unknown option '%s', known options: %s", name, buf);
+	}
+}
+
+/* Get an option with bounds checking (warning, result is not aligned) */
+uint8_t* FAST_FUNC udhcp_get_option(struct dhcp_packet *packet, int code)
+{
+	uint8_t *optionptr;
+	int len;
+	int rem;
+	int overload = 0;
+	enum {
+		FILE_FIELD101  = FILE_FIELD  * 0x101,
+		SNAME_FIELD101 = SNAME_FIELD * 0x101,
+	};
+
+	/* option bytes: [code][len][data1][data2]..[dataLEN] */
+	optionptr = packet->options;
+	rem = sizeof(packet->options);
+	while (1) {
+		if (rem <= 0) {
+			bb_error_msg("bad packet, malformed option field");
+			return NULL;
+		}
+		if (optionptr[OPT_CODE] == DHCP_PADDING) {
+			rem--;
+			optionptr++;
+			continue;
+		}
+		if (optionptr[OPT_CODE] == DHCP_END) {
+			if ((overload & FILE_FIELD101) == FILE_FIELD) {
+				/* can use packet->file, and didn't look at it yet */
+				overload |= FILE_FIELD101; /* "we looked at it" */
+				optionptr = packet->file;
+				rem = sizeof(packet->file);
+				continue;
+			}
+			if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
+				/* can use packet->sname, and didn't look at it yet */
+				overload |= SNAME_FIELD101; /* "we looked at it" */
+				optionptr = packet->sname;
+				rem = sizeof(packet->sname);
+				continue;
+			}
+			break;
+		}
+		len = 2 + optionptr[OPT_LEN];
+		rem -= len;
+		if (rem < 0)
+			continue; /* complain and return NULL */
+
+		if (optionptr[OPT_CODE] == code) {
+			log_option("Option found", optionptr);
+			return optionptr + OPT_DATA;
+		}
+
+		if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
+			overload |= optionptr[OPT_DATA];
+			/* fall through */
+		}
+		optionptr += len;
+	}
+
+	/* log3 because udhcpc uses it a lot - very noisy */
+	log3("Option 0x%02x not found", code);
+	return NULL;
+}
+
+/* Return the position of the 'end' option (no bounds checking) */
+int FAST_FUNC udhcp_end_option(uint8_t *optionptr)
+{
+	int i = 0;
+
+	while (optionptr[i] != DHCP_END) {
+		if (optionptr[i] != DHCP_PADDING)
+			i += optionptr[i + OPT_LEN] + OPT_DATA-1;
+		i++;
+	}
+	return i;
+}
+
+/* Add an option (supplied in binary form) to the options.
+ * Option format: [code][len][data1][data2]..[dataLEN]
+ */
+void FAST_FUNC udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
+{
+	unsigned len;
+	uint8_t *optionptr = packet->options;
+	unsigned end = udhcp_end_option(optionptr);
+
+	len = OPT_DATA + addopt[OPT_LEN];
+	/* end position + (option code/length + addopt length) + end option */
+	if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE) {
+//TODO: learn how to use overflow option if we exhaust packet->options[]
+		bb_error_msg("option 0x%02x did not fit into the packet",
+				addopt[OPT_CODE]);
+		return;
+	}
+	log_option("Adding option", addopt);
+	memcpy(optionptr + end, addopt, len);
+	optionptr[end + len] = DHCP_END;
+}
+
+/* Add an one to four byte option to a packet */
+void FAST_FUNC udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data)
+{
+	const struct dhcp_optflag *dh;
+
+	for (dh = dhcp_optflags; dh->code; dh++) {
+		if (dh->code == code) {
+			uint8_t option[6], len;
+
+			option[OPT_CODE] = code;
+			len = dhcp_option_lengths[dh->flags & OPTION_TYPE_MASK];
+			option[OPT_LEN] = len;
+			if (BB_BIG_ENDIAN)
+				data <<= 8 * (4 - len);
+			/* Assignment is unaligned! */
+			move_to_unaligned32(&option[OPT_DATA], data);
+			udhcp_add_binary_option(packet, option);
+			return;
+		}
+	}
+
+	bb_error_msg("can't add option 0x%02x", code);
+}
+
+/* Find option 'code' in opt_list */
+struct option_set* FAST_FUNC udhcp_find_option(struct option_set *opt_list, uint8_t code)
+{
+	while (opt_list && opt_list->data[OPT_CODE] < code)
+		opt_list = opt_list->next;
+
+	if (opt_list && opt_list->data[OPT_CODE] == code)
+		return opt_list;
+	return NULL;
+}
+
+/* Parse string to IP in network order */
+int FAST_FUNC udhcp_str2nip(const char *str, void *arg)
+{
+	len_and_sockaddr *lsa;
+
+	lsa = host_and_af2sockaddr(str, 0, AF_INET);
+	if (!lsa)
+		return 0;
+	*(uint32_t*)arg = lsa->u.sin.sin_addr.s_addr;
+	free(lsa);
+	return 1;
+}
+
+/* udhcp_str2optset:
+ * Parse string option representation to binary form and add it to opt_list.
+ * Called to parse "udhcpc -x OPTNAME:OPTVAL"
+ * and to parse udhcpd.conf's "opt OPTNAME OPTVAL" directives.
+ */
+/* helper for the helper */
+static char *allocate_tempopt_if_needed(
+		const struct dhcp_optflag *optflag,
+		char *buffer,
+		int *length_p)
+{
+	char *allocated = NULL;
+	if ((optflag->flags & OPTION_TYPE_MASK) == OPTION_BIN) {
+		const char *end;
+		allocated = xstrdup(buffer); /* more than enough */
+		end = hex2bin(allocated, buffer, 255);
+		if (errno)
+			bb_error_msg_and_die("malformed hex string '%s'", buffer);
+		*length_p = end - allocated;
+	}
+	return allocated;
+}
+/* helper: add an option to the opt_list */
+static NOINLINE void attach_option(
+		struct option_set **opt_list,
+		const struct dhcp_optflag *optflag,
+		char *buffer,
+		int length)
+{
+	struct option_set *existing, *new, **curr;
+	char *allocated = NULL;
+
+	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);
+#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);
+		}
+#endif
+		/* make a new option */
+		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, (allocated ? allocated : buffer), length);
+
+		curr = opt_list;
+		while (*curr && (*curr)->data[OPT_CODE] < optflag->code)
+			curr = &(*curr)->next;
+
+		new->next = *curr;
+		*curr = new;
+		goto ret;
+	}
+
+	if (optflag->flags & OPTION_LIST) {
+		unsigned old_len;
+
+		/* 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) {
+				/* 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);
+			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 */
+
+ ret:
+	free(allocated);
+}
+
+int FAST_FUNC udhcp_str2optset(const char *const_str, void *arg)
+{
+	struct option_set **opt_list = arg;
+	char *opt, *val, *endptr;
+	char *str;
+	const struct dhcp_optflag *optflag;
+	struct dhcp_optflag bin_optflag;
+	unsigned optcode;
+	int retval, length;
+	char buffer[8] ALIGNED(4);
+	uint16_t *result_u16 = (uint16_t *) buffer;
+	uint32_t *result_u32 = (uint32_t *) buffer;
+
+	/* Cheat, the only *const* str possible is "" */
+	str = (char *) const_str;
+	opt = strtok(str, " \t=");
+	if (!opt)
+		return 0;
+
+	optcode = bb_strtou(opt, NULL, 0);
+	if (!errno && optcode < 255) {
+		/* Raw (numeric) option code */
+		bin_optflag.flags = OPTION_BIN;
+		bin_optflag.code = optcode;
+		optflag = &bin_optflag;
+	} else {
+		optflag = &dhcp_optflags[udhcp_option_idx(opt)];
+	}
+
+	retval = 0;
+	do {
+		val = strtok(NULL, ", \t");
+		if (!val)
+			break;
+		length = dhcp_option_lengths[optflag->flags & OPTION_TYPE_MASK];
+		retval = 0;
+		opt = buffer; /* new meaning for variable opt */
+		switch (optflag->flags & OPTION_TYPE_MASK) {
+		case OPTION_IP:
+			retval = udhcp_str2nip(val, buffer);
+			break;
+		case OPTION_IP_PAIR:
+			retval = udhcp_str2nip(val, buffer);
+			val = strtok(NULL, ", \t/-");
+			if (!val)
+				retval = 0;
+			if (retval)
+				retval = udhcp_str2nip(val, buffer + 4);
+			break;
+		case OPTION_STRING:
+#if ENABLE_FEATURE_UDHCP_RFC3397
+		case OPTION_DNS_STRING:
+#endif
+			length = strnlen(val, 254);
+			if (length > 0) {
+				opt = val;
+				retval = 1;
+			}
+			break;
+//		case OPTION_BOOLEAN: {
+//			static const char no_yes[] ALIGN1 = "no\0yes\0";
+//			buffer[0] = retval = index_in_strings(no_yes, val);
+//			retval++; /* 0 - bad; 1: "no" 2: "yes" */
+//			break;
+//		}
+		case OPTION_U8:
+			buffer[0] = strtoul(val, &endptr, 0);
+			retval = (endptr[0] == '\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);
+			*result_u16 = htons(tmp);
+			retval = (endptr[0] == '\0' /*&& tmp < 0x10000*/);
+			break;
+		}
+//		case OPTION_S16: {
+//			long tmp = strtol(val, &endptr, 0);
+//			*result_u16 = htons(tmp);
+//			retval = (endptr[0] == '\0');
+//			break;
+//		}
+		case OPTION_U32: {
+			unsigned long tmp = strtoul(val, &endptr, 0);
+			*result_u32 = htonl(tmp);
+			retval = (endptr[0] == '\0');
+			break;
+		}
+		case OPTION_S32: {
+			long tmp = strtol(val, &endptr, 0);
+			*result_u32 = htonl(tmp);
+			retval = (endptr[0] == '\0');
+			break;
+		}
+		case OPTION_BIN: /* handled in attach_option() */
+			opt = val;
+			retval = 1;
+		default:
+			break;
+		}
+		if (retval)
+			attach_option(opt_list, optflag, opt, length);
+	} while (retval && optflag->flags & OPTION_LIST);
+
+	return retval;
+}
diff --git a/busybox-1.19.3/networking/udhcp/common.h b/busybox-1.19.3/networking/udhcp/common.h
new file mode 100644
index 0000000..ad6991c
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/common.h
@@ -0,0 +1,312 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 source tree.
+ */
+#ifndef UDHCP_COMMON_H
+#define UDHCP_COMMON_H 1
+
+#include "libbb.h"
+#include <netinet/udp.h>
+#include <netinet/ip.h>
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+extern const uint8_t MAC_BCAST_ADDR[6]; /* six all-ones */
+
+
+/*** DHCP packet ***/
+
+/* DHCP protocol. See RFC 2131 */
+#define DHCP_MAGIC              0x63825363
+#define DHCP_OPTIONS_BUFSIZE    308
+#define BOOTREQUEST             1
+#define BOOTREPLY               2
+
+//TODO: rename ciaddr/yiaddr/chaddr
+struct dhcp_packet {
+	uint8_t op;      /* BOOTREQUEST or 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;    /* unique id */
+	uint16_t secs;   /* elapsed since client began acquisition/renewal */
+	uint16_t flags;  /* only one flag so far: */
+#define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */
+	uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */
+	uint32_t yiaddr; /* 'your' (client) IP address */
+	/* IP address of next server to use in bootstrap, returned in DHCPOFFER, DHCPACK by server */
+	uint32_t siaddr_nip;
+	uint32_t gateway_nip; /* relay agent IP address */
+	uint8_t chaddr[16];   /* link-layer client hardware address (MAC) */
+	uint8_t sname[64];    /* server host name (ASCIZ) */
+	uint8_t file[128];    /* boot file name (ASCIZ) */
+	uint32_t cookie;      /* fixed first four option bytes (99,130,83,99 dec) */
+	uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS];
+} PACKED;
+#define DHCP_PKT_SNAME_LEN      64
+#define DHCP_PKT_FILE_LEN      128
+#define DHCP_PKT_SNAME_LEN_STR "64"
+#define DHCP_PKT_FILE_LEN_STR "128"
+
+struct ip_udp_dhcp_packet {
+	struct iphdr ip;
+	struct udphdr udp;
+	struct dhcp_packet data;
+} PACKED;
+
+struct udp_dhcp_packet {
+	struct udphdr udp;
+	struct dhcp_packet data;
+} PACKED;
+
+enum {
+	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_UDP_DHCP_SIZE == 576 ? 1 : -1];
+};
+
+
+/*** Options ***/
+
+enum {
+	OPTION_IP = 1,
+	OPTION_IP_PAIR,
+	OPTION_STRING,
+//	OPTION_BOOLEAN,
+	OPTION_U8,
+	OPTION_U16,
+//	OPTION_S16,
+	OPTION_U32,
+	OPTION_S32,
+	OPTION_BIN,
+	OPTION_STATIC_ROUTES,
+#if ENABLE_FEATURE_UDHCP_RFC3397
+	OPTION_DNS_STRING,  /* RFC1035 compressed domain name list */
+	OPTION_SIP_SERVERS,
+#endif
+
+	OPTION_TYPE_MASK = 0x0f,
+	/* Client requests this option by default */
+	OPTION_REQ  = 0x10,
+	/* There can be a list of 1 or more of these */
+	OPTION_LIST = 0x20,
+};
+
+/* 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).
+ */
+#define DHCP_PADDING            0x00
+#define DHCP_SUBNET             0x01
+//#define DHCP_TIME_OFFSET      0x02 /* (localtime - UTC_time) in seconds. signed */
+//#define DHCP_ROUTER           0x03
+//#define DHCP_TIME_SERVER      0x04 /* RFC 868 time server (32-bit, 0 = 1.1.1900) */
+//#define DHCP_NAME_SERVER      0x05 /* IEN 116 _really_ ancient kind of NS */
+//#define DHCP_DNS_SERVER       0x06
+//#define DHCP_LOG_SERVER       0x07 /* port 704 UDP log (not syslog)
+//#define DHCP_COOKIE_SERVER    0x08 /* "quote of the day" server */
+//#define DHCP_LPR_SERVER       0x09
+#define DHCP_HOST_NAME          0x0c /* either client informs server or server gives name to client */
+//#define DHCP_BOOT_SIZE        0x0d
+//#define DHCP_DOMAIN_NAME      0x0f /* server gives domain suffix */
+//#define DHCP_SWAP_SERVER      0x10
+//#define DHCP_ROOT_PATH        0x11
+//#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
+//#define DHCP_WINS_SERVER      0x2c
+#define DHCP_REQUESTED_IP       0x32 /* sent by client if specific IP is wanted */
+#define DHCP_LEASE_TIME         0x33
+#define DHCP_OPTION_OVERLOAD    0x34
+#define DHCP_MESSAGE_TYPE       0x35
+#define DHCP_SERVER_ID          0x36 /* by default server's IP */
+#define DHCP_PARAM_REQ          0x37 /* list of options client wants */
+//#define DHCP_ERR_MESSAGE      0x38 /* error message when sending NAK etc */
+#define DHCP_MAX_SIZE           0x39
+#define DHCP_VENDOR             0x3c /* client's vendor (a string) */
+#define DHCP_CLIENT_ID          0x3d /* by default client's MAC addr, but may be arbitrarily long */
+//#define DHCP_TFTP_SERVER_NAME 0x42 /* same as 'sname' field */
+//#define DHCP_BOOT_FILE        0x43 /* same as 'file' field */
+//#define DHCP_USER_CLASS       0x4d /* RFC 3004. set of LASCII strings. "I am a printer" etc */
+#define DHCP_FQDN               0x51 /* client asks to update DNS to map its FQDN to its new IP */
+//#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_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
+
+/* Offsets in option byte sequence */
+#define OPT_CODE                0
+#define OPT_LEN                 1
+#define OPT_DATA                2
+/* Bits in "overload" option */
+#define OPTION_FIELD            0
+#define FILE_FIELD              1
+#define SNAME_FIELD             2
+
+/* DHCP_MESSAGE_TYPE values */
+#define DHCPDISCOVER            1 /* client -> server */
+#define DHCPOFFER               2 /* client <- server */
+#define DHCPREQUEST             3 /* client -> server */
+#define DHCPDECLINE             4 /* client -> server */
+#define DHCPACK                 5 /* client <- server */
+#define DHCPNAK                 6 /* client <- server */
+#define DHCPRELEASE             7 /* client -> server */
+#define DHCPINFORM              8 /* client -> server */
+#define DHCP_MINTYPE DHCPDISCOVER
+#define DHCP_MAXTYPE DHCPINFORM
+
+struct dhcp_optflag {
+	uint8_t flags;
+	uint8_t code;
+};
+
+struct option_set {
+	uint8_t *data;
+	struct option_set *next;
+};
+
+extern const struct dhcp_optflag dhcp_optflags[];
+extern const char dhcp_option_strings[];
+extern const uint8_t dhcp_option_lengths[];
+
+unsigned FAST_FUNC udhcp_option_idx(const char *name);
+
+uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC;
+int udhcp_end_option(uint8_t *optionptr) FAST_FUNC;
+void udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt) FAST_FUNC;
+void udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) FAST_FUNC;
+#if ENABLE_FEATURE_UDHCP_RFC3397
+char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC;
+uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) FAST_FUNC;
+#endif
+struct option_set *udhcp_find_option(struct option_set *opt_list, uint8_t code) FAST_FUNC;
+
+
+// RFC 2131  Table 5: Fields and options used by DHCP clients
+//
+// Fields 'hops', 'yiaddr', 'siaddr', 'giaddr' are always zero
+//
+// Field      DHCPDISCOVER          DHCPINFORM            DHCPREQUEST           DHCPDECLINE         DHCPRELEASE
+// -----      ------------          ------------          -----------           -----------         -----------
+// 'xid'      selected by client    selected by client    'xid' from server     selected by client  selected by client
+//                                                        DHCPOFFER message
+// 'secs'     0 or seconds since    0 or seconds since    0 or seconds since    0                   0
+//            DHCP process started  DHCP process started  DHCP process started
+// 'flags'    Set 'BROADCAST'       Set 'BROADCAST'       Set 'BROADCAST'       0                   0
+//            flag if client        flag if client        flag if client
+//            requires broadcast    requires broadcast    requires broadcast
+//            reply                 reply                 reply
+// 'ciaddr'   0                     client's IP           0 or client's IP      0                   client's IP
+//                                                        (BOUND/RENEW/REBIND)
+// 'chaddr'   client's MAC          client's MAC          client's MAC          client's MAC        client's MAC
+// 'sname'    options or sname      options or sname      options or sname      (unused)            (unused)
+// 'file'     options or file       options or file       options or file       (unused)            (unused)
+// 'options'  options               options               options               message type opt    message type opt
+//
+// Option                     DHCPDISCOVER  DHCPINFORM  DHCPREQUEST      DHCPDECLINE  DHCPRELEASE
+// ------                     ------------  ----------  -----------      -----------  -----------
+// Requested IP address       MAY           MUST NOT    MUST (in         MUST         MUST NOT
+//                                                      SELECTING or
+//                                                      INIT-REBOOT)
+//                                                      MUST NOT (in
+//                                                      BOUND or
+//                                                      RENEWING)
+// IP address lease time      MAY           MUST NOT    MAY              MUST NOT     MUST NOT
+// Use 'file'/'sname' fields  MAY           MAY         MAY              MAY          MAY
+// Client identifier          MAY           MAY         MAY              MAY          MAY
+// Vendor class identifier    MAY           MAY         MAY              MUST NOT     MUST NOT
+// Server identifier          MUST NOT      MUST NOT    MUST (after      MUST         MUST
+//                                                      SELECTING)
+//                                                      MUST NOT (after
+//                                                      INIT-REBOOT,
+//                                                      BOUND, RENEWING
+//                                                      or REBINDING)
+// Parameter request list     MAY           MAY         MAY              MUST NOT     MUST NOT
+// Maximum message size       MAY           MAY         MAY              MUST NOT     MUST NOT
+// Message                    SHOULD NOT    SHOULD NOT  SHOULD NOT       SHOULD       SHOULD
+// Site-specific              MAY           MAY         MAY              MUST NOT     MUST NOT
+// All others                 MAY           MAY         MAY              MUST NOT     MUST NOT
+
+
+/*** Logging ***/
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+extern unsigned dhcp_verbose;
+# define log1(...) do { if (dhcp_verbose >= 1) bb_info_msg(__VA_ARGS__); } while (0)
+# if CONFIG_UDHCP_DEBUG >= 2
+void udhcp_dump_packet(struct dhcp_packet *packet) FAST_FUNC;
+#  define log2(...) do { if (dhcp_verbose >= 2) bb_info_msg(__VA_ARGS__); } while (0)
+# else
+#  define udhcp_dump_packet(...) ((void)0)
+#  define log2(...) ((void)0)
+# endif
+# if CONFIG_UDHCP_DEBUG >= 3
+#  define log3(...) do { if (dhcp_verbose >= 3) bb_info_msg(__VA_ARGS__); } while (0)
+# else
+#  define log3(...) ((void)0)
+# endif
+#else
+# define udhcp_dump_packet(...) ((void)0)
+# define log1(...) ((void)0)
+# define log2(...) ((void)0)
+# define log3(...) ((void)0)
+#endif
+
+
+/*** Other shared functions ***/
+
+/* 2nd param is "uint32_t*" */
+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;
+
+int udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
+		uint32_t source_nip, int source_port,
+		uint32_t dest_nip, int dest_port, const uint8_t *dest_arp,
+		int ifindex) FAST_FUNC;
+
+int udhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
+		uint32_t source_nip, int source_port,
+		uint32_t dest_nip, int dest_port) FAST_FUNC;
+
+void udhcp_sp_setup(void) FAST_FUNC;
+int udhcp_sp_fd_set(fd_set *rfds, int extra_fd) FAST_FUNC;
+int udhcp_sp_read(const fd_set *rfds) FAST_FUNC;
+
+int udhcp_read_interface(const char *interface, int *ifindex, uint32_t *nip, uint8_t *mac) FAST_FUNC;
+
+int udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf) FAST_FUNC;
+
+/* Returns 1 if no reply received */
+int arpping(uint32_t test_nip,
+		const uint8_t *safe_mac,
+		uint32_t from_ip,
+		uint8_t *from_mac,
+		const char *interface) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/udhcp/dhcpc.c b/busybox-1.19.3/networking/udhcp/dhcpc.c
new file mode 100644
index 0000000..4d755e6
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/dhcpc.c
@@ -0,0 +1,1562 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * udhcp client
+ *
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#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 <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 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"
+	"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"
+	"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 ***/
+
+/* get a rough idea of how long an option will be (rounding up...) */
+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_STRING          ] = 1,
+#if ENABLE_FEATURE_UDHCP_RFC3397
+	[OPTION_DNS_STRING      ] = 1, /* unused */
+	/* Hmmm, this severely overestimates size if SIP_SERVERS option
+	 * is in domain name form: N-byte option in binary form
+	 * mallocs ~16*N bytes. But it is freed almost at once.
+	 */
+	[OPTION_SIP_SERVERS     ] = sizeof("255.255.255.255 "),
+#endif
+//	[OPTION_BOOLEAN         ] = sizeof("yes "),
+	[OPTION_U8              ] = sizeof("255 "),
+	[OPTION_U16             ] = sizeof("65535 "),
+//	[OPTION_S16             ] = sizeof("-32768 "),
+	[OPTION_U32             ] = sizeof("4294967295 "),
+	[OPTION_S32             ] = sizeof("-2147483684 "),
+};
+
+/* note: ip is a pointer to an IP in network order, possibly misaliged */
+static int sprint_nip(char *dest, const char *pre, const uint8_t *ip)
+{
+	return sprintf(dest, "%s%u.%u.%u.%u", pre, ip[0], ip[1], ip[2], ip[3]);
+}
+
+/* really simple implementation, just count the bits */
+static int mton(uint32_t mask)
+{
+	int i = 0;
+	mask = ntohl(mask); /* 111110000-like bit pattern */
+	while (mask) {
+		i++;
+		mask <<= 1;
+	}
+	return i;
+}
+
+/* Create "opt_name=opt_value" string */
+static NOINLINE char *xmalloc_optname_optval(uint8_t *option, const struct dhcp_optflag *optflag, const char *opt_name)
+{
+	unsigned upper_length;
+	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];
+
+	type = optflag->flags & OPTION_TYPE_MASK;
+	optlen = dhcp_option_lengths[type];
+	upper_length = len_of_option_as_string[type] * ((unsigned)len / (unsigned)optlen);
+
+	dest = ret = xmalloc(upper_length + strlen(opt_name) + 2);
+	dest += sprintf(ret, "%s=", opt_name);
+
+	while (len >= optlen) {
+		unsigned ip_ofs = 0;
+
+		switch (type) {
+		case OPTION_IP_PAIR:
+			dest += sprint_nip(dest, "", option);
+			*dest++ = '/';
+			ip_ofs = 4;
+			/* fall through */
+		case OPTION_IP:
+			dest += sprint_nip(dest, "", option + ip_ofs);
+			break;
+//		case OPTION_BOOLEAN:
+//			dest += sprintf(dest, *option ? "yes" : "no");
+//			break;
+		case OPTION_U8:
+			dest += sprintf(dest, "%u", *option);
+			break;
+//		case OPTION_S16:
+		case OPTION_U16: {
+			uint16_t val_u16;
+			move_from_unaligned16(val_u16, option);
+			dest += sprintf(dest, "%u", ntohs(val_u16));
+			break;
+		}
+		case OPTION_S32:
+		case OPTION_U32: {
+			uint32_t val_u32;
+			move_from_unaligned32(val_u32, option);
+			dest += sprintf(dest, type == OPTION_U32 ? "%lu" : "%ld", (unsigned long) ntohl(val_u32));
+			break;
+		}
+		case OPTION_STRING:
+			memcpy(dest, option, len);
+			dest[len] = '\0';
+			return ret;	 /* Short circuit this case */
+		case OPTION_STATIC_ROUTES: {
+			/* Option binary format:
+			 * mask [one byte, 0..32]
+			 * ip [big endian, 0..4 bytes depending on mask]
+			 * router [big endian, 4 bytes]
+			 * may be repeated
+			 *
+			 * We convert it to a string "IP/MASK ROUTER IP2/MASK2 ROUTER2"
+			 */
+			const char *pfx = "";
+
+			while (len >= 1 + 4) { /* mask + 0-byte ip + router */
+				uint32_t nip;
+				uint8_t *p;
+				unsigned mask;
+				int bytes;
+
+				mask = *option++;
+				if (mask > 32)
+					break;
+				len--;
+
+				nip = 0;
+				p = (void*) &nip;
+				bytes = (mask + 7) / 8; /* 0 -> 0, 1..8 -> 1, 9..16 -> 2 etc */
+				while (--bytes >= 0) {
+					*p++ = *option++;
+					len--;
+				}
+				if (len < 4)
+					break;
+
+				/* print ip/mask */
+				dest += sprint_nip(dest, pfx, (void*) &nip);
+				pfx = " ";
+				dest += sprintf(dest, "/%u ", mask);
+				/* print router */
+				dest += sprint_nip(dest, "", option);
+				option += 4;
+				len -= 4;
+			}
+
+			return ret;
+		}
+#if ENABLE_FEATURE_UDHCP_RFC3397
+		case OPTION_DNS_STRING:
+			/* unpack option into dest; use ret for prefix (i.e., "optname=") */
+			dest = dname_dec(option, len, ret);
+			if (dest) {
+				free(ret);
+				return dest;
+			}
+			/* error. return "optname=" string */
+			return ret;
+		case OPTION_SIP_SERVERS:
+			/* Option binary format:
+			 * type: byte
+			 * type=0: domain names, dns-compressed
+			 * type=1: IP addrs
+			 */
+			option++;
+			len--;
+			if (option[-1] == 0) {
+				dest = dname_dec(option, len, ret);
+				if (dest) {
+					free(ret);
+					return dest;
+				}
+			} else
+			if (option[-1] == 1) {
+				const char *pfx = "";
+				while (1) {
+					len -= 4;
+					if (len < 0)
+						break;
+					dest += sprint_nip(dest, pfx, option);
+					pfx = " ";
+					option += 4;
+				}
+			}
+			return ret;
+#endif
+		} /* switch */
+		option += optlen;
+		len -= optlen;
+// 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 <= 0 /* || !(optflag->flags & OPTION_LIST) */)
+			break;
+		*dest++ = ' ';
+		*dest = '\0';
+	}
+	return ret;
+}
+
+/* put all the parameters into the environment */
+static char **fill_envp(struct dhcp_packet *packet)
+{
+	int envc;
+	int i;
+	char **envp, **curr;
+	const char *opt_name;
+	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
+	 * "siaddr=IP" from packet->siaddr_nip (unless 0)
+	 * "boot_file=FILE" from packet->file (unless overloaded)
+	 * "sname=SERVER_HOSTNAME" from packet->sname (unless overloaded)
+	 * terminating NULL
+	 */
+	envc = 6;
+	/* +1 element for each option, +2 for subnet option: */
+	if (packet) {
+		/* note: do not search for "pad" (0) and "end" (255) options */
+		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 mton */
+				envc++;
+				/*if (i != DHCP_MESSAGE_TYPE)*/
+				FOUND_OPTS(i) |= BMASK(i);
+			}
+		}
+	}
+	curr = envp = xzalloc(sizeof(envp[0]) * envc);
+
+	*curr = xasprintf("interface=%s", client_config.interface);
+	putenv(*curr++);
+
+	if (!packet)
+		return envp;
+
+	*curr = xmalloc(sizeof("ip=255.255.255.255"));
+	sprint_nip(*curr, "ip=", (uint8_t *) &packet->yiaddr);
+	putenv(*curr++);
+
+	opt_name = dhcp_option_strings;
+	i = 0;
+	while (*opt_name) {
+		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 (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));
+			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++);
+	}
+	/* Handle 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] = '\0';
+			putenv(*curr++);
+		}
+		i++;
+	}
+	return envp;
+}
+
+/* Call a script with a par file and env vars */
+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 */
+	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)
+{
+	return rand();
+}
+
+/* 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);
+}
+
+static void add_client_options(struct dhcp_packet *packet)
+{
+	uint8_t c;
+	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. */
+	end = udhcp_end_option(packet->options);
+	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;
+			len++;
+		}
+	}
+	if (len) {
+		packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
+		packet->options[end + OPT_LEN] = len;
+		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;
+		while (curr) {
+			udhcp_add_binary_option(packet, curr->data);
+			curr = curr->next;
+		}
+//		if (client_config.sname)
+//			strncpy((char*)packet->sname, client_config.sname, sizeof(packet->sname) - 1);
+//		if (client_config.boot_file)
+//			strncpy((char*)packet->file, client_config.boot_file, sizeof(packet->file) - 1);
+	}
+}
+
+/* RFC 2131
+ * 4.4.4 Use of broadcast and unicast
+ *
+ * The DHCP client broadcasts DHCPDISCOVER, DHCPREQUEST and DHCPINFORM
+ * messages, unless the client knows the address of a DHCP server.
+ * The client unicasts DHCPRELEASE messages to the server. Because
+ * the client is declining the use of the IP address supplied by the server,
+ * the client broadcasts DHCPDECLINE messages.
+ *
+ * When the DHCP client knows the address of a DHCP server, in either
+ * INIT or REBOOTING state, the client may use that address
+ * in the DHCPDISCOVER or DHCPREQUEST rather than the IP broadcast address.
+ * The client may also use unicast to send DHCPINFORM messages
+ * to a known DHCP server. If the client receives no response to DHCP
+ * messages sent to the IP address of a known DHCP server, the DHCP
+ * client reverts to using the IP broadcast address.
+ */
+
+static int raw_bcast_from_client_config_ifindex(struct dhcp_packet *packet)
+{
+	return udhcp_send_raw_packet(packet,
+		/*src*/ INADDR_ANY, CLIENT_PORT,
+		/*dst*/ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR,
+		client_config.ifindex);
+}
+
+/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
+/* 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);
+
+	/* 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...");
+	return raw_bcast_from_client_config_ifindex(&packet);
+}
+
+/* Broadcast a DHCP request message */
+/* RFC 2131 3.1 paragraph 3:
+ * "The client _broadcasts_ a DHCPREQUEST message..."
+ */
+/* 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;
+	bb_info_msg("Sending select for %s...", inet_ntoa(addr));
+	return raw_bcast_from_client_config_ifindex(&packet);
+}
+
+/* Unicast or broadcast a DHCP renew message */
+/* 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);
+}
+
+#if ENABLE_FEATURE_UDHCPC_ARPING
+/* Broadcast a DHCP decline message */
+/* 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);
+
+	/* 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;
+	/* 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...");
+	return raw_bcast_from_client_config_ifindex(&packet);
+}
+#endif
+
+/* Unicast a DHCP release message */
+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);
+
+	/* 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);
+}
+
+/* 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;
+
+	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 */
+	}
+
+	if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) {
+		log1("Packet is too short, ignoring");
+		return -2;
+	}
+
+	if (bytes < ntohs(packet.ip.tot_len)) {
+		/* packet is bigger than sizeof(packet), we did partial read */
+		log1("Oversized packet, ignoring");
+		return -2;
+	}
+
+	/* ignore any extra garbage bytes */
+	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
+	 || packet.ip.ihl != (sizeof(packet.ip) >> 2)
+	 || packet.udp.dest != htons(CLIENT_PORT)
+	/* || bytes > (int) sizeof(packet) - can't happen */
+	 || ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
+	) {
+		log1("Unrelated/bogus packet, ignoring");
+		return -2;
+	}
+
+	/* verify IP checksum */
+	check = packet.ip.check;
+	packet.ip.check = 0;
+	if (check != udhcp_checksum(&packet.ip, sizeof(packet.ip))) {
+		log1("Bad IP header checksum, ignoring");
+		return -2;
+	}
+
+	/* 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)) {
+		log1("Packet with bad UDP checksum received, ignoring");
+		return -2;
+	}
+
+	memcpy(dhcp_pkt, &packet.data, bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
+
+	if (dhcp_pkt->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));
+}
+
+
+/*** 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 udhcp_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?
+	 */
+	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_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]),
+		/* casting const away: */
+		.filter = (struct sock_filter *) filter_instr,
+	};
+
+	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?
+
+	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("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_PORT, client_config.interface);
+	else if (new_mode != LISTEN_NONE)
+		sockfd = udhcp_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 */
+		udhcp_run_script(NULL, "deconfig");
+	case REQUESTING:
+	case RELEASED:
+		change_listen_mode(LISTEN_RAW);
+		state = INIT_SELECTING;
+		break;
+	case INIT_SELECTING:
+		break;
+	}
+}
+
+static void perform_release(uint32_t requested_ip, uint32_t server_addr)
+{
+	char buffer[sizeof("255.255.255.255")];
+	struct in_addr temp_addr;
+
+	/* send release packet */
+	if (state == BOUND || state == RENEWING || state == REBINDING) {
+		temp_addr.s_addr = server_addr;
+		strcpy(buffer, inet_ntoa(temp_addr));
+		temp_addr.s_addr = requested_ip;
+		bb_info_msg("Unicasting a release of %s to %s",
+				inet_ntoa(temp_addr), buffer);
+		send_release(server_addr, requested_ip); /* unicast */
+		udhcp_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 udhcpc_trivial_usage
+//usage:       "[-fbnq"IF_UDHCP_VERBOSE("v")"oCRB] [-i IFACE] [-r IP] [-s PROG] [-p PIDFILE]\n"
+//usage:       "	[-H HOSTNAME] [-V VENDOR] [-x OPT:VAL]... [-O OPT]..." IF_FEATURE_UDHCP_PORT(" [-P N]")
+//usage:#define udhcpc_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 68)"
+//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:     "\n	-F,--fqdn NAME		Ask server to update DNS mapping for NAME"
+//usage:     "\n	-H,-h,--hostname NAME	Send NAME as client hostname (default none)"
+//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:     "\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 68)"
+//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:     "\n	-F NAME		Ask server to update DNS mapping for NAME"
+//usage:     "\n	-H,-h NAME	Send NAME as client hostname (default none)"
+//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 current lease"
+//usage:     "\n	USR2	Release current 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_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;
+	int discover_timeout = 3;
+	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 */
+	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;
+
+	/* Default options */
+	IF_FEATURE_UDHCP_PORT(SERVER_PORT = 67;)
+	IF_FEATURE_UDHCP_PORT(CLIENT_PORT = 68;)
+	client_config.interface = "eth0";
+	client_config.script = CONFIG_UDHCPC_DEFAULT_SCRIPT;
+	str_V = "udhcp "BB_VER;
+
+	/* Parse command line */
+	/* O,x: list; -T,-t,-A take numeric param */
+	opt_complementary = "O::x::T+:t+:A+"
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+		":vv"
+#endif
+		;
+	IF_LONG_OPTS(applet_long_options = udhcpc_longopts;)
+	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_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))
+		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);
+		/* Flag bits: 0000NEOS
+		 * S: 1 = Client requests server to update A RR in DNS as well as PTR
+		 * O: 1 = Server indicates to client that DNS has been updated regardless
+		 * E: 1 = Name is in DNS format, i.e. <4>host<6>domain<3>com<0>,
+		 *    not "host.domain.com". Format 0 is obsolete.
+		 * N: 1 = Client requests server to not update DNS (S must be 0 then)
+		 * Two [0] bytes which follow are deprecated and must be 0.
+		 */
+		client_config.fqdn[OPT_DATA + 0] = 0x1;
+		/*client_config.fqdn[OPT_DATA + 1] = 0; - xzalloc did it */
+		/*client_config.fqdn[OPT_DATA + 2] = 0; */
+	}
+	if (opt & OPT_r)
+		requested_ip = inet_addr(str_r);
+#if ENABLE_FEATURE_UDHCP_PORT
+	if (opt & OPT_P) {
+		CLIENT_PORT = xatou16(str_P);
+		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 = 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);
+	}
+	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;
+	}
+
+	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 */
+		clientid_mac_ptr = client_config.clientid + OPT_DATA+1;
+		memcpy(clientid_mac_ptr, client_config.client_mac, 6);
+	}
+	if (str_V[0] != '\0')
+		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)) {
+		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;
+	udhcp_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 (;;) {
+		/* 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) {
+			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 */
+				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)
+			) {
+				return 1; /* 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 (packet_num == 0)
+						xid = random_xid();
+					/* broadcast */
+					send_discover(xid, requested_ip);
+					timeout = discover_timeout;
+					packet_num++;
+					continue;
+				}
+ leasefail:
+				udhcp_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 (packet_num < discover_retries) {
+					/* send broadcast select packet */
+					send_select(xid, server_addr, requested_ip);
+					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_renew(xid, server_addr, requested_ip);
+					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_renew(xid, 0 /*INADDR_ANY*/, requested_ip);
+					timeout >>= 1;
+					continue;
+				}
+				/* Timed out, enter init state */
+				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;
+			/* 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 */
+			perform_renew();
+			if (state == RENEW_REQUESTED)
+				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);
+			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;
+		}
+
+		/* 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 = udhcp_recv_kernel_packet(&packet, sockfd);
+			else
+				len = udhcp_recv_raw_packet(&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;
+		}
+
+		if (packet.xid != xid) {
+			log1("xid %x (our is %x), ignoring packet",
+				(unsigned)packet.xid, (unsigned)xid);
+			continue;
+		}
+
+		/* Ignore packets that aren't for us */
+		if (packet.hlen != 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?
+			continue;
+		}
+
+		message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+		if (message == NULL) {
+			bb_error_msg("no message type option, ignoring packet");
+			continue;
+		}
+
+		switch (state) {
+		case INIT_SELECTING:
+			/* Must be a DHCPOFFER to one of our xid's */
+			if (*message == DHCPOFFER) {
+		/* TODO: why we don't just fetch server's IP from IP header? */
+				temp = udhcp_get_option(&packet, DHCP_SERVER_ID);
+				if (!temp) {
+					bb_error_msg("no server ID, ignoring packet");
+					continue;
+					/* still selecting - this server looks bad */
+				}
+				/* it IS unaligned sometimes, don't "optimize" */
+				move_from_unaligned32(server_addr, temp);
+				/*xid = packet.xid; - already is */
+				requested_ip = packet.yiaddr;
+
+				/* enter requesting state */
+				state = REQUESTING;
+				timeout = 0;
+				packet_num = 0;
+				already_waited_sec = 0;
+			}
+			continue;
+		case REQUESTING:
+		case RENEWING:
+		case RENEW_REQUESTED:
+		case REBINDING:
+			if (*message == DHCPACK) {
+				temp = udhcp_get_option(&packet, DHCP_LEASE_TIME);
+				if (!temp) {
+					bb_error_msg("no lease time with ACK, using 1 hour lease");
+					lease_seconds = 60 * 60;
+				} else {
+					/* 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;
+				}
+#if ENABLE_FEATURE_UDHCPC_ARPING
+				if (opt & OPT_a) {
+/* RFC 2131 3.1 paragraph 5:
+ * "The client receives the DHCPACK message with configuration
+ * parameters. The client SHOULD perform a final check on the
+ * parameters (e.g., ARP for allocated network address), and notes
+ * the duration of the lease specified in the DHCPACK message. At this
+ * point, the client is configured. If the client detects that the
+ * address is already in use (e.g., through the use of ARP),
+ * the client MUST send a DHCPDECLINE message to the server and restarts
+ * the configuration process..." */
+					if (!arpping(packet.yiaddr,
+							NULL,
+							(uint32_t) 0,
+							client_config.client_mac,
+							client_config.interface)
+					) {
+						bb_info_msg("Offered address is in use "
+							"(got ARP reply), declining");
+						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;
+						already_waited_sec = 0;
+						continue; /* back to main loop */
+					}
+				}
+#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);
+				}
+				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) */
+				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 */
+			}
+			if (*message == DHCPNAK) {
+				/* return to init state */
+				bb_info_msg("Received DHCP NAK");
+				udhcp_run_script(&packet, "nak");
+				if (state != REQUESTING)
+					udhcp_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_ip = 0;
+				timeout = 0;
+				packet_num = 0;
+				already_waited_sec = 0;
+			}
+			continue;
+		/* case BOUND: - ignore all packets */
+		/* case RELEASED: - ignore all packets */
+		}
+		/* back to main loop */
+	} /* for (;;) - main loop ends */
+
+ ret0:
+	retval = 0;
+ ret:
+	/*if (client_config.pidfile) - remove_pidfile has its own check */
+		remove_pidfile(client_config.pidfile);
+	return retval;
+}
diff --git a/busybox-1.19.3/networking/udhcp/dhcpc.h b/busybox-1.19.3/networking/udhcp/dhcpc.h
new file mode 100644
index 0000000..2b35991
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/dhcpc.h
@@ -0,0 +1,40 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#ifndef UDHCP_DHCPC_H
+#define UDHCP_DHCPC_H 1
+
+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) */
+	const char *interface;          /* The name of the interface to use */
+	char *pidfile;                  /* Optionally store the process ID */
+	const char *script;             /* User script to run at dhcp events */
+	struct option_set *options;     /* list of DHCP options to send to server */
+	uint8_t *clientid;              /* Optional client id to use */
+	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)
+#else
+#define CLIENT_PORT 68
+#endif
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/udhcp/dhcpd.c b/busybox-1.19.3/networking/udhcp/dhcpd.c
new file mode 100644
index 0000000..747472d
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/dhcpd.c
@@ -0,0 +1,664 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * udhcp server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ *			Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+//usage:#define udhcpd_trivial_usage
+//usage:       "[-fS]" 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:	IF_FEATURE_UDHCP_PORT(
+//usage:     "\n	-P N	Use port N (default 67)"
+//usage:	)
+
+#include <syslog.h>
+#include "common.h"
+#include "dhcpc.h"
+#include "dhcpd.h"
+
+
+/* Send a packet to a specific mac address and ip address by creating our own ip packet */
+static void send_packet_to_client(struct dhcp_packet *dhcp_pkt, int force_broadcast)
+{
+	const uint8_t *chaddr;
+	uint32_t ciaddr;
+
+	// Was:
+	//if (force_broadcast) { /* broadcast */ }
+	//else if (dhcp_pkt->ciaddr) { /* unicast to dhcp_pkt->ciaddr */ }
+	//else if (dhcp_pkt->flags & htons(BROADCAST_FLAG)) { /* broadcast */ }
+	//else { /* unicast to dhcp_pkt->yiaddr */ }
+	// But this is wrong: yiaddr is _our_ idea what client's IP is
+	// (for example, from lease file). Client may not know that,
+	// and may not have UDP socket listening on that IP!
+	// We should never unicast to dhcp_pkt->yiaddr!
+	// dhcp_pkt->ciaddr, OTOH, comes from client's request packet,
+	// and can be used.
+
+	if (force_broadcast
+	 || (dhcp_pkt->flags & htons(BROADCAST_FLAG))
+	 || dhcp_pkt->ciaddr == 0
+	) {
+		log1("Broadcasting packet to client");
+		ciaddr = INADDR_BROADCAST;
+		chaddr = MAC_BCAST_ADDR;
+	} else {
+		log1("Unicasting packet to client ciaddr");
+		ciaddr = dhcp_pkt->ciaddr;
+		chaddr = dhcp_pkt->chaddr;
+	}
+
+	udhcp_send_raw_packet(dhcp_pkt,
+		/*src*/ server_config.server_nip, SERVER_PORT,
+		/*dst*/ ciaddr, CLIENT_PORT, chaddr,
+		server_config.ifindex);
+}
+
+/* Send a packet to gateway_nip using the kernel ip stack */
+static void send_packet_to_relay(struct dhcp_packet *dhcp_pkt)
+{
+	log1("Forwarding packet to relay");
+
+	udhcp_send_kernel_packet(dhcp_pkt,
+			server_config.server_nip, SERVER_PORT,
+			dhcp_pkt->gateway_nip, SERVER_PORT);
+}
+
+static void send_packet(struct dhcp_packet *dhcp_pkt, int force_broadcast)
+{
+	if (dhcp_pkt->gateway_nip)
+		send_packet_to_relay(dhcp_pkt);
+	else
+		send_packet_to_client(dhcp_pkt, force_broadcast);
+}
+
+static void init_packet(struct dhcp_packet *packet, struct dhcp_packet *oldpacket, char type)
+{
+	/* Sets op, htype, hlen, cookie fields
+	 * and adds DHCP_MESSAGE_TYPE option */
+	udhcp_init_header(packet, type);
+
+	packet->xid = oldpacket->xid;
+	memcpy(packet->chaddr, oldpacket->chaddr, sizeof(oldpacket->chaddr));
+	packet->flags = oldpacket->flags;
+	packet->gateway_nip = oldpacket->gateway_nip;
+	packet->ciaddr = oldpacket->ciaddr;
+	udhcp_add_simple_option(packet, DHCP_SERVER_ID, server_config.server_nip);
+}
+
+/* Fill options field, siaddr_nip, and sname and boot_file fields.
+ * TODO: teach this code to use overload option.
+ */
+static void add_server_options(struct dhcp_packet *packet)
+{
+	struct option_set *curr = server_config.options;
+
+	while (curr) {
+		if (curr->data[OPT_CODE] != DHCP_LEASE_TIME)
+			udhcp_add_binary_option(packet, curr->data);
+		curr = curr->next;
+	}
+
+	packet->siaddr_nip = server_config.siaddr_nip;
+
+	if (server_config.sname)
+		strncpy((char*)packet->sname, server_config.sname, sizeof(packet->sname) - 1);
+	if (server_config.boot_file)
+		strncpy((char*)packet->file, server_config.boot_file, sizeof(packet->file) - 1);
+}
+
+static uint32_t select_lease_time(struct dhcp_packet *packet)
+{
+	uint32_t lease_time_sec = server_config.max_lease_sec;
+	uint8_t *lease_time_opt = udhcp_get_option(packet, DHCP_LEASE_TIME);
+	if (lease_time_opt) {
+		move_from_unaligned32(lease_time_sec, lease_time_opt);
+		lease_time_sec = ntohl(lease_time_sec);
+		if (lease_time_sec > server_config.max_lease_sec)
+			lease_time_sec = server_config.max_lease_sec;
+		if (lease_time_sec < server_config.min_lease_sec)
+			lease_time_sec = server_config.min_lease_sec;
+	}
+	return lease_time_sec;
+}
+
+/* We got a DHCP DISCOVER. Send an OFFER. */
+/* 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;
+	struct in_addr addr;
+
+	init_packet(&packet, oldpacket, DHCPOFFER);
+
+	/* If it is a static lease, use its IP */
+	packet.yiaddr = static_lease_nip;
+	/* Else: */
+	if (!static_lease_nip) {
+		/* We have no static lease for client's chaddr */
+		uint32_t req_nip;
+		const char *p_host_name;
+
+		if (lease) {
+			/* We have a dynamic lease for client's chaddr.
+			 * Reuse its IP (even if lease is expired).
+			 * Note that we ignore requested IP in this case.
+			 */
+			packet.yiaddr = lease->lease_nip;
+		}
+		/* Or: if client has requested an IP */
+		else if (requested_ip_opt != NULL
+		 /* (read IP) */
+		 && (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
+		 /* and */
+		 && (  !(lease = find_lease_by_nip(req_nip)) /* is not already taken */
+		    || is_expired_lease(lease) /* or is taken, but expired */
+		    )
+		) {
+			packet.yiaddr = req_nip;
+		}
+		else {
+			/* Otherwise, find a free IP */
+			packet.yiaddr = find_free_or_expired_nip(oldpacket->chaddr);
+		}
+
+		if (!packet.yiaddr) {
+			bb_error_msg("no free IP addresses. OFFER abandoned");
+			return;
+		}
+		/* Reserve the IP for a short time hoping to get DHCPREQUEST soon */
+		p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
+		lease = add_lease(packet.chaddr, packet.yiaddr,
+				server_config.offer_time,
+				p_host_name,
+				p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
+		);
+		if (!lease) {
+			bb_error_msg("no free IP addresses. OFFER abandoned");
+			return;
+		}
+	}
+
+	lease_time_sec = select_lease_time(oldpacket);
+	udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
+	add_server_options(&packet);
+
+	addr.s_addr = packet.yiaddr;
+	bb_info_msg("Sending OFFER of %s", inet_ntoa(addr));
+	/* send_packet emits error message itself if it detects failure */
+	send_packet(&packet, /*force_bcast:*/ 0);
+}
+
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE void send_NAK(struct dhcp_packet *oldpacket)
+{
+	struct dhcp_packet packet;
+
+	init_packet(&packet, oldpacket, DHCPNAK);
+
+	log1("Sending NAK");
+	send_packet(&packet, /*force_bcast:*/ 1);
+}
+
+/* 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;
+	struct in_addr addr;
+	const char *p_host_name;
+
+	init_packet(&packet, oldpacket, DHCPACK);
+	packet.yiaddr = yiaddr;
+
+	lease_time_sec = select_lease_time(oldpacket);
+	udhcp_add_simple_option(&packet, DHCP_LEASE_TIME, htonl(lease_time_sec));
+
+	add_server_options(&packet);
+
+	addr.s_addr = yiaddr;
+	bb_info_msg("Sending ACK to %s", inet_ntoa(addr));
+	send_packet(&packet, /*force_bcast:*/ 0);
+
+	p_host_name = (const char*) udhcp_get_option(oldpacket, DHCP_HOST_NAME);
+	add_lease(packet.chaddr, packet.yiaddr,
+		lease_time_sec,
+		p_host_name,
+		p_host_name ? (unsigned char)p_host_name[OPT_LEN - OPT_DATA] : 0
+	);
+	if (ENABLE_FEATURE_UDHCPD_WRITE_LEASES_EARLY) {
+		/* rewrite the file with leases at every new acceptance */
+		write_leases();
+	}
+}
+
+/* NOINLINE: limit stack usage in caller */
+static NOINLINE void send_inform(struct dhcp_packet *oldpacket)
+{
+	struct dhcp_packet packet;
+
+	/* "If a client has obtained a network address through some other means
+	 * (e.g., manual configuration), it may use a DHCPINFORM request message
+	 * to obtain other local configuration parameters.  Servers receiving a
+	 * DHCPINFORM message construct a DHCPACK message with any local
+	 * configuration parameters appropriate for the client without:
+	 * allocating a new address, checking for an existing binding, filling
+	 * in 'yiaddr' or including lease time parameters.  The servers SHOULD
+	 * unicast the DHCPACK reply to the address given in the 'ciaddr' field
+	 * of the DHCPINFORM message.
+	 * ...
+	 * The server responds to a DHCPINFORM message by sending a DHCPACK
+	 * message directly to the address given in the 'ciaddr' field
+	 * of the DHCPINFORM message.  The server MUST NOT send a lease
+	 * expiration time to the client and SHOULD NOT fill in 'yiaddr'."
+	 */
+//TODO: do a few sanity checks: is ciaddr set?
+//Better yet: is ciaddr == IP source addr?
+	init_packet(&packet, oldpacket, DHCPACK);
+	add_server_options(&packet);
+
+	send_packet(&packet, /*force_bcast:*/ 0);
+}
+
+
+/* globals */
+struct dyn_lease *g_leases;
+/* struct server_config_t server_config is in bb_common_bufsiz1 */
+
+
+int udhcpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int udhcpd_main(int argc UNUSED_PARAM, char **argv)
+{
+	int server_socket = -1, retval, max_sock;
+	uint8_t *state;
+	unsigned timeout_end;
+	unsigned num_ips;
+	unsigned opt;
+	struct option_set *option;
+	IF_FEATURE_UDHCP_PORT(char *str_P;)
+
+#if ENABLE_FEATURE_UDHCP_PORT
+	SERVER_PORT = 67;
+	CLIENT_PORT = 68;
+#endif
+
+#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
+		);
+	if (!(opt & 1)) { /* no -f */
+		bb_daemonize_or_rexec(0, argv);
+		logmode = LOGMODE_NONE;
+	}
+	/* update argv after the possible vfork+exec in daemonize */
+	argv += optind;
+	if (opt & 2) { /* -S */
+		openlog(applet_name, LOG_PID, LOG_DAEMON);
+		logmode |= LOGMODE_SYSLOG;
+	}
+#if ENABLE_FEATURE_UDHCP_PORT
+	if (opt & 8) { /* -P */
+		SERVER_PORT = xatou16(str_P);
+		CLIENT_PORT = SERVER_PORT + 1;
+	}
+#endif
+	/* Would rather not do read_config before daemonization -
+	 * otherwise NOMMU machines will parse config twice */
+	read_config(argv[0] ? argv[0] : DHCPD_CONF_FILE);
+
+	/* 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(server_config.pidfile);
+	/* if (!..) bb_perror_msg("can't create pidfile %s", pidfile); */
+
+	bb_info_msg("%s (v"BB_VER") started", applet_name);
+
+	option = udhcp_find_option(server_config.options, DHCP_LEASE_TIME);
+	server_config.max_lease_sec = DEFAULT_LEASE_TIME;
+	if (option) {
+		move_from_unaligned32(server_config.max_lease_sec, option->data + OPT_DATA);
+		server_config.max_lease_sec = ntohl(server_config.max_lease_sec);
+	}
+
+	/* Sanity check */
+	num_ips = server_config.end_ip - server_config.start_ip + 1;
+	if (server_config.max_leases > num_ips) {
+		bb_error_msg("max_leases=%u is too big, setting to %u",
+			(unsigned)server_config.max_leases, num_ips);
+		server_config.max_leases = num_ips;
+	}
+
+	g_leases = xzalloc(server_config.max_leases * sizeof(g_leases[0]));
+	read_leases(server_config.lease_file);
+
+	if (udhcp_read_interface(server_config.interface,
+			&server_config.ifindex,
+			&server_config.server_nip,
+			server_config.server_mac)
+	) {
+		retval = 1;
+		goto ret;
+	}
+
+	/* Setup the signal pipe */
+	udhcp_sp_setup();
+
+	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_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,
+					server_config.interface);
+		}
+
+		max_sock = udhcp_sp_fd_set(&rfds, server_socket);
+		if (server_config.auto_time) {
+			tv.tv_sec = timeout_end - monotonic_sec();
+			tv.tv_usec = 0;
+		}
+		retval = 0;
+		if (!server_config.auto_time || tv.tv_sec > 0) {
+			retval = select(max_sock + 1, &rfds, NULL, NULL,
+					server_config.auto_time ? &tv : NULL);
+		}
+		if (retval == 0) {
+			write_leases();
+			timeout_end = monotonic_sec() + server_config.auto_time;
+			continue;
+		}
+		if (retval < 0 && errno != EINTR) {
+			log1("Error on select");
+			continue;
+		}
+
+		switch (udhcp_sp_read(&rfds)) {
+		case SIGUSR1:
+			bb_info_msg("Received SIGUSR1");
+			write_leases();
+			/* why not just reset the timeout, eh */
+			timeout_end = monotonic_sec() + server_config.auto_time;
+			continue;
+		case SIGTERM:
+			bb_info_msg("Received SIGTERM");
+			goto ret0;
+		case 0: /* no signal: read a packet */
+			break;
+		default: /* signal or error (probably EINTR): back to select */
+			continue;
+		}
+
+		bytes = udhcp_recv_kernel_packet(&packet, server_socket);
+		if (bytes < 0) {
+			/* bytes can also be -2 ("bad packet data") */
+			if (bytes == -1 && errno != EINTR) {
+				log1("Read error: %s, reopening socket", strerror(errno));
+				close(server_socket);
+				server_socket = -1;
+			}
+			continue;
+		}
+		if (packet.hlen != 6) {
+			bb_error_msg("MAC length != 6, ignoring packet");
+			continue;
+		}
+		if (packet.op != BOOTREQUEST) {
+			bb_error_msg("not a REQUEST, ignoring packet");
+			continue;
+		}
+		state = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
+		if (state == NULL || state[0] < DHCP_MINTYPE || state[0] > DHCP_MAXTYPE) {
+			bb_error_msg("no or bad message type option, ignoring packet");
+			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) {
+			bb_info_msg("Found static lease: %x", static_lease_nip);
+			memcpy(&fake_lease.lease_mac, &packet.chaddr, 6);
+			fake_lease.lease_nip = static_lease_nip;
+			fake_lease.expires = 0;
+			lease = &fake_lease;
+		} else {
+			lease = find_lease_by_mac(packet.chaddr);
+		}
+
+		/* 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]) {
+
+		case DHCPDISCOVER:
+			log1("Received DISCOVER");
+
+			send_offer(&packet, static_lease_nip, lease, requested_ip_opt);
+			break;
+
+		case DHCPREQUEST:
+			log1("Received REQUEST");
+/* RFC 2131:
+
+o DHCPREQUEST generated during SELECTING state:
+
+   Client inserts the address of the selected server in 'server
+   identifier', 'ciaddr' MUST be zero, 'requested IP address' MUST be
+   filled in with the yiaddr value from the chosen DHCPOFFER.
+
+   Note that the client may choose to collect several DHCPOFFER
+   messages and select the "best" offer.  The client indicates its
+   selection by identifying the offering server in the DHCPREQUEST
+   message.  If the client receives no acceptable offers, the client
+   may choose to try another DHCPDISCOVER message.  Therefore, the
+   servers may not receive a specific DHCPREQUEST from which they can
+   decide whether or not the client has accepted the offer.
+
+o DHCPREQUEST generated during INIT-REBOOT state:
+
+   'server identifier' MUST NOT be filled in, 'requested IP address'
+   option MUST be filled in with client's notion of its previously
+   assigned address. 'ciaddr' MUST be zero. The client is seeking to
+   verify a previously allocated, cached configuration. Server SHOULD
+   send a DHCPNAK message to the client if the 'requested IP address'
+   is incorrect, or is on the wrong network.
+
+   Determining whether a client in the INIT-REBOOT state is on the
+   correct network is done by examining the contents of 'giaddr', the
+   'requested IP address' option, and a database lookup. If the DHCP
+   server detects that the client is on the wrong net (i.e., the
+   result of applying the local subnet mask or remote subnet mask (if
+   'giaddr' is not zero) to 'requested IP address' option value
+   doesn't match reality), then the server SHOULD send a DHCPNAK
+   message to the client.
+
+   If the network is correct, then the DHCP server should check if
+   the client's notion of its IP address is correct. If not, then the
+   server SHOULD send a DHCPNAK message to the client. If the DHCP
+   server has no record of this client, then it MUST remain silent,
+   and MAY output a warning to the network administrator. This
+   behavior is necessary for peaceful coexistence of non-
+   communicating DHCP servers on the same wire.
+
+   If 'giaddr' is 0x0 in the DHCPREQUEST message, the client is on
+   the same subnet as the server.  The server MUST broadcast the
+   DHCPNAK message to the 0xffffffff broadcast address because the
+   client may not have a correct network address or subnet mask, and
+   the client may not be answering ARP requests.
+
+   If 'giaddr' is set in the DHCPREQUEST message, the client is on a
+   different subnet.  The server MUST set the broadcast bit in the
+   DHCPNAK, so that the relay agent will broadcast the DHCPNAK to the
+   client, because the client may not have a correct network address
+   or subnet mask, and the client may not be answering ARP requests.
+
+o 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.
+
+   A client MAY choose to renew or extend its lease prior to T1.  The
+   server may choose not to extend the lease (as a policy decision by
+   the network administrator), but should return a DHCPACK message
+   regardless.
+
+o DHCPREQUEST generated during REBINDING 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 MUST
+   be broadcast to the 0xffffffff IP broadcast address.  The DHCP
+   server SHOULD check 'ciaddr' for correctness before replying to
+   the DHCPREQUEST.
+
+   The DHCPREQUEST from a REBINDING client is intended to accommodate
+   sites that have multiple DHCP servers and a mechanism for
+   maintaining consistency among leases managed by multiple servers.
+   A DHCP server MAY extend a client's lease only if it has local
+   administrative authority to do so.
+*/
+			if (!requested_ip_opt) {
+				requested_nip = packet.ciaddr;
+				if (requested_nip == 0) {
+					log1("no requested IP and no ciaddr, ignoring");
+					break;
+				}
+			}
+			if (lease && requested_nip == lease->lease_nip) {
+				/* client requested or configured IP matches the lease.
+				 * ACK it, and bump lease expiration time. */
+				send_ACK(&packet, lease->lease_nip);
+				break;
+			}
+			/* 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:
+			/* RFC 2131:
+			 * "If the server receives a DHCPDECLINE message,
+			 * the client has discovered through some other means
+			 * that the suggested network address is already
+			 * in use. The server MUST mark the network address
+			 * as not available and SHOULD notify the local
+			 * sysadmin of a possible configuration problem."
+			 *
+			 * SERVER_ID must be present,
+			 * REQUESTED_IP must be present,
+			 * chaddr must be filled in,
+			 * ciaddr must be 0 (we do not check this)
+			 */
+			log1("Received DECLINE");
+			if (server_id_opt
+			 && requested_ip_opt
+			 && lease  /* chaddr matches this lease */
+			 && requested_nip == lease->lease_nip
+			) {
+				memset(lease->lease_mac, 0, sizeof(lease->lease_mac));
+				lease->expires = time(NULL) + server_config.decline_time;
+			}
+			break;
+
+		case DHCPRELEASE:
+			/* "Upon receipt of a DHCPRELEASE message, the server
+			 * marks the network address as not allocated."
+			 *
+			 * SERVER_ID must be present,
+			 * REQUESTED_IP must not be present (we do not check this),
+			 * chaddr must be filled in,
+			 * ciaddr must be filled in
+			 */
+			log1("Received RELEASE");
+			if (server_id_opt
+			 && lease  /* chaddr matches this lease */
+			 && packet.ciaddr == lease->lease_nip
+			) {
+				lease->expires = time(NULL);
+			}
+			break;
+
+		case DHCPINFORM:
+			log1("Received INFORM");
+			send_inform(&packet);
+			break;
+		}
+	}
+ ret0:
+	retval = 0;
+ ret:
+	/*if (server_config.pidfile) - server_config.pidfile is never NULL */
+		remove_pidfile(server_config.pidfile);
+	return retval;
+}
diff --git a/busybox-1.19.3/networking/udhcp/dhcpd.h b/busybox-1.19.3/networking/udhcp/dhcpd.h
new file mode 100644
index 0000000..7c801bf
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/dhcpd.h
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#ifndef UDHCP_DHCPD_H
+#define UDHCP_DHCPD_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/* Defaults you may want to tweak */
+/* Default max_lease_sec */
+#define DEFAULT_LEASE_TIME      (60*60*24 * 10)
+#define LEASES_FILE             CONFIG_DHCPD_LEASES_FILE
+/* Where to find the DHCP server configuration file */
+#define DHCPD_CONF_FILE         "/etc/udhcpd.conf"
+
+
+struct static_lease {
+	struct static_lease *next;
+	uint32_t nip;
+	uint8_t mac[6];
+};
+
+struct server_config_t {
+	char *interface;                /* interface to use */
+//TODO: ifindex, server_nip, server_mac
+// are obtained from interface name.
+// Instead of querying them *once*, create update_server_network_data_cache()
+// and call it before any usage of these fields.
+// update_server_network_data_cache() must re-query data
+// if more than N seconds have passed after last use.
+	int ifindex;
+	uint32_t server_nip;
+#if ENABLE_FEATURE_UDHCP_PORT
+	uint16_t port;
+#endif
+	uint8_t server_mac[6];          /* our MAC address (used only for ARP probing) */
+	struct option_set *options;     /* list of DHCP options loaded from the config file */
+	/* start,end are in host order: we need to compare start <= ip <= end */
+	uint32_t start_ip;              /* start address of leases, in host order */
+	uint32_t end_ip;                /* end of leases, in host order */
+	uint32_t max_lease_sec;         /* maximum lease time (host order) */
+	uint32_t min_lease_sec;         /* minimum lease time a client can request */
+	uint32_t max_leases;            /* maximum number of leases (including reserved addresses) */
+	uint32_t auto_time;             /* how long should udhcpd wait before writing a config file.
+	                                 * if this is zero, it will only write one on SIGUSR1 */
+	uint32_t decline_time;          /* how long an address is reserved if a client returns a
+	                                 * decline message */
+	uint32_t conflict_time;         /* how long an arp conflict offender is leased for */
+	uint32_t offer_time;            /* how long an offered address is reserved */
+	uint32_t siaddr_nip;            /* "next server" bootp option */
+	char *lease_file;
+	char *pidfile;
+	char *notify_file;              /* what to run whenever leases are written */
+	char *sname;                    /* bootp server name */
+	char *boot_file;                /* bootp boot file option */
+	struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
+} FIX_ALIASING;
+
+#define server_config (*(struct server_config_t*)&bb_common_bufsiz1)
+/* client_config sits in 2nd half of bb_common_bufsiz1 */
+
+#if ENABLE_FEATURE_UDHCP_PORT
+#define SERVER_PORT (server_config.port)
+#else
+#define SERVER_PORT 67
+#endif
+
+
+typedef uint32_t leasetime_t;
+typedef int32_t signed_leasetime_t;
+
+struct dyn_lease {
+	/* Unix time when lease expires. Kept in memory in host order.
+	 * When written to file, converted to network order
+	 * and adjusted (current time subtracted) */
+	leasetime_t expires;
+	/* "nip": IP in network order */
+	uint32_t lease_nip;
+	/* We use lease_mac[6], since e.g. ARP probing uses
+	 * only 6 first bytes anyway. We check received dhcp packets
+	 * that their hlen == 6 and thus chaddr has only 6 significant bytes
+	 * (dhcp packet has chaddr[16], not [6])
+	 */
+	uint8_t lease_mac[6];
+	char hostname[20];
+	uint8_t pad[2];
+	/* total size is a multiply of 4 */
+} PACKED;
+
+extern struct dyn_lease *g_leases;
+
+struct dyn_lease *add_lease(
+		const uint8_t *chaddr, uint32_t yiaddr,
+		leasetime_t leasetime,
+		const char *hostname, int hostname_len
+		) FAST_FUNC;
+int is_expired_lease(struct dyn_lease *lease) FAST_FUNC;
+struct dyn_lease *find_lease_by_mac(const uint8_t *mac) FAST_FUNC;
+struct dyn_lease *find_lease_by_nip(uint32_t nip) FAST_FUNC;
+uint32_t find_free_or_expired_nip(const uint8_t *safe_mac) FAST_FUNC;
+
+
+/* Config file parser will pass static lease info to this function
+ * which will add it to a data structure that can be searched later */
+void add_static_lease(struct static_lease **st_lease_pp, uint8_t *mac, uint32_t nip) FAST_FUNC;
+/* Find static lease IP by mac */
+uint32_t get_static_nip_by_mac(struct static_lease *st_lease, void *arg) FAST_FUNC;
+/* Check to see if an IP is reserved as a static IP */
+int is_nip_reserved(struct static_lease *st_lease, uint32_t nip) FAST_FUNC;
+/* Print out static leases just to check what's going on (debug code) */
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+void log_static_leases(struct static_lease **st_lease_pp) FAST_FUNC;
+#else
+# define log_static_leases(st_lease_pp) ((void)0)
+#endif
+
+
+void read_config(const char *file) FAST_FUNC;
+void write_leases(void) FAST_FUNC;
+void read_leases(const char *file) FAST_FUNC;
+
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/networking/udhcp/dhcprelay.c b/busybox-1.19.3/networking/udhcp/dhcprelay.c
new file mode 100644
index 0000000..f82ac05
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/dhcprelay.c
@@ -0,0 +1,373 @@
+/* vi: set sw=4 ts=4: */
+/* Port to Busybox Copyright (C) 2006 Jesse Dutton <jessedutton@gmail.com>
+ *
+ * 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>,
+ *                   Zuercher Hochschule Winterthur,
+ *                   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
+
+/* 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 {
+	unsigned timestamp;
+	int client;
+	uint32_t xid;
+	struct sockaddr_in ip;
+	struct xid_item *next;
+} FIX_ALIASING;
+
+#define dhcprelay_xid_list (*(struct xid_item*)&bb_common_bufsiz1)
+
+static struct xid_item *xid_add(uint32_t xid, struct sockaddr_in *ip, int client)
+{
+	struct xid_item *item;
+
+	/* create new xid entry */
+	item = xmalloc(sizeof(struct xid_item));
+
+	/* add xid entry */
+	item->ip = *ip;
+	item->xid = xid;
+	item->client = client;
+	item->timestamp = monotonic_sec();
+	item->next = dhcprelay_xid_list.next;
+	dhcprelay_xid_list.next = item;
+
+	return item;
+}
+
+static void xid_expire(void)
+{
+	struct xid_item *item = dhcprelay_xid_list.next;
+	struct xid_item *last = &dhcprelay_xid_list;
+	unsigned current_time = monotonic_sec();
+
+	while (item != NULL) {
+		if ((current_time - item->timestamp) > MAX_LIFETIME) {
+			last->next = item->next;
+			free(item);
+			item = last->next;
+		} else {
+			last = item;
+			item = item->next;
+		}
+	}
+}
+
+static struct xid_item *xid_find(uint32_t xid)
+{
+	struct xid_item *item = dhcprelay_xid_list.next;
+	while (item != NULL) {
+		if (item->xid == xid) {
+			break;
+		}
+		item = item->next;
+	}
+	return item;
+}
+
+static void xid_del(uint32_t xid)
+{
+	struct xid_item *item = dhcprelay_xid_list.next;
+	struct xid_item *last = &dhcprelay_xid_list;
+	while (item != NULL) {
+		if (item->xid == xid) {
+			last->next = item->next;
+			free(item);
+			item = last->next;
+		} else {
+			last = item;
+			item = item->next;
+		}
+	}
+}
+
+/**
+ * get_dhcp_packet_type - gets the message type of a dhcp packet
+ * p - pointer to the dhcp packet
+ * returns the message type on success, -1 otherwise
+ */
+static int get_dhcp_packet_type(struct dhcp_packet *p)
+{
+	uint8_t *op;
+
+	/* it must be either a BOOTREQUEST or a BOOTREPLY */
+	if (p->op != BOOTREQUEST && p->op != BOOTREPLY)
+		return -1;
+	/* get message type option */
+	op = udhcp_get_option(p, DHCP_MESSAGE_TYPE);
+	if (op != NULL)
+		return op[0];
+	return -1;
+}
+
+/**
+ * make_iface_list - parses client/server interface names
+ * returns array
+ */
+static char **make_iface_list(char **client_and_server_ifaces, int *client_number)
+{
+	char *s, **iface_list;
+	int i, cn;
+
+	/* 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 == ',')
+			cn++;
+		s++;
+	}
+	*client_number = cn;
+
+	/* create vector of pointers */
+	iface_list = xzalloc(cn * sizeof(iface_list[0]));
+
+	iface_list[0] = client_and_server_ifaces[1]; /* server iface */
+
+	i = 1;
+	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 iface_list;
+}
+
+/* Creates listen sockets (in fds) bound to client and server ifaces,
+ * and returns numerically max fd.
+ */
+static int init_sockets(char **iface_list, int num_clients, int *fds)
+{
+	int 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
+ * client - number of the client
+ */
+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 type;
+
+	/* check packet_type */
+	type = get_dhcp_packet_type(p);
+	if (type != DHCPDISCOVER && type != DHCPREQUEST
+	 && type != DHCPDECLINE && type != DHCPRELEASE
+	 && type != DHCPINFORM
+	) {
+		return;
+	}
+
+	/* create new xid entry */
+	xid_add(p->xid, client_addr, client);
+
+	/* 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);
+}
+
+/**
+ * pass_to_client() - forwards dhcp packets from server to client
+ * p - packet to send
+ */
+static void pass_to_client(struct dhcp_packet *p, int packet_len, int *fds)
+{
+	int type;
+	struct xid_item *item;
+
+	/* check xid */
+	item = xid_find(p->xid);
+	if (!item) {
+		return;
+	}
+
+	/* check packet type */
+	type = get_dhcp_packet_type(p);
+	if (type != DHCPOFFER && type != DHCPACK && type != DHCPNAK) {
+		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);
+
+	if (sendto_ip4(fds[item->client], p, packet_len, &item->ip) != 0) {
+		return; /* send error occurred */
+	}
+
+	/* remove xid entry */
+	xid_del(p->xid);
+}
+
+int dhcprelay_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dhcprelay_main(int argc, char **argv)
+{
+	struct sockaddr_in server_addr;
+	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] */
+	if (argc == 4) {
+		if (!inet_aton(argv[3], &server_addr.sin_addr))
+			bb_perror_msg_and_die("bad server IP");
+	} else if (argc != 3) {
+		bb_show_usage();
+	}
+
+	iface_list = make_iface_list(argv + 1, &num_sockets);
+
+	fds = xmalloc(num_sockets * sizeof(fds[0]));
+
+	/* Create sockets and bind one to every iface */
+	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))
+		return 1;
+
+	/* Main loop */
+	while (1) {
+// reinit stuff from time to time? go back to make_iface_list
+// every N minutes?
+		fd_set rfds;
+		struct timeval tv;
+		int i;
+
+		FD_ZERO(&rfds);
+		for (i = 0; i < num_sockets; i++)
+			FD_SET(fds[i], &rfds);
+		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]);
+				if (packlen > 0) {
+					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(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 */
+// 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 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);
+			}
+		}
+		xid_expire();
+	} /* while (1) */
+
+	/* return 0; - not reached */
+}
diff --git a/busybox-1.19.3/networking/udhcp/domain_codec.c b/busybox-1.19.3/networking/udhcp/domain_codec.c
new file mode 100644
index 0000000..c1325d8
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/domain_codec.c
@@ -0,0 +1,247 @@
+/* vi: set sw=4 ts=4: */
+
+/* RFC1035 domain compression routines (C) 2007 Gabriel Somlo <somlo at cmu.edu>
+ *
+ * Loosely based on the isc-dhcpd implementation by dhankins@isc.org
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#ifdef DNS_COMPR_TESTING
+# define FAST_FUNC /* nothing */
+# define xmalloc malloc
+# include <stdlib.h>
+# include <stdint.h>
+# include <string.h>
+# include <stdio.h>
+#else
+# include "common.h"
+#endif
+
+#define NS_MAXDNAME  1025	/* max domain name length */
+#define NS_MAXCDNAME  255	/* max compressed domain name length */
+#define NS_MAXLABEL    63	/* max label length */
+#define NS_MAXDNSRCH    6	/* max domains in search path */
+#define NS_CMPRSFLGS 0xc0	/* name compression pointer flag */
+
+
+/* Expand a RFC1035-compressed list of domain names "cstr", of length "clen";
+ * returns a newly allocated string containing the space-separated domains,
+ * prefixed with the contents of string pre, or NULL if an error occurs.
+ */
+char* FAST_FUNC dname_dec(const uint8_t *cstr, int clen, const char *pre)
+{
+	char *ret = ret; /* for compiler */
+	char *dst = NULL;
+
+	/* We make two passes over the cstr string. First, we compute
+	 * how long the resulting string would be. Then we allocate a
+	 * new buffer of the required length, and fill it in with the
+	 * expanded content. The advantage of this approach is not
+	 * having to deal with requiring callers to supply their own
+	 * buffer, then having to check if it's sufficiently large, etc.
+	 */
+	while (1) {
+		/* note: "return NULL" below are leak-safe since
+		 * dst isn't yet allocated */
+		const uint8_t *c;
+		unsigned crtpos, retpos, depth, len;
+
+		crtpos = retpos = depth = len = 0;
+		while (crtpos < clen) {
+			c = cstr + crtpos;
+
+			if ((*c & NS_CMPRSFLGS) == NS_CMPRSFLGS) {
+				/* pointer */
+				if (crtpos + 2 > clen) /* no offset to jump to? abort */
+					return NULL;
+				if (retpos == 0) /* toplevel? save return spot */
+					retpos = crtpos + 2;
+				depth++;
+				crtpos = ((c[0] & 0x3f) << 8) | c[1]; /* jump */
+			} else if (*c) {
+				/* label */
+				if (crtpos + *c + 1 > clen) /* label too long? abort */
+					return NULL;
+				if (dst)
+					memcpy(dst + len, c + 1, *c);
+				len += *c + 1;
+				crtpos += *c + 1;
+				if (dst)
+					dst[len - 1] = '.';
+			} else {
+				/* NUL: end of current domain name */
+				if (retpos == 0) {
+					/* toplevel? keep going */
+					crtpos++;
+				} else {
+					/* return to toplevel saved spot */
+					crtpos = retpos;
+					retpos = depth = 0;
+				}
+				if (dst)
+					dst[len - 1] = ' ';
+			}
+
+			if (depth > NS_MAXDNSRCH /* too many jumps? abort, it's a loop */
+			 || len > NS_MAXDNAME * NS_MAXDNSRCH /* result too long? abort */
+			) {
+				return NULL;
+			}
+		}
+
+		if (!len) /* expanded string has 0 length? abort */
+			return NULL;
+
+		if (!dst) { /* first pass? */
+			/* allocate dst buffer and copy pre */
+			unsigned plen = strlen(pre);
+			ret = dst = xmalloc(plen + len);
+			memcpy(dst, pre, plen);
+			dst += plen;
+		} else {
+			dst[len - 1] = '\0';
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/* Convert a domain name (src) from human-readable "foo.blah.com" format into
+ * RFC1035 encoding "\003foo\004blah\003com\000". Return allocated string, or
+ * NULL if an error occurs.
+ */
+static uint8_t *convert_dname(const char *src)
+{
+	uint8_t c, *res, *lenptr, *dst;
+	int len;
+
+	res = xmalloc(strlen(src) + 2);
+	dst = lenptr = res;
+	dst++;
+
+	for (;;) {
+		c = (uint8_t)*src++;
+		if (c == '.' || c == '\0') {  /* end of label */
+			len = dst - lenptr - 1;
+			/* label too long, too short, or two '.'s in a row? abort */
+			if (len > NS_MAXLABEL || len == 0 || (c == '.' && *src == '.')) {
+				free(res);
+				return NULL;
+			}
+			*lenptr = len;
+			if (c == '\0' || *src == '\0')	/* "" or ".": end of src */
+				break;
+			lenptr = dst++;
+			continue;
+		}
+		if (c >= 'A' && c <= 'Z')  /* uppercase? convert to lower */
+			c += ('a' - 'A');
+		*dst++ = c;
+	}
+
+	if (dst - res >= NS_MAXCDNAME) {  /* dname too long? abort */
+		free(res);
+		return NULL;
+	}
+
+	*dst = 0;
+	return res;
+}
+
+/* Returns the offset within cstr at which dname can be found, or -1 */
+static int find_offset(const uint8_t *cstr, int clen, const uint8_t *dname)
+{
+	const uint8_t *c, *d;
+	int off;
+
+	/* find all labels in cstr */
+	off = 0;
+	while (off < clen) {
+		c = cstr + off;
+
+		if ((*c & NS_CMPRSFLGS) == NS_CMPRSFLGS) {  /* pointer, skip */
+			off += 2;
+			continue;
+		}
+		if (*c) {  /* label, try matching dname */
+			d = dname;
+			while (1) {
+				unsigned len1 = *c + 1;
+				if (memcmp(c, d, len1) != 0)
+					break;
+				if (len1 == 1)  /* at terminating NUL - match, return offset */
+					return off;
+				d += len1;
+				c += len1;
+				if ((*c & NS_CMPRSFLGS) == NS_CMPRSFLGS)  /* pointer, jump */
+					c = cstr + (((c[0] & 0x3f) << 8) | c[1]);
+			}
+			off += cstr[off] + 1;
+			continue;
+		}
+		/* NUL, skip */
+		off++;
+	}
+
+	return -1;
+}
+
+/* Computes string to be appended to cstr so that src would be added to
+ * the compression (best case, it's a 2-byte pointer to some offset within
+ * cstr; worst case, it's all of src, converted to <4>host<3>com<0> format).
+ * The computed string is returned directly; its length is returned via retlen;
+ * NULL and 0, respectively, are returned if an error occurs.
+ */
+uint8_t* FAST_FUNC dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen)
+{
+	uint8_t *d, *dname;
+	int off;
+
+	dname = convert_dname(src);
+	if (dname == NULL) {
+		*retlen = 0;
+		return NULL;
+	}
+
+	d = dname;
+	while (*d) {
+		if (cstr) {
+			off = find_offset(cstr, clen, d);
+			if (off >= 0) {	/* found a match, add pointer and return */
+				*d++ = NS_CMPRSFLGS | (off >> 8);
+				*d = off;
+				break;
+			}
+		}
+		d += *d + 1;
+	}
+
+	*retlen = d - dname + 1;
+	return dname;
+}
+
+#ifdef DNS_COMPR_TESTING
+/* gcc -Wall -DDNS_COMPR_TESTING domain_codec.c -o domain_codec && ./domain_codec */
+int main(int argc, char **argv)
+{
+	int len;
+	uint8_t *encoded;
+
+#define DNAME_DEC(encoded,pre) dname_dec((uint8_t*)(encoded), sizeof(encoded), (pre))
+	printf("'%s'\n",       DNAME_DEC("\4host\3com\0", "test1:"));
+	printf("test2:'%s'\n", DNAME_DEC("\4host\3com\0\4host\3com\0", ""));
+	printf("test3:'%s'\n", DNAME_DEC("\4host\3com\0\xC0\0", ""));
+	printf("test4:'%s'\n", DNAME_DEC("\4host\3com\0\xC0\5", ""));
+	printf("test5:'%s'\n", DNAME_DEC("\4host\3com\0\xC0\5\1z\xC0\xA", ""));
+
+#define DNAME_ENC(cache,source,lenp) dname_enc((uint8_t*)(cache), sizeof(cache), (source), (lenp))
+	encoded = dname_enc(NULL, 0, "test.net", &len);
+	printf("test6:'%s' len:%d\n", dname_dec(encoded, len, ""), len);
+	encoded = DNAME_ENC("\3net\0", "test.net", &len);
+	printf("test7:'%s' len:%d\n", dname_dec(encoded, len, ""), len);
+	encoded = DNAME_ENC("\4test\3net\0", "test.net", &len);
+	printf("test8:'%s' len:%d\n", dname_dec(encoded, len, ""), len);
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/networking/udhcp/dumpleases.c b/busybox-1.19.3/networking/udhcp/dumpleases.c
new file mode 100644
index 0000000..64cd73e
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/dumpleases.c
@@ -0,0 +1,107 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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"
+
+int dumpleases_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dumpleases_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+	int i;
+	unsigned opt;
+	int64_t written_at, curr, expires_abs;
+	const char *file = LEASES_FILE;
+	struct dyn_lease lease;
+	struct in_addr addr;
+
+	enum {
+		OPT_a = 0x1, // -a
+		OPT_r = 0x2, // -r
+		OPT_f = 0x4, // -f
+	};
+#if ENABLE_LONG_OPTS
+	static const char dumpleases_longopts[] ALIGN1 =
+		"absolute\0"  No_argument       "a"
+		"remaining\0" No_argument       "r"
+		"file\0"      Required_argument "f"
+		;
+
+	applet_long_options = dumpleases_longopts;
+#endif
+	init_unicode();
+
+	opt_complementary = "=0:a--r:r--a";
+	opt = getopt32(argv, "arf:", &file);
+
+	fd = xopen(file, O_RDONLY);
+
+	printf("Mac Address       IP Address      Host Name           Expires %s\n", (opt & OPT_a) ? "at" : "in");
+	/*     "00:00:00:00:00:00 255.255.255.255 ABCDEFGHIJKLMNOPQRS Wed Jun 30 21:49:08 1993" */
+	/*     "123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 */
+
+	xread(fd, &written_at, sizeof(written_at));
+	written_at = SWAP_BE64(written_at);
+	curr = time(NULL);
+	if (curr < written_at)
+		written_at = curr; /* lease file from future! :) */
+
+	while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
+		const char *fmt = ":%02x" + 1;
+		for (i = 0; i < 6; i++) {
+			printf(fmt, lease.lease_mac[i]);
+			fmt = ":%02x";
+		}
+		addr.s_addr = lease.lease_nip;
+#if ENABLE_UNICODE_SUPPORT
+		{
+			char *uni_name = unicode_conv_to_printable_fixedwidth(/*NULL,*/ lease.hostname, 19);
+			printf(" %-16s%s ", inet_ntoa(addr), uni_name);
+			free(uni_name);
+		}
+#else
+		/* actually, 15+1 and 19+1, +1 is a space between columns */
+		/* lease.hostname is char[20] and is always NUL terminated */
+		printf(" %-16s%-20s", inet_ntoa(addr), lease.hostname);
+#endif
+		expires_abs = ntohl(lease.expires) + written_at;
+		if (expires_abs <= curr) {
+			puts("expired");
+			continue;
+		}
+		if (!(opt & OPT_a)) { /* no -a */
+			unsigned d, h, m;
+			unsigned expires = expires_abs - curr;
+			d = expires / (24*60*60); expires %= (24*60*60);
+			h = expires / (60*60); expires %= (60*60);
+			m = expires / 60; expires %= 60;
+			if (d)
+				printf("%u days ", d);
+			printf("%02u:%02u:%02u\n", h, m, (unsigned)expires);
+		} else { /* -a */
+			time_t t = expires_abs;
+			fputs(ctime(&t), stdout);
+		}
+	}
+	/* close(fd); */
+
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/udhcp/files.c b/busybox-1.19.3/networking/udhcp/files.c
new file mode 100644
index 0000000..6840f3c
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/files.c
@@ -0,0 +1,216 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * DHCP server config and lease file manipulation
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include <netinet/ether.h>
+
+#include "common.h"
+#include "dhcpd.h"
+
+/* on these functions, make sure your datatype matches */
+static int FAST_FUNC read_str(const char *line, void *arg)
+{
+	char **dest = arg;
+
+	free(*dest);
+	*dest = xstrdup(line);
+	return 1;
+}
+
+static int FAST_FUNC read_u32(const char *line, void *arg)
+{
+	*(uint32_t*)arg = bb_strtou32(line, NULL, 10);
+	return errno == 0;
+}
+
+static int FAST_FUNC read_staticlease(const char *const_line, void *arg)
+{
+	char *line;
+	char *mac_string;
+	char *ip_string;
+	struct ether_addr mac_bytes; /* it's "struct { uint8_t mac[6]; }" */
+	uint32_t nip;
+
+	/* Read mac */
+	line = (char *) const_line;
+	mac_string = strtok_r(line, " \t", &line);
+	if (!mac_string || !ether_aton_r(mac_string, &mac_bytes))
+		return 0;
+
+	/* Read ip */
+	ip_string = strtok_r(NULL, " \t", &line);
+	if (!ip_string || !udhcp_str2nip(ip_string, &nip))
+		return 0;
+
+	add_static_lease(arg, (uint8_t*) &mac_bytes, nip);
+
+	log_static_leases(arg);
+
+	return 1;
+}
+
+
+struct config_keyword {
+	const char *keyword;
+	int (*handler)(const char *line, void *var) FAST_FUNC;
+	void *var;
+	const char *def;
+};
+
+static const struct config_keyword keywords[] = {
+	/* keyword        handler           variable address               default */
+	{"start"        , udhcp_str2nip   , &server_config.start_ip     , "192.168.0.20"},
+	{"end"          , udhcp_str2nip   , &server_config.end_ip       , "192.168.0.254"},
+	{"interface"    , read_str        , &server_config.interface    , "eth0"},
+	/* Avoid "max_leases value not sane" warning by setting default
+	 * to default_end_ip - default_start_ip + 1: */
+	{"max_leases"   , read_u32        , &server_config.max_leases   , "235"},
+	{"auto_time"    , read_u32        , &server_config.auto_time    , "7200"},
+	{"decline_time" , read_u32        , &server_config.decline_time , "3600"},
+	{"conflict_time", read_u32        , &server_config.conflict_time, "3600"},
+	{"offer_time"   , read_u32        , &server_config.offer_time   , "60"},
+	{"min_lease"    , read_u32        , &server_config.min_lease_sec, "60"},
+	{"lease_file"   , read_str        , &server_config.lease_file   , LEASES_FILE},
+	{"pidfile"      , read_str        , &server_config.pidfile      , "/var/run/udhcpd.pid"},
+	{"siaddr"       , udhcp_str2nip   , &server_config.siaddr_nip   , "0.0.0.0"},
+	/* 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  , 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 };
+
+void FAST_FUNC read_config(const char *file)
+{
+	parser_t *parser;
+	const struct config_keyword *k;
+	unsigned i;
+	char *token[2];
+
+	for (i = 0; i < KWS_WITH_DEFAULTS; i++)
+		keywords[i].handler(keywords[i].def, keywords[i].var);
+
+	parser = config_open(file);
+	while (config_read(parser, token, 2, 2, "# \t", PARSE_NORMAL)) {
+		for (k = keywords, i = 0; i < ARRAY_SIZE(keywords); k++, i++) {
+			if (strcasecmp(token[0], k->keyword) == 0) {
+				if (!k->handler(token[1], k->var)) {
+					bb_error_msg("can't parse line %u in %s",
+							parser->lineno, file);
+					/* reset back to the default value */
+					k->handler(k->def, k->var);
+				}
+				break;
+			}
+		}
+	}
+	config_close(parser);
+
+	server_config.start_ip = ntohl(server_config.start_ip);
+	server_config.end_ip = ntohl(server_config.end_ip);
+}
+
+void FAST_FUNC write_leases(void)
+{
+	int fd;
+	unsigned i;
+	leasetime_t curr;
+	int64_t written_at;
+
+	fd = open_or_warn(server_config.lease_file, O_WRONLY|O_CREAT|O_TRUNC);
+	if (fd < 0)
+		return;
+
+	curr = written_at = time(NULL);
+
+	written_at = SWAP_BE64(written_at);
+	full_write(fd, &written_at, sizeof(written_at));
+
+	for (i = 0; i < server_config.max_leases; i++) {
+		leasetime_t tmp_time;
+
+		if (g_leases[i].lease_nip == 0)
+			continue;
+
+		/* Screw with the time in the struct, for easier writing */
+		tmp_time = g_leases[i].expires;
+
+		g_leases[i].expires -= curr;
+		if ((signed_leasetime_t) g_leases[i].expires < 0)
+			g_leases[i].expires = 0;
+		g_leases[i].expires = htonl(g_leases[i].expires);
+
+		/* No error check. If the file gets truncated,
+		 * we lose some leases on restart. Oh well. */
+		full_write(fd, &g_leases[i], sizeof(g_leases[i]));
+
+		/* Then restore it when done */
+		g_leases[i].expires = tmp_time;
+	}
+	close(fd);
+
+	if (server_config.notify_file) {
+		char *argv[3];
+		argv[0] = server_config.notify_file;
+		argv[1] = server_config.lease_file;
+		argv[2] = NULL;
+		spawn_and_wait(argv);
+	}
+}
+
+void FAST_FUNC read_leases(const char *file)
+{
+	struct dyn_lease lease;
+	int64_t written_at, time_passed;
+	int fd;
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+	unsigned i = 0;
+#endif
+
+	fd = open_or_warn(file, O_RDONLY);
+	if (fd < 0)
+		return;
+
+	if (full_read(fd, &written_at, sizeof(written_at)) != sizeof(written_at))
+		goto ret;
+	written_at = SWAP_BE64(written_at);
+
+	time_passed = time(NULL) - written_at;
+	/* Strange written_at, or lease file from old version of udhcpd
+	 * which had no "written_at" field? */
+	if ((uint64_t)time_passed > 12 * 60 * 60)
+		goto ret;
+
+	while (full_read(fd, &lease, sizeof(lease)) == sizeof(lease)) {
+//FIXME: what if it matches some static lease?
+		uint32_t y = ntohl(lease.lease_nip);
+		if (y >= server_config.start_ip && y <= server_config.end_ip) {
+			signed_leasetime_t expires = ntohl(lease.expires) - (signed_leasetime_t)time_passed;
+			if (expires <= 0)
+				continue;
+			/* NB: add_lease takes "relative time", IOW,
+			 * lease duration, not lease deadline. */
+			if (add_lease(lease.lease_mac, lease.lease_nip,
+					expires,
+					lease.hostname, sizeof(lease.hostname)
+				) == 0
+			) {
+				bb_error_msg("too many leases while loading %s", file);
+				break;
+			}
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 1
+			i++;
+#endif
+		}
+	}
+	log1("Read %d leases", i);
+ ret:
+	close(fd);
+}
diff --git a/busybox-1.19.3/networking/udhcp/leases.c b/busybox-1.19.3/networking/udhcp/leases.c
new file mode 100644
index 0000000..c5b60b1
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/leases.c
@@ -0,0 +1,203 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "common.h"
+#include "dhcpd.h"
+
+/* Find the oldest expired lease, NULL if there are no expired leases */
+static struct dyn_lease *oldest_expired_lease(void)
+{
+	struct dyn_lease *oldest_lease = NULL;
+	leasetime_t oldest_time = time(NULL);
+	unsigned i;
+
+	/* Unexpired leases have g_leases[i].expires >= current time
+	 * and therefore can't ever match */
+	for (i = 0; i < server_config.max_leases; i++) {
+		if (g_leases[i].expires < oldest_time) {
+			oldest_time = g_leases[i].expires;
+			oldest_lease = &g_leases[i];
+		}
+	}
+	return oldest_lease;
+}
+
+/* Clear out all leases with matching nonzero chaddr OR yiaddr.
+ * If chaddr == NULL, this is a conflict lease.
+ */
+static void clear_leases(const uint8_t *chaddr, uint32_t yiaddr)
+{
+	unsigned i;
+
+	for (i = 0; i < server_config.max_leases; i++) {
+		if ((chaddr && memcmp(g_leases[i].lease_mac, chaddr, 6) == 0)
+		 || (yiaddr && g_leases[i].lease_nip == yiaddr)
+		) {
+			memset(&g_leases[i], 0, sizeof(g_leases[i]));
+		}
+	}
+}
+
+/* Add a lease into the table, clearing out any old ones.
+ * If chaddr == NULL, this is a conflict lease.
+ */
+struct dyn_lease* FAST_FUNC add_lease(
+		const uint8_t *chaddr, uint32_t yiaddr,
+		leasetime_t leasetime,
+		const char *hostname, int hostname_len)
+{
+	struct dyn_lease *oldest;
+
+	/* clean out any old ones */
+	clear_leases(chaddr, yiaddr);
+
+	oldest = oldest_expired_lease();
+
+	if (oldest) {
+		memset(oldest, 0, sizeof(*oldest));
+		if (hostname) {
+			char *p;
+
+			hostname_len++; /* include NUL */
+			if (hostname_len > sizeof(oldest->hostname))
+				hostname_len = sizeof(oldest->hostname);
+			p = safe_strncpy(oldest->hostname, hostname, hostname_len);
+			/* sanitization (s/non-ASCII/^/g) */
+			while (*p) {
+				if (*p < ' ' || *p > 126)
+					*p = '^';
+				p++;
+			}
+		}
+		if (chaddr)
+			memcpy(oldest->lease_mac, chaddr, 6);
+		oldest->lease_nip = yiaddr;
+		oldest->expires = time(NULL) + leasetime;
+	}
+
+	return oldest;
+}
+
+/* True if a lease has expired */
+int FAST_FUNC is_expired_lease(struct dyn_lease *lease)
+{
+	return (lease->expires < (leasetime_t) time(NULL));
+}
+
+/* Find the first lease that matches MAC, NULL if no match */
+struct dyn_lease* FAST_FUNC find_lease_by_mac(const uint8_t *mac)
+{
+	unsigned i;
+
+	for (i = 0; i < server_config.max_leases; i++)
+		if (memcmp(g_leases[i].lease_mac, mac, 6) == 0)
+			return &g_leases[i];
+
+	return NULL;
+}
+
+/* Find the first lease that matches IP, NULL is no match */
+struct dyn_lease* FAST_FUNC find_lease_by_nip(uint32_t nip)
+{
+	unsigned i;
+
+	for (i = 0; i < server_config.max_leases; i++)
+		if (g_leases[i].lease_nip == nip)
+			return &g_leases[i];
+
+	return NULL;
+}
+
+/* Check if the IP is taken; if it is, add it to the lease table */
+static int nobody_responds_to_arp(uint32_t nip, const uint8_t *safe_mac)
+{
+	struct in_addr temp;
+	int r;
+
+	r = arpping(nip, safe_mac,
+			server_config.server_nip,
+			server_config.server_mac,
+			server_config.interface);
+	if (r)
+		return r;
+
+	temp.s_addr = nip;
+	bb_info_msg("%s belongs to someone, reserving it for %u seconds",
+		inet_ntoa(temp), (unsigned)server_config.conflict_time);
+	add_lease(NULL, nip, server_config.conflict_time, NULL, 0);
+	return 0;
+}
+
+/* Find a new usable (we think) address */
+uint32_t FAST_FUNC find_free_or_expired_nip(const uint8_t *safe_mac)
+{
+	uint32_t addr;
+	struct dyn_lease *oldest_lease = NULL;
+
+#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)
+			goto next_addr;
+		/* ie, 192.168.55.255 */
+		if ((addr & 0xff) == 0xff)
+			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))
+			goto next_addr;
+
+		lease = find_lease_by_nip(nip);
+		if (!lease) {
+//TODO: DHCP servers do not always sit on the same subnet as clients: should *ping*, not arp-ping!
+			if (nobody_responds_to_arp(nip, safe_mac))
+				return nip;
+		} else {
+			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)
+	 && nobody_responds_to_arp(oldest_lease->lease_nip, safe_mac)
+	) {
+		return oldest_lease->lease_nip;
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/udhcp/packet.c b/busybox-1.19.3/networking/udhcp/packet.c
new file mode 100644
index 0000000..66b42c5
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/packet.c
@@ -0,0 +1,284 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Packet ops
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#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)
+{
+	memset(packet, 0, sizeof(*packet));
+	packet->op = BOOTREQUEST; /* if client to a server */
+	switch (type) {
+	case DHCPOFFER:
+	case DHCPACK:
+	case DHCPNAK:
+		packet->op = BOOTREPLY; /* if server to client */
+	}
+	packet->htype = 1; /* ethernet */
+	packet->hlen = 6;
+	packet->cookie = htonl(DHCP_MAGIC);
+	if (DHCP_END != 0)
+		packet->options[0] = DHCP_END;
+	udhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
+}
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+void FAST_FUNC udhcp_dump_packet(struct dhcp_packet *packet)
+{
+	char buf[sizeof(packet->chaddr)*2 + 1];
+
+	if (dhcp_verbose < 2)
+		return;
+
+	bb_info_msg(
+		//" op %x"
+		//" htype %x"
+		" hlen %x"
+		//" hops %x"
+		" xid %x"
+		//" secs %x"
+		//" flags %x"
+		" ciaddr %x"
+		" yiaddr %x"
+		" siaddr %x"
+		" giaddr %x"
+		//" chaddr %s"
+		//" sname %s"
+		//" file %s"
+		//" cookie %x"
+		//" options %s"
+		//, packet->op
+		//, packet->htype
+		, packet->hlen
+		//, packet->hops
+		, packet->xid
+		//, packet->secs
+		//, packet->flags
+		, packet->ciaddr
+		, packet->yiaddr
+		, packet->siaddr_nip
+		, packet->gateway_nip
+		//, packet->chaddr[16]
+		//, packet->sname[64]
+		//, packet->file[128]
+		//, packet->cookie
+		//, packet->options[]
+	);
+	*bin2hex(buf, (void *) packet->chaddr, sizeof(packet->chaddr)) = '\0';
+	bb_info_msg(" chaddr %s", buf);
+}
+#endif
+
+/* Read a packet from socket fd, return -1 on read error, -2 on packet error */
+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));
+	if (bytes < 0) {
+		log1("Packet read error, ignoring");
+		return bytes; /* returns -1 */
+	}
+
+	if (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,
+		uint32_t dest_nip, int dest_port, const uint8_t *dest_arp,
+		int ifindex)
+{
+	struct sockaddr_ll dest_sll;
+	struct ip_udp_dhcp_packet packet;
+	unsigned padding;
+	int fd;
+	int result = -1;
+	const char *msg;
+
+	fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+	if (fd < 0) {
+		msg = "socket(%s)";
+		goto ret_msg;
+	}
+
+	memset(&dest_sll, 0, sizeof(dest_sll));
+	memset(&packet, 0, offsetof(struct ip_udp_dhcp_packet, data));
+	packet.data = *dhcp_pkt; /* struct copy */
+
+	dest_sll.sll_family = AF_PACKET;
+	dest_sll.sll_protocol = htons(ETH_P_IP);
+	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;
+	}
+
+	/* We were sending full-sized DHCP packets (zero padded),
+	 * but some badly configured servers were seen dropping them.
+	 * Apparently they drop all DHCP packets >576 *ethernet* octets big,
+	 * whereas they may only drop packets >576 *IP* octets big
+	 * (which for typical Ethernet II means 590 octets: 6+6+2 + 576).
+	 *
+	 * In order to work with those buggy servers,
+	 * we truncate packets after end option byte.
+	 */
+	padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(packet.data.options);
+
+	packet.ip.protocol = IPPROTO_UDP;
+	packet.ip.saddr = source_nip;
+	packet.ip.daddr = dest_nip;
+	packet.udp.source = htons(source_port);
+	packet.udp.dest = htons(dest_port);
+	/* size, excluding IP header: */
+	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_UDP_DHCP_SIZE - padding);
+	/* but for sending, it is set to IP packet len */
+	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));
+
+	udhcp_dump_packet(dhcp_pkt);
+	result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*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 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;
+	unsigned padding;
+	int fd;
+	int result = -1;
+	const char *msg;
+
+	fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (fd < 0) {
+		msg = "socket(%s)";
+		goto ret_msg;
+	}
+	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) {
+		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) {
+		msg = "connect";
+		goto ret_close;
+	}
+
+	udhcp_dump_packet(dhcp_pkt);
+
+	padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(dhcp_pkt->options);
+	result = safe_write(fd, dhcp_pkt, DHCP_SIZE - padding);
+	msg = "write";
+ ret_close:
+	close(fd);
+	if (result < 0) {
+ ret_msg:
+		bb_perror_msg(msg, "UDP");
+	}
+	return result;
+}
diff --git a/busybox-1.19.3/networking/udhcp/signalpipe.c b/busybox-1.19.3/networking/udhcp/signalpipe.c
new file mode 100644
index 0000000..6355c5e
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/signalpipe.c
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Signal pipe infrastructure. A reliable way of delivering signals.
+ *
+ * Russ Dill <Russ.Dill@asu.edu> December 2003
+ *
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include "common.h"
+
+/* Global variable: we access it from signal handler */
+static struct fd_pair signal_pipe;
+
+static void signal_handler(int sig)
+{
+	unsigned char ch = sig; /* use char, avoid dealing with partial writes */
+	if (write(signal_pipe.wr, &ch, 1) != 1)
+		bb_perror_msg("can't send signal");
+}
+
+/* Call this before doing anything else. Sets up the socket pair
+ * and installs the signal handler */
+void FAST_FUNC udhcp_sp_setup(void)
+{
+	/* was socketpair, but it needs AF_UNIX in kernel */
+	xpiped_pair(signal_pipe);
+	close_on_exec_on(signal_pipe.rd);
+	close_on_exec_on(signal_pipe.wr);
+	ndelay_on(signal_pipe.wr);
+	bb_signals(0
+		+ (1 << SIGUSR1)
+		+ (1 << SIGUSR2)
+		+ (1 << SIGTERM)
+		, signal_handler);
+}
+
+/* Quick little function to setup the rfds. Will return the
+ * max_fd for use with select. Limited in that you can only pass
+ * one extra fd */
+int FAST_FUNC udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
+{
+	FD_ZERO(rfds);
+	FD_SET(signal_pipe.rd, rfds);
+	if (extra_fd >= 0) {
+		close_on_exec_on(extra_fd);
+		FD_SET(extra_fd, rfds);
+	}
+	return signal_pipe.rd > extra_fd ? signal_pipe.rd : extra_fd;
+}
+
+/* Read a signal from the signal pipe. Returns 0 if there is
+ * no signal, -1 on error (and sets errno appropriately), and
+ * your signal on success */
+int FAST_FUNC udhcp_sp_read(const fd_set *rfds)
+{
+	unsigned char sig;
+
+	if (!FD_ISSET(signal_pipe.rd, rfds))
+		return 0;
+
+	if (safe_read(signal_pipe.rd, &sig, 1) != 1)
+		return -1;
+
+	return sig;
+}
diff --git a/busybox-1.19.3/networking/udhcp/socket.c b/busybox-1.19.3/networking/udhcp/socket.c
new file mode 100644
index 0000000..a5220ba
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/socket.c
@@ -0,0 +1,102 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * DHCP server client/server socket creation
+ *
+ * udhcp client/server
+ * Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
+ *			Chris Trew <ctrew@moreton.com.au>
+ *
+ * Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
+ *
+ * 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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#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 sockaddr_in *our_ip;
+
+	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);
+	if (nip) {
+		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;
+		*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) {
+			close(fd);
+			return -1;
+		}
+		log1("Adapter index %d", ifr->ifr_ifindex);
+		*ifindex = ifr->ifr_ifindex;
+	}
+
+	if (mac) {
+		if (ioctl_or_warn(fd, SIOCGIFHWADDR, ifr) != 0) {
+			close(fd);
+			return -1;
+		}
+		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]);
+	}
+
+	close(fd);
+	return 0;
+}
+
+/* 1. None of the callers expects it to ever fail */
+/* 2. ip was always INADDR_ANY */
+int FAST_FUNC udhcp_listen_socket(/*uint32_t ip,*/ int port, const char *inf)
+{
+	int fd;
+	struct sockaddr_in addr;
+
+	log1("Opening listen socket on *:%d %s", port, inf);
+	fd = xsocket(PF_INET, 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.sin_family = AF_INET;
+	addr.sin_port = htons(port);
+	/* addr.sin_addr.s_addr = ip; - all-zeros is INADDR_ANY */
+	xbind(fd, (struct sockaddr *)&addr, sizeof(addr));
+
+	return fd;
+}
diff --git a/busybox-1.19.3/networking/udhcp/static_leases.c b/busybox-1.19.3/networking/udhcp/static_leases.c
new file mode 100644
index 0000000..f4a24ab
--- /dev/null
+++ b/busybox-1.19.3/networking/udhcp/static_leases.c
@@ -0,0 +1,77 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Storing and retrieving data for static leases
+ *
+ * Wade Berrier <wberrier@myrealbox.com> September 2004
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "common.h"
+#include "dhcpd.h"
+
+/* Takes the address of the pointer to the static_leases linked list,
+ * address to a 6 byte mac address,
+ * 4 byte IP address */
+void FAST_FUNC add_static_lease(struct static_lease **st_lease_pp,
+		uint8_t *mac,
+		uint32_t nip)
+{
+	struct static_lease *st_lease;
+
+	/* Find the tail of the list */
+	while ((st_lease = *st_lease_pp) != NULL) {
+		st_lease_pp = &st_lease->next;
+	}
+
+	/* Add new node */
+	*st_lease_pp = st_lease = xzalloc(sizeof(*st_lease));
+	memcpy(st_lease->mac, mac, 6);
+	st_lease->nip = nip;
+	/*st_lease->next = NULL;*/
+}
+
+/* Find static lease IP by mac */
+uint32_t FAST_FUNC get_static_nip_by_mac(struct static_lease *st_lease, void *mac)
+{
+	while (st_lease) {
+		if (memcmp(st_lease->mac, mac, 6) == 0)
+			return st_lease->nip;
+		st_lease = st_lease->next;
+	}
+
+	return 0;
+}
+
+/* Check to see if an IP is reserved as a static IP */
+int FAST_FUNC is_nip_reserved(struct static_lease *st_lease, uint32_t nip)
+{
+	while (st_lease) {
+		if (st_lease->nip == nip)
+			return 1;
+		st_lease = st_lease->next;
+	}
+
+	return 0;
+}
+
+#if defined CONFIG_UDHCP_DEBUG && CONFIG_UDHCP_DEBUG >= 2
+/* Print out static leases just to check what's going on */
+/* Takes the address of the pointer to the static_leases linked list */
+void FAST_FUNC log_static_leases(struct static_lease **st_lease_pp)
+{
+	struct static_lease *cur;
+
+	if (dhcp_verbose < 2)
+		return;
+
+	cur = *st_lease_pp;
+	while (cur) {
+		bb_info_msg("static lease: mac:%02x:%02x:%02x:%02x:%02x:%02x nip:%x",
+			cur->mac[0], cur->mac[1], cur->mac[2],
+			cur->mac[3], cur->mac[4], cur->mac[5],
+			cur->nip
+		);
+		cur = cur->next;
+	}
+}
+#endif
diff --git a/busybox-1.19.3/networking/vconfig.c b/busybox-1.19.3/networking/vconfig.c
new file mode 100644
index 0000000..48b45d9
--- /dev/null
+++ b/busybox-1.19.3/networking/vconfig.c
@@ -0,0 +1,173 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * vconfig implementation for busybox
+ *
+ * Copyright (C) 2001  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * 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		[interface-name] [vlan_id]"
+//usage:     "\n	rem		[vlan-name]"
+//usage:     "\n	set_flag	[interface-name] [flag-num] [0 | 1]"
+//usage:     "\n	set_egress_map	[vlan-name] [skb_priority] [vlan_qos]"
+//usage:     "\n	set_ingress_map	[vlan-name] [skb_priority] [vlan_qos]"
+//usage:     "\n	set_name_type	[name-type]"
+
+#include "libbb.h"
+#include <net/if.h>
+
+/* Stuff from linux/if_vlan.h, kernel version 2.4.23 */
+enum vlan_ioctl_cmds {
+	ADD_VLAN_CMD,
+	DEL_VLAN_CMD,
+	SET_VLAN_INGRESS_PRIORITY_CMD,
+	SET_VLAN_EGRESS_PRIORITY_CMD,
+	GET_VLAN_INGRESS_PRIORITY_CMD,
+	GET_VLAN_EGRESS_PRIORITY_CMD,
+	SET_VLAN_NAME_TYPE_CMD,
+	SET_VLAN_FLAG_CMD
+};
+enum vlan_name_types {
+	VLAN_NAME_TYPE_PLUS_VID, /* Name will look like:  vlan0005 */
+	VLAN_NAME_TYPE_RAW_PLUS_VID, /* name will look like:  eth1.0005 */
+	VLAN_NAME_TYPE_PLUS_VID_NO_PAD, /* Name will look like:  vlan5 */
+	VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD, /* Name will look like:  eth0.5 */
+	VLAN_NAME_TYPE_HIGHEST
+};
+
+struct vlan_ioctl_args {
+	int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
+	char device1[24];
+
+	union {
+		char device2[24];
+		int VID;
+		unsigned int skb_priority;
+		unsigned int name_type;
+		unsigned int bind_type;
+		unsigned int flag; /* Matches vlan_dev_info flags */
+	} u;
+
+	short vlan_qos;
+};
+
+#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) {
+			bb_show_usage();
+		}
+	}
+	return table - 1;
+}
+
+static const char cmds[] ALIGN1 = {
+	4, ADD_VLAN_CMD, 7,
+	'a', 'd', 'd', 0,
+	3, DEL_VLAN_CMD, 7,
+	'r', 'e', 'm', 0,
+	3, SET_VLAN_NAME_TYPE_CMD, 17,
+	'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,
+	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,
+};
+
+static const char name_types[] ALIGN1 = {
+	VLAN_NAME_TYPE_PLUS_VID, 16,
+	'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,
+	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,
+};
+
+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)
+{
+	struct vlan_ioctl_args ifr;
+	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);
+	ifr.cmd = *p;
+	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]);
+	} else {
+		strncpy_IFNAMSIZ(ifr.device1, argv[1]);
+		p = argv[2];
+
+		/* I suppose one could try to combine some of the function calls below,
+		 * 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);
+			/* 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.u.skb_priority = xatou(p);
+			ifr.vlan_qos = xatoul_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);
+
+	return 0;
+}
diff --git a/busybox-1.19.3/networking/wget.c b/busybox-1.19.3/networking/wget.c
new file mode 100644
index 0000000..6443705
--- /dev/null
+++ b/busybox-1.19.3/networking/wget.c
@@ -0,0 +1,941 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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.
+ *
+ * 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"
+//usage:       "	[--no-check-certificate] [-U|--user-agent AGENT]"
+//usage:			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"
+
+//#define log_io(...) bb_error_msg(__VA_ARGS__)
+#define log_io(...) ((void)0)
+
+
+struct host_info {
+	char *allocated;
+	const char *path;
+	const char *user;
+	char       *host;
+	int         port;
+	smallint    is_ftp;
+};
+
+
+/* Globals */
+struct globals {
+	off_t content_len;        /* Content-length of the file */
+	off_t beg_range;          /* Range at which continue begins */
+#if ENABLE_FEATURE_WGET_STATUSBAR
+	off_t transferred;        /* Number of bytes transferred so far */
+	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;
+#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 (*ptr_to_globals)
+#define INIT_G() do { \
+        SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;) \
+} while (0)
+
+
+/* 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)
+{
+	if (option_mask32 & WGET_OPT_QUIET)
+		return;
+
+	if (flag == PROGRESS_START)
+		bb_progress_init(&G.pmt, G.curfile);
+
+	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 == PROGRESS_END) {
+		bb_progress_free(&G.pmt);
+		bb_putchar_stderr('\n');
+		G.transferred = 0;
+	}
+}
+#else
+static ALWAYS_INLINE void progress_meter(int flag UNUSED_PARAM) { }
+#endif
+
+
+/* IPv6 knows scoped address types i.e. link and site local addresses. Link
+ * local addresses can have a scope identifier to specify the
+ * interface/link an address is valid on (e.g. fe80::1%eth0). This scope
+ * identifier is only valid on a single node.
+ *
+ * RFC 4007 says that the scope identifier MUST NOT be sent across the wire,
+ * unless all nodes agree on the semantic. Apache e.g. regards zone identifiers
+ * in the Host header as invalid requests, see
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=35122
+ */
+static void strip_ipv6_scope_id(char *host)
+{
+	char *scope, *cp;
+
+	/* bbox wget actually handles IPv6 addresses without [], like
+	 * wget "http://::1/xxx", but this is not standard.
+	 * To save code, _here_ we do not support it. */
+
+	if (host[0] != '[')
+		return; /* not IPv6 */
+
+	scope = strchr(host, '%');
+	if (!scope)
+		return;
+
+	/* Remove the IPv6 zone identifier from the host address */
+	cp = strchr(host, ']');
+	if (!cp || (cp[1] != ':' && cp[1] != '\0')) {
+		/* malformed address (not "[xx]:nn" or "[xx]") */
+		return;
+	}
+
+	/* cp points to "]...", scope points to "%eth0]..." */
+	overlapping_strcpy(scope, cp);
+}
+
+#if ENABLE_FEATURE_WGET_AUTHENTICATION
+/* Base64-encode character string. */
+static char *base64enc(const char *str)
+{
+	unsigned len = strlen(str);
+	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
+
+static char* sanitize_string(char *s)
+{
+	unsigned char *p = (void *) s;
+	while (*p >= ' ')
+		p++;
+	*p = '\0';
+	return s;
+}
+
+static FILE *open_socket(len_and_sockaddr *lsa)
+{
+	FILE *fp;
+
+	/* glibc 2.4 seems to try seeking on it - ??! */
+	/* hopefully it understands what ESPIPE means... */
+	fp = fdopen(xconnect_stream(lsa), "r+");
+	if (fp == NULL)
+		bb_perror_msg_and_die(bb_msg_memory_exhausted);
+
+	return fp;
+}
+
+/* Returns '\n' if it was seen, else '\0'. Trims at first '\r' or '\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 = "";
+		fprintf(fp, "%s%s\r\n", s1, s2);
+		fflush(fp);
+		log_io("> %s%s", s1, s2);
+	}
+
+	do {
+		fgets_and_trim(fp);
+	} while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
+
+	G.wget_buf[3] = '\0';
+	result = xatoi_positive(G.wget_buf);
+	G.wget_buf[3] = ' ';
+	return result;
+}
+
+static void parse_url(const char *src_url, struct host_info *h)
+{
+	char *url, *p, *sp;
+
+	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) {
+		h->port = bb_lookup_port("ftp", "tcp", 21);
+		h->host = url + 6;
+		h->is_ftp = 1;
+	} else
+		bb_error_msg_and_die("not an http or ftp url: %s", sanitize_string(url));
+
+	// FYI:
+	// "Real" wget 'http://busybox.net?var=a/b' sends this request:
+	//   'GET /?var=a/b HTTP 1.0'
+	//   and saves 'index.html?var=a%2Fb' (we save 'b')
+	// wget 'http://busybox.net?login=john@doe':
+	//   request: 'GET /?login=john@doe HTTP/1.0'
+	//   saves: 'index.html?login=john@doe' (we save '?login=john@doe')
+	// wget 'http://busybox.net#test/test':
+	//   request: 'GET / HTTP/1.0'
+	//   saves: 'index.html' (we save 'test')
+	//
+	// We also don't add unique .N suffix if file exists...
+	sp = strchr(h->host, '/');
+	p = strchr(h->host, '?'); if (!sp || (p && sp > p)) sp = p;
+	p = strchr(h->host, '#'); if (!sp || (p && sp > p)) sp = p;
+	if (!sp) {
+		h->path = "";
+	} else if (*sp == '/') {
+		*sp = '\0';
+		h->path = sp + 1;
+	} else { // '#' or '?'
+		// http://busybox.net?login=john@doe is a valid URL
+		// memmove converts to:
+		// http:/busybox.nett?login=john@doe...
+		memmove(h->host - 1, h->host, sp - h->host);
+		h->host--;
+		sp[-1] = '\0';
+		h->path = sp;
+	}
+
+	// We used to set h->user to NULL here, but this interferes
+	// with handling of code 302 ("object was moved")
+
+	sp = strrchr(h->host, '@');
+	if (sp != NULL) {
+		h->user = h->host;
+		*sp = '\0';
+		h->host = sp + 1;
+	}
+
+	sp = h->host;
+}
+
+static char *gethdr(FILE *fp)
+{
+	char *s, *hdrval;
+	int c;
+
+	/* *istrunc = 0; */
+
+	/* retrieve header line */
+	c = fgets_and_trim(fp);
+
+	/* end of the headers? */
+	if (G.wget_buf[0] == '\0')
+		return NULL;
+
+	/* convert the header name to lower case */
+	for (s = G.wget_buf; isalnum(*s) || *s == '-' || *s == '.'; ++s) {
+		/* tolower for "A-Z", no-op for "0-9a-z-." */
+		*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(G.wget_buf));
+
+	/* locate the start of the header value */
+	*s++ = '\0';
+	hdrval = skip_whitespace(s);
+
+	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;
+	}
+
+	return hdrval;
+}
+
+static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
+{
+	FILE *sfp;
+	char *str;
+	int port;
+
+	if (!target->user)
+		target->user = xstrdup("anonymous:busybox@");
+
+	sfp = open_socket(lsa);
+	if (ftpcmd(NULL, NULL, sfp) != 220)
+		bb_error_msg_and_die("%s", sanitize_string(G.wget_buf + 4));
+
+	/*
+	 * Splitting username:password pair,
+	 * trying to log in
+	 */
+	str = strchr(target->user, ':');
+	if (str)
+		*str++ = '\0';
+	switch (ftpcmd("USER ", target->user, sfp)) {
+	case 230:
+		break;
+	case 331:
+		if (ftpcmd("PASS ", str, sfp) == 230)
+			break;
+		/* fall through (failed login) */
+	default:
+		bb_error_msg_and_die("ftp login: %s", sanitize_string(G.wget_buf + 4));
+	}
+
+	ftpcmd("TYPE I", NULL, sfp);
+
+	/*
+	 * Querying file size
+	 */
+	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");
+		}
+		G.got_clen = 1;
+	}
+
+	/*
+	 * Entering passive mode
+	 */
+	if (ftpcmd("PASV", NULL, sfp) != 227) {
+ pasv_error:
+		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(G.wget_buf, ')');
+	if (str) str[0] = '\0';
+	str = strrchr(G.wget_buf, ',');
+	if (!str) goto pasv_error;
+	port = xatou_range(str+1, 0, 255);
+	*str = '\0';
+	str = strrchr(G.wget_buf, ',');
+	if (!str) goto pasv_error;
+	port += xatou_range(str+1, 0, 255) * 256;
+	set_nport(&lsa->u.sa, htons(port));
+
+	*dfpp = open_socket(lsa);
+
+	if (G.beg_range) {
+		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;
+	}
+
+	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;
+}
+
+static void NOINLINE retrieve_file_data(FILE *dfp)
+{
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+# if ENABLE_FEATURE_WGET_TIMEOUT
+	unsigned second_cnt;
+# 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() call.
+		 */
+		ndelay_on(polldata.fd);
+#endif
+		while (1) {
+			int n;
+			unsigned rdsz;
+
+			rdsz = sizeof(G.wget_buf);
+			if (G.got_clen) {
+				if (G.content_len < (off_t)sizeof(G.wget_buf)) {
+					if ((int)G.content_len <= 0)
+						break;
+					rdsz = (unsigned)G.content_len;
+				}
+			}
+
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+# if ENABLE_FEATURE_WGET_TIMEOUT
+			second_cnt = G.timeout_seconds;
+# endif
+			while (1) {
+				if (safe_poll(&polldata, 1, 1000) != 0)
+					break; /* error, EOF, or data is available */
+# 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
+				/* Needed for "stalled" indicator */
+				progress_meter(PROGRESS_BUMP);
+			}
+
+			/* 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);
+			errno = 0;
+#endif
+			n = fread(G.wget_buf, 1, rdsz, dfp);
+			/* 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 (n <= 0) {
+#if ENABLE_FEATURE_WGET_STATUSBAR || ENABLE_FEATURE_WGET_TIMEOUT
+				if (errno == EAGAIN) /* poll lied, there is no data? */
+					continue; /* yes */
+#endif
+				if (ferror(dfp))
+					bb_perror_msg_and_die(bb_msg_read_error);
+				break; /* EOF, not error */
+			}
+
+			xwrite(G.output_fd, G.wget_buf, n);
+
+#if ENABLE_FEATURE_WGET_STATUSBAR
+			G.transferred += n;
+			progress_meter(PROGRESS_BUMP);
+#endif
+			if (G.got_clen) {
+				G.content_len -= n;
+				if (G.content_len == 0)
+					break;
+			}
+		}
+#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;
+
+		fgets_and_trim(dfp); /* Eat empty line */
+ get_clen:
+		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;
+	}
+
+	/* 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);
+}
+
+static void download_one_url(const char *url)
+{
+	bool use_proxy;                 /* Use proxies if env vars are set  */
+	int redir_limit;
+	len_and_sockaddr *lsa;
+	FILE *sfp;                      /* socket to web/ftp server         */
+	FILE *dfp;                      /* socket to ftp server (data)      */
+	char *proxy = NULL;
+	char *fname_out_alloc;
+	struct host_info server;
+	struct host_info target;
+
+	server.allocated = NULL;
+	target.allocated = NULL;
+	server.user = NULL;
+	target.user = NULL;
+
+	parse_url(url, &target);
+
+	/* Use the proxy if necessary */
+	use_proxy = (strcmp(G.proxy_flag, "off") != 0);
+	if (use_proxy) {
+		proxy = getenv(target.is_ftp ? "ftp_proxy" : "http_proxy");
+		use_proxy = (proxy && proxy[0]);
+		if (use_proxy)
+			parse_url(proxy, &server);
+	}
+	if (!use_proxy) {
+		server.port = target.port;
+		if (ENABLE_FEATURE_IPV6) {
+			//free(server.allocated); - can't be non-NULL
+			server.host = server.allocated = xstrdup(target.host);
+		} else {
+			server.host = target.host;
+		}
+	}
+
+	if (ENABLE_FEATURE_IPV6)
+		strip_ipv6_scope_id(target.host);
+
+	/* 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 (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 */
+		else {
+			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(G.fname_out);
+#endif
+
+	/* Determine where to start transfer */
+	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 remote side */
+	}
+
+	redir_limit = 5;
+ resolve_lsa:
+	lsa = xhost2sockaddr(server.host, server.port);
+	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
+		 */
+		char *str;
+		int status;
+
+
+		/* Open socket to http server */
+		sfp = open_socket(lsa);
+
+		/* Send HTTP request */
+		if (use_proxy) {
+			fprintf(sfp, "GET %stp://%s/%s HTTP/1.1\r\n",
+				target.is_ftp ? "f" : "ht", target.host,
+				target.path);
+		} else {
+			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, 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) {
+//TODO: 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", instead of what we send now:
+// Authorization: Basic dGVzdDpteSUyMHBhc3M=
+//Can reuse decodeString() from httpd.c
+			fprintf(sfp, "Proxy-Authorization: Basic %s\r\n"+6,
+				base64enc(target.user));
+		}
+		if (use_proxy && server.user) {
+			fprintf(sfp, "Proxy-Authorization: Basic %s\r\n",
+				base64enc(server.user));
+		}
+#endif
+
+		if (G.beg_range)
+			fprintf(sfp, "Range: bytes=%"OFF_FMT"u-\r\n", G.beg_range);
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+		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
+		{
+			fprintf(sfp, "\r\n");
+		}
+
+		fflush(sfp);
+
+		/*
+		 * Retrieve HTTP response line and check for "200" status code.
+		 */
+ read_response:
+		fgets_and_trim(sfp);
+
+		str = G.wget_buf;
+		str = skip_non_whitespace(str);
+		str = skip_whitespace(str);
+		// FIXME: no error check
+		// xatou wouldn't work: "200 OK"
+		status = atoi(str);
+		switch (status) {
+		case 0:
+		case 100:
+			while (gethdr(sfp) != NULL)
+				/* eat all remaining headers */;
+			goto read_response;
+		case 200:
+/*
+Response 204 doesn't say "null file", it says "metadata
+has changed but data didn't":
+
+"10.2.5 204 No Content
+The server has fulfilled the request but does not need to return
+an entity-body, and might want to return updated metainformation.
+The response MAY include new or updated metainformation in the form
+of entity-headers, which if present SHOULD be associated with
+the requested variant.
+
+If the client is a user agent, it SHOULD NOT change its document
+view from that which caused the request to be sent. This response
+is primarily intended to allow input for actions to take place
+without causing a change to the user agent's active document view,
+although any new or updated metainformation SHOULD be applied
+to the document currently in the user agent's active view.
+
+The 204 response MUST NOT include a message-body, and thus
+is always terminated by the first empty line after the header fields."
+
+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:
+			break;
+		case 300:  /* redirection */
+		case 301:
+		case 302:
+		case 303:
+			break;
+		case 206:
+			if (G.beg_range)
+				break;
+			/* fall through */
+		default:
+			bb_error_msg_and_die("server returned error: %s", sanitize_string(G.wget_buf));
+		}
+
+		/*
+		 * Retrieve HTTP headers.
+		 */
+		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, G.wget_buf) + 1;
+			if (key == KEY_content_length) {
+				G.content_len = BB_STRTOOFF(str, NULL, 10);
+				if (G.content_len < 0 || errno) {
+					bb_error_msg_and_die("content-length %s is garbage", sanitize_string(str));
+				}
+				G.got_clen = 1;
+				continue;
+			}
+			if (key == KEY_transfer_encoding) {
+				if (strcmp(str_tolower(str), "chunked") != 0)
+					bb_error_msg_and_die("transfer encoding '%s' is not supported", sanitize_string(str));
+				G.chunked = 1;
+			}
+			if (key == KEY_location && status >= 300) {
+				if (--redir_limit == 0)
+					bb_error_msg_and_die("too many redirections");
+				fclose(sfp);
+				if (str[0] == '/') {
+					free(target.allocated);
+					target.path = target.allocated = xstrdup(str+1);
+					/* lsa stays the same: it's on the same server */
+				} 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 */
+						server.port = target.port;
+						free(lsa);
+						goto resolve_lsa;
+					} /* else: lsa stays the same: we use proxy */
+				}
+				goto establish_session;
+			}
+		}
+//		if (status >= 300)
+//			bb_error_msg_and_die("bad redirection (no Location: header from server)");
+
+		/* For HTTP, data is pumped over the same connection */
+		dfp = sfp;
+
+	} else {
+		/*
+		 *  FTP session
+		 */
+		sfp = prepare_ftp_session(&dfp, &target, lsa);
+	}
+
+	free(lsa);
+
+	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;
+		}
+	}
+
+	if (dfp != sfp) {
+		/* It's ftp. Close data connection properly */
+		fclose(dfp);
+		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);
+}
+
+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"
+		;
+#endif
+
+#if ENABLE_FEATURE_WGET_LONG_OPTIONS
+	llist_t *headers_llist = NULL;
+#endif
+
+	INIT_G();
+
+	IF_FEATURE_WGET_TIMEOUT(G.timeout_seconds = 900;)
+	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);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/networking/whois.c b/busybox-1.19.3/networking/whois.c
new file mode 100644
index 0000000..bf33033
--- /dev/null
+++ b/busybox-1.19.3/networking/whois.c
@@ -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;
+}
diff --git a/busybox-1.19.3/networking/zcip.c b/busybox-1.19.3/networking/zcip.c
new file mode 100644
index 0000000..8a35eca
--- /dev/null
+++ b/busybox-1.19.3/networking/zcip.c
@@ -0,0 +1,576 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * RFC3927 ZeroConf IPv4 Link-Local addressing
+ * (see <http://www.zeroconf.org/>)
+ *
+ * Copyright (C) 2003 by Arthur van Hoff (avh@strangeberry.com)
+ * Copyright (C) 2004 by David Brownell
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * ZCIP just manages the 169.254.*.* addresses.  That network is not
+ * routed at the IP level, though various proxies or bridges can
+ * certainly be used.  Its naming is built over multicast DNS.
+ */
+
+//#define DEBUG
+
+// TODO:
+// - more real-world usage/testing, especially daemon mode
+// - kernel packet filters to reduce scheduling noise
+// - 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/if.h>
+#include <net/if_arp.h>
+#include <linux/sockios.h>
+
+#include <syslog.h>
+
+/* We don't need more than 32 bits of the counter */
+#define MONOTONIC_US() ((unsigned)monotonic_us())
+
+struct arp_packet {
+	struct ether_header eth;
+	struct ether_arp arp;
+} PACKED;
+
+enum {
+/* 169.254.0.0 */
+	LINKLOCAL_ADDR = 0xa9fe0000,
+
+/* protocol timeout parameters, specified in seconds */
+	PROBE_WAIT = 1,
+	PROBE_MIN = 1,
+	PROBE_MAX = 2,
+	PROBE_NUM = 3,
+	MAX_CONFLICTS = 10,
+	RATE_LIMIT_INTERVAL = 60,
+	ANNOUNCE_WAIT = 2,
+	ANNOUNCE_NUM = 2,
+	ANNOUNCE_INTERVAL = 2,
+	DEFEND_INTERVAL = 10
+};
+
+/* States during the configuration process. */
+enum {
+	PROBE = 0,
+	RATE_LIMIT_PROBE,
+	ANNOUNCE,
+	MONITOR,
+	DEFEND
+};
+
+#define VDBG(...) do { } while (0)
+
+
+enum {
+	sock_fd = 3
+};
+
+struct globals {
+	struct sockaddr saddr;
+	struct ether_addr eth_addr;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define saddr    (G.saddr   )
+#define eth_addr (G.eth_addr)
+
+
+/**
+ * Pick a random link local IP address on 169.254/16, except that
+ * the first and last 256 addresses are reserved.
+ */
+static uint32_t pick(void)
+{
+	unsigned tmp;
+
+	do {
+		tmp = rand() & IN_CLASSB_HOST;
+	} while (tmp > (IN_CLASSB_HOST - 0x0200));
+	return htonl((LINKLOCAL_ADDR + 0x0100) + tmp);
+}
+
+/**
+ * Broadcast an ARP packet.
+ */
+static void arp(
+	/* int op, - always ARPOP_REQUEST */
+	/* const struct ether_addr *source_eth, - always &eth_addr */
+					struct in_addr source_ip,
+	const struct ether_addr *target_eth, struct in_addr target_ip)
+{
+	enum { op = ARPOP_REQUEST };
+#define source_eth (&eth_addr)
+
+	struct arp_packet p;
+	memset(&p, 0, sizeof(p));
+
+	// ether header
+	p.eth.ether_type = htons(ETHERTYPE_ARP);
+	memcpy(p.eth.ether_shost, source_eth, ETH_ALEN);
+	memset(p.eth.ether_dhost, 0xff, ETH_ALEN);
+
+	// arp request
+	p.arp.arp_hrd = htons(ARPHRD_ETHER);
+	p.arp.arp_pro = htons(ETHERTYPE_IP);
+	p.arp.arp_hln = ETH_ALEN;
+	p.arp.arp_pln = 4;
+	p.arp.arp_op = htons(op);
+	memcpy(&p.arp.arp_sha, source_eth, ETH_ALEN);
+	memcpy(&p.arp.arp_spa, &source_ip, sizeof(p.arp.arp_spa));
+	memcpy(&p.arp.arp_tha, target_eth, ETH_ALEN);
+	memcpy(&p.arp.arp_tpa, &target_ip, sizeof(p.arp.arp_tpa));
+
+	// send it
+	// Even though sock_fd is already bound to saddr, just send()
+	// won't work, because "socket is not connected"
+	// (and connect() won't fix that, "operation not supported").
+	// Thus we sendto() to saddr. I wonder which sockaddr
+	// (from bind() or from sendto()?) kernel actually uses
+	// to determine iface to emit the packet from...
+	xsendto(sock_fd, &p, sizeof(p), &saddr, sizeof(saddr));
+#undef source_eth
+}
+
+/**
+ * Run a script.
+ * argv[0]:intf argv[1]:script_name argv[2]:junk argv[3]:NULL
+ */
+static int run(char *argv[3], const char *param, struct in_addr *ip)
+{
+	int status;
+	char *addr = addr; /* for gcc */
+	const char *fmt = "%s %s %s" + 3;
+
+	argv[2] = (char*)param;
+
+	VDBG("%s run %s %s\n", argv[0], argv[1], argv[2]);
+
+	if (ip) {
+		addr = inet_ntoa(*ip);
+		xsetenv("ip", addr);
+		fmt -= 3;
+	}
+	bb_info_msg(fmt, argv[2], argv[0], addr);
+
+	status = spawn_and_wait(argv + 1);
+	if (status < 0) {
+		bb_perror_msg("%s %s %s" + 3, argv[2], argv[0]);
+		return -errno;
+	}
+	if (status != 0)
+		bb_error_msg("script %s %s failed, exitcode=%d", argv[1], argv[2], status & 0xff);
+	return status;
+}
+
+/**
+ * Return milliseconds of random delay, up to "secs" seconds.
+ */
+static ALWAYS_INLINE unsigned random_delay_ms(unsigned secs)
+{
+	return rand() % (secs * 1000);
+}
+
+/**
+ * main program
+ */
+int zcip_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int zcip_main(int argc UNUSED_PARAM, char **argv)
+{
+	int state;
+	char *r_opt;
+	unsigned opts;
+
+	// ugly trick, but I want these zeroed in one go
+	struct {
+		const struct in_addr null_ip;
+		const struct ether_addr null_addr;
+		struct in_addr ip;
+		struct ifreq ifr;
+		int timeout_ms; /* must be signed */
+		unsigned conflicts;
+		unsigned nprobes;
+		unsigned nclaims;
+		int ready;
+		int verbose;
+	} L;
+#define null_ip    (L.null_ip   )
+#define null_addr  (L.null_addr )
+#define ip         (L.ip        )
+#define ifr        (L.ifr       )
+#define timeout_ms (L.timeout_ms)
+#define conflicts  (L.conflicts )
+#define nprobes    (L.nprobes   )
+#define nclaims    (L.nclaims   )
+#define ready      (L.ready     )
+#define verbose    (L.verbose   )
+
+	memset(&L, 0, sizeof(L));
+
+#define FOREGROUND (opts & 1)
+#define QUIT       (opts & 2)
+	// parse commandline: prog [options] ifname script
+	// exactly 2 args; -v accumulates and implies -f
+	opt_complementary = "=2:vv:vf";
+	opts = getopt32(argv, "fqr:v", &r_opt, &verbose);
+#if !BB_MMU
+	// on NOMMU reexec early (or else we will rerun things twice)
+	if (!FOREGROUND)
+		bb_daemonize_or_rexec(0 /*was: DAEMON_CHDIR_ROOT*/, argv);
+#endif
+	// open an ARP socket
+	// (need to do it before openlog to prevent openlog from taking
+	// fd 3 (sock_fd==3))
+	xmove_fd(xsocket(AF_PACKET, SOCK_PACKET, htons(ETH_P_ARP)), sock_fd);
+	if (!FOREGROUND) {
+		// do it before all bb_xx_msg calls
+		openlog(applet_name, 0, LOG_DAEMON);
+		logmode |= LOGMODE_SYSLOG;
+	}
+	if (opts & 4) { // -r n.n.n.n
+		if (inet_aton(r_opt, &ip) == 0
+		 || (ntohl(ip.s_addr) & IN_CLASSB_NET) != LINKLOCAL_ADDR
+		) {
+			bb_error_msg_and_die("invalid link address");
+		}
+	}
+	argv += optind - 1;
+
+	/* Now: argv[0]:junk argv[1]:intf argv[2]:script argv[3]:NULL */
+	/* We need to make space for script argument: */
+	argv[0] = argv[1];
+	argv[1] = argv[2];
+	/* Now: argv[0]:intf argv[1]:script argv[2]:junk argv[3]:NULL */
+#define argv_intf (argv[0])
+
+	xsetenv("interface", argv_intf);
+
+	// initialize the interface (modprobe, ifup, etc)
+	if (run(argv, "init", NULL))
+		return EXIT_FAILURE;
+
+	// initialize saddr
+	// saddr is: { u16 sa_family; u8 sa_data[14]; }
+	//memset(&saddr, 0, sizeof(saddr));
+	//TODO: are we leaving sa_family == 0 (AF_UNSPEC)?!
+	safe_strncpy(saddr.sa_data, argv_intf, sizeof(saddr.sa_data));
+
+	// bind to the interface's ARP socket
+	xbind(sock_fd, &saddr, sizeof(saddr));
+
+	// get the interface's ethernet address
+	//memset(&ifr, 0, sizeof(ifr));
+	strncpy_IFNAMSIZ(ifr.ifr_name, argv_intf);
+	xioctl(sock_fd, SIOCGIFHWADDR, &ifr);
+	memcpy(&eth_addr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+	// start with some stable ip address, either a function of
+	// the hardware address or else the last address we used.
+	// we are taking low-order four bytes, as top-order ones
+	// aren't random enough.
+	// NOTE: the sequence of addresses we try changes only
+	// depending on when we detect conflicts.
+	{
+		uint32_t t;
+		move_from_unaligned32(t, ((char *)&eth_addr + 2));
+		srand(t);
+	}
+	if (ip.s_addr == 0)
+		ip.s_addr = pick();
+
+	// FIXME cases to handle:
+	//  - zcip already running!
+	//  - link already has local address... just defend/update
+
+	// daemonize now; don't delay system startup
+	if (!FOREGROUND) {
+#if BB_MMU
+		bb_daemonize(0 /*was: DAEMON_CHDIR_ROOT*/);
+#endif
+		bb_info_msg("start, interface %s", argv_intf);
+	}
+
+	// run the dynamic address negotiation protocol,
+	// restarting after address conflicts:
+	//  - start with some address we want to try
+	//  - short random delay
+	//  - arp probes to see if another host uses it
+	//  - arp announcements that we're claiming it
+	//  - use it
+	//  - defend it, within limits
+	// exit if:
+	// - address is successfully obtained and -q was given:
+	//   run "<script> config", then exit with exitcode 0
+	// - poll error (when does this happen?)
+	// - read error (when does this happen?)
+	// - sendto error (in arp()) (when does this happen?)
+	// - revents & POLLERR (link down). run "<script> deconfig" first
+	state = PROBE;
+	while (1) {
+		struct pollfd fds[1];
+		unsigned deadline_us;
+		struct arp_packet p;
+		int source_ip_conflict;
+		int target_ip_conflict;
+
+		fds[0].fd = sock_fd;
+		fds[0].events = POLLIN;
+		fds[0].revents = 0;
+
+		// poll, being ready to adjust current timeout
+		if (!timeout_ms) {
+			timeout_ms = random_delay_ms(PROBE_WAIT);
+			// FIXME setsockopt(sock_fd, SO_ATTACH_FILTER, ...) to
+			// make the kernel filter out all packets except
+			// ones we'd care about.
+		}
+		// set deadline_us to the point in time when we timeout
+		deadline_us = MONOTONIC_US() + timeout_ms * 1000;
+
+		VDBG("...wait %d %s nprobes=%u, nclaims=%u\n",
+				timeout_ms, argv_intf, nprobes, nclaims);
+
+		switch (safe_poll(fds, 1, timeout_ms)) {
+
+		default:
+			//bb_perror_msg("poll"); - done in safe_poll
+			return EXIT_FAILURE;
+
+		// timeout
+		case 0:
+			VDBG("state = %d\n", state);
+			switch (state) {
+			case PROBE:
+				// timeouts in the PROBE state mean no conflicting ARP packets
+				// have been received, so we can progress through the states
+				if (nprobes < PROBE_NUM) {
+					nprobes++;
+					VDBG("probe/%u %s@%s\n",
+							nprobes, argv_intf, inet_ntoa(ip));
+					arp(/* ARPOP_REQUEST, */
+							/* &eth_addr, */ null_ip,
+							&null_addr, ip);
+					timeout_ms = PROBE_MIN * 1000;
+					timeout_ms += random_delay_ms(PROBE_MAX - PROBE_MIN);
+				}
+				else {
+					// Switch to announce state.
+					state = ANNOUNCE;
+					nclaims = 0;
+					VDBG("announce/%u %s@%s\n",
+							nclaims, argv_intf, inet_ntoa(ip));
+					arp(/* ARPOP_REQUEST, */
+							/* &eth_addr, */ ip,
+							&eth_addr, ip);
+					timeout_ms = ANNOUNCE_INTERVAL * 1000;
+				}
+				break;
+			case RATE_LIMIT_PROBE:
+				// timeouts in the RATE_LIMIT_PROBE state mean no conflicting ARP packets
+				// have been received, so we can move immediately to the announce state
+				state = ANNOUNCE;
+				nclaims = 0;
+				VDBG("announce/%u %s@%s\n",
+						nclaims, argv_intf, inet_ntoa(ip));
+				arp(/* ARPOP_REQUEST, */
+						/* &eth_addr, */ ip,
+						&eth_addr, ip);
+				timeout_ms = ANNOUNCE_INTERVAL * 1000;
+				break;
+			case ANNOUNCE:
+				// timeouts in the ANNOUNCE state mean no conflicting ARP packets
+				// have been received, so we can progress through the states
+				if (nclaims < ANNOUNCE_NUM) {
+					nclaims++;
+					VDBG("announce/%u %s@%s\n",
+							nclaims, argv_intf, inet_ntoa(ip));
+					arp(/* ARPOP_REQUEST, */
+							/* &eth_addr, */ ip,
+							&eth_addr, ip);
+					timeout_ms = ANNOUNCE_INTERVAL * 1000;
+				}
+				else {
+					// Switch to monitor state.
+					state = MONITOR;
+					// link is ok to use earlier
+					// FIXME update filters
+					run(argv, "config", &ip);
+					ready = 1;
+					conflicts = 0;
+					timeout_ms = -1; // Never timeout in the monitor state.
+
+					// NOTE: all other exit paths
+					// should deconfig ...
+					if (QUIT)
+						return EXIT_SUCCESS;
+				}
+				break;
+			case DEFEND:
+				// We won!  No ARP replies, so just go back to monitor.
+				state = MONITOR;
+				timeout_ms = -1;
+				conflicts = 0;
+				break;
+			default:
+				// Invalid, should never happen.  Restart the whole protocol.
+				state = PROBE;
+				ip.s_addr = pick();
+				timeout_ms = 0;
+				nprobes = 0;
+				nclaims = 0;
+				break;
+			} // switch (state)
+			break; // case 0 (timeout)
+
+		// packets arriving, or link went down
+		case 1:
+			// We need to adjust the timeout in case we didn't receive
+			// a conflicting packet.
+			if (timeout_ms > 0) {
+				unsigned diff = deadline_us - MONOTONIC_US();
+				if ((int)(diff) < 0) {
+					// Current time is greater than the expected timeout time.
+					// Should never happen.
+					VDBG("missed an expected timeout\n");
+					timeout_ms = 0;
+				} else {
+					VDBG("adjusting timeout\n");
+					timeout_ms = (diff / 1000) | 1; /* never 0 */
+				}
+			}
+
+			if ((fds[0].revents & POLLIN) == 0) {
+				if (fds[0].revents & POLLERR) {
+					// FIXME: links routinely go down;
+					// this shouldn't necessarily exit.
+					bb_error_msg("iface %s is down", argv_intf);
+					if (ready) {
+						run(argv, "deconfig", &ip);
+					}
+					return EXIT_FAILURE;
+				}
+				continue;
+			}
+
+			// read ARP packet
+			if (safe_read(sock_fd, &p, sizeof(p)) < 0) {
+				bb_perror_msg_and_die(bb_msg_read_error);
+			}
+			if (p.eth.ether_type != htons(ETHERTYPE_ARP))
+				continue;
+#ifdef DEBUG
+			{
+				struct ether_addr *sha = (struct ether_addr *) p.arp.arp_sha;
+				struct ether_addr *tha = (struct ether_addr *) p.arp.arp_tha;
+				struct in_addr *spa = (struct in_addr *) p.arp.arp_spa;
+				struct in_addr *tpa = (struct in_addr *) p.arp.arp_tpa;
+				VDBG("%s recv arp type=%d, op=%d,\n",
+					argv_intf, ntohs(p.eth.ether_type),
+					ntohs(p.arp.arp_op));
+				VDBG("\tsource=%s %s\n",
+					ether_ntoa(sha),
+					inet_ntoa(*spa));
+				VDBG("\ttarget=%s %s\n",
+					ether_ntoa(tha),
+					inet_ntoa(*tpa));
+			}
+#endif
+			if (p.arp.arp_op != htons(ARPOP_REQUEST)
+			 && p.arp.arp_op != htons(ARPOP_REPLY))
+				continue;
+
+			source_ip_conflict = 0;
+			target_ip_conflict = 0;
+
+			if (memcmp(p.arp.arp_spa, &ip.s_addr, sizeof(struct in_addr)) == 0
+			 && memcmp(&p.arp.arp_sha, &eth_addr, ETH_ALEN) != 0
+			) {
+				source_ip_conflict = 1;
+			}
+			if (p.arp.arp_op == htons(ARPOP_REQUEST)
+			 && memcmp(p.arp.arp_tpa, &ip.s_addr, sizeof(struct in_addr)) == 0
+			 && memcmp(&p.arp.arp_tha, &eth_addr, ETH_ALEN) != 0
+			) {
+				target_ip_conflict = 1;
+			}
+
+			VDBG("state = %d, source ip conflict = %d, target ip conflict = %d\n",
+				state, source_ip_conflict, target_ip_conflict);
+			switch (state) {
+			case PROBE:
+			case ANNOUNCE:
+				// When probing or announcing, check for source IP conflicts
+				// and other hosts doing ARP probes (target IP conflicts).
+				if (source_ip_conflict || target_ip_conflict) {
+					conflicts++;
+					if (conflicts >= MAX_CONFLICTS) {
+						VDBG("%s ratelimit\n", argv_intf);
+						timeout_ms = RATE_LIMIT_INTERVAL * 1000;
+						state = RATE_LIMIT_PROBE;
+					}
+
+					// restart the whole protocol
+					ip.s_addr = pick();
+					timeout_ms = 0;
+					nprobes = 0;
+					nclaims = 0;
+				}
+				break;
+			case MONITOR:
+				// If a conflict, we try to defend with a single ARP probe.
+				if (source_ip_conflict) {
+					VDBG("monitor conflict -- defending\n");
+					state = DEFEND;
+					timeout_ms = DEFEND_INTERVAL * 1000;
+					arp(/* ARPOP_REQUEST, */
+						/* &eth_addr, */ ip,
+						&eth_addr, ip);
+				}
+				break;
+			case DEFEND:
+				// Well, we tried.  Start over (on conflict).
+				if (source_ip_conflict) {
+					state = PROBE;
+					VDBG("defend conflict -- starting over\n");
+					ready = 0;
+					run(argv, "deconfig", &ip);
+
+					// restart the whole protocol
+					ip.s_addr = pick();
+					timeout_ms = 0;
+					nprobes = 0;
+					nclaims = 0;
+				}
+				break;
+			default:
+				// Invalid, should never happen.  Restart the whole protocol.
+				VDBG("invalid state -- starting over\n");
+				state = PROBE;
+				ip.s_addr = pick();
+				timeout_ms = 0;
+				nprobes = 0;
+				nclaims = 0;
+				break;
+			} // switch state
+			break; // case 1 (packets arriving)
+		} // switch poll
+	} // while (1)
+#undef argv_intf
+}
diff --git a/busybox-1.19.3/printutils/Config.src b/busybox-1.19.3/printutils/Config.src
new file mode 100644
index 0000000..cc4ab8d
--- /dev/null
+++ b/busybox-1.19.3/printutils/Config.src
@@ -0,0 +1,28 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Print Utilities"
+
+INSERT
+
+config LPD
+	bool "lpd"
+	default y
+	help
+	  lpd is a print spooling daemon.
+
+config LPR
+	bool "lpr"
+	default y
+	help
+	  lpr sends files (or standard input) to a print spooling daemon.
+
+config LPQ
+	bool "lpq"
+	default y
+	help
+	  lpq is a print spool queue examination and manipulation program.
+
+endmenu
diff --git a/busybox-1.19.3/printutils/Kbuild.src b/busybox-1.19.3/printutils/Kbuild.src
new file mode 100644
index 0000000..194fe01
--- /dev/null
+++ b/busybox-1.19.3/printutils/Kbuild.src
@@ -0,0 +1,9 @@
+# Makefile for busybox
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y :=
+
+lib-$(CONFIG_LPD) += lpd.o
+lib-$(CONFIG_LPR) += lpr.o
+lib-$(CONFIG_LPQ) += lpr.o
diff --git a/busybox-1.19.3/printutils/lpd.c b/busybox-1.19.3/printutils/lpd.c
new file mode 100644
index 0000000..642e8a8
--- /dev/null
+++ b/busybox-1.19.3/printutils/lpd.c
@@ -0,0 +1,292 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * micro lpd
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * A typical usage of BB lpd looks as follows:
+ * # tcpsvd -E 0 515 lpd [SPOOLDIR] [HELPER-PROG [ARGS...]]
+ *
+ * This starts TCP listener on port 515 (default for LP protocol).
+ * When a client connection is made (via lpr) lpd first changes its
+ * working directory to SPOOLDIR (current dir is the default).
+ *
+ * SPOOLDIR is the spool directory which contains printing queues
+ * and should have the following structure:
+ *
+ * SPOOLDIR/
+ *      <queue1>
+ *      ...
+ *      <queueN>
+ *
+ * <queueX> can be of two types:
+ *      A. a printer character device, an ordinary file or a link to such;
+ *      B. a directory.
+ *
+ * In case A lpd just dumps the data it receives from client (lpr) to the
+ * end of queue file/device. This is non-spooling mode.
+ *
+ * In case B lpd enters spooling mode. It reliably saves client data along
+ * with control info in two unique files under the queue directory. These
+ * files are named dfAXXXHHHH and cfAXXXHHHH, where XXX is the job number
+ * and HHHH is the client hostname. Unless a printing helper application
+ * is specified lpd is done at this point.
+ *
+ * NB: file names are produced by peer! They actually may be anything at all.
+ * lpd only sanitizes them (by removing most non-alphanumerics).
+ *
+ * If HELPER-PROG (with optional arguments) is specified then lpd continues
+ * to process client data:
+ *      1. it reads and parses control file (cfA...). The parse process
+ *      results in setting environment variables whose values were passed
+ *      in control file; when parsing is complete, lpd deletes control file.
+ *      2. it spawns specified helper application. It is then
+ *      the helper application who is responsible for both actual printing
+ *      and deleting of processed data file.
+ *
+ * A good lpr passes control files which when parsed provides the following
+ * variables:
+ * $H = host which issues the job
+ * $P = user who prints
+ * $C = class of printing (what is printed on banner page)
+ * $J = the name of the job
+ * $L = print banner page
+ * $M = the user to whom a mail should be sent if a problem occurs
+ *
+ * We specifically filter out and NOT provide:
+ * $l = name of datafile ("dfAxxx") - file whose content are to be printed
+ *
+ * lpd provides $DATAFILE instead - the ACTUAL name
+ * of the datafile under which it was saved.
+ * $l would be not reliable (you would be at mercy of remote peer).
+ *
+ * Thus, a typical helper can be something like this:
+ * #!/bin/sh
+ * cat ./"$DATAFILE" >/dev/lp0
+ * 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
+static char *sane(char *str)
+{
+	char *s = str;
+	char *p = s;
+	while (*s) {
+		if (isalnum(*s) || '-' == *s || '_' == *s) {
+			*p++ = *s;
+		}
+		s++;
+	}
+	*p = '\0';
+	return str;
+}
+
+static char *xmalloc_read_stdin(void)
+{
+	// SECURITY:
+	size_t max = 4 * 1024; // more than enough for commands!
+	return xmalloc_reads(STDIN_FILENO, &max);
+}
+
+int lpd_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
+int lpd_main(int argc UNUSED_PARAM, char *argv[])
+{
+	int spooling = spooling; // for compiler
+	char *s, *queue;
+	char *filenames[2];
+
+	// goto spool directory
+	if (*++argv)
+		xchdir(*argv++);
+
+	// error messages of xfuncs will be sent over network
+	xdup2(STDOUT_FILENO, STDERR_FILENO);
+
+	// nullify ctrl/data filenames
+	memset(filenames, 0, sizeof(filenames));
+
+	// read command
+	s = queue = xmalloc_read_stdin();
+	// we understand only "receive job" command
+	if (2 != *queue) {
+ unsupported_cmd:
+		printf("Command %02x %s\n",
+			(unsigned char)s[0], "is not supported");
+		goto err_exit;
+	}
+
+	// parse command: "2 | QUEUE_NAME | '\n'"
+	queue++;
+	// protect against "/../" attacks
+	// *strchrnul(queue, '\n') = '\0'; - redundant, sane() will do
+	if (!*sane(queue))
+		return EXIT_FAILURE;
+
+	// queue is a directory -> chdir to it and enter spooling mode
+	spooling = chdir(queue) + 1; // 0: cannot chdir, 1: done
+	// we don't free(s), we might need "queue" var later
+
+	while (1) {
+		char *fname;
+		int fd;
+		// int is easier than ssize_t: can use xatoi_positive,
+		// and can correctly display error returns (-1)
+		int expected_len, real_len;
+
+		// signal OK
+		safe_write(STDOUT_FILENO, "", 1);
+
+		// get subcommand
+		// valid s must be of form: "SUBCMD | LEN | space | FNAME"
+		// N.B. we bail out on any error
+		s = xmalloc_read_stdin();
+		if (!s) { // (probably) EOF
+			char *p, *q, var[2];
+
+			// non-spooling mode or no spool helper specified
+			if (!spooling || !*argv)
+				return EXIT_SUCCESS; // the only non-error exit
+			// spooling mode but we didn't see both ctrlfile & datafile
+			if (spooling != 7)
+				goto err_exit; // reject job
+
+			// spooling mode and spool helper specified -> exec spool helper
+			// (we exit 127 if helper cannot be executed)
+			var[1] = '\0';
+			// read and delete ctrlfile
+			q = xmalloc_xopen_read_close(filenames[0], NULL);
+			unlink(filenames[0]);
+			// provide datafile name
+			// we can use leaky setenv since we are about to exec or exit
+			xsetenv("DATAFILE", filenames[1]);
+			// parse control file by "\n"
+			while ((p = strchr(q, '\n')) != NULL && isalpha(*q)) {
+				*p++ = '\0';
+				// q is a line of <SYM><VALUE>,
+				// we are setting environment string <SYM>=<VALUE>.
+				// Ignoring "l<datafile>", exporting others:
+				if (*q != 'l') {
+					var[0] = *q++;
+					xsetenv(var, q);
+				}
+				q = p; // next line
+			}
+			// helper should not talk over network.
+			// this call reopens stdio fds to "/dev/null"
+			// (no daemonization is done)
+			bb_daemonize_or_rexec(DAEMON_DEVNULL_STDIO | DAEMON_ONLY_SANITIZE, NULL);
+			BB_EXECVP_or_die(argv);
+		}
+
+		// validate input.
+		// we understand only "control file" or "data file" cmds
+		if (2 != s[0] && 3 != s[0])
+			goto unsupported_cmd;
+		if (spooling & (1 << (s[0]-1))) {
+			printf("Duplicated subcommand\n");
+			goto err_exit;
+		}
+		// get filename
+		*strchrnul(s, '\n') = '\0';
+		fname = strchr(s, ' ');
+		if (!fname) {
+// bad_fname:
+			printf("No or bad filename\n");
+			goto err_exit;
+		}
+		*fname++ = '\0';
+//		// s[0]==2: ctrlfile, must start with 'c'
+//		// s[0]==3: datafile, must start with 'd'
+//		if (fname[0] != s[0] + ('c'-2))
+//			goto bad_fname;
+		// get length
+		expected_len = bb_strtou(s + 1, NULL, 10);
+		if (errno || expected_len < 0) {
+			printf("Bad length\n");
+			goto err_exit;
+		}
+		if (2 == s[0] && expected_len > 16 * 1024) {
+			// SECURITY:
+			// ctrlfile can't be big (we want to read it back later!)
+			printf("File is too big\n");
+			goto err_exit;
+		}
+
+		// open the file
+		if (spooling) {
+			// spooling mode: dump both files
+			// job in flight has mode 0200 "only writable"
+			sane(fname);
+			fd = open3_or_warn(fname, O_CREAT | O_WRONLY | O_TRUNC | O_EXCL, 0200);
+			if (fd < 0)
+				goto err_exit;
+			filenames[s[0] - 2] = xstrdup(fname);
+		} else {
+			// non-spooling mode:
+			// 2: control file (ignoring), 3: data file
+			fd = -1;
+			if (3 == s[0])
+				fd = xopen(queue, O_RDWR | O_APPEND);
+		}
+
+		// signal OK
+		safe_write(STDOUT_FILENO, "", 1);
+
+		// copy the file
+		real_len = bb_copyfd_size(STDIN_FILENO, fd, expected_len);
+		if (real_len != expected_len) {
+			printf("Expected %d but got %d bytes\n",
+				expected_len, real_len);
+			goto err_exit;
+		}
+		// get EOF indicator, see whether it is NUL (ok)
+		// (and don't trash s[0]!)
+		if (safe_read(STDIN_FILENO, &s[1], 1) != 1 || s[1] != 0) {
+			// don't send error msg to peer - it obviously
+			// doesn't follow the protocol, so probably
+			// it can't understand us either
+			goto err_exit;
+		}
+
+		if (spooling) {
+			// chmod completely downloaded file as "readable+writable"
+			fchmod(fd, 0600);
+			// accumulate dump state
+			// N.B. after all files are dumped spooling should be 1+2+4==7
+			spooling |= (1 << (s[0]-1)); // bit 1: ctrlfile; bit 2: datafile
+		}
+
+		free(s);
+		close(fd); // NB: can do close(-1). Who cares?
+
+		// NB: don't do "signal OK" write here, it will be done
+		// at the top of the loop
+	} // while (1)
+
+ err_exit:
+	// don't keep corrupted files
+	if (spooling) {
+#define i spooling
+		for (i = 2; --i >= 0; )
+			if (filenames[i])
+				unlink(filenames[i]);
+	}
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/printutils/lpr.c b/busybox-1.19.3/printutils/lpr.c
new file mode 100644
index 0000000..fc6bca9
--- /dev/null
+++ b/busybox-1.19.3/printutils/lpr.c
@@ -0,0 +1,273 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * bare bones version of lpr & lpq: BSD printing utilities
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Original idea and code:
+ *      Walter Harms <WHarms@bfs.de>
+ *
+ * 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"
+
+/*
+ * LPD returns binary 0 on success.
+ * Otherwise it returns error message.
+ */
+static void get_response_or_say_and_die(int fd, const char *errmsg)
+{
+	ssize_t sz;
+	char buf[128];
+
+	buf[0] = ' ';
+	sz = safe_read(fd, buf, 1);
+	if ('\0' != buf[0]) {
+		// request has failed
+		// try to make sure last char is '\n', but do not add
+		// superfluous one
+		sz = full_read(fd, buf + 1, 126);
+		bb_error_msg("error while %s%s", errmsg,
+				(sz > 0 ? ". Server said:" : ""));
+		if (sz > 0) {
+			// sz = (bytes in buf) - 1
+			if (buf[sz] != '\n')
+				buf[++sz] = '\n';
+			safe_write(STDERR_FILENO, buf, sz + 1);
+		}
+		xfunc_die();
+	}
+}
+
+int lpqr_main(int argc, char *argv[]) MAIN_EXTERNALLY_VISIBLE;
+int lpqr_main(int argc UNUSED_PARAM, char *argv[])
+{
+	enum {
+		OPT_P           = 1 << 0, // -P queue[@host[:port]]. If no -P is given use $PRINTER, then "lp@localhost:515"
+		OPT_U           = 1 << 1, // -U username
+
+		LPR_V           = 1 << 2, // -V: be verbose
+		LPR_h           = 1 << 3, // -h: want banner printed
+		LPR_C           = 1 << 4, // -C class: job "class" (? supposedly printed on banner)
+		LPR_J           = 1 << 5, // -J title: the job title for the banner page
+		LPR_m           = 1 << 6, // -m: send mail back to user
+
+		LPQ_SHORT_FMT   = 1 << 2, // -s: short listing format
+		LPQ_DELETE      = 1 << 3, // -d: delete job(s)
+		LPQ_FORCE       = 1 << 4, // -f: force waiting job(s) to be printed
+	};
+	char tempfile[sizeof("/tmp/lprXXXXXX")];
+	const char *job_title;
+	const char *printer_class = "";   // printer class, max 32 char
+	const char *queue;                // name of printer queue
+	const char *server = "localhost"; // server[:port] of printer queue
+	char *hostname;
+	// N.B. IMHO getenv("USER") can be way easily spoofed!
+	const char *user = xuid2uname(getuid());
+	unsigned job;
+	unsigned opts;
+	int fd;
+
+	// parse options
+	// TODO: set opt_complementary: s,d,f are mutually exclusive
+	opts = getopt32(argv,
+		(/*lp*/'r' == applet_name[2]) ? "P:U:VhC:J:m" : "P:U:sdf"
+		, &queue, &user
+		, &printer_class, &job_title
+	);
+	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) {
+			// server name is to the right of '@'
+			*s = '\0';
+			server = s + 1;
+		}
+	}
+
+	// do connect
+	fd = create_and_connect_stream_or_die(server, 515);
+
+	//
+	// LPQ ------------------------
+	//
+	if (/*lp*/'q' == applet_name[2]) {
+		char cmd;
+		// force printing of every job still in queue
+		if (opts & LPQ_FORCE) {
+			cmd = 1;
+			goto command;
+		// delete job(s)
+		} else if (opts & LPQ_DELETE) {
+			fdprintf(fd, "\x5" "%s %s", queue, user);
+			while (*argv) {
+				fdprintf(fd, " %s", *argv++);
+			}
+			bb_putchar('\n');
+		// dump current jobs status
+		// N.B. periodical polling should be achieved
+		// via "watch -n delay lpq"
+		// They say it's the UNIX-way :)
+		} else {
+			cmd = (opts & LPQ_SHORT_FMT) ? 3 : 4;
+ command:
+			fdprintf(fd, "%c" "%s\n", cmd, queue);
+			bb_copyfd_eof(fd, STDOUT_FILENO);
+		}
+
+		return EXIT_SUCCESS;
+	}
+
+	//
+	// LPR ------------------------
+	//
+	if (opts & LPR_V)
+		bb_error_msg("connected to server");
+
+	job = getpid() % 1000;
+	hostname = safe_gethostname();
+
+	// no files given on command line? -> use stdin
+	if (!*argv)
+		*--argv = (char *)"-";
+
+	fdprintf(fd, "\x2" "%s\n", queue);
+	get_response_or_say_and_die(fd, "setting queue");
+
+	// process files
+	do {
+		unsigned cflen;
+		int dfd;
+		struct stat st;
+		char *c;
+		char *remote_filename;
+		char *controlfile;
+
+		// if data file is stdin, we need to dump it first
+		if (LONE_DASH(*argv)) {
+			strcpy(tempfile, "/tmp/lprXXXXXX");
+			dfd = xmkstemp(tempfile);
+			bb_copyfd_eof(STDIN_FILENO, dfd);
+			xlseek(dfd, 0, SEEK_SET);
+			*argv = (char*)bb_msg_standard_input;
+		} else {
+			dfd = xopen(*argv, O_RDONLY);
+		}
+
+		/* "The name ... should start with ASCII "cfA",
+		 * followed by a three digit job number, followed
+		 * by the host name which has constructed the file."
+		 * We supply 'c' or 'd' as needed for control/data file. */
+		remote_filename = xasprintf("fA%03u%s", job, hostname);
+
+		// create control file
+		// TODO: all lines but 2 last are constants! How we can use this fact?
+		controlfile = xasprintf(
+			"H" "%.32s\n" "P" "%.32s\n" /* H HOST, P USER */
+			"C" "%.32s\n" /* C CLASS - printed on banner page (if L cmd is also given) */
+			"J" "%.99s\n" /* J JOBNAME */
+			/* "class name for banner page and job name
+			 * for banner page commands must precede L command" */
+			"L" "%.32s\n" /* L USER - print banner page, with given user's name */
+			"M" "%.32s\n" /* M WHOM_TO_MAIL */
+			"l" "d%.31s\n" /* l DATA_FILE_NAME ("dfAxxx") */
+			, hostname, user
+			, printer_class /* can be "" */
+			, ((opts & LPR_J) ? job_title : *argv)
+			, (opts & LPR_h) ? user : ""
+			, (opts & LPR_m) ? user : ""
+			, remote_filename
+		);
+		// delete possible "\nX\n" 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;
+			} else {
+				c++;
+			}
+		}
+
+		// send control file
+		if (opts & LPR_V)
+			bb_error_msg("sending control file");
+		/* "Acknowledgement processing must occur as usual
+		 * after the command is sent." */
+		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
+		 * been delivered, an octet of zero bits is sent as
+		 * an indication that the file being sent is complete.
+		 * A second level of acknowledgement processing
+		 * must occur at this point." */
+		full_write(fd, controlfile, cflen + 1); /* writes NUL byte too */
+		get_response_or_say_and_die(fd, "sending control file");
+
+		// 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) {
+			// We're screwed. We sent less bytes than we advertised.
+			bb_error_msg_and_die("local file changed size?!");
+		}
+		write(fd, "", 1); // send ACK
+		get_response_or_say_and_die(fd, "sending data file");
+
+		// delete temporary file if we dumped stdin
+		if (*argv == (char*)bb_msg_standard_input)
+			unlink(tempfile);
+
+		// cleanup
+		close(fd);
+		free(remote_filename);
+		free(controlfile);
+
+		// say job accepted
+		if (opts & LPR_V)
+			bb_error_msg("job accepted");
+
+		// next, please!
+		job = (job + 1) % 1000;
+	} while (*++argv);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/procps/Config.src b/busybox-1.19.3/procps/Config.src
new file mode 100644
index 0000000..570b026
--- /dev/null
+++ b/busybox-1.19.3/procps/Config.src
@@ -0,0 +1,204 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Process Utilities"
+
+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.
+	  The shared memory column should be ignored; it is obsolete.
+
+config FUSER
+	bool "fuser"
+	default y
+	help
+	  fuser lists all PIDs (Process IDs) that currently have a given
+	  file open. fuser can also list all PIDs that have a given network
+	  (TCP or UDP) port open.
+
+config KILL
+	bool "kill"
+	default y
+	help
+	  The command kill sends the specified signal to the specified
+	  process or process group. If no signal is specified, the TERM
+	  signal is sent.
+
+config KILLALL
+	bool "killall"
+	default y
+	depends on KILL
+	help
+	  killall sends a signal to all processes running any of the
+	  specified commands. If no signal name is specified, SIGTERM is
+	  sent.
+
+config KILLALL5
+	bool "killall5"
+	default y
+	depends on KILL
+
+config PGREP
+	bool "pgrep"
+	default y
+	help
+	  Look for processes by name.
+
+config PIDOF
+	bool "pidof"
+	default y
+	help
+	  Pidof finds the process id's (pids) of the named programs. It prints
+	  those id's on the standard output.
+
+config FEATURE_PIDOF_SINGLE
+	bool "Enable argument for single shot (-s)"
+	default y
+	depends on PIDOF
+	help
+	  Support argument '-s' for returning only the first pid found.
+
+config FEATURE_PIDOF_OMIT
+	bool "Enable argument for omitting pids (-o)"
+	default y
+	depends on PIDOF
+	help
+	  Support argument '-o' for omitting the given pids in output.
+	  The special pid %PPID can be used to name the parent process
+	  of the pidof, in other words the calling shell or shell script.
+
+config PKILL
+	bool "pkill"
+	default y
+	help
+	  Send signals to processes by name.
+
+config PS
+	bool "ps"
+	default y
+	help
+	  ps gives a snapshot of the current processes.
+
+config FEATURE_PS_WIDE
+	bool "Enable wide output option (-w)"
+	default y
+	depends on PS
+	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_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.
+
+config FEATURE_PS_ADDITIONAL_COLUMNS
+	bool "Enable additional ps columns"
+	default y
+	depends on PS && DESKTOP
+	help
+	  Support -o rgroup, -o ruser, -o nice output specifiers.
+
+config FEATURE_PS_UNUSUAL_SYSTEMS
+	bool "Support Linux prior to 2.4.0 and non-ELF systems"
+	default n
+	depends on FEATURE_PS_TIME
+	help
+	  Include support for measuring HZ on old kernels and non-ELF systems
+	  (if you are on Linux 2.4.0+ and use ELF, you don't need this)
+
+config RENICE
+	bool "renice"
+	default y
+	help
+	  Renice alters the scheduling priority of one or more running
+	  processes.
+
+config BB_SYSCTL
+	bool "sysctl"
+	default y
+	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/pstree/top"
+	default y
+	depends on PS || TOP || PSTREE
+	help
+	  Enables the ps -T option, showing of threads in pstree,
+	  and 'h' command in top.
+
+config WATCH
+	bool "watch"
+	default y
+	help
+	  watch is used to execute a program periodically, showing
+	  output to the screen.
+
+endmenu
diff --git a/busybox-1.19.3/procps/Kbuild.src b/busybox-1.19.3/procps/Kbuild.src
new file mode 100644
index 0000000..89b1cc0
--- /dev/null
+++ b/busybox-1.19.3/procps/Kbuild.src
@@ -0,0 +1,22 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+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_PGREP)	+= pgrep.o
+lib-$(CONFIG_PKILL)	+= pgrep.o
+lib-$(CONFIG_PIDOF)	+= pidof.o
+lib-$(CONFIG_PS)	+= ps.o
+lib-$(CONFIG_RENICE)	+= renice.o
+lib-$(CONFIG_BB_SYSCTL)	+= sysctl.o
+lib-$(CONFIG_TOP)	+= top.o
+lib-$(CONFIG_UPTIME)	+= uptime.o
+lib-$(CONFIG_WATCH)     += watch.o
diff --git a/busybox-1.19.3/procps/free.c b/busybox-1.19.3/procps/free.c
new file mode 100644
index 0000000..47f2fc3
--- /dev/null
+++ b/busybox-1.19.3/procps/free.c
@@ -0,0 +1,122 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini free implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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;
+
+	INIT_G();
+
+#if ENABLE_DESKTOP
+	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... */
+	G.mem_unit = (info.mem_unit ? info.mem_unit : 1);
+
+	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)
+	);
+	/* 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("Swap:");
+	printf(FIELDS_3,
+		scale(info.totalswap),
+		scale(info.totalswap - info.freeswap),
+		scale(info.freeswap)
+	);
+#endif
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/procps/fuser.c b/busybox-1.19.3/procps/fuser.c
new file mode 100644
index 0000000..05b52ab
--- /dev/null
+++ b/busybox-1.19.3/procps/fuser.c
@@ -0,0 +1,321 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * tiny fuser implementation
+ *
+ * Copyright 2004 Tony J. White
+ *
+ * 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
+
+#define OPTION_STRING "mks64"
+enum {
+	OPT_MOUNT  = (1 << 0),
+	OPT_KILL   = (1 << 1),
+	OPT_SILENT = (1 << 2),
+	OPT_IP6    = (1 << 3),
+	OPT_IP4    = (1 << 4),
+};
+
+typedef struct inode_list {
+	struct inode_list *next;
+	ino_t inode;
+	dev_t dev;
+} inode_list;
+
+struct globals {
+	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 { \
+	G.mypid = getpid(); \
+	G.killsig = SIGKILL; \
+} while (0)
+
+static void add_inode(const struct stat *st)
+{
+	inode_list **curr = &G.inode_list_head;
+
+	while (*curr) {
+		if ((*curr)->dev == st->st_dev
+		 && (*curr)->inode == st->st_ino
+		) {
+			return;
+		}
+		curr = &(*curr)->next;
+	}
+
+	*curr = xzalloc(sizeof(inode_list));
+	(*curr)->dev = st->st_dev;
+	(*curr)->inode = st->st_ino;
+}
+
+static smallint search_dev_inode(const struct stat *st)
+{
+	inode_list *ilist = G.inode_list_head;
+
+	while (ilist) {
+		if (ilist->dev == st->st_dev) {
+			if (option_mask32 & OPT_MOUNT)
+				return 1;
+			if (ilist->inode == st->st_ino)
+				return 1;
+		}
+		ilist = ilist->next;
+	}
+	return 0;
+}
+
+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 *f;
+	char line[MAX_LINE + 1], addr[68];
+	int major, minor, r;
+	long long uint64_inode;
+	unsigned tmp_port;
+	smallint retval;
+	struct stat statbuf;
+	const char *fmt;
+	void *fag, *sag;
+
+	f = fopen_for_read(path);
+	if (!f)
+		return 0;
+
+	if (G.recursion_depth == PROC_NET) {
+		int fd;
+
+		/* find socket dev */
+		statbuf.st_dev = 0;
+		fd = socket(AF_INET, SOCK_DGRAM, 0);
+		if (fd >= 0) {
+			fstat(fd, &statbuf);
+			close(fd);
+		}
+
+		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;
+	}
+
+	retval = 0;
+	while (fgets(line, MAX_LINE, f)) {
+		r = sscanf(line, fmt, fag, sag, &uint64_inode);
+		if (r != 3)
+			continue;
+
+		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;
+			}
+		}
+	}
+	fclose(f);
+
+	return retval;
+}
+
+static smallint scan_recursive(const char *path)
+{
+	DIR *d;
+	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;
+
+		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)
+{
+	char **pp;
+
+	INIT_G();
+
+	/* Handle -SIGNAL. Oh my... */
+	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" */
+		sig = get_signum(&arg[1]);
+		if (sig < 0)
+			continue;
+		/* "-SIGNAL" option found. Remove it and bail out */
+		G.killsig = sig;
+		do {
+			pp[0] = arg = pp[1];
+			pp++;
+		} while (arg);
+		break;
+	}
+
+	opt_complementary = "-1"; /* at least one param */
+	getopt32(argv, OPTION_STRING);
+	argv += optind;
+
+	pp = argv;
+	while (*pp) {
+		/* parse net arg */
+		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++;
+	}
+
+	if (scan_recursive("/proc")) {
+		if (!(option_mask32 & OPT_SILENT))
+			bb_putchar('\n');
+		return G.kill_failed;
+	}
+
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/procps/iostat.c b/busybox-1.19.3/procps/iostat.c
new file mode 100644
index 0000000..978d234
--- /dev/null
+++ b/busybox-1.19.3/procps/iostat.c
@@ -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;
+}
diff --git a/busybox-1.19.3/procps/kill.c b/busybox-1.19.3/procps/kill.c
new file mode 100644
index 0000000..224e5ad
--- /dev/null
+++ b/busybox-1.19.3/procps/kill.c
@@ -0,0 +1,279 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini kill/killall[5] implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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
+ * kill built-in. Shell substitutes job ids with process groups first.
+ *
+ * This brings some complications:
+ *
+ * + we can't use xfunc here
+ * + we can't use applet_name
+ * + we can't use bb_show_usage
+ * (Above doesn't apply for killall[5] cases)
+ *
+ * kill %n gets translated into kill ' -<process group>' by shell (note space!)
+ * This is needed to avoid collision with kill -9 ... syntax
+ */
+
+int kill_main(int argc, char **argv)
+{
+	char *arg;
+	pid_t pid;
+	int signo = SIGTERM, errors = 0, quiet = 0;
+#if !ENABLE_KILLALL && !ENABLE_KILLALL5
+#define killall 0
+#define killall5 0
+#else
+/* How to determine who we are? find 3rd char from the end:
+ * kill, killall, killall5
+ *  ^i       ^a        ^l  - it's unique
+ * (checking from the start is complicated by /bin/kill... case) */
+	const char char3 = argv[0][strlen(argv[0]) - 3];
+#define killall (ENABLE_KILLALL && char3 == 'a')
+#define killall5 (ENABLE_KILLALL5 && char3 == 'l')
+#endif
+
+	/* Parse any options */
+	argc--;
+	arg = *++argv;
+
+	if (argc < 1 || arg[0] != '-') {
+		goto do_it_now;
+	}
+
+	/* The -l option, which prints out signal names.
+	 * Intended usage in shell:
+	 * 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) {
+			/* Print the whole signal list */
+			print_signames();
+			return 0;
+		}
+		/* -l <sig list> */
+		while ((arg = *++argv)) {
+			if (isdigit(arg[0])) {
+				signo = bb_strtou(arg, NULL, 10);
+				if (errno) {
+					bb_error_msg("unknown signal '%s'", arg);
+					return EXIT_FAILURE;
+				}
+				/* Exitcodes >= 0x80 are to be treated
+				 * as "killed by signal (exitcode & 0x7f)" */
+				puts(get_signame(signo & 0x7f));
+				/* TODO: 'bad' signal# - coreutils says:
+				 * kill: 127: invalid signal
+				 * we just print "127" instead */
+			} else {
+				signo = get_signum(arg);
+				if (signo < 0) {
+					bb_error_msg("unknown signal '%s'", arg);
+					return EXIT_FAILURE;
+				}
+				printf("%d\n", signo);
+			}
+		}
+		/* If they specified -l, we are all done */
+		return EXIT_SUCCESS;
+	}
+
+	/* The -q quiet option */
+	if (killall && arg[1] == 'q' && arg[2] == '\0') {
+		quiet = 1;
+		arg = *++argv;
+		argc--;
+		if (argc < 1)
+			bb_show_usage();
+		if (arg[0] != '-')
+			goto do_it_now;
+	}
+
+	arg++; /* skip '-' */
+
+	/* -o PID? (if present, it always is at the end of command line) */
+	if (killall5 && arg[0] == 'o')
+		goto do_it_now;
+
+	if (argc > 1 && arg[0] == 's' && arg[1] == '\0') { /* -s SIG? */
+		argc--;
+		arg = *++argv;
+	} /* else it must be -SIG */
+	signo = get_signum(arg);
+	if (signo < 0) { /* || signo > MAX_SIGNUM ? */
+		bb_error_msg("bad signal name '%s'", arg);
+		return EXIT_FAILURE;
+	}
+	arg = *++argv;
+	argc--;
+
+ do_it_now:
+	pid = getpid();
+
+	if (killall5) {
+		pid_t sid;
+		procps_status_t* p = NULL;
+		int ret = 0;
+
+		/* Find out our session id */
+		sid = getsid(pid);
+		/* Stop all processes */
+		kill(-1, SIGSTOP);
+		/* Signal all processes except those in our session */
+		while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_SID))) {
+			int i;
+
+			if (p->sid == (unsigned)sid
+			 || p->pid == (unsigned)pid
+			 || p->pid == 1)
+				continue;
+
+			/* All remaining args must be -o PID options.
+			 * Check p->pid against them. */
+			for (i = 0; i < argc; i++) {
+				pid_t omit;
+
+				arg = argv[i];
+				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];
+				omit = bb_strtoi(arg, NULL, 10);
+				if (errno) {
+					bb_error_msg("invalid number '%s'", arg);
+					ret = 1;
+					goto resume;
+				}
+				if (p->pid == omit)
+					goto dont_kill;
+			}
+			kill(p->pid, signo);
+ dont_kill: ;
+		}
+ resume:
+		/* And let them continue */
+		kill(-1, SIGCONT);
+		return ret;
+	}
+
+	/* Pid or name is required for kill/killall */
+	if (argc < 1) {
+		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) {
+			pid_t* pidList;
+
+			pidList = find_pid_by_name(arg);
+			if (*pidList == 0) {
+				errors++;
+				if (!quiet)
+					bb_error_msg("%s: no process killed", arg);
+			} else {
+				pid_t *pl;
+
+				for (pl = pidList; *pl; pl++) {
+					if (*pl == pid)
+						continue;
+					if (kill(*pl, signo) == 0)
+						continue;
+					errors++;
+					if (!quiet)
+						bb_perror_msg("can't kill pid %d", (int)*pl);
+				}
+			}
+			free(pidList);
+			arg = *++argv;
+		}
+		return errors;
+	}
+
+	/* Looks like they want to do a kill. Do that */
+	while (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);
+				*end = '\0';
+				errors++;
+			} else 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("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/busybox-1.19.3/procps/mpstat.c b/busybox-1.19.3/procps/mpstat.c
new file mode 100644
index 0000000..aa5a5c7
--- /dev/null
+++ b/busybox-1.19.3/procps/mpstat.c
@@ -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;
+}
diff --git a/busybox-1.19.3/procps/nmeter.c b/busybox-1.19.3/procps/nmeter.c
new file mode 100644
index 0000000..9999559
--- /dev/null
+++ b/busybox-1.19.3/procps/nmeter.c
@@ -0,0 +1,951 @@
+/*
+ * 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
+// /proc/locks
+// /proc/stat:
+// disk_io: (3,0):(22272,17897,410702,4375,54750)
+// btime 1059401962
+//TODO: use sysinfo libc call/syscall, if appropriate
+// (faster than open/read/close):
+// sysinfo({uptime=15017, loads=[5728, 15040, 16480]
+//  totalram=2107416576, freeram=211525632, sharedram=0, bufferram=157204480}
+//  totalswap=134209536, freeswap=134209536, procs=157})
+
+#include "libbb.h"
+
+typedef unsigned long long ullong;
+
+enum {  /* Preferably use powers of 2 */
+	PROC_MIN_FILE_SIZE = 256,
+	PROC_MAX_FILE_SIZE = 16 * 1024,
+};
+
+typedef struct proc_file {
+	char *file;
+	int file_sz;
+	smallint last_gen;
+} proc_file;
+
+static const char *const proc_name[] = {
+	"stat",		// Must match the order of proc_file's!
+	"loadavg",
+	"net/dev",
+	"meminfo",
+	"diskstats",
+	"sys/fs/file-nr"
+};
+
+struct globals {
+	// Sample generation flip-flop
+	smallint gen;
+	// Linux 2.6? (otherwise assumes 2.4)
+	smallint is26;
+	// 1 if sample delay is not an integer fraction of a second
+	smallint need_seconds;
+	char *cur_outbuf;
+	const char *final_str;
+	int delta;
+	int deltanz;
+	struct timeval tv;
+#define first_proc_file proc_stat
+	proc_file proc_stat;	// Must match the order of proc_name's!
+	proc_file proc_loadavg;
+	proc_file proc_net_dev;
+	proc_file proc_meminfo;
+	proc_file proc_diskstats;
+	proc_file proc_sys_fs_filenr;
+};
+#define G (*ptr_to_globals)
+#define gen                (G.gen               )
+#define is26               (G.is26              )
+#define need_seconds       (G.need_seconds      )
+#define cur_outbuf         (G.cur_outbuf        )
+#define final_str          (G.final_str         )
+#define delta              (G.delta             )
+#define deltanz            (G.deltanz           )
+#define tv                 (G.tv                )
+#define proc_stat          (G.proc_stat         )
+#define proc_loadavg       (G.proc_loadavg      )
+#define proc_net_dev       (G.proc_net_dev      )
+#define proc_meminfo       (G.proc_meminfo      )
+#define proc_diskstats     (G.proc_diskstats    )
+#define proc_sys_fs_filenr (G.proc_sys_fs_filenr)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	cur_outbuf = outbuf; \
+	final_str = "\n"; \
+	deltanz = delta = 1000000; \
+} while (0)
+
+// We depend on this being a char[], not char* - we take sizeof() of it
+#define outbuf bb_common_bufsiz1
+
+static inline void reset_outbuf(void)
+{
+	cur_outbuf = outbuf;
+}
+
+static inline int outbuf_count(void)
+{
+	return cur_outbuf - outbuf;
+}
+
+static void print_outbuf(void)
+{
+	int sz = cur_outbuf - outbuf;
+	if (sz > 0) {
+		xwrite(STDOUT_FILENO, outbuf, sz);
+		cur_outbuf = outbuf;
+	}
+}
+
+static void put(const char *s)
+{
+	int sz = strlen(s);
+	if (sz > outbuf + sizeof(outbuf) - cur_outbuf)
+		sz = outbuf + sizeof(outbuf) - cur_outbuf;
+	memcpy(cur_outbuf, s, sz);
+	cur_outbuf += sz;
+}
+
+static void put_c(char c)
+{
+	if (cur_outbuf < outbuf + sizeof(outbuf))
+		*cur_outbuf++ = c;
+}
+
+static void put_question_marks(int count)
+{
+	while (count--)
+		put_c('?');
+}
+
+static void readfile_z(proc_file *pf, const char* fname)
+{
+// open_read_close() will do two reads in order to be sure we are at EOF,
+// and we don't need/want that.
+	int fd;
+	int sz, rdsz;
+	char *buf;
+
+	sz = pf->file_sz;
+	buf = pf->file;
+	if (!buf) {
+		buf = xmalloc(PROC_MIN_FILE_SIZE);
+		sz = PROC_MIN_FILE_SIZE;
+	}
+ again:
+	fd = xopen(fname, O_RDONLY);
+	buf[0] = '\0';
+	rdsz = read(fd, buf, sz-1);
+	close(fd);
+	if (rdsz > 0) {
+		if (rdsz == sz-1 && sz < PROC_MAX_FILE_SIZE) {
+			sz *= 2;
+			buf = xrealloc(buf, sz);
+			goto again;
+		}
+		buf[rdsz] = '\0';
+	}
+	pf->file_sz = sz;
+	pf->file = buf;
+}
+
+static const char* get_file(proc_file *pf)
+{
+	if (pf->last_gen != gen) {
+		pf->last_gen = gen;
+		readfile_z(pf, proc_name[pf - &first_proc_file]);
+	}
+	return pf->file;
+}
+
+static ullong read_after_slash(const char *p)
+{
+	p = strchr(p, '/');
+	if (!p) return 0;
+	return strtoull(p+1, NULL, 10);
+}
+
+enum conv_type { conv_decimal, conv_slash };
+
+// Reads decimal values from line. Values start after key, for example:
+// "cpu  649369 0 341297 4336769..." - key is "cpu" here.
+// Values are stored in vec[]. arg_ptr has list of positions
+// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value.
+static int vrdval(const char* p, const char* key,
+	enum conv_type conv, ullong *vec, va_list arg_ptr)
+{
+	int indexline;
+	int indexnext;
+
+	p = strstr(p, key);
+	if (!p) return 1;
+
+	p += strlen(key);
+	indexline = 1;
+	indexnext = va_arg(arg_ptr, int);
+	while (1) {
+		while (*p == ' ' || *p == '\t') p++;
+		if (*p == '\n' || *p == '\0') break;
+
+		if (indexline == indexnext) { // read this value
+			*vec++ = conv==conv_decimal ?
+				strtoull(p, NULL, 10) :
+				read_after_slash(p);
+			indexnext = va_arg(arg_ptr, int);
+		}
+		while (*p > ' ') p++; // skip over value
+		indexline++;
+	}
+	return 0;
+}
+
+// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0":
+// rdval(file_contents, "string_to_find", result_vector, value#, value#...)
+// value# start with 1
+static int rdval(const char* p, const char* key, ullong *vec, ...)
+{
+	va_list arg_ptr;
+	int result;
+
+	va_start(arg_ptr, vec);
+	result = vrdval(p, key, conv_decimal, vec, arg_ptr);
+	va_end(arg_ptr);
+
+	return result;
+}
+
+// Parses files with lines like "... ... ... 3/148 ...."
+static int rdval_loadavg(const char* p, ullong *vec, ...)
+{
+	va_list arg_ptr;
+	int result;
+
+	va_start(arg_ptr, vec);
+	result = vrdval(p, "", conv_slash, vec, arg_ptr);
+	va_end(arg_ptr);
+
+	return result;
+}
+
+// Parses /proc/diskstats
+//   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
+static int rdval_diskstats(const char* p, ullong *vec)
+{
+	ullong rd = rd; // for compiler
+	int indexline = 0;
+	vec[0] = 0;
+	vec[1] = 0;
+	while (1) {
+		indexline++;
+		while (*p == ' ' || *p == '\t') p++;
+		if (*p == '\0') break;
+		if (*p == '\n') {
+			indexline = 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)
+			vec[1] += strtoull(p, NULL, 10);
+			while (*p != '\n' && *p != '\0') p++;
+			continue;
+		}
+		while (*p > ' ') p++; // skip over value
+	}
+	return 0;
+}
+
+static void scale(ullong ul)
+{
+	char buf[5];
+
+	/* see http://en.wikipedia.org/wiki/Tera */
+	smart_ulltoa4(ul, buf, " kmgtpezy");
+	buf[4] = '\0';
+	put(buf);
+}
+
+
+#define S_STAT(a) \
+typedef struct a { \
+	struct s_stat *next; \
+	void (*collect)(struct a *s) FAST_FUNC; \
+	const char *label;
+#define S_STAT_END(a) } a;
+
+S_STAT(s_stat)
+S_STAT_END(s_stat)
+
+static void FAST_FUNC collect_literal(s_stat *s UNUSED_PARAM)
+{
+}
+
+static s_stat* init_literal(void)
+{
+	s_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_literal;
+	return (s_stat*)s;
+}
+
+static s_stat* init_delay(const char *param)
+{
+	delta = strtoul(param, NULL, 0) * 1000; /* param can be "" */
+	deltanz = delta > 0 ? delta : 1;
+	need_seconds = (1000000%deltanz) != 0;
+	return NULL;
+}
+
+static s_stat* init_cr(const char *param UNUSED_PARAM)
+{
+	final_str = "\r";
+	return (s_stat*)0;
+}
+
+
+//     user nice system idle  iowait irq  softirq (last 3 only in 2.6)
+//cpu  649369 0 341297 4336769 11640 7122 1183
+//cpuN 649369 0 341297 4336769 11640 7122 1183
+enum { CPU_FIELDCNT = 7 };
+S_STAT(cpu_stat)
+	ullong old[CPU_FIELDCNT];
+	int bar_sz;
+	char *bar;
+S_STAT_END(cpu_stat)
+
+
+static void FAST_FUNC collect_cpu(cpu_stat *s)
+{
+	ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
+	unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 };
+	ullong all = 0;
+	int norm_all = 0;
+	int bar_sz = s->bar_sz;
+	char *bar = s->bar;
+	int i;
+
+	if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) {
+		put_question_marks(bar_sz);
+		return;
+	}
+
+	for (i = 0; i < CPU_FIELDCNT; i++) {
+		ullong old = s->old[i];
+		if (data[i] < old) old = data[i];		//sanitize
+		s->old[i] = data[i];
+		all += (data[i] -= old);
+	}
+
+	if (all) {
+		for (i = 0; i < CPU_FIELDCNT; i++) {
+			ullong t = bar_sz * data[i];
+			norm_all += data[i] = t / all;
+			frac[i] = t % all;
+		}
+
+		while (norm_all < bar_sz) {
+			unsigned max = frac[0];
+			int pos = 0;
+			for (i = 1; i < CPU_FIELDCNT; i++) {
+				if (frac[i] > max) max = frac[i], pos = i;
+			}
+			frac[pos] = 0;	//avoid bumping up same value twice
+			data[pos]++;
+			norm_all++;
+		}
+
+		memset(bar, '.', bar_sz);
+		memset(bar, 'S', data[2]); bar += data[2]; //sys
+		memset(bar, 'U', data[0]); bar += data[0]; //usr
+		memset(bar, 'N', data[1]); bar += data[1]; //nice
+		memset(bar, 'D', data[4]); bar += data[4]; //iowait
+		memset(bar, 'I', data[5]); bar += data[5]; //irq
+		memset(bar, 'i', data[6]); bar += data[6]; //softirq
+	} else {
+		memset(bar, '?', bar_sz);
+	}
+	put(s->bar);
+}
+
+
+static s_stat* init_cpu(const char *param)
+{
+	int sz;
+	cpu_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_cpu;
+	sz = strtoul(param, NULL, 0); /* param can be "" */
+	if (sz < 10) sz = 10;
+	if (sz > 1000) sz = 1000;
+	s->bar = xzalloc(sz+1);
+	/*s->bar[sz] = '\0'; - xzalloc did it */
+	s->bar_sz = sz;
+	return (s_stat*)s;
+}
+
+
+S_STAT(int_stat)
+	ullong old;
+	int no;
+S_STAT_END(int_stat)
+
+static void FAST_FUNC collect_int(int_stat *s)
+{
+	ullong data[1];
+	ullong old;
+
+	if (rdval(get_file(&proc_stat), "intr", data, s->no)) {
+		put_question_marks(4);
+		return;
+	}
+
+	old = s->old;
+	if (data[0] < old) old = data[0];		//sanitize
+	s->old = data[0];
+	scale(data[0] - old);
+}
+
+static s_stat* init_int(const char *param)
+{
+	int_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_int;
+	if (param[0] == '\0') {
+		s->no = 1;
+	} else {
+		int n = xatoi_positive(param);
+		s->no = n + 2;
+	}
+	return (s_stat*)s;
+}
+
+
+S_STAT(ctx_stat)
+	ullong old;
+S_STAT_END(ctx_stat)
+
+static void FAST_FUNC collect_ctx(ctx_stat *s)
+{
+	ullong data[1];
+	ullong old;
+
+	if (rdval(get_file(&proc_stat), "ctxt", data, 1)) {
+		put_question_marks(4);
+		return;
+	}
+
+	old = s->old;
+	if (data[0] < old) old = data[0];		//sanitize
+	s->old = data[0];
+	scale(data[0] - old);
+}
+
+static s_stat* init_ctx(const char *param UNUSED_PARAM)
+{
+	ctx_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_ctx;
+	return (s_stat*)s;
+}
+
+
+S_STAT(blk_stat)
+	const char* lookfor;
+	ullong old[2];
+S_STAT_END(blk_stat)
+
+static void FAST_FUNC collect_blk(blk_stat *s)
+{
+	ullong data[2];
+	int i;
+
+	if (is26) {
+		i = rdval_diskstats(get_file(&proc_diskstats), data);
+	} else {
+		i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2);
+		// Linux 2.4 reports bio in Kbytes, convert to sectors:
+		data[0] *= 2;
+		data[1] *= 2;
+	}
+	if (i) {
+		put_question_marks(9);
+		return;
+	}
+
+	for (i=0; i<2; i++) {
+		ullong old = s->old[i];
+		if (data[i] < old) old = data[i];		//sanitize
+		s->old[i] = data[i];
+		data[i] -= old;
+	}
+	scale(data[0]*512); // TODO: *sectorsize
+	put_c(' ');
+	scale(data[1]*512);
+}
+
+static s_stat* init_blk(const char *param UNUSED_PARAM)
+{
+	blk_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_blk;
+	s->lookfor = "page";
+	return (s_stat*)s;
+}
+
+
+S_STAT(fork_stat)
+	ullong old;
+S_STAT_END(fork_stat)
+
+static void FAST_FUNC collect_thread_nr(fork_stat *s UNUSED_PARAM)
+{
+	ullong data[1];
+
+	if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) {
+		put_question_marks(4);
+		return;
+	}
+	scale(data[0]);
+}
+
+static void FAST_FUNC collect_fork(fork_stat *s)
+{
+	ullong data[1];
+	ullong old;
+
+	if (rdval(get_file(&proc_stat), "processes", data, 1)) {
+		put_question_marks(4);
+		return;
+	}
+
+	old = s->old;
+	if (data[0] < old) old = data[0];	//sanitize
+	s->old = data[0];
+	scale(data[0] - old);
+}
+
+static s_stat* init_fork(const char *param)
+{
+	fork_stat *s = xzalloc(sizeof(*s));
+	if (*param == 'n') {
+		s->collect = collect_thread_nr;
+	} else {
+		s->collect = collect_fork;
+	}
+	return (s_stat*)s;
+}
+
+
+S_STAT(if_stat)
+	ullong old[4];
+	const char *device;
+	char *device_colon;
+S_STAT_END(if_stat)
+
+static void FAST_FUNC collect_if(if_stat *s)
+{
+	ullong data[4];
+	int i;
+
+	if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) {
+		put_question_marks(10);
+		return;
+	}
+
+	for (i=0; i<4; i++) {
+		ullong old = s->old[i];
+		if (data[i] < old) old = data[i];		//sanitize
+		s->old[i] = data[i];
+		data[i] -= old;
+	}
+	put_c(data[1] ? '*' : ' ');
+	scale(data[0]);
+	put_c(data[3] ? '*' : ' ');
+	scale(data[2]);
+}
+
+static s_stat* init_if(const char *device)
+{
+	if_stat *s = xzalloc(sizeof(*s));
+
+	if (!device || !device[0])
+		bb_show_usage();
+	s->collect = collect_if;
+
+	s->device = device;
+	s->device_colon = xasprintf("%s:", device);
+	return (s_stat*)s;
+}
+
+
+S_STAT(mem_stat)
+	char opt;
+S_STAT_END(mem_stat)
+
+// "Memory" value should not include any caches.
+// IOW: neither "ls -laR /" nor heavy read/write activity
+//      should affect it. We'd like to also include any
+//      long-term allocated kernel-side mem, but it is hard
+//      to figure out. For now, bufs, cached & slab are
+//      counted as "free" memory
+//2.6.16:
+//MemTotal:       773280 kB
+//MemFree:         25912 kB - genuinely free
+//Buffers:        320672 kB - cache
+//Cached:         146396 kB - cache
+//SwapCached:          0 kB
+//Active:         183064 kB
+//Inactive:       356892 kB
+//HighTotal:           0 kB
+//HighFree:            0 kB
+//LowTotal:       773280 kB
+//LowFree:         25912 kB
+//SwapTotal:      131064 kB
+//SwapFree:       131064 kB
+//Dirty:              48 kB
+//Writeback:           0 kB
+//Mapped:          96620 kB
+//Slab:           200668 kB - takes 7 Mb on my box fresh after boot,
+//                            but includes dentries and inodes
+//                            (== can take arbitrary amount of mem)
+//CommitLimit:    517704 kB
+//Committed_AS:   236776 kB
+//PageTables:       1248 kB
+//VmallocTotal:   516052 kB
+//VmallocUsed:      3852 kB
+//VmallocChunk:   512096 kB
+//HugePages_Total:     0
+//HugePages_Free:      0
+//Hugepagesize:     4096 kB
+static void FAST_FUNC collect_mem(mem_stat *s)
+{
+	ullong m_total = 0;
+	ullong m_free = 0;
+	ullong m_bufs = 0;
+	ullong m_cached = 0;
+	ullong m_slab = 0;
+
+	if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) {
+		put_question_marks(4);
+		return;
+	}
+	if (s->opt == 't') {
+		scale(m_total << 10);
+		return;
+	}
+
+	if (rdval(proc_meminfo.file, "MemFree:", &m_free  , 1)
+	 || rdval(proc_meminfo.file, "Buffers:", &m_bufs  , 1)
+	 || rdval(proc_meminfo.file, "Cached:",  &m_cached, 1)
+	 || rdval(proc_meminfo.file, "Slab:",    &m_slab  , 1)
+	) {
+		put_question_marks(4);
+		return;
+	}
+
+	m_free += m_bufs + m_cached + m_slab;
+	switch (s->opt) {
+	case 'f':
+		scale(m_free << 10); break;
+	default:
+		scale((m_total - m_free) << 10); break;
+	}
+}
+
+static s_stat* init_mem(const char *param)
+{
+	mem_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_mem;
+	s->opt = param[0];
+	return (s_stat*)s;
+}
+
+
+S_STAT(swp_stat)
+S_STAT_END(swp_stat)
+
+static void FAST_FUNC collect_swp(swp_stat *s UNUSED_PARAM)
+{
+	ullong s_total[1];
+	ullong s_free[1];
+	if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1)
+	 || rdval(proc_meminfo.file,       "SwapFree:" , s_free,  1)
+	) {
+		put_question_marks(4);
+		return;
+	}
+	scale((s_total[0]-s_free[0]) << 10);
+}
+
+static s_stat* init_swp(const char *param UNUSED_PARAM)
+{
+	swp_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_swp;
+	return (s_stat*)s;
+}
+
+
+S_STAT(fd_stat)
+S_STAT_END(fd_stat)
+
+static void FAST_FUNC collect_fd(fd_stat *s UNUSED_PARAM)
+{
+	ullong data[2];
+
+	if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) {
+		put_question_marks(4);
+		return;
+	}
+
+	scale(data[0] - data[1]);
+}
+
+static s_stat* init_fd(const char *param UNUSED_PARAM)
+{
+	fd_stat *s = xzalloc(sizeof(*s));
+	s->collect = collect_fd;
+	return (s_stat*)s;
+}
+
+
+S_STAT(time_stat)
+	int prec;
+	int scale;
+S_STAT_END(time_stat)
+
+static void FAST_FUNC collect_time(time_stat *s)
+{
+	char buf[sizeof("12:34:56.123456")];
+	struct tm* tm;
+	int us = tv.tv_usec + s->scale/2;
+	time_t t = tv.tv_sec;
+
+	if (us >= 1000000) {
+		t++;
+		us -= 1000000;
+	}
+	tm = localtime(&t);
+
+	sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec);
+	if (s->prec)
+		sprintf(buf+8, ".%0*d", s->prec, us / s->scale);
+	put(buf);
+}
+
+static s_stat* init_time(const char *param)
+{
+	int prec;
+	time_stat *s = xzalloc(sizeof(*s));
+
+	s->collect = collect_time;
+	prec = param[0] - '0';
+	if (prec < 0) prec = 0;
+	else if (prec > 6) prec = 6;
+	s->prec = prec;
+	s->scale = 1;
+	while (prec++ < 6)
+		s->scale *= 10;
+	return (s_stat*)s;
+}
+
+static void FAST_FUNC collect_info(s_stat *s)
+{
+	gen ^= 1;
+	while (s) {
+		put(s->label);
+		s->collect(s);
+		s = s->next;
+	}
+}
+
+
+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,
+	init_cpu,
+	init_mem,
+	init_swp,
+	init_fd,
+	init_int,
+	init_ctx,
+	init_fork,
+	init_time,
+	init_blk,
+	init_delay,
+	init_cr
+};
+
+int nmeter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nmeter_main(int argc UNUSED_PARAM, char **argv)
+{
+	char buf[32];
+	s_stat *first = NULL;
+	s_stat *last = NULL;
+	s_stat *s;
+	char *opt_d;
+	char *cur, *prev;
+
+	INIT_G();
+
+	xchdir("/proc");
+
+	if (open_read_close("version", buf, sizeof(buf)-1) > 0) {
+		buf[sizeof(buf)-1] = '\0';
+		is26 = (strstr(buf, " 2.4.") == NULL);
+	}
+
+	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[0]);
+	while (1) {
+		char *param, *p;
+		prev = cur;
+ again:
+		cur = strchr(cur, '%');
+		if (!cur)
+			break;
+		if (cur[1] == '%') {	// %%
+			overlapping_strcpy(cur, cur + 1);
+			cur++;
+			goto again;
+		}
+		*cur++ = '\0';		// overwrite %
+		if (cur[0] == '[') {
+			// format: %[foptstring]
+			cur++;
+			p = strchr(options, cur[0]);
+			param = cur+1;
+			while (cur[0] != ']') {
+				if (!cur[0])
+					bb_show_usage();
+				cur++;
+			}
+			*cur++ = '\0';	// overwrite [
+		} else {
+			// format: %NNNNNNf
+			param = cur;
+			while (cur[0] >= '0' && cur[0] <= '9')
+				cur++;
+			if (!cur[0])
+				bb_show_usage();
+			p = strchr(options, cur[0]);
+			*cur++ = '\0';	// overwrite format char
+		}
+		if (!p)
+			bb_show_usage();
+		s = init_functions[p-options](param);
+		if (s) {
+			s->label = prev;
+			/*s->next = NULL; - all initXXX funcs use xzalloc */
+			if (!first)
+				first = s;
+			else
+				last->next = s;
+			last = s;
+		} else {
+			// %NNNNd or %r option. remove it from string
+			strcpy(prev + strlen(prev), cur);
+			cur = prev;
+		}
+	}
+	if (prev[0]) {
+		s = init_literal();
+		s->label = prev;
+		/*s->next = NULL; - all initXXX funcs use xzalloc */
+		if (!first)
+			first = s;
+		else
+			last->next = s;
+		last = s;
+	}
+
+	// Generate first samples but do not print them, they're bogus
+	collect_info(first);
+	reset_outbuf();
+	if (delta >= 0) {
+		gettimeofday(&tv, NULL);
+		usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz);
+	}
+
+	while (1) {
+		gettimeofday(&tv, NULL);
+		collect_info(first);
+		put(final_str);
+		print_outbuf();
+
+		// Negative delta -> no usleep at all
+		// This will hog the CPU but you can have REALLY GOOD
+		// time resolution ;)
+		// TODO: detect and avoid useless updates
+		// (like: nothing happens except time)
+		if (delta >= 0) {
+			int rem;
+			// can be commented out, will sacrifice sleep time precision a bit
+			gettimeofday(&tv, NULL);
+			if (need_seconds)
+				rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz;
+			else
+				rem = delta - tv.tv_usec%deltanz;
+			// Sometimes kernel wakes us up just a tiny bit earlier than asked
+			// Do not go to very short sleep in this case
+			if (rem < delta/128) {
+				rem += delta;
+			}
+			usleep(rem);
+		}
+	}
+
+	/*return 0;*/
+}
diff --git a/busybox-1.19.3/procps/pgrep.c b/busybox-1.19.3/procps/pgrep.c
new file mode 100644
index 0000000..dc7ffff
--- /dev/null
+++ b/busybox-1.19.3/procps/pgrep.c
@@ -0,0 +1,182 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini pgrep/pkill implementation for busybox
+ *
+ * Copyright (C) 2007 Loic Grenie <loic.grenie@gmail.com>
+ *
+ * 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"
+
+/* Idea taken from kill.c */
+#define pgrep (ENABLE_PGREP && applet_name[1] == 'g')
+#define pkill (ENABLE_PKILL && applet_name[1] == 'k')
+
+enum {
+	/* "vlfxons:P:" */
+	OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
+	OPTBIT_L,
+	OPTBIT_F,
+	OPTBIT_X,
+	OPTBIT_O,
+	OPTBIT_N,
+	OPTBIT_S,
+	OPTBIT_P,
+};
+
+#define OPT_INVERT	(opt & (1 << OPTBIT_V))
+#define OPT_LIST	(opt & (1 << OPTBIT_L))
+#define OPT_FULL	(opt & (1 << OPTBIT_F))
+#define OPT_ANCHOR	(opt & (1 << OPTBIT_X))
+#define OPT_FIRST	(opt & (1 << OPTBIT_O))
+#define OPT_LAST	(opt & (1 << OPTBIT_N))
+#define OPT_SID		(opt & (1 << OPTBIT_S))
+#define OPT_PPID	(opt & (1 << OPTBIT_P))
+
+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);
+		else
+			printf("%d\n", pid);
+	} else
+		kill(pid, signo);
+}
+
+int pgrep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pgrep_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned pid;
+	int signo;
+	unsigned opt;
+	int scan_mask;
+	int matched_pid;
+	int sid2match, ppid2match;
+	char *cmd_last;
+	procps_status_t *proc;
+	/* These are initialized to 0 */
+	struct {
+		regex_t re_buffer;
+		regmatch_t re_match[1];
+	} Z;
+#define re_buffer (Z.re_buffer)
+#define re_match  (Z.re_match )
+
+	memset(&Z, 0, sizeof(Z));
+
+	/* Parse -SIGNAL for pkill. Must be first option, if present */
+	signo = SIGTERM;
+	if (pkill && argv[1] && argv[1][0] == '-') {
+		int temp = get_signum(argv[1]+1);
+		if (temp != -1) {
+			signo = temp;
+			argv++;
+		}
+	}
+
+	/* Parse remaining options */
+	ppid2match = -1;
+	sid2match = -1;
+	opt_complementary = "s+:P+"; /* numeric opts */
+	opt = getopt32(argv, "vlfxons:P:", &sid2match, &ppid2match);
+	argv += optind;
+
+	if (pkill && OPT_LIST) { /* -l: print the whole signal list */
+		print_signames();
+		return 0;
+	}
+
+	pid = getpid();
+	if (sid2match == 0)
+		sid2match = getsid(pid);
+
+	scan_mask = PSSCAN_COMM | PSSCAN_ARGV0;
+	if (OPT_FULL)
+		scan_mask |= PSSCAN_ARGVN;
+
+	/* One pattern is required, if no -s and no -P */
+	if ((sid2match & ppid2match) < 0 && (!argv[0] || argv[1]))
+		bb_show_usage();
+
+	if (argv[0])
+		xregcomp(&re_buffer, argv[0], REG_EXTENDED | REG_NOSUB);
+
+	matched_pid = 0;
+	cmd_last = NULL;
+	proc = NULL;
+	while ((proc = procps_scan(proc, scan_mask)) != NULL) {
+		char *cmd;
+
+		if (proc->pid == pid)
+			continue;
+
+		cmd = proc->argv0;
+		if (!cmd) {
+			cmd = proc->comm;
+		} else {
+			int i = proc->argv_len;
+			while (--i >= 0) {
+				if ((unsigned char)cmd[i] < ' ')
+					cmd[i] = ' ';
+			}
+		}
+
+		if (ppid2match >= 0 && ppid2match != proc->ppid)
+			continue;
+		if (sid2match >= 0  && sid2match != proc->sid)
+			continue;
+
+		/* NB: OPT_INVERT is always 0 or 1 */
+		if (!argv[0]
+		 || (regexec(&re_buffer, cmd, 1, re_match, 0) == 0 /* match found */
+		    && (!OPT_ANCHOR || (re_match[0].rm_so == 0 && re_match[0].rm_eo == (regoff_t)strlen(cmd)))
+		    ) ^ OPT_INVERT
+		) {
+			matched_pid = proc->pid;
+			if (OPT_LAST) {
+				free(cmd_last);
+				cmd_last = xstrdup(cmd);
+				continue;
+			}
+			act(proc->pid, cmd, signo);
+			if (OPT_FIRST)
+				break;
+		}
+	}
+
+	if (cmd_last) {
+		act(matched_pid, cmd_last, signo);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(cmd_last);
+	}
+	return matched_pid == 0; /* return 1 if no processes listed/signaled */
+}
diff --git a/busybox-1.19.3/procps/pidof.c b/busybox-1.19.3/procps/pidof.c
new file mode 100644
index 0000000..6d7b591
--- /dev/null
+++ b/busybox-1.19.3/procps/pidof.c
@@ -0,0 +1,114 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pidof implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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 {
+	IF_FEATURE_PIDOF_SINGLE(OPTBIT_SINGLE,)
+	IF_FEATURE_PIDOF_OMIT(  OPTBIT_OMIT  ,)
+	OPT_SINGLE = IF_FEATURE_PIDOF_SINGLE((1<<OPTBIT_SINGLE)) + 0,
+	OPT_OMIT   = IF_FEATURE_PIDOF_OMIT(  (1<<OPTBIT_OMIT  )) + 0,
+};
+
+int pidof_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pidof_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned first = 1;
+	unsigned opt;
+#if ENABLE_FEATURE_PIDOF_OMIT
+	llist_t *omits = NULL; /* list of pids to omit */
+	opt_complementary = "o::";
+#endif
+
+	/* do unconditional option parsing */
+	opt = getopt32(argv, ""
+			IF_FEATURE_PIDOF_SINGLE ("s")
+			IF_FEATURE_PIDOF_OMIT("o:", &omits));
+
+#if ENABLE_FEATURE_PIDOF_OMIT
+	/* fill omit list.  */
+	{
+		llist_t *omits_p = omits;
+		while (1) {
+			omits_p = llist_find_str(omits_p, "%PPID");
+			if (!omits_p)
+				break;
+			/* are we asked to exclude the parent's process ID?  */
+			omits_p->data = utoa((unsigned)getppid());
+		}
+	}
+#endif
+	/* Looks like everything is set to go.  */
+	argv += optind;
+	while (*argv) {
+		pid_t *pidList;
+		pid_t *pl;
+
+		/* reverse the pidlist like GNU pidof does.  */
+		pidList = pidlist_reverse(find_pid_by_name(*argv));
+		for (pl = pidList; *pl; pl++) {
+#if ENABLE_FEATURE_PIDOF_OMIT
+			if (opt & OPT_OMIT) {
+				llist_t *omits_p = omits;
+				while (omits_p) {
+					if (xatoul(omits_p->data) == (unsigned long)(*pl)) {
+						goto omitting;
+					}
+					omits_p = omits_p->link;
+				}
+			}
+#endif
+			printf(" %u" + first, (unsigned)*pl);
+			first = 0;
+			if (ENABLE_FEATURE_PIDOF_SINGLE && (opt & OPT_SINGLE))
+				break;
+#if ENABLE_FEATURE_PIDOF_OMIT
+ omitting: ;
+#endif
+		}
+		free(pidList);
+		argv++;
+	}
+	if (!first)
+		bb_putchar('\n');
+
+#if ENABLE_FEATURE_PIDOF_OMIT
+	if (ENABLE_FEATURE_CLEAN_UP)
+		llist_free(omits, NULL);
+#endif
+	return first; /* 1 (failure) - no processes found */
+}
diff --git a/busybox-1.19.3/procps/pmap.c b/busybox-1.19.3/procps/pmap.c
new file mode 100644
index 0000000..fd995a5
--- /dev/null
+++ b/busybox-1.19.3/procps/pmap.c
@@ -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/busybox-1.19.3/procps/powertop.c b/busybox-1.19.3/procps/powertop.c
new file mode 100644
index 0000000..008cdfc
--- /dev/null
+++ b/busybox-1.19.3/procps/powertop.c
@@ -0,0 +1,857 @@
+/* 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_BIN, 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];
+
+		strbuf6[5] = '\0';
+		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");
+				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 cpufreq_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;
+}
diff --git a/busybox-1.19.3/procps/ps.c b/busybox-1.19.3/procps/ps.c
new file mode 100644
index 0000000..dcc0f7b
--- /dev/null
+++ b/busybox-1.19.3/procps/ps.c
@@ -0,0 +1,707 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini ps implementation(s) for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Fix for SELinux Support:(c)2007 Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *                         (c)2007 Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * 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:
+//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"
+
+/* Absolute maximum on output line length */
+enum { MAX_WIDTH = 2*1024 };
+
+#if ENABLE_DESKTOP
+
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+#include <sys/times.h> /* for times() */
+#ifndef AT_CLKTCK
+# 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];
+	const char *header;
+	void (*f)(char *buf, int size, const procps_status_t *ps);
+	int ps_flags;
+} ps_out_t;
+
+struct globals {
+	ps_out_t* out;
+	int out_cnt;
+	int print_header;
+	int need_flags;
+	char *buffer;
+	unsigned terminal_width;
+#if ENABLE_FEATURE_PS_TIME
+	unsigned kernel_HZ;
+	unsigned long long seconds_since_boot;
+#endif
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define out                (G.out               )
+#define out_cnt            (G.out_cnt           )
+#define print_header       (G.print_header      )
+#define need_flags         (G.need_flags        )
+#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 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)
+{
+	ptrdiff_t *ep = (ptrdiff_t *) environ;
+
+	while (*ep++)
+		continue;
+	while (*ep) {
+		if (ep[0] == findme) {
+			return ep[1];
+		}
+		ep += 2;
+	}
+	return -1;
+}
+
+#if ENABLE_FEATURE_PS_UNUSUAL_SYSTEMS
+static unsigned get_HZ_by_waiting(void)
+{
+	struct timeval tv1, tv2;
+	unsigned t1, t2, r, hz;
+	unsigned cnt = cnt; /* for compiler */
+	int diff;
+
+	r = 0;
+
+	/* Wait for times() to reach new tick */
+	t1 = times(NULL);
+	do {
+		t2 = times(NULL);
+	} while (t2 == t1);
+	gettimeofday(&tv2, NULL);
+
+	do {
+		t1 = t2;
+		tv1.tv_usec = tv2.tv_usec;
+
+		/* Wait exactly one times() tick */
+		do {
+			t2 = times(NULL);
+		} while (t2 == t1);
+		gettimeofday(&tv2, NULL);
+
+		/* Calculate ticks per sec, rounding up to even */
+		diff = tv2.tv_usec - tv1.tv_usec;
+		if (diff <= 0) diff += 1000000;
+		hz = 1000000u / (unsigned)diff;
+		hz = (hz+1) & ~1;
+
+		/* Count how many same hz values we saw */
+		if (r != hz) {
+			r = hz;
+			cnt = 0;
+		}
+		cnt++;
+	} while (cnt < 3); /* exit if saw 3 same values */
+
+	return r;
+}
+#else
+static inline unsigned get_HZ_by_waiting(void)
+{
+	/* Better method? */
+	return 100;
+}
+#endif
+
+static unsigned get_kernel_HZ(void)
+{
+	//char buf[64];
+	struct sysinfo info;
+
+	if (kernel_HZ)
+		return kernel_HZ;
+
+	/* Works for ELF only, Linux 2.4.0+ */
+	kernel_HZ = find_elf_note(AT_CLKTCK);
+	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;
+
+	return kernel_HZ;
+}
+#endif
+
+/* Print value to buf, max size+1 chars (including trailing '\0') */
+
+static void func_user(char *buf, int size, const procps_status_t *ps)
+{
+#if 1
+	safe_strncpy(buf, get_cached_username(ps->uid), size+1);
+#else
+	/* "compatible" version, but it's larger */
+	/* procps 2.18 shows numeric UID if name overflows the field */
+	/* TODO: get_cached_username() returns numeric string if
+	 * user has no passwd record, we will display it
+	 * left-justified here; too long usernames are shown
+	 * as _right-justified_ IDs. Is it worth fixing? */
+	const char *user = get_cached_username(ps->uid);
+	if (strlen(user) <= size)
+		safe_strncpy(buf, user, size+1);
+	else
+		sprintf(buf, "%*u", size, (unsigned)ps->uid);
+#endif
+}
+
+static void func_group(char *buf, int size, const procps_status_t *ps)
+{
+	safe_strncpy(buf, get_cached_groupname(ps->gid), size+1);
+}
+
+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);
+}
+
+static void func_pid(char *buf, int size, const procps_status_t *ps)
+{
+	sprintf(buf, "%*u", size, ps->pid);
+}
+
+static void func_ppid(char *buf, int size, const procps_status_t *ps)
+{
+	sprintf(buf, "%*u", size, ps->ppid);
+}
+
+static void func_pgid(char *buf, int size, const procps_status_t *ps)
+{
+	sprintf(buf, "%*u", size, ps->pgid);
+}
+
+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';
+	sprintf(buf, "%.*s", size, buf4);
+}
+
+static void func_vsz(char *buf, int size, const procps_status_t *ps)
+{
+	put_lu(buf, size, ps->vsz);
+}
+
+static void func_rss(char *buf, int size, const procps_status_t *ps)
+{
+	put_lu(buf, size, ps->rss);
+}
+
+static void func_tty(char *buf, int size, const procps_status_t *ps)
+{
+	buf[0] = '?';
+	buf[1] = '\0';
+	if (ps->tty_major) /* tty field of "0" means "no tty" */
+		snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
+}
+
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+
+static void func_rgroup(char *buf, int size, const procps_status_t *ps)
+{
+	safe_strncpy(buf, get_cached_groupname(ps->rgid), size+1);
+}
+
+static void func_ruser(char *buf, int size, const procps_status_t *ps)
+{
+	safe_strncpy(buf, get_cached_username(ps->ruid), size+1);
+}
+
+static void func_nice(char *buf, int size, const procps_status_t *ps)
+{
+	sprintf(buf, "%*d", size, ps->niceness);
+}
+
+#endif
+
+#if ENABLE_FEATURE_PS_TIME
+
+static void func_etime(char *buf, int size, const procps_status_t *ps)
+{
+	/* elapsed time [[dd-]hh:]mm:ss; here only mm:ss */
+	unsigned long mm;
+	unsigned ss;
+
+	mm = ps->start_time / get_kernel_HZ();
+	/* must be after get_kernel_HZ()! */
+	mm = seconds_since_boot - mm;
+	ss = mm % 60;
+	mm /= 60;
+	snprintf(buf, size+1, "%3lu:%02u", mm, ss);
+}
+
+static void func_time(char *buf, int size, const procps_status_t *ps)
+{
+	/* cumulative time [[dd-]hh:]mm:ss; here only mm:ss */
+	unsigned long mm;
+	unsigned ss;
+
+	mm = (ps->utime + ps->stime) / get_kernel_HZ();
+	ss = mm % 60;
+	mm /= 60;
+	snprintf(buf, size+1, "%3lu:%02u", mm, ss);
+}
+
+#endif
+
+#if ENABLE_SELINUX
+static void func_label(char *buf, int size, const procps_status_t *ps)
+{
+	safe_strncpy(buf, ps->context ? ps->context : "unknown", size+1);
+}
+#endif
+
+/*
+static void func_nice(char *buf, int size, const procps_status_t *ps)
+{
+	ps->???
+}
+
+static void func_pcpu(char *buf, int size, const procps_status_t *ps)
+{
+}
+*/
+
+static const ps_out_t out_spec[] = {
+/* 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    },
+	{ MAX_WIDTH          , "args"  ,"COMMAND",func_args  ,PSSCAN_COMM    },
+	{ 5                  , "pid"   ,"PID"    ,func_pid   ,PSSCAN_PID     },
+	{ 5                  , "ppid"  ,"PPID"   ,func_ppid  ,PSSCAN_PPID    },
+	{ 5                  , "pgid"  ,"PGID"   ,func_pgid  ,PSSCAN_PGID    },
+#if ENABLE_FEATURE_PS_TIME
+	{ sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME },
+#endif
+#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
+	{ 5                  , "nice"  ,"NI"     ,func_nice  ,PSSCAN_NICE    },
+	{ 8                  , "rgroup","RGROUP" ,func_rgroup,PSSCAN_RUIDGID },
+	{ 8                  , "ruser" ,"RUSER"  ,func_ruser ,PSSCAN_RUIDGID },
+//	{ 5                  , "pcpu"  ,"%CPU"   ,func_pcpu  ,PSSCAN_        },
+#endif
+#if ENABLE_FEATURE_PS_TIME
+	{ 6                  , "time"  ,"TIME"   ,func_time  ,PSSCAN_STIME | PSSCAN_UTIME },
+#endif
+	{ 6                  , "tty"   ,"TT"     ,func_tty   ,PSSCAN_TTY     },
+	{ 4                  , "vsz"   ,"VSZ"    ,func_vsz   ,PSSCAN_VSZ     },
+/* 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 },
+#endif
+};
+
+static ps_out_t* new_out_t(void)
+{
+	out = xrealloc_vector(out, 2, out_cnt);
+	return &out[out_cnt++];
+}
+
+static const ps_out_t* find_out_spec(const char *name)
+{
+	unsigned i;
+	char buf[ARRAY_SIZE(out_spec)*7 + 1];
+	char *p = buf;
+
+	for (i = 0; i < ARRAY_SIZE(out_spec); i++) {
+		if (strncmp(name, out_spec[i].name6, 6) == 0)
+			return &out_spec[i];
+		p += sprintf(p, "%.6s,", out_spec[i].name6);
+	}
+	p[-1] = '\0';
+	bb_error_msg_and_die("bad -o argument '%s', supported arguments: %s", name, buf);
+}
+
+static void parse_o(char* opt)
+{
+	ps_out_t* new;
+	// POSIX: "-o is blank- or comma-separated list" (FIXME)
+	char *comma, *equal;
+	while (1) {
+		comma = strchr(opt, ',');
+		equal = strchr(opt, '=');
+		if (comma && (!equal || equal > comma)) {
+			*comma = '\0';
+			*new_out_t() = *find_out_spec(opt);
+			*comma = ',';
+			opt = comma + 1;
+			continue;
+		}
+		break;
+	}
+	// opt points to last spec in comma separated list.
+	// This one can have =HEADER part.
+	new = new_out_t();
+	if (equal)
+		*equal = '\0';
+	*new = *find_out_spec(opt);
+	if (equal) {
+		*equal = '=';
+		new->header = equal + 1;
+		// POSIX: the field widths shall be ... at least as wide as
+		// the header text (default or overridden value).
+		// If the header text is null, such as -o user=,
+		// the field width shall be at least as wide as the
+		// default header text
+		if (new->header[0]) {
+			new->width = strlen(new->header);
+			print_header = 1;
+		}
+	} else
+		print_header = 1;
+}
+
+static void alloc_line_buffer(void)
+{
+	int i;
+	int width = 0;
+	for (i = 0; i < out_cnt; i++) {
+		need_flags |= out[i].ps_flags;
+		if (out[i].header[0]) {
+			print_header = 1;
+		}
+		width += out[i].width + 1; /* "FIELD " */
+		if ((int)(width - terminal_width) > 0) {
+			/* The rest does not fit on the screen */
+			//out[i].width -= (width - terminal_width - 1);
+			out_cnt = i + 1;
+			break;
+		}
+	}
+#if ENABLE_SELINUX
+	if (!is_selinux_enabled())
+		need_flags &= ~PSSCAN_CONTEXT;
+#endif
+	buffer = xmalloc(width + 1); /* for trailing \0 */
+}
+
+static void format_header(void)
+{
+	int i;
+	ps_out_t* op;
+	char *p;
+
+	if (!print_header)
+		return;
+	p = buffer;
+	i = 0;
+	if (out_cnt) {
+		while (1) {
+			op = &out[i];
+			if (++i == out_cnt) /* do not pad last field */
+				break;
+			p += sprintf(p, "%-*s ", op->width, op->header);
+		}
+		strcpy(p, op->header);
+	}
+	printf("%.*s\n", terminal_width, buffer);
+}
+
+static void format_process(const procps_status_t *ps)
+{
+	int i, len;
+	char *p = buffer;
+	i = 0;
+	if (out_cnt) while (1) {
+		out[i].f(p, out[i].width, ps);
+		// POSIX: Any field need not be meaningful in all
+		// implementations. In such a case a hyphen ( '-' )
+		// should be output in place of the field value.
+		if (!p[0]) {
+			p[0] = '-';
+			p[1] = '\0';
+		}
+		len = strlen(p);
+		p += len;
+		len = out[i].width - len + 1;
+		if (++i == out_cnt) /* do not pad last field */
+			break;
+		p += sprintf(p, "%*s", len, "");
+	}
+	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),
+		OPT_o = (1 << 1),
+		OPT_a = (1 << 2),
+		OPT_A = (1 << 3),
+		OPT_d = (1 << 4),
+		OPT_e = (1 << 5),
+		OPT_f = (1 << 6),
+		OPT_l = (1 << 7),
+		OPT_T = (1 << 8) * ENABLE_FEATURE_SHOW_THREADS,
+	};
+
+	INIT_G();
+
+	// POSIX:
+	// -a  Write information for all processes associated with terminals
+	//     Implementations may omit session leaders from this list
+	// -A  Write information for all processes
+	// -d  Write information for all processes, except session leaders
+	// -e  Write information for all processes (equivalent to -A)
+	// -f  Generate a full listing
+	// -l  Generate a long listing
+	// -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).
+	 * procps v3.2.7 supports -T and shows tids as SPID column,
+	 * it also supports -L where it shows tids as LWP column.
+	 */
+	opt_complementary = "o::";
+	opt = getopt32(argv, "Zo:aAdefl"IF_FEATURE_SHOW_THREADS("T"), &opt_o);
+	if (opt_o) {
+		do {
+			parse_o(llist_pop(&opt_o));
+		} while (opt_o);
+	} else {
+		/* Below: parse_o() needs char*, NOT const char*, can't give it default_o */
+#if ENABLE_SELINUX
+		if (!(opt & OPT_Z) || !is_selinux_enabled()) {
+			/* no -Z or no SELinux: do not show LABEL */
+			strcpy(default_o, DEFAULT_O_STR + sizeof(SELINUX_O_PREFIX)-1);
+		} else
+#endif
+		{
+			strcpy(default_o, DEFAULT_O_STR);
+		}
+		parse_o(default_o);
+	}
+#if ENABLE_FEATURE_SHOW_THREADS
+	if (opt & OPT_T)
+		need_flags |= PSSCAN_TASKS;
+#endif
+
+	/* Was INT_MAX, but some libc's go belly up with printf("%.*s")
+	 * and such large widths */
+	terminal_width = MAX_WIDTH;
+	if (isatty(1)) {
+		get_terminal_width_height(0, &terminal_width, NULL);
+		if (--terminal_width > MAX_WIDTH)
+			terminal_width = MAX_WIDTH;
+	}
+	alloc_line_buffer();
+	format_header();
+
+	p = NULL;
+	while ((p = procps_scan(p, need_flags)) != NULL) {
+		format_process(p);
+	}
+
+	return EXIT_SUCCESS;
+}
+
+
+#else /* !ENABLE_DESKTOP */
+
+
+int ps_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ps_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	procps_status_t *p;
+	int psscan_flags = PSSCAN_PID | PSSCAN_UIDGID
+			| 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,
+	};
+	int opts = 0;
+	/* If we support any options, parse argv */
+#if ENABLE_SELINUX || ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_PS_WIDE
+# 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);
+	/* if w is given once, GNU ps sets the width to 132,
+	 * if w is given more than once, it is "unlimited"
+	 */
+	if (w_count) {
+		terminal_width = (w_count == 1) ? 132 : MAX_WIDTH;
+	} else {
+		get_terminal_width_height(0, &terminal_width, NULL);
+		/* Go one less... */
+		if (--terminal_width > MAX_WIDTH)
+			terminal_width = MAX_WIDTH;
+	}
+# else
+	/* -w is not supported, only -Z and/or -T */
+	opt_complementary = "-";
+	opts = getopt32(argv, IF_SELINUX("Z")IF_FEATURE_SHOW_THREADS("T"));
+# endif
+#endif
+
+#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
+	{
+		puts("  PID USER       VSZ STAT COMMAND");
+	}
+	if (opts & OPT_T) {
+		psscan_flags |= PSSCAN_TASKS;
+	}
+
+	p = NULL;
+	while ((p = procps_scan(p, psscan_flags)) != NULL) {
+		int len;
+#if ENABLE_SELINUX
+		if (psscan_flags & PSSCAN_CONTEXT) {
+			len = printf("%5u %-32.32s %s  ",
+					p->pid,
+					p->context ? p->context : "unknown",
+					p->state);
+		} 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");
+				buf6[5] = '\0';
+				len = printf("%5u %-8.8s %s %s  ",
+					p->pid, user, buf6, p->state);
+			}
+		}
+
+		{
+			int sz = terminal_width - len;
+			char buf[sz + 1];
+			read_cmdline(buf, sz, p->pid, p->comm);
+			puts(buf);
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		clear_username_cache();
+	return EXIT_SUCCESS;
+}
+
+#endif /* !ENABLE_DESKTOP */
diff --git a/busybox-1.19.3/procps/ps.posix b/busybox-1.19.3/procps/ps.posix
new file mode 100644
index 0000000..57f4fa8
--- /dev/null
+++ b/busybox-1.19.3/procps/ps.posix
@@ -0,0 +1,175 @@
+This is what POSIX 2003 says about ps:
+
+By default, ps shall select  all processes with the same effective user
+ID as the current user and the same controlling terminal as the invoker
+
+ps [-aA][-defl][-G grouplist][-o format]...[-p proclist][-t termlist]
+[-U userlist][-g grouplist][-n namelist][-u userlist]
+
+-a     Write information for all processes associated  with  terminals.
+       Implementations may omit session leaders from this list.
+
+-A     Write information for all processes.
+
+-d     Write information for all processes, except session leaders.
+
+-e     Write information for all processes.  (Equivalent to -A.)
+
+-f     Generate  a  full  listing. (See the STDOUT section for the con-
+       tents of a full listing.)
+
+-g  grouplist
+       Write information for processes whose session leaders are  given
+       in grouplist. The application shall ensure that the grouplist is
+       a single argument in the form of a  <blank>  or  comma-separated
+       list.
+
+-G  grouplist
+       Write  information for processes whose real group ID numbers are
+       given in grouplist. The application shall ensure that the  grou-
+       plist  is  a  single argument in the form of a <blank> or comma-
+       separated list.
+
+-l     Generate a long listing. (See STDOUT for the contents of a  long
+       listing.)
+
+-n  namelist
+       Specify the name of an alternative system namelist file in place
+       of the default. The name of the default file and the format of a
+       namelist file are unspecified.
+
+-o  format
+       Write information according to the format specification given in
+       format.  Multiple -o options can be specified; the format speci-
+       fication shall be interpreted as the  <space>-separated concate-
+       nation of all the format option-arguments.
+
+-p  proclist
+       Write  information  for  processes  whose process ID numbers are
+       given in proclist. The application shall ensure  that  the  pro-
+       clist  is  a  single argument in the form of a <blank> or comma-
+       separated list.
+
+-t  termlist
+       Write information for processes associated with terminals  given
+       in termlist. The application shall ensure that the termlist is a
+       single argument in the form  of  a  <blank>  or  comma-separated
+       list.  Terminal identifiers shall be given in an implementation-
+       defined format.    On  XSI-conformant  systems,  they  shall  be
+       given  in  one of two forms: the device's filename (for example,
+       tty04) or, if the device's filename starts with  tty,  just  the
+       identifier following the characters tty (for example, "04" ).
+
+-u  userlist
+       Write  information  for processes whose user ID numbers or login
+       names are given in userlist. The application shall  ensure  that
+       the  userlist  is  a single argument in the form of a <blank> or
+       comma-separated list. In the  listing,  the  numerical  user  ID
+       shall be written unless the -f option is used, in which case the
+       login name shall be written.
+
+-U  userlist
+       Write information for processes whose real user  ID  numbers  or
+       login  names are given in userlist. The application shall ensure
+       that the userlist is a single argument in the form of a  <blank>
+       or comma-separated list.
+
+With  the  exception of -o format, all of the options shown are used to
+select processes. If any are  specified,  the  default  list  shall  be
+ignored  and ps shall select the processes represented by the inclusive
+OR of all the selection-criteria options.
+
+The  -o option allows the output format to be specified under user con-
+trol.
+
+The application shall ensure that the format specification is a list of
+names  presented as a single argument, <blank> or comma-separated. Each
+variable has a default header. The default header can be overridden  by
+appending  an  equals  sign and the new text of the header. The rest of
+the characters in the argument shall be used as the  header  text.  The
+fields specified shall be written in the order specified on the command
+line, and should be arranged in columns in the output. The field widths
+shall  be  selected  by the system to be at least as wide as the header
+text (default or overridden value). If the header text is null, such as
+-o  user=,  the  field  width  shall be at least as wide as the default
+header text. If all header text fields are null, no header  line  shall
+be written.
+
+ruser  The  real user ID of the process. This shall be the textual user
+       ID, if it can be obtained and the field width permits, or a dec-
+       imal representation otherwise.
+
+user   The  effective user ID of the process. This shall be the textual
+       user ID, if it can be obtained and the field width permits, or a
+       decimal representation otherwise.
+
+rgroup The  real  group  ID  of  the process. This shall be the textual
+       group ID, if it can be obtained and the field width permits,  or
+       a decimal representation otherwise.
+
+group  The effective group ID of the process. This shall be the textual
+       group ID, if it can be obtained and the field width permits,  or
+       a decimal representation otherwise.
+
+pid    The decimal value of the process ID.
+
+ppid   The decimal value of the parent process ID.
+
+pgid   The decimal value of the process group ID.
+
+pcpu   The ratio of CPU time used recently to CPU time available in the
+       same  period,  expressed  as  a  percentage.  The   meaning   of
+       "recently"  in  this context is unspecified. The CPU time avail-
+       able is determined in an unspecified manner.
+
+vsz    The size of the process in (virtual) memory in 1024  byte  units
+       as a decimal integer.
+
+nice   The decimal value of the nice value of the process; see nice() .
+
+etime  In the POSIX locale, the elapsed  time  since  the  process  was
+       started, in the form: [[dd-]hh:]mm:ss
+
+time   In the POSIX locale, the cumulative CPU time of the  process  in
+       the form: [dd-]hh:mm:ss
+
+tty    The name of the controlling terminal of the process (if any)  in
+       the same format used by the who utility.
+
+comm   The  name  of  the  command being executed ( argv[0] value) as a
+       string.
+
+args   The command with all its arguments as a string. The  implementa-
+       tion may truncate this value to the field width; it is implemen-
+       tation-defined whether any  further  truncation  occurs.  It  is
+       unspecified  whether  the string represented is a version of the
+       argument list as it was passed to the command when  it  started,
+       or  is a version of the arguments as they may have been modified
+       by the application. Applications cannot depend on being able  to
+       modify  their  argument  list  and  having  that modification be
+       reflected in the output of ps.
+
+Any field need not be meaningful in all implementations. In such a case
+a hyphen ( '-' ) should be output in place of the field value.
+
+Only  comm  and  args  shall be allowed to contain <blank>s; all others
+shall not.
+
+The following table specifies the default header  to  be  used  in  the
+POSIX locale corresponding to each format specifier.
+
+    Format Specifier Default Header Format Specifier Default Header
+    args             COMMAND        ppid             PPID
+    comm             COMMAND        rgroup           RGROUP
+    etime            ELAPSED        ruser            RUSER
+    group            GROUP          time             TIME
+    nice             NI             tty              TT
+    pcpu             %CPU           user             USER
+    pgid             PGID           vsz              VSZ
+    pid              PID
+
+There  is no special quoting mechanism for header text. The header text
+is the rest of the argument. If multiple  header  changes  are  needed,
+multiple -o options can be used, such as:
+
+        ps -o "user=User Name" -o pid=Process\ ID
diff --git a/busybox-1.19.3/procps/pstree.c b/busybox-1.19.3/procps/pstree.c
new file mode 100644
index 0000000..8ba3079
--- /dev/null
+++ b/busybox-1.19.3/procps/pstree.c
@@ -0,0 +1,407 @@
+/*
+ * 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;
+
+typedef struct proc {
+	char comm[COMM_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_LEN + 2];
+	sprintf(threadname, "{%.*s}", COMM_LEN - 2, 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/busybox-1.19.3/procps/pwdx.c b/busybox-1.19.3/procps/pwdx.c
new file mode 100644
index 0000000..7818104
--- /dev/null
+++ b/busybox-1.19.3/procps/pwdx.c
@@ -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;
+}
diff --git a/busybox-1.19.3/procps/renice.c b/busybox-1.19.3/procps/renice.c
new file mode 100644
index 0000000..77f400a
--- /dev/null
+++ b/busybox-1.19.3/procps/renice.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * renice implementation for busybox
+ *
+ * Copyright (C) 2005  Manuel Novoa III  <mjn3@codepoet.org>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/* Notes:
+ *   Setting an absolute priority was obsoleted in SUSv2 and removed
+ *   in SUSv3.  However, the common linux version of renice does
+ *   absolute and not relative.  So we'll continue supporting absolute,
+ *   although the stdout logging has been removed since both SUSv2 and
+ *   SUSv3 specify that stdout isn't used.
+ *
+ *   This version is lenient in that it doesn't require any IDs.  The
+ *   options -p, -g, and -u are treated as mode switches for the
+ *   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>
+
+void BUG_bad_PRIO_PROCESS(void);
+void BUG_bad_PRIO_PGRP(void);
+void BUG_bad_PRIO_USER(void);
+
+int renice_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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 use_relative = 0;
+	int adjustment, new_priority;
+	unsigned who;
+	char *arg;
+
+	/* Yes, they are not #defines in glibc 2.4! #if won't work */
+	if (PRIO_PROCESS < CHAR_MIN || PRIO_PROCESS > CHAR_MAX)
+		BUG_bad_PRIO_PROCESS();
+	if (PRIO_PGRP < CHAR_MIN || PRIO_PGRP > CHAR_MAX)
+		BUG_bad_PRIO_PGRP();
+	if (PRIO_USER < CHAR_MIN || PRIO_USER > CHAR_MAX)
+		BUG_bad_PRIO_USER();
+
+	arg = *++argv;
+
+	/* Check if we are using a relative adjustment. */
+	if (arg && arg[0] == '-' && arg[1] == 'n') {
+		use_relative = 1;
+		if (!arg[2])
+			arg = *++argv;
+		else
+			arg += 2;
+	}
+
+	if (!arg) {  /* No args?  Then show usage. */
+		bb_show_usage();
+	}
+
+	/* Get the priority adjustment (absolute or relative). */
+	adjustment = xatoi_range(arg, INT_MIN/2, INT_MAX/2);
+
+	while ((arg = *++argv) != NULL) {
+		/* Check for a mode switch. */
+		if (arg[0] == '-' && arg[1]) {
+			static const char opts[] ALIGN1 = {
+				'p', 'g', 'u', 0, PRIO_PROCESS, PRIO_PGRP, PRIO_USER
+			};
+			const char *p = strchr(opts, arg[1]);
+			if (p) {
+				which = p[4];
+				if (!arg[2])
+					continue;
+				arg += 2;
+			}
+		}
+
+		/* Process an ID arg. */
+		if (which == PRIO_USER) {
+			struct passwd *p;
+			p = getpwnam(arg);
+			if (!p) {
+				bb_error_msg("unknown user %s", arg);
+				goto HAD_ERROR;
+			}
+			who = p->pw_uid;
+		} else {
+			who = bb_strtou(arg, NULL, 10);
+			if (errno) {
+				bb_error_msg("invalid number '%s'", arg);
+				goto HAD_ERROR;
+			}
+		}
+
+		/* Get priority to use, and set it. */
+		if (use_relative) {
+			int old_priority;
+
+			errno = 0;  /* Needed for getpriority error detection. */
+			old_priority = getpriority(which, who);
+			if (errno) {
+				bb_perror_msg(Xetpriority_msg, 'g');
+				goto HAD_ERROR;
+			}
+
+			new_priority = old_priority + adjustment;
+		} else {
+			new_priority = adjustment;
+		}
+
+		if (setpriority(which, who, new_priority) == 0) {
+			continue;
+		}
+
+		bb_perror_msg(Xetpriority_msg, 's');
+ HAD_ERROR:
+		retval = EXIT_FAILURE;
+	}
+
+	/* No need to check for errors outputing to stderr since, if it
+	 * was used, the HAD_ERROR label was reached and retval was set. */
+
+	return retval;
+}
diff --git a/busybox-1.19.3/procps/smemcap.c b/busybox-1.19.3/procps/smemcap.c
new file mode 100644
index 0000000..e108d88
--- /dev/null
+++ b/busybox-1.19.3/procps/smemcap.c
@@ -0,0 +1,132 @@
+/*
+ smemcap - a tool for meaningful memory reporting
+
+ Copyright 2008-2009 Matt Mackall <mpm@selenic.com>
+
+ This software may be used and distributed according to the terms of
+ the GNU General Public License version 2 or later, incorporated
+ herein by reference.
+*/
+
+//applet:IF_SMEMCAP(APPLET(smemcap, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o
+
+//config:config SMEMCAP
+//config:	bool "smemcap"
+//config:	default y
+//config:	help
+//config:	  smemcap is a tool for capturing process data for smem,
+//config:	  a memory usage statistic tool.
+
+#include "libbb.h"
+#include "archive.h"
+
+struct fileblock {
+	struct fileblock *next;
+	char data[TAR_BLOCK_SIZE];
+};
+
+static void writeheader(const char *path, struct stat *sb, int type)
+{
+	struct tar_header_t header;
+	int i, sum;
+
+	memset(&header, 0, TAR_BLOCK_SIZE);
+	strcpy(header.name, path);
+	sprintf(header.mode, "%o", sb->st_mode & 0777);
+	/* careful to not overflow fields! */
+	sprintf(header.uid, "%o", sb->st_uid & 07777777);
+	sprintf(header.gid, "%o", sb->st_gid & 07777777);
+	sprintf(header.size, "%o", (unsigned)sb->st_size);
+	sprintf(header.mtime, "%llo", sb->st_mtime & 077777777777LL);
+	header.typeflag = type;
+	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
+	 * calculation. The checksum field is formatted differently from the
+	 * other fields: it has 6 digits, a NUL, then a space -- rather than
+	 * digits, followed by a NUL like the other fields... */
+	header.chksum[7] = ' ';
+	sum = ' ' * 7;
+	for (i = 0; i < TAR_BLOCK_SIZE; i++)
+		sum += ((unsigned char*)&header)[i];
+	sprintf(header.chksum, "%06o", sum);
+
+	xwrite(STDOUT_FILENO, &header, TAR_BLOCK_SIZE);
+}
+
+static void archivefile(const char *path)
+{
+	struct fileblock *start, *cur;
+	struct fileblock **prev = &start;
+	int fd, r;
+	unsigned size = 0;
+	struct stat s;
+
+	/* buffer the file */
+	fd = xopen(path, O_RDONLY);
+	do {
+		cur = xzalloc(sizeof(*cur));
+		*prev = cur;
+		prev = &cur->next;
+		r = full_read(fd, cur->data, TAR_BLOCK_SIZE);
+		if (r > 0)
+			size += r;
+	} while (r == TAR_BLOCK_SIZE);
+
+	/* write archive header */
+	fstat(fd, &s);
+	close(fd);
+	s.st_size = size;
+	writeheader(path, &s, '0');
+
+	/* dump file contents */
+	for (cur = start; (int)size > 0; size -= TAR_BLOCK_SIZE) {
+		xwrite(STDOUT_FILENO, cur->data, TAR_BLOCK_SIZE);
+		start = cur;
+		cur = cur->next;
+		free(start);
+	}
+}
+
+static void archivejoin(const char *sub, const char *name)
+{
+	char path[sizeof(long long)*3 + sizeof("/cmdline")];
+	sprintf(path, "%s/%s", sub, name);
+	archivefile(path);
+}
+
+//usage:#define smemcap_trivial_usage ">SMEMDATA.TAR"
+//usage:#define smemcap_full_usage "\n\n"
+//usage:       "Collect memory usage data in /proc and write it to stdout"
+
+int smemcap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int smemcap_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	DIR *d;
+	struct dirent *de;
+
+	xchdir("/proc");
+	d = xopendir(".");
+
+	archivefile("meminfo");
+	archivefile("version");
+	while ((de = readdir(d)) != NULL) {
+		if (isdigit(de->d_name[0])) {
+			struct stat s;
+			memset(&s, 0, sizeof(s));
+			s.st_mode = 0555;
+			writeheader(de->d_name, &s, '5');
+			archivejoin(de->d_name, "smaps");
+			archivejoin(de->d_name, "cmdline");
+			archivejoin(de->d_name, "stat");
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		closedir(d);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/procps/sysctl.c b/busybox-1.19.3/procps/sysctl.c
new file mode 100644
index 0000000..cb3b6a2
--- /dev/null
+++ b/busybox-1.19.3/procps/sysctl.c
@@ -0,0 +1,276 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
+ *
+ * Copyright 1999 George Staikos
+ *
+ * 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] [VALUE]..."
+//usage:#define sysctl_full_usage "\n\n"
+//usage:       "Configure kernel parameters at runtime\n"
+//usage:     "\n	-n	Don't print key names"
+//usage:     "\n	-e	Don't warn about unknown keys"
+//usage:     "\n	-w	Change sysctl setting"
+//usage:     "\n	-p FILE	Load sysctl settings from FILE (default /etc/sysctl.conf)"
+//usage:     "\n	-a	Display all values"
+//usage:     "\n	-A	Display all values in table form"
+//usage:
+//usage:#define sysctl_example_usage
+//usage:       "sysctl [-n] [-e] variable...\n"
+//usage:       "sysctl [-n] [-e] -w variable=value...\n"
+//usage:       "sysctl [-n] [-e] -a\n"
+//usage:       "sysctl [-n] [-e] -p file	(default /etc/sysctl.conf)\n"
+//usage:       "sysctl [-n] [-e] -A\n"
+
+#include "libbb.h"
+
+enum {
+	FLAG_SHOW_KEYS       = 1 << 0,
+	FLAG_SHOW_KEY_ERRORS = 1 << 1,
+	FLAG_TABLE_FORMAT    = 1 << 2, /* not implemented */
+	FLAG_SHOW_ALL        = 1 << 3,
+	FLAG_PRELOAD_FILE    = 1 << 4,
+	FLAG_WRITE           = 1 << 5,
+};
+#define OPTION_STR "neAapw"
+
+static void sysctl_dots_to_slashes(char *name)
+{
+	char *cptr, *last_good, *end;
+
+	/* Convert minimum number of '.' to '/' so that
+	 * we end up with existing file's name.
+	 *
+	 * Example from bug 3894:
+	 * net.ipv4.conf.eth0.100.mc_forwarding ->
+	 * net/ipv4/conf/eth0.100/mc_forwarding
+	 * NB: net/ipv4/conf/eth0/mc_forwarding *also exists*,
+	 * therefore we must start from the end, and if
+	 * we replaced even one . -> /, start over again,
+	 * but never replace dots before the position
+	 * where last replacement occurred.
+	 *
+	 * Another bug we later had is that
+	 * net.ipv4.conf.eth0.100
+	 * (without .mc_forwarding) was mishandled.
+	 *
+	 * To set up testing: modprobe 8021q; vconfig add eth0 100
+	 */
+	end = name + strlen(name);
+	last_good = name - 1;
+	*end = '.'; /* trick the loop into trying full name too */
+
+ again:
+	cptr = end;
+	while (cptr > last_good) {
+		if (*cptr == '.') {
+			*cptr = '\0';
+			//bb_error_msg("trying:'%s'", name);
+			if (access(name, F_OK) == 0) {
+				*cptr = '/';
+				//bb_error_msg("replaced:'%s'", name);
+				last_good = cptr;
+				goto again;
+			}
+			*cptr = '.';
+		}
+		cptr--;
+	}
+	*end = '\0';
+}
+
+static int sysctl_act_on_setting(char *setting)
+{
+	int fd, retval = EXIT_SUCCESS;
+	char *cptr, *outname;
+	char *value = value; /* for compiler */
+
+	outname = xstrdup(setting);
+
+	cptr = outname;
+	while (*cptr) {
+		if (*cptr == '/')
+			*cptr = '.';
+		cptr++;
+	}
+
+	if (option_mask32 & FLAG_WRITE) {
+		cptr = strchr(setting, '=');
+		if (cptr == NULL) {
+			bb_error_msg("error: '%s' must be of the form name=value",
+				outname);
+			retval = EXIT_FAILURE;
+			goto end;
+		}
+		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;
+			goto end;
+		}
+		*cptr = '\0';
+		outname[cptr - setting] = '\0';
+		/* procps 3.2.7 actually uses these flags */
+		fd = open(setting, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+	} else {
+		fd = open(setting, O_RDONLY);
+	}
+
+	if (fd < 0) {
+		switch (errno) {
+		case ENOENT:
+			if (option_mask32 & FLAG_SHOW_KEY_ERRORS)
+				bb_error_msg("error: '%s' is an unknown key", outname);
+			break;
+		default:
+			bb_perror_msg("error %sing key '%s'",
+					option_mask32 & FLAG_WRITE ?
+						"sett" : "read",
+					outname);
+			break;
+		}
+		retval = EXIT_FAILURE;
+		goto end;
+	}
+
+	if (option_mask32 & FLAG_WRITE) {
+//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);
+	} else {
+		char c;
+
+		value = cptr = xmalloc_read(fd, NULL);
+		close(fd);
+		if (value == NULL) {
+			bb_perror_msg("error reading key '%s'", outname);
+			goto end;
+		}
+
+		/* dev.cdrom.info and sunrpc.transports, for example,
+		 * are multi-line. Try "sysctl sunrpc.transports"
+		 */
+		while ((c = *cptr) != '\0') {
+			if (option_mask32 & FLAG_SHOW_KEYS)
+				printf("%s = ", outname);
+			while (1) {
+				fputc(c, stdout);
+				cptr++;
+				if (c == '\n')
+					break;
+				c = *cptr;
+				if (c == '\0')
+					break;
+			}
+		}
+		free(value);
+	}
+ end:
+	free(outname);
+	return retval;
+}
+
+static int sysctl_act_recursive(const char *path)
+{
+	DIR *dirp;
+	struct stat buf;
+	struct dirent *entry;
+	char *next;
+	int retval = 0;
+
+	stat(path, &buf);
+	if (S_ISDIR(buf.st_mode) && !(option_mask32 & FLAG_WRITE)) {
+		dirp = opendir(path);
+		if (dirp == NULL)
+			return -1;
+		while ((entry = readdir(dirp)) != NULL) {
+			next = concat_subpath_file(path, entry->d_name);
+			if (next == NULL)
+				continue; /* d_name is "." or ".." */
+			/* if path was ".", drop "./" prefix: */
+			retval |= sysctl_act_recursive((next[0] == '.' && next[1] == '/') ?
+					    next + 2 : next);
+			free(next);
+		}
+		closedir(dirp);
+	} else {
+		char *name = xstrdup(path);
+		retval |= sysctl_act_on_setting(name);
+		free(name);
+	}
+
+	return retval;
+}
+
+/* Set sysctl's from a conf file. Format example:
+ * # Controls IP packet forwarding
+ * net.ipv4.ip_forward = 0
+ */
+static int sysctl_handle_preload_file(const char *filename)
+{
+	char *token[2];
+	parser_t *parser;
+
+	parser = config_open(filename);
+	/* Must do it _after_ config_open(): */
+	xchdir("/proc/sys");
+	/* xchroot(".") - 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
+// (but _whitespace_ from ends should be trimmed first (and we do it right))
+//TODO: "var==1" is mishandled (must use "=1" as a value, but uses "1")
+// can it be fixed by removing PARSE_COLLAPSE bit?
+	while (config_read(parser, token, 2, 2, "# \t=", PARSE_NORMAL)) {
+		char *tp;
+		sysctl_dots_to_slashes(token[0]);
+		tp = xasprintf("%s=%s", token[0], token[1]);
+		sysctl_act_recursive(tp);
+		free(tp);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		config_close(parser);
+	return 0;
+}
+
+int sysctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sysctl_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval;
+	int opt;
+
+	opt = getopt32(argv, "+" OPTION_STR); /* '+' - stop on first non-option */
+	argv += optind;
+	opt ^= (FLAG_SHOW_KEYS | FLAG_SHOW_KEY_ERRORS);
+	option_mask32 = opt;
+
+	if (opt & FLAG_PRELOAD_FILE) {
+		option_mask32 |= FLAG_WRITE;
+		/* xchdir("/proc/sys") is inside */
+		return sysctl_handle_preload_file(*argv ? *argv : "/etc/sysctl.conf");
+	}
+	xchdir("/proc/sys");
+	/* xchroot(".") - if you are paranoid */
+	if (opt & (FLAG_TABLE_FORMAT | FLAG_SHOW_ALL)) {
+		return sysctl_act_recursive(".");
+	}
+
+	retval = 0;
+	while (*argv) {
+		sysctl_dots_to_slashes(*argv);
+		retval |= sysctl_act_recursive(*argv);
+		argv++;
+	}
+
+	return retval;
+}
diff --git a/busybox-1.19.3/procps/top.c b/busybox-1.19.3/procps/top.c
new file mode 100644
index 0000000..011bbf1
--- /dev/null
+++ b/busybox-1.19.3/procps/top.c
@@ -0,0 +1,1192 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * A tiny 'top' utility.
+ *
+ * This is written specifically for the linux /proc/<PID>/stat(m)
+ * files format.
+ *
+ * This reads the PIDs of all processes and their status and shows
+ * the status of processes (first ones that fit to screen) at given
+ * intervals.
+ *
+ * NOTES:
+ * - At startup this changes to /proc, all the reads are then
+ *   relative to that.
+ *
+ * (C) Eero Tamminen <oak at welho dot com>
+ *
+ * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
+ *
+ * Sept 2008: Vineet Gupta <vineet.gupta@arc.com>
+ * Added Support for reporting SMP Information
+ * - CPU where process was last seen running
+ *   (to see effect of sched_setaffinity() etc)
+ * - 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 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
+ */
+
+#include "libbb.h"
+
+
+typedef struct top_status_t {
+	unsigned long vsz;
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+	unsigned long ticks;
+	unsigned pcpu; /* delta of ticks */
+#endif
+	unsigned pid, ppid;
+	unsigned uid;
+	char state[4];
+	char comm[COMM_LEN];
+#if ENABLE_FEATURE_TOP_SMP_PROCESS
+	int last_seen_on_cpu;
+#endif
+} top_status_t;
+
+typedef struct jiffy_counts_t {
+	/* Linux 2.4.x has only first four */
+	unsigned long long usr, nic, sys, idle;
+	unsigned long long iowait, irq, softirq, steal;
+	unsigned long long total;
+	unsigned long long busy;
+} jiffy_counts_t;
+
+/* This structure stores some critical information from one frame to
+   the next. Used for finding deltas. */
+typedef struct save_hist {
+	unsigned long ticks;
+	pid_t pid;
+} save_hist;
+
+typedef int (*cmp_funcp)(top_status_t *P, top_status_t *Q);
+
+
+enum { SORT_DEPTH = 3 };
+
+
+struct globals {
+	top_status_t *top;
+	int ntop;
+	smallint inverted;
+#if ENABLE_FEATURE_TOPMEM
+	smallint sort_field;
+#endif
+#if ENABLE_FEATURE_TOP_SMP_CPU
+	smallint smp_cpu_info; /* one/many cpu info lines? */
+#endif
+#if ENABLE_FEATURE_USE_TERMIOS
+	struct termios initial_settings;
+#endif
+#if !ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+	cmp_funcp sort_function[1];
+#else
+	cmp_funcp sort_function[SORT_DEPTH];
+	struct save_hist *prev_hist;
+	int prev_hist_count;
+	jiffy_counts_t cur_jif, prev_jif;
+	/* int hist_iterations; */
+	unsigned total_pcpu;
+	/* unsigned long total_vsz; */
+#endif
+#if ENABLE_FEATURE_TOP_SMP_CPU
+	/* Per CPU samples: current and last */
+	jiffy_counts_t *cpu_jif, *cpu_prev_jif;
+	int num_cpus;
+#endif
+	char line_buf[80];
+}; //FIX_ALIASING; - large code growth
+enum { LINE_BUF_SIZE = COMMON_BUFSIZE - offsetof(struct globals, line_buf) };
+#define G (*(struct globals*)&bb_common_bufsiz1)
+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        )
+#define inverted         (G.inverted          )
+#define smp_cpu_info     (G.smp_cpu_info      )
+#define initial_settings (G.initial_settings  )
+#define sort_function    (G.sort_function     )
+#define prev_hist        (G.prev_hist         )
+#define prev_hist_count  (G.prev_hist_count   )
+#define cur_jif          (G.cur_jif           )
+#define prev_jif         (G.prev_jif          )
+#define cpu_jif          (G.cpu_jif           )
+#define cpu_prev_jif     (G.cpu_prev_jif      )
+#define num_cpus         (G.num_cpus          )
+#define total_pcpu       (G.total_pcpu        )
+#define line_buf         (G.line_buf          )
+
+enum {
+	OPT_d = (1 << 0),
+	OPT_n = (1 << 1),
+	OPT_b = (1 << 2),
+	OPT_m = (1 << 3),
+	OPT_EOF = (1 << 4), /* pseudo: "we saw EOF in stdin" */
+};
+#define OPT_BATCH_MODE (option_mask32 & OPT_b)
+
+
+#if ENABLE_FEATURE_USE_TERMIOS
+static int pid_sort(top_status_t *P, top_status_t *Q)
+{
+	/* Buggy wrt pids with high bit set */
+	/* (linux pids are in [1..2^15-1]) */
+	return (Q->pid - P->pid);
+}
+#endif
+
+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 ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+
+static int pcpu_sort(top_status_t *P, top_status_t *Q)
+{
+	/* Buggy wrt ticks with high bit set */
+	/* Affects only processes for which ticks overflow */
+	return (int)Q->pcpu - (int)P->pcpu;
+}
+
+static int time_sort(top_status_t *P, top_status_t *Q)
+{
+	/* We want to avoid unsigned->signed and truncation errors */
+	if (Q->ticks < P->ticks) return -1;
+	return Q->ticks != P->ticks; /* 0 if ==, 1 if > */
+}
+
+static int mult_lvl_cmp(void* a, void* b)
+{
+	int i, cmp_val;
+
+	for (i = 0; i < SORT_DEPTH; i++) {
+		cmp_val = (*sort_function[i])(a, b);
+		if (cmp_val != 0)
+			break;
+	}
+	return inverted ? -cmp_val : cmp_val;
+}
+
+static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif)
+{
+#if !ENABLE_FEATURE_TOP_SMP_CPU
+	static const char fmt[] = "cpu %llu %llu %llu %llu %llu %llu %llu %llu";
+#else
+	static const char fmt[] = "cp%*s %llu %llu %llu %llu %llu %llu %llu %llu";
+#endif
+	int ret;
+
+	if (!fgets(line_buf, LINE_BUF_SIZE, fp) || line_buf[0] != 'c' /* not "cpu" */)
+		return 0;
+	ret = sscanf(line_buf, fmt,
+			&p_jif->usr, &p_jif->nic, &p_jif->sys, &p_jif->idle,
+			&p_jif->iowait, &p_jif->irq, &p_jif->softirq,
+			&p_jif->steal);
+	if (ret >= 4) {
+		p_jif->total = p_jif->usr + p_jif->nic + p_jif->sys + p_jif->idle
+			+ p_jif->iowait + p_jif->irq + p_jif->softirq + p_jif->steal;
+		/* procps 2.x does not count iowait as busy time */
+		p_jif->busy = p_jif->total - p_jif->idle - p_jif->iowait;
+	}
+
+	return ret;
+}
+
+static void get_jiffy_counts(void)
+{
+	FILE* fp = xfopen_for_read("stat");
+
+	/* We need to parse cumulative counts even if SMP CPU display is on,
+	 * 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");
+
+#if !ENABLE_FEATURE_TOP_SMP_CPU
+	fclose(fp);
+	return;
+#else
+	if (!smp_cpu_info) {
+		fclose(fp);
+		return;
+	}
+
+	if (!num_cpus) {
+		/* First time here. How many CPUs?
+		 * There will be at least 1 /proc/stat line with cpu%d
+		 */
+		while (1) {
+			cpu_jif = xrealloc_vector(cpu_jif, 1, num_cpus);
+			if (read_cpu_jiffy(fp, &cpu_jif[num_cpus]) <= 4)
+				break;
+			num_cpus++;
+		}
+		if (num_cpus == 0) /* /proc/stat with only "cpu ..." line?! */
+			smp_cpu_info = 0;
+
+		cpu_prev_jif = xzalloc(sizeof(cpu_prev_jif[0]) * num_cpus);
+
+		/* Otherwise the first per cpu display shows all 100% idles */
+		usleep(50000);
+	} else { /* Non first time invocation */
+		jiffy_counts_t *tmp;
+		int i;
+
+		/* First switch the sample pointers: no need to copy */
+		tmp = cpu_prev_jif;
+		cpu_prev_jif = cpu_jif;
+		cpu_jif = tmp;
+
+		/* Get the new samples */
+		for (i = 0; i < num_cpus; i++)
+			read_cpu_jiffy(fp, &cpu_jif[i]);
+	}
+#endif
+	fclose(fp);
+}
+
+static void do_stats(void)
+{
+	top_status_t *cur;
+	pid_t pid;
+	int i, last_i, n;
+	struct save_hist *new_hist;
+
+	get_jiffy_counts();
+	total_pcpu = 0;
+	/* total_vsz = 0; */
+	new_hist = xmalloc(sizeof(new_hist[0]) * ntop);
+	/*
+	 * Make a pass through the data to get stats.
+	 */
+	/* hist_iterations = 0; */
+	i = 0;
+	for (n = 0; n < ntop; n++) {
+		cur = top + n;
+
+		/*
+		 * Calculate time in cur process.  Time is sum of user time
+		 * and system time
+		 */
+		pid = cur->pid;
+		new_hist[n].ticks = cur->ticks;
+		new_hist[n].pid = pid;
+
+		/* find matching entry from previous pass */
+		cur->pcpu = 0;
+		/* do not start at index 0, continue at last used one
+		 * (brought hist_iterations from ~14000 down to 172) */
+		last_i = i;
+		if (prev_hist_count) do {
+			if (prev_hist[i].pid == pid) {
+				cur->pcpu = cur->ticks - prev_hist[i].ticks;
+				total_pcpu += cur->pcpu;
+				break;
+			}
+			i = (i+1) % prev_hist_count;
+			/* hist_iterations++; */
+		} while (i != last_i);
+		/* total_vsz += cur->vsz; */
+	}
+
+	/*
+	 * Save cur frame's information.
+	 */
+	free(prev_hist);
+	prev_hist = new_hist;
+	prev_hist_count = ntop;
+}
+
+#endif /* FEATURE_TOP_CPU_USAGE_PERCENTAGE */
+
+#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS && ENABLE_FEATURE_TOP_DECIMALS
+/* formats 7 char string (8 with terminating NUL) */
+static char *fmt_100percent_8(char pbuf[8], unsigned value, unsigned total)
+{
+	unsigned t;
+	if (value >= total) { /* 100% ? */
+		strcpy(pbuf, "  100% ");
+		return pbuf;
+	}
+	/* else generate " [N/space]N.N% " string */
+	value = 1000 * value / total;
+	t = value / 100;
+	value = value % 100;
+	pbuf[0] = ' ';
+	pbuf[1] = t ? t + '0' : ' ';
+	pbuf[2] = '0' + (value / 10);
+	pbuf[3] = '.';
+	pbuf[4] = '0' + (value % 10);
+	pbuf[5] = '%';
+	pbuf[6] = ' ';
+	pbuf[7] = '\0';
+	return pbuf;
+}
+#endif
+
+#if ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS
+static void display_cpus(int scr_width, char *scrbuf, int *lines_rem_p)
+{
+	/*
+	 * xxx% = (cur_jif.xxx - prev_jif.xxx) / (cur_jif.total - prev_jif.total) * 100%
+	 */
+	unsigned total_diff;
+	jiffy_counts_t *p_jif, *p_prev_jif;
+	int i;
+# if ENABLE_FEATURE_TOP_SMP_CPU
+	int n_cpu_lines;
+# endif
+
+	/* using (unsigned) casts to make operations cheaper */
+# define  CALC_TOTAL_DIFF do { \
+	total_diff = (unsigned)(p_jif->total - p_prev_jif->total); \
+	if (total_diff == 0) total_diff = 1; \
+} while (0)
+
+# if ENABLE_FEATURE_TOP_DECIMALS
+#  define CALC_STAT(xxx) char xxx[8]
+#  define SHOW_STAT(xxx) fmt_100percent_8(xxx, (unsigned)(p_jif->xxx - p_prev_jif->xxx), total_diff)
+#  define FMT "%s"
+# else
+#  define CALC_STAT(xxx) unsigned xxx = 100 * (unsigned)(p_jif->xxx - p_prev_jif->xxx) / total_diff
+#  define SHOW_STAT(xxx) xxx
+#  define FMT "%4u%% "
+# endif
+
+# if !ENABLE_FEATURE_TOP_SMP_CPU
+	{
+		i = 1;
+		p_jif = &cur_jif;
+		p_prev_jif = &prev_jif;
+# else
+	/* Loop thru CPU(s) */
+	n_cpu_lines = smp_cpu_info ? num_cpus : 1;
+	if (n_cpu_lines > *lines_rem_p)
+		n_cpu_lines = *lines_rem_p;
+
+	for (i = 0; i < n_cpu_lines; i++) {
+		p_jif = &cpu_jif[i];
+		p_prev_jif = &cpu_prev_jif[i];
+# endif
+		CALC_TOTAL_DIFF;
+
+		{ /* Need a block: CALC_STAT are declarations */
+			CALC_STAT(usr);
+			CALC_STAT(sys);
+			CALC_STAT(nic);
+			CALC_STAT(idle);
+			CALC_STAT(iowait);
+			CALC_STAT(irq);
+			CALC_STAT(softirq);
+			/*CALC_STAT(steal);*/
+
+			snprintf(scrbuf, scr_width,
+				/* Barely fits in 79 chars when in "decimals" mode. */
+# if ENABLE_FEATURE_TOP_SMP_CPU
+				"CPU%s:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
+				(smp_cpu_info ? utoa(i) : ""),
+# else
+				"CPU:"FMT"usr"FMT"sys"FMT"nic"FMT"idle"FMT"io"FMT"irq"FMT"sirq",
+# endif
+				SHOW_STAT(usr), SHOW_STAT(sys), SHOW_STAT(nic), SHOW_STAT(idle),
+				SHOW_STAT(iowait), SHOW_STAT(irq), SHOW_STAT(softirq)
+				/*, SHOW_STAT(steal) - what is this 'steal' thing? */
+				/* I doubt anyone wants to know it */
+			);
+			puts(scrbuf);
+		}
+	}
+# undef SHOW_STAT
+# undef CALC_STAT
+# undef FMT
+	*lines_rem_p -= i;
+}
+#else  /* !ENABLE_FEATURE_TOP_CPU_GLOBAL_PERCENTS */
+# define display_cpus(scr_width, scrbuf, lines_rem) ((void)0)
+#endif
+
+static unsigned long display_header(int scr_width, int *lines_rem_p)
+{
+	FILE *fp;
+	char buf[80];
+	char scrbuf[80];
+	unsigned long total, used, mfree, shared, buffers, cached;
+
+	/* read memory info */
+	fp = xfopen_for_read("meminfo");
+
+	/*
+	 * Old kernels (such as 2.4.x) had a nice summary of memory info that
+	 * we could parse, however this is gone entirely in 2.6. Try parsing
+	 * the old way first, and if that fails, parse each field manually.
+	 *
+	 * First, we read in the first line. Old kernels will have bogus
+	 * strings we don't care about, whereas new kernels will start right
+	 * out with MemTotal:
+	 *                              -- PFM.
+	 */
+	if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
+		fgets(buf, sizeof(buf), fp);    /* skip first line */
+
+		fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
+			&total, &used, &mfree, &shared, &buffers, &cached);
+		/* convert to kilobytes */
+		used /= 1024;
+		mfree /= 1024;
+		shared /= 1024;
+		buffers /= 1024;
+		cached /= 1024;
+		total /= 1024;
+	} else {
+		/*
+		 * Revert to manual parsing, which incidentally already has the
+		 * sizes in kilobytes. This should be safe for both 2.4 and
+		 * 2.6.
+		 */
+		fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
+
+		/*
+		 * MemShared: is no longer present in 2.6. Report this as 0,
+		 * to maintain consistent behavior with normal procps.
+		 */
+		if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
+			shared = 0;
+
+		fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
+		fscanf(fp, "Cached: %lu %s\n", &cached, buf);
+
+		used = total - mfree;
+	}
+	fclose(fp);
+
+	/* output memory info */
+	if (scr_width > (int)sizeof(scrbuf))
+		scr_width = sizeof(scrbuf);
+	snprintf(scrbuf, scr_width,
+		"Mem: %luK used, %luK free, %luK shrd, %luK buff, %luK cached",
+		used, mfree, shared, buffers, cached);
+	/* go to top & clear to the end of screen */
+	printf(OPT_BATCH_MODE ? "%s\n" : "\033[H\033[J%s\n", scrbuf);
+	(*lines_rem_p)--;
+
+	/* Display CPU time split as percentage of total time
+	 * This displays either a cumulative line or one line per CPU
+	 */
+	display_cpus(scr_width, scrbuf, lines_rem_p);
+
+	/* read load average as a string */
+	buf[0] = '\0';
+	open_read_close("loadavg", buf, sizeof(buf) - 1);
+	buf[sizeof(buf) - 1] = '\n';
+	*strchr(buf, '\n') = '\0';
+	snprintf(scrbuf, scr_width, "Load average: %s", buf);
+	puts(scrbuf);
+	(*lines_rem_p)--;
+
+	return total;
+}
+
+static NOINLINE void display_process_list(int lines_rem, int scr_width)
+{
+	enum {
+		BITS_PER_INT = sizeof(int) * 8
+	};
+
+	top_status_t *s;
+	char vsz_str_buf[8];
+	unsigned long total_memory = display_header(scr_width, &lines_rem); /* or use total_vsz? */
+	/* xxx_shift and xxx_scale variables allow us to replace
+	 * expensive divides with multiply and shift */
+	unsigned pmem_shift, pmem_scale, pmem_half;
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+	unsigned tmp_unsigned;
+	unsigned pcpu_shift, pcpu_scale, pcpu_half;
+	unsigned busy_jifs;
+#endif
+
+	/* what info of the processes is shown */
+	printf(OPT_BATCH_MODE ? "%.*s" : "\033[7m%.*s\033[0m", scr_width,
+		"  PID  PPID USER     STAT   VSZ %VSZ"
+		IF_FEATURE_TOP_SMP_PROCESS(" CPU")
+		IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(" %CPU")
+		" COMMAND");
+	lines_rem--;
+
+#if ENABLE_FEATURE_TOP_DECIMALS
+# define UPSCALE 1000
+# define CALC_STAT(name, val) div_t name = div((val), 10)
+# define SHOW_STAT(name) name.quot, '0'+name.rem
+# define FMT "%3u.%c"
+#else
+# define UPSCALE 100
+# define CALC_STAT(name, val) unsigned name = (val)
+# define SHOW_STAT(name) name
+# define FMT "%4u%%"
+#endif
+	/*
+	 * %VSZ = s->vsz/MemTotal
+	 */
+	pmem_shift = BITS_PER_INT-11;
+	pmem_scale = UPSCALE*(1U<<(BITS_PER_INT-11)) / total_memory;
+	/* s->vsz is in kb. we want (s->vsz * pmem_scale) to never overflow */
+	while (pmem_scale >= 512) {
+		pmem_scale /= 4;
+		pmem_shift -= 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
+	 * between two top updates (e.g. compilation) */
+	if (total_pcpu < busy_jifs) total_pcpu = busy_jifs;
+
+	/*
+	 * CPU% = s->pcpu/sum(s->pcpu) * busy_cpu_ticks/total_cpu_ticks
+	 * (pcpu is delta of sys+user time between samples)
+	 */
+	/* (cur_jif.xxx - prev_jif.xxx) and s->pcpu are
+	 * in 0..~64000 range (HZ*update_interval).
+	 * we assume that unsigned is at least 32-bit.
+	 */
+	pcpu_shift = 6;
+	pcpu_scale = UPSCALE*64 * (uint16_t)busy_jifs;
+	if (pcpu_scale == 0)
+		pcpu_scale = 1;
+	while (pcpu_scale < (1U << (BITS_PER_INT-2))) {
+		pcpu_scale *= 4;
+		pcpu_shift += 2;
+	}
+	tmp_unsigned = (uint16_t)(cur_jif.total - prev_jif.total) * total_pcpu;
+	if (tmp_unsigned != 0)
+		pcpu_scale /= tmp_unsigned;
+	/* we want (s->pcpu * pcpu_scale) to never overflow */
+	while (pcpu_scale >= 1024) {
+		pcpu_scale /= 4;
+		pcpu_shift -= 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;
+	while (--lines_rem >= 0) {
+		unsigned col;
+		CALC_STAT(pmem, (s->vsz*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);
+		else
+			sprintf(vsz_str_buf, "%7ld", 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")
+				IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(FMT)
+				" ",
+				s->pid, s->ppid, get_cached_username(s->uid),
+				s->state, vsz_str_buf,
+				SHOW_STAT(pmem)
+				IF_FEATURE_TOP_SMP_PROCESS(, s->last_seen_on_cpu)
+				IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE(, SHOW_STAT(pcpu))
+		);
+		if ((int)(col + 1) < scr_width)
+			read_cmdline(line_buf + col, scr_width - col, s->pid, s->comm);
+		fputs(line_buf, stdout);
+		/* printf(" %d/%d %lld/%lld", s->pcpu, total_pcpu,
+			cur_jif.busy - prev_jif.busy, cur_jif.total - prev_jif.total); */
+		s++;
+	}
+	/* printf(" %d", hist_iterations); */
+	bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
+	fflush_all();
+}
+#undef UPSCALE
+#undef SHOW_STAT
+#undef CALC_STAT
+#undef FMT
+
+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 (ENABLE_FEATURE_CLEAN_UP) {
+		clearmems();
+# if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+		free(prev_hist);
+# endif
+	}
+}
+
+static void sig_catcher(int sig UNUSED_PARAM)
+{
+	reset_term();
+	_exit(EXIT_FAILURE);
+}
+
+#endif /* FEATURE_USE_TERMIOS */
+
+/*
+ * TOPMEM support
+ */
+
+typedef unsigned long mem_t;
+
+typedef struct topmem_status_t {
+	unsigned pid;
+	char comm[COMM_LEN];
+	/* vsz doesn't count /dev/xxx mappings except /dev/zero */
+	mem_t vsz     ;
+	mem_t vszrw   ;
+	mem_t rss     ;
+	mem_t rss_sh  ;
+	mem_t dirty   ;
+	mem_t dirty_sh;
+	mem_t stack   ;
+} topmem_status_t;
+
+enum { NUM_SORT_FIELD = 7 };
+
+#define topmem ((topmem_status_t*)top)
+
+#if ENABLE_FEATURE_TOPMEM
+
+static int topmem_sort(char *a, char *b)
+{
+	int n;
+	mem_t l, r;
+
+	n = offsetof(topmem_status_t, vsz) + (sort_field * sizeof(mem_t));
+	l = *(mem_t*)(a + n);
+	r = *(mem_t*)(b + n);
+	if (l == r) {
+		l = ((topmem_status_t*)a)->dirty;
+		r = ((topmem_status_t*)b)->dirty;
+	}
+	/* We want to avoid unsigned->signed and truncation errors */
+	/* l>r: -1, l=r: 0, l<r: 1 */
+	n = (l > r) ? -1 : (l != r);
+	return inverted ? -n : n;
+}
+
+/* display header info (meminfo / loadavg) */
+static void display_topmem_header(int scr_width, int *lines_rem_p)
+{
+	enum {
+		TOTAL = 0, MFREE, BUF, CACHE,
+		SWAPTOTAL, SWAPFREE, DIRTY,
+		MWRITE, ANON, MAP, SLAB,
+		NUM_FIELDS
+	};
+	static const char match[NUM_FIELDS][12] = {
+		"\x09" "MemTotal:",  // TOTAL
+		"\x08" "MemFree:",   // MFREE
+		"\x08" "Buffers:",   // BUF
+		"\x07" "Cached:",    // CACHE
+		"\x0a" "SwapTotal:", // SWAPTOTAL
+		"\x09" "SwapFree:",  // SWAPFREE
+		"\x06" "Dirty:",     // DIRTY
+		"\x0a" "Writeback:", // MWRITE
+		"\x0a" "AnonPages:", // ANON
+		"\x07" "Mapped:",    // MAP
+		"\x05" "Slab:",      // SLAB
+	};
+	char meminfo_buf[4 * 1024];
+	const char *Z[NUM_FIELDS];
+	unsigned i;
+	int sz;
+
+	for (i = 0; i < NUM_FIELDS; i++)
+		Z[i] = "?";
+
+	/* read memory info */
+	sz = open_read_close("meminfo", meminfo_buf, sizeof(meminfo_buf) - 1);
+	if (sz >= 0) {
+		char *p = meminfo_buf;
+		meminfo_buf[sz] = '\0';
+		/* Note that fields always appear in the match[] order */
+		for (i = 0; i < NUM_FIELDS; i++) {
+			char *found = strstr(p, match[i] + 1);
+			if (found) {
+				/* Cut "NNNN" out of "    NNNN kb" */
+				char *s = skip_whitespace(found + match[i][0]);
+				p = skip_non_whitespace(s);
+				*p++ = '\0';
+				Z[i] = s;
+			}
+		}
+	}
+
+	snprintf(line_buf, LINE_BUF_SIZE,
+		"Mem total:%s anon:%s map:%s free:%s",
+		Z[TOTAL], Z[ANON], Z[MAP], Z[MFREE]);
+	printf(OPT_BATCH_MODE ? "%.*s\n" : "\033[H\033[J%.*s\n", scr_width, line_buf);
+
+	snprintf(line_buf, LINE_BUF_SIZE,
+		" slab:%s buf:%s cache:%s dirty:%s write:%s",
+		Z[SLAB], Z[BUF], Z[CACHE], Z[DIRTY], Z[MWRITE]);
+	printf("%.*s\n", scr_width, line_buf);
+
+	snprintf(line_buf, LINE_BUF_SIZE,
+		"Swap total:%s free:%s", // TODO: % used?
+		Z[SWAPTOTAL], Z[SWAPFREE]);
+	printf("%.*s\n", scr_width, line_buf);
+
+	(*lines_rem_p) -= 3;
+}
+
+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] = ' ';
+}
+
+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;
+
+	display_topmem_header(scr_width, &lines_rem);
+	strcpy(line_buf, HDR_STR " COMMAND");
+	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;
+	while (--lines_rem >= 0) {
+		/* PID VSZ VSZRW RSS (SHR) DIRTY (SHR) COMMAND */
+		ulltoa6_and_space(s->pid     , &line_buf[0*6]);
+		ulltoa6_and_space(s->vsz     , &line_buf[1*6]);
+		ulltoa6_and_space(s->vszrw   , &line_buf[2*6]);
+		ulltoa6_and_space(s->rss     , &line_buf[3*6]);
+		ulltoa6_and_space(s->rss_sh  , &line_buf[4*6]);
+		ulltoa6_and_space(s->dirty   , &line_buf[5*6]);
+		ulltoa6_and_space(s->dirty_sh, &line_buf[6*6]);
+		ulltoa6_and_space(s->stack   , &line_buf[7*6]);
+		line_buf[8*6] = '\0';
+		if (scr_width > (int)MIN_WIDTH) {
+			read_cmdline(&line_buf[8*6], scr_width - MIN_WIDTH, s->pid, s->comm);
+		}
+		printf("\n""%.*s", scr_width, line_buf);
+		s++;
+	}
+	bb_putchar(OPT_BATCH_MODE ? '\n' : '\r');
+	fflush_all();
+#undef HDR_STR
+#undef MIN_WIDTH
+}
+
+#else
+void display_topmem_process_list(int lines_rem, int scr_width);
+int topmem_sort(char *a, char *b);
+#endif /* TOPMEM */
+
+/*
+ * end TOPMEM support
+ */
+
+enum {
+	TOP_MASK = 0
+		| PSSCAN_PID
+		| PSSCAN_PPID
+		| PSSCAN_VSZ
+		| PSSCAN_STIME
+		| PSSCAN_UTIME
+		| PSSCAN_STATE
+		| PSSCAN_COMM
+		| PSSCAN_CPU
+		| PSSCAN_UIDGID,
+	TOPMEM_MASK = 0
+		| PSSCAN_PID
+		| PSSCAN_SMAPS
+		| PSSCAN_COMM,
+	EXIT_MASK = (unsigned)-1,
+};
+
+#if ENABLE_FEATURE_USE_TERMIOS
+static unsigned handle_input(unsigned scan_mask, unsigned interval)
+{
+	unsigned char c;
+	struct pollfd pfd[1];
+
+	pfd[0].fd = 0;
+	pfd[0].events = POLLIN;
+
+	while (1) {
+		if (safe_poll(pfd, 1, interval * 1000) <= 0)
+			return scan_mask;
+		interval = 0;
+
+		if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */
+			option_mask32 |= OPT_EOF;
+			return scan_mask;
+		}
+
+		if (c == initial_settings.c_cc[VINTR])
+			return EXIT_MASK;
+		if (c == initial_settings.c_cc[VEOF])
+			return EXIT_MASK;
+		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;
+	unsigned interval;
+	char *str_interval, *str_iterations;
+	unsigned scan_mask = TOP_MASK;
+#if ENABLE_FEATURE_USE_TERMIOS
+	struct termios new_settings;
+#endif
+
+	INIT_G();
+
+	interval = 5; /* default update interval is 5 seconds */
+	iterations = 0; /* infinite */
+#if ENABLE_FEATURE_TOP_SMP_CPU
+	/*num_cpus = 0;*/
+	/*smp_cpu_info = 0;*/  /* to start with show aggregate */
+	cpu_jif = &cur_jif;
+	cpu_prev_jif = &prev_jif;
+#endif
+
+	/* all args are options; -n NUM */
+	opt_complementary = "-"; /* options can be specified w/o dash */
+	col = getopt32(argv, "d:n:b"IF_FEATURE_TOPMEM("m"), &str_interval, &str_iterations);
+#if ENABLE_FEATURE_TOPMEM
+	if (col & OPT_m) /* -m (busybox specific) */
+		scan_mask = TOPMEM_MASK;
+#endif
+	if (col & OPT_d) {
+		/* work around for "-d 1" -> "-d -1" done by getopt32
+		 * (opt_complementary == "-" does this) */
+		if (str_interval[0] == '-')
+			str_interval++;
+		/* Need to limit it to not overflow poll timeout */
+		interval = xatou16(str_interval);
+	}
+	if (col & OPT_n) {
+		if (str_iterations[0] == '-')
+			str_iterations++;
+		iterations = xatou(str_iterations);
+	}
+
+	/* change to /proc */
+	xchdir("/proc");
+
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+	sort_function[0] = pcpu_sort;
+	sort_function[1] = mem_sort;
+	sort_function[2] = time_sort;
+#else
+	sort_function[0] = mem_sort;
+#endif
+
+#if ENABLE_FEATURE_USE_TERMIOS
+	tcgetattr(0, (void *) &initial_settings);
+	memcpy(&new_settings, &initial_settings, sizeof(new_settings));
+	if (!OPT_BATCH_MODE) {
+		/* 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;
+
+		if (OPT_BATCH_MODE) {
+			lines = INT_MAX;
+			col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */
+		} else {
+			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;
+			}
+#endif
+			if (col > LINE_BUF_SIZE - 2)
+				col = LINE_BUF_SIZE - 2;
+		}
+
+		/* read process IDs & status for all the processes */
+		while ((p = procps_scan(p, scan_mask)) != NULL) {
+			int n;
+#if ENABLE_FEATURE_TOPMEM
+			if (scan_mask != TOPMEM_MASK)
+#endif
+			{
+				n = ntop;
+				top = xrealloc_vector(top, 6, ntop++);
+				top[n].pid = p->pid;
+				top[n].ppid = p->ppid;
+				top[n].vsz = p->vsz;
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+				top[n].ticks = p->stime + p->utime;
+#endif
+				top[n].uid = p->uid;
+				strcpy(top[n].state, p->state);
+				strcpy(top[n].comm, p->comm);
+#if ENABLE_FEATURE_TOP_SMP_PROCESS
+				top[n].last_seen_on_cpu = p->last_seen_on_cpu;
+#endif
+			}
+#if ENABLE_FEATURE_TOPMEM
+			else { /* TOPMEM */
+				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->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" */
+		if (ntop == 0) {
+			bb_error_msg("no process info in /proc");
+			break;
+		}
+
+		if (scan_mask != TOPMEM_MASK) {
+#if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE
+			if (!prev_hist_count) {
+				do_stats();
+				usleep(100000);
+				clearmems();
+				continue;
+			}
+			do_stats();
+			/* TODO: we don't need to sort all 10000 processes, we need to find top 24! */
+			qsort(top, ntop, sizeof(top_status_t), (void*)mult_lvl_cmp);
+#else
+			qsort(top, ntop, sizeof(top_status_t), (void*)(sort_function[0]));
+#endif
+		}
+#if ENABLE_FEATURE_TOPMEM
+		else { /* TOPMEM */
+			qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort);
+		}
+#endif
+		if (scan_mask != TOPMEM_MASK)
+			display_process_list(lines, col);
+#if ENABLE_FEATURE_TOPMEM
+		else
+			display_topmem_process_list(lines, col);
+#endif
+		clearmems();
+		if (iterations >= 0 && !--iterations)
+			break;
+#if !ENABLE_FEATURE_USE_TERMIOS
+		sleep(interval);
+#else
+		if (option_mask32 & OPT_EOF)
+			/* EOF on stdin ("top </dev/null") */
+			sleep(interval);
+		else
+			scan_mask = handle_input(scan_mask, interval);
+#endif /* FEATURE_USE_TERMIOS */
+	} /* end of "while (not Q)" */
+
+	bb_putchar('\n');
+#if ENABLE_FEATURE_USE_TERMIOS
+	reset_term();
+#endif
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/procps/uptime.c b/busybox-1.19.3/procps/uptime.c
new file mode 100644
index 0000000..778812a
--- /dev/null
+++ b/busybox-1.19.3/procps/uptime.c
@@ -0,0 +1,100 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini uptime implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/* 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)  (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)
+{
+	unsigned updays, uphours, upminutes;
+	struct sysinfo info;
+	struct tm *current_time;
+	time_t current_secs;
+
+	time(&current_secs);
+	current_time = localtime(&current_secs);
+
+	sysinfo(&info);
+
+	printf(" %02u:%02u:%02u up ",
+			current_time->tm_hour, current_time->tm_min, current_time->tm_sec);
+	updays = (unsigned) info.uptime / (unsigned)(60*60*24);
+	if (updays)
+		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("%2u:%02u", uphours, upminutes);
+	else
+		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: %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]));
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/procps/watch.c b/busybox-1.19.3/procps/watch.c
new file mode 100644
index 0000000..36af1cc
--- /dev/null
+++ b/busybox-1.19.3/procps/watch.c
@@ -0,0 +1,98 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini watch implementation for busybox
+ *
+ * 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 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:
+// watch [-d] [-n seconds]
+//   [--differences[=cumulative]] [--interval=seconds] command
+//
+// procps-3.2.3:
+// watch [-dt] [-n seconds]
+//   [--differences[=cumulative]] [--interval=seconds] [--no-title] command
+//
+// (procps 3.x and procps 2.x are forks, not newer/older versions of the same)
+
+int watch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int watch_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	unsigned period = 2;
+	unsigned width, new_width;
+	char *header;
+	char *cmd;
+
+#if 0 // maybe ENABLE_DESKTOP?
+	// procps3 compat - "echo TEST | watch cat" doesn't show TEST:
+	close(STDIN_FILENO);
+	xopen("/dev/null", O_RDONLY);
+#endif
+
+	opt_complementary = "-1:n+"; // at least one param; -n NUM
+	// "+": stop at first non-option (procps 3.x only)
+	opt = getopt32(argv, "+dtn:", &period);
+	argv += optind;
+
+	// watch from both procps 2.x and 3.x does concatenation. Example:
+	// watch ls -l "a /tmp" "2>&1" - ls won't see "a /tmp" as one param
+	cmd = *argv;
+	while (*++argv)
+		cmd = xasprintf("%s %s", cmd, *argv); // leaks cmd
+
+	width = (unsigned)-1; // make sure first time new_width != width
+	header = NULL;
+	while (1) {
+		/* home; clear to the end of screen */
+		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
+			get_terminal_width_height(STDERR_FILENO, &new_width, NULL);
+			if (new_width != width) {
+				width = new_width;
+				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));
+
+			// compat: empty line between header and cmd output
+			printf("%s\n\n", header);
+		}
+		fflush_all();
+		// TODO: 'real' watch pipes cmd's output to itself
+		// and does not allow it to overflow the screen
+		// (taking into account linewrap!)
+		system(cmd);
+		sleep(period);
+	}
+	return 0; // gcc thinks we can reach this :)
+}
diff --git a/busybox-1.19.3/runit/Config.src b/busybox-1.19.3/runit/Config.src
new file mode 100644
index 0000000..9db9740
--- /dev/null
+++ b/busybox-1.19.3/runit/Config.src
@@ -0,0 +1,89 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Runit Utilities"
+
+INSERT
+
+config RUNSV
+	bool "runsv"
+	default y
+	help
+	  runsv starts and monitors a service and optionally an appendant log
+	  service.
+
+config RUNSVDIR
+	bool "runsvdir"
+	default y
+	help
+	  runsvdir starts a runsv process for each subdirectory, or symlink to
+	  a directory, in the services directory dir, up to a limit of 1000
+	  subdirectories, and restarts a runsv process if it terminates.
+
+config FEATURE_RUNSVDIR_LOG
+	bool "Enable scrolling argument log"
+	depends on RUNSVDIR
+	default n
+	help
+	  Enable feature where second parameter of runsvdir holds last error
+	  message (viewable via top/ps). Otherwise (feature is off
+	  or no parameter), error messages go to stderr only.
+
+config SV
+	bool "sv"
+	default y
+	help
+	  sv reports the current status and controls the state of services
+	  monitored by the runsv supervisor.
+
+config SV_DEFAULT_SERVICE_DIR
+	string "Default directory for services"
+	default "/var/service"
+	depends on SV
+	help
+	  Default directory for services.
+	  Defaults to "/var/service"
+
+config SVLOGD
+	bool "svlogd"
+	default y
+	help
+	  svlogd continuously reads log data from its standard input, optionally
+	  filters log messages, and writes the data to one or more automatically
+	  rotated logs.
+
+config CHPST
+	bool "chpst"
+	default y
+	help
+	  chpst changes the process state according to the given options, and
+	  execs specified program.
+
+config SETUIDGID
+	bool "setuidgid"
+	default y
+	help
+	  Sets soft resource limits as specified by options
+
+config ENVUIDGID
+	bool "envuidgid"
+	default y
+	help
+	  Sets $UID to account's uid and $GID to account's gid
+
+config ENVDIR
+	bool "envdir"
+	default y
+	help
+	  Sets various environment variables as specified by files
+	  in the given directory
+
+config SOFTLIMIT
+	bool "softlimit"
+	default y
+	help
+	  Sets soft resource limits as specified by options
+
+endmenu
diff --git a/busybox-1.19.3/runit/Kbuild.src b/busybox-1.19.3/runit/Kbuild.src
new file mode 100644
index 0000000..0fce955
--- /dev/null
+++ b/busybox-1.19.3/runit/Kbuild.src
@@ -0,0 +1,20 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+
+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
+lib-$(CONFIG_ENVUIDGID) += chpst.o
+lib-$(CONFIG_SETUIDGID) += chpst.o
+lib-$(CONFIG_SOFTLIMIT) += chpst.o
diff --git a/busybox-1.19.3/runit/chpst.c b/busybox-1.19.3/runit/chpst.c
new file mode 100644
index 0000000..1857060
--- /dev/null
+++ b/busybox-1.19.3/runit/chpst.c
@@ -0,0 +1,447 @@
+/*
+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> */
+/* 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"
+
+/*
+Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
+
+Only softlimit and chpst are taking options:
+
+# common
+-o N            Limit number of open files per process
+-p N            Limit number of processes per uid
+-m BYTES        Same as -d BYTES -s BYTES -l BYTES [-a BYTES]
+-d BYTES        Limit data segment
+-f BYTES        Limit output file sizes
+-c BYTES        Limit core file size
+# softlimit
+-a BYTES        Limit total size of all segments
+-s BYTES        Limit stack segment
+-l BYTES        Limit locked memory size
+-r BYTES        Limit resident set size
+-t N            Limit CPU time
+# chpst
+-u USER[:GRP]   Set uid and gid
+-U USER[:GRP]   Set $UID and $GID in environment
+-e DIR          Set environment variables as specified by files in DIR
+-/ DIR          Chroot to DIR
+-n NICE         Add NICE to nice value
+-v              Verbose
+-P              Create new process group
+-0 -1 -2        Close fd 0,1,2
+
+Even though we accept all these options for both softlimit and chpst,
+they are not to be advertised on their help texts.
+We have enough problems with feature creep in other people's
+software, don't want to add our own.
+
+envdir, envuidgid, setuidgid take no options, but they reuse code which
+handles -e, -U and -u.
+*/
+
+enum {
+	OPT_a = (1 << 0) * ENABLE_SOFTLIMIT,
+	OPT_c = (1 << 1) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
+	OPT_d = (1 << 2) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
+	OPT_f = (1 << 3) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
+	OPT_l = (1 << 4) * ENABLE_SOFTLIMIT,
+	OPT_m = (1 << 5) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
+	OPT_o = (1 << 6) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
+	OPT_p = (1 << 7) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
+	OPT_r = (1 << 8) * ENABLE_SOFTLIMIT,
+	OPT_s = (1 << 9) * ENABLE_SOFTLIMIT,
+	OPT_t = (1 << 10) * ENABLE_SOFTLIMIT,
+	OPT_u = (1 << 11) * (ENABLE_CHPST || ENABLE_SETUIDGID),
+	OPT_U = (1 << 12) * (ENABLE_CHPST || ENABLE_ENVUIDGID),
+	OPT_e = (1 << 13) * (ENABLE_CHPST || ENABLE_ENVDIR),
+	OPT_root = (1 << 14) * ENABLE_CHPST,
+	OPT_n = (1 << 15) * ENABLE_CHPST,
+	OPT_v = (1 << 16) * ENABLE_CHPST,
+	OPT_P = (1 << 17) * ENABLE_CHPST,
+	OPT_0 = (1 << 18) * ENABLE_CHPST,
+	OPT_1 = (1 << 19) * ENABLE_CHPST,
+	OPT_2 = (1 << 20) * ENABLE_CHPST,
+};
+
+/* TODO: use recursive_action? */
+static NOINLINE void edir(const char *directory_name)
+{
+	int wdir;
+	DIR *dir;
+	struct dirent *d;
+	int fd;
+
+	wdir = xopen(".", O_RDONLY | O_NDELAY);
+	xchdir(directory_name);
+	dir = xopendir(".");
+	for (;;) {
+		char buf[256];
+		char *tail;
+		int size;
+
+		errno = 0;
+		d = readdir(dir);
+		if (!d) {
+			if (errno)
+				bb_perror_msg_and_die("readdir %s",
+						directory_name);
+			break;
+		}
+		if (d->d_name[0] == '.')
+			continue;
+		fd = open(d->d_name, O_RDONLY | O_NDELAY);
+		if (fd < 0) {
+			if ((errno == EISDIR) && directory_name) {
+				if (option_mask32 & OPT_v)
+					bb_perror_msg("warning: %s/%s is a directory",
+						directory_name, d->d_name);
+				continue;
+			}
+			bb_perror_msg_and_die("open %s/%s",
+						directory_name, d->d_name);
+		}
+		size = full_read(fd, buf, sizeof(buf)-1);
+		close(fd);
+		if (size < 0)
+			bb_perror_msg_and_die("read %s/%s",
+					directory_name, d->d_name);
+		if (size == 0) {
+			unsetenv(d->d_name);
+			continue;
+		}
+		buf[size] = '\n';
+		tail = strchr(buf, '\n');
+		/* skip trailing whitespace */
+		while (1) {
+			*tail = '\0';
+			tail--;
+			if (tail < buf || !isspace(*tail))
+				break;
+		}
+		xsetenv(d->d_name, buf);
+	}
+	closedir(dir);
+	if (fchdir(wdir) == -1)
+		bb_perror_msg_and_die("fchdir");
+	close(wdir);
+}
+
+static void limit(int what, long l)
+{
+	struct rlimit r;
+
+	/* Never fails under Linux (except if you pass it bad arguments) */
+	getrlimit(what, &r);
+	if ((l < 0) || (l > r.rlim_max))
+		r.rlim_cur = r.rlim_max;
+	else
+		r.rlim_cur = l;
+	if (setrlimit(what, &r) == -1)
+		bb_perror_msg_and_die("setrlimit");
+}
+
+int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+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;
+	unsigned limita;
+	unsigned limitc;
+	unsigned limitd;
+	unsigned limitf;
+	unsigned limitl;
+	unsigned limitm;
+	unsigned limito;
+	unsigned limitp;
+	unsigned limitr;
+	unsigned limits;
+	unsigned limitt;
+	unsigned opt;
+
+	if ((ENABLE_CHPST && applet_name[0] == 'c')
+	 || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
+	) {
+		// FIXME: can we live with int-sized limits?
+		// can we live with 40000 days?
+		// if yes -> getopt converts strings to numbers for us
+		opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
+		opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
+			IF_CHPST("/:n:vP012"),
+			&limita, &limitc, &limitd, &limitf, &limitl,
+			&limitm, &limito, &limitp, &limitr, &limits, &limitt,
+			&set_user, &env_user, &env_dir
+			IF_CHPST(, &root, &nicestr));
+		argv += optind;
+		if (opt & OPT_m) { // -m means -asld
+			limita = limits = limitl = limitd = limitm;
+			opt |= (OPT_s | OPT_l | OPT_a | OPT_d);
+		}
+	} else {
+		option_mask32 = opt = 0;
+		argv++;
+		if (!*argv)
+			bb_show_usage();
+	}
+
+	// envdir?
+	if (ENABLE_ENVDIR && applet_name[3] == 'd') {
+		env_dir = *argv++;
+		opt |= OPT_e;
+	}
+
+	// setuidgid?
+	if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
+		set_user = *argv++;
+		opt |= OPT_u;
+	}
+
+	// envuidgid?
+	if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
+		env_user = *argv++;
+		opt |= OPT_U;
+	}
+
+	// we must have PROG [ARGS]
+	if (!*argv)
+		bb_show_usage();
+
+	// set limits
+	if (opt & OPT_d) {
+#ifdef RLIMIT_DATA
+		limit(RLIMIT_DATA, limitd);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"DATA");
+#endif
+	}
+	if (opt & OPT_s) {
+#ifdef RLIMIT_STACK
+		limit(RLIMIT_STACK, limits);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"STACK");
+#endif
+	}
+	if (opt & OPT_l) {
+#ifdef RLIMIT_MEMLOCK
+		limit(RLIMIT_MEMLOCK, limitl);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"MEMLOCK");
+#endif
+	}
+	if (opt & OPT_a) {
+#ifdef RLIMIT_VMEM
+		limit(RLIMIT_VMEM, limita);
+#else
+#ifdef RLIMIT_AS
+		limit(RLIMIT_AS, limita);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"VMEM");
+#endif
+#endif
+	}
+	if (opt & OPT_o) {
+#ifdef RLIMIT_NOFILE
+		limit(RLIMIT_NOFILE, limito);
+#else
+#ifdef RLIMIT_OFILE
+		limit(RLIMIT_OFILE, limito);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"NOFILE");
+#endif
+#endif
+	}
+	if (opt & OPT_p) {
+#ifdef RLIMIT_NPROC
+		limit(RLIMIT_NPROC, limitp);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"NPROC");
+#endif
+	}
+	if (opt & OPT_f) {
+#ifdef RLIMIT_FSIZE
+		limit(RLIMIT_FSIZE, limitf);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"FSIZE");
+#endif
+	}
+	if (opt & OPT_c) {
+#ifdef RLIMIT_CORE
+		limit(RLIMIT_CORE, limitc);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"CORE");
+#endif
+	}
+	if (opt & OPT_r) {
+#ifdef RLIMIT_RSS
+		limit(RLIMIT_RSS, limitr);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"RSS");
+#endif
+	}
+	if (opt & OPT_t) {
+#ifdef RLIMIT_CPU
+		limit(RLIMIT_CPU, limitt);
+#else
+		if (opt & OPT_v)
+			bb_error_msg("system does not support RLIMIT_%s",
+				"CPU");
+#endif
+	}
+
+	if (opt & OPT_P)
+		setsid();
+
+	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) {
+		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(".");
+	}
+
+	if (opt & OPT_u) {
+		if (setgroups(1, &ugid.gid) == -1)
+			bb_perror_msg_and_die("setgroups");
+		xsetgid(ugid.gid);
+		xsetuid(ugid.uid);
+	}
+
+	if (opt & OPT_n) {
+		errno = 0;
+		if (nice(xatoi(nicestr)) == -1)
+			bb_perror_msg_and_die("nice");
+	}
+
+	if (opt & OPT_0)
+		close(STDIN_FILENO);
+	if (opt & OPT_1)
+		close(STDOUT_FILENO);
+	if (opt & OPT_2)
+		close(STDERR_FILENO);
+
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/runit/runit_lib.h b/busybox-1.19.3/runit/runit_lib.h
new file mode 100644
index 0000000..c36ea4c
--- /dev/null
+++ b/busybox-1.19.3/runit/runit_lib.h
@@ -0,0 +1,46 @@
+/*
+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.
+*/
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+/*
+ * runsv / supervise / sv stuff
+ */
+typedef struct svstatus_t {
+	uint64_t time_be64 PACKED;
+	uint32_t time_nsec_be32 PACKED;
+	uint32_t pid_le32 PACKED;
+	uint8_t  paused;
+	uint8_t  want; /* 'u' or 'd' */
+	uint8_t  got_term;
+	uint8_t  run_or_finish;
+} svstatus_t;
+struct ERR_svstatus_must_be_20_bytes {
+	char ERR_svstatus_must_be_20_bytes[sizeof(svstatus_t) == 20 ? 1 : -1];
+};
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/busybox-1.19.3/runit/runsv.c b/busybox-1.19.3/runit/runsv.c
new file mode 100644
index 0000000..ad8d84f
--- /dev/null
+++ b/busybox-1.19.3/runit/runsv.c
@@ -0,0 +1,671 @@
+/*
+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> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+//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/poll.h>
+#include <sys/file.h>
+#include "libbb.h"
+#include "runit_lib.h"
+
+#if ENABLE_MONOTONIC_SYSCALL
+#include <sys/syscall.h>
+
+/* libc has incredibly messy way of doing this,
+ * typically requiring -lrt. We just skip all this mess */
+static void gettimeofday_ns(struct timespec *ts)
+{
+	syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
+}
+#else
+static void gettimeofday_ns(struct timespec *ts)
+{
+	if (sizeof(struct timeval) == sizeof(struct timespec)
+	 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
+	) {
+		/* Cheat */
+		gettimeofday((void*)ts, NULL);
+		ts->tv_nsec *= 1000;
+	} else {
+		extern void BUG_need_to_implement_gettimeofday_ns(void);
+		BUG_need_to_implement_gettimeofday_ns();
+	}
+}
+#endif
+
+/* Compare possibly overflowing unsigned counters */
+#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
+
+/* state */
+#define S_DOWN 0
+#define S_RUN 1
+#define S_FINISH 2
+/* ctrl */
+#define C_NOOP 0
+#define C_TERM 1
+#define C_PAUSE 2
+/* want */
+#define W_UP 0
+#define W_DOWN 1
+#define W_EXIT 2
+
+struct svdir {
+	int pid;
+	smallint state;
+	smallint ctrl;
+	smallint sd_want;
+	smallint islog;
+	struct timespec start;
+	int fdlock;
+	int fdcontrol;
+	int fdcontrolwrite;
+	int wstat;
+};
+
+struct globals {
+	smallint haslog;
+	smallint sigterm;
+	smallint pidchanged;
+	struct fd_pair selfpipe;
+	struct fd_pair logpipe;
+	char *dir;
+	struct svdir svd[2];
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define haslog       (G.haslog      )
+#define sigterm      (G.sigterm     )
+#define pidchanged   (G.pidchanged  )
+#define selfpipe     (G.selfpipe    )
+#define logpipe      (G.logpipe     )
+#define dir          (G.dir         )
+#define svd          (G.svd         )
+#define INIT_G() do { \
+	pidchanged = 1; \
+} while (0)
+
+static void fatal2_cannot(const char *m1, const char *m2)
+{
+	bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+	/* was exiting 111 */
+}
+static void fatal_cannot(const char *m)
+{
+	fatal2_cannot(m, "");
+	/* was exiting 111 */
+}
+static void fatal2x_cannot(const char *m1, const char *m2)
+{
+	bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
+	/* was exiting 111 */
+}
+static void warn_cannot(const char *m)
+{
+	bb_perror_msg("%s: warning: cannot %s", dir, m);
+}
+
+static void s_child(int sig_no UNUSED_PARAM)
+{
+	write(selfpipe.wr, "", 1);
+}
+
+static void s_term(int sig_no UNUSED_PARAM)
+{
+	sigterm = 1;
+	write(selfpipe.wr, "", 1); /* XXX */
+}
+
+static int open_trunc_or_warn(const char *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);
+	return fd;
+}
+
+static void update_status(struct svdir *s)
+{
+	ssize_t sz;
+	int fd;
+	svstatus_t status;
+
+	/* pid */
+	if (pidchanged) {
+		fd = open_trunc_or_warn("supervise/pid.new");
+		if (fd < 0)
+			return;
+		if (s->pid) {
+			char spid[sizeof(int)*3 + 2];
+			int size = sprintf(spid, "%u\n", (unsigned)s->pid);
+			write(fd, spid, size);
+		}
+		close(fd);
+		if (rename_or_warn("supervise/pid.new",
+		    s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
+			return;
+		pidchanged = 0;
+	}
+
+	/* stat */
+	fd = open_trunc_or_warn("supervise/stat.new");
+	if (fd < -1)
+		return;
+
+	{
+		char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
+		char *p = stat_buf;
+		switch (s->state) {
+		case S_DOWN:
+			p = stpcpy(p, "down");
+			break;
+		case S_RUN:
+			p = stpcpy(p, "run");
+			break;
+		case S_FINISH:
+			p = stpcpy(p, "finish");
+			break;
+		}
+		if (s->ctrl & C_PAUSE)
+			p = stpcpy(p, ", paused");
+		if (s->ctrl & C_TERM)
+			p = stpcpy(p, ", got TERM");
+		if (s->state != S_DOWN)
+			switch (s->sd_want) {
+			case W_DOWN:
+				p = stpcpy(p, ", want down");
+				break;
+			case W_EXIT:
+				p = stpcpy(p, ", want exit");
+				break;
+			}
+		*p++ = '\n';
+		write(fd, stat_buf, p - stat_buf);
+		close(fd);
+	}
+
+	rename_or_warn("supervise/stat.new",
+		s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
+
+	/* supervise compatibility */
+	memset(&status, 0, sizeof(status));
+	status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
+	status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
+	status.pid_le32 = SWAP_LE32(s->pid);
+	if (s->ctrl & C_PAUSE)
+		status.paused = 1;
+	if (s->sd_want == W_UP)
+		status.want = 'u';
+	else
+		status.want = 'd';
+	if (s->ctrl & C_TERM)
+		status.got_term = 1;
+	status.run_or_finish = s->state;
+	fd = open_trunc_or_warn("supervise/status.new");
+	if (fd < 0)
+		return;
+	sz = write(fd, &status, sizeof(status));
+	close(fd);
+	if (sz != sizeof(status)) {
+		warn_cannot("write supervise/status.new");
+		unlink("supervise/status.new");
+		return;
+	}
+	rename_or_warn("supervise/status.new",
+		s->islog ? "log/supervise/status" : "log/supervise/status"+4);
+}
+
+static unsigned custom(struct svdir *s, char c)
+{
+	pid_t pid;
+	int w;
+	char a[10];
+	struct stat st;
+
+	if (s->islog)
+		return 0;
+	strcpy(a, "control/?");
+	a[8] = c; /* replace '?' */
+	if (stat(a, &st) == 0) {
+		if (st.st_mode & S_IXUSR) {
+			pid = vfork();
+			if (pid == -1) {
+				warn_cannot("vfork for control/?");
+				return 0;
+			}
+			if (pid == 0) {
+				/* child */
+				if (haslog && dup2(logpipe.wr, 1) == -1)
+					warn_cannot("setup stdout for control/?");
+				execl(a, a, (char *) NULL);
+				fatal_cannot("run control/?");
+			}
+			/* parent */
+			if (safe_waitpid(pid, &w, 0) == -1) {
+				warn_cannot("wait for child control/?");
+				return 0;
+			}
+			return WEXITSTATUS(w) == 0;
+		}
+	} else {
+		if (errno != ENOENT)
+			warn_cannot("stat control/?");
+	}
+	return 0;
+}
+
+static void stopservice(struct svdir *s)
+{
+	if (s->pid && !custom(s, 't')) {
+		kill(s->pid, SIGTERM);
+		s->ctrl |= C_TERM;
+		update_status(s);
+	}
+	if (s->sd_want == W_DOWN) {
+		kill(s->pid, SIGCONT);
+		custom(s, 'd');
+		return;
+	}
+	if (s->sd_want == W_EXIT) {
+		kill(s->pid, SIGCONT);
+		custom(s, 'x');
+	}
+}
+
+static void startservice(struct svdir *s)
+{
+	int p;
+	const char *arg[4];
+	char exitcode[sizeof(int)*3 + 2];
+
+	if (s->state == S_FINISH) {
+/* Two arguments are given to ./finish. The first one is ./run exit code,
+ * or -1 if ./run didnt exit normally. The second one is
+ * the least significant byte of the exit status as determined by waitpid;
+ * for instance it is 0 if ./run exited normally, and the signal number
+ * if ./run was terminated by a signal. If runsv cannot start ./run
+ * for some reason, the exit code is 111 and the status is 0.
+ */
+		arg[0] = "./finish";
+		arg[1] = "-1";
+		if (WIFEXITED(s->wstat)) {
+			*utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
+			arg[1] = exitcode;
+		}
+		//arg[2] = "0";
+		//if (WIFSIGNALED(s->wstat)) {
+			arg[2] = utoa(WTERMSIG(s->wstat));
+		//}
+		arg[3] = NULL;
+	} else {
+		arg[0] = "./run";
+		arg[1] = NULL;
+		custom(s, 'u');
+	}
+
+	if (s->pid != 0)
+		stopservice(s); /* should never happen */
+	while ((p = vfork()) == -1) {
+		warn_cannot("vfork, sleeping");
+		sleep(5);
+	}
+	if (p == 0) {
+		/* child */
+		if (haslog) {
+			/* NB: bug alert! right order is close, then dup2 */
+			if (s->islog) {
+				xchdir("./log");
+				close(logpipe.wr);
+				xdup2(logpipe.rd, 0);
+			} else {
+				close(logpipe.rd);
+				xdup2(logpipe.wr, 1);
+			}
+		}
+		/* Non-ignored signals revert to SIG_DFL on exec anyway */
+		/*bb_signals(0
+			+ (1 << SIGCHLD)
+			+ (1 << SIGTERM)
+			, SIG_DFL);*/
+		sig_unblock(SIGCHLD);
+		sig_unblock(SIGTERM);
+		execv(arg[0], (char**) arg);
+		fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
+	}
+	/* parent */
+	if (s->state != S_FINISH) {
+		gettimeofday_ns(&s->start);
+		s->state = S_RUN;
+	}
+	s->pid = p;
+	pidchanged = 1;
+	s->ctrl = C_NOOP;
+	update_status(s);
+}
+
+static int ctrl(struct svdir *s, char c)
+{
+	int sig;
+
+	switch (c) {
+	case 'd': /* down */
+		s->sd_want = W_DOWN;
+		update_status(s);
+		if (s->pid && s->state != S_FINISH)
+			stopservice(s);
+		break;
+	case 'u': /* up */
+		s->sd_want = W_UP;
+		update_status(s);
+		if (s->pid == 0)
+			startservice(s);
+		break;
+	case 'x': /* exit */
+		if (s->islog)
+			break;
+		s->sd_want = W_EXIT;
+		update_status(s);
+		/* FALLTHROUGH */
+	case 't': /* sig term */
+		if (s->pid && s->state != S_FINISH)
+			stopservice(s);
+		break;
+	case 'k': /* sig kill */
+		if (s->pid && !custom(s, c))
+			kill(s->pid, SIGKILL);
+		s->state = S_DOWN;
+		break;
+	case 'p': /* sig pause */
+		if (s->pid && !custom(s, c))
+			kill(s->pid, SIGSTOP);
+		s->ctrl |= C_PAUSE;
+		update_status(s);
+		break;
+	case 'c': /* sig cont */
+		if (s->pid && !custom(s, c))
+			kill(s->pid, SIGCONT);
+		s->ctrl &= ~C_PAUSE;
+		update_status(s);
+		break;
+	case 'o': /* once */
+		s->sd_want = W_DOWN;
+		update_status(s);
+		if (!s->pid)
+			startservice(s);
+		break;
+	case 'a': /* sig alarm */
+		sig = SIGALRM;
+		goto sendsig;
+	case 'h': /* sig hup */
+		sig = SIGHUP;
+		goto sendsig;
+	case 'i': /* sig int */
+		sig = SIGINT;
+		goto sendsig;
+	case 'q': /* sig quit */
+		sig = SIGQUIT;
+		goto sendsig;
+	case '1': /* sig usr1 */
+		sig = SIGUSR1;
+		goto sendsig;
+	case '2': /* sig usr2 */
+		sig = SIGUSR2;
+		goto sendsig;
+	}
+	return 1;
+ sendsig:
+	if (s->pid && !custom(s, c))
+		kill(s->pid, sig);
+	return 1;
+}
+
+int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int runsv_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat s;
+	int fd;
+	int r;
+	char buf[256];
+
+	INIT_G();
+
+	dir = single_argv(argv);
+
+	xpiped_pair(selfpipe);
+	close_on_exec_on(selfpipe.rd);
+	close_on_exec_on(selfpipe.wr);
+	ndelay_on(selfpipe.rd);
+	ndelay_on(selfpipe.wr);
+
+	sig_block(SIGCHLD);
+	bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
+	sig_block(SIGTERM);
+	bb_signals_recursive_norestart(1 << SIGTERM, s_term);
+
+	xchdir(dir);
+	/* bss: svd[0].pid = 0; */
+	if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
+	if (C_NOOP) svd[0].ctrl = C_NOOP;
+	if (W_UP) svd[0].sd_want = W_UP;
+	/* bss: svd[0].islog = 0; */
+	/* bss: svd[1].pid = 0; */
+	gettimeofday_ns(&svd[0].start);
+	if (stat("down", &s) != -1)
+		svd[0].sd_want = W_DOWN;
+
+	if (stat("log", &s) == -1) {
+		if (errno != ENOENT)
+			warn_cannot("stat ./log");
+	} else {
+		if (!S_ISDIR(s.st_mode)) {
+			errno = 0;
+			warn_cannot("stat log/down: log is not a directory");
+		} else {
+			haslog = 1;
+			svd[1].state = S_DOWN;
+			svd[1].ctrl = C_NOOP;
+			svd[1].sd_want = W_UP;
+			svd[1].islog = 1;
+			gettimeofday_ns(&svd[1].start);
+			if (stat("log/down", &s) != -1)
+				svd[1].sd_want = W_DOWN;
+			xpiped_pair(logpipe);
+			close_on_exec_on(logpipe.rd);
+			close_on_exec_on(logpipe.wr);
+		}
+	}
+
+	if (mkdir("supervise", 0700) == -1) {
+		r = readlink("supervise", buf, sizeof(buf));
+		if (r != -1) {
+			if (r == sizeof(buf))
+				fatal2x_cannot("readlink ./supervise", ": name too long");
+			buf[r] = 0;
+			mkdir(buf, 0700);
+		} else {
+			if ((errno != ENOENT) && (errno != EINVAL))
+				fatal_cannot("readlink ./supervise");
+		}
+	}
+	svd[0].fdlock = xopen3("log/supervise/lock"+4,
+			O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+	if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
+		fatal_cannot("lock supervise/lock");
+	close_on_exec_on(svd[0].fdlock);
+	if (haslog) {
+		if (mkdir("log/supervise", 0700) == -1) {
+			r = readlink("log/supervise", buf, 256);
+			if (r != -1) {
+				if (r == 256)
+					fatal2x_cannot("readlink ./log/supervise", ": name too long");
+				buf[r] = 0;
+				fd = xopen(".", O_RDONLY|O_NDELAY);
+				xchdir("./log");
+				mkdir(buf, 0700);
+				if (fchdir(fd) == -1)
+					fatal_cannot("change back to service directory");
+				close(fd);
+			}
+			else {
+				if ((errno != ENOENT) && (errno != EINVAL))
+					fatal_cannot("readlink ./log/supervise");
+			}
+		}
+		svd[1].fdlock = xopen3("log/supervise/lock",
+				O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+		if (flock(svd[1].fdlock, LOCK_EX) == -1)
+			fatal_cannot("lock log/supervise/lock");
+		close_on_exec_on(svd[1].fdlock);
+	}
+
+	mkfifo("log/supervise/control"+4, 0600);
+	svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
+	close_on_exec_on(svd[0].fdcontrol);
+	svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
+	close_on_exec_on(svd[0].fdcontrolwrite);
+	update_status(&svd[0]);
+	if (haslog) {
+		mkfifo("log/supervise/control", 0600);
+		svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
+		close_on_exec_on(svd[1].fdcontrol);
+		svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
+		close_on_exec_on(svd[1].fdcontrolwrite);
+		update_status(&svd[1]);
+	}
+	mkfifo("log/supervise/ok"+4, 0600);
+	fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
+	close_on_exec_on(fd);
+	if (haslog) {
+		mkfifo("log/supervise/ok", 0600);
+		fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
+		close_on_exec_on(fd);
+	}
+	for (;;) {
+		struct pollfd x[3];
+		unsigned deadline;
+		char ch;
+
+		if (haslog)
+			if (!svd[1].pid && svd[1].sd_want == W_UP)
+				startservice(&svd[1]);
+		if (!svd[0].pid)
+			if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
+				startservice(&svd[0]);
+
+		x[0].fd = selfpipe.rd;
+		x[0].events = POLLIN;
+		x[1].fd = svd[0].fdcontrol;
+		x[1].events = POLLIN;
+		/* x[2] is used only if haslog == 1 */
+		x[2].fd = svd[1].fdcontrol;
+		x[2].events = POLLIN;
+		sig_unblock(SIGTERM);
+		sig_unblock(SIGCHLD);
+		poll(x, 2 + haslog, 3600*1000);
+		sig_block(SIGTERM);
+		sig_block(SIGCHLD);
+
+		while (read(selfpipe.rd, &ch, 1) == 1)
+			continue;
+
+		for (;;) {
+			pid_t child;
+			int wstat;
+
+			child = wait_any_nohang(&wstat);
+			if (!child)
+				break;
+			if ((child == -1) && (errno != EINTR))
+				break;
+			if (child == svd[0].pid) {
+				svd[0].wstat = wstat;
+				svd[0].pid = 0;
+				pidchanged = 1;
+				svd[0].ctrl &= ~C_TERM;
+				if (svd[0].state != S_FINISH) {
+					fd = open("finish", O_RDONLY|O_NDELAY);
+					if (fd != -1) {
+						close(fd);
+						svd[0].state = S_FINISH;
+						update_status(&svd[0]);
+						continue;
+					}
+				}
+				svd[0].state = S_DOWN;
+				deadline = svd[0].start.tv_sec + 1;
+				gettimeofday_ns(&svd[0].start);
+				update_status(&svd[0]);
+				if (LESS(svd[0].start.tv_sec, deadline))
+					sleep(1);
+			}
+			if (haslog) {
+				if (child == svd[1].pid) {
+					svd[0].wstat = wstat;
+					svd[1].pid = 0;
+					pidchanged = 1;
+					svd[1].state = S_DOWN;
+					svd[1].ctrl &= ~C_TERM;
+					deadline = svd[1].start.tv_sec + 1;
+					gettimeofday_ns(&svd[1].start);
+					update_status(&svd[1]);
+					if (LESS(svd[1].start.tv_sec, deadline))
+						sleep(1);
+				}
+			}
+		} /* for (;;) */
+		if (read(svd[0].fdcontrol, &ch, 1) == 1)
+			ctrl(&svd[0], ch);
+		if (haslog)
+			if (read(svd[1].fdcontrol, &ch, 1) == 1)
+				ctrl(&svd[1], ch);
+
+		if (sigterm) {
+			ctrl(&svd[0], 'x');
+			sigterm = 0;
+		}
+
+		if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
+			if (svd[1].pid == 0)
+				_exit(EXIT_SUCCESS);
+			if (svd[1].sd_want != W_EXIT) {
+				svd[1].sd_want = W_EXIT;
+				/* stopservice(&svd[1]); */
+				update_status(&svd[1]);
+				close(logpipe.wr);
+				close(logpipe.rd);
+			}
+		}
+	} /* for (;;) */
+	/* not reached */
+	return 0;
+}
diff --git a/busybox-1.19.3/runit/runsvdir.c b/busybox-1.19.3/runit/runsvdir.c
new file mode 100644
index 0000000..9495a2a
--- /dev/null
+++ b/busybox-1.19.3/runit/runsvdir.c
@@ -0,0 +1,403 @@
+/*
+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> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+//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/poll.h>
+#include <sys/file.h>
+#include "libbb.h"
+#include "runit_lib.h"
+
+#define MAXSERVICES 1000
+
+/* Should be not needed - all dirs are on same FS, right? */
+#define CHECK_DEVNO_TOO 0
+
+struct service {
+#if CHECK_DEVNO_TOO
+	dev_t dev;
+#endif
+	ino_t ino;
+	pid_t pid;
+	smallint isgone;
+};
+
+struct globals {
+	struct service *sv;
+	char *svdir;
+	int svnum;
+#if ENABLE_FEATURE_RUNSVDIR_LOG
+	char *rplog;
+	int rploglen;
+	struct fd_pair logpipe;
+	struct pollfd pfd[1];
+	unsigned stamplog;
+#endif
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define sv          (G.sv          )
+#define svdir       (G.svdir       )
+#define svnum       (G.svnum       )
+#define rplog       (G.rplog       )
+#define rploglen    (G.rploglen    )
+#define logpipe     (G.logpipe     )
+#define pfd         (G.pfd         )
+#define stamplog    (G.stamplog    )
+#define INIT_G() do { \
+} while (0)
+
+static void fatal2_cannot(const char *m1, const char *m2)
+{
+	bb_perror_msg_and_die("%s: fatal: can't %s%s", svdir, m1, m2);
+	/* was exiting 100 */
+}
+static void warn3x(const char *m1, const char *m2, const char *m3)
+{
+	bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
+}
+static void warn2_cannot(const char *m1, const char *m2)
+{
+	warn3x("can't ", m1, m2);
+}
+#if ENABLE_FEATURE_RUNSVDIR_LOG
+static void warnx(const char *m1)
+{
+	warn3x(m1, "", "");
+}
+#endif
+
+/* inlining + vfork -> bigger code */
+static NOINLINE pid_t runsv(const char *name)
+{
+	pid_t pid;
+
+	/* If we got signaled, stop spawning children at once! */
+	if (bb_got_signal)
+		return 0;
+
+	pid = vfork();
+	if (pid == -1) {
+		warn2_cannot("vfork", "");
+		return 0;
+	}
+	if (pid == 0) {
+		/* child */
+		if (option_mask32 & 1) /* -P option? */
+			setsid();
+/* man execv:
+ * "Signals set to be caught by the calling process image
+ *  shall be set to the default action in the new process image."
+ * Therefore, we do not need this: */
+#if 0
+		bb_signals(0
+			| (1 << SIGHUP)
+			| (1 << SIGTERM)
+			, SIG_DFL);
+#endif
+		execlp("runsv", "runsv", name, (char *) NULL);
+		fatal2_cannot("start runsv ", name);
+	}
+	return pid;
+}
+
+/* gcc 4.3.0 does better with NOINLINE */
+static NOINLINE int do_rescan(void)
+{
+	DIR *dir;
+	struct dirent *d;
+	int i;
+	struct stat s;
+	int need_rescan = 0;
+
+	dir = opendir(".");
+	if (!dir) {
+		warn2_cannot("open directory ", svdir);
+		return 1; /* need to rescan again soon */
+	}
+	for (i = 0; i < svnum; i++)
+		sv[i].isgone = 1;
+
+	while (1) {
+		errno = 0;
+		d = readdir(dir);
+		if (!d)
+			break;
+		if (d->d_name[0] == '.')
+			continue;
+		if (stat(d->d_name, &s) == -1) {
+			warn2_cannot("stat ", d->d_name);
+			continue;
+		}
+		if (!S_ISDIR(s.st_mode))
+			continue;
+		/* Do we have this service listed already? */
+		for (i = 0; i < svnum; i++) {
+			if ((sv[i].ino == s.st_ino)
+#if CHECK_DEVNO_TOO
+			 && (sv[i].dev == s.st_dev)
+#endif
+			) {
+				if (sv[i].pid == 0) /* restart if it has died */
+					goto run_ith_sv;
+				sv[i].isgone = 0; /* "we still see you" */
+				goto next_dentry;
+			}
+		}
+		{ /* Not found, make new service */
+			struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
+			if (!svnew) {
+				warn2_cannot("start runsv ", d->d_name);
+				need_rescan = 1;
+				continue;
+			}
+			sv = svnew;
+			svnum++;
+#if CHECK_DEVNO_TOO
+			sv[i].dev = s.st_dev;
+#endif
+			sv[i].ino = s.st_ino;
+ run_ith_sv:
+			sv[i].pid = runsv(d->d_name);
+			sv[i].isgone = 0;
+		}
+ next_dentry: ;
+	}
+	i = errno;
+	closedir(dir);
+	if (i) { /* readdir failed */
+		warn2_cannot("read directory ", svdir);
+		return 1; /* need to rescan again soon */
+	}
+
+	/* Send SIGTERM to runsv whose directories
+	 * were no longer found (-> must have been removed) */
+	for (i = 0; i < svnum; i++) {
+		if (!sv[i].isgone)
+			continue;
+		if (sv[i].pid)
+			kill(sv[i].pid, SIGTERM);
+		svnum--;
+		sv[i] = sv[svnum];
+		i--; /* so that we don't skip new sv[i] (bug was here!) */
+	}
+	return need_rescan;
+}
+
+int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int runsvdir_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat s;
+	dev_t last_dev = last_dev; /* for gcc */
+	ino_t last_ino = last_ino; /* for gcc */
+	time_t last_mtime = 0;
+	int wstat;
+	int curdir;
+	pid_t pid;
+	unsigned deadline;
+	unsigned now;
+	unsigned stampcheck;
+	int i;
+	int need_rescan = 1;
+	char *opt_s_argv[3];
+
+	INIT_G();
+
+	opt_complementary = "-1";
+	opt_s_argv[0] = NULL;
+	opt_s_argv[2] = NULL;
+	getopt32(argv, "Ps:", &opt_s_argv[0]);
+	argv += optind;
+
+	bb_signals(0
+		| (1 << SIGTERM)
+		| (1 << SIGHUP)
+		/* For busybox's init, SIGTERM == reboot,
+		 * SIGUSR1 == halt
+		 * SIGUSR2 == poweroff
+		 * so we need to intercept SIGUSRn too.
+		 * Note that we do not implement actual reboot
+		 * (killall(TERM) + umount, etc), we just pause
+		 * respawing and avoid exiting (-> making kernel oops).
+		 * The user is responsible for the rest. */
+		| (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
+		, record_signo);
+	svdir = *argv++;
+
+#if ENABLE_FEATURE_RUNSVDIR_LOG
+	/* setup log */
+	if (*argv) {
+		rplog = *argv;
+		rploglen = strlen(rplog);
+		if (rploglen < 7) {
+			warnx("log must have at least seven characters");
+		} else if (piped_pair(logpipe)) {
+			warnx("can't create pipe for log");
+		} else {
+			close_on_exec_on(logpipe.rd);
+			close_on_exec_on(logpipe.wr);
+			ndelay_on(logpipe.rd);
+			ndelay_on(logpipe.wr);
+			if (dup2(logpipe.wr, 2) == -1) {
+				warnx("can't set filedescriptor for log");
+			} else {
+				pfd[0].fd = logpipe.rd;
+				pfd[0].events = POLLIN;
+				stamplog = monotonic_sec();
+				goto run;
+			}
+		}
+		rplog = NULL;
+		warnx("log service disabled");
+	}
+ run:
+#endif
+	curdir = open(".", O_RDONLY|O_NDELAY);
+	if (curdir == -1)
+		fatal2_cannot("open current directory", "");
+	close_on_exec_on(curdir);
+
+	stampcheck = monotonic_sec();
+
+	for (;;) {
+		/* collect children */
+		for (;;) {
+			pid = wait_any_nohang(&wstat);
+			if (pid <= 0)
+				break;
+			for (i = 0; i < svnum; i++) {
+				if (pid == sv[i].pid) {
+					/* runsv has died */
+					sv[i].pid = 0;
+					need_rescan = 1;
+				}
+			}
+		}
+
+		now = monotonic_sec();
+		if ((int)(now - stampcheck) >= 0) {
+			/* wait at least a second */
+			stampcheck = now + 1;
+
+			if (stat(svdir, &s) != -1) {
+				if (need_rescan || s.st_mtime != last_mtime
+				 || s.st_ino != last_ino || s.st_dev != last_dev
+				) {
+					/* svdir modified */
+					if (chdir(svdir) != -1) {
+						last_mtime = s.st_mtime;
+						last_dev = s.st_dev;
+						last_ino = s.st_ino;
+						/* 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", "");
+							sleep(5);
+						}
+					} else {
+						warn2_cannot("change directory to ", svdir);
+					}
+				}
+			} else {
+				warn2_cannot("stat ", svdir);
+			}
+		}
+
+#if ENABLE_FEATURE_RUNSVDIR_LOG
+		if (rplog) {
+			if ((int)(now - stamplog) >= 0) {
+				write(logpipe.wr, ".", 1);
+				stamplog = now + 900;
+			}
+		}
+		pfd[0].revents = 0;
+#endif
+		deadline = (need_rescan ? 1 : 5);
+		sig_block(SIGCHLD);
+#if ENABLE_FEATURE_RUNSVDIR_LOG
+		if (rplog)
+			poll(pfd, 1, deadline*1000);
+		else
+#endif
+			sleep(deadline);
+		sig_unblock(SIGCHLD);
+
+#if ENABLE_FEATURE_RUNSVDIR_LOG
+		if (pfd[0].revents & POLLIN) {
+			char ch;
+			while (read(logpipe.rd, &ch, 1) > 0) {
+				if (ch < ' ')
+					ch = ' ';
+				for (i = 6; i < rploglen; i++)
+					rplog[i-1] = rplog[i];
+				rplog[rploglen-1] = ch;
+			}
+		}
+#endif
+		if (!bb_got_signal)
+			continue;
+
+		/* -s SCRIPT: useful if we are init.
+		 * In this case typically script never returns,
+		 * it halts/powers off/reboots the system. */
+		if (opt_s_argv[0]) {
+			/* Single parameter: signal# */
+			opt_s_argv[1] = utoa(bb_got_signal);
+			pid = spawn(opt_s_argv);
+			if (pid > 0) {
+				/* Remembering to wait for _any_ children,
+				 * not just pid */
+				while (wait(NULL) != pid)
+					continue;
+			}
+		}
+
+		if (bb_got_signal == SIGHUP) {
+			for (i = 0; i < svnum; i++)
+				if (sv[i].pid)
+					kill(sv[i].pid, SIGTERM);
+		}
+		/* SIGHUP or SIGTERM (or SIGUSRn if we are init) */
+		/* Exit unless we are init */
+		if (getpid() != 1)
+			return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;
+
+		/* init continues to monitor services forever */
+		bb_got_signal = 0;
+	} /* for (;;) */
+}
diff --git a/busybox-1.19.3/runit/sv.c b/busybox-1.19.3/runit/sv.c
new file mode 100644
index 0000000..5b01c87
--- /dev/null
+++ b/busybox-1.19.3/runit/sv.c
@@ -0,0 +1,618 @@
+/*
+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.
+*/
+
+/* Taken from http://smarden.sunsite.dk/runit/sv.8.html:
+
+sv - control and manage services monitored by runsv
+
+sv [-v] [-w sec] command services
+/etc/init.d/service [-w sec] command
+
+The sv program reports the current status and controls the state of services
+monitored by the runsv(8) supervisor.
+
+services consists of one or more arguments, each argument naming a directory
+service used by runsv(8). If service doesn't start with a dot or slash,
+it is searched in the default services directory /var/service/, otherwise
+relative to the current directory.
+
+command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
+1, 2, term, kill, or exit, or start, stop, restart, shutdown, force-stop,
+force-reload, force-restart, force-shutdown.
+
+The sv program can be sym-linked to /etc/init.d/ to provide an LSB init
+script interface. The service to be controlled then is specified by the
+base name of the "init script".
+
+status
+    Report the current status of the service, and the appendant log service
+    if available, to standard output.
+up
+    If the service is not running, start it. If the service stops, restart it.
+down
+    If the service is running, send it the TERM signal, and the CONT signal.
+    If ./run exits, start ./finish if it exists. After it stops, do not
+    restart service.
+once
+    If the service is not running, start it. Do not restart it if it stops.
+pause cont hup alarm interrupt quit 1 2 term kill
+    If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
+    USR1, USR2, TERM, or KILL signal respectively.
+exit
+    If the service is running, send it the TERM signal, and the CONT signal.
+    Do not restart the service. If the service is down, and no log service
+    exists, runsv(8) exits. If the service is down and a log service exists,
+    send the TERM signal to the log service. If the log service is down,
+    runsv(8) exits. This command is ignored if it is given to an appendant
+    log service.
+
+sv actually looks only at the first character of above commands.
+
+Commands compatible to LSB init script actions:
+
+status
+    Same as status.
+start
+    Same as up, but wait up to 7 seconds for the command to take effect.
+    Then report the status or timeout. If the script ./check exists in
+    the service directory, sv runs this script to check whether the service
+    is up and available; it's considered to be available if ./check exits
+    with 0.
+stop
+    Same as down, but wait up to 7 seconds for the service to become down.
+    Then report the status or timeout.
+restart
+    Send the commands term, cont, and up to the service, and wait up to
+    7 seconds for the service to restart. Then report the status or timeout.
+    If the script ./check exists in the service directory, sv runs this script
+    to check whether the service is up and available again; it's considered
+    to be available if ./check exits with 0.
+shutdown
+    Same as exit, but wait up to 7 seconds for the runsv(8) process
+    to terminate. Then report the status or timeout.
+force-stop
+    Same as down, but wait up to 7 seconds for the service to become down.
+    Then report the status, and on timeout send the service the kill command.
+force-reload
+    Send the service the term and cont commands, and wait up to
+    7 seconds for the service to restart. Then report the status,
+    and on timeout send the service the kill command.
+force-restart
+    Send the service the term, cont and up commands, and wait up to
+    7 seconds for the service to restart. Then report the status, and
+    on timeout send the service the kill command. If the script ./check
+    exists in the service directory, sv runs this script to check whether
+    the service is up and available again; it's considered to be available
+    if ./check exits with 0.
+force-shutdown
+    Same as exit, but wait up to 7 seconds for the runsv(8) process to
+    terminate. Then report the status, and on timeout send the service
+    the kill command.
+
+Additional Commands
+
+check
+    Check for the service to be in the state that's been requested. Wait up to
+    7 seconds for the service to reach the requested state, then report
+    the status or timeout. If the requested state of the service is up,
+    and the script ./check exists in the service directory, sv runs
+    this script to check whether the service is up and running;
+    it's considered to be up if ./check exits with 0.
+
+Options
+
+-v
+    wait up to 7 seconds for the command to take effect.
+    Then report the status or timeout.
+-w sec
+    Override the default timeout of 7 seconds with sec seconds. Implies -v.
+
+Environment
+
+SVDIR
+    The environment variable $SVDIR overrides the default services directory
+    /var/service.
+SVWAIT
+    The environment variable $SVWAIT overrides the default 7 seconds to wait
+    for a command to take effect. It is overridden by the -w option.
+
+Exit Codes
+    sv exits 0, if the command was successfully sent to all services, and,
+    if it was told to wait, the command has taken effect to all services.
+
+    For each service that caused an error (e.g. the directory is not
+    controlled by a runsv(8) process, or sv timed out while waiting),
+    sv increases the exit code by one and exits non zero. The maximum
+    is 99. sv exits 100 on error.
+*/
+
+/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+//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/poll.h>
+#include <sys/file.h>
+#include "libbb.h"
+#include "runit_lib.h"
+
+struct globals {
+	const char *acts;
+	char **service;
+	unsigned rc;
+/* "Bernstein" time format: unix + 0x400000000000000aULL */
+	uint64_t tstart, tnow;
+	svstatus_t svstatus;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define acts         (G.acts        )
+#define service      (G.service     )
+#define rc           (G.rc          )
+#define tstart       (G.tstart      )
+#define tnow         (G.tnow        )
+#define svstatus     (G.svstatus    )
+#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)
+{
+	bb_perror_msg("fatal: can't %s", m1);
+	_exit(151);
+}
+
+static void out(const char *p, const char *m1)
+{
+	printf("%s%s: %s", p, *service, m1);
+	if (errno) {
+		printf(": %s", strerror(errno));
+	}
+	bb_putchar('\n'); /* will also flush the output */
+}
+
+#define WARN    "warning: "
+#define OK      "ok: "
+
+static void fail(const char *m1)
+{
+	++rc;
+	out("fail: ", m1);
+}
+static void failx(const char *m1)
+{
+	errno = 0;
+	fail(m1);
+}
+static void warn(const char *m1)
+{
+	++rc;
+	/* "warning: <service>: <m1>\n" */
+	out("warning: ", m1);
+}
+static void ok(const char *m1)
+{
+	errno = 0;
+	out(OK, m1);
+}
+
+static int svstatus_get(void)
+{
+	int fd, r;
+
+	fd = open("supervise/ok", O_WRONLY|O_NDELAY);
+	if (fd == -1) {
+		if (errno == ENODEV) {
+			*acts == 'x' ? ok("runsv not running")
+			             : failx("runsv not running");
+			return 0;
+		}
+		warn("can't open supervise/ok");
+		return -1;
+	}
+	close(fd);
+	fd = open("supervise/status", O_RDONLY|O_NDELAY);
+	if (fd == -1) {
+		warn("can't open supervise/status");
+		return -1;
+	}
+	r = read(fd, &svstatus, 20);
+	close(fd);
+	switch (r) {
+	case 20:
+		break;
+	case -1:
+		warn("can't read supervise/status");
+		return -1;
+	default:
+		errno = 0;
+		warn("can't read supervise/status: bad format");
+		return -1;
+	}
+	return 1;
+}
+
+static unsigned svstatus_print(const char *m)
+{
+	int diff;
+	int pid;
+	int normallyup = 0;
+	struct stat s;
+	uint64_t timestamp;
+
+	if (stat("down", &s) == -1) {
+		if (errno != ENOENT) {
+			bb_perror_msg(WARN"can't stat %s/down", *service);
+			return 0;
+		}
+		normallyup = 1;
+	}
+	pid = SWAP_LE32(svstatus.pid_le32);
+	timestamp = SWAP_BE64(svstatus.time_be64);
+	if (pid) {
+		switch (svstatus.run_or_finish) {
+		case 1: printf("run: "); break;
+		case 2: printf("finish: "); break;
+		}
+		printf("%s: (pid %d) ", m, pid);
+	} else {
+		printf("down: %s: ", m);
+	}
+	diff = tnow - timestamp;
+	printf("%us", (diff < 0 ? 0 : diff));
+	if (pid) {
+		if (!normallyup) printf(", normally down");
+		if (svstatus.paused) printf(", paused");
+		if (svstatus.want == 'd') printf(", want down");
+		if (svstatus.got_term) printf(", got TERM");
+	} else {
+		if (normallyup) printf(", normally up");
+		if (svstatus.want == 'u') printf(", want up");
+	}
+	return pid ? 1 : 2;
+}
+
+static int status(const char *unused UNUSED_PARAM)
+{
+	int r;
+
+	if (svstatus_get() <= 0)
+		return 0;
+
+	r = svstatus_print(*service);
+	if (chdir("log") == -1) {
+		if (errno != ENOENT) {
+			printf("; log: "WARN"can't change to log service directory: %s",
+					strerror(errno));
+		}
+	} else if (svstatus_get()) {
+		printf("; ");
+		svstatus_print("log");
+	}
+	bb_putchar('\n'); /* will also flush the output */
+	return r;
+}
+
+static int checkscript(void)
+{
+	char *prog[2];
+	struct stat s;
+	int pid, w;
+
+	if (stat("check", &s) == -1) {
+		if (errno == ENOENT) return 1;
+		bb_perror_msg(WARN"can't stat %s/check", *service);
+		return 0;
+	}
+	/* if (!(s.st_mode & S_IXUSR)) return 1; */
+	prog[0] = (char*)"./check";
+	prog[1] = NULL;
+	pid = spawn(prog);
+	if (pid <= 0) {
+		bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
+		return 0;
+	}
+	while (safe_waitpid(pid, &w, 0) == -1) {
+		bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
+		return 0;
+	}
+	return WEXITSTATUS(w) == 0;
+}
+
+static int check(const char *a)
+{
+	int r;
+	unsigned pid_le32;
+	uint64_t timestamp;
+
+	r = svstatus_get();
+	if (r == -1)
+		return -1;
+	if (r == 0) {
+		if (*a == 'x')
+			return 1;
+		return -1;
+	}
+	pid_le32 = svstatus.pid_le32;
+	switch (*a) {
+	case 'x':
+		return 0;
+	case 'u':
+		if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
+		if (!checkscript()) return 0;
+		break;
+	case 'd':
+		if (pid_le32) return 0;
+		break;
+	case 'c':
+		if (pid_le32 && !checkscript()) return 0;
+		break;
+	case 't':
+		if (!pid_le32 && svstatus.want == 'd') break;
+		timestamp = SWAP_BE64(svstatus.time_be64);
+		if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
+			return 0;
+		break;
+	case 'o':
+		timestamp = SWAP_BE64(svstatus.time_be64);
+		if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
+			return 0;
+	}
+	printf(OK);
+	svstatus_print(*service);
+	bb_putchar('\n'); /* will also flush the output */
+	return 1;
+}
+
+static int control(const char *a)
+{
+	int fd, r, l;
+
+/* Is it an optimization?
+   It causes problems with "sv o SRV; ...; sv d SRV"
+   ('d' is not passed to SRV because its .want == 'd'):
+	if (svstatus_get() <= 0)
+		return -1;
+	if (svstatus.want == *a)
+		return 0;
+*/
+	fd = open("supervise/control", O_WRONLY|O_NDELAY);
+	if (fd == -1) {
+		if (errno != ENODEV)
+			warn("can't open supervise/control");
+		else
+			*a == 'x' ? ok("runsv not running") : failx("runsv not running");
+		return -1;
+	}
+	l = strlen(a);
+	r = write(fd, a, l);
+	close(fd);
+	if (r != l) {
+		warn("can't write to supervise/control");
+		return -1;
+	}
+	return 1;
+}
+
+int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sv_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *x;
+	char *action;
+	const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
+	unsigned waitsec = 7;
+	smallint kll = 0;
+	int verbose = 0;
+	int (*act)(const char*);
+	int (*cbk)(const char*);
+	int curdir;
+
+	INIT_G();
+
+	xfunc_error_retval = 100;
+
+	x = getenv("SVDIR");
+	if (x) varservice = x;
+	x = getenv("SVWAIT");
+	if (x) waitsec = xatou(x);
+
+	opt_complementary = "w+:vv"; /* -w N, -v is a counter */
+	getopt32(argv, "w:v", &waitsec, &verbose);
+	argv += optind;
+	action = *argv++;
+	if (!action || !*argv) bb_show_usage();
+
+	tnow = time(NULL) + 0x400000000000000aULL;
+	tstart = tnow;
+	curdir = open(".", O_RDONLY|O_NDELAY);
+	if (curdir == -1)
+		fatal_cannot("open current directory");
+
+	act = &control;
+	acts = "s";
+	cbk = &check;
+
+	switch (*action) {
+	case 'x':
+	case 'e':
+		acts = "x";
+		if (!verbose) cbk = NULL;
+		break;
+	case 'X':
+	case 'E':
+		acts = "x";
+		kll = 1;
+		break;
+	case 'D':
+		acts = "d";
+		kll = 1;
+		break;
+	case 'T':
+		acts = "tc";
+		kll = 1;
+		break;
+	case 'c':
+		if (str_equal(action, "check")) {
+			act = NULL;
+			acts = "c";
+			break;
+		}
+	case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
+	case 'a': case 'i': case 'k': case 'q': case '1': case '2':
+		action[1] = '\0';
+		acts = action;
+		if (!verbose) cbk = NULL;
+		break;
+	case 's':
+		if (str_equal(action, "shutdown")) {
+			acts = "x";
+			break;
+		}
+		if (str_equal(action, "start")) {
+			acts = "u";
+			break;
+		}
+		if (str_equal(action, "stop")) {
+			acts = "d";
+			break;
+		}
+		/* "status" */
+		act = &status;
+		cbk = NULL;
+		break;
+	case 'r':
+		if (str_equal(action, "restart")) {
+			acts = "tcu";
+			break;
+		}
+		bb_show_usage();
+	case 'f':
+		if (str_equal(action, "force-reload")) {
+			acts = "tc";
+			kll = 1;
+			break;
+		}
+		if (str_equal(action, "force-restart")) {
+			acts = "tcu";
+			kll = 1;
+			break;
+		}
+		if (str_equal(action, "force-shutdown")) {
+			acts = "x";
+			kll = 1;
+			break;
+		}
+		if (str_equal(action, "force-stop")) {
+			acts = "d";
+			kll = 1;
+			break;
+		}
+	default:
+		bb_show_usage();
+	}
+
+	service = argv;
+	while ((x = *service) != NULL) {
+		if (x[0] != '/' && x[0] != '.') {
+			if (chdir(varservice) == -1)
+				goto chdir_failed_0;
+		}
+		if (chdir(x) == -1) {
+ chdir_failed_0:
+			fail("can't change to service directory");
+			goto nullify_service_0;
+		}
+		if (act && (act(acts) == -1)) {
+ nullify_service_0:
+			*service = (char*) -1L; /* "dead" */
+		}
+		if (fchdir(curdir) == -1)
+			fatal_cannot("change to original directory");
+		service++;
+	}
+
+	if (cbk) while (1) {
+		int want_exit;
+		int diff;
+
+		diff = tnow - tstart;
+		service = argv;
+		want_exit = 1;
+		while ((x = *service) != NULL) {
+			if (x == (char*) -1L) /* "dead" */
+				goto next;
+			if (x[0] != '/' && x[0] != '.') {
+				if (chdir(varservice) == -1)
+					goto chdir_failed;
+			}
+			if (chdir(x) == -1) {
+ chdir_failed:
+				fail("can't change to service directory");
+				goto nullify_service;
+			}
+			if (cbk(acts) != 0)
+				goto nullify_service;
+			want_exit = 0;
+			if (diff >= waitsec) {
+				printf(kll ? "kill: " : "timeout: ");
+				if (svstatus_get() > 0) {
+					svstatus_print(x);
+					++rc;
+				}
+				bb_putchar('\n'); /* will also flush the output */
+				if (kll)
+					control("k");
+ nullify_service:
+				*service = (char*) -1L; /* "dead" */
+			}
+			if (fchdir(curdir) == -1)
+				fatal_cannot("change to original directory");
+ next:
+			service++;
+		}
+		if (want_exit) break;
+		usleep(420000);
+		tnow = time(NULL) + 0x400000000000000aULL;
+	}
+	return rc > 99 ? 99 : rc;
+}
diff --git a/busybox-1.19.3/runit/svlogd.c b/busybox-1.19.3/runit/svlogd.c
new file mode 100644
index 0000000..b0ba21b
--- /dev/null
+++ b/busybox-1.19.3/runit/svlogd.c
@@ -0,0 +1,1232 @@
+/*
+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> */
+/* TODO: depends on runit_lib.c - review and reduce/eliminate */
+
+/*
+Config files
+
+On startup, and after receiving a HUP signal, svlogd checks for each
+log directory log if the configuration file log/config exists,
+and if so, reads the file line by line and adjusts configuration
+for log as follows:
+
+If the line is empty, or starts with a #, it is ignored. A line
+of the form
+
+ssize
+    sets the maximum file size of current when svlogd should rotate
+    the current log file to size bytes. Default is 1000000.
+    If size is zero, svlogd doesnt rotate log files
+    You should set size to at least (2 * len).
+nnum
+    sets the number of old log files svlogd should maintain to num.
+    If svlogd sees more that num old log files in log after log file
+    rotation, it deletes the oldest one. Default is 10.
+    If num is zero, svlogd doesnt remove old log files.
+Nmin
+    sets the minimum number of old log files svlogd should maintain
+    to min. min must be less than num. If min is set, and svlogd
+    cannot write to current because the filesystem is full,
+    and it sees more than min old log files, it deletes the oldest one.
+ttimeout
+    sets the maximum age of the current log file when svlogd should
+    rotate the current log file to timeout seconds. If current
+    is timeout seconds old, and is not empty, svlogd forces log file rotation.
+!processor
+    tells svlogd to feed each recent log file through processor
+    (see above) on log file rotation. By default log files are not processed.
+ua.b.c.d[:port]
+    tells svlogd to transmit the first len characters of selected
+    log messages to the IP address a.b.c.d, port number port.
+    If port isnt set, the default port for syslog is used (514).
+    len can be set through the -l option, see below. If svlogd
+    has trouble sending udp packets, it writes error messages
+    to the log directory. Attention: logging through udp is unreliable,
+    and should be used in private networks only.
+Ua.b.c.d[:port]
+    is the same as the u line above, but the log messages are no longer
+    written to the log directory, but transmitted through udp only.
+    Error messages from svlogd concerning sending udp packages still go
+    to the log directory.
+pprefix
+    tells svlogd to prefix each line to be written to the log directory,
+    to standard error, or through UDP, with prefix.
+
+If a line starts with a -, +, e, or E, svlogd matches the first len characters
+of each log message against pattern and acts accordingly:
+
+-pattern
+    the log message is deselected.
++pattern
+    the log message is selected.
+epattern
+    the log message is selected to be printed to standard error.
+Epattern
+    the log message is deselected to be printed to standard error.
+
+Initially each line is selected to be written to log/current. Deselected
+log messages are discarded from log. Initially each line is deselected
+to be written to standard err. Log messages selected for standard error
+are written to standard error.
+
+Pattern Matching
+
+svlogd matches a log message against the string pattern as follows:
+
+pattern is applied to the log message one character by one, starting
+with the first. A character not a star (*) and not a plus (+) matches itself.
+A plus matches the next character in pattern in the log message one
+or more times. A star before the end of pattern matches any string
+in the log message that does not include the next character in pattern.
+A star at the end of pattern matches any string.
+
+Timestamps optionally added by svlogd are not considered part
+of the log message.
+
+An svlogd pattern is not a regular expression. For example consider
+a log message like this
+
+2005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
+
+The following pattern doesnt match
+
+-*pid*
+
+because the first star matches up to the first p in tcpsvd,
+and then the match fails because i is not s. To match this
+log message, you can use a pattern like this instead
+
+-*: *: pid *
+*/
+
+//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/poll.h>
+#include <sys/file.h>
+#include "libbb.h"
+#include "runit_lib.h"
+
+#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
+
+#define FMT_PTIME 30
+
+struct logdir {
+	////char *btmp;
+	/* pattern list to match, in "aa\0bb\0\cc\0\0" form */
+	char *inst;
+	char *processor;
+	char *name;
+	unsigned size;
+	unsigned sizemax;
+	unsigned nmax;
+	unsigned nmin;
+	unsigned rotate_period;
+	int ppid;
+	int fddir;
+	int fdcur;
+	FILE* filecur; ////
+	int fdlock;
+	unsigned next_rotate;
+	char fnsave[FMT_PTIME];
+	char match;
+	char matcherr;
+};
+
+
+struct globals {
+	struct logdir *dir;
+	unsigned verbose;
+	int linemax;
+	////int buflen;
+	int linelen;
+
+	int fdwdir;
+	char **fndir;
+	int wstat;
+	unsigned nearest_rotate;
+
+	void* (*memRchr)(const void *, int, size_t);
+	char *shell;
+
+	smallint exitasap;
+	smallint rotateasap;
+	smallint reopenasap;
+	smallint linecomplete;
+	smallint tmaxflag;
+
+	char repl;
+	const char *replace;
+	int fl_flag_0;
+	unsigned dirn;
+
+	sigset_t blocked_sigset;
+};
+#define G (*ptr_to_globals)
+#define dir            (G.dir           )
+#define verbose        (G.verbose       )
+#define linemax        (G.linemax       )
+#define buflen         (G.buflen        )
+#define linelen        (G.linelen       )
+#define fndir          (G.fndir         )
+#define fdwdir         (G.fdwdir        )
+#define wstat          (G.wstat         )
+#define memRchr        (G.memRchr       )
+#define nearest_rotate (G.nearest_rotate)
+#define exitasap       (G.exitasap      )
+#define rotateasap     (G.rotateasap    )
+#define reopenasap     (G.reopenasap    )
+#define linecomplete   (G.linecomplete  )
+#define tmaxflag       (G.tmaxflag      )
+#define repl           (G.repl          )
+#define replace        (G.replace       )
+#define blocked_sigset (G.blocked_sigset)
+#define fl_flag_0      (G.fl_flag_0     )
+#define dirn           (G.dirn          )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	linemax = 1000; \
+	/*buflen = 1024;*/ \
+	linecomplete = 1; \
+	replace = ""; \
+} while (0)
+
+#define line bb_common_bufsiz1
+
+
+#define FATAL "fatal: "
+#define WARNING "warning: "
+#define PAUSE "pausing: "
+#define INFO "info: "
+
+static void fatalx(const char *m0)
+{
+	bb_error_msg_and_die(FATAL"%s", m0);
+}
+static void warn(const char *m0)
+{
+	bb_perror_msg(WARNING"%s", m0);
+}
+static void warn2(const char *m0, const char *m1)
+{
+	bb_perror_msg(WARNING"%s: %s", m0, m1);
+}
+static void warnx(const char *m0, const char *m1)
+{
+	bb_error_msg(WARNING"%s: %s", m0, m1);
+}
+static void pause_nomem(void)
+{
+	bb_error_msg(PAUSE"out of memory");
+	sleep(3);
+}
+static void pause1cannot(const char *m0)
+{
+	bb_perror_msg(PAUSE"can't %s", m0);
+	sleep(3);
+}
+static void pause2cannot(const char *m0, const char *m1)
+{
+	bb_perror_msg(PAUSE"can't %s %s", m0, m1);
+	sleep(3);
+}
+
+static char* wstrdup(const char *str)
+{
+	char *s;
+	while (!(s = strdup(str)))
+		pause_nomem();
+	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 */
+static void fmt_time_human_30nul(char *s)
+{
+	struct tm *ptm;
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);
+	ptm = gmtime(&tv.tv_sec);
+	sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
+		(unsigned)(1900 + ptm->tm_year),
+		(unsigned)(ptm->tm_mon + 1),
+		(unsigned)(ptm->tm_mday),
+		(unsigned)(ptm->tm_hour),
+		(unsigned)(ptm->tm_min),
+		(unsigned)(ptm->tm_sec),
+		(unsigned)(tv.tv_usec)
+	);
+	/* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
+	/* 5   + 3   + 3   + 3   + 3   + 3   + 9 = */
+	/* 20 (up to '.' inclusive) + 9 (not including '\0') */
+}
+
+/* NOT terminated! */
+static void fmt_time_bernstein_25(char *s)
+{
+	uint32_t pack[3];
+	struct timeval tv;
+	unsigned sec_hi;
+
+	gettimeofday(&tv, NULL);
+	sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
+	tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
+	tv.tv_usec *= 1000;
+	/* Network order is big-endian: most significant byte first.
+	 * This is exactly what we want here */
+	pack[0] = htonl(sec_hi);
+	pack[1] = htonl(tv.tv_sec);
+	pack[2] = htonl(tv.tv_usec);
+	*s++ = '@';
+	bin2hex(s, (char*)pack, 12);
+}
+
+static void processorstart(struct logdir *ld)
+{
+	char sv_ch;
+	int pid;
+
+	if (!ld->processor) return;
+	if (ld->ppid) {
+		warnx("processor already running", ld->name);
+		return;
+	}
+
+	/* 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) {
+		int fd;
+
+		/* child */
+		/* Non-ignored signals revert to SIG_DFL on exec anyway */
+		/*bb_signals(0
+			+ (1 << SIGTERM)
+			+ (1 << SIGALRM)
+			+ (1 << SIGHUP)
+			, SIG_DFL);*/
+		sig_unblock(SIGTERM);
+		sig_unblock(SIGALRM);
+		sig_unblock(SIGHUP);
+
+		if (verbose)
+			bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
+		fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
+		xmove_fd(fd, 0);
+		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("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);
+			close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
+			fd = xopen("state", O_RDONLY|O_NDELAY);
+		}
+		xmove_fd(fd, 4);
+		fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
+		xmove_fd(fd, 5);
+
+		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 */
+	ld->ppid = pid;
+}
+
+static unsigned processorstop(struct logdir *ld)
+{
+	char f[28];
+
+	if (ld->ppid) {
+		sig_unblock(SIGHUP);
+		while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
+			pause2cannot("wait for processor", ld->name);
+		sig_block(SIGHUP);
+		ld->ppid = 0;
+	}
+	if (ld->fddir == -1)
+		return 1;
+	while (fchdir(ld->fddir) == -1)
+		pause2cannot("change directory, want processor", ld->name);
+	if (WEXITSTATUS(wstat) != 0) {
+		warnx("processor failed, restart", ld->name);
+		ld->fnsave[26] = 't';
+		unlink(ld->fnsave);
+		ld->fnsave[26] = 'u';
+		processorstart(ld);
+		while (fchdir(fdwdir) == -1)
+			pause1cannot("change to initial working directory");
+		return ld->processor ? 0 : 1;
+	}
+	ld->fnsave[26] = 't';
+	memcpy(f, ld->fnsave, 26);
+	f[26] = 's';
+	f[27] = '\0';
+	while (rename(ld->fnsave, f) == -1)
+		pause2cannot("rename processed", ld->name);
+	while (chmod(f, 0744) == -1)
+		pause2cannot("set mode of processed", ld->name);
+	ld->fnsave[26] = 'u';
+	if (unlink(ld->fnsave) == -1)
+		bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
+	while (rename("newstate", "state") == -1)
+		pause2cannot("rename state", ld->name);
+	if (verbose)
+		bb_error_msg(INFO"processed: %s/%s", ld->name, f);
+	while (fchdir(fdwdir) == -1)
+		pause1cannot("change to initial working directory");
+	return 1;
+}
+
+static void rmoldest(struct logdir *ld)
+{
+	DIR *d;
+	struct dirent *f;
+	char oldest[FMT_PTIME];
+	int n = 0;
+
+	oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
+	while (!(d = opendir(".")))
+		pause2cannot("open directory, want rotate", ld->name);
+	errno = 0;
+	while ((f = readdir(d))) {
+		if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
+			if (f->d_name[26] == 't') {
+				if (unlink(f->d_name) == -1)
+					warn2("can't unlink processor leftover", f->d_name);
+			} else {
+				++n;
+				if (strcmp(f->d_name, oldest) < 0)
+					memcpy(oldest, f->d_name, 27);
+			}
+			errno = 0;
+		}
+	}
+	if (errno)
+		warn2("can't read directory", ld->name);
+	closedir(d);
+
+	if (ld->nmax && (n > ld->nmax)) {
+		if (verbose)
+			bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
+		if ((*oldest == '@') && (unlink(oldest) == -1))
+			warn2("can't unlink oldest logfile", ld->name);
+	}
+}
+
+static unsigned rotate(struct logdir *ld)
+{
+	struct stat st;
+	unsigned now;
+
+	if (ld->fddir == -1) {
+		ld->rotate_period = 0;
+		return 0;
+	}
+	if (ld->ppid)
+		while (!processorstop(ld))
+			continue;
+
+	while (fchdir(ld->fddir) == -1)
+		pause2cannot("change directory, want rotate", ld->name);
+
+	/* create new filename */
+	ld->fnsave[25] = '.';
+	ld->fnsave[26] = 's';
+	if (ld->processor)
+		ld->fnsave[26] = 'u';
+	ld->fnsave[27] = '\0';
+	do {
+		fmt_time_bernstein_25(ld->fnsave);
+		errno = 0;
+		stat(ld->fnsave, &st);
+	} while (errno != ENOENT);
+
+	now = monotonic_sec();
+	if (ld->rotate_period && LESS(ld->next_rotate, now)) {
+		ld->next_rotate = now + ld->rotate_period;
+		if (LESS(ld->next_rotate, nearest_rotate))
+			nearest_rotate = ld->next_rotate;
+	}
+
+	if (ld->size > 0) {
+		while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
+			pause2cannot("fsync current logfile", ld->name);
+		while (fchmod(ld->fdcur, 0744) == -1)
+			pause2cannot("set mode of current", ld->name);
+		////close(ld->fdcur);
+		fclose(ld->filecur);
+
+		if (verbose) {
+			bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
+					ld->fnsave, ld->size);
+		}
+		while (rename("current", ld->fnsave) == -1)
+			pause2cannot("rename current", ld->name);
+		while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
+			pause2cannot("create new current", ld->name);
+		while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
+			pause2cannot("create new current", ld->name); /* very unlikely */
+		setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
+		close_on_exec_on(ld->fdcur);
+		ld->size = 0;
+		while (fchmod(ld->fdcur, 0644) == -1)
+			pause2cannot("set mode of current", ld->name);
+
+		rmoldest(ld);
+		processorstart(ld);
+	}
+
+	while (fchdir(fdwdir) == -1)
+		pause1cannot("change to initial working directory");
+	return 1;
+}
+
+static int buffer_pwrite(int n, char *s, unsigned len)
+{
+	int i;
+	struct logdir *ld = &dir[n];
+
+	if (ld->sizemax) {
+		if (ld->size >= ld->sizemax)
+			rotate(ld);
+		if (len > (ld->sizemax - ld->size))
+			len = ld->sizemax - ld->size;
+	}
+	while (1) {
+		////i = full_write(ld->fdcur, s, len);
+		////if (i != -1) break;
+		i = fwrite(s, 1, len, ld->filecur);
+		if (i == len) break;
+
+		if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
+			DIR *d;
+			struct dirent *f;
+			char oldest[FMT_PTIME];
+			int j = 0;
+
+			while (fchdir(ld->fddir) == -1)
+				pause2cannot("change directory, want remove old logfile",
+							 ld->name);
+			oldest[0] = 'A';
+			oldest[1] = oldest[27] = '\0';
+			while (!(d = opendir(".")))
+				pause2cannot("open directory, want remove old logfile",
+							 ld->name);
+			errno = 0;
+			while ((f = readdir(d)))
+				if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
+					++j;
+					if (strcmp(f->d_name, oldest) < 0)
+						memcpy(oldest, f->d_name, 27);
+				}
+			if (errno) warn2("can't read directory, want remove old logfile",
+					ld->name);
+			closedir(d);
+			errno = ENOSPC;
+			if (j > ld->nmin) {
+				if (*oldest == '@') {
+					bb_error_msg(WARNING"out of disk space, delete: %s/%s",
+							ld->name, oldest);
+					errno = 0;
+					if (unlink(oldest) == -1) {
+						warn2("can't unlink oldest logfile", ld->name);
+						errno = ENOSPC;
+					}
+					while (fchdir(fdwdir) == -1)
+						pause1cannot("change to initial working directory");
+				}
+			}
+		}
+		if (errno)
+			pause2cannot("write to current", ld->name);
+	}
+
+	ld->size += i;
+	if (ld->sizemax)
+		if (s[i-1] == '\n')
+			if (ld->size >= (ld->sizemax - linemax))
+				rotate(ld);
+	return i;
+}
+
+static void logdir_close(struct logdir *ld)
+{
+	if (ld->fddir == -1)
+		return;
+	if (verbose)
+		bb_error_msg(INFO"close: %s", ld->name);
+	close(ld->fddir);
+	ld->fddir = -1;
+	if (ld->fdcur == -1)
+		return; /* impossible */
+	while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
+		pause2cannot("fsync current logfile", ld->name);
+	while (fchmod(ld->fdcur, 0744) == -1)
+		pause2cannot("set mode of current", ld->name);
+	////close(ld->fdcur);
+	fclose(ld->filecur);
+	ld->fdcur = -1;
+	if (ld->fdlock == -1)
+		return; /* impossible */
+	close(ld->fdlock);
+	ld->fdlock = -1;
+	free(ld->processor);
+	ld->processor = NULL;
+}
+
+static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
+{
+	char buf[128];
+	unsigned now;
+	char *new, *s, *np;
+	int i;
+	struct stat st;
+
+	now = monotonic_sec();
+
+	ld->fddir = open(fn, O_RDONLY|O_NDELAY);
+	if (ld->fddir == -1) {
+		warn2("can't open log directory", (char*)fn);
+		return 0;
+	}
+	close_on_exec_on(ld->fddir);
+	if (fchdir(ld->fddir) == -1) {
+		logdir_close(ld);
+		warn2("can't change directory", (char*)fn);
+		return 0;
+	}
+	ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
+	if ((ld->fdlock == -1)
+	 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
+	) {
+		logdir_close(ld);
+		warn2("can't lock directory", (char*)fn);
+		while (fchdir(fdwdir) == -1)
+			pause1cannot("change to initial working directory");
+		return 0;
+	}
+	close_on_exec_on(ld->fdlock);
+
+	ld->size = 0;
+	ld->sizemax = 1000000;
+	ld->nmax = ld->nmin = 10;
+	ld->rotate_period = 0;
+	ld->name = (char*)fn;
+	ld->ppid = 0;
+	ld->match = '+';
+	free(ld->inst); ld->inst = NULL;
+	free(ld->processor); ld->processor = NULL;
+
+	/* read config */
+	i = open_read_close("config", buf, sizeof(buf) - 1);
+	if (i < 0 && errno != ENOENT)
+		bb_perror_msg(WARNING"%s/config", ld->name);
+	if (i > 0) {
+		buf[i] = '\0';
+		if (verbose)
+			bb_error_msg(INFO"read: %s/config", ld->name);
+		s = buf;
+		while (s) {
+			np = strchr(s, '\n');
+			if (np)
+				*np++ = '\0';
+			switch (s[0]) {
+			case '+':
+			case '-':
+			case 'e':
+			case 'E':
+				/* Filtering requires one-line buffering,
+				 * resetting the "find newline" function
+				 * accordingly */
+				memRchr = memchr;
+				/* Add '\n'-terminated line to ld->inst */
+				while (1) {
+					int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
+					if (l >= 0 && new)
+						break;
+					pause_nomem();
+				}
+				free(ld->inst);
+				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_positive(&s[1]);
+				break;
+			case 'N':
+				ld->nmin = xatoi_positive(&s[1]);
+				break;
+			case 't': {
+				static const struct suffix_mult mh_suffixes[] = {
+					{ "m", 60 },
+					{ "h", 60*60 },
+					/*{ "d", 24*60*60 },*/
+					{ "", 0 }
+				};
+				ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
+				if (ld->rotate_period) {
+					ld->next_rotate = now + ld->rotate_period;
+					if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
+						nearest_rotate = ld->next_rotate;
+					tmaxflag = 1;
+				}
+				break;
+			}
+			case '!':
+				if (s[1]) {
+					free(ld->processor);
+					ld->processor = wstrdup(s);
+				}
+				break;
+			}
+			s = np;
+		}
+		/* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
+		s = ld->inst;
+		while (s) {
+			np = strchr(s, '\n');
+			if (np)
+				*np++ = '\0';
+			s = np;
+		}
+	}
+
+	/* open current */
+	i = stat("current", &st);
+	if (i != -1) {
+		if (st.st_size && !(st.st_mode & S_IXUSR)) {
+			ld->fnsave[25] = '.';
+			ld->fnsave[26] = 'u';
+			ld->fnsave[27] = '\0';
+			do {
+				fmt_time_bernstein_25(ld->fnsave);
+				errno = 0;
+				stat(ld->fnsave, &st);
+			} while (errno != ENOENT);
+			while (rename("current", ld->fnsave) == -1)
+				pause2cannot("rename current", ld->name);
+			rmoldest(ld);
+			i = -1;
+		} else {
+			/* st.st_size can be not just bigger, but WIDER!
+			 * This code is safe: if st.st_size > 4GB, we select
+			 * ld->sizemax (because it's "unsigned") */
+			ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
+		}
+	} else {
+		if (errno != ENOENT) {
+			logdir_close(ld);
+			warn2("can't stat current", ld->name);
+			while (fchdir(fdwdir) == -1)
+				pause1cannot("change to initial working directory");
+			return 0;
+		}
+	}
+	while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
+		pause2cannot("open current", ld->name);
+	while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
+		pause2cannot("open current", ld->name); ////
+	setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
+
+	close_on_exec_on(ld->fdcur);
+	while (fchmod(ld->fdcur, 0644) == -1)
+		pause2cannot("set mode of current", ld->name);
+
+	if (verbose) {
+		if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
+		else bb_error_msg(INFO"new: %s/current", ld->name);
+	}
+
+	while (fchdir(fdwdir) == -1)
+		pause1cannot("change to initial working directory");
+	return 1;
+}
+
+static void logdirs_reopen(void)
+{
+	int l;
+	int ok = 0;
+
+	tmaxflag = 0;
+	for (l = 0; l < dirn; ++l) {
+		logdir_close(&dir[l]);
+		if (logdir_open(&dir[l], fndir[l]))
+			ok = 1;
+	}
+	if (!ok)
+		fatalx("no functional log directories");
+}
+
+/* Will look good in libbb one day */
+static ssize_t ndelay_read(int fd, void *buf, size_t count)
+{
+	if (!(fl_flag_0 & O_NONBLOCK))
+		fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
+	count = safe_read(fd, buf, count);
+	if (!(fl_flag_0 & O_NONBLOCK))
+		fcntl(fd, F_SETFL, fl_flag_0);
+	return count;
+}
+
+/* Used for reading stdin */
+static int buffer_pread(/*int fd, */char *s, unsigned len)
+{
+	unsigned now;
+	struct pollfd input;
+	int i;
+
+	input.fd = STDIN_FILENO;
+	input.events = POLLIN;
+
+	do {
+		if (rotateasap) {
+			for (i = 0; i < dirn; ++i)
+				rotate(dir + i);
+			rotateasap = 0;
+		}
+		if (exitasap) {
+			if (linecomplete)
+				return 0;
+			len = 1;
+		}
+		if (reopenasap) {
+			logdirs_reopen();
+			reopenasap = 0;
+		}
+		now = monotonic_sec();
+		nearest_rotate = now + (45 * 60 + 45);
+		for (i = 0; i < dirn; ++i) {
+			if (dir[i].rotate_period) {
+				if (LESS(dir[i].next_rotate, now))
+					rotate(dir + i);
+				if (LESS(dir[i].next_rotate, nearest_rotate))
+					nearest_rotate = dir[i].next_rotate;
+			}
+		}
+
+		sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
+		i = nearest_rotate - now;
+		if (i > 1000000)
+			i = 1000000;
+		if (i <= 0)
+			i = 1;
+		poll(&input, 1, i * 1000);
+		sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
+
+		i = ndelay_read(STDIN_FILENO, s, len);
+		if (i >= 0)
+			break;
+		if (errno == EINTR)
+			continue;
+		if (errno != EAGAIN) {
+			warn("can't read standard input");
+			break;
+		}
+		/* else: EAGAIN - normal, repeat silently */
+	} while (!exitasap);
+
+	if (i > 0) {
+		int cnt;
+		linecomplete = (s[i-1] == '\n');
+		if (!repl)
+			return i;
+
+		cnt = i;
+		while (--cnt >= 0) {
+			char ch = *s;
+			if (ch != '\n') {
+				if (ch < 32 || ch > 126)
+					*s = repl;
+				else {
+					int j;
+					for (j = 0; replace[j]; ++j) {
+						if (ch == replace[j]) {
+							*s = repl;
+							break;
+						}
+					}
+				}
+			}
+			s++;
+		}
+	}
+	return i;
+}
+
+static void sig_term_handler(int sig_no UNUSED_PARAM)
+{
+	if (verbose)
+		bb_error_msg(INFO"sig%s received", "term");
+	exitasap = 1;
+}
+
+static void sig_child_handler(int sig_no UNUSED_PARAM)
+{
+	pid_t pid;
+	int l;
+
+	if (verbose)
+		bb_error_msg(INFO"sig%s received", "child");
+	while ((pid = wait_any_nohang(&wstat)) > 0) {
+		for (l = 0; l < dirn; ++l) {
+			if (dir[l].ppid == pid) {
+				dir[l].ppid = 0;
+				processorstop(&dir[l]);
+				break;
+			}
+		}
+	}
+}
+
+static void sig_alarm_handler(int sig_no UNUSED_PARAM)
+{
+	if (verbose)
+		bb_error_msg(INFO"sig%s received", "alarm");
+	rotateasap = 1;
+}
+
+static void sig_hangup_handler(int sig_no UNUSED_PARAM)
+{
+	if (verbose)
+		bb_error_msg(INFO"sig%s received", "hangup");
+	reopenasap = 1;
+}
+
+static void logmatch(struct logdir *ld)
+{
+	char *s;
+
+	ld->match = '+';
+	ld->matcherr = 'E';
+	s = ld->inst;
+	while (s && s[0]) {
+		switch (s[0]) {
+		case '+':
+		case '-':
+			if (pmatch(s+1, line, linelen))
+				ld->match = s[0];
+			break;
+		case 'e':
+		case 'E':
+			if (pmatch(s+1, line, linelen))
+				ld->matcherr = s[0];
+			break;
+		}
+		s += strlen(s) + 1;
+	}
+}
+
+int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int svlogd_main(int argc, char **argv)
+{
+	char *r, *l, *b;
+	ssize_t stdin_cnt = 0;
+	int i;
+	unsigned opt;
+	unsigned timestamp = 0;
+
+	INIT_G();
+
+	opt_complementary = "tt:vv";
+	opt = getopt32(argv, "r:R:l:b:tv",
+			&r, &replace, &l, &b, &timestamp, &verbose);
+	if (opt & 1) { // -r
+		repl = r[0];
+		if (!repl || r[1])
+			bb_show_usage();
+	}
+	if (opt & 2) if (!repl) repl = '_'; // -R
+	if (opt & 4) { // -l
+		linemax = xatou_range(l, 0, BUFSIZ-26);
+		if (linemax == 0)
+			linemax = BUFSIZ-26;
+		if (linemax < 256)
+			linemax = 256;
+	}
+	////if (opt & 8) { // -b
+	////	buflen = xatoi_positive(b);
+	////	if (buflen == 0) buflen = 1024;
+	////}
+	//if (opt & 0x10) timestamp++; // -t
+	//if (opt & 0x20) verbose++; // -v
+	//if (timestamp > 2) timestamp = 2;
+	argv += optind;
+	argc -= optind;
+
+	dirn = argc;
+	if (dirn <= 0)
+		bb_show_usage();
+	////if (buflen <= linemax) bb_show_usage();
+	fdwdir = xopen(".", O_RDONLY|O_NDELAY);
+	close_on_exec_on(fdwdir);
+	dir = xzalloc(dirn * sizeof(dir[0]));
+	for (i = 0; i < dirn; ++i) {
+		dir[i].fddir = -1;
+		dir[i].fdcur = -1;
+		////dir[i].btmp = xmalloc(buflen);
+		/*dir[i].ppid = 0;*/
+	}
+	/* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
+	fndir = argv;
+	/* We cannot set NONBLOCK on fd #0 permanently - this setting
+	 * _isn't_ per-process! It is shared among all other processes
+	 * with the same stdin */
+	fl_flag_0 = fcntl(0, F_GETFL);
+
+	sigemptyset(&blocked_sigset);
+	sigaddset(&blocked_sigset, SIGTERM);
+	sigaddset(&blocked_sigset, SIGCHLD);
+	sigaddset(&blocked_sigset, SIGALRM);
+	sigaddset(&blocked_sigset, SIGHUP);
+	sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
+	bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
+	bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
+	bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
+	bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
+
+	/* Without timestamps, we don't have to print each line
+	 * separately, so we can look for _last_ newline, not first,
+	 * thus batching writes. If filtering is enabled in config,
+	 * logdirs_reopen resets it to memchr.
+	 */
+	memRchr = (timestamp ? memchr : memrchr);
+
+	logdirs_reopen();
+
+	setvbuf(stderr, NULL, _IOFBF, linelen);
+
+	/* Each iteration processes one or more lines */
+	while (1) {
+		char stamp[FMT_PTIME];
+		char *lineptr;
+		char *printptr;
+		char *np;
+		int printlen;
+		char ch;
+
+		lineptr = line;
+		if (timestamp)
+			lineptr += 26;
+
+		/* lineptr[0..linemax-1] - buffer for stdin */
+		/* (possibly has some unprocessed data from prev loop) */
+
+		/* Refill the buffer if needed */
+		np = memRchr(lineptr, '\n', stdin_cnt);
+		if (!np && !exitasap) {
+			i = linemax - stdin_cnt; /* avail. bytes at tail */
+			if (i >= 128) {
+				i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
+				if (i <= 0) /* EOF or error on stdin */
+					exitasap = 1;
+				else {
+					np = memRchr(lineptr + stdin_cnt, '\n', i);
+					stdin_cnt += i;
+				}
+			}
+		}
+		if (stdin_cnt <= 0 && exitasap)
+			break;
+
+		/* 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
+			 * farther out into line[] */
+			linelen = np - lineptr + 1;
+		}
+		/* linelen == no of chars incl. '\n' (or == stdin_cnt) */
+		ch = lineptr[linelen-1];
+
+		/* Biggest performance hit was coming from the fact
+		 * that we did not buffer writes. We were reading many lines
+		 * in one read() above, but wrote one line per write().
+		 * We are using stdio to fix that */
+
+		/* write out lineptr[0..linelen-1] to each log destination
+		 * (or lineptr[-26..linelen-1] if timestamping) */
+		printlen = linelen;
+		printptr = lineptr;
+		if (timestamp) {
+			if (timestamp == 1)
+				fmt_time_bernstein_25(stamp);
+			else /* 2: */
+				fmt_time_human_30nul(stamp);
+			printlen += 26;
+			printptr -= 26;
+			memcpy(printptr, stamp, 25);
+			printptr[25] = ' ';
+		}
+		for (i = 0; i < dirn; ++i) {
+			struct logdir *ld = &dir[i];
+			if (ld->fddir == -1)
+				continue;
+			if (ld->inst)
+				logmatch(ld);
+			if (ld->matcherr == 'e') {
+				/* runit-1.8.0 compat: if timestamping, do it on stderr too */
+				////full_write(STDERR_FILENO, printptr, printlen);
+				fwrite(printptr, 1, printlen, stderr);
+			}
+			if (ld->match != '+')
+				continue;
+			buffer_pwrite(i, printptr, printlen);
+		}
+
+		/* If we didn't see '\n' (long input line), */
+		/* read/write repeatedly until we see it */
+		while (ch != '\n') {
+			/* lineptr is emptied now, safe to use as buffer */
+			stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
+			if (stdin_cnt <= 0) { /* EOF or error on stdin */
+				exitasap = 1;
+				lineptr[0] = ch = '\n';
+				linelen = 1;
+				stdin_cnt = 1;
+			} else {
+				linelen = stdin_cnt;
+				np = memRchr(lineptr, '\n', stdin_cnt);
+				if (np)
+					linelen = np - lineptr + 1;
+				ch = lineptr[linelen-1];
+			}
+			/* linelen == no of chars incl. '\n' (or == stdin_cnt) */
+			for (i = 0; i < dirn; ++i) {
+				if (dir[i].fddir == -1)
+					continue;
+				if (dir[i].matcherr == 'e') {
+					////full_write(STDERR_FILENO, lineptr, linelen);
+					fwrite(lineptr, 1, linelen, stderr);
+				}
+				if (dir[i].match != '+')
+					continue;
+				buffer_pwrite(i, lineptr, linelen);
+			}
+		}
+
+		stdin_cnt -= linelen;
+		if (stdin_cnt > 0) {
+			lineptr += linelen;
+			/* If we see another '\n', we don't need to read
+			 * next piece of input: can print what we have */
+			np = memRchr(lineptr, '\n', stdin_cnt);
+			if (np)
+				goto print_to_nl;
+			/* Move unprocessed data to the front of line */
+			memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
+		}
+		fflush_all();////
+	}
+
+	for (i = 0; i < dirn; ++i) {
+		if (dir[i].ppid)
+			while (!processorstop(&dir[i]))
+				continue;
+		logdir_close(&dir[i]);
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/Kbuild.include b/busybox-1.19.3/scripts/Kbuild.include
new file mode 100644
index 0000000..6ec1809
--- /dev/null
+++ b/busybox-1.19.3/scripts/Kbuild.include
@@ -0,0 +1,154 @@
+####
+# kbuild: Generic definitions
+
+# Convinient variables
+comma   := ,
+squote  := '
+empty   :=
+space   := $(empty) $(empty)
+
+###
+# The temporary file to save gcc -MD generated dependencies must not
+# contain a comma
+depfile = $(subst $(comma),_,$(@D)/.$(@F).d)
+
+###
+# Escape single quote for use in echo statements
+escsq = $(subst $(squote),'\$(squote)',$1)
+
+###
+# filechk is used to check if the content of a generated file is updated.
+# Sample usage:
+# define filechk_sample
+#	echo $KERNELRELEASE
+# endef
+# version.h : Makefile
+#	$(call filechk,sample)
+# The rule defined shall write to stdout the content of the new file.
+# The existing file will be compared with the new one.
+# - If no file exist it is created
+# - If the content differ the new file is used
+# - If they are equal no change, and no timestamp update
+# - stdin is piped in from the first prerequisite ($<) so one has
+#   to specify a valid file as first prerequisite (often the kbuild file)
+define filechk
+	$(Q)set -e;				\
+	echo '  CHK     $@';			\
+	mkdir -p $(dir $@);			\
+	$(filechk_$(1)) < $< > $@.tmp;		\
+	if [ -r $@ ] && cmp -s $@ $@.tmp; then	\
+		rm -f $@.tmp;			\
+	else					\
+		echo '  UPD     $@';		\
+		mv -f $@.tmp $@;		\
+	fi
+endef
+
+######
+# gcc support functions
+# See documentation in Documentation/kbuild/makefiles.txt
+
+# as-option
+# Usage: cflags-y += $(call as-option, -Wa$(comma)-isa=foo,)
+
+as-option = $(shell if $(CC) $(CFLAGS) $(1) -Wa,-Z -c -o /dev/null \
+	     -xassembler /dev/null > /dev/null 2>&1; then echo "$(1)"; \
+	     else echo "$(2)"; fi ;)
+
+# cc-option
+# Usage: cflags-y += $(call cc-option, -march=winchip-c6, -march=i586)
+
+cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
+             > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+# hostcc-option
+# Usage: hostcflags-y += $(call hostcc-option, -march=winchip-c6, -march=i586)
+
+hostcc-option = $(shell if $(HOSTCC) $(HOSTCFLAGS) $(1) -S -o /dev/null -xc /dev/null \
+             > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
+
+# cc-option-yn
+# Usage: flag := $(call cc-option-yn, -march=winchip-c6)
+cc-option-yn = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
+                > /dev/null 2>&1; then echo "y"; else echo "n"; fi;)
+
+# cc-option-align
+# Prefix align with either -falign or -malign
+cc-option-align = $(subst -functions=0,,\
+	$(call cc-option,-falign-functions=0,-malign-functions=0))
+
+# cc-version
+# Usage gcc-ver := $(call cc-version, $(CC))
+cc-version = $(shell PATH="$(PATH)" $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh \
+              $(if $(1), $(1), $(CC)))
+
+# cc-ifversion
+# Usage:  EXTRA_CFLAGS += $(call cc-ifversion, -lt, 0402, -O1)
+cc-ifversion = $(shell if [ $(call cc-version, $(CC)) $(1) $(2) ]; then \
+                       echo $(3); fi;)
+
+###
+# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
+# Usage:
+# $(Q)$(MAKE) $(build)=dir
+build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
+
+# Prefix -I with $(srctree) if it is not an absolute path
+addtree = $(if $(filter-out -I/%,$(1)),$(patsubst -I%,-I$(srctree)/%,$(1))) $(1)
+# Find all -I options and call addtree
+flags = $(foreach o,$($(1)),$(if $(filter -I%,$(o)),$(call addtree,$(o)),$(o)))
+
+# If quiet is set, only print short version of command
+cmd = @$(echo-cmd) $(cmd_$(1))
+
+# Add $(obj)/ for paths that is not absolute
+objectify = $(foreach o,$(1),$(if $(filter /%,$(o)),$(o),$(obj)/$(o)))
+
+###
+# if_changed      - execute command if any prerequisite is newer than
+#                   target, or command line has changed
+# if_changed_dep  - as if_changed, but uses fixdep to reveal dependencies
+#                   including used config symbols
+# if_changed_rule - as if_changed but execute rule instead
+# See Documentation/kbuild/makefiles.txt for more info
+
+ifneq ($(KBUILD_NOCMDDEP),1)
+# Check if both arguments has same arguments. Result in empty string if equal
+# User may override this check using make KBUILD_NOCMDDEP=1
+arg-check = $(strip $(filter-out $(1), $(2)) $(filter-out $(2), $(1)) )
+endif
+
+# echo command. Short version is $(quiet) equals quiet, otherwise full command
+echo-cmd = $(if $($(quiet)cmd_$(1)), \
+	echo '  $(call escsq,$($(quiet)cmd_$(1)))';)
+
+make-cmd = $(subst \#,\\\#,$(subst $$,$$$$,$(call escsq,$(cmd_$(1)))))
+
+# function to only execute the passed command if necessary
+# >'< substitution is for echo to work, >$< substitution to preserve $ when reloading .cmd file
+# note: when using inline perl scripts [perl -e '...$$t=1;...'] in $(cmd_xxx) double $$ your perl vars
+#
+if_changed = $(if $(strip $(filter-out $(PHONY),$?)          \
+		$(call arg-check, $(cmd_$(1)), $(cmd_$@)) ), \
+	@set -e; \
+	$(echo-cmd) $(cmd_$(1)); \
+	echo 'cmd_$@ := $(make-cmd)' > $(@D)/.$(@F).cmd)
+
+# execute the command and also postprocess generated .d dependencies
+# file
+if_changed_dep = $(if $(strip $(filter-out $(PHONY),$?)  \
+		$(filter-out FORCE $(wildcard $^),$^)    \
+	$(call arg-check, $(cmd_$(1)), $(cmd_$@)) ),     \
+	@set -e; \
+	$(echo-cmd) $(cmd_$(1)); \
+	scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(@D)/.$(@F).tmp; \
+	rm -f $(depfile); \
+	mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd)
+
+# Usage: $(call if_changed_rule,foo)
+# will check if $(cmd_foo) changed, or any of the prequisites changed,
+# and if so will execute $(rule_foo)
+if_changed_rule = $(if $(strip $(filter-out $(PHONY),$?)            \
+			$(call arg-check, $(cmd_$(1)), $(cmd_$@)) ),\
+			@set -e; \
+			$(rule_$(1)))
diff --git a/busybox-1.19.3/scripts/Kbuild.src b/busybox-1.19.3/scripts/Kbuild.src
new file mode 100644
index 0000000..83b4232
--- /dev/null
+++ b/busybox-1.19.3/scripts/Kbuild.src
@@ -0,0 +1,7 @@
+###
+# scripts contains sources for various helper programs used throughout
+# the kernel for the build process.
+# ---------------------------------------------------------------------------
+
+# Let clean descend into subdirs
+subdir- += basic kconfig
diff --git a/busybox-1.19.3/scripts/Makefile.IMA b/busybox-1.19.3/scripts/Makefile.IMA
new file mode 100644
index 0000000..0eced29
--- /dev/null
+++ b/busybox-1.19.3/scripts/Makefile.IMA
@@ -0,0 +1,208 @@
+# This is completely unsupported.
+#
+# Uasge: make -f scripts/Makefile.IMA
+#
+# Fix COMBINED_COMPILE upstream (in the Kbuild) and propagate
+# the changes back
+srctree		:= $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
+objtree		:= $(CURDIR)
+src		:= $(srctree)
+obj		:= $(objtree)
+
+# 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
+
+include .config
+
+# Cross compiling and selecting different set of gcc/bin-utils
+ifeq ($(CROSS_COMPILE),)
+CROSS_COMPILE := $(subst ",,$(CONFIG_CROSS_COMPILER_PREFIX))
+endif
+
+ifneq ($(CROSS_COMPILE),)
+SUBARCH := $(shell echo $(CROSS_COMPILE) | cut -d- -f1)
+else
+SUBARCH := $(shell uname -m)
+endif
+SUBARCH := $(shell echo $(SUBARCH) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
+                                         -e s/arm.*/arm/ -e s/sa110/arm/ \
+                                         -e s/s390x/s390/ -e s/parisc64/parisc/ \
+                                         -e s/ppc.*/powerpc/ -e s/mips.*/mips/ )
+ARCH ?= $(SUBARCH)
+
+ifndef HOSTCC
+HOSTCC = cc
+endif
+AS              = $(CROSS_COMPILE)as
+CC              = $(CROSS_COMPILE)gcc
+LD              = $(CC) -nostdlib
+CPP             = $(CC) -E
+AR              = $(CROSS_COMPILE)ar
+NM              = $(CROSS_COMPILE)nm
+STRIP           = $(CROSS_COMPILE)strip
+OBJCOPY         = $(CROSS_COMPILE)objcopy
+OBJDUMP         = $(CROSS_COMPILE)objdump
+
+CFLAGS   := $(CFLAGS)
+CPPFLAGS += -D"KBUILD_STR(s)=\#s" #-Q
+
+# We need some generic definitions
+include $(srctree)/scripts/Kbuild.include
+
+include Makefile.flags
+
+-include $(srctree)/arch/$(ARCH)/Makefile
+ifdef CONFIG_FEATURE_COMPRESS_USAGE
+usage_stuff = include/usage_compressed.h
+endif
+
+ifndef BB_VER
+BB_VER:=""
+endif
+
+WHOLE_PROGRAM:=$(call cc-option,-fwhole-program,)
+
+# pull in the config stuff
+lib-all-y := applets/applets.o
+lib-y:=
+include procps/Kbuild
+lib-all-y += $(patsubst %,procps/%,$(sort $(lib-y)))
+lib-y:=
+include networking/Kbuild
+lib-all-y += $(patsubst %,networking/%,$(sort $(lib-y)))
+lib-y:=
+include networking/udhcp/Kbuild
+lib-all-y += $(patsubst %,networking/udhcp/%,$(sort $(lib-y)))
+lib-y:=
+include networking/libiproute/Kbuild
+lib-all-y += $(patsubst %,networking/libiproute/%,$(sort $(lib-y)))
+lib-y:=
+include loginutils/Kbuild
+lib-all-y += $(patsubst %,loginutils/%,$(sort $(lib-y)))
+lib-y:=
+include archival/Kbuild
+lib-all-y += $(patsubst %,archival/%,$(sort $(lib-y)))
+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)))
+lib-y:=
+include e2fsprogs/Kbuild
+lib-all-y += $(patsubst %,e2fsprogs/%,$(sort $(lib-y)))
+lib-y:=
+#include e2fsprogs/old_e2fsprogs/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/ext2fs/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/ext2fs/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/blkid/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/blkid/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/uuid/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/uuid/%,$(sort $(lib-y)))
+#lib-y:=
+#include e2fsprogs/old_e2fsprogs/e2p/Kbuild
+#lib-all-y += $(patsubst %,e2fsprogs/old_e2fsprogs/e2p/%,$(sort $(lib-y)))
+#lib-y:=
+include debianutils/Kbuild
+lib-all-y += $(patsubst %,debianutils/%,$(sort $(lib-y)))
+lib-y:=
+include runit/Kbuild
+lib-all-y += $(patsubst %,runit/%,$(sort $(lib-y)))
+lib-y:=
+include modutils/Kbuild
+lib-all-y += $(patsubst %,modutils/%,$(sort $(lib-y)))
+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:=
+include coreutils/Kbuild
+lib-all-y += $(patsubst %,coreutils/%,$(sort $(lib-y)))
+lib-y:=
+include sysklogd/Kbuild
+lib-all-y += $(patsubst %,sysklogd/%,$(sort $(lib-y)))
+lib-y:=
+include shell/Kbuild
+lib-all-y += $(patsubst %,shell/%,$(sort $(lib-y)))
+lib-y:=
+include console-tools/Kbuild
+lib-all-y += $(patsubst %,console-tools/%,$(sort $(lib-y)))
+lib-y:=
+include findutils/Kbuild
+lib-all-y += $(patsubst %,findutils/%,$(sort $(lib-y)))
+lib-y:=
+include util-linux/Kbuild
+lib-all-y += $(patsubst %,util-linux/%,$(sort $(lib-y)))
+lib-y:=
+include util-linux/volume_id/Kbuild
+lib-all-y += $(patsubst %,util-linux/volume_id/%,$(sort $(lib-y)))
+lib-y:=
+include init/Kbuild
+lib-all-y += $(patsubst %,init/%,$(sort $(lib-y)))
+lib-y:=
+include libpwdgrp/Kbuild
+lib-all-y += $(patsubst %,libpwdgrp/%,$(sort $(lib-y)))
+lib-y:=
+include editors/Kbuild
+lib-all-y += $(patsubst %,editors/%,$(sort $(lib-y)))
+lib-y:=
+include printutils/Kbuild
+lib-all-y += $(patsubst %,printutils/%,$(sort $(lib-y)))
+lib-y:=
+include selinux/Kbuild
+lib-all-y += $(patsubst %,selinux/%,$(sort $(lib-y)))
+lib-y:=
+include scripts/Kbuild
+lib-all-y += $(patsubst %,scripts/%,$(sort $(lib-y)))
+lib-y:=
+include libbb/Kbuild
+lib-all-y += $(patsubst %,libbb/%,$(sort $(lib-y)))
+lib-y:=
+
+comma:=,
+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 \
+		--combine $(WHOLE_PROGRAM) \
+		-funit-at-a-time -Wno-error -std=gnu99  \
+		-c -o busybox_unstripped.o \
+		$(lib-all-y:.o=.c)
+
+busybox: busybox_unstripped.o
+	$(srctree)/scripts/trylink \
+		busybox_unstripped \
+		"$(CC) $(CFLAGS_busybox)" \
+		"$(CFLAGS)" \
+		"$(LDFLAGS)" \
+		"busybox_unstripped.o" \
+		"" \
+		"crypt m"
+	cp -f $(@)_unstripped $@
+	-$(STRIP) -s -R .note -R .comment -R .version $@
+
+# If .config is newer than include/autoconf.h, someone tinkered
+# with it and forgot to run make oldconfig.
+include/autoconf.h: .config
+	$(MAKE) -f $(srctree)/Makefile silentoldconfig
+
+# Override rules for host compile
+applets/usage: include/autoconf.h
+	$(HOSTCC) -Wall -O2 -I$(srctree)/include -o applets/usage applets/usage.c
+
+applets/applet_tables: include/autoconf.h
+	$(HOSTCC) -Wall -O2 -I$(srctree)/include -o applets/applet_tables applets/applet_tables.c
diff --git a/busybox-1.19.3/scripts/Makefile.build b/busybox-1.19.3/scripts/Makefile.build
new file mode 100644
index 0000000..5685b5b
--- /dev/null
+++ b/busybox-1.19.3/scripts/Makefile.build
@@ -0,0 +1,343 @@
+# ==========================================================================
+# Building
+# ==========================================================================
+
+src := $(obj)
+
+PHONY := __build
+__build:
+
+# Read .config if it exist, otherwise ignore
+-include .config
+
+include scripts/Kbuild.include
+
+# The filename Kbuild has precedence over Makefile
+# bbox: we also try to include Kbuild file in obj tree first
+kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+include $(if $(wildcard $(src)/Kbuild), $(src)/Kbuild, \
+		$(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, \
+			$(kbuild-dir)/Makefile \
+		) \
+	)
+
+include scripts/Makefile.lib
+
+ifdef host-progs
+ifneq ($(hostprogs-y),$(host-progs))
+$(warning kbuild: $(obj)/Makefile - Usage of host-progs is deprecated. Please replace with hostprogs-y!)
+hostprogs-y += $(host-progs)
+endif
+endif
+
+# Do not include host rules unles needed
+ifneq ($(hostprogs-y)$(hostprogs-m),)
+include scripts/Makefile.host
+endif
+
+ifneq ($(KBUILD_SRC),)
+# Create output directory if not already present
+_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj))
+
+# Create directories for object files if directory does not exist
+# Needed when obj-y := dir/file.o syntax is used
+_dummy := $(foreach d,$(obj-dirs), $(shell [ -d $(d) ] || mkdir -p $(d)))
+endif
+
+
+ifdef EXTRA_TARGETS
+$(warning kbuild: $(obj)/Makefile - Usage of EXTRA_TARGETS is obsolete in 2.6. Please fix!)
+endif
+
+ifdef build-targets
+$(warning kbuild: $(obj)/Makefile - Usage of build-targets is obsolete in 2.6. Please fix!)
+endif
+
+ifdef export-objs
+$(warning kbuild: $(obj)/Makefile - Usage of export-objs is obsolete in 2.6. Please fix!)
+endif
+
+ifdef O_TARGET
+$(warning kbuild: $(obj)/Makefile - Usage of O_TARGET := $(O_TARGET) is obsolete in 2.6. Please fix!)
+endif
+
+ifdef L_TARGET
+$(error kbuild: $(obj)/Makefile - Use of L_TARGET is replaced by lib-y in 2.6. Please fix!)
+endif
+
+ifdef list-multi
+$(warning kbuild: $(obj)/Makefile - list-multi := $(list-multi) is obsolete in 2.6. Please fix!)
+endif
+
+ifndef obj
+$(warning kbuild: Makefile.build is included improperly)
+endif
+
+# ===========================================================================
+
+ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),)
+lib-target := $(obj)/lib.a
+endif
+
+ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(lib-target)),)
+builtin-target := $(obj)/built-in.o
+endif
+
+# We keep a list of all modules in $(MODVERDIR)
+
+__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
+	 $(if $(KBUILD_MODULES),$(obj-m)) \
+	 $(subdir-ym) $(always)
+	@:
+
+# Linus' kernel sanity checking tool
+ifneq ($(KBUILD_CHECKSRC),0)
+  ifeq ($(KBUILD_CHECKSRC),2)
+    quiet_cmd_force_checksrc = CHECK   $<
+          cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ;
+  else
+      quiet_cmd_checksrc     = CHECK   $<
+            cmd_checksrc     = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ;
+  endif
+endif
+
+
+# Compile C sources (.c)
+# ---------------------------------------------------------------------------
+
+# Default is built-in, unless we know otherwise
+modkern_cflags := $(CFLAGS_KERNEL)
+quiet_modtag := $(empty)   $(empty)
+
+$(real-objs-m)        : modkern_cflags := $(CFLAGS_MODULE)
+$(real-objs-m:.o=.i)  : modkern_cflags := $(CFLAGS_MODULE)
+$(real-objs-m:.o=.s)  : modkern_cflags := $(CFLAGS_MODULE)
+$(real-objs-m:.o=.lst): modkern_cflags := $(CFLAGS_MODULE)
+
+$(real-objs-m)        : quiet_modtag := [M]
+$(real-objs-m:.o=.i)  : quiet_modtag := [M]
+$(real-objs-m:.o=.s)  : quiet_modtag := [M]
+$(real-objs-m:.o=.lst): quiet_modtag := [M]
+
+$(obj-m)              : quiet_modtag := [M]
+
+# Default for not multi-part modules
+modname = $(*F)
+
+$(multi-objs-m)         : modname = $(modname-multi)
+$(multi-objs-m:.o=.i)   : modname = $(modname-multi)
+$(multi-objs-m:.o=.s)   : modname = $(modname-multi)
+$(multi-objs-m:.o=.lst) : modname = $(modname-multi)
+$(multi-objs-y)         : modname = $(modname-multi)
+$(multi-objs-y:.o=.i)   : modname = $(modname-multi)
+$(multi-objs-y:.o=.s)   : modname = $(modname-multi)
+$(multi-objs-y:.o=.lst) : modname = $(modname-multi)
+
+quiet_cmd_cc_s_c = CC $(quiet_modtag)  $@
+cmd_cc_s_c       = $(CC) $(c_flags) -fverbose-asm -S -o $@ $<
+
+%.s: %.c FORCE
+	$(call if_changed_dep,cc_s_c)
+
+quiet_cmd_cc_i_c = CPP $(quiet_modtag) $@
+cmd_cc_i_c       = $(CPP) $(c_flags)   -o $@ $<
+
+%.i: %.c FORCE
+	$(call if_changed_dep,cc_i_c)
+
+# C (.c) files
+# The C file is compiled and updated dependency information is generated.
+# (See cmd_cc_o_c + relevant part of rule_cc_o_c)
+
+quiet_cmd_cc_o_c = CC $(quiet_modtag)  $@
+
+ifndef CONFIG_MODVERSIONS
+cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
+
+else
+# When module versioning is enabled the following steps are executed:
+# o compile a .tmp_<file>.o from <file>.c
+# o if .tmp_<file>.o doesn't contain a __ksymtab version, i.e. does
+#   not export symbols, we just rename .tmp_<file>.o to <file>.o and
+#   are done.
+# o otherwise, we calculate symbol versions using the good old
+#   genksyms on the preprocessed source and postprocess them in a way
+#   that they are usable as a linker script
+# o generate <file>.o from .tmp_<file>.o using the linker to
+#   replace the unresolved symbols __crc_exported_symbol with
+#   the actual value of the checksum generated by genksyms
+
+cmd_cc_o_c = $(CC) $(c_flags) -c -o $(@D)/.tmp_$(@F) $<
+cmd_modversions =							\
+	if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then	\
+		$(CPP) -D__GENKSYMS__ $(c_flags) $<			\
+		| $(GENKSYMS) -a $(ARCH)				\
+		> $(@D)/.tmp_$(@F:.o=.ver);				\
+									\
+		$(LD) $(LDFLAGS) -r -o $@ $(@D)/.tmp_$(@F) 		\
+			-T $(@D)/.tmp_$(@F:.o=.ver);			\
+		rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver);	\
+	else								\
+		mv -f $(@D)/.tmp_$(@F) $@;				\
+	fi;
+endif
+
+define rule_cc_o_c
+	$(call echo-cmd,checksrc) $(cmd_checksrc)			  \
+	$(call echo-cmd,cc_o_c) $(cmd_cc_o_c);				  \
+	$(cmd_modversions)						  \
+	scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' > $(@D)/.$(@F).tmp;  \
+	rm -f $(depfile);						  \
+	mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd
+endef
+
+# Built-in and composite module parts
+
+%.o: %.c FORCE
+	$(call cmd,force_checksrc)
+	$(call if_changed_rule,cc_o_c)
+
+# Single-part modules are special since we need to mark them in $(MODVERDIR)
+
+$(single-used-m): %.o: %.c FORCE
+	$(call cmd,force_checksrc)
+	$(call if_changed_rule,cc_o_c)
+	@{ echo $(@:.o=.ko); echo $@; } > $(MODVERDIR)/$(@F:.o=.mod)
+
+quiet_cmd_cc_lst_c = MKLST   $@
+      cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && \
+		     $(CONFIG_SHELL) $(srctree)/scripts/makelst $*.o \
+				     System.map $(OBJDUMP) > $@
+
+%.lst: %.c FORCE
+	$(call if_changed_dep,cc_lst_c)
+
+# Compile assembler sources (.S)
+# ---------------------------------------------------------------------------
+
+modkern_aflags := $(AFLAGS_KERNEL)
+
+$(real-objs-m)      : modkern_aflags := $(AFLAGS_MODULE)
+$(real-objs-m:.o=.s): modkern_aflags := $(AFLAGS_MODULE)
+
+quiet_cmd_as_s_S = CPP $(quiet_modtag) $@
+cmd_as_s_S       = $(CPP) $(a_flags)   -o $@ $<
+
+%.s: %.S FORCE
+	$(call if_changed_dep,as_s_S)
+
+quiet_cmd_as_o_S = AS $(quiet_modtag)  $@
+cmd_as_o_S       = $(CC) $(a_flags) -c -o $@ $<
+
+%.o: %.S FORCE
+	$(call if_changed_dep,as_o_S)
+
+targets += $(real-objs-y) $(real-objs-m) $(lib-y)
+targets += $(extra-y) $(MAKECMDGOALS) $(always)
+
+# Linker scripts preprocessor (.lds.S -> .lds)
+# ---------------------------------------------------------------------------
+quiet_cmd_cpp_lds_S = LDS     $@
+      cmd_cpp_lds_S = $(CPP) $(cpp_flags) -D__ASSEMBLY__ -o $@ $<
+
+%.lds: %.lds.S FORCE
+	$(call if_changed_dep,cpp_lds_S)
+
+# Build the compiled-in targets
+# ---------------------------------------------------------------------------
+
+# To build objects in subdirs, we need to descend into the directories
+$(sort $(subdir-obj-y)): $(subdir-ym) ;
+
+#
+# Rule to compile a set of .o files into one .o file
+#
+ifdef builtin-target
+quiet_cmd_link_o_target = LD      $@
+# If the list of objects to link is empty, just create an empty built-in.o
+cmd_link_o_target = $(if $(strip $(obj-y)),\
+		$(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^),\
+		rm -f $@; $(AR) rcs $@)
+
+$(builtin-target): $(obj-y) FORCE
+	$(call if_changed,link_o_target)
+
+targets += $(builtin-target)
+endif # builtin-target
+
+#
+# Rule to compile a set of .o files into one .a file
+#
+ifdef lib-target
+quiet_cmd_link_l_target = AR      $@
+cmd_link_l_target = rm -f $@; $(AR) $(EXTRA_ARFLAGS) rcs $@ $(lib-y)
+
+$(lib-target): $(lib-y) FORCE
+	$(call if_changed,link_l_target)
+
+targets += $(lib-target)
+endif
+
+#
+# Rule to link composite objects
+#
+#  Composite objects are specified in kbuild makefile as follows:
+#    <composite-object>-objs := <list of .o files>
+#  or
+#    <composite-object>-y    := <list of .o files>
+link_multi_deps =                     \
+$(filter $(addprefix $(obj)/,         \
+$($(subst $(obj)/,,$(@:.o=-objs)))    \
+$($(subst $(obj)/,,$(@:.o=-y)))), $^)
+
+quiet_cmd_link_multi-y = LD      $@
+cmd_link_multi-y = $(LD) $(ld_flags) -r -o $@ $(link_multi_deps)
+
+quiet_cmd_link_multi-m = LD [M]  $@
+cmd_link_multi-m = $(LD) $(ld_flags) $(LDFLAGS_MODULE) -o $@ $(link_multi_deps)
+
+# We would rather have a list of rules like
+# 	foo.o: $(foo-objs)
+# but that's not so easy, so we rather make all composite objects depend
+# on the set of all their parts
+$(multi-used-y) : %.o: $(multi-objs-y) FORCE
+	$(call if_changed,link_multi-y)
+
+$(multi-used-m) : %.o: $(multi-objs-m) FORCE
+	$(call if_changed,link_multi-m)
+	@{ echo $(@:.o=.ko); echo $(link_multi_deps); } > $(MODVERDIR)/$(@F:.o=.mod)
+
+targets += $(multi-used-y) $(multi-used-m)
+
+
+# Descending
+# ---------------------------------------------------------------------------
+
+PHONY += $(subdir-ym)
+$(subdir-ym):
+	$(Q)$(MAKE) $(build)=$@
+
+# Add FORCE to the prequisites of a target to force it to be always rebuilt.
+# ---------------------------------------------------------------------------
+
+PHONY += FORCE
+
+FORCE:
+
+# Read all saved command lines and dependencies for the $(targets) we
+# may be building above, using $(if_changed{,_dep}). As an
+# optimization, we don't need to read them if the target does not
+# exist, we will rebuild anyway in that case.
+
+targets := $(wildcard $(sort $(targets)))
+cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd))
+
+ifneq ($(cmd_files),)
+  include $(cmd_files)
+endif
+
+
+# Declare the contents of the .PHONY variable as phony.  We keep that
+# information in a variable se we can use it in if_changed and friends.
+
+.PHONY: $(PHONY)
diff --git a/busybox-1.19.3/scripts/Makefile.clean b/busybox-1.19.3/scripts/Makefile.clean
new file mode 100644
index 0000000..03e397f
--- /dev/null
+++ b/busybox-1.19.3/scripts/Makefile.clean
@@ -0,0 +1,102 @@
+# ==========================================================================
+# Cleaning up
+# ==========================================================================
+
+src := $(obj)
+
+PHONY := __clean
+__clean:
+
+# Shorthand for $(Q)$(MAKE) scripts/Makefile.clean obj=dir
+# Usage:
+# $(Q)$(MAKE) $(clean)=dir
+clean := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.clean obj
+
+# The filename Kbuild has precedence over Makefile
+kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+-include $(if $(wildcard $(kbuild-dir)/Kbuild), $(kbuild-dir)/Kbuild, $(kbuild-dir)/Makefile)
+
+# Figure out what we need to build from the various variables
+# ==========================================================================
+
+__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
+subdir-y	+= $(__subdir-y)
+__subdir-m	:= $(patsubst %/,%,$(filter %/, $(obj-m)))
+subdir-m	+= $(__subdir-m)
+__subdir-n	:= $(patsubst %/,%,$(filter %/, $(obj-n)))
+subdir-n	+= $(__subdir-n)
+__subdir-	:= $(patsubst %/,%,$(filter %/, $(obj-)))
+subdir-		+= $(__subdir-)
+
+# Subdirectories we need to descend into
+
+subdir-ym	:= $(sort $(subdir-y) $(subdir-m))
+subdir-ymn      := $(sort $(subdir-ym) $(subdir-n) $(subdir-))
+
+# Add subdir path
+
+subdir-ymn	:= $(addprefix $(obj)/,$(subdir-ymn))
+
+# build a list of files to remove, usually releative to the current
+# directory
+
+__clean-files	:= $(extra-y) $(EXTRA_TARGETS) $(always) \
+		   $(targets) $(clean-files)             \
+		   $(host-progs)                         \
+		   $(hostprogs-y) $(hostprogs-m) $(hostprogs-)
+
+# as clean-files is given relative to the current directory, this adds
+# a $(obj) prefix, except for absolute paths
+
+__clean-files   := $(wildcard                                               \
+                   $(addprefix $(obj)/, $(filter-out /%, $(__clean-files))) \
+		   $(filter /%, $(__clean-files)))
+
+# as clean-dirs is given relative to the current directory, this adds
+# a $(obj) prefix, except for absolute paths
+
+__clean-dirs    := $(wildcard                                               \
+                   $(addprefix $(obj)/, $(filter-out /%, $(clean-dirs)))    \
+		   $(filter /%, $(clean-dirs)))
+
+# ==========================================================================
+
+quiet_cmd_clean    = CLEAN   $(obj)
+      cmd_clean    = rm -f $(__clean-files)
+quiet_cmd_cleandir = CLEAN   $(__clean-dirs)
+      cmd_cleandir = rm -rf $(__clean-dirs)
+
+
+__clean: $(subdir-ymn)
+ifneq ($(strip $(__clean-files)),)
+	+$(call cmd,clean)
+endif
+ifneq ($(strip $(__clean-dirs)),)
+	+$(call cmd,cleandir)
+endif
+ifneq ($(strip $(clean-rule)),)
+	+$(clean-rule)
+endif
+	@:
+
+
+# ===========================================================================
+# Generic stuff
+# ===========================================================================
+
+# Descending
+# ---------------------------------------------------------------------------
+
+PHONY += $(subdir-ymn)
+$(subdir-ymn):
+	$(Q)$(MAKE) $(clean)=$@
+
+# If quiet is set, only print short version of command
+
+cmd = @$(if $($(quiet)cmd_$(1)),echo '  $($(quiet)cmd_$(1))' &&) $(cmd_$(1))
+
+
+# Declare the contents of the .PHONY variable as phony.  We keep that
+# information in a variable se we can use it in if_changed and friends.
+
+.PHONY: $(PHONY)
diff --git a/busybox-1.19.3/scripts/Makefile.host b/busybox-1.19.3/scripts/Makefile.host
new file mode 100644
index 0000000..2e62850
--- /dev/null
+++ b/busybox-1.19.3/scripts/Makefile.host
@@ -0,0 +1,155 @@
+# ==========================================================================
+# Building binaries on the host system
+# Binaries are used during the compilation of the kernel, for example
+# to preprocess a data file.
+#
+# Both C and C++ is supported, but preferred language is C for such utilities.
+#
+# Samle syntax (see Documentation/kbuild/makefile.txt for reference)
+# hostprogs-y := bin2hex
+# Will compile bin2hex.c and create an executable named bin2hex
+#
+# hostprogs-y    := lxdialog
+# lxdialog-objs := checklist.o lxdialog.o
+# Will compile lxdialog.c and checklist.c, and then link the executable
+# lxdialog, based on checklist.o and lxdialog.o
+#
+# hostprogs-y      := qconf
+# qconf-cxxobjs   := qconf.o
+# qconf-objs      := menu.o
+# Will compile qconf as a C++ program, and menu as a C program.
+# They are linked as C++ code to the executable qconf
+
+# hostprogs-y := conf
+# conf-objs  := conf.o libkconfig.so
+# libkconfig-objs := expr.o type.o
+# Will create a shared library named libkconfig.so that consist of
+# expr.o and type.o (they are both compiled as C code and the object file
+# are made as position independent code).
+# conf.c is compiled as a c program, and conf.o is linked together with
+# libkconfig.so as the executable conf.
+# Note: Shared libraries consisting of C++ files are not supported
+
+__hostprogs := $(sort $(hostprogs-y)$(hostprogs-m))
+
+# hostprogs-y := tools/build may have been specified. Retreive directory
+obj-dirs += $(foreach f,$(__hostprogs), $(if $(dir $(f)),$(dir $(f))))
+obj-dirs := $(strip $(sort $(filter-out ./,$(obj-dirs))))
+
+
+# C code
+# Executables compiled from a single .c file
+host-csingle	:= $(foreach m,$(__hostprogs),$(if $($(m)-objs),,$(m)))
+
+# C executables linked based on several .o files
+host-cmulti	:= $(foreach m,$(__hostprogs),\
+		   $(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m))))
+
+# Object (.o) files compiled from .c files
+host-cobjs	:= $(sort $(foreach m,$(__hostprogs),$($(m)-objs)))
+
+# C++ code
+# C++ executables compiled from at least on .cc file
+# and zero or more .c files
+host-cxxmulti	:= $(foreach m,$(__hostprogs),$(if $($(m)-cxxobjs),$(m)))
+
+# C++ Object (.o) files compiled from .cc files
+host-cxxobjs	:= $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs)))
+
+# Shared libaries (only .c supported)
+# Shared libraries (.so) - all .so files referenced in "xxx-objs"
+host-cshlib	:= $(sort $(filter %.so, $(host-cobjs)))
+# Remove .so files from "xxx-objs"
+host-cobjs	:= $(filter-out %.so,$(host-cobjs))
+
+#Object (.o) files used by the shared libaries
+host-cshobjs	:= $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs))))
+
+__hostprogs     := $(addprefix $(obj)/,$(__hostprogs))
+host-csingle	:= $(addprefix $(obj)/,$(host-csingle))
+host-cmulti	:= $(addprefix $(obj)/,$(host-cmulti))
+host-cobjs	:= $(addprefix $(obj)/,$(host-cobjs))
+host-cxxmulti	:= $(addprefix $(obj)/,$(host-cxxmulti))
+host-cxxobjs	:= $(addprefix $(obj)/,$(host-cxxobjs))
+host-cshlib	:= $(addprefix $(obj)/,$(host-cshlib))
+host-cshobjs	:= $(addprefix $(obj)/,$(host-cshobjs))
+obj-dirs        := $(addprefix $(obj)/,$(obj-dirs))
+
+#####
+# Handle options to gcc. Support building with separate output directory
+
+_hostc_flags   = $(HOSTCFLAGS)   $(HOST_EXTRACFLAGS)   $(HOSTCFLAGS_$(*F).o)
+_hostcxx_flags = $(HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) $(HOSTCXXFLAGS_$(*F).o)
+
+ifeq ($(KBUILD_SRC),)
+__hostc_flags	= $(_hostc_flags)
+__hostcxx_flags	= $(_hostcxx_flags)
+else
+__hostc_flags	= -I$(obj) $(call flags,_hostc_flags)
+__hostcxx_flags	= -I$(obj) $(call flags,_hostcxx_flags)
+endif
+
+hostc_flags    = -Wp,-MD,$(depfile) $(__hostc_flags)
+hostcxx_flags  = -Wp,-MD,$(depfile) $(__hostcxx_flags)
+
+#####
+# Compile programs on the host
+
+# Create executable from a single .c file
+# host-csingle -> Executable
+quiet_cmd_host-csingle 	= HOSTCC  $@
+      cmd_host-csingle	= $(HOSTCC) $(hostc_flags) -o $@ $< \
+			  $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-csingle): %: %.c FORCE
+	$(call if_changed_dep,host-csingle)
+
+# Link an executable based on list of .o files, all plain c
+# host-cmulti -> executable
+quiet_cmd_host-cmulti	= HOSTLD  $@
+      cmd_host-cmulti	= $(HOSTCC) $(HOSTLDFLAGS) -o $@ \
+			  $(addprefix $(obj)/,$($(@F)-objs)) \
+			  $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-cmulti): %: $(host-cobjs) $(host-cshlib) FORCE
+	$(call if_changed,host-cmulti)
+
+# Create .o file from a single .c file
+# host-cobjs -> .o
+quiet_cmd_host-cobjs	= HOSTCC  $@
+      cmd_host-cobjs	= $(HOSTCC) $(hostc_flags) -c -o $@ $<
+$(host-cobjs): %.o: %.c FORCE
+	$(call if_changed_dep,host-cobjs)
+
+# Link an executable based on list of .o files, a mixture of .c and .cc
+# host-cxxmulti -> executable
+quiet_cmd_host-cxxmulti	= HOSTLD  $@
+      cmd_host-cxxmulti	= $(HOSTCXX) $(HOSTLDFLAGS) -o $@ \
+			  $(foreach o,objs cxxobjs,\
+			  $(addprefix $(obj)/,$($(@F)-$(o)))) \
+			  $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-cxxmulti): %: $(host-cobjs) $(host-cxxobjs) $(host-cshlib) FORCE
+	$(call if_changed,host-cxxmulti)
+
+# Create .o file from a single .cc (C++) file
+quiet_cmd_host-cxxobjs	= HOSTCXX $@
+      cmd_host-cxxobjs	= $(HOSTCXX) $(hostcxx_flags) -c -o $@ $<
+$(host-cxxobjs): %.o: %.cc FORCE
+	$(call if_changed_dep,host-cxxobjs)
+
+# Compile .c file, create position independent .o file
+# host-cshobjs -> .o
+quiet_cmd_host-cshobjs	= HOSTCC  -fPIC $@
+      cmd_host-cshobjs	= $(HOSTCC) $(hostc_flags) -fPIC -c -o $@ $<
+$(host-cshobjs): %.o: %.c FORCE
+	$(call if_changed_dep,host-cshobjs)
+
+# Link a shared library, based on position independent .o files
+# *.o -> .so shared library (host-cshlib)
+quiet_cmd_host-cshlib	= HOSTLLD -shared $@
+      cmd_host-cshlib	= $(HOSTCC) $(HOSTLDFLAGS) -shared -o $@ \
+			  $(addprefix $(obj)/,$($(@F:.so=-objs))) \
+			  $(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
+$(host-cshlib): %: $(host-cshobjs) FORCE
+	$(call if_changed,host-cshlib)
+
+targets += $(host-csingle)  $(host-cmulti) $(host-cobjs)\
+	   $(host-cxxmulti) $(host-cxxobjs) $(host-cshlib) $(host-cshobjs)
diff --git a/busybox-1.19.3/scripts/Makefile.lib b/busybox-1.19.3/scripts/Makefile.lib
new file mode 100644
index 0000000..3e54ea7
--- /dev/null
+++ b/busybox-1.19.3/scripts/Makefile.lib
@@ -0,0 +1,170 @@
+# Backward compatibility - to be removed...
+extra-y	+= $(EXTRA_TARGETS)
+# Figure out what we need to build from the various variables
+# ===========================================================================
+
+# When an object is listed to be built compiled-in and modular,
+# only build the compiled-in version
+
+obj-m := $(filter-out $(obj-y),$(obj-m))
+
+# Libraries are always collected in one lib file.
+# Filter out objects already built-in
+
+lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m)))
+
+
+# Handle objects in subdirs
+# ---------------------------------------------------------------------------
+# o if we encounter foo/ in $(obj-y), replace it by foo/built-in.o
+#   and add the directory to the list of dirs to descend into: $(subdir-y)
+# o if we encounter foo/ in $(obj-m), remove it from $(obj-m)
+#   and add the directory to the list of dirs to descend into: $(subdir-m)
+
+__subdir-y	:= $(patsubst %/,%,$(filter %/, $(obj-y)))
+subdir-y	+= $(__subdir-y)
+__subdir-m	:= $(patsubst %/,%,$(filter %/, $(obj-m)))
+subdir-m	+= $(__subdir-m)
+obj-y		:= $(patsubst %/, %/built-in.o, $(obj-y))
+obj-m		:= $(filter-out %/, $(obj-m))
+
+# Subdirectories we need to descend into
+
+subdir-ym	:= $(sort $(subdir-y) $(subdir-m))
+
+# if $(foo-objs) exists, foo.o is a composite object
+multi-used-y := $(sort $(foreach m,$(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
+multi-used-m := $(sort $(foreach m,$(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m))))
+multi-used   := $(multi-used-y) $(multi-used-m)
+single-used-m := $(sort $(filter-out $(multi-used-m),$(obj-m)))
+
+# Build list of the parts of our composite objects, our composite
+# objects depend on those (obviously)
+multi-objs-y := $(foreach m, $(multi-used-y), $($(m:.o=-objs)) $($(m:.o=-y)))
+multi-objs-m := $(foreach m, $(multi-used-m), $($(m:.o=-objs)) $($(m:.o=-y)))
+multi-objs   := $(multi-objs-y) $(multi-objs-m)
+
+# $(subdir-obj-y) is the list of objects in $(obj-y) which do not live
+# in the local directory
+subdir-obj-y := $(foreach o,$(obj-y),$(if $(filter-out $(o),$(notdir $(o))),$(o)))
+
+# $(obj-dirs) is a list of directories that contain object files
+obj-dirs := $(dir $(multi-objs) $(subdir-obj-y))
+
+# Replace multi-part objects by their individual parts, look at local dir only
+real-objs-y := $(foreach m, $(filter-out $(subdir-obj-y), $(obj-y)), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) $(extra-y)
+real-objs-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))
+
+# Add subdir path
+
+extra-y		:= $(addprefix $(obj)/,$(extra-y))
+always		:= $(addprefix $(obj)/,$(always))
+targets		:= $(addprefix $(obj)/,$(targets))
+obj-y		:= $(addprefix $(obj)/,$(obj-y))
+obj-m		:= $(addprefix $(obj)/,$(obj-m))
+lib-y		:= $(addprefix $(obj)/,$(lib-y))
+subdir-obj-y	:= $(addprefix $(obj)/,$(subdir-obj-y))
+real-objs-y	:= $(addprefix $(obj)/,$(real-objs-y))
+real-objs-m	:= $(addprefix $(obj)/,$(real-objs-m))
+single-used-m	:= $(addprefix $(obj)/,$(single-used-m))
+multi-used-y	:= $(addprefix $(obj)/,$(multi-used-y))
+multi-used-m	:= $(addprefix $(obj)/,$(multi-used-m))
+multi-objs-y	:= $(addprefix $(obj)/,$(multi-objs-y))
+multi-objs-m	:= $(addprefix $(obj)/,$(multi-objs-m))
+subdir-ym	:= $(addprefix $(obj)/,$(subdir-ym))
+obj-dirs	:= $(addprefix $(obj)/,$(obj-dirs))
+
+# These flags are needed for modversions and compiling, so we define them here
+# already
+# $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will
+# end up in (or would, if it gets compiled in)
+# Note: It's possible that one object gets potentially linked into more
+#       than one module. In that case KBUILD_MODNAME will be set to foo_bar,
+#       where foo and bar are the name of the modules.
+name-fix = $(subst $(comma),_,$(subst -,_,$1))
+basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(*F)))"
+modname_flags  = $(if $(filter 1,$(words $(modname))),\
+                 -D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))")
+
+_c_flags       = $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F).o)
+_a_flags       = $(AFLAGS) $(EXTRA_AFLAGS) $(AFLAGS_$(*F).o)
+_cpp_flags     = $(CPPFLAGS) $(EXTRA_CPPFLAGS) $(CPPFLAGS_$(@F))
+
+# If building the kernel in a separate objtree expand all occurrences
+# of -Idir to -I$(srctree)/dir except for absolute paths (starting with '/').
+
+ifeq ($(KBUILD_SRC),)
+__c_flags	= $(_c_flags)
+__a_flags	= $(_a_flags)
+__cpp_flags     = $(_cpp_flags)
+else
+
+# -I$(obj) locates generated .h files
+# $(call addtree,-I$(obj)) locates .h files in srctree, from generated .c files
+#   and locates generated .h files
+# FIXME: Replace both with specific CFLAGS* statements in the makefiles
+__c_flags	= $(call addtree,-I$(obj)) $(call flags,_c_flags)
+__a_flags	=                          $(call flags,_a_flags)
+__cpp_flags     =                          $(call flags,_cpp_flags)
+endif
+
+c_flags        = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \
+		 $(__c_flags) $(modkern_cflags) \
+		 -D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags)
+
+a_flags        = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(CPPFLAGS) \
+		 $(__a_flags) $(modkern_aflags)
+
+cpp_flags      = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(__cpp_flags)
+
+# Seems to be a wrong thing to do. LDFLAGS contains gcc's flags,
+# yet ld_flags is fed to ld.
+#ld_flags       = $(LDFLAGS) $(EXTRA_LDFLAGS)
+# Remove the -Wl, prefix from linker options normally passed through gcc
+ld_flags       = $(filter-out -Wl$(comma)%,$(LDFLAGS) $(EXTRA_LDFLAGS))
+
+
+# Finds the multi-part object the current object will be linked into
+modname-multi = $(sort $(foreach m,$(multi-used),\
+		$(if $(filter $(subst $(obj)/,,$*.o), $($(m:.o=-objs)) $($(m:.o=-y))),$(m:.o=))))
+
+# Shipped files
+# ===========================================================================
+
+quiet_cmd_shipped = SHIPPED $@
+cmd_shipped = cat $< > $@
+
+$(obj)/%:: $(src)/%_shipped
+	$(call cmd,shipped)
+
+# Commands useful for building a boot image
+# ===========================================================================
+#
+#	Use as following:
+#
+#	target: source(s) FORCE
+#		$(if_changed,ld/objcopy/gzip)
+#
+#	and add target to EXTRA_TARGETS so that we know we have to
+#	read in the saved command line
+
+# Linking
+# ---------------------------------------------------------------------------
+
+# TODO: LDFLAGS usually is supposed to contain gcc's flags, not ld's.
+# but here we feed them to ld!
+quiet_cmd_ld = LD      $@
+cmd_ld = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$(@F)) \
+	       $(filter-out FORCE,$^) -o $@
+
+# Objcopy
+# ---------------------------------------------------------------------------
+
+quiet_cmd_objcopy = OBJCOPY $@
+cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
+
+# Gzip
+# ---------------------------------------------------------------------------
+
+quiet_cmd_gzip = GZIP    $@
+cmd_gzip = gzip -f -9 < $< > $@
diff --git a/busybox-1.19.3/scripts/basic/Makefile b/busybox-1.19.3/scripts/basic/Makefile
new file mode 100644
index 0000000..119f079
--- /dev/null
+++ b/busybox-1.19.3/scripts/basic/Makefile
@@ -0,0 +1,18 @@
+###
+# Makefile.basic list the most basic programs used during the build process.
+# The programs listed herein is what is needed to do the basic stuff,
+# such as splitting .config and fix dependency file.
+# This initial step is needed to avoid files to be recompiled
+# when busybox configuration changes (which is what happens when
+# .config is included by main Makefile.
+# ---------------------------------------------------------------------------
+# fixdep: 	 Used to generate dependency information during build process
+# split-include: Divide all config symbols up in a number of files in
+#                include/config/...
+# docproc:	 Used in Documentation/docbook
+
+hostprogs-y	:= fixdep split-include docproc
+always		:= $(hostprogs-y)
+
+# fixdep is needed to compile other host programs
+$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep
diff --git a/busybox-1.19.3/scripts/basic/docproc.c b/busybox-1.19.3/scripts/basic/docproc.c
new file mode 100644
index 0000000..0984e7d
--- /dev/null
+++ b/busybox-1.19.3/scripts/basic/docproc.c
@@ -0,0 +1,399 @@
+/*
+ *	docproc is a simple preprocessor for the template files
+ *      used as placeholders for the kernel internal documentation.
+ *	docproc is used for documentation-frontend and
+ *      dependency-generator.
+ *	The two usages have in common that they require
+ *	some knowledge of the .tmpl syntax, therefore they
+ *	are kept together.
+ *
+ *	documentation-frontend
+ *		Scans the template file and call kernel-doc for
+ *		all occurrences of ![EIF]file
+ *		Beforehand each referenced file are scanned for
+ *		any exported sympols "EXPORT_SYMBOL()" statements.
+ *		This is used to create proper -function and
+ *		-nofunction arguments in calls to kernel-doc.
+ *		Usage: docproc doc file.tmpl
+ *
+ *	dependency-generator:
+ *		Scans the template file and list all files
+ *		referenced in a format recognized by make.
+ *		Usage:	docproc depend file.tmpl
+ *		Writes dependency information to stdout
+ *		in the following format:
+ *		file.tmpl src.c	src2.c
+ *		The filenames are obtained from the following constructs:
+ *		!Efilename
+ *		!Ifilename
+ *		!Dfilename
+ *		!Ffilename
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* exitstatus is used to keep track of any failing calls to kernel-doc,
+ * but execution continues. */
+int exitstatus = 0;
+
+typedef void DFL(char *);
+DFL *defaultline;
+
+typedef void FILEONLY(char * file);
+FILEONLY *internalfunctions;
+FILEONLY *externalfunctions;
+FILEONLY *symbolsonly;
+
+typedef void FILELINE(char * file, char * line);
+FILELINE * singlefunctions;
+FILELINE * entity_system;
+
+#define MAXLINESZ     2048
+#define MAXFILES      250
+#define KERNELDOCPATH "scripts/"
+#define KERNELDOC     "kernel-doc"
+#define DOCBOOK       "-docbook"
+#define FUNCTION      "-function"
+#define NOFUNCTION    "-nofunction"
+
+void usage (void)
+{
+	fprintf(stderr, "Usage: docproc {doc|depend} file\n");
+	fprintf(stderr, "Input is read from file.tmpl. Output is sent to stdout\n");
+	fprintf(stderr, "doc: frontend when generating kernel documentation\n");
+	fprintf(stderr, "depend: generate list of files referenced within file\n");
+}
+
+/*
+ * Execute kernel-doc with parameters givin in svec
+ */
+void exec_kernel_doc(char **svec)
+{
+	pid_t pid;
+	int ret;
+	char *real_filename;
+	int rflen;
+
+	/* Make sure output generated so far are flushed */
+	fflush(stdout);
+	switch(pid=fork()) {
+		case -1:
+			perror("vfork"+1);
+			exit(1);
+		case  0:
+			rflen  = strlen(getenv("SRCTREE"));
+			rflen += strlen(KERNELDOCPATH KERNELDOC);
+			real_filename = alloca(rflen + 1);
+			strcpy(real_filename, getenv("SRCTREE"));
+			strcat(real_filename, KERNELDOCPATH KERNELDOC);
+			execvp(real_filename, svec);
+			fprintf(stderr, "exec ");
+			perror(real_filename);
+			exit(1);
+		default:
+			waitpid(pid, &ret ,0);
+	}
+	if (WIFEXITED(ret))
+		exitstatus |= WEXITSTATUS(ret);
+	else
+		exitstatus = 0xff;
+}
+
+/* Types used to create list of all exported symbols in a number of files */
+struct symbols
+{
+	char *name;
+};
+
+struct symfile
+{
+	char *filename;
+	struct symbols *symbollist;
+	int symbolcnt;
+};
+
+struct symfile symfilelist[MAXFILES];
+int symfilecnt = 0;
+
+void add_new_symbol(struct symfile *sym, char * symname)
+{
+	sym->symbollist =
+	  realloc(sym->symbollist, (sym->symbolcnt + 1) * sizeof(char *));
+	sym->symbollist[sym->symbolcnt++].name = strdup(symname);
+}
+
+/* Add a filename to the list */
+struct symfile * add_new_file(char * filename)
+{
+	symfilelist[symfilecnt++].filename = strdup(filename);
+	return &symfilelist[symfilecnt - 1];
+}
+/* Check if file already are present in the list */
+struct symfile * filename_exist(char * filename)
+{
+	int i;
+	for (i=0; i < symfilecnt; i++)
+		if (strcmp(symfilelist[i].filename, filename) == 0)
+			return &symfilelist[i];
+	return NULL;
+}
+
+/*
+ * List all files referenced within the template file.
+ * Files are separated by tabs.
+ */
+void adddep(char * file)		   { printf("\t%s", file); }
+void adddep2(char * file, char * line)     { line = line; adddep(file); }
+void noaction(char * line)		   { line = line; }
+void noaction2(char * file, char * line)   { file = file; line = line; }
+
+/* Echo the line without further action */
+void printline(char * line)               { printf("%s", line); }
+
+/*
+ * Find all symbols exported with EXPORT_SYMBOL and EXPORT_SYMBOL_GPL
+ * in filename.
+ * All symbols located are stored in symfilelist.
+ */
+void find_export_symbols(char * filename)
+{
+	FILE * fp;
+	struct symfile *sym;
+	char line[MAXLINESZ];
+	if (filename_exist(filename) == NULL) {
+		int rflen = strlen(getenv("SRCTREE")) + strlen(filename);
+		char *real_filename = alloca(rflen + 1);
+		strcpy(real_filename, getenv("SRCTREE"));
+		strcat(real_filename, filename);
+		sym = add_new_file(filename);
+		fp = fopen(real_filename, "r");
+		if (fp == NULL)
+		{
+			fprintf(stderr, "docproc: ");
+			perror(real_filename);
+		}
+		while (fgets(line, MAXLINESZ, fp)) {
+			char *p;
+			char *e;
+			if (((p = strstr(line, "EXPORT_SYMBOL_GPL")) != 0) ||
+			    ((p = strstr(line, "EXPORT_SYMBOL")) != 0)) {
+				/* Skip EXPORT_SYMBOL{_GPL} */
+				while (isalnum(*p) || *p == '_')
+					p++;
+				/* Remove paranteses and additional ws */
+				while (isspace(*p))
+					p++;
+				if (*p != '(')
+					continue; /* Syntax error? */
+				else
+					p++;
+				while (isspace(*p))
+					p++;
+				e = p;
+				while (isalnum(*e) || *e == '_')
+					e++;
+				*e = '\0';
+				add_new_symbol(sym, p);
+			}
+		}
+		fclose(fp);
+	}
+}
+
+/*
+ * 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 src files
+ * by find_export_symbols.
+ * intfunc uses -nofunction
+ * extfunc uses -function
+ */
+void docfunctions(char * filename, char * type)
+{
+	int i,j;
+	int symcnt = 0;
+	int idx = 0;
+	char **vec;
+
+	for (i=0; i <= symfilecnt; i++)
+		symcnt += symfilelist[i].symbolcnt;
+	vec = malloc((2 + 2 * symcnt + 2) * sizeof(char*));
+	if (vec == NULL) {
+		perror("docproc: ");
+		exit(1);
+	}
+	vec[idx++] = KERNELDOC;
+	vec[idx++] = DOCBOOK;
+	for (i=0; i < symfilecnt; i++) {
+		struct symfile * sym = &symfilelist[i];
+		for (j=0; j < sym->symbolcnt; j++) {
+			vec[idx++]     = type;
+			vec[idx++] = sym->symbollist[j].name;
+		}
+	}
+	vec[idx++]     = filename;
+	vec[idx] = NULL;
+	printf("<!-- %s -->\n", filename);
+	exec_kernel_doc(vec);
+	fflush(stdout);
+	free(vec);
+}
+void intfunc(char * filename) {	docfunctions(filename, NOFUNCTION); }
+void extfunc(char * filename) { docfunctions(filename, FUNCTION);   }
+
+/*
+ * Document specific function(s) in a file.
+ * Call kernel-doc with the following parameters:
+ * kernel-doc -docbook -function function1 [-function function2]
+ */
+void singfunc(char * filename, char * line)
+{
+	char *vec[200]; /* Enough for specific functions */
+	int i, idx = 0;
+	int startofsym = 1;
+	vec[idx++] = KERNELDOC;
+	vec[idx++] = DOCBOOK;
+
+	/* Split line up in individual parameters preceeded by FUNCTION */
+	for (i=0; line[i]; i++) {
+		if (isspace(line[i])) {
+			line[i] = '\0';
+			startofsym = 1;
+			continue;
+		}
+		if (startofsym) {
+			startofsym = 0;
+			vec[idx++] = FUNCTION;
+			vec[idx++] = &line[i];
+		}
+	}
+	vec[idx++] = filename;
+	vec[idx] = NULL;
+	exec_kernel_doc(vec);
+}
+
+/*
+ * Parse file, calling action specific functions for:
+ * 1) Lines containing !E
+ * 2) Lines containing !I
+ * 3) Lines containing !D
+ * 4) Lines containing !F
+ * 5) Default lines - lines not matching the above
+ */
+void parse_file(FILE *infile)
+{
+	char line[MAXLINESZ];
+	char * s;
+	while (fgets(line, MAXLINESZ, infile)) {
+		if (line[0] == '!') {
+			s = line + 2;
+			switch (line[1]) {
+				case 'E':
+					while (*s && !isspace(*s)) s++;
+					*s = '\0';
+					externalfunctions(line+2);
+					break;
+				case 'I':
+					while (*s && !isspace(*s)) s++;
+					*s = '\0';
+					internalfunctions(line+2);
+					break;
+				case 'D':
+					while (*s && !isspace(*s)) s++;
+					*s = '\0';
+					symbolsonly(line+2);
+					break;
+				case 'F':
+					/* filename */
+					while (*s && !isspace(*s)) s++;
+					*s++ = '\0';
+					/* function names */
+					while (isspace(*s))
+						s++;
+					singlefunctions(line +2, s);
+					break;
+				default:
+					defaultline(line);
+			}
+		}
+		else {
+			defaultline(line);
+		}
+	}
+	fflush(stdout);
+}
+
+
+int main(int argc, char **argv)
+{
+	FILE * infile;
+	if (argc != 3) {
+		usage();
+		exit(1);
+	}
+	/* Open file, exit on error */
+	infile = fopen(argv[2], "r");
+	if (infile == NULL) {
+		fprintf(stderr, "docproc: ");
+		perror(argv[2]);
+		exit(2);
+	}
+
+	if (strcmp("doc", argv[1]) == 0)
+	{
+		/* Need to do this in two passes.
+		 * First pass is used to collect all symbols exported
+		 * in the various files.
+		 * Second pass generate the documentation.
+		 * This is required because function are declared
+		 * and exported in different files :-((
+		 */
+		/* Collect symbols */
+		defaultline       = noaction;
+		internalfunctions = find_export_symbols;
+		externalfunctions = find_export_symbols;
+		symbolsonly       = find_export_symbols;
+		singlefunctions   = noaction2;
+		parse_file(infile);
+
+		/* Rewind to start from beginning of file again */
+		fseek(infile, 0, SEEK_SET);
+		defaultline       = printline;
+		internalfunctions = intfunc;
+		externalfunctions = extfunc;
+		symbolsonly       = printline;
+		singlefunctions   = singfunc;
+
+		parse_file(infile);
+	}
+	else if (strcmp("depend", argv[1]) == 0)
+	{
+		/* Create first part of dependency chain
+		 * file.tmpl */
+		printf("%s\t", argv[2]);
+		defaultline       = noaction;
+		internalfunctions = adddep;
+		externalfunctions = adddep;
+		symbolsonly       = adddep;
+		singlefunctions   = adddep2;
+		parse_file(infile);
+		printf("\n");
+	}
+	else
+	{
+		fprintf(stderr, "Unknown option: %s\n", argv[1]);
+		exit(1);
+	}
+	fclose(infile);
+	fflush(stdout);
+	return exitstatus;
+}
diff --git a/busybox-1.19.3/scripts/basic/fixdep.c b/busybox-1.19.3/scripts/basic/fixdep.c
new file mode 100644
index 0000000..f27a179
--- /dev/null
+++ b/busybox-1.19.3/scripts/basic/fixdep.c
@@ -0,0 +1,413 @@
+/*
+ * "Optimize" a list of dependencies as spit out by gcc -MD
+ * for the kernel build
+ * ===========================================================================
+ *
+ * Author       Kai Germaschewski
+ * Copyright    2002 by Kai Germaschewski  <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ *
+ * Introduction:
+ *
+ * gcc produces a very nice and correct list of dependencies which
+ * tells make when to remake a file.
+ *
+ * To use this list as-is however has the drawback that virtually
+ * every file in the kernel includes <linux/config.h> which then again
+ * includes <linux/autoconf.h>
+ *
+ * If the user re-runs make *config, linux/autoconf.h will be
+ * regenerated.  make notices that and will rebuild every file which
+ * includes autoconf.h, i.e. basically all files. This is extremely
+ * annoying if the user just changed CONFIG_HIS_DRIVER from n to m.
+ *
+ * So we play the same trick that "mkdep" played before. We replace
+ * the dependency on linux/autoconf.h by a dependency on every config
+ * option which is mentioned in any of the listed prequisites.
+ *
+ * To be exact, split-include populates a tree in include/config/,
+ * e.g. include/config/his/driver.h, which contains the #define/#undef
+ * for the CONFIG_HIS_DRIVER option.
+ *
+ * So if the user changes his CONFIG_HIS_DRIVER option, only the objects
+ * which depend on "include/linux/config/his/driver.h" will be rebuilt,
+ * so most likely only his driver ;-)
+ *
+ * The idea above dates, by the way, back to Michael E Chastain, AFAIK.
+ *
+ * So to get dependencies right, there are two issues:
+ * o if any of the files the compiler read changed, we need to rebuild
+ * o if the command line given to the compile the file changed, we
+ *   better rebuild as well.
+ *
+ * The former is handled by using the -MD output, the later by saving
+ * the command line used to compile the old object and comparing it
+ * to the one we would now use.
+ *
+ * Again, also this idea is pretty old and has been discussed on
+ * kbuild-devel a long time ago. I don't have a sensibly working
+ * internet connection right now, so I rather don't mention names
+ * without double checking.
+ *
+ * This code here has been based partially based on mkdep.c, which
+ * says the following about its history:
+ *
+ *   Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
+ *   This is a C version of syncdep.pl by Werner Almesberger.
+ *
+ *
+ * It is invoked as
+ *
+ *   fixdep <depfile> <target> <cmdline>
+ *
+ * and will read the dependency file <depfile>
+ *
+ * The transformed dependency snipped is written to stdout.
+ *
+ * It first generates a line
+ *
+ *   cmd_<target> = <cmdline>
+ *
+ * and then basically copies the .<target>.d file to stdout, in the
+ * process filtering out the dependency on linux/autoconf.h and adding
+ * dependencies on include/config/my/option.h for every
+ * CONFIG_MY_OPTION encountered in any of the prequisites.
+ *
+ * It will also filter out all the dependencies on *.ver. We need
+ * to make sure that the generated version checksum are globally up
+ * to date before even starting the recursive build, so it's too late
+ * at this point anyway.
+ *
+ * The algorithm to grep for "CONFIG_..." is bit unusual, but should
+ * be fast ;-) We don't even try to really parse the header files, but
+ * merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will
+ * be picked up as well. It's not a problem with respect to
+ * correctness, since that can only give too many dependencies, thus
+ * we cannot miss a rebuild. Since people tend to not mention totally
+ * unrelated CONFIG_ options all over the place, it's not an
+ * efficiency problem either.
+ *
+ * (Note: it'd be easy to port over the complete mkdep state machine,
+ *  but I don't think the added complexity is worth it)
+ */
+/*
+ * Note 2: if somebody writes HELLO_CONFIG_BOOM in a file, it will depend onto
+ * CONFIG_BOOM. This could seem a bug (not too hard to fix), but please do not
+ * fix it! Some UserModeLinux files (look at arch/um/) call CONFIG_BOOM as
+ * UML_CONFIG_BOOM, to avoid conflicts with /usr/include/linux/autoconf.h,
+ * through arch/um/include/uml-config.h; this fixdep "bug" makes sure that
+ * those files will have correct dependencies.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+
+/* bbox: not needed
+#define INT_CONF ntohl(0x434f4e46)
+#define INT_ONFI ntohl(0x4f4e4649)
+#define INT_NFIG ntohl(0x4e464947)
+#define INT_FIG_ ntohl(0x4649475f)
+*/
+
+char *target;
+char *depfile;
+char *cmdline;
+
+void usage(void)
+
+{
+	fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
+	exit(1);
+}
+
+/*
+ * Print out the commandline prefixed with cmd_<target filename> :=
+ */
+void print_cmdline(void)
+{
+	printf("cmd_%s := %s\n\n", target, cmdline);
+}
+
+char * str_config  = NULL;
+int    size_config = 0;
+int    len_config  = 0;
+
+/*
+ * Grow the configuration string to a desired length.
+ * Usually the first growth is plenty.
+ */
+void grow_config(int len)
+{
+	while (len_config + len > size_config) {
+		if (size_config == 0)
+			size_config = 2048;
+		str_config = realloc(str_config, size_config *= 2);
+		if (str_config == NULL)
+			{ perror("fixdep:malloc"); exit(1); }
+	}
+}
+
+
+
+/*
+ * Lookup a value in the configuration string.
+ */
+int is_defined_config(const char * name, int len)
+{
+	const char * pconfig;
+	const char * plast = str_config + len_config - len;
+	for ( pconfig = str_config + 1; pconfig < plast; pconfig++ ) {
+		if (pconfig[ -1] == '\n'
+		&&  pconfig[len] == '\n'
+		&&  !memcmp(pconfig, name, len))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Add a new value to the configuration string.
+ */
+void define_config(const char * name, int len)
+{
+	grow_config(len + 1);
+
+	memcpy(str_config+len_config, name, len);
+	len_config += len;
+	str_config[len_config++] = '\n';
+}
+
+/*
+ * Clear the set of configuration strings.
+ */
+void clear_config(void)
+{
+	len_config = 0;
+	define_config("", 0);
+}
+
+/*
+ * Record the use of a CONFIG_* word.
+ */
+void use_config(char *m, int slen)
+{
+	char *s = alloca(slen+1);
+	char *p;
+
+	if (is_defined_config(m, slen))
+	    return;
+
+	define_config(m, slen);
+
+	memcpy(s, m, slen); s[slen] = 0;
+
+	for (p = s; p < s + slen; p++) {
+		if (*p == '_')
+			*p = '/';
+		else
+			*p = tolower((int)*p);
+	}
+	printf("    $(wildcard include/config/%s.h) \\\n", s);
+}
+
+void parse_config_file(char *map, size_t len)
+{
+	/* modified for bbox */
+	char *end_3 = map + len - 3; /* 3 == length of "IF_" */
+	char *end_7 = map + len - 7;
+	char *p = map;
+	char *q;
+	int off;
+
+	for (; p <= end_3; p++) {
+		/* Find next identifier's beginning */
+		if (!(isalnum(*p) || *p == '_'))
+			continue;
+
+		/* Check it */
+		if (p < end_7 && p[6] == '_') {
+			if (!memcmp(p, "CONFIG", 6)) goto conf7;
+			if (!memcmp(p, "ENABLE", 6)) goto conf7;
+			if (!memcmp(p, "IF_NOT", 6)) goto conf7;
+		}
+		/* we have at least 3 chars because of p <= end_3 */
+		/*if (!memcmp(p, "IF_", 3)) ...*/
+		if (p[0] == 'I' && p[1] == 'F' && p[2] == '_') {
+			off = 3;
+			goto conf;
+		}
+
+		/* This identifier is not interesting, skip it */
+		while (p <= end_3 && (isalnum(*p) || *p == '_'))
+			p++;
+		continue;
+
+	conf7:	off = 7;
+	conf:
+		p += off;
+		for (q = p; q < end_3+3; q++) {
+			if (!(isalnum(*q) || *q == '_'))
+				break;
+		}
+		if (q != p) {
+			use_config(p, q-p);
+		}
+	}
+}
+
+/* test is s ends in sub */
+int strrcmp(char *s, char *sub)
+{
+	int slen = strlen(s);
+	int sublen = strlen(sub);
+
+	if (sublen > slen)
+		return 1;
+
+	return memcmp(s + slen - sublen, sub, sublen);
+}
+
+void do_config_file(char *filename)
+{
+	struct stat st;
+	int fd;
+	void *map;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "fixdep: ");
+		perror(filename);
+		exit(2);
+	}
+	fstat(fd, &st);
+	if (st.st_size == 0) {
+		close(fd);
+		return;
+	}
+	map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if ((long) map == -1) {
+		perror("fixdep: mmap");
+		close(fd);
+		return;
+	}
+
+	parse_config_file(map, st.st_size);
+
+	munmap(map, st.st_size);
+
+	close(fd);
+}
+
+void parse_dep_file(void *map, size_t len)
+{
+	char *m = map;
+	char *end = m + len;
+	char *p;
+	char *s = alloca(len);
+
+	p = memchr(m, ':', len);
+	if (!p) {
+		fprintf(stderr, "fixdep: parse error\n");
+		exit(1);
+	}
+	memcpy(s, m, p-m); s[p-m] = 0;
+	printf("deps_%s := \\\n", target);
+	m = p+1;
+
+	clear_config();
+
+	while (m < end) {
+		while (m < end && (*m == ' ' || *m == '\\' || *m == '\n' || *m == '\r'))
+			m++;
+		p = m;
+		while (p < end && *p != ' ') p++;
+		if (p == end) {
+			do p--; while (!isalnum(*p));
+			p++;
+		}
+		memcpy(s, m, p-m); s[p-m] = 0;
+		if (strrcmp(s, "include/autoconf.h") &&
+		    strrcmp(s, "arch/um/include/uml-config.h") &&
+		    strrcmp(s, ".ver")) {
+			printf("  %s \\\n", s);
+			do_config_file(s);
+		}
+		m = p + 1;
+	}
+	printf("\n%s: $(deps_%s)\n\n", target, target);
+	printf("$(deps_%s):\n", target);
+}
+
+void print_deps(void)
+{
+	struct stat st;
+	int fd;
+	void *map;
+
+	fd = open(depfile, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "fixdep: ");
+		perror(depfile);
+		exit(2);
+	}
+	fstat(fd, &st);
+	if (st.st_size == 0) {
+		fprintf(stderr,"fixdep: %s is empty\n",depfile);
+		close(fd);
+		return;
+	}
+	map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if ((long) map == -1) {
+		perror("fixdep: mmap");
+		close(fd);
+		return;
+	}
+
+	parse_dep_file(map, st.st_size);
+
+	munmap(map, st.st_size);
+
+	close(fd);
+}
+
+void traps(void)
+{
+/* bbox: not needed
+	static char test[] __attribute__((aligned(sizeof(int)))) = "CONF";
+
+	if (*(int *)test != INT_CONF) {
+		fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianess? %#x\n",
+			*(int *)test);
+		exit(2);
+	}
+*/
+}
+
+int main(int argc, char **argv)
+{
+	traps();
+
+	if (argc != 4)
+		usage();
+
+	depfile = argv[1];
+	target = argv[2];
+	cmdline = argv[3];
+
+	print_cmdline();
+	print_deps();
+
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/basic/split-include.c b/busybox-1.19.3/scripts/basic/split-include.c
new file mode 100644
index 0000000..e328788
--- /dev/null
+++ b/busybox-1.19.3/scripts/basic/split-include.c
@@ -0,0 +1,227 @@
+/*
+ * split-include.c
+ *
+ * Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
+ * This is a C version of syncdep.pl by Werner Almesberger.
+ *
+ * This program takes autoconf.h as input and outputs a directory full
+ * of one-line include files, merging onto the old values.
+ *
+ * Think of the configuration options as key-value pairs.  Then there
+ * are five cases:
+ *
+ *    key      old value   new value   action
+ *
+ *    KEY-1    VALUE-1     VALUE-1     leave file alone
+ *    KEY-2    VALUE-2A    VALUE-2B    write VALUE-2B into file
+ *    KEY-3    -           VALUE-3     write VALUE-3  into file
+ *    KEY-4    VALUE-4     -           write an empty file
+ *    KEY-5    (empty)     -           leave old empty file alone
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define ERROR_EXIT(strExit)						\
+    {									\
+	const int errnoSave = errno;					\
+	fprintf(stderr, "%s: ", str_my_name);				\
+	errno = errnoSave;						\
+	perror((strExit));						\
+	exit(1);							\
+    }
+
+
+
+int main(int argc, const char * argv [])
+{
+    const char * str_my_name;
+    const char * str_file_autoconf;
+    const char * str_dir_config;
+
+    FILE * fp_config;
+    FILE * fp_target;
+    FILE * fp_find;
+
+    int buffer_size;
+
+    char * line;
+    char * old_line;
+    char * list_target;
+    char * ptarget;
+
+    struct stat stat_buf;
+
+    /* Check arg count. */
+    if (argc != 3)
+    {
+	fprintf(stderr, "%s: wrong number of arguments.\n", argv[0]);
+	exit(1);
+    }
+
+    str_my_name       = argv[0];
+    str_file_autoconf = argv[1];
+    str_dir_config    = argv[2];
+
+    /* Find a buffer size. */
+    if (stat(str_file_autoconf, &stat_buf) != 0)
+	ERROR_EXIT(str_file_autoconf);
+    buffer_size = 2 * stat_buf.st_size + 4096;
+
+    /* Allocate buffers. */
+    if ( (line        = malloc(buffer_size)) == NULL
+    ||   (old_line    = malloc(buffer_size)) == NULL
+    ||   (list_target = malloc(buffer_size)) == NULL )
+	ERROR_EXIT(str_file_autoconf);
+
+    /* Open autoconfig file. */
+    if ((fp_config = fopen(str_file_autoconf, "r")) == NULL)
+	ERROR_EXIT(str_file_autoconf);
+
+    /* Make output directory if needed. */
+    if (stat(str_dir_config, &stat_buf) != 0)
+    {
+	if (mkdir(str_dir_config, 0755) != 0)
+	    ERROR_EXIT(str_dir_config);
+    }
+
+    /* Change to output directory. */
+    if (chdir(str_dir_config) != 0)
+	ERROR_EXIT(str_dir_config);
+
+    /* Put initial separator into target list. */
+    ptarget = list_target;
+    *ptarget++ = '\n';
+
+    /* Read config lines. */
+    while (fgets(line, buffer_size, fp_config))
+    {
+	const char * str_config;
+	int is_same;
+	int itarget;
+
+	if (line[0] != '#')
+	    continue;
+	if ((str_config = strstr(line, " CONFIG_")) == NULL)
+	    continue;
+
+	/* We found #define CONFIG_foo or #undef CONFIG_foo.
+	 * Make the output file name. */
+	str_config += sizeof(" CONFIG_") - 1;
+	for (itarget = 0; !isspace(str_config[itarget]); itarget++)
+	{
+	    int c = (unsigned char) str_config[itarget];
+	    if (isupper(c)) c = tolower(c);
+	    if (c == '_')   c = '/';
+	    ptarget[itarget] = c;
+	}
+	ptarget[itarget++] = '.';
+	ptarget[itarget++] = 'h';
+	ptarget[itarget++] = '\0';
+
+	/* Check for existing file. */
+	is_same = 0;
+	if ((fp_target = fopen(ptarget, "r")) != NULL)
+	{
+	    fgets(old_line, buffer_size, fp_target);
+	    if (fclose(fp_target) != 0)
+		ERROR_EXIT(ptarget);
+	    if (!strcmp(line, old_line))
+		is_same = 1;
+	}
+
+	if (!is_same)
+	{
+	    /* Auto-create directories. */
+	    int islash;
+	    for (islash = 0; islash < itarget; islash++)
+	    {
+		if (ptarget[islash] == '/')
+		{
+		    ptarget[islash] = '\0';
+		    if (stat(ptarget, &stat_buf) != 0
+		    &&  mkdir(ptarget, 0755)     != 0)
+			ERROR_EXIT( ptarget );
+		    ptarget[islash] = '/';
+		}
+	    }
+
+	    /* Write the file. */
+	    if ((fp_target = fopen(ptarget,  "w")) == NULL)
+		ERROR_EXIT(ptarget);
+	    fputs(line, fp_target);
+	    if (ferror(fp_target) || fclose(fp_target) != 0)
+		ERROR_EXIT(ptarget);
+	}
+
+	/* Update target list */
+	ptarget += itarget;
+	*(ptarget-1) = '\n';
+    }
+
+    /*
+     * Close autoconfig file.
+     * Terminate the target list.
+     */
+    if (fclose(fp_config) != 0)
+	ERROR_EXIT(str_file_autoconf);
+    *ptarget = '\0';
+
+    /*
+     * Fix up existing files which have no new value.
+     * This is Case 4 and Case 5.
+     *
+     * I re-read the tree and filter it against list_target.
+     * This is crude.  But it avoids data copies.  Also, list_target
+     * is compact and contiguous, so it easily fits into cache.
+     *
+     * Notice that list_target contains strings separated by \n,
+     * with a \n before the first string and after the last.
+     * fgets gives the incoming names a terminating \n.
+     * So by having an initial \n, strstr will find exact matches.
+     */
+
+    fp_find = popen("find * -type f -name \"*.h\" -print", "r");
+    if (fp_find == 0)
+	ERROR_EXIT( "find" );
+
+    line[0] = '\n';
+    while (fgets(line+1, buffer_size, fp_find))
+    {
+	if (strstr(list_target, line) == NULL)
+	{
+	    /*
+	     * This is an old file with no CONFIG_* flag in autoconf.h.
+	     */
+
+	    /* First strip the \n. */
+	    line[strlen(line)-1] = '\0';
+
+	    /* Grab size. */
+	    if (stat(line+1, &stat_buf) != 0)
+		ERROR_EXIT(line);
+
+	    /* If file is not empty, make it empty and give it a fresh date. */
+	    if (stat_buf.st_size != 0)
+	    {
+		if ((fp_target = fopen(line+1, "w")) == NULL)
+		    ERROR_EXIT(line);
+		if (fclose(fp_target) != 0)
+		    ERROR_EXIT(line);
+	    }
+	}
+    }
+
+    if (pclose(fp_find) != 0)
+	ERROR_EXIT("find");
+
+    return 0;
+}
diff --git a/busybox-1.19.3/scripts/bb_release b/busybox-1.19.3/scripts/bb_release
new file mode 100755
index 0000000..8aa3804
--- /dev/null
+++ b/busybox-1.19.3/scripts/bb_release
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+# Create signed release tarballs and signature files from current svn.
+# Since you don't have my gpg key, this doesn't do you much good,
+# but if I get hit by a bus the next maintainer might find this useful.
+# Run this in an empty directory.  The VERSION= line can get confused
+# otherwise.
+
+#svn co svn://busybox.net/trunk/busybox
+cd busybox || { echo "cd busybox failed"; exit 1; }
+make release || { echo "make release failed"; exit 1; }
+cd ..
+
+VERSION=`ls busybox-*.tar.gz | sed 's/busybox-\(.*\)\.tar\.gz/\1/'`
+
+zcat busybox-$VERSION.tar.gz | bzip2 > busybox-$VERSION.tar.bz2
+
+test -f busybox-$VERSION.tar.gz || { echo "no busybox-$VERSION.tar.gz"; exit 1; }
+test -f busybox-$VERSION.tar.bz2 || { echo "no busybox-$VERSION.tar.bz2"; exit 1; }
+
+signit()
+{
+echo "$1 released `date -r $1 -R`
+
+MD5:  `md5sum $1`
+SHA1: `sha1sum $1`
+
+To verify this signature, you can obtain my public key
+from http://busybox.net/~vda/vda_pubkey.gpg
+" | gpg --clearsign > "$1.sign"
+}
+
+signit busybox-$VERSION.tar.gz
+signit busybox-$VERSION.tar.bz2
diff --git a/busybox-1.19.3/scripts/bloat-o-meter b/busybox-1.19.3/scripts/bloat-o-meter
new file mode 100755
index 0000000..0d3ff7f
--- /dev/null
+++ b/busybox-1.19.3/scripts/bloat-o-meter
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+#
+# Copyright 2004 Matt Mackall <mpm@selenic.com>
+#
+# 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
+
+def usage():
+    sys.stderr.write("usage: %s [-t] file1 file2\n" % sys.argv[0])
+    sys.exit(-1)
+
+f1, f2 = (None, None)
+flag_timing, dashes = (False, False)
+
+for f in sys.argv[1:]:
+    if f.startswith("-"):
+        if f == "--": # sym_args
+            dashes = True
+            break
+        if f == "-t": # timings
+            flag_timing = True
+    else:
+        if not os.path.exists(f):
+            sys.stderr.write("Error: file '%s' does not exist\n" % f)
+            usage()
+        if f1 is None:
+            f1 = f
+        elif f2 is None:
+            f2 = f
+if flag_timing:
+    import time
+if f1 is None or f2 is None:
+    usage()
+
+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
+        if "." in name: name = "static." + name.split(".")[0]
+        value = int(value, 16)
+        size = 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 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:
+            # If this non-GLOBAL sym does not have an implementation at
+            # another address, then treat it as a normal symbol.
+            sym[alias[(addr, sz)]["name"]] = {"addr" : addr, "size": sz}
+    for l in os.popen("readelf -W -S " + file).readlines():
+        x = l.split()
+        if len(x)<6: continue
+        # Should take these into account too!
+        #if x[1] not in [".text", ".rodata", ".symtab", ".strtab"]: continue
+        if x[1] not in [".rodata"]: continue
+        sym[x[1]] = {"addr" : int(x[3], 16), "size" : int(x[5], 16)}
+    return sym
+
+if flag_timing:
+    start_t1 = int(time.time() * 1e9)
+old = getsizes(f1)
+if flag_timing:
+    end_t1 = int(time.time() * 1e9)
+    start_t2 = int(time.time() * 1e9)
+new = getsizes(f2)
+if flag_timing:
+    end_t2 = int(time.time() * 1e9)
+    start_t3 = int(time.time() * 1e9)
+grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
+delta, common = [], {}
+
+for name in iter(old.keys()):
+    if name in new:
+        common[name] = 1
+
+for name in old:
+    if name not in common:
+        remove += 1
+        sz = old[name]["size"]
+        down += sz
+        delta.append((-sz, name))
+
+for name in new:
+    if name not in common:
+        add += 1
+        sz = new[name]["size"]
+        up += sz
+        delta.append((sz, name))
+
+for name in common:
+        d = new[name].get("size", 0) - old[name].get("size", 0)
+        if d>0: grow, up = grow+1, up+d
+        elif d<0: shrink, down = shrink+1, down-d
+        else:
+            continue
+        delta.append((d, name))
+
+delta.sort()
+delta.reverse()
+if flag_timing:
+    end_t3 = int(time.time() * 1e9)
+
+print("%-48s %7s %7s %+7s" % ("function", "old", "new", "delta"))
+for d, n in delta:
+    if d:
+        old_sz = old.get(n, {}).get("size", "-")
+        new_sz = new.get(n, {}).get("size", "-")
+        print("%-48s %7s %7s %+7d" % (n, old_sz, new_sz, d))
+print("-"*78)
+total="(add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s)%%sTotal: %s bytes"\
+    % (add, remove, grow, shrink, up, -down, up-down)
+print(total % (" "*(80-len(total))))
+if flag_timing:
+    print("\n%d/%d; %d Parse origin/new; processing nsecs" %
+        (end_t1-start_t1, end_t2-start_t2, end_t3-start_t3))
+    print("total nsecs: %d" % (end_t3-start_t1))
diff --git a/busybox-1.19.3/scripts/checkhelp.awk b/busybox-1.19.3/scripts/checkhelp.awk
new file mode 100755
index 0000000..94843d6
--- /dev/null
+++ b/busybox-1.19.3/scripts/checkhelp.awk
@@ -0,0 +1,40 @@
+#!/usr/bin/awk -f
+# AWK script to check for missing help entries for config options
+#
+# Copyright (C) 2006 Bernhard Reutner-Fischer
+#
+# This file is distributed under the terms and conditions of the
+# MIT/X public licenses. See http://opensource.org/licenses/mit-license.html
+# and notice http://www.gnu.org/licenses/license-list.html#X11License
+
+
+/^choice/ { is_choice = 1; }
+/^endchoice/ { is_choice = 0; }
+/^config/ {
+	pos++;
+	conf[pos] = $2;
+	file[pos] = FILENAME;
+	if (is_choice) {
+		help[pos] = 1; # do not warn about 'choice' config entries.
+	} else {
+		help[pos] = 0;
+	}
+}
+/^[ \t]*help[ \t]*$/ {
+	help[pos] = 1;
+}
+/^[ \t]*bool[ \t]*$/ {
+	help[pos] = 1; # ignore options which are not selectable
+}
+BEGIN {
+	pos = -1;
+	is_choice = 0;
+}
+END {
+	for (i = 0; i <= pos; i++) {
+#	printf("%s: help for #%i '%s' == %i\n", file[i], i, conf[i], help[i]);
+		if (help[i] == 0) {
+			printf("%s: No helptext for '%s'\n", file[i], conf[i]);
+		}
+	}
+}
diff --git a/busybox-1.19.3/scripts/checkstack.pl b/busybox-1.19.3/scripts/checkstack.pl
new file mode 100755
index 0000000..55cdd78
--- /dev/null
+++ b/busybox-1.19.3/scripts/checkstack.pl
@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+
+# Stolen from Linux kernel :)
+
+#	Check the stack usage of functions
+#
+#	Copyright Joern Engel <joern@wh.fh-wedel.de>
+#	Inspired by Linus Torvalds
+#	Original idea maybe from Keith Owens
+#	s390 port and big speedup by Arnd Bergmann <arnd@bergmann-dalldorf.de>
+#	Mips port by Juan Quintela <quintela@mandrakesoft.com>
+#	IA64 port via Andreas Dilger
+#	Arm port by Holger Schurig
+#	sh64 port by Paul Mundt
+#	Random bits by Matt Mackall <mpm@selenic.com>
+#	M68k port by Geert Uytterhoeven and Andreas Schwab
+#
+#	Usage:
+#	objdump -d vmlinux | checkstack.pl [arch]
+#
+#	TODO :	Port to all architectures (one regex per arch)
+
+# check for arch
+#
+# $re is used for two matches:
+# $& (whole re) matches the complete objdump line with the stack growth
+# $1 (first bracket) matches the size of the stack growth
+#
+# use anything else and feel the pain ;)
+my (@stack, $re, $x, $xs);
+{
+	my $arch = shift;
+	if ($arch eq "") {
+		$arch = `uname -m`;
+	}
+
+	$x	= "[0-9a-f]";	# hex character
+	$xs	= "[0-9a-f ]";	# hex character or space
+	if ($arch eq 'arm') {
+		#c0008ffc:	e24dd064	sub	sp, sp, #100	; 0x64
+		$re = qr/.*sub.*sp, sp, #(([0-9]{2}|[3-9])[0-9]{2})/o;
+	} elsif ($arch eq 'blackfin') {
+		#      52:       00 e8 03 00     LINK 0xc;
+		$re = qr/.*LINK (0x$x{1,5});$/o;
+	} elsif ($arch =~ /^i[3456]86$/) {
+		#c0105234:       81 ec ac 05 00 00       sub    $0x5ac,%esp
+		$re = qr/^.*[as][du][db]    \$(0x$x{1,8}),\%esp$/o;
+	} elsif ($arch eq 'x86_64') {
+		#    2f60:	48 81 ec e8 05 00 00 	sub    $0x5e8,%rsp
+		$re = qr/^.*[as][du][db]    \$(0x$x{1,8}),\%rsp$/o;
+	} elsif ($arch eq 'ia64') {
+		#e0000000044011fc:       01 0f fc 8c     adds r12=-384,r12
+		$re = qr/.*adds.*r12=-(([0-9]{2}|[3-9])[0-9]{2}),r12/o;
+	} elsif ($arch eq 'm68k') {
+		#    2b6c:       4e56 fb70       linkw %fp,#-1168
+		#  1df770:       defc ffe4       addaw #-28,%sp
+		$re = qr/.*(?:linkw %fp,|addaw )#-([0-9]{1,4})(?:,%sp)?$/o;
+	} elsif ($arch eq 'mips64') {
+		#8800402c:       67bdfff0        daddiu  sp,sp,-16
+		$re = qr/.*daddiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o;
+	} elsif ($arch eq 'mips') {
+		#88003254:       27bdffe0        addiu   sp,sp,-32
+		$re = qr/.*addiu.*sp,sp,-(([0-9]{2}|[3-9])[0-9]{2})/o;
+	} elsif ($arch eq 'ppc') {
+		#c00029f4:       94 21 ff 30     stwu    r1,-208(r1)
+		$re = qr/.*stwu.*r1,-($x{1,8})\(r1\)/o;
+	} elsif ($arch eq 'ppc64') {
+		#XXX
+		$re = qr/.*stdu.*r1,-($x{1,8})\(r1\)/o;
+	} elsif ($arch eq 'powerpc') {
+		$re = qr/.*st[dw]u.*r1,-($x{1,8})\(r1\)/o;
+	} elsif ($arch =~ /^s390x?$/) {
+		#   11160:       a7 fb ff 60             aghi   %r15,-160
+		$re = qr/.*ag?hi.*\%r15,-(([0-9]{2}|[3-9])[0-9]{2})/o;
+	} elsif ($arch =~ /^sh64$/) {
+		#XXX: we only check for the immediate case presently,
+		#     though we will want to check for the movi/sub
+		#     pair for larger users. -- PFM.
+		#a00048e0:       d4fc40f0        addi.l  r15,-240,r15
+		$re = qr/.*addi\.l.*r15,-(([0-9]{2}|[3-9])[0-9]{2}),r15/o;
+	} else {
+		print("wrong or unknown architecture\n");
+		exit
+	}
+}
+
+sub bysize($) {
+	my ($asize, $bsize);
+	($asize = $a) =~ s/.*:	*(.*)$/$1/;
+	($bsize = $b) =~ s/.*:	*(.*)$/$1/;
+	$bsize <=> $asize
+}
+
+#
+# main()
+#
+my $funcre = qr/^$x* <(.*)>:$/;
+my $func;
+my $file, $lastslash;
+
+while (my $line = <STDIN>) {
+	if ($line =~ m/$funcre/) {
+		$func = $1;
+	}
+	elsif ($line =~ m/(.*):\s*file format/) {
+		$file = $1;
+		$file =~ s/\.ko//;
+		$lastslash = rindex($file, "/");
+		if ($lastslash != -1) {
+			$file = substr($file, $lastslash + 1);
+		}
+	}
+	elsif ($line =~ m/$re/) {
+		my $size = $1;
+		$size = hex($size) if ($size =~ /^0x/);
+
+		if ($size > 0xf0000000) {
+			$size = - $size;
+			$size += 0x80000000;
+			$size += 0x80000000;
+		}
+		next if ($size > 0x10000000);
+
+		next if $line !~ m/^($xs*)/;
+		my $addr = $1;
+		$addr =~ s/ /0/g;
+		$addr = "0x$addr";
+
+		# bbox: was: my $intro = "$addr $func [$file]:";
+		my $intro = "$func [$file]:";
+		my $padlen = 56 - length($intro);
+		while ($padlen > 0) {
+			$intro .= '	';
+			$padlen -= 8;
+		}
+		next if ($size < 100);
+		push @stack, "$intro$size\n";
+	}
+}
+
+print sort bysize @stack;
diff --git a/busybox-1.19.3/scripts/cleanup_printf2puts b/busybox-1.19.3/scripts/cleanup_printf2puts
new file mode 100755
index 0000000..00193a8
--- /dev/null
+++ b/busybox-1.19.3/scripts/cleanup_printf2puts
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# Processes current directory recursively:
+# printf("abc\n") -> puts("abc"). Beware of fprintf etc...
+
+# 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
diff --git a/busybox-1.19.3/scripts/echo.c b/busybox-1.19.3/scripts/echo.c
new file mode 100644
index 0000000..cb207ae
--- /dev/null
+++ b/busybox-1.19.3/scripts/echo.c
@@ -0,0 +1,230 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * echo implementation for busybox - used as a helper for testsuite/*
+ * on systems lacking "echo -en"
+ *
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+
+/* BB_AUDIT SUSv3 compliant -- unless configured as fancy echo. */
+/* http://www.opengroup.org/onlinepubs/007904975/utilities/echo.html */
+
+/* Mar 16, 2003      Manuel Novoa III   (mjn3@codepoet.org)
+ *
+ * Because of behavioral differences, implemented configurable SUSv3
+ * or 'fancy' gnu-ish behaviors.  Also, reduced size and fixed bugs.
+ * 1) In handling '\c' escape, the previous version only suppressed the
+ *     trailing newline.  SUSv3 specifies _no_ output after '\c'.
+ * 2) SUSv3 specifies that octal escapes are of the form \0{#{#{#}}}.
+ *    The previous version did not allow 4-digit octals.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#define WANT_HEX_ESCAPES 1
+
+/* Usual "this only works for ascii compatible encodings" disclaimer. */
+#undef _tolower
+#define _tolower(X) ((X)|((char) 0x20))
+
+static char bb_process_escape_sequence(const char **ptr)
+{
+	static const char charmap[] = {
+		'a',  'b',  'f',  'n',  'r',  't',  'v',  '\\', 0,
+		'\a', '\b', '\f', '\n', '\r', '\t', '\v', '\\', '\\' };
+
+	const char *p;
+	const char *q;
+	unsigned int num_digits;
+	unsigned int r;
+	unsigned int n;
+	unsigned int d;
+	unsigned int base;
+
+	num_digits = n = 0;
+	base = 8;
+	q = *ptr;
+
+#ifdef WANT_HEX_ESCAPES
+	if (*q == 'x') {
+		++q;
+		base = 16;
+		++num_digits;
+	}
+#endif
+
+	do {
+		d = (unsigned char)(*q) - '0';
+#ifdef WANT_HEX_ESCAPES
+		if (d >= 10) {
+			d = (unsigned char)(_tolower(*q)) - 'a' + 10;
+		}
+#endif
+
+		if (d >= base) {
+#ifdef WANT_HEX_ESCAPES
+			if ((base == 16) && (!--num_digits)) {
+/*				return '\\'; */
+				--q;
+			}
+#endif
+			break;
+		}
+
+		r = n * base + d;
+		if (r > UCHAR_MAX) {
+			break;
+		}
+
+		n = r;
+		++q;
+	} while (++num_digits < 3);
+
+	if (num_digits == 0) {	/* mnemonic escape sequence? */
+		p = charmap;
+		do {
+			if (*p == *q) {
+				q++;
+				break;
+			}
+		} while (*++p);
+		n = *(p + (sizeof(charmap)/2));
+	}
+
+	*ptr = q;
+
+	return (char) n;
+}
+
+
+int main(int argc, char **argv)
+{
+	const char *arg;
+	const char *p;
+	char nflag = 1;
+	char eflag = 0;
+
+	/* We must check that stdout is not closed. */
+	if (dup2(1, 1) != 1)
+		return -1;
+
+	while (1) {
+		arg = *++argv;
+		if (!arg)
+			goto newline_ret;
+		if (*arg != '-')
+			break;
+
+		/* 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;
+
+		do {
+			if (!strrchr("neE", *p))
+				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);
+	}
+ just_echo:
+	while (1) {
+		/* arg is already == *argv and isn't NULL */
+		int c;
+
+		if (!eflag) {
+			/* optimization for very common case */
+			fputs(arg, stdout);
+		} else while ((c = *arg++)) {
+			if (c == eflag) {	/* Check for escape seq. */
+				if (*arg == 'c') {
+					/* '\c' means cancel newline and
+					 * ignore all subsequent chars. */
+					goto ret;
+				}
+				{
+					/* 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++;
+						}
+					}
+					/* bb_process_escape_sequence handles NUL correctly
+					 * ("...\" case. */
+					c = bb_process_escape_sequence(&arg);
+				}
+			}
+			putchar(c);
+		}
+
+		arg = *++argv;
+		if (!arg)
+			break;
+		putchar(' ');
+	}
+
+ newline_ret:
+	if (nflag) {
+		putchar('\n');
+	}
+ ret:
+	return fflush(NULL);
+}
+
+/*-
+ * Copyright (c) 1991, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change
+ *              ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change>
+ *
+ *      California, Berkeley and its contributors.
+ * 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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ *      @(#)echo.c      8.1 (Berkeley) 5/31/93
+ */
diff --git a/busybox-1.19.3/scripts/find_bad_common_bufsiz b/busybox-1.19.3/scripts/find_bad_common_bufsiz
new file mode 100755
index 0000000..e80cf62
--- /dev/null
+++ b/busybox-1.19.3/scripts/find_bad_common_bufsiz
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# This script finds applets with multiple uses of bb_common_bufsiz1
+# (== possible bugs).
+# Currently (2007-06-04) reports 3 false positives:
+# ./coreutils/diff.c:7
+# ./loginutils/getty.c:2
+# ./util-linux/mount.c:5
+
+find -name '*.c' \
+| while read name; do
+    grep -Hc bb_common_bufsiz1 "$name"
+done | grep -v ':[01]$'
diff --git a/busybox-1.19.3/scripts/find_stray_common_vars b/busybox-1.19.3/scripts/find_stray_common_vars
new file mode 100755
index 0000000..3a25d7a
--- /dev/null
+++ b/busybox-1.19.3/scripts/find_stray_common_vars
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+# Common variables are elusive, they don't show up in size output!
+# This script will show all commons in *.o, sorted by size
+
+find ! -path './scripts/*' -a ! -name built-in.o -a -name '*.o' \
+| while read name; do
+    b=`basename "$name"`
+    nm "$name" | sed "s/^/$b: /"
+done | grep -i ' c ' | sort -k2
diff --git a/busybox-1.19.3/scripts/find_stray_empty_lines b/busybox-1.19.3/scripts/find_stray_empty_lines
new file mode 100755
index 0000000..aae18f9
--- /dev/null
+++ b/busybox-1.19.3/scripts/find_stray_empty_lines
@@ -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
diff --git a/busybox-1.19.3/scripts/fix_ws.sh b/busybox-1.19.3/scripts/fix_ws.sh
new file mode 100755
index 0000000..e7cf529
--- /dev/null
+++ b/busybox-1.19.3/scripts/fix_ws.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+# Whitespace fixer
+# Usage: fix_ws [dir]...
+
+temp="/tmp/fix_ws.$$.$RANDOM"
+
+# Using $'xxx' bashism
+begin8sp_tab=$'s/^        /\t/'
+beginchar7sp_chartab=$'s/^\\([^ \t]\\)       /\\1\t/'
+tab8sp_tabtab=$'s/\t        /\t\t/g'
+tab8sptab_tabtabtab=$'s/\t        \t/\t\t\t/g'
+begin17sptab_tab=$'s/^ \\{1,7\\}\t/\t/'
+tab17sptab_tabtab=$'s/\t \\{1,7\\}\t/\t\t/g'
+trailingws_=$'s/[ \t]*$//'
+
+#>fix_ws.diff
+
+find "$@" -type f \
+| while read name; do
+    test "YES" = "${name/*.bz2/YES}" && continue
+    test "YES" = "${name/*.gz/YES}" && continue
+    test "YES" = "${name/*.png/YES}" && continue
+    test "YES" = "${name/*.gif/YES}" && continue
+    test "YES" = "${name/*.jpg/YES}" && continue
+    test "YES" = "${name/*.diff/YES}" && continue
+    test "YES" = "${name/*.patch/YES}" && continue
+    # shell testsuite entries are not to be touched too
+    test "YES" = "${name/*.right/YES}" && continue
+
+    if test "YES" = "${name/*.[chsS]/YES}" \
+	-o "YES" = "${name/*.sh/YES}" \
+	-o "YES" = "${name/*.txt/YES}" \
+	-o "YES" = "${name/*.html/YES}" \
+	-o "YES" = "${name/*.htm/YES}" \
+	-o "YES" = "${name/*Config.in*/YES}" \
+    ; then
+    # More aggressive whitespace fixes for known file types
+	echo "Formatting: $name" >&2
+	cat "$name" \
+	| sed -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \
+	      -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \
+	| sed "$begin17sptab_tab" \
+	| sed -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \
+	      -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \
+	      -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \
+	| sed "$trailingws_"
+    elif test "YES" = "${name/*Makefile*/YES}" \
+	-o "YES" = "${name/*Kbuild*/YES}" \
+    ; then
+    # For Makefiles, never convert "1-7spaces+tab" into "tabtab"
+	echo "Makefile: $name" >&2
+	cat "$name" \
+	| sed -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \
+	      -e "$tab8sptab_tabtabtab" -e "$tab8sptab_tabtabtab" \
+	| sed -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \
+	      -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \
+	      -e "$tab17sptab_tabtab" -e "$tab17sptab_tabtab" \
+	| sed "$trailingws_"
+    else
+    # Only remove trailing WS for the rest
+	echo "Removing trailing whitespace: $name" >&2
+	cat "$name" \
+	| sed "$trailingws_"
+    fi >"$temp"
+
+#    diff -u "$temp" "$name" >>fix_ws.diff
+
+    # Conserve mode/symlink:
+    cat "$temp" >"$name"
+done
+rm "$temp" 2>/dev/null
diff --git a/busybox-1.19.3/scripts/gcc-version.sh b/busybox-1.19.3/scripts/gcc-version.sh
new file mode 100755
index 0000000..3451080
--- /dev/null
+++ b/busybox-1.19.3/scripts/gcc-version.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# gcc-version gcc-command
+#
+# Prints the gcc version of `gcc-command' in a canonical 4-digit form
+# such as `0295' for gcc-2.95, `0303' for gcc-3.3, etc.
+#
+
+compiler="$*"
+
+MAJ_MIN=$(echo __GNUC__ __GNUC_MINOR__ | $compiler -E -xc - | tail -n 1)
+printf '%02d%02d\n' $MAJ_MIN
diff --git a/busybox-1.19.3/scripts/gen_build_files.sh b/busybox-1.19.3/scripts/gen_build_files.sh
new file mode 100755
index 0000000..e518a90
--- /dev/null
+++ b/busybox-1.19.3/scripts/gen_build_files.sh
@@ -0,0 +1,99 @@
+#!/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
+cd -- "$2" || { echo "Syntax: $0 SRCTREE OBJTREE"; exit 1; }
+# In separate objtree build, include/ might not exist yet
+mkdir include 2>/dev/null
+
+srctree="$1"
+
+status() { printf '  %-8s%s\n' "$1" "$2"; }
+gen() { status "GEN" "$@"; }
+chk() { status "CHK" "$@"; }
+
+generate()
+{
+	local src="$1" dst="$2" header="$3" insert="$4"
+	#chk "${dst}"
+	(
+		# Need to use printf: different shells have inconsistent
+		# rules re handling of "\n" in echo params,
+		# and ${insert} definitely contains "\n".
+		# Therefore, echo "${header}" would not work:
+		printf "%s\n" "${header}"
+		if grep -qs '^INSERT$' "${src}"; then
+			sed -n '1,/^INSERT$/p' "${src}"
+			printf "%s\n" "${insert}"
+			sed -n '/^INSERT$/,$p' "${src}"
+		else
+			if [ -n "${insert}" ]; then
+				printf "%s\n" "ERROR: INSERT line missing in: ${src}" 1>&2
+			fi
+			cat "${src}"
+		fi
+	) | sed '/^INSERT$/d' > "${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
+s=`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 */" \
+	"${s}"
+
+# (Re)generate 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`
+generate \
+	"$srctree/include/usage.src.h" \
+	"include/usage.h" \
+	"/* DO NOT EDIT. This file is generated from usage.src.h */" \
+	"${s}"
+
+# (Re)generate */Kbuild and */Config.in
+{ cd -- "$srctree" && find . -type d; } | 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
+
+		s=`sed -n 's@^//kbuild:@@p' "$srctree/$d"/*.c`
+		generate \
+			"${src}" "${dst}" \
+			"# DO NOT EDIT. This file is generated from Kbuild.src" \
+			"${s}"
+	fi
+
+	src="$srctree/$d/Config.src"
+	dst="$d/Config.in"
+	if test -f "$src"; then
+		mkdir -p -- "$d" 2>/dev/null
+
+		s=`sed -n 's@^//config:@@p' "$srctree/$d"/*.c`
+		generate \
+			"${src}" "${dst}" \
+			"# DO NOT EDIT. This file is generated from Config.src" \
+			"${s}"
+	fi
+done
+
+# Last read failed. This is normal. Don't exit with its error code:
+exit 0
diff --git a/busybox-1.19.3/scripts/kconfig/Makefile b/busybox-1.19.3/scripts/kconfig/Makefile
new file mode 100644
index 0000000..b5708e2
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/Makefile
@@ -0,0 +1,271 @@
+# ===========================================================================
+# Kernel configuration targets
+# These targets are used from top-level makefile
+
+PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config
+
+xconfig: $(obj)/qconf
+	$< Config.in
+
+gconfig: $(obj)/gconf
+	$< Config.in
+
+menuconfig: $(obj)/mconf
+	$(Q)$(MAKE) $(build)=scripts/kconfig/lxdialog
+	$< Config.in
+
+config: $(obj)/conf
+	$< Config.in
+
+# Mtime granularity problem.
+# It was observed that these commands:
+# make allnoconfig; sed -i -e '/CONFIG_TRUE/s/.*/CONFIG_TRUE=y/' .config; make
+# sometimes produce busybox with "true" applet still disabled.
+# This is caused by .config updated by sed having mtime which is still
+# equal to (not bigger than) include/autoconf.h's mtime,
+# and thus 2nd make does not regenerate include/autoconf.h.
+# Waiting for 1 second after non-interactive "make XXXXconfig"
+# prevents this from happening.
+#
+# We'd like to detect whether filesystem we are on has coarse mtimes,
+# but can't do it yet, bbox ls hasn't got --full-time.
+#MTIME_IS_COARSE:=@ls --full-time -ld | grep -F .000 >/dev/null
+MTIME_IS_COARSE:=@true
+
+oldconfig: $(obj)/conf
+	$< -o Config.in
+	$(MTIME_IS_COARSE) && sleep 1
+
+silentoldconfig: $(obj)/conf
+	$< -s Config.in
+	$(MTIME_IS_COARSE) && sleep 1
+
+update-po-config: $(obj)/kxgettext
+	xgettext --default-domain=linux \
+          --add-comments --keyword=_ --keyword=N_ \
+          --files-from=scripts/kconfig/POTFILES.in \
+          --output scripts/kconfig/config.pot
+	$(Q)ln -fs Kconfig_i386 arch/um/Kconfig_arch
+	$(Q)for i in `ls arch/`; \
+	do \
+	  scripts/kconfig/kxgettext arch/$$i/Kconfig \
+	    | msguniq -o scripts/kconfig/linux_$${i}.pot; \
+	done
+	$(Q)msgcat scripts/kconfig/config.pot \
+	  `find scripts/kconfig/ -type f -name linux_*.pot` \
+	  --output scripts/kconfig/linux_raw.pot
+	$(Q)msguniq --sort-by-file scripts/kconfig/linux_raw.pot \
+	    --output scripts/kconfig/linux.pot
+	$(Q)rm -f arch/um/Kconfig_arch
+	$(Q)rm -f scripts/kconfig/linux_*.pot scripts/kconfig/config.pot
+
+PHONY += randconfig allyesconfig allnoconfig allmodconfig defconfig
+
+randconfig: $(obj)/conf
+	$< -r Config.in
+	$(MTIME_IS_COARSE) && sleep 1
+
+allyesconfig: $(obj)/conf
+	$< -y Config.in
+	$(MTIME_IS_COARSE) && sleep 1
+
+allnoconfig: $(obj)/conf
+	$< -n Config.in
+	$(MTIME_IS_COARSE) && sleep 1
+
+allmodconfig: $(obj)/conf
+	$< -m Config.in
+	$(MTIME_IS_COARSE) && sleep 1
+
+defconfig: $(obj)/conf
+ifeq ($(KBUILD_DEFCONFIG),)
+	$< -d Config.in
+else
+	@echo *** Default configuration is based on '$(KBUILD_DEFCONFIG)'
+	$(Q)$< -D $(KBUILD_DEFCONFIG) Config.in
+endif
+	$(MTIME_IS_COARSE) && sleep 1
+
+%_defconfig: $(obj)/conf
+	$(Q)$< -D $@ Config.in
+	$(MTIME_IS_COARSE) && sleep 1
+
+# Help text used by make help
+help:
+	@echo  '  config	  - Update current config utilising a line-oriented program'
+	@echo  '  menuconfig	  - Update current config utilising a menu based program'
+	@echo  '  xconfig	  - Update current config utilising a QT based front-end'
+	@echo  '  gconfig	  - Update current config utilising a GTK based front-end'
+	@echo  '  oldconfig	  - Update current config utilising a provided .config as base'
+	@echo  '  randconfig	  - New config with random answer to all options'
+	@echo  '  defconfig	  - New config with default answer to all options'
+	@echo  '  allmodconfig	  - New config selecting modules when possible'
+	@echo  '  allyesconfig	  - New config where all options are accepted with yes'
+	@echo  '  allnoconfig	  - New config where all options are answered with no'
+
+# ===========================================================================
+# Shared Makefile for the various kconfig executables:
+# conf:	  Used for defconfig, oldconfig and related targets
+# mconf:  Used for the mconfig target.
+#         Utilizes the lxdialog package
+# qconf:  Used for the xconfig target
+#         Based on QT which needs to be installed to compile it
+# gconf:  Used for the gconfig target
+#         Based on GTK which needs to be installed to compile it
+# object files used by all kconfig flavours
+
+hostprogs-y	:= conf mconf qconf gconf kxgettext
+conf-objs	:= conf.o  zconf.tab.o
+mconf-objs	:= mconf.o zconf.tab.o
+kxgettext-objs	:= kxgettext.o zconf.tab.o
+
+ifeq ($(MAKECMDGOALS),xconfig)
+	qconf-target := 1
+endif
+ifeq ($(MAKECMDGOALS),gconfig)
+	gconf-target := 1
+endif
+
+
+ifeq ($(qconf-target),1)
+qconf-cxxobjs	:= qconf.o
+qconf-objs	:= kconfig_load.o zconf.tab.o
+endif
+
+ifeq ($(gconf-target),1)
+gconf-objs	:= gconf.o kconfig_load.o zconf.tab.o
+endif
+
+clean-files	:= lkc_defs.h qconf.moc .tmp_qtcheck \
+		   .tmp_gtkcheck zconf.tab.c lex.zconf.c zconf.hash.c
+subdir- += lxdialog
+
+# Add environment specific flags
+HOST_EXTRACFLAGS += $(shell $(CONFIG_SHELL) $(srctree)/$(src)/check.sh $(HOSTCC) $(HOSTCFLAGS))
+
+# generated files seem to need this to find local include files
+HOSTCFLAGS_lex.zconf.o	:= -I$(src)
+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`
+HOSTCFLAGS_gconf.o	= `pkg-config --cflags gtk+-2.0 gmodule-2.0 libglade-2.0` \
+                          -D LKC_DIRECT_LINK
+
+$(obj)/qconf.o: $(obj)/.tmp_qtcheck
+
+ifeq ($(qconf-target),1)
+$(obj)/.tmp_qtcheck: $(src)/Makefile
+-include $(obj)/.tmp_qtcheck
+
+# QT needs some extra effort...
+$(obj)/.tmp_qtcheck:
+	@set -e; echo "  CHECK   qt"; dir=""; pkg=""; \
+	pkg-config --exists qt 2> /dev/null && pkg=qt; \
+	pkg-config --exists qt-mt 2> /dev/null && pkg=qt-mt; \
+	if [ -n "$$pkg" ]; then \
+	  cflags="\$$(shell pkg-config $$pkg --cflags)"; \
+	  libs="\$$(shell pkg-config $$pkg --libs)"; \
+	  moc="\$$(shell pkg-config $$pkg --variable=prefix)/bin/moc"; \
+	  dir="$$(pkg-config $$pkg --variable=prefix)"; \
+	else \
+	  for d in $$QTDIR /usr/share/qt* /usr/lib/qt*; do \
+	    if [ -f $$d/include/qconfig.h ]; then dir=$$d; break; fi; \
+	  done; \
+	  if [ -z "$$dir" ]; then \
+	    echo "*"; \
+	    echo "* Unable to find the QT installation. Please make sure that"; \
+	    echo "* the QT development package is correctly installed and"; \
+	    echo "* either install pkg-config or set the QTDIR environment"; \
+	    echo "* variable to the correct location."; \
+	    echo "*"; \
+	    false; \
+	  fi; \
+	  libpath=$$dir/lib; lib=qt; osdir=""; \
+	  $(HOSTCXX) -print-multi-os-directory > /dev/null 2>&1 && \
+	    osdir=x$$($(HOSTCXX) -print-multi-os-directory); \
+	  test -d $$libpath/$$osdir && libpath=$$libpath/$$osdir; \
+	  test -f $$libpath/libqt-mt.so && lib=qt-mt; \
+	  cflags="-I$$dir/include"; \
+	  libs="-L$$libpath -Wl,-rpath,$$libpath -l$$lib"; \
+	  moc="$$dir/bin/moc"; \
+	fi; \
+	if [ ! -x $$dir/bin/moc -a -x /usr/bin/moc ]; then \
+	  echo "*"; \
+	  echo "* Unable to find $$dir/bin/moc, using /usr/bin/moc instead."; \
+	  echo "*"; \
+	  moc="/usr/bin/moc"; \
+	fi; \
+	echo "KC_QT_CFLAGS=$$cflags" > $@; \
+	echo "KC_QT_LIBS=$$libs" >> $@; \
+	echo "KC_QT_MOC=$$moc" >> $@
+endif
+
+$(obj)/gconf.o: $(obj)/.tmp_gtkcheck
+
+ifeq ($(gconf-target),1)
+-include $(obj)/.tmp_gtkcheck
+
+# GTK needs some extra effort, too...
+$(obj)/.tmp_gtkcheck:
+	@if `pkg-config --exists gtk+-2.0 gmodule-2.0 libglade-2.0`; then		\
+		if `pkg-config --atleast-version=2.0.0 gtk+-2.0`; then			\
+			touch $@;								\
+		else									\
+			echo "*"; 							\
+			echo "* GTK+ is present but version >= 2.0.0 is required.";	\
+			echo "*";							\
+			false;								\
+		fi									\
+	else										\
+		echo "*"; 								\
+		echo "* Unable to find the GTK+ installation. Please make sure that"; 	\
+		echo "* the GTK+ 2.0 development package is correctly installed..."; 	\
+		echo "* You need gtk+-2.0, glib-2.0 and libglade-2.0."; 		\
+		echo "*"; 								\
+		false;									\
+	fi
+endif
+
+$(obj)/zconf.tab.o: $(obj)/lex.zconf.c $(obj)/zconf.hash.c
+
+$(obj)/kconfig_load.o: $(obj)/lkc_defs.h
+
+$(obj)/qconf.o: $(obj)/qconf.moc $(obj)/lkc_defs.h
+
+$(obj)/gconf.o: $(obj)/lkc_defs.h
+
+$(obj)/%.moc: $(src)/%.h
+	$(KC_QT_MOC) -i $< -o $@
+
+$(obj)/lkc_defs.h: $(src)/lkc_proto.h
+	sed < $< > $@ 's/P(\([^,]*\),.*/#define \1 (\*\1_p)/'
+
+
+###
+# The following requires flex/bison/gperf
+# By default we use the _shipped versions, uncomment the following line if
+# you are modifying the flex/bison src.
+# LKC_GENPARSER := 1
+
+ifdef LKC_GENPARSER
+
+$(obj)/zconf.tab.c: $(src)/zconf.y
+$(obj)/lex.zconf.c: $(src)/zconf.l
+$(obj)/zconf.hash.c: $(src)/zconf.gperf
+
+%.tab.c: %.y
+	bison -l -b $* -p $(notdir $*) $<
+	cp $@ $@_shipped
+
+lex.%.c: %.l
+	flex -L -P$(notdir $*) -o$@ $<
+	cp $@ $@_shipped
+
+%.hash.c: %.gperf
+	gperf < $< > $@
+	cp $@ $@_shipped
+
+endif
diff --git a/busybox-1.19.3/scripts/kconfig/POTFILES.in b/busybox-1.19.3/scripts/kconfig/POTFILES.in
new file mode 100644
index 0000000..cc94e46
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/POTFILES.in
@@ -0,0 +1,5 @@
+scripts/kconfig/mconf.c
+scripts/kconfig/conf.c
+scripts/kconfig/confdata.c
+scripts/kconfig/gconf.c
+scripts/kconfig/qconf.cc
diff --git a/busybox-1.19.3/scripts/kconfig/check.sh b/busybox-1.19.3/scripts/kconfig/check.sh
new file mode 100755
index 0000000..15fc294
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/check.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Needed for systems without gettext
+$* -xc -o /dev/null - > /dev/null 2>&1 << EOF
+#include <libintl.h>
+int main()
+{
+	gettext("");
+	return 0;
+}
+EOF
+if [ ! "$?" -eq "0"  ]; then
+	echo -DKBUILD_NO_NLS;
+fi
diff --git a/busybox-1.19.3/scripts/kconfig/conf.c b/busybox-1.19.3/scripts/kconfig/conf.c
new file mode 100644
index 0000000..ea2446a
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/conf.c
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#define _XOPEN_SOURCE 700
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf(struct menu *menu);
+static void check_conf(struct menu *menu);
+
+enum {
+	ask_all,
+	ask_new,
+	ask_silent,
+	set_default,
+	set_yes,
+	set_mod,
+	set_no,
+	set_random
+} input_mode = ask_all;
+char *defconfig_file;
+
+static int indent = 1;
+static int valid_stdin = 1;
+static int conf_cnt;
+static char line[128];
+static struct menu *rootEntry;
+
+static char nohelp_text[] = N_("Sorry, no help available for this option yet.\n");
+
+static void strip(char *str)
+{
+	char *p = str;
+	int l;
+
+	while ((isspace(*p)))
+		p++;
+	l = strlen(p);
+	if (p != str)
+		memmove(str, p, l + 1);
+	if (!l)
+		return;
+	p = str + l - 1;
+	while ((isspace(*p)))
+		*p-- = 0;
+}
+
+static void check_stdin(void)
+{
+	if (!valid_stdin && input_mode == ask_silent) {
+		printf(_("aborted!\n\n"));
+		printf(_("Console input/output is redirected. "));
+		printf(_("Run 'make oldconfig' to update configuration.\n\n"));
+		exit(1);
+	}
+}
+
+static void conf_askvalue(struct symbol *sym, const char *def)
+{
+	enum symbol_type type = sym_get_type(sym);
+	tristate val;
+
+	if (!sym_has_value(sym))
+		printf("(NEW) ");
+
+	line[0] = '\n';
+	line[1] = 0;
+
+	if (!sym_is_changable(sym)) {
+		printf("%s\n", def);
+		line[0] = '\n';
+		line[1] = 0;
+		return;
+	}
+
+	switch (input_mode) {
+	case set_no:
+	case set_mod:
+	case set_yes:
+	case set_random:
+		if (sym_has_value(sym)) {
+			printf("%s\n", def);
+			return;
+		}
+		break;
+	case ask_new:
+	case ask_silent:
+		if (sym_has_value(sym)) {
+			printf("%s\n", def);
+			return;
+		}
+		check_stdin();
+	case ask_all:
+		fflush(stdout);
+		fgets(line, 128, stdin);
+		return;
+	case set_default:
+		printf("%s\n", def);
+		return;
+	default:
+		break;
+	}
+
+	switch (type) {
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		printf("%s\n", def);
+		return;
+	default:
+		;
+	}
+	switch (input_mode) {
+	case set_yes:
+		if (sym_tristate_within_range(sym, yes)) {
+			line[0] = 'y';
+			line[1] = '\n';
+			line[2] = 0;
+			break;
+		}
+	case set_mod:
+		if (type == S_TRISTATE) {
+			if (sym_tristate_within_range(sym, mod)) {
+				line[0] = 'm';
+				line[1] = '\n';
+				line[2] = 0;
+				break;
+			}
+		} else {
+			if (sym_tristate_within_range(sym, yes)) {
+				line[0] = 'y';
+				line[1] = '\n';
+				line[2] = 0;
+				break;
+			}
+		}
+	case set_no:
+		if (sym_tristate_within_range(sym, no)) {
+			line[0] = 'n';
+			line[1] = '\n';
+			line[2] = 0;
+			break;
+		}
+	case set_random:
+		do {
+			val = (tristate)(random() % 3);
+		} while (!sym_tristate_within_range(sym, val));
+		switch (val) {
+		case no: line[0] = 'n'; break;
+		case mod: line[0] = 'm'; break;
+		case yes: line[0] = 'y'; break;
+		}
+		line[1] = '\n';
+		line[2] = 0;
+		break;
+	default:
+		break;
+	}
+	printf("%s", line);
+}
+
+int conf_string(struct menu *menu)
+{
+	struct symbol *sym = menu->sym;
+	const char *def;
+
+	while (1) {
+		printf("%*s%s ", indent - 1, "", menu->prompt->text);
+		printf("(%s) ", sym->name);
+		def = sym_get_string_value(sym);
+		if (sym_get_string_value(sym))
+			printf("[%s] ", def);
+		conf_askvalue(sym, def);
+		switch (line[0]) {
+		case '\n':
+			break;
+		case '?':
+			/* print help */
+			if (line[1] == '\n') {
+				printf("\n%s\n", menu->sym->help ? menu->sym->help : nohelp_text);
+				def = NULL;
+				break;
+			}
+		default:
+			line[strlen(line)-1] = 0;
+			def = line;
+		}
+		if (def && sym_set_string_value(sym, def))
+			return 0;
+	}
+}
+
+static int conf_sym(struct menu *menu)
+{
+	struct symbol *sym = menu->sym;
+	tristate oldval, newval;
+	const char *help;
+
+	while (1) {
+		printf("%*s%s ", indent - 1, "", menu->prompt->text);
+		if (sym->name)
+			printf("(%s) ", sym->name);
+		putchar('[');
+		oldval = sym_get_tristate_value(sym);
+		switch (oldval) {
+		case no:
+			putchar('N');
+			break;
+		case mod:
+			putchar('M');
+			break;
+		case yes:
+			putchar('Y');
+			break;
+		}
+		if (oldval != no && sym_tristate_within_range(sym, no))
+			printf("/n");
+		if (oldval != mod && sym_tristate_within_range(sym, mod))
+			printf("/m");
+		if (oldval != yes && sym_tristate_within_range(sym, yes))
+			printf("/y");
+		if (sym->help)
+			printf("/?");
+		printf("] ");
+		conf_askvalue(sym, sym_get_string_value(sym));
+		strip(line);
+
+		switch (line[0]) {
+		case 'n':
+		case 'N':
+			newval = no;
+			if (!line[1] || !strcmp(&line[1], "o"))
+				break;
+			continue;
+		case 'm':
+		case 'M':
+			newval = mod;
+			if (!line[1])
+				break;
+			continue;
+		case 'y':
+		case 'Y':
+			newval = yes;
+			if (!line[1] || !strcmp(&line[1], "es"))
+				break;
+			continue;
+		case 0:
+			newval = oldval;
+			break;
+		case '?':
+			goto help;
+		default:
+			continue;
+		}
+		if (sym_set_tristate_value(sym, newval))
+			return 0;
+help:
+		help = nohelp_text;
+		if (sym->help)
+			help = sym->help;
+		printf("\n%s\n", help);
+	}
+}
+
+static int conf_choice(struct menu *menu)
+{
+	struct symbol *sym, *def_sym;
+	struct menu *child;
+	bool is_new;
+
+	sym = menu->sym;
+	is_new = !sym_has_value(sym);
+	if (sym_is_changable(sym)) {
+		conf_sym(menu);
+		sym_calc_value(sym);
+		switch (sym_get_tristate_value(sym)) {
+		case no:
+			return 1;
+		case mod:
+			return 0;
+		case yes:
+			break;
+		}
+	} else {
+		switch (sym_get_tristate_value(sym)) {
+		case no:
+			return 1;
+		case mod:
+			printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+			return 0;
+		case yes:
+			break;
+		}
+	}
+
+	while (1) {
+		int cnt, def;
+
+		printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+		def_sym = sym_get_choice_value(sym);
+		cnt = def = 0;
+		line[0] = 0;
+		for (child = menu->list; child; child = child->next) {
+			if (!menu_is_visible(child))
+				continue;
+			if (!child->sym) {
+				printf("%*c %s\n", indent, '*', menu_get_prompt(child));
+				continue;
+			}
+			cnt++;
+			if (child->sym == def_sym) {
+				def = cnt;
+				printf("%*c", indent, '>');
+			} else
+				printf("%*c", indent, ' ');
+			printf(" %d. %s", cnt, menu_get_prompt(child));
+			if (child->sym->name)
+				printf(" (%s)", child->sym->name);
+			if (!sym_has_value(child->sym))
+				printf(" (NEW)");
+			printf("\n");
+		}
+		printf("%*schoice", indent - 1, "");
+		if (cnt == 1) {
+			printf("[1]: 1\n");
+			goto conf_childs;
+		}
+		printf("[1-%d", cnt);
+		if (sym->help)
+			printf("?");
+		printf("]: ");
+		switch (input_mode) {
+		case ask_new:
+		case ask_silent:
+			if (!is_new) {
+				cnt = def;
+				printf("%d\n", cnt);
+				break;
+			}
+			check_stdin();
+		case ask_all:
+			fflush(stdout);
+			fgets(line, 128, stdin);
+			strip(line);
+			if (line[0] == '?') {
+				printf("\n%s\n", menu->sym->help ?
+					menu->sym->help : nohelp_text);
+				continue;
+			}
+			if (!line[0])
+				cnt = def;
+			else if (isdigit(line[0]))
+				cnt = atoi(line);
+			else
+				continue;
+			break;
+		case set_random:
+			def = (random() % cnt) + 1;
+		case set_default:
+		case set_yes:
+		case set_mod:
+		case set_no:
+			cnt = def;
+			printf("%d\n", cnt);
+			break;
+		}
+
+	conf_childs:
+		for (child = menu->list; child; child = child->next) {
+			if (!child->sym || !menu_is_visible(child))
+				continue;
+			if (!--cnt)
+				break;
+		}
+		if (!child)
+			continue;
+		if (strlen(line) > 0 && line[strlen(line) - 1] == '?') {
+			printf("\n%s\n", child->sym->help ?
+				child->sym->help : nohelp_text);
+			continue;
+		}
+		sym_set_choice_value(sym, child->sym);
+		if (child->list) {
+			indent += 2;
+			conf(child->list);
+			indent -= 2;
+		}
+		return 1;
+	}
+}
+
+static void conf(struct menu *menu)
+{
+	struct symbol *sym;
+	struct property *prop;
+	struct menu *child;
+
+	if (!menu_is_visible(menu))
+		return;
+
+	sym = menu->sym;
+	prop = menu->prompt;
+	if (prop) {
+		const char *prompt;
+
+		switch (prop->type) {
+		case P_MENU:
+			if (input_mode == ask_silent && rootEntry != menu) {
+				check_conf(menu);
+				return;
+			}
+		case P_COMMENT:
+			prompt = menu_get_prompt(menu);
+			if (prompt)
+				printf("%*c\n%*c %s\n%*c\n",
+					indent, '*',
+					indent, '*', prompt,
+					indent, '*');
+		default:
+			;
+		}
+	}
+
+	if (!sym)
+		goto conf_childs;
+
+	if (sym_is_choice(sym)) {
+		conf_choice(menu);
+		if (sym->curr.tri != mod)
+			return;
+		goto conf_childs;
+	}
+
+	switch (sym->type) {
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		conf_string(menu);
+		break;
+	default:
+		conf_sym(menu);
+		break;
+	}
+
+conf_childs:
+	if (sym)
+		indent += 2;
+	for (child = menu->list; child; child = child->next)
+		conf(child);
+	if (sym)
+		indent -= 2;
+}
+
+static void check_conf(struct menu *menu)
+{
+	struct symbol *sym;
+	struct menu *child;
+
+	if (!menu_is_visible(menu))
+		return;
+
+	sym = menu->sym;
+	if (sym && !sym_has_value(sym)) {
+		if (sym_is_changable(sym) ||
+		    (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
+			if (!conf_cnt++)
+				printf(_("*\n* Restart config...\n*\n"));
+			rootEntry = menu_get_parent_menu(menu);
+			conf(rootEntry);
+		}
+	}
+
+	for (child = menu->list; child; child = child->next)
+		check_conf(child);
+}
+
+int main(int ac, char **av)
+{
+	int i = 1;
+	const char *name;
+	struct stat tmpstat;
+
+	if (ac > i && av[i][0] == '-') {
+		switch (av[i++][1]) {
+		case 'o':
+			input_mode = ask_new;
+			break;
+		case 's':
+			input_mode = ask_silent;
+			valid_stdin = isatty(0); //bbox: && isatty(1) && isatty(2);
+			break;
+		case 'd':
+			input_mode = set_default;
+			break;
+		case 'D':
+			input_mode = set_default;
+			defconfig_file = av[i++];
+			if (!defconfig_file) {
+				printf(_("%s: No default config file specified\n"),
+					av[0]);
+				exit(1);
+			}
+			break;
+		case 'n':
+			input_mode = set_no;
+			break;
+		case 'm':
+			input_mode = set_mod;
+			break;
+		case 'y':
+			input_mode = set_yes;
+			break;
+		case 'r':
+			input_mode = set_random;
+			srandom(time(NULL));
+			break;
+		case 'h':
+		case '?':
+			fprintf(stderr, "See README for usage info\n");
+			exit(0);
+		}
+	}
+	name = av[i];
+	if (!name) {
+		printf(_("%s: Kconfig file missing\n"), av[0]);
+	}
+	conf_parse(name);
+	//zconfdump(stdout);
+	switch (input_mode) {
+	case set_default:
+		if (!defconfig_file)
+			defconfig_file = conf_get_default_confname();
+		if (conf_read(defconfig_file)) {
+			printf("***\n"
+				"*** Can't find default configuration \"%s\"!\n"
+				"***\n", defconfig_file);
+			exit(1);
+		}
+		break;
+	case ask_silent:
+		if (stat(".config", &tmpstat)) {
+			printf(_("***\n"
+				"*** You have not yet configured busybox!\n"
+				"***\n"
+				"*** Please run some configurator (e.g. \"make oldconfig\" or\n"
+				"*** \"make menuconfig\" or \"make defconfig\").\n"
+				"***\n"));
+			exit(1);
+		}
+	case ask_all:
+	case ask_new:
+		conf_read(NULL);
+		break;
+	case set_no:
+	case set_mod:
+	case set_yes:
+	case set_random:
+		name = getenv("KCONFIG_ALLCONFIG");
+		if (name && !stat(name, &tmpstat)) {
+			conf_read_simple(name);
+			break;
+		}
+		switch (input_mode) {
+		case set_no:	 name = "allno.config"; break;
+		case set_mod:	 name = "allmod.config"; break;
+		case set_yes:	 name = "allyes.config"; break;
+		case set_random: name = "allrandom.config"; break;
+		default: break;
+		}
+		if (!stat(name, &tmpstat))
+			conf_read_simple(name);
+		else if (!stat("all.config", &tmpstat))
+			conf_read_simple("all.config");
+		break;
+	default:
+		break;
+	}
+
+	if (input_mode != ask_silent) {
+		rootEntry = &rootmenu;
+		conf(&rootmenu);
+		if (input_mode == ask_all) {
+			input_mode = ask_silent;
+			valid_stdin = 1;
+		}
+	}
+	do {
+		conf_cnt = 0;
+		check_conf(&rootmenu);
+	} while (conf_cnt);
+	if (conf_write(NULL)) {
+		fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n"));
+		return 1;
+	}
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/confdata.c b/busybox-1.19.3/scripts/kconfig/confdata.c
new file mode 100644
index 0000000..bd2d70e
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/confdata.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static void conf_warning(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
+
+static const char *conf_filename;
+static int conf_lineno, conf_warnings, conf_unsaved;
+
+const char conf_def_filename[] = ".config";
+
+const char conf_defname[] = "/dev/null"; //bbox
+
+const char *conf_confnames[] = {
+	conf_def_filename,
+	conf_defname,
+	NULL,
+};
+
+static void conf_warning(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+	conf_warnings++;
+}
+
+static char *conf_expand_value(const char *in)
+{
+	struct symbol *sym;
+	const char *src;
+	static char res_value[SYMBOL_MAXLENGTH];
+	char *dst, name[SYMBOL_MAXLENGTH];
+
+	res_value[0] = 0;
+	dst = name;
+	while ((src = strchr(in, '$'))) {
+		strncat(res_value, in, src - in);
+		src++;
+		dst = name;
+		while (isalnum(*src) || *src == '_')
+			*dst++ = *src++;
+		*dst = 0;
+		sym = sym_lookup(name, 0);
+		sym_calc_value(sym);
+		strcat(res_value, sym_get_string_value(sym));
+		in = src;
+	}
+	strcat(res_value, in);
+
+	return res_value;
+}
+
+char *conf_get_default_confname(void)
+{
+	struct stat buf;
+	static char *fullname = NULL;
+	char *env, *name;
+
+	name = conf_expand_value(conf_defname);
+	env = getenv(SRCTREE);
+	if (env) {
+		fullname = realloc(fullname, strlen(env) + strlen(name) + 2);
+		sprintf(fullname, "%s/%s", env, name);
+		if (!stat(fullname, &buf))
+			return fullname;
+	}
+	return name;
+}
+
+int conf_read_simple(const char *name)
+{
+	FILE *in = NULL;
+	char line[1024];
+	char *p, *p2;
+	struct symbol *sym;
+	int i;
+
+	if (name) {
+		in = zconf_fopen(name);
+	} else {
+		const char **names = conf_confnames;
+		while ((name = *names++)) {
+			name = conf_expand_value(name);
+			in = zconf_fopen(name);
+			if (in) {
+				printf(_("#\n"
+				         "# using defaults found in %s\n"
+				         "#\n"), name);
+				break;
+			}
+		}
+	}
+	if (!in)
+		return 1;
+
+	conf_filename = name;
+	conf_lineno = 0;
+	conf_warnings = 0;
+	conf_unsaved = 0;
+
+	for_all_symbols(i, sym) {
+		sym->flags |= SYMBOL_NEW | SYMBOL_CHANGED;
+		if (sym_is_choice(sym))
+			sym->flags &= ~SYMBOL_NEW;
+		sym->flags &= ~SYMBOL_VALID;
+		switch (sym->type) {
+		case S_INT:
+		case S_HEX:
+		case S_STRING:
+			if (sym->user.val)
+				free(sym->user.val);
+		default:
+			sym->user.val = NULL;
+			sym->user.tri = no;
+		}
+	}
+
+	while (fgets(line, sizeof(line), in)) {
+		conf_lineno++;
+		sym = NULL;
+		switch (line[0]) {
+		case '#':
+			if (memcmp(line + 2, "CONFIG_", 7))
+				continue;
+			p = strchr(line + 9, ' ');
+			if (!p)
+				continue;
+			*p++ = 0;
+			if (strncmp(p, "is not set", 10))
+				continue;
+			sym = sym_find(line + 9);
+			if (!sym) {
+				conf_warning("trying to assign nonexistent symbol %s", line + 9);
+				break;
+			} else if (!(sym->flags & SYMBOL_NEW)) {
+				conf_warning("trying to reassign symbol %s", sym->name);
+				break;
+			}
+			switch (sym->type) {
+			case S_BOOLEAN:
+			case S_TRISTATE:
+				sym->user.tri = no;
+				sym->flags &= ~SYMBOL_NEW;
+				break;
+			default:
+				;
+			}
+			break;
+		case 'C':
+			if (memcmp(line, "CONFIG_", 7)) {
+				conf_warning("unexpected data");
+				continue;
+			}
+			p = strchr(line + 7, '=');
+			if (!p)
+				continue;
+			*p++ = 0;
+			p2 = strchr(p, '\n');
+			if (p2)
+				*p2 = 0;
+			sym = sym_find(line + 7);
+			if (!sym) {
+				conf_warning("trying to assign nonexistent symbol %s", line + 7);
+				break;
+			} else if (!(sym->flags & SYMBOL_NEW)) {
+				conf_warning("trying to reassign symbol %s", sym->name);
+				break;
+			}
+			switch (sym->type) {
+			case S_TRISTATE:
+				if (p[0] == 'm') {
+					sym->user.tri = mod;
+					sym->flags &= ~SYMBOL_NEW;
+					break;
+				}
+			case S_BOOLEAN:
+				if (p[0] == 'y') {
+					sym->user.tri = yes;
+					sym->flags &= ~SYMBOL_NEW;
+					break;
+				}
+				if (p[0] == 'n') {
+					sym->user.tri = no;
+					sym->flags &= ~SYMBOL_NEW;
+					break;
+				}
+				conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+				break;
+			case S_STRING:
+				if (*p++ != '"')
+					break;
+				for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+					if (*p2 == '"') {
+						*p2 = 0;
+						break;
+					}
+					memmove(p2, p2 + 1, strlen(p2));
+				}
+				if (!p2) {
+					conf_warning("invalid string found");
+					continue;
+				}
+			case S_INT:
+			case S_HEX:
+				if (sym_string_valid(sym, p)) {
+					sym->user.val = strdup(p);
+					sym->flags &= ~SYMBOL_NEW;
+				} else {
+					if (p[0]) /* bbox */
+						conf_warning("symbol value '%s' invalid for %s", p, sym->name);
+					continue;
+				}
+				break;
+			default:
+				;
+			}
+			break;
+		case '\n':
+			break;
+		default:
+			conf_warning("unexpected data");
+			continue;
+		}
+		if (sym && sym_is_choice_value(sym)) {
+			struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+			switch (sym->user.tri) {
+			case no:
+				break;
+			case mod:
+				if (cs->user.tri == yes) {
+					conf_warning("%s creates inconsistent choice state", sym->name);
+					cs->flags |= SYMBOL_NEW;
+				}
+				break;
+			case yes:
+				if (cs->user.tri != no) {
+					conf_warning("%s creates inconsistent choice state", sym->name);
+					cs->flags |= SYMBOL_NEW;
+				} else
+					cs->user.val = sym;
+				break;
+			}
+			cs->user.tri = E_OR(cs->user.tri, sym->user.tri);
+		}
+	}
+	fclose(in);
+
+	if (modules_sym)
+		sym_calc_value(modules_sym);
+	return 0;
+}
+
+int conf_read(const char *name)
+{
+	struct symbol *sym;
+	struct property *prop;
+	struct expr *e;
+	int i;
+
+	if (conf_read_simple(name))
+		return 1;
+
+	for_all_symbols(i, sym) {
+		sym_calc_value(sym);
+		if (sym_is_choice(sym) || (sym->flags & SYMBOL_AUTO))
+			goto sym_ok;
+		if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
+			/* check that calculated value agrees with saved value */
+			switch (sym->type) {
+			case S_BOOLEAN:
+			case S_TRISTATE:
+				if (sym->user.tri != sym_get_tristate_value(sym))
+					break;
+				if (!sym_is_choice(sym))
+					goto sym_ok;
+			default:
+				if (!strcmp(sym->curr.val, sym->user.val))
+					goto sym_ok;
+				break;
+			}
+		} else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
+			/* no previous value and not saved */
+			goto sym_ok;
+		conf_unsaved++;
+		/* maybe print value in verbose mode... */
+	sym_ok:
+		if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
+			if (sym->visible == no)
+				sym->flags |= SYMBOL_NEW;
+			switch (sym->type) {
+			case S_STRING:
+			case S_INT:
+			case S_HEX:
+				if (!sym_string_within_range(sym, sym->user.val)) {
+					sym->flags |= SYMBOL_NEW;
+					sym->flags &= ~SYMBOL_VALID;
+				}
+			default:
+				break;
+			}
+		}
+		if (!sym_is_choice(sym))
+			continue;
+		prop = sym_get_choice_prop(sym);
+		for (e = prop->expr; e; e = e->left.expr)
+			if (e->right.sym->visible != no)
+				sym->flags |= e->right.sym->flags & SYMBOL_NEW;
+	}
+
+	sym_change_count = conf_warnings || conf_unsaved;
+
+	return 0;
+}
+
+int conf_write(const char *name)
+{
+	FILE *out, *out_h;
+	struct symbol *sym;
+	struct menu *menu;
+	const char *basename;
+	char dirname[128], tmpname[128], newname[128];
+	int type, l;
+	const char *str;
+	time_t now;
+	int use_timestamp = 1;
+	char *env;
+
+	dirname[0] = 0;
+	if (name && name[0]) {
+		struct stat st;
+		char *slash;
+
+		if (!stat(name, &st) && S_ISDIR(st.st_mode)) {
+			strcpy(dirname, name);
+			strcat(dirname, "/");
+			basename = conf_def_filename;
+		} else if ((slash = strrchr(name, '/'))) {
+			int size = slash - name + 1;
+			memcpy(dirname, name, size);
+			dirname[size] = 0;
+			if (slash[1])
+				basename = slash + 1;
+			else
+				basename = conf_def_filename;
+		} else
+			basename = name;
+	} else
+		basename = conf_def_filename;
+
+	sprintf(newname, "%s.tmpconfig.%d", dirname, (int)getpid());
+	out = fopen(newname, "w");
+	if (!out)
+		return 1;
+	out_h = NULL;
+	if (!name) {
+		out_h = fopen(".tmpconfig.h", "w");
+		if (!out_h)
+			return 1;
+		file_write_dep(NULL);
+	}
+	sym = sym_lookup("KERNELVERSION", 0);
+	sym_calc_value(sym);
+	time(&now);
+	env = getenv("KCONFIG_NOTIMESTAMP");
+	if (env && *env)
+		use_timestamp = 0;
+
+	fprintf(out, _("#\n"
+		       "# Automatically generated make config: don't edit\n"
+		       "# Busybox version: %s\n"
+		       "%s%s"
+		       "#\n"),
+		     sym_get_string_value(sym),
+		     use_timestamp ? "# " : "",
+		     use_timestamp ? ctime(&now) : "");
+	if (out_h) {
+		char buf[sizeof("#define AUTOCONF_TIMESTAMP "
+				"\"YYYY-MM-DD HH:MM:SS some_timezone\"\n")];
+		buf[0] = '\0';
+		if (use_timestamp) {
+			size_t ret = \
+				strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
+					"\"%Y-%m-%d %H:%M:%S %Z\"\n", localtime(&now));
+			/* if user has Factory timezone or some other odd install, the
+			 * %Z above will overflow the string leaving us with undefined
+			 * results ... so let's try again without the timezone.
+			 */
+			if (ret == 0)
+				strftime(buf, sizeof(buf), "#define AUTOCONF_TIMESTAMP "
+					"\"%Y-%m-%d %H:%M:%S\"\n", localtime(&now));
+		} else { /* bbox */
+			strcpy(buf, "#define AUTOCONF_TIMESTAMP \"\"\n");
+		}
+		fprintf(out_h, "/*\n"
+			       " * Automatically generated C config: don't edit\n"
+			       " * Busybox version: %s\n"
+			       " */\n"
+			       "%s"
+			       "\n",
+			       sym_get_string_value(sym),
+			       buf);
+	}
+	if (!sym_change_count)
+		sym_clear_all_valid();
+
+	menu = rootmenu.list;
+	while (menu) {
+		sym = menu->sym;
+		if (!sym) {
+			if (!menu_is_visible(menu))
+				goto next;
+			str = menu_get_prompt(menu);
+			fprintf(out, "\n"
+				     "#\n"
+				     "# %s\n"
+				     "#\n", str);
+			if (out_h)
+				fprintf(out_h, "\n"
+					       "/*\n"
+					       " * %s\n"
+					       " */\n", str);
+		} else if (!(sym->flags & SYMBOL_CHOICE)) {
+			sym_calc_value(sym);
+/* bbox: we want to see all syms
+			if (!(sym->flags & SYMBOL_WRITE))
+				goto next;
+*/
+			sym->flags &= ~SYMBOL_WRITE;
+			type = sym->type;
+			if (type == S_TRISTATE) {
+				sym_calc_value(modules_sym);
+				if (modules_sym->curr.tri == no)
+					type = S_BOOLEAN;
+			}
+			switch (type) {
+			case S_BOOLEAN:
+			case S_TRISTATE:
+				switch (sym_get_tristate_value(sym)) {
+				case no:
+					fprintf(out, "# CONFIG_%s is not set\n", sym->name);
+					if (out_h) {
+						fprintf(out_h, "#undef CONFIG_%s\n", sym->name);
+						/* bbox */
+						fprintf(out_h, "#define ENABLE_%s 0\n", sym->name);
+						fprintf(out_h, "#define IF_%s(...)\n", sym->name);
+						fprintf(out_h, "#define IF_NOT_%s(...) __VA_ARGS__\n", sym->name);
+					}
+					break;
+				case mod:
+					fprintf(out, "CONFIG_%s=m\n", sym->name);
+					if (out_h)
+						fprintf(out_h, "#define CONFIG_%s_MODULE 1\n", sym->name);
+					break;
+				case yes:
+					fprintf(out, "CONFIG_%s=y\n", sym->name);
+					if (out_h) {
+						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, "#define IF_NOT_%s(...)\n", sym->name);
+					}
+					break;
+				}
+				break;
+			case S_STRING:
+				// fix me
+				str = sym_get_string_value(sym);
+				fprintf(out, "CONFIG_%s=\"", sym->name);
+				if (out_h)
+					fprintf(out_h, "#define CONFIG_%s \"", sym->name);
+				do {
+					l = strcspn(str, "\"\\");
+					if (l) {
+						fwrite(str, l, 1, out);
+						if (out_h)
+							fwrite(str, l, 1, out_h);
+					}
+					str += l;
+					while (*str == '\\' || *str == '"') {
+						fprintf(out, "\\%c", *str);
+						if (out_h)
+							fprintf(out_h, "\\%c", *str);
+						str++;
+					}
+				} while (*str);
+				fputs("\"\n", out);
+				if (out_h) {
+					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, "#define IF_NOT_%s(...)\n", sym->name);
+				}
+				break;
+			case S_HEX:
+				str = sym_get_string_value(sym);
+				if (str[0] != '0' || (str[1] != 'x' && str[1] != 'X')) {
+					fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
+					if (out_h) {
+						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, "#define IF_NOT_%s(...)\n", sym->name);
+					}
+					break;
+				}
+			case S_INT:
+				str = sym_get_string_value(sym);
+				if (!str[0])
+					str = "0";
+				fprintf(out, "CONFIG_%s=%s\n", sym->name, str);
+				if (out_h) {
+					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, "#define IF_NOT_%s(...)\n", sym->name);
+				}
+				break;
+			}
+		}
+
+	next:
+		if (menu->list) {
+			menu = menu->list;
+			continue;
+		}
+		if (menu->next)
+			menu = menu->next;
+		else while ((menu = menu->parent)) {
+			if (menu->next) {
+				menu = menu->next;
+				break;
+			}
+		}
+	}
+	fclose(out);
+	if (out_h) {
+		fclose(out_h);
+		rename(".tmpconfig.h", "include/autoconf.h");
+	}
+	if (!name || basename != conf_def_filename) {
+		if (!name)
+			name = conf_def_filename;
+		sprintf(tmpname, "%s.old", name);
+		rename(name, tmpname);
+	}
+	sprintf(tmpname, "%s%s", dirname, basename);
+	if (rename(newname, tmpname))
+		return 1;
+
+	sym_change_count = 0;
+
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/expr.c b/busybox-1.19.3/scripts/kconfig/expr.c
new file mode 100644
index 0000000..6f39e7a
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/expr.c
@@ -0,0 +1,1099 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define DEBUG_EXPR	0
+
+struct expr *expr_alloc_symbol(struct symbol *sym)
+{
+	struct expr *e = malloc(sizeof(*e));
+	memset(e, 0, sizeof(*e));
+	e->type = E_SYMBOL;
+	e->left.sym = sym;
+	return e;
+}
+
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
+{
+	struct expr *e = malloc(sizeof(*e));
+	memset(e, 0, sizeof(*e));
+	e->type = type;
+	e->left.expr = ce;
+	return e;
+}
+
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
+{
+	struct expr *e = malloc(sizeof(*e));
+	memset(e, 0, sizeof(*e));
+	e->type = type;
+	e->left.expr = e1;
+	e->right.expr = e2;
+	return e;
+}
+
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
+{
+	struct expr *e = malloc(sizeof(*e));
+	memset(e, 0, sizeof(*e));
+	e->type = type;
+	e->left.sym = s1;
+	e->right.sym = s2;
+	return e;
+}
+
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
+{
+	if (!e1)
+		return e2;
+	return e2 ? expr_alloc_two(E_AND, e1, e2) : e1;
+}
+
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
+{
+	if (!e1)
+		return e2;
+	return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
+}
+
+struct expr *expr_copy(struct expr *org)
+{
+	struct expr *e;
+
+	if (!org)
+		return NULL;
+
+	e = malloc(sizeof(*org));
+	memcpy(e, org, sizeof(*org));
+	switch (org->type) {
+	case E_SYMBOL:
+		e->left = org->left;
+		break;
+	case E_NOT:
+		e->left.expr = expr_copy(org->left.expr);
+		break;
+	case E_EQUAL:
+	case E_UNEQUAL:
+		e->left.sym = org->left.sym;
+		e->right.sym = org->right.sym;
+		break;
+	case E_AND:
+	case E_OR:
+	case E_CHOICE:
+		e->left.expr = expr_copy(org->left.expr);
+		e->right.expr = expr_copy(org->right.expr);
+		break;
+	default:
+		printf("can't copy type %d\n", e->type);
+		free(e);
+		e = NULL;
+		break;
+	}
+
+	return e;
+}
+
+void expr_free(struct expr *e)
+{
+	if (!e)
+		return;
+
+	switch (e->type) {
+	case E_SYMBOL:
+		break;
+	case E_NOT:
+		expr_free(e->left.expr);
+		return;
+	case E_EQUAL:
+	case E_UNEQUAL:
+		break;
+	case E_OR:
+	case E_AND:
+		expr_free(e->left.expr);
+		expr_free(e->right.expr);
+		break;
+	default:
+		printf("how to free type %d?\n", e->type);
+		break;
+	}
+	free(e);
+}
+
+static int trans_count;
+
+#define e1 (*ep1)
+#define e2 (*ep2)
+
+static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+	if (e1->type == type) {
+		__expr_eliminate_eq(type, &e1->left.expr, &e2);
+		__expr_eliminate_eq(type, &e1->right.expr, &e2);
+		return;
+	}
+	if (e2->type == type) {
+		__expr_eliminate_eq(type, &e1, &e2->left.expr);
+		__expr_eliminate_eq(type, &e1, &e2->right.expr);
+		return;
+	}
+	if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+	    e1->left.sym == e2->left.sym && (e1->left.sym->flags & (SYMBOL_YES|SYMBOL_NO)))
+		return;
+	if (!expr_eq(e1, e2))
+		return;
+	trans_count++;
+	expr_free(e1); expr_free(e2);
+	switch (type) {
+	case E_OR:
+		e1 = expr_alloc_symbol(&symbol_no);
+		e2 = expr_alloc_symbol(&symbol_no);
+		break;
+	case E_AND:
+		e1 = expr_alloc_symbol(&symbol_yes);
+		e2 = expr_alloc_symbol(&symbol_yes);
+		break;
+	default:
+		;
+	}
+}
+
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+{
+	if (!e1 || !e2)
+		return;
+	switch (e1->type) {
+	case E_OR:
+	case E_AND:
+		__expr_eliminate_eq(e1->type, ep1, ep2);
+	default:
+		;
+	}
+	if (e1->type != e2->type) switch (e2->type) {
+	case E_OR:
+	case E_AND:
+		__expr_eliminate_eq(e2->type, ep1, ep2);
+	default:
+		;
+	}
+	e1 = expr_eliminate_yn(e1);
+	e2 = expr_eliminate_yn(e2);
+}
+
+#undef e1
+#undef e2
+
+int expr_eq(struct expr *e1, struct expr *e2)
+{
+	int res, old_count;
+
+	if (e1->type != e2->type)
+		return 0;
+	switch (e1->type) {
+	case E_EQUAL:
+	case E_UNEQUAL:
+		return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
+	case E_SYMBOL:
+		return e1->left.sym == e2->left.sym;
+	case E_NOT:
+		return expr_eq(e1->left.expr, e2->left.expr);
+	case E_AND:
+	case E_OR:
+		e1 = expr_copy(e1);
+		e2 = expr_copy(e2);
+		old_count = trans_count;
+		expr_eliminate_eq(&e1, &e2);
+		res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+		       e1->left.sym == e2->left.sym);
+		expr_free(e1);
+		expr_free(e2);
+		trans_count = old_count;
+		return res;
+	case E_CHOICE:
+	case E_RANGE:
+	case E_NONE:
+		/* panic */;
+	}
+
+	if (DEBUG_EXPR) {
+		expr_fprint(e1, stdout);
+		printf(" = ");
+		expr_fprint(e2, stdout);
+		printf(" ?\n");
+	}
+
+	return 0;
+}
+
+struct expr *expr_eliminate_yn(struct expr *e)
+{
+	struct expr *tmp;
+
+	if (e) switch (e->type) {
+	case E_AND:
+		e->left.expr = expr_eliminate_yn(e->left.expr);
+		e->right.expr = expr_eliminate_yn(e->right.expr);
+		if (e->left.expr->type == E_SYMBOL) {
+			if (e->left.expr->left.sym == &symbol_no) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_no;
+				e->right.expr = NULL;
+				return e;
+			} else if (e->left.expr->left.sym == &symbol_yes) {
+				free(e->left.expr);
+				tmp = e->right.expr;
+				*e = *(e->right.expr);
+				free(tmp);
+				return e;
+			}
+		}
+		if (e->right.expr->type == E_SYMBOL) {
+			if (e->right.expr->left.sym == &symbol_no) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_no;
+				e->right.expr = NULL;
+				return e;
+			} else if (e->right.expr->left.sym == &symbol_yes) {
+				free(e->right.expr);
+				tmp = e->left.expr;
+				*e = *(e->left.expr);
+				free(tmp);
+				return e;
+			}
+		}
+		break;
+	case E_OR:
+		e->left.expr = expr_eliminate_yn(e->left.expr);
+		e->right.expr = expr_eliminate_yn(e->right.expr);
+		if (e->left.expr->type == E_SYMBOL) {
+			if (e->left.expr->left.sym == &symbol_no) {
+				free(e->left.expr);
+				tmp = e->right.expr;
+				*e = *(e->right.expr);
+				free(tmp);
+				return e;
+			} else if (e->left.expr->left.sym == &symbol_yes) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_yes;
+				e->right.expr = NULL;
+				return e;
+			}
+		}
+		if (e->right.expr->type == E_SYMBOL) {
+			if (e->right.expr->left.sym == &symbol_no) {
+				free(e->right.expr);
+				tmp = e->left.expr;
+				*e = *(e->left.expr);
+				free(tmp);
+				return e;
+			} else if (e->right.expr->left.sym == &symbol_yes) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_yes;
+				e->right.expr = NULL;
+				return e;
+			}
+		}
+		break;
+	default:
+		;
+	}
+	return e;
+}
+
+/*
+ * bool FOO!=n => FOO
+ */
+struct expr *expr_trans_bool(struct expr *e)
+{
+	if (!e)
+		return NULL;
+	switch (e->type) {
+	case E_AND:
+	case E_OR:
+	case E_NOT:
+		e->left.expr = expr_trans_bool(e->left.expr);
+		e->right.expr = expr_trans_bool(e->right.expr);
+		break;
+	case E_UNEQUAL:
+		// FOO!=n -> FOO
+		if (e->left.sym->type == S_TRISTATE) {
+			if (e->right.sym == &symbol_no) {
+				e->type = E_SYMBOL;
+				e->right.sym = NULL;
+			}
+		}
+		break;
+	default:
+		;
+	}
+	return e;
+}
+
+/*
+ * e1 || e2 -> ?
+ */
+struct expr *expr_join_or(struct expr *e1, struct expr *e2)
+{
+	struct expr *tmp;
+	struct symbol *sym1, *sym2;
+
+	if (expr_eq(e1, e2))
+		return expr_copy(e1);
+	if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+		return NULL;
+	if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+		return NULL;
+	if (e1->type == E_NOT) {
+		tmp = e1->left.expr;
+		if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+			return NULL;
+		sym1 = tmp->left.sym;
+	} else
+		sym1 = e1->left.sym;
+	if (e2->type == E_NOT) {
+		if (e2->left.expr->type != E_SYMBOL)
+			return NULL;
+		sym2 = e2->left.expr->left.sym;
+	} else
+		sym2 = e2->left.sym;
+	if (sym1 != sym2)
+		return NULL;
+	if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+		return NULL;
+	if (sym1->type == S_TRISTATE) {
+		if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+		    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+		     (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
+			// (a='y') || (a='m') -> (a!='n')
+			return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
+		}
+		if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+		    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+		     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
+			// (a='y') || (a='n') -> (a!='m')
+			return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
+		}
+		if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+		    ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+		     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
+			// (a='m') || (a='n') -> (a!='y')
+			return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
+		}
+	}
+	if (sym1->type == S_BOOLEAN && sym1 == sym2) {
+		if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
+		    (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
+			return expr_alloc_symbol(&symbol_yes);
+	}
+
+	if (DEBUG_EXPR) {
+		printf("optimize (");
+		expr_fprint(e1, stdout);
+		printf(") || (");
+		expr_fprint(e2, stdout);
+		printf(")?\n");
+	}
+	return NULL;
+}
+
+struct expr *expr_join_and(struct expr *e1, struct expr *e2)
+{
+	struct expr *tmp;
+	struct symbol *sym1, *sym2;
+
+	if (expr_eq(e1, e2))
+		return expr_copy(e1);
+	if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+		return NULL;
+	if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+		return NULL;
+	if (e1->type == E_NOT) {
+		tmp = e1->left.expr;
+		if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+			return NULL;
+		sym1 = tmp->left.sym;
+	} else
+		sym1 = e1->left.sym;
+	if (e2->type == E_NOT) {
+		if (e2->left.expr->type != E_SYMBOL)
+			return NULL;
+		sym2 = e2->left.expr->left.sym;
+	} else
+		sym2 = e2->left.sym;
+	if (sym1 != sym2)
+		return NULL;
+	if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+		return NULL;
+
+	if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
+	    (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
+		// (a) && (a='y') -> (a='y')
+		return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+	if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
+	    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
+		// (a) && (a!='n') -> (a)
+		return expr_alloc_symbol(sym1);
+
+	if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
+	    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
+		// (a) && (a!='m') -> (a='y')
+		return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+	if (sym1->type == S_TRISTATE) {
+		if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
+			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+			sym2 = e1->right.sym;
+			if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+				return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+							     : expr_alloc_symbol(&symbol_no);
+		}
+		if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
+			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+			sym2 = e2->right.sym;
+			if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+				return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+							     : expr_alloc_symbol(&symbol_no);
+		}
+		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
+			// (a!='y') && (a!='n') -> (a='m')
+			return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);
+
+		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+			    (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
+			// (a!='y') && (a!='m') -> (a='n')
+			return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);
+
+		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+			   ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
+			// (a!='m') && (a!='n') -> (a='m')
+			return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+		if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
+		    (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
+		    (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
+		    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
+			return NULL;
+	}
+
+	if (DEBUG_EXPR) {
+		printf("optimize (");
+		expr_fprint(e1, stdout);
+		printf(") && (");
+		expr_fprint(e2, stdout);
+		printf(")?\n");
+	}
+	return NULL;
+}
+
+static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+	struct expr *tmp;
+
+	if (e1->type == type) {
+		expr_eliminate_dups1(type, &e1->left.expr, &e2);
+		expr_eliminate_dups1(type, &e1->right.expr, &e2);
+		return;
+	}
+	if (e2->type == type) {
+		expr_eliminate_dups1(type, &e1, &e2->left.expr);
+		expr_eliminate_dups1(type, &e1, &e2->right.expr);
+		return;
+	}
+	if (e1 == e2)
+		return;
+
+	switch (e1->type) {
+	case E_OR: case E_AND:
+		expr_eliminate_dups1(e1->type, &e1, &e1);
+	default:
+		;
+	}
+
+	switch (type) {
+	case E_OR:
+		tmp = expr_join_or(e1, e2);
+		if (tmp) {
+			expr_free(e1); expr_free(e2);
+			e1 = expr_alloc_symbol(&symbol_no);
+			e2 = tmp;
+			trans_count++;
+		}
+		break;
+	case E_AND:
+		tmp = expr_join_and(e1, e2);
+		if (tmp) {
+			expr_free(e1); expr_free(e2);
+			e1 = expr_alloc_symbol(&symbol_yes);
+			e2 = tmp;
+			trans_count++;
+		}
+		break;
+	default:
+		;
+	}
+#undef e1
+#undef e2
+}
+
+static void expr_eliminate_dups2(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+	struct expr *tmp, *tmp1, *tmp2;
+
+	if (e1->type == type) {
+		expr_eliminate_dups2(type, &e1->left.expr, &e2);
+		expr_eliminate_dups2(type, &e1->right.expr, &e2);
+		return;
+	}
+	if (e2->type == type) {
+		expr_eliminate_dups2(type, &e1, &e2->left.expr);
+		expr_eliminate_dups2(type, &e1, &e2->right.expr);
+	}
+	if (e1 == e2)
+		return;
+
+	switch (e1->type) {
+	case E_OR:
+		expr_eliminate_dups2(e1->type, &e1, &e1);
+		// (FOO || BAR) && (!FOO && !BAR) -> n
+		tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+		tmp2 = expr_copy(e2);
+		tmp = expr_extract_eq_and(&tmp1, &tmp2);
+		if (expr_is_yes(tmp1)) {
+			expr_free(e1);
+			e1 = expr_alloc_symbol(&symbol_no);
+			trans_count++;
+		}
+		expr_free(tmp2);
+		expr_free(tmp1);
+		expr_free(tmp);
+		break;
+	case E_AND:
+		expr_eliminate_dups2(e1->type, &e1, &e1);
+		// (FOO && BAR) || (!FOO || !BAR) -> y
+		tmp1 = expr_transform(expr_alloc_one(E_NOT, expr_copy(e1)));
+		tmp2 = expr_copy(e2);
+		tmp = expr_extract_eq_or(&tmp1, &tmp2);
+		if (expr_is_no(tmp1)) {
+			expr_free(e1);
+			e1 = expr_alloc_symbol(&symbol_yes);
+			trans_count++;
+		}
+		expr_free(tmp2);
+		expr_free(tmp1);
+		expr_free(tmp);
+		break;
+	default:
+		;
+	}
+#undef e1
+#undef e2
+}
+
+struct expr *expr_eliminate_dups(struct expr *e)
+{
+	int oldcount;
+	if (!e)
+		return e;
+
+	oldcount = trans_count;
+	while (1) {
+		trans_count = 0;
+		switch (e->type) {
+		case E_OR: case E_AND:
+			expr_eliminate_dups1(e->type, &e, &e);
+			expr_eliminate_dups2(e->type, &e, &e);
+		default:
+			;
+		}
+		if (!trans_count)
+			break;
+		e = expr_eliminate_yn(e);
+	}
+	trans_count = oldcount;
+	return e;
+}
+
+struct expr *expr_transform(struct expr *e)
+{
+	struct expr *tmp;
+
+	if (!e)
+		return NULL;
+	switch (e->type) {
+	case E_EQUAL:
+	case E_UNEQUAL:
+	case E_SYMBOL:
+	case E_CHOICE:
+		break;
+	default:
+		e->left.expr = expr_transform(e->left.expr);
+		e->right.expr = expr_transform(e->right.expr);
+	}
+
+	switch (e->type) {
+	case E_EQUAL:
+		if (e->left.sym->type != S_BOOLEAN)
+			break;
+		if (e->right.sym == &symbol_no) {
+			e->type = E_NOT;
+			e->left.expr = expr_alloc_symbol(e->left.sym);
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_mod) {
+			printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
+			e->type = E_SYMBOL;
+			e->left.sym = &symbol_no;
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_yes) {
+			e->type = E_SYMBOL;
+			e->right.sym = NULL;
+			break;
+		}
+		break;
+	case E_UNEQUAL:
+		if (e->left.sym->type != S_BOOLEAN)
+			break;
+		if (e->right.sym == &symbol_no) {
+			e->type = E_SYMBOL;
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_mod) {
+			printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
+			e->type = E_SYMBOL;
+			e->left.sym = &symbol_yes;
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_yes) {
+			e->type = E_NOT;
+			e->left.expr = expr_alloc_symbol(e->left.sym);
+			e->right.sym = NULL;
+			break;
+		}
+		break;
+	case E_NOT:
+		switch (e->left.expr->type) {
+		case E_NOT:
+			// !!a -> a
+			tmp = e->left.expr->left.expr;
+			free(e->left.expr);
+			free(e);
+			e = tmp;
+			e = expr_transform(e);
+			break;
+		case E_EQUAL:
+		case E_UNEQUAL:
+			// !a='x' -> a!='x'
+			tmp = e->left.expr;
+			free(e);
+			e = tmp;
+			e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
+			break;
+		case E_OR:
+			// !(a || b) -> !a && !b
+			tmp = e->left.expr;
+			e->type = E_AND;
+			e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+			tmp->type = E_NOT;
+			tmp->right.expr = NULL;
+			e = expr_transform(e);
+			break;
+		case E_AND:
+			// !(a && b) -> !a || !b
+			tmp = e->left.expr;
+			e->type = E_OR;
+			e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+			tmp->type = E_NOT;
+			tmp->right.expr = NULL;
+			e = expr_transform(e);
+			break;
+		case E_SYMBOL:
+			if (e->left.expr->left.sym == &symbol_yes) {
+				// !'y' -> 'n'
+				tmp = e->left.expr;
+				free(e);
+				e = tmp;
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_no;
+				break;
+			}
+			if (e->left.expr->left.sym == &symbol_mod) {
+				// !'m' -> 'm'
+				tmp = e->left.expr;
+				free(e);
+				e = tmp;
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_mod;
+				break;
+			}
+			if (e->left.expr->left.sym == &symbol_no) {
+				// !'n' -> 'y'
+				tmp = e->left.expr;
+				free(e);
+				e = tmp;
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_yes;
+				break;
+			}
+			break;
+		default:
+			;
+		}
+		break;
+	default:
+		;
+	}
+	return e;
+}
+
+int expr_contains_symbol(struct expr *dep, struct symbol *sym)
+{
+	if (!dep)
+		return 0;
+
+	switch (dep->type) {
+	case E_AND:
+	case E_OR:
+		return expr_contains_symbol(dep->left.expr, sym) ||
+		       expr_contains_symbol(dep->right.expr, sym);
+	case E_SYMBOL:
+		return dep->left.sym == sym;
+	case E_EQUAL:
+	case E_UNEQUAL:
+		return dep->left.sym == sym ||
+		       dep->right.sym == sym;
+	case E_NOT:
+		return expr_contains_symbol(dep->left.expr, sym);
+	default:
+		;
+	}
+	return 0;
+}
+
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
+{
+	if (!dep)
+		return false;
+
+	switch (dep->type) {
+	case E_AND:
+		return expr_depends_symbol(dep->left.expr, sym) ||
+		       expr_depends_symbol(dep->right.expr, sym);
+	case E_SYMBOL:
+		return dep->left.sym == sym;
+	case E_EQUAL:
+		if (dep->left.sym == sym) {
+			if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod)
+				return true;
+		}
+		break;
+	case E_UNEQUAL:
+		if (dep->left.sym == sym) {
+			if (dep->right.sym == &symbol_no)
+				return true;
+		}
+		break;
+	default:
+		;
+	}
+	return false;
+}
+
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2)
+{
+	struct expr *tmp = NULL;
+	expr_extract_eq(E_AND, &tmp, ep1, ep2);
+	if (tmp) {
+		*ep1 = expr_eliminate_yn(*ep1);
+		*ep2 = expr_eliminate_yn(*ep2);
+	}
+	return tmp;
+}
+
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2)
+{
+	struct expr *tmp = NULL;
+	expr_extract_eq(E_OR, &tmp, ep1, ep2);
+	if (tmp) {
+		*ep1 = expr_eliminate_yn(*ep1);
+		*ep2 = expr_eliminate_yn(*ep2);
+	}
+	return tmp;
+}
+
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+	if (e1->type == type) {
+		expr_extract_eq(type, ep, &e1->left.expr, &e2);
+		expr_extract_eq(type, ep, &e1->right.expr, &e2);
+		return;
+	}
+	if (e2->type == type) {
+		expr_extract_eq(type, ep, ep1, &e2->left.expr);
+		expr_extract_eq(type, ep, ep1, &e2->right.expr);
+		return;
+	}
+	if (expr_eq(e1, e2)) {
+		*ep = *ep ? expr_alloc_two(type, *ep, e1) : e1;
+		expr_free(e2);
+		if (type == E_AND) {
+			e1 = expr_alloc_symbol(&symbol_yes);
+			e2 = expr_alloc_symbol(&symbol_yes);
+		} else if (type == E_OR) {
+			e1 = expr_alloc_symbol(&symbol_no);
+			e2 = expr_alloc_symbol(&symbol_no);
+		}
+	}
+#undef e1
+#undef e2
+}
+
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
+{
+	struct expr *e1, *e2;
+
+	if (!e) {
+		e = expr_alloc_symbol(sym);
+		if (type == E_UNEQUAL)
+			e = expr_alloc_one(E_NOT, e);
+		return e;
+	}
+	switch (e->type) {
+	case E_AND:
+		e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+		e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+		if (sym == &symbol_yes)
+			e = expr_alloc_two(E_AND, e1, e2);
+		if (sym == &symbol_no)
+			e = expr_alloc_two(E_OR, e1, e2);
+		if (type == E_UNEQUAL)
+			e = expr_alloc_one(E_NOT, e);
+		return e;
+	case E_OR:
+		e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+		e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+		if (sym == &symbol_yes)
+			e = expr_alloc_two(E_OR, e1, e2);
+		if (sym == &symbol_no)
+			e = expr_alloc_two(E_AND, e1, e2);
+		if (type == E_UNEQUAL)
+			e = expr_alloc_one(E_NOT, e);
+		return e;
+	case E_NOT:
+		return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
+	case E_UNEQUAL:
+	case E_EQUAL:
+		if (type == E_EQUAL) {
+			if (sym == &symbol_yes)
+				return expr_copy(e);
+			if (sym == &symbol_mod)
+				return expr_alloc_symbol(&symbol_no);
+			if (sym == &symbol_no)
+				return expr_alloc_one(E_NOT, expr_copy(e));
+		} else {
+			if (sym == &symbol_yes)
+				return expr_alloc_one(E_NOT, expr_copy(e));
+			if (sym == &symbol_mod)
+				return expr_alloc_symbol(&symbol_yes);
+			if (sym == &symbol_no)
+				return expr_copy(e);
+		}
+		break;
+	case E_SYMBOL:
+		return expr_alloc_comp(type, e->left.sym, sym);
+	case E_CHOICE:
+	case E_RANGE:
+	case E_NONE:
+		/* panic */;
+	}
+	return NULL;
+}
+
+tristate expr_calc_value(struct expr *e)
+{
+	tristate val1, val2;
+	const char *str1, *str2;
+
+	if (!e)
+		return yes;
+
+	switch (e->type) {
+	case E_SYMBOL:
+		sym_calc_value(e->left.sym);
+		return e->left.sym->curr.tri;
+	case E_AND:
+		val1 = expr_calc_value(e->left.expr);
+		val2 = expr_calc_value(e->right.expr);
+		return E_AND(val1, val2);
+	case E_OR:
+		val1 = expr_calc_value(e->left.expr);
+		val2 = expr_calc_value(e->right.expr);
+		return E_OR(val1, val2);
+	case E_NOT:
+		val1 = expr_calc_value(e->left.expr);
+		return E_NOT(val1);
+	case E_EQUAL:
+		sym_calc_value(e->left.sym);
+		sym_calc_value(e->right.sym);
+		str1 = sym_get_string_value(e->left.sym);
+		str2 = sym_get_string_value(e->right.sym);
+		return !strcmp(str1, str2) ? yes : no;
+	case E_UNEQUAL:
+		sym_calc_value(e->left.sym);
+		sym_calc_value(e->right.sym);
+		str1 = sym_get_string_value(e->left.sym);
+		str2 = sym_get_string_value(e->right.sym);
+		return !strcmp(str1, str2) ? no : yes;
+	default:
+		printf("expr_calc_value: %d?\n", e->type);
+		return no;
+	}
+}
+
+int expr_compare_type(enum expr_type t1, enum expr_type t2)
+{
+#if 0
+	return 1;
+#else
+	if (t1 == t2)
+		return 0;
+	switch (t1) {
+	case E_EQUAL:
+	case E_UNEQUAL:
+		if (t2 == E_NOT)
+			return 1;
+	case E_NOT:
+		if (t2 == E_AND)
+			return 1;
+	case E_AND:
+		if (t2 == E_OR)
+			return 1;
+	case E_OR:
+		if (t2 == E_CHOICE)
+			return 1;
+	case E_CHOICE:
+		if (t2 == 0)
+			return 1;
+	default:
+		return -1;
+	}
+	printf("[%dgt%d?]", t1, t2);
+	return 0;
+#endif
+}
+
+void expr_print(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken)
+{
+	if (!e) {
+		fn(data, "y");
+		return;
+	}
+
+	if (expr_compare_type(prevtoken, e->type) > 0)
+		fn(data, "(");
+	switch (e->type) {
+	case E_SYMBOL:
+		if (e->left.sym->name)
+			fn(data, e->left.sym->name);
+		else
+			fn(data, "<choice>");
+		break;
+	case E_NOT:
+		fn(data, "!");
+		expr_print(e->left.expr, fn, data, E_NOT);
+		break;
+	case E_EQUAL:
+		fn(data, e->left.sym->name);
+		fn(data, "=");
+		fn(data, e->right.sym->name);
+		break;
+	case E_UNEQUAL:
+		fn(data, e->left.sym->name);
+		fn(data, "!=");
+		fn(data, e->right.sym->name);
+		break;
+	case E_OR:
+		expr_print(e->left.expr, fn, data, E_OR);
+		fn(data, " || ");
+		expr_print(e->right.expr, fn, data, E_OR);
+		break;
+	case E_AND:
+		expr_print(e->left.expr, fn, data, E_AND);
+		fn(data, " && ");
+		expr_print(e->right.expr, fn, data, E_AND);
+		break;
+	case E_CHOICE:
+		fn(data, e->right.sym->name);
+		if (e->left.expr) {
+			fn(data, " ^ ");
+			expr_print(e->left.expr, fn, data, E_CHOICE);
+		}
+		break;
+	case E_RANGE:
+		fn(data, "[");
+		fn(data, e->left.sym->name);
+		fn(data, " ");
+		fn(data, e->right.sym->name);
+		fn(data, "]");
+		break;
+	default:
+	  {
+		char buf[32];
+		sprintf(buf, "<unknown type %d>", e->type);
+		fn(data, buf);
+		break;
+	  }
+	}
+	if (expr_compare_type(prevtoken, e->type) > 0)
+		fn(data, ")");
+}
+
+static void expr_print_file_helper(void *data, const char *str)
+{
+	fwrite(str, strlen(str), 1, data);
+}
+
+void expr_fprint(struct expr *e, FILE *out)
+{
+	expr_print(e, expr_print_file_helper, out, E_NONE);
+}
+
+static void expr_print_gstr_helper(void *data, const char *str)
+{
+	str_append((struct gstr*)data, str);
+}
+
+void expr_gstr_print(struct expr *e, struct gstr *gs)
+{
+	expr_print(e, expr_print_gstr_helper, gs, E_NONE);
+}
diff --git a/busybox-1.19.3/scripts/kconfig/expr.h b/busybox-1.19.3/scripts/kconfig/expr.h
new file mode 100644
index 0000000..1b36ef1
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/expr.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef EXPR_H
+#define EXPR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+struct file {
+	struct file *next;
+	struct file *parent;
+	char *name;
+	int lineno;
+	int flags;
+};
+
+#define FILE_BUSY		0x0001
+#define FILE_SCANNED		0x0002
+#define FILE_PRINTED		0x0004
+
+typedef enum tristate {
+	no, mod, yes
+} tristate;
+
+enum expr_type {
+	E_NONE, E_OR, E_AND, E_NOT, E_EQUAL, E_UNEQUAL, E_CHOICE, E_SYMBOL, E_RANGE
+};
+
+union expr_data {
+	struct expr *expr;
+	struct symbol *sym;
+};
+
+struct expr {
+	enum expr_type type;
+	union expr_data left, right;
+};
+
+#define E_OR(dep1, dep2)	(((dep1)>(dep2))?(dep1):(dep2))
+#define E_AND(dep1, dep2)	(((dep1)<(dep2))?(dep1):(dep2))
+#define E_NOT(dep)		(2-(dep))
+
+struct expr_value {
+	struct expr *expr;
+	tristate tri;
+};
+
+struct symbol_value {
+	void *val;
+	tristate tri;
+};
+
+enum symbol_type {
+	S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING, S_OTHER
+};
+
+struct symbol {
+	struct symbol *next;
+	char *name;
+	char *help;
+	enum symbol_type type;
+	struct symbol_value curr, user;
+	tristate visible;
+	int flags;
+	struct property *prop;
+	struct expr *dep, *dep2;
+	struct expr_value rev_dep;
+};
+
+#define for_all_symbols(i, sym) for (i = 0; i < 257; i++) for (sym = symbol_hash[i]; sym; sym = sym->next) if (sym->type != S_OTHER)
+
+#define SYMBOL_YES		0x0001
+#define SYMBOL_MOD		0x0002
+#define SYMBOL_NO		0x0004
+#define SYMBOL_CONST		0x0007
+#define SYMBOL_CHECK		0x0008
+#define SYMBOL_CHOICE		0x0010
+#define SYMBOL_CHOICEVAL	0x0020
+#define SYMBOL_PRINTED		0x0040
+#define SYMBOL_VALID		0x0080
+#define SYMBOL_OPTIONAL		0x0100
+#define SYMBOL_WRITE		0x0200
+#define SYMBOL_CHANGED		0x0400
+#define SYMBOL_NEW		0x0800
+#define SYMBOL_AUTO		0x1000
+#define SYMBOL_CHECKED		0x2000
+#define SYMBOL_WARNED		0x8000
+
+#define SYMBOL_MAXLENGTH	256
+#define SYMBOL_HASHSIZE		257
+#define SYMBOL_HASHMASK		0xff
+
+enum prop_type {
+	P_UNKNOWN, P_PROMPT, P_COMMENT, P_MENU, P_DEFAULT, P_CHOICE, P_SELECT, P_RANGE
+};
+
+struct property {
+	struct property *next;
+	struct symbol *sym;
+	enum prop_type type;
+	const char *text;
+	struct expr_value visible;
+	struct expr *expr;
+	struct menu *menu;
+	struct file *file;
+	int lineno;
+};
+
+#define for_all_properties(sym, st, tok) \
+	for (st = sym->prop; st; st = st->next) \
+		if (st->type == (tok))
+#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
+#define for_all_choices(sym, st) for_all_properties(sym, st, P_CHOICE)
+#define for_all_prompts(sym, st) \
+	for (st = sym->prop; st; st = st->next) \
+		if (st->text)
+
+struct menu {
+	struct menu *next;
+	struct menu *parent;
+	struct menu *list;
+	struct symbol *sym;
+	struct property *prompt;
+	struct expr *dep;
+	unsigned int flags;
+	//char *help;
+	struct file *file;
+	int lineno;
+	void *data;
+};
+
+#define MENU_CHANGED		0x0001
+#define MENU_ROOT		0x0002
+
+#ifndef SWIG
+
+extern struct file *file_list;
+extern struct file *current_file;
+struct file *lookup_file(const char *name);
+
+extern struct symbol symbol_yes, symbol_no, symbol_mod;
+extern struct symbol *modules_sym;
+extern int cdebug;
+struct expr *expr_alloc_symbol(struct symbol *sym);
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce);
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
+struct expr *expr_copy(struct expr *org);
+void expr_free(struct expr *e);
+int expr_eq(struct expr *e1, struct expr *e2);
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
+tristate expr_calc_value(struct expr *e);
+struct expr *expr_eliminate_yn(struct expr *e);
+struct expr *expr_trans_bool(struct expr *e);
+struct expr *expr_eliminate_dups(struct expr *e);
+struct expr *expr_transform(struct expr *e);
+int expr_contains_symbol(struct expr *dep, struct symbol *sym);
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
+struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2);
+struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2);
+void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2);
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
+
+void expr_fprint(struct expr *e, FILE *out);
+struct gstr; /* forward */
+void expr_gstr_print(struct expr *e, struct gstr *gs);
+
+static inline int expr_is_yes(struct expr *e)
+{
+	return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
+}
+
+static inline int expr_is_no(struct expr *e)
+{
+	return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXPR_H */
diff --git a/busybox-1.19.3/scripts/kconfig/gconf.c b/busybox-1.19.3/scripts/kconfig/gconf.c
new file mode 100644
index 0000000..9bab17d
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/gconf.c
@@ -0,0 +1,1644 @@
+/* Hey EMACS -*- linux-c -*- */
+/*
+ *
+ * Copyright (C) 2002-2003 Romain Lievin <roms@tilp.info>
+ * Released under the terms of the GNU GPL v2.0.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "lkc.h"
+#include "images.c"
+
+#include <glade/glade.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdlib.h>
+
+//#define DEBUG
+
+enum {
+	SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
+};
+
+static gint view_mode = FULL_VIEW;
+static gboolean show_name = TRUE;
+static gboolean show_range = TRUE;
+static gboolean show_value = TRUE;
+static gboolean show_all = FALSE;
+static gboolean show_debug = FALSE;
+static gboolean resizeable = FALSE;
+
+static gboolean config_changed = FALSE;
+
+static char nohelp_text[] =
+    N_("Sorry, no help available for this option yet.\n");
+
+GtkWidget *main_wnd = NULL;
+GtkWidget *tree1_w = NULL;	// left  frame
+GtkWidget *tree2_w = NULL;	// right frame
+GtkWidget *text_w = NULL;
+GtkWidget *hpaned = NULL;
+GtkWidget *vpaned = NULL;
+GtkWidget *back_btn = NULL;
+
+GtkTextTag *tag1, *tag2;
+GdkColor color;
+
+GtkTreeStore *tree1, *tree2, *tree;
+GtkTreeModel *model1, *model2;
+static GtkTreeIter *parents[256];
+static gint indent;
+
+static struct menu *current; // current node for SINGLE view
+static struct menu *browsed; // browsed node for SPLIT view
+
+enum {
+	COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE,
+	COL_MENU, COL_COLOR, COL_EDIT, COL_PIXBUF,
+	COL_PIXVIS, COL_BTNVIS, COL_BTNACT, COL_BTNINC, COL_BTNRAD,
+	COL_NUMBER
+};
+
+static void display_list(void);
+static void display_tree(struct menu *menu);
+static void display_tree_part(void);
+static void update_tree(struct menu *src, GtkTreeIter * dst);
+static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row);
+static gchar **fill_row(struct menu *menu);
+
+
+/* Helping/Debugging Functions */
+
+
+const char *dbg_print_stype(int val)
+{
+	static char buf[256];
+
+	memset(buf, 0, 256);
+
+	if (val == S_UNKNOWN)
+		strcpy(buf, "unknown");
+	if (val == S_BOOLEAN)
+		strcpy(buf, "boolean");
+	if (val == S_TRISTATE)
+		strcpy(buf, "tristate");
+	if (val == S_INT)
+		strcpy(buf, "int");
+	if (val == S_HEX)
+		strcpy(buf, "hex");
+	if (val == S_STRING)
+		strcpy(buf, "string");
+	if (val == S_OTHER)
+		strcpy(buf, "other");
+
+#ifdef DEBUG
+	printf("%s", buf);
+#endif
+
+	return buf;
+}
+
+const char *dbg_print_flags(int val)
+{
+	static char buf[256];
+
+	memset(buf, 0, 256);
+
+	if (val & SYMBOL_YES)
+		strcat(buf, "yes/");
+	if (val & SYMBOL_MOD)
+		strcat(buf, "mod/");
+	if (val & SYMBOL_NO)
+		strcat(buf, "no/");
+	if (val & SYMBOL_CONST)
+		strcat(buf, "const/");
+	if (val & SYMBOL_CHECK)
+		strcat(buf, "check/");
+	if (val & SYMBOL_CHOICE)
+		strcat(buf, "choice/");
+	if (val & SYMBOL_CHOICEVAL)
+		strcat(buf, "choiceval/");
+	if (val & SYMBOL_PRINTED)
+		strcat(buf, "printed/");
+	if (val & SYMBOL_VALID)
+		strcat(buf, "valid/");
+	if (val & SYMBOL_OPTIONAL)
+		strcat(buf, "optional/");
+	if (val & SYMBOL_WRITE)
+		strcat(buf, "write/");
+	if (val & SYMBOL_CHANGED)
+		strcat(buf, "changed/");
+	if (val & SYMBOL_NEW)
+		strcat(buf, "new/");
+	if (val & SYMBOL_AUTO)
+		strcat(buf, "auto/");
+
+	buf[strlen(buf) - 1] = '\0';
+#ifdef DEBUG
+	printf("%s", buf);
+#endif
+
+	return buf;
+}
+
+const char *dbg_print_ptype(int val)
+{
+	static char buf[256];
+
+	memset(buf, 0, 256);
+
+	if (val == P_UNKNOWN)
+		strcpy(buf, "unknown");
+	if (val == P_PROMPT)
+		strcpy(buf, "prompt");
+	if (val == P_COMMENT)
+		strcpy(buf, "comment");
+	if (val == P_MENU)
+		strcpy(buf, "menu");
+	if (val == P_DEFAULT)
+		strcpy(buf, "default");
+	if (val == P_CHOICE)
+		strcpy(buf, "choice");
+
+#ifdef DEBUG
+	printf("%s", buf);
+#endif
+
+	return buf;
+}
+
+
+void replace_button_icon(GladeXML * xml, GdkDrawable * window,
+			 GtkStyle * style, gchar * btn_name, gchar ** xpm)
+{
+	GdkPixmap *pixmap;
+	GdkBitmap *mask;
+	GtkToolButton *button;
+	GtkWidget *image;
+
+	pixmap = gdk_pixmap_create_from_xpm_d(window, &mask,
+					      &style->bg[GTK_STATE_NORMAL],
+					      xpm);
+
+	button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name));
+	image = gtk_image_new_from_pixmap(pixmap, mask);
+	gtk_widget_show(image);
+	gtk_tool_button_set_icon_widget(button, image);
+}
+
+/* Main Window Initialization */
+void init_main_window(const gchar * glade_file)
+{
+	GladeXML *xml;
+	GtkWidget *widget;
+	GtkTextBuffer *txtbuf;
+	char title[256];
+	GtkStyle *style;
+
+	xml = glade_xml_new(glade_file, "window1", NULL);
+	if (!xml)
+		g_error(_("GUI loading failed !\n"));
+	glade_xml_signal_autoconnect(xml);
+
+	main_wnd = glade_xml_get_widget(xml, "window1");
+	hpaned = glade_xml_get_widget(xml, "hpaned1");
+	vpaned = glade_xml_get_widget(xml, "vpaned1");
+	tree1_w = glade_xml_get_widget(xml, "treeview1");
+	tree2_w = glade_xml_get_widget(xml, "treeview2");
+	text_w = glade_xml_get_widget(xml, "textview3");
+
+	back_btn = glade_xml_get_widget(xml, "button1");
+	gtk_widget_set_sensitive(back_btn, FALSE);
+
+	widget = glade_xml_get_widget(xml, "show_name1");
+	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+				       show_name);
+
+	widget = glade_xml_get_widget(xml, "show_range1");
+	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+				       show_range);
+
+	widget = glade_xml_get_widget(xml, "show_data1");
+	gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+				       show_value);
+
+	style = gtk_widget_get_style(main_wnd);
+	widget = glade_xml_get_widget(xml, "toolbar1");
+
+#if 0	/* Use stock Gtk icons instead */
+	replace_button_icon(xml, main_wnd->window, style,
+			    "button1", (gchar **) xpm_back);
+	replace_button_icon(xml, main_wnd->window, style,
+			    "button2", (gchar **) xpm_load);
+	replace_button_icon(xml, main_wnd->window, style,
+			    "button3", (gchar **) xpm_save);
+#endif
+	replace_button_icon(xml, main_wnd->window, style,
+			    "button4", (gchar **) xpm_single_view);
+	replace_button_icon(xml, main_wnd->window, style,
+			    "button5", (gchar **) xpm_split_view);
+	replace_button_icon(xml, main_wnd->window, style,
+			    "button6", (gchar **) xpm_tree_view);
+
+#if 0
+	switch (view_mode) {
+	case SINGLE_VIEW:
+		widget = glade_xml_get_widget(xml, "button4");
+		g_signal_emit_by_name(widget, "clicked");
+		break;
+	case SPLIT_VIEW:
+		widget = glade_xml_get_widget(xml, "button5");
+		g_signal_emit_by_name(widget, "clicked");
+		break;
+	case FULL_VIEW:
+		widget = glade_xml_get_widget(xml, "button6");
+		g_signal_emit_by_name(widget, "clicked");
+		break;
+	}
+#endif
+	txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+	tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
+					  "foreground", "red",
+					  "weight", PANGO_WEIGHT_BOLD,
+					  NULL);
+	tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
+					  /*"style", PANGO_STYLE_OBLIQUE, */
+					  NULL);
+
+	sprintf(title, _("BusyBox %s Configuration"),
+		getenv("KERNELVERSION"));
+	gtk_window_set_title(GTK_WINDOW(main_wnd), title);
+
+	gtk_widget_show(main_wnd);
+}
+
+void init_tree_model(void)
+{
+	gint i;
+
+	tree = tree2 = gtk_tree_store_new(COL_NUMBER,
+					  G_TYPE_STRING, G_TYPE_STRING,
+					  G_TYPE_STRING, G_TYPE_STRING,
+					  G_TYPE_STRING, G_TYPE_STRING,
+					  G_TYPE_POINTER, GDK_TYPE_COLOR,
+					  G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
+					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+					  G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+					  G_TYPE_BOOLEAN);
+	model2 = GTK_TREE_MODEL(tree2);
+
+	for (parents[0] = NULL, i = 1; i < 256; i++)
+		parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));
+
+	tree1 = gtk_tree_store_new(COL_NUMBER,
+				   G_TYPE_STRING, G_TYPE_STRING,
+				   G_TYPE_STRING, G_TYPE_STRING,
+				   G_TYPE_STRING, G_TYPE_STRING,
+				   G_TYPE_POINTER, GDK_TYPE_COLOR,
+				   G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
+				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+				   G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+				   G_TYPE_BOOLEAN);
+	model1 = GTK_TREE_MODEL(tree1);
+}
+
+void init_left_tree(void)
+{
+	GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
+	GtkCellRenderer *renderer;
+	GtkTreeSelection *sel;
+	GtkTreeViewColumn *column;
+
+	gtk_tree_view_set_model(view, model1);
+	gtk_tree_view_set_headers_visible(view, TRUE);
+	gtk_tree_view_set_rules_hint(view, FALSE);
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_append_column(view, column);
+	gtk_tree_view_column_set_title(column, _("Options"));
+
+	renderer = gtk_cell_renderer_toggle_new();
+	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+					renderer, FALSE);
+	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+					    renderer,
+					    "active", COL_BTNACT,
+					    "inconsistent", COL_BTNINC,
+					    "visible", COL_BTNVIS,
+					    "radio", COL_BTNRAD, NULL);
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+					renderer, FALSE);
+	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+					    renderer,
+					    "text", COL_OPTION,
+					    "foreground-gdk",
+					    COL_COLOR, NULL);
+
+	sel = gtk_tree_view_get_selection(view);
+	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+	gtk_widget_realize(tree1_w);
+}
+
+static void renderer_edited(GtkCellRendererText * cell,
+			    const gchar * path_string,
+			    const gchar * new_text, gpointer user_data);
+static void renderer_toggled(GtkCellRendererToggle * cellrenderertoggle,
+			     gchar * arg1, gpointer user_data);
+
+void init_right_tree(void)
+{
+	GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
+	GtkCellRenderer *renderer;
+	GtkTreeSelection *sel;
+	GtkTreeViewColumn *column;
+	gint i;
+
+	gtk_tree_view_set_model(view, model2);
+	gtk_tree_view_set_headers_visible(view, TRUE);
+	gtk_tree_view_set_rules_hint(view, FALSE);
+
+	column = gtk_tree_view_column_new();
+	gtk_tree_view_append_column(view, column);
+	gtk_tree_view_column_set_title(column, _("Options"));
+
+	renderer = gtk_cell_renderer_pixbuf_new();
+	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+					renderer, FALSE);
+	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+					    renderer,
+					    "pixbuf", COL_PIXBUF,
+					    "visible", COL_PIXVIS, NULL);
+	renderer = gtk_cell_renderer_toggle_new();
+	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+					renderer, FALSE);
+	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+					    renderer,
+					    "active", COL_BTNACT,
+					    "inconsistent", COL_BTNINC,
+					    "visible", COL_BTNVIS,
+					    "radio", COL_BTNRAD, NULL);
+	/*g_signal_connect(G_OBJECT(renderer), "toggled",
+	   G_CALLBACK(renderer_toggled), NULL); */
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+					renderer, FALSE);
+	gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+					    renderer,
+					    "text", COL_OPTION,
+					    "foreground-gdk",
+					    COL_COLOR, NULL);
+
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(view, -1,
+						    _("Name"), renderer,
+						    "text", COL_NAME,
+						    "foreground-gdk",
+						    COL_COLOR, NULL);
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(view, -1,
+						    "N", renderer,
+						    "text", COL_NO,
+						    "foreground-gdk",
+						    COL_COLOR, NULL);
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(view, -1,
+						    "M", renderer,
+						    "text", COL_MOD,
+						    "foreground-gdk",
+						    COL_COLOR, NULL);
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(view, -1,
+						    "Y", renderer,
+						    "text", COL_YES,
+						    "foreground-gdk",
+						    COL_COLOR, NULL);
+	renderer = gtk_cell_renderer_text_new();
+	gtk_tree_view_insert_column_with_attributes(view, -1,
+						    _("Value"), renderer,
+						    "text", COL_VALUE,
+						    "editable",
+						    COL_EDIT,
+						    "foreground-gdk",
+						    COL_COLOR, NULL);
+	g_signal_connect(G_OBJECT(renderer), "edited",
+			 G_CALLBACK(renderer_edited), NULL);
+
+	column = gtk_tree_view_get_column(view, COL_NAME);
+	gtk_tree_view_column_set_visible(column, show_name);
+	column = gtk_tree_view_get_column(view, COL_NO);
+	gtk_tree_view_column_set_visible(column, show_range);
+	column = gtk_tree_view_get_column(view, COL_MOD);
+	gtk_tree_view_column_set_visible(column, show_range);
+	column = gtk_tree_view_get_column(view, COL_YES);
+	gtk_tree_view_column_set_visible(column, show_range);
+	column = gtk_tree_view_get_column(view, COL_VALUE);
+	gtk_tree_view_column_set_visible(column, show_value);
+
+	if (resizeable) {
+		for (i = 0; i < COL_VALUE; i++) {
+			column = gtk_tree_view_get_column(view, i);
+			gtk_tree_view_column_set_resizable(column, TRUE);
+		}
+	}
+
+	sel = gtk_tree_view_get_selection(view);
+	gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+}
+
+
+/* Utility Functions */
+
+
+static void text_insert_help(struct menu *menu)
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter start, end;
+	const char *prompt = menu_get_prompt(menu);
+	gchar *name;
+	const char *help = _(nohelp_text);
+
+	if (!menu->sym)
+		help = "";
+	else if (menu->sym->help)
+		help = _(menu->sym->help);
+
+	if (menu->sym && menu->sym->name)
+		name = g_strdup_printf(_(menu->sym->name));
+	else
+		name = g_strdup("");
+
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+	gtk_text_buffer_get_bounds(buffer, &start, &end);
+	gtk_text_buffer_delete(buffer, &start, &end);
+	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
+
+	gtk_text_buffer_get_end_iter(buffer, &end);
+	gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
+					 NULL);
+	gtk_text_buffer_insert_at_cursor(buffer, " ", 1);
+	gtk_text_buffer_get_end_iter(buffer, &end);
+	gtk_text_buffer_insert_with_tags(buffer, &end, name, -1, tag1,
+					 NULL);
+	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
+	gtk_text_buffer_get_end_iter(buffer, &end);
+	gtk_text_buffer_insert_with_tags(buffer, &end, help, -1, tag2,
+					 NULL);
+}
+
+
+static void text_insert_msg(const char *title, const char *message)
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter start, end;
+	const char *msg = message;
+
+	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+	gtk_text_buffer_get_bounds(buffer, &start, &end);
+	gtk_text_buffer_delete(buffer, &start, &end);
+	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
+
+	gtk_text_buffer_get_end_iter(buffer, &end);
+	gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
+					 NULL);
+	gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
+	gtk_text_buffer_get_end_iter(buffer, &end);
+	gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
+					 NULL);
+}
+
+
+/* Main Windows Callbacks */
+
+void on_save1_activate(GtkMenuItem * menuitem, gpointer user_data);
+gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
+				 gpointer user_data)
+{
+	GtkWidget *dialog, *label;
+	gint result;
+
+	if (config_changed == FALSE)
+		return FALSE;
+
+	dialog = gtk_dialog_new_with_buttons(_("Warning !"),
+					     GTK_WINDOW(main_wnd),
+					     (GtkDialogFlags)
+					     (GTK_DIALOG_MODAL |
+					      GTK_DIALOG_DESTROY_WITH_PARENT),
+					     GTK_STOCK_OK,
+					     GTK_RESPONSE_YES,
+					     GTK_STOCK_NO,
+					     GTK_RESPONSE_NO,
+					     GTK_STOCK_CANCEL,
+					     GTK_RESPONSE_CANCEL, NULL);
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+					GTK_RESPONSE_CANCEL);
+
+	label = gtk_label_new(_("\nSave configuration ?\n"));
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
+	gtk_widget_show(label);
+
+	result = gtk_dialog_run(GTK_DIALOG(dialog));
+	switch (result) {
+	case GTK_RESPONSE_YES:
+		on_save1_activate(NULL, NULL);
+		return FALSE;
+	case GTK_RESPONSE_NO:
+		return FALSE;
+	case GTK_RESPONSE_CANCEL:
+	case GTK_RESPONSE_DELETE_EVENT:
+	default:
+		gtk_widget_destroy(dialog);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+
+void on_window1_destroy(GtkObject * object, gpointer user_data)
+{
+	gtk_main_quit();
+}
+
+
+void
+on_window1_size_request(GtkWidget * widget,
+			GtkRequisition * requisition, gpointer user_data)
+{
+	static gint old_h;
+	gint w, h;
+
+	if (widget->window == NULL)
+		gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
+	else
+		gdk_window_get_size(widget->window, &w, &h);
+
+	if (h == old_h)
+		return;
+	old_h = h;
+
+	gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
+}
+
+
+/* Menu & Toolbar Callbacks */
+
+
+static void
+load_filename(GtkFileSelection * file_selector, gpointer user_data)
+{
+	const gchar *fn;
+
+	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
+					     (user_data));
+
+	if (conf_read(fn))
+		text_insert_msg(_("Error"), _("Unable to load configuration !"));
+	else
+		display_tree(&rootmenu);
+}
+
+void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkWidget *fs;
+
+	fs = gtk_file_selection_new(_("Load file..."));
+	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
+			 "clicked",
+			 G_CALLBACK(load_filename), (gpointer) fs);
+	g_signal_connect_swapped(GTK_OBJECT
+				 (GTK_FILE_SELECTION(fs)->ok_button),
+				 "clicked", G_CALLBACK(gtk_widget_destroy),
+				 (gpointer) fs);
+	g_signal_connect_swapped(GTK_OBJECT
+				 (GTK_FILE_SELECTION(fs)->cancel_button),
+				 "clicked", G_CALLBACK(gtk_widget_destroy),
+				 (gpointer) fs);
+	gtk_widget_show(fs);
+}
+
+
+void on_save1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	if (conf_write(NULL))
+		text_insert_msg(_("Error"), _("Unable to save configuration !"));
+
+	config_changed = FALSE;
+}
+
+
+static void
+store_filename(GtkFileSelection * file_selector, gpointer user_data)
+{
+	const gchar *fn;
+
+	fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
+					     (user_data));
+
+	if (conf_write(fn))
+		text_insert_msg(_("Error"), _("Unable to save configuration !"));
+
+	gtk_widget_destroy(GTK_WIDGET(user_data));
+}
+
+void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkWidget *fs;
+
+	fs = gtk_file_selection_new(_("Save file as..."));
+	g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
+			 "clicked",
+			 G_CALLBACK(store_filename), (gpointer) fs);
+	g_signal_connect_swapped(GTK_OBJECT
+				 (GTK_FILE_SELECTION(fs)->ok_button),
+				 "clicked", G_CALLBACK(gtk_widget_destroy),
+				 (gpointer) fs);
+	g_signal_connect_swapped(GTK_OBJECT
+				 (GTK_FILE_SELECTION(fs)->cancel_button),
+				 "clicked", G_CALLBACK(gtk_widget_destroy),
+				 (gpointer) fs);
+	gtk_widget_show(fs);
+}
+
+
+void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	if (!on_window1_delete_event(NULL, NULL, NULL))
+		gtk_widget_destroy(GTK_WIDGET(main_wnd));
+}
+
+
+void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkTreeViewColumn *col;
+
+	show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
+	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
+	if (col)
+		gtk_tree_view_column_set_visible(col, show_name);
+}
+
+
+void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkTreeViewColumn *col;
+
+	show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
+	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
+	if (col)
+		gtk_tree_view_column_set_visible(col, show_range);
+	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_MOD);
+	if (col)
+		gtk_tree_view_column_set_visible(col, show_range);
+	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_YES);
+	if (col)
+		gtk_tree_view_column_set_visible(col, show_range);
+
+}
+
+
+void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkTreeViewColumn *col;
+
+	show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
+	col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
+	if (col)
+		gtk_tree_view_column_set_visible(col, show_value);
+}
+
+
+void
+on_show_all_options1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	show_all = GTK_CHECK_MENU_ITEM(menuitem)->active;
+
+	gtk_tree_store_clear(tree2);
+	display_tree(&rootmenu);	// instead of update_tree to speed-up
+}
+
+
+void
+on_show_debug_info1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	show_debug = GTK_CHECK_MENU_ITEM(menuitem)->active;
+	update_tree(&rootmenu, NULL);
+}
+
+
+void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkWidget *dialog;
+	const gchar *intro_text = _(
+	    "Welcome to gkc, the GTK+ graphical configuration tool.\n"
+	    "For each option, a blank box indicates the feature is disabled, a\n"
+	    "check indicates it is enabled, and a dot indicates that it is to\n"
+	    "be compiled as a module.  Clicking on the box will cycle through the three states.\n"
+	    "\n"
+	    "If you do not see an option (e.g., a device driver) that you\n"
+	    "believe should be present, try turning on Show All Options\n"
+	    "under the Options menu.\n"
+	    "Although there is no cross reference yet to help you figure out\n"
+	    "what other options must be enabled to support the option you\n"
+	    "are interested in, you can still view the help of a grayed-out\n"
+	    "option.\n"
+	    "\n"
+	    "Toggling Show Debug Info under the Options menu will show\n"
+	    "the dependencies, which you can then match by examining other options.");
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
+					GTK_DIALOG_DESTROY_WITH_PARENT,
+					GTK_MESSAGE_INFO,
+					GTK_BUTTONS_CLOSE, intro_text);
+	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
+				 G_CALLBACK(gtk_widget_destroy),
+				 GTK_OBJECT(dialog));
+	gtk_widget_show_all(dialog);
+}
+
+
+void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkWidget *dialog;
+	const gchar *about_text =
+	    _("gkc is copyright (c) 2002 Romain Lievin <roms@lpg.ticalc.org>.\n"
+	      "Based on the source code from Roman Zippel.\n");
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
+					GTK_DIALOG_DESTROY_WITH_PARENT,
+					GTK_MESSAGE_INFO,
+					GTK_BUTTONS_CLOSE, about_text);
+	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
+				 G_CALLBACK(gtk_widget_destroy),
+				 GTK_OBJECT(dialog));
+	gtk_widget_show_all(dialog);
+}
+
+
+void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+	GtkWidget *dialog;
+	const gchar *license_text =
+	    _("gkc is released under the terms of the GNU GPL v2.\n"
+	      "For more information, please see the source code or\n"
+	      "visit http://www.fsf.org/licenses/licenses.html\n");
+
+	dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
+					GTK_DIALOG_DESTROY_WITH_PARENT,
+					GTK_MESSAGE_INFO,
+					GTK_BUTTONS_CLOSE, license_text);
+	g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
+				 G_CALLBACK(gtk_widget_destroy),
+				 GTK_OBJECT(dialog));
+	gtk_widget_show_all(dialog);
+}
+
+
+void on_back_clicked(GtkButton * button, gpointer user_data)
+{
+	enum prop_type ptype;
+
+	current = current->parent;
+	ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
+	if (ptype != P_MENU)
+		current = current->parent;
+	display_tree_part();
+
+	if (current == &rootmenu)
+		gtk_widget_set_sensitive(back_btn, FALSE);
+}
+
+
+void on_load_clicked(GtkButton * button, gpointer user_data)
+{
+	on_load1_activate(NULL, user_data);
+}
+
+
+void on_save_clicked(GtkButton * button, gpointer user_data)
+{
+	on_save1_activate(NULL, user_data);
+}
+
+
+void on_single_clicked(GtkButton * button, gpointer user_data)
+{
+	view_mode = SINGLE_VIEW;
+	gtk_paned_set_position(GTK_PANED(hpaned), 0);
+	gtk_widget_hide(tree1_w);
+	current = &rootmenu;
+	display_tree_part();
+}
+
+
+void on_split_clicked(GtkButton * button, gpointer user_data)
+{
+	gint w, h;
+	view_mode = SPLIT_VIEW;
+	gtk_widget_show(tree1_w);
+	gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
+	gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
+	if (tree2)
+		gtk_tree_store_clear(tree2);
+	display_list();
+
+	/* Disable back btn, like in full mode. */
+	gtk_widget_set_sensitive(back_btn, FALSE);
+}
+
+
+void on_full_clicked(GtkButton * button, gpointer user_data)
+{
+	view_mode = FULL_VIEW;
+	gtk_paned_set_position(GTK_PANED(hpaned), 0);
+	gtk_widget_hide(tree1_w);
+	if (tree2)
+		gtk_tree_store_clear(tree2);
+	display_tree(&rootmenu);
+	gtk_widget_set_sensitive(back_btn, FALSE);
+}
+
+
+void on_collapse_clicked(GtkButton * button, gpointer user_data)
+{
+	gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
+}
+
+
+void on_expand_clicked(GtkButton * button, gpointer user_data)
+{
+	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
+}
+
+
+/* CTree Callbacks */
+
+/* Change hex/int/string value in the cell */
+static void renderer_edited(GtkCellRendererText * cell,
+			    const gchar * path_string,
+			    const gchar * new_text, gpointer user_data)
+{
+	GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
+	GtkTreeIter iter;
+	const char *old_def, *new_def;
+	struct menu *menu;
+	struct symbol *sym;
+
+	if (!gtk_tree_model_get_iter(model2, &iter, path))
+		return;
+
+	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+	sym = menu->sym;
+
+	gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
+	new_def = new_text;
+
+	sym_set_string_value(sym, new_def);
+
+	config_changed = TRUE;
+	update_tree(&rootmenu, NULL);
+
+	gtk_tree_path_free(path);
+}
+
+/* Change the value of a symbol and update the tree */
+static void change_sym_value(struct menu *menu, gint col)
+{
+	struct symbol *sym = menu->sym;
+	tristate oldval, newval;
+
+	if (!sym)
+		return;
+
+	if (col == COL_NO)
+		newval = no;
+	else if (col == COL_MOD)
+		newval = mod;
+	else if (col == COL_YES)
+		newval = yes;
+	else
+		return;
+
+	switch (sym_get_type(sym)) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		oldval = sym_get_tristate_value(sym);
+		if (!sym_tristate_within_range(sym, newval))
+			newval = yes;
+		sym_set_tristate_value(sym, newval);
+		config_changed = TRUE;
+		if (view_mode == FULL_VIEW)
+			update_tree(&rootmenu, NULL);
+		else if (view_mode == SPLIT_VIEW) {
+			update_tree(browsed, NULL);
+			display_list();
+		}
+		else if (view_mode == SINGLE_VIEW)
+			display_tree_part();	//fixme: keep exp/coll
+		break;
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+	default:
+		break;
+	}
+}
+
+static void toggle_sym_value(struct menu *menu)
+{
+	if (!menu->sym)
+		return;
+
+	sym_toggle_tristate_value(menu->sym);
+	if (view_mode == FULL_VIEW)
+		update_tree(&rootmenu, NULL);
+	else if (view_mode == SPLIT_VIEW) {
+		update_tree(browsed, NULL);
+		display_list();
+	}
+	else if (view_mode == SINGLE_VIEW)
+		display_tree_part();	//fixme: keep exp/coll
+}
+
+static void renderer_toggled(GtkCellRendererToggle * cell,
+			     gchar * path_string, gpointer user_data)
+{
+	GtkTreePath *path, *sel_path = NULL;
+	GtkTreeIter iter, sel_iter;
+	GtkTreeSelection *sel;
+	struct menu *menu;
+
+	path = gtk_tree_path_new_from_string(path_string);
+	if (!gtk_tree_model_get_iter(model2, &iter, path))
+		return;
+
+	sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree2_w));
+	if (gtk_tree_selection_get_selected(sel, NULL, &sel_iter))
+		sel_path = gtk_tree_model_get_path(model2, &sel_iter);
+	if (!sel_path)
+		goto out1;
+	if (gtk_tree_path_compare(path, sel_path))
+		goto out2;
+
+	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+	toggle_sym_value(menu);
+
+      out2:
+	gtk_tree_path_free(sel_path);
+      out1:
+	gtk_tree_path_free(path);
+}
+
+static gint column2index(GtkTreeViewColumn * column)
+{
+	gint i;
+
+	for (i = 0; i < COL_NUMBER; i++) {
+		GtkTreeViewColumn *col;
+
+		col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), i);
+		if (col == column)
+			return i;
+	}
+
+	return -1;
+}
+
+
+/* User click: update choice (full) or goes down (single) */
+gboolean
+on_treeview2_button_press_event(GtkWidget * widget,
+				GdkEventButton * event, gpointer user_data)
+{
+	GtkTreeView *view = GTK_TREE_VIEW(widget);
+	GtkTreePath *path;
+	GtkTreeViewColumn *column;
+	GtkTreeIter iter;
+	struct menu *menu;
+	gint col;
+
+#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
+	gint tx = (gint) event->x;
+	gint ty = (gint) event->y;
+	gint cx, cy;
+
+	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
+				      &cy);
+#else
+	gtk_tree_view_get_cursor(view, &path, &column);
+#endif
+	if (path == NULL)
+		return FALSE;
+
+	if (!gtk_tree_model_get_iter(model2, &iter, path))
+		return FALSE;
+	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+
+	col = column2index(column);
+	if (event->type == GDK_2BUTTON_PRESS) {
+		enum prop_type ptype;
+		ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+
+		if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
+			// goes down into menu
+			current = menu;
+			display_tree_part();
+			gtk_widget_set_sensitive(back_btn, TRUE);
+		} else if ((col == COL_OPTION)) {
+			toggle_sym_value(menu);
+			gtk_tree_view_expand_row(view, path, TRUE);
+		}
+	} else {
+		if (col == COL_VALUE) {
+			toggle_sym_value(menu);
+			gtk_tree_view_expand_row(view, path, TRUE);
+		} else if (col == COL_NO || col == COL_MOD
+			   || col == COL_YES) {
+			change_sym_value(menu, col);
+			gtk_tree_view_expand_row(view, path, TRUE);
+		}
+	}
+
+	return FALSE;
+}
+
+/* Key pressed: update choice */
+gboolean
+on_treeview2_key_press_event(GtkWidget * widget,
+			     GdkEventKey * event, gpointer user_data)
+{
+	GtkTreeView *view = GTK_TREE_VIEW(widget);
+	GtkTreePath *path;
+	GtkTreeViewColumn *column;
+	GtkTreeIter iter;
+	struct menu *menu;
+	gint col;
+
+	gtk_tree_view_get_cursor(view, &path, &column);
+	if (path == NULL)
+		return FALSE;
+
+	if (event->keyval == GDK_space) {
+		if (gtk_tree_view_row_expanded(view, path))
+			gtk_tree_view_collapse_row(view, path);
+		else
+			gtk_tree_view_expand_row(view, path, FALSE);
+		return TRUE;
+	}
+	if (event->keyval == GDK_KP_Enter) {
+	}
+	if (widget == tree1_w)
+		return FALSE;
+
+	gtk_tree_model_get_iter(model2, &iter, path);
+	gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+
+	if (!strcasecmp(event->string, "n"))
+		col = COL_NO;
+	else if (!strcasecmp(event->string, "m"))
+		col = COL_MOD;
+	else if (!strcasecmp(event->string, "y"))
+		col = COL_YES;
+	else
+		col = -1;
+	change_sym_value(menu, col);
+
+	return FALSE;
+}
+
+
+/* Row selection changed: update help */
+void
+on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
+{
+	GtkTreeSelection *selection;
+	GtkTreeIter iter;
+	struct menu *menu;
+
+	selection = gtk_tree_view_get_selection(treeview);
+	if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
+		gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+		text_insert_help(menu);
+	}
+}
+
+
+/* User click: display sub-tree in the right frame. */
+gboolean
+on_treeview1_button_press_event(GtkWidget * widget,
+				GdkEventButton * event, gpointer user_data)
+{
+	GtkTreeView *view = GTK_TREE_VIEW(widget);
+	GtkTreePath *path;
+	GtkTreeViewColumn *column;
+	GtkTreeIter iter;
+	struct menu *menu;
+
+	gint tx = (gint) event->x;
+	gint ty = (gint) event->y;
+	gint cx, cy;
+
+	gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
+				      &cy);
+	if (path == NULL)
+		return FALSE;
+
+	gtk_tree_model_get_iter(model1, &iter, path);
+	gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
+
+	if (event->type == GDK_2BUTTON_PRESS) {
+		toggle_sym_value(menu);
+		current = menu;
+		display_tree_part();
+	} else {
+		browsed = menu;
+		display_tree_part();
+	}
+
+	gtk_widget_realize(tree2_w);
+	gtk_tree_view_set_cursor(view, path, NULL, FALSE);
+	gtk_widget_grab_focus(tree2_w);
+
+	return FALSE;
+}
+
+
+/* Fill a row of strings */
+static gchar **fill_row(struct menu *menu)
+{
+	static gchar *row[COL_NUMBER];
+	struct symbol *sym = menu->sym;
+	const char *def;
+	int stype;
+	tristate val;
+	enum prop_type ptype;
+	int i;
+
+	for (i = COL_OPTION; i <= COL_COLOR; i++)
+		g_free(row[i]);
+	memset(row, 0, sizeof(row));
+
+	row[COL_OPTION] =
+	    g_strdup_printf("%s %s", menu_get_prompt(menu),
+			    sym ? (sym->
+				   flags & SYMBOL_NEW ? "(NEW)" : "") :
+			    "");
+
+	if (show_all && !menu_is_visible(menu))
+		row[COL_COLOR] = g_strdup("DarkGray");
+	else
+		row[COL_COLOR] = g_strdup("Black");
+
+	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+	switch (ptype) {
+	case P_MENU:
+		row[COL_PIXBUF] = (gchar *) xpm_menu;
+		if (view_mode == SINGLE_VIEW)
+			row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
+		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+		break;
+	case P_COMMENT:
+		row[COL_PIXBUF] = (gchar *) xpm_void;
+		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
+		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+		break;
+	default:
+		row[COL_PIXBUF] = (gchar *) xpm_void;
+		row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
+		row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
+		break;
+	}
+
+	if (!sym)
+		return row;
+	row[COL_NAME] = g_strdup(sym->name);
+
+	sym_calc_value(sym);
+	sym->flags &= ~SYMBOL_CHANGED;
+
+	if (sym_is_choice(sym)) {	// parse childs for getting final value
+		struct menu *child;
+		struct symbol *def_sym = sym_get_choice_value(sym);
+		struct menu *def_menu = NULL;
+
+		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+
+		for (child = menu->list; child; child = child->next) {
+			if (menu_is_visible(child)
+			    && child->sym == def_sym)
+				def_menu = child;
+		}
+
+		if (def_menu)
+			row[COL_VALUE] =
+			    g_strdup(menu_get_prompt(def_menu));
+	}
+	if (sym->flags & SYMBOL_CHOICEVAL)
+		row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
+
+	stype = sym_get_type(sym);
+	switch (stype) {
+	case S_BOOLEAN:
+		if (GPOINTER_TO_INT(row[COL_PIXVIS]) == FALSE)
+			row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
+		if (sym_is_choice(sym))
+			break;
+	case S_TRISTATE:
+		val = sym_get_tristate_value(sym);
+		switch (val) {
+		case no:
+			row[COL_NO] = g_strdup("N");
+			row[COL_VALUE] = g_strdup("N");
+			row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
+			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
+			break;
+		case mod:
+			row[COL_MOD] = g_strdup("M");
+			row[COL_VALUE] = g_strdup("M");
+			row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
+			break;
+		case yes:
+			row[COL_YES] = g_strdup("Y");
+			row[COL_VALUE] = g_strdup("Y");
+			row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
+			row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
+			break;
+		}
+
+		if (val != no && sym_tristate_within_range(sym, no))
+			row[COL_NO] = g_strdup("_");
+		if (val != mod && sym_tristate_within_range(sym, mod))
+			row[COL_MOD] = g_strdup("_");
+		if (val != yes && sym_tristate_within_range(sym, yes))
+			row[COL_YES] = g_strdup("_");
+		break;
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		def = sym_get_string_value(sym);
+		row[COL_VALUE] = g_strdup(def);
+		row[COL_EDIT] = GINT_TO_POINTER(TRUE);
+		row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
+		break;
+	}
+
+	return row;
+}
+
+
+/* Set the node content with a row of strings */
+static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
+{
+	GdkColor color;
+	gboolean success;
+	GdkPixbuf *pix;
+
+	pix = gdk_pixbuf_new_from_xpm_data((const char **)
+					   row[COL_PIXBUF]);
+
+	gdk_color_parse(row[COL_COLOR], &color);
+	gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
+				  FALSE, FALSE, &success);
+
+	gtk_tree_store_set(tree, node,
+			   COL_OPTION, row[COL_OPTION],
+			   COL_NAME, row[COL_NAME],
+			   COL_NO, row[COL_NO],
+			   COL_MOD, row[COL_MOD],
+			   COL_YES, row[COL_YES],
+			   COL_VALUE, row[COL_VALUE],
+			   COL_MENU, (gpointer) menu,
+			   COL_COLOR, &color,
+			   COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
+			   COL_PIXBUF, pix,
+			   COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
+			   COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
+			   COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
+			   COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
+			   COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
+			   -1);
+
+	g_object_unref(pix);
+}
+
+
+/* Add a node to the tree */
+static void place_node(struct menu *menu, char **row)
+{
+	GtkTreeIter *parent = parents[indent - 1];
+	GtkTreeIter *node = parents[indent];
+
+	gtk_tree_store_append(tree, node, parent);
+	set_node(node, menu, row);
+}
+
+
+/* Find a node in the GTK+ tree */
+static GtkTreeIter found;
+
+/*
+ * Find a menu in the GtkTree starting at parent.
+ */
+GtkTreeIter *gtktree_iter_find_node(GtkTreeIter * parent,
+				    struct menu *tofind)
+{
+	GtkTreeIter iter;
+	GtkTreeIter *child = &iter;
+	gboolean valid;
+	GtkTreeIter *ret;
+
+	valid = gtk_tree_model_iter_children(model2, child, parent);
+	while (valid) {
+		struct menu *menu;
+
+		gtk_tree_model_get(model2, child, 6, &menu, -1);
+
+		if (menu == tofind) {
+			memcpy(&found, child, sizeof(GtkTreeIter));
+			return &found;
+		}
+
+		ret = gtktree_iter_find_node(child, tofind);
+		if (ret)
+			return ret;
+
+		valid = gtk_tree_model_iter_next(model2, child);
+	}
+
+	return NULL;
+}
+
+
+/*
+ * Update the tree by adding/removing entries
+ * Does not change other nodes
+ */
+static void update_tree(struct menu *src, GtkTreeIter * dst)
+{
+	struct menu *child1;
+	GtkTreeIter iter, tmp;
+	GtkTreeIter *child2 = &iter;
+	gboolean valid;
+	GtkTreeIter *sibling;
+	struct symbol *sym;
+	struct property *prop;
+	struct menu *menu1, *menu2;
+
+	if (src == &rootmenu)
+		indent = 1;
+
+	valid = gtk_tree_model_iter_children(model2, child2, dst);
+	for (child1 = src->list; child1; child1 = child1->next) {
+
+		prop = child1->prompt;
+		sym = child1->sym;
+
+	      reparse:
+		menu1 = child1;
+		if (valid)
+			gtk_tree_model_get(model2, child2, COL_MENU,
+					   &menu2, -1);
+		else
+			menu2 = NULL;	// force adding of a first child
+
+#ifdef DEBUG
+		printf("%*c%s | %s\n", indent, ' ',
+		       menu1 ? menu_get_prompt(menu1) : "nil",
+		       menu2 ? menu_get_prompt(menu2) : "nil");
+#endif
+
+		if (!menu_is_visible(child1) && !show_all) {	// remove node
+			if (gtktree_iter_find_node(dst, menu1) != NULL) {
+				memcpy(&tmp, child2, sizeof(GtkTreeIter));
+				valid = gtk_tree_model_iter_next(model2,
+								 child2);
+				gtk_tree_store_remove(tree2, &tmp);
+				if (!valid)
+					return;	// next parent
+				else
+					goto reparse;	// next child
+			} else
+				continue;
+		}
+
+		if (menu1 != menu2) {
+			if (gtktree_iter_find_node(dst, menu1) == NULL) {	// add node
+				if (!valid && !menu2)
+					sibling = NULL;
+				else
+					sibling = child2;
+				gtk_tree_store_insert_before(tree2,
+							     child2,
+							     dst, sibling);
+				set_node(child2, menu1, fill_row(menu1));
+				if (menu2 == NULL)
+					valid = TRUE;
+			} else {	// remove node
+				memcpy(&tmp, child2, sizeof(GtkTreeIter));
+				valid = gtk_tree_model_iter_next(model2,
+								 child2);
+				gtk_tree_store_remove(tree2, &tmp);
+				if (!valid)
+					return;	// next parent
+				else
+					goto reparse;	// next child
+			}
+		} else if (sym && (sym->flags & SYMBOL_CHANGED)) {
+			set_node(child2, menu1, fill_row(menu1));
+		}
+
+		indent++;
+		update_tree(child1, child2);
+		indent--;
+
+		valid = gtk_tree_model_iter_next(model2, child2);
+	}
+}
+
+
+/* Display the whole tree (single/split/full view) */
+static void display_tree(struct menu *menu)
+{
+	struct symbol *sym;
+	struct property *prop;
+	struct menu *child;
+	enum prop_type ptype;
+
+	if (menu == &rootmenu) {
+		indent = 1;
+		current = &rootmenu;
+	}
+
+	for (child = menu->list; child; child = child->next) {
+		prop = child->prompt;
+		sym = child->sym;
+		ptype = prop ? prop->type : P_UNKNOWN;
+
+		if (sym)
+			sym->flags &= ~SYMBOL_CHANGED;
+
+		if ((view_mode == SPLIT_VIEW)
+		    && !(child->flags & MENU_ROOT) && (tree == tree1))
+			continue;
+
+		if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
+		    && (tree == tree2))
+			continue;
+
+		if (menu_is_visible(child) || show_all)
+			place_node(child, fill_row(child));
+#ifdef DEBUG
+		printf("%*c%s: ", indent, ' ', menu_get_prompt(child));
+		printf("%s", child->flags & MENU_ROOT ? "rootmenu | " : "");
+		dbg_print_ptype(ptype);
+		printf(" | ");
+		if (sym) {
+			dbg_print_stype(sym->type);
+			printf(" | ");
+			dbg_print_flags(sym->flags);
+			printf("\n");
+		} else
+			printf("\n");
+#endif
+		if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
+		    && (tree == tree2))
+			continue;
+/*
+		if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
+		    || (view_mode == FULL_VIEW)
+		    || (view_mode == SPLIT_VIEW))*/
+		if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
+		    || (view_mode == FULL_VIEW)
+		    || (view_mode == SPLIT_VIEW)) {
+			indent++;
+			display_tree(child);
+			indent--;
+		}
+	}
+}
+
+/* Display a part of the tree starting at current node (single/split view) */
+static void display_tree_part(void)
+{
+	if (tree2)
+		gtk_tree_store_clear(tree2);
+	if (view_mode == SINGLE_VIEW)
+		display_tree(current);
+	else if (view_mode == SPLIT_VIEW)
+		display_tree(browsed);
+	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
+}
+
+/* Display the list in the left frame (split view) */
+static void display_list(void)
+{
+	if (tree1)
+		gtk_tree_store_clear(tree1);
+
+	tree = tree1;
+	display_tree(&rootmenu);
+	gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
+	tree = tree2;
+}
+
+void fixup_rootmenu(struct menu *menu)
+{
+	struct menu *child;
+	static int menu_cnt = 0;
+
+	menu->flags |= MENU_ROOT;
+	for (child = menu->list; child; child = child->next) {
+		if (child->prompt && child->prompt->type == P_MENU) {
+			menu_cnt++;
+			fixup_rootmenu(child);
+			menu_cnt--;
+		} else if (!menu_cnt)
+			fixup_rootmenu(child);
+	}
+}
+
+
+/* Main */
+int main(int ac, char *av[])
+{
+	const char *name;
+	char *env;
+	gchar *glade_file;
+
+#ifndef LKC_DIRECT_LINK
+	kconfig_load();
+#endif
+
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset(PACKAGE, "UTF-8");
+	textdomain(PACKAGE);
+
+	/* GTK stuffs */
+	gtk_set_locale();
+	gtk_init(&ac, &av);
+	glade_init();
+
+	//add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
+	//add_pixmap_directory (PACKAGE_SOURCE_DIR "/pixmaps");
+
+	/* Determine GUI path */
+	env = getenv(SRCTREE);
+	if (env)
+		glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
+	else if (av[0][0] == '/')
+		glade_file = g_strconcat(av[0], ".glade", NULL);
+	else
+		glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
+
+	/* Load the interface and connect signals */
+	init_main_window(glade_file);
+	init_tree_model();
+	init_left_tree();
+	init_right_tree();
+
+	/* Conf stuffs */
+	if (ac > 1 && av[1][0] == '-') {
+		switch (av[1][1]) {
+		case 'a':
+			//showAll = 1;
+			break;
+		case 'h':
+		case '?':
+			printf("%s <config>\n", av[0]);
+			exit(0);
+		}
+		name = av[2];
+	} else
+		name = av[1];
+
+	conf_parse(name);
+	fixup_rootmenu(&rootmenu);
+	conf_read(NULL);
+
+	switch (view_mode) {
+	case SINGLE_VIEW:
+		display_tree_part();
+		break;
+	case SPLIT_VIEW:
+		display_list();
+		break;
+	case FULL_VIEW:
+		display_tree(&rootmenu);
+		break;
+	}
+
+	gtk_main();
+
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/gconf.glade b/busybox-1.19.3/scripts/kconfig/gconf.glade
new file mode 100644
index 0000000..f8744ed
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/gconf.glade
@@ -0,0 +1,648 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+
+<widget class="GtkWindow" id="window1">
+  <property name="visible">True</property>
+  <property name="title" translatable="yes">Gtk Kernel Configurator</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_NONE</property>
+  <property name="modal">False</property>
+  <property name="default_width">640</property>
+  <property name="default_height">480</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <signal name="destroy" handler="on_window1_destroy" object="window1"/>
+  <signal name="size_request" handler="on_window1_size_request" object="vpaned1" last_modification_time="Fri, 11 Jan 2002 16:17:11 GMT"/>
+  <signal name="delete_event" handler="on_window1_delete_event" object="window1" last_modification_time="Sun, 09 Mar 2003 19:42:46 GMT"/>
+
+  <child>
+    <widget class="GtkVBox" id="vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child>
+	<widget class="GtkMenuBar" id="menubar1">
+	  <property name="visible">True</property>
+
+	  <child>
+	    <widget class="GtkMenuItem" id="file1">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">_File</property>
+	      <property name="use_underline">True</property>
+
+	      <child>
+		<widget class="GtkMenu" id="file1_menu">
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="load1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Load a config file</property>
+		      <property name="label" translatable="yes">_Load</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_load1_activate"/>
+		      <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image39">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-open</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="save1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Save the config in .config</property>
+		      <property name="label" translatable="yes">_Save</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_save1_activate"/>
+		      <accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image40">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-save</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="save_as1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Save the config in a file</property>
+		      <property name="label" translatable="yes">Save _as</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_save_as1_activate"/>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image41">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-save-as</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkSeparatorMenuItem" id="separator1">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="quit1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_Quit</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_quit1_activate"/>
+		      <accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image42">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-quit</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkMenuItem" id="options1">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">_Options</property>
+	      <property name="use_underline">True</property>
+
+	      <child>
+		<widget class="GtkMenu" id="options1_menu">
+
+		  <child>
+		    <widget class="GtkCheckMenuItem" id="show_name1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Show name</property>
+		      <property name="label" translatable="yes">Show _name</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">False</property>
+		      <signal name="activate" handler="on_show_name1_activate"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkCheckMenuItem" id="show_range1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Show range (Y/M/N)</property>
+		      <property name="label" translatable="yes">Show _range</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">False</property>
+		      <signal name="activate" handler="on_show_range1_activate"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkCheckMenuItem" id="show_data1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Show value of the option</property>
+		      <property name="label" translatable="yes">Show _data</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">False</property>
+		      <signal name="activate" handler="on_show_data1_activate"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkSeparatorMenuItem" id="separator2">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkCheckMenuItem" id="show_all_options1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Show all options</property>
+		      <property name="label" translatable="yes">Show all _options</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">False</property>
+		      <signal name="activate" handler="on_show_all_options1_activate"/>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkCheckMenuItem" id="show_debug_info1">
+		      <property name="visible">True</property>
+		      <property name="tooltip" translatable="yes">Show masked options</property>
+		      <property name="label" translatable="yes">Show _debug info</property>
+		      <property name="use_underline">True</property>
+		      <property name="active">False</property>
+		      <signal name="activate" handler="on_show_debug_info1_activate"/>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkMenuItem" id="help1">
+	      <property name="visible">True</property>
+	      <property name="label" translatable="yes">_Help</property>
+	      <property name="use_underline">True</property>
+
+	      <child>
+		<widget class="GtkMenu" id="help1_menu">
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="introduction1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_Introduction</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_introduction1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
+		      <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image43">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-dialog-question</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="about1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_About</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_about1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
+		      <accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image44">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-properties</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+
+		  <child>
+		    <widget class="GtkImageMenuItem" id="license1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">_License</property>
+		      <property name="use_underline">True</property>
+		      <signal name="activate" handler="on_license1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
+
+		      <child internal-child="image">
+			<widget class="GtkImage" id="image45">
+			  <property name="visible">True</property>
+			  <property name="stock">gtk-justify-fill</property>
+			  <property name="icon_size">1</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHandleBox" id="handlebox1">
+	  <property name="visible">True</property>
+	  <property name="shadow_type">GTK_SHADOW_OUT</property>
+	  <property name="handle_position">GTK_POS_LEFT</property>
+	  <property name="snap_edge">GTK_POS_TOP</property>
+
+	  <child>
+	    <widget class="GtkToolbar" id="toolbar1">
+	      <property name="visible">True</property>
+	      <property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
+	      <property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
+	      <property name="tooltips">True</property>
+	      <property name="show_arrow">True</property>
+
+	      <child>
+		<widget class="GtkToolButton" id="button1">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Goes up of one level (single view)</property>
+		  <property name="label" translatable="yes">Back</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-undo</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_back_clicked"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolItem" id="toolitem1">
+		  <property name="visible">True</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+
+		  <child>
+		    <widget class="GtkVSeparator" id="vseparator1">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolButton" id="button2">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Load a config file</property>
+		  <property name="label" translatable="yes">Load</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-open</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_load_clicked"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolButton" id="button3">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Save a config file</property>
+		  <property name="label" translatable="yes">Save</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-save</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_save_clicked"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolItem" id="toolitem2">
+		  <property name="visible">True</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+
+		  <child>
+		    <widget class="GtkVSeparator" id="vseparator2">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolButton" id="button4">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Single view</property>
+		  <property name="label" translatable="yes">Single</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-missing-image</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_single_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:39 GMT"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolButton" id="button5">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Split view</property>
+		  <property name="label" translatable="yes">Split</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-missing-image</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_split_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:45 GMT"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolButton" id="button6">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Full view</property>
+		  <property name="label" translatable="yes">Full</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-missing-image</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_full_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:50 GMT"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolItem" id="toolitem3">
+		  <property name="visible">True</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+
+		  <child>
+		    <widget class="GtkVSeparator" id="vseparator3">
+		      <property name="visible">True</property>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolButton" id="button7">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Collapse the whole tree in the right frame</property>
+		  <property name="label" translatable="yes">Collapse</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-remove</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_collapse_clicked"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkToolButton" id="button8">
+		  <property name="visible">True</property>
+		  <property name="tooltip" translatable="yes">Expand the whole tree in the right frame</property>
+		  <property name="label" translatable="yes">Expand</property>
+		  <property name="use_underline">True</property>
+		  <property name="stock_id">gtk-add</property>
+		  <property name="visible_horizontal">True</property>
+		  <property name="visible_vertical">True</property>
+		  <property name="is_important">False</property>
+		  <signal name="clicked" handler="on_expand_clicked"/>
+		</widget>
+		<packing>
+		  <property name="expand">False</property>
+		  <property name="homogeneous">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">False</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHPaned" id="hpaned1">
+	  <property name="width_request">1</property>
+	  <property name="visible">True</property>
+	  <property name="can_focus">True</property>
+	  <property name="position">0</property>
+
+	  <child>
+	    <widget class="GtkScrolledWindow" id="scrolledwindow1">
+	      <property name="visible">True</property>
+	      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+	      <property name="shadow_type">GTK_SHADOW_IN</property>
+	      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+	      <child>
+		<widget class="GtkTreeView" id="treeview1">
+		  <property name="visible">True</property>
+		  <property name="can_focus">True</property>
+		  <property name="headers_visible">True</property>
+		  <property name="rules_hint">False</property>
+		  <property name="reorderable">False</property>
+		  <property name="enable_search">True</property>
+		  <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:58:22 GMT"/>
+		  <signal name="button_press_event" handler="on_treeview1_button_press_event" last_modification_time="Sun, 12 Jan 2003 16:03:52 GMT"/>
+		  <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 16:11:44 GMT"/>
+		</widget>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="shrink">True</property>
+	      <property name="resize">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkVPaned" id="vpaned1">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="position">0</property>
+
+	      <child>
+		<widget class="GtkScrolledWindow" id="scrolledwindow2">
+		  <property name="visible">True</property>
+		  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		  <property name="shadow_type">GTK_SHADOW_IN</property>
+		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+		  <child>
+		    <widget class="GtkTreeView" id="treeview2">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="has_focus">True</property>
+		      <property name="headers_visible">True</property>
+		      <property name="rules_hint">False</property>
+		      <property name="reorderable">False</property>
+		      <property name="enable_search">True</property>
+		      <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:57:55 GMT"/>
+		      <signal name="button_press_event" handler="on_treeview2_button_press_event" last_modification_time="Sun, 12 Jan 2003 15:57:58 GMT"/>
+		      <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 15:58:01 GMT"/>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="shrink">True</property>
+		  <property name="resize">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkScrolledWindow" id="scrolledwindow3">
+		  <property name="visible">True</property>
+		  <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+		  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		  <property name="shadow_type">GTK_SHADOW_IN</property>
+		  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+		  <child>
+		    <widget class="GtkTextView" id="textview3">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="editable">False</property>
+		      <property name="overwrite">False</property>
+		      <property name="accepts_tab">True</property>
+		      <property name="justification">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap_mode">GTK_WRAP_WORD</property>
+		      <property name="cursor_visible">True</property>
+		      <property name="pixels_above_lines">0</property>
+		      <property name="pixels_below_lines">0</property>
+		      <property name="pixels_inside_wrap">0</property>
+		      <property name="left_margin">0</property>
+		      <property name="right_margin">0</property>
+		      <property name="indent">0</property>
+		      <property name="text" translatable="yes">Sorry, no help available for this option yet.</property>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="shrink">True</property>
+		  <property name="resize">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="shrink">True</property>
+	      <property name="resize">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
diff --git a/busybox-1.19.3/scripts/kconfig/images.c b/busybox-1.19.3/scripts/kconfig/images.c
new file mode 100644
index 0000000..d4f84bd
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/images.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+static const char *xpm_load[] = {
+"22 22 5 1",
+". c None",
+"# c #000000",
+"c c #838100",
+"a c #ffff00",
+"b c #ffffff",
+"......................",
+"......................",
+"......................",
+"............####....#.",
+"...........#....##.##.",
+"..................###.",
+".................####.",
+".####...........#####.",
+"#abab##########.......",
+"#babababababab#.......",
+"#ababababababa#.......",
+"#babababababab#.......",
+"#ababab###############",
+"#babab##cccccccccccc##",
+"#abab##cccccccccccc##.",
+"#bab##cccccccccccc##..",
+"#ab##cccccccccccc##...",
+"#b##cccccccccccc##....",
+"###cccccccccccc##.....",
+"##cccccccccccc##......",
+"###############.......",
+"......................"};
+
+static const char *xpm_save[] = {
+"22 22 5 1",
+". c None",
+"# c #000000",
+"a c #838100",
+"b c #c5c2c5",
+"c c #cdb6d5",
+"......................",
+".####################.",
+".#aa#bbbbbbbbbbbb#bb#.",
+".#aa#bbbbbbbbbbbb#bb#.",
+".#aa#bbbbbbbbbcbb####.",
+".#aa#bbbccbbbbbbb#aa#.",
+".#aa#bbbccbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aa#bbbbbbbbbbbb#aa#.",
+".#aaa############aaa#.",
+".#aaaaaaaaaaaaaaaaaa#.",
+".#aaaaaaaaaaaaaaaaaa#.",
+".#aaa#############aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+".#aaa#########bbb#aa#.",
+"..##################..",
+"......................"};
+
+static const char *xpm_back[] = {
+"22 22 3 1",
+". c None",
+"# c #000083",
+"a c #838183",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................",
+"...........######a....",
+"..#......##########...",
+"..##...####......##a..",
+"..###.###.........##..",
+"..######..........##..",
+"..#####...........##..",
+"..######..........##..",
+"..#######.........##..",
+"..########.......##a..",
+"...............a###...",
+"...............###....",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................",
+"......................"};
+
+static const char *xpm_tree_view[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......#...............",
+"......########........",
+"......................",
+"......................"};
+
+static const char *xpm_single_view[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"..........#...........",
+"......................",
+"......................"};
+
+static const char *xpm_split_view[] = {
+"22 22 2 1",
+". c None",
+"# c #000000",
+"......................",
+"......................",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......#......#........",
+"......................",
+"......................"};
+
+static const char *xpm_symbol_no[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+" .......... ",
+" .        . ",
+" .        . ",
+" .        . ",
+" .        . ",
+" .        . ",
+" .        . ",
+" .        . ",
+" .        . ",
+" .......... ",
+"            "};
+
+static const char *xpm_symbol_mod[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+" .......... ",
+" .        . ",
+" .        . ",
+" .   ..   . ",
+" .  ....  . ",
+" .  ....  . ",
+" .   ..   . ",
+" .        . ",
+" .        . ",
+" .......... ",
+"            "};
+
+static const char *xpm_symbol_yes[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+" .......... ",
+" .        . ",
+" .        . ",
+" .      . . ",
+" .     .. . ",
+" . .  ..  . ",
+" . ....   . ",
+" .  ..    . ",
+" .        . ",
+" .......... ",
+"            "};
+
+static const char *xpm_choice_no[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+"    ....    ",
+"  ..    ..  ",
+"  .      .  ",
+" .        . ",
+" .        . ",
+" .        . ",
+" .        . ",
+"  .      .  ",
+"  ..    ..  ",
+"    ....    ",
+"            "};
+
+static const char *xpm_choice_yes[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+"    ....    ",
+"  ..    ..  ",
+"  .      .  ",
+" .   ..   . ",
+" .  ....  . ",
+" .  ....  . ",
+" .   ..   . ",
+"  .      .  ",
+"  ..    ..  ",
+"    ....    ",
+"            "};
+
+static const char *xpm_menu[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+" .......... ",
+" .        . ",
+" . ..     . ",
+" . ....   . ",
+" . ...... . ",
+" . ...... . ",
+" . ....   . ",
+" . ..     . ",
+" .        . ",
+" .......... ",
+"            "};
+
+static const char *xpm_menu_inv[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+" .......... ",
+" .......... ",
+" ..  ...... ",
+" ..    .... ",
+" ..      .. ",
+" ..      .. ",
+" ..    .... ",
+" ..  ...... ",
+" .......... ",
+" .......... ",
+"            "};
+
+static const char *xpm_menuback[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+" .......... ",
+" .        . ",
+" .     .. . ",
+" .   .... . ",
+" . ...... . ",
+" . ...... . ",
+" .   .... . ",
+" .     .. . ",
+" .        . ",
+" .......... ",
+"            "};
+
+static const char *xpm_void[] = {
+"12 12 2 1",
+"  c white",
+". c black",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            "};
diff --git a/busybox-1.19.3/scripts/kconfig/kconfig_load.c b/busybox-1.19.3/scripts/kconfig/kconfig_load.c
new file mode 100644
index 0000000..5e87dd5
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/kconfig_load.c
@@ -0,0 +1,35 @@
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lkc.h"
+
+#define P(name,type,arg)	type (*name ## _p) arg
+#include "lkc_proto.h"
+#undef P
+
+void kconfig_load(void)
+{
+	void *handle;
+	char *error;
+
+	handle = dlopen("./libkconfig.so", RTLD_LAZY);
+	if (!handle) {
+		handle = dlopen("./scripts/kconfig/libkconfig.so", RTLD_LAZY);
+		if (!handle) {
+			fprintf(stderr, "%s\n", dlerror());
+			exit(1);
+		}
+	}
+
+#define P(name,type,arg)			\
+{						\
+	name ## _p = dlsym(handle, #name);	\
+	if ((error = dlerror()))  {		\
+		fprintf(stderr, "%s\n", error);	\
+		exit(1);			\
+	}					\
+}
+#include "lkc_proto.h"
+#undef P
+}
diff --git a/busybox-1.19.3/scripts/kconfig/kxgettext.c b/busybox-1.19.3/scripts/kconfig/kxgettext.c
new file mode 100644
index 0000000..abee55c
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/kxgettext.c
@@ -0,0 +1,227 @@
+/*
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2005
+ *
+ * Released under the terms of the GNU GPL v2.0
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static char *escape(const char* text, char *bf, int len)
+{
+	char *bfp = bf;
+	int multiline = strchr(text, '\n') != NULL;
+	int eol = 0;
+	int textlen = strlen(text);
+
+	if ((textlen > 0) && (text[textlen-1] == '\n'))
+		eol = 1;
+
+	*bfp++ = '"';
+	--len;
+
+	if (multiline) {
+		*bfp++ = '"';
+		*bfp++ = '\n';
+		*bfp++ = '"';
+		len -= 3;
+	}
+
+	while (*text != '\0' && len > 1) {
+		if (*text == '"')
+			*bfp++ = '\\';
+		else if (*text == '\n') {
+			*bfp++ = '\\';
+			*bfp++ = 'n';
+			*bfp++ = '"';
+			*bfp++ = '\n';
+			*bfp++ = '"';
+			len -= 5;
+			++text;
+			goto next;
+		}
+		*bfp++ = *text++;
+next:
+		--len;
+	}
+
+	if (multiline && eol)
+		bfp -= 3;
+
+	*bfp++ = '"';
+	*bfp = '\0';
+
+	return bf;
+}
+
+struct file_line {
+	struct file_line *next;
+	char*		 file;
+	int		 lineno;
+};
+
+static struct file_line *file_line__new(char *file, int lineno)
+{
+	struct file_line *self = malloc(sizeof(*self));
+
+	if (self == NULL)
+		goto out;
+
+	self->file   = file;
+	self->lineno = lineno;
+	self->next   = NULL;
+out:
+	return self;
+}
+
+struct message {
+	const char	 *msg;
+	const char	 *option;
+	struct message	 *next;
+	struct file_line *files;
+};
+
+static struct message *message__list;
+
+static struct message *message__new(const char *msg, char *option, char *file, int lineno)
+{
+	struct message *self = malloc(sizeof(*self));
+
+	if (self == NULL)
+		goto out;
+
+	self->files = file_line__new(file, lineno);
+	if (self->files == NULL)
+		goto out_fail;
+
+	self->msg = strdup(msg);
+	if (self->msg == NULL)
+		goto out_fail_msg;
+
+	self->option = option;
+	self->next = NULL;
+out:
+	return self;
+out_fail_msg:
+	free(self->files);
+out_fail:
+	free(self);
+	self = NULL;
+	goto out;
+}
+
+static struct message *mesage__find(const char *msg)
+{
+	struct message *m = message__list;
+
+	while (m != NULL) {
+		if (strcmp(m->msg, msg) == 0)
+			break;
+		m = m->next;
+	}
+
+	return m;
+}
+
+static int message__add_file_line(struct message *self, char *file, int lineno)
+{
+	int rc = -1;
+	struct file_line *fl = file_line__new(file, lineno);
+
+	if (fl == NULL)
+		goto out;
+
+	fl->next    = self->files;
+	self->files = fl;
+	rc = 0;
+out:
+	return rc;
+}
+
+static int message__add(const char *msg, char *option, char *file, int lineno)
+{
+	int rc = 0;
+	char bf[16384];
+	char *escaped = escape(msg, bf, sizeof(bf));
+	struct message *m = mesage__find(escaped);
+
+	if (m != NULL)
+		rc = message__add_file_line(m, file, lineno);
+	else {
+		m = message__new(escaped, option, file, lineno);
+
+		if (m != NULL) {
+			m->next	      = message__list;
+			message__list = m;
+		} else
+			rc = -1;
+	}
+	return rc;
+}
+
+void menu_build_message_list(struct menu *menu)
+{
+	struct menu *child;
+
+	message__add(menu_get_prompt(menu), NULL,
+		     menu->file == NULL ? "Root Menu" : menu->file->name,
+		     menu->lineno);
+
+	if (menu->sym != NULL && menu->sym->help != NULL)
+		message__add(menu->sym->help, menu->sym->name,
+			     menu->file == NULL ? "Root Menu" : menu->file->name,
+			     menu->lineno);
+
+	for (child = menu->list; child != NULL; child = child->next)
+		if (child->prompt != NULL)
+			menu_build_message_list(child);
+}
+
+static void message__print_file_lineno(struct message *self)
+{
+	struct file_line *fl = self->files;
+
+	putchar('\n');
+	if (self->option != NULL)
+		printf("# %s:00000\n", self->option);
+
+	printf("#: %s:%d", fl->file, fl->lineno);
+	fl = fl->next;
+
+	while (fl != NULL) {
+		printf(", %s:%d", fl->file, fl->lineno);
+		fl = fl->next;
+	}
+
+	putchar('\n');
+}
+
+static void message__print_gettext_msgid_msgstr(struct message *self)
+{
+	message__print_file_lineno(self);
+
+	printf("msgid %s\n"
+	       "msgstr \"\"\n", self->msg);
+}
+
+void menu__xgettext(void)
+{
+	struct message *m = message__list;
+
+	while (m != NULL) {
+		message__print_gettext_msgid_msgstr(m);
+		m = m->next;
+	}
+}
+
+int main(int ac, char **av)
+{
+	conf_parse(av[1]);
+
+	menu_build_message_list(menu_get_root_menu(NULL));
+	menu__xgettext();
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lex.zconf.c_shipped b/busybox-1.19.3/scripts/kconfig/lex.zconf.c_shipped
new file mode 100644
index 0000000..51f15e1
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lex.zconf.c_shipped
@@ -0,0 +1,2325 @@
+
+#line 3 "scripts/kconfig/lex.zconf.c"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+#define YY_FLEX_SUBMINOR_VERSION 31
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+#endif /* ! C99 */
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else	/* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_CONST
+
+#endif	/* __STDC__ */
+#endif	/* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index.  If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE zconfrestart(zconfin  )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+extern int zconfleng;
+
+extern FILE *zconfin, *zconfout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+    #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up zconftext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = (yy_hold_char); \
+		YY_RESTORE_YY_MORE_OFFSET \
+		(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up zconftext again */ \
+		} \
+	while ( 0 )
+
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef unsigned int yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	yy_size_t yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via zconfrestart()), so that the user can continue scanning by
+	 * just pointing zconfin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when zconftext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+int zconfleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow zconfwrap()'s to do buffer switches
+ * instead of setting up a fresh zconfin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void zconfrestart (FILE *input_file  );
+void zconf_switch_to_buffer (YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE zconf_create_buffer (FILE *file,int size  );
+void zconf_delete_buffer (YY_BUFFER_STATE b  );
+void zconf_flush_buffer (YY_BUFFER_STATE b  );
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer  );
+void zconfpop_buffer_state (void );
+
+static void zconfensure_buffer_stack (void );
+static void zconf_load_buffer_state (void );
+static void zconf_init_buffer (YY_BUFFER_STATE b,FILE *file  );
+
+#define YY_FLUSH_BUFFER zconf_flush_buffer(YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE zconf_scan_buffer (char *base,yy_size_t size  );
+YY_BUFFER_STATE zconf_scan_string (yyconst char *yy_str  );
+YY_BUFFER_STATE zconf_scan_bytes (yyconst char *bytes,int len  );
+
+void *zconfalloc (yy_size_t  );
+void *zconfrealloc (void *,yy_size_t  );
+void zconffree (void *  );
+
+#define yy_new_buffer zconf_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        zconfensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        zconfensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            zconf_create_buffer(zconfin,YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define zconfwrap() 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+FILE *zconfin = (FILE *) 0, *zconfout = (FILE *) 0;
+
+typedef int yy_state_type;
+
+extern int zconflineno;
+
+int zconflineno = 1;
+
+extern char *zconftext;
+#define yytext_ptr zconftext
+static yyconst flex_int16_t yy_nxt[][17] =
+    {
+    {
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0
+    },
+
+    {
+       11,   12,   13,   14,   12,   12,   15,   12,   12,   12,
+       12,   12,   12,   12,   12,   12,   12
+    },
+
+    {
+       11,   12,   13,   14,   12,   12,   15,   12,   12,   12,
+       12,   12,   12,   12,   12,   12,   12
+    },
+
+    {
+       11,   16,   16,   17,   16,   16,   16,   16,   16,   16,
+       16,   16,   16,   18,   16,   16,   16
+    },
+
+    {
+       11,   16,   16,   17,   16,   16,   16,   16,   16,   16,
+       16,   16,   16,   18,   16,   16,   16
+
+    },
+
+    {
+       11,   19,   20,   21,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19
+    },
+
+    {
+       11,   19,   20,   21,   19,   19,   19,   19,   19,   19,
+       19,   19,   19,   19,   19,   19,   19
+    },
+
+    {
+       11,   22,   22,   23,   22,   24,   22,   22,   24,   22,
+       22,   22,   22,   22,   22,   25,   22
+    },
+
+    {
+       11,   22,   22,   23,   22,   24,   22,   22,   24,   22,
+       22,   22,   22,   22,   22,   25,   22
+    },
+
+    {
+       11,   26,   26,   27,   28,   29,   30,   31,   29,   32,
+       33,   34,   35,   35,   36,   37,   38
+
+    },
+
+    {
+       11,   26,   26,   27,   28,   29,   30,   31,   29,   32,
+       33,   34,   35,   35,   36,   37,   38
+    },
+
+    {
+      -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -11,  -11,  -11,  -11,  -11,  -11
+    },
+
+    {
+       11,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,
+      -12,  -12,  -12,  -12,  -12,  -12,  -12
+    },
+
+    {
+       11,  -13,   39,   40,  -13,  -13,   41,  -13,  -13,  -13,
+      -13,  -13,  -13,  -13,  -13,  -13,  -13
+    },
+
+    {
+       11,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14
+
+    },
+
+    {
+       11,   42,   42,   43,   42,   42,   42,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42
+    },
+
+    {
+       11,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,  -16,
+      -16,  -16,  -16,  -16,  -16,  -16,  -16
+    },
+
+    {
+       11,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,
+      -17,  -17,  -17,  -17,  -17,  -17,  -17
+    },
+
+    {
+       11,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,
+      -18,  -18,  -18,   44,  -18,  -18,  -18
+    },
+
+    {
+       11,   45,   45,  -19,   45,   45,   45,   45,   45,   45,
+       45,   45,   45,   45,   45,   45,   45
+
+    },
+
+    {
+       11,  -20,   46,   47,  -20,  -20,  -20,  -20,  -20,  -20,
+      -20,  -20,  -20,  -20,  -20,  -20,  -20
+    },
+
+    {
+       11,   48,  -21,  -21,   48,   48,   48,   48,   48,   48,
+       48,   48,   48,   48,   48,   48,   48
+    },
+
+    {
+       11,   49,   49,   50,   49,  -22,   49,   49,  -22,   49,
+       49,   49,   49,   49,   49,  -22,   49
+    },
+
+    {
+       11,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,
+      -23,  -23,  -23,  -23,  -23,  -23,  -23
+    },
+
+    {
+       11,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
+      -24,  -24,  -24,  -24,  -24,  -24,  -24
+
+    },
+
+    {
+       11,   51,   51,   52,   51,   51,   51,   51,   51,   51,
+       51,   51,   51,   51,   51,   51,   51
+    },
+
+    {
+       11,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,
+      -26,  -26,  -26,  -26,  -26,  -26,  -26
+    },
+
+    {
+       11,  -27,  -27,  -27,  -27,  -27,  -27,  -27,  -27,  -27,
+      -27,  -27,  -27,  -27,  -27,  -27,  -27
+    },
+
+    {
+       11,  -28,  -28,  -28,  -28,  -28,  -28,  -28,  -28,  -28,
+      -28,  -28,  -28,  -28,   53,  -28,  -28
+    },
+
+    {
+       11,  -29,  -29,  -29,  -29,  -29,  -29,  -29,  -29,  -29,
+      -29,  -29,  -29,  -29,  -29,  -29,  -29
+
+    },
+
+    {
+       11,   54,   54,  -30,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54
+    },
+
+    {
+       11,  -31,  -31,  -31,  -31,  -31,  -31,   55,  -31,  -31,
+      -31,  -31,  -31,  -31,  -31,  -31,  -31
+    },
+
+    {
+       11,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,  -32,
+      -32,  -32,  -32,  -32,  -32,  -32,  -32
+    },
+
+    {
+       11,  -33,  -33,  -33,  -33,  -33,  -33,  -33,  -33,  -33,
+      -33,  -33,  -33,  -33,  -33,  -33,  -33
+    },
+
+    {
+       11,  -34,  -34,  -34,  -34,  -34,  -34,  -34,  -34,  -34,
+      -34,   56,   57,   57,  -34,  -34,  -34
+
+    },
+
+    {
+       11,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,  -35,
+      -35,   57,   57,   57,  -35,  -35,  -35
+    },
+
+    {
+       11,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,  -36,
+      -36,  -36,  -36,  -36,  -36,  -36,  -36
+    },
+
+    {
+       11,  -37,  -37,   58,  -37,  -37,  -37,  -37,  -37,  -37,
+      -37,  -37,  -37,  -37,  -37,  -37,  -37
+    },
+
+    {
+       11,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,  -38,
+      -38,  -38,  -38,  -38,  -38,  -38,   59
+    },
+
+    {
+       11,  -39,   39,   40,  -39,  -39,   41,  -39,  -39,  -39,
+      -39,  -39,  -39,  -39,  -39,  -39,  -39
+
+    },
+
+    {
+       11,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,  -40,  -40,  -40
+    },
+
+    {
+       11,   42,   42,   43,   42,   42,   42,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42
+    },
+
+    {
+       11,   42,   42,   43,   42,   42,   42,   42,   42,   42,
+       42,   42,   42,   42,   42,   42,   42
+    },
+
+    {
+       11,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,
+      -43,  -43,  -43,  -43,  -43,  -43,  -43
+    },
+
+    {
+       11,  -44,  -44,  -44,  -44,  -44,  -44,  -44,  -44,  -44,
+      -44,  -44,  -44,   44,  -44,  -44,  -44
+
+    },
+
+    {
+       11,   45,   45,  -45,   45,   45,   45,   45,   45,   45,
+       45,   45,   45,   45,   45,   45,   45
+    },
+
+    {
+       11,  -46,   46,   47,  -46,  -46,  -46,  -46,  -46,  -46,
+      -46,  -46,  -46,  -46,  -46,  -46,  -46
+    },
+
+    {
+       11,   48,  -47,  -47,   48,   48,   48,   48,   48,   48,
+       48,   48,   48,   48,   48,   48,   48
+    },
+
+    {
+       11,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48
+    },
+
+    {
+       11,   49,   49,   50,   49,  -49,   49,   49,  -49,   49,
+       49,   49,   49,   49,   49,  -49,   49
+
+    },
+
+    {
+       11,  -50,  -50,  -50,  -50,  -50,  -50,  -50,  -50,  -50,
+      -50,  -50,  -50,  -50,  -50,  -50,  -50
+    },
+
+    {
+       11,  -51,  -51,   52,  -51,  -51,  -51,  -51,  -51,  -51,
+      -51,  -51,  -51,  -51,  -51,  -51,  -51
+    },
+
+    {
+       11,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52,  -52,  -52,  -52,  -52
+    },
+
+    {
+       11,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,
+      -53,  -53,  -53,  -53,  -53,  -53,  -53
+    },
+
+    {
+       11,   54,   54,  -54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54
+
+    },
+
+    {
+       11,  -55,  -55,  -55,  -55,  -55,  -55,  -55,  -55,  -55,
+      -55,  -55,  -55,  -55,  -55,  -55,  -55
+    },
+
+    {
+       11,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,
+      -56,   60,   57,   57,  -56,  -56,  -56
+    },
+
+    {
+       11,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+      -57,   57,   57,   57,  -57,  -57,  -57
+    },
+
+    {
+       11,  -58,  -58,  -58,  -58,  -58,  -58,  -58,  -58,  -58,
+      -58,  -58,  -58,  -58,  -58,  -58,  -58
+    },
+
+    {
+       11,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,
+      -59,  -59,  -59,  -59,  -59,  -59,  -59
+
+    },
+
+    {
+       11,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,
+      -60,   57,   57,   57,  -60,  -60,  -60
+    },
+
+    } ;
+
+static yy_state_type yy_get_previous_state (void );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state  );
+static int yy_get_next_buffer (void );
+static void yy_fatal_error (yyconst char msg[]  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up zconftext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	(yytext_ptr) = yy_bp; \
+	zconfleng = (size_t) (yy_cp - yy_bp); \
+	(yy_hold_char) = *yy_cp; \
+	*yy_cp = '\0'; \
+	(yy_c_buf_p) = yy_cp;
+
+#define YY_NUM_RULES 33
+#define YY_END_OF_BUFFER 34
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static yyconst flex_int16_t yy_accept[61] =
+    {   0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+       34,    5,    4,    2,    3,    7,    8,    6,   32,   29,
+       31,   24,   28,   27,   26,   22,   17,   13,   16,   20,
+       22,   11,   12,   19,   19,   14,   22,   22,    4,    2,
+        3,    3,    1,    6,   32,   29,   31,   30,   24,   23,
+       26,   25,   15,   20,    9,   19,   19,   21,   10,   18
+    } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    5,    6,    1,    1,    7,    8,    9,
+       10,    1,    1,    1,   11,   12,   12,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,    1,    1,    1,
+       14,    1,    1,    1,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+        1,   15,    1,    1,   13,    1,   13,   13,   13,   13,
+
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,    1,   16,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+extern int zconf_flex_debug;
+int zconf_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *zconftext;
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define START_STRSIZE	16
+
+static struct {
+	struct file *file;
+	int lineno;
+} current_pos;
+
+static char *text;
+static int text_size, text_asize;
+
+struct buffer {
+        struct buffer *parent;
+        YY_BUFFER_STATE state;
+};
+
+struct buffer *current_buf;
+
+static int last_ts, first_ts;
+
+static void zconf_endhelp(void);
+static void zconf_endfile(void);
+
+void new_string(void)
+{
+	text = malloc(START_STRSIZE);
+	text_asize = START_STRSIZE;
+	text_size = 0;
+	*text = 0;
+}
+
+void append_string(const char *str, int size)
+{
+	int new_size = text_size + size + 1;
+	if (size > 70) {
+		fprintf (stderr, "%s:%d error: Overlong line\n",
+		current_file->name, current_file->lineno);
+	}
+
+	if (new_size > text_asize) {
+		new_size += START_STRSIZE - 1;
+		new_size &= -START_STRSIZE;
+		text = realloc(text, new_size);
+		text_asize = new_size;
+	}
+	memcpy(text + text_size, str, size);
+	text_size += size;
+	text[text_size] = 0;
+}
+
+void alloc_string(const char *str, int size)
+{
+	text = malloc(size + 1);
+	memcpy(text, str, size);
+	text[size] = 0;
+}
+
+#define INITIAL 0
+#define COMMAND 1
+#define HELP 2
+#define STRING 3
+#define PARAM 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int zconfwrap (void );
+#else
+extern int zconfwrap (void );
+#endif
+#endif
+
+    static void yyunput (int c,char *buf_ptr  );
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * );
+#endif
+
+//bbox: suppressing "defined but not used" warning
+#define YY_NO_INPUT 1
+
+#ifndef YY_NO_INPUT
+
+#ifdef __cplusplus
+static int yyinput (void );
+#else
+static int input (void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( zconftext, zconfleng, 1, zconfout )
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	errno=0; \
+	while ( (result = read( fileno(zconfin), (char *) buf, max_size )) < 0 ) \
+	{ \
+		if( errno != EINTR) \
+		{ \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+			break; \
+		} \
+		errno=0; \
+		clearerr(zconfin); \
+	}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int zconflex (void);
+
+#define YY_DECL int zconflex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after zconftext and zconfleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp, *yy_bp;
+	register int yy_act;
+
+	int str = 0;
+	int ts, i;
+
+	if ( (yy_init) )
+		{
+		(yy_init) = 0;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! (yy_start) )
+			(yy_start) = 1;	/* first start state */
+
+		if ( ! zconfin )
+			zconfin = stdin;
+
+		if ( ! zconfout )
+			zconfout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			zconfensure_buffer_stack ();
+			YY_CURRENT_BUFFER_LVALUE =
+				zconf_create_buffer(zconfin,YY_BUF_SIZE );
+		}
+
+		zconf_load_buffer_state( );
+		}
+
+	while ( 1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = (yy_c_buf_p);
+
+		/* Support of zconftext. */
+		*yy_cp = (yy_hold_char);
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = (yy_start);
+yy_match:
+		while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)]  ]) > 0 )
+			++yy_cp;
+
+		yy_current_state = -yy_current_state;
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+
+		YY_DO_BEFORE_ACTION;
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+case 1:
+/* rule 1 can match eol */
+case 2:
+/* rule 2 can match eol */
+YY_RULE_SETUP
+{
+	current_file->lineno++;
+	return T_EOL;
+}
+	YY_BREAK
+case 3:
+YY_RULE_SETUP
+
+	YY_BREAK
+case 4:
+YY_RULE_SETUP
+{
+	BEGIN(COMMAND);
+}
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+{
+	unput(zconftext[0]);
+	BEGIN(COMMAND);
+}
+	YY_BREAK
+
+case 6:
+YY_RULE_SETUP
+{
+		struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+		BEGIN(PARAM);
+		current_pos.file = current_file;
+		current_pos.lineno = current_file->lineno;
+		if (id && id->flags & TF_COMMAND) {
+			zconflval.id = id;
+			return id->token;
+		}
+		alloc_string(zconftext, zconfleng);
+		zconflval.string = text;
+		return T_WORD;
+	}
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+
+	YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+{
+		BEGIN(INITIAL);
+		current_file->lineno++;
+		return T_EOL;
+	}
+	YY_BREAK
+
+case 9:
+YY_RULE_SETUP
+return T_AND;
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+return T_OR;
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+return T_OPEN_PAREN;
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+return T_CLOSE_PAREN;
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+return T_NOT;
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+return T_EQUAL;
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+return T_UNEQUAL;
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+{
+		str = zconftext[0];
+		new_string();
+		BEGIN(STRING);
+	}
+	YY_BREAK
+case 17:
+/* rule 17 can match eol */
+YY_RULE_SETUP
+BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+/* ignore */
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+{
+		struct kconf_id *id = kconf_id_lookup(zconftext, zconfleng);
+		if (id && id->flags & TF_PARAM) {
+			zconflval.id = id;
+			return id->token;
+		}
+		alloc_string(zconftext, zconfleng);
+		zconflval.string = text;
+		return T_WORD;
+	}
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+/* comment */
+	YY_BREAK
+case 21:
+/* rule 21 can match eol */
+YY_RULE_SETUP
+current_file->lineno++;
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+
+	YY_BREAK
+case YY_STATE_EOF(PARAM):
+{
+		BEGIN(INITIAL);
+	}
+	YY_BREAK
+
+case 23:
+/* rule 23 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+		append_string(zconftext, zconfleng);
+		zconflval.string = text;
+		return T_WORD_QUOTE;
+	}
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+{
+		append_string(zconftext, zconfleng);
+	}
+	YY_BREAK
+case 25:
+/* rule 25 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+		append_string(zconftext + 1, zconfleng - 1);
+		zconflval.string = text;
+		return T_WORD_QUOTE;
+	}
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+{
+		append_string(zconftext + 1, zconfleng - 1);
+	}
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+{
+		if (str == zconftext[0]) {
+			BEGIN(PARAM);
+			zconflval.string = text;
+			return T_WORD_QUOTE;
+		} else
+			append_string(zconftext, 1);
+	}
+	YY_BREAK
+case 28:
+/* rule 28 can match eol */
+YY_RULE_SETUP
+{
+		printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+		current_file->lineno++;
+		BEGIN(INITIAL);
+		return T_EOL;
+	}
+	YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+		BEGIN(INITIAL);
+	}
+	YY_BREAK
+
+case 29:
+YY_RULE_SETUP
+{
+		ts = 0;
+		for (i = 0; i < zconfleng; i++) {
+			if (zconftext[i] == '\t')
+				ts = (ts & ~7) + 8;
+			else
+				ts++;
+		}
+		last_ts = ts;
+		if (first_ts) {
+			if (ts < first_ts) {
+				zconf_endhelp();
+				return T_HELPTEXT;
+			}
+			ts -= first_ts;
+			while (ts > 8) {
+				append_string("        ", 8);
+				ts -= 8;
+			}
+			append_string("        ", ts);
+		}
+	}
+	YY_BREAK
+case 30:
+/* rule 30 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up zconftext */
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up zconftext again */
+YY_RULE_SETUP
+{
+		current_file->lineno++;
+		zconf_endhelp();
+		return T_HELPTEXT;
+	}
+	YY_BREAK
+case 31:
+/* rule 31 can match eol */
+YY_RULE_SETUP
+{
+		current_file->lineno++;
+		append_string("\n", 1);
+	}
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+{
+		append_string(zconftext, zconfleng);
+		if (!first_ts)
+			first_ts = last_ts;
+	}
+	YY_BREAK
+case YY_STATE_EOF(HELP):
+{
+		zconf_endhelp();
+		return T_HELPTEXT;
+	}
+	YY_BREAK
+
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(COMMAND):
+{
+	if (current_file) {
+		zconf_endfile();
+		return T_EOL;
+	}
+	fclose(zconfin);
+	yyterminate();
+}
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = (yy_hold_char);
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed zconfin at a new source and called
+			 * zconflex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = zconfin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state(  );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++(yy_c_buf_p);
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = (yy_c_buf_p);
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer(  ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				(yy_did_buffer_switch_on_eof) = 0;
+
+				if ( zconfwrap( ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * zconftext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				(yy_c_buf_p) =
+					(yytext_ptr) + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				(yy_c_buf_p) =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+} /* end of zconflex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+    	register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	register char *source = (yytext_ptr);
+	register int number_to_move, i;
+	int ret_val;
+
+	if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1;
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+	else
+		{
+			size_t num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+			int yy_c_buf_p_offset =
+				(int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					zconfrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2  );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = 0;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			(yy_n_chars), num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	if ( (yy_n_chars) == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			zconfrestart(zconfin  );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	(yy_n_chars) += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+	(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+	register yy_state_type yy_current_state;
+	register char *yy_cp;
+
+	yy_current_state = (yy_start);
+
+	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+		{
+		yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+	register int yy_is_jam;
+
+	yy_current_state = yy_nxt[yy_current_state][1];
+	yy_is_jam = (yy_current_state <= 0);
+
+	return yy_is_jam ? 0 : yy_current_state;
+}
+
+    static void yyunput (int c, register char * yy_bp )
+{
+	register char *yy_cp;
+
+    yy_cp = (yy_c_buf_p);
+
+	/* undo effects of setting up zconftext */
+	*yy_cp = (yy_hold_char);
+
+	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		register int number_to_move = (yy_n_chars) + 2;
+		register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+		register char *source =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+	(yytext_ptr) = yy_bp;
+	(yy_hold_char) = *yy_cp;
+	(yy_c_buf_p) = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+	int c;
+
+	*(yy_c_buf_p) = (yy_hold_char);
+
+	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			/* This was really a NUL. */
+			*(yy_c_buf_p) = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (yy_c_buf_p) - (yytext_ptr);
+			++(yy_c_buf_p);
+
+			switch ( yy_get_next_buffer(  ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					zconfrestart(zconfin );
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( zconfwrap( ) )
+						return EOF;
+
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					(yy_c_buf_p) = (yytext_ptr) + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) (yy_c_buf_p);	/* cast for 8-bit char's */
+	*(yy_c_buf_p) = '\0';	/* preserve zconftext */
+	(yy_hold_char) = *++(yy_c_buf_p);
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void zconfrestart  (FILE * input_file )
+{
+
+	if ( ! YY_CURRENT_BUFFER ){
+        zconfensure_buffer_stack ();
+		YY_CURRENT_BUFFER_LVALUE =
+            zconf_create_buffer(zconfin,YY_BUF_SIZE );
+	}
+
+	zconf_init_buffer(YY_CURRENT_BUFFER,input_file );
+	zconf_load_buffer_state( );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+    void zconf_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		zconfpop_buffer_state();
+	 *		zconfpush_buffer_state(new_buffer);
+     */
+	zconfensure_buffer_stack ();
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	zconf_load_buffer_state( );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (zconfwrap()) processing, but the only time this flag
+	 * is looked at is after zconfwrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void zconf_load_buffer_state  (void)
+{
+    	(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	zconfin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	(yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE zconf_create_buffer  (FILE * file, int  size )
+{
+	YY_BUFFER_STATE b;
+
+	b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) zconfalloc(b->yy_buf_size + 2  );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in zconf_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	zconf_init_buffer(b,file );
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with zconf_create_buffer()
+ *
+ */
+    void zconf_delete_buffer (YY_BUFFER_STATE  b )
+{
+
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		zconffree((void *) b->yy_ch_buf  );
+
+	zconffree((void *) b  );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a zconfrestart() or at EOF.
+ */
+    static void zconf_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+	int oerrno = errno;
+
+	zconf_flush_buffer(b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then zconf_init_buffer was _probably_
+     * called from zconfrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = 0;
+
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+    void zconf_flush_buffer (YY_BUFFER_STATE  b )
+{
+    	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		zconf_load_buffer_state( );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *
+ */
+void zconfpush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+    	if (new_buffer == NULL)
+		return;
+
+	zconfensure_buffer_stack();
+
+	/* This block is copied from zconf_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		(yy_buffer_stack_top)++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from zconf_switch_to_buffer. */
+	zconf_load_buffer_state( );
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *
+ */
+void zconfpop_buffer_state (void)
+{
+    	if (!YY_CURRENT_BUFFER)
+		return;
+
+	zconf_delete_buffer(YY_CURRENT_BUFFER );
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if ((yy_buffer_stack_top) > 0)
+		--(yy_buffer_stack_top);
+
+	if (YY_CURRENT_BUFFER) {
+		zconf_load_buffer_state( );
+		(yy_did_buffer_switch_on_eof) = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void zconfensure_buffer_stack (void)
+{
+	int num_to_alloc;
+
+	if (!(yy_buffer_stack)) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+		num_to_alloc = 1;
+		(yy_buffer_stack) = (struct yy_buffer_state**)zconfalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+
+		memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+		(yy_buffer_stack_max) = num_to_alloc;
+		(yy_buffer_stack_top) = 0;
+		return;
+	}
+
+	if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		int grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = (yy_buffer_stack_max) + grow_size;
+		(yy_buffer_stack) = (struct yy_buffer_state**)zconfrealloc
+								((yy_buffer_stack),
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+
+		/* zero only the new slots.*/
+		memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+		(yy_buffer_stack_max) = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_buffer  (char * base, yy_size_t  size )
+{
+	YY_BUFFER_STATE b;
+
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return 0;
+
+	b = (YY_BUFFER_STATE) zconfalloc(sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_buffer()" );
+
+	b->yy_buf_size = size - 2;	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = 0;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	zconf_switch_to_buffer(b  );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to zconflex() will
+ * scan from a @e copy of @a str.
+ * @param yy_str a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       zconf_scan_bytes() instead.
+ */
+YY_BUFFER_STATE zconf_scan_string (yyconst char * yy_str )
+{
+
+	return zconf_scan_bytes(yy_str,strlen(yy_str) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to zconflex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE zconf_scan_bytes  (yyconst char * bytes, int  len )
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = len + 2;
+	buf = (char *) zconfalloc(n  );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in zconf_scan_bytes()" );
+
+	for ( i = 0; i < len; ++i )
+		buf[i] = bytes[i];
+
+	buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = zconf_scan_buffer(buf,n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in zconf_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg )
+{
+    	(void) fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up zconftext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		zconftext[zconfleng] = (yy_hold_char); \
+		(yy_c_buf_p) = zconftext + yyless_macro_arg; \
+		(yy_hold_char) = *(yy_c_buf_p); \
+		*(yy_c_buf_p) = '\0'; \
+		zconfleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ *
+ */
+int zconfget_lineno  (void)
+{
+
+    return zconflineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *zconfget_in  (void)
+{
+        return zconfin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *zconfget_out  (void)
+{
+        return zconfout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int zconfget_leng  (void)
+{
+        return zconfleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *zconfget_text  (void)
+{
+        return zconftext;
+}
+
+/** Set the current line number.
+ * @param line_number
+ *
+ */
+void zconfset_lineno (int  line_number )
+{
+
+    zconflineno = line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ *
+ * @see zconf_switch_to_buffer
+ */
+void zconfset_in (FILE *  in_str )
+{
+        zconfin = in_str ;
+}
+
+void zconfset_out (FILE *  out_str )
+{
+        zconfout = out_str ;
+}
+
+int zconfget_debug  (void)
+{
+        return zconf_flex_debug;
+}
+
+void zconfset_debug (int  bdebug )
+{
+        zconf_flex_debug = bdebug ;
+}
+
+/* zconflex_destroy is for both reentrant and non-reentrant scanners. */
+int zconflex_destroy  (void)
+{
+
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		zconf_delete_buffer(YY_CURRENT_BUFFER  );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		zconfpop_buffer_state();
+	}
+
+	/* Destroy the stack itself. */
+	zconffree((yy_buffer_stack) );
+	(yy_buffer_stack) = NULL;
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n )
+{
+	register int i;
+    	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s )
+{
+	register int n;
+    	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *zconfalloc (yy_size_t  size )
+{
+	return (void *) malloc( size );
+}
+
+void *zconfrealloc  (void * ptr, yy_size_t  size )
+{
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return (void *) realloc( (char *) ptr, size );
+}
+
+void zconffree (void * ptr )
+{
+	free( (char *) ptr );	/* see zconfrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef yytext_ptr
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+void zconf_starthelp(void)
+{
+	new_string();
+	last_ts = first_ts = 0;
+	BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+	zconflval.string = text;
+	BEGIN(INITIAL);
+}
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+	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");
+		}
+	}
+	return f;
+}
+
+void zconf_initscan(const char *name)
+{
+	zconfin = zconf_fopen(name);
+	if (!zconfin) {
+		printf("can't find file %s\n", name);
+		exit(1);
+	}
+
+	current_buf = malloc(sizeof(*current_buf));
+	memset(current_buf, 0, sizeof(*current_buf));
+
+	current_file = file_lookup(name);
+	current_file->lineno = 1;
+	current_file->flags = FILE_BUSY;
+}
+
+void zconf_nextfile(const char *name)
+{
+	struct file *file = file_lookup(name);
+	struct buffer *buf = malloc(sizeof(*buf));
+	memset(buf, 0, sizeof(*buf));
+
+	current_buf->state = YY_CURRENT_BUFFER;
+	zconfin = zconf_fopen(name);
+	if (!zconfin) {
+		printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
+		exit(1);
+	}
+	zconf_switch_to_buffer(zconf_create_buffer(zconfin,YY_BUF_SIZE));
+	buf->parent = current_buf;
+	current_buf = buf;
+
+	if (file->flags & FILE_BUSY) {
+		printf("recursive scan (%s)?\n", name);
+		exit(1);
+	}
+	if (file->flags & FILE_SCANNED) {
+		printf("file %s already scanned?\n", name);
+		exit(1);
+	}
+	file->flags |= FILE_BUSY;
+	file->lineno = 1;
+	file->parent = current_file;
+	current_file = file;
+}
+
+static void zconf_endfile(void)
+{
+	struct buffer *parent;
+
+	current_file->flags |= FILE_SCANNED;
+	current_file->flags &= ~FILE_BUSY;
+	current_file = current_file->parent;
+
+	parent = current_buf->parent;
+	if (parent) {
+		fclose(zconfin);
+		zconf_delete_buffer(YY_CURRENT_BUFFER);
+		zconf_switch_to_buffer(parent->state);
+	}
+	free(current_buf);
+	current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+	return current_pos.lineno;
+}
+
+char *zconf_curname(void)
+{
+	return current_pos.file ? current_pos.file->name : "<none>";
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lkc.h b/busybox-1.19.3/scripts/kconfig/lkc.h
new file mode 100644
index 0000000..527f60c
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lkc.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#ifndef LKC_H
+#define LKC_H
+
+#include "expr.h"
+
+#ifndef KBUILD_NO_NLS
+# include <libintl.h>
+#else
+# define gettext(Msgid) ((const char *) (Msgid))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LKC_DIRECT_LINK
+#define P(name,type,arg)	extern type name arg
+#else
+#include "lkc_defs.h"
+#define P(name,type,arg)	extern type (*name ## _p) arg
+#endif
+#include "lkc_proto.h"
+#undef P
+
+#define SRCTREE "srctree"
+
+#define PACKAGE "linux"
+#define LOCALEDIR "/usr/share/locale"
+
+#define _(text) gettext(text)
+#define N_(text) (text)
+
+
+#define TF_COMMAND	0x0001
+#define TF_PARAM	0x0002
+
+struct kconf_id {
+	int name;
+	int token;
+	unsigned int flags;
+	enum symbol_type stype;
+};
+
+int zconfparse(void);
+void zconfdump(FILE *out);
+
+extern int zconfdebug;
+void zconf_starthelp(void);
+FILE *zconf_fopen(const char *name);
+void zconf_initscan(const char *name);
+void zconf_nextfile(const char *name);
+int zconf_lineno(void);
+char *zconf_curname(void);
+
+/* confdata.c */
+extern const char conf_def_filename[];
+
+char *conf_get_default_confname(void);
+
+/* kconfig_load.c */
+void kconfig_load(void);
+
+/* menu.c */
+void menu_init(void);
+struct menu *menu_add_menu(void);
+void menu_end_menu(void);
+void menu_add_entry(struct symbol *sym);
+void menu_end_entry(void);
+void menu_add_dep(struct expr *dep);
+struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep);
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep);
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
+void menu_finalize(struct menu *parent);
+void menu_set_type(int type);
+
+/* util.c */
+struct file *file_lookup(const char *name);
+int file_write_dep(const char *name);
+
+struct gstr {
+	size_t len;
+	char  *s;
+};
+struct gstr str_new(void);
+struct gstr str_assign(const char *s);
+void str_free(struct gstr *gs);
+void str_append(struct gstr *gs, const char *s);
+void str_printf(struct gstr *gs, const char *fmt, ...);
+const char *str_get(struct gstr *gs);
+
+/* symbol.c */
+void sym_init(void);
+void sym_clear_all_valid(void);
+void sym_set_changed(struct symbol *sym);
+struct symbol *sym_check_deps(struct symbol *sym);
+struct property *prop_alloc(enum prop_type type, struct symbol *sym);
+struct symbol *prop_get_symbol(struct property *prop);
+
+static inline tristate sym_get_tristate_value(struct symbol *sym)
+{
+	return sym->curr.tri;
+}
+
+
+static inline struct symbol *sym_get_choice_value(struct symbol *sym)
+{
+	return (struct symbol *)sym->curr.val;
+}
+
+static inline bool sym_set_choice_value(struct symbol *ch, struct symbol *chval)
+{
+	return sym_set_tristate_value(chval, yes);
+}
+
+static inline bool sym_is_choice(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_CHOICE ? true : false;
+}
+
+static inline bool sym_is_choice_value(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_CHOICEVAL ? true : false;
+}
+
+static inline bool sym_is_optional(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_OPTIONAL ? true : false;
+}
+
+static inline bool sym_has_value(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_NEW ? false : true;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LKC_H */
diff --git a/busybox-1.19.3/scripts/kconfig/lkc_proto.h b/busybox-1.19.3/scripts/kconfig/lkc_proto.h
new file mode 100644
index 0000000..b6a389c
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lkc_proto.h
@@ -0,0 +1,41 @@
+
+/* confdata.c */
+P(conf_parse,void,(const char *name));
+P(conf_read,int,(const char *name));
+P(conf_read_simple,int,(const char *name));
+P(conf_write,int,(const char *name));
+
+/* menu.c */
+P(rootmenu,struct menu,);
+
+P(menu_is_visible,bool,(struct menu *menu));
+P(menu_get_prompt,const char *,(struct menu *menu));
+P(menu_get_root_menu,struct menu *,(struct menu *menu));
+P(menu_get_parent_menu,struct menu *,(struct menu *menu));
+
+/* symbol.c */
+P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]);
+P(sym_change_count,int,);
+
+P(sym_lookup,struct symbol *,(const char *name, int isconst));
+P(sym_find,struct symbol *,(const char *name));
+P(sym_re_search,struct symbol **,(const char *pattern));
+P(sym_type_name,const char *,(enum symbol_type type));
+P(sym_calc_value,void,(struct symbol *sym));
+P(sym_get_type,enum symbol_type,(struct symbol *sym));
+P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri));
+P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri));
+P(sym_toggle_tristate_value,tristate,(struct symbol *sym));
+P(sym_string_valid,bool,(struct symbol *sym, const char *newval));
+P(sym_string_within_range,bool,(struct symbol *sym, const char *str));
+P(sym_set_string_value,bool,(struct symbol *sym, const char *newval));
+P(sym_is_changable,bool,(struct symbol *sym));
+P(sym_get_choice_prop,struct property *,(struct symbol *sym));
+P(sym_get_default_prop,struct property *,(struct symbol *sym));
+P(sym_get_string_value,const char *,(struct symbol *sym));
+
+P(prop_get_type_name,const char *,(enum prop_type type));
+
+/* expr.c */
+P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2));
+P(expr_print,void,(struct expr *e, void (*fn)(void *, const char *), void *data, int prevtoken));
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/BIG.FAT.WARNING b/busybox-1.19.3/scripts/kconfig/lxdialog/BIG.FAT.WARNING
new file mode 100644
index 0000000..4cadb80
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/BIG.FAT.WARNING
@@ -0,0 +1,4 @@
+This is NOT the official version of dialog.  This version has been
+significantly modified from the original.  It was used by the Linux
+kernel configuration script, and subsequently adapted for busybox.
+Please do not bother Savio Lam with questions about this program.
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/Makefile b/busybox-1.19.3/scripts/kconfig/lxdialog/Makefile
new file mode 100644
index 0000000..2c9dc48
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/Makefile
@@ -0,0 +1,21 @@
+# Makefile to build lxdialog package
+#
+
+check-lxdialog  := $(srctree)/$(src)/check-lxdialog.sh
+
+# Use reursively expanded variables so we do not call gcc unless
+# we really need to do so. (Do not call gcc as part of make mrproper)
+HOST_EXTRACFLAGS = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ccflags)
+HOST_LOADLIBES   = $(shell $(CONFIG_SHELL) $(check-lxdialog) -ldflags $(HOSTCC))
+
+HOST_EXTRACFLAGS += -DLOCALE
+
+PHONY += dochecklxdialog
+$(obj)/dochecklxdialog:
+	$(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES)
+
+hostprogs-y	:= lxdialog
+always		:= $(hostprogs-y) dochecklxdialog
+
+lxdialog-objs := checklist.o menubox.o textbox.o yesno.o inputbox.o \
+		 util.o lxdialog.o msgbox.o
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/check-lxdialog.sh b/busybox-1.19.3/scripts/kconfig/lxdialog/check-lxdialog.sh
new file mode 100644
index 0000000..d34dfd4
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/check-lxdialog.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+# Check ncurses compatibility
+
+# What library to link
+ldflags()
+{
+	for ext in so a dylib ; do
+		for lib in ncursesw ncurses curses ; do
+			$cc -print-file-name=lib${lib}.${ext} | grep -q /
+			if [ $? -eq 0 ]; then
+				echo "-l${lib}"
+				exit
+			fi
+		done
+	done
+	exit 1
+}
+
+# Where is ncurses.h?
+ccflags()
+{
+	if [ -f /usr/include/ncursesw/ncurses.h ]; then
+		echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncurses.h>"'
+	elif [ -f /usr/include/ncursesw/curses.h ]; then
+		echo '-I/usr/include/ncursesw -DCURSES_LOC="<ncursesw/curses.h>"'
+	elif [ -f /usr/include/ncurses/ncurses.h ]; then
+		echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses.h>"'
+	elif [ -f /usr/include/ncurses/curses.h ]; then
+		echo '-I/usr/include/ncurses -DCURSES_LOC="<ncurses/curses.h>"'
+	elif [ -f /usr/include/ncurses.h ]; then
+		echo '-DCURSES_LOC="<ncurses.h>"'
+	else
+		echo '-DCURSES_LOC="<curses.h>"'
+	fi
+}
+
+# Temp file, try to clean up after us
+tmp=.lxdialog.tmp
+trap "rm -f $tmp" 0 1 2 3 15
+
+# Check if we can link to ncurses
+check() {
+        $cc -xc - -o $tmp 2>/dev/null <<'EOF'
+#include CURSES_LOC
+main() {}
+EOF
+	if [ $? != 0 ]; then
+	    echo " *** Unable to find the ncurses libraries or the"       1>&2
+	    echo " *** required header files."                            1>&2
+	    echo " *** 'make menuconfig' requires the ncurses libraries." 1>&2
+	    echo " *** "                                                  1>&2
+	    echo " *** Install ncurses (ncurses-devel) and try again."    1>&2
+	    echo " *** "                                                  1>&2
+	    exit 1
+	fi
+}
+
+usage() {
+	printf "Usage: $0 [-check compiler options|-ccflags|-ldflags compiler options]\n"
+}
+
+if [ $# -eq 0 ]; then
+	usage
+	exit 1
+fi
+
+cc=""
+case "$1" in
+	"-check")
+		shift
+		cc="$@"
+		check
+		;;
+	"-ccflags")
+		ccflags
+		;;
+	"-ldflags")
+		shift
+		cc="$@"
+		ldflags
+		;;
+	"*")
+		usage
+		exit 1
+		;;
+esac
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/checklist.c b/busybox-1.19.3/scripts/kconfig/lxdialog/checklist.c
new file mode 100644
index 0000000..be0200e
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/checklist.c
@@ -0,0 +1,333 @@
+/*
+ *  checklist.c -- implements the checklist box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *     Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
+ *     Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static int list_width, check_x, item_x;
+
+/*
+ * Print list item
+ */
+static void print_item(WINDOW * win, const char *item, int status, int choice,
+		       int selected)
+{
+	int i;
+
+	/* Clear 'residue' of last item */
+	wattrset(win, menubox_attr);
+	wmove(win, choice, 0);
+	for (i = 0; i < list_width; i++)
+		waddch(win, ' ');
+
+	wmove(win, choice, check_x);
+	wattrset(win, selected ? check_selected_attr : check_attr);
+	wprintw(win, "(%c)", status ? 'X' : ' ');
+
+	wattrset(win, selected ? tag_selected_attr : tag_attr);
+	mvwaddch(win, choice, item_x, item[0]);
+	wattrset(win, selected ? item_selected_attr : item_attr);
+	waddstr(win, (char *)item + 1);
+	if (selected) {
+		wmove(win, choice, check_x + 1);
+		wrefresh(win);
+	}
+}
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int choice, int item_no, int scroll,
+	     int y, int x, int height)
+{
+	wmove(win, y, x);
+
+	if (scroll > 0) {
+		wattrset(win, uarrow_attr);
+		waddch(win, ACS_UARROW);
+		waddstr(win, "(-)");
+	} else {
+		wattrset(win, menubox_attr);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+	}
+
+	y = y + height + 1;
+	wmove(win, y, x);
+
+	if ((height < item_no) && (scroll + choice < item_no - 1)) {
+		wattrset(win, darrow_attr);
+		waddch(win, ACS_DARROW);
+		waddstr(win, "(+)");
+	} else {
+		wattrset(win, menubox_border_attr);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+	}
+}
+
+/*
+ *  Display the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+	int x = width / 2 - 11;
+	int y = height - 2;
+
+	print_button(dialog, "Select", y, x, selected == 0);
+	print_button(dialog, " Help ", y, x + 14, selected == 1);
+
+	wmove(dialog, y, x + 1 + 14 * selected);
+	wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with a list of options that can be turned on or off
+ * in the style of radiolist (only one option turned on at a time).
+ */
+int dialog_checklist(const char *title, const char *prompt, int height,
+		     int width, int list_height, int item_no,
+		     const char *const *items)
+{
+	int i, x, y, box_x, box_y;
+	int key = 0, button = 0, choice = 0, scroll = 0, max_choice, *status;
+	WINDOW *dialog, *list;
+
+	/* Allocate space for storing item on/off status */
+	if ((status = malloc(sizeof(int) * item_no)) == NULL) {
+		endwin();
+		fprintf(stderr,
+			"\nCan't allocate memory in dialog_checklist().\n");
+		exit(-1);
+	}
+
+	/* Initializes status */
+	for (i = 0; i < item_no; i++) {
+		status[i] = !strcasecmp(items[i * 3 + 2], "on");
+		if ((!choice && status[i])
+		    || !strcasecmp(items[i * 3 + 2], "selected"))
+			choice = i + 1;
+	}
+	if (choice)
+		choice--;
+
+	max_choice = MIN(list_height, item_no);
+
+	/* center dialog box on screen */
+	x = (COLS - width) / 2;
+	y = (LINES - height) / 2;
+
+	draw_shadow(stdscr, y, x, height, width);
+
+	dialog = newwin(height, width, y, x);
+	keypad(dialog, TRUE);
+
+	draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+	wattrset(dialog, border_attr);
+	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+	for (i = 0; i < width - 2; i++)
+		waddch(dialog, ACS_HLINE);
+	wattrset(dialog, dialog_attr);
+	waddch(dialog, ACS_RTEE);
+
+	print_title(dialog, title, width);
+
+	wattrset(dialog, dialog_attr);
+	print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+	list_width = width - 6;
+	box_y = height - list_height - 5;
+	box_x = (width - list_width) / 2 - 1;
+
+	/* create new window for the list */
+	list = subwin(dialog, list_height, list_width, y + box_y + 1,
+	              x + box_x + 1);
+
+	keypad(list, TRUE);
+
+	/* draw a box around the list items */
+	draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
+	         menubox_border_attr, menubox_attr);
+
+	/* Find length of longest item in order to center checklist */
+	check_x = 0;
+	for (i = 0; i < item_no; i++)
+		check_x = MAX(check_x, +strlen(items[i * 3 + 1]) + 4);
+
+	check_x = (list_width - check_x) / 2;
+	item_x = check_x + 4;
+
+	if (choice >= list_height) {
+		scroll = choice - list_height + 1;
+		choice -= scroll;
+	}
+
+	/* Print the list */
+	for (i = 0; i < max_choice; i++) {
+		print_item(list, items[(scroll + i) * 3 + 1],
+			   status[i + scroll], i, i == choice);
+	}
+
+	print_arrows(dialog, choice, item_no, scroll,
+		     box_y, box_x + check_x + 5, list_height);
+
+	print_buttons(dialog, height, width, 0);
+
+	wnoutrefresh(dialog);
+	wnoutrefresh(list);
+	doupdate();
+
+	while (key != ESC) {
+		key = wgetch(dialog);
+
+		for (i = 0; i < max_choice; i++)
+			if (toupper(key) ==
+			    toupper(items[(scroll + i) * 3 + 1][0]))
+				break;
+
+		if (i < max_choice || key == KEY_UP || key == KEY_DOWN ||
+		    key == '+' || key == '-') {
+			if (key == KEY_UP || key == '-') {
+				if (!choice) {
+					if (!scroll)
+						continue;
+					/* Scroll list down */
+					if (list_height > 1) {
+						/* De-highlight current first item */
+						print_item(list, items[scroll * 3 + 1],
+							   status[scroll], 0, FALSE);
+						scrollok(list, TRUE);
+						wscrl(list, -1);
+						scrollok(list, FALSE);
+					}
+					scroll--;
+					print_item(list, items[scroll * 3 + 1], status[scroll], 0, TRUE);
+					print_arrows(dialog, choice, item_no,
+						     scroll, box_y, box_x + check_x + 5, list_height);
+
+					wnoutrefresh(dialog);
+					wrefresh(list);
+
+					continue;	/* wait for another key press */
+				} else
+					i = choice - 1;
+			} else if (key == KEY_DOWN || key == '+') {
+				if (choice == max_choice - 1) {
+					if (scroll + choice >= item_no - 1)
+						continue;
+					/* Scroll list up */
+					if (list_height > 1) {
+						/* De-highlight current last item before scrolling up */
+						print_item(list, items[(scroll + max_choice - 1) * 3 + 1],
+							   status[scroll + max_choice - 1],
+							   max_choice - 1, FALSE);
+						scrollok(list, TRUE);
+						wscrl(list, 1);
+						scrollok(list, FALSE);
+					}
+					scroll++;
+					print_item(list, items[(scroll + max_choice - 1) * 3 + 1],
+						   status[scroll + max_choice - 1], max_choice - 1, TRUE);
+
+					print_arrows(dialog, choice, item_no,
+						     scroll, box_y, box_x + check_x + 5, list_height);
+
+					wnoutrefresh(dialog);
+					wrefresh(list);
+
+					continue;	/* wait for another key press */
+				} else
+					i = choice + 1;
+			}
+			if (i != choice) {
+				/* De-highlight current item */
+				print_item(list, items[(scroll + choice) * 3 + 1],
+					   status[scroll + choice], choice, FALSE);
+				/* Highlight new item */
+				choice = i;
+				print_item(list, items[(scroll + choice) * 3 + 1],
+					   status[scroll + choice], choice, TRUE);
+				wnoutrefresh(dialog);
+				wrefresh(list);
+			}
+			continue;	/* wait for another key press */
+		}
+		switch (key) {
+		case 'H':
+		case 'h':
+		case '?':
+			fprintf(stderr, "%s", items[(scroll + choice) * 3]);
+			delwin(dialog);
+			free(status);
+			return 1;
+		case TAB:
+		case KEY_LEFT:
+		case KEY_RIGHT:
+			button = ((key == KEY_LEFT ? --button : ++button) < 0)
+			    ? 1 : (button > 1 ? 0 : button);
+
+			print_buttons(dialog, height, width, button);
+			wrefresh(dialog);
+			break;
+		case 'S':
+		case 's':
+		case ' ':
+		case '\n':
+			if (!button) {
+				if (!status[scroll + choice]) {
+					for (i = 0; i < item_no; i++)
+						status[i] = 0;
+					status[scroll + choice] = 1;
+					for (i = 0; i < max_choice; i++)
+						print_item(list, items[(scroll + i) * 3 + 1],
+							   status[scroll + i], i, i == choice);
+				}
+				wnoutrefresh(dialog);
+				wrefresh(list);
+
+				for (i = 0; i < item_no; i++)
+					if (status[i])
+						fprintf(stderr, "%s", items[i * 3]);
+			} else
+				fprintf(stderr, "%s", items[(scroll + choice) * 3]);
+			delwin(dialog);
+			free(status);
+			return button;
+		case 'X':
+		case 'x':
+			key = ESC;
+		case ESC:
+			break;
+		}
+
+		/* Now, update everything... */
+		doupdate();
+	}
+
+	delwin(dialog);
+	free(status);
+	return -1;		/* ESC pressed */
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/colors.h b/busybox-1.19.3/scripts/kconfig/lxdialog/colors.h
new file mode 100644
index 0000000..db071df
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/colors.h
@@ -0,0 +1,154 @@
+/*
+ *  colors.h -- color attribute definitions
+ *
+ *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ *  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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *   Default color definitions
+ *
+ *   *_FG = foreground
+ *   *_BG = background
+ *   *_HL = highlight?
+ */
+#define SCREEN_FG                    COLOR_CYAN
+#define SCREEN_BG                    COLOR_BLUE
+#define SCREEN_HL                    TRUE
+
+#define SHADOW_FG                    COLOR_BLACK
+#define SHADOW_BG                    COLOR_BLACK
+#define SHADOW_HL                    TRUE
+
+#define DIALOG_FG                    COLOR_BLACK
+#define DIALOG_BG                    COLOR_WHITE
+#define DIALOG_HL                    FALSE
+
+#define TITLE_FG                     COLOR_YELLOW
+#define TITLE_BG                     COLOR_WHITE
+#define TITLE_HL                     TRUE
+
+#define BORDER_FG                    COLOR_WHITE
+#define BORDER_BG                    COLOR_WHITE
+#define BORDER_HL                    TRUE
+
+#define BUTTON_ACTIVE_FG             COLOR_WHITE
+#define BUTTON_ACTIVE_BG             COLOR_BLUE
+#define BUTTON_ACTIVE_HL             TRUE
+
+#define BUTTON_INACTIVE_FG           COLOR_BLACK
+#define BUTTON_INACTIVE_BG           COLOR_WHITE
+#define BUTTON_INACTIVE_HL           FALSE
+
+#define BUTTON_KEY_ACTIVE_FG         COLOR_WHITE
+#define BUTTON_KEY_ACTIVE_BG         COLOR_BLUE
+#define BUTTON_KEY_ACTIVE_HL         TRUE
+
+#define BUTTON_KEY_INACTIVE_FG       COLOR_RED
+#define BUTTON_KEY_INACTIVE_BG       COLOR_WHITE
+#define BUTTON_KEY_INACTIVE_HL       FALSE
+
+#define BUTTON_LABEL_ACTIVE_FG       COLOR_YELLOW
+#define BUTTON_LABEL_ACTIVE_BG       COLOR_BLUE
+#define BUTTON_LABEL_ACTIVE_HL       TRUE
+
+#define BUTTON_LABEL_INACTIVE_FG     COLOR_BLACK
+#define BUTTON_LABEL_INACTIVE_BG     COLOR_WHITE
+#define BUTTON_LABEL_INACTIVE_HL     TRUE
+
+#define INPUTBOX_FG                  COLOR_BLACK
+#define INPUTBOX_BG                  COLOR_WHITE
+#define INPUTBOX_HL                  FALSE
+
+#define INPUTBOX_BORDER_FG           COLOR_BLACK
+#define INPUTBOX_BORDER_BG           COLOR_WHITE
+#define INPUTBOX_BORDER_HL           FALSE
+
+#define SEARCHBOX_FG                 COLOR_BLACK
+#define SEARCHBOX_BG                 COLOR_WHITE
+#define SEARCHBOX_HL                 FALSE
+
+#define SEARCHBOX_TITLE_FG           COLOR_YELLOW
+#define SEARCHBOX_TITLE_BG           COLOR_WHITE
+#define SEARCHBOX_TITLE_HL           TRUE
+
+#define SEARCHBOX_BORDER_FG          COLOR_WHITE
+#define SEARCHBOX_BORDER_BG          COLOR_WHITE
+#define SEARCHBOX_BORDER_HL          TRUE
+
+#define POSITION_INDICATOR_FG        COLOR_YELLOW
+#define POSITION_INDICATOR_BG        COLOR_WHITE
+#define POSITION_INDICATOR_HL        TRUE
+
+#define MENUBOX_FG                   COLOR_BLACK
+#define MENUBOX_BG                   COLOR_WHITE
+#define MENUBOX_HL                   FALSE
+
+#define MENUBOX_BORDER_FG            COLOR_WHITE
+#define MENUBOX_BORDER_BG            COLOR_WHITE
+#define MENUBOX_BORDER_HL            TRUE
+
+#define ITEM_FG                      COLOR_BLACK
+#define ITEM_BG                      COLOR_WHITE
+#define ITEM_HL                      FALSE
+
+#define ITEM_SELECTED_FG             COLOR_WHITE
+#define ITEM_SELECTED_BG             COLOR_BLUE
+#define ITEM_SELECTED_HL             TRUE
+
+#define TAG_FG                       COLOR_YELLOW
+#define TAG_BG                       COLOR_WHITE
+#define TAG_HL                       TRUE
+
+#define TAG_SELECTED_FG              COLOR_YELLOW
+#define TAG_SELECTED_BG              COLOR_BLUE
+#define TAG_SELECTED_HL              TRUE
+
+#define TAG_KEY_FG                   COLOR_YELLOW
+#define TAG_KEY_BG                   COLOR_WHITE
+#define TAG_KEY_HL                   TRUE
+
+#define TAG_KEY_SELECTED_FG          COLOR_YELLOW
+#define TAG_KEY_SELECTED_BG          COLOR_BLUE
+#define TAG_KEY_SELECTED_HL          TRUE
+
+#define CHECK_FG                     COLOR_BLACK
+#define CHECK_BG                     COLOR_WHITE
+#define CHECK_HL                     FALSE
+
+#define CHECK_SELECTED_FG            COLOR_WHITE
+#define CHECK_SELECTED_BG            COLOR_BLUE
+#define CHECK_SELECTED_HL            TRUE
+
+#define UARROW_FG                    COLOR_GREEN
+#define UARROW_BG                    COLOR_WHITE
+#define UARROW_HL                    TRUE
+
+#define DARROW_FG                    COLOR_GREEN
+#define DARROW_BG                    COLOR_WHITE
+#define DARROW_HL                    TRUE
+
+/* End of default color definitions */
+
+#define C_ATTR(x,y)                  ((x ? A_BOLD : 0) | COLOR_PAIR((y)))
+#define COLOR_NAME_LEN               10
+#define COLOR_COUNT                  8
+
+/*
+ * Global variables
+ */
+
+extern int color_table[][3];
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/dialog.h b/busybox-1.19.3/scripts/kconfig/lxdialog/dialog.h
new file mode 100644
index 0000000..af3cf71
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/dialog.h
@@ -0,0 +1,177 @@
+/*
+ *  dialog.h -- common declarations for all dialog modules
+ *
+ *  AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *
+ *  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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __sun__
+#define CURS_MACROS
+#endif
+#include CURSES_LOC
+
+/*
+ * Colors in ncurses 1.9.9e do not work properly since foreground and
+ * background colors are OR'd rather than separately masked.  This version
+ * of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
+ * with standard curses.  The simplest fix (to make this work with standard
+ * curses) uses the wbkgdset() function, not used in the original hack.
+ * Turn it off if we're building with 1.9.9e, since it just confuses things.
+ */
+#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
+#define OLD_NCURSES 1
+#undef  wbkgdset
+#define wbkgdset(w,p)		/*nothing */
+#else
+#define OLD_NCURSES 0
+#endif
+
+#define TR(params) _tracef params
+
+#define ESC 27
+#define TAB 9
+#define MAX_LEN 2048
+#define BUF_SIZE (10*1024)
+#define MIN(x,y) (x < y ? x : y)
+#define MAX(x,y) (x > y ? x : y)
+
+#ifndef ACS_ULCORNER
+#define ACS_ULCORNER '+'
+#endif
+#ifndef ACS_LLCORNER
+#define ACS_LLCORNER '+'
+#endif
+#ifndef ACS_URCORNER
+#define ACS_URCORNER '+'
+#endif
+#ifndef ACS_LRCORNER
+#define ACS_LRCORNER '+'
+#endif
+#ifndef ACS_HLINE
+#define ACS_HLINE '-'
+#endif
+#ifndef ACS_VLINE
+#define ACS_VLINE '|'
+#endif
+#ifndef ACS_LTEE
+#define ACS_LTEE '+'
+#endif
+#ifndef ACS_RTEE
+#define ACS_RTEE '+'
+#endif
+#ifndef ACS_UARROW
+#define ACS_UARROW '^'
+#endif
+#ifndef ACS_DARROW
+#define ACS_DARROW 'v'
+#endif
+
+/*
+ * Attribute names
+ */
+#define screen_attr                   attributes[0]
+#define shadow_attr                   attributes[1]
+#define dialog_attr                   attributes[2]
+#define title_attr                    attributes[3]
+#define border_attr                   attributes[4]
+#define button_active_attr            attributes[5]
+#define button_inactive_attr          attributes[6]
+#define button_key_active_attr        attributes[7]
+#define button_key_inactive_attr      attributes[8]
+#define button_label_active_attr      attributes[9]
+#define button_label_inactive_attr    attributes[10]
+#define inputbox_attr                 attributes[11]
+#define inputbox_border_attr          attributes[12]
+#define searchbox_attr                attributes[13]
+#define searchbox_title_attr          attributes[14]
+#define searchbox_border_attr         attributes[15]
+#define position_indicator_attr       attributes[16]
+#define menubox_attr                  attributes[17]
+#define menubox_border_attr           attributes[18]
+#define item_attr                     attributes[19]
+#define item_selected_attr            attributes[20]
+#define tag_attr                      attributes[21]
+#define tag_selected_attr             attributes[22]
+#define tag_key_attr                  attributes[23]
+#define tag_key_selected_attr         attributes[24]
+#define check_attr                    attributes[25]
+#define check_selected_attr           attributes[26]
+#define uarrow_attr                   attributes[27]
+#define darrow_attr                   attributes[28]
+
+/* number of attributes */
+#define ATTRIBUTE_COUNT               29
+
+/*
+ * Global variables
+ */
+extern bool use_colors;
+extern bool use_shadow;
+
+extern chtype attributes[];
+
+extern const char *backtitle;
+
+/*
+ * Function prototypes
+ */
+extern void create_rc(const char *filename);
+extern int parse_rc(void);
+
+void init_dialog(void);
+void end_dialog(void);
+void attr_clear(WINDOW * win, int height, int width, chtype attr);
+void dialog_clear(void);
+void color_setup(void);
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x);
+void print_button(WINDOW * win, const char *label, int y, int x, int selected);
+void print_title(WINDOW *dialog, const char *title, int width);
+void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box,
+	      chtype border);
+void draw_shadow(WINDOW * win, int y, int x, int height, int width);
+
+int first_alpha(const char *string, const char *exempt);
+int dialog_yesno(const char *title, const char *prompt, int height, int width);
+int dialog_msgbox(const char *title, const char *prompt, int height,
+		  int width, int pause);
+int dialog_textbox(const char *title, const char *file, int height, int width);
+int dialog_menu(const char *title, const char *prompt, int height, int width,
+		int menu_height, const char *choice, int item_no,
+		const char *const *items);
+int dialog_checklist(const char *title, const char *prompt, int height,
+		     int width, int list_height, int item_no,
+		     const char *const *items);
+extern char dialog_input_result[];
+int dialog_inputbox(const char *title, const char *prompt, int height,
+		    int width, const char *init);
+
+/*
+ * This is the base for fictitious keys, which activate
+ * the buttons.
+ *
+ * Mouse-generated keys are the following:
+ *   -- the first 32 are used as numbers, in addition to '0'-'9'
+ *   -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
+ *   -- uppercase chars are used to invoke the button (M_EVENT + 'O')
+ */
+#define M_EVENT (KEY_MAX+1)
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/inputbox.c b/busybox-1.19.3/scripts/kconfig/lxdialog/inputbox.c
new file mode 100644
index 0000000..7795037
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/inputbox.c
@@ -0,0 +1,224 @@
+/*
+ *  inputbox.c -- implements the input box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+char dialog_input_result[MAX_LEN + 1];
+
+/*
+ *  Print the termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+	int x = width / 2 - 11;
+	int y = height - 2;
+
+	print_button(dialog, "  Ok  ", y, x, selected == 0);
+	print_button(dialog, " Help ", y, x + 14, selected == 1);
+
+	wmove(dialog, y, x + 1 + 14 * selected);
+	wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box for inputing a string
+ */
+int dialog_inputbox(const char *title, const char *prompt, int height, int width,
+                    const char *init)
+{
+	int i, x, y, box_y, box_x, box_width;
+	int input_x = 0, scroll = 0, key = 0, button = -1;
+	char *instr = dialog_input_result;
+	WINDOW *dialog;
+
+	/* center dialog box on screen */
+	x = (COLS - width) / 2;
+	y = (LINES - height) / 2;
+
+	draw_shadow(stdscr, y, x, height, width);
+
+	dialog = newwin(height, width, y, x);
+	keypad(dialog, TRUE);
+
+	draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+	wattrset(dialog, border_attr);
+	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+	for (i = 0; i < width - 2; i++)
+		waddch(dialog, ACS_HLINE);
+	wattrset(dialog, dialog_attr);
+	waddch(dialog, ACS_RTEE);
+
+	print_title(dialog, title, width);
+
+	wattrset(dialog, dialog_attr);
+	print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+	/* Draw the input field box */
+	box_width = width - 6;
+	getyx(dialog, y, x);
+	box_y = y + 2;
+	box_x = (width - box_width) / 2;
+	draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2, border_attr, dialog_attr);
+
+	print_buttons(dialog, height, width, 0);
+
+	/* Set up the initial value */
+	wmove(dialog, box_y, box_x);
+	wattrset(dialog, inputbox_attr);
+
+	if (!init)
+		instr[0] = '\0';
+	else
+		strcpy(instr, init);
+
+	input_x = strlen(instr);
+
+	if (input_x >= box_width) {
+		scroll = input_x - box_width + 1;
+		input_x = box_width - 1;
+		for (i = 0; i < box_width - 1; i++)
+			waddch(dialog, instr[scroll + i]);
+	} else {
+		waddstr(dialog, instr);
+	}
+
+	wmove(dialog, box_y, box_x + input_x);
+
+	wrefresh(dialog);
+
+	while (key != ESC) {
+		key = wgetch(dialog);
+
+		if (button == -1) {	/* Input box selected */
+			switch (key) {
+			case TAB:
+			case KEY_UP:
+			case KEY_DOWN:
+				break;
+			case KEY_LEFT:
+				continue;
+			case KEY_RIGHT:
+				continue;
+			case KEY_BACKSPACE:
+			case 127:
+				if (input_x || scroll) {
+					wattrset(dialog, inputbox_attr);
+					if (!input_x) {
+						scroll = scroll < box_width - 1 ? 0 : scroll - (box_width - 1);
+						wmove(dialog, box_y, box_x);
+						for (i = 0; i < box_width; i++)
+							waddch(dialog,
+							       instr[scroll + input_x + i] ?
+							       instr[scroll + input_x + i] : ' ');
+						input_x = strlen(instr) - scroll;
+					} else
+						input_x--;
+					instr[scroll + input_x] = '\0';
+					mvwaddch(dialog, box_y, input_x + box_x, ' ');
+					wmove(dialog, box_y, input_x + box_x);
+					wrefresh(dialog);
+				}
+				continue;
+			default:
+				if (key < 0x100 && isprint(key)) {
+					if (scroll + input_x < MAX_LEN) {
+						wattrset(dialog, inputbox_attr);
+						instr[scroll + input_x] = key;
+						instr[scroll + input_x + 1] = '\0';
+						if (input_x == box_width - 1) {
+							scroll++;
+							wmove(dialog, box_y, box_x);
+							for (i = 0; i < box_width - 1; i++)
+								waddch(dialog, instr [scroll + i]);
+						} else {
+							wmove(dialog, box_y, input_x++ + box_x);
+							waddch(dialog, key);
+						}
+						wrefresh(dialog);
+					} else
+						flash();	/* Alarm user about overflow */
+					continue;
+				}
+			}
+		}
+		switch (key) {
+		case 'O':
+		case 'o':
+			delwin(dialog);
+			return 0;
+		case 'H':
+		case 'h':
+			delwin(dialog);
+			return 1;
+		case KEY_UP:
+		case KEY_LEFT:
+			switch (button) {
+			case -1:
+				button = 1;	/* Indicates "Cancel" button is selected */
+				print_buttons(dialog, height, width, 1);
+				break;
+			case 0:
+				button = -1;	/* Indicates input box is selected */
+				print_buttons(dialog, height, width, 0);
+				wmove(dialog, box_y, box_x + input_x);
+				wrefresh(dialog);
+				break;
+			case 1:
+				button = 0;	/* Indicates "OK" button is selected */
+				print_buttons(dialog, height, width, 0);
+				break;
+			}
+			break;
+		case TAB:
+		case KEY_DOWN:
+		case KEY_RIGHT:
+			switch (button) {
+			case -1:
+				button = 0;	/* Indicates "OK" button is selected */
+				print_buttons(dialog, height, width, 0);
+				break;
+			case 0:
+				button = 1;	/* Indicates "Cancel" button is selected */
+				print_buttons(dialog, height, width, 1);
+				break;
+			case 1:
+				button = -1;	/* Indicates input box is selected */
+				print_buttons(dialog, height, width, 0);
+				wmove(dialog, box_y, box_x + input_x);
+				wrefresh(dialog);
+				break;
+			}
+			break;
+		case ' ':
+		case '\n':
+			delwin(dialog);
+			return (button == -1 ? 0 : button);
+		case 'X':
+		case 'x':
+			key = ESC;
+		case ESC:
+			break;
+		}
+	}
+
+	delwin(dialog);
+	return -1;		/* ESC pressed */
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/lxdialog.c b/busybox-1.19.3/scripts/kconfig/lxdialog/lxdialog.c
new file mode 100644
index 0000000..79f6c5f
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/lxdialog.c
@@ -0,0 +1,204 @@
+/*
+ *  dialog - Display simple dialog boxes from shell scripts
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static void Usage(const char *name);
+
+typedef int (jumperFn) (const char *title, int argc, const char *const *argv);
+
+struct Mode {
+	char *name;
+	int argmin, argmax, argmod;
+	jumperFn *jumper;
+};
+
+jumperFn j_menu, j_radiolist, j_yesno, j_textbox, j_inputbox;
+jumperFn j_msgbox, j_infobox;
+
+static struct Mode modes[] = {
+	{"--menu", 9, 0, 3, j_menu},
+	{"--radiolist", 9, 0, 3, j_radiolist},
+	{"--yesno", 5, 5, 1, j_yesno},
+	{"--textbox", 5, 5, 1, j_textbox},
+	{"--inputbox", 5, 6, 1, j_inputbox},
+	{"--msgbox", 5, 5, 1, j_msgbox},
+	{"--infobox", 5, 5, 1, j_infobox},
+	{NULL, 0, 0, 0, NULL}
+};
+
+static struct Mode *modePtr;
+
+#ifdef LOCALE
+#include <locale.h>
+#endif
+
+int main(int argc, const char *const *argv)
+{
+	int offset = 0, opt_clear = 0, end_common_opts = 0, retval;
+	const char *title = NULL;
+
+#ifdef LOCALE
+	(void)setlocale(LC_ALL, "");
+#endif
+
+#ifdef TRACE
+	trace(TRACE_CALLS | TRACE_UPDATE);
+#endif
+	if (argc < 2) {
+		Usage(argv[0]);
+		exit(-1);
+	}
+
+	while (offset < argc - 1 && !end_common_opts) {	/* Common options */
+		if (!strcmp(argv[offset + 1], "--title")) {
+			if (argc - offset < 3 || title != NULL) {
+				Usage(argv[0]);
+				exit(-1);
+			} else {
+				title = argv[offset + 2];
+				offset += 2;
+			}
+		} else if (!strcmp(argv[offset + 1], "--backtitle")) {
+			if (backtitle != NULL) {
+				Usage(argv[0]);
+				exit(-1);
+			} else {
+				backtitle = argv[offset + 2];
+				offset += 2;
+			}
+		} else if (!strcmp(argv[offset + 1], "--clear")) {
+			if (opt_clear) {	/* Hey, "--clear" can't appear twice! */
+				Usage(argv[0]);
+				exit(-1);
+			} else if (argc == 2) {	/* we only want to clear the screen */
+				init_dialog();
+				refresh();	/* init_dialog() will clear the screen for us */
+				end_dialog();
+				return 0;
+			} else {
+				opt_clear = 1;
+				offset++;
+			}
+		} else		/* no more common options */
+			end_common_opts = 1;
+	}
+
+	if (argc - 1 == offset) {	/* no more options */
+		Usage(argv[0]);
+		exit(-1);
+	}
+	/* use a table to look for the requested mode, to avoid code duplication */
+
+	for (modePtr = modes; modePtr->name; modePtr++)	/* look for the mode */
+		if (!strcmp(argv[offset + 1], modePtr->name))
+			break;
+
+	if (!modePtr->name)
+		Usage(argv[0]);
+	if (argc - offset < modePtr->argmin)
+		Usage(argv[0]);
+	if (modePtr->argmax && argc - offset > modePtr->argmax)
+		Usage(argv[0]);
+
+	init_dialog();
+	retval = (*(modePtr->jumper)) (title, argc - offset, argv + offset);
+
+	if (opt_clear) {	/* clear screen before exit */
+		attr_clear(stdscr, LINES, COLS, screen_attr);
+		refresh();
+	}
+	end_dialog();
+
+	exit(retval);
+}
+
+/*
+ * Print program usage
+ */
+static void Usage(const char *name)
+{
+	fprintf(stderr, "\
+\ndialog, by Savio Lam (lam836@cs.cuhk.hk).\
+\n  patched by Stuart Herbert (S.Herbert@shef.ac.uk)\
+\n  modified/gutted for use as a Linux kernel config tool by \
+\n  William Roadcap (roadcapw@cfw.com)\
+\n\
+\n* Display dialog boxes from shell scripts *\
+\n\
+\nUsage: %s --clear\
+\n       %s [--title <title>] [--backtitle <backtitle>] --clear <Box options>\
+\n\
+\nBox options:\
+\n\
+\n  --menu      <text> <height> <width> <menu height> <tag1> <item1>...\
+\n  --radiolist <text> <height> <width> <list height> <tag1> <item1> <status1>...\
+\n  --textbox   <file> <height> <width>\
+\n  --inputbox  <text> <height> <width> [<init>]\
+\n  --yesno     <text> <height> <width>\
+\n", name, name);
+	exit(-1);
+}
+
+/*
+ * These are the program jumpers
+ */
+
+int j_menu(const char *t, int ac, const char *const *av)
+{
+	return dialog_menu(t, av[2], atoi(av[3]), atoi(av[4]),
+			   atoi(av[5]), av[6], (ac - 6) / 2, av + 7);
+}
+
+int j_radiolist(const char *t, int ac, const char *const *av)
+{
+	return dialog_checklist(t, av[2], atoi(av[3]), atoi(av[4]),
+				atoi(av[5]), (ac - 6) / 3, av + 6);
+}
+
+int j_textbox(const char *t, int ac, const char *const *av)
+{
+	return dialog_textbox(t, av[2], atoi(av[3]), atoi(av[4]));
+}
+
+int j_yesno(const char *t, int ac, const char *const *av)
+{
+	return dialog_yesno(t, av[2], atoi(av[3]), atoi(av[4]));
+}
+
+int j_inputbox(const char *t, int ac, const char *const *av)
+{
+	int ret = dialog_inputbox(t, av[2], atoi(av[3]), atoi(av[4]),
+				  ac == 6 ? av[5] : (char *)NULL);
+	if (ret == 0)
+		fprintf(stderr, dialog_input_result);
+	return ret;
+}
+
+int j_msgbox(const char *t, int ac, const char *const *av)
+{
+	return dialog_msgbox(t, av[2], atoi(av[3]), atoi(av[4]), 1);
+}
+
+int j_infobox(const char *t, int ac, const char *const *av)
+{
+	return dialog_msgbox(t, av[2], atoi(av[3]), atoi(av[4]), 0);
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/menubox.c b/busybox-1.19.3/scripts/kconfig/lxdialog/menubox.c
new file mode 100644
index 0000000..1fd501b
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/menubox.c
@@ -0,0 +1,426 @@
+/*
+ *  menubox.c -- implements the menu box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *  Changes by Clifford Wolf (god@clifford.at)
+ *
+ *  [ 1998-06-13 ]
+ *
+ *    *)  A bugfix for the Page-Down problem
+ *
+ *    *)  Formerly when I used Page Down and Page Up, the cursor would be set
+ *        to the first position in the menu box.  Now lxdialog is a bit
+ *        smarter and works more like other menu systems (just have a look at
+ *        it).
+ *
+ *    *)  Formerly if I selected something my scrolling would be broken because
+ *        lxdialog is re-invoked by the Menuconfig shell script, can't
+ *        remember the last scrolling position, and just sets it so that the
+ *        cursor is at the bottom of the box.  Now it writes the temporary file
+ *        lxdialog.scrltmp which contains this information. The file is
+ *        deleted by lxdialog if the user leaves a submenu or enters a new
+ *        one, but it would be nice if Menuconfig could make another "rm -f"
+ *        just to be sure.  Just try it out - you will recognise a difference!
+ *
+ *  [ 1998-06-14 ]
+ *
+ *    *)  Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
+ *        and menus change their size on the fly.
+ *
+ *    *)  If for some reason the last scrolling position is not saved by
+ *        lxdialog, it sets the scrolling so that the selected item is in the
+ *        middle of the menu box, not at the bottom.
+ *
+ * 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
+ * Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
+ * This fixes a bug in Menuconfig where using ' ' to descend into menus
+ * would leave mis-synchronized lxdialog.scrltmp files lying around,
+ * fscanf would read in 'scroll', and eventually that value would get used.
+ */
+
+#include "dialog.h"
+
+static int menu_width, item_x;
+
+/*
+ * Print menu item
+ */
+static void do_print_item(WINDOW * win, const char *item, int choice,
+                          int selected, int hotkey)
+{
+	int j;
+	char *menu_item = malloc(menu_width + 1);
+
+	strncpy(menu_item, item, menu_width - item_x);
+	menu_item[menu_width] = 0;
+	j = first_alpha(menu_item, "YyNnMmHh");
+
+	/* Clear 'residue' of last item */
+	wattrset(win, menubox_attr);
+	wmove(win, choice, 0);
+#if OLD_NCURSES
+	{
+		int i;
+		for (i = 0; i < menu_width; i++)
+			waddch(win, ' ');
+	}
+#else
+	wclrtoeol(win);
+#endif
+	wattrset(win, selected ? item_selected_attr : item_attr);
+	mvwaddstr(win, choice, item_x, menu_item);
+	if (hotkey) {
+		wattrset(win, selected ? tag_key_selected_attr : tag_key_attr);
+		mvwaddch(win, choice, item_x + j, menu_item[j]);
+	}
+	if (selected) {
+		wmove(win, choice, item_x + 1);
+	}
+	free(menu_item);
+	wrefresh(win);
+}
+
+#define print_item(index, choice, selected) \
+do {\
+	int hotkey = (items[(index) * 2][0] != ':'); \
+	do_print_item(menu, items[(index) * 2 + 1], choice, selected, hotkey); \
+} while (0)
+
+/*
+ * Print the scroll indicators.
+ */
+static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
+			 int height)
+{
+	int cur_y, cur_x;
+
+	getyx(win, cur_y, cur_x);
+
+	wmove(win, y, x);
+
+	if (scroll > 0) {
+		wattrset(win, uarrow_attr);
+		waddch(win, ACS_UARROW);
+		waddstr(win, "(-)");
+	} else {
+		wattrset(win, menubox_attr);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+	}
+
+	y = y + height + 1;
+	wmove(win, y, x);
+	wrefresh(win);
+
+	if ((height < item_no) && (scroll + height < item_no)) {
+		wattrset(win, darrow_attr);
+		waddch(win, ACS_DARROW);
+		waddstr(win, "(+)");
+	} else {
+		wattrset(win, menubox_border_attr);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+		waddch(win, ACS_HLINE);
+	}
+
+	wmove(win, cur_y, cur_x);
+	wrefresh(win);
+}
+
+/*
+ * Display the termination buttons.
+ */
+static void print_buttons(WINDOW * win, int height, int width, int selected)
+{
+	int x = width / 2 - 16;
+	int y = height - 2;
+
+	print_button(win, "Select", y, x, selected == 0);
+	print_button(win, " Exit ", y, x + 12, selected == 1);
+	print_button(win, " Help ", y, x + 24, selected == 2);
+
+	wmove(win, y, x + 1 + 12 * selected);
+	wrefresh(win);
+}
+
+/* scroll up n lines (n may be negative) */
+static void do_scroll(WINDOW *win, int *scroll, int n)
+{
+	/* Scroll menu up */
+	scrollok(win, TRUE);
+	wscrl(win, n);
+	scrollok(win, FALSE);
+	*scroll = *scroll + n;
+	wrefresh(win);
+}
+
+/*
+ * Display a menu for choosing among a number of options
+ */
+int dialog_menu(const char *title, const char *prompt, int height, int width,
+		int menu_height, const char *current, int item_no,
+		const char *const *items)
+{
+	int i, j, x, y, box_x, box_y;
+	int key = 0, button = 0, scroll = 0, choice = 0;
+	int first_item =  0, max_choice;
+	WINDOW *dialog, *menu;
+	FILE *f;
+
+	max_choice = MIN(menu_height, item_no);
+
+	/* center dialog box on screen */
+	x = (COLS - width) / 2;
+	y = (LINES - height) / 2;
+
+	draw_shadow(stdscr, y, x, height, width);
+
+	dialog = newwin(height, width, y, x);
+	keypad(dialog, TRUE);
+
+	draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+	wattrset(dialog, border_attr);
+	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+	for (i = 0; i < width - 2; i++)
+		waddch(dialog, ACS_HLINE);
+	wattrset(dialog, dialog_attr);
+	wbkgdset(dialog, dialog_attr & A_COLOR);
+	waddch(dialog, ACS_RTEE);
+
+	print_title(dialog, title, width);
+
+	wattrset(dialog, dialog_attr);
+	print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+	menu_width = width - 6;
+	box_y = height - menu_height - 5;
+	box_x = (width - menu_width) / 2 - 1;
+
+	/* create new window for the menu */
+	menu = subwin(dialog, menu_height, menu_width,
+		      y + box_y + 1, x + box_x + 1);
+	keypad(menu, TRUE);
+
+	/* draw a box around the menu items */
+	draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
+		 menubox_border_attr, menubox_attr);
+
+	item_x = (menu_width - 70) / 2;
+
+	/* Set choice to default item */
+	for (i = 0; i < item_no; i++)
+		if (strcmp(current, items[i * 2]) == 0)
+			choice = i;
+
+	/* get the scroll info from the temp file */
+	if ((f = fopen("lxdialog.scrltmp", "r")) != NULL) {
+		if ((fscanf(f, "%d\n", &scroll) == 1) && (scroll <= choice) &&
+		    (scroll + max_choice > choice) && (scroll >= 0) &&
+		    (scroll + max_choice <= item_no)) {
+			first_item = scroll;
+			choice = choice - scroll;
+			fclose(f);
+		} else {
+			scroll = 0;
+			remove("lxdialog.scrltmp");
+			fclose(f);
+			f = NULL;
+		}
+	}
+	if ((choice >= max_choice) || (f == NULL && choice >= max_choice / 2)) {
+		if (choice >= item_no - max_choice / 2)
+			scroll = first_item = item_no - max_choice;
+		else
+			scroll = first_item = choice - max_choice / 2;
+		choice = choice - scroll;
+	}
+
+	/* Print the menu */
+	for (i = 0; i < max_choice; i++) {
+		print_item(first_item + i, i, i == choice);
+	}
+
+	wnoutrefresh(menu);
+
+	print_arrows(dialog, item_no, scroll,
+		     box_y, box_x + item_x + 1, menu_height);
+
+	print_buttons(dialog, height, width, 0);
+	wmove(menu, choice, item_x + 1);
+	wrefresh(menu);
+
+	while (key != ESC) {
+		key = wgetch(menu);
+
+		if (key < 256 && isalpha(key))
+			key = tolower(key);
+
+		if (strchr("ynmh", key))
+			i = max_choice;
+		else {
+			for (i = choice + 1; i < max_choice; i++) {
+				j = first_alpha(items[(scroll + i) * 2 + 1], "YyNnMmHh");
+				if (key == tolower(items[(scroll + i) * 2 + 1][j]))
+					break;
+			}
+			if (i == max_choice)
+				for (i = 0; i < max_choice; i++) {
+					j = first_alpha(items [(scroll + i) * 2 + 1], "YyNnMmHh");
+					if (key == tolower(items[(scroll + i) * 2 + 1][j]))
+						break;
+				}
+		}
+
+		if (i < max_choice ||
+		    key == KEY_UP || key == KEY_DOWN ||
+		    key == '-' || key == '+' ||
+		    key == KEY_PPAGE || key == KEY_NPAGE) {
+			/* Remove highligt of current item */
+			print_item(scroll + choice, choice, FALSE);
+
+			if (key == KEY_UP || key == '-') {
+				if (choice < 2 && scroll) {
+					/* Scroll menu down */
+					do_scroll(menu, &scroll, -1);
+
+					print_item(scroll, 0, FALSE);
+				} else
+					choice = MAX(choice - 1, 0);
+
+			} else if (key == KEY_DOWN || key == '+') {
+				print_item(scroll+choice, choice, FALSE);
+
+				if ((choice > max_choice - 3) &&
+				    (scroll + max_choice < item_no)) {
+					/* Scroll menu up */
+					do_scroll(menu, &scroll, 1);
+
+					print_item(scroll+max_choice - 1,
+						   max_choice - 1, FALSE);
+				} else
+					choice = MIN(choice + 1, max_choice - 1);
+
+			} else if (key == KEY_PPAGE) {
+				scrollok(menu, TRUE);
+				for (i = 0; (i < max_choice); i++) {
+					if (scroll > 0) {
+						do_scroll(menu, &scroll, -1);
+						print_item(scroll, 0, FALSE);
+					} else {
+						if (choice > 0)
+							choice--;
+					}
+				}
+
+			} else if (key == KEY_NPAGE) {
+				for (i = 0; (i < max_choice); i++) {
+					if (scroll + max_choice < item_no) {
+						do_scroll(menu, &scroll, 1);
+						print_item(scroll+max_choice-1,
+							   max_choice - 1, FALSE);
+					} else {
+						if (choice + 1 < max_choice)
+							choice++;
+					}
+				}
+			} else
+				choice = i;
+
+			print_item(scroll + choice, choice, TRUE);
+
+			print_arrows(dialog, item_no, scroll,
+				     box_y, box_x + item_x + 1, menu_height);
+
+			wnoutrefresh(dialog);
+			wrefresh(menu);
+
+			continue;	/* wait for another key press */
+		}
+
+		switch (key) {
+		case KEY_LEFT:
+		case TAB:
+		case KEY_RIGHT:
+			button = ((key == KEY_LEFT ? --button : ++button) < 0)
+			    ? 2 : (button > 2 ? 0 : button);
+
+			print_buttons(dialog, height, width, button);
+			wrefresh(menu);
+			break;
+		case ' ':
+		case 's':
+		case 'y':
+		case 'n':
+		case 'm':
+		case '/':
+			/* save scroll info */
+			if ((f = fopen("lxdialog.scrltmp", "w")) != NULL) {
+				fprintf(f, "%d\n", scroll);
+				fclose(f);
+			}
+			delwin(dialog);
+			fprintf(stderr, "%s\n", items[(scroll + choice) * 2]);
+			switch (key) {
+			case 's':
+				return 3;
+			case 'y':
+				return 3;
+			case 'n':
+				return 4;
+			case 'm':
+				return 5;
+			case ' ':
+				return 6;
+			case '/':
+				return 7;
+			}
+			return 0;
+		case 'h':
+		case '?':
+			button = 2;
+		case '\n':
+			delwin(dialog);
+			if (button == 2)
+				fprintf(stderr, "%s \"%s\"\n",
+					items[(scroll + choice) * 2],
+					items[(scroll + choice) * 2 + 1] +
+					first_alpha(items [(scroll + choice) * 2 + 1], ""));
+			else
+				fprintf(stderr, "%s\n",
+					items[(scroll + choice) * 2]);
+
+			remove("lxdialog.scrltmp");
+			return button;
+		case 'e':
+		case 'x':
+			key = ESC;
+		case ESC:
+			break;
+		}
+	}
+
+	delwin(dialog);
+	remove("lxdialog.scrltmp");
+	return -1;		/* ESC pressed */
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/msgbox.c b/busybox-1.19.3/scripts/kconfig/lxdialog/msgbox.c
new file mode 100644
index 0000000..7323f54
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/msgbox.c
@@ -0,0 +1,71 @@
+/*
+ *  msgbox.c -- implements the message box and info box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display a message box. Program will pause and display an "OK" button
+ * if the parameter 'pause' is non-zero.
+ */
+int dialog_msgbox(const char *title, const char *prompt, int height, int width,
+                  int pause)
+{
+	int i, x, y, key = 0;
+	WINDOW *dialog;
+
+	/* center dialog box on screen */
+	x = (COLS - width) / 2;
+	y = (LINES - height) / 2;
+
+	draw_shadow(stdscr, y, x, height, width);
+
+	dialog = newwin(height, width, y, x);
+	keypad(dialog, TRUE);
+
+	draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+	print_title(dialog, title, width);
+
+	wattrset(dialog, dialog_attr);
+	print_autowrap(dialog, prompt, width - 2, 1, 2);
+
+	if (pause) {
+		wattrset(dialog, border_attr);
+		mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+		for (i = 0; i < width - 2; i++)
+			waddch(dialog, ACS_HLINE);
+		wattrset(dialog, dialog_attr);
+		waddch(dialog, ACS_RTEE);
+
+		print_button(dialog, "  Ok  ", height - 2, width / 2 - 4, TRUE);
+
+		wrefresh(dialog);
+		while (key != ESC && key != '\n' && key != ' ' &&
+		       key != 'O' && key != 'o' && key != 'X' && key != 'x')
+			key = wgetch(dialog);
+	} else {
+		key = '\n';
+		wrefresh(dialog);
+	}
+
+	delwin(dialog);
+	return key == ESC ? -1 : 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/textbox.c b/busybox-1.19.3/scripts/kconfig/lxdialog/textbox.c
new file mode 100644
index 0000000..de4ae41
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/textbox.c
@@ -0,0 +1,531 @@
+/*
+ *  textbox.c -- implements the text box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+static void back_lines(int n);
+static void print_page(WINDOW * win, int height, int width);
+static void print_line(WINDOW * win, int row, int width);
+static char *get_line(void);
+static void print_position(WINDOW * win, int height, int width);
+
+static int hscroll, fd, file_size, bytes_read;
+static int begin_reached = 1, end_reached, page_length;
+static char *buf, *page;
+
+/*
+ * Display text from a file in a dialog box.
+ */
+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;
+	WINDOW *dialog, *text;
+
+	/* Open input file for reading */
+	if ((fd = open(file, O_RDONLY)) == -1) {
+		endwin();
+		fprintf(stderr, "\nCan't open input file in dialog_textbox().\n");
+		exit(-1);
+	}
+	/* Get file size. Actually, 'file_size' is the real file size - 1,
+	   since it's only the last byte offset from the beginning */
+	if ((file_size = lseek(fd, 0, SEEK_END)) == -1) {
+		endwin();
+		fprintf(stderr, "\nError getting file size in dialog_textbox().\n");
+		exit(-1);
+	}
+	/* Restore file pointer to beginning of file after getting file size */
+	if (lseek(fd, 0, SEEK_SET) == -1) {
+		endwin();
+		fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+		exit(-1);
+	}
+	/* Allocate space for read buffer */
+	if ((buf = malloc(BUF_SIZE + 1)) == NULL) {
+		endwin();
+		fprintf(stderr, "\nCan't allocate memory in dialog_textbox().\n");
+		exit(-1);
+	}
+	if ((bytes_read = read(fd, buf, BUF_SIZE)) == -1) {
+		endwin();
+		fprintf(stderr, "\nError reading file in dialog_textbox().\n");
+		exit(-1);
+	}
+	buf[bytes_read] = '\0';	/* mark end of valid data */
+	page = buf;		/* page is pointer to start of page to be displayed */
+
+	/* center dialog box on screen */
+	x = (COLS - width) / 2;
+	y = (LINES - height) / 2;
+
+	draw_shadow(stdscr, y, x, height, width);
+
+	dialog = newwin(height, width, y, x);
+	keypad(dialog, TRUE);
+
+	/* Create window for text region, used for scrolling text */
+	text = subwin(dialog, height - 4, width - 2, y + 1, x + 1);
+	wattrset(text, dialog_attr);
+	wbkgdset(text, dialog_attr & A_COLOR);
+
+	keypad(text, TRUE);
+
+	/* register the new window, along with its borders */
+	draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+
+	wattrset(dialog, border_attr);
+	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+	for (i = 0; i < width - 2; i++)
+		waddch(dialog, ACS_HLINE);
+	wattrset(dialog, dialog_attr);
+	wbkgdset(dialog, dialog_attr & A_COLOR);
+	waddch(dialog, ACS_RTEE);
+
+	print_title(dialog, title, width);
+
+	print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
+	wnoutrefresh(dialog);
+	getyx(dialog, cur_y, cur_x);	/* Save cursor position */
+
+	/* Print first page of text */
+	attr_clear(text, height - 4, width - 2, dialog_attr);
+	print_page(text, height - 4, width - 2);
+	print_position(dialog, height, width);
+	wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
+	wrefresh(dialog);
+
+	while ((key != ESC) && (key != '\n')) {
+		key = wgetch(dialog);
+		switch (key) {
+		case 'E':	/* Exit */
+		case 'e':
+		case 'X':
+		case 'x':
+			delwin(dialog);
+			free(buf);
+			close(fd);
+			return 0;
+		case 'g':	/* First page */
+		case KEY_HOME:
+			if (!begin_reached) {
+				begin_reached = 1;
+				/* First page not in buffer? */
+				if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+					endwin();
+					fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+					exit(-1);
+				}
+				if (fpos > bytes_read) {	/* Yes, we have to read it in */
+					if (lseek(fd, 0, SEEK_SET) == -1) {
+						endwin();
+						fprintf(stderr, "\nError moving file pointer in "
+							        "dialog_textbox().\n");
+						exit(-1);
+					}
+					if ((bytes_read =
+					     read(fd, buf, BUF_SIZE)) == -1) {
+						endwin();
+						fprintf(stderr, "\nError reading file in dialog_textbox().\n");
+						exit(-1);
+					}
+					buf[bytes_read] = '\0';
+				}
+				page = buf;
+				print_page(text, height - 4, width - 2);
+				print_position(dialog, height, width);
+				wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
+				wrefresh(dialog);
+			}
+			break;
+		case 'G':	/* Last page */
+		case KEY_END:
+
+			end_reached = 1;
+			/* Last page not in buffer? */
+			if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+				endwin();
+				fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+				exit(-1);
+			}
+			if (fpos < file_size) {	/* Yes, we have to read it in */
+				if (lseek(fd, -BUF_SIZE, SEEK_END) == -1) {
+					endwin();
+					fprintf(stderr, "\nError moving file pointer in dialog_textbox().\n");
+					exit(-1);
+				}
+				if ((bytes_read =
+				     read(fd, buf, BUF_SIZE)) == -1) {
+					endwin();
+					fprintf(stderr, "\nError reading file in dialog_textbox().\n");
+					exit(-1);
+				}
+				buf[bytes_read] = '\0';
+			}
+			page = buf + bytes_read;
+			back_lines(height - 4);
+			print_page(text, height - 4, width - 2);
+			print_position(dialog, height, width);
+			wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
+			wrefresh(dialog);
+			break;
+		case 'K':	/* Previous line */
+		case 'k':
+		case KEY_UP:
+			if (!begin_reached) {
+				back_lines(page_length + 1);
+
+				/* We don't call print_page() here but use scrolling to ensure
+				   faster screen update. However, 'end_reached' and
+				   'page_length' should still be updated, and 'page' should
+				   point to start of next page. This is done by calling
+				   get_line() in the following 'for' loop. */
+				scrollok(text, TRUE);
+				wscrl(text, -1);	/* Scroll text region down one line */
+				scrollok(text, FALSE);
+				page_length = 0;
+				passed_end = 0;
+				for (i = 0; i < height - 4; i++) {
+					if (!i) {
+						/* print first line of page */
+						print_line(text, 0, width - 2);
+						wnoutrefresh(text);
+					} else
+						/* Called to update 'end_reached' and 'page' */
+						get_line();
+					if (!passed_end)
+						page_length++;
+					if (end_reached && !passed_end)
+						passed_end = 1;
+				}
+
+				print_position(dialog, height, width);
+				wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
+				wrefresh(dialog);
+			}
+			break;
+		case 'B':	/* Previous page */
+		case 'b':
+		case KEY_PPAGE:
+			if (begin_reached)
+				break;
+			back_lines(page_length + height - 4);
+			print_page(text, height - 4, width - 2);
+			print_position(dialog, height, width);
+			wmove(dialog, cur_y, cur_x);
+			wrefresh(dialog);
+			break;
+		case 'J':	/* Next line */
+		case 'j':
+		case KEY_DOWN:
+			if (!end_reached) {
+				begin_reached = 0;
+				scrollok(text, TRUE);
+				scroll(text);	/* Scroll text region up one line */
+				scrollok(text, FALSE);
+				print_line(text, height - 5, width - 2);
+				wnoutrefresh(text);
+				print_position(dialog, height, width);
+				wmove(dialog, cur_y, cur_x);	/* Restore cursor position */
+				wrefresh(dialog);
+			}
+			break;
+		case KEY_NPAGE:	/* Next page */
+		case ' ':
+			if (end_reached)
+				break;
+
+			begin_reached = 0;
+			print_page(text, height - 4, width - 2);
+			print_position(dialog, height, width);
+			wmove(dialog, cur_y, cur_x);
+			wrefresh(dialog);
+			break;
+		case '0':	/* Beginning of line */
+		case 'H':	/* Scroll left */
+		case 'h':
+		case KEY_LEFT:
+			if (hscroll <= 0)
+				break;
+
+			if (key == '0')
+				hscroll = 0;
+			else
+				hscroll--;
+			/* Reprint current page to scroll horizontally */
+			back_lines(page_length);
+			print_page(text, height - 4, width - 2);
+			wmove(dialog, cur_y, cur_x);
+			wrefresh(dialog);
+			break;
+		case 'L':	/* Scroll right */
+		case 'l':
+		case KEY_RIGHT:
+			if (hscroll >= MAX_LEN)
+				break;
+			hscroll++;
+			/* Reprint current page to scroll horizontally */
+			back_lines(page_length);
+			print_page(text, height - 4, width - 2);
+			wmove(dialog, cur_y, cur_x);
+			wrefresh(dialog);
+			break;
+		case ESC:
+			break;
+		}
+	}
+
+	delwin(dialog);
+	free(buf);
+	close(fd);
+	return -1;		/* ESC pressed */
+}
+
+/*
+ * Go back 'n' lines in text file. Called by dialog_textbox().
+ * 'page' will be updated to point to the desired line in 'buf'.
+ */
+static void back_lines(int n)
+{
+	int i, fpos;
+
+	begin_reached = 0;
+	/* We have to distinguish between end_reached and !end_reached
+	   since at end of file, the line is not ended by a '\n'.
+	   The code inside 'if' basically does a '--page' to move one
+	   character backward so as to skip '\n' of the previous line */
+	if (!end_reached) {
+		/* Either beginning of buffer or beginning of file reached? */
+		if (page == buf) {
+			if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+				endwin();
+				fprintf(stderr, "\nError moving file pointer in "
+					        "back_lines().\n");
+				exit(-1);
+			}
+			if (fpos > bytes_read) {	/* Not beginning of file yet */
+				/* We've reached beginning of buffer, but not beginning of
+				   file yet, so read previous part of file into buffer.
+				   Note that we only move backward for BUF_SIZE/2 bytes,
+				   but not BUF_SIZE bytes to avoid re-reading again in
+				   print_page() later */
+				/* Really possible to move backward BUF_SIZE/2 bytes? */
+				if (fpos < BUF_SIZE / 2 + bytes_read) {
+					/* No, move less then */
+					if (lseek(fd, 0, SEEK_SET) == -1) {
+						endwin();
+						fprintf(stderr, "\nError moving file pointer in "
+						                "back_lines().\n");
+						exit(-1);
+					}
+					page = buf + fpos - bytes_read;
+				} else {	/* Move backward BUF_SIZE/2 bytes */
+					if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) {
+						endwin();
+						fprintf(stderr, "\nError moving file pointer "
+						                "in back_lines().\n");
+						exit(-1);
+					}
+					page = buf + BUF_SIZE / 2;
+				}
+				if ((bytes_read =
+				     read(fd, buf, BUF_SIZE)) == -1) {
+					endwin();
+					fprintf(stderr, "\nError reading file in back_lines().\n");
+					exit(-1);
+				}
+				buf[bytes_read] = '\0';
+			} else {	/* Beginning of file reached */
+				begin_reached = 1;
+				return;
+			}
+		}
+		if (*(--page) != '\n') {	/* '--page' here */
+			/* Something's wrong... */
+			endwin();
+			fprintf(stderr, "\nInternal error in back_lines().\n");
+			exit(-1);
+		}
+	}
+	/* Go back 'n' lines */
+	for (i = 0; i < n; i++)
+		do {
+			if (page == buf) {
+				if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+					endwin();
+					fprintf(stderr, "\nError moving file pointer in back_lines().\n");
+					exit(-1);
+				}
+				if (fpos > bytes_read) {
+					/* Really possible to move backward BUF_SIZE/2 bytes? */
+					if (fpos < BUF_SIZE / 2 + bytes_read) {
+						/* No, move less then */
+						if (lseek(fd, 0, SEEK_SET) == -1) {
+							endwin();
+							fprintf(stderr, "\nError moving file pointer "
+							                "in back_lines().\n");
+							exit(-1);
+						}
+						page = buf + fpos - bytes_read;
+					} else {	/* Move backward BUF_SIZE/2 bytes */
+						if (lseek (fd, -(BUF_SIZE / 2 + bytes_read), SEEK_CUR) == -1) {
+							endwin();
+							fprintf(stderr, "\nError moving file pointer"
+							                " in back_lines().\n");
+							exit(-1);
+						}
+						page = buf + BUF_SIZE / 2;
+					}
+					if ((bytes_read =
+					     read(fd, buf, BUF_SIZE)) == -1) {
+						endwin();
+						fprintf(stderr, "\nError reading file in "
+						                "back_lines().\n");
+						exit(-1);
+					}
+					buf[bytes_read] = '\0';
+				} else {	/* Beginning of file reached */
+					begin_reached = 1;
+					return;
+				}
+			}
+		} while (*(--page) != '\n');
+	page++;
+}
+
+/*
+ * Print a new page of text. Called by dialog_textbox().
+ */
+static void print_page(WINDOW * win, int height, int width)
+{
+	int i, passed_end = 0;
+
+	page_length = 0;
+	for (i = 0; i < height; i++) {
+		print_line(win, i, width);
+		if (!passed_end)
+			page_length++;
+		if (end_reached && !passed_end)
+			passed_end = 1;
+	}
+	wnoutrefresh(win);
+}
+
+/*
+ * Print a new line of text. Called by dialog_textbox() and print_page().
+ */
+static void print_line(WINDOW * win, int row, int width)
+{
+	char *line;
+
+	line = get_line();
+	line += MIN(strlen(line), hscroll);	/* Scroll horizontally */
+	wmove(win, row, 0);	/* move cursor to correct line */
+	waddch(win, ' ');
+	waddnstr(win, line, MIN(strlen(line), width - 2));
+
+	/* 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, ' ');
+	}
+#else
+	wclrtoeol(win);
+#endif
+}
+
+/*
+ * Return current line of text. Called by dialog_textbox() and print_line().
+ * 'page' should point to start of current line before calling, and will be
+ * updated to point to start of next line.
+ */
+static char *get_line(void)
+{
+	int i = 0, fpos;
+	static char line[MAX_LEN + 1];
+
+	end_reached = 0;
+	while (*page != '\n') {
+		if (*page == '\0') {
+			/* Either end of file or end of buffer reached */
+			if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+				endwin();
+				fprintf(stderr, "\nError moving file pointer in "
+				                "get_line().\n");
+				exit(-1);
+			}
+			if (fpos < file_size) {	/* Not end of file yet */
+				/* We've reached end of buffer, but not end of file yet,
+				   so read next part of file into buffer */
+				if ((bytes_read =
+				     read(fd, buf, BUF_SIZE)) == -1) {
+					endwin();
+					fprintf(stderr, "\nError reading file in get_line().\n");
+					exit(-1);
+				}
+				buf[bytes_read] = '\0';
+				page = buf;
+			} else {
+				if (!end_reached)
+					end_reached = 1;
+				break;
+			}
+		} else if (i < MAX_LEN)
+			line[i++] = *(page++);
+		else {
+			/* Truncate lines longer than MAX_LEN characters */
+			if (i == MAX_LEN)
+				line[i++] = '\0';
+			page++;
+		}
+	}
+	if (i <= MAX_LEN)
+		line[i] = '\0';
+	if (!end_reached)
+		page++;		/* move pass '\n' */
+
+	return line;
+}
+
+/*
+ * Print current position
+ */
+static void print_position(WINDOW * win, int height, int width)
+{
+	int fpos, percent;
+
+	if ((fpos = lseek(fd, 0, SEEK_CUR)) == -1) {
+		endwin();
+		fprintf(stderr, "\nError moving file pointer in print_position().\n");
+		exit(-1);
+	}
+	wattrset(win, position_indicator_attr);
+	wbkgdset(win, position_indicator_attr & A_COLOR);
+	percent = !file_size ?
+	    100 : ((fpos - bytes_read + page - buf) * 100) / file_size;
+	wmove(win, height - 3, width - 9);
+	wprintw(win, "(%3d%%)", percent);
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/util.c b/busybox-1.19.3/scripts/kconfig/lxdialog/util.c
new file mode 100644
index 0000000..072d3ee
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/util.c
@@ -0,0 +1,362 @@
+/*
+ *  util.c
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/* use colors by default? */
+bool use_colors = 1;
+
+const char *backtitle = NULL;
+
+/*
+ * Attribute values, default is for mono display
+ */
+chtype attributes[] = {
+	A_NORMAL,		/* screen_attr */
+	A_NORMAL,		/* shadow_attr */
+	A_NORMAL,		/* dialog_attr */
+	A_BOLD,			/* title_attr */
+	A_NORMAL,		/* border_attr */
+	A_REVERSE,		/* button_active_attr */
+	A_DIM,			/* button_inactive_attr */
+	A_REVERSE,		/* button_key_active_attr */
+	A_BOLD,			/* button_key_inactive_attr */
+	A_REVERSE,		/* button_label_active_attr */
+	A_NORMAL,		/* button_label_inactive_attr */
+	A_NORMAL,		/* inputbox_attr */
+	A_NORMAL,		/* inputbox_border_attr */
+	A_NORMAL,		/* searchbox_attr */
+	A_BOLD,			/* searchbox_title_attr */
+	A_NORMAL,		/* searchbox_border_attr */
+	A_BOLD,			/* position_indicator_attr */
+	A_NORMAL,		/* menubox_attr */
+	A_NORMAL,		/* menubox_border_attr */
+	A_NORMAL,		/* item_attr */
+	A_REVERSE,		/* item_selected_attr */
+	A_BOLD,			/* tag_attr */
+	A_REVERSE,		/* tag_selected_attr */
+	A_BOLD,			/* tag_key_attr */
+	A_REVERSE,		/* tag_key_selected_attr */
+	A_BOLD,			/* check_attr */
+	A_REVERSE,		/* check_selected_attr */
+	A_BOLD,			/* uarrow_attr */
+	A_BOLD			/* darrow_attr */
+};
+
+#include "colors.h"
+
+/*
+ * Table of color values
+ */
+int color_table[][3] = {
+	{SCREEN_FG, SCREEN_BG, SCREEN_HL},
+	{SHADOW_FG, SHADOW_BG, SHADOW_HL},
+	{DIALOG_FG, DIALOG_BG, DIALOG_HL},
+	{TITLE_FG, TITLE_BG, TITLE_HL},
+	{BORDER_FG, BORDER_BG, BORDER_HL},
+	{BUTTON_ACTIVE_FG, BUTTON_ACTIVE_BG, BUTTON_ACTIVE_HL},
+	{BUTTON_INACTIVE_FG, BUTTON_INACTIVE_BG, BUTTON_INACTIVE_HL},
+	{BUTTON_KEY_ACTIVE_FG, BUTTON_KEY_ACTIVE_BG, BUTTON_KEY_ACTIVE_HL},
+	{BUTTON_KEY_INACTIVE_FG, BUTTON_KEY_INACTIVE_BG,
+	 BUTTON_KEY_INACTIVE_HL},
+	{BUTTON_LABEL_ACTIVE_FG, BUTTON_LABEL_ACTIVE_BG,
+	 BUTTON_LABEL_ACTIVE_HL},
+	{BUTTON_LABEL_INACTIVE_FG, BUTTON_LABEL_INACTIVE_BG,
+	 BUTTON_LABEL_INACTIVE_HL},
+	{INPUTBOX_FG, INPUTBOX_BG, INPUTBOX_HL},
+	{INPUTBOX_BORDER_FG, INPUTBOX_BORDER_BG, INPUTBOX_BORDER_HL},
+	{SEARCHBOX_FG, SEARCHBOX_BG, SEARCHBOX_HL},
+	{SEARCHBOX_TITLE_FG, SEARCHBOX_TITLE_BG, SEARCHBOX_TITLE_HL},
+	{SEARCHBOX_BORDER_FG, SEARCHBOX_BORDER_BG, SEARCHBOX_BORDER_HL},
+	{POSITION_INDICATOR_FG, POSITION_INDICATOR_BG, POSITION_INDICATOR_HL},
+	{MENUBOX_FG, MENUBOX_BG, MENUBOX_HL},
+	{MENUBOX_BORDER_FG, MENUBOX_BORDER_BG, MENUBOX_BORDER_HL},
+	{ITEM_FG, ITEM_BG, ITEM_HL},
+	{ITEM_SELECTED_FG, ITEM_SELECTED_BG, ITEM_SELECTED_HL},
+	{TAG_FG, TAG_BG, TAG_HL},
+	{TAG_SELECTED_FG, TAG_SELECTED_BG, TAG_SELECTED_HL},
+	{TAG_KEY_FG, TAG_KEY_BG, TAG_KEY_HL},
+	{TAG_KEY_SELECTED_FG, TAG_KEY_SELECTED_BG, TAG_KEY_SELECTED_HL},
+	{CHECK_FG, CHECK_BG, CHECK_HL},
+	{CHECK_SELECTED_FG, CHECK_SELECTED_BG, CHECK_SELECTED_HL},
+	{UARROW_FG, UARROW_BG, UARROW_HL},
+	{DARROW_FG, DARROW_BG, DARROW_HL},
+};				/* color_table */
+
+/*
+ * Set window to attribute 'attr'
+ */
+void attr_clear(WINDOW * win, int height, int width, chtype attr)
+{
+	int i, j;
+
+	wattrset(win, attr);
+	for (i = 0; i < height; i++) {
+		wmove(win, i, 0);
+		for (j = 0; j < width; j++)
+			waddch(win, ' ');
+	}
+	touchwin(win);
+}
+
+void dialog_clear(void)
+{
+	attr_clear(stdscr, LINES, COLS, screen_attr);
+	/* Display background title if it exists ... - SLH */
+	if (backtitle != NULL) {
+		int i;
+
+		wattrset(stdscr, screen_attr);
+		mvwaddstr(stdscr, 0, 1, (char *)backtitle);
+		wmove(stdscr, 1, 1);
+		for (i = 1; i < COLS - 1; i++)
+			waddch(stdscr, ACS_HLINE);
+	}
+	wnoutrefresh(stdscr);
+}
+
+/*
+ * Do some initialization for dialog
+ */
+void init_dialog(void)
+{
+	initscr();		/* Init curses */
+	keypad(stdscr, TRUE);
+	cbreak();
+	noecho();
+
+	if (use_colors)		/* Set up colors */
+		color_setup();
+
+	dialog_clear();
+}
+
+/*
+ * Setup for color display
+ */
+void color_setup(void)
+{
+	int i;
+
+	if (has_colors()) {	/* Terminal supports color? */
+		start_color();
+
+		/* Initialize color pairs */
+		for (i = 0; i < ATTRIBUTE_COUNT; i++)
+			init_pair(i + 1, color_table[i][0], color_table[i][1]);
+
+		/* Setup color attributes */
+		for (i = 0; i < ATTRIBUTE_COUNT; i++)
+			attributes[i] = C_ATTR(color_table[i][2], i + 1);
+	}
+}
+
+/*
+ * End using dialog functions.
+ */
+void end_dialog(void)
+{
+	endwin();
+}
+
+/* Print the title of the dialog. Center the title and truncate
+ * tile if wider than dialog (- 2 chars).
+ **/
+void print_title(WINDOW *dialog, const char *title, int width)
+{
+	if (title) {
+		int tlen = MIN(width - 2, strlen(title));
+		wattrset(dialog, title_attr);
+		mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
+		mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
+		waddch(dialog, ' ');
+	}
+}
+
+/*
+ * Print a string of text in a window, automatically wrap around to the
+ * next line if the string is too long to fit on one line. Newline
+ * characters '\n' are replaced by spaces.  We start on a new line
+ * if there is no room for at least 4 nonblanks following a double-space.
+ */
+void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
+{
+	int newl, cur_x, cur_y;
+	int i, prompt_len, room, wlen;
+	char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
+
+	strcpy(tempstr, prompt);
+
+	prompt_len = strlen(tempstr);
+
+	/*
+	 * Remove newlines
+	 */
+	for (i = 0; i < prompt_len; i++) {
+		if (tempstr[i] == '\n')
+			tempstr[i] = ' ';
+	}
+
+	if (prompt_len <= width - x * 2) {	/* If prompt is short */
+		wmove(win, y, (width - prompt_len) / 2);
+		waddstr(win, tempstr);
+	} else {
+		cur_x = x;
+		cur_y = y;
+		newl = 1;
+		word = tempstr;
+		while (word && *word) {
+			sp = strchr(word, ' ');
+			if (sp)
+				*sp++ = 0;
+
+			/* Wrap to next line if either the word does not fit,
+			   or it is the first word of a new sentence, and it is
+			   short, and the next word does not fit. */
+			room = width - cur_x;
+			wlen = strlen(word);
+			if (wlen > room ||
+			    (newl && wlen < 4 && sp
+			     && wlen + 1 + strlen(sp) > room
+			     && (!(sp2 = strchr(sp, ' '))
+				 || wlen + 1 + (sp2 - sp) > room))) {
+				cur_y++;
+				cur_x = x;
+			}
+			wmove(win, cur_y, cur_x);
+			waddstr(win, word);
+			getyx(win, cur_y, cur_x);
+			cur_x++;
+			if (sp && *sp == ' ') {
+				cur_x++;	/* double space */
+				while (*++sp == ' ') ;
+				newl = 1;
+			} else
+				newl = 0;
+			word = sp;
+		}
+	}
+}
+
+/*
+ * Print a button
+ */
+void print_button(WINDOW * win, const char *label, int y, int x, int selected)
+{
+	int i, temp;
+
+	wmove(win, y, x);
+	wattrset(win, selected ? button_active_attr : button_inactive_attr);
+	waddstr(win, "<");
+	temp = strspn(label, " ");
+	label += temp;
+	wattrset(win, selected ? button_label_active_attr
+		 : button_label_inactive_attr);
+	for (i = 0; i < temp; i++)
+		waddch(win, ' ');
+	wattrset(win, selected ? button_key_active_attr
+		 : button_key_inactive_attr);
+	waddch(win, label[0]);
+	wattrset(win, selected ? button_label_active_attr
+		 : button_label_inactive_attr);
+	waddstr(win, (char *)label + 1);
+	wattrset(win, selected ? button_active_attr : button_inactive_attr);
+	waddstr(win, ">");
+	wmove(win, y, x + temp + 1);
+}
+
+/*
+ * Draw a rectangular box with line drawing characters
+ */
+void
+draw_box(WINDOW * win, int y, int x, int height, int width,
+	 chtype box, chtype border)
+{
+	int i, j;
+
+	wattrset(win, 0);
+	for (i = 0; i < height; i++) {
+		wmove(win, y + i, x);
+		for (j = 0; j < width; j++)
+			if (!i && !j)
+				waddch(win, border | ACS_ULCORNER);
+			else if (i == height - 1 && !j)
+				waddch(win, border | ACS_LLCORNER);
+			else if (!i && j == width - 1)
+				waddch(win, box | ACS_URCORNER);
+			else if (i == height - 1 && j == width - 1)
+				waddch(win, box | ACS_LRCORNER);
+			else if (!i)
+				waddch(win, border | ACS_HLINE);
+			else if (i == height - 1)
+				waddch(win, box | ACS_HLINE);
+			else if (!j)
+				waddch(win, border | ACS_VLINE);
+			else if (j == width - 1)
+				waddch(win, box | ACS_VLINE);
+			else
+				waddch(win, box | ' ');
+	}
+}
+
+/*
+ * Draw shadows along the right and bottom edge to give a more 3D look
+ * to the boxes
+ */
+void draw_shadow(WINDOW * win, int y, int x, int height, int width)
+{
+	int i;
+
+	if (has_colors()) {	/* Whether terminal supports color? */
+		wattrset(win, shadow_attr);
+		wmove(win, y + height, x + 2);
+		for (i = 0; i < width; i++)
+			waddch(win, winch(win) & A_CHARTEXT);
+		for (i = y + 1; i < y + height + 1; i++) {
+			wmove(win, i, x + width);
+			waddch(win, winch(win) & A_CHARTEXT);
+			waddch(win, winch(win) & A_CHARTEXT);
+		}
+		wnoutrefresh(win);
+	}
+}
+
+/*
+ *  Return the position of the first alphabetic character in a string.
+ */
+int first_alpha(const char *string, const char *exempt)
+{
+	int i, in_paren = 0, c;
+
+	for (i = 0; i < strlen(string); i++) {
+		c = tolower(string[i]);
+
+		if (strchr("<[(", c))
+			++in_paren;
+		if (strchr(">])", c) && in_paren > 0)
+			--in_paren;
+
+		if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
+			return i;
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/lxdialog/yesno.c b/busybox-1.19.3/scripts/kconfig/lxdialog/yesno.c
new file mode 100644
index 0000000..cb2568a
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/lxdialog/yesno.c
@@ -0,0 +1,102 @@
+/*
+ *  yesno.c -- implements the yes/no box
+ *
+ *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
+ *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.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; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "dialog.h"
+
+/*
+ * Display termination buttons
+ */
+static void print_buttons(WINDOW * dialog, int height, int width, int selected)
+{
+	int x = width / 2 - 10;
+	int y = height - 2;
+
+	print_button(dialog, " Yes ", y, x, selected == 0);
+	print_button(dialog, "  No  ", y, x + 13, selected == 1);
+
+	wmove(dialog, y, x + 1 + 13 * selected);
+	wrefresh(dialog);
+}
+
+/*
+ * Display a dialog box with two buttons - Yes and No
+ */
+int dialog_yesno(const char *title, const char *prompt, int height, int width)
+{
+	int i, x, y, key = 0, button = 0;
+	WINDOW *dialog;
+
+	/* center dialog box on screen */
+	x = (COLS - width) / 2;
+	y = (LINES - height) / 2;
+
+	draw_shadow(stdscr, y, x, height, width);
+
+	dialog = newwin(height, width, y, x);
+	keypad(dialog, TRUE);
+
+	draw_box(dialog, 0, 0, height, width, dialog_attr, border_attr);
+	wattrset(dialog, border_attr);
+	mvwaddch(dialog, height - 3, 0, ACS_LTEE);
+	for (i = 0; i < width - 2; i++)
+		waddch(dialog, ACS_HLINE);
+	wattrset(dialog, dialog_attr);
+	waddch(dialog, ACS_RTEE);
+
+	print_title(dialog, title, width);
+
+	wattrset(dialog, dialog_attr);
+	print_autowrap(dialog, prompt, width - 2, 1, 3);
+
+	print_buttons(dialog, height, width, 0);
+
+	while (key != ESC) {
+		key = wgetch(dialog);
+		switch (key) {
+		case 'Y':
+		case 'y':
+			delwin(dialog);
+			return 0;
+		case 'N':
+		case 'n':
+			delwin(dialog);
+			return 1;
+
+		case TAB:
+		case KEY_LEFT:
+		case KEY_RIGHT:
+			button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button);
+
+			print_buttons(dialog, height, width, button);
+			wrefresh(dialog);
+			break;
+		case ' ':
+		case '\n':
+			delwin(dialog);
+			return button;
+		case ESC:
+			break;
+		}
+	}
+
+	delwin(dialog);
+	return -1;		/* ESC pressed */
+}
diff --git a/busybox-1.19.3/scripts/kconfig/mconf.c b/busybox-1.19.3/scripts/kconfig/mconf.c
new file mode 100644
index 0000000..d3f69f8
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/mconf.c
@@ -0,0 +1,1101 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ *
+ * Introduced single menu mode (show all sub-menus in one large tree).
+ * 2002-11-06 Petr Baudis <pasky@ucw.cz>
+ *
+ * i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ */
+
+#define _XOPEN_SOURCE 700
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h> /* for strcasecmp */
+#include <termios.h>
+#include <unistd.h>
+#include <locale.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+static char menu_backtitle[128];
+static const char mconf_readme[] = N_(
+"Overview\n"
+"--------\n"
+"Some features may be built directly into busybox.\n"
+"Some may be made into standalone applets.  Some features\n"
+"may be completely removed altogether.  There are also certain\n"
+"parameters which are not really features, but must be\n"
+"entered in as decimal or hexadecimal numbers or possibly text.\n"
+"\n"
+"Menu items beginning with [*], <M> or [ ] represent features\n"
+"configured to be built in, modularized or removed respectively.\n"
+"Pointed brackets <> represent module capable features.\n"
+"\n"
+"To change any of these features, highlight it with the cursor\n"
+"keys and press <Y> to build it in, <M> to make it a module or\n"
+"<N> to removed it.  You may also press the <Space Bar> to cycle\n"
+"through the available options (ie. Y->N->M->Y).\n"
+"\n"
+"Some additional keyboard hints:\n"
+"\n"
+"Menus\n"
+"----------\n"
+"o  Use the Up/Down arrow keys (cursor keys) to highlight the item\n"
+"   you wish to change or submenu wish to select and press <Enter>.\n"
+"   Submenus are designated by \"--->\".\n"
+"\n"
+"   Shortcut: Press the option's highlighted letter (hotkey).\n"
+"             Pressing a hotkey more than once will sequence\n"
+"             through all visible items which use that hotkey.\n"
+"\n"
+"   You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
+"   unseen options into view.\n"
+"\n"
+"o  To exit a menu use the cursor keys to highlight the <Exit> button\n"
+"   and press <ENTER>.\n"
+"\n"
+"   Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
+"             using those letters.  You may press a single <ESC>, but\n"
+"             there is a delayed response which you may find annoying.\n"
+"\n"
+"   Also, the <TAB> and cursor keys will cycle between <Select>,\n"
+"   <Exit> and <Help>\n"
+"\n"
+"o  To get help with an item, use the cursor keys to highlight <Help>\n"
+"   and Press <ENTER>.\n"
+"\n"
+"   Shortcut: Press <H> or <?>.\n"
+"\n"
+"\n"
+"Radiolists  (Choice lists)\n"
+"-----------\n"
+"o  Use the cursor keys to select the option you wish to set and press\n"
+"   <S> or the <SPACE BAR>.\n"
+"\n"
+"   Shortcut: Press the first letter of the option you wish to set then\n"
+"             press <S> or <SPACE BAR>.\n"
+"\n"
+"o  To see available help for the item, use the cursor keys to highlight\n"
+"   <Help> and Press <ENTER>.\n"
+"\n"
+"   Shortcut: Press <H> or <?>.\n"
+"\n"
+"   Also, the <TAB> and cursor keys will cycle between <Select> and\n"
+"   <Help>\n"
+"\n"
+"\n"
+"Data Entry\n"
+"-----------\n"
+"o  Enter the requested information and press <ENTER>\n"
+"   If you are entering hexadecimal values, it is not necessary to\n"
+"   add the '0x' prefix to the entry.\n"
+"\n"
+"o  For help, use the <TAB> or cursor keys to highlight the help option\n"
+"   and press <ENTER>.  You can try <TAB><H> as well.\n"
+"\n"
+"\n"
+"Text Box    (Help Window)\n"
+"--------\n"
+"o  Use the cursor keys to scroll up/down/left/right.  The VI editor\n"
+"   keys h,j,k,l function here as do <SPACE BAR> and <B> for those\n"
+"   who are familiar with less and lynx.\n"
+"\n"
+"o  Press <E>, <X>, <Enter> or <Esc><Esc> to exit.\n"
+"\n"
+"\n"
+"Alternate Configuration Files\n"
+"-----------------------------\n"
+"Menuconfig supports the use of alternate configuration files for\n"
+"those who, for various reasons, find it necessary to switch\n"
+"between different configurations.\n"
+"\n"
+"At the end of the main menu you will find two options.  One is\n"
+"for saving the current configuration to a file of your choosing.\n"
+"The other option is for loading a previously saved alternate\n"
+"configuration.\n"
+"\n"
+"Even if you don't use alternate configuration files, but you\n"
+"find during a Menuconfig session that you have completely messed\n"
+"up your settings, you may use the \"Load Alternate...\" option to\n"
+"restore your previously saved settings from \".config\" without\n"
+"restarting Menuconfig.\n"
+"\n"
+"Other information\n"
+"-----------------\n"
+"If you use Menuconfig in an XTERM window make sure you have your\n"
+"$TERM variable set to point to a xterm definition which supports color.\n"
+"Otherwise, Menuconfig will look rather bad.  Menuconfig will not\n"
+"display correctly in a RXVT window because rxvt displays only one\n"
+"intensity of color, bright.\n"
+"\n"
+"Menuconfig will display larger menus on screens or xterms which are\n"
+"set to display more than the standard 25 row by 80 column geometry.\n"
+"In order for this to work, the \"stty size\" command must be able to\n"
+"display the screen's current row and column geometry.  I STRONGLY\n"
+"RECOMMEND that you make sure you do NOT have the shell variables\n"
+"LINES and COLUMNS exported into your environment.  Some distributions\n"
+"export those variables via /etc/profile.  Some ncurses programs can\n"
+"become confused when those variables (LINES & COLUMNS) don't reflect\n"
+"the true screen size.\n"
+"\n"
+"Optional personality available\n"
+"------------------------------\n"
+"If you prefer to have all of the options listed in a single\n"
+"menu, rather than the default multimenu hierarchy, run the menuconfig\n"
+"with MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
+"\n"
+"make MENUCONFIG_MODE=single_menu menuconfig\n"
+"\n"
+"<Enter> will then unroll the appropriate category, or enfold it if it\n"
+"is already unrolled.\n"
+"\n"
+"Note that this mode can eventually be a little more CPU expensive\n"
+"(especially with a larger number of unrolled categories) than the\n"
+"default mode.\n"),
+menu_instructions[] = N_(
+	"Arrow keys navigate the menu.  "
+	"<Enter> selects submenus --->.  "
+	"Highlighted letters are hotkeys.  "
+	"Pressing <Y> includes, <N> excludes, <M> modularizes features.  "
+	"Press <Esc><Esc> to exit, <?> for Help, </> for Search.  "
+	"Legend: [*] built-in  [ ] excluded  <M> module  < > module capable"),
+radiolist_instructions[] = N_(
+	"Use the arrow keys to navigate this window or "
+	"press the hotkey of the item you wish to select "
+	"followed by the <SPACE BAR>. "
+	"Press <?> for additional information about this option."),
+inputbox_instructions_int[] = N_(
+	"Please enter a decimal value. "
+	"Fractions will not be accepted.  "
+	"Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_hex[] = N_(
+	"Please enter a hexadecimal value. "
+	"Use the <TAB> key to move from the input field to the buttons below it."),
+inputbox_instructions_string[] = N_(
+	"Please enter a string value. "
+	"Use the <TAB> key to move from the input field to the buttons below it."),
+setmod_text[] = N_(
+	"This feature depends on another which has been configured as a module.\n"
+	"As a result, this feature will be built as a module."),
+nohelp_text[] = N_(
+	"There is no help available for this option.\n"),
+load_config_text[] = N_(
+	"Enter the name of the configuration file you wish to load.  "
+	"Accept the name shown to restore the configuration you "
+	"last retrieved.  Leave blank to abort."),
+load_config_help[] = N_(
+	"\n"
+	"For various reasons, one may wish to keep several different\n"
+	"configurations available on a single machine.\n"
+	"\n"
+	"If you have saved a previous configuration in a file other than\n"
+	"default, entering the name of the file here will allow you\n"
+	"to modify that configuration.\n"
+	"\n"
+	"If you are uncertain, then you have probably never used alternate\n"
+	"configuration files.  You should therefor leave this blank to abort.\n"),
+save_config_text[] = N_(
+	"Enter a filename to which this configuration should be saved "
+	"as an alternate.  Leave blank to abort."),
+save_config_help[] = N_(
+	"\n"
+	"For various reasons, one may wish to keep different\n"
+	"configurations available on a single machine.\n"
+	"\n"
+	"Entering a file name here will allow you to later retrieve, modify\n"
+	"and use the current configuration as an alternate to whatever\n"
+	"configuration options you have selected at that time.\n"
+	"\n"
+	"If you are uncertain what all this means then you should probably\n"
+	"leave this blank.\n"),
+search_help[] = N_(
+	"\n"
+	"Search for CONFIG_ symbols and display their relations.\n"
+	"Regular expressions are allowed.\n"
+	"Example: search for \"^FOO\"\n"
+	"Result:\n"
+	"-----------------------------------------------------------------\n"
+	"Symbol: FOO [=m]\n"
+	"Prompt: Foo bus is used to drive the bar HW\n"
+	"Defined at drivers/pci/Kconfig:47\n"
+	"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
+	"Location:\n"
+	"  -> Bus options (PCI, PCMCIA, EISA, MCA, ISA)\n"
+	"    -> PCI support (PCI [=y])\n"
+	"      -> PCI access mode (<choice> [=y])\n"
+	"Selects: LIBCRC32\n"
+	"Selected by: BAR\n"
+	"-----------------------------------------------------------------\n"
+	"o The line 'Prompt:' shows the text used in the menu structure for\n"
+	"  this CONFIG_ symbol\n"
+	"o The 'Defined at' line tell at what file / line number the symbol\n"
+	"  is defined\n"
+	"o The 'Depends on:' line tell what symbols needs to be defined for\n"
+	"  this symbol to be visible in the menu (selectable)\n"
+	"o The 'Location:' lines tell where in the menu structure this symbol\n"
+	"  is located\n"
+	"    A location followed by a [=y] indicate that this is a selectable\n"
+	"    menu item - and current value is displayed inside brackets.\n"
+	"o The 'Selects:' line tell what symbol will be automatically\n"
+	"  selected if this symbol is selected (y or m)\n"
+	"o The 'Selected by' line tell what symbol has selected this symbol\n"
+	"\n"
+	"Only relevant lines are shown.\n"
+	"\n\n"
+	"Search examples:\n"
+	"Examples: USB	=> find all CONFIG_ symbols containing USB\n"
+	"          ^USB => find all CONFIG_ symbols starting with USB\n"
+	"          USB$ => find all CONFIG_ symbols ending with USB\n"
+	"\n");
+
+static char buf[4096*10], *bufptr = buf;
+static char input_buf[4096];
+static const char filename[] = ".config";
+static char *args[1024], **argptr = args;
+static int indent;
+static struct termios ios_org;
+static int rows = 0, cols = 0;
+static struct menu *current_menu;
+static int child_count;
+static int do_resize;
+static int single_menu_mode;
+
+static void conf(struct menu *menu);
+static void conf_choice(struct menu *menu);
+static void conf_string(struct menu *menu);
+static void conf_load(void);
+static void conf_save(void);
+static void show_textbox(const char *title, const char *text, int r, int c);
+static void show_helptext(const char *title, const char *text);
+static void show_help(struct menu *menu);
+static void show_file(const char *filename, const char *title, int r, int c);
+
+static void cprint_init(void);
+static int cprint1(const char *fmt, ...);
+static void cprint_done(void);
+static int cprint(const char *fmt, ...);
+
+static void init_wsize(void)
+{
+	struct winsize ws;
+	char *env;
+
+	if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)) {
+		rows = ws.ws_row;
+		cols = ws.ws_col;
+	}
+
+	if (!rows) {
+		env = getenv("LINES");
+		if (env)
+			rows = atoi(env);
+		if (!rows)
+			rows = 24;
+	}
+	if (!cols) {
+		env = getenv("COLUMNS");
+		if (env)
+			cols = atoi(env);
+		if (!cols)
+			cols = 80;
+	}
+
+	if (rows < 19 || cols < 80) {
+		fprintf(stderr, N_("Your display is too small to run Menuconfig!\n"));
+		fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n"));
+		exit(1);
+	}
+
+	rows -= 4;
+	cols -= 5;
+}
+
+static void cprint_init(void)
+{
+	bufptr = buf;
+	argptr = args;
+	memset(args, 0, sizeof(args));
+	indent = 0;
+	child_count = 0;
+	cprint("./scripts/kconfig/lxdialog/lxdialog");
+	cprint("--backtitle");
+	cprint(menu_backtitle);
+}
+
+static int cprint1(const char *fmt, ...)
+{
+	va_list ap;
+	int res;
+
+	if (!*argptr)
+		*argptr = bufptr;
+	va_start(ap, fmt);
+	res = vsprintf(bufptr, fmt, ap);
+	va_end(ap);
+	bufptr += res;
+
+	return res;
+}
+
+static void cprint_done(void)
+{
+	*bufptr++ = 0;
+	argptr++;
+}
+
+static int cprint(const char *fmt, ...)
+{
+	va_list ap;
+	int res;
+
+	*argptr++ = bufptr;
+	va_start(ap, fmt);
+	res = vsprintf(bufptr, fmt, ap);
+	va_end(ap);
+	bufptr += res;
+	*bufptr++ = 0;
+
+	return res;
+}
+
+static void get_prompt_str(struct gstr *r, struct property *prop)
+{
+	int i, j;
+	struct menu *submenu[8], *menu;
+
+	str_printf(r, "Prompt: %s\n", prop->text);
+	str_printf(r, "  Defined at %s:%d\n", prop->menu->file->name,
+		prop->menu->lineno);
+	if (!expr_is_yes(prop->visible.expr)) {
+		str_append(r, "  Depends on: ");
+		expr_gstr_print(prop->visible.expr, r);
+		str_append(r, "\n");
+	}
+	menu = prop->menu->parent;
+	for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
+		submenu[i++] = menu;
+	if (i > 0) {
+		str_printf(r, "  Location:\n");
+		for (j = 4; --i >= 0; j += 2) {
+			menu = submenu[i];
+			str_printf(r, "%*c-> %s", j, ' ', menu_get_prompt(menu));
+			if (menu->sym) {
+				str_printf(r, " (%s [=%s])", menu->sym->name ?
+					menu->sym->name : "<choice>",
+					sym_get_string_value(menu->sym));
+			}
+			str_append(r, "\n");
+		}
+	}
+}
+
+static void get_symbol_str(struct gstr *r, struct symbol *sym)
+{
+	bool hit;
+	struct property *prop;
+
+	str_printf(r, "Symbol: %s [=%s]\n", sym->name,
+	                               sym_get_string_value(sym));
+	for_all_prompts(sym, prop)
+		get_prompt_str(r, prop);
+	hit = false;
+	for_all_properties(sym, prop, P_SELECT) {
+		if (!hit) {
+			str_append(r, "  Selects: ");
+			hit = true;
+		} else
+			str_printf(r, " && ");
+		expr_gstr_print(prop->expr, r);
+	}
+	if (hit)
+		str_append(r, "\n");
+	if (sym->rev_dep.expr) {
+		str_append(r, "  Selected by: ");
+		expr_gstr_print(sym->rev_dep.expr, r);
+		str_append(r, "\n");
+	}
+	str_append(r, "\n\n");
+}
+
+static struct gstr get_relations_str(struct symbol **sym_arr)
+{
+	struct symbol *sym;
+	struct gstr res = str_new();
+	int i;
+
+	for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
+		get_symbol_str(&res, sym);
+	if (!i)
+		str_append(&res, "No matches found.\n");
+	return res;
+}
+
+pid_t pid;
+
+static void winch_handler(int sig)
+{
+	if (!do_resize) {
+		kill(pid, SIGINT);
+		do_resize = 1;
+	}
+}
+
+static int exec_conf(void)
+{
+	int pipefd[2], stat, size;
+	struct sigaction sa;
+	sigset_t sset, osset;
+
+	sigemptyset(&sset);
+	sigaddset(&sset, SIGINT);
+	sigprocmask(SIG_BLOCK, &sset, &osset);
+
+	signal(SIGINT, SIG_DFL);
+
+	sa.sa_handler = winch_handler;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = SA_RESTART;
+	sigaction(SIGWINCH, &sa, NULL);
+
+	*argptr++ = NULL;
+
+	pipe(pipefd);
+	pid = fork();
+	if (pid == 0) {
+		sigprocmask(SIG_SETMASK, &osset, NULL);
+		dup2(pipefd[1], 2);
+		close(pipefd[0]);
+		close(pipefd[1]);
+		execv(args[0], args);
+		_exit(EXIT_FAILURE);
+	}
+
+	close(pipefd[1]);
+	bufptr = input_buf;
+	while (1) {
+		size = input_buf + sizeof(input_buf) - bufptr;
+		size = read(pipefd[0], bufptr, size);
+		if (size <= 0) {
+			if (size < 0) {
+				if (errno == EINTR || errno == EAGAIN)
+					continue;
+				perror("read");
+			}
+			break;
+		}
+		bufptr += size;
+	}
+	*bufptr++ = 0;
+	close(pipefd[0]);
+	waitpid(pid, &stat, 0);
+
+	if (do_resize) {
+		init_wsize();
+		do_resize = 0;
+		sigprocmask(SIG_SETMASK, &osset, NULL);
+		return -1;
+	}
+	if (WIFSIGNALED(stat)) {
+		printf("\finterrupted(%d)\n", WTERMSIG(stat));
+		exit(1);
+	}
+#if 0
+	printf("\fexit state: %d\nexit data: '%s'\n", WEXITSTATUS(stat), input_buf);
+	sleep(1);
+#endif
+	sigpending(&sset);
+	if (sigismember(&sset, SIGINT)) {
+		printf("\finterrupted\n");
+		exit(1);
+	}
+	sigprocmask(SIG_SETMASK, &osset, NULL);
+
+	return WEXITSTATUS(stat);
+}
+
+static void search_conf(void)
+{
+	struct symbol **sym_arr;
+	int stat;
+	struct gstr res;
+
+again:
+	cprint_init();
+	cprint("--title");
+	cprint(_("Search Configuration Parameter"));
+	cprint("--inputbox");
+	cprint(_("Enter CONFIG_ (sub)string to search for (omit CONFIG_)"));
+	cprint("10");
+	cprint("75");
+	cprint("");
+	stat = exec_conf();
+	if (stat < 0)
+		goto again;
+	switch (stat) {
+	case 0:
+		break;
+	case 1:
+		show_helptext(_("Search Configuration"), search_help);
+		goto again;
+	default:
+		return;
+	}
+
+	sym_arr = sym_re_search(input_buf);
+	res = get_relations_str(sym_arr);
+	free(sym_arr);
+	show_textbox(_("Search Results"), str_get(&res), 0, 0);
+	str_free(&res);
+}
+
+static void build_conf(struct menu *menu)
+{
+	struct symbol *sym;
+	struct property *prop;
+	struct menu *child;
+	int type, tmp, doint = 2;
+	tristate val;
+	char ch;
+
+	if (!menu_is_visible(menu))
+		return;
+
+	sym = menu->sym;
+	prop = menu->prompt;
+	if (!sym) {
+		if (prop && menu != current_menu) {
+			const char *prompt = menu_get_prompt(menu);
+			switch (prop->type) {
+			case P_MENU:
+				child_count++;
+				cprint("m%p", menu);
+
+				if (single_menu_mode) {
+					cprint1("%s%*c%s",
+						menu->data ? "-->" : "++>",
+						indent + 1, ' ', prompt);
+				} else
+					cprint1("   %*c%s  --->", indent + 1, ' ', prompt);
+
+				cprint_done();
+				if (single_menu_mode && menu->data)
+					goto conf_childs;
+				return;
+			default:
+				if (prompt) {
+					child_count++;
+					cprint(":%p", menu);
+					cprint("---%*c%s", indent + 1, ' ', prompt);
+				}
+			}
+		} else
+			doint = 0;
+		goto conf_childs;
+	}
+
+	type = sym_get_type(sym);
+	if (sym_is_choice(sym)) {
+		struct symbol *def_sym = sym_get_choice_value(sym);
+		struct menu *def_menu = NULL;
+
+		child_count++;
+		for (child = menu->list; child; child = child->next) {
+			if (menu_is_visible(child) && child->sym == def_sym)
+				def_menu = child;
+		}
+
+		val = sym_get_tristate_value(sym);
+		if (sym_is_changable(sym)) {
+			cprint("t%p", menu);
+			switch (type) {
+			case S_BOOLEAN:
+				cprint1("[%c]", val == no ? ' ' : '*');
+				break;
+			case S_TRISTATE:
+				switch (val) {
+				case yes: ch = '*'; break;
+				case mod: ch = 'M'; break;
+				default:  ch = ' '; break;
+				}
+				cprint1("<%c>", ch);
+				break;
+			}
+		} else {
+			cprint("%c%p", def_menu ? 't' : ':', menu);
+			cprint1("   ");
+		}
+
+		cprint1("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+		if (val == yes) {
+			if (def_menu) {
+				cprint1(" (%s)", menu_get_prompt(def_menu));
+				cprint1("  --->");
+				cprint_done();
+				if (def_menu->list) {
+					indent += 2;
+					build_conf(def_menu);
+					indent -= 2;
+				}
+			} else
+				cprint_done();
+			return;
+		}
+		cprint_done();
+	} else {
+		if (menu == current_menu) {
+			cprint(":%p", menu);
+			cprint("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+			goto conf_childs;
+		}
+		child_count++;
+		val = sym_get_tristate_value(sym);
+		if (sym_is_choice_value(sym) && val == yes) {
+			cprint(":%p", menu);
+			cprint1("   ");
+		} else {
+			switch (type) {
+			case S_BOOLEAN:
+				cprint("t%p", menu);
+				if (sym_is_changable(sym))
+					cprint1("[%c]", val == no ? ' ' : '*');
+				else
+					cprint1("---");
+				break;
+			case S_TRISTATE:
+				cprint("t%p", menu);
+				switch (val) {
+				case yes: ch = '*'; break;
+				case mod: ch = 'M'; break;
+				default:  ch = ' '; break;
+				}
+				if (sym_is_changable(sym))
+					cprint1("<%c>", ch);
+				else
+					cprint1("---");
+				break;
+			default:
+				cprint("s%p", menu);
+				tmp = cprint1("(%s)", sym_get_string_value(sym));
+				tmp = indent - tmp + 4;
+				if (tmp < 0)
+					tmp = 0;
+				cprint1("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
+					(sym_has_value(sym) || !sym_is_changable(sym)) ?
+					"" : " (NEW)");
+				cprint_done();
+				goto conf_childs;
+			}
+		}
+		cprint1("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
+			(sym_has_value(sym) || !sym_is_changable(sym)) ?
+			"" : " (NEW)");
+		if (menu->prompt->type == P_MENU) {
+			cprint1("  --->");
+			cprint_done();
+			return;
+		}
+		cprint_done();
+	}
+
+conf_childs:
+	indent += doint;
+	for (child = menu->list; child; child = child->next)
+		build_conf(child);
+	indent -= doint;
+}
+
+static void conf(struct menu *menu)
+{
+	struct menu *submenu;
+	const char *prompt = menu_get_prompt(menu);
+	struct symbol *sym;
+	char active_entry[40];
+	int stat, type, i;
+
+	unlink("lxdialog.scrltmp");
+	active_entry[0] = 0;
+	while (1) {
+		cprint_init();
+		cprint("--title");
+		cprint("%s", prompt ? prompt : _("Main Menu"));
+		cprint("--menu");
+		cprint(_(menu_instructions));
+		cprint("%d", rows);
+		cprint("%d", cols);
+		cprint("%d", rows - 10);
+		cprint("%s", active_entry);
+		current_menu = menu;
+		build_conf(menu);
+		if (!child_count)
+			break;
+		if (menu == &rootmenu) {
+			cprint(":");
+			cprint("--- ");
+			cprint("L");
+			cprint(_("    Load an Alternate Configuration File"));
+			cprint("S");
+			cprint(_("    Save Configuration to an Alternate File"));
+		}
+		stat = exec_conf();
+		if (stat < 0)
+			continue;
+
+		if (stat == 1 || stat == 255)
+			break;
+
+		type = input_buf[0];
+		if (!type)
+			continue;
+
+		for (i = 0; input_buf[i] && !isspace(input_buf[i]); i++)
+			;
+		if (i >= sizeof(active_entry))
+			i = sizeof(active_entry) - 1;
+		input_buf[i] = 0;
+		strcpy(active_entry, input_buf);
+
+		sym = NULL;
+		submenu = NULL;
+		if (sscanf(input_buf + 1, "%p", &submenu) == 1)
+			sym = submenu->sym;
+
+		switch (stat) {
+		case 0:
+			switch (type) {
+			case 'm':
+				if (single_menu_mode)
+					submenu->data = (void *) (long) !submenu->data;
+				else
+					conf(submenu);
+				break;
+			case 't':
+				if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)
+					conf_choice(submenu);
+				else if (submenu->prompt->type == P_MENU)
+					conf(submenu);
+				break;
+			case 's':
+				conf_string(submenu);
+				break;
+			case 'L':
+				conf_load();
+				break;
+			case 'S':
+				conf_save();
+				break;
+			}
+			break;
+		case 2:
+			if (sym)
+				show_help(submenu);
+			else
+				show_helptext("README", _(mconf_readme));
+			break;
+		case 3:
+			if (type == 't') {
+				if (sym_set_tristate_value(sym, yes))
+					break;
+				if (sym_set_tristate_value(sym, mod))
+					show_textbox(NULL, setmod_text, 6, 74);
+			}
+			break;
+		case 4:
+			if (type == 't')
+				sym_set_tristate_value(sym, no);
+			break;
+		case 5:
+			if (type == 't')
+				sym_set_tristate_value(sym, mod);
+			break;
+		case 6:
+			if (type == 't')
+				sym_toggle_tristate_value(sym);
+			else if (type == 'm')
+				conf(submenu);
+			break;
+		case 7:
+			search_conf();
+			break;
+		}
+	}
+}
+
+static void show_textbox(const char *title, const char *text, int r, int c)
+{
+	int fd;
+
+	fd = creat(".help.tmp", 0777);
+	write(fd, text, strlen(text));
+	close(fd);
+	show_file(".help.tmp", title, r, c);
+	unlink(".help.tmp");
+}
+
+static void show_helptext(const char *title, const char *text)
+{
+	show_textbox(title, text, 0, 0);
+}
+
+static void show_help(struct menu *menu)
+{
+	struct gstr help = str_new();
+	struct symbol *sym = menu->sym;
+
+	if (sym->help)
+	{
+		if (sym->name) {
+			str_printf(&help, "CONFIG_%s:\n\n", sym->name);
+			str_append(&help, _(sym->help));
+			str_append(&help, "\n");
+		}
+	} else {
+		str_append(&help, nohelp_text);
+	}
+	get_symbol_str(&help, sym);
+	show_helptext(menu_get_prompt(menu), str_get(&help));
+	str_free(&help);
+}
+
+static void show_file(const char *filename, const char *title, int r, int c)
+{
+	do {
+		cprint_init();
+		if (title) {
+			cprint("--title");
+			cprint("%s", title);
+		}
+		cprint("--textbox");
+		cprint("%s", filename);
+		cprint("%d", r ? r : rows);
+		cprint("%d", c ? c : cols);
+	} while (exec_conf() < 0);
+}
+
+static void conf_choice(struct menu *menu)
+{
+	const char *prompt = menu_get_prompt(menu);
+	struct menu *child;
+	struct symbol *active;
+	int stat;
+
+	active = sym_get_choice_value(menu->sym);
+	while (1) {
+		cprint_init();
+		cprint("--title");
+		cprint("%s", prompt ? prompt : _("Main Menu"));
+		cprint("--radiolist");
+		cprint(_(radiolist_instructions));
+		cprint("15");
+		cprint("70");
+		cprint("6");
+
+		current_menu = menu;
+		for (child = menu->list; child; child = child->next) {
+			if (!menu_is_visible(child))
+				continue;
+			cprint("%p", child);
+			cprint("%s", menu_get_prompt(child));
+			if (child->sym == sym_get_choice_value(menu->sym))
+				cprint("ON");
+			else if (child->sym == active)
+				cprint("SELECTED");
+			else
+				cprint("OFF");
+		}
+
+		stat = exec_conf();
+		switch (stat) {
+		case 0:
+			if (sscanf(input_buf, "%p", &child) != 1)
+				break;
+			sym_set_tristate_value(child->sym, yes);
+			return;
+		case 1:
+			if (sscanf(input_buf, "%p", &child) == 1) {
+				show_help(child);
+				active = child->sym;
+			} else
+				show_help(menu);
+			break;
+		case 255:
+			return;
+		}
+	}
+}
+
+static void conf_string(struct menu *menu)
+{
+	const char *prompt = menu_get_prompt(menu);
+	int stat;
+
+	while (1) {
+		cprint_init();
+		cprint("--title");
+		cprint("%s", prompt ? prompt : _("Main Menu"));
+		cprint("--inputbox");
+		switch (sym_get_type(menu->sym)) {
+		case S_INT:
+			cprint(_(inputbox_instructions_int));
+			break;
+		case S_HEX:
+			cprint(_(inputbox_instructions_hex));
+			break;
+		case S_STRING:
+			cprint(_(inputbox_instructions_string));
+			break;
+		default:
+			/* panic? */;
+		}
+		cprint("10");
+		cprint("75");
+		cprint("%s", sym_get_string_value(menu->sym));
+		stat = exec_conf();
+		switch (stat) {
+		case 0:
+			if (sym_set_string_value(menu->sym, input_buf))
+				return;
+			show_textbox(NULL, _("You have made an invalid entry."), 5, 43);
+			break;
+		case 1:
+			show_help(menu);
+			break;
+		case 255:
+			return;
+		}
+	}
+}
+
+static void conf_load(void)
+{
+	int stat;
+
+	while (1) {
+		cprint_init();
+		cprint("--inputbox");
+		cprint(load_config_text);
+		cprint("11");
+		cprint("55");
+		cprint("%s", filename);
+		stat = exec_conf();
+		switch(stat) {
+		case 0:
+			if (!input_buf[0])
+				return;
+			if (!conf_read(input_buf))
+				return;
+			show_textbox(NULL, _("File does not exist!"), 5, 38);
+			break;
+		case 1:
+			show_helptext(_("Load Alternate Configuration"), load_config_help);
+			break;
+		case 255:
+			return;
+		}
+	}
+}
+
+static void conf_save(void)
+{
+	int stat;
+
+	while (1) {
+		cprint_init();
+		cprint("--inputbox");
+		cprint(save_config_text);
+		cprint("11");
+		cprint("55");
+		cprint("%s", filename);
+		stat = exec_conf();
+		switch(stat) {
+		case 0:
+			if (!input_buf[0])
+				return;
+			if (!conf_write(input_buf))
+				return;
+			show_textbox(NULL, _("Can't create file!  Probably a nonexistent directory."), 5, 60);
+			break;
+		case 1:
+			show_helptext(_("Save Alternate Configuration"), save_config_help);
+			break;
+		case 255:
+			return;
+		}
+	}
+}
+
+static void conf_cleanup(void)
+{
+	tcsetattr(1, TCSAFLUSH, &ios_org);
+	unlink(".help.tmp");
+	unlink("lxdialog.scrltmp");
+}
+
+int main(int ac, char **av)
+{
+	struct symbol *sym;
+	char *mode;
+	int stat;
+
+	setlocale(LC_ALL, "");
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+
+	conf_parse(av[1]);
+	conf_read(NULL);
+
+	sym = sym_lookup("KERNELVERSION", 0);
+	sym_calc_value(sym);
+	sprintf(menu_backtitle, _("BusyBox %s Configuration"),
+		sym_get_string_value(sym));
+
+	mode = getenv("MENUCONFIG_MODE");
+	if (mode) {
+		if (!strcasecmp(mode, "single_menu"))
+			single_menu_mode = 1;
+	}
+
+	tcgetattr(1, &ios_org);
+	atexit(conf_cleanup);
+	init_wsize();
+	conf(&rootmenu);
+
+	do {
+		cprint_init();
+		cprint("--yesno");
+		cprint(_("Do you wish to save your new configuration?"));
+		cprint("5");
+		cprint("60");
+		stat = exec_conf();
+	} while (stat < 0);
+
+	if (stat == 0) {
+		if (conf_write(NULL)) {
+			fprintf(stderr, _("\n\n"
+				"Error during writing of the configuration.\n"
+				"Your configuration changes were NOT saved."
+				"\n\n"));
+			return 1;
+		}
+		printf(_("\n\n"
+			"*** End of configuration.\n"
+			"*** Execute 'make' to build the project or try 'make help'."
+			"\n\n"));
+	} else {
+		fprintf(stderr, _("\n\n"
+			"Your configuration changes were NOT saved."
+			"\n\n"));
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/menu.c b/busybox-1.19.3/scripts/kconfig/menu.c
new file mode 100644
index 0000000..14cf2ea
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/menu.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct menu rootmenu;
+static struct menu **last_entry_ptr;
+
+struct file *file_list;
+struct file *current_file;
+
+static void menu_warn(struct menu *menu, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+}
+
+static void prop_warn(struct property *prop, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	fprintf(stderr, "%s:%d:warning: ", prop->file->name, prop->lineno);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+}
+
+void menu_init(void)
+{
+	current_entry = current_menu = &rootmenu;
+	last_entry_ptr = &rootmenu.list;
+}
+
+void menu_add_entry(struct symbol *sym)
+{
+	struct menu *menu;
+
+	menu = malloc(sizeof(*menu));
+	memset(menu, 0, sizeof(*menu));
+	menu->sym = sym;
+	menu->parent = current_menu;
+	menu->file = current_file;
+	menu->lineno = zconf_lineno();
+
+	*last_entry_ptr = menu;
+	last_entry_ptr = &menu->next;
+	current_entry = menu;
+}
+
+void menu_end_entry(void)
+{
+}
+
+struct menu *menu_add_menu(void)
+{
+	menu_end_entry();
+	last_entry_ptr = &current_entry->list;
+	return current_menu = current_entry;
+}
+
+void menu_end_menu(void)
+{
+	last_entry_ptr = &current_menu->next;
+	current_menu = current_menu->parent;
+}
+
+struct expr *menu_check_dep(struct expr *e)
+{
+	if (!e)
+		return e;
+
+	switch (e->type) {
+	case E_NOT:
+		e->left.expr = menu_check_dep(e->left.expr);
+		break;
+	case E_OR:
+	case E_AND:
+		e->left.expr = menu_check_dep(e->left.expr);
+		e->right.expr = menu_check_dep(e->right.expr);
+		break;
+	case E_SYMBOL:
+		/* change 'm' into 'm' && MODULES */
+		if (e->left.sym == &symbol_mod)
+			return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
+		break;
+	default:
+		break;
+	}
+	return e;
+}
+
+void menu_add_dep(struct expr *dep)
+{
+	current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep));
+}
+
+void menu_set_type(int type)
+{
+	struct symbol *sym = current_entry->sym;
+
+	if (sym->type == type)
+		return;
+	if (sym->type == S_UNKNOWN) {
+		sym->type = type;
+		return;
+	}
+	menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n",
+	    sym->name ? sym->name : "<choice>",
+	    sym_type_name(sym->type), sym_type_name(type));
+}
+
+struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep)
+{
+	struct property *prop = prop_alloc(type, current_entry->sym);
+
+	prop->menu = current_entry;
+	prop->text = prompt;
+	prop->expr = expr;
+	prop->visible.expr = menu_check_dep(dep);
+
+	if (prompt) {
+		if (current_entry->prompt)
+			menu_warn(current_entry, "prompt redefined\n");
+		current_entry->prompt = prop;
+	}
+
+	return prop;
+}
+
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
+{
+	return menu_add_prop(type, prompt, NULL, dep);
+}
+
+void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
+{
+	menu_add_prop(type, NULL, expr, dep);
+}
+
+void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
+{
+	menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
+}
+
+static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2)
+{
+	return sym2->type == S_INT || sym2->type == S_HEX ||
+	       (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
+}
+
+void sym_check_prop(struct symbol *sym)
+{
+	struct property *prop;
+	struct symbol *sym2;
+	for (prop = sym->prop; prop; prop = prop->next) {
+		switch (prop->type) {
+		case P_DEFAULT:
+			if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
+			    prop->expr->type != E_SYMBOL)
+				prop_warn(prop,
+				    "default for config symbol '%'"
+				    " must be a single symbol", sym->name);
+			break;
+		case P_SELECT:
+			sym2 = prop_get_symbol(prop);
+			if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
+				prop_warn(prop,
+				    "config symbol '%s' uses select, but is "
+				    "not boolean or tristate", sym->name);
+			else if (sym2->type == S_UNKNOWN)
+				prop_warn(prop,
+				    "'select' used by config symbol '%s' "
+				    "refer to undefined symbol '%s'",
+				    sym->name, sym2->name);
+			else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE)
+				prop_warn(prop,
+				    "'%s' has wrong type. 'select' only "
+				    "accept arguments of boolean and "
+				    "tristate type", sym2->name);
+			break;
+		case P_RANGE:
+			if (sym->type != S_INT && sym->type != S_HEX)
+				prop_warn(prop, "range is only allowed "
+				                "for int or hex symbols");
+			if (!menu_range_valid_sym(sym, prop->expr->left.sym) ||
+			    !menu_range_valid_sym(sym, prop->expr->right.sym))
+				prop_warn(prop, "range is invalid");
+			break;
+		default:
+			;
+		}
+	}
+}
+
+void menu_finalize(struct menu *parent)
+{
+	struct menu *menu, *last_menu;
+	struct symbol *sym;
+	struct property *prop;
+	struct expr *parentdep, *basedep, *dep, *dep2, **ep;
+
+	sym = parent->sym;
+	if (parent->list) {
+		if (sym && sym_is_choice(sym)) {
+			/* find the first choice value and find out choice type */
+			for (menu = parent->list; menu; menu = menu->next) {
+				if (menu->sym) {
+					current_entry = parent;
+					menu_set_type(menu->sym->type);
+					current_entry = menu;
+					menu_set_type(sym->type);
+					break;
+				}
+			}
+			parentdep = expr_alloc_symbol(sym);
+		} else if (parent->prompt)
+			parentdep = parent->prompt->visible.expr;
+		else
+			parentdep = parent->dep;
+
+		for (menu = parent->list; menu; menu = menu->next) {
+			basedep = expr_transform(menu->dep);
+			basedep = expr_alloc_and(expr_copy(parentdep), basedep);
+			basedep = expr_eliminate_dups(basedep);
+			menu->dep = basedep;
+			if (menu->sym)
+				prop = menu->sym->prop;
+			else
+				prop = menu->prompt;
+			for (; prop; prop = prop->next) {
+				if (prop->menu != menu)
+					continue;
+				dep = expr_transform(prop->visible.expr);
+				dep = expr_alloc_and(expr_copy(basedep), dep);
+				dep = expr_eliminate_dups(dep);
+				if (menu->sym && menu->sym->type != S_TRISTATE)
+					dep = expr_trans_bool(dep);
+				prop->visible.expr = dep;
+				if (prop->type == P_SELECT) {
+					struct symbol *es = prop_get_symbol(prop);
+					es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
+							expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
+				}
+			}
+		}
+		for (menu = parent->list; menu; menu = menu->next)
+			menu_finalize(menu);
+	} else if (sym) {
+		basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
+		basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
+		basedep = expr_eliminate_dups(expr_transform(basedep));
+		last_menu = NULL;
+		for (menu = parent->next; menu; menu = menu->next) {
+			dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
+			if (!expr_contains_symbol(dep, sym))
+				break;
+			if (expr_depends_symbol(dep, sym))
+				goto next;
+			dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
+			dep = expr_eliminate_dups(expr_transform(dep));
+			dep2 = expr_copy(basedep);
+			expr_eliminate_eq(&dep, &dep2);
+			expr_free(dep);
+			if (!expr_is_yes(dep2)) {
+				expr_free(dep2);
+				break;
+			}
+			expr_free(dep2);
+		next:
+			menu_finalize(menu);
+			menu->parent = parent;
+			last_menu = menu;
+		}
+		if (last_menu) {
+			parent->list = parent->next;
+			parent->next = last_menu->next;
+			last_menu->next = NULL;
+		}
+	}
+	for (menu = parent->list; menu; menu = menu->next) {
+		if (sym && sym_is_choice(sym) && menu->sym) {
+			menu->sym->flags |= SYMBOL_CHOICEVAL;
+			if (!menu->prompt)
+				menu_warn(menu, "choice value must have a prompt");
+			for (prop = menu->sym->prop; prop; prop = prop->next) {
+				if (prop->type == P_PROMPT && prop->menu != menu) {
+					prop_warn(prop, "choice values "
+					    "currently only support a "
+					    "single prompt");
+				}
+				if (prop->type == P_DEFAULT)
+					prop_warn(prop, "defaults for choice "
+					    "values not supported");
+			}
+			current_entry = menu;
+			menu_set_type(sym->type);
+			menu_add_symbol(P_CHOICE, sym, NULL);
+			prop = sym_get_choice_prop(sym);
+			for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
+				;
+			*ep = expr_alloc_one(E_CHOICE, NULL);
+			(*ep)->right.sym = menu->sym;
+		}
+		if (menu->list && (!menu->prompt || !menu->prompt->text)) {
+			for (last_menu = menu->list; ; last_menu = last_menu->next) {
+				last_menu->parent = parent;
+				if (!last_menu->next)
+					break;
+			}
+			last_menu->next = menu->next;
+			menu->next = menu->list;
+			menu->list = NULL;
+		}
+	}
+
+	if (sym && !(sym->flags & SYMBOL_WARNED)) {
+		if (sym->type == S_UNKNOWN)
+			menu_warn(parent, "config symbol defined "
+			    "without type\n");
+
+		if (sym_is_choice(sym) && !parent->prompt)
+			menu_warn(parent, "choice must have a prompt\n");
+
+		/* Check properties connected to this symbol */
+		sym_check_prop(sym);
+		sym->flags |= SYMBOL_WARNED;
+	}
+
+	if (sym && !sym_is_optional(sym) && parent->prompt) {
+		sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
+				expr_alloc_and(parent->prompt->visible.expr,
+					expr_alloc_symbol(&symbol_mod)));
+	}
+}
+
+bool menu_is_visible(struct menu *menu)
+{
+	struct menu *child;
+	struct symbol *sym;
+	tristate visible;
+
+	if (!menu->prompt)
+		return false;
+	sym = menu->sym;
+	if (sym) {
+		sym_calc_value(sym);
+		visible = menu->prompt->visible.tri;
+	} else
+		visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
+
+	if (visible != no)
+		return true;
+	if (!sym || sym_get_tristate_value(menu->sym) == no)
+		return false;
+
+	for (child = menu->list; child; child = child->next)
+		if (menu_is_visible(child))
+			return true;
+	return false;
+}
+
+const char *menu_get_prompt(struct menu *menu)
+{
+	if (menu->prompt)
+		return _(menu->prompt->text);
+	else if (menu->sym)
+		return _(menu->sym->name);
+	return NULL;
+}
+
+struct menu *menu_get_root_menu(struct menu *menu)
+{
+	return &rootmenu;
+}
+
+struct menu *menu_get_parent_menu(struct menu *menu)
+{
+	enum prop_type type;
+
+	for (; menu != &rootmenu; menu = menu->parent) {
+		type = menu->prompt ? menu->prompt->type : 0;
+		if (type == P_MENU)
+			break;
+	}
+	return menu;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/qconf.cc b/busybox-1.19.3/scripts/kconfig/qconf.cc
new file mode 100644
index 0000000..2a189c1
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/qconf.cc
@@ -0,0 +1,1425 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <qapplication.h>
+#include <qmainwindow.h>
+#include <qtoolbar.h>
+#include <qvbox.h>
+#include <qsplitter.h>
+#include <qlistview.h>
+#include <qtextview.h>
+#include <qlineedit.h>
+#include <qmenubar.h>
+#include <qmessagebox.h>
+#include <qaction.h>
+#include <qheader.h>
+#include <qfiledialog.h>
+#include <qregexp.h>
+
+#include <stdlib.h>
+
+#include "lkc.h"
+#include "qconf.h"
+
+#include "qconf.moc"
+#include "images.c"
+
+#ifdef _
+# undef _
+# define _ qgettext
+#endif
+
+static QApplication *configApp;
+
+static inline QString qgettext(const char* str)
+{
+  return QString::fromLocal8Bit(gettext(str));
+}
+
+static inline QString qgettext(const QString& str)
+{
+  return QString::fromLocal8Bit(gettext(str.latin1()));
+}
+
+ConfigSettings::ConfigSettings()
+	: showAll(false), showName(false), showRange(false), showData(false)
+{
+}
+
+#if QT_VERSION >= 300
+/**
+ * Reads the list column settings from the application settings.
+ */
+void ConfigSettings::readListSettings()
+{
+	showAll = readBoolEntry("/kconfig/qconf/showAll", false);
+	showName = readBoolEntry("/kconfig/qconf/showName", false);
+	showRange = readBoolEntry("/kconfig/qconf/showRange", false);
+	showData = readBoolEntry("/kconfig/qconf/showData", false);
+}
+
+/**
+ * Reads a list of integer values from the application settings.
+ */
+QValueList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
+{
+	QValueList<int> result;
+	QStringList entryList = readListEntry(key, ok);
+	if (ok) {
+		QStringList::Iterator it;
+		for (it = entryList.begin(); it != entryList.end(); ++it)
+			result.push_back((*it).toInt());
+	}
+
+	return result;
+}
+
+/**
+ * Writes a list of integer values to the application settings.
+ */
+bool ConfigSettings::writeSizes(const QString& key, const QValueList<int>& value)
+{
+	QStringList stringList;
+	QValueList<int>::ConstIterator it;
+
+	for (it = value.begin(); it != value.end(); ++it)
+		stringList.push_back(QString::number(*it));
+	return writeEntry(key, stringList);
+}
+#endif
+
+
+/*
+ * update all the children of a menu entry
+ *   removes/adds the entries from the parent widget as necessary
+ *
+ * parent: either the menu list widget or a menu entry widget
+ * menu: entry to be updated
+ */
+template <class P>
+void ConfigList::updateMenuList(P* parent, struct menu* menu)
+{
+	struct menu* child;
+	ConfigItem* item;
+	ConfigItem* last;
+	bool visible;
+	enum prop_type type;
+
+	if (!menu) {
+		while ((item = parent->firstChild()))
+			delete item;
+		return;
+	}
+
+	last = parent->firstChild();
+	if (last && !last->goParent)
+		last = 0;
+	for (child = menu->list; child; child = child->next) {
+		item = last ? last->nextSibling() : parent->firstChild();
+		type = child->prompt ? child->prompt->type : P_UNKNOWN;
+
+		switch (mode) {
+		case menuMode:
+			if (!(child->flags & MENU_ROOT))
+				goto hide;
+			break;
+		case symbolMode:
+			if (child->flags & MENU_ROOT)
+				goto hide;
+			break;
+		default:
+			break;
+		}
+
+		visible = menu_is_visible(child);
+		if (showAll || visible) {
+			if (!item || item->menu != child)
+				item = new ConfigItem(parent, last, child, visible);
+			else
+				item->testUpdateMenu(visible);
+
+			if (mode == fullMode || mode == menuMode || type != P_MENU)
+				updateMenuList(item, child);
+			else
+				updateMenuList(item, 0);
+			last = item;
+			continue;
+		}
+	hide:
+		if (item && item->menu == child) {
+			last = parent->firstChild();
+			if (last == item)
+				last = 0;
+			else while (last->nextSibling() != item)
+				last = last->nextSibling();
+			delete item;
+		}
+	}
+}
+
+#if QT_VERSION >= 300
+/*
+ * set the new data
+ * TODO check the value
+ */
+void ConfigItem::okRename(int col)
+{
+	Parent::okRename(col);
+	sym_set_string_value(menu->sym, text(dataColIdx).latin1());
+}
+#endif
+
+/*
+ * update the displayed of a menu entry
+ */
+void ConfigItem::updateMenu(void)
+{
+	ConfigList* list;
+	struct symbol* sym;
+	struct property *prop;
+	QString prompt;
+	int type;
+	tristate expr;
+
+	list = listView();
+	if (goParent) {
+		setPixmap(promptColIdx, list->menuBackPix);
+		prompt = "..";
+		goto set_prompt;
+	}
+
+	sym = menu->sym;
+	prop = menu->prompt;
+	prompt = QString::fromLocal8Bit(menu_get_prompt(menu));
+
+	if (prop) switch (prop->type) {
+	case P_MENU:
+		if (list->mode == singleMode || list->mode == symbolMode) {
+			/* a menuconfig entry is displayed differently
+			 * depending whether it's at the view root or a child.
+			 */
+			if (sym && list->rootEntry == menu)
+				break;
+			setPixmap(promptColIdx, list->menuPix);
+		} else {
+			if (sym)
+				break;
+			setPixmap(promptColIdx, 0);
+		}
+		goto set_prompt;
+	case P_COMMENT:
+		setPixmap(promptColIdx, 0);
+		goto set_prompt;
+	default:
+		;
+	}
+	if (!sym)
+		goto set_prompt;
+
+	setText(nameColIdx, QString::fromLocal8Bit(sym->name));
+
+	type = sym_get_type(sym);
+	switch (type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		char ch;
+
+		if (!sym_is_changable(sym) && !list->showAll) {
+			setPixmap(promptColIdx, 0);
+			setText(noColIdx, QString::null);
+			setText(modColIdx, QString::null);
+			setText(yesColIdx, QString::null);
+			break;
+		}
+		expr = sym_get_tristate_value(sym);
+		switch (expr) {
+		case yes:
+			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
+				setPixmap(promptColIdx, list->choiceYesPix);
+			else
+				setPixmap(promptColIdx, list->symbolYesPix);
+			setText(yesColIdx, "Y");
+			ch = 'Y';
+			break;
+		case mod:
+			setPixmap(promptColIdx, list->symbolModPix);
+			setText(modColIdx, "M");
+			ch = 'M';
+			break;
+		default:
+			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
+				setPixmap(promptColIdx, list->choiceNoPix);
+			else
+				setPixmap(promptColIdx, list->symbolNoPix);
+			setText(noColIdx, "N");
+			ch = 'N';
+			break;
+		}
+		if (expr != no)
+			setText(noColIdx, sym_tristate_within_range(sym, no) ? "_" : 0);
+		if (expr != mod)
+			setText(modColIdx, sym_tristate_within_range(sym, mod) ? "_" : 0);
+		if (expr != yes)
+			setText(yesColIdx, sym_tristate_within_range(sym, yes) ? "_" : 0);
+
+		setText(dataColIdx, QChar(ch));
+		break;
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		const char* data;
+
+		data = sym_get_string_value(sym);
+
+#if QT_VERSION >= 300
+		int i = list->mapIdx(dataColIdx);
+		if (i >= 0)
+			setRenameEnabled(i, TRUE);
+#endif
+		setText(dataColIdx, data);
+		if (type == S_STRING)
+			prompt = QString("%1: %2").arg(prompt).arg(data);
+		else
+			prompt = QString("(%2) %1").arg(prompt).arg(data);
+		break;
+	}
+	if (!sym_has_value(sym) && visible)
+		prompt += " (NEW)";
+set_prompt:
+	setText(promptColIdx, prompt);
+}
+
+void ConfigItem::testUpdateMenu(bool v)
+{
+	ConfigItem* i;
+
+	visible = v;
+	if (!menu)
+		return;
+
+	sym_calc_value(menu->sym);
+	if (menu->flags & MENU_CHANGED) {
+		/* the menu entry changed, so update all list items */
+		menu->flags &= ~MENU_CHANGED;
+		for (i = (ConfigItem*)menu->data; i; i = i->nextItem)
+			i->updateMenu();
+	} else if (listView()->updateAll)
+		updateMenu();
+}
+
+void ConfigItem::paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align)
+{
+	ConfigList* list = listView();
+
+	if (visible) {
+		if (isSelected() && !list->hasFocus() && list->mode == menuMode)
+			Parent::paintCell(p, list->inactivedColorGroup, column, width, align);
+		else
+			Parent::paintCell(p, cg, column, width, align);
+	} else
+		Parent::paintCell(p, list->disabledColorGroup, column, width, align);
+}
+
+/*
+ * construct a menu entry
+ */
+void ConfigItem::init(void)
+{
+	if (menu) {
+		ConfigList* list = listView();
+		nextItem = (ConfigItem*)menu->data;
+		menu->data = this;
+
+		if (list->mode != fullMode)
+			setOpen(TRUE);
+		sym_calc_value(menu->sym);
+	}
+	updateMenu();
+}
+
+/*
+ * destruct a menu entry
+ */
+ConfigItem::~ConfigItem(void)
+{
+	if (menu) {
+		ConfigItem** ip = (ConfigItem**)&menu->data;
+		for (; *ip; ip = &(*ip)->nextItem) {
+			if (*ip == this) {
+				*ip = nextItem;
+				break;
+			}
+		}
+	}
+}
+
+void ConfigLineEdit::show(ConfigItem* i)
+{
+	item = i;
+	if (sym_get_string_value(item->menu->sym))
+		setText(QString::fromLocal8Bit(sym_get_string_value(item->menu->sym)));
+	else
+		setText(QString::null);
+	Parent::show();
+	setFocus();
+}
+
+void ConfigLineEdit::keyPressEvent(QKeyEvent* e)
+{
+	switch (e->key()) {
+	case Key_Escape:
+		break;
+	case Key_Return:
+	case Key_Enter:
+		sym_set_string_value(item->menu->sym, text().latin1());
+		parent()->updateList(item);
+		break;
+	default:
+		Parent::keyPressEvent(e);
+		return;
+	}
+	e->accept();
+	parent()->list->setFocus();
+	hide();
+}
+
+ConfigList::ConfigList(ConfigView* p, ConfigMainWindow* cv, ConfigSettings* configSettings)
+	: Parent(p), cview(cv),
+	  updateAll(false),
+	  symbolYesPix(xpm_symbol_yes), symbolModPix(xpm_symbol_mod), symbolNoPix(xpm_symbol_no),
+	  choiceYesPix(xpm_choice_yes), choiceNoPix(xpm_choice_no),
+	  menuPix(xpm_menu), menuInvPix(xpm_menu_inv), menuBackPix(xpm_menuback), voidPix(xpm_void),
+	  showAll(false), showName(false), showRange(false), showData(false),
+	  rootEntry(0)
+{
+	int i;
+
+	setSorting(-1);
+	setRootIsDecorated(TRUE);
+	disabledColorGroup = palette().active();
+	disabledColorGroup.setColor(QColorGroup::Text, palette().disabled().text());
+	inactivedColorGroup = palette().active();
+	inactivedColorGroup.setColor(QColorGroup::Highlight, palette().disabled().highlight());
+
+	connect(this, SIGNAL(selectionChanged(void)),
+		SLOT(updateSelection(void)));
+
+	if (configSettings) {
+		showAll = configSettings->showAll;
+		showName = configSettings->showName;
+		showRange = configSettings->showRange;
+		showData = configSettings->showData;
+	}
+
+	for (i = 0; i < colNr; i++)
+		colMap[i] = colRevMap[i] = -1;
+	addColumn(promptColIdx, "Option");
+
+	reinit();
+}
+
+void ConfigList::reinit(void)
+{
+	removeColumn(dataColIdx);
+	removeColumn(yesColIdx);
+	removeColumn(modColIdx);
+	removeColumn(noColIdx);
+	removeColumn(nameColIdx);
+
+	if (showName)
+		addColumn(nameColIdx, "Name");
+	if (showRange) {
+		addColumn(noColIdx, "N");
+		addColumn(modColIdx, "M");
+		addColumn(yesColIdx, "Y");
+	}
+	if (showData)
+		addColumn(dataColIdx, "Value");
+
+	updateListAll();
+}
+
+void ConfigList::updateSelection(void)
+{
+	struct menu *menu;
+	enum prop_type type;
+
+	ConfigItem* item = (ConfigItem*)selectedItem();
+	if (!item)
+		return;
+
+	cview->setHelp(item);
+
+	menu = item->menu;
+	if (!menu)
+		return;
+	type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+	if (mode == menuMode && type == P_MENU)
+		emit menuSelected(menu);
+}
+
+void ConfigList::updateList(ConfigItem* item)
+{
+	ConfigItem* last = 0;
+
+	if (!rootEntry)
+		goto update;
+
+	if (rootEntry != &rootmenu && (mode == singleMode ||
+	    (mode == symbolMode && rootEntry->parent != &rootmenu))) {
+		item = firstChild();
+		if (!item)
+			item = new ConfigItem(this, 0, true);
+		last = item;
+	}
+	if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
+	    rootEntry->sym && rootEntry->prompt) {
+		item = last ? last->nextSibling() : firstChild();
+		if (!item)
+			item = new ConfigItem(this, last, rootEntry, true);
+		else
+			item->testUpdateMenu(true);
+
+		updateMenuList(item, rootEntry);
+		triggerUpdate();
+		return;
+	}
+update:
+	updateMenuList(this, rootEntry);
+	triggerUpdate();
+}
+
+void ConfigList::setAllOpen(bool open)
+{
+	QListViewItemIterator it(this);
+
+	for (; it.current(); it++)
+		it.current()->setOpen(open);
+}
+
+void ConfigList::setValue(ConfigItem* item, tristate val)
+{
+	struct symbol* sym;
+	int type;
+	tristate oldval;
+
+	sym = item->menu ? item->menu->sym : 0;
+	if (!sym)
+		return;
+
+	type = sym_get_type(sym);
+	switch (type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		oldval = sym_get_tristate_value(sym);
+
+		if (!sym_set_tristate_value(sym, val))
+			return;
+		if (oldval == no && item->menu->list)
+			item->setOpen(TRUE);
+		parent()->updateList(item);
+		break;
+	}
+}
+
+void ConfigList::changeValue(ConfigItem* item)
+{
+	struct symbol* sym;
+	struct menu* menu;
+	int type, oldexpr, newexpr;
+
+	menu = item->menu;
+	if (!menu)
+		return;
+	sym = menu->sym;
+	if (!sym) {
+		if (item->menu->list)
+			item->setOpen(!item->isOpen());
+		return;
+	}
+
+	type = sym_get_type(sym);
+	switch (type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		oldexpr = sym_get_tristate_value(sym);
+		newexpr = sym_toggle_tristate_value(sym);
+		if (item->menu->list) {
+			if (oldexpr == newexpr)
+				item->setOpen(!item->isOpen());
+			else if (oldexpr == no)
+				item->setOpen(TRUE);
+		}
+		if (oldexpr != newexpr)
+			parent()->updateList(item);
+		break;
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+#if QT_VERSION >= 300
+		if (colMap[dataColIdx] >= 0)
+			item->startRename(colMap[dataColIdx]);
+		else
+#endif
+			parent()->lineEdit->show(item);
+		break;
+	}
+}
+
+void ConfigList::setRootMenu(struct menu *menu)
+{
+	enum prop_type type;
+
+	if (rootEntry == menu)
+		return;
+	type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
+	if (type != P_MENU)
+		return;
+	updateMenuList(this, 0);
+	rootEntry = menu;
+	updateListAll();
+	setSelected(currentItem(), hasFocus());
+}
+
+void ConfigList::setParentMenu(void)
+{
+	ConfigItem* item;
+	struct menu *oldroot;
+
+	oldroot = rootEntry;
+	if (rootEntry == &rootmenu)
+		return;
+	setRootMenu(menu_get_parent_menu(rootEntry->parent));
+
+	QListViewItemIterator it(this);
+	for (; (item = (ConfigItem*)it.current()); it++) {
+		if (item->menu == oldroot) {
+			setCurrentItem(item);
+			ensureItemVisible(item);
+			break;
+		}
+	}
+}
+
+void ConfigList::keyPressEvent(QKeyEvent* ev)
+{
+	QListViewItem* i = currentItem();
+	ConfigItem* item;
+	struct menu *menu;
+	enum prop_type type;
+
+	if (ev->key() == Key_Escape && mode != fullMode) {
+		emit parentSelected();
+		ev->accept();
+		return;
+	}
+
+	if (!i) {
+		Parent::keyPressEvent(ev);
+		return;
+	}
+	item = (ConfigItem*)i;
+
+	switch (ev->key()) {
+	case Key_Return:
+	case Key_Enter:
+		if (item->goParent) {
+			emit parentSelected();
+			break;
+		}
+		menu = item->menu;
+		if (!menu)
+			break;
+		type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+		if (type == P_MENU && rootEntry != menu &&
+		    mode != fullMode && mode != menuMode) {
+			emit menuSelected(menu);
+			break;
+		}
+	case Key_Space:
+		changeValue(item);
+		break;
+	case Key_N:
+		setValue(item, no);
+		break;
+	case Key_M:
+		setValue(item, mod);
+		break;
+	case Key_Y:
+		setValue(item, yes);
+		break;
+	default:
+		Parent::keyPressEvent(ev);
+		return;
+	}
+	ev->accept();
+}
+
+void ConfigList::contentsMousePressEvent(QMouseEvent* e)
+{
+	//QPoint p(contentsToViewport(e->pos()));
+	//printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
+	Parent::contentsMousePressEvent(e);
+}
+
+void ConfigList::contentsMouseReleaseEvent(QMouseEvent* e)
+{
+	QPoint p(contentsToViewport(e->pos()));
+	ConfigItem* item = (ConfigItem*)itemAt(p);
+	struct menu *menu;
+	enum prop_type ptype;
+	const QPixmap* pm;
+	int idx, x;
+
+	if (!item)
+		goto skip;
+
+	menu = item->menu;
+	x = header()->offset() + p.x();
+	idx = colRevMap[header()->sectionAt(x)];
+	switch (idx) {
+	case promptColIdx:
+		pm = item->pixmap(promptColIdx);
+		if (pm) {
+			int off = header()->sectionPos(0) + itemMargin() +
+				treeStepSize() * (item->depth() + (rootIsDecorated() ? 1 : 0));
+			if (x >= off && x < off + pm->width()) {
+				if (item->goParent) {
+					emit parentSelected();
+					break;
+				} else if (!menu)
+					break;
+				ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+				if (ptype == P_MENU && rootEntry != menu &&
+				    mode != fullMode && mode != menuMode)
+					emit menuSelected(menu);
+				else
+					changeValue(item);
+			}
+		}
+		break;
+	case noColIdx:
+		setValue(item, no);
+		break;
+	case modColIdx:
+		setValue(item, mod);
+		break;
+	case yesColIdx:
+		setValue(item, yes);
+		break;
+	case dataColIdx:
+		changeValue(item);
+		break;
+	}
+
+skip:
+	//printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
+	Parent::contentsMouseReleaseEvent(e);
+}
+
+void ConfigList::contentsMouseMoveEvent(QMouseEvent* e)
+{
+	//QPoint p(contentsToViewport(e->pos()));
+	//printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
+	Parent::contentsMouseMoveEvent(e);
+}
+
+void ConfigList::contentsMouseDoubleClickEvent(QMouseEvent* e)
+{
+	QPoint p(contentsToViewport(e->pos()));
+	ConfigItem* item = (ConfigItem*)itemAt(p);
+	struct menu *menu;
+	enum prop_type ptype;
+
+	if (!item)
+		goto skip;
+	if (item->goParent) {
+		emit parentSelected();
+		goto skip;
+	}
+	menu = item->menu;
+	if (!menu)
+		goto skip;
+	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+	if (ptype == P_MENU && (mode == singleMode || mode == symbolMode))
+		emit menuSelected(menu);
+	else if (menu->sym)
+		changeValue(item);
+
+skip:
+	//printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
+	Parent::contentsMouseDoubleClickEvent(e);
+}
+
+void ConfigList::focusInEvent(QFocusEvent *e)
+{
+	Parent::focusInEvent(e);
+
+	QListViewItem* item = currentItem();
+	if (!item)
+		return;
+
+	setSelected(item, TRUE);
+	emit gotFocus();
+}
+
+ConfigView* ConfigView::viewList;
+
+ConfigView::ConfigView(QWidget* parent, ConfigMainWindow* cview,
+		       ConfigSettings *configSettings)
+	: Parent(parent)
+{
+	list = new ConfigList(this, cview, configSettings);
+	lineEdit = new ConfigLineEdit(this);
+	lineEdit->hide();
+
+	this->nextView = viewList;
+	viewList = this;
+}
+
+ConfigView::~ConfigView(void)
+{
+	ConfigView** vp;
+
+	for (vp = &viewList; *vp; vp = &(*vp)->nextView) {
+		if (*vp == this) {
+			*vp = nextView;
+			break;
+		}
+	}
+}
+
+void ConfigView::updateList(ConfigItem* item)
+{
+	ConfigView* v;
+
+	for (v = viewList; v; v = v->nextView)
+		v->list->updateList(item);
+}
+
+void ConfigView::updateListAll(void)
+{
+	ConfigView* v;
+
+	for (v = viewList; v; v = v->nextView)
+		v->list->updateListAll();
+}
+
+/*
+ * Construct the complete config widget
+ */
+ConfigMainWindow::ConfigMainWindow(void)
+{
+	QMenuBar* menu;
+	bool ok;
+	int x, y, width, height;
+
+	QWidget *d = configApp->desktop();
+
+	ConfigSettings* configSettings = new ConfigSettings();
+#if QT_VERSION >= 300
+	width = configSettings->readNumEntry("/kconfig/qconf/window width", d->width() - 64);
+	height = configSettings->readNumEntry("/kconfig/qconf/window height", d->height() - 64);
+	resize(width, height);
+	x = configSettings->readNumEntry("/kconfig/qconf/window x", 0, &ok);
+	if (ok)
+		y = configSettings->readNumEntry("/kconfig/qconf/window y", 0, &ok);
+	if (ok)
+		move(x, y);
+	showDebug = configSettings->readBoolEntry("/kconfig/qconf/showDebug", false);
+
+	// read list settings into configSettings, will be used later for ConfigList setup
+	configSettings->readListSettings();
+#else
+	width = d->width() - 64;
+	height = d->height() - 64;
+	resize(width, height);
+	showDebug = false;
+#endif
+
+	split1 = new QSplitter(this);
+	split1->setOrientation(QSplitter::Horizontal);
+	setCentralWidget(split1);
+
+	menuView = new ConfigView(split1, this, configSettings);
+	menuList = menuView->list;
+
+	split2 = new QSplitter(split1);
+	split2->setOrientation(QSplitter::Vertical);
+
+	// create config tree
+	configView = new ConfigView(split2, this, configSettings);
+	configList = configView->list;
+
+	helpText = new QTextView(split2);
+	helpText->setTextFormat(Qt::RichText);
+
+	setTabOrder(configList, helpText);
+	configList->setFocus();
+
+	menu = menuBar();
+	toolBar = new QToolBar("Tools", this);
+
+	backAction = new QAction("Back", QPixmap(xpm_back), "Back", 0, this);
+	  connect(backAction, SIGNAL(activated()), SLOT(goBack()));
+	  backAction->setEnabled(FALSE);
+	QAction *quitAction = new QAction("Quit", "&Quit", CTRL+Key_Q, this);
+	  connect(quitAction, SIGNAL(activated()), SLOT(close()));
+	QAction *loadAction = new QAction("Load", QPixmap(xpm_load), "&Load", CTRL+Key_L, this);
+	  connect(loadAction, SIGNAL(activated()), SLOT(loadConfig()));
+	QAction *saveAction = new QAction("Save", QPixmap(xpm_save), "&Save", CTRL+Key_S, this);
+	  connect(saveAction, SIGNAL(activated()), SLOT(saveConfig()));
+	QAction *saveAsAction = new QAction("Save As...", "Save &As...", 0, this);
+	  connect(saveAsAction, SIGNAL(activated()), SLOT(saveConfigAs()));
+	QAction *singleViewAction = new QAction("Single View", QPixmap(xpm_single_view), "Split View", 0, this);
+	  connect(singleViewAction, SIGNAL(activated()), SLOT(showSingleView()));
+	QAction *splitViewAction = new QAction("Split View", QPixmap(xpm_split_view), "Split View", 0, this);
+	  connect(splitViewAction, SIGNAL(activated()), SLOT(showSplitView()));
+	QAction *fullViewAction = new QAction("Full View", QPixmap(xpm_tree_view), "Full View", 0, this);
+	  connect(fullViewAction, SIGNAL(activated()), SLOT(showFullView()));
+
+	QAction *showNameAction = new QAction(NULL, "Show Name", 0, this);
+	  showNameAction->setToggleAction(TRUE);
+	  showNameAction->setOn(configList->showName);
+	  connect(showNameAction, SIGNAL(toggled(bool)), SLOT(setShowName(bool)));
+	QAction *showRangeAction = new QAction(NULL, "Show Range", 0, this);
+	  showRangeAction->setToggleAction(TRUE);
+	  showRangeAction->setOn(configList->showRange);
+	  connect(showRangeAction, SIGNAL(toggled(bool)), SLOT(setShowRange(bool)));
+	QAction *showDataAction = new QAction(NULL, "Show Data", 0, this);
+	  showDataAction->setToggleAction(TRUE);
+	  showDataAction->setOn(configList->showData);
+	  connect(showDataAction, SIGNAL(toggled(bool)), SLOT(setShowData(bool)));
+	QAction *showAllAction = new QAction(NULL, "Show All Options", 0, this);
+	  showAllAction->setToggleAction(TRUE);
+	  showAllAction->setOn(configList->showAll);
+	  connect(showAllAction, SIGNAL(toggled(bool)), SLOT(setShowAll(bool)));
+	QAction *showDebugAction = new QAction(NULL, "Show Debug Info", 0, this);
+	  showDebugAction->setToggleAction(TRUE);
+	  showDebugAction->setOn(showDebug);
+	  connect(showDebugAction, SIGNAL(toggled(bool)), SLOT(setShowDebug(bool)));
+
+	QAction *showIntroAction = new QAction(NULL, "Introduction", 0, this);
+	  connect(showIntroAction, SIGNAL(activated()), SLOT(showIntro()));
+	QAction *showAboutAction = new QAction(NULL, "About", 0, this);
+	  connect(showAboutAction, SIGNAL(activated()), SLOT(showAbout()));
+
+	// init tool bar
+	backAction->addTo(toolBar);
+	toolBar->addSeparator();
+	loadAction->addTo(toolBar);
+	saveAction->addTo(toolBar);
+	toolBar->addSeparator();
+	singleViewAction->addTo(toolBar);
+	splitViewAction->addTo(toolBar);
+	fullViewAction->addTo(toolBar);
+
+	// create config menu
+	QPopupMenu* config = new QPopupMenu(this);
+	menu->insertItem("&File", config);
+	loadAction->addTo(config);
+	saveAction->addTo(config);
+	saveAsAction->addTo(config);
+	config->insertSeparator();
+	quitAction->addTo(config);
+
+	// create options menu
+	QPopupMenu* optionMenu = new QPopupMenu(this);
+	menu->insertItem("&Option", optionMenu);
+	showNameAction->addTo(optionMenu);
+	showRangeAction->addTo(optionMenu);
+	showDataAction->addTo(optionMenu);
+	optionMenu->insertSeparator();
+	showAllAction->addTo(optionMenu);
+	showDebugAction->addTo(optionMenu);
+
+	// create help menu
+	QPopupMenu* helpMenu = new QPopupMenu(this);
+	menu->insertSeparator();
+	menu->insertItem("&Help", helpMenu);
+	showIntroAction->addTo(helpMenu);
+	showAboutAction->addTo(helpMenu);
+
+	connect(configList, SIGNAL(menuSelected(struct menu *)),
+		SLOT(changeMenu(struct menu *)));
+	connect(configList, SIGNAL(parentSelected()),
+		SLOT(goBack()));
+	connect(menuList, SIGNAL(menuSelected(struct menu *)),
+		SLOT(changeMenu(struct menu *)));
+
+	connect(configList, SIGNAL(gotFocus(void)),
+		SLOT(listFocusChanged(void)));
+	connect(menuList, SIGNAL(gotFocus(void)),
+		SLOT(listFocusChanged(void)));
+
+#if QT_VERSION >= 300
+	QString listMode = configSettings->readEntry("/kconfig/qconf/listMode", "symbol");
+	if (listMode == "single")
+		showSingleView();
+	else if (listMode == "full")
+		showFullView();
+	else /*if (listMode == "split")*/
+		showSplitView();
+
+	// UI setup done, restore splitter positions
+	QValueList<int> sizes = configSettings->readSizes("/kconfig/qconf/split1", &ok);
+	if (ok)
+		split1->setSizes(sizes);
+
+	sizes = configSettings->readSizes("/kconfig/qconf/split2", &ok);
+	if (ok)
+		split2->setSizes(sizes);
+#else
+	showSplitView();
+#endif
+	delete configSettings;
+}
+
+static QString print_filter(const QString &str)
+{
+	QRegExp re("[<>&\"\\n]");
+	QString res = str;
+	for (int i = 0; (i = res.find(re, i)) >= 0;) {
+		switch (res[i].latin1()) {
+		case '<':
+			res.replace(i, 1, "&lt;");
+			i += 4;
+			break;
+		case '>':
+			res.replace(i, 1, "&gt;");
+			i += 4;
+			break;
+		case '&':
+			res.replace(i, 1, "&amp;");
+			i += 5;
+			break;
+		case '"':
+			res.replace(i, 1, "&quot;");
+			i += 6;
+			break;
+		case '\n':
+			res.replace(i, 1, "<br>");
+			i += 4;
+			break;
+		}
+	}
+	return res;
+}
+
+static void expr_print_help(void *data, const char *str)
+{
+	reinterpret_cast<QString*>(data)->append(print_filter(str));
+}
+
+/*
+ * display a new help entry as soon as a new menu entry is selected
+ */
+void ConfigMainWindow::setHelp(QListViewItem* item)
+{
+	struct symbol* sym;
+	struct menu* menu = 0;
+
+	configList->parent()->lineEdit->hide();
+	if (item)
+		menu = ((ConfigItem*)item)->menu;
+	if (!menu) {
+		helpText->setText(QString::null);
+		return;
+	}
+
+	QString head, debug, help;
+	menu = ((ConfigItem*)item)->menu;
+	sym = menu->sym;
+	if (sym) {
+		if (menu->prompt) {
+			head += "<big><b>";
+			head += print_filter(_(menu->prompt->text));
+			head += "</b></big>";
+			if (sym->name) {
+				head += " (";
+				head += print_filter(_(sym->name));
+				head += ")";
+			}
+		} else if (sym->name) {
+			head += "<big><b>";
+			head += print_filter(_(sym->name));
+			head += "</b></big>";
+		}
+		head += "<br><br>";
+
+		if (showDebug) {
+			debug += "type: ";
+			debug += print_filter(sym_type_name(sym->type));
+			if (sym_is_choice(sym))
+				debug += " (choice)";
+			debug += "<br>";
+			if (sym->rev_dep.expr) {
+				debug += "reverse dep: ";
+				expr_print(sym->rev_dep.expr, expr_print_help, &debug, E_NONE);
+				debug += "<br>";
+			}
+			for (struct property *prop = sym->prop; prop; prop = prop->next) {
+				switch (prop->type) {
+				case P_PROMPT:
+				case P_MENU:
+					debug += "prompt: ";
+					debug += print_filter(_(prop->text));
+					debug += "<br>";
+					break;
+				case P_DEFAULT:
+					debug += "default: ";
+					expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+					debug += "<br>";
+					break;
+				case P_CHOICE:
+					if (sym_is_choice(sym)) {
+						debug += "choice: ";
+						expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+						debug += "<br>";
+					}
+					break;
+				case P_SELECT:
+					debug += "select: ";
+					expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+					debug += "<br>";
+					break;
+				case P_RANGE:
+					debug += "range: ";
+					expr_print(prop->expr, expr_print_help, &debug, E_NONE);
+					debug += "<br>";
+					break;
+				default:
+					debug += "unknown property: ";
+					debug += prop_get_type_name(prop->type);
+					debug += "<br>";
+				}
+				if (prop->visible.expr) {
+					debug += "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
+					expr_print(prop->visible.expr, expr_print_help, &debug, E_NONE);
+					debug += "<br>";
+				}
+			}
+			debug += "<br>";
+		}
+
+		help = print_filter(_(sym->help));
+	} else if (menu->prompt) {
+		head += "<big><b>";
+		head += print_filter(_(menu->prompt->text));
+		head += "</b></big><br><br>";
+		if (showDebug) {
+			if (menu->prompt->visible.expr) {
+				debug += "&nbsp;&nbsp;dep: ";
+				expr_print(menu->prompt->visible.expr, expr_print_help, &debug, E_NONE);
+				debug += "<br><br>";
+			}
+		}
+	}
+	if (showDebug)
+		debug += QString().sprintf("defined at %s:%d<br><br>", menu->file->name, menu->lineno);
+	helpText->setText(head + debug + help);
+}
+
+void ConfigMainWindow::loadConfig(void)
+{
+	QString s = QFileDialog::getOpenFileName(".config", NULL, this);
+	if (s.isNull())
+		return;
+	if (conf_read(QFile::encodeName(s)))
+		QMessageBox::information(this, "qconf", "Unable to load configuration!");
+	ConfigView::updateListAll();
+}
+
+void ConfigMainWindow::saveConfig(void)
+{
+	if (conf_write(NULL))
+		QMessageBox::information(this, "qconf", "Unable to save configuration!");
+}
+
+void ConfigMainWindow::saveConfigAs(void)
+{
+	QString s = QFileDialog::getSaveFileName(".config", NULL, this);
+	if (s.isNull())
+		return;
+	if (conf_write(QFile::encodeName(s)))
+		QMessageBox::information(this, "qconf", "Unable to save configuration!");
+}
+
+void ConfigMainWindow::changeMenu(struct menu *menu)
+{
+	configList->setRootMenu(menu);
+	backAction->setEnabled(TRUE);
+}
+
+void ConfigMainWindow::listFocusChanged(void)
+{
+	if (menuList->hasFocus()) {
+		if (menuList->mode == menuMode)
+			configList->clearSelection();
+		setHelp(menuList->selectedItem());
+	} else if (configList->hasFocus()) {
+		setHelp(configList->selectedItem());
+	}
+}
+
+void ConfigMainWindow::goBack(void)
+{
+	ConfigItem* item;
+
+	configList->setParentMenu();
+	if (configList->rootEntry == &rootmenu)
+		backAction->setEnabled(FALSE);
+	item = (ConfigItem*)menuList->selectedItem();
+	while (item) {
+		if (item->menu == configList->rootEntry) {
+			menuList->setSelected(item, TRUE);
+			break;
+		}
+		item = (ConfigItem*)item->parent();
+	}
+}
+
+void ConfigMainWindow::showSingleView(void)
+{
+	menuView->hide();
+	menuList->setRootMenu(0);
+	configList->mode = singleMode;
+	if (configList->rootEntry == &rootmenu)
+		configList->updateListAll();
+	else
+		configList->setRootMenu(&rootmenu);
+	configList->setAllOpen(TRUE);
+	configList->setFocus();
+}
+
+void ConfigMainWindow::showSplitView(void)
+{
+	configList->mode = symbolMode;
+	if (configList->rootEntry == &rootmenu)
+		configList->updateListAll();
+	else
+		configList->setRootMenu(&rootmenu);
+	configList->setAllOpen(TRUE);
+	configApp->processEvents();
+	menuList->mode = menuMode;
+	menuList->setRootMenu(&rootmenu);
+	menuList->setAllOpen(TRUE);
+	menuView->show();
+	menuList->setFocus();
+}
+
+void ConfigMainWindow::showFullView(void)
+{
+	menuView->hide();
+	menuList->setRootMenu(0);
+	configList->mode = fullMode;
+	if (configList->rootEntry == &rootmenu)
+		configList->updateListAll();
+	else
+		configList->setRootMenu(&rootmenu);
+	configList->setAllOpen(FALSE);
+	configList->setFocus();
+}
+
+void ConfigMainWindow::setShowAll(bool b)
+{
+	if (configList->showAll == b)
+		return;
+	configList->showAll = b;
+	configList->updateListAll();
+	menuList->showAll = b;
+	menuList->updateListAll();
+}
+
+void ConfigMainWindow::setShowDebug(bool b)
+{
+	if (showDebug == b)
+		return;
+	showDebug = b;
+}
+
+void ConfigMainWindow::setShowName(bool b)
+{
+	if (configList->showName == b)
+		return;
+	configList->showName = b;
+	configList->reinit();
+	menuList->showName = b;
+	menuList->reinit();
+}
+
+void ConfigMainWindow::setShowRange(bool b)
+{
+	if (configList->showRange == b)
+		return;
+	configList->showRange = b;
+	configList->reinit();
+	menuList->showRange = b;
+	menuList->reinit();
+}
+
+void ConfigMainWindow::setShowData(bool b)
+{
+	if (configList->showData == b)
+		return;
+	configList->showData = b;
+	configList->reinit();
+	menuList->showData = b;
+	menuList->reinit();
+}
+
+/*
+ * ask for saving configuration before quitting
+ * TODO ask only when something changed
+ */
+void ConfigMainWindow::closeEvent(QCloseEvent* e)
+{
+	if (!sym_change_count) {
+		e->accept();
+		return;
+	}
+	QMessageBox mb("qconf", "Save configuration?", QMessageBox::Warning,
+			QMessageBox::Yes | QMessageBox::Default, QMessageBox::No, QMessageBox::Cancel | QMessageBox::Escape);
+	mb.setButtonText(QMessageBox::Yes, "&Save Changes");
+	mb.setButtonText(QMessageBox::No, "&Discard Changes");
+	mb.setButtonText(QMessageBox::Cancel, "Cancel Exit");
+	switch (mb.exec()) {
+	case QMessageBox::Yes:
+		conf_write(NULL);
+	case QMessageBox::No:
+		e->accept();
+		break;
+	case QMessageBox::Cancel:
+		e->ignore();
+		break;
+	}
+}
+
+void ConfigMainWindow::showIntro(void)
+{
+	static char str[] = "Welcome to the qconf graphical configuration tool.\n\n"
+		"For each option, a blank box indicates the feature is disabled, a check\n"
+		"indicates it is enabled, and a dot indicates that it is to be compiled\n"
+		"as a module.  Clicking on the box will cycle through the three states.\n\n"
+		"If you do not see an option (e.g., a device driver) that you believe\n"
+		"should be present, try turning on Show All Options under the Options menu.\n"
+		"Although there is no cross reference yet to help you figure out what other\n"
+		"options must be enabled to support the option you are interested in, you can\n"
+		"still view the help of a grayed-out option.\n\n"
+		"Toggling Show Debug Info under the Options menu will show the dependencies,\n"
+		"which you can then match by examining other options.\n\n";
+
+	QMessageBox::information(this, "qconf", str);
+}
+
+void ConfigMainWindow::showAbout(void)
+{
+	static char str[] = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n";
+
+	QMessageBox::information(this, "qconf", str);
+}
+
+void ConfigMainWindow::saveSettings(void)
+{
+#if QT_VERSION >= 300
+	ConfigSettings *configSettings = new ConfigSettings;
+	configSettings->writeEntry("/kconfig/qconf/window x", pos().x());
+	configSettings->writeEntry("/kconfig/qconf/window y", pos().y());
+	configSettings->writeEntry("/kconfig/qconf/window width", size().width());
+	configSettings->writeEntry("/kconfig/qconf/window height", size().height());
+	configSettings->writeEntry("/kconfig/qconf/showName", configList->showName);
+	configSettings->writeEntry("/kconfig/qconf/showRange", configList->showRange);
+	configSettings->writeEntry("/kconfig/qconf/showData", configList->showData);
+	configSettings->writeEntry("/kconfig/qconf/showAll", configList->showAll);
+	configSettings->writeEntry("/kconfig/qconf/showDebug", showDebug);
+
+	QString entry;
+	switch(configList->mode) {
+	case singleMode :
+		entry = "single";
+		break;
+
+	case symbolMode :
+		entry = "split";
+		break;
+
+	case fullMode :
+		entry = "full";
+		break;
+	}
+	configSettings->writeEntry("/kconfig/qconf/listMode", entry);
+
+	configSettings->writeSizes("/kconfig/qconf/split1", split1->sizes());
+	configSettings->writeSizes("/kconfig/qconf/split2", split2->sizes());
+
+	delete configSettings;
+#endif
+}
+
+void fixup_rootmenu(struct menu *menu)
+{
+	struct menu *child;
+	static int menu_cnt = 0;
+
+	menu->flags |= MENU_ROOT;
+	for (child = menu->list; child; child = child->next) {
+		if (child->prompt && child->prompt->type == P_MENU) {
+			menu_cnt++;
+			fixup_rootmenu(child);
+			menu_cnt--;
+		} else if (!menu_cnt)
+			fixup_rootmenu(child);
+	}
+}
+
+static const char *progname;
+
+static void usage(void)
+{
+	printf("%s <config>\n", progname);
+	exit(0);
+}
+
+int main(int ac, char** av)
+{
+	ConfigMainWindow* v;
+	const char *name;
+
+	bindtextdomain(PACKAGE, LOCALEDIR);
+	textdomain(PACKAGE);
+
+#ifndef LKC_DIRECT_LINK
+	kconfig_load();
+#endif
+
+	progname = av[0];
+	configApp = new QApplication(ac, av);
+	if (ac > 1 && av[1][0] == '-') {
+		switch (av[1][1]) {
+		case 'h':
+		case '?':
+			usage();
+		}
+		name = av[2];
+	} else
+		name = av[1];
+	if (!name)
+		usage();
+
+	conf_parse(name);
+	fixup_rootmenu(&rootmenu);
+	conf_read(NULL);
+	//zconfdump(stdout);
+
+	v = new ConfigMainWindow();
+
+	//zconfdump(stdout);
+	v->show();
+	configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
+	configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
+	configApp->exec();
+
+	return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/qconf.h b/busybox-1.19.3/scripts/kconfig/qconf.h
new file mode 100644
index 0000000..e52f3e9
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/qconf.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <qlistview.h>
+#if QT_VERSION >= 300
+#include <qsettings.h>
+#else
+class QSettings { };
+#endif
+
+class ConfigList;
+class ConfigItem;
+class ConfigLineEdit;
+class ConfigMainWindow;
+
+
+class ConfigSettings : public QSettings {
+public:
+	ConfigSettings();
+
+#if QT_VERSION >= 300
+	void readListSettings();
+	QValueList<int> readSizes(const QString& key, bool *ok);
+	bool writeSizes(const QString& key, const QValueList<int>& value);
+#endif
+
+	bool showAll;
+	bool showName;
+	bool showRange;
+	bool showData;
+};
+
+class ConfigView : public QVBox {
+	Q_OBJECT
+	typedef class QVBox Parent;
+public:
+	ConfigView(QWidget* parent, ConfigMainWindow* cview, ConfigSettings* configSettings);
+	~ConfigView(void);
+	static void updateList(ConfigItem* item);
+	static void updateListAll(void);
+
+public:
+	ConfigList* list;
+	ConfigLineEdit* lineEdit;
+
+	static ConfigView* viewList;
+	ConfigView* nextView;
+};
+
+enum colIdx {
+	promptColIdx, nameColIdx, noColIdx, modColIdx, yesColIdx, dataColIdx, colNr
+};
+enum listMode {
+	singleMode, menuMode, symbolMode, fullMode
+};
+
+class ConfigList : public QListView {
+	Q_OBJECT
+	typedef class QListView Parent;
+public:
+	ConfigList(ConfigView* p, ConfigMainWindow* cview, ConfigSettings *configSettings);
+	void reinit(void);
+	ConfigView* parent(void) const
+	{
+		return (ConfigView*)Parent::parent();
+	}
+
+protected:
+	ConfigMainWindow* cview;
+
+	void keyPressEvent(QKeyEvent *e);
+	void contentsMousePressEvent(QMouseEvent *e);
+	void contentsMouseReleaseEvent(QMouseEvent *e);
+	void contentsMouseMoveEvent(QMouseEvent *e);
+	void contentsMouseDoubleClickEvent(QMouseEvent *e);
+	void focusInEvent(QFocusEvent *e);
+public slots:
+	void setRootMenu(struct menu *menu);
+
+	void updateList(ConfigItem *item);
+	void setValue(ConfigItem* item, tristate val);
+	void changeValue(ConfigItem* item);
+	void updateSelection(void);
+signals:
+	void menuSelected(struct menu *menu);
+	void parentSelected(void);
+	void gotFocus(void);
+
+public:
+	void updateListAll(void)
+	{
+		updateAll = true;
+		updateList(NULL);
+		updateAll = false;
+	}
+	ConfigList* listView()
+	{
+		return this;
+	}
+	ConfigItem* firstChild() const
+	{
+		return (ConfigItem *)Parent::firstChild();
+	}
+	int mapIdx(colIdx idx)
+	{
+		return colMap[idx];
+	}
+	void addColumn(colIdx idx, const QString& label)
+	{
+		colMap[idx] = Parent::addColumn(label);
+		colRevMap[colMap[idx]] = idx;
+	}
+	void removeColumn(colIdx idx)
+	{
+		int col = colMap[idx];
+		if (col >= 0) {
+			Parent::removeColumn(col);
+			colRevMap[col] = colMap[idx] = -1;
+		}
+	}
+	void setAllOpen(bool open);
+	void setParentMenu(void);
+
+	template <class P>
+	void updateMenuList(P*, struct menu*);
+
+	bool updateAll;
+
+	QPixmap symbolYesPix, symbolModPix, symbolNoPix;
+	QPixmap choiceYesPix, choiceNoPix;
+	QPixmap menuPix, menuInvPix, menuBackPix, voidPix;
+
+	bool showAll, showName, showRange, showData;
+	enum listMode mode;
+	struct menu *rootEntry;
+	QColorGroup disabledColorGroup;
+	QColorGroup inactivedColorGroup;
+
+private:
+	int colMap[colNr];
+	int colRevMap[colNr];
+};
+
+class ConfigItem : public QListViewItem {
+	typedef class QListViewItem Parent;
+public:
+	ConfigItem(QListView *parent, ConfigItem *after, struct menu *m, bool v)
+	: Parent(parent, after), menu(m), visible(v), goParent(false)
+	{
+		init();
+	}
+	ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v)
+	: Parent(parent, after), menu(m), visible(v), goParent(false)
+	{
+		init();
+	}
+	ConfigItem(QListView *parent, ConfigItem *after, bool v)
+	: Parent(parent, after), menu(0), visible(v), goParent(true)
+	{
+		init();
+	}
+	~ConfigItem(void);
+	void init(void);
+#if QT_VERSION >= 300
+	void okRename(int col);
+#endif
+	void updateMenu(void);
+	void testUpdateMenu(bool v);
+	ConfigList* listView() const
+	{
+		return (ConfigList*)Parent::listView();
+	}
+	ConfigItem* firstChild() const
+	{
+		return (ConfigItem *)Parent::firstChild();
+	}
+	ConfigItem* nextSibling() const
+	{
+		return (ConfigItem *)Parent::nextSibling();
+	}
+	void setText(colIdx idx, const QString& text)
+	{
+		Parent::setText(listView()->mapIdx(idx), text);
+	}
+	QString text(colIdx idx) const
+	{
+		return Parent::text(listView()->mapIdx(idx));
+	}
+	void setPixmap(colIdx idx, const QPixmap& pm)
+	{
+		Parent::setPixmap(listView()->mapIdx(idx), pm);
+	}
+	const QPixmap* pixmap(colIdx idx) const
+	{
+		return Parent::pixmap(listView()->mapIdx(idx));
+	}
+	void paintCell(QPainter* p, const QColorGroup& cg, int column, int width, int align);
+
+	ConfigItem* nextItem;
+	struct menu *menu;
+	bool visible;
+	bool goParent;
+};
+
+class ConfigLineEdit : public QLineEdit {
+	Q_OBJECT
+	typedef class QLineEdit Parent;
+public:
+	ConfigLineEdit(ConfigView* parent)
+	: Parent(parent)
+	{ }
+	ConfigView* parent(void) const
+	{
+		return (ConfigView*)Parent::parent();
+	}
+	void show(ConfigItem *i);
+	void keyPressEvent(QKeyEvent *e);
+
+public:
+	ConfigItem *item;
+};
+
+class ConfigMainWindow : public QMainWindow {
+	Q_OBJECT
+public:
+	ConfigMainWindow(void);
+public slots:
+	void setHelp(QListViewItem* item);
+	void changeMenu(struct menu *);
+	void listFocusChanged(void);
+	void goBack(void);
+	void loadConfig(void);
+	void saveConfig(void);
+	void saveConfigAs(void);
+	void showSingleView(void);
+	void showSplitView(void);
+	void showFullView(void);
+	void setShowAll(bool);
+	void setShowDebug(bool);
+	void setShowRange(bool);
+	void setShowName(bool);
+	void setShowData(bool);
+	void showIntro(void);
+	void showAbout(void);
+	void saveSettings(void);
+
+protected:
+	void closeEvent(QCloseEvent *e);
+
+	ConfigView *menuView;
+	ConfigList *menuList;
+	ConfigView *configView;
+	ConfigList *configList;
+	QTextView *helpText;
+	QToolBar *toolBar;
+	QAction *backAction;
+	QSplitter* split1;
+	QSplitter* split2;
+
+	bool showDebug;
+};
diff --git a/busybox-1.19.3/scripts/kconfig/symbol.c b/busybox-1.19.3/scripts/kconfig/symbol.c
new file mode 100644
index 0000000..3d7877a
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/symbol.c
@@ -0,0 +1,882 @@
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+#include <sys/utsname.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+	.name = "y",
+	.curr = { "y", yes },
+	.flags = SYMBOL_YES|SYMBOL_VALID,
+}, symbol_mod = {
+	.name = "m",
+	.curr = { "m", mod },
+	.flags = SYMBOL_MOD|SYMBOL_VALID,
+}, symbol_no = {
+	.name = "n",
+	.curr = { "n", no },
+	.flags = SYMBOL_NO|SYMBOL_VALID,
+}, symbol_empty = {
+	.name = "",
+	.curr = { "", no },
+	.flags = SYMBOL_VALID,
+};
+
+int sym_change_count;
+struct symbol *modules_sym;
+tristate modules_val;
+
+void sym_add_default(struct symbol *sym, const char *def)
+{
+	struct property *prop = prop_alloc(P_DEFAULT, sym);
+
+	prop->expr = expr_alloc_symbol(sym_lookup(def, 1));
+}
+
+void sym_init(void)
+{
+	struct symbol *sym;
+	struct utsname uts;
+	char *p;
+	static bool inited = false;
+
+	if (inited)
+		return;
+	inited = true;
+
+	uname(&uts);
+
+	sym = sym_lookup("ARCH", 0);
+	sym->type = S_STRING;
+	sym->flags |= SYMBOL_AUTO;
+	p = getenv("ARCH");
+	if (p)
+		sym_add_default(sym, p);
+
+	sym = sym_lookup("KERNELVERSION", 0);
+	sym->type = S_STRING;
+	sym->flags |= SYMBOL_AUTO;
+	p = getenv("KERNELVERSION");
+	if (p)
+		sym_add_default(sym, p);
+
+	sym = sym_lookup("UNAME_RELEASE", 0);
+	sym->type = S_STRING;
+	sym->flags |= SYMBOL_AUTO;
+	sym_add_default(sym, uts.release);
+}
+
+enum symbol_type sym_get_type(struct symbol *sym)
+{
+	enum symbol_type type = sym->type;
+
+	if (type == S_TRISTATE) {
+		if (sym_is_choice_value(sym) && sym->visible == yes)
+			type = S_BOOLEAN;
+		else if (modules_val == no)
+			type = S_BOOLEAN;
+	}
+	return type;
+}
+
+const char *sym_type_name(enum symbol_type type)
+{
+	switch (type) {
+	case S_BOOLEAN:
+		return "boolean";
+	case S_TRISTATE:
+		return "tristate";
+	case S_INT:
+		return "integer";
+	case S_HEX:
+		return "hex";
+	case S_STRING:
+		return "string";
+	case S_UNKNOWN:
+		return "unknown";
+	case S_OTHER:
+		break;
+	}
+	return "???";
+}
+
+struct property *sym_get_choice_prop(struct symbol *sym)
+{
+	struct property *prop;
+
+	for_all_choices(sym, prop)
+		return prop;
+	return NULL;
+}
+
+struct property *sym_get_default_prop(struct symbol *sym)
+{
+	struct property *prop;
+
+	for_all_defaults(sym, prop) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		if (prop->visible.tri != no)
+			return prop;
+	}
+	return NULL;
+}
+
+struct property *sym_get_range_prop(struct symbol *sym)
+{
+	struct property *prop;
+
+	for_all_properties(sym, prop, P_RANGE) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		if (prop->visible.tri != no)
+			return prop;
+	}
+	return NULL;
+}
+
+static int sym_get_range_val(struct symbol *sym, int base)
+{
+	sym_calc_value(sym);
+	switch (sym->type) {
+	case S_INT:
+		base = 10;
+		break;
+	case S_HEX:
+		base = 16;
+		break;
+	default:
+		break;
+	}
+	return strtol(sym->curr.val, NULL, base);
+}
+
+static void sym_validate_range(struct symbol *sym)
+{
+	struct property *prop;
+	int base, val, val2;
+	char str[64];
+
+	switch (sym->type) {
+	case S_INT:
+		base = 10;
+		break;
+	case S_HEX:
+		base = 16;
+		break;
+	default:
+		return;
+	}
+	prop = sym_get_range_prop(sym);
+	if (!prop)
+		return;
+	val = strtol(sym->curr.val, NULL, base);
+	val2 = sym_get_range_val(prop->expr->left.sym, base);
+	if (val >= val2) {
+		val2 = sym_get_range_val(prop->expr->right.sym, base);
+		if (val <= val2)
+			return;
+	}
+	if (sym->type == S_INT)
+		sprintf(str, "%d", val2);
+	else
+		sprintf(str, "0x%x", val2);
+	sym->curr.val = strdup(str);
+}
+
+static void sym_calc_visibility(struct symbol *sym)
+{
+	struct property *prop;
+	tristate tri;
+
+	/* any prompt visible? */
+	tri = no;
+	for_all_prompts(sym, prop) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		tri = E_OR(tri, prop->visible.tri);
+	}
+	if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
+		tri = yes;
+	if (sym->visible != tri) {
+		sym->visible = tri;
+		sym_set_changed(sym);
+	}
+	if (sym_is_choice_value(sym))
+		return;
+	tri = no;
+	if (sym->rev_dep.expr)
+		tri = expr_calc_value(sym->rev_dep.expr);
+	if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+		tri = yes;
+	if (sym->rev_dep.tri != tri) {
+		sym->rev_dep.tri = tri;
+		sym_set_changed(sym);
+	}
+}
+
+static struct symbol *sym_calc_choice(struct symbol *sym)
+{
+	struct symbol *def_sym;
+	struct property *prop;
+	struct expr *e;
+
+	/* is the user choice visible? */
+	def_sym = sym->user.val;
+	if (def_sym) {
+		sym_calc_visibility(def_sym);
+		if (def_sym->visible != no)
+			return def_sym;
+	}
+
+	/* any of the defaults visible? */
+	for_all_defaults(sym, prop) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		if (prop->visible.tri == no)
+			continue;
+		def_sym = prop_get_symbol(prop);
+		sym_calc_visibility(def_sym);
+		if (def_sym->visible != no)
+			return def_sym;
+	}
+
+	/* just get the first visible value */
+	prop = sym_get_choice_prop(sym);
+	for (e = prop->expr; e; e = e->left.expr) {
+		def_sym = e->right.sym;
+		sym_calc_visibility(def_sym);
+		if (def_sym->visible != no)
+			return def_sym;
+	}
+
+	/* no choice? reset tristate value */
+	sym->curr.tri = no;
+	return NULL;
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+	struct symbol_value newval, oldval;
+	struct property *prop;
+	struct expr *e;
+
+	if (!sym)
+		return;
+
+	if (sym->flags & SYMBOL_VALID)
+		return;
+	sym->flags |= SYMBOL_VALID;
+
+	oldval = sym->curr;
+
+	switch (sym->type) {
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		newval = symbol_empty.curr;
+		break;
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		newval = symbol_no.curr;
+		break;
+	default:
+		sym->curr.val = sym->name;
+		sym->curr.tri = no;
+		return;
+	}
+	if (!sym_is_choice_value(sym))
+		sym->flags &= ~SYMBOL_WRITE;
+
+	sym_calc_visibility(sym);
+
+	/* set default if recursively called */
+	sym->curr = newval;
+
+	switch (sym_get_type(sym)) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		if (sym_is_choice_value(sym) && sym->visible == yes) {
+			prop = sym_get_choice_prop(sym);
+			newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
+		} else if (E_OR(sym->visible, sym->rev_dep.tri) != no) {
+			sym->flags |= SYMBOL_WRITE;
+			if (sym_has_value(sym))
+				newval.tri = sym->user.tri;
+			else if (!sym_is_choice(sym)) {
+				prop = sym_get_default_prop(sym);
+				if (prop)
+					newval.tri = expr_calc_value(prop->expr);
+			}
+			newval.tri = E_OR(E_AND(newval.tri, sym->visible), sym->rev_dep.tri);
+		} else if (!sym_is_choice(sym)) {
+			prop = sym_get_default_prop(sym);
+			if (prop) {
+				sym->flags |= SYMBOL_WRITE;
+				newval.tri = expr_calc_value(prop->expr);
+			}
+		}
+		if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
+			newval.tri = yes;
+		break;
+	case S_STRING:
+	case S_HEX:
+	case S_INT:
+		if (sym->visible != no) {
+			sym->flags |= SYMBOL_WRITE;
+			if (sym_has_value(sym)) {
+				newval.val = sym->user.val;
+				break;
+			}
+		}
+		prop = sym_get_default_prop(sym);
+		if (prop) {
+			struct symbol *ds = prop_get_symbol(prop);
+			if (ds) {
+				sym->flags |= SYMBOL_WRITE;
+				sym_calc_value(ds);
+				newval.val = ds->curr.val;
+			}
+		}
+		break;
+	default:
+		;
+	}
+
+	sym->curr = newval;
+	if (sym_is_choice(sym) && newval.tri == yes)
+		sym->curr.val = sym_calc_choice(sym);
+	sym_validate_range(sym);
+
+	if (memcmp(&oldval, &sym->curr, sizeof(oldval)))
+		sym_set_changed(sym);
+	if (modules_sym == sym)
+		modules_val = modules_sym->curr.tri;
+
+	if (sym_is_choice(sym)) {
+		int flags = sym->flags & (SYMBOL_CHANGED | SYMBOL_WRITE);
+		prop = sym_get_choice_prop(sym);
+		for (e = prop->expr; e; e = e->left.expr) {
+			e->right.sym->flags |= flags;
+			if (flags & SYMBOL_CHANGED)
+				sym_set_changed(e->right.sym);
+		}
+	}
+}
+
+void sym_clear_all_valid(void)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, sym)
+		sym->flags &= ~SYMBOL_VALID;
+	sym_change_count++;
+	if (modules_sym)
+		sym_calc_value(modules_sym);
+}
+
+void sym_set_changed(struct symbol *sym)
+{
+	struct property *prop;
+
+	sym->flags |= SYMBOL_CHANGED;
+	for (prop = sym->prop; prop; prop = prop->next) {
+		if (prop->menu)
+			prop->menu->flags |= MENU_CHANGED;
+	}
+}
+
+void sym_set_all_changed(void)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, sym)
+		sym_set_changed(sym);
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+	int type = sym_get_type(sym);
+
+	if (sym->visible == no)
+		return false;
+
+	if (type != S_BOOLEAN && type != S_TRISTATE)
+		return false;
+
+	if (type == S_BOOLEAN && val == mod)
+		return false;
+	if (sym->visible <= sym->rev_dep.tri)
+		return false;
+	if (sym_is_choice_value(sym) && sym->visible == yes)
+		return val == yes;
+	return val >= sym->rev_dep.tri && val <= sym->visible;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+	tristate oldval = sym_get_tristate_value(sym);
+
+	if (oldval != val && !sym_tristate_within_range(sym, val))
+		return false;
+
+	if (sym->flags & SYMBOL_NEW) {
+		sym->flags &= ~SYMBOL_NEW;
+		sym_set_changed(sym);
+	}
+	/*
+	 * setting a choice value also resets the new flag of the choice
+	 * symbol and all other choice values.
+	 */
+	if (sym_is_choice_value(sym) && val == yes) {
+		struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+		struct property *prop;
+		struct expr *e;
+
+		cs->user.val = sym;
+		cs->flags &= ~SYMBOL_NEW;
+		prop = sym_get_choice_prop(cs);
+		for (e = prop->expr; e; e = e->left.expr) {
+			if (e->right.sym->visible != no)
+				e->right.sym->flags &= ~SYMBOL_NEW;
+		}
+	}
+
+	sym->user.tri = val;
+	if (oldval != val) {
+		sym_clear_all_valid();
+		if (sym == modules_sym)
+			sym_set_all_changed();
+	}
+
+	return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+	tristate oldval, newval;
+
+	oldval = newval = sym_get_tristate_value(sym);
+	do {
+		switch (newval) {
+		case no:
+			newval = mod;
+			break;
+		case mod:
+			newval = yes;
+			break;
+		case yes:
+			newval = no;
+			break;
+		}
+		if (sym_set_tristate_value(sym, newval))
+			break;
+	} while (oldval != newval);
+	return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+	signed char ch;
+
+	switch (sym->type) {
+	case S_STRING:
+		return true;
+	case S_INT:
+		ch = *str++;
+		if (ch == '-')
+			ch = *str++;
+		if (!isdigit(ch))
+			return false;
+		if (ch == '0' && *str != 0)
+			return false;
+		while ((ch = *str++)) {
+			if (!isdigit(ch))
+				return false;
+		}
+		return true;
+	case S_HEX:
+		if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+			str += 2;
+		ch = *str++;
+		do {
+			if (!isxdigit(ch))
+				return false;
+		} while ((ch = *str++));
+		return true;
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (str[0]) {
+		case 'y': case 'Y':
+		case 'm': case 'M':
+		case 'n': case 'N':
+			return true;
+		}
+		return false;
+	default:
+		return false;
+	}
+}
+
+bool sym_string_within_range(struct symbol *sym, const char *str)
+{
+	struct property *prop;
+	int val;
+
+	switch (sym->type) {
+	case S_STRING:
+		return sym_string_valid(sym, str);
+	case S_INT:
+		if (!sym_string_valid(sym, str))
+			return false;
+		prop = sym_get_range_prop(sym);
+		if (!prop)
+			return true;
+		val = strtol(str, NULL, 10);
+		return val >= sym_get_range_val(prop->expr->left.sym, 10) &&
+		       val <= sym_get_range_val(prop->expr->right.sym, 10);
+	case S_HEX:
+		if (!sym_string_valid(sym, str))
+			return false;
+		prop = sym_get_range_prop(sym);
+		if (!prop)
+			return true;
+		val = strtol(str, NULL, 16);
+		return val >= sym_get_range_val(prop->expr->left.sym, 16) &&
+		       val <= sym_get_range_val(prop->expr->right.sym, 16);
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (str[0]) {
+		case 'y': case 'Y':
+			return sym_tristate_within_range(sym, yes);
+		case 'm': case 'M':
+			return sym_tristate_within_range(sym, mod);
+		case 'n': case 'N':
+			return sym_tristate_within_range(sym, no);
+		}
+		return false;
+	default:
+		return false;
+	}
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+	const char *oldval;
+	char *val;
+	int size;
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (newval[0]) {
+		case 'y': case 'Y':
+			return sym_set_tristate_value(sym, yes);
+		case 'm': case 'M':
+			return sym_set_tristate_value(sym, mod);
+		case 'n': case 'N':
+			return sym_set_tristate_value(sym, no);
+		}
+		return false;
+	default:
+		;
+	}
+
+	if (!sym_string_within_range(sym, newval))
+		return false;
+
+	if (sym->flags & SYMBOL_NEW) {
+		sym->flags &= ~SYMBOL_NEW;
+		sym_set_changed(sym);
+	}
+
+	oldval = sym->user.val;
+	size = strlen(newval) + 1;
+	if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+		size += 2;
+		sym->user.val = val = malloc(size);
+		*val++ = '0';
+		*val++ = 'x';
+	} else if (!oldval || strcmp(oldval, newval))
+		sym->user.val = val = malloc(size);
+	else
+		return true;
+
+	strcpy(val, newval);
+	free((void *)oldval);
+	sym_clear_all_valid();
+
+	return true;
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+	tristate val;
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		val = sym_get_tristate_value(sym);
+		switch (val) {
+		case no:
+			return "n";
+		case mod:
+			return "m";
+		case yes:
+			return "y";
+		}
+		break;
+	default:
+		;
+	}
+	return (const char *)sym->curr.val;
+}
+
+bool sym_is_changable(struct symbol *sym)
+{
+	return sym->visible > sym->rev_dep.tri;
+}
+
+struct symbol *sym_lookup(const char *name, int isconst)
+{
+	struct symbol *symbol;
+	const char *ptr;
+	char *new_name;
+	int hash = 0;
+
+	if (name) {
+		if (name[0] && !name[1]) {
+			switch (name[0]) {
+			case 'y': return &symbol_yes;
+			case 'm': return &symbol_mod;
+			case 'n': return &symbol_no;
+			}
+		}
+		for (ptr = name; *ptr; ptr++)
+			hash += *ptr;
+		hash &= 0xff;
+
+		for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+			if (!strcmp(symbol->name, name)) {
+				if ((isconst && symbol->flags & SYMBOL_CONST) ||
+				    (!isconst && !(symbol->flags & SYMBOL_CONST)))
+					return symbol;
+			}
+		}
+		new_name = strdup(name);
+	} else {
+		new_name = NULL;
+		hash = 256;
+	}
+
+	symbol = malloc(sizeof(*symbol));
+	memset(symbol, 0, sizeof(*symbol));
+	symbol->name = new_name;
+	symbol->type = S_UNKNOWN;
+	symbol->flags = SYMBOL_NEW;
+	if (isconst)
+		symbol->flags |= SYMBOL_CONST;
+
+	symbol->next = symbol_hash[hash];
+	symbol_hash[hash] = symbol;
+
+	return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+	struct symbol *symbol = NULL;
+	const char *ptr;
+	int hash = 0;
+
+	if (!name)
+		return NULL;
+
+	if (name[0] && !name[1]) {
+		switch (name[0]) {
+		case 'y': return &symbol_yes;
+		case 'm': return &symbol_mod;
+		case 'n': return &symbol_no;
+		}
+	}
+	for (ptr = name; *ptr; ptr++)
+		hash += *ptr;
+	hash &= 0xff;
+
+	for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+		if (!strcmp(symbol->name, name) &&
+		    !(symbol->flags & SYMBOL_CONST))
+				break;
+	}
+
+	return symbol;
+}
+
+struct symbol **sym_re_search(const char *pattern)
+{
+	struct symbol *sym, **sym_arr = NULL;
+	int i, cnt, size;
+	regex_t re;
+
+	cnt = size = 0;
+	/* Skip if empty */
+	if (strlen(pattern) == 0)
+		return NULL;
+	if (regcomp(&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE))
+		return NULL;
+
+	for_all_symbols(i, sym) {
+		if (sym->flags & SYMBOL_CONST || !sym->name)
+			continue;
+		if (regexec(&re, sym->name, 0, NULL, 0))
+			continue;
+		if (cnt + 1 >= size) {
+			void *tmp = sym_arr;
+			size += 16;
+			sym_arr = realloc(sym_arr, size * sizeof(struct symbol *));
+			if (!sym_arr) {
+				free(tmp);
+				return NULL;
+			}
+		}
+		sym_arr[cnt++] = sym;
+	}
+	if (sym_arr)
+		sym_arr[cnt] = NULL;
+	regfree(&re);
+
+	return sym_arr;
+}
+
+
+struct symbol *sym_check_deps(struct symbol *sym);
+
+static struct symbol *sym_check_expr_deps(struct expr *e)
+{
+	struct symbol *sym;
+
+	if (!e)
+		return NULL;
+	switch (e->type) {
+	case E_OR:
+	case E_AND:
+		sym = sym_check_expr_deps(e->left.expr);
+		if (sym)
+			return sym;
+		return sym_check_expr_deps(e->right.expr);
+	case E_NOT:
+		return sym_check_expr_deps(e->left.expr);
+	case E_EQUAL:
+	case E_UNEQUAL:
+		sym = sym_check_deps(e->left.sym);
+		if (sym)
+			return sym;
+		return sym_check_deps(e->right.sym);
+	case E_SYMBOL:
+		return sym_check_deps(e->left.sym);
+	default:
+		break;
+	}
+	printf("Oops! How to check %d?\n", e->type);
+	return NULL;
+}
+
+struct symbol *sym_check_deps(struct symbol *sym)
+{
+	struct symbol *sym2;
+	struct property *prop;
+
+	if (sym->flags & SYMBOL_CHECK) {
+		printf("Warning! Found recursive dependency: %s", sym->name);
+		return sym;
+	}
+	if (sym->flags & SYMBOL_CHECKED)
+		return NULL;
+
+	sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+	sym2 = sym_check_expr_deps(sym->rev_dep.expr);
+	if (sym2)
+		goto out;
+
+	for (prop = sym->prop; prop; prop = prop->next) {
+		if (prop->type == P_CHOICE || prop->type == P_SELECT)
+			continue;
+		sym2 = sym_check_expr_deps(prop->visible.expr);
+		if (sym2)
+			goto out;
+		if (prop->type != P_DEFAULT || sym_is_choice(sym))
+			continue;
+		sym2 = sym_check_expr_deps(prop->expr);
+		if (sym2)
+			goto out;
+	}
+out:
+	if (sym2) {
+		printf(" %s", sym->name);
+		if (sym2 == sym) {
+			printf("\n");
+			sym2 = NULL;
+		}
+	}
+	sym->flags &= ~SYMBOL_CHECK;
+	return sym2;
+}
+
+struct property *prop_alloc(enum prop_type type, struct symbol *sym)
+{
+	struct property *prop;
+	struct property **propp;
+
+	prop = malloc(sizeof(*prop));
+	memset(prop, 0, sizeof(*prop));
+	prop->type = type;
+	prop->sym = sym;
+	prop->file = current_file;
+	prop->lineno = zconf_lineno();
+
+	/* append property to the prop list of symbol */
+	if (sym) {
+		for (propp = &sym->prop; *propp; propp = &(*propp)->next)
+			;
+		*propp = prop;
+	}
+
+	return prop;
+}
+
+struct symbol *prop_get_symbol(struct property *prop)
+{
+	if (prop->expr && (prop->expr->type == E_SYMBOL ||
+			   prop->expr->type == E_CHOICE))
+		return prop->expr->left.sym;
+	return NULL;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+	switch (type) {
+	case P_PROMPT:
+		return "prompt";
+	case P_COMMENT:
+		return "comment";
+	case P_MENU:
+		return "menu";
+	case P_DEFAULT:
+		return "default";
+	case P_CHOICE:
+		return "choice";
+	case P_SELECT:
+		return "select";
+	case P_RANGE:
+		return "range";
+	case P_UNKNOWN:
+		break;
+	}
+	return "unknown";
+}
diff --git a/busybox-1.19.3/scripts/kconfig/util.c b/busybox-1.19.3/scripts/kconfig/util.c
new file mode 100644
index 0000000..2630919
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/util.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
+ * Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
+ *
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <string.h>
+#include "lkc.h"
+
+/* file already present in list? If not add it */
+struct file *file_lookup(const char *name)
+{
+	struct file *file;
+
+	for (file = file_list; file; file = file->next) {
+		if (!strcmp(name, file->name))
+			return file;
+	}
+
+	file = malloc(sizeof(*file));
+	memset(file, 0, sizeof(*file));
+	file->name = strdup(name);
+	file->next = file_list;
+	file_list = file;
+	return file;
+}
+
+/* write a dependency file as used by kbuild to track dependencies */
+int file_write_dep(const char *name)
+{
+	struct file *file;
+	FILE *out;
+
+	if (!name)
+		name = ".kconfig.d";
+	out = fopen("..config.tmp", "w");
+	if (!out)
+		return 1;
+	fprintf(out, "deps_config := \\\n");
+	for (file = file_list; file; file = file->next) {
+		if (file->next)
+			fprintf(out, "\t%s \\\n", file->name);
+		else
+			fprintf(out, "\t%s\n", file->name);
+	}
+	fprintf(out,
+		"\n"
+		".config include/autoconf.h: $(deps_config)\n"
+		"\n"
+		"include/autoconf.h: .config\n" /* bbox */
+		"\n"
+		"$(deps_config):\n");
+	fclose(out);
+	rename("..config.tmp", name);
+	return 0;
+}
+
+
+/* Allocate initial growable string */
+struct gstr str_new(void)
+{
+	struct gstr gs;
+	gs.s = malloc(sizeof(char) * 64);
+	gs.len = 16;
+	strcpy(gs.s, "\0");
+	return gs;
+}
+
+/* Allocate and assign growable string */
+struct gstr str_assign(const char *s)
+{
+	struct gstr gs;
+	gs.s = strdup(s);
+	gs.len = strlen(s) + 1;
+	return gs;
+}
+
+/* Free storage for growable string */
+void str_free(struct gstr *gs)
+{
+	if (gs->s)
+		free(gs->s);
+	gs->s = NULL;
+	gs->len = 0;
+}
+
+/* Append to growable string */
+void str_append(struct gstr *gs, const char *s)
+{
+	size_t l = strlen(gs->s) + strlen(s) + 1;
+	if (l > gs->len) {
+		gs->s   = realloc(gs->s, l);
+		gs->len = l;
+	}
+	strcat(gs->s, s);
+}
+
+/* Append printf formatted string to growable string */
+void str_printf(struct gstr *gs, const char *fmt, ...)
+{
+	va_list ap;
+	char s[10000]; /* big enough... */
+	va_start(ap, fmt);
+	vsnprintf(s, sizeof(s), fmt, ap);
+	str_append(gs, s);
+	va_end(ap);
+}
+
+/* Retrieve value of growable string */
+const char *str_get(struct gstr *gs)
+{
+	return gs->s;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/zconf.gperf b/busybox-1.19.3/scripts/kconfig/zconf.gperf
new file mode 100644
index 0000000..b032206
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/zconf.gperf
@@ -0,0 +1,43 @@
+%language=ANSI-C
+%define hash-function-name kconf_id_hash
+%define lookup-function-name kconf_id_lookup
+%define string-pool-name kconf_id_strings
+%compare-strncmp
+%enum
+%pic
+%struct-type
+
+struct kconf_id;
+
+%%
+mainmenu,	T_MAINMENU,	TF_COMMAND
+menu,		T_MENU,		TF_COMMAND
+endmenu,	T_ENDMENU,	TF_COMMAND
+source,		T_SOURCE,	TF_COMMAND
+choice,		T_CHOICE,	TF_COMMAND
+endchoice,	T_ENDCHOICE,	TF_COMMAND
+comment,	T_COMMENT,	TF_COMMAND
+config,		T_CONFIG,	TF_COMMAND
+menuconfig,	T_MENUCONFIG,	TF_COMMAND
+help,		T_HELP,		TF_COMMAND
+if,		T_IF,		TF_COMMAND|TF_PARAM
+endif,		T_ENDIF,	TF_COMMAND
+depends,	T_DEPENDS,	TF_COMMAND
+requires,	T_REQUIRES,	TF_COMMAND
+optional,	T_OPTIONAL,	TF_COMMAND
+default,	T_DEFAULT,	TF_COMMAND, S_UNKNOWN
+prompt,		T_PROMPT,	TF_COMMAND
+tristate,	T_TYPE,		TF_COMMAND, S_TRISTATE
+def_tristate,	T_DEFAULT,	TF_COMMAND, S_TRISTATE
+bool,		T_TYPE,		TF_COMMAND, S_BOOLEAN
+boolean,	T_TYPE,		TF_COMMAND, S_BOOLEAN
+def_bool,	T_DEFAULT,	TF_COMMAND, S_BOOLEAN
+def_boolean,	T_DEFAULT,	TF_COMMAND, S_BOOLEAN
+int,		T_TYPE,		TF_COMMAND, S_INT
+hex,		T_TYPE,		TF_COMMAND, S_HEX
+string,		T_TYPE,		TF_COMMAND, S_STRING
+select,		T_SELECT,	TF_COMMAND
+enable,		T_SELECT,	TF_COMMAND
+range,		T_RANGE,	TF_COMMAND
+on,		T_ON,		TF_PARAM
+%%
diff --git a/busybox-1.19.3/scripts/kconfig/zconf.hash.c_shipped b/busybox-1.19.3/scripts/kconfig/zconf.hash.c_shipped
new file mode 100644
index 0000000..d39cf18
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/zconf.hash.c_shipped
@@ -0,0 +1,230 @@
+/* ANSI-C code produced by gperf version 3.0.1 */
+/* Command-line: gperf  */
+/* Computed positions: -k'1,3' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646.  */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+struct kconf_id;
+/* maximum key range = 45, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+kconf_id_hash (register const char *str, register unsigned int len)
+{
+  static unsigned char asso_values[] =
+    {
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 25, 10, 15,
+       0,  0,  5, 47,  0,  0, 47, 47,  0, 10,
+       0, 20, 20, 20,  5,  0,  0, 20, 47, 47,
+      20, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
+      47, 47, 47, 47, 47, 47
+    };
+  register int hval = len;
+
+  switch (hval)
+    {
+      default:
+        hval += asso_values[(unsigned char)str[2]];
+      /*FALLTHROUGH*/
+      case 2:
+      case 1:
+        hval += asso_values[(unsigned char)str[0]];
+        break;
+    }
+  return hval;
+}
+
+struct kconf_id_strings_t
+  {
+    char kconf_id_strings_str2[sizeof("if")];
+    char kconf_id_strings_str3[sizeof("int")];
+    char kconf_id_strings_str4[sizeof("help")];
+    char kconf_id_strings_str5[sizeof("endif")];
+    char kconf_id_strings_str6[sizeof("select")];
+    char kconf_id_strings_str7[sizeof("endmenu")];
+    char kconf_id_strings_str8[sizeof("tristate")];
+    char kconf_id_strings_str9[sizeof("endchoice")];
+    char kconf_id_strings_str10[sizeof("range")];
+    char kconf_id_strings_str11[sizeof("string")];
+    char kconf_id_strings_str12[sizeof("default")];
+    char kconf_id_strings_str13[sizeof("def_bool")];
+    char kconf_id_strings_str14[sizeof("menu")];
+    char kconf_id_strings_str16[sizeof("def_boolean")];
+    char kconf_id_strings_str17[sizeof("def_tristate")];
+    char kconf_id_strings_str18[sizeof("mainmenu")];
+    char kconf_id_strings_str20[sizeof("menuconfig")];
+    char kconf_id_strings_str21[sizeof("config")];
+    char kconf_id_strings_str22[sizeof("on")];
+    char kconf_id_strings_str23[sizeof("hex")];
+    char kconf_id_strings_str26[sizeof("source")];
+    char kconf_id_strings_str27[sizeof("depends")];
+    char kconf_id_strings_str28[sizeof("optional")];
+    char kconf_id_strings_str31[sizeof("enable")];
+    char kconf_id_strings_str32[sizeof("comment")];
+    char kconf_id_strings_str33[sizeof("requires")];
+    char kconf_id_strings_str34[sizeof("bool")];
+    char kconf_id_strings_str37[sizeof("boolean")];
+    char kconf_id_strings_str41[sizeof("choice")];
+    char kconf_id_strings_str46[sizeof("prompt")];
+  };
+static struct kconf_id_strings_t kconf_id_strings_contents =
+  {
+    "if",
+    "int",
+    "help",
+    "endif",
+    "select",
+    "endmenu",
+    "tristate",
+    "endchoice",
+    "range",
+    "string",
+    "default",
+    "def_bool",
+    "menu",
+    "def_boolean",
+    "def_tristate",
+    "mainmenu",
+    "menuconfig",
+    "config",
+    "on",
+    "hex",
+    "source",
+    "depends",
+    "optional",
+    "enable",
+    "comment",
+    "requires",
+    "bool",
+    "boolean",
+    "choice",
+    "prompt"
+  };
+#define kconf_id_strings ((const char *) &kconf_id_strings_contents)
+#ifdef __GNUC__
+__inline
+#endif
+struct kconf_id *
+kconf_id_lookup (register const char *str, register unsigned int len)
+{
+  enum
+    {
+      TOTAL_KEYWORDS = 30,
+      MIN_WORD_LENGTH = 2,
+      MAX_WORD_LENGTH = 12,
+      MIN_HASH_VALUE = 2,
+      MAX_HASH_VALUE = 46
+    };
+
+  static struct kconf_id wordlist[] =
+    {
+      {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2,		T_IF,		TF_COMMAND|TF_PARAM},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3,		T_TYPE,		TF_COMMAND, S_INT},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str4,		T_HELP,		TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5,		T_ENDIF,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6,		T_SELECT,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7,	T_ENDMENU,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8,	T_TYPE,		TF_COMMAND, S_TRISTATE},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9,	T_ENDCHOICE,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10,		T_RANGE,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11,		T_TYPE,		TF_COMMAND, S_STRING},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12,	T_DEFAULT,	TF_COMMAND, S_UNKNOWN},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13,	T_DEFAULT,	TF_COMMAND, S_BOOLEAN},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14,		T_MENU,		TF_COMMAND},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16,	T_DEFAULT,	TF_COMMAND, S_BOOLEAN},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17,	T_DEFAULT,	TF_COMMAND, S_TRISTATE},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18,	T_MAINMENU,	TF_COMMAND},
+      {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20,	T_MENUCONFIG,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21,		T_CONFIG,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22,		T_ON,		TF_PARAM},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23,		T_TYPE,		TF_COMMAND, S_HEX},
+      {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26,		T_SOURCE,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27,	T_DEPENDS,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28,	T_OPTIONAL,	TF_COMMAND},
+      {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31,		T_SELECT,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32,	T_COMMENT,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33,	T_REQUIRES,	TF_COMMAND},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34,		T_TYPE,		TF_COMMAND, S_BOOLEAN},
+      {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37,	T_TYPE,		TF_COMMAND, S_BOOLEAN},
+      {-1}, {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41,		T_CHOICE,	TF_COMMAND},
+      {-1}, {-1}, {-1}, {-1},
+      {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46,		T_PROMPT,	TF_COMMAND}
+    };
+
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register int key = kconf_id_hash (str, len);
+
+      if (key <= MAX_HASH_VALUE && key >= 0)
+        {
+          register int o = wordlist[key].name;
+          if (o >= 0)
+            {
+              register const char *s = o + kconf_id_strings;
+
+              if (*str == *s && !strncmp (str + 1, s + 1, len - 1) && s[len] == '\0')
+                return &wordlist[key];
+            }
+        }
+    }
+  return 0;
+}
diff --git a/busybox-1.19.3/scripts/kconfig/zconf.l b/busybox-1.19.3/scripts/kconfig/zconf.l
new file mode 100644
index 0000000..6a58b80
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/zconf.l
@@ -0,0 +1,355 @@
+%option backup nostdinit noyywrap never-interactive full ecs
+%option 8bit backup nodefault perf-report perf-report
+%x COMMAND HELP STRING PARAM
+%{
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define START_STRSIZE	16
+
+static struct {
+	struct file *file;
+	int lineno;
+} current_pos;
+
+static char *text;
+static int text_size, text_asize;
+
+struct buffer {
+        struct buffer *parent;
+        YY_BUFFER_STATE state;
+};
+
+struct buffer *current_buf;
+
+static int last_ts, first_ts;
+
+static void zconf_endhelp(void);
+static void zconf_endfile(void);
+
+void new_string(void)
+{
+	text = malloc(START_STRSIZE);
+	text_asize = START_STRSIZE;
+	text_size = 0;
+	*text = 0;
+}
+
+void append_string(const char *str, int size)
+{
+	int new_size = text_size + size + 1;
+	if (size > 70) {
+	        fprintf (stderr, "%s:%d error: Overlong line\n",
+			 current_file->name, current_file->lineno);
+	}
+	if (new_size > text_asize) {
+		new_size += START_STRSIZE - 1;
+		new_size &= -START_STRSIZE;
+		text = realloc(text, new_size);
+		text_asize = new_size;
+	}
+	memcpy(text + text_size, str, size);
+	text_size += size;
+	text[text_size] = 0;
+}
+
+void alloc_string(const char *str, int size)
+{
+	text = malloc(size + 1);
+	memcpy(text, str, size);
+	text[size] = 0;
+}
+%}
+
+ws	[ \n\t]
+n	[A-Za-z0-9_]
+
+%%
+	int str = 0;
+	int ts, i;
+
+[ \t]*#.*\n	|
+[ \t]*\n	{
+	current_file->lineno++;
+	return T_EOL;
+}
+[ \t]*#.*
+
+
+[ \t]+	{
+	BEGIN(COMMAND);
+}
+
+.	{
+	unput(yytext[0]);
+	BEGIN(COMMAND);
+}
+
+
+<COMMAND>{
+	{n}+	{
+		struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
+		BEGIN(PARAM);
+		current_pos.file = current_file;
+		current_pos.lineno = current_file->lineno;
+		if (id && id->flags & TF_COMMAND) {
+			zconflval.id = id;
+			return id->token;
+		}
+		alloc_string(yytext, yyleng);
+		zconflval.string = text;
+		return T_WORD;
+	}
+	.
+	\n	{
+		BEGIN(INITIAL);
+		current_file->lineno++;
+		return T_EOL;
+	}
+}
+
+<PARAM>{
+	"&&"	return T_AND;
+	"||"	return T_OR;
+	"("	return T_OPEN_PAREN;
+	")"	return T_CLOSE_PAREN;
+	"!"	return T_NOT;
+	"="	return T_EQUAL;
+	"!="	return T_UNEQUAL;
+	\"|\'	{
+		str = yytext[0];
+		new_string();
+		BEGIN(STRING);
+	}
+	\n	BEGIN(INITIAL); current_file->lineno++; return T_EOL;
+	---	/* ignore */
+	({n}|[-/.])+	{
+		struct kconf_id *id = kconf_id_lookup(yytext, yyleng);
+		if (id && id->flags & TF_PARAM) {
+			zconflval.id = id;
+			return id->token;
+		}
+		alloc_string(yytext, yyleng);
+		zconflval.string = text;
+		return T_WORD;
+	}
+	#.*	/* comment */
+	\\\n	current_file->lineno++;
+	.
+	<<EOF>> {
+		BEGIN(INITIAL);
+	}
+}
+
+<STRING>{
+	[^'"\\\n]+/\n	{
+		append_string(yytext, yyleng);
+		zconflval.string = text;
+		return T_WORD_QUOTE;
+	}
+	[^'"\\\n]+	{
+		append_string(yytext, yyleng);
+	}
+	\\.?/\n	{
+		append_string(yytext + 1, yyleng - 1);
+		zconflval.string = text;
+		return T_WORD_QUOTE;
+	}
+	\\.?	{
+		append_string(yytext + 1, yyleng - 1);
+	}
+	\'|\"	{
+		if (str == yytext[0]) {
+			BEGIN(PARAM);
+			zconflval.string = text;
+			return T_WORD_QUOTE;
+		} else
+			append_string(yytext, 1);
+	}
+	\n	{
+		printf("%s:%d:warning: multi-line strings not supported\n", zconf_curname(), zconf_lineno());
+		current_file->lineno++;
+		BEGIN(INITIAL);
+		return T_EOL;
+	}
+	<<EOF>>	{
+		BEGIN(INITIAL);
+	}
+}
+
+<HELP>{
+	[ \t]+	{
+		ts = 0;
+		for (i = 0; i < yyleng; i++) {
+			if (yytext[i] == '\t')
+				ts = (ts & ~7) + 8;
+			else
+				ts++;
+		}
+		last_ts = ts;
+		if (first_ts) {
+			if (ts < first_ts) {
+				zconf_endhelp();
+				return T_HELPTEXT;
+			}
+			ts -= first_ts;
+			while (ts > 8) {
+				append_string("        ", 8);
+				ts -= 8;
+			}
+			append_string("        ", ts);
+		}
+	}
+	[ \t]*\n/[^ \t\n] {
+		current_file->lineno++;
+		zconf_endhelp();
+		return T_HELPTEXT;
+	}
+	[ \t]*\n	{
+		current_file->lineno++;
+		append_string("\n", 1);
+	}
+	[^ \t\n].* {
+		append_string(yytext, yyleng);
+		if (!first_ts)
+			first_ts = last_ts;
+	}
+	<<EOF>>	{
+		zconf_endhelp();
+		return T_HELPTEXT;
+	}
+}
+
+<<EOF>>	{
+	if (current_file) {
+		zconf_endfile();
+		return T_EOL;
+	}
+	fclose(yyin);
+	yyterminate();
+}
+
+%%
+void zconf_starthelp(void)
+{
+	new_string();
+	last_ts = first_ts = 0;
+	BEGIN(HELP);
+}
+
+static void zconf_endhelp(void)
+{
+	zconflval.string = text;
+	BEGIN(INITIAL);
+}
+
+
+/*
+ * Try to open specified file with following names:
+ * ./name
+ * $(srctree)/name
+ * The latter is used when srctree is separate from objtree
+ * when compiling the kernel.
+ * Return NULL if file is not found.
+ */
+FILE *zconf_fopen(const char *name)
+{
+	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");
+		}
+	}
+	return f;
+}
+
+void zconf_initscan(const char *name)
+{
+	yyin = zconf_fopen(name);
+	if (!yyin) {
+		printf("can't find file %s\n", name);
+		exit(1);
+	}
+
+	current_buf = malloc(sizeof(*current_buf));
+	memset(current_buf, 0, sizeof(*current_buf));
+
+	current_file = file_lookup(name);
+	current_file->lineno = 1;
+	current_file->flags = FILE_BUSY;
+}
+
+void zconf_nextfile(const char *name)
+{
+	struct file *file = file_lookup(name);
+	struct buffer *buf = malloc(sizeof(*buf));
+	memset(buf, 0, sizeof(*buf));
+
+	current_buf->state = YY_CURRENT_BUFFER;
+	yyin = zconf_fopen(name);
+	if (!yyin) {
+		printf("%s:%d: can't open file \"%s\"\n", zconf_curname(), zconf_lineno(), name);
+		exit(1);
+	}
+	yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+	buf->parent = current_buf;
+	current_buf = buf;
+
+	if (file->flags & FILE_BUSY) {
+		printf("recursive scan (%s)?\n", name);
+		exit(1);
+	}
+	if (file->flags & FILE_SCANNED) {
+		printf("file %s already scanned?\n", name);
+		exit(1);
+	}
+	file->flags |= FILE_BUSY;
+	file->lineno = 1;
+	file->parent = current_file;
+	current_file = file;
+}
+
+static void zconf_endfile(void)
+{
+	struct buffer *parent;
+
+	current_file->flags |= FILE_SCANNED;
+	current_file->flags &= ~FILE_BUSY;
+	current_file = current_file->parent;
+
+	parent = current_buf->parent;
+	if (parent) {
+		fclose(yyin);
+		yy_delete_buffer(YY_CURRENT_BUFFER);
+		yy_switch_to_buffer(parent->state);
+	}
+	free(current_buf);
+	current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+	return current_pos.lineno;
+}
+
+char *zconf_curname(void)
+{
+	return current_pos.file ? current_pos.file->name : "<none>";
+}
diff --git a/busybox-1.19.3/scripts/kconfig/zconf.tab.c_shipped b/busybox-1.19.3/scripts/kconfig/zconf.tab.c_shipped
new file mode 100644
index 0000000..a27d256
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/zconf.tab.c_shipped
@@ -0,0 +1,2171 @@
+/* A Bison parser, made by GNU Bison 2.0.  */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+   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, or (at your option)
+   any later version.
+
+   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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* Written by Richard Stallman by simplifying the original so called
+   ``semantic'' parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names.  */
+#define yyparse zconfparse
+#define yylex   zconflex
+#define yyerror zconferror
+#define yylval  zconflval
+#define yychar  zconfchar
+#define yydebug zconfdebug
+#define yynerrs zconfnerrs
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     T_MAINMENU = 258,
+     T_MENU = 259,
+     T_ENDMENU = 260,
+     T_SOURCE = 261,
+     T_CHOICE = 262,
+     T_ENDCHOICE = 263,
+     T_COMMENT = 264,
+     T_CONFIG = 265,
+     T_MENUCONFIG = 266,
+     T_HELP = 267,
+     T_HELPTEXT = 268,
+     T_IF = 269,
+     T_ENDIF = 270,
+     T_DEPENDS = 271,
+     T_REQUIRES = 272,
+     T_OPTIONAL = 273,
+     T_PROMPT = 274,
+     T_TYPE = 275,
+     T_DEFAULT = 276,
+     T_SELECT = 277,
+     T_RANGE = 278,
+     T_ON = 279,
+     T_WORD = 280,
+     T_WORD_QUOTE = 281,
+     T_UNEQUAL = 282,
+     T_CLOSE_PAREN = 283,
+     T_OPEN_PAREN = 284,
+     T_EOL = 285,
+     T_OR = 286,
+     T_AND = 287,
+     T_EQUAL = 288,
+     T_NOT = 289
+   };
+#endif
+#define T_MAINMENU 258
+#define T_MENU 259
+#define T_ENDMENU 260
+#define T_SOURCE 261
+#define T_CHOICE 262
+#define T_ENDCHOICE 263
+#define T_COMMENT 264
+#define T_CONFIG 265
+#define T_MENUCONFIG 266
+#define T_HELP 267
+#define T_HELPTEXT 268
+#define T_IF 269
+#define T_ENDIF 270
+#define T_DEPENDS 271
+#define T_REQUIRES 272
+#define T_OPTIONAL 273
+#define T_PROMPT 274
+#define T_TYPE 275
+#define T_DEFAULT 276
+#define T_SELECT 277
+#define T_RANGE 278
+#define T_ON 279
+#define T_WORD 280
+#define T_WORD_QUOTE 281
+#define T_UNEQUAL 282
+#define T_CLOSE_PAREN 283
+#define T_OPEN_PAREN 284
+#define T_EOL 285
+#define T_OR 286
+#define T_AND 287
+#define T_EQUAL 288
+#define T_NOT 289
+
+
+
+
+/* Copy the first part of user declarations.  */
+
+
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#include "zconf.hash.c"
+
+#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
+
+#define PRINTD		0x0001
+#define DEBUG_PARSE	0x0002
+
+int cdebug = PRINTD;
+
+extern int zconflex(void);
+static void zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static void zconferror(const char *err);
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+static struct menu *current_menu, *current_entry;
+
+#define YYDEBUG 0
+#if YYDEBUG
+#define YYERROR_VERBOSE
+#endif
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+
+typedef union YYSTYPE {
+	char *string;
+	struct file *file;
+	struct symbol *symbol;
+	struct expr *expr;
+	struct menu *menu;
+	struct kconf_id *id;
+} YYSTYPE;
+/* Line 190 of yacc.c.  */
+
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 213 of yacc.c.  */
+
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+#  define YYFREE free
+# endif
+# ifndef YYMALLOC
+#  define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning. */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+#  if defined (__STDC__) || defined (__cplusplus)
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   define YYSIZE_T size_t
+#  endif
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+     && (! defined (__cplusplus) \
+	 || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  short int yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (short int) + sizeof (YYSTYPE))			\
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined (__GNUC__) && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  register YYSIZE_T yyi;		\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (0)
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+   typedef signed char yysigned_char;
+#else
+   typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL  3
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   264
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS  35
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS  42
+/* YYNRULES -- Number of rules. */
+#define YYNRULES  104
+/* YYNRULES -- Number of states. */
+#define YYNSTATES  175
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   289
+
+#define YYTRANSLATE(YYX) 						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const unsigned char yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const unsigned short int yyprhs[] =
+{
+       0,     0,     3,     5,     6,     9,    12,    15,    20,    23,
+      28,    33,    37,    39,    41,    43,    45,    47,    49,    51,
+      53,    55,    57,    59,    61,    63,    67,    70,    74,    77,
+      81,    84,    85,    88,    91,    94,    97,   100,   104,   109,
+     114,   119,   125,   128,   131,   133,   137,   138,   141,   144,
+     147,   150,   153,   158,   162,   165,   170,   171,   174,   178,
+     180,   184,   185,   188,   191,   194,   198,   201,   203,   207,
+     208,   211,   214,   217,   221,   225,   228,   231,   234,   235,
+     238,   241,   244,   249,   253,   257,   258,   261,   263,   265,
+     268,   271,   274,   276,   279,   280,   283,   285,   289,   293,
+     297,   300,   304,   308,   310
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+      36,     0,    -1,    37,    -1,    -1,    37,    39,    -1,    37,
+      50,    -1,    37,    61,    -1,    37,     3,    71,    73,    -1,
+      37,    72,    -1,    37,    25,     1,    30,    -1,    37,    38,
+       1,    30,    -1,    37,     1,    30,    -1,    16,    -1,    19,
+      -1,    20,    -1,    22,    -1,    18,    -1,    23,    -1,    21,
+      -1,    30,    -1,    56,    -1,    65,    -1,    42,    -1,    44,
+      -1,    63,    -1,    25,     1,    30,    -1,     1,    30,    -1,
+      10,    25,    30,    -1,    41,    45,    -1,    11,    25,    30,
+      -1,    43,    45,    -1,    -1,    45,    46,    -1,    45,    69,
+      -1,    45,    67,    -1,    45,    40,    -1,    45,    30,    -1,
+      20,    70,    30,    -1,    19,    71,    74,    30,    -1,    21,
+      75,    74,    30,    -1,    22,    25,    74,    30,    -1,    23,
+      76,    76,    74,    30,    -1,     7,    30,    -1,    47,    51,
+      -1,    72,    -1,    48,    53,    49,    -1,    -1,    51,    52,
+      -1,    51,    69,    -1,    51,    67,    -1,    51,    30,    -1,
+      51,    40,    -1,    19,    71,    74,    30,    -1,    20,    70,
+      30,    -1,    18,    30,    -1,    21,    25,    74,    30,    -1,
+      -1,    53,    39,    -1,    14,    75,    73,    -1,    72,    -1,
+      54,    57,    55,    -1,    -1,    57,    39,    -1,    57,    61,
+      -1,    57,    50,    -1,     4,    71,    30,    -1,    58,    68,
+      -1,    72,    -1,    59,    62,    60,    -1,    -1,    62,    39,
+      -1,    62,    61,    -1,    62,    50,    -1,     6,    71,    30,
+      -1,     9,    71,    30,    -1,    64,    68,    -1,    12,    30,
+      -1,    66,    13,    -1,    -1,    68,    69,    -1,    68,    30,
+      -1,    68,    40,    -1,    16,    24,    75,    30,    -1,    16,
+      75,    30,    -1,    17,    75,    30,    -1,    -1,    71,    74,
+      -1,    25,    -1,    26,    -1,     5,    30,    -1,     8,    30,
+      -1,    15,    30,    -1,    30,    -1,    73,    30,    -1,    -1,
+      14,    75,    -1,    76,    -1,    76,    33,    76,    -1,    76,
+      27,    76,    -1,    29,    75,    28,    -1,    34,    75,    -1,
+      75,    31,    75,    -1,    75,    32,    75,    -1,    25,    -1,
+      26,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const unsigned short int yyrline[] =
+{
+       0,   103,   103,   105,   107,   108,   109,   110,   111,   112,
+     113,   117,   121,   121,   121,   121,   121,   121,   121,   125,
+     126,   127,   128,   129,   130,   134,   135,   141,   149,   155,
+     163,   173,   175,   176,   177,   178,   179,   182,   190,   196,
+     206,   212,   220,   229,   234,   242,   245,   247,   248,   249,
+     250,   251,   254,   260,   271,   277,   287,   289,   294,   302,
+     310,   313,   315,   316,   317,   322,   329,   334,   342,   345,
+     347,   348,   349,   352,   360,   367,   374,   380,   387,   389,
+     390,   391,   394,   399,   404,   412,   414,   419,   420,   423,
+     424,   425,   429,   430,   433,   434,   437,   438,   439,   440,
+     441,   442,   443,   446,   447
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "T_MAINMENU", "T_MENU", "T_ENDMENU",
+  "T_SOURCE", "T_CHOICE", "T_ENDCHOICE", "T_COMMENT", "T_CONFIG",
+  "T_MENUCONFIG", "T_HELP", "T_HELPTEXT", "T_IF", "T_ENDIF", "T_DEPENDS",
+  "T_REQUIRES", "T_OPTIONAL", "T_PROMPT", "T_TYPE", "T_DEFAULT",
+  "T_SELECT", "T_RANGE", "T_ON", "T_WORD", "T_WORD_QUOTE", "T_UNEQUAL",
+  "T_CLOSE_PAREN", "T_OPEN_PAREN", "T_EOL", "T_OR", "T_AND", "T_EQUAL",
+  "T_NOT", "$accept", "input", "stmt_list", "option_name", "common_stmt",
+  "option_error", "config_entry_start", "config_stmt",
+  "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
+  "config_option", "choice", "choice_entry", "choice_end", "choice_stmt",
+  "choice_option_list", "choice_option", "choice_block", "if_entry",
+  "if_end", "if_stmt", "if_block", "menu", "menu_entry", "menu_end",
+  "menu_stmt", "menu_block", "source_stmt", "comment", "comment_stmt",
+  "help_start", "help", "depends_list", "depends", "prompt_stmt_opt",
+  "prompt", "end", "nl", "if_expr", "expr", "symbol", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const unsigned short int yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const unsigned char yyr1[] =
+{
+       0,    35,    36,    37,    37,    37,    37,    37,    37,    37,
+      37,    37,    38,    38,    38,    38,    38,    38,    38,    39,
+      39,    39,    39,    39,    39,    40,    40,    41,    42,    43,
+      44,    45,    45,    45,    45,    45,    45,    46,    46,    46,
+      46,    46,    47,    48,    49,    50,    51,    51,    51,    51,
+      51,    51,    52,    52,    52,    52,    53,    53,    54,    55,
+      56,    57,    57,    57,    57,    58,    59,    60,    61,    62,
+      62,    62,    62,    63,    64,    65,    66,    67,    68,    68,
+      68,    68,    69,    69,    69,    70,    70,    71,    71,    72,
+      72,    72,    73,    73,    74,    74,    75,    75,    75,    75,
+      75,    75,    75,    76,    76
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const unsigned char yyr2[] =
+{
+       0,     2,     1,     0,     2,     2,     2,     4,     2,     4,
+       4,     3,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     3,     2,     3,     2,     3,
+       2,     0,     2,     2,     2,     2,     2,     3,     4,     4,
+       4,     5,     2,     2,     1,     3,     0,     2,     2,     2,
+       2,     2,     4,     3,     2,     4,     0,     2,     3,     1,
+       3,     0,     2,     2,     2,     3,     2,     1,     3,     0,
+       2,     2,     2,     3,     3,     2,     2,     2,     0,     2,
+       2,     2,     4,     3,     3,     0,     2,     1,     1,     2,
+       2,     2,     1,     2,     0,     2,     1,     3,     3,     3,
+       2,     3,     3,     1,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const unsigned char yydefact[] =
+{
+       3,     0,     0,     1,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,    12,    16,    13,    14,
+      18,    15,    17,     0,    19,     0,     4,    31,    22,    31,
+      23,    46,    56,     5,    61,    20,    78,    69,     6,    24,
+      78,    21,     8,    11,    87,    88,     0,     0,    89,     0,
+      42,    90,     0,     0,     0,   103,   104,     0,     0,     0,
+      96,    91,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,    92,     7,    65,    73,    74,    27,    29,     0,
+     100,     0,     0,    58,     0,     0,     9,    10,     0,     0,
+       0,     0,     0,    85,     0,     0,     0,     0,    36,    35,
+      32,     0,    34,    33,     0,     0,    85,     0,    50,    51,
+      47,    49,    48,    57,    45,    44,    62,    64,    60,    63,
+      59,    80,    81,    79,    70,    72,    68,    71,    67,    93,
+      99,   101,   102,    98,    97,    26,    76,     0,     0,     0,
+      94,     0,    94,    94,    94,     0,     0,    77,    54,    94,
+       0,    94,     0,    83,    84,     0,     0,    37,    86,     0,
+       0,    94,    25,     0,    53,     0,    82,    95,    38,    39,
+      40,     0,    52,    55,    41
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const short int yydefgoto[] =
+{
+      -1,     1,     2,    25,    26,    99,    27,    28,    29,    30,
+      64,   100,    31,    32,   114,    33,    66,   110,    67,    34,
+     118,    35,    68,    36,    37,   126,    38,    70,    39,    40,
+      41,   101,   102,    69,   103,   141,   142,    42,    73,   156,
+      59,    60
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -78
+static const short int yypact[] =
+{
+     -78,     2,   159,   -78,   -21,     0,     0,   -12,     0,     1,
+       4,     0,    27,    38,    60,    58,   -78,   -78,   -78,   -78,
+     -78,   -78,   -78,   100,   -78,   104,   -78,   -78,   -78,   -78,
+     -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,
+     -78,   -78,   -78,   -78,   -78,   -78,    86,   113,   -78,   114,
+     -78,   -78,   125,   127,   128,   -78,   -78,    60,    60,   210,
+      65,   -78,   141,   142,    39,   103,   182,   200,     6,    66,
+       6,   131,   -78,   146,   -78,   -78,   -78,   -78,   -78,   196,
+     -78,    60,    60,   146,    40,    40,   -78,   -78,   155,   156,
+      -2,    60,     0,     0,    60,   105,    40,   194,   -78,   -78,
+     -78,   206,   -78,   -78,   183,     0,     0,   195,   -78,   -78,
+     -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,
+     -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,   -78,
+     -78,   197,   -78,   -78,   -78,   -78,   -78,    60,   213,   216,
+     212,   203,   212,   190,   212,    40,   208,   -78,   -78,   212,
+     222,   212,   219,   -78,   -78,    60,   223,   -78,   -78,   224,
+     225,   212,   -78,   226,   -78,   227,   -78,    47,   -78,   -78,
+     -78,   228,   -78,   -78,   -78
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const short int yypgoto[] =
+{
+     -78,   -78,   -78,   -78,   164,   -36,   -78,   -78,   -78,   -78,
+     230,   -78,   -78,   -78,   -78,    29,   -78,   -78,   -78,   -78,
+     -78,   -78,   -78,   -78,   -78,   -78,    59,   -78,   -78,   -78,
+     -78,   -78,   198,   220,    24,   157,    -5,   169,   202,    74,
+     -53,   -77
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -76
+static const short int yytable[] =
+{
+      46,    47,     3,    49,    79,    80,    52,   133,   134,    43,
+       6,     7,     8,     9,    10,    11,    12,    13,    48,   145,
+      14,    15,   137,    55,    56,    44,    45,    57,   131,   132,
+     109,    50,    58,   122,    51,   122,    24,   138,   139,   -28,
+      88,   143,   -28,   -28,   -28,   -28,   -28,   -28,   -28,   -28,
+     -28,    89,    53,   -28,   -28,    90,    91,   -28,    92,    93,
+      94,    95,    96,    54,    97,    55,    56,    88,   161,    98,
+     -66,   -66,   -66,   -66,   -66,   -66,   -66,   -66,    81,    82,
+     -66,   -66,    90,    91,   152,    55,    56,   140,    61,    57,
+     112,    97,    84,   123,    58,   123,   121,   117,    85,   125,
+     149,    62,   167,   -30,    88,    63,   -30,   -30,   -30,   -30,
+     -30,   -30,   -30,   -30,   -30,    89,    72,   -30,   -30,    90,
+      91,   -30,    92,    93,    94,    95,    96,   119,    97,   127,
+     144,   -75,    88,    98,   -75,   -75,   -75,   -75,   -75,   -75,
+     -75,   -75,   -75,    74,    75,   -75,   -75,    90,    91,   -75,
+     -75,   -75,   -75,   -75,   -75,    76,    97,    77,    78,    -2,
+       4,   121,     5,     6,     7,     8,     9,    10,    11,    12,
+      13,    86,    87,    14,    15,    16,   129,    17,    18,    19,
+      20,    21,    22,    88,    23,   135,   136,   -43,   -43,    24,
+     -43,   -43,   -43,   -43,    89,   146,   -43,   -43,    90,    91,
+     104,   105,   106,   107,   155,     7,     8,    97,    10,    11,
+      12,    13,   108,   148,    14,    15,   158,   159,   160,   147,
+     151,    81,    82,   163,   130,   165,   155,    81,    82,    82,
+      24,   113,   116,   157,   124,   171,   115,   120,   162,   128,
+      72,    81,    82,   153,    81,    82,   154,    81,    82,   166,
+      81,    82,   164,   168,   169,   170,   172,   173,   174,    65,
+      71,    83,     0,   150,   111
+};
+
+static const short int yycheck[] =
+{
+       5,     6,     0,     8,    57,    58,    11,    84,    85,    30,
+       4,     5,     6,     7,     8,     9,    10,    11,    30,    96,
+      14,    15,    24,    25,    26,    25,    26,    29,    81,    82,
+      66,    30,    34,    69,    30,    71,    30,    90,    91,     0,
+       1,    94,     3,     4,     5,     6,     7,     8,     9,    10,
+      11,    12,    25,    14,    15,    16,    17,    18,    19,    20,
+      21,    22,    23,    25,    25,    25,    26,     1,   145,    30,
+       4,     5,     6,     7,     8,     9,    10,    11,    31,    32,
+      14,    15,    16,    17,   137,    25,    26,    92,    30,    29,
+      66,    25,    27,    69,    34,    71,    30,    68,    33,    70,
+     105,     1,   155,     0,     1,     1,     3,     4,     5,     6,
+       7,     8,     9,    10,    11,    12,    30,    14,    15,    16,
+      17,    18,    19,    20,    21,    22,    23,    68,    25,    70,
+      25,     0,     1,    30,     3,     4,     5,     6,     7,     8,
+       9,    10,    11,    30,    30,    14,    15,    16,    17,    18,
+      19,    20,    21,    22,    23,    30,    25,    30,    30,     0,
+       1,    30,     3,     4,     5,     6,     7,     8,     9,    10,
+      11,    30,    30,    14,    15,    16,    30,    18,    19,    20,
+      21,    22,    23,     1,    25,    30,    30,     5,     6,    30,
+       8,     9,    10,    11,    12,     1,    14,    15,    16,    17,
+      18,    19,    20,    21,    14,     5,     6,    25,     8,     9,
+      10,    11,    30,    30,    14,    15,   142,   143,   144,    13,
+      25,    31,    32,   149,    28,   151,    14,    31,    32,    32,
+      30,    67,    68,    30,    70,   161,    67,    68,    30,    70,
+      30,    31,    32,    30,    31,    32,    30,    31,    32,    30,
+      31,    32,    30,    30,    30,    30,    30,    30,    30,    29,
+      40,    59,    -1,   106,    66
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const unsigned char yystos[] =
+{
+       0,    36,    37,     0,     1,     3,     4,     5,     6,     7,
+       8,     9,    10,    11,    14,    15,    16,    18,    19,    20,
+      21,    22,    23,    25,    30,    38,    39,    41,    42,    43,
+      44,    47,    48,    50,    54,    56,    58,    59,    61,    63,
+      64,    65,    72,    30,    25,    26,    71,    71,    30,    71,
+      30,    30,    71,    25,    25,    25,    26,    29,    34,    75,
+      76,    30,     1,     1,    45,    45,    51,    53,    57,    68,
+      62,    68,    30,    73,    30,    30,    30,    30,    30,    75,
+      75,    31,    32,    73,    27,    33,    30,    30,     1,    12,
+      16,    17,    19,    20,    21,    22,    23,    25,    30,    40,
+      46,    66,    67,    69,    18,    19,    20,    21,    30,    40,
+      52,    67,    69,    39,    49,    72,    39,    50,    55,    61,
+      72,    30,    40,    69,    39,    50,    60,    61,    72,    30,
+      28,    75,    75,    76,    76,    30,    30,    24,    75,    75,
+      71,    70,    71,    75,    25,    76,     1,    13,    30,    71,
+      70,    25,    75,    30,    30,    14,    74,    30,    74,    74,
+      74,    76,    30,    74,    30,    74,    30,    75,    30,    30,
+      30,    74,    30,    30,    30
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { 								\
+      yyerror ("syntax error: cannot back up");\
+      YYERROR;							\
+    }								\
+while (0)
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (N)								\
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (0)
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+              (Loc).first_line, (Loc).first_column,	\
+              (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (0)
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)		\
+do {								\
+  if (yydebug)							\
+    {								\
+      YYFPRINTF (stderr, "%s ", Title);				\
+      yysymprint (stderr, 					\
+                  Type, Value);	\
+      YYFPRINTF (stderr, "\n");					\
+    }								\
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    short int *bottom;
+    short int *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (/* Nothing. */; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+    int yyrule;
+#endif
+{
+  int yyi;
+  unsigned int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+             yyrule - 1, yylno);
+  /* Print the symbols being reduced, and their result.  */
+  for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+    YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+  YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (Rule);		\
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined (__GLIBC__) && defined (_STRING_H)
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+#   if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+#   else
+yystrlen (yystr)
+     const char *yystr;
+#   endif
+{
+  register const char *yys = yystr;
+
+  while (*yys++ != '\0')
+    continue;
+
+  return yys - yystr - 1;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+#   if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+#   else
+yystpcpy (yydest, yysrc)
+     char *yydest;
+     const char *yysrc;
+#   endif
+{
+  register char *yyd = yydest;
+  register const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+  switch (yytype)
+    {
+      default:
+        break;
+    }
+  YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  /* Pacify ``unused variable'' warnings.  */
+  (void) yyvaluep;
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+      case 48: /* choice_entry */
+
+        {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		(yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+	if (current_menu == (yyvaluep->menu))
+		menu_end_menu();
+};
+
+        break;
+      case 54: /* if_entry */
+
+        {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		(yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+	if (current_menu == (yyvaluep->menu))
+		menu_end_menu();
+};
+
+        break;
+      case 59: /* menu_entry */
+
+        {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		(yyvaluep->menu)->file->name, (yyvaluep->menu)->lineno);
+	if (current_menu == (yyvaluep->menu))
+		menu_end_menu();
+};
+
+        break;
+
+      default:
+        break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+  void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+  register int yystate;
+  register int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks through separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  short int yyssa[YYINITDEPTH];
+  short int *yyss = yyssa;
+  register short int *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* When reducing, the number of symbols on the RHS of the reduced
+     rule.  */
+  int yylen;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+
+  yyvsp[0] = yylval;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed. so pushing a state here evens the stacks.
+     */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack. Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	short int *yyss1 = yyss;
+
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow ("parser stack overflow",
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+
+		    &yystacksize);
+
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyoverflowlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyoverflowlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	short int *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyoverflowlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a look-ahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 8:
+
+    { zconf_error("unexpected end statement"); ;}
+    break;
+
+  case 9:
+
+    { zconf_error("unknown statement \"%s\"", (yyvsp[-2].string)); ;}
+    break;
+
+  case 10:
+
+    {
+	zconf_error("unexpected option \"%s\"", kconf_id_strings + (yyvsp[-2].id)->name);
+;}
+    break;
+
+  case 11:
+
+    { zconf_error("invalid statement"); ;}
+    break;
+
+  case 25:
+
+    { zconf_error("unknown option \"%s\"", (yyvsp[-2].string)); ;}
+    break;
+
+  case 26:
+
+    { zconf_error("invalid option"); ;}
+    break;
+
+  case 27:
+
+    {
+	struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+	sym->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry(sym);
+	printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+    break;
+
+  case 28:
+
+    {
+	menu_end_entry();
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 29:
+
+    {
+	struct symbol *sym = sym_lookup((yyvsp[-1].string), 0);
+	sym->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry(sym);
+	printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+;}
+    break;
+
+  case 30:
+
+    {
+	if (current_entry->prompt)
+		current_entry->prompt->type = P_MENU;
+	else
+		zconfprint("warning: menuconfig statement without prompt");
+	menu_end_entry();
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 37:
+
+    {
+	menu_set_type((yyvsp[-2].id)->stype);
+	printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+		zconf_curname(), zconf_lineno(),
+		(yyvsp[-2].id)->stype);
+;}
+    break;
+
+  case 38:
+
+    {
+	menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 39:
+
+    {
+	menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr));
+	if ((yyvsp[-3].id)->stype != S_UNKNOWN)
+		menu_set_type((yyvsp[-3].id)->stype);
+	printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+		zconf_curname(), zconf_lineno(),
+		(yyvsp[-3].id)->stype);
+;}
+    break;
+
+  case 40:
+
+    {
+	menu_add_symbol(P_SELECT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 41:
+
+    {
+	menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 42:
+
+    {
+	struct symbol *sym = sym_lookup(NULL, 0);
+	sym->flags |= SYMBOL_CHOICE;
+	menu_add_entry(sym);
+	menu_add_expr(P_CHOICE, NULL, NULL);
+	printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 43:
+
+    {
+	(yyval.menu) = menu_add_menu();
+;}
+    break;
+
+  case 44:
+
+    {
+	if (zconf_endtoken((yyvsp[0].id), T_CHOICE, T_ENDCHOICE)) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+	}
+;}
+    break;
+
+  case 52:
+
+    {
+	menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 53:
+
+    {
+	if ((yyvsp[-2].id)->stype == S_BOOLEAN || (yyvsp[-2].id)->stype == S_TRISTATE) {
+		menu_set_type((yyvsp[-2].id)->stype);
+		printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+			zconf_curname(), zconf_lineno(),
+			(yyvsp[-2].id)->stype);
+	} else
+		YYERROR;
+;}
+    break;
+
+  case 54:
+
+    {
+	current_entry->sym->flags |= SYMBOL_OPTIONAL;
+	printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 55:
+
+    {
+	if ((yyvsp[-3].id)->stype == S_UNKNOWN) {
+		menu_add_symbol(P_DEFAULT, sym_lookup((yyvsp[-2].string), 0), (yyvsp[-1].expr));
+		printd(DEBUG_PARSE, "%s:%d:default\n",
+			zconf_curname(), zconf_lineno());
+	} else
+		YYERROR;
+;}
+    break;
+
+  case 58:
+
+    {
+	printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+	menu_add_entry(NULL);
+	menu_add_dep((yyvsp[-1].expr));
+	(yyval.menu) = menu_add_menu();
+;}
+    break;
+
+  case 59:
+
+    {
+	if (zconf_endtoken((yyvsp[0].id), T_IF, T_ENDIF)) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+	}
+;}
+    break;
+
+  case 65:
+
+    {
+	menu_add_entry(NULL);
+	menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL);
+	printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 66:
+
+    {
+	(yyval.menu) = menu_add_menu();
+;}
+    break;
+
+  case 67:
+
+    {
+	if (zconf_endtoken((yyvsp[0].id), T_MENU, T_ENDMENU)) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+	}
+;}
+    break;
+
+  case 73:
+
+    {
+	printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+	zconf_nextfile((yyvsp[-1].string));
+;}
+    break;
+
+  case 74:
+
+    {
+	menu_add_entry(NULL);
+	menu_add_prompt(P_COMMENT, (yyvsp[-1].string), NULL);
+	printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 75:
+
+    {
+	menu_end_entry();
+;}
+    break;
+
+  case 76:
+
+    {
+	printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+	zconf_starthelp();
+;}
+    break;
+
+  case 77:
+
+    {
+	current_entry->sym->help = (yyvsp[0].string);
+;}
+    break;
+
+  case 82:
+
+    {
+	menu_add_dep((yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 83:
+
+    {
+	menu_add_dep((yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 84:
+
+    {
+	menu_add_dep((yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+;}
+    break;
+
+  case 86:
+
+    {
+	menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr));
+;}
+    break;
+
+  case 89:
+
+    { (yyval.id) = (yyvsp[-1].id); ;}
+    break;
+
+  case 90:
+
+    { (yyval.id) = (yyvsp[-1].id); ;}
+    break;
+
+  case 91:
+
+    { (yyval.id) = (yyvsp[-1].id); ;}
+    break;
+
+  case 94:
+
+    { (yyval.expr) = NULL; ;}
+    break;
+
+  case 95:
+
+    { (yyval.expr) = (yyvsp[0].expr); ;}
+    break;
+
+  case 96:
+
+    { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); ;}
+    break;
+
+  case 97:
+
+    { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+    break;
+
+  case 98:
+
+    { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); ;}
+    break;
+
+  case 99:
+
+    { (yyval.expr) = (yyvsp[-1].expr); ;}
+    break;
+
+  case 100:
+
+    { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); ;}
+    break;
+
+  case 101:
+
+    { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+    break;
+
+  case 102:
+
+    { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;}
+    break;
+
+  case 103:
+
+    { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); ;}
+    break;
+
+  case 104:
+
+    { (yyval.symbol) = sym_lookup((yyvsp[0].string), 1); free((yyvsp[0].string)); ;}
+    break;
+
+
+    }
+
+/* Line 1037 of yacc.c.  */
+
+
+  yyvsp -= yylen;
+  yyssp -= yylen;
+
+
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (YYPACT_NINF < yyn && yyn < YYLAST)
+	{
+	  YYSIZE_T yysize = 0;
+	  int yytype = YYTRANSLATE (yychar);
+	  const char* yyprefix;
+	  char *yymsg;
+	  int yyx;
+
+	  /* Start YYX at -YYN if negative to avoid negative indexes in
+	     YYCHECK.  */
+	  int yyxbegin = yyn < 0 ? -yyn : 0;
+
+	  /* Stay within bounds of both yycheck and yytname.  */
+	  int yychecklim = YYLAST - yyn;
+	  int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+	  int yycount = 0;
+
+	  yyprefix = ", expecting ";
+	  for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	      {
+		yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+		yycount += 1;
+		if (yycount == 5)
+		  {
+		    yysize = 0;
+		    break;
+		  }
+	      }
+	  yysize += (sizeof ("syntax error, unexpected ")
+		     + yystrlen (yytname[yytype]));
+	  yymsg = (char *) YYSTACK_ALLOC (yysize);
+	  if (yymsg != 0)
+	    {
+	      char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+	      yyp = yystpcpy (yyp, yytname[yytype]);
+
+	      if (yycount < 5)
+		{
+		  yyprefix = ", expecting ";
+		  for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+		    if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+		      {
+			yyp = yystpcpy (yyp, yyprefix);
+			yyp = yystpcpy (yyp, yytname[yyx]);
+			yyprefix = " or ";
+		      }
+		}
+	      yyerror (yymsg);
+	      YYSTACK_FREE (yymsg);
+	    }
+	  else
+	    yyerror ("syntax error; also virtual memory exhausted");
+	}
+      else
+#endif /* YYERROR_VERBOSE */
+	yyerror ("syntax error");
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* If at end of input, pop the error token,
+	     then the rest of the stack, then return failure.  */
+	  if (yychar == YYEOF)
+	     for (;;)
+	       {
+
+		 YYPOPSTACK;
+		 if (yyssp == yyss)
+		   YYABORT;
+		 yydestruct ("Error: popping",
+                             yystos[*yyssp], yyvsp);
+	       }
+        }
+      else
+	{
+	  yydestruct ("Error: discarding", yytoken, &yylval);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+  /* Pacify GCC when the user code never invokes YYERROR and the label
+     yyerrorlab therefore never appears in user code.  */
+  if (0)
+     goto yyerrorlab;
+#endif
+
+yyvsp -= yylen;
+  yyssp -= yylen;
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+
+      yydestruct ("Error: popping", yystos[yystate], yyvsp);
+      YYPOPSTACK;
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+
+  /* Shift the error token. */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yydestruct ("Error: discarding lookahead",
+              yytoken, &yylval);
+  yychar = YYEMPTY;
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here.  |
+`----------------------------------------------*/
+yyoverflowlab:
+  yyerror ("parser stack overflow");
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+  return yyresult;
+}
+
+
+
+
+
+void conf_parse(const char *name)
+{
+	struct symbol *sym;
+	int i;
+
+	zconf_initscan(name);
+
+	sym_init();
+	menu_init();
+	modules_sym = sym_lookup("MODULES", 0);
+	rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL);
+
+#if YYDEBUG
+	if (getenv("ZCONF_DEBUG"))
+		zconfdebug = 1;
+#endif
+	zconfparse();
+	if (zconfnerrs)
+		exit(1);
+	menu_finalize(&rootmenu);
+	for_all_symbols(i, sym) {
+		sym_check_deps(sym);
+        }
+
+	sym_change_count = 1;
+}
+
+const char *zconf_tokenname(int token)
+{
+	switch (token) {
+	case T_MENU:		return "menu";
+	case T_ENDMENU:		return "endmenu";
+	case T_CHOICE:		return "choice";
+	case T_ENDCHOICE:	return "endchoice";
+	case T_IF:		return "if";
+	case T_ENDIF:		return "endif";
+	case T_DEPENDS:		return "depends";
+	}
+	return "<token>";
+}
+
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
+{
+	if (id->token != endtoken) {
+		zconf_error("unexpected '%s' within %s block",
+			kconf_id_strings + id->name, zconf_tokenname(starttoken));
+		zconfnerrs++;
+		return false;
+	}
+	if (current_menu->file != current_file) {
+		zconf_error("'%s' in different file than '%s'",
+			kconf_id_strings + id->name, zconf_tokenname(starttoken));
+		fprintf(stderr, "%s:%d: location of the '%s'\n",
+			current_menu->file->name, current_menu->lineno,
+			zconf_tokenname(starttoken));
+		zconfnerrs++;
+		return false;
+	}
+	return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+	va_start(ap, err);
+	vfprintf(stderr, err, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+	va_list ap;
+
+	zconfnerrs++;
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+	va_start(ap, err);
+	vfprintf(stderr, err, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+#if YYDEBUG
+	fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+#endif
+}
+
+void print_quoted_string(FILE *out, const char *str)
+{
+	const char *p;
+	int len;
+
+	putc('"', out);
+	while ((p = strchr(str, '"'))) {
+		len = p - str;
+		if (len)
+			fprintf(out, "%.*s", len, str);
+		fputs("\\\"", out);
+		str = p + 1;
+	}
+	fputs(str, out);
+	putc('"', out);
+}
+
+void print_symbol(FILE *out, struct menu *menu)
+{
+	struct symbol *sym = menu->sym;
+	struct property *prop;
+
+	if (sym_is_choice(sym))
+		fprintf(out, "choice\n");
+	else
+		fprintf(out, "config %s\n", sym->name);
+	switch (sym->type) {
+	case S_BOOLEAN:
+		fputs("  boolean\n", out);
+		break;
+	case S_TRISTATE:
+		fputs("  tristate\n", out);
+		break;
+	case S_STRING:
+		fputs("  string\n", out);
+		break;
+	case S_INT:
+		fputs("  integer\n", out);
+		break;
+	case S_HEX:
+		fputs("  hex\n", out);
+		break;
+	default:
+		fputs("  ???\n", out);
+		break;
+	}
+	for (prop = sym->prop; prop; prop = prop->next) {
+		if (prop->menu != menu)
+			continue;
+		switch (prop->type) {
+		case P_PROMPT:
+			fputs("  prompt ", out);
+			print_quoted_string(out, prop->text);
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs(" if ", out);
+				expr_fprint(prop->visible.expr, out);
+			}
+			fputc('\n', out);
+			break;
+		case P_DEFAULT:
+			fputs( "  default ", out);
+			expr_fprint(prop->expr, out);
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs(" if ", out);
+				expr_fprint(prop->visible.expr, out);
+			}
+			fputc('\n', out);
+			break;
+		case P_CHOICE:
+			fputs("  #choice value\n", out);
+			break;
+		default:
+			fprintf(out, "  unknown prop %d!\n", prop->type);
+			break;
+		}
+	}
+	if (sym->help) {
+		int len = strlen(sym->help);
+		while (sym->help[--len] == '\n')
+			sym->help[len] = 0;
+		fprintf(out, "  help\n%s\n", sym->help);
+	}
+	fputc('\n', out);
+}
+
+void zconfdump(FILE *out)
+{
+	struct property *prop;
+	struct symbol *sym;
+	struct menu *menu;
+
+	menu = rootmenu.list;
+	while (menu) {
+		if ((sym = menu->sym))
+			print_symbol(out, menu);
+		else if ((prop = menu->prompt)) {
+			switch (prop->type) {
+			case P_COMMENT:
+				fputs("\ncomment ", out);
+				print_quoted_string(out, prop->text);
+				fputs("\n", out);
+				break;
+			case P_MENU:
+				fputs("\nmenu ", out);
+				print_quoted_string(out, prop->text);
+				fputs("\n", out);
+				break;
+			default:
+				;
+			}
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs("  depends ", out);
+				expr_fprint(prop->visible.expr, out);
+				fputc('\n', out);
+			}
+			fputs("\n", out);
+		}
+
+		if (menu->list)
+			menu = menu->list;
+		else if (menu->next)
+			menu = menu->next;
+		else while ((menu = menu->parent)) {
+			if (menu->prompt && menu->prompt->type == P_MENU)
+				fputs("\nendmenu\n", out);
+			if (menu->next) {
+				menu = menu->next;
+				break;
+			}
+		}
+	}
+}
+
+#include "lex.zconf.c"
+#include "util.c"
+#include "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
diff --git a/busybox-1.19.3/scripts/kconfig/zconf.y b/busybox-1.19.3/scripts/kconfig/zconf.y
new file mode 100644
index 0000000..bef5e92
--- /dev/null
+++ b/busybox-1.19.3/scripts/kconfig/zconf.y
@@ -0,0 +1,683 @@
+%{
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Released under the terms of the GNU GPL v2.0.
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define LKC_DIRECT_LINK
+#include "lkc.h"
+
+#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
+
+#define PRINTD		0x0001
+#define DEBUG_PARSE	0x0002
+
+int cdebug = PRINTD;
+
+extern int zconflex(void);
+static void zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static void zconferror(const char *err);
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken);
+
+struct symbol *symbol_hash[257];
+
+static struct menu *current_menu, *current_entry;
+
+#define YYDEBUG 0
+#if YYDEBUG
+#define YYERROR_VERBOSE
+#endif
+%}
+%expect 26
+
+%union
+{
+	char *string;
+	struct file *file;
+	struct symbol *symbol;
+	struct expr *expr;
+	struct menu *menu;
+	struct kconf_id *id;
+}
+
+%token <id>T_MAINMENU
+%token <id>T_MENU
+%token <id>T_ENDMENU
+%token <id>T_SOURCE
+%token <id>T_CHOICE
+%token <id>T_ENDCHOICE
+%token <id>T_COMMENT
+%token <id>T_CONFIG
+%token <id>T_MENUCONFIG
+%token <id>T_HELP
+%token <string> T_HELPTEXT
+%token <id>T_IF
+%token <id>T_ENDIF
+%token <id>T_DEPENDS
+%token <id>T_REQUIRES
+%token <id>T_OPTIONAL
+%token <id>T_PROMPT
+%token <id>T_TYPE
+%token <id>T_DEFAULT
+%token <id>T_SELECT
+%token <id>T_RANGE
+%token <id>T_ON
+%token <string> T_WORD
+%token <string> T_WORD_QUOTE
+%token T_UNEQUAL
+%token T_CLOSE_PAREN
+%token T_OPEN_PAREN
+%token T_EOL
+
+%left T_OR
+%left T_AND
+%left T_EQUAL T_UNEQUAL
+%nonassoc T_NOT
+
+%type <string> prompt
+%type <symbol> symbol
+%type <expr> expr
+%type <expr> if_expr
+%type <id> end
+%type <id> option_name
+%type <menu> if_entry menu_entry choice_entry
+
+%destructor {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		$$->file->name, $$->lineno);
+	if (current_menu == $$)
+		menu_end_menu();
+} if_entry menu_entry choice_entry
+
+%{
+#include "zconf.hash.c"
+%}
+
+%%
+input: stmt_list;
+
+stmt_list:
+	  /* empty */
+	| stmt_list common_stmt
+	| stmt_list choice_stmt
+	| stmt_list menu_stmt
+	| stmt_list T_MAINMENU prompt nl
+	| stmt_list end			{ zconf_error("unexpected end statement"); }
+	| stmt_list T_WORD error T_EOL	{ zconf_error("unknown statement \"%s\"", $2); }
+	| stmt_list option_name error T_EOL
+{
+	zconf_error("unexpected option \"%s\"", kconf_id_strings + $2->name);
+}
+	| stmt_list error T_EOL		{ zconf_error("invalid statement"); }
+;
+
+option_name:
+	T_DEPENDS | T_PROMPT | T_TYPE | T_SELECT | T_OPTIONAL | T_RANGE | T_DEFAULT
+;
+
+common_stmt:
+	  T_EOL
+	| if_stmt
+	| comment_stmt
+	| config_stmt
+	| menuconfig_stmt
+	| source_stmt
+;
+
+option_error:
+	  T_WORD error T_EOL		{ zconf_error("unknown option \"%s\"", $1); }
+	| error T_EOL			{ zconf_error("invalid option"); }
+;
+
+
+/* config/menuconfig entry */
+
+config_entry_start: T_CONFIG T_WORD T_EOL
+{
+	struct symbol *sym = sym_lookup($2, 0);
+	sym->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry(sym);
+	printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2);
+};
+
+config_stmt: config_entry_start config_option_list
+{
+	menu_end_entry();
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+};
+
+menuconfig_entry_start: T_MENUCONFIG T_WORD T_EOL
+{
+	struct symbol *sym = sym_lookup($2, 0);
+	sym->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry(sym);
+	printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2);
+};
+
+menuconfig_stmt: menuconfig_entry_start config_option_list
+{
+	if (current_entry->prompt)
+		current_entry->prompt->type = P_MENU;
+	else
+		zconfprint("warning: menuconfig statement without prompt");
+	menu_end_entry();
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+};
+
+config_option_list:
+	  /* empty */
+	| config_option_list config_option
+	| config_option_list depends
+	| config_option_list help
+	| config_option_list option_error
+	| config_option_list T_EOL
+;
+
+config_option: T_TYPE prompt_stmt_opt T_EOL
+{
+	menu_set_type($1->stype);
+	printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+		zconf_curname(), zconf_lineno(),
+		$1->stype);
+};
+
+config_option: T_PROMPT prompt if_expr T_EOL
+{
+	menu_add_prompt(P_PROMPT, $2, $3);
+	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_DEFAULT expr if_expr T_EOL
+{
+	menu_add_expr(P_DEFAULT, $2, $3);
+	if ($1->stype != S_UNKNOWN)
+		menu_set_type($1->stype);
+	printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+		zconf_curname(), zconf_lineno(),
+		$1->stype);
+};
+
+config_option: T_SELECT T_WORD if_expr T_EOL
+{
+	menu_add_symbol(P_SELECT, sym_lookup($2, 0), $3);
+	printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+};
+
+config_option: T_RANGE symbol symbol if_expr T_EOL
+{
+	menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
+	printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+};
+
+/* choice entry */
+
+choice: T_CHOICE T_EOL
+{
+	struct symbol *sym = sym_lookup(NULL, 0);
+	sym->flags |= SYMBOL_CHOICE;
+	menu_add_entry(sym);
+	menu_add_expr(P_CHOICE, NULL, NULL);
+	printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+};
+
+choice_entry: choice choice_option_list
+{
+	$$ = menu_add_menu();
+};
+
+choice_end: end
+{
+	if (zconf_endtoken($1, T_CHOICE, T_ENDCHOICE)) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+	}
+};
+
+choice_stmt: choice_entry choice_block choice_end
+;
+
+choice_option_list:
+	  /* empty */
+	| choice_option_list choice_option
+	| choice_option_list depends
+	| choice_option_list help
+	| choice_option_list T_EOL
+	| choice_option_list option_error
+;
+
+choice_option: T_PROMPT prompt if_expr T_EOL
+{
+	menu_add_prompt(P_PROMPT, $2, $3);
+	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_TYPE prompt_stmt_opt T_EOL
+{
+	if ($1->stype == S_BOOLEAN || $1->stype == S_TRISTATE) {
+		menu_set_type($1->stype);
+		printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+			zconf_curname(), zconf_lineno(),
+			$1->stype);
+	} else
+		YYERROR;
+};
+
+choice_option: T_OPTIONAL T_EOL
+{
+	current_entry->sym->flags |= SYMBOL_OPTIONAL;
+	printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_DEFAULT T_WORD if_expr T_EOL
+{
+	if ($1->stype == S_UNKNOWN) {
+		menu_add_symbol(P_DEFAULT, sym_lookup($2, 0), $3);
+		printd(DEBUG_PARSE, "%s:%d:default\n",
+			zconf_curname(), zconf_lineno());
+	} else
+		YYERROR;
+};
+
+choice_block:
+	  /* empty */
+	| choice_block common_stmt
+;
+
+/* if entry */
+
+if_entry: T_IF expr nl
+{
+	printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+	menu_add_entry(NULL);
+	menu_add_dep($2);
+	$$ = menu_add_menu();
+};
+
+if_end: end
+{
+	if (zconf_endtoken($1, T_IF, T_ENDIF)) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+	}
+};
+
+if_stmt: if_entry if_block if_end
+;
+
+if_block:
+	  /* empty */
+	| if_block common_stmt
+	| if_block menu_stmt
+	| if_block choice_stmt
+;
+
+/* menu entry */
+
+menu: T_MENU prompt T_EOL
+{
+	menu_add_entry(NULL);
+	menu_add_prompt(P_MENU, $2, NULL);
+	printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+};
+
+menu_entry: menu depends_list
+{
+	$$ = menu_add_menu();
+};
+
+menu_end: end
+{
+	if (zconf_endtoken($1, T_MENU, T_ENDMENU)) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+	}
+};
+
+menu_stmt: menu_entry menu_block menu_end
+;
+
+menu_block:
+	  /* empty */
+	| menu_block common_stmt
+	| menu_block menu_stmt
+	| menu_block choice_stmt
+;
+
+source_stmt: T_SOURCE prompt T_EOL
+{
+	printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), $2);
+	zconf_nextfile($2);
+};
+
+/* comment entry */
+
+comment: T_COMMENT prompt T_EOL
+{
+	menu_add_entry(NULL);
+	menu_add_prompt(P_COMMENT, $2, NULL);
+	printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+};
+
+comment_stmt: comment depends_list
+{
+	menu_end_entry();
+};
+
+/* help option */
+
+help_start: T_HELP T_EOL
+{
+	printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+	zconf_starthelp();
+};
+
+help: help_start T_HELPTEXT
+{
+	current_entry->sym->help = $2;
+};
+
+/* depends option */
+
+depends_list:
+	  /* empty */
+	| depends_list depends
+	| depends_list T_EOL
+	| depends_list option_error
+;
+
+depends: T_DEPENDS T_ON expr T_EOL
+{
+	menu_add_dep($3);
+	printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+}
+	| T_DEPENDS expr T_EOL
+{
+	menu_add_dep($2);
+	printd(DEBUG_PARSE, "%s:%d:depends\n", zconf_curname(), zconf_lineno());
+}
+	| T_REQUIRES expr T_EOL
+{
+	menu_add_dep($2);
+	printd(DEBUG_PARSE, "%s:%d:requires\n", zconf_curname(), zconf_lineno());
+};
+
+/* prompt statement */
+
+prompt_stmt_opt:
+	  /* empty */
+	| prompt if_expr
+{
+	menu_add_prompt(P_PROMPT, $1, $2);
+};
+
+prompt:	  T_WORD
+	| T_WORD_QUOTE
+;
+
+end:	  T_ENDMENU T_EOL	{ $$ = $1; }
+	| T_ENDCHOICE T_EOL	{ $$ = $1; }
+	| T_ENDIF T_EOL		{ $$ = $1; }
+;
+
+nl:
+	  T_EOL
+	| nl T_EOL
+;
+
+if_expr:  /* empty */			{ $$ = NULL; }
+	| T_IF expr			{ $$ = $2; }
+;
+
+expr:	  symbol				{ $$ = expr_alloc_symbol($1); }
+	| symbol T_EQUAL symbol			{ $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
+	| symbol T_UNEQUAL symbol		{ $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
+	| T_OPEN_PAREN expr T_CLOSE_PAREN	{ $$ = $2; }
+	| T_NOT expr				{ $$ = expr_alloc_one(E_NOT, $2); }
+	| expr T_OR expr			{ $$ = expr_alloc_two(E_OR, $1, $3); }
+	| expr T_AND expr			{ $$ = expr_alloc_two(E_AND, $1, $3); }
+;
+
+symbol:	  T_WORD	{ $$ = sym_lookup($1, 0); free($1); }
+	| T_WORD_QUOTE	{ $$ = sym_lookup($1, 1); free($1); }
+;
+
+%%
+
+void conf_parse(const char *name)
+{
+	struct symbol *sym;
+	int i;
+
+	zconf_initscan(name);
+
+	sym_init();
+	menu_init();
+	modules_sym = sym_lookup("MODULES", 0);
+	rootmenu.prompt = menu_add_prompt(P_MENU, "Busybox Configuration", NULL);
+
+#if YYDEBUG
+	if (getenv("ZCONF_DEBUG"))
+		zconfdebug = 1;
+#endif
+	zconfparse();
+	if (zconfnerrs)
+		exit(1);
+	menu_finalize(&rootmenu);
+	for_all_symbols(i, sym) {
+		sym_check_deps(sym);
+	}
+
+	sym_change_count = 1;
+}
+
+const char *zconf_tokenname(int token)
+{
+	switch (token) {
+	case T_MENU:		return "menu";
+	case T_ENDMENU:		return "endmenu";
+	case T_CHOICE:		return "choice";
+	case T_ENDCHOICE:	return "endchoice";
+	case T_IF:		return "if";
+	case T_ENDIF:		return "endif";
+	case T_DEPENDS:		return "depends";
+	}
+	return "<token>";
+}
+
+static bool zconf_endtoken(struct kconf_id *id, int starttoken, int endtoken)
+{
+	if (id->token != endtoken) {
+		zconf_error("unexpected '%s' within %s block",
+			kconf_id_strings + id->name, zconf_tokenname(starttoken));
+		zconfnerrs++;
+		return false;
+	}
+	if (current_menu->file != current_file) {
+		zconf_error("'%s' in different file than '%s'",
+			kconf_id_strings + id->name, zconf_tokenname(starttoken));
+		fprintf(stderr, "%s:%d: location of the '%s'\n",
+			current_menu->file->name, current_menu->lineno,
+			zconf_tokenname(starttoken));
+		zconfnerrs++;
+		return false;
+	}
+	return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+	va_start(ap, err);
+	vfprintf(stderr, err, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+	va_list ap;
+
+	zconfnerrs++;
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+	va_start(ap, err);
+	vfprintf(stderr, err, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
+static void zconferror(const char *err)
+{
+#if YYDEBUG
+	fprintf(stderr, "%s:%d: %s\n", zconf_curname(), zconf_lineno() + 1, err);
+#endif
+}
+
+void print_quoted_string(FILE *out, const char *str)
+{
+	const char *p;
+	int len;
+
+	putc('"', out);
+	while ((p = strchr(str, '"'))) {
+		len = p - str;
+		if (len)
+			fprintf(out, "%.*s", len, str);
+		fputs("\\\"", out);
+		str = p + 1;
+	}
+	fputs(str, out);
+	putc('"', out);
+}
+
+void print_symbol(FILE *out, struct menu *menu)
+{
+	struct symbol *sym = menu->sym;
+	struct property *prop;
+
+	if (sym_is_choice(sym))
+		fprintf(out, "choice\n");
+	else
+		fprintf(out, "config %s\n", sym->name);
+	switch (sym->type) {
+	case S_BOOLEAN:
+		fputs("  boolean\n", out);
+		break;
+	case S_TRISTATE:
+		fputs("  tristate\n", out);
+		break;
+	case S_STRING:
+		fputs("  string\n", out);
+		break;
+	case S_INT:
+		fputs("  integer\n", out);
+		break;
+	case S_HEX:
+		fputs("  hex\n", out);
+		break;
+	default:
+		fputs("  ???\n", out);
+		break;
+	}
+	for (prop = sym->prop; prop; prop = prop->next) {
+		if (prop->menu != menu)
+			continue;
+		switch (prop->type) {
+		case P_PROMPT:
+			fputs("  prompt ", out);
+			print_quoted_string(out, prop->text);
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs(" if ", out);
+				expr_fprint(prop->visible.expr, out);
+			}
+			fputc('\n', out);
+			break;
+		case P_DEFAULT:
+			fputs( "  default ", out);
+			expr_fprint(prop->expr, out);
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs(" if ", out);
+				expr_fprint(prop->visible.expr, out);
+			}
+			fputc('\n', out);
+			break;
+		case P_CHOICE:
+			fputs("  #choice value\n", out);
+			break;
+		default:
+			fprintf(out, "  unknown prop %d!\n", prop->type);
+			break;
+		}
+	}
+	if (sym->help) {
+		int len = strlen(sym->help);
+		while (sym->help[--len] == '\n')
+			sym->help[len] = 0;
+		fprintf(out, "  help\n%s\n", sym->help);
+	}
+	fputc('\n', out);
+}
+
+void zconfdump(FILE *out)
+{
+	struct property *prop;
+	struct symbol *sym;
+	struct menu *menu;
+
+	menu = rootmenu.list;
+	while (menu) {
+		if ((sym = menu->sym))
+			print_symbol(out, menu);
+		else if ((prop = menu->prompt)) {
+			switch (prop->type) {
+			case P_COMMENT:
+				fputs("\ncomment ", out);
+				print_quoted_string(out, prop->text);
+				fputs("\n", out);
+				break;
+			case P_MENU:
+				fputs("\nmenu ", out);
+				print_quoted_string(out, prop->text);
+				fputs("\n", out);
+				break;
+			default:
+				;
+			}
+			if (!expr_is_yes(prop->visible.expr)) {
+				fputs("  depends ", out);
+				expr_fprint(prop->visible.expr, out);
+				fputc('\n', out);
+			}
+			fputs("\n", out);
+		}
+
+		if (menu->list)
+			menu = menu->list;
+		else if (menu->next)
+			menu = menu->next;
+		else while ((menu = menu->parent)) {
+			if (menu->prompt && menu->prompt->type == P_MENU)
+				fputs("\nendmenu\n", out);
+			if (menu->next) {
+				menu = menu->next;
+				break;
+			}
+		}
+	}
+}
+
+#include "lex.zconf.c"
+#include "util.c"
+#include "confdata.c"
+#include "expr.c"
+#include "symbol.c"
+#include "menu.c"
diff --git a/busybox-1.19.3/scripts/memusage b/busybox-1.19.3/scripts/memusage
new file mode 100755
index 0000000..4ef5608
--- /dev/null
+++ b/busybox-1.19.3/scripts/memusage
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+busybox=../busybox
+
+i=4000
+echo "Before we started $i copies of '$busybox sleep 10':"
+$busybox nmeter '%t %[pn] %m' | head -3
+
+while test $i != 0; do
+    $busybox sleep 10 &
+    i=$((i-1))
+done
+sleep 1
+
+echo "After:"
+$busybox nmeter '%t %[pn] %m' | head -3
diff --git a/busybox-1.19.3/scripts/mkconfigs b/busybox-1.19.3/scripts/mkconfigs
new file mode 100755
index 0000000..db94fcc
--- /dev/null
+++ b/busybox-1.19.3/scripts/mkconfigs
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# Copyright (C) 2002 Khalid Aziz <khalid_aziz at hp.com>
+# Copyright (C) 2002 Randy Dunlap <rddunlap at osdl.org>
+# Copyright (C) 2002 Al Stone <ahs3 at fc.hp.com>
+# Copyright (C) 2002 Hewlett-Packard Company
+#
+#   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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+#   Busybox version by Matteo Croce <3297627799 at wind.it>
+#
+# Rules to generate bbconfigopts.h from .config:
+#      - Retain lines that begin with "CONFIG_"
+#      - Retain lines that begin with "# CONFIG_"
+#      - lines that use double-quotes must \\-escape-quote them
+
+config=.config
+
+{
+echo "\
+#ifndef _BBCONFIGOPTS_H
+#define _BBCONFIGOPTS_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[] ALIGN1 ="
+
+grep -e '^# CONFIG_' -e '^CONFIG_' "$config" \
+| sed -e 's/\"/\\\"/g' -e 's/^/"/' -e 's/$/\\n"/'
+
+echo ";"
+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 -t x1 \
+| sed -e 's/^[^ ]*//' \
+        -e 's/ //g' \
+        -e '/^$/d' \
+        -e 's/\(..\)/0x\1,/g'
+
+echo "};"
+echo "#endif"
+} >"$2"
diff --git a/busybox-1.19.3/scripts/mkdiff_obj b/busybox-1.19.3/scripts/mkdiff_obj
new file mode 100755
index 0000000..3074748
--- /dev/null
+++ b/busybox-1.19.3/scripts/mkdiff_obj
@@ -0,0 +1,46 @@
+#!/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" || usage 1
+test -d "$2" || usage 1
+
+{
+	(
+		cd "$1" || exit 1
+		find -name '*.o' # -o -name '*.os' # -o -name '*.so'
+	)
+	(
+		cd "$2" || exit 1
+		find -name '*.o' # -o -name '*.os' # -o -name '*.so'
+	)
+} | sed 's:^\./::' | sort | uniq | \
+(
+while IFS='' read -r oname; do
+	if ! test -f "$1/$oname"; then
+		echo "Only $2/$oname"
+		continue
+	fi
+	if ! test -f "$2/$oname"; then
+		echo "Only $1/$oname"
+		continue
+	fi
+	diff -q -- "$1/$oname" "$2/$oname" >/dev/null && continue
+	(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
+)
diff --git a/busybox-1.19.3/scripts/mkdiff_obj_bloat b/busybox-1.19.3/scripts/mkdiff_obj_bloat
new file mode 100755
index 0000000..63b2c2e
--- /dev/null
+++ b/busybox-1.19.3/scripts/mkdiff_obj_bloat
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+test -d "$1" || exit 1
+test -d "$2" || exit 1
+
+{
+	(
+		cd "$1" || exit 1
+		find -name '*.o' -o -name '*.os' # -o -name '*.so'
+	)
+	(
+		cd "$2" || exit 1
+		find -name '*.o' -o -name '*.os' # -o -name '*.so'
+	)
+} | sed 's:^\./::' | sort | uniq | \
+tee LST | \
+(
+IFS=''
+while read -r oname; do
+	if ! test -f "$1/$oname"; then
+		echo "Only $2/$oname"
+		continue
+	fi
+	if ! test -f "$2/$oname"; then
+		echo "Only $1/$oname"
+		continue
+	fi
+	$1/scripts/bloat-o-meter $1/$oname $2/$oname | grep 'otal: 0 byte' >/dev/null && continue
+	$1/scripts/bloat-o-meter $1/$oname $2/$oname
+	size $1/$oname $2/$oname
+	echo
+done
+)
diff --git a/busybox-1.19.3/scripts/mkmakefile b/busybox-1.19.3/scripts/mkmakefile
new file mode 100755
index 0000000..9fc51a7
--- /dev/null
+++ b/busybox-1.19.3/scripts/mkmakefile
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Generates a small Makefile used in the root of the output
+# directory, to allow make to be started from there.
+# The Makefile also allow for more convinient build of external modules
+
+# Usage
+# $1 - Kernel src directory
+# $2 - Output directory
+# $3 - version
+# $4 - patchlevel
+
+
+test ! -r $2/Makefile -o -O $2/Makefile || exit 0
+echo "  GEN     $2/Makefile"
+
+cat << EOF > $2/Makefile
+# Automatically generated by $0: don't edit
+
+VERSION = $3
+PATCHLEVEL = $4
+
+KERNELSRC    := $1
+KERNELOUTPUT := $2
+
+MAKEFLAGS += --no-print-directory
+
+.PHONY: all \$(MAKECMDGOALS)
+
+all:
+	\$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT)
+
+Makefile:;
+
+\$(filter-out all Makefile,\$(MAKECMDGOALS)):
+	\$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@
+
+%/:
+	\$(MAKE) -C \$(KERNELSRC) O=\$(KERNELOUTPUT) \$@
+EOF
diff --git a/busybox-1.19.3/scripts/objsizes b/busybox-1.19.3/scripts/objsizes
new file mode 100755
index 0000000..6df0c4f
--- /dev/null
+++ b/busybox-1.19.3/scripts/objsizes
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+t_text=0
+t_data=0
+t_bss=0
+
+printf "%9s %11s %9s %9s %s\n" "text+data" "text+rodata" rwdata bss filename
+
+find -name '*.o' | grep -v '^\./scripts/' | grep -vF built-in.o \
+| sed 's:^\./::' | xargs "${CROSS_COMPILE}size" | grep '^ *[0-9]' \
+| {
+while read text data bss dec hex filename; do
+    t_text=$((t_text+text))
+    t_data=$((t_data+data))
+    t_bss=$((t_bss+bss))
+    printf "%9d %11d %9d %9d %s\n" $((text+data)) $text $data $bss "$filename"
+done
+printf "%9d %11d %9d %9d %s\n" $((t_text+t_data)) $t_text $t_data $t_bss "TOTAL"
+} | env -uLANG -uLC_ALL sort -r
diff --git a/busybox-1.19.3/scripts/randomtest b/busybox-1.19.3/scripts/randomtest
new file mode 100755
index 0000000..a102593
--- /dev/null
+++ b/busybox-1.19.3/scripts/randomtest
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+# If not specified in environment...
+if ! test "$LIBC"; then
+	# Select which libc to build against
+	LIBC="glibc"
+	LIBC="uclibc"
+fi
+# x86 32-bit:
+#CROSS_COMPILER_PREFIX="i486-linux-uclibc-"
+# My system has strange prefix for x86 64-bit uclibc:
+#CROSS_COMPILER_PREFIX="x86_64-pc-linux-gnu-"
+
+if test $# -lt 2 || ! test -d "$1" || test -e "$2"; then
+	echo "Usage: $0 SRC_DIR TMP_DIR"
+	echo
+	echo "SRC_DIR will be copied to TMP_DIR directory."
+	echo "Then a random build will be performed."
+	echo
+	echo "Useful variables:"
+	echo "\$LIBC, \$CROSS_COMPILER_PREFIX, \$MAKEOPTS"
+	exit 1
+fi
+
+cp -dpr -- "$1" "$2" || { echo "copy error"; exit 1; }
+cd -- "$2" || { echo "cd $dir error"; exit 1; }
+
+# Generate random config
+make randconfig >/dev/null || { echo "randconfig error"; exit 1; }
+
+# Tweak resulting config
+cat .config \
+| grep -v CONFIG_DEBUG_PESSIMIZE \
+| grep -v CONFIG_WERROR \
+| grep -v CONFIG_CROSS_COMPILER_PREFIX \
+| grep -v CONFIG_SELINUX \
+| grep -v CONFIG_EFENCE \
+| grep -v CONFIG_DMALLOC \
+\
+| grep -v CONFIG_RFKILL \
+>.config.new
+mv .config.new .config
+echo '# CONFIG_DEBUG_PESSIMIZE is not set' >>.config
+echo '# CONFIG_WERROR is not set' >>.config
+echo "CONFIG_CROSS_COMPILER_PREFIX=\"${CROSS_COMPILER_PREFIX}\"" >>.config
+echo '# CONFIG_SELINUX is not set' >>.config
+echo '# CONFIG_EFENCE is not set' >>.config
+echo '# CONFIG_DMALLOC is not set' >>.config
+echo '# CONFIG_RFKILL is not set' >>.config
+
+# If glibc, don't build static
+if test x"$LIBC" = x"glibc"; then
+	cat .config \
+	| grep -v CONFIG_STATIC \
+	>.config.new
+	mv .config.new .config
+	echo '# CONFIG_STATIC is not set' >>.config
+fi
+
+# If uclibc, build static, and remove some things
+# likely to not work on uclibc.
+if test x"$LIBC" = x"uclibc"; then
+	cat .config \
+	| grep -v CONFIG_STATIC \
+	| grep -v CONFIG_BUILD_LIBBUSYBOX \
+	| grep -v CONFIG_PIE \
+	\
+	| grep -v CONFIG_FEATURE_2_4_MODULES \
+	>.config.new
+	mv .config.new .config
+	echo 'CONFIG_STATIC=y' >>.config
+	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
+fi
+
+# If STATIC, remove some things.
+# PAM with static linking is probably pointless
+# (but I need to try - now I don't have libpam.a on my system, only libpam.so)
+if grep -q "^CONFIG_STATIC=y" .config; then
+	cat .config \
+	| grep -v CONFIG_PAM \
+	>.config.new
+	mv .config.new .config
+	echo '# CONFIG_PAM is not set' >>.config
+fi
+
+# Regenerate .config with default answers for yanked-off options
+# (most of default answers are "no").
+{ yes "" | make oldconfig >/dev/null; } || { echo "oldconfig error"; exit 1; }
+
+# Build!
+nice -n 10 make $MAKEOPTS 2>&1 | tee make.log
+
+# Return exitcode 1 if busybox executable does not exist
+test -x busybox
diff --git a/busybox-1.19.3/scripts/randomtest.loop b/busybox-1.19.3/scripts/randomtest.loop
new file mode 100755
index 0000000..2c8a9bd
--- /dev/null
+++ b/busybox-1.19.3/scripts/randomtest.loop
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+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="i686-"
+export MAKEOPTS="-j9"
+
+cnt=0
+fail=0
+while sleep 1; do
+	echo "Passes: $cnt Failures: $fail"
+	dir="test.$$"
+	while test -e "$dir" -o -e "failed.$dir"; do
+		dir="test.$$.$RANDOM"
+	done
+	echo "Running randconfig test in $dir..."
+	if ! "$1/scripts/randomtest" "$1" "$dir" >/dev/null; then
+		mv -- "$dir" "failed.$dir"
+		echo "Failed build in: failed.$dir"
+		exit 1 # you may comment this out...
+		let fail++
+	else
+		(
+			cd -- "$dir/testsuite" || exit 1
+			echo "Running testsuite in $dir..."
+			SKIP_KNOWN_BUGS=1 SKIP_INTERNET_TESTS=1 ./runtest -v >runtest.log 2>&1
+		)
+		if test $? != 0; then
+			echo "Failed runtest in $dir"
+			exit 1
+		fi
+		tail -n10 -- "$dir/testsuite/runtest.log"
+		rm -rf -- "$dir"
+	fi
+	let cnt++
+done
diff --git a/busybox-1.19.3/scripts/sample_pmap b/busybox-1.19.3/scripts/sample_pmap
new file mode 100755
index 0000000..e7fb457
--- /dev/null
+++ b/busybox-1.19.3/scripts/sample_pmap
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+busybox=../busybox
+
+$busybox sleep 10 &
+pid=$!
+sleep 1
+
+echo "Memory map of '$busybox sleep 10':"
+size $busybox
+pmap $pid | env - grep "^[0-9a-f][0-9a-f]* " | sort -r -t " " -k2,999
diff --git a/busybox-1.19.3/scripts/showasm b/busybox-1.19.3/scripts/showasm
new file mode 100755
index 0000000..b61ab98
--- /dev/null
+++ b/busybox-1.19.3/scripts/showasm
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Copyright 2006 Rob Landley <rob@landley.net>
+# 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
+# there would be a way to get objdump to do this, but I can't find it.
+
+[ $# -lt 1 ] || [ $# -gt 2 ] && { echo "usage: showasm file function"; exit 1; }
+
+[ ! -f $1 ] && { echo "File $1 not found"; exit 1; }
+
+if [ $# -eq 1 ]
+then
+  objdump -d $1 | sed -n -e 's/^[0-9a-fA-F]* <\(.*\)>:$/\1/p'
+  exit 0
+fi
+
+objdump -d $1 | sed -n -e '/./{H;$!d}' -e "x;/^.[0-9a-fA-F]* <$2>:/p"
diff --git a/busybox-1.19.3/scripts/test_make_O b/busybox-1.19.3/scripts/test_make_O
new file mode 100755
index 0000000..a0ee6a8
--- /dev/null
+++ b/busybox-1.19.3/scripts/test_make_O
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+b=`basename $PWD`
+test "${b#busybox}" != "$b" || { echo "Must be run in busybox tree"; exit 1; }
+
+rm -rf ../testdir_make_O.$$
+mkdir ../testdir_make_O.$$
+odir=`cd ../testdir_make_O.$$ && pwd`
+test -d "$odir" || exit 1
+
+make O="$odir" $MAKEOPTS "$@" defconfig busybox 2>&1 | tee test_make_O.log
diff --git a/busybox-1.19.3/scripts/test_make_clean b/busybox-1.19.3/scripts/test_make_clean
new file mode 100755
index 0000000..fa3a543
--- /dev/null
+++ b/busybox-1.19.3/scripts/test_make_clean
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+b=`basename $PWD`
+test "${b#busybox}" != "$b" || { echo "Must be run in busybox tree"; exit 1; }
+
+cd ..
+cp -pPR "$b" busybox.$$.test_tree
+cd busybox.$$.test_tree
+make defconfig
+make $MAKEOPTS
+make clean
+cd ..
+diff -urp "$b" busybox.$$.test_tree >busybox.$$.test_tree.diff
+cat busybox.$$.test_tree.diff
diff --git a/busybox-1.19.3/scripts/trylink b/busybox-1.19.3/scripts/trylink
new file mode 100755
index 0000000..a8b0b2e
--- /dev/null
+++ b/busybox-1.19.3/scripts/trylink
@@ -0,0 +1,310 @@
+#!/bin/sh
+
+debug=false
+
+# Linker flags used:
+#
+# Informational:
+# --warn-common
+# -Map $EXE.map
+# --verbose
+#
+# Optimizations:
+# --sort-common                 reduces padding
+# --sort-section alignment      reduces padding
+# --gc-sections                 throws out unused sections,
+#                               does not work for shared libs
+# -On                           Not used, maybe useful?
+#
+# List of files to link:
+# $l_list                       == --start-group -llib1 -llib2 --end-group
+# --start-group $O_FILES $A_FILES --end-group
+#
+# Shared library link:
+# -shared                       self-explanatory
+# -fPIC                         position-independent code
+# --enable-new-dtags            ?
+# -z,combreloc                  ?
+# -soname="libbusybox.so.$BB_VER"
+# --undefined=lbb_main          Seed name to start pulling from
+#                               (otherwise we'll need --whole-archive)
+# -static                       Not used, but may be useful! manpage:
+#                               "... This option can be used with -shared.
+#                               Doing so means that a shared library
+#                               is being created but that all of the library's
+#                               external references must be resolved by pulling
+#                               in entries from static libraries."
+
+
+try() {
+    printf "%s\n" "Output of:" >$EXE.out
+    printf "%s\n" "$*" >>$EXE.out
+    printf "%s\n" "==========" >>$EXE.out
+    $debug && echo "Trying: $*"
+    $@ >>$EXE.out 2>&1
+    return $?
+}
+
+check_cc() {
+    local tempname="/tmp/temp.$$.$RANDOM"
+    # Can use "-o /dev/null", but older gcc tend to *unlink it* on failure! :(
+    # "-xc": C language. "/dev/null" is an empty source file.
+    if $CC $1 -shared -xc /dev/null -o "$tempname".o >/dev/null 2>&1; then
+	echo "$1";
+    else
+	echo "$2";
+    fi
+    rm "$tempname".o 2>/dev/null
+}
+
+check_libc_is_glibc() {
+    local tempname="/tmp/temp.$$.$RANDOM"
+    echo "\
+	#include <stdlib.h>
+	/* Apparently uclibc defines __GLIBC__ (compat trick?). Oh well. */
+	#if defined(__GLIBC__) && !defined(__UCLIBC__)
+	syntax error here
+	#endif
+	" >"$tempname".c
+    if $CC "$tempname".c -c -o "$tempname".o >/dev/null 2>&1; then
+	echo "$2";
+    else
+	echo "$1";
+    fi
+    rm "$tempname".c "$tempname".o 2>/dev/null
+}
+
+EXE="$1"
+CC="$2"
+CFLAGS="$3"
+LDFLAGS="$4"
+O_FILES="$5"
+A_FILES="$6"
+LDLIBS="$7"
+
+# The --sort-section option is not supported by older versions of ld
+SORT_SECTION=`check_cc "-Wl,--sort-section,alignment" ""`
+
+START_GROUP="-Wl,--start-group"
+END_GROUP="-Wl,--end-group"
+INFO_OPTS="-Wl,--warn-common -Wl,-Map,$EXE.map -Wl,--verbose"
+
+# gold may not support --sort-common (yet)
+SORT_COMMON=`check_cc "-Wl,--sort-common" ""`
+
+# Static linking against glibc produces buggy executables
+# (glibc does not cope well with ld --gc-sections).
+# See sources.redhat.com/bugzilla/show_bug.cgi?id=3400
+# Note that glibc is unsuitable for static linking anyway.
+# We are removing -Wl,--gc-sections from link command line.
+GC_SECTIONS=`(
+. ./.config
+if test x"$CONFIG_STATIC" = x"y"; then
+    check_libc_is_glibc "" "-Wl,--gc-sections"
+else
+    echo "-Wl,--gc-sections"
+fi
+)`
+
+# The --gc-sections option is not supported by older versions of ld
+if test -n "$GC_SECTIONS"; then
+    GC_SECTIONS=`check_cc "$GC_SECTIONS" ""`
+fi
+
+# Sanitize lib list (dups, extra spaces etc)
+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$//'`
+test "x$l_list" != "x" && l_list="$START_GROUP $l_list $END_GROUP"
+try $CC $CFLAGS $LDFLAGS \
+	-o $EXE \
+	$SORT_COMMON \
+	$SORT_SECTION \
+	$GC_SECTIONS \
+	$START_GROUP $O_FILES $A_FILES $END_GROUP \
+	$l_list \
+|| {
+    echo "Failed: $l_list"
+    cat $EXE.out
+    exit 1
+}
+
+# Now try to remove each lib and build without it.
+# Stop when no lib can be removed.
+while test "$LDLIBS"; do
+    $debug && echo "Trying libraries: $LDLIBS"
+    all_needed=true
+    last_needed=false
+    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$//'`
+	test x"$l_list" != x"" && l_list="$START_GROUP $l_list $END_GROUP"
+	$debug && echo "Trying -l options: '$l_list'"
+	try $CC $CFLAGS $LDFLAGS \
+		-o $EXE \
+		$SORT_COMMON \
+		$SORT_SECTION \
+		$GC_SECTIONS \
+		$START_GROUP $O_FILES $A_FILES $END_GROUP \
+		$l_list
+	if test $? = 0; then
+	    echo " Library $one is not needed, excluding it"
+	    LDLIBS="$without_one"
+	    all_needed=false
+	    last_needed=false
+	else
+	    echo " Library $one is needed, can't exclude it (yet)"
+	    last_needed=true
+	fi
+    done
+    # All libs were needed, can't remove any
+    $all_needed && break
+    # Optimization: was the last tried lib needed?
+    if $last_needed; then
+	# Was it the only one lib left? Don't test again then.
+	{ echo "$LDLIBS" | grep -q ' '; } || break
+    fi
+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$//'`
+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
+    try $CC $CFLAGS $LDFLAGS \
+	    -o $EXE \
+	    $SORT_COMMON \
+	    $SORT_SECTION \
+	    $GC_SECTIONS \
+	    $START_GROUP $O_FILES $A_FILES $END_GROUP \
+	    $l_list \
+	    $INFO_OPTS \
+    || {
+	cat $EXE.out
+	exit 1
+    }
+else
+    echo "Custom linker script 'busybox_ldscript' found, using it"
+    # Add SORT_BY_ALIGNMENT to linker script (found in $EXE.out):
+    #  .rodata         : { *(.rodata SORT_BY_ALIGNMENT(.rodata.*) .gnu.linkonce.r.*) }
+    #  *(.data SORT_BY_ALIGNMENT(.data.*) .gnu.linkonce.d.*)
+    #  *(.bss SORT_BY_ALIGNMENT(.bss.*) .gnu.linkonce.b.*)
+    # This will eliminate most of the padding (~3kb).
+    # Hmm, "ld --sort-section alignment" should do it too.
+    try $CC $CFLAGS $LDFLAGS \
+	    -o $EXE \
+	    $SORT_COMMON \
+	    $SORT_SECTION \
+	    $GC_SECTIONS \
+	    -Wl,-T,busybox_ldscript \
+	    $START_GROUP $O_FILES $A_FILES $END_GROUP \
+	    $l_list \
+	    $INFO_OPTS \
+    || {
+	cat $EXE.out
+	exit 1
+    }
+fi
+
+. ./.config
+
+sharedlib_dir="0_lib"
+
+if test "$CONFIG_BUILD_LIBBUSYBOX" = y; then
+    mkdir "$sharedlib_dir" 2>/dev/null
+    test -d "$sharedlib_dir" || {
+	echo "Cannot make directory $sharedlib_dir"
+	exit 1
+    }
+    ln -s "libbusybox.so.$BB_VER" "$sharedlib_dir"/libbusybox.so 2>/dev/null
+
+    EXE="$sharedlib_dir/libbusybox.so.${BB_VER}_unstripped"
+    try $CC $CFLAGS $LDFLAGS \
+	    -o $EXE \
+	    -shared -fPIC \
+	    -Wl,--enable-new-dtags \
+	    -Wl,-z,combreloc \
+	    -Wl,-soname="libbusybox.so.$BB_VER" \
+	    -Wl,--undefined=lbb_main \
+	    $SORT_COMMON \
+	    $SORT_SECTION \
+	    $START_GROUP $A_FILES $END_GROUP \
+	    $l_list \
+	    $INFO_OPTS \
+    || {
+	echo "Linking $EXE failed"
+	cat $EXE.out
+	exit 1
+    }
+    $STRIP -s --remove-section=.note --remove-section=.comment $EXE -o "$sharedlib_dir/libbusybox.so.$BB_VER"
+    chmod a+x "$sharedlib_dir/libbusybox.so.$BB_VER"
+    echo "libbusybox: $sharedlib_dir/libbusybox.so.$BB_VER"
+fi
+
+if test "$CONFIG_FEATURE_SHARED_BUSYBOX" = y; then
+    EXE="$sharedlib_dir/busybox_unstripped"
+    try $CC $CFLAGS $LDFLAGS \
+	    -o $EXE \
+	    $SORT_COMMON \
+	    $SORT_SECTION \
+	    $GC_SECTIONS \
+	    $START_GROUP $O_FILES $END_GROUP \
+	    -L"$sharedlib_dir" -lbusybox \
+	    $l_list \
+	    $INFO_OPTS \
+    || {
+	echo "Linking $EXE failed"
+	cat $EXE.out
+	exit 1
+    }
+    $STRIP -s --remove-section=.note --remove-section=.comment $EXE -o "$sharedlib_dir/busybox"
+    echo "busybox linked against libbusybox: $sharedlib_dir/busybox"
+fi
+
+if test "$CONFIG_FEATURE_INDIVIDUAL" = y; then
+    echo "Linking individual applets against libbusybox (see $sharedlib_dir/*)"
+    gcc -DNAME_MAIN_CNAME -E -include include/autoconf.h include/applets.h \
+    | grep -v "^#" \
+    | grep -v "^$" \
+    > applet_lst.tmp
+    while read name main junk; do
+
+	echo "\
+void lbb_prepare(const char *applet, char **argv);
+int $main(int argc, char **argv);
+
+int main(int argc, char **argv)
+{
+	lbb_prepare(\"$name\", argv);
+	return $main(argc, argv);
+}
+" >"$sharedlib_dir/applet.c"
+
+	EXE="$sharedlib_dir/$name"
+	try $CC $CFLAGS $LDFLAGS "$sharedlib_dir/applet.c" \
+		-o $EXE \
+		$SORT_COMMON \
+		$SORT_SECTION \
+		$GC_SECTIONS \
+		-L"$sharedlib_dir" -lbusybox \
+		-Wl,--warn-common \
+	|| {
+	    echo "Linking $EXE failed"
+	    cat $EXE.out
+	    exit 1
+	}
+	rm -- "$sharedlib_dir/applet.c" $EXE.out
+	$STRIP -s --remove-section=.note --remove-section=.comment $EXE
+
+    done <applet_lst.tmp
+fi
+
+# libbusybox.so is needed only for -lbusybox at link time,
+# it is not needed at runtime. Deleting to reduce confusion.
+rm "$sharedlib_dir"/libbusybox.so 2>/dev/null
+exit 0 # or else we may confuse make
diff --git a/busybox-1.19.3/selinux/Config.src b/busybox-1.19.3/selinux/Config.src
new file mode 100644
index 0000000..47d15b6
--- /dev/null
+++ b/busybox-1.19.3/selinux/Config.src
@@ -0,0 +1,124 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "SELinux Utilities"
+	depends on SELINUX
+
+INSERT
+
+config CHCON
+	bool "chcon"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to change the security context of file.
+
+config FEATURE_CHCON_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on CHCON && LONG_OPTS
+	help
+	  Support long options for the chcon applet.
+
+config GETENFORCE
+	bool "getenforce"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to get the current mode of SELinux.
+
+config GETSEBOOL
+	bool "getsebool"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to get SELinux boolean values.
+
+config LOAD_POLICY
+	bool "load_policy"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to load SELinux policy.
+
+config MATCHPATHCON
+	bool "matchpathcon"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to get default security context of the
+	  specified path from the file contexts configuration.
+
+config RESTORECON
+	bool "restorecon"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to relabel files. The feature is almost
+	  the same as setfiles, but usage is a little different.
+
+config RUNCON
+	bool "runcon"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to run command in speficied security context.
+
+config FEATURE_RUNCON_LONG_OPTIONS
+	bool "Enable long options"
+	default y
+	depends on RUNCON && LONG_OPTS
+	help
+	  Support long options for the runcon applet.
+
+config SELINUXENABLED
+	bool "selinuxenabled"
+	default n
+	depends on SELINUX
+	help
+	  Enable support for this command to be used within shell scripts
+	  to determine if selinux is enabled.
+
+config SETENFORCE
+	bool "setenforce"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to modify the mode SELinux is running in.
+
+config SETFILES
+	bool "setfiles"
+	default n
+	depends on SELINUX
+	help
+	  Enable support to modify to relabel files.
+	  Notice: If you built libselinux with -D_FILE_OFFSET_BITS=64,
+	  (It is default in libselinux's Makefile), you _must_ enable
+	  CONFIG_LFS.
+
+config FEATURE_SETFILES_CHECK_OPTION
+	bool "Enable check option"
+	default n
+	depends on SETFILES
+	help
+	  Support "-c" option (check the validity of the contexts against
+	  the specified binary policy) for setfiles. Requires libsepol.
+
+config SETSEBOOL
+	bool "setsebool"
+	default n
+	depends on SELINUX
+	help
+	  Enable support for change boolean.
+	  semanage and -P option is not supported yet.
+
+config SESTATUS
+	bool "sestatus"
+	default n
+	depends on SELINUX
+	help
+	  Displays the status of SELinux.
+
+endmenu
diff --git a/busybox-1.19.3/selinux/Kbuild.src b/busybox-1.19.3/selinux/Kbuild.src
new file mode 100644
index 0000000..cdd5f2a
--- /dev/null
+++ b/busybox-1.19.3/selinux/Kbuild.src
@@ -0,0 +1,22 @@
+# 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 GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_CHCON)		+= chcon.o
+lib-$(CONFIG_GETENFORCE)	+= getenforce.o
+lib-$(CONFIG_GETSEBOOL)		+= getsebool.o
+lib-$(CONFIG_LOAD_POLICY)	+= load_policy.o
+lib-$(CONFIG_MATCHPATHCON)	+= matchpathcon.o
+lib-$(CONFIG_RUNCON)		+= runcon.o
+lib-$(CONFIG_SELINUXENABLED)	+= selinuxenabled.o
+lib-$(CONFIG_SETENFORCE)	+= setenforce.o
+lib-$(CONFIG_SETFILES)		+= setfiles.o
+lib-$(CONFIG_RESTORECON)	+= setfiles.o
+lib-$(CONFIG_SETSEBOOL)		+= setsebool.o
+lib-$(CONFIG_SESTATUS)		+= sestatus.o
diff --git a/busybox-1.19.3/selinux/chcon.c b/busybox-1.19.3/selinux/chcon.c
new file mode 100644
index 0000000..88d0cfe
--- /dev/null
+++ b/busybox-1.19.3/selinux/chcon.c
@@ -0,0 +1,211 @@
+/*
+ * chcon -- change security context, based on coreutils-5.97-13
+ *
+ * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
+ *
+ * Copyright (C) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//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"
+
+#define OPT_RECURSIVE		(1<<0)	/* 'R' */
+#define OPT_CHANHES		(1<<1)	/* 'c' */
+#define OPT_NODEREFERENCE	(1<<2)	/* 'h' */
+#define OPT_QUIET		(1<<3)	/* 'f' */
+#define OPT_USER		(1<<4)	/* 'u' */
+#define OPT_ROLE		(1<<5)	/* 'r' */
+#define OPT_TYPE		(1<<6)	/* 't' */
+#define OPT_RANGE		(1<<7)	/* 'l' */
+#define OPT_VERBOSE		(1<<8)	/* 'v' */
+#define OPT_REFERENCE		((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS)
+#define OPT_COMPONENT_SPECIFIED	(OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE)
+
+static char *user = NULL;
+static char *role = NULL;
+static char *type = NULL;
+static char *range = NULL;
+static char *specified_context = NULL;
+
+static int FAST_FUNC change_filedir_context(
+		const char *fname,
+		struct stat *stbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	context_t context = NULL;
+	security_context_t file_context = NULL;
+	security_context_t context_string;
+	int rc = FALSE;
+	int status = 0;
+
+	if (option_mask32 & OPT_NODEREFERENCE) {
+		status = lgetfilecon(fname, &file_context);
+	} else {
+		status = getfilecon(fname, &file_context);
+	}
+	if (status < 0 && errno != ENODATA) {
+		if ((option_mask32 & OPT_QUIET) == 0)
+			bb_error_msg("can't obtain security context: %s", fname);
+		goto skip;
+	}
+
+	if (file_context == NULL && specified_context == NULL) {
+		bb_error_msg("can't apply partial context to unlabeled file %s", fname);
+		goto skip;
+	}
+
+	if (specified_context == NULL) {
+		context = set_security_context_component(file_context,
+							 user, role, type, range);
+		if (!context) {
+			bb_error_msg("can't compute security context from %s", file_context);
+			goto skip;
+		}
+	} else {
+		context = context_new(specified_context);
+		if (!context) {
+			bb_error_msg("invalid context: %s", specified_context);
+			goto skip;
+		}
+	}
+
+	context_string = context_str(context);
+	if (!context_string) {
+		bb_error_msg("can't obtain security context in text expression");
+		goto skip;
+	}
+
+	if (file_context == NULL || strcmp(context_string, file_context) != 0) {
+		int fail;
+
+		if (option_mask32 & OPT_NODEREFERENCE) {
+			fail = lsetfilecon(fname, context_string);
+		} else {
+			fail = setfilecon(fname, context_string);
+		}
+		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);
+		}
+		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);
+		}
+	} else if (option_mask32 & OPT_VERBOSE) {
+		printf("context of %s retained as %s\n", fname, context_string);
+		rc = TRUE;
+	}
+skip:
+	context_free(context);
+	freecon(file_context);
+
+	return rc;
+}
+
+#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
+static const char chcon_longopts[] ALIGN1 =
+	"recursive\0"      No_argument       "R"
+	"changes\0"        No_argument       "c"
+	"no-dereference\0" No_argument       "h"
+	"silent\0"         No_argument       "f"
+	"quiet\0"          No_argument       "f"
+	"user\0"           Required_argument "u"
+	"role\0"           Required_argument "r"
+	"type\0"           Required_argument "t"
+	"range\0"          Required_argument "l"
+	"verbose\0"        No_argument       "v"
+	"reference\0"      Required_argument "\xff" /* no short option */
+	;
+#endif
+
+int chcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chcon_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *reference_file;
+	char *fname;
+	int i, errors = 0;
+
+#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
+	applet_long_options = chcon_longopts;
+#endif
+	opt_complementary = "-1"  /* at least 1 param */
+		":?"  /* error if exclusivity constraints are violated */
+#if ENABLE_FEATURE_CHCON_LONG_OPTIONS
+		":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"
+#endif
+		":f--v:v--f";  /* 'verbose' and 'quiet' are exclusive */
+	getopt32(argv, "Rchfu:r:t:l:v",
+		&user, &role, &type, &range, &reference_file);
+	argv += optind;
+
+#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. */
+		if (getfilecon(reference_file, &specified_context) < 0)
+			bb_perror_msg_and_die("getfilecon('%s') failed", reference_file);
+	} else
+#endif
+	if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) {
+		specified_context = *argv++;
+		/* specified_context is never NULL -
+		 * "-1" in opt_complementary prevents this. */
+		if (!argv[0])
+			bb_error_msg_and_die("too few arguments");
+	}
+
+	for (i = 0; (fname = argv[i]) != NULL; i++) {
+		int fname_len = strlen(fname);
+		while (fname_len > 1 && fname[fname_len - 1] == '/')
+			fname_len--;
+		fname[fname_len] = '\0';
+
+		if (recursive_action(fname,
+				     1<<option_mask32 & OPT_RECURSIVE,
+				     change_filedir_context,
+				     change_filedir_context,
+				     NULL, 0) != TRUE)
+			errors = 1;
+	}
+	return errors;
+}
diff --git a/busybox-1.19.3/selinux/getenforce.c b/busybox-1.19.3/selinux/getenforce.c
new file mode 100644
index 0000000..56611d6
--- /dev/null
+++ b/busybox-1.19.3/selinux/getenforce.c
@@ -0,0 +1,38 @@
+/*
+ * getenforce
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ * 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;
+int getenforce_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	int rc;
+
+	rc = is_selinux_enabled();
+	if (rc < 0)
+		bb_error_msg_and_die("is_selinux_enabled() failed");
+
+	if (rc == 1) {
+		rc = security_getenforce();
+		if (rc < 0)
+			bb_error_msg_and_die("getenforce() failed");
+
+		if (rc)
+			puts("Enforcing");
+		else
+			puts("Permissive");
+	} else {
+		puts("Disabled");
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/selinux/getsebool.c b/busybox-1.19.3/selinux/getsebool.c
new file mode 100644
index 0000000..e8f0fef
--- /dev/null
+++ b/busybox-1.19.3/selinux/getsebool.c
@@ -0,0 +1,72 @@
+/*
+ * getsebool
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ * 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;
+int getsebool_main(int argc, char **argv)
+{
+	int i, rc = 0, active, pending, len = 0;
+	char **names;
+	unsigned opt;
+
+	selinux_or_die();
+	opt = getopt32(argv, "a");
+
+	if (opt) { /* -a */
+		if (argc > 2)
+			bb_show_usage();
+
+		rc = security_get_boolean_names(&names, &len);
+		if (rc)
+			bb_perror_msg_and_die("can't get boolean names");
+
+		if (!len) {
+			puts("No booleans");
+			return 0;
+		}
+	}
+
+	if (!len) {
+		if (argc < 2)
+			bb_show_usage();
+		len = argc - 1;
+		names = xmalloc(sizeof(char *) * len);
+		for (i = 0; i < len; i++)
+			names[i] = xstrdup(argv[i + 1]);
+	}
+
+	for (i = 0; i < len; i++) {
+		active = security_get_boolean_active(names[i]);
+		if (active < 0) {
+			bb_error_msg_and_die("error getting active value for %s", names[i]);
+		}
+		pending = security_get_boolean_pending(names[i]);
+		if (pending < 0) {
+			bb_error_msg_and_die("error getting pending value for %s", names[i]);
+		}
+		printf("%s --> %s", names[i], (active ? "on" : "off"));
+		if (pending != active)
+			printf(" pending: %s", (pending ? "on" : "off"));
+		bb_putchar('\n');
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		for (i = 0; i < len; i++)
+			free(names[i]);
+		free(names);
+	}
+
+	return rc;
+}
diff --git a/busybox-1.19.3/selinux/load_policy.c b/busybox-1.19.3/selinux/load_policy.c
new file mode 100644
index 0000000..ce139db
--- /dev/null
+++ b/busybox-1.19.3/selinux/load_policy.c
@@ -0,0 +1,28 @@
+/*
+ * load_policy
+ * Author: Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * 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;
+int load_policy_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	int rc;
+
+	if (argv[1]) {
+		bb_show_usage();
+	}
+
+	rc = selinux_mkload_policy(1);
+	if (rc < 0) {
+		bb_perror_msg_and_die("can't load policy");
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/selinux/matchpathcon.c b/busybox-1.19.3/selinux/matchpathcon.c
new file mode 100644
index 0000000..9e5728e
--- /dev/null
+++ b/busybox-1.19.3/selinux/matchpathcon.c
@@ -0,0 +1,97 @@
+/* matchpathcon  -  get the default security context for the specified
+ *                  path from the file contexts configuration.
+ *                  based on libselinux-1.32
+ * Port to busybox: KaiGai Kohei <kaigai@kaigai.gr.jp>
+ *
+ * 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)
+{
+	char *buf;
+	int rc = matchpathcon(path, 0, &buf);
+	if (rc < 0) {
+		bb_perror_msg("matchpathcon(%s) failed", path);
+		return 1;
+	}
+	if (!noprint)
+		printf("%s\t%s\n", path, buf);
+	else
+		puts(buf);
+
+	freecon(buf);
+	return 0;
+}
+
+#define OPT_NOT_PRINT   (1<<0)  /* -n */
+#define OPT_NOT_TRANS   (1<<1)  /* -N */
+#define OPT_FCONTEXT    (1<<2)  /* -f */
+#define OPT_PREFIX      (1<<3)  /* -p */
+#define OPT_VERIFY      (1<<4)  /* -V */
+
+int matchpathcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int matchpathcon_main(int argc UNUSED_PARAM, char **argv)
+{
+	int error = 0;
+	unsigned opts;
+	char *fcontext, *prefix, *path;
+
+	opt_complementary = "-1" /* at least one param reqd */
+		":?:f--p:p--f"; /* mutually exclusive */
+	opts = getopt32(argv, "nNf:p:V", &fcontext, &prefix);
+	argv += optind;
+
+	if (opts & OPT_NOT_TRANS) {
+		set_matchpathcon_flags(MATCHPATHCON_NOTRANS);
+	}
+	if (opts & OPT_FCONTEXT) {
+		if (matchpathcon_init(fcontext))
+			bb_perror_msg_and_die("error while processing %s", fcontext);
+	}
+	if (opts & OPT_PREFIX) {
+		if (matchpathcon_init_prefix(NULL, prefix))
+			bb_perror_msg_and_die("error while processing %s", prefix);
+	}
+
+	while ((path = *argv++) != NULL) {
+		security_context_t con;
+		int rc;
+
+		if (!(opts & OPT_VERIFY)) {
+			error += print_matchpathcon(path, opts & OPT_NOT_PRINT);
+			continue;
+		}
+
+		if (selinux_file_context_verify(path, 0)) {
+			printf("%s verified\n", path);
+			continue;
+		}
+
+		if (opts & OPT_NOT_TRANS)
+			rc = lgetfilecon_raw(path, &con);
+		else
+			rc = lgetfilecon(path, &con);
+
+		if (rc >= 0) {
+			printf("%s has context %s, should be ", path, con);
+			error += print_matchpathcon(path, 1);
+			freecon(con);
+			continue;
+		}
+		printf("actual context unknown: %s, should be ", strerror(errno));
+		error += print_matchpathcon(path, 1);
+	}
+	matchpathcon_fini();
+	return error;
+}
diff --git a/busybox-1.19.3/selinux/runcon.c b/busybox-1.19.3/selinux/runcon.c
new file mode 100644
index 0000000..3183a22
--- /dev/null
+++ b/busybox-1.19.3/selinux/runcon.c
@@ -0,0 +1,157 @@
+/*
+ * runcon [ context |
+ *         ( [ -c ] [ -r role ] [-t type] [ -u user ] [ -l levelrange ] )
+ *         command [arg1 [arg2 ...] ]
+ *
+ * attempt to run the specified command with the specified context.
+ *
+ * -r role  : use the current context with the specified role
+ * -t type  : use the current context with the specified type
+ * -u user  : use the current context with the specified user
+ * -l level : use the current context with the specified level range
+ * -c       : compute process transition context before modifying
+ *
+ * Contexts are interpreted as follows:
+ *
+ * Number of       MLS
+ * components    system?
+ *
+ *     1            -         type
+ *     2            -         role:type
+ *     3            Y         role:type:range
+ *     3            N         user:role:type
+ *     4            Y         user:role:type:range
+ *     4            N         error
+ *
+ * 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 source tree.
+ */
+
+//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)
+{
+	context_t con;
+	security_context_t cur_context;
+
+	if (getcon(&cur_context))
+		bb_error_msg_and_die("can't get current context");
+
+	if (compute_trans) {
+		security_context_t file_context, new_context;
+
+		if (getfilecon(command, &file_context) < 0)
+			bb_error_msg_and_die("can't retrieve attributes of '%s'",
+					     command);
+		if (security_compute_create(cur_context, file_context,
+					    SECCLASS_PROCESS, &new_context))
+			bb_error_msg_and_die("unable to compute a new context");
+		cur_context = new_context;
+	}
+
+	con = context_new(cur_context);
+	if (!con)
+		bb_error_msg_and_die("'%s' is not a valid context", cur_context);
+	if (user && context_user_set(con, user))
+		bb_error_msg_and_die("can't set new user '%s'", user);
+	if (type && context_type_set(con, type))
+		bb_error_msg_and_die("can't set new type '%s'", type);
+	if (range && context_range_set(con, range))
+		bb_error_msg_and_die("can't set new range '%s'", range);
+	if (role && context_role_set(con, role))
+		bb_error_msg_and_die("can't set new role '%s'", role);
+
+	return con;
+}
+
+#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS
+static const char runcon_longopts[] ALIGN1 =
+	"user\0"    Required_argument "u"
+	"role\0"    Required_argument "r"
+	"type\0"    Required_argument "t"
+	"range\0"   Required_argument "l"
+	"compute\0" No_argument "c"
+	"help\0"    No_argument "h"
+	;
+#endif
+
+#define OPTS_ROLE	(1<<0)	/* r */
+#define OPTS_TYPE	(1<<1)	/* t */
+#define OPTS_USER	(1<<2)	/* u */
+#define OPTS_RANGE	(1<<3)	/* l */
+#define OPTS_COMPUTE	(1<<4)	/* c */
+#define OPTS_HELP	(1<<5)	/* h */
+#define OPTS_CONTEXT_COMPONENT		(OPTS_ROLE | OPTS_TYPE | OPTS_USER | OPTS_RANGE)
+
+int runcon_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int runcon_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *role = NULL;
+	char *range = NULL;
+	char *user = NULL;
+	char *type = NULL;
+	char *context = NULL;
+	unsigned opts;
+	context_t con;
+
+	selinux_or_die();
+
+#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS
+	applet_long_options = runcon_longopts;
+#endif
+	opt_complementary = "-1";
+	opts = getopt32(argv, "r:t:u:l:ch", &role, &type, &user, &range);
+	argv += optind;
+
+	if (!(opts & OPTS_CONTEXT_COMPONENT)) {
+		context = *argv++;
+		if (!argv[0])
+			bb_error_msg_and_die("no command given");
+	}
+
+	if (context) {
+		con = context_new(context);
+		if (!con)
+			bb_error_msg_and_die("'%s' is not a valid context", context);
+	} else {
+		con = runcon_compute_new_context(user, role, type, range,
+				argv[0], opts & OPTS_COMPUTE);
+	}
+
+	if (security_check_context(context_str(con)))
+		bb_error_msg_and_die("'%s' is not a valid context",
+				     context_str(con));
+
+	if (setexeccon(context_str(con)))
+		bb_error_msg_and_die("can't set up security context '%s'",
+				     context_str(con));
+
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/selinux/selinuxenabled.c b/busybox-1.19.3/selinux/selinuxenabled.c
new file mode 100644
index 0000000..ce830dc
--- /dev/null
+++ b/busybox-1.19.3/selinux/selinuxenabled.c
@@ -0,0 +1,19 @@
+/*
+ * selinuxenabled
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ * 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;
+int selinuxenabled_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	return !is_selinux_enabled();
+}
diff --git a/busybox-1.19.3/selinux/sestatus.c b/busybox-1.19.3/selinux/sestatus.c
new file mode 100644
index 0000000..0bd1a0d
--- /dev/null
+++ b/busybox-1.19.3/selinux/sestatus.c
@@ -0,0 +1,211 @@
+/*
+ * sestatus -- displays the status of SELinux
+ *
+ * Ported to busybox: KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * Copyright (C) KaiGai Kohei <kaigai@ak.jp.nec.com>
+ *
+ * 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 COL_FMT  "%-31s "
+
+static void display_boolean(void)
+{
+	char **bools;
+	int i, active, pending, nbool;
+
+	if (security_get_boolean_names(&bools, &nbool) < 0)
+		return;
+
+	puts("\nPolicy booleans:");
+
+	for (i = 0; i < nbool; i++) {
+		active = security_get_boolean_active(bools[i]);
+		if (active < 0)
+			goto skip;
+		pending = security_get_boolean_pending(bools[i]);
+		if (pending < 0)
+			goto skip;
+		printf(COL_FMT "%s",
+		       bools[i], active == 0 ? "off" : "on");
+		if (active != pending)
+			printf(" (%sactivate pending)", pending == 0 ? "in" : "");
+		bb_putchar('\n');
+ skip:
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(bools[i]);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(bools);
+}
+
+static void read_config(char **pc, int npc, char **fc, int nfc)
+{
+	char *buf;
+	parser_t *parser;
+	int pc_ofs = 0, fc_ofs = 0, section = -1;
+
+	pc[0] = fc[0] = NULL;
+
+	parser = config_open("/etc/sestatus.conf");
+	while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) {
+		if (strcmp(buf, "[process]") == 0) {
+			section = 1;
+		} else if (strcmp(buf, "[files]") == 0) {
+			section = 2;
+		} else {
+			if (section == 1 && pc_ofs < npc -1) {
+				pc[pc_ofs++] = xstrdup(buf);
+				pc[pc_ofs] = NULL;
+			} else if (section == 2 && fc_ofs < nfc - 1) {
+				fc[fc_ofs++] = xstrdup(buf);
+				fc[fc_ofs] = NULL;
+			}
+		}
+	}
+	config_close(parser);
+}
+
+static void display_verbose(void)
+{
+	security_context_t con, _con;
+	char *fc[50], *pc[50], *cterm;
+	pid_t *pidList;
+	int i;
+
+	read_config(pc, ARRAY_SIZE(pc), fc, ARRAY_SIZE(fc));
+
+	/* process contexts */
+	puts("\nProcess contexts:");
+
+	/* current context */
+	if (getcon(&con) == 0) {
+		printf(COL_FMT "%s\n", "Current context:", con);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			freecon(con);
+	}
+	/* /sbin/init context */
+	if (getpidcon(1, &con) == 0) {
+		printf(COL_FMT "%s\n", "Init context:", con);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			freecon(con);
+	}
+
+	/* [process] context */
+	for (i = 0; pc[i] != NULL; i++) {
+		pidList = find_pid_by_name(bb_basename(pc[i]));
+		if (pidList[0] > 0 && getpidcon(pidList[0], &con) == 0) {
+			printf(COL_FMT "%s\n", pc[i], con);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				freecon(con);
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(pidList);
+	}
+
+	/* files contexts */
+	puts("\nFile contexts:");
+
+	cterm = xmalloc_ttyname(0);
+//FIXME: if cterm == NULL, we segfault!??
+	puts(cterm);
+	if (cterm && lgetfilecon(cterm, &con) >= 0) {
+		printf(COL_FMT "%s\n", "Controlling term:", con);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			freecon(con);
+	}
+
+	for (i = 0; fc[i] != NULL; i++) {
+		struct stat stbuf;
+
+		if (lgetfilecon(fc[i], &con) < 0)
+			continue;
+		if (lstat(fc[i], &stbuf) == 0) {
+			if (S_ISLNK(stbuf.st_mode)) {
+				if (getfilecon(fc[i], &_con) >= 0) {
+					printf(COL_FMT "%s -> %s\n", fc[i], _con, con);
+					if (ENABLE_FEATURE_CLEAN_UP)
+						freecon(_con);
+				}
+			} else {
+				printf(COL_FMT "%s\n", fc[i], con);
+			}
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			freecon(con);
+	}
+}
+
+int sestatus_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sestatus_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opts;
+	const char *pol_path;
+	int rc;
+
+	opt_complementary = "?0";  /* no arguments are required. */
+	opts = getopt32(argv, "vb");
+
+	/* SELinux status: line */
+	rc = is_selinux_enabled();
+	if (rc < 0)
+		goto error;
+	printf(COL_FMT "%s\n", "SELinux status:",
+	       rc == 1 ? "enabled" : "disabled");
+
+	/* SELinuxfs mount: line */
+	if (!selinux_mnt)
+		goto error;
+	printf(COL_FMT "%s\n", "SELinuxfs mount:",
+	       selinux_mnt);
+
+	/* Current mode: line */
+	rc = security_getenforce();
+	if (rc < 0)
+		goto error;
+	printf(COL_FMT "%s\n", "Current mode:",
+	       rc == 0 ? "permissive" : "enforcing");
+
+	/* Mode from config file: line */
+	if (selinux_getenforcemode(&rc) != 0)
+		goto error;
+	printf(COL_FMT "%s\n", "Mode from config file:",
+	       rc < 0 ? "disabled" : (rc == 0 ? "permissive" : "enforcing"));
+
+	/* Policy version: line */
+	rc = security_policyvers();
+	if (rc < 0)
+		goto error;
+	printf(COL_FMT "%u\n", "Policy version:", rc);
+
+	/* Policy from config file: line */
+	pol_path = selinux_policy_root();
+	if (!pol_path)
+		goto error;
+	printf(COL_FMT "%s\n", "Policy from config file:",
+	       bb_basename(pol_path));
+
+	if (opts & OPT_BOOLEAN)
+		display_boolean();
+	if (opts & OPT_VERBOSE)
+		display_verbose();
+
+	return 0;
+
+  error:
+	bb_perror_msg_and_die("libselinux returns unknown state");
+}
diff --git a/busybox-1.19.3/selinux/setenforce.c b/busybox-1.19.3/selinux/setenforce.c
new file mode 100644
index 0000000..c5bc0a5
--- /dev/null
+++ b/busybox-1.19.3/selinux/setenforce.c
@@ -0,0 +1,47 @@
+/*
+ * setenforce
+ *
+ * Based on libselinux 1.33.1
+ * Port to BusyBox  Hiroshi Shinji <shiroshi@my.email.ne.jp>
+ *
+ * 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
+ * result in security_setenforce(1) being done,
+ * the rest will do security_setenforce(0) */
+static const char *const setenforce_cmd[] = {
+	"0",
+	"1",
+	"permissive",
+	"enforcing",
+	NULL,
+};
+
+int setenforce_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setenforce_main(int argc UNUSED_PARAM, char **argv)
+{
+	int i, rc;
+
+	if (!argv[1] || argv[2])
+		bb_show_usage();
+
+	selinux_or_die();
+
+	for (i = 0; setenforce_cmd[i]; i++) {
+		if (strcasecmp(argv[1], setenforce_cmd[i]) != 0)
+			continue;
+		rc = security_setenforce(i & 1);
+		if (rc < 0)
+			bb_perror_msg_and_die("setenforce() failed");
+		return 0;
+	}
+
+	bb_show_usage();
+}
diff --git a/busybox-1.19.3/selinux/setfiles.c b/busybox-1.19.3/selinux/setfiles.c
new file mode 100644
index 0000000..ca3fd93
--- /dev/null
+++ b/busybox-1.19.3/selinux/setfiles.c
@@ -0,0 +1,680 @@
+/*
+  setfiles: based on policycoreutils 2.0.19
+  policycoreutils was released under GPL 2.
+  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>
+#endif
+
+#define MAX_EXCLUDES 50
+
+struct edir {
+	char *directory;
+	size_t size;
+};
+
+struct globals {
+	FILE *outfile;
+	char *policyfile;
+	char *rootpath;
+	int rootpathlen;
+	unsigned count;
+	int excludeCtr;
+	int errors;
+	int verbose; /* getopt32 uses it, has to be int */
+	smallint recurse; /* Recursive descent */
+	smallint follow_mounts;
+	/* Behavior flags determined based on setfiles vs. restorecon */
+	smallint expand_realpath;  /* Expand paths via realpath */
+	smallint abort_on_error; /* Abort the file tree walk upon an error */
+	int add_assoc; /* Track inode associations for conflict detection */
+	int matchpathcon_flags; /* Flags to matchpathcon */
+	dev_t dev_id; /* Device id where target file exists */
+	int nerr;
+	struct edir excludeArray[MAX_EXCLUDES];
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+void BUG_setfiles_globals_too_big(void);
+#define INIT_G() do { \
+	if (sizeof(G) > COMMON_BUFSIZE) \
+		BUG_setfiles_globals_too_big(); \
+	/* memset(&G, 0, sizeof(G)); - already is */ \
+} while (0)
+#define outfile            (G.outfile           )
+#define policyfile         (G.policyfile        )
+#define rootpath           (G.rootpath          )
+#define rootpathlen        (G.rootpathlen       )
+#define count              (G.count             )
+#define excludeCtr         (G.excludeCtr        )
+#define errors             (G.errors            )
+#define verbose            (G.verbose           )
+#define recurse            (G.recurse           )
+#define follow_mounts      (G.follow_mounts     )
+#define expand_realpath    (G.expand_realpath   )
+#define abort_on_error     (G.abort_on_error    )
+#define add_assoc          (G.add_assoc         )
+#define matchpathcon_flags (G.matchpathcon_flags)
+#define dev_id             (G.dev_id            )
+#define nerr               (G.nerr              )
+#define excludeArray       (G.excludeArray      )
+
+/* Must match getopt32 string! */
+enum {
+	OPT_d = (1 << 0),
+	OPT_e = (1 << 1),
+	OPT_f = (1 << 2),
+	OPT_i = (1 << 3),
+	OPT_l = (1 << 4),
+	OPT_n = (1 << 5),
+	OPT_p = (1 << 6),
+	OPT_q = (1 << 7),
+	OPT_r = (1 << 8),
+	OPT_s = (1 << 9),
+	OPT_v = (1 << 10),
+	OPT_o = (1 << 11),
+	OPT_F = (1 << 12),
+	OPT_W = (1 << 13),
+	OPT_c = (1 << 14), /* c only for setfiles */
+	OPT_R = (1 << 14), /* R only for restorecon */
+};
+#define FLAG_d_debug         (option_mask32 & OPT_d)
+#define FLAG_e               (option_mask32 & OPT_e)
+#define FLAG_f               (option_mask32 & OPT_f)
+#define FLAG_i_ignore_enoent (option_mask32 & OPT_i)
+#define FLAG_l_take_log      (option_mask32 & OPT_l)
+#define FLAG_n_dry_run       (option_mask32 & OPT_n)
+#define FLAG_p_progress      (option_mask32 & OPT_p)
+#define FLAG_q_quiet         (option_mask32 & OPT_q)
+#define FLAG_r               (option_mask32 & OPT_r)
+#define FLAG_s               (option_mask32 & OPT_s)
+#define FLAG_v               (option_mask32 & OPT_v)
+#define FLAG_o               (option_mask32 & OPT_o)
+#define FLAG_F_force         (option_mask32 & OPT_F)
+#define FLAG_W_warn_no_match (option_mask32 & OPT_W)
+#define FLAG_c               (option_mask32 & OPT_c)
+#define FLAG_R               (option_mask32 & OPT_R)
+
+
+static void qprintf(const char *fmt UNUSED_PARAM, ...)
+{
+	/* quiet, do nothing */
+}
+
+static void inc_err(void)
+{
+	nerr++;
+	if (nerr > 9 && !FLAG_d_debug) {
+		bb_error_msg_and_die("exiting after 10 errors");
+	}
+}
+
+static void add_exclude(const char *directory)
+{
+	struct stat sb;
+	size_t len;
+
+	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);
+		return;
+	}
+	if ((sb.st_mode & S_IFDIR) == 0) {
+		bb_error_msg("\"%s\" is not a directory: mode %o, ignoring",
+			directory, sb.st_mode);
+		return;
+	}
+	if (excludeCtr == MAX_EXCLUDES) {
+		bb_error_msg_and_die("maximum excludes %d exceeded", MAX_EXCLUDES);
+	}
+
+	len = strlen(directory);
+	while (len > 1 && directory[len - 1] == '/') {
+		len--;
+	}
+	excludeArray[excludeCtr].directory = xstrndup(directory, len);
+	excludeArray[excludeCtr++].size = len;
+}
+
+static bool exclude(const char *file)
+{
+	int i = 0;
+	for (i = 0; i < excludeCtr; i++) {
+		if (strncmp(file, excludeArray[i].directory,
+					excludeArray[i].size) == 0) {
+			if (file[excludeArray[i].size] == '\0'
+			 || file[excludeArray[i].size] == '/') {
+				return 1;
+			}
+		}
+	}
+	return 0;
+}
+
+static int match(const char *name, struct stat *sb, char **con)
+{
+	int ret;
+	char path[PATH_MAX + 1];
+	char *tmp_path = xstrdup(name);
+
+	if (excludeCtr > 0 && exclude(name)) {
+		goto err;
+	}
+	ret = lstat(name, sb);
+	if (ret) {
+		if (FLAG_i_ignore_enoent && errno == ENOENT) {
+			free(tmp_path);
+			return 0;
+		}
+		bb_error_msg("stat(%s)", name);
+		goto err;
+	}
+
+	if (expand_realpath) {
+		if (S_ISLNK(sb->st_mode)) {
+			char *p = NULL;
+			char *file_sep;
+
+			size_t len = 0;
+
+			if (verbose > 1)
+				bb_error_msg("warning! %s refers to a symbolic link, not following last component", name);
+
+			file_sep = strrchr(tmp_path, '/');
+			if (file_sep == tmp_path) {
+				file_sep++;
+				path[0] = '\0';
+				p = path;
+			} else if (file_sep) {
+				*file_sep++ = '\0';
+				p = realpath(tmp_path, path);
+			} else {
+				file_sep = tmp_path;
+				p = realpath("./", path);
+			}
+			if (p)
+				len = strlen(p);
+			if (!p || len + strlen(file_sep) + 2 > PATH_MAX) {
+				bb_perror_msg("realpath(%s) failed", name);
+				goto err;
+			}
+			p += len;
+			/* ensure trailing slash of directory name */
+			if (len == 0 || p[-1] != '/') {
+				*p++ = '/';
+			}
+			strcpy(p, file_sep);
+			name = path;
+			if (excludeCtr > 0 && exclude(name))
+				goto err;
+
+		} else {
+			char *p;
+			p = realpath(name, path);
+			if (!p) {
+				bb_perror_msg("realpath(%s)", name);
+				goto err;
+			}
+			name = p;
+			if (excludeCtr > 0 && exclude(name))
+				goto err;
+		}
+	}
+
+	/* name will be what is matched in the policy */
+	if (NULL != rootpath) {
+		if (0 != strncmp(rootpath, name, rootpathlen)) {
+			bb_error_msg("%s is not located in %s",
+				name, rootpath);
+			goto err;
+		}
+		name += rootpathlen;
+	}
+
+	free(tmp_path);
+	if (rootpath != NULL && name[0] == '\0')
+		/* this is actually the root dir of the alt root */
+		return matchpathcon_index("/", sb->st_mode, con);
+	return matchpathcon_index(name, sb->st_mode, con);
+ err:
+	free(tmp_path);
+	return -1;
+}
+
+/* Compare two contexts to see if their differences are "significant",
+ * or whether the only difference is in the user. */
+static bool only_changed_user(const char *a, const char *b)
+{
+	if (FLAG_F_force)
+		return 0;
+	if (!a || !b)
+		return 0;
+	a = strchr(a, ':'); /* Rest of the context after the user */
+	b = strchr(b, ':');
+	if (!a || !b)
+		return 0;
+	return (strcmp(a, b) == 0);
+}
+
+static int restore(const char *file)
+{
+	char *my_file;
+	struct stat my_sb;
+	int i, j, ret;
+	char *context = NULL;
+	char *newcon = NULL;
+	bool user_only_changed = 0;
+	int retval = 0;
+
+	my_file = bb_simplify_path(file);
+
+	i = match(my_file, &my_sb, &newcon);
+
+	if (i < 0) /* No matching specification. */
+		goto out;
+
+	if (FLAG_p_progress) {
+		count++;
+		if (count % 0x400 == 0) { /* every 1024 times */
+			count = (count % (80*0x400));
+			if (count == 0)
+				bb_putchar('\n');
+			bb_putchar('*');
+			fflush_all();
+		}
+	}
+
+	/*
+	 * Try to add an association between this inode and
+	 * this specification. If there is already an association
+	 * for this inode and it conflicts with this specification,
+	 * then use the last matching specification.
+	 */
+	if (add_assoc) {
+		j = matchpathcon_filespec_add(my_sb.st_ino, i, my_file);
+		if (j < 0)
+			goto err;
+
+		if (j != i) {
+			/* There was already an association and it took precedence. */
+			goto out;
+		}
+	}
+
+	if (FLAG_d_debug)
+		printf("%s: %s matched by %s\n", applet_name, my_file, newcon);
+
+	/* Get the current context of the file. */
+	ret = lgetfilecon_raw(my_file, &context);
+	if (ret < 0) {
+		if (errno == ENODATA) {
+			context = NULL; /* paranoia */
+		} else {
+			bb_perror_msg("lgetfilecon_raw on %s", my_file);
+			goto err;
+		}
+		user_only_changed = 0;
+	} else
+		user_only_changed = only_changed_user(context, newcon);
+
+	/*
+	 * Do not relabel the file if the matching specification is
+	 * <<none>> or the file is already labeled according to the
+	 * specification.
+	 */
+	if ((strcmp(newcon, "<<none>>") == 0)
+	 || (context && (strcmp(context, newcon) == 0) && !FLAG_F_force)) {
+		goto out;
+	}
+
+	if (!FLAG_F_force && context && (is_context_customizable(context) > 0)) {
+		if (verbose > 1) {
+			bb_error_msg("skipping %s. %s is customizable_types",
+				my_file, context);
+		}
+		goto out;
+	}
+
+	if (verbose) {
+		/* If we're just doing "-v", trim out any relabels where
+		 * the user has changed but the role and type are the
+		 * same.  For "-vv", emit everything. */
+		if (verbose > 1 || !user_only_changed) {
+			bb_info_msg("%s: reset %s context %s->%s",
+				applet_name, my_file, context ? context : "", newcon);
+		}
+	}
+
+	if (FLAG_l_take_log && !user_only_changed) {
+		if (context)
+			bb_info_msg("relabeling %s from %s to %s", my_file, context, newcon);
+		else
+			bb_info_msg("labeling %s to %s", my_file, newcon);
+	}
+
+	if (outfile && !user_only_changed)
+		fprintf(outfile, "%s\n", my_file);
+
+	/*
+	 * Do not relabel the file if -n was used.
+	 */
+	if (FLAG_n_dry_run || user_only_changed)
+		goto out;
+
+	/*
+	 * Relabel the file to the specified context.
+	 */
+	ret = lsetfilecon(my_file, newcon);
+	if (ret) {
+		bb_perror_msg("lsetfileconon(%s,%s)", my_file, newcon);
+		goto err;
+	}
+
+ out:
+	freecon(context);
+	freecon(newcon);
+	free(my_file);
+	return retval;
+ err:
+	retval--; /* -1 */
+	goto out;
+}
+
+/*
+ * Apply the last matching specification to a file.
+ * This function is called by recursive_action on each file during
+ * the directory traversal.
+ */
+static int FAST_FUNC apply_spec(
+		const char *file,
+		struct stat *sb,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	if (!follow_mounts) {
+		/* setfiles does not process across different mount points */
+		if (sb->st_dev != dev_id) {
+			return SKIP;
+		}
+	}
+	errors |= restore(file);
+	if (abort_on_error && errors)
+		return FALSE;
+	return TRUE;
+}
+
+
+static int canoncon(const char *path, unsigned lineno, char **contextp)
+{
+	static const char err_msg[] ALIGN1 = "%s: line %u has invalid context %s";
+
+	char *tmpcon;
+	char *context = *contextp;
+	int invalid = 0;
+
+#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
+	if (policyfile) {
+		if (sepol_check_context(context) >= 0)
+			return 0;
+		/* Exit immediately if we're in checking mode. */
+		bb_error_msg_and_die(err_msg, path, lineno, context);
+	}
+#endif
+
+	if (security_canonicalize_context_raw(context, &tmpcon) < 0) {
+		if (errno != ENOENT) {
+			invalid = 1;
+			inc_err();
+		}
+	} else {
+		free(context);
+		*contextp = tmpcon;
+	}
+
+	if (invalid) {
+		bb_error_msg(err_msg, path, lineno, context);
+	}
+
+	return invalid;
+}
+
+static int process_one(char *name)
+{
+	struct stat sb;
+	int rc;
+
+	rc = lstat(name, &sb);
+	if (rc < 0) {
+		if (FLAG_i_ignore_enoent && errno == ENOENT)
+			return 0;
+		bb_perror_msg("stat(%s)", name);
+		goto err;
+	}
+	dev_id = sb.st_dev;
+
+	if (S_ISDIR(sb.st_mode) && recurse) {
+		if (recursive_action(name,
+				     ACTION_RECURSE,
+				     apply_spec,
+				     apply_spec,
+				     NULL, 0) != TRUE) {
+			bb_error_msg("error while labeling %s", name);
+			goto err;
+		}
+	} else {
+		rc = restore(name);
+		if (rc)
+			goto err;
+	}
+
+ out:
+	if (add_assoc) {
+		if (FLAG_q_quiet)
+			set_matchpathcon_printf(&qprintf);
+		matchpathcon_filespec_eval();
+		set_matchpathcon_printf(NULL);
+		matchpathcon_filespec_destroy();
+	}
+
+	return rc;
+
+ err:
+	rc = -1;
+	goto out;
+}
+
+int setfiles_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setfiles_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat sb;
+	int rc, i = 0;
+	const char *input_filename = NULL;
+	char *buf = NULL;
+	size_t buf_len;
+	int flags;
+	llist_t *exclude_dir = NULL;
+	char *out_filename = NULL;
+
+	INIT_G();
+
+	if (applet_name[0] == 's') { /* "setfiles" */
+		/*
+		 * setfiles:
+		 * Recursive descent,
+		 * Does not expand paths via realpath,
+		 * Aborts on errors during the file tree walk,
+		 * Try to track inode associations for conflict detection,
+		 * Does not follow mounts,
+		 * Validates all file contexts at init time.
+		 */
+		recurse = 1;
+		abort_on_error = 1;
+		add_assoc = 1;
+		/* follow_mounts = 0; - already is */
+		matchpathcon_flags = MATCHPATHCON_VALIDATE | MATCHPATHCON_NOTRANS;
+	} else {
+		/*
+		 * restorecon:
+		 * No recursive descent unless -r/-R,
+		 * Expands paths via realpath,
+		 * Do not abort on errors during the file tree walk,
+		 * Do not try to track inode associations for conflict detection,
+		 * Follows mounts,
+		 * Does lazy validation of contexts upon use.
+		 */
+		expand_realpath = 1;
+		follow_mounts = 1;
+		matchpathcon_flags = MATCHPATHCON_NOTRANS;
+		/* restorecon only */
+		selinux_or_die();
+	}
+
+	set_matchpathcon_flags(matchpathcon_flags);
+
+	opt_complementary = "e::vv:v--p:p--v:v--q:q--v";
+	/* Option order must match OPT_x definitions! */
+	if (applet_name[0] == 'r') { /* restorecon */
+		flags = getopt32(argv, "de:f:ilnpqrsvo:FWR",
+			&exclude_dir, &input_filename, &out_filename, &verbose);
+	} else { /* setfiles */
+		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,)
+			&verbose);
+	}
+	argv += optind;
+
+#if ENABLE_FEATURE_SETFILES_CHECK_OPTION
+	if ((applet_name[0] == 's') && (flags & OPT_c)) {
+		FILE *policystream;
+
+		policystream = xfopen_for_read(policyfile);
+		if (sepol_set_policydb_from_file(policystream) < 0) {
+			bb_error_msg_and_die("sepol_set_policydb_from_file on %s", policyfile);
+		}
+		fclose(policystream);
+
+		/* Only process the specified file_contexts file, not
+		   any .homedirs or .local files, and do not perform
+		   context translations. */
+		set_matchpathcon_flags(MATCHPATHCON_BASEONLY |
+				       MATCHPATHCON_NOTRANS |
+				       MATCHPATHCON_VALIDATE);
+	}
+#endif
+
+	while (exclude_dir)
+		add_exclude(llist_pop(&exclude_dir));
+
+	if (flags & OPT_o) {
+		outfile = stdout;
+		if (NOT_LONE_CHAR(out_filename, '-')) {
+			outfile = xfopen_for_write(out_filename);
+		}
+	}
+	if (applet_name[0] == 'r') { /* restorecon */
+		if (flags & (OPT_r | OPT_R))
+			recurse = 1;
+	} else { /* setfiles */
+		if (flags & OPT_r)
+			rootpathlen = strlen(rootpath);
+	}
+	if (flags & OPT_s) {
+		input_filename = "-";
+		add_assoc = 0;
+	}
+
+	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. */
+		set_matchpathcon_canoncon(&canoncon);
+		if (!argv[0])
+			bb_show_usage();
+		xstat(argv[0], &sb);
+		if (!S_ISREG(sb.st_mode)) {
+			bb_error_msg_and_die("spec file %s is not a regular file", argv[0]);
+		}
+		/* Load the file contexts configuration and check it. */
+		rc = matchpathcon_init(argv[0]);
+		if (rc < 0) {
+			bb_simple_perror_msg_and_die(argv[0]);
+		}
+		if (nerr)
+			exit(EXIT_FAILURE);
+		argv++;
+	}
+
+	if (input_filename) {
+		ssize_t len;
+		FILE *f = stdin;
+
+		if (NOT_LONE_CHAR(input_filename, '-'))
+			f = xfopen_for_read(input_filename);
+		while ((len = getline(&buf, &buf_len, f)) > 0) {
+			buf[len - 1] = '\0';
+			errors |= process_one(buf);
+		}
+		if (ENABLE_FEATURE_CLEAN_UP)
+			fclose_if_not_stdin(f);
+	} else {
+		if (!argv[0])
+			bb_show_usage();
+		for (i = 0; argv[i]; i++) {
+			errors |= process_one(argv[i]);
+		}
+	}
+
+	if (FLAG_W_warn_no_match)
+		matchpathcon_checkmatches(argv[0]);
+
+	if (ENABLE_FEATURE_CLEAN_UP && outfile)
+		fclose(outfile);
+
+	return errors;
+}
diff --git a/busybox-1.19.3/selinux/setsebool.c b/busybox-1.19.3/selinux/setsebool.c
new file mode 100644
index 0000000..ec682e5
--- /dev/null
+++ b/busybox-1.19.3/selinux/setsebool.c
@@ -0,0 +1,41 @@
+/*
+ * setsebool
+ * Simple setsebool
+ * NOTE: -P option requires libsemanage, so this feature is
+ * omitted in this version
+ * Yuichi Nakamura <ynakam@hitachisoft.jp>
+ *
+ * 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;
+int setsebool_main(int argc, char **argv)
+{
+	char *p;
+	int value;
+
+	if (argc != 3)
+		bb_show_usage();
+
+	p = argv[2];
+
+	if (LONE_CHAR(p, '1') || strcasecmp(p, "true") == 0 || strcasecmp(p, "on") == 0) {
+		value = 1;
+	} else if (LONE_CHAR(p, '0') || strcasecmp(p, "false") == 0 || strcasecmp(p, "off") == 0) {
+		value = 0;
+	} else {
+		bb_show_usage();
+	}
+
+	if (security_set_boolean(argv[1], value) < 0)
+		bb_error_msg_and_die("can't set boolean");
+
+	return 0;
+}
diff --git a/busybox-1.19.3/shell/Config.src b/busybox-1.19.3/shell/Config.src
new file mode 100644
index 0000000..b31e62d
--- /dev/null
+++ b/busybox-1.19.3/shell/Config.src
@@ -0,0 +1,149 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Shells"
+
+INSERT
+
+
+choice
+	prompt "Choose which shell is aliased to 'sh' name"
+	default FEATURE_SH_IS_ASH
+	help
+	  Choose which shell you want to be executed by 'sh' alias.
+	  The ash shell is the most bash compatible and full featured one.
+
+# note: cannot use "select ASH" here, it breaks "make allnoconfig"
+config FEATURE_SH_IS_ASH
+	depends on ASH
+	bool "ash"
+	depends on !NOMMU
+
+config FEATURE_SH_IS_HUSH
+	depends on HUSH
+	bool "hush"
+
+config FEATURE_SH_IS_NONE
+	bool "none"
+
+endchoice
+
+choice
+	prompt "Choose which shell is aliased to 'bash' name"
+	default FEATURE_BASH_IS_NONE
+	help
+	  Choose which shell you want to be executed by 'bash' alias.
+	  The ash shell is the most bash compatible and full featured one.
+
+	  Note that selecting this option does not switch on any bash
+	  compatibility code. It merely makes it possible to install
+	  /bin/bash (sym)link and run scripts which start with
+	  #!/bin/bash line.
+
+	  Many systems use it in scripts which use bash-specific features,
+	  even simple ones like $RANDOM. Without this option, busybox
+	  can't be used for running them because it won't recongnize
+	  "bash" as a supported applet name.
+
+config FEATURE_BASH_IS_ASH
+	depends on ASH
+	bool "ash"
+	depends on !NOMMU
+
+config FEATURE_BASH_IS_HUSH
+	depends on HUSH
+	bool "hush"
+
+config FEATURE_BASH_IS_NONE
+	bool "none"
+
+endchoice
+
+
+config SH_MATH_SUPPORT
+	bool "POSIX math support"
+	default y
+	depends on ASH || HUSH
+	help
+	  Enable math support in the shell via $((...)) syntax.
+
+config SH_MATH_SUPPORT_64
+	bool "Extend POSIX math support to 64 bit"
+	default y
+	depends on SH_MATH_SUPPORT
+	help
+	  Enable 64-bit math support in the shell. This will make the shell
+	  slightly larger, but will allow computation with very large numbers.
+	  This is not in POSIX, so do not rely on this in portable code.
+
+config FEATURE_SH_EXTRA_QUIET
+	bool "Hide message on interactive shell startup"
+	default y
+	depends on HUSH || ASH
+	help
+	  Remove the busybox introduction when starting a shell.
+
+config FEATURE_SH_STANDALONE
+	bool "Standalone shell"
+	default n
+	depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
+	help
+	  This option causes busybox shells to use busybox applets
+	  in preference to executables in the PATH whenever possible. For
+	  example, entering the command 'ifconfig' into the shell would cause
+	  busybox to use the ifconfig busybox applet. Specifying the fully
+	  qualified executable name, such as '/sbin/ifconfig' will still
+	  execute the /sbin/ifconfig executable on the filesystem. This option
+	  is generally used when creating a statically linked version of busybox
+	  for use as a rescue shell, in the event that you screw up your system.
+
+	  This is implemented by re-execing /proc/self/exe (typically)
+	  with right parameters. Some selected applets ("NOFORK" applets)
+	  can even be executed without creating new process.
+	  Instead, busybox will call <applet>_main() internally.
+
+	  However, this causes problems in chroot jails without mounted /proc
+	  and with ps/top (command name can be shown as 'exe' for applets
+	  started this way).
+# untrue?
+#	  Note that this will *also* cause applets to take precedence
+#	  over shell builtins of the same name. So turning this on will
+#	  eliminate any performance gained by turning on the builtin "echo"
+#	  and "test" commands in ash.
+# untrue?
+#	  Note that when using this option, the shell will attempt to directly
+#	  run '/bin/busybox'. If you do not have the busybox binary sitting in
+#	  that exact location with that exact name, this option will not work at
+#	  all.
+
+config FEATURE_SH_NOFORK
+	bool "Run 'nofork' applets directly"
+	default n
+	depends on (HUSH || ASH) && FEATURE_PREFER_APPLETS
+	help
+	  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
+	  NOFORK in include/applets.h).
+
+	  This may significantly speed up some shell scripts.
+
+	  This feature is relatively new. Use with care. Report bugs
+	  to project mailing list.
+
+config FEATURE_SH_HISTFILESIZE
+	bool "Use $HISTFILESIZE"
+	default y
+	depends on HUSH || ASH
+	help
+	  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.
+
+
+endmenu
diff --git a/busybox-1.19.3/shell/Kbuild.src b/busybox-1.19.3/shell/Kbuild.src
new file mode 100644
index 0000000..c00aec9
--- /dev/null
+++ b/busybox-1.19.3/shell/Kbuild.src
@@ -0,0 +1,11 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+
+lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
diff --git a/busybox-1.19.3/shell/README b/busybox-1.19.3/shell/README
new file mode 100644
index 0000000..6a9f5b6
--- /dev/null
+++ b/busybox-1.19.3/shell/README
@@ -0,0 +1,82 @@
+http://www.opengroup.org/onlinepubs/9699919799/
+Open Group Base Specifications Issue 7
+
+
+http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap01.html
+Shell & Utilities
+
+It says that any of the standard utilities may be implemented
+as a regular shell built-in. It gives a list of utilities which
+are usually implemented that way (and some of them can only
+be implemented as built-ins, like "alias"):
+
+alias
+bg
+cd
+command
+false
+fc
+fg
+getopts
+jobs
+kill
+newgrp
+pwd
+read
+true
+umask
+unalias
+wait
+
+
+http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
+Shell Command Language
+
+It says that shell must implement special built-ins. Special built-ins
+differ from regular ones by the fact that variable assignments
+done on special builtin are *PRESERVED*. That is,
+
+VAR=VAL special_builtin; echo $VAR
+
+should print VAL.
+
+(Another distinction is that an error in special built-in should
+abort the shell, but this is not such a critical difference,
+and moreover, at least bash's "set" does not follow this rule,
+which is even codified in autoconf configure logic now...)
+
+List of special builtins:
+
+. file
+: [argument...]
+break [n]
+continue [n]
+eval [argument...]
+exec [command [argument...]]
+exit [n]
+export name[=word]...
+export -p
+readonly name[=word]...
+readonly -p
+return [n]
+set [-abCefhmnuvx] [-o option] [argument...]
+set [+abCefhmnuvx] [+o option] [argument...]
+set -- [argument...]
+set -o
+set +o
+shift [n]
+times
+trap n [condition...]
+trap [action condition...]
+unset [-fv] name...
+
+In practice, no one uses this obscure feature - none of these builtins
+gives any special reasons to play such dirty tricks.
+
+However. This section also says that *function invocation* should act
+similar to special built-in. That is, variable assignments
+done on function invocation should be preserved after function invocation.
+
+This is significant: it is not unthinkable to want to run a function
+with some variables set to special values. But because of the above,
+it does not work: variable will "leak" out of the function.
diff --git a/busybox-1.19.3/shell/README.job b/busybox-1.19.3/shell/README.job
new file mode 100644
index 0000000..d5ba965
--- /dev/null
+++ b/busybox-1.19.3/shell/README.job
@@ -0,0 +1,304 @@
+strace of "sleep 1 | sleep 2" being run from interactive bash 3.0
+
+
+Synopsis:
+open /dev/tty [, if fails, open ttyname(0)]; close /* helps re-establish ctty */
+get current signal mask
+TCGETS on fd# 0
+TCGETS on fd# 2 /* NB: if returns ENOTTY (2>/dev/null), sh seems to disable job control,
+                   does not show prompt, but still executes cmds from fd# 0 */
+install default handlers for CHLD QUIT TERM
+install common handler for HUP INT ILL TRAP ABRT FPE BUS SEGV SYS PIPE ALRM TERM XCPU XFSZ VTALRM USR1 USR2
+ignore QUIT
+install handler for INT
+ignore TERM
+install handler for INT
+ignore TSTP TTOU TTIN
+install handler for WINCH
+get pid, ppid
+block all signals
+unblock all signals
+get our pprocess group
+    minidoc:
+    Each process group is a member of a session and each process is a member
+    of the session of which its process group is a member.
+    Process groups are used for distribution of signals, and by terminals
+    to arbitrate requests for their input: processes that have the same
+    process group as the terminal are foreground and may read, while others
+    will block with a signal if they attempt to read.  These calls are thus used
+    by programs (shells) to create process groups in implementing job control.
+    The TIOCGPGRP and TIOCSPGRP calls described in termios(3) are used to get/set
+    the process group of the control terminal.
+    If a session has a controlling terminal, CLOCAL is not set and a hangup occurs,
+    then the session leader is sent a SIGHUP.  If the session leader exits,
+    the SIGHUP signal will be sent to each process in the foreground process
+    group of the controlling terminal.
+    If the exit of the process causes a process group to become orphaned,
+    and if any member of the newly-orphaned process group is stopped, then a SIGHUP
+    signal followed by a SIGCONT signal will be sent to each process
+    in the newly-orphaned process group.
+...
+dup stderr to fd# 255
+move ourself to our own process group
+block CHLD TSTP TTIN TTOU
+set tty's (255, stderr's) foreground process group to our group
+allow all signals
+mark 255 CLOEXEC
+set CHLD handler
+get signal mask
+get fd#0 flags
+get signal mask
+set INT handler
+block CHLD TSTP TTIN TTOU
+set fd #255 foreground process group to our group
+allow all signals
+set INT handler
+block all signals
+allow all signals
+block INT
+allow all signals
+lotsa sigactions: set INT,ALRM,WINCH handlers, ignore TERM,QUIT,TSTP,TTOU,TTIN
+block all signals
+allow all signals
+block all signals
+allow all signals
+block all signals
+allow all signals
+read "sleep 1 | sleep 2\n"
+block INT
+TCSETSW on fd# 0
+allow all signals
+lotsa sigactions: set INT,ALRM,WINCH handlers, ignore TERM,QUIT,TSTP,TTOU,TTIN
+block CHLD
+pipe([4, 5])  /* oops seems I lost another pipe() in editing... */
+fork child #1
+put child in it's own process group
+block only CHLD
+close(5)
+block only INT CHLD
+fork child #2
+put child in the same process group as first one
+block only CHLD
+close(4)
+block only CHLD
+block only CHLD TSTP TTIN TTOU
+set fd# 255 foreground process group to first child's one
+block only CHLD
+block only CHLD
+block only CHLD
+/* note: because shell is not in foreground now, e.g. Ctrl-C will send INT to children only! */
+wait4 for children to die or stop - first child exits
+wait4 for children to die or stop - second child exits
+block CHLD TSTP TTIN TTOU
+set fd# 255 foreground process group to our own one
+block only CHLD
+block only CHLD
+block nothing
+--- SIGCHLD (Child exited) @ 0 (0) ---
+    wait for it - no child (already waited for)
+    sigreturn()
+read signal mask
+lotsa sigactions...
+read next command
+
+
+execve("/bin/sh", ["sh"], [/* 34 vars */]) = 0
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
+ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
+rt_sigaction(SIGCHLD, {SIG_DFL}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGQUIT, {SIG_DFL}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGTERM, {SIG_DFL}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGHUP, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGINT, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGILL, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGTRAP, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGABRT, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGFPE, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGBUS, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGSEGV, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGSYS, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGPIPE, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGALRM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGTERM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGXCPU, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGXFSZ, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGVTALRM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGUSR1, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGUSR2, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTERM, {SIG_IGN}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTSTP, {SIG_IGN}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGTTOU, {SIG_IGN}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGTTIN, {SIG_IGN}, {SIG_DFL}, 8) = 0
+rt_sigaction(SIGWINCH, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+getpid()                = 19473
+getppid()               = 19472
+rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+getpgrp()               = 1865
+dup(2)                  = 4
+fcntl64(255, F_GETFD)   = -1 EBADF (Bad file descriptor)
+dup2(4, 255)            = 255
+close(4)                = 0
+ioctl(255, TIOCGPGRP, [1865]) = 0
+getpid()                = 19473
+setpgid(0, 19473)       = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0
+ioctl(255, TIOCSPGRP, [19473]) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+fcntl64(255, F_SETFD, FD_CLOEXEC) = 0
+rt_sigaction(SIGCHLD, {0x807c922, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_DFL}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+fcntl64(0, F_GETFL)     = 0x2 (flags O_RDWR)
+...
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0
+ioctl(255, TIOCSPGRP, [19473]) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigaction(SIGINT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTERM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTERM, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGQUIT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGQUIT, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGALRM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTSTP, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTSTP, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTTOU, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTOU, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTTIN, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTIN, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGWINCH, {0x80ca5cd, [], SA_RESTORER|SA_RESTART, 0x6ff7a4f8}, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+write(2, "sh-3.00# ", 9) = 9
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+read(0, "s", 1)         = 1
+write(2, "s", 1)        = 1
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+read(0, "l", 1)         = 1
+write(2, "l", 1)        = 1
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+... rest of "sleep 1 | sleep 2" entered...
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+read(0, "2", 1)         = 1
+write(2, "2", 1)        = 1
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+read(0, "\r", 1)        = 1
+write(2, "\n", 1)       = 1
+rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
+ioctl(0, SNDCTL_TMR_STOP or TCSETSW, {B38400 opost isig icanon echo ...}) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTERM, {SIG_IGN}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGALRM, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTSTP, {SIG_IGN}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTOU, {SIG_IGN}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTIN, {SIG_IGN}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGWINCH, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, {0x80ca5cd, [], SA_RESTORER|SA_RESTART, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
+pipe([4, 5])            = 0
+rt_sigprocmask(SIG_BLOCK, [INT CHLD], [CHLD], 8) = 0
+fork()                  = 19755
+setpgid(19755, 19755)                = 0
+rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
+close(5)                = 0
+rt_sigprocmask(SIG_BLOCK, [INT CHLD], [CHLD], 8) = 0
+fork()                  = 19756
+setpgid(19756, 19755)   = 0
+rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
+close(4)                = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [CHLD], 8) = 0
+ioctl(255, TIOCSPGRP, [19755]) = 0
+rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
+rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD], [CHLD], 8) = 0
+wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WUNTRACED, NULL) = 19755
+wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WUNTRACED, NULL) = 19756
+rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [CHLD], 8) = 0
+ioctl(255, TIOCSPGRP, [19473]) = 0
+rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
+rt_sigprocmask(SIG_SETMASK, [CHLD], NULL, 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+--- SIGCHLD (Child exited) @ 0 (0) ---
+wait4(-1, 0x77fc9c54, WNOHANG|WUNTRACED, NULL) = -1 ECHILD (No child processes)
+sigreturn()             = ? (mask now [])
+rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0
+ioctl(255, TIOCSPGRP, [19473]) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigaction(SIGINT, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigaction(SIGINT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTERM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTERM, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGQUIT, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGQUIT, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGALRM, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {0x808f752, [HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTSTP, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTSTP, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTTOU, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTOU, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGTTIN, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTIN, {SIG_IGN}, {0x80ca530, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGWINCH, {0x80ca5cd, [], SA_RESTORER|SA_RESTART, 0x6ff7a4f8}, {0x807dc33, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+write(2, "sh-3.00# ", 9) = 9
+
+
+getpid() = 19755
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigaction(SIGTSTP, {SIG_DFL}, {SIG_IGN}, 8)    = 0
+rt_sigaction(SIGTTIN, {SIG_DFL}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTOU, {SIG_DFL}, {SIG_IGN}, 8) = 0
+setpgid(19755, 19755) = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0
+ioctl(255, TIOCSPGRP, [19755]) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+close(4)   = 0
+dup2(5, 1) = 1
+close(5)              = 0
+rt_sigaction(SIGINT, {SIG_DFL}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGQUIT, {SIG_DFL}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTERM, {SIG_DFL}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGCHLD, {SIG_DFL}, {0x807c922, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+execve("/bin/sleep", ["sleep", "1"], [/* 34 vars */]) = 0
+...
+_exit(0)                = ?
+
+
+getpid() = 19756
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+rt_sigaction(SIGTSTP, {SIG_DFL}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTIN, {SIG_DFL}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTTOU, {SIG_DFL}, {SIG_IGN}, 8) = 0
+setpgid(19756, 19755) = 0
+rt_sigprocmask(SIG_BLOCK, [CHLD TSTP TTIN TTOU], [], 8) = 0
+ioctl(255, TIOCSPGRP, [19755]) = 0
+rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
+dup2(4, 0) = 0
+close(4) = 0
+rt_sigaction(SIGINT, {SIG_DFL}, {0x808f7d7, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+rt_sigaction(SIGQUIT, {SIG_DFL}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGTERM, {SIG_DFL}, {SIG_IGN}, 8) = 0
+rt_sigaction(SIGCHLD, {SIG_DFL}, {0x807c922, [], SA_RESTORER, 0x6ff7a4f8}, 8) = 0
+execve("/bin/sleep", ["sleep", "2"], [/* 34 vars */]) = 0
+...
+_exit(0)                = ?
diff --git a/busybox-1.19.3/shell/ash.c b/busybox-1.19.3/shell/ash.c
new file mode 100644
index 0000000..d48cd01
--- /dev/null
+++ b/busybox-1.19.3/shell/ash.c
@@ -0,0 +1,13256 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ash shell port for busybox
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Original BSD copyright notice is retained at the end of this file.
+ *
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
+ * was re-ported from NetBSD and debianized.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * The following should be set to reflect the type of system you have:
+ *      JOBS -> 1 if you have Berkeley job control, 0 otherwise.
+ *      define SYSV if you are running under System V.
+ *      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.
+ */
+#define DEBUG 0
+/* Tweak debug output verbosity here */
+#define DEBUG_TIME 0
+#define DEBUG_PID 1
+#define DEBUG_SIG 1
+
+#define PROFILE 0
+
+#define JOBS ENABLE_ASH_JOB_CONTROL
+
+#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"
+#if ENABLE_SH_MATH_SUPPORT
+# include "math.h"
+#endif
+#if ENABLE_ASH_RANDOM_SUPPORT
+# include "random.h"
+#else
+# define CLEAR_RANDOM_T(rnd) ((void)0)
+#endif
+
+#include "NUM_APPLETS.h"
+#if NUM_APPLETS == 1
+/* STANDALONE does not make sense, and won't compile */
+# undef CONFIG_FEATURE_SH_STANDALONE
+# undef ENABLE_FEATURE_SH_STANDALONE
+# undef IF_FEATURE_SH_STANDALONE
+# undef IF_NOT_FEATURE_SH_STANDALONE
+# define ENABLE_FEATURE_SH_STANDALONE 0
+# define IF_FEATURE_SH_STANDALONE(...)
+# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
+#endif
+
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096           /* amount of buffering in a pipe */
+#endif
+
+#if !BB_MMU
+# 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. */
+
+#define VTABSIZE 39
+#define ATABSIZE 39
+#define CMDTABLESIZE 31         /* should be prime */
+
+
+/* ============ Shell options */
+
+static const char *const optletters_optnames[] = {
+	"e"   "errexit",
+	"f"   "noglob",
+	"I"   "ignoreeof",
+	"i"   "interactive",
+	"m"   "monitor",
+	"n"   "noexec",
+	"s"   "stdin",
+	"x"   "xtrace",
+	"v"   "verbose",
+	"C"   "noclobber",
+	"a"   "allexport",
+	"b"   "notify",
+	"u"   "nounset",
+	"\0"  "vi"
+#if ENABLE_ASH_BASH_COMPAT
+	,"\0"  "pipefail"
+#endif
+#if DEBUG
+	,"\0"  "nolog"
+	,"\0"  "debug"
+#endif
+};
+
+#define optletters(n)  optletters_optnames[n][0]
+#define optnames(n)   (optletters_optnames[n] + 1)
+
+enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
+
+
+/* ============ Misc data */
+
+#define msg_illnum "Illegal number: %s"
+
+/*
+ * We enclose jmp_buf in a structure so that we can declare pointers to
+ * jump locations.  The global variable handler contains the location to
+ * jump to when an exception occurs, and the global variable exception_type
+ * contains a code identifying the exception.  To implement nested
+ * exception handlers, the user should save the value of handler on entry
+ * to an inner scope, set handler to point to a jmploc structure for the
+ * inner scope, and restore handler on exit from the scope.
+ */
+struct jmploc {
+	jmp_buf loc;
+};
+
+struct globals_misc {
+	/* pid of main shell */
+	int rootpid;
+	/* shell level: 0 for the main shell, 1 for its children, and so on */
+	int shlvl;
+#define rootshell (!shlvl)
+	char *minusc;  /* argument to -c option */
+
+	char *curdir; // = nullstr;     /* current working directory */
+	char *physdir; // = nullstr;    /* physical working directory */
+
+	char *arg0; /* value of $0 */
+
+	struct jmploc *exception_handler;
+
+	volatile int suppress_int; /* counter */
+	volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
+	/* last pending signal */
+	volatile /*sig_atomic_t*/ smallint pending_sig;
+	smallint exception_type; /* kind of exception (0..5) */
+	/* exceptions */
+#define EXINT 0         /* SIGINT received */
+#define EXERROR 1       /* a generic error */
+#define EXSHELLPROC 2   /* execute a shell procedure */
+#define EXEXEC 3        /* command execution failed */
+#define EXEXIT 4        /* exit the shell */
+#define EXSIG 5         /* trapped signal in wait(1) */
+
+	smallint isloginsh;
+	char nullstr[1];        /* zero length string */
+
+	char optlist[NOPTS];
+#define eflag optlist[0]
+#define fflag optlist[1]
+#define Iflag optlist[2]
+#define iflag optlist[3]
+#define mflag optlist[4]
+#define nflag optlist[5]
+#define sflag optlist[6]
+#define xflag optlist[7]
+#define vflag optlist[8]
+#define Cflag optlist[9]
+#define aflag optlist[10]
+#define bflag optlist[11]
+#define uflag optlist[12]
+#define viflag optlist[13]
+#if ENABLE_ASH_BASH_COMPAT
+# define pipefail optlist[14]
+#else
+# define pipefail 0
+#endif
+#if DEBUG
+# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT]
+# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT]
+#endif
+
+	/* trap handler commands */
+	/*
+	 * Sigmode records the current value of the signal handlers for the various
+	 * modes.  A value of zero means that the current handler is not known.
+	 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
+	 */
+	char sigmode[NSIG - 1];
+#define S_DFL      1            /* default signal handling (SIG_DFL) */
+#define S_CATCH    2            /* signal is caught */
+#define S_IGN      3            /* signal is ignored (SIG_IGN) */
+#define S_HARD_IGN 4            /* signal is ignored permenantly */
+
+	/* indicates specified signal received */
+	uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
+	uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
+	char *trap[NSIG];
+	char **trap_ptr;        /* used only by "trap hack" */
+
+	/* Rarely referenced stuff */
+#if ENABLE_ASH_RANDOM_SUPPORT
+	random_t random_gen;
+#endif
+	pid_t backgndpid;        /* pid of last background process */
+	smallint job_warning;    /* user was warned about stopped jobs (can be 2, 1 or 0). */
+};
+extern struct globals_misc *const ash_ptr_to_globals_misc;
+#define G_misc (*ash_ptr_to_globals_misc)
+#define rootpid     (G_misc.rootpid    )
+#define shlvl       (G_misc.shlvl      )
+#define minusc      (G_misc.minusc     )
+#define curdir      (G_misc.curdir     )
+#define physdir     (G_misc.physdir    )
+#define arg0        (G_misc.arg0       )
+#define exception_handler (G_misc.exception_handler)
+#define exception_type    (G_misc.exception_type   )
+#define suppress_int      (G_misc.suppress_int     )
+#define pending_int       (G_misc.pending_int      )
+#define pending_sig       (G_misc.pending_sig      )
+#define isloginsh   (G_misc.isloginsh  )
+#define nullstr     (G_misc.nullstr    )
+#define optlist     (G_misc.optlist    )
+#define sigmode     (G_misc.sigmode    )
+#define gotsig      (G_misc.gotsig     )
+#define may_have_traps    (G_misc.may_have_traps   )
+#define trap        (G_misc.trap       )
+#define trap_ptr    (G_misc.trap_ptr   )
+#define random_gen  (G_misc.random_gen )
+#define backgndpid  (G_misc.backgndpid )
+#define job_warning (G_misc.job_warning)
+#define INIT_G_misc() do { \
+	(*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
+	barrier(); \
+	curdir = nullstr; \
+	physdir = nullstr; \
+	trap_ptr = trap; \
+} while (0)
+
+
+/* ============ DEBUG */
+#if DEBUG
+static void trace_printf(const char *fmt, ...);
+static void trace_vprintf(const char *fmt, va_list va);
+# define TRACE(param)    trace_printf param
+# define TRACEV(param)   trace_vprintf param
+# define close(fd) do { \
+	int dfd = (fd); \
+	if (close(dfd) < 0) \
+		bb_error_msg("bug on %d: closing %d(0x%x)", \
+			__LINE__, dfd, dfd); \
+} while (0)
+#else
+# define TRACE(param)
+# define TRACEV(param)
+#endif
+
+
+/* ============ Utility functions */
+#define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
+
+static int isdigit_str9(const char *str)
+{
+	int maxlen = 9 + 1; /* max 9 digits: 999999999 */
+	while (--maxlen && isdigit(*str))
+		str++;
+	return (*str == '\0');
+}
+
+static const char *var_end(const char *var)
+{
+	while (*var)
+		if (*var++ == '=')
+			break;
+	return 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
+ * much more efficient and portable.  (But hacking the kernel is so much
+ * more fun than worrying about efficiency and portability. :-))
+ */
+#define INT_OFF do { \
+	suppress_int++; \
+	xbarrier(); \
+} while (0)
+
+/*
+ * Called to raise an exception.  Since C doesn't include exceptions, we
+ * just do a longjmp to the exception handler.  The type of exception is
+ * stored in the global variable "exception_type".
+ */
+static void raise_exception(int) NORETURN;
+static void
+raise_exception(int e)
+{
+#if DEBUG
+	if (exception_handler == NULL)
+		abort();
+#endif
+	INT_OFF;
+	exception_type = e;
+	longjmp(exception_handler->loc, 1);
+}
+#if DEBUG
+#define raise_exception(e) do { \
+	TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
+	raise_exception(e); \
+} while (0)
+#endif
+
+/*
+ * Called from trap.c when a SIGINT is received.  (If the user specifies
+ * that SIGINT is to be trapped or ignored using the trap builtin, then
+ * this routine is not called.)  Suppressint is nonzero when interrupts
+ * are held using the INT_OFF macro.  (The test for iflag is just
+ * defensive programming.)
+ */
+static void raise_interrupt(void) NORETURN;
+static void
+raise_interrupt(void)
+{
+	int ex_type;
+
+	pending_int = 0;
+	/* Signal is not automatically unmasked after it is raised,
+	 * do it ourself - unmask all signals */
+	sigprocmask_allsigs(SIG_UNBLOCK);
+	/* pending_sig = 0; - now done in signal_handler() */
+
+	ex_type = EXSIG;
+	if (gotsig[SIGINT - 1] && !trap[SIGINT]) {
+		if (!(rootshell && iflag)) {
+			/* Kill ourself with SIGINT */
+			signal(SIGINT, SIG_DFL);
+			raise(SIGINT);
+		}
+		ex_type = EXINT;
+	}
+	raise_exception(ex_type);
+	/* NOTREACHED */
+}
+#if DEBUG
+#define raise_interrupt() do { \
+	TRACE(("raising interrupt on line %d\n", __LINE__)); \
+	raise_interrupt(); \
+} while (0)
+#endif
+
+static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
+int_on(void)
+{
+	xbarrier();
+	if (--suppress_int == 0 && pending_int) {
+		raise_interrupt();
+	}
+}
+#define INT_ON int_on()
+static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
+force_int_on(void)
+{
+	xbarrier();
+	suppress_int = 0;
+	if (pending_int)
+		raise_interrupt();
+}
+#define FORCE_INT_ON force_int_on()
+
+#define SAVE_INT(v) ((v) = suppress_int)
+
+#define RESTORE_INT(v) do { \
+	xbarrier(); \
+	suppress_int = (v); \
+	if (suppress_int == 0 && pending_int) \
+		raise_interrupt(); \
+} while (0)
+
+
+/* ============ Stdout/stderr output */
+
+static void
+outstr(const char *p, FILE *file)
+{
+	INT_OFF;
+	fputs(p, file);
+	INT_ON;
+}
+
+static void
+flush_stdout_stderr(void)
+{
+	INT_OFF;
+	fflush_all();
+	INT_ON;
+}
+
+static void
+outcslow(int c, FILE *dest)
+{
+	INT_OFF;
+	putc(c, dest);
+	fflush(dest);
+	INT_ON;
+}
+
+static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
+static int
+out1fmt(const char *fmt, ...)
+{
+	va_list ap;
+	int r;
+
+	INT_OFF;
+	va_start(ap, fmt);
+	r = vprintf(fmt, ap);
+	va_end(ap);
+	INT_ON;
+	return r;
+}
+
+static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
+static int
+fmtstr(char *outbuf, size_t length, const char *fmt, ...)
+{
+	va_list ap;
+	int ret;
+
+	va_start(ap, fmt);
+	INT_OFF;
+	ret = vsnprintf(outbuf, length, fmt, ap);
+	va_end(ap);
+	INT_ON;
+	return ret;
+}
+
+static void
+out1str(const char *p)
+{
+	outstr(p, stdout);
+}
+
+static void
+out2str(const char *p)
+{
+	outstr(p, stderr);
+	flush_stdout_stderr();
+}
+
+
+/* ============ Parser structures */
+
+/* control characters in argument strings */
+#define CTL_FIRST CTLESC
+#define CTLESC       ((unsigned char)'\201')    /* escape next character */
+#define CTLVAR       ((unsigned char)'\202')    /* variable defn */
+#define CTLENDVAR    ((unsigned char)'\203')
+#define CTLBACKQ     ((unsigned char)'\204')
+#define CTLQUOTE 01             /* ored with CTLBACKQ code if in quotes */
+/*      CTLBACKQ | CTLQUOTE == '\205' */
+#define CTLARI       ((unsigned char)'\206')    /* arithmetic expression */
+#define CTLENDARI    ((unsigned char)'\207')
+#define CTLQUOTEMARK ((unsigned char)'\210')
+#define CTL_LAST CTLQUOTEMARK
+
+/* variable substitution byte (follows CTLVAR) */
+#define VSTYPE  0x0f            /* type of variable substitution */
+#define VSNUL   0x10            /* colon--treat the empty string as unset */
+#define VSQUOTE 0x80            /* inside double quotes--suppress splitting */
+
+/* values of VSTYPE field */
+#define VSNORMAL        0x1     /* normal variable:  $var or ${var} */
+#define VSMINUS         0x2     /* ${var-text} */
+#define VSPLUS          0x3     /* ${var+text} */
+#define VSQUESTION      0x4     /* ${var?message} */
+#define VSASSIGN        0x5     /* ${var=text} */
+#define VSTRIMRIGHT     0x6     /* ${var%pattern} */
+#define VSTRIMRIGHTMAX  0x7     /* ${var%%pattern} */
+#define VSTRIMLEFT      0x8     /* ${var#pattern} */
+#define VSTRIMLEFTMAX   0x9     /* ${var##pattern} */
+#define VSLENGTH        0xa     /* ${#var} */
+#if ENABLE_ASH_BASH_COMPAT
+#define VSSUBSTR        0xc     /* ${var:position:length} */
+#define VSREPLACE       0xd     /* ${var/pattern/replacement} */
+#define VSREPLACEALL    0xe     /* ${var//pattern/replacement} */
+#endif
+
+static const char dolatstr[] ALIGN1 = {
+	CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'
+};
+
+#define NCMD      0
+#define NPIPE     1
+#define NREDIR    2
+#define NBACKGND  3
+#define NSUBSHELL 4
+#define NAND      5
+#define NOR       6
+#define NSEMI     7
+#define NIF       8
+#define NWHILE    9
+#define NUNTIL   10
+#define NFOR     11
+#define NCASE    12
+#define NCLIST   13
+#define NDEFUN   14
+#define NARG     15
+#define NTO      16
+#if ENABLE_ASH_BASH_COMPAT
+#define NTO2     17
+#endif
+#define NCLOBBER 18
+#define NFROM    19
+#define NFROMTO  20
+#define NAPPEND  21
+#define NTOFD    22
+#define NFROMFD  23
+#define NHERE    24
+#define NXHERE   25
+#define NNOT     26
+#define N_NUMBER 27
+
+union node;
+
+struct ncmd {
+	smallint type; /* Nxxxx */
+	union node *assign;
+	union node *args;
+	union node *redirect;
+};
+
+struct npipe {
+	smallint type;
+	smallint pipe_backgnd;
+	struct nodelist *cmdlist;
+};
+
+struct nredir {
+	smallint type;
+	union node *n;
+	union node *redirect;
+};
+
+struct nbinary {
+	smallint type;
+	union node *ch1;
+	union node *ch2;
+};
+
+struct nif {
+	smallint type;
+	union node *test;
+	union node *ifpart;
+	union node *elsepart;
+};
+
+struct nfor {
+	smallint type;
+	union node *args;
+	union node *body;
+	char *var;
+};
+
+struct ncase {
+	smallint type;
+	union node *expr;
+	union node *cases;
+};
+
+struct nclist {
+	smallint type;
+	union node *next;
+	union node *pattern;
+	union node *body;
+};
+
+struct narg {
+	smallint type;
+	union node *next;
+	char *text;
+	struct nodelist *backquote;
+};
+
+/* nfile and ndup layout must match!
+ * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
+ * that it is actually NTO2 (>&file), and change its type.
+ */
+struct nfile {
+	smallint type;
+	union node *next;
+	int fd;
+	int _unused_dupfd;
+	union node *fname;
+	char *expfname;
+};
+
+struct ndup {
+	smallint type;
+	union node *next;
+	int fd;
+	int dupfd;
+	union node *vname;
+	char *_unused_expfname;
+};
+
+struct nhere {
+	smallint type;
+	union node *next;
+	int fd;
+	union node *doc;
+};
+
+struct nnot {
+	smallint type;
+	union node *com;
+};
+
+union node {
+	smallint type;
+	struct ncmd ncmd;
+	struct npipe npipe;
+	struct nredir nredir;
+	struct nbinary nbinary;
+	struct nif nif;
+	struct nfor nfor;
+	struct ncase ncase;
+	struct nclist nclist;
+	struct narg narg;
+	struct nfile nfile;
+	struct ndup ndup;
+	struct nhere nhere;
+	struct nnot nnot;
+};
+
+/*
+ * NODE_EOF is returned by parsecmd when it encounters an end of file.
+ * It must be distinct from NULL.
+ */
+#define NODE_EOF ((union node *) -1L)
+
+struct nodelist {
+	struct nodelist *next;
+	union node *n;
+};
+
+struct funcnode {
+	int count;
+	union node n;
+};
+
+/*
+ * Free a parse tree.
+ */
+static void
+freefunc(struct funcnode *f)
+{
+	if (f && --f->count < 0)
+		free(f);
+}
+
+
+/* ============ Debugging output */
+
+#if DEBUG
+
+static FILE *tracefile;
+
+static void
+trace_printf(const char *fmt, ...)
+{
+	va_list va;
+
+	if (debug != 1)
+		return;
+	if (DEBUG_TIME)
+		fprintf(tracefile, "%u ", (int) time(NULL));
+	if (DEBUG_PID)
+		fprintf(tracefile, "[%u] ", (int) getpid());
+	if (DEBUG_SIG)
+		fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
+	va_start(va, fmt);
+	vfprintf(tracefile, fmt, va);
+	va_end(va);
+}
+
+static void
+trace_vprintf(const char *fmt, va_list va)
+{
+	if (debug != 1)
+		return;
+	if (DEBUG_TIME)
+		fprintf(tracefile, "%u ", (int) time(NULL));
+	if (DEBUG_PID)
+		fprintf(tracefile, "[%u] ", (int) getpid());
+	if (DEBUG_SIG)
+		fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
+	vfprintf(tracefile, fmt, va);
+}
+
+static void
+trace_puts(const char *s)
+{
+	if (debug != 1)
+		return;
+	fputs(s, tracefile);
+}
+
+static void
+trace_puts_quoted(char *s)
+{
+	char *p;
+	char c;
+
+	if (debug != 1)
+		return;
+	putc('"', tracefile);
+	for (p = s; *p; p++) {
+		switch ((unsigned char)*p) {
+		case '\n': c = 'n'; goto backslash;
+		case '\t': c = 't'; goto backslash;
+		case '\r': c = 'r'; goto backslash;
+		case '\"': c = '\"'; goto backslash;
+		case '\\': c = '\\'; goto backslash;
+		case CTLESC: c = 'e'; goto backslash;
+		case CTLVAR: c = 'v'; goto backslash;
+		case CTLVAR+CTLQUOTE: c = 'V'; goto backslash;
+		case CTLBACKQ: c = 'q'; goto backslash;
+		case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash;
+ backslash:
+			putc('\\', tracefile);
+			putc(c, tracefile);
+			break;
+		default:
+			if (*p >= ' ' && *p <= '~')
+				putc(*p, tracefile);
+			else {
+				putc('\\', tracefile);
+				putc((*p >> 6) & 03, tracefile);
+				putc((*p >> 3) & 07, tracefile);
+				putc(*p & 07, tracefile);
+			}
+			break;
+		}
+	}
+	putc('"', tracefile);
+}
+
+static void
+trace_puts_args(char **ap)
+{
+	if (debug != 1)
+		return;
+	if (!*ap)
+		return;
+	while (1) {
+		trace_puts_quoted(*ap);
+		if (!*++ap) {
+			putc('\n', tracefile);
+			break;
+		}
+		putc(' ', tracefile);
+	}
+}
+
+static void
+opentrace(void)
+{
+	char s[100];
+#ifdef O_APPEND
+	int flags;
+#endif
+
+	if (debug != 1) {
+		if (tracefile)
+			fflush(tracefile);
+		/* leave open because libedit might be using it */
+		return;
+	}
+	strcpy(s, "./trace");
+	if (tracefile) {
+		if (!freopen(s, "a", tracefile)) {
+			fprintf(stderr, "Can't re-open %s\n", s);
+			debug = 0;
+			return;
+		}
+	} else {
+		tracefile = fopen(s, "a");
+		if (tracefile == NULL) {
+			fprintf(stderr, "Can't open %s\n", s);
+			debug = 0;
+			return;
+		}
+	}
+#ifdef O_APPEND
+	flags = fcntl(fileno(tracefile), F_GETFL);
+	if (flags >= 0)
+		fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
+#endif
+	setlinebuf(tracefile);
+	fputs("\nTracing started.\n", tracefile);
+}
+
+static void
+indent(int amount, char *pfx, FILE *fp)
+{
+	int i;
+
+	for (i = 0; i < amount; i++) {
+		if (pfx && i == amount - 1)
+			fputs(pfx, fp);
+		putc('\t', fp);
+	}
+}
+
+/* little circular references here... */
+static void shtree(union node *n, int ind, char *pfx, FILE *fp);
+
+static void
+sharg(union node *arg, FILE *fp)
+{
+	char *p;
+	struct nodelist *bqlist;
+	unsigned char subtype;
+
+	if (arg->type != NARG) {
+		out1fmt("<node type %d>\n", arg->type);
+		abort();
+	}
+	bqlist = arg->narg.backquote;
+	for (p = arg->narg.text; *p; p++) {
+		switch ((unsigned char)*p) {
+		case CTLESC:
+			p++;
+			putc(*p, fp);
+			break;
+		case CTLVAR:
+			putc('$', fp);
+			putc('{', fp);
+			subtype = *++p;
+			if (subtype == VSLENGTH)
+				putc('#', fp);
+
+			while (*p != '=') {
+				putc(*p, fp);
+				p++;
+			}
+
+			if (subtype & VSNUL)
+				putc(':', fp);
+
+			switch (subtype & VSTYPE) {
+			case VSNORMAL:
+				putc('}', fp);
+				break;
+			case VSMINUS:
+				putc('-', fp);
+				break;
+			case VSPLUS:
+				putc('+', fp);
+				break;
+			case VSQUESTION:
+				putc('?', fp);
+				break;
+			case VSASSIGN:
+				putc('=', fp);
+				break;
+			case VSTRIMLEFT:
+				putc('#', fp);
+				break;
+			case VSTRIMLEFTMAX:
+				putc('#', fp);
+				putc('#', fp);
+				break;
+			case VSTRIMRIGHT:
+				putc('%', fp);
+				break;
+			case VSTRIMRIGHTMAX:
+				putc('%', fp);
+				putc('%', fp);
+				break;
+			case VSLENGTH:
+				break;
+			default:
+				out1fmt("<subtype %d>", subtype);
+			}
+			break;
+		case CTLENDVAR:
+			putc('}', fp);
+			break;
+		case CTLBACKQ:
+		case CTLBACKQ|CTLQUOTE:
+			putc('$', fp);
+			putc('(', fp);
+			shtree(bqlist->n, -1, NULL, fp);
+			putc(')', fp);
+			break;
+		default:
+			putc(*p, fp);
+			break;
+		}
+	}
+}
+
+static void
+shcmd(union node *cmd, FILE *fp)
+{
+	union node *np;
+	int first;
+	const char *s;
+	int dftfd;
+
+	first = 1;
+	for (np = cmd->ncmd.args; np; np = np->narg.next) {
+		if (!first)
+			putc(' ', fp);
+		sharg(np, fp);
+		first = 0;
+	}
+	for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
+		if (!first)
+			putc(' ', fp);
+		dftfd = 0;
+		switch (np->nfile.type) {
+		case NTO:      s = ">>"+1; dftfd = 1; break;
+		case NCLOBBER: s = ">|"; dftfd = 1; break;
+		case NAPPEND:  s = ">>"; dftfd = 1; break;
+#if ENABLE_ASH_BASH_COMPAT
+		case NTO2:
+#endif
+		case NTOFD:    s = ">&"; dftfd = 1; break;
+		case NFROM:    s = "<"; break;
+		case NFROMFD:  s = "<&"; break;
+		case NFROMTO:  s = "<>"; break;
+		default:       s = "*error*"; break;
+		}
+		if (np->nfile.fd != dftfd)
+			fprintf(fp, "%d", np->nfile.fd);
+		fputs(s, fp);
+		if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
+			fprintf(fp, "%d", np->ndup.dupfd);
+		} else {
+			sharg(np->nfile.fname, fp);
+		}
+		first = 0;
+	}
+}
+
+static void
+shtree(union node *n, int ind, char *pfx, FILE *fp)
+{
+	struct nodelist *lp;
+	const char *s;
+
+	if (n == NULL)
+		return;
+
+	indent(ind, pfx, fp);
+
+	if (n == NODE_EOF) {
+		fputs("<EOF>", fp);
+		return;
+	}
+
+	switch (n->type) {
+	case NSEMI:
+		s = "; ";
+		goto binop;
+	case NAND:
+		s = " && ";
+		goto binop;
+	case NOR:
+		s = " || ";
+ binop:
+		shtree(n->nbinary.ch1, ind, NULL, fp);
+		/* if (ind < 0) */
+			fputs(s, fp);
+		shtree(n->nbinary.ch2, ind, NULL, fp);
+		break;
+	case NCMD:
+		shcmd(n, fp);
+		if (ind >= 0)
+			putc('\n', fp);
+		break;
+	case NPIPE:
+		for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
+			shtree(lp->n, 0, NULL, fp);
+			if (lp->next)
+				fputs(" | ", fp);
+		}
+		if (n->npipe.pipe_backgnd)
+			fputs(" &", fp);
+		if (ind >= 0)
+			putc('\n', fp);
+		break;
+	default:
+		fprintf(fp, "<node type %d>", n->type);
+		if (ind >= 0)
+			putc('\n', fp);
+		break;
+	}
+}
+
+static void
+showtree(union node *n)
+{
+	trace_puts("showtree called\n");
+	shtree(n, 1, NULL, stderr);
+}
+
+#endif /* DEBUG */
+
+
+/* ============ Parser data */
+
+/*
+ * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
+ */
+struct strlist {
+	struct strlist *next;
+	char *text;
+};
+
+struct alias;
+
+struct strpush {
+	struct strpush *prev;   /* preceding string on stack */
+	char *prev_string;
+	int prev_left_in_line;
+#if ENABLE_ASH_ALIAS
+	struct alias *ap;       /* if push was associated with an alias */
+#endif
+	char *string;           /* remember the string since it may change */
+};
+
+struct parsefile {
+	struct parsefile *prev; /* preceding file on stack */
+	int linno;              /* current line */
+	int pf_fd;              /* file descriptor (or -1 if string) */
+	int left_in_line;       /* number of chars left in this line */
+	int left_in_buffer;     /* number of chars left in this buffer past the line */
+	char *next_to_pgetc;    /* next char in buffer */
+	char *buf;              /* input buffer */
+	struct strpush *strpush; /* for pushing strings at this level */
+	struct strpush basestrpush; /* so pushing one is fast */
+};
+
+static struct parsefile basepf;        /* top level input file */
+static struct parsefile *g_parsefile = &basepf;  /* current input file */
+static int startlinno;                 /* line # where last token started */
+static char *commandname;              /* currently executing command */
+static struct strlist *cmdenviron;     /* environment for builtin command */
+static uint8_t exitstatus;             /* exit status of last command */
+
+
+/* ============ Message printing */
+
+static void
+ash_vmsg(const char *msg, va_list ap)
+{
+	fprintf(stderr, "%s: ", arg0);
+	if (commandname) {
+		if (strcmp(arg0, commandname))
+			fprintf(stderr, "%s: ", commandname);
+		if (!iflag || g_parsefile->pf_fd > 0)
+			fprintf(stderr, "line %d: ", startlinno);
+	}
+	vfprintf(stderr, msg, ap);
+	outcslow('\n', stderr);
+}
+
+/*
+ * Exverror is called to raise the error exception.  If the second argument
+ * is not NULL then error prints an error message using printf style
+ * formatting.  It then raises the error exception.
+ */
+static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
+static void
+ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
+{
+#if DEBUG
+	if (msg) {
+		TRACE(("ash_vmsg_and_raise(%d, \"", cond));
+		TRACEV((msg, ap));
+		TRACE(("\") pid=%d\n", getpid()));
+	} else
+		TRACE(("ash_vmsg_and_raise(%d, NULL) pid=%d\n", cond, getpid()));
+	if (msg)
+#endif
+		ash_vmsg(msg, ap);
+
+	flush_stdout_stderr();
+	raise_exception(cond);
+	/* NOTREACHED */
+}
+
+static void ash_msg_and_raise_error(const char *, ...) NORETURN;
+static void
+ash_msg_and_raise_error(const char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	ash_vmsg_and_raise(EXERROR, msg, ap);
+	/* NOTREACHED */
+	va_end(ap);
+}
+
+static void raise_error_syntax(const char *) NORETURN;
+static void
+raise_error_syntax(const char *msg)
+{
+	ash_msg_and_raise_error("syntax error: %s", msg);
+	/* NOTREACHED */
+}
+
+static void ash_msg_and_raise(int, const char *, ...) NORETURN;
+static void
+ash_msg_and_raise(int cond, const char *msg, ...)
+{
+	va_list ap;
+
+	va_start(ap, msg);
+	ash_vmsg_and_raise(cond, msg, ap);
+	/* NOTREACHED */
+	va_end(ap);
+}
+
+/*
+ * error/warning routines for external builtins
+ */
+static void
+ash_msg(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	ash_vmsg(fmt, ap);
+	va_end(ap);
+}
+
+/*
+ * Return a string describing an error.  The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+static const char *
+errmsg(int e, const char *em)
+{
+	if (e == ENOENT || e == ENOTDIR) {
+		return em;
+	}
+	return strerror(e);
+}
+
+
+/* ============ Memory allocation */
+
+#if 0
+/* I consider these wrappers nearly useless:
+ * ok, they return you to nearest exception handler, but
+ * how much memory do you leak in the process, making
+ * memory starvation worse?
+ */
+static void *
+ckrealloc(void * p, size_t nbytes)
+{
+	p = realloc(p, nbytes);
+	if (!p)
+		ash_msg_and_raise_error(bb_msg_memory_exhausted);
+	return p;
+}
+
+static void *
+ckmalloc(size_t nbytes)
+{
+	return ckrealloc(NULL, nbytes);
+}
+
+static void *
+ckzalloc(size_t nbytes)
+{
+	return memset(ckmalloc(nbytes), 0, nbytes);
+}
+
+static char *
+ckstrdup(const char *s)
+{
+	char *p = strdup(s);
+	if (!p)
+		ash_msg_and_raise_error(bb_msg_memory_exhausted);
+	return p;
+}
+#else
+/* Using bbox equivalents. They exit if out of memory */
+# define ckrealloc xrealloc
+# define ckmalloc  xmalloc
+# define ckzalloc  xzalloc
+# define ckstrdup  xstrdup
+#endif
+
+/*
+ * It appears that grabstackstr() will barf with such alignments
+ * because stalloc() will return a string allocated in a new stackblock.
+ */
+#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
+enum {
+	/* Most machines require the value returned from malloc to be aligned
+	 * in some way.  The following macro will get this right
+	 * on many machines.  */
+	SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
+	/* Minimum size of a block */
+	MINSIZE = SHELL_ALIGN(504),
+};
+
+struct stack_block {
+	struct stack_block *prev;
+	char space[MINSIZE];
+};
+
+struct stackmark {
+	struct stack_block *stackp;
+	char *stacknxt;
+	size_t stacknleft;
+	struct stackmark *marknext;
+};
+
+
+struct globals_memstack {
+	struct stack_block *g_stackp; // = &stackbase;
+	struct stackmark *markp;
+	char *g_stacknxt; // = stackbase.space;
+	char *sstrend; // = stackbase.space + MINSIZE;
+	size_t g_stacknleft; // = MINSIZE;
+	int    herefd; // = -1;
+	struct stack_block stackbase;
+};
+extern struct globals_memstack *const ash_ptr_to_globals_memstack;
+#define G_memstack (*ash_ptr_to_globals_memstack)
+#define g_stackp     (G_memstack.g_stackp    )
+#define markp        (G_memstack.markp       )
+#define g_stacknxt   (G_memstack.g_stacknxt  )
+#define sstrend      (G_memstack.sstrend     )
+#define g_stacknleft (G_memstack.g_stacknleft)
+#define herefd       (G_memstack.herefd      )
+#define stackbase    (G_memstack.stackbase   )
+#define INIT_G_memstack() do { \
+	(*(struct globals_memstack**)&ash_ptr_to_globals_memstack) = xzalloc(sizeof(G_memstack)); \
+	barrier(); \
+	g_stackp = &stackbase; \
+	g_stacknxt = stackbase.space; \
+	g_stacknleft = MINSIZE; \
+	sstrend = stackbase.space + MINSIZE; \
+	herefd = -1; \
+} while (0)
+
+
+#define stackblock()     ((void *)g_stacknxt)
+#define stackblocksize() g_stacknleft
+
+/*
+ * Parse trees for commands are allocated in lifo order, so we use a stack
+ * to make this more efficient, and also to avoid all sorts of exception
+ * handling code to handle interrupts in the middle of a parse.
+ *
+ * The size 504 was chosen because the Ultrix malloc handles that size
+ * well.
+ */
+static void *
+stalloc(size_t nbytes)
+{
+	char *p;
+	size_t aligned;
+
+	aligned = SHELL_ALIGN(nbytes);
+	if (aligned > g_stacknleft) {
+		size_t len;
+		size_t blocksize;
+		struct stack_block *sp;
+
+		blocksize = aligned;
+		if (blocksize < MINSIZE)
+			blocksize = MINSIZE;
+		len = sizeof(struct stack_block) - MINSIZE + blocksize;
+		if (len < blocksize)
+			ash_msg_and_raise_error(bb_msg_memory_exhausted);
+		INT_OFF;
+		sp = ckmalloc(len);
+		sp->prev = g_stackp;
+		g_stacknxt = sp->space;
+		g_stacknleft = blocksize;
+		sstrend = g_stacknxt + blocksize;
+		g_stackp = sp;
+		INT_ON;
+	}
+	p = g_stacknxt;
+	g_stacknxt += aligned;
+	g_stacknleft -= aligned;
+	return p;
+}
+
+static void *
+stzalloc(size_t nbytes)
+{
+	return memset(stalloc(nbytes), 0, nbytes);
+}
+
+static void
+stunalloc(void *p)
+{
+#if DEBUG
+	if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
+		write(STDERR_FILENO, "stunalloc\n", 10);
+		abort();
+	}
+#endif
+	g_stacknleft += g_stacknxt - (char *)p;
+	g_stacknxt = p;
+}
+
+/*
+ * Like strdup but works with the ash stack.
+ */
+static char *
+ststrdup(const char *p)
+{
+	size_t len = strlen(p) + 1;
+	return memcpy(stalloc(len), p, len);
+}
+
+static void
+setstackmark(struct stackmark *mark)
+{
+	mark->stackp = g_stackp;
+	mark->stacknxt = g_stacknxt;
+	mark->stacknleft = g_stacknleft;
+	mark->marknext = markp;
+	markp = mark;
+}
+
+static void
+popstackmark(struct stackmark *mark)
+{
+	struct stack_block *sp;
+
+	if (!mark->stackp)
+		return;
+
+	INT_OFF;
+	markp = mark->marknext;
+	while (g_stackp != mark->stackp) {
+		sp = g_stackp;
+		g_stackp = sp->prev;
+		free(sp);
+	}
+	g_stacknxt = mark->stacknxt;
+	g_stacknleft = mark->stacknleft;
+	sstrend = mark->stacknxt + mark->stacknleft;
+	INT_ON;
+}
+
+/*
+ * When the parser reads in a string, it wants to stick the string on the
+ * stack and only adjust the stack pointer when it knows how big the
+ * string is.  Stackblock (defined in stack.h) returns a pointer to a block
+ * of space on top of the stack and stackblocklen returns the length of
+ * this block.  Growstackblock will grow this space by at least one byte,
+ * possibly moving it (like realloc).  Grabstackblock actually allocates the
+ * part of the block that has been used.
+ */
+static void
+growstackblock(void)
+{
+	size_t newlen;
+
+	newlen = g_stacknleft * 2;
+	if (newlen < g_stacknleft)
+		ash_msg_and_raise_error(bb_msg_memory_exhausted);
+	if (newlen < 128)
+		newlen += 128;
+
+	if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
+		struct stack_block *oldstackp;
+		struct stackmark *xmark;
+		struct stack_block *sp;
+		struct stack_block *prevstackp;
+		size_t grosslen;
+
+		INT_OFF;
+		oldstackp = g_stackp;
+		sp = g_stackp;
+		prevstackp = sp->prev;
+		grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
+		sp = ckrealloc(sp, grosslen);
+		sp->prev = prevstackp;
+		g_stackp = sp;
+		g_stacknxt = sp->space;
+		g_stacknleft = newlen;
+		sstrend = sp->space + newlen;
+
+		/*
+		 * Stack marks pointing to the start of the old block
+		 * must be relocated to point to the new block
+		 */
+		xmark = markp;
+		while (xmark != NULL && xmark->stackp == oldstackp) {
+			xmark->stackp = g_stackp;
+			xmark->stacknxt = g_stacknxt;
+			xmark->stacknleft = g_stacknleft;
+			xmark = xmark->marknext;
+		}
+		INT_ON;
+	} else {
+		char *oldspace = g_stacknxt;
+		size_t oldlen = g_stacknleft;
+		char *p = stalloc(newlen);
+
+		/* free the space we just allocated */
+		g_stacknxt = memcpy(p, oldspace, oldlen);
+		g_stacknleft += newlen;
+	}
+}
+
+static void
+grabstackblock(size_t len)
+{
+	len = SHELL_ALIGN(len);
+	g_stacknxt += len;
+	g_stacknleft -= len;
+}
+
+/*
+ * The following routines are somewhat easier to use than the above.
+ * The user declares a variable of type STACKSTR, which may be declared
+ * to be a register.  The macro STARTSTACKSTR initializes things.  Then
+ * the user uses the macro STPUTC to add characters to the string.  In
+ * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
+ * grown as necessary.  When the user is done, she can just leave the
+ * string there and refer to it using stackblock().  Or she can allocate
+ * the space for it using grabstackstr().  If it is necessary to allow
+ * someone else to use the stack temporarily and then continue to grow
+ * the string, the user should use grabstack to allocate the space, and
+ * then call ungrabstr(p) to return to the previous mode of operation.
+ *
+ * USTPUTC is like STPUTC except that it doesn't check for overflow.
+ * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
+ * is space for at least one character.
+ */
+static void *
+growstackstr(void)
+{
+	size_t len = stackblocksize();
+	if (herefd >= 0 && len >= 1024) {
+		full_write(herefd, stackblock(), len);
+		return stackblock();
+	}
+	growstackblock();
+	return (char *)stackblock() + len;
+}
+
+/*
+ * Called from CHECKSTRSPACE.
+ */
+static char *
+makestrspace(size_t newlen, char *p)
+{
+	size_t len = p - g_stacknxt;
+	size_t size = stackblocksize();
+
+	for (;;) {
+		size_t nleft;
+
+		size = stackblocksize();
+		nleft = size - len;
+		if (nleft >= newlen)
+			break;
+		growstackblock();
+	}
+	return (char *)stackblock() + len;
+}
+
+static char *
+stack_nputstr(const char *s, size_t n, char *p)
+{
+	p = makestrspace(n, p);
+	p = (char *)memcpy(p, s, n) + n;
+	return p;
+}
+
+static char *
+stack_putstr(const char *s, char *p)
+{
+	return stack_nputstr(s, strlen(s), p);
+}
+
+static char *
+_STPUTC(int c, char *p)
+{
+	if (p == sstrend)
+		p = growstackstr();
+	*p++ = c;
+	return p;
+}
+
+#define STARTSTACKSTR(p)        ((p) = stackblock())
+#define STPUTC(c, p)            ((p) = _STPUTC((c), (p)))
+#define CHECKSTRSPACE(n, p) do { \
+	char *q = (p); \
+	size_t l = (n); \
+	size_t m = sstrend - q; \
+	if (l > m) \
+		(p) = makestrspace(l, q); \
+} while (0)
+#define USTPUTC(c, p)           (*(p)++ = (c))
+#define STACKSTRNUL(p) do { \
+	if ((p) == sstrend) \
+		(p) = growstackstr(); \
+	*(p) = '\0'; \
+} while (0)
+#define STUNPUTC(p)             (--(p))
+#define STTOPC(p)               ((p)[-1])
+#define STADJUST(amount, p)     ((p) += (amount))
+
+#define grabstackstr(p)         stalloc((char *)(p) - (char *)stackblock())
+#define ungrabstackstr(s, p)    stunalloc(s)
+#define stackstrend()           ((void *)sstrend)
+
+
+/* ============ String helpers */
+
+/*
+ * prefix -- see if pfx is a prefix of string.
+ */
+static char *
+prefix(const char *string, const char *pfx)
+{
+	while (*pfx) {
+		if (*pfx++ != *string++)
+			return NULL;
+	}
+	return (char *) string;
+}
+
+/*
+ * Check for a valid number.  This should be elsewhere.
+ */
+static int
+is_number(const char *p)
+{
+	do {
+		if (!isdigit(*p))
+			return 0;
+	} while (*++p != '\0');
+	return 1;
+}
+
+/*
+ * Convert a string of digits to an integer, printing an error message on
+ * failure.
+ */
+static int
+number(const char *s)
+{
+	if (!is_number(s))
+		ash_msg_and_raise_error(msg_illnum, s);
+	return atoi(s);
+}
+
+/*
+ * Produce a possibly single quoted string suitable as input to the shell.
+ * The return string is allocated on the stack.
+ */
+static char *
+single_quote(const char *s)
+{
+	char *p;
+
+	STARTSTACKSTR(p);
+
+	do {
+		char *q;
+		size_t len;
+
+		len = strchrnul(s, '\'') - s;
+
+		q = p = makestrspace(len + 3, p);
+
+		*q++ = '\'';
+		q = (char *)memcpy(q, s, len) + len;
+		*q++ = '\'';
+		s += len;
+
+		STADJUST(q - p, p);
+
+		if (*s != '\'')
+			break;
+		len = 0;
+		do len++; while (*++s == '\'');
+
+		q = p = makestrspace(len + 3, p);
+
+		*q++ = '"';
+		q = (char *)memcpy(q, s - len, len) + len;
+		*q++ = '"';
+
+		STADJUST(q - p, p);
+	} while (*s);
+
+	USTPUTC('\0', p);
+
+	return stackblock();
+}
+
+
+/* ============ nextopt */
+
+static char **argptr;                  /* argument list for builtin commands */
+static char *optionarg;                /* set by nextopt (like getopt) */
+static char *optptr;                   /* used by nextopt */
+
+/*
+ * XXX - should get rid of. Have all builtins use getopt(3).
+ * The library getopt must have the BSD extension static variable
+ * "optreset", otherwise it can't be used within the shell safely.
+ *
+ * Standard option processing (a la getopt) for builtin routines.
+ * The only argument that is passed to nextopt is the option string;
+ * the other arguments are unnecessary. It returns the character,
+ * or '\0' on end of input.
+ */
+static int
+nextopt(const char *optstring)
+{
+	char *p;
+	const char *q;
+	char c;
+
+	p = optptr;
+	if (p == NULL || *p == '\0') {
+		/* We ate entire "-param", take next one */
+		p = *argptr;
+		if (p == NULL)
+			return '\0';
+		if (*p != '-')
+			return '\0';
+		if (*++p == '\0') /* just "-" ? */
+			return '\0';
+		argptr++;
+		if (LONE_DASH(p)) /* "--" ? */
+			return '\0';
+		/* p => next "-param" */
+	}
+	/* p => some option char in the middle of a "-param" */
+	c = *p++;
+	for (q = optstring; *q != c;) {
+		if (*q == '\0')
+			ash_msg_and_raise_error("illegal option -%c", c);
+		if (*++q == ':')
+			q++;
+	}
+	if (*++q == ':') {
+		if (*p == '\0') {
+			p = *argptr++;
+			if (p == NULL)
+				ash_msg_and_raise_error("no arg for -%c option", c);
+		}
+		optionarg = p;
+		p = NULL;
+	}
+	optptr = p;
+	return c;
+}
+
+
+/* ============ Shell variables */
+
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
+struct shparam {
+	int nparam;             /* # of positional parameters (without $0) */
+#if ENABLE_ASH_GETOPTS
+	int optind;             /* next parameter to be processed by getopts */
+	int optoff;             /* used by getopts */
+#endif
+	unsigned char malloced; /* if parameter list dynamically allocated */
+	char **p;               /* parameter list */
+};
+
+/*
+ * Free the list of positional parameters.
+ */
+static void
+freeparam(volatile struct shparam *param)
+{
+	if (param->malloced) {
+		char **ap, **ap1;
+		ap = ap1 = param->p;
+		while (*ap)
+			free(*ap++);
+		free(ap1);
+	}
+}
+
+#if ENABLE_ASH_GETOPTS
+static void FAST_FUNC getoptsreset(const char *value);
+#endif
+
+struct var {
+	struct var *next;               /* next entry in hash list */
+	int flags;                      /* flags are defined above */
+	const char *var_text;           /* name=value */
+	void (*var_func)(const char *) FAST_FUNC; /* function to be called when  */
+					/* the variable gets set/unset */
+};
+
+struct localvar {
+	struct localvar *next;          /* next local variable in list */
+	struct var *vp;                 /* the variable that was made local */
+	int flags;                      /* saved flags */
+	const char *text;               /* saved text */
+};
+
+/* flags */
+#define VEXPORT         0x01    /* variable is exported */
+#define VREADONLY       0x02    /* variable cannot be modified */
+#define VSTRFIXED       0x04    /* variable struct is statically allocated */
+#define VTEXTFIXED      0x08    /* text is statically allocated */
+#define VSTACK          0x10    /* text is allocated on the stack */
+#define VUNSET          0x20    /* the variable is not set */
+#define VNOFUNC         0x40    /* don't call the callback function */
+#define VNOSET          0x80    /* do not set variable - just readonly test */
+#define VNOSAVE         0x100   /* when text is on the heap before setvareq */
+#if ENABLE_ASH_RANDOM_SUPPORT
+# define VDYNAMIC       0x200   /* dynamic variable */
+#else
+# define VDYNAMIC       0
+#endif
+
+
+/* Need to be before varinit_data[] */
+#if ENABLE_LOCALE_SUPPORT
+static void FAST_FUNC
+change_lc_all(const char *value)
+{
+	if (value && *value != '\0')
+		setlocale(LC_ALL, value);
+}
+static void FAST_FUNC
+change_lc_ctype(const char *value)
+{
+	if (value && *value != '\0')
+		setlocale(LC_CTYPE, value);
+}
+#endif
+#if ENABLE_ASH_MAIL
+static void chkmail(void);
+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
+static void change_random(const char *) FAST_FUNC;
+#endif
+
+static const struct {
+	int flags;
+	const char *var_text;
+	void (*var_func)(const char *) FAST_FUNC;
+} varinit_data[] = {
+	{ VSTRFIXED|VTEXTFIXED       , defifsvar   , NULL            },
+#if ENABLE_ASH_MAIL
+	{ VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL"      , changemail      },
+	{ VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH"  , changemail      },
+#endif
+	{ VSTRFIXED|VTEXTFIXED       , bb_PATH_root_path, changepath },
+	{ VSTRFIXED|VTEXTFIXED       , "PS1=$ "    , NULL            },
+	{ VSTRFIXED|VTEXTFIXED       , "PS2=> "    , NULL            },
+	{ VSTRFIXED|VTEXTFIXED       , "PS4=+ "    , NULL            },
+#if ENABLE_ASH_GETOPTS
+	{ VSTRFIXED|VTEXTFIXED       , "OPTIND=1"  , getoptsreset    },
+#endif
+#if ENABLE_ASH_RANDOM_SUPPORT
+	{ VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
+#endif
+#if ENABLE_LOCALE_SUPPORT
+	{ VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL"    , change_lc_all   },
+	{ VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE"  , change_lc_ctype },
+#endif
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+	{ VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE"  , NULL            },
+#endif
+};
+
+struct redirtab;
+
+struct globals_var {
+	struct shparam shellparam;      /* $@ current positional parameters */
+	struct redirtab *redirlist;
+	int g_nullredirs;
+	int preverrout_fd;   /* save fd2 before print debug if xflag is set. */
+	struct var *vartab[VTABSIZE];
+	struct var varinit[ARRAY_SIZE(varinit_data)];
+};
+extern struct globals_var *const ash_ptr_to_globals_var;
+#define G_var (*ash_ptr_to_globals_var)
+#define shellparam    (G_var.shellparam   )
+//#define redirlist     (G_var.redirlist    )
+#define g_nullredirs  (G_var.g_nullredirs )
+#define preverrout_fd (G_var.preverrout_fd)
+#define vartab        (G_var.vartab       )
+#define varinit       (G_var.varinit      )
+#define INIT_G_var() do { \
+	unsigned i; \
+	(*(struct globals_var**)&ash_ptr_to_globals_var) = xzalloc(sizeof(G_var)); \
+	barrier(); \
+	for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
+		varinit[i].flags    = varinit_data[i].flags; \
+		varinit[i].var_text = varinit_data[i].var_text; \
+		varinit[i].var_func = varinit_data[i].var_func; \
+	} \
+} while (0)
+
+#define vifs      varinit[0]
+#if ENABLE_ASH_MAIL
+# define vmail    (&vifs)[1]
+# define vmpath   (&vmail)[1]
+# define vpath    (&vmpath)[1]
+#else
+# define vpath    (&vifs)[1]
+#endif
+#define vps1      (&vpath)[1]
+#define vps2      (&vps1)[1]
+#define vps4      (&vps2)[1]
+#if ENABLE_ASH_GETOPTS
+# define voptind  (&vps4)[1]
+# if ENABLE_ASH_RANDOM_SUPPORT
+#  define vrandom (&voptind)[1]
+# endif
+#else
+# if ENABLE_ASH_RANDOM_SUPPORT
+#  define vrandom (&vps4)[1]
+# endif
+#endif
+
+/*
+ * The following macros access the values of the above variables.
+ * They have to skip over the name.  They return the null string
+ * for unset variables.
+ */
+#define ifsval()        (vifs.var_text + 4)
+#define ifsset()        ((vifs.flags & VUNSET) == 0)
+#if ENABLE_ASH_MAIL
+# define mailval()      (vmail.var_text + 5)
+# define mpathval()     (vmpath.var_text + 9)
+# define mpathset()     ((vmpath.flags & VUNSET) == 0)
+#endif
+#define pathval()       (vpath.var_text + 5)
+#define ps1val()        (vps1.var_text + 4)
+#define ps2val()        (vps2.var_text + 4)
+#define ps4val()        (vps4.var_text + 4)
+#if ENABLE_ASH_GETOPTS
+# define optindval()    (voptind.var_text + 7)
+#endif
+
+#if ENABLE_ASH_GETOPTS
+static void FAST_FUNC
+getoptsreset(const char *value)
+{
+	shellparam.optind = number(value);
+	shellparam.optoff = -1;
+}
+#endif
+
+/* math.h has these, otherwise define our private copies */
+#if !ENABLE_SH_MATH_SUPPORT
+#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
+/*
+ * Return the pointer to the first char which is not part of a legal variable name
+ * (a letter or underscore followed by letters, underscores, and digits).
+ */
+static const char*
+endofname(const char *name)
+{
+	if (!is_name(*name))
+		return name;
+	while (*++name) {
+		if (!is_in_name(*name))
+			break;
+	}
+	return name;
+}
+#endif
+
+/*
+ * 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'.
+ */
+static int
+varcmp(const char *p, const char *q)
+{
+	int c, d;
+
+	while ((c = *p) == (d = *q)) {
+		if (!c || c == '=')
+			goto out;
+		p++;
+		q++;
+	}
+	if (c == '=')
+		c = '\0';
+	if (d == '=')
+		d = '\0';
+ out:
+	return c - d;
+}
+
+/*
+ * Find the appropriate entry in the hash table from the name.
+ */
+static struct var **
+hashvar(const char *p)
+{
+	unsigned hashval;
+
+	hashval = ((unsigned char) *p) << 4;
+	while (*p && *p != '=')
+		hashval += (unsigned char) *p++;
+	return &vartab[hashval % VTABSIZE];
+}
+
+static int
+vpcmp(const void *a, const void *b)
+{
+	return varcmp(*(const char **)a, *(const char **)b);
+}
+
+/*
+ * This routine initializes the builtin variables.
+ */
+static void
+initvar(void)
+{
+	struct var *vp;
+	struct var *end;
+	struct var **vpp;
+
+	/*
+	 * PS1 depends on uid
+	 */
+#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
+	vps1.var_text = "PS1=\\w \\$ ";
+#else
+	if (!geteuid())
+		vps1.var_text = "PS1=# ";
+#endif
+	vp = varinit;
+	end = vp + ARRAY_SIZE(varinit);
+	do {
+		vpp = hashvar(vp->var_text);
+		vp->next = *vpp;
+		*vpp = vp;
+	} while (++vp < end);
+}
+
+static struct var **
+findvar(struct var **vpp, const char *name)
+{
+	for (; *vpp; vpp = &(*vpp)->next) {
+		if (varcmp((*vpp)->var_text, name) == 0) {
+			break;
+		}
+	}
+	return vpp;
+}
+
+/*
+ * Find the value of a variable.  Returns NULL if not set.
+ */
+static const char* FAST_FUNC
+lookupvar(const char *name)
+{
+	struct var *v;
+
+	v = *findvar(hashvar(name), name);
+	if (v) {
+#if ENABLE_ASH_RANDOM_SUPPORT
+	/*
+	 * Dynamic variables are implemented roughly the same way they are
+	 * in bash. Namely, they're "special" so long as they aren't unset.
+	 * As soon as they're unset, they're no longer dynamic, and dynamic
+	 * lookup will no longer happen at that point. -- PFM.
+	 */
+		if (v->flags & VDYNAMIC)
+			v->var_func(NULL);
+#endif
+		if (!(v->flags & VUNSET))
+			return var_end(v->var_text);
+	}
+	return NULL;
+}
+
+/*
+ * Search the environment of a builtin command.
+ */
+static const char *
+bltinlookup(const char *name)
+{
+	struct strlist *sp;
+
+	for (sp = cmdenviron; sp; sp = sp->next) {
+		if (varcmp(sp->text, name) == 0)
+			return var_end(sp->text);
+	}
+	return lookupvar(name);
+}
+
+/*
+ * Same as setvar except that the variable and value are passed in
+ * the first argument as name=value.  Since the first argument will
+ * be actually stored in the table, it should not be a string that
+ * will go away.
+ * Called with interrupts off.
+ */
+static void
+setvareq(char *s, int flags)
+{
+	struct var *vp, **vpp;
+
+	vpp = hashvar(s);
+	flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
+	vp = *findvar(vpp, s);
+	if (vp) {
+		if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
+			const char *n;
+
+			if (flags & VNOSAVE)
+				free(s);
+			n = vp->var_text;
+			ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
+		}
+
+		if (flags & VNOSET)
+			return;
+
+		if (vp->var_func && !(flags & VNOFUNC))
+			vp->var_func(var_end(s));
+
+		if (!(vp->flags & (VTEXTFIXED|VSTACK)))
+			free((char*)vp->var_text);
+
+		flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
+	} else {
+		/* variable s is not found */
+		if (flags & VNOSET)
+			return;
+		vp = ckzalloc(sizeof(*vp));
+		vp->next = *vpp;
+		/*vp->func = NULL; - ckzalloc did it */
+		*vpp = vp;
+	}
+	if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
+		s = ckstrdup(s);
+	vp->var_text = s;
+	vp->flags = flags;
+}
+
+/*
+ * Set the value of a variable.  The flags argument is ored with the
+ * flags of the variable.  If val is NULL, the variable is unset.
+ */
+static void
+setvar(const char *name, const char *val, int flags)
+{
+	const char *q;
+	char *p;
+	char *nameeq;
+	size_t namelen;
+	size_t vallen;
+
+	q = endofname(name);
+	p = strchrnul(q, '=');
+	namelen = p - name;
+	if (!namelen || p != q)
+		ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
+	vallen = 0;
+	if (val == NULL) {
+		flags |= VUNSET;
+	} else {
+		vallen = strlen(val);
+	}
+
+	INT_OFF;
+	nameeq = ckmalloc(namelen + vallen + 2);
+	p = memcpy(nameeq, name, namelen) + namelen;
+	if (val) {
+		*p++ = '=';
+		p = memcpy(p, val, vallen) + vallen;
+	}
+	*p = '\0';
+	setvareq(nameeq, flags | VNOSAVE);
+	INT_ON;
+}
+
+static void FAST_FUNC
+setvar2(const char *name, const char *val)
+{
+	setvar(name, val, 0);
+}
+
+#if ENABLE_ASH_GETOPTS
+/*
+ * Safe version of setvar, returns 1 on success 0 on failure.
+ */
+static int
+setvarsafe(const char *name, const char *val, int flags)
+{
+	int err;
+	volatile int saveint;
+	struct jmploc *volatile savehandler = exception_handler;
+	struct jmploc jmploc;
+
+	SAVE_INT(saveint);
+	if (setjmp(jmploc.loc))
+		err = 1;
+	else {
+		exception_handler = &jmploc;
+		setvar(name, val, flags);
+		err = 0;
+	}
+	exception_handler = savehandler;
+	RESTORE_INT(saveint);
+	return err;
+}
+#endif
+
+/*
+ * Unset the specified variable.
+ */
+static int
+unsetvar(const char *s)
+{
+	struct var **vpp;
+	struct var *vp;
+	int retval;
+
+	vpp = findvar(hashvar(s), s);
+	vp = *vpp;
+	retval = 2;
+	if (vp) {
+		int flags = vp->flags;
+
+		retval = 1;
+		if (flags & VREADONLY)
+			goto out;
+#if ENABLE_ASH_RANDOM_SUPPORT
+		vp->flags &= ~VDYNAMIC;
+#endif
+		if (flags & VUNSET)
+			goto ok;
+		if ((flags & VSTRFIXED) == 0) {
+			INT_OFF;
+			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+				free((char*)vp->var_text);
+			*vpp = vp->next;
+			free(vp);
+			INT_ON;
+		} else {
+			setvar(s, 0, 0);
+			vp->flags &= ~VEXPORT;
+		}
+ ok:
+		retval = 0;
+	}
+ out:
+	return retval;
+}
+
+/*
+ * Process a linked list of variable assignments.
+ */
+static void
+listsetvar(struct strlist *list_set_var, int flags)
+{
+	struct strlist *lp = list_set_var;
+
+	if (!lp)
+		return;
+	INT_OFF;
+	do {
+		setvareq(lp->text, flags);
+		lp = lp->next;
+	} while (lp);
+	INT_ON;
+}
+
+/*
+ * Generate a list of variables satisfying the given conditions.
+ */
+static char **
+listvars(int on, int off, char ***end)
+{
+	struct var **vpp;
+	struct var *vp;
+	char **ep;
+	int mask;
+
+	STARTSTACKSTR(ep);
+	vpp = vartab;
+	mask = on | off;
+	do {
+		for (vp = *vpp; vp; vp = vp->next) {
+			if ((vp->flags & mask) == on) {
+				if (ep == stackstrend())
+					ep = growstackstr();
+				*ep++ = (char*)vp->var_text;
+			}
+		}
+	} while (++vpp < vartab + VTABSIZE);
+	if (ep == stackstrend())
+		ep = growstackstr();
+	if (end)
+		*end = ep;
+	*ep++ = NULL;
+	return grabstackstr(ep);
+}
+
+
+/* ============ Path search helper
+ *
+ * The variable path (passed by reference) should be set to the start
+ * of the path before the first call; path_advance will update
+ * this value as it proceeds.  Successive calls to path_advance will return
+ * the possible path expansions in sequence.  If an option (indicated by
+ * a percent sign) appears in the path entry then the global variable
+ * pathopt will be set to point to it; otherwise pathopt will be set to
+ * NULL.
+ */
+static const char *pathopt;     /* set by path_advance */
+
+static char *
+path_advance(const char **path, const char *name)
+{
+	const char *p;
+	char *q;
+	const char *start;
+	size_t len;
+
+	if (*path == NULL)
+		return NULL;
+	start = *path;
+	for (p = start; *p && *p != ':' && *p != '%'; p++)
+		continue;
+	len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
+	while (stackblocksize() < len)
+		growstackblock();
+	q = stackblock();
+	if (p != start) {
+		memcpy(q, start, p - start);
+		q += p - start;
+		*q++ = '/';
+	}
+	strcpy(q, name);
+	pathopt = NULL;
+	if (*p == '%') {
+		pathopt = ++p;
+		while (*p && *p != ':')
+			p++;
+	}
+	if (*p == ':')
+		*path = p + 1;
+	else
+		*path = NULL;
+	return stalloc(len);
+}
+
+
+/* ============ Prompt */
+
+static smallint doprompt;                   /* if set, prompt the user */
+static smallint needprompt;                 /* true if interactive and at start of line */
+
+#if ENABLE_FEATURE_EDITING
+static line_input_t *line_input_state;
+static const char *cmdedit_prompt;
+static void
+putprompt(const char *s)
+{
+	if (ENABLE_ASH_EXPAND_PRMT) {
+		free((char*)cmdedit_prompt);
+		cmdedit_prompt = ckstrdup(s);
+		return;
+	}
+	cmdedit_prompt = s;
+}
+#else
+static void
+putprompt(const char *s)
+{
+	out2str(s);
+}
+#endif
+
+#if ENABLE_ASH_EXPAND_PRMT
+/* expandstr() needs parsing machinery, so it is far away ahead... */
+static const char *expandstr(const char *ps);
+#else
+#define expandstr(s) s
+#endif
+
+static void
+setprompt_if(smallint do_set, int whichprompt)
+{
+	const char *prompt;
+	IF_ASH_EXPAND_PRMT(struct stackmark smark;)
+
+	if (!do_set)
+		return;
+
+	needprompt = 0;
+
+	switch (whichprompt) {
+	case 1:
+		prompt = ps1val();
+		break;
+	case 2:
+		prompt = ps2val();
+		break;
+	default:                        /* 0 */
+		prompt = nullstr;
+	}
+#if ENABLE_ASH_EXPAND_PRMT
+	setstackmark(&smark);
+	stalloc(stackblocksize());
+#endif
+	putprompt(expandstr(prompt));
+#if ENABLE_ASH_EXPAND_PRMT
+	popstackmark(&smark);
+#endif
+}
+
+
+/* ============ The cd and pwd commands */
+
+#define CD_PHYSICAL 1
+#define CD_PRINT 2
+
+static int
+cdopt(void)
+{
+	int flags = 0;
+	int i, j;
+
+	j = 'L';
+	while ((i = nextopt("LP")) != '\0') {
+		if (i != j) {
+			flags ^= CD_PHYSICAL;
+			j = i;
+		}
+	}
+
+	return flags;
+}
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.
+ */
+static const char *
+updatepwd(const char *dir)
+{
+	char *new;
+	char *p;
+	char *cdcomppath;
+	const char *lim;
+
+	cdcomppath = ststrdup(dir);
+	STARTSTACKSTR(new);
+	if (*dir != '/') {
+		if (curdir == nullstr)
+			return 0;
+		new = stack_putstr(curdir, new);
+	}
+	new = makestrspace(strlen(dir) + 2, new);
+	lim = (char *)stackblock() + 1;
+	if (*dir != '/') {
+		if (new[-1] != '/')
+			USTPUTC('/', new);
+		if (new > lim && *lim == '/')
+			lim++;
+	} else {
+		USTPUTC('/', new);
+		cdcomppath++;
+		if (dir[1] == '/' && dir[2] != '/') {
+			USTPUTC('/', new);
+			cdcomppath++;
+			lim++;
+		}
+	}
+	p = strtok(cdcomppath, "/");
+	while (p) {
+		switch (*p) {
+		case '.':
+			if (p[1] == '.' && p[2] == '\0') {
+				while (new > lim) {
+					STUNPUTC(new);
+					if (new[-1] == '/')
+						break;
+				}
+				break;
+			}
+			if (p[1] == '\0')
+				break;
+			/* fall through */
+		default:
+			new = stack_putstr(p, new);
+			USTPUTC('/', new);
+		}
+		p = strtok(0, "/");
+	}
+	if (new > lim)
+		STUNPUTC(new);
+	*new = 0;
+	return stackblock();
+}
+
+/*
+ * Find out what the current directory is. If we already know the current
+ * directory, this routine returns immediately.
+ */
+static char *
+getpwd(void)
+{
+	char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
+	return dir ? dir : nullstr;
+}
+
+static void
+setpwd(const char *val, int setold)
+{
+	char *oldcur, *dir;
+
+	oldcur = dir = curdir;
+
+	if (setold) {
+		setvar("OLDPWD", oldcur, VEXPORT);
+	}
+	INT_OFF;
+	if (physdir != nullstr) {
+		if (physdir != oldcur)
+			free(physdir);
+		physdir = nullstr;
+	}
+	if (oldcur == val || !val) {
+		char *s = getpwd();
+		physdir = s;
+		if (!val)
+			dir = s;
+	} else
+		dir = ckstrdup(val);
+	if (oldcur != dir && oldcur != nullstr) {
+		free(oldcur);
+	}
+	curdir = dir;
+	INT_ON;
+	setvar("PWD", dir, VEXPORT);
+}
+
+static void hashcd(void);
+
+/*
+ * Actually do the chdir.  We also call hashcd to let the routines in exec.c
+ * know that the current directory has changed.
+ */
+static int
+docd(const char *dest, int flags)
+{
+	const char *dir = NULL;
+	int err;
+
+	TRACE(("docd(\"%s\", %d) called\n", dest, flags));
+
+	INT_OFF;
+	if (!(flags & CD_PHYSICAL)) {
+		dir = updatepwd(dest);
+		if (dir)
+			dest = dir;
+	}
+	err = chdir(dest);
+	if (err)
+		goto out;
+	setpwd(dir, 1);
+	hashcd();
+ out:
+	INT_ON;
+	return err;
+}
+
+static int FAST_FUNC
+cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	const char *dest;
+	const char *path;
+	const char *p;
+	char c;
+	struct stat statb;
+	int flags;
+
+	flags = cdopt();
+	dest = *argptr;
+	if (!dest)
+		dest = bltinlookup("HOME");
+	else if (LONE_DASH(dest)) {
+		dest = bltinlookup("OLDPWD");
+		flags |= CD_PRINT;
+	}
+	if (!dest)
+		dest = nullstr;
+	if (*dest == '/')
+		goto step7;
+	if (*dest == '.') {
+		c = dest[1];
+ dotdot:
+		switch (c) {
+		case '\0':
+		case '/':
+			goto step6;
+		case '.':
+			c = dest[2];
+			if (c != '.')
+				goto dotdot;
+		}
+	}
+	if (!*dest)
+		dest = ".";
+	path = bltinlookup("CDPATH");
+	if (!path) {
+ step6:
+ step7:
+		p = dest;
+		goto docd;
+	}
+	do {
+		c = *path;
+		p = path_advance(&path, dest);
+		if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+			if (c && c != ':')
+				flags |= CD_PRINT;
+ docd:
+			if (!docd(p, flags))
+				goto out;
+			break;
+		}
+	} while (path);
+	ash_msg_and_raise_error("can't cd to %s", dest);
+	/* NOTREACHED */
+ out:
+	if (flags & CD_PRINT)
+		out1fmt("%s\n", curdir);
+	return 0;
+}
+
+static int FAST_FUNC
+pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	int flags;
+	const char *dir = curdir;
+
+	flags = cdopt();
+	if (flags) {
+		if (physdir == nullstr)
+			setpwd(dir, 0);
+		dir = physdir;
+	}
+	out1fmt("%s\n", dir);
+	return 0;
+}
+
+
+/* ============ ... */
+
+
+#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
+
+/* Syntax classes */
+#define CWORD     0             /* character is nothing special */
+#define CNL       1             /* newline character */
+#define CBACK     2             /* a backslash character */
+#define CSQUOTE   3             /* single quote */
+#define CDQUOTE   4             /* double quote */
+#define CENDQUOTE 5             /* a terminating quote */
+#define CBQUOTE   6             /* backwards single quote */
+#define CVAR      7             /* a dollar sign */
+#define CENDVAR   8             /* a '}' character */
+#define CLP       9             /* a left paren in arithmetic */
+#define CRP      10             /* a right paren in arithmetic */
+#define CENDFILE 11             /* end of file */
+#define CCTL     12             /* like CWORD, except it must be escaped */
+#define CSPCL    13             /* these terminate a word */
+#define CIGN     14             /* character should be ignored */
+
+#define PEOF     256
+#if ENABLE_ASH_ALIAS
+# define PEOA    257
+#endif
+
+#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
+
+#if ENABLE_SH_MATH_SUPPORT
+# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
+#else
+# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
+#endif
+static const uint16_t S_I_T[] = {
+#if ENABLE_ASH_ALIAS
+	SIT_ITEM(CSPCL   , CIGN     , CIGN , CIGN   ),    /* 0, PEOA */
+#endif
+	SIT_ITEM(CSPCL   , CWORD    , CWORD, CWORD  ),    /* 1, ' ' */
+	SIT_ITEM(CNL     , CNL      , CNL  , CNL    ),    /* 2, \n */
+	SIT_ITEM(CWORD   , CCTL     , CCTL , CWORD  ),    /* 3, !*-/:=?[]~ */
+	SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD  ),    /* 4, '"' */
+	SIT_ITEM(CVAR    , CVAR     , CWORD, CVAR   ),    /* 5, $ */
+	SIT_ITEM(CSQUOTE , CWORD    , CENDQUOTE, CWORD),  /* 6, "'" */
+	SIT_ITEM(CSPCL   , CWORD    , CWORD, CLP    ),    /* 7, ( */
+	SIT_ITEM(CSPCL   , CWORD    , CWORD, CRP    ),    /* 8, ) */
+	SIT_ITEM(CBACK   , CBACK    , CCTL , CBACK  ),    /* 9, \ */
+	SIT_ITEM(CBQUOTE , CBQUOTE  , CWORD, CBQUOTE),    /* 10, ` */
+	SIT_ITEM(CENDVAR , CENDVAR  , CWORD, CENDVAR),    /* 11, } */
+#if !USE_SIT_FUNCTION
+	SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
+	SIT_ITEM(CWORD   , CWORD    , CWORD, CWORD  ),    /* 13, 0-9A-Za-z */
+	SIT_ITEM(CCTL    , CCTL     , CCTL , CCTL   )     /* 14, CTLESC ... */
+#endif
+#undef SIT_ITEM
+};
+/* Constants below must match table above */
+enum {
+#if ENABLE_ASH_ALIAS
+	CSPCL_CIGN_CIGN_CIGN               , /*  0 */
+#endif
+	CSPCL_CWORD_CWORD_CWORD            , /*  1 */
+	CNL_CNL_CNL_CNL                    , /*  2 */
+	CWORD_CCTL_CCTL_CWORD              , /*  3 */
+	CDQUOTE_CENDQUOTE_CWORD_CWORD      , /*  4 */
+	CVAR_CVAR_CWORD_CVAR               , /*  5 */
+	CSQUOTE_CWORD_CENDQUOTE_CWORD      , /*  6 */
+	CSPCL_CWORD_CWORD_CLP              , /*  7 */
+	CSPCL_CWORD_CWORD_CRP              , /*  8 */
+	CBACK_CBACK_CCTL_CBACK             , /*  9 */
+	CBQUOTE_CBQUOTE_CWORD_CBQUOTE      , /* 10 */
+	CENDVAR_CENDVAR_CWORD_CENDVAR      , /* 11 */
+	CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
+	CWORD_CWORD_CWORD_CWORD            , /* 13 */
+	CCTL_CCTL_CCTL_CCTL                , /* 14 */
+};
+
+/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
+ * caller must ensure proper cast on it if c is *char_ptr!
+ */
+/* Values for syntax param */
+#define BASESYNTAX 0    /* not in quotes */
+#define DQSYNTAX   1    /* in double quotes */
+#define SQSYNTAX   2    /* in single quotes */
+#define ARISYNTAX  3    /* in arithmetic */
+#define PSSYNTAX   4    /* prompt. never passed to SIT() */
+
+#if USE_SIT_FUNCTION
+
+static int
+SIT(int c, int syntax)
+{
+	static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
+# if ENABLE_ASH_ALIAS
+	static const uint8_t syntax_index_table[] ALIGN1 = {
+		1, 2, 1, 3, 4, 5, 1, 6,         /* "\t\n !\"$&'" */
+		7, 8, 3, 3, 3, 3, 1, 1,         /* "()*-/:;<" */
+		3, 1, 3, 3, 9, 3, 10, 1,        /* "=>?[\\]`|" */
+		11, 3                           /* "}~" */
+	};
+# else
+	static const uint8_t syntax_index_table[] ALIGN1 = {
+		0, 1, 0, 2, 3, 4, 0, 5,         /* "\t\n !\"$&'" */
+		6, 7, 2, 2, 2, 2, 0, 0,         /* "()*-/:;<" */
+		2, 0, 2, 2, 8, 2, 9, 0,         /* "=>?[\\]`|" */
+		10, 2                           /* "}~" */
+	};
+# endif
+	const char *s;
+	int indx;
+
+	if (c == PEOF)
+		return CENDFILE;
+# if ENABLE_ASH_ALIAS
+	if (c == PEOA)
+		indx = 0;
+	else
+# endif
+	{
+		/* Cast is purely for paranoia here,
+		 * just in case someone passed signed char to us */
+		if ((unsigned char)c >= CTL_FIRST
+		 && (unsigned char)c <= CTL_LAST
+		) {
+			return CCTL;
+		}
+		s = strchrnul(spec_symbls, c);
+		if (*s == '\0')
+			return CWORD;
+		indx = syntax_index_table[s - spec_symbls];
+	}
+	return (S_I_T[indx] >> (syntax*4)) & 0xf;
+}
+
+#else   /* !USE_SIT_FUNCTION */
+
+static const uint8_t syntax_index_table[] = {
+	/* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
+	/*   0      */ CWORD_CWORD_CWORD_CWORD,
+	/*   1      */ CWORD_CWORD_CWORD_CWORD,
+	/*   2      */ CWORD_CWORD_CWORD_CWORD,
+	/*   3      */ CWORD_CWORD_CWORD_CWORD,
+	/*   4      */ CWORD_CWORD_CWORD_CWORD,
+	/*   5      */ CWORD_CWORD_CWORD_CWORD,
+	/*   6      */ CWORD_CWORD_CWORD_CWORD,
+	/*   7      */ CWORD_CWORD_CWORD_CWORD,
+	/*   8      */ CWORD_CWORD_CWORD_CWORD,
+	/*   9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
+	/*  10 "\n" */ CNL_CNL_CNL_CNL,
+	/*  11      */ CWORD_CWORD_CWORD_CWORD,
+	/*  12      */ CWORD_CWORD_CWORD_CWORD,
+	/*  13      */ CWORD_CWORD_CWORD_CWORD,
+	/*  14      */ CWORD_CWORD_CWORD_CWORD,
+	/*  15      */ CWORD_CWORD_CWORD_CWORD,
+	/*  16      */ CWORD_CWORD_CWORD_CWORD,
+	/*  17      */ CWORD_CWORD_CWORD_CWORD,
+	/*  18      */ CWORD_CWORD_CWORD_CWORD,
+	/*  19      */ CWORD_CWORD_CWORD_CWORD,
+	/*  20      */ CWORD_CWORD_CWORD_CWORD,
+	/*  21      */ CWORD_CWORD_CWORD_CWORD,
+	/*  22      */ CWORD_CWORD_CWORD_CWORD,
+	/*  23      */ CWORD_CWORD_CWORD_CWORD,
+	/*  24      */ CWORD_CWORD_CWORD_CWORD,
+	/*  25      */ CWORD_CWORD_CWORD_CWORD,
+	/*  26      */ CWORD_CWORD_CWORD_CWORD,
+	/*  27      */ CWORD_CWORD_CWORD_CWORD,
+	/*  28      */ CWORD_CWORD_CWORD_CWORD,
+	/*  29      */ CWORD_CWORD_CWORD_CWORD,
+	/*  30      */ CWORD_CWORD_CWORD_CWORD,
+	/*  31      */ CWORD_CWORD_CWORD_CWORD,
+	/*  32  " " */ CSPCL_CWORD_CWORD_CWORD,
+	/*  33  "!" */ CWORD_CCTL_CCTL_CWORD,
+	/*  34  """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
+	/*  35  "#" */ CWORD_CWORD_CWORD_CWORD,
+	/*  36  "$" */ CVAR_CVAR_CWORD_CVAR,
+	/*  37  "%" */ CWORD_CWORD_CWORD_CWORD,
+	/*  38  "&" */ CSPCL_CWORD_CWORD_CWORD,
+	/*  39  "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
+	/*  40  "(" */ CSPCL_CWORD_CWORD_CLP,
+	/*  41  ")" */ CSPCL_CWORD_CWORD_CRP,
+	/*  42  "*" */ CWORD_CCTL_CCTL_CWORD,
+	/*  43  "+" */ CWORD_CWORD_CWORD_CWORD,
+	/*  44  "," */ CWORD_CWORD_CWORD_CWORD,
+	/*  45  "-" */ CWORD_CCTL_CCTL_CWORD,
+	/*  46  "." */ CWORD_CWORD_CWORD_CWORD,
+	/*  47  "/" */ CWORD_CCTL_CCTL_CWORD,
+	/*  48  "0" */ CWORD_CWORD_CWORD_CWORD,
+	/*  49  "1" */ CWORD_CWORD_CWORD_CWORD,
+	/*  50  "2" */ CWORD_CWORD_CWORD_CWORD,
+	/*  51  "3" */ CWORD_CWORD_CWORD_CWORD,
+	/*  52  "4" */ CWORD_CWORD_CWORD_CWORD,
+	/*  53  "5" */ CWORD_CWORD_CWORD_CWORD,
+	/*  54  "6" */ CWORD_CWORD_CWORD_CWORD,
+	/*  55  "7" */ CWORD_CWORD_CWORD_CWORD,
+	/*  56  "8" */ CWORD_CWORD_CWORD_CWORD,
+	/*  57  "9" */ CWORD_CWORD_CWORD_CWORD,
+	/*  58  ":" */ CWORD_CCTL_CCTL_CWORD,
+	/*  59  ";" */ CSPCL_CWORD_CWORD_CWORD,
+	/*  60  "<" */ CSPCL_CWORD_CWORD_CWORD,
+	/*  61  "=" */ CWORD_CCTL_CCTL_CWORD,
+	/*  62  ">" */ CSPCL_CWORD_CWORD_CWORD,
+	/*  63  "?" */ CWORD_CCTL_CCTL_CWORD,
+	/*  64  "@" */ CWORD_CWORD_CWORD_CWORD,
+	/*  65  "A" */ CWORD_CWORD_CWORD_CWORD,
+	/*  66  "B" */ CWORD_CWORD_CWORD_CWORD,
+	/*  67  "C" */ CWORD_CWORD_CWORD_CWORD,
+	/*  68  "D" */ CWORD_CWORD_CWORD_CWORD,
+	/*  69  "E" */ CWORD_CWORD_CWORD_CWORD,
+	/*  70  "F" */ CWORD_CWORD_CWORD_CWORD,
+	/*  71  "G" */ CWORD_CWORD_CWORD_CWORD,
+	/*  72  "H" */ CWORD_CWORD_CWORD_CWORD,
+	/*  73  "I" */ CWORD_CWORD_CWORD_CWORD,
+	/*  74  "J" */ CWORD_CWORD_CWORD_CWORD,
+	/*  75  "K" */ CWORD_CWORD_CWORD_CWORD,
+	/*  76  "L" */ CWORD_CWORD_CWORD_CWORD,
+	/*  77  "M" */ CWORD_CWORD_CWORD_CWORD,
+	/*  78  "N" */ CWORD_CWORD_CWORD_CWORD,
+	/*  79  "O" */ CWORD_CWORD_CWORD_CWORD,
+	/*  80  "P" */ CWORD_CWORD_CWORD_CWORD,
+	/*  81  "Q" */ CWORD_CWORD_CWORD_CWORD,
+	/*  82  "R" */ CWORD_CWORD_CWORD_CWORD,
+	/*  83  "S" */ CWORD_CWORD_CWORD_CWORD,
+	/*  84  "T" */ CWORD_CWORD_CWORD_CWORD,
+	/*  85  "U" */ CWORD_CWORD_CWORD_CWORD,
+	/*  86  "V" */ CWORD_CWORD_CWORD_CWORD,
+	/*  87  "W" */ CWORD_CWORD_CWORD_CWORD,
+	/*  88  "X" */ CWORD_CWORD_CWORD_CWORD,
+	/*  89  "Y" */ CWORD_CWORD_CWORD_CWORD,
+	/*  90  "Z" */ CWORD_CWORD_CWORD_CWORD,
+	/*  91  "[" */ CWORD_CCTL_CCTL_CWORD,
+	/*  92  "\" */ CBACK_CBACK_CCTL_CBACK,
+	/*  93  "]" */ CWORD_CCTL_CCTL_CWORD,
+	/*  94  "^" */ CWORD_CWORD_CWORD_CWORD,
+	/*  95  "_" */ CWORD_CWORD_CWORD_CWORD,
+	/*  96  "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
+	/*  97  "a" */ CWORD_CWORD_CWORD_CWORD,
+	/*  98  "b" */ CWORD_CWORD_CWORD_CWORD,
+	/*  99  "c" */ CWORD_CWORD_CWORD_CWORD,
+	/* 100  "d" */ CWORD_CWORD_CWORD_CWORD,
+	/* 101  "e" */ CWORD_CWORD_CWORD_CWORD,
+	/* 102  "f" */ CWORD_CWORD_CWORD_CWORD,
+	/* 103  "g" */ CWORD_CWORD_CWORD_CWORD,
+	/* 104  "h" */ CWORD_CWORD_CWORD_CWORD,
+	/* 105  "i" */ CWORD_CWORD_CWORD_CWORD,
+	/* 106  "j" */ CWORD_CWORD_CWORD_CWORD,
+	/* 107  "k" */ CWORD_CWORD_CWORD_CWORD,
+	/* 108  "l" */ CWORD_CWORD_CWORD_CWORD,
+	/* 109  "m" */ CWORD_CWORD_CWORD_CWORD,
+	/* 110  "n" */ CWORD_CWORD_CWORD_CWORD,
+	/* 111  "o" */ CWORD_CWORD_CWORD_CWORD,
+	/* 112  "p" */ CWORD_CWORD_CWORD_CWORD,
+	/* 113  "q" */ CWORD_CWORD_CWORD_CWORD,
+	/* 114  "r" */ CWORD_CWORD_CWORD_CWORD,
+	/* 115  "s" */ CWORD_CWORD_CWORD_CWORD,
+	/* 116  "t" */ CWORD_CWORD_CWORD_CWORD,
+	/* 117  "u" */ CWORD_CWORD_CWORD_CWORD,
+	/* 118  "v" */ CWORD_CWORD_CWORD_CWORD,
+	/* 119  "w" */ CWORD_CWORD_CWORD_CWORD,
+	/* 120  "x" */ CWORD_CWORD_CWORD_CWORD,
+	/* 121  "y" */ CWORD_CWORD_CWORD_CWORD,
+	/* 122  "z" */ CWORD_CWORD_CWORD_CWORD,
+	/* 123  "{" */ CWORD_CWORD_CWORD_CWORD,
+	/* 124  "|" */ CSPCL_CWORD_CWORD_CWORD,
+	/* 125  "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
+	/* 126  "~" */ CWORD_CCTL_CCTL_CWORD,
+	/* 127  del */ CWORD_CWORD_CWORD_CWORD,
+	/* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
+	/* 129 CTLESC       */ CCTL_CCTL_CCTL_CCTL,
+	/* 130 CTLVAR       */ CCTL_CCTL_CCTL_CCTL,
+	/* 131 CTLENDVAR    */ CCTL_CCTL_CCTL_CCTL,
+	/* 132 CTLBACKQ     */ CCTL_CCTL_CCTL_CCTL,
+	/* 133 CTLQUOTE     */ CCTL_CCTL_CCTL_CCTL,
+	/* 134 CTLARI       */ CCTL_CCTL_CCTL_CCTL,
+	/* 135 CTLENDARI    */ CCTL_CCTL_CCTL_CCTL,
+	/* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
+	/* 137      */ CWORD_CWORD_CWORD_CWORD,
+	/* 138      */ CWORD_CWORD_CWORD_CWORD,
+	/* 139      */ CWORD_CWORD_CWORD_CWORD,
+	/* 140      */ CWORD_CWORD_CWORD_CWORD,
+	/* 141      */ CWORD_CWORD_CWORD_CWORD,
+	/* 142      */ CWORD_CWORD_CWORD_CWORD,
+	/* 143      */ CWORD_CWORD_CWORD_CWORD,
+	/* 144      */ CWORD_CWORD_CWORD_CWORD,
+	/* 145      */ CWORD_CWORD_CWORD_CWORD,
+	/* 146      */ CWORD_CWORD_CWORD_CWORD,
+	/* 147      */ CWORD_CWORD_CWORD_CWORD,
+	/* 148      */ CWORD_CWORD_CWORD_CWORD,
+	/* 149      */ CWORD_CWORD_CWORD_CWORD,
+	/* 150      */ CWORD_CWORD_CWORD_CWORD,
+	/* 151      */ CWORD_CWORD_CWORD_CWORD,
+	/* 152      */ CWORD_CWORD_CWORD_CWORD,
+	/* 153      */ CWORD_CWORD_CWORD_CWORD,
+	/* 154      */ CWORD_CWORD_CWORD_CWORD,
+	/* 155      */ CWORD_CWORD_CWORD_CWORD,
+	/* 156      */ CWORD_CWORD_CWORD_CWORD,
+	/* 157      */ CWORD_CWORD_CWORD_CWORD,
+	/* 158      */ CWORD_CWORD_CWORD_CWORD,
+	/* 159      */ CWORD_CWORD_CWORD_CWORD,
+	/* 160      */ CWORD_CWORD_CWORD_CWORD,
+	/* 161      */ CWORD_CWORD_CWORD_CWORD,
+	/* 162      */ CWORD_CWORD_CWORD_CWORD,
+	/* 163      */ CWORD_CWORD_CWORD_CWORD,
+	/* 164      */ CWORD_CWORD_CWORD_CWORD,
+	/* 165      */ CWORD_CWORD_CWORD_CWORD,
+	/* 166      */ CWORD_CWORD_CWORD_CWORD,
+	/* 167      */ CWORD_CWORD_CWORD_CWORD,
+	/* 168      */ CWORD_CWORD_CWORD_CWORD,
+	/* 169      */ CWORD_CWORD_CWORD_CWORD,
+	/* 170      */ CWORD_CWORD_CWORD_CWORD,
+	/* 171      */ CWORD_CWORD_CWORD_CWORD,
+	/* 172      */ CWORD_CWORD_CWORD_CWORD,
+	/* 173      */ CWORD_CWORD_CWORD_CWORD,
+	/* 174      */ CWORD_CWORD_CWORD_CWORD,
+	/* 175      */ CWORD_CWORD_CWORD_CWORD,
+	/* 176      */ CWORD_CWORD_CWORD_CWORD,
+	/* 177      */ CWORD_CWORD_CWORD_CWORD,
+	/* 178      */ CWORD_CWORD_CWORD_CWORD,
+	/* 179      */ CWORD_CWORD_CWORD_CWORD,
+	/* 180      */ CWORD_CWORD_CWORD_CWORD,
+	/* 181      */ CWORD_CWORD_CWORD_CWORD,
+	/* 182      */ CWORD_CWORD_CWORD_CWORD,
+	/* 183      */ CWORD_CWORD_CWORD_CWORD,
+	/* 184      */ CWORD_CWORD_CWORD_CWORD,
+	/* 185      */ CWORD_CWORD_CWORD_CWORD,
+	/* 186      */ CWORD_CWORD_CWORD_CWORD,
+	/* 187      */ CWORD_CWORD_CWORD_CWORD,
+	/* 188      */ CWORD_CWORD_CWORD_CWORD,
+	/* 189      */ CWORD_CWORD_CWORD_CWORD,
+	/* 190      */ CWORD_CWORD_CWORD_CWORD,
+	/* 191      */ CWORD_CWORD_CWORD_CWORD,
+	/* 192      */ CWORD_CWORD_CWORD_CWORD,
+	/* 193      */ CWORD_CWORD_CWORD_CWORD,
+	/* 194      */ CWORD_CWORD_CWORD_CWORD,
+	/* 195      */ CWORD_CWORD_CWORD_CWORD,
+	/* 196      */ CWORD_CWORD_CWORD_CWORD,
+	/* 197      */ CWORD_CWORD_CWORD_CWORD,
+	/* 198      */ CWORD_CWORD_CWORD_CWORD,
+	/* 199      */ CWORD_CWORD_CWORD_CWORD,
+	/* 200      */ CWORD_CWORD_CWORD_CWORD,
+	/* 201      */ CWORD_CWORD_CWORD_CWORD,
+	/* 202      */ CWORD_CWORD_CWORD_CWORD,
+	/* 203      */ CWORD_CWORD_CWORD_CWORD,
+	/* 204      */ CWORD_CWORD_CWORD_CWORD,
+	/* 205      */ CWORD_CWORD_CWORD_CWORD,
+	/* 206      */ CWORD_CWORD_CWORD_CWORD,
+	/* 207      */ CWORD_CWORD_CWORD_CWORD,
+	/* 208      */ CWORD_CWORD_CWORD_CWORD,
+	/* 209      */ CWORD_CWORD_CWORD_CWORD,
+	/* 210      */ CWORD_CWORD_CWORD_CWORD,
+	/* 211      */ CWORD_CWORD_CWORD_CWORD,
+	/* 212      */ CWORD_CWORD_CWORD_CWORD,
+	/* 213      */ CWORD_CWORD_CWORD_CWORD,
+	/* 214      */ CWORD_CWORD_CWORD_CWORD,
+	/* 215      */ CWORD_CWORD_CWORD_CWORD,
+	/* 216      */ CWORD_CWORD_CWORD_CWORD,
+	/* 217      */ CWORD_CWORD_CWORD_CWORD,
+	/* 218      */ CWORD_CWORD_CWORD_CWORD,
+	/* 219      */ CWORD_CWORD_CWORD_CWORD,
+	/* 220      */ CWORD_CWORD_CWORD_CWORD,
+	/* 221      */ CWORD_CWORD_CWORD_CWORD,
+	/* 222      */ CWORD_CWORD_CWORD_CWORD,
+	/* 223      */ CWORD_CWORD_CWORD_CWORD,
+	/* 224      */ CWORD_CWORD_CWORD_CWORD,
+	/* 225      */ CWORD_CWORD_CWORD_CWORD,
+	/* 226      */ CWORD_CWORD_CWORD_CWORD,
+	/* 227      */ CWORD_CWORD_CWORD_CWORD,
+	/* 228      */ CWORD_CWORD_CWORD_CWORD,
+	/* 229      */ CWORD_CWORD_CWORD_CWORD,
+	/* 230      */ CWORD_CWORD_CWORD_CWORD,
+	/* 231      */ CWORD_CWORD_CWORD_CWORD,
+	/* 232      */ CWORD_CWORD_CWORD_CWORD,
+	/* 233      */ CWORD_CWORD_CWORD_CWORD,
+	/* 234      */ CWORD_CWORD_CWORD_CWORD,
+	/* 235      */ CWORD_CWORD_CWORD_CWORD,
+	/* 236      */ CWORD_CWORD_CWORD_CWORD,
+	/* 237      */ CWORD_CWORD_CWORD_CWORD,
+	/* 238      */ CWORD_CWORD_CWORD_CWORD,
+	/* 239      */ CWORD_CWORD_CWORD_CWORD,
+	/* 230      */ CWORD_CWORD_CWORD_CWORD,
+	/* 241      */ CWORD_CWORD_CWORD_CWORD,
+	/* 242      */ CWORD_CWORD_CWORD_CWORD,
+	/* 243      */ CWORD_CWORD_CWORD_CWORD,
+	/* 244      */ CWORD_CWORD_CWORD_CWORD,
+	/* 245      */ CWORD_CWORD_CWORD_CWORD,
+	/* 246      */ CWORD_CWORD_CWORD_CWORD,
+	/* 247      */ CWORD_CWORD_CWORD_CWORD,
+	/* 248      */ CWORD_CWORD_CWORD_CWORD,
+	/* 249      */ CWORD_CWORD_CWORD_CWORD,
+	/* 250      */ CWORD_CWORD_CWORD_CWORD,
+	/* 251      */ CWORD_CWORD_CWORD_CWORD,
+	/* 252      */ CWORD_CWORD_CWORD_CWORD,
+	/* 253      */ CWORD_CWORD_CWORD_CWORD,
+	/* 254      */ CWORD_CWORD_CWORD_CWORD,
+	/* 255      */ CWORD_CWORD_CWORD_CWORD,
+	/* PEOF */     CENDFILE_CENDFILE_CENDFILE_CENDFILE,
+# if ENABLE_ASH_ALIAS
+	/* PEOA */     CSPCL_CIGN_CIGN_CIGN,
+# endif
+};
+
+# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
+
+#endif  /* !USE_SIT_FUNCTION */
+
+
+/* ============ Alias handling */
+
+#if ENABLE_ASH_ALIAS
+
+#define ALIASINUSE 1
+#define ALIASDEAD  2
+
+struct alias {
+	struct alias *next;
+	char *name;
+	char *val;
+	int flag;
+};
+
+
+static struct alias **atab; // [ATABSIZE];
+#define INIT_G_alias() do { \
+	atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
+} while (0)
+
+
+static struct alias **
+__lookupalias(const char *name) {
+	unsigned int hashval;
+	struct alias **app;
+	const char *p;
+	unsigned int ch;
+
+	p = name;
+
+	ch = (unsigned char)*p;
+	hashval = ch << 4;
+	while (ch) {
+		hashval += ch;
+		ch = (unsigned char)*++p;
+	}
+	app = &atab[hashval % ATABSIZE];
+
+	for (; *app; app = &(*app)->next) {
+		if (strcmp(name, (*app)->name) == 0) {
+			break;
+		}
+	}
+
+	return app;
+}
+
+static struct alias *
+lookupalias(const char *name, int check)
+{
+	struct alias *ap = *__lookupalias(name);
+
+	if (check && ap && (ap->flag & ALIASINUSE))
+		return NULL;
+	return ap;
+}
+
+static struct alias *
+freealias(struct alias *ap)
+{
+	struct alias *next;
+
+	if (ap->flag & ALIASINUSE) {
+		ap->flag |= ALIASDEAD;
+		return ap;
+	}
+
+	next = ap->next;
+	free(ap->name);
+	free(ap->val);
+	free(ap);
+	return next;
+}
+
+static void
+setalias(const char *name, const char *val)
+{
+	struct alias *ap, **app;
+
+	app = __lookupalias(name);
+	ap = *app;
+	INT_OFF;
+	if (ap) {
+		if (!(ap->flag & ALIASINUSE)) {
+			free(ap->val);
+		}
+		ap->val = ckstrdup(val);
+		ap->flag &= ~ALIASDEAD;
+	} else {
+		/* not found */
+		ap = ckzalloc(sizeof(struct alias));
+		ap->name = ckstrdup(name);
+		ap->val = ckstrdup(val);
+		/*ap->flag = 0; - ckzalloc did it */
+		/*ap->next = NULL;*/
+		*app = ap;
+	}
+	INT_ON;
+}
+
+static int
+unalias(const char *name)
+{
+	struct alias **app;
+
+	app = __lookupalias(name);
+
+	if (*app) {
+		INT_OFF;
+		*app = freealias(*app);
+		INT_ON;
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+rmaliases(void)
+{
+	struct alias *ap, **app;
+	int i;
+
+	INT_OFF;
+	for (i = 0; i < ATABSIZE; i++) {
+		app = &atab[i];
+		for (ap = *app; ap; ap = *app) {
+			*app = freealias(*app);
+			if (ap == *app) {
+				app = &ap->next;
+			}
+		}
+	}
+	INT_ON;
+}
+
+static void
+printalias(const struct alias *ap)
+{
+	out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
+}
+
+/*
+ * TODO - sort output
+ */
+static int FAST_FUNC
+aliascmd(int argc UNUSED_PARAM, char **argv)
+{
+	char *n, *v;
+	int ret = 0;
+	struct alias *ap;
+
+	if (!argv[1]) {
+		int i;
+
+		for (i = 0; i < ATABSIZE; i++) {
+			for (ap = atab[i]; ap; ap = ap->next) {
+				printalias(ap);
+			}
+		}
+		return 0;
+	}
+	while ((n = *++argv) != NULL) {
+		v = strchr(n+1, '=');
+		if (v == NULL) { /* n+1: funny ksh stuff */
+			ap = *__lookupalias(n);
+			if (ap == NULL) {
+				fprintf(stderr, "%s: %s not found\n", "alias", n);
+				ret = 1;
+			} else
+				printalias(ap);
+		} else {
+			*v++ = '\0';
+			setalias(n, v);
+		}
+	}
+
+	return ret;
+}
+
+static int FAST_FUNC
+unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	int i;
+
+	while ((i = nextopt("a")) != '\0') {
+		if (i == 'a') {
+			rmaliases();
+			return 0;
+		}
+	}
+	for (i = 0; *argptr; argptr++) {
+		if (unalias(*argptr)) {
+			fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
+			i = 1;
+		}
+	}
+
+	return i;
+}
+
+#endif /* ASH_ALIAS */
+
+
+/* ============ jobs.c */
+
+/* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
+#define FORK_FG    0
+#define FORK_BG    1
+#define FORK_NOJOB 2
+
+/* mode flags for showjob(s) */
+#define SHOW_ONLY_PGID  0x01    /* show only pgid (jobs -p) */
+#define SHOW_PIDS       0x02    /* show individual pids, not just one line per job */
+#define SHOW_CHANGED    0x04    /* only jobs whose state has changed */
+
+/*
+ * A job structure contains information about a job.  A job is either a
+ * single process or a set of processes contained in a pipeline.  In the
+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
+ * array of pids.
+ */
+struct procstat {
+	pid_t   ps_pid;         /* process id */
+	int     ps_status;      /* last process status from wait() */
+	char    *ps_cmd;        /* text of command being run */
+};
+
+struct job {
+	struct procstat ps0;    /* status of process */
+	struct procstat *ps;    /* status or processes when more than one */
+#if JOBS
+	int stopstatus;         /* status of a stopped job */
+#endif
+	uint32_t
+		nprocs: 16,     /* number of processes */
+		state: 8,
+#define JOBRUNNING      0       /* at least one proc running */
+#define JOBSTOPPED      1       /* all procs are stopped */
+#define JOBDONE         2       /* all procs are completed */
+#if JOBS
+		sigint: 1,      /* job was killed by SIGINT */
+		jobctl: 1,      /* job running under job control */
+#endif
+		waited: 1,      /* true if this entry has been waited for */
+		used: 1,        /* true if this entry is in used */
+		changed: 1;     /* true if status has changed */
+	struct job *prev_job;   /* previous job */
+};
+
+static struct job *makejob(/*union node *,*/ int);
+static int forkshell(struct job *, union node *, int);
+static int waitforjob(struct job *);
+
+#if !JOBS
+enum { doing_jobctl = 0 };
+#define setjobctl(on) do {} while (0)
+#else
+static smallint doing_jobctl; //references:8
+static void setjobctl(int);
+#endif
+
+/*
+ * Ignore a signal.
+ */
+static void
+ignoresig(int signo)
+{
+	/* Avoid unnecessary system calls. Is it already SIG_IGNed? */
+	if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
+		/* No, need to do it */
+		signal(signo, SIG_IGN);
+	}
+	sigmode[signo - 1] = S_HARD_IGN;
+}
+
+/*
+ * Only one usage site - in setsignal()
+ */
+static void
+signal_handler(int signo)
+{
+	gotsig[signo - 1] = 1;
+
+	if (signo == SIGINT && !trap[SIGINT]) {
+		if (!suppress_int) {
+			pending_sig = 0;
+			raise_interrupt(); /* does not return */
+		}
+		pending_int = 1;
+	} else {
+		pending_sig = signo;
+	}
+}
+
+/*
+ * Set the signal handler for the specified signal.  The routine figures
+ * out what it should be set to.
+ */
+static void
+setsignal(int signo)
+{
+	char *t;
+	char cur_act, new_act;
+	struct sigaction act;
+
+	t = trap[signo];
+	new_act = S_DFL;
+	if (t != NULL) { /* trap for this sig is set */
+		new_act = S_CATCH;
+		if (t[0] == '\0') /* trap is "": ignore this sig */
+			new_act = S_IGN;
+	}
+
+	if (rootshell && new_act == S_DFL) {
+		switch (signo) {
+		case SIGINT:
+			if (iflag || minusc || sflag == 0)
+				new_act = S_CATCH;
+			break;
+		case SIGQUIT:
+#if DEBUG
+			if (debug)
+				break;
+#endif
+			/* man bash:
+			 * "In all cases, bash ignores SIGQUIT. Non-builtin
+			 * commands run by bash have signal handlers
+			 * set to the values inherited by the shell
+			 * from its parent". */
+			new_act = S_IGN;
+			break;
+		case SIGTERM:
+			if (iflag)
+				new_act = S_IGN;
+			break;
+#if JOBS
+		case SIGTSTP:
+		case SIGTTOU:
+			if (mflag)
+				new_act = S_IGN;
+			break;
+#endif
+		}
+	}
+//TODO: if !rootshell, we reset SIGQUIT to DFL,
+//whereas we have to restore it to what shell got on entry
+//from the parent. See comment above
+
+	t = &sigmode[signo - 1];
+	cur_act = *t;
+	if (cur_act == 0) {
+		/* current setting is not yet known */
+		if (sigaction(signo, NULL, &act)) {
+			/* pretend it worked; maybe we should give a warning,
+			 * but other shells don't. We don't alter sigmode,
+			 * so we retry every time.
+			 * btw, in Linux it never fails. --vda */
+			return;
+		}
+		if (act.sa_handler == SIG_IGN) {
+			cur_act = S_HARD_IGN;
+			if (mflag
+			 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
+			) {
+				cur_act = S_IGN;   /* don't hard ignore these */
+			}
+		}
+	}
+	if (cur_act == S_HARD_IGN || cur_act == new_act)
+		return;
+
+	act.sa_handler = SIG_DFL;
+	switch (new_act) {
+	case S_CATCH:
+		act.sa_handler = signal_handler;
+		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;
+}
+
+/* mode flags for set_curjob */
+#define CUR_DELETE 2
+#define CUR_RUNNING 1
+#define CUR_STOPPED 0
+
+/* mode flags for dowait */
+#define DOWAIT_NONBLOCK WNOHANG
+#define DOWAIT_BLOCK    0
+
+#if JOBS
+/* pgrp of shell on invocation */
+static int initialpgrp; //references:2
+static int ttyfd = -1; //5
+#endif
+/* array of jobs */
+static struct job *jobtab; //5
+/* size of array */
+static unsigned njobs; //4
+/* current job */
+static struct job *curjob; //lots
+/* number of presumed living untracked jobs */
+static int jobless; //4
+
+static void
+set_curjob(struct job *jp, unsigned mode)
+{
+	struct job *jp1;
+	struct job **jpp, **curp;
+
+	/* first remove from list */
+	jpp = curp = &curjob;
+	while (1) {
+		jp1 = *jpp;
+		if (jp1 == jp)
+			break;
+		jpp = &jp1->prev_job;
+	}
+	*jpp = jp1->prev_job;
+
+	/* Then re-insert in correct position */
+	jpp = curp;
+	switch (mode) {
+	default:
+#if DEBUG
+		abort();
+#endif
+	case CUR_DELETE:
+		/* job being deleted */
+		break;
+	case CUR_RUNNING:
+		/* newly created job or backgrounded job,
+		   put after all stopped jobs. */
+		while (1) {
+			jp1 = *jpp;
+#if JOBS
+			if (!jp1 || jp1->state != JOBSTOPPED)
+#endif
+				break;
+			jpp = &jp1->prev_job;
+		}
+		/* FALLTHROUGH */
+#if JOBS
+	case CUR_STOPPED:
+#endif
+		/* newly stopped job - becomes curjob */
+		jp->prev_job = *jpp;
+		*jpp = jp;
+		break;
+	}
+}
+
+#if JOBS || DEBUG
+static int
+jobno(const struct job *jp)
+{
+	return jp - jobtab + 1;
+}
+#endif
+
+/*
+ * Convert a job name to a job structure.
+ */
+#if !JOBS
+#define getjob(name, getctl) getjob(name)
+#endif
+static struct job *
+getjob(const char *name, int getctl)
+{
+	struct job *jp;
+	struct job *found;
+	const char *err_msg = "%s: no such job";
+	unsigned num;
+	int c;
+	const char *p;
+	char *(*match)(const char *, const char *);
+
+	jp = curjob;
+	p = name;
+	if (!p)
+		goto currentjob;
+
+	if (*p != '%')
+		goto err;
+
+	c = *++p;
+	if (!c)
+		goto currentjob;
+
+	if (!p[1]) {
+		if (c == '+' || c == '%') {
+ currentjob:
+			err_msg = "No current job";
+			goto check;
+		}
+		if (c == '-') {
+			if (jp)
+				jp = jp->prev_job;
+			err_msg = "No previous job";
+ check:
+			if (!jp)
+				goto err;
+			goto gotit;
+		}
+	}
+
+	if (is_number(p)) {
+		num = atoi(p);
+		if (num < njobs) {
+			jp = jobtab + num - 1;
+			if (jp->used)
+				goto gotit;
+			goto err;
+		}
+	}
+
+	match = prefix;
+	if (*p == '?') {
+		match = strstr;
+		p++;
+	}
+
+	found = NULL;
+	while (jp) {
+		if (match(jp->ps[0].ps_cmd, p)) {
+			if (found)
+				goto err;
+			found = jp;
+			err_msg = "%s: ambiguous";
+		}
+		jp = jp->prev_job;
+	}
+	if (!found)
+		goto err;
+	jp = found;
+
+ gotit:
+#if JOBS
+	err_msg = "job %s not created under job control";
+	if (getctl && jp->jobctl == 0)
+		goto err;
+#endif
+	return jp;
+ err:
+	ash_msg_and_raise_error(err_msg, name);
+}
+
+/*
+ * Mark a job structure as unused.
+ */
+static void
+freejob(struct job *jp)
+{
+	struct procstat *ps;
+	int i;
+
+	INT_OFF;
+	for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
+		if (ps->ps_cmd != nullstr)
+			free(ps->ps_cmd);
+	}
+	if (jp->ps != &jp->ps0)
+		free(jp->ps);
+	jp->used = 0;
+	set_curjob(jp, CUR_DELETE);
+	INT_ON;
+}
+
+#if JOBS
+static void
+xtcsetpgrp(int fd, pid_t pgrp)
+{
+	if (tcsetpgrp(fd, pgrp))
+		ash_msg_and_raise_error("can't set tty process group (%m)");
+}
+
+/*
+ * Turn job control on and off.
+ *
+ * Note:  This code assumes that the third arg to ioctl is a character
+ * pointer, which is true on Berkeley systems but not System V.  Since
+ * System V doesn't have job control yet, this isn't a problem now.
+ *
+ * Called with interrupts off.
+ */
+static void
+setjobctl(int on)
+{
+	int fd;
+	int pgrp;
+
+	if (on == doing_jobctl || rootshell == 0)
+		return;
+	if (on) {
+		int ofd;
+		ofd = fd = open(_PATH_TTY, O_RDWR);
+		if (fd < 0) {
+	/* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
+	 * That sometimes helps to acquire controlling tty.
+	 * Obviously, a workaround for bugs when someone
+	 * failed to provide a controlling tty to bash! :) */
+			fd = 2;
+			while (!isatty(fd))
+				if (--fd < 0)
+					goto out;
+		}
+		fd = fcntl(fd, F_DUPFD, 10);
+		if (ofd >= 0)
+			close(ofd);
+		if (fd < 0)
+			goto out;
+		/* fd is a tty at this point */
+		close_on_exec_on(fd);
+		while (1) { /* while we are in the background */
+			pgrp = tcgetpgrp(fd);
+			if (pgrp < 0) {
+ out:
+				ash_msg("can't access tty; job control turned off");
+				mflag = on = 0;
+				goto close;
+			}
+			if (pgrp == getpgrp())
+				break;
+			killpg(0, SIGTTIN);
+		}
+		initialpgrp = pgrp;
+
+		setsignal(SIGTSTP);
+		setsignal(SIGTTOU);
+		setsignal(SIGTTIN);
+		pgrp = rootpid;
+		setpgid(0, pgrp);
+		xtcsetpgrp(fd, pgrp);
+	} else {
+		/* turning job control off */
+		fd = ttyfd;
+		pgrp = initialpgrp;
+		/* was xtcsetpgrp, but this can make exiting ash
+		 * loop forever if pty is already deleted */
+		tcsetpgrp(fd, pgrp);
+		setpgid(0, pgrp);
+		setsignal(SIGTSTP);
+		setsignal(SIGTTOU);
+		setsignal(SIGTTIN);
+ close:
+		if (fd >= 0)
+			close(fd);
+		fd = -1;
+	}
+	ttyfd = fd;
+	doing_jobctl = on;
+}
+
+static int FAST_FUNC
+killcmd(int argc, char **argv)
+{
+	if (argv[1] && strcmp(argv[1], "-l") != 0) {
+		int i = 1;
+		do {
+			if (argv[i][0] == '%') {
+				/*
+				 * "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]);
+	}
+	return kill_main(argc, argv);
+}
+
+static void
+showpipe(struct job *jp /*, FILE *out*/)
+{
+	struct procstat *ps;
+	struct procstat *psend;
+
+	psend = jp->ps + jp->nprocs;
+	for (ps = jp->ps + 1; ps < psend; ps++)
+		printf(" | %s", ps->ps_cmd);
+	outcslow('\n', stdout);
+	flush_stdout_stderr();
+}
+
+
+static int
+restartjob(struct job *jp, int mode)
+{
+	struct procstat *ps;
+	int i;
+	int status;
+	pid_t pgid;
+
+	INT_OFF;
+	if (jp->state == JOBDONE)
+		goto out;
+	jp->state = JOBRUNNING;
+	pgid = jp->ps[0].ps_pid;
+	if (mode == FORK_FG)
+		xtcsetpgrp(ttyfd, pgid);
+	killpg(pgid, SIGCONT);
+	ps = jp->ps;
+	i = jp->nprocs;
+	do {
+		if (WIFSTOPPED(ps->ps_status)) {
+			ps->ps_status = -1;
+		}
+		ps++;
+	} while (--i);
+ out:
+	status = (mode == FORK_FG) ? waitforjob(jp) : 0;
+	INT_ON;
+	return status;
+}
+
+static int FAST_FUNC
+fg_bgcmd(int argc UNUSED_PARAM, char **argv)
+{
+	struct job *jp;
+	int mode;
+	int retval;
+
+	mode = (**argv == 'f') ? FORK_FG : FORK_BG;
+	nextopt(nullstr);
+	argv = argptr;
+	do {
+		jp = getjob(*argv, 1);
+		if (mode == FORK_BG) {
+			set_curjob(jp, CUR_RUNNING);
+			printf("[%d] ", jobno(jp));
+		}
+		out1str(jp->ps[0].ps_cmd);
+		showpipe(jp /*, stdout*/);
+		retval = restartjob(jp, mode);
+	} while (*argv && *++argv);
+	return retval;
+}
+#endif
+
+static int
+sprint_status(char *s, int status, int sigonly)
+{
+	int col;
+	int st;
+
+	col = 0;
+	if (!WIFEXITED(status)) {
+#if JOBS
+		if (WIFSTOPPED(status))
+			st = WSTOPSIG(status);
+		else
+#endif
+			st = WTERMSIG(status);
+		if (sigonly) {
+			if (st == SIGINT || st == SIGPIPE)
+				goto out;
+#if JOBS
+			if (WIFSTOPPED(status))
+				goto out;
+#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)");
+		}
+	} else if (!sigonly) {
+		st = WEXITSTATUS(status);
+		if (st)
+			col = fmtstr(s, 16, "Done(%d)", st);
+		else
+			col = fmtstr(s, 16, "Done");
+	}
+ out:
+	return col;
+}
+
+static int
+dowait(int wait_flags, struct job *job)
+{
+	int pid;
+	int status;
+	struct job *jp;
+	struct job *thisjob;
+	int state;
+
+	TRACE(("dowait(0x%x) called\n", wait_flags));
+
+	/* Do a wait system call. If job control is compiled in, we accept
+	 * stopped processes. wait_flags may have WNOHANG, preventing blocking.
+	 * NB: _not_ safe_waitpid, we need to detect EINTR */
+	if (doing_jobctl)
+		wait_flags |= WUNTRACED;
+	pid = waitpid(-1, &status, wait_flags);
+	TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n",
+				pid, status, errno, strerror(errno)));
+	if (pid <= 0)
+		return pid;
+
+	INT_OFF;
+	thisjob = NULL;
+	for (jp = curjob; jp; jp = jp->prev_job) {
+		struct procstat *ps;
+		struct procstat *psend;
+		if (jp->state == JOBDONE)
+			continue;
+		state = JOBDONE;
+		ps = jp->ps;
+		psend = ps + jp->nprocs;
+		do {
+			if (ps->ps_pid == pid) {
+				TRACE(("Job %d: changing status of proc %d "
+					"from 0x%x to 0x%x\n",
+					jobno(jp), pid, ps->ps_status, status));
+				ps->ps_status = status;
+				thisjob = jp;
+			}
+			if (ps->ps_status == -1)
+				state = JOBRUNNING;
+#if JOBS
+			if (state == JOBRUNNING)
+				continue;
+			if (WIFSTOPPED(ps->ps_status)) {
+				jp->stopstatus = ps->ps_status;
+				state = JOBSTOPPED;
+			}
+#endif
+		} while (++ps < psend);
+		if (thisjob)
+			goto gotjob;
+	}
+#if JOBS
+	if (!WIFSTOPPED(status))
+#endif
+		jobless--;
+	goto out;
+
+ gotjob:
+	if (state != JOBRUNNING) {
+		thisjob->changed = 1;
+
+		if (thisjob->state != state) {
+			TRACE(("Job %d: changing state from %d to %d\n",
+				jobno(thisjob), thisjob->state, state));
+			thisjob->state = state;
+#if JOBS
+			if (state == JOBSTOPPED) {
+				set_curjob(thisjob, CUR_STOPPED);
+			}
+#endif
+		}
+	}
+
+ out:
+	INT_ON;
+
+	if (thisjob && thisjob == job) {
+		char s[48 + 1];
+		int len;
+
+		len = sprint_status(s, status, 1);
+		if (len) {
+			s[len] = '\n';
+			s[len + 1] = '\0';
+			out2str(s);
+		}
+	}
+	return pid;
+}
+
+static int
+blocking_wait_with_raise_on_sig(void)
+{
+	pid_t pid = dowait(DOWAIT_BLOCK, NULL);
+	if (pid <= 0 && pending_sig)
+		raise_exception(EXSIG);
+	return pid;
+}
+
+#if JOBS
+static void
+showjob(FILE *out, struct job *jp, int mode)
+{
+	struct procstat *ps;
+	struct procstat *psend;
+	int col;
+	int indent_col;
+	char s[80];
+
+	ps = jp->ps;
+
+	if (mode & SHOW_ONLY_PGID) { /* jobs -p */
+		/* just output process (group) id of pipeline */
+		fprintf(out, "%d\n", ps->ps_pid);
+		return;
+	}
+
+	col = fmtstr(s, 16, "[%d]   ", jobno(jp));
+	indent_col = col;
+
+	if (jp == curjob)
+		s[col - 3] = '+';
+	else if (curjob && jp == curjob->prev_job)
+		s[col - 3] = '-';
+
+	if (mode & SHOW_PIDS)
+		col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
+
+	psend = ps + jp->nprocs;
+
+	if (jp->state == JOBRUNNING) {
+		strcpy(s + col, "Running");
+		col += sizeof("Running") - 1;
+	} else {
+		int status = psend[-1].ps_status;
+		if (jp->state == JOBSTOPPED)
+			status = jp->stopstatus;
+		col += sprint_status(s + col, status, 0);
+	}
+	/* By now, "[JOBID]*  [maybe PID] STATUS" is printed */
+
+	/* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
+	 * or prints several "PID             | <cmdN>" lines,
+	 * depending on SHOW_PIDS bit.
+	 * We do not print status of individual processes
+	 * between PID and <cmdN>. bash does it, but not very well:
+	 * first line shows overall job status, not process status,
+	 * making it impossible to know 1st process status.
+	 */
+	goto start;
+	do {
+		/* for each process */
+		s[0] = '\0';
+		col = 33;
+		if (mode & SHOW_PIDS)
+			col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
+ start:
+		fprintf(out, "%s%*c%s%s",
+				s,
+				33 - col >= 0 ? 33 - col : 0, ' ',
+				ps == jp->ps ? "" : "| ",
+				ps->ps_cmd
+		);
+	} while (++ps != psend);
+	outcslow('\n', out);
+
+	jp->changed = 0;
+
+	if (jp->state == JOBDONE) {
+		TRACE(("showjob: freeing job %d\n", jobno(jp)));
+		freejob(jp);
+	}
+}
+
+/*
+ * Print a list of jobs.  If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ */
+static void
+showjobs(FILE *out, int mode)
+{
+	struct job *jp;
+
+	TRACE(("showjobs(0x%x) called\n", mode));
+
+	/* Handle all finished jobs */
+	while (dowait(DOWAIT_NONBLOCK, NULL) > 0)
+		continue;
+
+	for (jp = curjob; jp; jp = jp->prev_job) {
+		if (!(mode & SHOW_CHANGED) || jp->changed) {
+			showjob(out, jp, mode);
+		}
+	}
+}
+
+static int FAST_FUNC
+jobscmd(int argc UNUSED_PARAM, char **argv)
+{
+	int mode, m;
+
+	mode = 0;
+	while ((m = nextopt("lp")) != '\0') {
+		if (m == 'l')
+			mode |= SHOW_PIDS;
+		else
+			mode |= SHOW_ONLY_PGID;
+	}
+
+	argv = argptr;
+	if (*argv) {
+		do
+			showjob(stdout, getjob(*argv, 0), mode);
+		while (*++argv);
+	} else {
+		showjobs(stdout, mode);
+	}
+
+	return 0;
+}
+#endif /* JOBS */
+
+/* Called only on finished or stopped jobs (no members are running) */
+static int
+getstatus(struct job *job)
+{
+	int status;
+	int retval;
+	struct procstat *ps;
+
+	/* Fetch last member's status */
+	ps = job->ps + job->nprocs - 1;
+	status = ps->ps_status;
+	if (pipefail) {
+		/* "set -o pipefail" mode: use last _nonzero_ status */
+		while (status == 0 && --ps >= job->ps)
+			status = ps->ps_status;
+	}
+
+	retval = WEXITSTATUS(status);
+	if (!WIFEXITED(status)) {
+#if JOBS
+		retval = WSTOPSIG(status);
+		if (!WIFSTOPPED(status))
+#endif
+		{
+			/* XXX: limits number of signals */
+			retval = WTERMSIG(status);
+#if JOBS
+			if (retval == SIGINT)
+				job->sigint = 1;
+#endif
+		}
+		retval += 128;
+	}
+	TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
+		jobno(job), job->nprocs, status, retval));
+	return retval;
+}
+
+static int FAST_FUNC
+waitcmd(int argc UNUSED_PARAM, char **argv)
+{
+	struct job *job;
+	int retval;
+	struct job *jp;
+
+	if (pending_sig)
+		raise_exception(EXSIG);
+
+	nextopt(nullstr);
+	retval = 0;
+
+	argv = argptr;
+	if (!*argv) {
+		/* wait for all jobs */
+		for (;;) {
+			jp = curjob;
+			while (1) {
+				if (!jp) /* no running procs */
+					goto ret;
+				if (jp->state == JOBRUNNING)
+					break;
+				jp->waited = 1;
+				jp = jp->prev_job;
+			}
+			blocking_wait_with_raise_on_sig();
+	/* man bash:
+	 * "When bash is waiting for an asynchronous command via
+	 * the wait builtin, the reception of a signal for which a trap
+	 * has been set will cause the wait builtin to return immediately
+	 * with an exit status greater than 128, immediately after which
+	 * the trap is executed."
+	 *
+	 * blocking_wait_with_raise_on_sig raises signal handlers
+	 * if it gets no pid (pid < 0). However,
+	 * if child sends us a signal *and immediately exits*,
+	 * blocking_wait_with_raise_on_sig gets pid > 0
+	 * and does not handle pending_sig. Check this case: */
+			if (pending_sig)
+				raise_exception(EXSIG);
+		}
+	}
+
+	retval = 127;
+	do {
+		if (**argv != '%') {
+			pid_t pid = number(*argv);
+			job = curjob;
+			while (1) {
+				if (!job)
+					goto repeat;
+				if (job->ps[job->nprocs - 1].ps_pid == pid)
+					break;
+				job = job->prev_job;
+			}
+		} else {
+			job = getjob(*argv, 0);
+		}
+		/* loop until process terminated or stopped */
+		while (job->state == JOBRUNNING)
+			blocking_wait_with_raise_on_sig();
+		job->waited = 1;
+		retval = getstatus(job);
+ repeat: ;
+	} while (*++argv);
+
+ ret:
+	return retval;
+}
+
+static struct job *
+growjobtab(void)
+{
+	size_t len;
+	ptrdiff_t offset;
+	struct job *jp, *jq;
+
+	len = njobs * sizeof(*jp);
+	jq = jobtab;
+	jp = ckrealloc(jq, len + 4 * sizeof(*jp));
+
+	offset = (char *)jp - (char *)jq;
+	if (offset) {
+		/* Relocate pointers */
+		size_t l = len;
+
+		jq = (struct job *)((char *)jq + l);
+		while (l) {
+			l -= sizeof(*jp);
+			jq--;
+#define joff(p) ((struct job *)((char *)(p) + l))
+#define jmove(p) (p) = (void *)((char *)(p) + offset)
+			if (joff(jp)->ps == &jq->ps0)
+				jmove(joff(jp)->ps);
+			if (joff(jp)->prev_job)
+				jmove(joff(jp)->prev_job);
+		}
+		if (curjob)
+			jmove(curjob);
+#undef joff
+#undef jmove
+	}
+
+	njobs += 4;
+	jobtab = jp;
+	jp = (struct job *)((char *)jp + len);
+	jq = jp + 3;
+	do {
+		jq->used = 0;
+	} while (--jq >= jp);
+	return jp;
+}
+
+/*
+ * Return a new job structure.
+ * Called with interrupts off.
+ */
+static struct job *
+makejob(/*union node *node,*/ int nprocs)
+{
+	int i;
+	struct job *jp;
+
+	for (i = njobs, jp = jobtab; ; jp++) {
+		if (--i < 0) {
+			jp = growjobtab();
+			break;
+		}
+		if (jp->used == 0)
+			break;
+		if (jp->state != JOBDONE || !jp->waited)
+			continue;
+#if JOBS
+		if (doing_jobctl)
+			continue;
+#endif
+		freejob(jp);
+		break;
+	}
+	memset(jp, 0, sizeof(*jp));
+#if JOBS
+	/* jp->jobctl is a bitfield.
+	 * "jp->jobctl |= jobctl" likely to give awful code */
+	if (doing_jobctl)
+		jp->jobctl = 1;
+#endif
+	jp->prev_job = curjob;
+	curjob = jp;
+	jp->used = 1;
+	jp->ps = &jp->ps0;
+	if (nprocs > 1) {
+		jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
+	}
+	TRACE(("makejob(%d) returns %%%d\n", nprocs,
+				jobno(jp)));
+	return jp;
+}
+
+#if JOBS
+/*
+ * Return a string identifying a command (to be printed by the
+ * jobs command).
+ */
+static char *cmdnextc;
+
+static void
+cmdputs(const char *s)
+{
+	static const char vstype[VSTYPE + 1][3] = {
+		"", "}", "-", "+", "?", "=",
+		"%", "%%", "#", "##"
+		IF_ASH_BASH_COMPAT(, ":", "/", "//")
+	};
+
+	const char *p, *str;
+	char cc[2];
+	char *nextc;
+	unsigned char c;
+	unsigned char subtype = 0;
+	int quoted = 0;
+
+	cc[1] = '\0';
+	nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
+	p = s;
+	while ((c = *p++) != '\0') {
+		str = NULL;
+		switch (c) {
+		case CTLESC:
+			c = *p++;
+			break;
+		case CTLVAR:
+			subtype = *p++;
+			if ((subtype & VSTYPE) == VSLENGTH)
+				str = "${#";
+			else
+				str = "${";
+			if (!(subtype & VSQUOTE) == !(quoted & 1))
+				goto dostr;
+			quoted ^= 1;
+			c = '"';
+			break;
+		case CTLENDVAR:
+			str = "\"}" + !(quoted & 1);
+			quoted >>= 1;
+			subtype = 0;
+			goto dostr;
+		case CTLBACKQ:
+			str = "$(...)";
+			goto dostr;
+		case CTLBACKQ+CTLQUOTE:
+			str = "\"$(...)\"";
+			goto dostr;
+#if ENABLE_SH_MATH_SUPPORT
+		case CTLARI:
+			str = "$((";
+			goto dostr;
+		case CTLENDARI:
+			str = "))";
+			goto dostr;
+#endif
+		case CTLQUOTEMARK:
+			quoted ^= 1;
+			c = '"';
+			break;
+		case '=':
+			if (subtype == 0)
+				break;
+			if ((subtype & VSTYPE) != VSNORMAL)
+				quoted <<= 1;
+			str = vstype[subtype & VSTYPE];
+			if (subtype & VSNUL)
+				c = ':';
+			else
+				goto checkstr;
+			break;
+		case '\'':
+		case '\\':
+		case '"':
+		case '$':
+			/* These can only happen inside quotes */
+			cc[0] = c;
+			str = cc;
+			c = '\\';
+			break;
+		default:
+			break;
+		}
+		USTPUTC(c, nextc);
+ checkstr:
+		if (!str)
+			continue;
+ dostr:
+		while ((c = *str++) != '\0') {
+			USTPUTC(c, nextc);
+		}
+	} /* while *p++ not NUL */
+
+	if (quoted & 1) {
+		USTPUTC('"', nextc);
+	}
+	*nextc = 0;
+	cmdnextc = nextc;
+}
+
+/* cmdtxt() and cmdlist() call each other */
+static void cmdtxt(union node *n);
+
+static void
+cmdlist(union node *np, int sep)
+{
+	for (; np; np = np->narg.next) {
+		if (!sep)
+			cmdputs(" ");
+		cmdtxt(np);
+		if (sep && np->narg.next)
+			cmdputs(" ");
+	}
+}
+
+static void
+cmdtxt(union node *n)
+{
+	union node *np;
+	struct nodelist *lp;
+	const char *p;
+
+	if (!n)
+		return;
+	switch (n->type) {
+	default:
+#if DEBUG
+		abort();
+#endif
+	case NPIPE:
+		lp = n->npipe.cmdlist;
+		for (;;) {
+			cmdtxt(lp->n);
+			lp = lp->next;
+			if (!lp)
+				break;
+			cmdputs(" | ");
+		}
+		break;
+	case NSEMI:
+		p = "; ";
+		goto binop;
+	case NAND:
+		p = " && ";
+		goto binop;
+	case NOR:
+		p = " || ";
+ binop:
+		cmdtxt(n->nbinary.ch1);
+		cmdputs(p);
+		n = n->nbinary.ch2;
+		goto donode;
+	case NREDIR:
+	case NBACKGND:
+		n = n->nredir.n;
+		goto donode;
+	case NNOT:
+		cmdputs("!");
+		n = n->nnot.com;
+ donode:
+		cmdtxt(n);
+		break;
+	case NIF:
+		cmdputs("if ");
+		cmdtxt(n->nif.test);
+		cmdputs("; then ");
+		if (n->nif.elsepart) {
+			cmdtxt(n->nif.ifpart);
+			cmdputs("; else ");
+			n = n->nif.elsepart;
+		} else {
+			n = n->nif.ifpart;
+		}
+		p = "; fi";
+		goto dotail;
+	case NSUBSHELL:
+		cmdputs("(");
+		n = n->nredir.n;
+		p = ")";
+		goto dotail;
+	case NWHILE:
+		p = "while ";
+		goto until;
+	case NUNTIL:
+		p = "until ";
+ until:
+		cmdputs(p);
+		cmdtxt(n->nbinary.ch1);
+		n = n->nbinary.ch2;
+		p = "; done";
+ dodo:
+		cmdputs("; do ");
+ dotail:
+		cmdtxt(n);
+		goto dotail2;
+	case NFOR:
+		cmdputs("for ");
+		cmdputs(n->nfor.var);
+		cmdputs(" in ");
+		cmdlist(n->nfor.args, 1);
+		n = n->nfor.body;
+		p = "; done";
+		goto dodo;
+	case NDEFUN:
+		cmdputs(n->narg.text);
+		p = "() { ... }";
+		goto dotail2;
+	case NCMD:
+		cmdlist(n->ncmd.args, 1);
+		cmdlist(n->ncmd.redirect, 0);
+		break;
+	case NARG:
+		p = n->narg.text;
+ dotail2:
+		cmdputs(p);
+		break;
+	case NHERE:
+	case NXHERE:
+		p = "<<...";
+		goto dotail2;
+	case NCASE:
+		cmdputs("case ");
+		cmdputs(n->ncase.expr->narg.text);
+		cmdputs(" in ");
+		for (np = n->ncase.cases; np; np = np->nclist.next) {
+			cmdtxt(np->nclist.pattern);
+			cmdputs(") ");
+			cmdtxt(np->nclist.body);
+			cmdputs(";; ");
+		}
+		p = "esac";
+		goto dotail2;
+	case NTO:
+		p = ">";
+		goto redir;
+	case NCLOBBER:
+		p = ">|";
+		goto redir;
+	case NAPPEND:
+		p = ">>";
+		goto redir;
+#if ENABLE_ASH_BASH_COMPAT
+	case NTO2:
+#endif
+	case NTOFD:
+		p = ">&";
+		goto redir;
+	case NFROM:
+		p = "<";
+		goto redir;
+	case NFROMFD:
+		p = "<&";
+		goto redir;
+	case NFROMTO:
+		p = "<>";
+ redir:
+		cmdputs(utoa(n->nfile.fd));
+		cmdputs(p);
+		if (n->type == NTOFD || n->type == NFROMFD) {
+			cmdputs(utoa(n->ndup.dupfd));
+			break;
+		}
+		n = n->nfile.fname;
+		goto donode;
+	}
+}
+
+static char *
+commandtext(union node *n)
+{
+	char *name;
+
+	STARTSTACKSTR(cmdnextc);
+	cmdtxt(n);
+	name = stackblock();
+	TRACE(("commandtext: name %p, end %p\n\t\"%s\"\n",
+			name, cmdnextc, cmdnextc));
+	return ckstrdup(name);
+}
+#endif /* JOBS */
+
+/*
+ * Fork off a subshell.  If we are doing job control, give the subshell its
+ * own process group.  Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child.  Both jp and n may
+ * be NULL.  The mode parameter can be one of the following:
+ *      FORK_FG - Fork off a foreground process.
+ *      FORK_BG - Fork off a background process.
+ *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *                   process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ *
+ * Called with interrupts off.
+ */
+/*
+ * Clear traps on a fork.
+ */
+static void
+clear_traps(void)
+{
+	char **tp;
+
+	for (tp = trap; tp < &trap[NSIG]; tp++) {
+		if (*tp && **tp) {      /* trap not NULL or "" (SIG_IGN) */
+			INT_OFF;
+			if (trap_ptr == trap)
+				free(*tp);
+			/* else: it "belongs" to trap_ptr vector, don't free */
+			*tp = NULL;
+			if ((tp - trap) != 0)
+				setsignal(tp - trap);
+			INT_ON;
+		}
+	}
+	may_have_traps = 0;
+}
+
+/* Lives far away from here, needed for forkchild */
+static void closescript(void);
+
+/* Called after fork(), in child */
+static NOINLINE void
+forkchild(struct job *jp, union node *n, int mode)
+{
+	int oldlvl;
+
+	TRACE(("Child shell %d\n", getpid()));
+	oldlvl = shlvl;
+	shlvl++;
+
+	/* man bash: "Non-builtin commands run by bash have signal handlers
+	 * set to the values inherited by the shell from its parent".
+	 * Do we do it correctly? */
+
+	closescript();
+
+	if (mode == FORK_NOJOB          /* is it `xxx` ? */
+	 && n && n->type == NCMD        /* is it single cmd? */
+	/* && n->ncmd.args->type == NARG - always true? */
+	 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
+	 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
+	/* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
+	) {
+		TRACE(("Trap hack\n"));
+		/* 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.
+		 */
+		/* Save trap handler strings for trap builtin to print */
+		trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap));
+		/* Fall through into clearing traps */
+	}
+	clear_traps();
+#if JOBS
+	/* do job control only in root shell */
+	doing_jobctl = 0;
+	if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
+		pid_t pgrp;
+
+		if (jp->nprocs == 0)
+			pgrp = getpid();
+		else
+			pgrp = jp->ps[0].ps_pid;
+		/* this can fail because we are doing it in the parent also */
+		setpgid(0, pgrp);
+		if (mode == FORK_FG)
+			xtcsetpgrp(ttyfd, pgrp);
+		setsignal(SIGTSTP);
+		setsignal(SIGTTOU);
+	} else
+#endif
+	if (mode == FORK_BG) {
+		/* man bash: "When job control is not in effect,
+		 * asynchronous commands ignore SIGINT and SIGQUIT" */
+		ignoresig(SIGINT);
+		ignoresig(SIGQUIT);
+		if (jp->nprocs == 0) {
+			close(0);
+			if (open(bb_dev_null, O_RDONLY) != 0)
+				ash_msg_and_raise_error("can't open '%s'", bb_dev_null);
+		}
+	}
+	if (oldlvl == 0) {
+		if (iflag) { /* why if iflag only? */
+			setsignal(SIGINT);
+			setsignal(SIGTERM);
+		}
+		/* man bash:
+		 * "In all cases, bash ignores SIGQUIT. Non-builtin
+		 * commands run by bash have signal handlers
+		 * set to the values inherited by the shell
+		 * from its parent".
+		 * Take care of the second rule: */
+		setsignal(SIGQUIT);
+	}
+#if JOBS
+	if (n && n->type == NCMD
+	 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
+	) {
+		TRACE(("Job hack\n"));
+		/* "jobs": we do not want to clear job list for it,
+		 * instead we remove only _its_ own_ job from job list.
+		 * This makes "jobs .... | cat" more useful.
+		 */
+		freejob(curjob);
+		return;
+	}
+#endif
+	for (jp = curjob; jp; jp = jp->prev_job)
+		freejob(jp);
+	jobless = 0;
+}
+
+/* Called after fork(), in parent */
+#if !JOBS
+#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
+#endif
+static void
+forkparent(struct job *jp, union node *n, int mode, pid_t pid)
+{
+	TRACE(("In parent shell: child = %d\n", pid));
+	if (!jp) {
+		while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
+			continue;
+		jobless++;
+		return;
+	}
+#if JOBS
+	if (mode != FORK_NOJOB && jp->jobctl) {
+		int pgrp;
+
+		if (jp->nprocs == 0)
+			pgrp = pid;
+		else
+			pgrp = jp->ps[0].ps_pid;
+		/* This can fail because we are doing it in the child also */
+		setpgid(pid, pgrp);
+	}
+#endif
+	if (mode == FORK_BG) {
+		backgndpid = pid;               /* set $! */
+		set_curjob(jp, CUR_RUNNING);
+	}
+	if (jp) {
+		struct procstat *ps = &jp->ps[jp->nprocs++];
+		ps->ps_pid = pid;
+		ps->ps_status = -1;
+		ps->ps_cmd = nullstr;
+#if JOBS
+		if (doing_jobctl && n)
+			ps->ps_cmd = commandtext(n);
+#endif
+	}
+}
+
+static int
+forkshell(struct job *jp, union node *n, int mode)
+{
+	int pid;
+
+	TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
+	pid = fork();
+	if (pid < 0) {
+		TRACE(("Fork failed, errno=%d", errno));
+		if (jp)
+			freejob(jp);
+		ash_msg_and_raise_error("can't fork");
+	}
+	if (pid == 0) {
+		CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
+		forkchild(jp, n, mode);
+	} else {
+		forkparent(jp, n, mode, pid);
+	}
+	return pid;
+}
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process
+ * is running interrupts generated by the user are sent to the child
+ * but not to the shell.  This means that an infinite loop started by
+ * an interactive user may be hard to kill.  With job control turned off,
+ * an interactive user may place an interactive program inside a loop.
+ * If the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop.  The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * foreground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ *
+ * Called with interrupts off.
+ */
+static int
+waitforjob(struct job *jp)
+{
+	int st;
+
+	TRACE(("waitforjob(%%%d) called\n", jobno(jp)));
+
+	INT_OFF;
+	while (jp->state == JOBRUNNING) {
+		/* In non-interactive shells, we _can_ get
+		 * a keyboard signal here and be EINTRed,
+		 * but we just loop back, waiting for command to complete.
+		 *
+		 * man bash:
+		 * "If bash is waiting for a command to complete and receives
+		 * a signal for which a trap has been set, the trap
+		 * will not be executed until the command completes."
+		 *
+		 * Reality is that even if trap is not set, bash
+		 * will not act on the signal until command completes.
+		 * Try this. sleep5intoff.c:
+		 * #include <signal.h>
+		 * #include <unistd.h>
+		 * int main() {
+		 *         sigset_t set;
+		 *         sigemptyset(&set);
+		 *         sigaddset(&set, SIGINT);
+		 *         sigaddset(&set, SIGQUIT);
+		 *         sigprocmask(SIG_BLOCK, &set, NULL);
+		 *         sleep(5);
+		 *         return 0;
+		 * }
+		 * $ bash -c './sleep5intoff; echo hi'
+		 * ^C^C^C^C <--- pressing ^C once a second
+		 * $ _
+		 * $ bash -c './sleep5intoff; echo hi'
+		 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
+		 * $ _
+		 */
+		dowait(DOWAIT_BLOCK, jp);
+	}
+	INT_ON;
+
+	st = getstatus(jp);
+#if JOBS
+	if (jp->jobctl) {
+		xtcsetpgrp(ttyfd, rootpid);
+		/*
+		 * This is truly gross.
+		 * If we're doing job control, then we did a TIOCSPGRP which
+		 * caused us (the shell) to no longer be in the controlling
+		 * session -- so we wouldn't have seen any ^C/SIGINT.  So, we
+		 * intuit from the subprocess exit status whether a SIGINT
+		 * occurred, and if so interrupt ourselves.  Yuck.  - mycroft
+		 */
+		if (jp->sigint) /* TODO: do the same with all signals */
+			raise(SIGINT); /* ... by raise(jp->sig) instead? */
+	}
+	if (jp->state == JOBDONE)
+#endif
+		freejob(jp);
+	return st;
+}
+
+/*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+static int
+stoppedjobs(void)
+{
+	struct job *jp;
+	int retval;
+
+	retval = 0;
+	if (job_warning)
+		goto out;
+	jp = curjob;
+	if (jp && jp->state == JOBSTOPPED) {
+		out2str("You have stopped jobs.\n");
+		job_warning = 2;
+		retval++;
+	}
+ out:
+	return retval;
+}
+
+
+/* ============ redir.c
+ *
+ * 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 */
+
+/*
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
+ */
+static int
+noclobberopen(const char *fname)
+{
+	int r, fd;
+	struct stat finfo, finfo2;
+
+	/*
+	 * If the file exists and is a regular file, return an error
+	 * immediately.
+	 */
+	r = stat(fname, &finfo);
+	if (r == 0 && S_ISREG(finfo.st_mode)) {
+		errno = EEXIST;
+		return -1;
+	}
+
+	/*
+	 * If the file was not present (r != 0), make sure we open it
+	 * exclusively so that if it is created before we open it, our open
+	 * will fail.  Make sure that we do not truncate an existing file.
+	 * Note that we don't turn on O_EXCL unless the stat failed -- if the
+	 * file was not a regular file, we leave O_EXCL off.
+	 */
+	if (r != 0)
+		return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
+	fd = open(fname, O_WRONLY|O_CREAT, 0666);
+
+	/* If the open failed, return the file descriptor right away. */
+	if (fd < 0)
+		return fd;
+
+	/*
+	 * OK, the open succeeded, but the file may have been changed from a
+	 * non-regular file to a regular file between the stat and the open.
+	 * We are assuming that the O_EXCL open handles the case where FILENAME
+	 * did not exist and is symlinked to an existing file between the stat
+	 * and open.
+	 */
+
+	/*
+	 * If we can open it and fstat the file descriptor, and neither check
+	 * revealed that it was a regular file, and the file has not been
+	 * replaced, return the file descriptor.
+	 */
+	if (fstat(fd, &finfo2) == 0
+	 && !S_ISREG(finfo2.st_mode)
+	 && finfo.st_dev == finfo2.st_dev
+	 && finfo.st_ino == finfo2.st_ino
+	) {
+		return fd;
+	}
+
+	/* The file has been replaced.  badness. */
+	close(fd);
+	errno = EEXIST;
+	return -1;
+}
+
+/*
+ * Handle here documents.  Normally we fork off a process to write the
+ * data to a pipe.  If the document is short, we can stuff the data in
+ * the pipe without forking.
+ */
+/* openhere needs this forward reference */
+static void expandhere(union node *arg, int fd);
+static int
+openhere(union node *redir)
+{
+	int pip[2];
+	size_t len = 0;
+
+	if (pipe(pip) < 0)
+		ash_msg_and_raise_error("pipe call failed");
+	if (redir->type == NHERE) {
+		len = strlen(redir->nhere.doc->narg.text);
+		if (len <= PIPE_BUF) {
+			full_write(pip[1], redir->nhere.doc->narg.text, len);
+			goto out;
+		}
+	}
+	if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
+		/* child */
+		close(pip[0]);
+		ignoresig(SIGINT);  //signal(SIGINT, SIG_IGN);
+		ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
+		ignoresig(SIGHUP);  //signal(SIGHUP, SIG_IGN);
+		ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
+		signal(SIGPIPE, SIG_DFL);
+		if (redir->type == NHERE)
+			full_write(pip[1], redir->nhere.doc->narg.text, len);
+		else /* NXHERE */
+			expandhere(redir->nhere.doc, pip[1]);
+		_exit(EXIT_SUCCESS);
+	}
+ out:
+	close(pip[1]);
+	return pip[0];
+}
+
+static int
+openredirect(union node *redir)
+{
+	char *fname;
+	int f;
+
+	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;
+		break;
+	case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+	case NTO2:
+#endif
+		/* Take care of noclobber mode. */
+		if (Cflag) {
+			fname = redir->nfile.expfname;
+			f = noclobberopen(fname);
+			if (f < 0)
+				goto ecreate;
+			break;
+		}
+		/* 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;
+		break;
+	default:
+#if DEBUG
+		abort();
+#endif
+		/* Fall through to eliminate warning. */
+/* Our single caller does this itself */
+//	case NTOFD:
+//	case NFROMFD:
+//		f = -1;
+//		break;
+	case NHERE:
+	case NXHERE:
+		f = openhere(redir);
+		break;
+	}
+
+	return f;
+ ecreate:
+	ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
+ eopen:
+	ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
+}
+
+/*
+ * Copy a file descriptor to be >= to.  Returns -1
+ * if the source file descriptor is closed, EMPTY if there are no unused
+ * file descriptors left.
+ */
+/* 0x800..00: bit to set in "to" to request dup2 instead of fcntl(F_DUPFD).
+ * old code was doing close(to) prior to copyfd() to achieve the same */
+enum {
+	COPYFD_EXACT   = (int)~(INT_MAX),
+	COPYFD_RESTORE = (int)((unsigned)COPYFD_EXACT >> 1),
+};
+static int
+copyfd(int from, int to)
+{
+	int newfd;
+
+	if (to & COPYFD_EXACT) {
+		to &= ~COPYFD_EXACT;
+		/*if (from != to)*/
+			newfd = dup2(from, to);
+	} else {
+		newfd = fcntl(from, F_DUPFD, to);
+	}
+	if (newfd < 0) {
+		if (errno == EMFILE)
+			return EMPTY;
+		/* Happens when source fd is not open: try "echo >&99" */
+		ash_msg_and_raise_error("%d: %m", from);
+	}
+	return newfd;
+}
+
+/* Struct def and variable are moved down to the first usage site */
+struct two_fd_t {
+	int orig, copy;
+};
+struct redirtab {
+	struct redirtab *next;
+	int nullredirs;
+	int pair_count;
+	struct two_fd_t two_fd[];
+};
+#define redirlist (G_var.redirlist)
+
+static int need_to_remember(struct redirtab *rp, int fd)
+{
+	int i;
+
+	if (!rp) /* remembering was not requested */
+		return 0;
+
+	for (i = 0; i < rp->pair_count; i++) {
+		if (rp->two_fd[i].orig == fd) {
+			/* already remembered */
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* "hidden" fd is a fd used to read scripts, or a copy of such */
+static int is_hidden_fd(struct redirtab *rp, int fd)
+{
+	int i;
+	struct parsefile *pf;
+
+	if (fd == -1)
+		return 0;
+	/* Check open scripts' fds */
+	pf = g_parsefile;
+	while (pf) {
+		/* We skip pf_fd == 0 case because of the following case:
+		 * $ ash  # running ash interactively
+		 * $ . ./script.sh
+		 * and in script.sh: "exec 9>&0".
+		 * Even though top-level pf_fd _is_ 0,
+		 * it's still ok to use it: "read" builtin uses it,
+		 * why should we cripple "exec" builtin?
+		 */
+		if (pf->pf_fd > 0 && fd == pf->pf_fd) {
+			return 1;
+		}
+		pf = pf->prev;
+	}
+
+	if (!rp)
+		return 0;
+	/* Check saved fds of redirects */
+	fd |= COPYFD_RESTORE;
+	for (i = 0; i < rp->pair_count; i++) {
+		if (rp->two_fd[i].copy == fd) {
+			return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Process a list of redirection commands.  If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir.
+ */
+/* flags passed to redirect */
+#define REDIR_PUSH    01        /* save previous values of file descriptors */
+#define REDIR_SAVEFD2 03        /* set preverrout */
+static void
+redirect(union node *redir, int flags)
+{
+	struct redirtab *sv;
+	int sv_pos;
+	int i;
+	int fd;
+	int newfd;
+	int copied_fd2 = -1;
+
+	g_nullredirs++;
+	if (!redir) {
+		return;
+	}
+
+	sv = NULL;
+	sv_pos = 0;
+	INT_OFF;
+	if (flags & REDIR_PUSH) {
+		union node *tmp = redir;
+		do {
+			sv_pos++;
+#if ENABLE_ASH_BASH_COMPAT
+			if (tmp->nfile.type == NTO2)
+				sv_pos++;
+#endif
+			tmp = tmp->nfile.next;
+		} while (tmp);
+		sv = ckmalloc(sizeof(*sv) + sv_pos * sizeof(sv->two_fd[0]));
+		sv->next = redirlist;
+		sv->pair_count = sv_pos;
+		redirlist = sv;
+		sv->nullredirs = g_nullredirs - 1;
+		g_nullredirs = 0;
+		while (sv_pos > 0) {
+			sv_pos--;
+			sv->two_fd[sv_pos].orig = sv->two_fd[sv_pos].copy = EMPTY;
+		}
+	}
+
+	do {
+		int right_fd = -1;
+		fd = redir->nfile.fd;
+		if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+			right_fd = redir->ndup.dupfd;
+			//bb_error_msg("doing %d > %d", fd, right_fd);
+			/* redirect from/to same file descriptor? */
+			if (right_fd == fd)
+				continue;
+			/* "echo >&10" and 10 is a fd opened to a sh script? */
+			if (is_hidden_fd(sv, right_fd)) {
+				errno = EBADF; /* as if it is closed */
+				ash_msg_and_raise_error("%d: %m", right_fd);
+			}
+			newfd = -1;
+		} else {
+			newfd = openredirect(redir); /* always >= 0 */
+			if (fd == newfd) {
+				/* Descriptor wasn't open before redirect.
+				 * Mark it for close in the future */
+				if (need_to_remember(sv, fd)) {
+					goto remember_to_close;
+				}
+				continue;
+			}
+		}
+#if ENABLE_ASH_BASH_COMPAT
+ redirect_more:
+#endif
+		if (need_to_remember(sv, fd)) {
+			/* Copy old descriptor */
+			/* Careful to not accidentally "save"
+			 * to the same fd as right side fd in N>&M */
+			int minfd = right_fd < 10 ? 10 : right_fd + 1;
+			i = fcntl(fd, F_DUPFD, minfd);
+/* You'd expect copy to be CLOEXECed. Currently these extra "saved" fds
+ * are closed in popredir() in the child, preventing them from leaking
+ * into child. (popredir() also cleans up the mess in case of failures)
+ */
+			if (i == -1) {
+				i = errno;
+				if (i != EBADF) {
+					/* Strange error (e.g. "too many files" EMFILE?) */
+					if (newfd >= 0)
+						close(newfd);
+					errno = i;
+					ash_msg_and_raise_error("%d: %m", fd);
+					/* NOTREACHED */
+				}
+				/* EBADF: it is not open - good, remember to close it */
+ remember_to_close:
+				i = CLOSED;
+			} else { /* fd is open, save its copy */
+				/* "exec fd>&-" should not close fds
+				 * which point to script file(s).
+				 * Force them to be restored afterwards */
+				if (is_hidden_fd(sv, fd))
+					i |= COPYFD_RESTORE;
+			}
+			if (fd == 2)
+				copied_fd2 = i;
+			sv->two_fd[sv_pos].orig = fd;
+			sv->two_fd[sv_pos].copy = i;
+			sv_pos++;
+		}
+		if (newfd < 0) {
+			/* NTOFD/NFROMFD: copy redir->ndup.dupfd to fd */
+			if (redir->ndup.dupfd < 0) { /* "fd>&-" */
+				/* Don't want to trigger debugging */
+				if (fd != -1)
+					close(fd);
+			} else {
+				copyfd(redir->ndup.dupfd, fd | COPYFD_EXACT);
+			}
+		} else if (fd != newfd) { /* move newfd to fd */
+			copyfd(newfd, fd | COPYFD_EXACT);
+#if ENABLE_ASH_BASH_COMPAT
+			if (!(redir->nfile.type == NTO2 && fd == 2))
+#endif
+				close(newfd);
+		}
+#if ENABLE_ASH_BASH_COMPAT
+		if (redir->nfile.type == NTO2 && fd == 1) {
+			/* We already redirected it to fd 1, now copy it to 2 */
+			newfd = 1;
+			fd = 2;
+			goto redirect_more;
+		}
+#endif
+	} while ((redir = redir->nfile.next) != NULL);
+
+	INT_ON;
+	if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
+		preverrout_fd = copied_fd2;
+}
+
+/*
+ * Undo the effects of the last redirection.
+ */
+static void
+popredir(int drop, int restore)
+{
+	struct redirtab *rp;
+	int i;
+
+	if (--g_nullredirs >= 0)
+		return;
+	INT_OFF;
+	rp = redirlist;
+	for (i = 0; i < rp->pair_count; i++) {
+		int fd = rp->two_fd[i].orig;
+		int copy = rp->two_fd[i].copy;
+		if (copy == CLOSED) {
+			if (!drop)
+				close(fd);
+			continue;
+		}
+		if (copy != EMPTY) {
+			if (!drop || (restore && (copy & COPYFD_RESTORE))) {
+				copy &= ~COPYFD_RESTORE;
+				/*close(fd);*/
+				copyfd(copy, fd | COPYFD_EXACT);
+			}
+			close(copy & ~COPYFD_RESTORE);
+		}
+	}
+	redirlist = rp->next;
+	g_nullredirs = rp->nullredirs;
+	free(rp);
+	INT_ON;
+}
+
+/*
+ * Undo all redirections.  Called on error or interrupt.
+ */
+
+/*
+ * Discard all saved file descriptors.
+ */
+static void
+clearredir(int drop)
+{
+	for (;;) {
+		g_nullredirs = 0;
+		if (!redirlist)
+			break;
+		popredir(drop, /*restore:*/ 0);
+	}
+}
+
+static int
+redirectsafe(union node *redir, int flags)
+{
+	int err;
+	volatile int saveint;
+	struct jmploc *volatile savehandler = exception_handler;
+	struct jmploc jmploc;
+
+	SAVE_INT(saveint);
+	/* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
+	err = setjmp(jmploc.loc); // huh?? was = setjmp(jmploc.loc) * 2;
+	if (!err) {
+		exception_handler = &jmploc;
+		redirect(redir, flags);
+	}
+	exception_handler = savehandler;
+	if (err && exception_type != EXERROR)
+		longjmp(exception_handler->loc, 1);
+	RESTORE_INT(saveint);
+	return err;
+}
+
+
+/* ============ Routines to expand arguments to commands
+ *
+ * We have to deal with backquotes, shell variables, and file metacharacters.
+ */
+
+#if ENABLE_SH_MATH_SUPPORT
+static arith_t
+ash_arith(const char *s)
+{
+	arith_state_t math_state;
+	arith_t result;
+
+	math_state.lookupvar = lookupvar;
+	math_state.setvar    = setvar2;
+	//math_state.endofname = endofname;
+
+	INT_OFF;
+	result = arith(&math_state, s);
+	if (math_state.errmsg)
+		ash_msg_and_raise_error(math_state.errmsg);
+	INT_ON;
+
+	return result;
+}
+#endif
+
+/*
+ * expandarg flags
+ */
+#define EXP_FULL        0x1     /* perform word splitting & file globbing */
+#define EXP_TILDE       0x2     /* do normal tilde expansion */
+#define EXP_VARTILDE    0x4     /* expand tildes in an assignment */
+#define EXP_REDIR       0x8     /* file glob for a redirection (1 match only) */
+#define EXP_CASE        0x10    /* keeps quotes around for CASE pattern */
+#define EXP_RECORD      0x20    /* need to record arguments for ifs breakup */
+#define EXP_VARTILDE2   0x40    /* expand tildes after colons only */
+#define EXP_WORD        0x80    /* expand word in parameter expansion */
+#define EXP_QWORD       0x100   /* expand word in quoted parameter expansion */
+/*
+ * rmescape() flags
+ */
+#define RMESCAPE_ALLOC  0x1     /* Allocate a new string */
+#define RMESCAPE_GLOB   0x2     /* Add backslashes for glob */
+#define RMESCAPE_QUOTED 0x4     /* Remove CTLESC unless in quotes */
+#define RMESCAPE_GROW   0x8     /* Grow strings instead of stalloc */
+#define RMESCAPE_HEAP   0x10    /* Malloc strings instead of stalloc */
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+struct ifsregion {
+	struct ifsregion *next; /* next region in list */
+	int begoff;             /* offset of start of region */
+	int endoff;             /* offset of end of region */
+	int nulonly;            /* search for nul bytes only */
+};
+
+struct arglist {
+	struct strlist *list;
+	struct strlist **lastp;
+};
+
+/* output of current string */
+static char *expdest;
+/* list of back quote expressions */
+static struct nodelist *argbackq;
+/* first struct in list of ifs regions */
+static struct ifsregion ifsfirst;
+/* last struct in list */
+static struct ifsregion *ifslastp;
+/* holds expanded arg list */
+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_FMT, num);
+	STADJUST(len, expdest);
+	return len;
+}
+
+static size_t
+esclen(const char *start, const char *p)
+{
+	size_t esc = 0;
+
+	while (p > start && (unsigned char)*--p == CTLESC) {
+		esc++;
+	}
+	return esc;
+}
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+static char *
+rmescapes(char *str, int flag)
+{
+	static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' };
+
+	char *p, *q, *r;
+	unsigned inquotes;
+	unsigned protect_against_glob;
+	unsigned globbing;
+
+	p = strpbrk(str, qchars);
+	if (!p)
+		return str;
+
+	q = p;
+	r = str;
+	if (flag & RMESCAPE_ALLOC) {
+		size_t len = p - str;
+		size_t fulllen = len + strlen(p) + 1;
+
+		if (flag & RMESCAPE_GROW) {
+			int strloc = str - (char *)stackblock();
+			r = makestrspace(fulllen, expdest);
+			/* p and str may be invalidated by makestrspace */
+			str = (char *)stackblock() + strloc;
+			p = str + len;
+		} else if (flag & RMESCAPE_HEAP) {
+			r = ckmalloc(fulllen);
+		} else {
+			r = stalloc(fulllen);
+		}
+		q = r;
+		if (len > 0) {
+			q = (char *)memcpy(q, str, len) + len;
+		}
+	}
+
+	inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED;
+	globbing = flag & RMESCAPE_GLOB;
+	protect_against_glob = globbing;
+	while (*p) {
+		if ((unsigned char)*p == CTLQUOTEMARK) {
+// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0
+// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok?
+// Note: both inquotes and protect_against_glob only affect whether
+// CTLESC,<ch> gets converted to <ch> or to \<ch>
+			inquotes = ~inquotes;
+			p++;
+			protect_against_glob = globbing;
+			continue;
+		}
+		if (*p == '\\') {
+			/* naked back slash */
+			protect_against_glob = 0;
+			goto copy;
+		}
+		if ((unsigned char)*p == CTLESC) {
+			p++;
+			if (protect_against_glob && inquotes && *p != '/') {
+				*q++ = '\\';
+			}
+		}
+		protect_against_glob = globbing;
+ copy:
+		*q++ = *p++;
+	}
+	*q = '\0';
+	if (flag & RMESCAPE_GROW) {
+		expdest = r;
+		STADJUST(q - r + 1, expdest);
+	}
+	return r;
+}
+#define pmatch(a, b) !fnmatch((a), (b), 0)
+
+/*
+ * Prepare a pattern for a expmeta (internal glob(3)) call.
+ *
+ * Returns an stalloced string.
+ */
+static char *
+preglob(const char *pattern, int quoted, int flag)
+{
+	flag |= RMESCAPE_GLOB;
+	if (quoted) {
+		flag |= RMESCAPE_QUOTED;
+	}
+	return rmescapes((char *)pattern, flag);
+}
+
+/*
+ * Put a string on the stack.
+ */
+static void
+memtodest(const char *p, size_t len, int syntax, int quotes)
+{
+	char *q = expdest;
+
+	q = makestrspace(quotes ? len * 2 : len, q);
+
+	while (len--) {
+		unsigned char c = *p++;
+		if (c == '\0')
+			continue;
+		if (quotes) {
+			int n = SIT(c, syntax);
+			if (n == CCTL || n == CBACK)
+				USTPUTC(CTLESC, q);
+		}
+		USTPUTC(c, q);
+	}
+
+	expdest = q;
+}
+
+static void
+strtodest(const char *p, int syntax, int quotes)
+{
+	memtodest(p, strlen(p), syntax, quotes);
+}
+
+/*
+ * Record the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+static void
+recordregion(int start, int end, int nulonly)
+{
+	struct ifsregion *ifsp;
+
+	if (ifslastp == NULL) {
+		ifsp = &ifsfirst;
+	} else {
+		INT_OFF;
+		ifsp = ckzalloc(sizeof(*ifsp));
+		/*ifsp->next = NULL; - ckzalloc did it */
+		ifslastp->next = ifsp;
+		INT_ON;
+	}
+	ifslastp = ifsp;
+	ifslastp->begoff = start;
+	ifslastp->endoff = end;
+	ifslastp->nulonly = nulonly;
+}
+
+static void
+removerecordregions(int endoff)
+{
+	if (ifslastp == NULL)
+		return;
+
+	if (ifsfirst.endoff > endoff) {
+		while (ifsfirst.next) {
+			struct ifsregion *ifsp;
+			INT_OFF;
+			ifsp = ifsfirst.next->next;
+			free(ifsfirst.next);
+			ifsfirst.next = ifsp;
+			INT_ON;
+		}
+		if (ifsfirst.begoff > endoff) {
+			ifslastp = NULL;
+		} else {
+			ifslastp = &ifsfirst;
+			ifsfirst.endoff = endoff;
+		}
+		return;
+	}
+
+	ifslastp = &ifsfirst;
+	while (ifslastp->next && ifslastp->next->begoff < endoff)
+		ifslastp = ifslastp->next;
+	while (ifslastp->next) {
+		struct ifsregion *ifsp;
+		INT_OFF;
+		ifsp = ifslastp->next->next;
+		free(ifslastp->next);
+		ifslastp->next = ifsp;
+		INT_ON;
+	}
+	if (ifslastp->endoff > endoff)
+		ifslastp->endoff = endoff;
+}
+
+static char *
+exptilde(char *startp, char *p, int flags)
+{
+	unsigned char c;
+	char *name;
+	struct passwd *pw;
+	const char *home;
+	int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
+	int startloc;
+
+	name = p + 1;
+
+	while ((c = *++p) != '\0') {
+		switch (c) {
+		case CTLESC:
+			return startp;
+		case CTLQUOTEMARK:
+			return startp;
+		case ':':
+			if (flags & EXP_VARTILDE)
+				goto done;
+			break;
+		case '/':
+		case CTLENDVAR:
+			goto done;
+		}
+	}
+ done:
+	*p = '\0';
+	if (*name == '\0') {
+		home = lookupvar("HOME");
+	} else {
+		pw = getpwnam(name);
+		if (pw == NULL)
+			goto lose;
+		home = pw->pw_dir;
+	}
+	if (!home || !*home)
+		goto lose;
+	*p = c;
+	startloc = expdest - (char *)stackblock();
+	strtodest(home, SQSYNTAX, quotes);
+	recordregion(startloc, expdest - (char *)stackblock(), 0);
+	return p;
+ lose:
+	*p = c;
+	return startp;
+}
+
+/*
+ * Execute a command inside back quotes.  If it's a builtin command, we
+ * want to save its output in a block obtained from malloc.  Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+struct backcmd {                /* result of evalbackcmd */
+	int fd;                 /* file descriptor to read from */
+	int nleft;              /* number of chars in buffer */
+	char *buf;              /* buffer */
+	struct job *jp;         /* job structure for command */
+};
+
+/* These forward decls are needed to use "eval" code for backticks handling: */
+static uint8_t back_exitstatus; /* exit status of backquoted command */
+#define EV_EXIT 01              /* exit after evaluating tree */
+static void evaltree(union node *, int);
+
+static void FAST_FUNC
+evalbackcmd(union node *n, struct backcmd *result)
+{
+	int saveherefd;
+
+	result->fd = -1;
+	result->buf = NULL;
+	result->nleft = 0;
+	result->jp = NULL;
+	if (n == NULL)
+		goto out;
+
+	saveherefd = herefd;
+	herefd = -1;
+
+	{
+		int pip[2];
+		struct job *jp;
+
+		if (pipe(pip) < 0)
+			ash_msg_and_raise_error("pipe call failed");
+		jp = makejob(/*n,*/ 1);
+		if (forkshell(jp, n, FORK_NOJOB) == 0) {
+			FORCE_INT_ON;
+			close(pip[0]);
+			if (pip[1] != 1) {
+				/*close(1);*/
+				copyfd(pip[1], 1 | COPYFD_EXACT);
+				close(pip[1]);
+			}
+			eflag = 0;
+			evaltree(n, EV_EXIT); /* actually evaltreenr... */
+			/* NOTREACHED */
+		}
+		close(pip[1]);
+		result->fd = pip[0];
+		result->jp = jp;
+	}
+	herefd = saveherefd;
+ out:
+	TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+		result->fd, result->buf, result->nleft, result->jp));
+}
+
+/*
+ * Expand stuff in backwards quotes.
+ */
+static void
+expbackq(union node *cmd, int quoted, int quotes)
+{
+	struct backcmd in;
+	int i;
+	char buf[128];
+	char *p;
+	char *dest;
+	int startloc;
+	int syntax = quoted ? DQSYNTAX : BASESYNTAX;
+	struct stackmark smark;
+
+	INT_OFF;
+	setstackmark(&smark);
+	dest = expdest;
+	startloc = dest - (char *)stackblock();
+	grabstackstr(dest);
+	evalbackcmd(cmd, &in);
+	popstackmark(&smark);
+
+	p = in.buf;
+	i = in.nleft;
+	if (i == 0)
+		goto read;
+	for (;;) {
+		memtodest(p, i, syntax, quotes);
+ read:
+		if (in.fd < 0)
+			break;
+		i = nonblock_immune_read(in.fd, buf, sizeof(buf), /*loop_on_EINTR:*/ 1);
+		TRACE(("expbackq: read returns %d\n", i));
+		if (i <= 0)
+			break;
+		p = buf;
+	}
+
+	free(in.buf);
+	if (in.fd >= 0) {
+		close(in.fd);
+		back_exitstatus = waitforjob(in.jp);
+	}
+	INT_ON;
+
+	/* Eat all trailing newlines */
+	dest = expdest;
+	for (; dest > (char *)stackblock() && dest[-1] == '\n';)
+		STUNPUTC(dest);
+	expdest = dest;
+
+	if (quoted == 0)
+		recordregion(startloc, dest - (char *)stackblock(), 0);
+	TRACE(("evalbackq: size:%d:'%.*s'\n",
+		(int)((dest - (char *)stackblock()) - startloc),
+		(int)((dest - (char *)stackblock()) - startloc),
+		stackblock() + startloc));
+}
+
+#if ENABLE_SH_MATH_SUPPORT
+/*
+ * Expand arithmetic expression.  Backup to start of expression,
+ * evaluate, place result in (backed up) result, adjust string position.
+ */
+static void
+expari(int quotes)
+{
+	char *p, *start;
+	int begoff;
+	int flag;
+	int len;
+
+	/* ifsfree(); */
+
+	/*
+	 * This routine is slightly over-complicated for
+	 * efficiency.  Next we scan backwards looking for the
+	 * start of arithmetic.
+	 */
+	start = stackblock();
+	p = expdest - 1;
+	*p = '\0';
+	p--;
+	while (1) {
+		int esc;
+
+		while ((unsigned char)*p != CTLARI) {
+			p--;
+#if DEBUG
+			if (p < start) {
+				ash_msg_and_raise_error("missing CTLARI (shouldn't happen)");
+			}
+#endif
+		}
+
+		esc = esclen(start, p);
+		if (!(esc % 2)) {
+			break;
+		}
+
+		p -= esc + 1;
+	}
+
+	begoff = p - start;
+
+	removerecordregions(begoff);
+
+	flag = p[1];
+
+	expdest = p;
+
+	if (quotes)
+		rmescapes(p + 2, 0);
+
+	len = cvtnum(ash_arith(p + 2));
+
+	if (flag != '"')
+		recordregion(begoff, begoff + len, 0);
+}
+#endif
+
+/* argstr needs it */
+static char *evalvar(char *p, int flags, struct strlist *var_str_list);
+
+/*
+ * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
+ * characters to allow for further processing.  Otherwise treat
+ * $@ like $* since no splitting will be performed.
+ *
+ * var_str_list (can be NULL) is a list of "VAR=val" strings which take precedence
+ * over shell varables. Needed for "A=a B=$A; echo $B" case - we use it
+ * for correct expansion of "B=$A" word.
+ */
+static void
+argstr(char *p, int flags, struct strlist *var_str_list)
+{
+	static const char spclchars[] ALIGN1 = {
+		'=',
+		':',
+		CTLQUOTEMARK,
+		CTLENDVAR,
+		CTLESC,
+		CTLVAR,
+		CTLBACKQ,
+		CTLBACKQ | CTLQUOTE,
+#if ENABLE_SH_MATH_SUPPORT
+		CTLENDARI,
+#endif
+		'\0'
+	};
+	const char *reject = spclchars;
+	int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */
+	int breakall = flags & EXP_WORD;
+	int inquotes;
+	size_t length;
+	int startloc;
+
+	if (!(flags & EXP_VARTILDE)) {
+		reject += 2;
+	} else if (flags & EXP_VARTILDE2) {
+		reject++;
+	}
+	inquotes = 0;
+	length = 0;
+	if (flags & EXP_TILDE) {
+		char *q;
+
+		flags &= ~EXP_TILDE;
+ tilde:
+		q = p;
+		if ((unsigned char)*q == CTLESC && (flags & EXP_QWORD))
+			q++;
+		if (*q == '~')
+			p = exptilde(p, q, flags);
+	}
+ start:
+	startloc = expdest - (char *)stackblock();
+	for (;;) {
+		unsigned char c;
+
+		length += strcspn(p + length, reject);
+		c = p[length];
+		if (c) {
+			if (!(c & 0x80)
+			IF_SH_MATH_SUPPORT(|| c == CTLENDARI)
+			) {
+				/* c == '=' || c == ':' || c == CTLENDARI */
+				length++;
+			}
+		}
+		if (length > 0) {
+			int newloc;
+			expdest = stack_nputstr(p, length, expdest);
+			newloc = expdest - (char *)stackblock();
+			if (breakall && !inquotes && newloc > startloc) {
+				recordregion(startloc, newloc, 0);
+			}
+			startloc = newloc;
+		}
+		p += length + 1;
+		length = 0;
+
+		switch (c) {
+		case '\0':
+			goto breakloop;
+		case '=':
+			if (flags & EXP_VARTILDE2) {
+				p--;
+				continue;
+			}
+			flags |= EXP_VARTILDE2;
+			reject++;
+			/* fall through */
+		case ':':
+			/*
+			 * sort of a hack - expand tildes in variable
+			 * assignments (after the first '=' and after ':'s).
+			 */
+			if (*--p == '~') {
+				goto tilde;
+			}
+			continue;
+		}
+
+		switch (c) {
+		case CTLENDVAR: /* ??? */
+			goto breakloop;
+		case CTLQUOTEMARK:
+			/* "$@" syntax adherence hack */
+			if (!inquotes
+			 && memcmp(p, dolatstr, 4) == 0
+			 && (  p[4] == (char)CTLQUOTEMARK
+			    || (p[4] == (char)CTLENDVAR && p[5] == (char)CTLQUOTEMARK)
+			    )
+			) {
+				p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1;
+				goto start;
+			}
+			inquotes = !inquotes;
+ addquote:
+			if (quotes) {
+				p--;
+				length++;
+				startloc++;
+			}
+			break;
+		case CTLESC:
+			startloc++;
+			length++;
+			goto addquote;
+		case CTLVAR:
+			p = evalvar(p, flags, var_str_list);
+			goto start;
+		case CTLBACKQ:
+			c = '\0';
+		case CTLBACKQ|CTLQUOTE:
+			expbackq(argbackq->n, c, quotes);
+			argbackq = argbackq->next;
+			goto start;
+#if ENABLE_SH_MATH_SUPPORT
+		case CTLENDARI:
+			p--;
+			expari(quotes);
+			goto start;
+#endif
+		}
+	}
+ breakloop: ;
+}
+
+static char *
+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;
+		const char *s = loc2;
+
+		c = *loc2;
+		if (zero) {
+			*loc2 = '\0';
+			s = rmesc;
+		}
+		match = pmatch(pattern, s);
+
+		*loc2 = c;
+		if (match)
+			return loc;
+		if (quotes && (unsigned char)*loc == CTLESC)
+			loc++;
+		loc++;
+		loc2++;
+	} while (c);
+	return NULL;
+}
+
+static char *
+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;
+#endif
+	int esc = 0;
+	char *loc;
+	char *loc2;
+
+	/* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
+	 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
+	 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
+	 * Logic:
+	 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
+	 * and on each iteration they go back two/one char until they reach the beginning.
+	 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
+	 */
+	/* TODO: document in what other circumstances we are called. */
+
+	for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
+		int match;
+		char c = *loc2;
+		const char *s = loc2;
+		if (match_at_start) {
+			*loc2 = '\0';
+			s = rmesc;
+		}
+		match = pmatch(pattern, s);
+		//bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
+		*loc2 = c;
+		if (match)
+			return loc;
+#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
+		if (try2optimize) {
+			/* Maybe we can optimize this:
+			 * if pattern ends with unescaped *, we can avoid checking
+			 * shorter strings: if "foo*" doesnt match "raw_value_of_v",
+			 * it wont match truncated "raw_value_of_" strings too.
+			 */
+			unsigned plen = strlen(pattern);
+			/* Does it end with "*"? */
+			if (plen != 0 && pattern[--plen] == '*') {
+				/* "xxxx*" is not escaped */
+				/* "xxx\*" is escaped */
+				/* "xx\\*" is not escaped */
+				/* "x\\\*" is escaped */
+				int slashes = 0;
+				while (plen != 0 && pattern[--plen] == '\\')
+					slashes++;
+				if (!(slashes & 1))
+					break; /* ends with unescaped "*" */
+			}
+			try2optimize = 0;
+		}
+#endif
+		loc--;
+		if (quotes) {
+			if (--esc < 0) {
+				esc = esclen(startp, loc);
+			}
+			if (esc % 2) {
+				esc--;
+				loc--;
+			}
+		}
+	}
+	return NULL;
+}
+
+static void varunset(const char *, const char *, const char *, int) NORETURN;
+static void
+varunset(const char *end, const char *var, const char *umsg, int varflags)
+{
+	const char *msg;
+	const char *tail;
+
+	tail = nullstr;
+	msg = "parameter not set";
+	if (umsg) {
+		if ((unsigned char)*end == CTLENDVAR) {
+			if (varflags & VSNUL)
+				tail = " or null";
+		} else {
+			msg = umsg;
+		}
+	}
+	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 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;
+		if (!c)
+			break;
+		if (c == '/') {
+			/* Only the first '/' seen is our separator */
+			if (!repl) {
+				repl = idx + 1;
+				c = '\0';
+			}
+		}
+		*idx++ = c;
+		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 *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 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);
+	STPUTC('\0', expdest);
+	herefd = saveherefd;
+	argbackq = saveargbackq;
+	startp = (char *)stackblock() + startloc;
+
+	switch (subtype) {
+	case VSASSIGN:
+		setvar(varname, startp, 0);
+		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;
+		/* Read POS in ${var:POS:LEN} */
+		pos = atoi(loc); /* number(loc) errors out on "1:4" */
+		len = str - startp - 1;
+
+		/* *loc != '\0', guaranteed by parser */
+		if (quotes) {
+			char *ptr;
+
+			/* Adjust the length by the number of escapes */
+			for (ptr = startp; ptr < (str - 1); ptr++) {
+				if ((unsigned char)*ptr == CTLESC) {
+					len--;
+					ptr++;
+				}
+			}
+		}
+		orig_len = len;
+
+		if (*loc++ == ':') {
+			/* ${var::LEN} */
+			len = number(loc);
+		} else {
+			/* Skip POS in ${var:POS:LEN} */
+			len = orig_len;
+			while (*loc && *loc != ':') {
+				/* TODO?
+				 * bash complains on: var=qwe; echo ${var:1a:123}
+				if (!isdigit(*loc))
+					ash_msg_and_raise_error(msg_illnum, str);
+				 */
+				loc++;
+			}
+			if (*loc++ == ':') {
+				len = number(loc);
+			}
+		}
+		if (pos >= orig_len) {
+			pos = 0;
+			len = 0;
+		}
+		if (len > (orig_len - pos))
+			len = orig_len - pos;
+
+		for (str = startp; pos; str++, pos--) {
+			if (quotes && (unsigned char)*str == CTLESC)
+				str++;
+		}
+		for (loc = startp; len; len--) {
+			if (quotes && (unsigned char)*str == CTLESC)
+				*loc++ = *str++;
+			*loc++ = *str++;
+		}
+		*loc = '\0';
+		amount = loc - expdest;
+		STADJUST(amount, expdest);
+		return loc;
+#endif
+	}
+
+	resetloc = expdest - (char *)stackblock();
+
+	/* We'll comeback here if we grow the stack while handling
+	 * a VSREPLACE or VSREPLACEALL, since our pointers into the
+	 * stack will need rebasing, and we'll need to remove our work
+	 * areas each time
+	 */
+ IF_ASH_BASH_COMPAT(restart:)
+
+	amount = expdest - ((char *)stackblock() + resetloc);
+	STADJUST(-amount, expdest);
+	startp = (char *)stackblock() + startloc;
+
+	rmesc = startp;
+	rmescend = (char *)stackblock() + strloc;
+	if (quotes) {
+		rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW);
+		if (rmesc != startp) {
+			rmescend = expdest;
+			startp = (char *)stackblock() + startloc;
+		}
+	}
+	rmescend--;
+	str = (char *)stackblock() + strloc;
+	preglob(str, varflags & VSQUOTE, 0);
+	workloc = expdest - (char *)stackblock();
+
+#if ENABLE_ASH_BASH_COMPAT
+	if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
+		char *idx, *end;
+
+		if (!repl) {
+			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 NULL;
+
+		len = 0;
+		idx = startp;
+		end = str - 1;
+		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();
+ skip_matching:
+				STPUTC(*idx, expdest);
+				if (quotes && (unsigned char)*idx == CTLESC) {
+					idx++;
+					len++;
+					STPUTC(*idx, expdest);
+				}
+				if (stackblock() != restart_detect)
+					goto restart;
+				idx++;
+				len++;
+				rmesc++;
+				/* continue; - prone to quadratic behavior, smarter code: */
+				if (idx >= end)
+					break;
+				if (str[0] == '*') {
+					/* Pattern is "*foo". If "*foo" does not match "long_string",
+					 * it would never match "ong_string" etc, no point in trying.
+					 */
+					goto skip_matching;
+				}
+				goto try_to_match;
+			}
+
+			if (subtype == VSREPLACEALL) {
+				while (idx < loc) {
+					if (quotes && (unsigned char)*idx == CTLESC)
+						idx++;
+					idx++;
+					rmesc++;
+				}
+			} else {
+				idx = loc;
+			}
+
+			//bb_error_msg("repl:'%s'", repl);
+			for (loc = (char*)repl; *loc; loc++) {
+				char *restart_detect = stackblock();
+				if (quotes && *loc == '\\') {
+					STPUTC(CTLESC, expdest);
+					len++;
+				}
+				STPUTC(*loc, expdest);
+				if (stackblock() != restart_detect)
+					goto restart;
+				len++;
+			}
+
+			if (subtype == VSREPLACE) {
+				//bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
+				while (*idx) {
+					char *restart_detect = stackblock();
+					STPUTC(*idx, expdest);
+					if (stackblock() != restart_detect)
+						goto restart;
+					len++;
+					idx++;
+				}
+				break;
+			}
+		}
+
+		/* We've put the replaced text into a buffer at workloc, now
+		 * move it to the right place and adjust the stack.
+		 */
+		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;
+	}
+#endif /* ENABLE_ASH_BASH_COMPAT */
+
+	subtype -= VSTRIMRIGHT;
+#if DEBUG
+	if (subtype < 0 || subtype > 7)
+		abort();
+#endif
+	/* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
+	zero = subtype >> 1;
+	/* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
+	scan = (subtype & 1) ^ zero ? scanleft : scanright;
+
+	loc = scan(startp, rmesc, rmescend, str, quotes, zero);
+	if (loc) {
+		if (zero) {
+			memmove(startp, loc, str - loc);
+			loc = startp + (str - loc) - 1;
+		}
+		*loc = '\0';
+		amount = loc - expdest;
+		STADJUST(amount, expdest);
+	}
+	return loc;
+}
+
+/*
+ * Add the value of a specialized variable to the stack string.
+ * name parameter (examples):
+ * ash -c 'echo $1'      name:'1='
+ * ash -c 'echo $qwe'    name:'qwe='
+ * ash -c 'echo $$'      name:'$='
+ * ash -c 'echo ${$}'    name:'$='
+ * ash -c 'echo ${$##q}' name:'$=q'
+ * ash -c 'echo ${#$}'   name:'$='
+ * note: examples with bad shell syntax:
+ * ash -c 'echo ${#$1}'  name:'$=1'
+ * ash -c 'echo ${#1#}'  name:'1=#'
+ */
+static NOINLINE ssize_t
+varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
+{
+	const char *p;
+	int num;
+	int i;
+	int sepq = 0;
+	ssize_t len = 0;
+	int subtype = varflags & VSTYPE;
+	int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR);
+	int quoted = varflags & VSQUOTE;
+	int syntax = quoted ? DQSYNTAX : BASESYNTAX;
+
+	switch (*name) {
+	case '$':
+		num = rootpid;
+		goto numvar;
+	case '?':
+		num = exitstatus;
+		goto numvar;
+	case '#':
+		num = shellparam.nparam;
+		goto numvar;
+	case '!':
+		num = backgndpid;
+		if (num == 0)
+			return -1;
+ numvar:
+		len = cvtnum(num);
+		goto check_1char_name;
+	case '-':
+		expdest = makestrspace(NOPTS, expdest);
+		for (i = NOPTS - 1; i >= 0; i--) {
+			if (optlist[i]) {
+				USTPUTC(optletters(i), expdest);
+				len++;
+			}
+		}
+ check_1char_name:
+#if 0
+		/* handles cases similar to ${#$1} */
+		if (name[2] != '\0')
+			raise_error_syntax("bad substitution");
+#endif
+		break;
+	case '@': {
+		char **ap;
+		int sep;
+
+		if (quoted && (flags & EXP_FULL)) {
+			/* note: this is not meant as PEOF value */
+			sep = 1 << CHAR_BIT;
+			goto param;
+		}
+		/* fall through */
+	case '*':
+		sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' ';
+		i = SIT(sep, syntax);
+		if (quotes && (i == CCTL || i == CBACK))
+			sepq = 1;
+ param:
+		ap = shellparam.p;
+		if (!ap)
+			return -1;
+		while ((p = *ap++) != NULL) {
+			size_t partlen;
+
+			partlen = strlen(p);
+			len += partlen;
+
+			if (!(subtype == VSPLUS || subtype == VSLENGTH))
+				memtodest(p, partlen, syntax, quotes);
+
+			if (*ap && sep) {
+				char *q;
+
+				len++;
+				if (subtype == VSPLUS || subtype == VSLENGTH) {
+					continue;
+				}
+				q = expdest;
+				if (sepq)
+					STPUTC(CTLESC, q);
+				/* note: may put NUL despite sep != 0
+				 * (see sep = 1 << CHAR_BIT above) */
+				STPUTC(sep, q);
+				expdest = q;
+			}
+		}
+		return len;
+	} /* case '@' and '*' */
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+		num = atoi(name); /* number(name) fails on ${N#str} etc */
+		if (num < 0 || num > shellparam.nparam)
+			return -1;
+		p = num ? shellparam.p[num - 1] : arg0;
+		goto value;
+	default:
+		/* NB: name has form "VAR=..." */
+
+		/* "A=a B=$A" case: var_str_list is a list of "A=a" strings
+		 * which should be considered before we check variables. */
+		if (var_str_list) {
+			unsigned name_len = (strchrnul(name, '=') - name) + 1;
+			p = NULL;
+			do {
+				char *str, *eq;
+				str = var_str_list->text;
+				eq = strchr(str, '=');
+				if (!eq) /* stop at first non-assignment */
+					break;
+				eq++;
+				if (name_len == (unsigned)(eq - str)
+				 && strncmp(str, name, name_len) == 0
+				) {
+					p = eq;
+					/* goto value; - WRONG! */
+					/* think "A=1 A=2 B=$A" */
+				}
+				var_str_list = var_str_list->next;
+			} while (var_str_list);
+			if (p)
+				goto value;
+		}
+		p = lookupvar(name);
+ value:
+		if (!p)
+			return -1;
+
+		len = strlen(p);
+		if (!(subtype == VSPLUS || subtype == VSLENGTH))
+			memtodest(p, len, syntax, quotes);
+		return len;
+	}
+
+	if (subtype == VSPLUS || subtype == VSLENGTH)
+		STADJUST(-len, expdest);
+	return len;
+}
+
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+static char *
+evalvar(char *p, int flags, struct strlist *var_str_list)
+{
+	char varflags;
+	char subtype;
+	char quoted;
+	char easy;
+	char *var;
+	int patloc;
+	int startloc;
+	ssize_t varlen;
+
+	varflags = (unsigned char) *p++;
+	subtype = varflags & VSTYPE;
+	quoted = varflags & VSQUOTE;
+	var = p;
+	easy = (!quoted || (*var == '@' && shellparam.nparam));
+	startloc = expdest - (char *)stackblock();
+	p = strchr(p, '=') + 1; //TODO: use var_end(p)?
+
+ again:
+	varlen = varvalue(var, varflags, flags, var_str_list);
+	if (varflags & VSNUL)
+		varlen--;
+
+	if (subtype == VSPLUS) {
+		varlen = -1 - varlen;
+		goto vsplus;
+	}
+
+	if (subtype == VSMINUS) {
+ vsplus:
+		if (varlen < 0) {
+			argstr(
+				p,
+				flags | (quoted ? EXP_TILDE|EXP_QWORD : EXP_TILDE|EXP_WORD),
+				var_str_list
+			);
+			goto end;
+		}
+		if (easy)
+			goto record;
+		goto end;
+	}
+
+	if (subtype == VSASSIGN || subtype == VSQUESTION) {
+		if (varlen < 0) {
+			if (subevalvar(p, var, /* strloc: */ 0,
+					subtype, startloc, varflags,
+					/* quotes: */ 0,
+					var_str_list)
+			) {
+				varflags &= ~VSNUL;
+				/*
+				 * Remove any recorded regions beyond
+				 * start of variable
+				 */
+				removerecordregions(startloc);
+				goto again;
+			}
+			goto end;
+		}
+		if (easy)
+			goto record;
+		goto end;
+	}
+
+	if (varlen < 0 && uflag)
+		varunset(p, var, 0, 0);
+
+	if (subtype == VSLENGTH) {
+		cvtnum(varlen > 0 ? varlen : 0);
+		goto record;
+	}
+
+	if (subtype == VSNORMAL) {
+		if (easy)
+			goto record;
+		goto end;
+	}
+
+#if DEBUG
+	switch (subtype) {
+	case VSTRIMLEFT:
+	case VSTRIMLEFTMAX:
+	case VSTRIMRIGHT:
+	case VSTRIMRIGHTMAX:
+#if ENABLE_ASH_BASH_COMPAT
+	case VSSUBSTR:
+	case VSREPLACE:
+	case VSREPLACEALL:
+#endif
+		break;
+	default:
+		abort();
+	}
+#endif
+
+	if (varlen >= 0) {
+		/*
+		 * Terminate the string and start recording the pattern
+		 * right after it
+		 */
+		STPUTC('\0', expdest);
+		patloc = expdest - (char *)stackblock();
+		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),
+				var_str_list)
+		) {
+			int amount = expdest - (
+				(char *)stackblock() + patloc - 1
+			);
+			STADJUST(-amount, expdest);
+		}
+		/* Remove any recorded regions beyond start of variable */
+		removerecordregions(startloc);
+ record:
+		recordregion(startloc, expdest - (char *)stackblock(), quoted);
+	}
+
+ end:
+	if (subtype != VSNORMAL) {      /* skip to end of alternative */
+		int nesting = 1;
+		for (;;) {
+			unsigned char c = *p++;
+			if (c == CTLESC)
+				p++;
+			else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+				if (varlen >= 0)
+					argbackq = argbackq->next;
+			} else if (c == CTLVAR) {
+				if ((*p++ & VSTYPE) != VSNORMAL)
+					nesting++;
+			} else if (c == CTLENDVAR) {
+				if (--nesting == 0)
+					break;
+			}
+		}
+	}
+	return p;
+}
+
+/*
+ * Break the argument string into pieces based upon IFS and add the
+ * strings to the argument list.  The regions of the string to be
+ * searched for IFS characters have been stored by recordregion.
+ */
+static void
+ifsbreakup(char *string, struct arglist *arglist)
+{
+	struct ifsregion *ifsp;
+	struct strlist *sp;
+	char *start;
+	char *p;
+	char *q;
+	const char *ifs, *realifs;
+	int ifsspc;
+	int nulonly;
+
+	start = string;
+	if (ifslastp != NULL) {
+		ifsspc = 0;
+		nulonly = 0;
+		realifs = ifsset() ? ifsval() : defifs;
+		ifsp = &ifsfirst;
+		do {
+			p = string + ifsp->begoff;
+			nulonly = ifsp->nulonly;
+			ifs = nulonly ? nullstr : realifs;
+			ifsspc = 0;
+			while (p < string + ifsp->endoff) {
+				q = p;
+				if ((unsigned char)*p == CTLESC)
+					p++;
+				if (!strchr(ifs, *p)) {
+					p++;
+					continue;
+				}
+				if (!nulonly)
+					ifsspc = (strchr(defifs, *p) != NULL);
+				/* Ignore IFS whitespace at start */
+				if (q == start && ifsspc) {
+					p++;
+					start = p;
+					continue;
+				}
+				*q = '\0';
+				sp = stzalloc(sizeof(*sp));
+				sp->text = start;
+				*arglist->lastp = sp;
+				arglist->lastp = &sp->next;
+				p++;
+				if (!nulonly) {
+					for (;;) {
+						if (p >= string + ifsp->endoff) {
+							break;
+						}
+						q = p;
+						if ((unsigned char)*p == CTLESC)
+							p++;
+						if (strchr(ifs, *p) == NULL) {
+							p = q;
+							break;
+						}
+						if (strchr(defifs, *p) == NULL) {
+							if (ifsspc) {
+								p++;
+								ifsspc = 0;
+							} else {
+								p = q;
+								break;
+							}
+						} else
+							p++;
+					}
+				}
+				start = p;
+			} /* while */
+			ifsp = ifsp->next;
+		} while (ifsp != NULL);
+		if (nulonly)
+			goto add;
+	}
+
+	if (!*start)
+		return;
+
+ add:
+	sp = stzalloc(sizeof(*sp));
+	sp->text = start;
+	*arglist->lastp = sp;
+	arglist->lastp = &sp->next;
+}
+
+static void
+ifsfree(void)
+{
+	struct ifsregion *p;
+
+	INT_OFF;
+	p = ifsfirst.next;
+	do {
+		struct ifsregion *ifsp;
+		ifsp = p->next;
+		free(p);
+		p = ifsp;
+	} while (p);
+	ifslastp = NULL;
+	ifsfirst.next = NULL;
+	INT_ON;
+}
+
+/*
+ * Add a file name to the list.
+ */
+static void
+addfname(const char *name)
+{
+	struct strlist *sp;
+
+	sp = stzalloc(sizeof(*sp));
+	sp->text = ststrdup(name);
+	*exparg.lastp = sp;
+	exparg.lastp = &sp->next;
+}
+
+/*
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
+ */
+static void
+expmeta(char *expdir, char *enddir, char *name)
+{
+	char *p;
+	const char *cp;
+	char *start;
+	char *endname;
+	int metaflag;
+	struct stat statb;
+	DIR *dirp;
+	struct dirent *dp;
+	int atend;
+	int matchdot;
+
+	metaflag = 0;
+	start = name;
+	for (p = name; *p; p++) {
+		if (*p == '*' || *p == '?')
+			metaflag = 1;
+		else if (*p == '[') {
+			char *q = p + 1;
+			if (*q == '!')
+				q++;
+			for (;;) {
+				if (*q == '\\')
+					q++;
+				if (*q == '/' || *q == '\0')
+					break;
+				if (*++q == ']') {
+					metaflag = 1;
+					break;
+				}
+			}
+		} else if (*p == '\\')
+			p++;
+		else if (*p == '/') {
+			if (metaflag)
+				goto out;
+			start = p + 1;
+		}
+	}
+ out:
+	if (metaflag == 0) {    /* we've reached the end of the file name */
+		if (enddir != expdir)
+			metaflag++;
+		p = name;
+		do {
+			if (*p == '\\')
+				p++;
+			*enddir++ = *p;
+		} while (*p++);
+		if (metaflag == 0 || lstat(expdir, &statb) >= 0)
+			addfname(expdir);
+		return;
+	}
+	endname = p;
+	if (name < start) {
+		p = name;
+		do {
+			if (*p == '\\')
+				p++;
+			*enddir++ = *p++;
+		} while (p < start);
+	}
+	if (enddir == expdir) {
+		cp = ".";
+	} else if (enddir == expdir + 1 && *expdir == '/') {
+		cp = "/";
+	} else {
+		cp = expdir;
+		enddir[-1] = '\0';
+	}
+	dirp = opendir(cp);
+	if (dirp == NULL)
+		return;
+	if (enddir != expdir)
+		enddir[-1] = '/';
+	if (*endname == 0) {
+		atend = 1;
+	} else {
+		atend = 0;
+		*endname++ = '\0';
+	}
+	matchdot = 0;
+	p = start;
+	if (*p == '\\')
+		p++;
+	if (*p == '.')
+		matchdot++;
+	while (!pending_int && (dp = readdir(dirp)) != NULL) {
+		if (dp->d_name[0] == '.' && !matchdot)
+			continue;
+		if (pmatch(start, dp->d_name)) {
+			if (atend) {
+				strcpy(enddir, dp->d_name);
+				addfname(expdir);
+			} else {
+				for (p = enddir, cp = dp->d_name; (*p++ = *cp++) != '\0';)
+					continue;
+				p[-1] = '/';
+				expmeta(expdir, p, endname);
+			}
+		}
+	}
+	closedir(dirp);
+	if (!atend)
+		endname[-1] = '/';
+}
+
+static struct strlist *
+msort(struct strlist *list, int len)
+{
+	struct strlist *p, *q = NULL;
+	struct strlist **lpp;
+	int half;
+	int n;
+
+	if (len <= 1)
+		return list;
+	half = len >> 1;
+	p = list;
+	for (n = half; --n >= 0;) {
+		q = p;
+		p = p->next;
+	}
+	q->next = NULL;                 /* terminate first half of list */
+	q = msort(list, half);          /* sort first half of list */
+	p = msort(p, len - half);               /* sort second half */
+	lpp = &list;
+	for (;;) {
+#if ENABLE_LOCALE_SUPPORT
+		if (strcoll(p->text, q->text) < 0)
+#else
+		if (strcmp(p->text, q->text) < 0)
+#endif
+						{
+			*lpp = p;
+			lpp = &p->next;
+			p = *lpp;
+			if (p == NULL) {
+				*lpp = q;
+				break;
+			}
+		} else {
+			*lpp = q;
+			lpp = &q->next;
+			q = *lpp;
+			if (q == NULL) {
+				*lpp = p;
+				break;
+			}
+		}
+	}
+	return list;
+}
+
+/*
+ * Sort the results of file name expansion.  It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+static struct strlist *
+expsort(struct strlist *str)
+{
+	int len;
+	struct strlist *sp;
+
+	len = 0;
+	for (sp = str; sp; sp = sp->next)
+		len++;
+	return msort(str, len);
+}
+
+static void
+expandmeta(struct strlist *str /*, int flag*/)
+{
+	static const char metachars[] ALIGN1 = {
+		'*', '?', '[', 0
+	};
+	/* TODO - EXP_REDIR */
+
+	while (str) {
+		char *expdir;
+		struct strlist **savelastp;
+		struct strlist *sp;
+		char *p;
+
+		if (fflag)
+			goto nometa;
+		if (!strpbrk(str->text, metachars))
+			goto nometa;
+		savelastp = exparg.lastp;
+
+		INT_OFF;
+		p = preglob(str->text, 0, RMESCAPE_ALLOC | RMESCAPE_HEAP);
+		{
+			int i = strlen(str->text);
+			expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+		}
+		expmeta(expdir, expdir, p);
+		free(expdir);
+		if (p != str->text)
+			free(p);
+		INT_ON;
+		if (exparg.lastp == savelastp) {
+			/*
+			 * no matches
+			 */
+ nometa:
+			*exparg.lastp = str;
+			rmescapes(str->text, 0);
+			exparg.lastp = &str->next;
+		} else {
+			*exparg.lastp = NULL;
+			*savelastp = sp = expsort(*savelastp);
+			while (sp->next != NULL)
+				sp = sp->next;
+			exparg.lastp = &sp->next;
+		}
+		str = str->next;
+	}
+}
+
+/*
+ * Perform variable substitution and command substitution on an argument,
+ * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
+ * perform splitting and file name expansion.  When arglist is NULL, perform
+ * here document expansion.
+ */
+static void
+expandarg(union node *arg, struct arglist *arglist, int flag)
+{
+	struct strlist *sp;
+	char *p;
+
+	argbackq = arg->narg.backquote;
+	STARTSTACKSTR(expdest);
+	ifsfirst.next = NULL;
+	ifslastp = NULL;
+	argstr(arg->narg.text, flag,
+			/* var_str_list: */ arglist ? arglist->list : NULL);
+	p = _STPUTC('\0', expdest);
+	expdest = p - 1;
+	if (arglist == NULL) {
+		return;                 /* here document expanded */
+	}
+	p = grabstackstr(p);
+	exparg.lastp = &exparg.list;
+	/*
+	 * TODO - EXP_REDIR
+	 */
+	if (flag & EXP_FULL) {
+		ifsbreakup(p, &exparg);
+		*exparg.lastp = NULL;
+		exparg.lastp = &exparg.list;
+		expandmeta(exparg.list /*, flag*/);
+	} else {
+		if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+			rmescapes(p, 0);
+		sp = stzalloc(sizeof(*sp));
+		sp->text = p;
+		*exparg.lastp = sp;
+		exparg.lastp = &sp->next;
+	}
+	if (ifsfirst.next)
+		ifsfree();
+	*exparg.lastp = NULL;
+	if (exparg.list) {
+		*arglist->lastp = exparg.list;
+		arglist->lastp = exparg.lastp;
+	}
+}
+
+/*
+ * Expand shell variables and backquotes inside a here document.
+ */
+static void
+expandhere(union node *arg, int fd)
+{
+	herefd = fd;
+	expandarg(arg, (struct arglist *)NULL, 0);
+	full_write(fd, stackblock(), expdest - (char *)stackblock());
+}
+
+/*
+ * Returns true if the pattern matches the string.
+ */
+static int
+patmatch(char *pattern, const char *string)
+{
+	return pmatch(preglob(pattern, 0, 0), string);
+}
+
+/*
+ * See if a pattern matches in a case statement.
+ */
+static int
+casematch(union node *pattern, char *val)
+{
+	struct stackmark smark;
+	int result;
+
+	setstackmark(&smark);
+	argbackq = pattern->narg.backquote;
+	STARTSTACKSTR(expdest);
+	ifslastp = NULL;
+	argstr(pattern->narg.text, EXP_TILDE | EXP_CASE,
+			/* var_str_list: */ NULL);
+	STACKSTRNUL(expdest);
+	result = patmatch(stackblock(), val);
+	popstackmark(&smark);
+	return result;
+}
+
+
+/* ============ find_command */
+
+struct builtincmd {
+	const char *name;
+	int (*builtin)(int, char **) FAST_FUNC;
+	/* unsigned flags; */
+};
+#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
+/* "regular" builtins always take precedence over commands,
+ * regardless of PATH=....%builtin... position */
+#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
+#define IS_BUILTIN_ASSIGN(b)  ((b)->name[0] & 4)
+
+struct cmdentry {
+	smallint cmdtype;       /* CMDxxx */
+	union param {
+		int index;
+		/* index >= 0 for commands without path (slashes) */
+		/* (TODO: what exactly does the value mean? PATH position?) */
+		/* index == -1 for commands with slashes */
+		/* index == (-2 - applet_no) for NOFORK applets */
+		const struct builtincmd *cmd;
+		struct funcnode *func;
+	} u;
+};
+/* values of cmdtype */
+#define CMDUNKNOWN      -1      /* no entry in table for command */
+#define CMDNORMAL       0       /* command is an executable program */
+#define CMDFUNCTION     1       /* command is a shell function */
+#define CMDBUILTIN      2       /* command is a shell builtin */
+
+/* action to find_command() */
+#define DO_ERR          0x01    /* prints errors */
+#define DO_ABS          0x02    /* checks absolute paths */
+#define DO_NOFUNC       0x04    /* don't return shell functions, for command */
+#define DO_ALTPATH      0x08    /* using alternate path */
+#define DO_ALTBLTIN     0x20    /* %builtin in alt. path */
+
+static void find_command(char *, struct cmdentry *, int, const char *);
+
+
+/* ============ Hashing commands */
+
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+
+struct tblentry {
+	struct tblentry *next;  /* next entry in hash chain */
+	union param param;      /* definition of builtin function */
+	smallint cmdtype;       /* CMDxxx */
+	char rehash;            /* if set, cd done since entry created */
+	char cmdname[1];        /* name of command */
+};
+
+static struct tblentry **cmdtable;
+#define INIT_G_cmdtable() do { \
+	cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
+} while (0)
+
+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)
+{
+#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 {
+		execve(cmd, argv, envp);
+	} while (errno == EINTR);
+#else
+	execve(cmd, argv, envp);
+#endif
+	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.
+		 */
+		char **ap;
+		char **new;
+
+		for (ap = argv; *ap; ap++)
+			continue;
+		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;
+		goto repeat;
+	}
+}
+
+/*
+ * Exec a program.  Never returns.  If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+static void shellexec(char **, const char *, int) NORETURN;
+static void
+shellexec(char **argv, const char *path, int idx)
+{
+	char *cmdname;
+	int e;
+	char **envp;
+	int exerrno;
+#if ENABLE_FEATURE_SH_STANDALONE
+	int applet_no = -1;
+#endif
+
+	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
+	) {
+		tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp);
+		e = errno;
+	} else {
+		e = ENOENT;
+		while ((cmdname = path_advance(&path, argv[0])) != NULL) {
+			if (--idx < 0 && pathopt == NULL) {
+				tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
+				if (errno != ENOENT && errno != ENOTDIR)
+					e = errno;
+			}
+			stunalloc(cmdname);
+		}
+	}
+
+	/* Map to POSIX errors */
+	switch (e) {
+	case EACCES:
+		exerrno = 126;
+		break;
+	case ENOENT:
+		exerrno = 127;
+		break;
+	default:
+		exerrno = 2;
+		break;
+	}
+	exitstatus = exerrno;
+	TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
+		argv[0], e, suppress_int));
+	ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found"));
+	/* NOTREACHED */
+}
+
+static void
+printentry(struct tblentry *cmdp)
+{
+	int idx;
+	const char *path;
+	char *name;
+
+	idx = cmdp->param.index;
+	path = pathval();
+	do {
+		name = path_advance(&path, cmdp->cmdname);
+		stunalloc(name);
+	} while (--idx >= 0);
+	out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
+}
+
+/*
+ * Clear out command entries.  The argument specifies the first entry in
+ * PATH which has changed.
+ */
+static void
+clearcmdentry(int firstchange)
+{
+	struct tblentry **tblp;
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+
+	INT_OFF;
+	for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
+		pp = tblp;
+		while ((cmdp = *pp) != NULL) {
+			if ((cmdp->cmdtype == CMDNORMAL &&
+			     cmdp->param.index >= firstchange)
+			 || (cmdp->cmdtype == CMDBUILTIN &&
+			     builtinloc >= firstchange)
+			) {
+				*pp = cmdp->next;
+				free(cmdp);
+			} else {
+				pp = &cmdp->next;
+			}
+		}
+	}
+	INT_ON;
+}
+
+/*
+ * Locate a command in the command hash table.  If "add" is nonzero,
+ * add the command to the table if it is not already present.  The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ *
+ * Interrupts must be off if called with add != 0.
+ */
+static struct tblentry **lastcmdentry;
+
+static struct tblentry *
+cmdlookup(const char *name, int add)
+{
+	unsigned int hashval;
+	const char *p;
+	struct tblentry *cmdp;
+	struct tblentry **pp;
+
+	p = name;
+	hashval = (unsigned char)*p << 4;
+	while (*p)
+		hashval += (unsigned char)*p++;
+	hashval &= 0x7FFF;
+	pp = &cmdtable[hashval % CMDTABLESIZE];
+	for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
+		if (strcmp(cmdp->cmdname, name) == 0)
+			break;
+		pp = &cmdp->next;
+	}
+	if (add && cmdp == NULL) {
+		cmdp = *pp = ckzalloc(sizeof(struct tblentry)
+				+ strlen(name)
+				/* + 1 - already done because
+				 * tblentry::cmdname is char[1] */);
+		/*cmdp->next = NULL; - ckzalloc did it */
+		cmdp->cmdtype = CMDUNKNOWN;
+		strcpy(cmdp->cmdname, name);
+	}
+	lastcmdentry = pp;
+	return cmdp;
+}
+
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+static void
+delete_cmd_entry(void)
+{
+	struct tblentry *cmdp;
+
+	INT_OFF;
+	cmdp = *lastcmdentry;
+	*lastcmdentry = cmdp->next;
+	if (cmdp->cmdtype == CMDFUNCTION)
+		freefunc(cmdp->param.func);
+	free(cmdp);
+	INT_ON;
+}
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name - except special builtins.
+ */
+static void
+addcmdentry(char *name, struct cmdentry *entry)
+{
+	struct tblentry *cmdp;
+
+	cmdp = cmdlookup(name, 1);
+	if (cmdp->cmdtype == CMDFUNCTION) {
+		freefunc(cmdp->param.func);
+	}
+	cmdp->cmdtype = entry->cmdtype;
+	cmdp->param = entry->u;
+	cmdp->rehash = 0;
+}
+
+static int FAST_FUNC
+hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+	int c;
+	struct cmdentry entry;
+	char *name;
+
+	if (nextopt("r") != '\0') {
+		clearcmdentry(0);
+		return 0;
+	}
+
+	if (*argptr == NULL) {
+		for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
+			for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
+				if (cmdp->cmdtype == CMDNORMAL)
+					printentry(cmdp);
+			}
+		}
+		return 0;
+	}
+
+	c = 0;
+	while ((name = *argptr) != NULL) {
+		cmdp = cmdlookup(name, 0);
+		if (cmdp != NULL
+		 && (cmdp->cmdtype == CMDNORMAL
+		     || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
+		) {
+			delete_cmd_entry();
+		}
+		find_command(name, &entry, DO_ERR, pathval());
+		if (entry.cmdtype == CMDUNKNOWN)
+			c = 1;
+		argptr++;
+	}
+	return c;
+}
+
+/*
+ * Called when a cd is done.  Marks all commands so the next time they
+ * are executed they will be rehashed.
+ */
+static void
+hashcd(void)
+{
+	struct tblentry **pp;
+	struct tblentry *cmdp;
+
+	for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
+		for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
+			if (cmdp->cmdtype == CMDNORMAL
+			 || (cmdp->cmdtype == CMDBUILTIN
+			     && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
+			     && builtinloc > 0)
+			) {
+				cmdp->rehash = 1;
+			}
+		}
+	}
+}
+
+/*
+ * Fix command hash table when PATH changed.
+ * Called before PATH is changed.  The argument is the new value of PATH;
+ * pathval() still returns the old value at this point.
+ * Called with interrupts off.
+ */
+static void FAST_FUNC
+changepath(const char *new)
+{
+	const char *old;
+	int firstchange;
+	int idx;
+	int idx_bltin;
+
+	old = pathval();
+	firstchange = 9999;     /* assume no change */
+	idx = 0;
+	idx_bltin = -1;
+	for (;;) {
+		if (*old != *new) {
+			firstchange = idx;
+			if ((*old == '\0' && *new == ':')
+			 || (*old == ':' && *new == '\0')
+			) {
+				firstchange++;
+			}
+			old = new;      /* ignore subsequent differences */
+		}
+		if (*new == '\0')
+			break;
+		if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
+			idx_bltin = idx;
+		if (*new == ':')
+			idx++;
+		new++;
+		old++;
+	}
+	if (builtinloc < 0 && idx_bltin >= 0)
+		builtinloc = idx_bltin;             /* zap builtins */
+	if (builtinloc >= 0 && idx_bltin < 0)
+		firstchange = 0;
+	clearcmdentry(firstchange);
+	builtinloc = idx_bltin;
+}
+
+#define TEOF 0
+#define TNL 1
+#define TREDIR 2
+#define TWORD 3
+#define TSEMI 4
+#define TBACKGND 5
+#define TAND 6
+#define TOR 7
+#define TPIPE 8
+#define TLP 9
+#define TRP 10
+#define TENDCASE 11
+#define TENDBQUOTE 12
+#define TNOT 13
+#define TCASE 14
+#define TDO 15
+#define TDONE 16
+#define TELIF 17
+#define TELSE 18
+#define TESAC 19
+#define TFI 20
+#define TFOR 21
+#define TIF 22
+#define TIN 23
+#define TTHEN 24
+#define TUNTIL 25
+#define TWHILE 26
+#define TBEGIN 27
+#define TEND 28
+typedef smallint token_id_t;
+
+/* first char is indicating which tokens mark the end of a list */
+static const char *const tokname_array[] = {
+	"\1end of file",
+	"\0newline",
+	"\0redirection",
+	"\0word",
+	"\0;",
+	"\0&",
+	"\0&&",
+	"\0||",
+	"\0|",
+	"\0(",
+	"\1)",
+	"\1;;",
+	"\1`",
+#define KWDOFFSET 13
+	/* the following are keywords */
+	"\0!",
+	"\0case",
+	"\1do",
+	"\1done",
+	"\1elif",
+	"\1else",
+	"\1esac",
+	"\1fi",
+	"\0for",
+	"\0if",
+	"\0in",
+	"\1then",
+	"\0until",
+	"\0while",
+	"\0{",
+	"\1}",
+};
+
+/* Wrapper around strcmp for qsort/bsearch/... */
+static int
+pstrcmp(const void *a, const void *b)
+{
+	return strcmp((char*) a, (*(char**) b) + 1);
+}
+
+static const char *const *
+findkwd(const char *s)
+{
+	return bsearch(s, tokname_array + KWDOFFSET,
+			ARRAY_SIZE(tokname_array) - KWDOFFSET,
+			sizeof(tokname_array[0]), pstrcmp);
+}
+
+/*
+ * Locate and print what a word is...
+ */
+static int
+describe_command(char *command, int describe_command_verbose)
+{
+	struct cmdentry entry;
+	struct tblentry *cmdp;
+#if ENABLE_ASH_ALIAS
+	const struct alias *ap;
+#endif
+	const char *path = pathval();
+
+	if (describe_command_verbose) {
+		out1str(command);
+	}
+
+	/* First look at the keywords */
+	if (findkwd(command)) {
+		out1str(describe_command_verbose ? " is a shell keyword" : command);
+		goto out;
+	}
+
+#if ENABLE_ASH_ALIAS
+	/* Then look at the aliases */
+	ap = lookupalias(command, 0);
+	if (ap != NULL) {
+		if (!describe_command_verbose) {
+			out1str("alias ");
+			printalias(ap);
+			return 0;
+		}
+		out1fmt(" is an alias for %s", ap->val);
+		goto out;
+	}
+#endif
+	/* Then check if it is a tracked alias */
+	cmdp = cmdlookup(command, 0);
+	if (cmdp != NULL) {
+		entry.cmdtype = cmdp->cmdtype;
+		entry.u = cmdp->param;
+	} else {
+		/* Finally use brute force */
+		find_command(command, &entry, DO_ABS, path);
+	}
+
+	switch (entry.cmdtype) {
+	case CMDNORMAL: {
+		int j = entry.u.index;
+		char *p;
+		if (j < 0) {
+			p = command;
+		} else {
+			do {
+				p = path_advance(&path, command);
+				stunalloc(p);
+			} while (--j >= 0);
+		}
+		if (describe_command_verbose) {
+			out1fmt(" is%s %s",
+				(cmdp ? " a tracked alias for" : nullstr), p
+			);
+		} else {
+			out1str(p);
+		}
+		break;
+	}
+
+	case CMDFUNCTION:
+		if (describe_command_verbose) {
+			out1str(" is a shell function");
+		} else {
+			out1str(command);
+		}
+		break;
+
+	case CMDBUILTIN:
+		if (describe_command_verbose) {
+			out1fmt(" is a %sshell builtin",
+				IS_BUILTIN_SPECIAL(entry.u.cmd) ?
+					"special " : nullstr
+			);
+		} else {
+			out1str(command);
+		}
+		break;
+
+	default:
+		if (describe_command_verbose) {
+			out1str(": not found\n");
+		}
+		return 127;
+	}
+ out:
+	out1str("\n");
+	return 0;
+}
+
+static int FAST_FUNC
+typecmd(int argc UNUSED_PARAM, char **argv)
+{
+	int i = 1;
+	int err = 0;
+	int verbose = 1;
+
+	/* type -p ... ? (we don't bother checking for 'p') */
+	if (argv[1] && argv[1][0] == '-') {
+		i++;
+		verbose = 0;
+	}
+	while (argv[i]) {
+		err |= describe_command(argv[i++], verbose);
+	}
+	return err;
+}
+
+#if ENABLE_ASH_CMDCMD
+static int FAST_FUNC
+commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	int c;
+	enum {
+		VERIFY_BRIEF = 1,
+		VERIFY_VERBOSE = 2,
+	} verify = 0;
+
+	while ((c = nextopt("pvV")) != '\0')
+		if (c == 'V')
+			verify |= VERIFY_VERBOSE;
+		else if (c == 'v')
+			verify |= VERIFY_BRIEF;
+#if DEBUG
+		else if (c != 'p')
+			abort();
+#endif
+	/* Mimic bash: just "command -v" doesn't complain, it's a nop */
+	if (verify && (*argptr != NULL)) {
+		return describe_command(*argptr, verify - VERIFY_BRIEF);
+	}
+
+	return 0;
+}
+#endif
+
+
+/* ============ eval.c */
+
+static int funcblocksize;       /* size of structures in function */
+static int funcstringsize;      /* size of strings in node */
+static void *funcblock;         /* block to allocate function from */
+static char *funcstring;        /* block to allocate strings from */
+
+/* flags in argument to evaltree */
+#define EV_EXIT    01           /* exit after evaluating tree */
+#define EV_TESTED  02           /* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04           /* command executing within back quotes */
+
+static const uint8_t nodesize[N_NUMBER] = {
+	[NCMD     ] = SHELL_ALIGN(sizeof(struct ncmd)),
+	[NPIPE    ] = SHELL_ALIGN(sizeof(struct npipe)),
+	[NREDIR   ] = SHELL_ALIGN(sizeof(struct nredir)),
+	[NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
+	[NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
+	[NAND     ] = SHELL_ALIGN(sizeof(struct nbinary)),
+	[NOR      ] = SHELL_ALIGN(sizeof(struct nbinary)),
+	[NSEMI    ] = SHELL_ALIGN(sizeof(struct nbinary)),
+	[NIF      ] = SHELL_ALIGN(sizeof(struct nif)),
+	[NWHILE   ] = SHELL_ALIGN(sizeof(struct nbinary)),
+	[NUNTIL   ] = SHELL_ALIGN(sizeof(struct nbinary)),
+	[NFOR     ] = SHELL_ALIGN(sizeof(struct nfor)),
+	[NCASE    ] = SHELL_ALIGN(sizeof(struct ncase)),
+	[NCLIST   ] = SHELL_ALIGN(sizeof(struct nclist)),
+	[NDEFUN   ] = SHELL_ALIGN(sizeof(struct narg)),
+	[NARG     ] = SHELL_ALIGN(sizeof(struct narg)),
+	[NTO      ] = SHELL_ALIGN(sizeof(struct nfile)),
+#if ENABLE_ASH_BASH_COMPAT
+	[NTO2     ] = SHELL_ALIGN(sizeof(struct nfile)),
+#endif
+	[NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
+	[NFROM    ] = SHELL_ALIGN(sizeof(struct nfile)),
+	[NFROMTO  ] = SHELL_ALIGN(sizeof(struct nfile)),
+	[NAPPEND  ] = SHELL_ALIGN(sizeof(struct nfile)),
+	[NTOFD    ] = SHELL_ALIGN(sizeof(struct ndup)),
+	[NFROMFD  ] = SHELL_ALIGN(sizeof(struct ndup)),
+	[NHERE    ] = SHELL_ALIGN(sizeof(struct nhere)),
+	[NXHERE   ] = SHELL_ALIGN(sizeof(struct nhere)),
+	[NNOT     ] = SHELL_ALIGN(sizeof(struct nnot)),
+};
+
+static void calcsize(union node *n);
+
+static void
+sizenodelist(struct nodelist *lp)
+{
+	while (lp) {
+		funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
+		calcsize(lp->n);
+		lp = lp->next;
+	}
+}
+
+static void
+calcsize(union node *n)
+{
+	if (n == NULL)
+		return;
+	funcblocksize += nodesize[n->type];
+	switch (n->type) {
+	case NCMD:
+		calcsize(n->ncmd.redirect);
+		calcsize(n->ncmd.args);
+		calcsize(n->ncmd.assign);
+		break;
+	case NPIPE:
+		sizenodelist(n->npipe.cmdlist);
+		break;
+	case NREDIR:
+	case NBACKGND:
+	case NSUBSHELL:
+		calcsize(n->nredir.redirect);
+		calcsize(n->nredir.n);
+		break;
+	case NAND:
+	case NOR:
+	case NSEMI:
+	case NWHILE:
+	case NUNTIL:
+		calcsize(n->nbinary.ch2);
+		calcsize(n->nbinary.ch1);
+		break;
+	case NIF:
+		calcsize(n->nif.elsepart);
+		calcsize(n->nif.ifpart);
+		calcsize(n->nif.test);
+		break;
+	case NFOR:
+		funcstringsize += strlen(n->nfor.var) + 1;
+		calcsize(n->nfor.body);
+		calcsize(n->nfor.args);
+		break;
+	case NCASE:
+		calcsize(n->ncase.cases);
+		calcsize(n->ncase.expr);
+		break;
+	case NCLIST:
+		calcsize(n->nclist.body);
+		calcsize(n->nclist.pattern);
+		calcsize(n->nclist.next);
+		break;
+	case NDEFUN:
+	case NARG:
+		sizenodelist(n->narg.backquote);
+		funcstringsize += strlen(n->narg.text) + 1;
+		calcsize(n->narg.next);
+		break;
+	case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+	case NTO2:
+#endif
+	case NCLOBBER:
+	case NFROM:
+	case NFROMTO:
+	case NAPPEND:
+		calcsize(n->nfile.fname);
+		calcsize(n->nfile.next);
+		break;
+	case NTOFD:
+	case NFROMFD:
+		calcsize(n->ndup.vname);
+		calcsize(n->ndup.next);
+	break;
+	case NHERE:
+	case NXHERE:
+		calcsize(n->nhere.doc);
+		calcsize(n->nhere.next);
+		break;
+	case NNOT:
+		calcsize(n->nnot.com);
+		break;
+	};
+}
+
+static char *
+nodeckstrdup(char *s)
+{
+	char *rtn = funcstring;
+
+	strcpy(funcstring, s);
+	funcstring += strlen(s) + 1;
+	return rtn;
+}
+
+static union node *copynode(union node *);
+
+static struct nodelist *
+copynodelist(struct nodelist *lp)
+{
+	struct nodelist *start;
+	struct nodelist **lpp;
+
+	lpp = &start;
+	while (lp) {
+		*lpp = funcblock;
+		funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
+		(*lpp)->n = copynode(lp->n);
+		lp = lp->next;
+		lpp = &(*lpp)->next;
+	}
+	*lpp = NULL;
+	return start;
+}
+
+static union node *
+copynode(union node *n)
+{
+	union node *new;
+
+	if (n == NULL)
+		return NULL;
+	new = funcblock;
+	funcblock = (char *) funcblock + nodesize[n->type];
+
+	switch (n->type) {
+	case NCMD:
+		new->ncmd.redirect = copynode(n->ncmd.redirect);
+		new->ncmd.args = copynode(n->ncmd.args);
+		new->ncmd.assign = copynode(n->ncmd.assign);
+		break;
+	case NPIPE:
+		new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
+		new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
+		break;
+	case NREDIR:
+	case NBACKGND:
+	case NSUBSHELL:
+		new->nredir.redirect = copynode(n->nredir.redirect);
+		new->nredir.n = copynode(n->nredir.n);
+		break;
+	case NAND:
+	case NOR:
+	case NSEMI:
+	case NWHILE:
+	case NUNTIL:
+		new->nbinary.ch2 = copynode(n->nbinary.ch2);
+		new->nbinary.ch1 = copynode(n->nbinary.ch1);
+		break;
+	case NIF:
+		new->nif.elsepart = copynode(n->nif.elsepart);
+		new->nif.ifpart = copynode(n->nif.ifpart);
+		new->nif.test = copynode(n->nif.test);
+		break;
+	case NFOR:
+		new->nfor.var = nodeckstrdup(n->nfor.var);
+		new->nfor.body = copynode(n->nfor.body);
+		new->nfor.args = copynode(n->nfor.args);
+		break;
+	case NCASE:
+		new->ncase.cases = copynode(n->ncase.cases);
+		new->ncase.expr = copynode(n->ncase.expr);
+		break;
+	case NCLIST:
+		new->nclist.body = copynode(n->nclist.body);
+		new->nclist.pattern = copynode(n->nclist.pattern);
+		new->nclist.next = copynode(n->nclist.next);
+		break;
+	case NDEFUN:
+	case NARG:
+		new->narg.backquote = copynodelist(n->narg.backquote);
+		new->narg.text = nodeckstrdup(n->narg.text);
+		new->narg.next = copynode(n->narg.next);
+		break;
+	case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+	case NTO2:
+#endif
+	case NCLOBBER:
+	case NFROM:
+	case NFROMTO:
+	case NAPPEND:
+		new->nfile.fname = copynode(n->nfile.fname);
+		new->nfile.fd = n->nfile.fd;
+		new->nfile.next = copynode(n->nfile.next);
+		break;
+	case NTOFD:
+	case NFROMFD:
+		new->ndup.vname = copynode(n->ndup.vname);
+		new->ndup.dupfd = n->ndup.dupfd;
+		new->ndup.fd = n->ndup.fd;
+		new->ndup.next = copynode(n->ndup.next);
+		break;
+	case NHERE:
+	case NXHERE:
+		new->nhere.doc = copynode(n->nhere.doc);
+		new->nhere.fd = n->nhere.fd;
+		new->nhere.next = copynode(n->nhere.next);
+		break;
+	case NNOT:
+		new->nnot.com = copynode(n->nnot.com);
+		break;
+	};
+	new->type = n->type;
+	return new;
+}
+
+/*
+ * Make a copy of a parse tree.
+ */
+static struct funcnode *
+copyfunc(union node *n)
+{
+	struct funcnode *f;
+	size_t blocksize;
+
+	funcblocksize = offsetof(struct funcnode, n);
+	funcstringsize = 0;
+	calcsize(n);
+	blocksize = funcblocksize;
+	f = ckmalloc(blocksize + funcstringsize);
+	funcblock = (char *) f + offsetof(struct funcnode, n);
+	funcstring = (char *) f + blocksize;
+	copynode(n);
+	f->count = 0;
+	return f;
+}
+
+/*
+ * Define a shell function.
+ */
+static void
+defun(char *name, union node *func)
+{
+	struct cmdentry entry;
+
+	INT_OFF;
+	entry.cmdtype = CMDFUNCTION;
+	entry.u.func = copyfunc(func);
+	addcmdentry(name, &entry);
+	INT_ON;
+}
+
+/* Reasons for skipping commands (see comment on breakcmd routine) */
+#define SKIPBREAK      (1 << 0)
+#define SKIPCONT       (1 << 1)
+#define SKIPFUNC       (1 << 2)
+#define SKIPFILE       (1 << 3)
+#define SKIPEVAL       (1 << 4)
+static smallint evalskip;       /* set to SKIPxxx if we are skipping commands */
+static int skipcount;           /* number of levels to skip */
+static int funcnest;            /* depth of function calls */
+static int loopnest;            /* current loop nesting level */
+
+/* Forward decl way out to parsing code - dotrap needs it */
+static int evalstring(char *s, int mask);
+
+/* Called to execute a trap.
+ * Single callsite - at the end of evaltree().
+ * 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?]
+ */
+static int
+dotrap(void)
+{
+	uint8_t *g;
+	int sig;
+	uint8_t savestatus;
+
+	savestatus = exitstatus;
+	pending_sig = 0;
+	xbarrier();
+
+	TRACE(("dotrap entered\n"));
+	for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
+		int want_exexit;
+		char *t;
+
+		if (*g == 0)
+			continue;
+		t = trap[sig];
+		/* non-trapped SIGINT is handled separately by raise_interrupt,
+		 * don't upset it by resetting gotsig[SIGINT-1] */
+		if (sig == SIGINT && !t)
+			continue;
+
+		TRACE(("sig %d is active, will run handler '%s'\n", sig, t));
+		*g = 0;
+		if (!t)
+			continue;
+		want_exexit = evalstring(t, SKIPEVAL);
+		exitstatus = savestatus;
+		if (want_exexit) {
+			TRACE(("dotrap returns %d\n", want_exexit));
+			return want_exexit;
+		}
+	}
+
+	TRACE(("dotrap returns 0\n"));
+	return 0;
+}
+
+/* forward declarations - evaluation is fairly recursive business... */
+static void evalloop(union node *, int);
+static void evalfor(union node *, int);
+static void evalcase(union node *, int);
+static void evalsubshell(union node *, int);
+static void expredir(union node *);
+static void evalpipe(union node *, int);
+static void evalcommand(union node *, int);
+static int evalbltin(const struct builtincmd *, int, char **);
+static void prehash(union node *);
+
+/*
+ * Evaluate a parse tree.  The value is left in the global variable
+ * exitstatus.
+ */
+static void
+evaltree(union node *n, int flags)
+{
+	struct jmploc *volatile savehandler = exception_handler;
+	struct jmploc jmploc;
+	int checkexit = 0;
+	void (*evalfn)(union node *, int);
+	int status;
+	int int_level;
+
+	SAVE_INT(int_level);
+
+	if (n == NULL) {
+		TRACE(("evaltree(NULL) called\n"));
+		goto out1;
+	}
+	TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
+
+	exception_handler = &jmploc;
+	{
+		int err = setjmp(jmploc.loc);
+		if (err) {
+			/* if it was a signal, check for trap handlers */
+			if (exception_type == EXSIG) {
+				TRACE(("exception %d (EXSIG) in evaltree, err=%d\n",
+						exception_type, err));
+				goto out;
+			}
+			/* continue on the way out */
+			TRACE(("exception %d in evaltree, propagating err=%d\n",
+					exception_type, err));
+			exception_handler = savehandler;
+			longjmp(exception_handler->loc, err);
+		}
+	}
+
+	switch (n->type) {
+	default:
+#if DEBUG
+		out1fmt("Node type = %d\n", n->type);
+		fflush_all();
+		break;
+#endif
+	case NNOT:
+		evaltree(n->nnot.com, EV_TESTED);
+		status = !exitstatus;
+		goto setstatus;
+	case NREDIR:
+		expredir(n->nredir.redirect);
+		status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
+		if (!status) {
+			evaltree(n->nredir.n, flags & EV_TESTED);
+			status = exitstatus;
+		}
+		popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */);
+		goto setstatus;
+	case NCMD:
+		evalfn = evalcommand;
+ checkexit:
+		if (eflag && !(flags & EV_TESTED))
+			checkexit = ~0;
+		goto calleval;
+	case NFOR:
+		evalfn = evalfor;
+		goto calleval;
+	case NWHILE:
+	case NUNTIL:
+		evalfn = evalloop;
+		goto calleval;
+	case NSUBSHELL:
+	case NBACKGND:
+		evalfn = evalsubshell;
+		goto calleval;
+	case NPIPE:
+		evalfn = evalpipe;
+		goto checkexit;
+	case NCASE:
+		evalfn = evalcase;
+		goto calleval;
+	case NAND:
+	case NOR:
+	case NSEMI: {
+
+#if NAND + 1 != NOR
+#error NAND + 1 != NOR
+#endif
+#if NOR + 1 != NSEMI
+#error NOR + 1 != NSEMI
+#endif
+		unsigned is_or = n->type - NAND;
+		evaltree(
+			n->nbinary.ch1,
+			(flags | ((is_or >> 1) - 1)) & EV_TESTED
+		);
+		if (!exitstatus == is_or)
+			break;
+		if (!evalskip) {
+			n = n->nbinary.ch2;
+ evaln:
+			evalfn = evaltree;
+ calleval:
+			evalfn(n, flags);
+			break;
+		}
+		break;
+	}
+	case NIF:
+		evaltree(n->nif.test, EV_TESTED);
+		if (evalskip)
+			break;
+		if (exitstatus == 0) {
+			n = n->nif.ifpart;
+			goto evaln;
+		}
+		if (n->nif.elsepart) {
+			n = n->nif.elsepart;
+			goto evaln;
+		}
+		goto success;
+	case NDEFUN:
+		defun(n->narg.text, n->narg.next);
+ success:
+		status = 0;
+ setstatus:
+		exitstatus = status;
+		break;
+	}
+
+ 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;
+
+	if (flags & EV_EXIT) {
+ exexit:
+		raise_exception(EXEXIT);
+	}
+
+	RESTORE_INT(int_level);
+	TRACE(("leaving evaltree (no interrupts)\n"));
+}
+
+#if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3)
+static
+#endif
+void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__));
+
+static void
+evalloop(union node *n, int flags)
+{
+	int status;
+
+	loopnest++;
+	status = 0;
+	flags &= EV_TESTED;
+	for (;;) {
+		int i;
+
+		evaltree(n->nbinary.ch1, EV_TESTED);
+		if (evalskip) {
+ skipping:
+			if (evalskip == SKIPCONT && --skipcount <= 0) {
+				evalskip = 0;
+				continue;
+			}
+			if (evalskip == SKIPBREAK && --skipcount <= 0)
+				evalskip = 0;
+			break;
+		}
+		i = exitstatus;
+		if (n->type != NWHILE)
+			i = !i;
+		if (i != 0)
+			break;
+		evaltree(n->nbinary.ch2, flags);
+		status = exitstatus;
+		if (evalskip)
+			goto skipping;
+	}
+	loopnest--;
+	exitstatus = status;
+}
+
+static void
+evalfor(union node *n, int flags)
+{
+	struct arglist arglist;
+	union node *argp;
+	struct strlist *sp;
+	struct stackmark smark;
+
+	setstackmark(&smark);
+	arglist.list = NULL;
+	arglist.lastp = &arglist.list;
+	for (argp = n->nfor.args; argp; argp = argp->narg.next) {
+		expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
+		/* XXX */
+		if (evalskip)
+			goto out;
+	}
+	*arglist.lastp = NULL;
+
+	exitstatus = 0;
+	loopnest++;
+	flags &= EV_TESTED;
+	for (sp = arglist.list; sp; sp = sp->next) {
+		setvar(n->nfor.var, sp->text, 0);
+		evaltree(n->nfor.body, flags);
+		if (evalskip) {
+			if (evalskip == SKIPCONT && --skipcount <= 0) {
+				evalskip = 0;
+				continue;
+			}
+			if (evalskip == SKIPBREAK && --skipcount <= 0)
+				evalskip = 0;
+			break;
+		}
+	}
+	loopnest--;
+ out:
+	popstackmark(&smark);
+}
+
+static void
+evalcase(union node *n, int flags)
+{
+	union node *cp;
+	union node *patp;
+	struct arglist arglist;
+	struct stackmark smark;
+
+	setstackmark(&smark);
+	arglist.list = NULL;
+	arglist.lastp = &arglist.list;
+	expandarg(n->ncase.expr, &arglist, EXP_TILDE);
+	exitstatus = 0;
+	for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
+		for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
+			if (casematch(patp, arglist.list->text)) {
+				if (evalskip == 0) {
+					evaltree(cp->nclist.body, flags);
+				}
+				goto out;
+			}
+		}
+	}
+ out:
+	popstackmark(&smark);
+}
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+static void
+evalsubshell(union node *n, int flags)
+{
+	struct job *jp;
+	int backgnd = (n->type == NBACKGND);
+	int status;
+
+	expredir(n->nredir.redirect);
+	if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
+		goto nofork;
+	INT_OFF;
+	jp = makejob(/*n,*/ 1);
+	if (forkshell(jp, n, backgnd) == 0) {
+		/* child */
+		INT_ON;
+		flags |= EV_EXIT;
+		if (backgnd)
+			flags &= ~EV_TESTED;
+ nofork:
+		redirect(n->nredir.redirect, 0);
+		evaltreenr(n->nredir.n, flags);
+		/* never returns */
+	}
+	status = 0;
+	if (!backgnd)
+		status = waitforjob(jp);
+	exitstatus = status;
+	INT_ON;
+}
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+static void fixredir(union node *, const char *, int);
+static void
+expredir(union node *n)
+{
+	union node *redir;
+
+	for (redir = n; redir; redir = redir->nfile.next) {
+		struct arglist fn;
+
+		fn.list = NULL;
+		fn.lastp = &fn.list;
+		switch (redir->type) {
+		case NFROMTO:
+		case NFROM:
+		case NTO:
+#if ENABLE_ASH_BASH_COMPAT
+		case NTO2:
+#endif
+		case NCLOBBER:
+		case NAPPEND:
+			expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+#if ENABLE_ASH_BASH_COMPAT
+ store_expfname:
+#endif
+			redir->nfile.expfname = fn.list->text;
+			break;
+		case NFROMFD:
+		case NTOFD: /* >& */
+			if (redir->ndup.vname) {
+				expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+				if (fn.list == NULL)
+					ash_msg_and_raise_error("redir error");
+#if ENABLE_ASH_BASH_COMPAT
+//FIXME: we used expandarg with different args!
+				if (!isdigit_str9(fn.list->text)) {
+					/* >&file, not >&fd */
+					if (redir->nfile.fd != 1) /* 123>&file - BAD */
+						ash_msg_and_raise_error("redir error");
+					redir->type = NTO2;
+					goto store_expfname;
+				}
+#endif
+				fixredir(redir, fn.list->text, 1);
+			}
+			break;
+		}
+	}
+}
+
+/*
+ * Evaluate a pipeline.  All the processes in the pipeline are children
+ * of the process creating the pipeline.  (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
+ */
+static void
+evalpipe(union node *n, int flags)
+{
+	struct job *jp;
+	struct nodelist *lp;
+	int pipelen;
+	int prevfd;
+	int pip[2];
+
+	TRACE(("evalpipe(0x%lx) called\n", (long)n));
+	pipelen = 0;
+	for (lp = n->npipe.cmdlist; lp; lp = lp->next)
+		pipelen++;
+	flags |= EV_EXIT;
+	INT_OFF;
+	jp = makejob(/*n,*/ pipelen);
+	prevfd = -1;
+	for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
+		prehash(lp->n);
+		pip[1] = -1;
+		if (lp->next) {
+			if (pipe(pip) < 0) {
+				close(prevfd);
+				ash_msg_and_raise_error("pipe call failed");
+			}
+		}
+		if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
+			INT_ON;
+			if (pip[1] >= 0) {
+				close(pip[0]);
+			}
+			if (prevfd > 0) {
+				dup2(prevfd, 0);
+				close(prevfd);
+			}
+			if (pip[1] > 1) {
+				dup2(pip[1], 1);
+				close(pip[1]);
+			}
+			evaltreenr(lp->n, flags);
+			/* never returns */
+		}
+		if (prevfd >= 0)
+			close(prevfd);
+		prevfd = pip[0];
+		/* Don't want to trigger debugging */
+		if (pip[1] != -1)
+			close(pip[1]);
+	}
+	if (n->npipe.pipe_backgnd == 0) {
+		exitstatus = waitforjob(jp);
+		TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
+	}
+	INT_ON;
+}
+
+/*
+ * Controls whether the shell is interactive or not.
+ */
+static void
+setinteractive(int on)
+{
+	static smallint is_interactive;
+
+	if (++on == is_interactive)
+		return;
+	is_interactive = on;
+	setsignal(SIGINT);
+	setsignal(SIGQUIT);
+	setsignal(SIGTERM);
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+	if (is_interactive > 1) {
+		/* Looks like they want an interactive shell */
+		static smallint did_banner;
+
+		if (!did_banner) {
+			/* note: ash and hush share this string */
+			out1fmt("\n\n%s %s\n"
+				"Enter 'help' for a list of built-in commands."
+				"\n\n",
+				bb_banner,
+				"built-in shell (ash)"
+			);
+			did_banner = 1;
+		}
+	}
+#endif
+}
+
+static void
+optschanged(void)
+{
+#if DEBUG
+	opentrace();
+#endif
+	setinteractive(iflag);
+	setjobctl(mflag);
+#if ENABLE_FEATURE_EDITING_VI
+	if (viflag)
+		line_input_state->flags |= VI_MODE;
+	else
+		line_input_state->flags &= ~VI_MODE;
+#else
+	viflag = 0; /* forcibly keep the option off */
+#endif
+}
+
+static struct localvar *localvars;
+
+/*
+ * Called after a function returns.
+ * Interrupts must be off.
+ */
+static void
+poplocalvars(void)
+{
+	struct localvar *lvp;
+	struct var *vp;
+
+	while ((lvp = localvars) != NULL) {
+		localvars = lvp->next;
+		vp = lvp->vp;
+		TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
+		if (vp == NULL) {       /* $- saved */
+			memcpy(optlist, lvp->text, sizeof(optlist));
+			free((char*)lvp->text);
+			optschanged();
+		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+			unsetvar(vp->var_text);
+		} else {
+			if (vp->var_func)
+				vp->var_func(var_end(lvp->text));
+			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+				free((char*)vp->var_text);
+			vp->flags = lvp->flags;
+			vp->var_text = lvp->text;
+		}
+		free(lvp);
+	}
+}
+
+static int
+evalfun(struct funcnode *func, int argc, char **argv, int flags)
+{
+	volatile struct shparam saveparam;
+	struct localvar *volatile savelocalvars;
+	struct jmploc *volatile savehandler;
+	struct jmploc jmploc;
+	int e;
+
+	saveparam = shellparam;
+	savelocalvars = localvars;
+	e = setjmp(jmploc.loc);
+	if (e) {
+		goto funcdone;
+	}
+	INT_OFF;
+	savehandler = exception_handler;
+	exception_handler = &jmploc;
+	localvars = NULL;
+	shellparam.malloced = 0;
+	func->count++;
+	funcnest++;
+	INT_ON;
+	shellparam.nparam = argc - 1;
+	shellparam.p = argv + 1;
+#if ENABLE_ASH_GETOPTS
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
+#endif
+	evaltree(&func->n, flags & EV_TESTED);
+ funcdone:
+	INT_OFF;
+	funcnest--;
+	freefunc(func);
+	poplocalvars();
+	localvars = savelocalvars;
+	freeparam(&shellparam);
+	shellparam = saveparam;
+	exception_handler = savehandler;
+	INT_ON;
+	evalskip &= ~SKIPFUNC;
+	return e;
+}
+
+#if ENABLE_ASH_CMDCMD
+static char **
+parse_command_args(char **argv, const char **path)
+{
+	char *cp, c;
+
+	for (;;) {
+		cp = *++argv;
+		if (!cp)
+			return 0;
+		if (*cp++ != '-')
+			break;
+		c = *cp++;
+		if (!c)
+			break;
+		if (c == '-' && !*cp) {
+			argv++;
+			break;
+		}
+		do {
+			switch (c) {
+			case 'p':
+				*path = bb_default_path;
+				break;
+			default:
+				/* run 'typecmd' for other options */
+				return 0;
+			}
+			c = *cp++;
+		} while (c);
+	}
+	return argv;
+}
+#endif
+
+/*
+ * Make a variable a local variable.  When a variable is made local, it's
+ * value and flags are saved in a localvar structure.  The saved values
+ * will be restored when the shell function returns.  We handle the name
+ * "-" as a special case.
+ */
+static void
+mklocal(char *name)
+{
+	struct localvar *lvp;
+	struct var **vpp;
+	struct var *vp;
+
+	INT_OFF;
+	lvp = ckzalloc(sizeof(struct localvar));
+	if (LONE_DASH(name)) {
+		char *p;
+		p = ckmalloc(sizeof(optlist));
+		lvp->text = memcpy(p, optlist, sizeof(optlist));
+		vp = NULL;
+	} else {
+		char *eq;
+
+		vpp = hashvar(name);
+		vp = *findvar(vpp, name);
+		eq = strchr(name, '=');
+		if (vp == NULL) {
+			if (eq)
+				setvareq(name, VSTRFIXED);
+			else
+				setvar(name, NULL, VSTRFIXED);
+			vp = *vpp;      /* the new variable */
+			lvp->flags = VUNSET;
+		} else {
+			lvp->text = vp->var_text;
+			lvp->flags = vp->flags;
+			vp->flags |= VSTRFIXED|VTEXTFIXED;
+			if (eq)
+				setvareq(name, 0);
+		}
+	}
+	lvp->vp = vp;
+	lvp->next = localvars;
+	localvars = lvp;
+	INT_ON;
+}
+
+/*
+ * The "local" command.
+ */
+static int FAST_FUNC
+localcmd(int argc UNUSED_PARAM, char **argv)
+{
+	char *name;
+
+	argv = argptr;
+	while ((name = *argv++) != NULL) {
+		mklocal(name);
+	}
+	return 0;
+}
+
+static int FAST_FUNC
+falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	return 1;
+}
+
+static int FAST_FUNC
+truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	return 0;
+}
+
+static int FAST_FUNC
+execcmd(int argc UNUSED_PARAM, char **argv)
+{
+	if (argv[1]) {
+		iflag = 0;              /* exit on error */
+		mflag = 0;
+		optschanged();
+		shellexec(argv + 1, pathval(), 0);
+	}
+	return 0;
+}
+
+/*
+ * The return command.
+ */
+static int FAST_FUNC
+returncmd(int argc UNUSED_PARAM, char **argv)
+{
+	/*
+	 * If called outside a function, do what ksh does;
+	 * skip the rest of the file.
+	 */
+	evalskip = funcnest ? SKIPFUNC : SKIPFILE;
+	return argv[1] ? number(argv[1]) : exitstatus;
+}
+
+/* Forward declarations for builtintab[] */
+static int breakcmd(int, char **) FAST_FUNC;
+static int dotcmd(int, char **) FAST_FUNC;
+static int evalcmd(int, char **) FAST_FUNC;
+static int exitcmd(int, char **) FAST_FUNC;
+static int exportcmd(int, char **) FAST_FUNC;
+#if ENABLE_ASH_GETOPTS
+static int getoptscmd(int, char **) FAST_FUNC;
+#endif
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+static int helpcmd(int, char **) FAST_FUNC;
+#endif
+#if ENABLE_SH_MATH_SUPPORT
+static int letcmd(int, char **) FAST_FUNC;
+#endif
+static int readcmd(int, char **) FAST_FUNC;
+static int setcmd(int, char **) FAST_FUNC;
+static int shiftcmd(int, char **) FAST_FUNC;
+static int timescmd(int, char **) FAST_FUNC;
+static int trapcmd(int, char **) FAST_FUNC;
+static int umaskcmd(int, char **) FAST_FUNC;
+static int unsetcmd(int, char **) FAST_FUNC;
+static int ulimitcmd(int, char **) FAST_FUNC;
+
+#define BUILTIN_NOSPEC          "0"
+#define BUILTIN_SPECIAL         "1"
+#define BUILTIN_REGULAR         "2"
+#define BUILTIN_SPEC_REG        "3"
+#define BUILTIN_ASSIGN          "4"
+#define BUILTIN_SPEC_ASSG       "5"
+#define BUILTIN_REG_ASSG        "6"
+#define BUILTIN_SPEC_REG_ASSG   "7"
+
+/* Stubs for calling non-FAST_FUNC's */
+#if ENABLE_ASH_BUILTIN_ECHO
+static int FAST_FUNC echocmd(int argc, char **argv)   { return echo_main(argc, argv); }
+#endif
+#if ENABLE_ASH_BUILTIN_PRINTF
+static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
+#endif
+#if ENABLE_ASH_BUILTIN_TEST
+static int FAST_FUNC testcmd(int argc, char **argv)   { return test_main(argc, argv); }
+#endif
+
+/* Keep these in proper order since it is searched via bsearch() */
+static const struct builtincmd builtintab[] = {
+	{ BUILTIN_SPEC_REG      "."       , dotcmd     },
+	{ BUILTIN_SPEC_REG      ":"       , truecmd    },
+#if ENABLE_ASH_BUILTIN_TEST
+	{ BUILTIN_REGULAR       "["       , testcmd    },
+#if ENABLE_ASH_BASH_COMPAT
+	{ BUILTIN_REGULAR       "[["      , testcmd    },
+#endif
+#endif
+#if ENABLE_ASH_ALIAS
+	{ BUILTIN_REG_ASSG      "alias"   , aliascmd   },
+#endif
+#if JOBS
+	{ BUILTIN_REGULAR       "bg"      , fg_bgcmd   },
+#endif
+	{ BUILTIN_SPEC_REG      "break"   , breakcmd   },
+	{ BUILTIN_REGULAR       "cd"      , cdcmd      },
+	{ BUILTIN_NOSPEC        "chdir"   , cdcmd      },
+#if ENABLE_ASH_CMDCMD
+	{ BUILTIN_REGULAR       "command" , commandcmd },
+#endif
+	{ BUILTIN_SPEC_REG      "continue", breakcmd   },
+#if ENABLE_ASH_BUILTIN_ECHO
+	{ BUILTIN_REGULAR       "echo"    , echocmd    },
+#endif
+	{ BUILTIN_SPEC_REG      "eval"    , evalcmd    },
+	{ BUILTIN_SPEC_REG      "exec"    , execcmd    },
+	{ BUILTIN_SPEC_REG      "exit"    , exitcmd    },
+	{ BUILTIN_SPEC_REG_ASSG "export"  , exportcmd  },
+	{ BUILTIN_REGULAR       "false"   , falsecmd   },
+#if JOBS
+	{ BUILTIN_REGULAR       "fg"      , fg_bgcmd   },
+#endif
+#if ENABLE_ASH_GETOPTS
+	{ BUILTIN_REGULAR       "getopts" , getoptscmd },
+#endif
+	{ BUILTIN_NOSPEC        "hash"    , hashcmd    },
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+	{ BUILTIN_NOSPEC        "help"    , helpcmd    },
+#endif
+#if JOBS
+	{ BUILTIN_REGULAR       "jobs"    , jobscmd    },
+	{ BUILTIN_REGULAR       "kill"    , killcmd    },
+#endif
+#if ENABLE_SH_MATH_SUPPORT
+	{ BUILTIN_NOSPEC        "let"     , letcmd     },
+#endif
+	{ BUILTIN_ASSIGN        "local"   , localcmd   },
+#if ENABLE_ASH_BUILTIN_PRINTF
+	{ BUILTIN_REGULAR       "printf"  , printfcmd  },
+#endif
+	{ BUILTIN_NOSPEC        "pwd"     , pwdcmd     },
+	{ BUILTIN_REGULAR       "read"    , readcmd    },
+	{ BUILTIN_SPEC_REG_ASSG "readonly", exportcmd  },
+	{ BUILTIN_SPEC_REG      "return"  , returncmd  },
+	{ BUILTIN_SPEC_REG      "set"     , setcmd     },
+	{ BUILTIN_SPEC_REG      "shift"   , shiftcmd   },
+#if ENABLE_ASH_BASH_COMPAT
+	{ BUILTIN_SPEC_REG      "source"  , dotcmd     },
+#endif
+#if ENABLE_ASH_BUILTIN_TEST
+	{ BUILTIN_REGULAR       "test"    , testcmd    },
+#endif
+	{ BUILTIN_SPEC_REG      "times"   , timescmd   },
+	{ BUILTIN_SPEC_REG      "trap"    , trapcmd    },
+	{ BUILTIN_REGULAR       "true"    , truecmd    },
+	{ BUILTIN_NOSPEC        "type"    , typecmd    },
+	{ BUILTIN_NOSPEC        "ulimit"  , ulimitcmd  },
+	{ BUILTIN_REGULAR       "umask"   , umaskcmd   },
+#if ENABLE_ASH_ALIAS
+	{ BUILTIN_REGULAR       "unalias" , unaliascmd },
+#endif
+	{ BUILTIN_SPEC_REG      "unset"   , unsetcmd   },
+	{ BUILTIN_REGULAR       "wait"    , waitcmd    },
+};
+
+/* Should match the above table! */
+#define COMMANDCMD (builtintab + \
+	2 + \
+	1 * ENABLE_ASH_BUILTIN_TEST + \
+	1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
+	1 * ENABLE_ASH_ALIAS + \
+	1 * ENABLE_ASH_JOB_CONTROL + \
+	3)
+#define EXECCMD (builtintab + \
+	2 + \
+	1 * ENABLE_ASH_BUILTIN_TEST + \
+	1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \
+	1 * ENABLE_ASH_ALIAS + \
+	1 * ENABLE_ASH_JOB_CONTROL + \
+	3 + \
+	1 * ENABLE_ASH_CMDCMD + \
+	1 + \
+	ENABLE_ASH_BUILTIN_ECHO + \
+	1)
+
+/*
+ * Search the table of builtin commands.
+ */
+static struct builtincmd *
+find_builtin(const char *name)
+{
+	struct builtincmd *bp;
+
+	bp = bsearch(
+		name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
+		pstrcmp
+	);
+	return bp;
+}
+
+/*
+ * Execute a simple command.
+ */
+static int
+isassignment(const char *p)
+{
+	const char *q = endofname(p);
+	if (p == q)
+		return 0;
+	return *q == '=';
+}
+static int FAST_FUNC
+bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	/* Preserve exitstatus of a previous possible redirection
+	 * as POSIX mandates */
+	return back_exitstatus;
+}
+static void
+evalcommand(union node *cmd, int flags)
+{
+	static const struct builtincmd null_bltin = {
+		"\0\0", bltincmd /* why three NULs? */
+	};
+	struct stackmark smark;
+	union node *argp;
+	struct arglist arglist;
+	struct arglist varlist;
+	char **argv;
+	int argc;
+	const struct strlist *sp;
+	struct cmdentry cmdentry;
+	struct job *jp;
+	char *lastarg;
+	const char *path;
+	int spclbltin;
+	int status;
+	char **nargv;
+	struct builtincmd *bcmd;
+	smallint cmd_is_exec;
+	smallint pseudovarflag = 0;
+
+	/* First expand the arguments. */
+	TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
+	setstackmark(&smark);
+	back_exitstatus = 0;
+
+	cmdentry.cmdtype = CMDBUILTIN;
+	cmdentry.u.cmd = &null_bltin;
+	varlist.lastp = &varlist.list;
+	*varlist.lastp = NULL;
+	arglist.lastp = &arglist.list;
+	*arglist.lastp = NULL;
+
+	argc = 0;
+	if (cmd->ncmd.args) {
+		bcmd = find_builtin(cmd->ncmd.args->narg.text);
+		pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
+	}
+
+	for (argp = cmd->ncmd.args; argp; argp = argp->narg.next) {
+		struct strlist **spp;
+
+		spp = arglist.lastp;
+		if (pseudovarflag && isassignment(argp->narg.text))
+			expandarg(argp, &arglist, EXP_VARTILDE);
+		else
+			expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+
+		for (sp = *spp; sp; sp = sp->next)
+			argc++;
+	}
+
+	argv = nargv = stalloc(sizeof(char *) * (argc + 1));
+	for (sp = arglist.list; sp; sp = sp->next) {
+		TRACE(("evalcommand arg: %s\n", sp->text));
+		*nargv++ = sp->text;
+	}
+	*nargv = NULL;
+
+	lastarg = NULL;
+	if (iflag && funcnest == 0 && argc > 0)
+		lastarg = nargv[-1];
+
+	preverrout_fd = 2;
+	expredir(cmd->ncmd.redirect);
+	status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
+
+	path = vpath.var_text;
+	for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
+		struct strlist **spp;
+		char *p;
+
+		spp = varlist.lastp;
+		expandarg(argp, &varlist, EXP_VARTILDE);
+
+		/*
+		 * Modify the command lookup path, if a PATH= assignment
+		 * is present
+		 */
+		p = (*spp)->text;
+		if (varcmp(p, path) == 0)
+			path = p;
+	}
+
+	/* Print the command if xflag is set. */
+	if (xflag) {
+		int n;
+		const char *p = " %s" + 1;
+
+		fdprintf(preverrout_fd, p, expandstr(ps4val()));
+		sp = varlist.list;
+		for (n = 0; n < 2; n++) {
+			while (sp) {
+				fdprintf(preverrout_fd, p, sp->text);
+				sp = sp->next;
+				p = " %s";
+			}
+			sp = arglist.list;
+		}
+		safe_write(preverrout_fd, "\n", 1);
+	}
+
+	cmd_is_exec = 0;
+	spclbltin = -1;
+
+	/* Now locate the command. */
+	if (argc) {
+		const char *oldpath;
+		int cmd_flag = DO_ERR;
+
+		path += 5;
+		oldpath = path;
+		for (;;) {
+			find_command(argv[0], &cmdentry, cmd_flag, path);
+			if (cmdentry.cmdtype == CMDUNKNOWN) {
+				flush_stdout_stderr();
+				status = 127;
+				goto bail;
+			}
+
+			/* implement bltin and command here */
+			if (cmdentry.cmdtype != CMDBUILTIN)
+				break;
+			if (spclbltin < 0)
+				spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
+			if (cmdentry.u.cmd == EXECCMD)
+				cmd_is_exec = 1;
+#if ENABLE_ASH_CMDCMD
+			if (cmdentry.u.cmd == COMMANDCMD) {
+				path = oldpath;
+				nargv = parse_command_args(argv, &path);
+				if (!nargv)
+					break;
+				argc -= nargv - argv;
+				argv = nargv;
+				cmd_flag |= DO_NOFUNC;
+			} else
+#endif
+				break;
+		}
+	}
+
+	if (status) {
+		/* We have a redirection error. */
+		if (spclbltin > 0)
+			raise_exception(EXERROR);
+ bail:
+		exitstatus = status;
+		goto out;
+	}
+
+	/* Execute the command. */
+	switch (cmdentry.cmdtype) {
+	default: {
+
+#if ENABLE_FEATURE_SH_NOFORK
+/* (1) BUG: if variables are set, we need to fork, or save/restore them
+ *     around run_nofork_applet() call.
+ * (2) Should this check also be done in forkshell()?
+ *     (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
+ */
+		/* find_command() encodes applet_no as (-2 - applet_no) */
+		int applet_no = (- cmdentry.u.index - 2);
+		if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
+			listsetvar(varlist.list, VEXPORT|VSTACK);
+			/* run <applet>_main() */
+			exitstatus = run_nofork_applet(applet_no, argv);
+			break;
+		}
+#endif
+		/* Can we avoid forking off? For example, very last command
+		 * in a script or a subshell does not need forking,
+		 * we can just exec it.
+		 */
+		if (!(flags & EV_EXIT) || may_have_traps) {
+			/* No, forking off a child is necessary */
+			INT_OFF;
+			jp = makejob(/*cmd,*/ 1);
+			if (forkshell(jp, cmd, FORK_FG) != 0) {
+				/* parent */
+				exitstatus = waitforjob(jp);
+				INT_ON;
+				TRACE(("forked child exited with %d\n", exitstatus));
+				break;
+			}
+			/* child */
+			FORCE_INT_ON;
+			/* fall through to exec'ing external program */
+		}
+		listsetvar(varlist.list, VEXPORT|VSTACK);
+		shellexec(argv, path, cmdentry.u.index);
+		/* NOTREACHED */
+	} /* default */
+	case CMDBUILTIN:
+		cmdenviron = varlist.list;
+		if (cmdenviron) {
+			struct strlist *list = cmdenviron;
+			int i = VNOSET;
+			if (spclbltin > 0 || argc == 0) {
+				i = 0;
+				if (cmd_is_exec && argc > 1)
+					i = VEXPORT;
+			}
+			listsetvar(list, i);
+		}
+		/* Tight loop with builtins only:
+		 * "while kill -0 $child; do true; done"
+		 * will never exit even if $child died, unless we do this
+		 * to reap the zombie and make kill detect that it's gone: */
+		dowait(DOWAIT_NONBLOCK, NULL);
+
+		if (evalbltin(cmdentry.u.cmd, argc, argv)) {
+			int exit_status;
+			int i = exception_type;
+			if (i == EXEXIT)
+				goto raise;
+			exit_status = 2;
+			if (i == EXINT)
+				exit_status = 128 + SIGINT;
+			if (i == EXSIG)
+				exit_status = 128 + pending_sig;
+			exitstatus = exit_status;
+			if (i == EXINT || spclbltin > 0) {
+ raise:
+				longjmp(exception_handler->loc, 1);
+			}
+			FORCE_INT_ON;
+		}
+		break;
+
+	case CMDFUNCTION:
+		listsetvar(varlist.list, 0);
+		/* See above for the rationale */
+		dowait(DOWAIT_NONBLOCK, NULL);
+		if (evalfun(cmdentry.u.func, argc, argv, flags))
+			goto raise;
+		break;
+
+	} /* switch */
+
+ out:
+	popredir(/*drop:*/ cmd_is_exec, /*restore:*/ cmd_is_exec);
+	if (lastarg) {
+		/* dsl: I think this is intended to be used to support
+		 * '_' in 'vi' command mode during line editing...
+		 * However I implemented that within libedit itself.
+		 */
+		setvar("_", lastarg, 0);
+	}
+	popstackmark(&smark);
+}
+
+static int
+evalbltin(const struct builtincmd *cmd, int argc, char **argv)
+{
+	char *volatile savecmdname;
+	struct jmploc *volatile savehandler;
+	struct jmploc jmploc;
+	int i;
+
+	savecmdname = commandname;
+	i = setjmp(jmploc.loc);
+	if (i)
+		goto cmddone;
+	savehandler = exception_handler;
+	exception_handler = &jmploc;
+	commandname = argv[0];
+	argptr = argv + 1;
+	optptr = NULL;                  /* initialize nextopt */
+	exitstatus = (*cmd->builtin)(argc, argv);
+	flush_stdout_stderr();
+ cmddone:
+	exitstatus |= ferror(stdout);
+	clearerr(stdout);
+	commandname = savecmdname;
+	exception_handler = savehandler;
+
+	return i;
+}
+
+static int
+goodname(const char *p)
+{
+	return endofname(p)[0] == '\0';
+}
+
+
+/*
+ * Search for a command.  This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+static void
+prehash(union node *n)
+{
+	struct cmdentry entry;
+
+	if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
+		find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
+}
+
+
+/* ============ Builtin commands
+ *
+ * Builtin commands whose functions are closely tied to evaluation
+ * are implemented here.
+ */
+
+/*
+ * Handle break and continue commands.  Break, continue, and return are
+ * all handled by setting the evalskip flag.  The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them.  The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return.  (The latter is always 1.)  It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+static int FAST_FUNC
+breakcmd(int argc UNUSED_PARAM, char **argv)
+{
+	int n = argv[1] ? number(argv[1]) : 1;
+
+	if (n <= 0)
+		ash_msg_and_raise_error(msg_illnum, argv[1]);
+	if (n > loopnest)
+		n = loopnest;
+	if (n > 0) {
+		evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
+		skipcount = n;
+	}
+	return 0;
+}
+
+
+/* ============ input.c
+ *
+ * This implements the input routines used by the parser.
+ */
+
+enum {
+	INPUT_PUSH_FILE = 1,
+	INPUT_NOFILE_OK = 2,
+};
+
+static smallint checkkwd;
+/* values of checkkwd variable */
+#define CHKALIAS        0x1
+#define CHKKWD          0x2
+#define CHKNL           0x4
+
+/*
+ * Push a string back onto the input at this current parsefile level.
+ * We handle aliases this way.
+ */
+#if !ENABLE_ASH_ALIAS
+#define pushstring(s, ap) pushstring(s)
+#endif
+static void
+pushstring(char *s, struct alias *ap)
+{
+	struct strpush *sp;
+	int len;
+
+	len = strlen(s);
+	INT_OFF;
+	if (g_parsefile->strpush) {
+		sp = ckzalloc(sizeof(*sp));
+		sp->prev = g_parsefile->strpush;
+	} else {
+		sp = &(g_parsefile->basestrpush);
+	}
+	g_parsefile->strpush = sp;
+	sp->prev_string = g_parsefile->next_to_pgetc;
+	sp->prev_left_in_line = g_parsefile->left_in_line;
+#if ENABLE_ASH_ALIAS
+	sp->ap = ap;
+	if (ap) {
+		ap->flag |= ALIASINUSE;
+		sp->string = s;
+	}
+#endif
+	g_parsefile->next_to_pgetc = s;
+	g_parsefile->left_in_line = len;
+	INT_ON;
+}
+
+static void
+popstring(void)
+{
+	struct strpush *sp = g_parsefile->strpush;
+
+	INT_OFF;
+#if ENABLE_ASH_ALIAS
+	if (sp->ap) {
+		if (g_parsefile->next_to_pgetc[-1] == ' '
+		 || g_parsefile->next_to_pgetc[-1] == '\t'
+		) {
+			checkkwd |= CHKALIAS;
+		}
+		if (sp->string != sp->ap->val) {
+			free(sp->string);
+		}
+		sp->ap->flag &= ~ALIASINUSE;
+		if (sp->ap->flag & ALIASDEAD) {
+			unalias(sp->ap->name);
+		}
+	}
+#endif
+	g_parsefile->next_to_pgetc = sp->prev_string;
+	g_parsefile->left_in_line = sp->prev_left_in_line;
+	g_parsefile->strpush = sp->prev;
+	if (sp != &(g_parsefile->basestrpush))
+		free(sp);
+	INT_ON;
+}
+
+//FIXME: BASH_COMPAT with "...&" does TWO pungetc():
+//it peeks whether it is &>, and then pushes back both chars.
+//This function needs to save last *next_to_pgetc to buf[0]
+//to make two pungetc() reliable. Currently,
+// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work...
+static int
+preadfd(void)
+{
+	int nr;
+	char *buf = g_parsefile->buf;
+
+	g_parsefile->next_to_pgetc = buf;
+#if ENABLE_FEATURE_EDITING
+ retry:
+	if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
+		nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
+	else {
+		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
+		/* 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:
+		 */
+		reinit_unicode(lookupvar("LANG"));
+		nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
+		if (nr == 0) {
+			/* Ctrl+C pressed */
+			if (trap[SIGINT]) {
+				buf[0] = '\n';
+				buf[1] = '\0';
+				raise(SIGINT);
+				return 1;
+			}
+			goto retry;
+		}
+		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_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1, /*loop_on_EINTR:*/ 1);
+#endif
+
+#if 0 /* disabled: nonblock_immune_read() handles this problem */
+	if (nr < 0) {
+		if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
+			int flags = fcntl(0, F_GETFL);
+			if (flags >= 0 && (flags & O_NONBLOCK)) {
+				flags &= ~O_NONBLOCK;
+				if (fcntl(0, F_SETFL, flags) >= 0) {
+					out2str("sh: turning off NDELAY mode\n");
+					goto retry;
+				}
+			}
+		}
+	}
+#endif
+	return nr;
+}
+
+/*
+ * Refill the input buffer and return the next input character:
+ *
+ * 1) If a string was pushed back on the input, pop it;
+ * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
+ *    or we are reading from a string so we can't refill the buffer,
+ *    return EOF.
+ * 3) If there is more stuff in this buffer, use it else call read to fill it.
+ * 4) Process input up to the next newline, deleting nul characters.
+ */
+//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
+#define pgetc_debug(...) ((void)0)
+static int
+preadbuffer(void)
+{
+	char *q;
+	int more;
+
+	while (g_parsefile->strpush) {
+#if ENABLE_ASH_ALIAS
+		if (g_parsefile->left_in_line == -1
+		 && g_parsefile->strpush->ap
+		 && g_parsefile->next_to_pgetc[-1] != ' '
+		 && g_parsefile->next_to_pgetc[-1] != '\t'
+		) {
+			pgetc_debug("preadbuffer PEOA");
+			return PEOA;
+		}
+#endif
+		popstring();
+		/* try "pgetc" now: */
+		pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'",
+				g_parsefile->left_in_line,
+				g_parsefile->next_to_pgetc,
+				g_parsefile->next_to_pgetc);
+		if (--g_parsefile->left_in_line >= 0)
+			return (unsigned char)(*g_parsefile->next_to_pgetc++);
+	}
+	/* on both branches above g_parsefile->left_in_line < 0.
+	 * "pgetc" needs refilling.
+	 */
+
+	/* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
+	 * pungetc() may increment it a few times.
+	 * Assuming it won't increment it to less than -90.
+	 */
+	if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
+		pgetc_debug("preadbuffer PEOF1");
+		/* even in failure keep left_in_line and next_to_pgetc
+		 * in lock step, for correct multi-layer pungetc.
+		 * left_in_line was decremented before preadbuffer(),
+		 * must inc next_to_pgetc: */
+		g_parsefile->next_to_pgetc++;
+		return PEOF;
+	}
+
+	more = g_parsefile->left_in_buffer;
+	if (more <= 0) {
+		flush_stdout_stderr();
+ again:
+		more = preadfd();
+		if (more <= 0) {
+			/* don't try reading again */
+			g_parsefile->left_in_line = -99;
+			pgetc_debug("preadbuffer PEOF2");
+			g_parsefile->next_to_pgetc++;
+			return PEOF;
+		}
+	}
+
+	/* Find out where's the end of line.
+	 * Set g_parsefile->left_in_line
+	 * and g_parsefile->left_in_buffer acordingly.
+	 * NUL chars are deleted.
+	 */
+	q = g_parsefile->next_to_pgetc;
+	for (;;) {
+		char c;
+
+		more--;
+
+		c = *q;
+		if (c == '\0') {
+			memmove(q, q + 1, more);
+		} else {
+			q++;
+			if (c == '\n') {
+				g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
+				break;
+			}
+		}
+
+		if (more <= 0) {
+			g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
+			if (g_parsefile->left_in_line < 0)
+				goto again;
+			break;
+		}
+	}
+	g_parsefile->left_in_buffer = more;
+
+	if (vflag) {
+		char save = *q;
+		*q = '\0';
+		out2str(g_parsefile->next_to_pgetc);
+		*q = save;
+	}
+
+	pgetc_debug("preadbuffer at %d:%p'%s'",
+			g_parsefile->left_in_line,
+			g_parsefile->next_to_pgetc,
+			g_parsefile->next_to_pgetc);
+	return (unsigned char)*g_parsefile->next_to_pgetc++;
+}
+
+#define pgetc_as_macro() \
+	(--g_parsefile->left_in_line >= 0 \
+	? (unsigned char)*g_parsefile->next_to_pgetc++ \
+	: preadbuffer() \
+	)
+
+static int
+pgetc(void)
+{
+	pgetc_debug("pgetc_fast at %d:%p'%s'",
+			g_parsefile->left_in_line,
+			g_parsefile->next_to_pgetc,
+			g_parsefile->next_to_pgetc);
+	return pgetc_as_macro();
+}
+
+#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
+# define pgetc_fast() pgetc()
+#else
+# define pgetc_fast() pgetc_as_macro()
+#endif
+
+#if ENABLE_ASH_ALIAS
+static int
+pgetc_without_PEOA(void)
+{
+	int c;
+	do {
+		pgetc_debug("pgetc_fast at %d:%p'%s'",
+				g_parsefile->left_in_line,
+				g_parsefile->next_to_pgetc,
+				g_parsefile->next_to_pgetc);
+		c = pgetc_fast();
+	} while (c == PEOA);
+	return c;
+}
+#else
+# define pgetc_without_PEOA() pgetc()
+#endif
+
+/*
+ * Read a line from the script.
+ */
+static char *
+pfgets(char *line, int len)
+{
+	char *p = line;
+	int nleft = len;
+	int c;
+
+	while (--nleft > 0) {
+		c = pgetc_without_PEOA();
+		if (c == PEOF) {
+			if (p == line)
+				return NULL;
+			break;
+		}
+		*p++ = c;
+		if (c == '\n')
+			break;
+	}
+	*p = '\0';
+	return line;
+}
+
+/*
+ * Undo the last call to pgetc.  Only one character may be pushed back.
+ * PEOF may be pushed back.
+ */
+static void
+pungetc(void)
+{
+	g_parsefile->left_in_line++;
+	g_parsefile->next_to_pgetc--;
+	pgetc_debug("pushed back to %d:%p'%s'",
+			g_parsefile->left_in_line,
+			g_parsefile->next_to_pgetc,
+			g_parsefile->next_to_pgetc);
+}
+
+/*
+ * To handle the "." command, a stack of input files is used.  Pushfile
+ * adds a new entry to the stack and popfile restores the previous level.
+ */
+static void
+pushfile(void)
+{
+	struct parsefile *pf;
+
+	pf = ckzalloc(sizeof(*pf));
+	pf->prev = g_parsefile;
+	pf->pf_fd = -1;
+	/*pf->strpush = NULL; - ckzalloc did it */
+	/*pf->basestrpush.prev = NULL;*/
+	g_parsefile = pf;
+}
+
+static void
+popfile(void)
+{
+	struct parsefile *pf = g_parsefile;
+
+	INT_OFF;
+	if (pf->pf_fd >= 0)
+		close(pf->pf_fd);
+	free(pf->buf);
+	while (pf->strpush)
+		popstring();
+	g_parsefile = pf->prev;
+	free(pf);
+	INT_ON;
+}
+
+/*
+ * Return to top level.
+ */
+static void
+popallfiles(void)
+{
+	while (g_parsefile != &basepf)
+		popfile();
+}
+
+/*
+ * Close the file(s) that the shell is reading commands from.  Called
+ * after a fork is done.
+ */
+static void
+closescript(void)
+{
+	popallfiles();
+	if (g_parsefile->pf_fd > 0) {
+		close(g_parsefile->pf_fd);
+		g_parsefile->pf_fd = 0;
+	}
+}
+
+/*
+ * Like setinputfile, but takes an open file descriptor.  Call this with
+ * interrupts off.
+ */
+static void
+setinputfd(int fd, int push)
+{
+	close_on_exec_on(fd);
+	if (push) {
+		pushfile();
+		g_parsefile->buf = NULL;
+	}
+	g_parsefile->pf_fd = fd;
+	if (g_parsefile->buf == NULL)
+		g_parsefile->buf = ckmalloc(IBUFSIZ);
+	g_parsefile->left_in_buffer = 0;
+	g_parsefile->left_in_line = 0;
+	g_parsefile->linno = 1;
+}
+
+/*
+ * Set the input to take input from a file.  If push is set, push the
+ * old input onto the stack first.
+ */
+static int
+setinputfile(const char *fname, int flags)
+{
+	int fd;
+	int fd2;
+
+	INT_OFF;
+	fd = open(fname, O_RDONLY);
+	if (fd < 0) {
+		if (flags & INPUT_NOFILE_OK)
+			goto out;
+		ash_msg_and_raise_error("can't open '%s'", fname);
+	}
+	if (fd < 10) {
+		fd2 = copyfd(fd, 10);
+		close(fd);
+		if (fd2 < 0)
+			ash_msg_and_raise_error("out of file descriptors");
+		fd = fd2;
+	}
+	setinputfd(fd, flags & INPUT_PUSH_FILE);
+ out:
+	INT_ON;
+	return fd;
+}
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+static void
+setinputstring(char *string)
+{
+	INT_OFF;
+	pushfile();
+	g_parsefile->next_to_pgetc = string;
+	g_parsefile->left_in_line = strlen(string);
+	g_parsefile->buf = NULL;
+	g_parsefile->linno = 1;
+	INT_ON;
+}
+
+
+/* ============ mail.c
+ *
+ * Routines to check for mail.
+ */
+
+#if ENABLE_ASH_MAIL
+
+#define MAXMBOXES 10
+
+/* times of mailboxes */
+static time_t mailtime[MAXMBOXES];
+/* Set if MAIL or MAILPATH is changed. */
+static smallint mail_var_path_changed;
+
+/*
+ * Print appropriate message(s) if mail has arrived.
+ * If mail_var_path_changed is set,
+ * then the value of MAIL has mail_var_path_changed,
+ * so we just update the values.
+ */
+static void
+chkmail(void)
+{
+	const char *mpath;
+	char *p;
+	char *q;
+	time_t *mtp;
+	struct stackmark smark;
+	struct stat statb;
+
+	setstackmark(&smark);
+	mpath = mpathset() ? mpathval() : mailval();
+	for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
+		p = path_advance(&mpath, nullstr);
+		if (p == NULL)
+			break;
+		if (*p == '\0')
+			continue;
+		for (q = p; *q; q++)
+			continue;
+#if DEBUG
+		if (q[-1] != '/')
+			abort();
+#endif
+		q[-1] = '\0';                   /* delete trailing '/' */
+		if (stat(p, &statb) < 0) {
+			*mtp = 0;
+			continue;
+		}
+		if (!mail_var_path_changed && statb.st_mtime != *mtp) {
+			fprintf(
+				stderr, "%s\n",
+				pathopt ? pathopt : "you have mail"
+			);
+		}
+		*mtp = statb.st_mtime;
+	}
+	mail_var_path_changed = 0;
+	popstackmark(&smark);
+}
+
+static void FAST_FUNC
+changemail(const char *val UNUSED_PARAM)
+{
+	mail_var_path_changed = 1;
+}
+
+#endif /* ASH_MAIL */
+
+
+/* ============ ??? */
+
+/*
+ * Set the shell parameters.
+ */
+static void
+setparam(char **argv)
+{
+	char **newparam;
+	char **ap;
+	int nparam;
+
+	for (nparam = 0; argv[nparam]; nparam++)
+		continue;
+	ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
+	while (*argv) {
+		*ap++ = ckstrdup(*argv++);
+	}
+	*ap = NULL;
+	freeparam(&shellparam);
+	shellparam.malloced = 1;
+	shellparam.nparam = nparam;
+	shellparam.p = newparam;
+#if ENABLE_ASH_GETOPTS
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
+#endif
+}
+
+/*
+ * Process shell options.  The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
+ *
+ * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
+ * For a non-interactive shell, an error condition encountered
+ * by a special built-in ... shall cause the shell to write a diagnostic message
+ * to standard error and exit as shown in the following table:
+ * Error                                           Special Built-In
+ * ...
+ * Utility syntax error (option or operand error)  Shall exit
+ * ...
+ * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
+ * we see that bash does not do that (set "finishes" with error code 1 instead,
+ * and shell continues), and people rely on this behavior!
+ * Testcase:
+ * set -o barfoo 2>/dev/null
+ * echo $?
+ *
+ * Oh well. Let's mimic that.
+ */
+static int
+plus_minus_o(char *name, int val)
+{
+	int i;
+
+	if (name) {
+		for (i = 0; i < NOPTS; i++) {
+			if (strcmp(name, optnames(i)) == 0) {
+				optlist[i] = val;
+				return 0;
+			}
+		}
+		ash_msg("illegal option %co %s", val ? '-' : '+', name);
+		return 1;
+	}
+	for (i = 0; i < NOPTS; i++) {
+		if (val) {
+			out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
+		} else {
+			out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
+		}
+	}
+	return 0;
+}
+static void
+setoption(int flag, int val)
+{
+	int i;
+
+	for (i = 0; i < NOPTS; i++) {
+		if (optletters(i) == flag) {
+			optlist[i] = val;
+			return;
+		}
+	}
+	ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
+	/* NOTREACHED */
+}
+static int
+options(int cmdline)
+{
+	char *p;
+	int val;
+	int c;
+
+	if (cmdline)
+		minusc = NULL;
+	while ((p = *argptr) != NULL) {
+		c = *p++;
+		if (c != '-' && c != '+')
+			break;
+		argptr++;
+		val = 0; /* val = 0 if c == '+' */
+		if (c == '-') {
+			val = 1;
+			if (p[0] == '\0' || LONE_DASH(p)) {
+				if (!cmdline) {
+					/* "-" means turn off -x and -v */
+					if (p[0] == '\0')
+						xflag = vflag = 0;
+					/* "--" means reset params */
+					else if (*argptr == NULL)
+						setparam(argptr);
+				}
+				break;    /* "-" or "--" terminates options */
+			}
+		}
+		/* first char was + or - */
+		while ((c = *p++) != '\0') {
+			/* bash 3.2 indeed handles -c CMD and +c CMD the same */
+			if (c == 'c' && cmdline) {
+				minusc = p;     /* command is after shell args */
+			} else if (c == 'o') {
+				if (plus_minus_o(*argptr, val)) {
+					/* it already printed err message */
+					return 1; /* error */
+				}
+				if (*argptr)
+					argptr++;
+			} else if (cmdline && (c == 'l')) { /* -l or +l == --login */
+				isloginsh = 1;
+			/* bash does not accept +-login, we also won't */
+			} else if (cmdline && val && (c == '-')) { /* long options */
+				if (strcmp(p, "login") == 0)
+					isloginsh = 1;
+				break;
+			} else {
+				setoption(c, val);
+			}
+		}
+	}
+	return 0;
+}
+
+/*
+ * The shift builtin command.
+ */
+static int FAST_FUNC
+shiftcmd(int argc UNUSED_PARAM, char **argv)
+{
+	int n;
+	char **ap1, **ap2;
+
+	n = 1;
+	if (argv[1])
+		n = number(argv[1]);
+	if (n > shellparam.nparam)
+		n = 0; /* bash compat, was = shellparam.nparam; */
+	INT_OFF;
+	shellparam.nparam -= n;
+	for (ap1 = shellparam.p; --n >= 0; ap1++) {
+		if (shellparam.malloced)
+			free(*ap1);
+	}
+	ap2 = shellparam.p;
+	while ((*ap2++ = *ap1++) != NULL)
+		continue;
+#if ENABLE_ASH_GETOPTS
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
+#endif
+	INT_ON;
+	return 0;
+}
+
+/*
+ * POSIX requires that 'set' (but not export or readonly) output the
+ * variables in lexicographic order - by the locale's collating order (sigh).
+ * Maybe we could keep them in an ordered balanced binary tree
+ * instead of hashed lists.
+ * For now just roll 'em through qsort for printing...
+ */
+static int
+showvars(const char *sep_prefix, int on, int off)
+{
+	const char *sep;
+	char **ep, **epend;
+
+	ep = listvars(on, off, &epend);
+	qsort(ep, epend - ep, sizeof(char *), vpcmp);
+
+	sep = *sep_prefix ? " " : sep_prefix;
+
+	for (; ep < epend; ep++) {
+		const char *p;
+		const char *q;
+
+		p = strchrnul(*ep, '=');
+		q = nullstr;
+		if (*p)
+			q = single_quote(++p);
+		out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
+	}
+	return 0;
+}
+
+/*
+ * The set command builtin.
+ */
+static int FAST_FUNC
+setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	int retval;
+
+	if (!argv[1])
+		return showvars(nullstr, 0, VUNSET);
+
+	INT_OFF;
+	retval = options(/*cmdline:*/ 0);
+	if (retval == 0) { /* if no parse error... */
+		optschanged();
+		if (*argptr != NULL) {
+			setparam(argptr);
+		}
+	}
+	INT_ON;
+	return retval;
+}
+
+#if ENABLE_ASH_RANDOM_SUPPORT
+static void FAST_FUNC
+change_random(const char *value)
+{
+	uint32_t t;
+
+	if (value == NULL) {
+		/* "get", generate */
+		t = next_random(&random_gen);
+		/* set without recursion */
+		setvar(vrandom.var_text, utoa(t), VNOFUNC);
+		vrandom.flags &= ~VNOFUNC;
+	} else {
+		/* set/reset */
+		t = strtoul(value, NULL, 10);
+		INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
+	}
+}
+#endif
+
+#if ENABLE_ASH_GETOPTS
+static int
+getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
+{
+	char *p, *q;
+	char c = '?';
+	int done = 0;
+	int err = 0;
+	char s[12];
+	char **optnext;
+
+	if (*param_optind < 1)
+		return 1;
+	optnext = optfirst + *param_optind - 1;
+
+	if (*param_optind <= 1 || *optoff < 0 || (int)strlen(optnext[-1]) < *optoff)
+		p = NULL;
+	else
+		p = optnext[-1] + *optoff;
+	if (p == NULL || *p == '\0') {
+		/* Current word is done, advance */
+		p = *optnext;
+		if (p == NULL || *p != '-' || *++p == '\0') {
+ atend:
+			p = NULL;
+			done = 1;
+			goto out;
+		}
+		optnext++;
+		if (LONE_DASH(p))        /* check for "--" */
+			goto atend;
+	}
+
+	c = *p++;
+	for (q = optstr; *q != c;) {
+		if (*q == '\0') {
+			if (optstr[0] == ':') {
+				s[0] = c;
+				s[1] = '\0';
+				err |= setvarsafe("OPTARG", s, 0);
+			} else {
+				fprintf(stderr, "Illegal option -%c\n", c);
+				unsetvar("OPTARG");
+			}
+			c = '?';
+			goto out;
+		}
+		if (*++q == ':')
+			q++;
+	}
+
+	if (*++q == ':') {
+		if (*p == '\0' && (p = *optnext) == NULL) {
+			if (optstr[0] == ':') {
+				s[0] = c;
+				s[1] = '\0';
+				err |= setvarsafe("OPTARG", s, 0);
+				c = ':';
+			} else {
+				fprintf(stderr, "No arg for -%c option\n", c);
+				unsetvar("OPTARG");
+				c = '?';
+			}
+			goto out;
+		}
+
+		if (p == *optnext)
+			optnext++;
+		err |= setvarsafe("OPTARG", p, 0);
+		p = NULL;
+	} else
+		err |= setvarsafe("OPTARG", nullstr, 0);
+ out:
+	*optoff = p ? p - *(optnext - 1) : -1;
+	*param_optind = optnext - optfirst + 1;
+	fmtstr(s, sizeof(s), "%d", *param_optind);
+	err |= setvarsafe("OPTIND", s, VNOFUNC);
+	s[0] = c;
+	s[1] = '\0';
+	err |= setvarsafe(optvar, s, 0);
+	if (err) {
+		*param_optind = 1;
+		*optoff = -1;
+		flush_stdout_stderr();
+		raise_exception(EXERROR);
+	}
+	return done;
+}
+
+/*
+ * The getopts builtin.  Shellparam.optnext points to the next argument
+ * to be processed.  Shellparam.optptr points to the next character to
+ * be processed in the current argument.  If shellparam.optnext is NULL,
+ * then it's the first time getopts has been called.
+ */
+static int FAST_FUNC
+getoptscmd(int argc, char **argv)
+{
+	char **optbase;
+
+	if (argc < 3)
+		ash_msg_and_raise_error("usage: getopts optstring var [arg]");
+	if (argc == 3) {
+		optbase = shellparam.p;
+		if (shellparam.optind > shellparam.nparam + 1) {
+			shellparam.optind = 1;
+			shellparam.optoff = -1;
+		}
+	} else {
+		optbase = &argv[3];
+		if (shellparam.optind > argc - 2) {
+			shellparam.optind = 1;
+			shellparam.optoff = -1;
+		}
+	}
+
+	return getopts(argv[1], argv[2], optbase, &shellparam.optind,
+			&shellparam.optoff);
+}
+#endif /* ASH_GETOPTS */
+
+
+/* ============ Shell parser */
+
+struct heredoc {
+	struct heredoc *next;   /* next here document in list */
+	union node *here;       /* redirection node */
+	char *eofmark;          /* string indicating end of input */
+	smallint striptabs;     /* if set, strip leading tabs */
+};
+
+static smallint tokpushback;           /* last token pushed back */
+static smallint parsebackquote;        /* nonzero if we are inside backquotes */
+static smallint quoteflag;             /* set if (part of) last token was quoted */
+static token_id_t lasttoken;           /* last token read (integer id Txxx) */
+static struct heredoc *heredoclist;    /* list of here documents to read */
+static char *wordtext;                 /* text of last word returned by readtoken */
+static struct nodelist *backquotelist;
+static union node *redirnode;
+static struct heredoc *heredoc;
+
+static const char *
+tokname(char *buf, int tok)
+{
+	if (tok < TSEMI)
+		return tokname_array[tok] + 1;
+	sprintf(buf, "\"%s\"", tokname_array[tok] + 1);
+	return buf;
+}
+
+/* raise_error_unexpected_syntax:
+ * Called when an unexpected token is read during the parse.  The argument
+ * is the token that is expected, or -1 if more than one type of token can
+ * occur at this point.
+ */
+static void raise_error_unexpected_syntax(int) NORETURN;
+static void
+raise_error_unexpected_syntax(int token)
+{
+	char msg[64];
+	char buf[16];
+	int l;
+
+	l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
+	if (token >= 0)
+		sprintf(msg + l, " (expecting %s)", tokname(buf, token));
+	raise_error_syntax(msg);
+	/* NOTREACHED */
+}
+
+#define EOFMARKLEN 79
+
+/* parsing is heavily cross-recursive, need these forward decls */
+static union node *andor(void);
+static union node *pipeline(void);
+static union node *parse_command(void);
+static void parseheredoc(void);
+static char peektoken(void);
+static int readtoken(void);
+
+static union node *
+list(int nlflag)
+{
+	union node *n1, *n2, *n3;
+	int tok;
+
+	checkkwd = CHKNL | CHKKWD | CHKALIAS;
+	if (nlflag == 2 && peektoken())
+		return NULL;
+	n1 = NULL;
+	for (;;) {
+		n2 = andor();
+		tok = readtoken();
+		if (tok == TBACKGND) {
+			if (n2->type == NPIPE) {
+				n2->npipe.pipe_backgnd = 1;
+			} else {
+				if (n2->type != NREDIR) {
+					n3 = stzalloc(sizeof(struct nredir));
+					n3->nredir.n = n2;
+					/*n3->nredir.redirect = NULL; - stzalloc did it */
+					n2 = n3;
+				}
+				n2->type = NBACKGND;
+			}
+		}
+		if (n1 == NULL) {
+			n1 = n2;
+		} else {
+			n3 = stzalloc(sizeof(struct nbinary));
+			n3->type = NSEMI;
+			n3->nbinary.ch1 = n1;
+			n3->nbinary.ch2 = n2;
+			n1 = n3;
+		}
+		switch (tok) {
+		case TBACKGND:
+		case TSEMI:
+			tok = readtoken();
+			/* fall through */
+		case TNL:
+			if (tok == TNL) {
+				parseheredoc();
+				if (nlflag == 1)
+					return n1;
+			} else {
+				tokpushback = 1;
+			}
+			checkkwd = CHKNL | CHKKWD | CHKALIAS;
+			if (peektoken())
+				return n1;
+			break;
+		case TEOF:
+			if (heredoclist)
+				parseheredoc();
+			else
+				pungetc();              /* push back EOF on input */
+			return n1;
+		default:
+			if (nlflag == 1)
+				raise_error_unexpected_syntax(-1);
+			tokpushback = 1;
+			return n1;
+		}
+	}
+}
+
+static union node *
+andor(void)
+{
+	union node *n1, *n2, *n3;
+	int t;
+
+	n1 = pipeline();
+	for (;;) {
+		t = readtoken();
+		if (t == TAND) {
+			t = NAND;
+		} else if (t == TOR) {
+			t = NOR;
+		} else {
+			tokpushback = 1;
+			return n1;
+		}
+		checkkwd = CHKNL | CHKKWD | CHKALIAS;
+		n2 = pipeline();
+		n3 = stzalloc(sizeof(struct nbinary));
+		n3->type = t;
+		n3->nbinary.ch1 = n1;
+		n3->nbinary.ch2 = n2;
+		n1 = n3;
+	}
+}
+
+static union node *
+pipeline(void)
+{
+	union node *n1, *n2, *pipenode;
+	struct nodelist *lp, *prev;
+	int negate;
+
+	negate = 0;
+	TRACE(("pipeline: entered\n"));
+	if (readtoken() == TNOT) {
+		negate = !negate;
+		checkkwd = CHKKWD | CHKALIAS;
+	} else
+		tokpushback = 1;
+	n1 = parse_command();
+	if (readtoken() == TPIPE) {
+		pipenode = stzalloc(sizeof(struct npipe));
+		pipenode->type = NPIPE;
+		/*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
+		lp = stzalloc(sizeof(struct nodelist));
+		pipenode->npipe.cmdlist = lp;
+		lp->n = n1;
+		do {
+			prev = lp;
+			lp = stzalloc(sizeof(struct nodelist));
+			checkkwd = CHKNL | CHKKWD | CHKALIAS;
+			lp->n = parse_command();
+			prev->next = lp;
+		} while (readtoken() == TPIPE);
+		lp->next = NULL;
+		n1 = pipenode;
+	}
+	tokpushback = 1;
+	if (negate) {
+		n2 = stzalloc(sizeof(struct nnot));
+		n2->type = NNOT;
+		n2->nnot.com = n1;
+		return n2;
+	}
+	return n1;
+}
+
+static union node *
+makename(void)
+{
+	union node *n;
+
+	n = stzalloc(sizeof(struct narg));
+	n->type = NARG;
+	/*n->narg.next = NULL; - stzalloc did it */
+	n->narg.text = wordtext;
+	n->narg.backquote = backquotelist;
+	return n;
+}
+
+static void
+fixredir(union node *n, const char *text, int err)
+{
+	int fd;
+
+	TRACE(("Fix redir %s %d\n", text, err));
+	if (!err)
+		n->ndup.vname = NULL;
+
+	fd = bb_strtou(text, NULL, 10);
+	if (!errno && fd >= 0)
+		n->ndup.dupfd = fd;
+	else if (LONE_DASH(text))
+		n->ndup.dupfd = -1;
+	else {
+		if (err)
+			raise_error_syntax("bad fd number");
+		n->ndup.vname = makename();
+	}
+}
+
+/*
+ * Returns true if the text contains nothing to expand (no dollar signs
+ * or backquotes).
+ */
+static int
+noexpand(const char *text)
+{
+	unsigned char c;
+
+	while ((c = *text++) != '\0') {
+		if (c == CTLQUOTEMARK)
+			continue;
+		if (c == CTLESC)
+			text++;
+		else if (SIT(c, BASESYNTAX) == CCTL)
+			return 0;
+	}
+	return 1;
+}
+
+static void
+parsefname(void)
+{
+	union node *n = redirnode;
+
+	if (readtoken() != TWORD)
+		raise_error_unexpected_syntax(-1);
+	if (n->type == NHERE) {
+		struct heredoc *here = heredoc;
+		struct heredoc *p;
+		int i;
+
+		if (quoteflag == 0)
+			n->type = NXHERE;
+		TRACE(("Here document %d\n", n->type));
+		if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
+			raise_error_syntax("illegal eof marker for << redirection");
+		rmescapes(wordtext, 0);
+		here->eofmark = wordtext;
+		here->next = NULL;
+		if (heredoclist == NULL)
+			heredoclist = here;
+		else {
+			for (p = heredoclist; p->next; p = p->next)
+				continue;
+			p->next = here;
+		}
+	} else if (n->type == NTOFD || n->type == NFROMFD) {
+		fixredir(n, wordtext, 0);
+	} else {
+		n->nfile.fname = makename();
+	}
+}
+
+static union node *
+simplecmd(void)
+{
+	union node *args, **app;
+	union node *n = NULL;
+	union node *vars, **vpp;
+	union node **rpp, *redir;
+	int savecheckkwd;
+#if ENABLE_ASH_BASH_COMPAT
+	smallint double_brackets_flag = 0;
+#endif
+
+	args = NULL;
+	app = &args;
+	vars = NULL;
+	vpp = &vars;
+	redir = NULL;
+	rpp = &redir;
+
+	savecheckkwd = CHKALIAS;
+	for (;;) {
+		int t;
+		checkkwd = savecheckkwd;
+		t = readtoken();
+		switch (t) {
+#if ENABLE_ASH_BASH_COMPAT
+		case TAND: /* "&&" */
+		case TOR: /* "||" */
+			if (!double_brackets_flag) {
+				tokpushback = 1;
+				goto out;
+			}
+			wordtext = (char *) (t == TAND ? "-a" : "-o");
+#endif
+		case TWORD:
+			n = stzalloc(sizeof(struct narg));
+			n->type = NARG;
+			/*n->narg.next = NULL; - stzalloc did it */
+			n->narg.text = wordtext;
+#if ENABLE_ASH_BASH_COMPAT
+			if (strcmp("[[", wordtext) == 0)
+				double_brackets_flag = 1;
+			else if (strcmp("]]", wordtext) == 0)
+				double_brackets_flag = 0;
+#endif
+			n->narg.backquote = backquotelist;
+			if (savecheckkwd && isassignment(wordtext)) {
+				*vpp = n;
+				vpp = &n->narg.next;
+			} else {
+				*app = n;
+				app = &n->narg.next;
+				savecheckkwd = 0;
+			}
+			break;
+		case TREDIR:
+			*rpp = n = redirnode;
+			rpp = &n->nfile.next;
+			parsefname();   /* read name of redirection file */
+			break;
+		case TLP:
+			if (args && app == &args->narg.next
+			 && !vars && !redir
+			) {
+				struct builtincmd *bcmd;
+				const char *name;
+
+				/* We have a function */
+				if (readtoken() != TRP)
+					raise_error_unexpected_syntax(TRP);
+				name = n->narg.text;
+				if (!goodname(name)
+				 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
+				) {
+					raise_error_syntax("bad function name");
+				}
+				n->type = NDEFUN;
+				checkkwd = CHKNL | CHKKWD | CHKALIAS;
+				n->narg.next = parse_command();
+				return n;
+			}
+			/* fall through */
+		default:
+			tokpushback = 1;
+			goto out;
+		}
+	}
+ out:
+	*app = NULL;
+	*vpp = NULL;
+	*rpp = NULL;
+	n = stzalloc(sizeof(struct ncmd));
+	n->type = NCMD;
+	n->ncmd.args = args;
+	n->ncmd.assign = vars;
+	n->ncmd.redirect = redir;
+	return n;
+}
+
+static union node *
+parse_command(void)
+{
+	union node *n1, *n2;
+	union node *ap, **app;
+	union node *cp, **cpp;
+	union node *redir, **rpp;
+	union node **rpp2;
+	int t;
+
+	redir = NULL;
+	rpp2 = &redir;
+
+	switch (readtoken()) {
+	default:
+		raise_error_unexpected_syntax(-1);
+		/* NOTREACHED */
+	case TIF:
+		n1 = stzalloc(sizeof(struct nif));
+		n1->type = NIF;
+		n1->nif.test = list(0);
+		if (readtoken() != TTHEN)
+			raise_error_unexpected_syntax(TTHEN);
+		n1->nif.ifpart = list(0);
+		n2 = n1;
+		while (readtoken() == TELIF) {
+			n2->nif.elsepart = stzalloc(sizeof(struct nif));
+			n2 = n2->nif.elsepart;
+			n2->type = NIF;
+			n2->nif.test = list(0);
+			if (readtoken() != TTHEN)
+				raise_error_unexpected_syntax(TTHEN);
+			n2->nif.ifpart = list(0);
+		}
+		if (lasttoken == TELSE)
+			n2->nif.elsepart = list(0);
+		else {
+			n2->nif.elsepart = NULL;
+			tokpushback = 1;
+		}
+		t = TFI;
+		break;
+	case TWHILE:
+	case TUNTIL: {
+		int got;
+		n1 = stzalloc(sizeof(struct nbinary));
+		n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
+		n1->nbinary.ch1 = list(0);
+		got = readtoken();
+		if (got != TDO) {
+			TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1,
+					got == TWORD ? wordtext : ""));
+			raise_error_unexpected_syntax(TDO);
+		}
+		n1->nbinary.ch2 = list(0);
+		t = TDONE;
+		break;
+	}
+	case TFOR:
+		if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
+			raise_error_syntax("bad for loop variable");
+		n1 = stzalloc(sizeof(struct nfor));
+		n1->type = NFOR;
+		n1->nfor.var = wordtext;
+		checkkwd = CHKKWD | CHKALIAS;
+		if (readtoken() == TIN) {
+			app = &ap;
+			while (readtoken() == TWORD) {
+				n2 = stzalloc(sizeof(struct narg));
+				n2->type = NARG;
+				/*n2->narg.next = NULL; - stzalloc did it */
+				n2->narg.text = wordtext;
+				n2->narg.backquote = backquotelist;
+				*app = n2;
+				app = &n2->narg.next;
+			}
+			*app = NULL;
+			n1->nfor.args = ap;
+			if (lasttoken != TNL && lasttoken != TSEMI)
+				raise_error_unexpected_syntax(-1);
+		} else {
+			n2 = stzalloc(sizeof(struct narg));
+			n2->type = NARG;
+			/*n2->narg.next = NULL; - stzalloc did it */
+			n2->narg.text = (char *)dolatstr;
+			/*n2->narg.backquote = NULL;*/
+			n1->nfor.args = n2;
+			/*
+			 * Newline or semicolon here is optional (but note
+			 * that the original Bourne shell only allowed NL).
+			 */
+			if (lasttoken != TNL && lasttoken != TSEMI)
+				tokpushback = 1;
+		}
+		checkkwd = CHKNL | CHKKWD | CHKALIAS;
+		if (readtoken() != TDO)
+			raise_error_unexpected_syntax(TDO);
+		n1->nfor.body = list(0);
+		t = TDONE;
+		break;
+	case TCASE:
+		n1 = stzalloc(sizeof(struct ncase));
+		n1->type = NCASE;
+		if (readtoken() != TWORD)
+			raise_error_unexpected_syntax(TWORD);
+		n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
+		n2->type = NARG;
+		/*n2->narg.next = NULL; - stzalloc did it */
+		n2->narg.text = wordtext;
+		n2->narg.backquote = backquotelist;
+		do {
+			checkkwd = CHKKWD | CHKALIAS;
+		} while (readtoken() == TNL);
+		if (lasttoken != TIN)
+			raise_error_unexpected_syntax(TIN);
+		cpp = &n1->ncase.cases;
+ next_case:
+		checkkwd = CHKNL | CHKKWD;
+		t = readtoken();
+		while (t != TESAC) {
+			if (lasttoken == TLP)
+				readtoken();
+			*cpp = cp = stzalloc(sizeof(struct nclist));
+			cp->type = NCLIST;
+			app = &cp->nclist.pattern;
+			for (;;) {
+				*app = ap = stzalloc(sizeof(struct narg));
+				ap->type = NARG;
+				/*ap->narg.next = NULL; - stzalloc did it */
+				ap->narg.text = wordtext;
+				ap->narg.backquote = backquotelist;
+				if (readtoken() != TPIPE)
+					break;
+				app = &ap->narg.next;
+				readtoken();
+			}
+			//ap->narg.next = NULL;
+			if (lasttoken != TRP)
+				raise_error_unexpected_syntax(TRP);
+			cp->nclist.body = list(2);
+
+			cpp = &cp->nclist.next;
+
+			checkkwd = CHKNL | CHKKWD;
+			t = readtoken();
+			if (t != TESAC) {
+				if (t != TENDCASE)
+					raise_error_unexpected_syntax(TENDCASE);
+				goto next_case;
+			}
+		}
+		*cpp = NULL;
+		goto redir;
+	case TLP:
+		n1 = stzalloc(sizeof(struct nredir));
+		n1->type = NSUBSHELL;
+		n1->nredir.n = list(0);
+		/*n1->nredir.redirect = NULL; - stzalloc did it */
+		t = TRP;
+		break;
+	case TBEGIN:
+		n1 = list(0);
+		t = TEND;
+		break;
+	case TWORD:
+	case TREDIR:
+		tokpushback = 1;
+		return simplecmd();
+	}
+
+	if (readtoken() != t)
+		raise_error_unexpected_syntax(t);
+
+ redir:
+	/* Now check for redirection which may follow command */
+	checkkwd = CHKKWD | CHKALIAS;
+	rpp = rpp2;
+	while (readtoken() == TREDIR) {
+		*rpp = n2 = redirnode;
+		rpp = &n2->nfile.next;
+		parsefname();
+	}
+	tokpushback = 1;
+	*rpp = NULL;
+	if (redir) {
+		if (n1->type != NSUBSHELL) {
+			n2 = stzalloc(sizeof(struct nredir));
+			n2->type = NREDIR;
+			n2->nredir.n = n1;
+			n1 = n2;
+		}
+		n1->nredir.redirect = redir;
+	}
+	return n1;
+}
+
+#if ENABLE_ASH_BASH_COMPAT
+static int decode_dollar_squote(void)
+{
+	static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
+	int c, cnt;
+	char *p;
+	char buf[4];
+
+	c = pgetc();
+	p = strchr(C_escapes, c);
+	if (p) {
+		buf[0] = c;
+		p = buf;
+		cnt = 3;
+		if ((unsigned char)(c - '0') <= 7) { /* \ooo */
+			do {
+				c = pgetc();
+				*++p = c;
+			} while ((unsigned char)(c - '0') <= 7 && --cnt);
+			pungetc();
+		} else if (c == 'x') { /* \xHH */
+			do {
+				c = pgetc();
+				*++p = c;
+			} while (isxdigit(c) && --cnt);
+			pungetc();
+			if (cnt == 3) { /* \x but next char is "bad" */
+				c = 'x';
+				goto unrecognized;
+			}
+		} else { /* simple seq like \\ or \t */
+			p++;
+		}
+		*p = '\0';
+		p = buf;
+		c = bb_process_escape_sequence((void*)&p);
+	} else { /* unrecognized "\z": print both chars unless ' or " */
+		if (c != '\'' && c != '"') {
+ unrecognized:
+			c |= 0x100; /* "please encode \, then me" */
+		}
+	}
+	return c;
+}
+#endif
+
+/*
+ * If eofmark is NULL, read a word or a redirection symbol.  If eofmark
+ * is not NULL, read a here document.  In the latter case, eofmark is the
+ * word which marks the end of the document and striptabs is true if
+ * leading tabs should be stripped from the document.  The argument c
+ * is the first character of the input token or document.
+ *
+ * Because C does not have internal subroutines, I have simulated them
+ * using goto's to implement the subroutine linkage.  The following macros
+ * will run code that appears at the end of readtoken1.
+ */
+#define CHECKEND()      {goto checkend; checkend_return:;}
+#define PARSEREDIR()    {goto parseredir; parseredir_return:;}
+#define PARSESUB()      {goto parsesub; parsesub_return:;}
+#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
+#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
+#define PARSEARITH()    {goto parsearith; parsearith_return:;}
+static int
+readtoken1(int c, int syntax, char *eofmark, int striptabs)
+{
+	/* NB: syntax parameter fits into smallint */
+	/* c parameter is an unsigned char or PEOF or PEOA */
+	char *out;
+	int len;
+	char line[EOFMARKLEN + 1];
+	struct nodelist *bqlist;
+	smallint quotef;
+	smallint dblquote;
+	smallint oldstyle;
+	smallint prevsyntax; /* syntax before arithmetic */
+#if ENABLE_ASH_EXPAND_PRMT
+	smallint pssyntax;   /* we are expanding a prompt string */
+#endif
+	int varnest;         /* levels of variables expansion */
+	int arinest;         /* levels of arithmetic expansion */
+	int parenlevel;      /* levels of parens in arithmetic */
+	int dqvarnest;       /* levels of variables expansion within double quotes */
+
+	IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;)
+
+#if __GNUC__
+	/* Avoid longjmp clobbering */
+	(void) &out;
+	(void) &quotef;
+	(void) &dblquote;
+	(void) &varnest;
+	(void) &arinest;
+	(void) &parenlevel;
+	(void) &dqvarnest;
+	(void) &oldstyle;
+	(void) &prevsyntax;
+	(void) &syntax;
+#endif
+	startlinno = g_parsefile->linno;
+	bqlist = NULL;
+	quotef = 0;
+	prevsyntax = 0;
+#if ENABLE_ASH_EXPAND_PRMT
+	pssyntax = (syntax == PSSYNTAX);
+	if (pssyntax)
+		syntax = DQSYNTAX;
+#endif
+	dblquote = (syntax == DQSYNTAX);
+	varnest = 0;
+	arinest = 0;
+	parenlevel = 0;
+	dqvarnest = 0;
+
+	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++;
+			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;
+				}
+			}
+#endif
+			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);
+				}
+#endif
+				/* 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);
+				}
+				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);
+			}
+			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;
+				}
+				quotef = 1;
+				goto quotemark;
+			}
+			break;
+		case CVAR:      /* '$' */
+			PARSESUB();             /* parse substitution */
+			break;
+		case CENDVAR:   /* '}' */
+			if (varnest > 0) {
+				varnest--;
+				if (dqvarnest > 0) {
+					dqvarnest--;
+				}
+				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) {
+				parenlevel--;
+			} else {
+				if (pgetc() == ')') {
+					if (--arinest == 0) {
+						syntax = prevsyntax;
+						dblquote = (syntax == DQSYNTAX);
+						c = CTLENDARI;
+					}
+				} else {
+					/*
+					 * unbalanced parens
+					 * (don't 2nd guess - no error)
+					 */
+					pungetc();
+				}
+			}
+			USTPUTC(c, out);
+			break;
+#endif
+		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_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 '))'");
+#endif
+	if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL)
+		raise_error_syntax("unterminated quoted string");
+	if (varnest != 0) {
+		startlinno = g_parsefile->linno;
+		/* { */
+		raise_error_syntax("missing '}'");
+	}
+	USTPUTC('\0', out);
+	len = out - (char *)stackblock();
+	out = stackblock();
+	if (eofmark == NULL) {
+		if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>'))
+		 && quotef == 0
+		) {
+			if (isdigit_str9(out)) {
+				PARSEREDIR(); /* passed as params: out, c */
+				lasttoken = TREDIR;
+				return lasttoken;
+			}
+			/* else: non-number X seen, interpret it
+			 * as "NNNX>file" = "NNNX >file" */
+		}
+		pungetc();
+	}
+	quoteflag = quotef;
+	backquotelist = bqlist;
+	grabstackblock(len);
+	wordtext = out;
+	lasttoken = TWORD;
+	return lasttoken;
+/* end of readtoken routine */
+
+/*
+ * Check to see whether we are at the end of the here document.  When this
+ * is called, c is set to the first character of the next input line.  If
+ * we are at the end of the here document, this routine sets the c to PEOF.
+ */
+checkend: {
+	if (eofmark) {
+#if ENABLE_ASH_ALIAS
+		if (c == PEOA)
+			c = pgetc_without_PEOA();
+#endif
+		if (striptabs) {
+			while (c == '\t') {
+				c = pgetc_without_PEOA();
+			}
+		}
+		if (c == *eofmark) {
+			if (pfgets(line, sizeof(line)) != NULL) {
+				char *p, *q;
+
+				p = line;
+				for (q = eofmark + 1; *q && *p == *q; p++, q++)
+					continue;
+				if (*p == '\n' && *q == '\0') {
+					c = PEOF;
+					g_parsefile->linno++;
+					needprompt = doprompt;
+				} else {
+					pushstring(line, NULL);
+				}
+			}
+		}
+	}
+	goto checkend_return;
+}
+
+/*
+ * Parse a redirection operator.  The variable "out" points to a string
+ * specifying the fd to be redirected.  The variable "c" contains the
+ * first character of the redirection operator.
+ */
+parseredir: {
+	/* out is already checked to be a valid number or "" */
+	int fd = (*out == '\0' ? -1 : atoi(out));
+	union node *np;
+
+	np = stzalloc(sizeof(struct nfile));
+	if (c == '>') {
+		np->nfile.fd = 1;
+		c = pgetc();
+		if (c == '>')
+			np->type = NAPPEND;
+		else if (c == '|')
+			np->type = NCLOBBER;
+		else if (c == '&')
+			np->type = NTOFD;
+			/* it also can be NTO2 (>&file), but we can't figure it out yet */
+		else {
+			np->type = NTO;
+			pungetc();
+		}
+	}
+#if ENABLE_ASH_BASH_COMPAT
+	else if (c == 0x100 + '>') { /* this flags &> redirection */
+		np->nfile.fd = 1;
+		pgetc(); /* this is '>', no need to check */
+		np->type = NTO2;
+	}
+#endif
+	else { /* c == '<' */
+		/*np->nfile.fd = 0; - stzalloc did it */
+		c = pgetc();
+		switch (c) {
+		case '<':
+			if (sizeof(struct nfile) != sizeof(struct nhere)) {
+				np = stzalloc(sizeof(struct nhere));
+				/*np->nfile.fd = 0; - stzalloc did it */
+			}
+			np->type = NHERE;
+			heredoc = stzalloc(sizeof(struct heredoc));
+			heredoc->here = np;
+			c = pgetc();
+			if (c == '-') {
+				heredoc->striptabs = 1;
+			} else {
+				/*heredoc->striptabs = 0; - stzalloc did it */
+				pungetc();
+			}
+			break;
+
+		case '&':
+			np->type = NFROMFD;
+			break;
+
+		case '>':
+			np->type = NFROMTO;
+			break;
+
+		default:
+			np->type = NFROM;
+			pungetc();
+			break;
+		}
+	}
+	if (fd >= 0)
+		np->nfile.fd = fd;
+	redirnode = np;
+	goto parseredir_return;
+}
+
+/*
+ * Parse a substitution.  At this point, we have read the dollar sign
+ * and nothing else.
+ */
+
+/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
+ * (assuming ascii char codes, as the original implementation did) */
+#define is_special(c) \
+	(((unsigned)(c) - 33 < 32) \
+			&& ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
+parsesub: {
+	unsigned char subtype;
+	int typeloc;
+	int flags;
+
+	c = pgetc();
+	if (c > 255 /* PEOA or PEOF */
+	 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
+	) {
+#if ENABLE_ASH_BASH_COMPAT
+		if (c == '\'')
+			bash_dollar_squote = 1;
+		else
+#endif
+			USTPUTC('$', out);
+		pungetc();
+	} else if (c == '(') {
+		/* $(command) or $((arith)) */
+		if (pgetc() == '(') {
+#if ENABLE_SH_MATH_SUPPORT
+			PARSEARITH();
+#else
+			raise_error_syntax("you disabled math support for $((arith)) syntax");
+#endif
+		} else {
+			pungetc();
+			PARSEBACKQNEW();
+		}
+	} else {
+		/* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
+		USTPUTC(CTLVAR, out);
+		typeloc = out - (char *)stackblock();
+		USTPUTC(VSNORMAL, out);
+		subtype = VSNORMAL;
+		if (c == '{') {
+			c = pgetc();
+			if (c == '#') {
+				c = pgetc();
+				if (c == '}')
+					c = '#'; /* ${#} - same as $# */
+				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) {
+			/* ${#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)) {
+//TODO: support more general format ${v:EXPR:EXPR},
+// where EXPR follows $(()) rules
+					subtype = VSSUBSTR;
+					pungetc();
+					break; /* "goto do_pungetc" is bigger (!) */
+				}
+#endif
+				flags = VSNUL;
+				/*FALLTHROUGH*/
+			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);
+				c = pgetc();
+				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 != '/')
+					goto do_pungetc;
+				subtype++; /* VSREPLACEALL */
+				break;
+#endif
+			}
+		} else {
+ do_pungetc:
+			pungetc();
+		}
+		if (dblquote || arinest)
+			flags |= VSQUOTE;
+		((unsigned char *)stackblock())[typeloc] = subtype | flags;
+		if (subtype != VSNORMAL) {
+			varnest++;
+			if (dblquote || arinest) {
+				dqvarnest++;
+			}
+		}
+	}
+	goto parsesub_return;
+}
+
+/*
+ * Called to parse command substitutions.  Newstyle is set if the command
+ * is enclosed inside $(...); nlpp is a pointer to the head of the linked
+ * list of commands (passed by reference), and savelen is the number of
+ * characters on the top of the stack which must be preserved.
+ */
+parsebackq: {
+	struct nodelist **nlpp;
+	smallint savepbq;
+	union node *n;
+	char *volatile str;
+	struct jmploc jmploc;
+	struct jmploc *volatile savehandler;
+	size_t savelen;
+	smallint saveprompt = 0;
+
+#ifdef __GNUC__
+	(void) &saveprompt;
+#endif
+	savepbq = parsebackquote;
+	if (setjmp(jmploc.loc)) {
+		free(str);
+		parsebackquote = 0;
+		exception_handler = savehandler;
+		longjmp(exception_handler->loc, 1);
+	}
+	INT_OFF;
+	str = NULL;
+	savelen = out - (char *)stackblock();
+	if (savelen > 0) {
+		str = ckmalloc(savelen);
+		memcpy(str, stackblock(), savelen);
+	}
+	savehandler = exception_handler;
+	exception_handler = &jmploc;
+	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.  */
+		char *pout;
+		size_t psavelen;
+		char *pstr;
+
+		STARTSTACKSTR(pout);
+		for (;;) {
+			int pc;
+
+			setprompt_if(needprompt, 2);
+			pc = pgetc();
+			switch (pc) {
+			case '`':
+				goto done;
+
+			case '\\':
+				pc = pgetc();
+				if (pc == '\n') {
+					g_parsefile->linno++;
+					setprompt_if(doprompt, 2);
+					/*
+					 * If eating a newline, avoid putting
+					 * the newline into the new character
+					 * stream (via the STPUTC after the
+					 * switch).
+					 */
+					continue;
+				}
+				if (pc != '\\' && pc != '`' && pc != '$'
+				 && (!dblquote || pc != '"')
+				) {
+					STPUTC('\\', pout);
+				}
+				if (pc <= 255 /* not PEOA or PEOF */) {
+					break;
+				}
+				/* fall through */
+
+			case PEOF:
+			IF_ASH_ALIAS(case PEOA:)
+				startlinno = g_parsefile->linno;
+				raise_error_syntax("EOF in backquote substitution");
+
+			case '\n':
+				g_parsefile->linno++;
+				needprompt = doprompt;
+				break;
+
+			default:
+				break;
+			}
+			STPUTC(pc, pout);
+		}
+ done:
+		STPUTC('\0', pout);
+		psavelen = pout - (char *)stackblock();
+		if (psavelen > 0) {
+			pstr = grabstackstr(pout);
+			setinputstring(pstr);
+		}
+	}
+	nlpp = &bqlist;
+	while (*nlpp)
+		nlpp = &(*nlpp)->next;
+	*nlpp = stzalloc(sizeof(**nlpp));
+	/* (*nlpp)->next = NULL; - stzalloc did it */
+	parsebackquote = oldstyle;
+
+	if (oldstyle) {
+		saveprompt = doprompt;
+		doprompt = 0;
+	}
+
+	n = list(2);
+
+	if (oldstyle)
+		doprompt = saveprompt;
+	else if (readtoken() != TRP)
+		raise_error_unexpected_syntax(TRP);
+
+	(*nlpp)->n = n;
+	if (oldstyle) {
+		/*
+		 * Start reading from old file again, ignoring any pushed back
+		 * tokens left from the backquote parsing
+		 */
+		popfile();
+		tokpushback = 0;
+	}
+	while (stackblocksize() <= savelen)
+		growstackblock();
+	STARTSTACKSTR(out);
+	if (str) {
+		memcpy(out, str, savelen);
+		STADJUST(savelen, out);
+		INT_OFF;
+		free(str);
+		str = NULL;
+		INT_ON;
+	}
+	parsebackquote = savepbq;
+	exception_handler = savehandler;
+	if (arinest || dblquote)
+		USTPUTC(CTLBACKQ | CTLQUOTE, out);
+	else
+		USTPUTC(CTLBACKQ, out);
+	if (oldstyle)
+		goto parsebackq_oldreturn;
+	goto parsebackq_newreturn;
+}
+
+#if ENABLE_SH_MATH_SUPPORT
+/*
+ * Parse an arithmetic expansion (indicate start of one and set state)
+ */
+parsearith: {
+	if (++arinest == 1) {
+		prevsyntax = syntax;
+		syntax = ARISYNTAX;
+		USTPUTC(CTLARI, out);
+		if (dblquote)
+			USTPUTC('"', out);
+		else
+			USTPUTC(' ', out);
+	} else {
+		/*
+		 * we collapse embedded arithmetic expansion to
+		 * parenthesis, which should be equivalent
+		 */
+		USTPUTC('(', out);
+	}
+	goto parsearith_return;
+}
+#endif
+
+} /* end of readtoken */
+
+/*
+ * Read the next input token.
+ * If the token is a word, we set backquotelist to the list of cmds in
+ *      backquotes.  We set quoteflag to true if any part of the word was
+ *      quoted.
+ * If the token is TREDIR, then we set redirnode to a structure containing
+ *      the redirection.
+ * In all cases, the variable startlinno is set to the number of the line
+ *      on which the token starts.
+ *
+ * [Change comment:  here documents and internal procedures]
+ * [Readtoken shouldn't have any arguments.  Perhaps we should make the
+ *  word parsing code into a separate routine.  In this case, readtoken
+ *  doesn't need to have any internal procedures, but parseword does.
+ *  We could also make parseoperator in essence the main routine, and
+ *  have parseword (readtoken1?) handle both words and redirection.]
+ */
+#define NEW_xxreadtoken
+#ifdef NEW_xxreadtoken
+/* singles must be first! */
+static const char xxreadtoken_chars[7] ALIGN1 = {
+	'\n', '(', ')', /* singles */
+	'&', '|', ';',  /* doubles */
+	0
+};
+
+#define xxreadtoken_singles 3
+#define xxreadtoken_doubles 3
+
+static const char xxreadtoken_tokens[] ALIGN1 = {
+	TNL, TLP, TRP,          /* only single occurrence allowed */
+	TBACKGND, TPIPE, TSEMI, /* if single occurrence */
+	TEOF,                   /* corresponds to trailing nul */
+	TAND, TOR, TENDCASE     /* if double occurrence */
+};
+
+static int
+xxreadtoken(void)
+{
+	int c;
+
+	if (tokpushback) {
+		tokpushback = 0;
+		return lasttoken;
+	}
+	setprompt_if(needprompt, 2);
+	startlinno = g_parsefile->linno;
+	for (;;) {                      /* until token or start of word found */
+		c = pgetc_fast();
+		if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
+			continue;
+
+		if (c == '#') {
+			while ((c = pgetc()) != '\n' && c != PEOF)
+				continue;
+			pungetc();
+		} else if (c == '\\') {
+			if (pgetc() != '\n') {
+				pungetc();
+				break; /* return readtoken1(...) */
+			}
+			startlinno = ++g_parsefile->linno;
+			setprompt_if(doprompt, 2);
+		} else {
+			const char *p;
+
+			p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
+			if (c != PEOF) {
+				if (c == '\n') {
+					g_parsefile->linno++;
+					needprompt = doprompt;
+				}
+
+				p = strchr(xxreadtoken_chars, c);
+				if (p == NULL)
+					break; /* return readtoken1(...) */
+
+				if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
+					int cc = pgetc();
+					if (cc == c) {    /* double occurrence? */
+						p += xxreadtoken_doubles + 1;
+					} else {
+						pungetc();
+#if ENABLE_ASH_BASH_COMPAT
+						if (c == '&' && cc == '>') /* &> */
+							break; /* return readtoken1(...) */
+#endif
+					}
+				}
+			}
+			lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+			return lasttoken;
+		}
+	} /* for (;;) */
+
+	return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
+}
+#else /* old xxreadtoken */
+#define RETURN(token)   return lasttoken = token
+static int
+xxreadtoken(void)
+{
+	int c;
+
+	if (tokpushback) {
+		tokpushback = 0;
+		return lasttoken;
+	}
+	setprompt_if(needprompt, 2);
+	startlinno = g_parsefile->linno;
+	for (;;) {      /* until token or start of word found */
+		c = pgetc_fast();
+		switch (c) {
+		case ' ': case '\t':
+		IF_ASH_ALIAS(case PEOA:)
+			continue;
+		case '#':
+			while ((c = pgetc()) != '\n' && c != PEOF)
+				continue;
+			pungetc();
+			continue;
+		case '\\':
+			if (pgetc() == '\n') {
+				startlinno = ++g_parsefile->linno;
+				setprompt_if(doprompt, 2);
+				continue;
+			}
+			pungetc();
+			goto breakloop;
+		case '\n':
+			g_parsefile->linno++;
+			needprompt = doprompt;
+			RETURN(TNL);
+		case PEOF:
+			RETURN(TEOF);
+		case '&':
+			if (pgetc() == '&')
+				RETURN(TAND);
+			pungetc();
+			RETURN(TBACKGND);
+		case '|':
+			if (pgetc() == '|')
+				RETURN(TOR);
+			pungetc();
+			RETURN(TPIPE);
+		case ';':
+			if (pgetc() == ';')
+				RETURN(TENDCASE);
+			pungetc();
+			RETURN(TSEMI);
+		case '(':
+			RETURN(TLP);
+		case ')':
+			RETURN(TRP);
+		default:
+			goto breakloop;
+		}
+	}
+ breakloop:
+	return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+#undef RETURN
+}
+#endif /* old xxreadtoken */
+
+static int
+readtoken(void)
+{
+	int t;
+#if DEBUG
+	smallint alreadyseen = tokpushback;
+#endif
+
+#if ENABLE_ASH_ALIAS
+ top:
+#endif
+
+	t = xxreadtoken();
+
+	/*
+	 * eat newlines
+	 */
+	if (checkkwd & CHKNL) {
+		while (t == TNL) {
+			parseheredoc();
+			t = xxreadtoken();
+		}
+	}
+
+	if (t != TWORD || quoteflag) {
+		goto out;
+	}
+
+	/*
+	 * check for keywords
+	 */
+	if (checkkwd & CHKKWD) {
+		const char *const *pp;
+
+		pp = findkwd(wordtext);
+		if (pp) {
+			lasttoken = t = pp - tokname_array;
+			TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1));
+			goto out;
+		}
+	}
+
+	if (checkkwd & CHKALIAS) {
+#if ENABLE_ASH_ALIAS
+		struct alias *ap;
+		ap = lookupalias(wordtext, 1);
+		if (ap != NULL) {
+			if (*ap->val) {
+				pushstring(ap->val, ap);
+			}
+			goto top;
+		}
+#endif
+	}
+ out:
+	checkkwd = 0;
+#if DEBUG
+	if (!alreadyseen)
+		TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
+	else
+		TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : ""));
+#endif
+	return t;
+}
+
+static char
+peektoken(void)
+{
+	int t;
+
+	t = readtoken();
+	tokpushback = 1;
+	return tokname_array[t][0];
+}
+
+/*
+ * Read and parse a command.  Returns NODE_EOF on end of file.
+ * (NULL is a valid parse tree indicating a blank line.)
+ */
+static union node *
+parsecmd(int interact)
+{
+	int t;
+
+	tokpushback = 0;
+	doprompt = interact;
+	setprompt_if(doprompt, doprompt);
+	needprompt = 0;
+	t = readtoken();
+	if (t == TEOF)
+		return NODE_EOF;
+	if (t == TNL)
+		return NULL;
+	tokpushback = 1;
+	return list(1);
+}
+
+/*
+ * Input any here documents.
+ */
+static void
+parseheredoc(void)
+{
+	struct heredoc *here;
+	union node *n;
+
+	here = heredoclist;
+	heredoclist = NULL;
+
+	while (here) {
+		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;
+		/*n->narg.next = NULL; - stzalloc did it */
+		n->narg.text = wordtext;
+		n->narg.backquote = backquotelist;
+		here->here->nhere.doc = n;
+		here = here->next;
+	}
+}
+
+
+/*
+ * called by editline -- any expansions to the prompt should be added here.
+ */
+#if ENABLE_ASH_EXPAND_PRMT
+static const char *
+expandstr(const char *ps)
+{
+	union node n;
+
+	/* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value,
+	 * and token processing _can_ alter it (delete NULs etc). */
+	setinputstring((char *)ps);
+	readtoken1(pgetc(), PSSYNTAX, nullstr, 0);
+	popfile();
+
+	n.narg.type = NARG;
+	n.narg.next = NULL;
+	n.narg.text = wordtext;
+	n.narg.backquote = backquotelist;
+
+	expandarg(&n, NULL, 0);
+	return stackblock();
+}
+#endif
+
+/*
+ * Execute a command or commands contained in a string.
+ */
+static int
+evalstring(char *s, int mask)
+{
+	union node *n;
+	struct stackmark smark;
+	int skip;
+
+	setinputstring(s);
+	setstackmark(&smark);
+
+	skip = 0;
+	while ((n = parsecmd(0)) != NODE_EOF) {
+		evaltree(n, 0);
+		popstackmark(&smark);
+		skip = evalskip;
+		if (skip)
+			break;
+	}
+	popfile();
+
+	skip &= mask;
+	evalskip = skip;
+	return skip;
+}
+
+/*
+ * The eval command.
+ */
+static int FAST_FUNC
+evalcmd(int argc UNUSED_PARAM, char **argv)
+{
+	char *p;
+	char *concat;
+
+	if (argv[1]) {
+		p = argv[1];
+		argv += 2;
+		if (argv[0]) {
+			STARTSTACKSTR(concat);
+			for (;;) {
+				concat = stack_putstr(p, concat);
+				p = *argv++;
+				if (p == NULL)
+					break;
+				STPUTC(' ', concat);
+			}
+			STPUTC('\0', concat);
+			p = grabstackstr(concat);
+		}
+		evalstring(p, ~SKIPEVAL);
+	}
+	return exitstatus;
+}
+
+/*
+ * Read and execute commands.
+ * "Top" is nonzero for the top level command loop;
+ * it turns on prompting if the shell is interactive.
+ */
+static int
+cmdloop(int top)
+{
+	union node *n;
+	struct stackmark smark;
+	int inter;
+	int numeof = 0;
+
+	TRACE(("cmdloop(%d) called\n", top));
+	for (;;) {
+		int skip;
+
+		setstackmark(&smark);
+#if JOBS
+		if (doing_jobctl)
+			showjobs(stderr, SHOW_CHANGED);
+#endif
+		inter = 0;
+		if (iflag && top) {
+			inter++;
+			chkmail();
+		}
+		n = parsecmd(inter);
+#if DEBUG
+		if (DEBUG > 2 && debug && (n != NODE_EOF))
+			showtree(n);
+#endif
+		if (n == NODE_EOF) {
+			if (!top || numeof >= 50)
+				break;
+			if (!stoppedjobs()) {
+				if (!Iflag)
+					break;
+				out2str("\nUse \"exit\" to leave shell.\n");
+			}
+			numeof++;
+		} else if (nflag == 0) {
+			/* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
+			job_warning >>= 1;
+			numeof = 0;
+			evaltree(n, 0);
+		}
+		popstackmark(&smark);
+		skip = evalskip;
+
+		if (skip) {
+			evalskip = 0;
+			return skip & SKIPEVAL;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Take commands from a file.  To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
+ */
+static char *
+find_dot_file(char *name)
+{
+	char *fullname;
+	const char *path = pathval();
+	struct stat statb;
+
+	/* don't try this for absolute or relative paths */
+	if (strchr(name, '/'))
+		return name;
+
+	/* IIRC standards do not say whether . is to be searched.
+	 * And it is even smaller this way, making it unconditional for now:
+	 */
+	if (1) { /* ENABLE_ASH_BASH_COMPAT */
+		fullname = name;
+		goto try_cur_dir;
+	}
+
+	while ((fullname = path_advance(&path, name)) != NULL) {
+ try_cur_dir:
+		if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) {
+			/*
+			 * Don't bother freeing here, since it will
+			 * be freed by the caller.
+			 */
+			return fullname;
+		}
+		if (fullname != name)
+			stunalloc(fullname);
+	}
+
+	/* not found in the PATH */
+	ash_msg_and_raise_error("%s: not found", name);
+	/* NOTREACHED */
+}
+
+static int FAST_FUNC
+dotcmd(int argc, char **argv)
+{
+	char *fullname;
+	struct strlist *sp;
+	volatile struct shparam saveparam;
+
+	for (sp = cmdenviron; sp; sp = sp->next)
+		setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
+
+	if (!argv[1]) {
+		/* bash says: "bash: .: filename argument required" */
+		return 2; /* bash compat */
+	}
+
+	/* "false; . empty_file; echo $?" should print 0, not 1: */
+	exitstatus = 0;
+
+	fullname = find_dot_file(argv[1]);
+
+	argv += 2;
+	argc -= 2;
+	if (argc) { /* argc > 0, argv[0] != NULL */
+		saveparam = shellparam;
+		shellparam.malloced = 0;
+		shellparam.nparam = argc;
+		shellparam.p = argv;
+	};
+
+	setinputfile(fullname, INPUT_PUSH_FILE);
+	commandname = fullname;
+	cmdloop(0);
+	popfile();
+
+	if (argc) {
+		freeparam(&shellparam);
+		shellparam = saveparam;
+	};
+
+	return exitstatus;
+}
+
+static int FAST_FUNC
+exitcmd(int argc UNUSED_PARAM, char **argv)
+{
+	if (stoppedjobs())
+		return 0;
+	if (argv[1])
+		exitstatus = number(argv[1]);
+	raise_exception(EXEXIT);
+	/* NOTREACHED */
+}
+
+/*
+ * Read a file containing shell functions.
+ */
+static void
+readcmdfile(char *name)
+{
+	setinputfile(name, INPUT_PUSH_FILE);
+	cmdloop(0);
+	popfile();
+}
+
+
+/* ============ find_command inplementation */
+
+/*
+ * Resolve a command name.  If you change this routine, you may have to
+ * change the shellexec routine as well.
+ */
+static void
+find_command(char *name, struct cmdentry *entry, int act, const char *path)
+{
+	struct tblentry *cmdp;
+	int idx;
+	int prev;
+	char *fullname;
+	struct stat statb;
+	int e;
+	int updatetbl;
+	struct builtincmd *bcmd;
+
+	/* If name contains a slash, don't use PATH or hash table */
+	if (strchr(name, '/') != NULL) {
+		entry->u.index = -1;
+		if (act & DO_ABS) {
+			while (stat(name, &statb) < 0) {
+#ifdef SYSV
+				if (errno == EINTR)
+					continue;
+#endif
+				entry->cmdtype = CMDUNKNOWN;
+				return;
+			}
+		}
+		entry->cmdtype = CMDNORMAL;
+		return;
+	}
+
+/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
+
+	updatetbl = (path == pathval());
+	if (!updatetbl) {
+		act |= DO_ALTPATH;
+		if (strstr(path, "%builtin") != NULL)
+			act |= DO_ALTBLTIN;
+	}
+
+	/* If name is in the table, check answer will be ok */
+	cmdp = cmdlookup(name, 0);
+	if (cmdp != NULL) {
+		int bit;
+
+		switch (cmdp->cmdtype) {
+		default:
+#if DEBUG
+			abort();
+#endif
+		case CMDNORMAL:
+			bit = DO_ALTPATH;
+			break;
+		case CMDFUNCTION:
+			bit = DO_NOFUNC;
+			break;
+		case CMDBUILTIN:
+			bit = DO_ALTBLTIN;
+			break;
+		}
+		if (act & bit) {
+			updatetbl = 0;
+			cmdp = NULL;
+		} else if (cmdp->rehash == 0)
+			/* if not invalidated by cd, we're done */
+			goto success;
+	}
+
+	/* If %builtin not in path, check for builtin next */
+	bcmd = find_builtin(name);
+	if (bcmd) {
+		if (IS_BUILTIN_REGULAR(bcmd))
+			goto builtin_success;
+		if (act & DO_ALTPATH) {
+			if (!(act & DO_ALTBLTIN))
+				goto builtin_success;
+		} else if (builtinloc <= 0) {
+			goto builtin_success;
+		}
+	}
+
+#if ENABLE_FEATURE_SH_STANDALONE
+	{
+		int applet_no = find_applet_by_name(name);
+		if (applet_no >= 0) {
+			entry->cmdtype = CMDNORMAL;
+			entry->u.index = -2 - applet_no;
+			return;
+		}
+	}
+#endif
+
+	/* We have to search path. */
+	prev = -1;              /* where to start */
+	if (cmdp && cmdp->rehash) {     /* doing a rehash */
+		if (cmdp->cmdtype == CMDBUILTIN)
+			prev = builtinloc;
+		else
+			prev = cmdp->param.index;
+	}
+
+	e = ENOENT;
+	idx = -1;
+ loop:
+	while ((fullname = path_advance(&path, name)) != NULL) {
+		stunalloc(fullname);
+		/* NB: code below will still use fullname
+		 * despite it being "unallocated" */
+		idx++;
+		if (pathopt) {
+			if (prefix(pathopt, "builtin")) {
+				if (bcmd)
+					goto builtin_success;
+				continue;
+			}
+			if ((act & DO_NOFUNC)
+			 || !prefix(pathopt, "func")
+			) {     /* ignore unimplemented options */
+				continue;
+			}
+		}
+		/* if rehash, don't redo absolute path names */
+		if (fullname[0] == '/' && idx <= prev) {
+			if (idx < prev)
+				continue;
+			TRACE(("searchexec \"%s\": no change\n", name));
+			goto success;
+		}
+		while (stat(fullname, &statb) < 0) {
+#ifdef SYSV
+			if (errno == EINTR)
+				continue;
+#endif
+			if (errno != ENOENT && errno != ENOTDIR)
+				e = errno;
+			goto loop;
+		}
+		e = EACCES;     /* if we fail, this will be the error */
+		if (!S_ISREG(statb.st_mode))
+			continue;
+		if (pathopt) {          /* this is a %func directory */
+			stalloc(strlen(fullname) + 1);
+			/* NB: stalloc will return space pointed by fullname
+			 * (because we don't have any intervening allocations
+			 * between stunalloc above and this stalloc) */
+			readcmdfile(fullname);
+			cmdp = cmdlookup(name, 0);
+			if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
+				ash_msg_and_raise_error("%s not defined in %s", name, fullname);
+			stunalloc(fullname);
+			goto success;
+		}
+		TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+		if (!updatetbl) {
+			entry->cmdtype = CMDNORMAL;
+			entry->u.index = idx;
+			return;
+		}
+		INT_OFF;
+		cmdp = cmdlookup(name, 1);
+		cmdp->cmdtype = CMDNORMAL;
+		cmdp->param.index = idx;
+		INT_ON;
+		goto success;
+	}
+
+	/* We failed.  If there was an entry for this command, delete it */
+	if (cmdp && updatetbl)
+		delete_cmd_entry();
+	if (act & DO_ERR)
+		ash_msg("%s: %s", name, errmsg(e, "not found"));
+	entry->cmdtype = CMDUNKNOWN;
+	return;
+
+ builtin_success:
+	if (!updatetbl) {
+		entry->cmdtype = CMDBUILTIN;
+		entry->u.cmd = bcmd;
+		return;
+	}
+	INT_OFF;
+	cmdp = cmdlookup(name, 1);
+	cmdp->cmdtype = CMDBUILTIN;
+	cmdp->param.cmd = bcmd;
+	INT_ON;
+ success:
+	cmdp->rehash = 0;
+	entry->cmdtype = cmdp->cmdtype;
+	entry->u = cmdp->param;
+}
+
+
+/* ============ trap.c */
+
+/*
+ * The trap builtin.
+ */
+static int FAST_FUNC
+trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	char *action;
+	char **ap;
+	int signo, exitcode;
+
+	nextopt(nullstr);
+	ap = argptr;
+	if (!*ap) {
+		for (signo = 0; signo < NSIG; signo++) {
+			char *tr = trap_ptr[signo];
+			if (tr) {
+				/* note: bash adds "SIG", but only if invoked
+				 * as "bash". If called as "sh", or if set -o posix,
+				 * then it prints short signal names.
+				 * We are printing short names: */
+				out1fmt("trap -- %s %s\n",
+						single_quote(tr),
+						get_signame(signo));
+		/* trap_ptr != trap only if we are in special-cased `trap` code.
+		 * In this case, we will exit very soon, no need to free(). */
+				/* if (trap_ptr != trap && tp[0]) */
+				/*	free(tr); */
+			}
+		}
+		/*
+		if (trap_ptr != trap) {
+			free(trap_ptr);
+			trap_ptr = trap;
+		}
+		*/
+		return 0;
+	}
+
+	action = NULL;
+	if (ap[1])
+		action = *ap++;
+	exitcode = 0;
+	while (*ap) {
+		signo = get_signum(*ap);
+		if (signo < 0) {
+			/* Mimic bash message exactly */
+			ash_msg("%s: invalid signal specification", *ap);
+			exitcode = 1;
+			goto next;
+		}
+		INT_OFF;
+		if (action) {
+			if (LONE_DASH(action))
+				action = NULL;
+			else
+				action = ckstrdup(action);
+		}
+		free(trap[signo]);
+		if (action)
+			may_have_traps = 1;
+		trap[signo] = action;
+		if (signo != 0)
+			setsignal(signo);
+		INT_ON;
+ next:
+		ap++;
+	}
+	return exitcode;
+}
+
+
+/* ============ Builtins */
+
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+/*
+ * Lists available builtins
+ */
+static int FAST_FUNC
+helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	unsigned col;
+	unsigned i;
+
+	out1fmt(
+		"Built-in commands:\n"
+		"------------------\n");
+	for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
+		col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
+					builtintab[i].name + 1);
+		if (col > 60) {
+			out1fmt("\n");
+			col = 0;
+		}
+	}
+#if ENABLE_FEATURE_SH_STANDALONE
+	{
+		const char *a = applet_names;
+		while (*a) {
+			col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
+			if (col > 60) {
+				out1fmt("\n");
+				col = 0;
+			}
+			a += strlen(a) + 1;
+		}
+	}
+#endif
+	out1fmt("\n\n");
+	return EXIT_SUCCESS;
+}
+#endif /* FEATURE_SH_EXTRA_QUIET */
+
+/*
+ * The export and readonly commands.
+ */
+static int FAST_FUNC
+exportcmd(int argc UNUSED_PARAM, char **argv)
+{
+	struct var *vp;
+	char *name;
+	const char *p;
+	char **aptr;
+	int flag = argv[0][0] == 'r' ? VREADONLY : VEXPORT;
+
+	if (nextopt("p") != 'p') {
+		aptr = argptr;
+		name = *aptr;
+		if (name) {
+			do {
+				p = strchr(name, '=');
+				if (p != NULL) {
+					p++;
+				} else {
+					vp = *findvar(hashvar(name), name);
+					if (vp) {
+						vp->flags |= flag;
+						continue;
+					}
+				}
+				setvar(name, p, flag);
+			} while ((name = *++aptr) != NULL);
+			return 0;
+		}
+	}
+	showvars(argv[0], flag, 0);
+	return 0;
+}
+
+/*
+ * Delete a function if it exists.
+ */
+static void
+unsetfunc(const char *name)
+{
+	struct tblentry *cmdp;
+
+	cmdp = cmdlookup(name, 0);
+	if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
+		delete_cmd_entry();
+}
+
+/*
+ * The unset builtin command.  We unset the function before we unset the
+ * variable to allow a function to be unset when there is a readonly variable
+ * with the same name.
+ */
+static int FAST_FUNC
+unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	char **ap;
+	int i;
+	int flag = 0;
+	int ret = 0;
+
+	while ((i = nextopt("vf")) != 0) {
+		flag = i;
+	}
+
+	for (ap = argptr; *ap; ap++) {
+		if (flag != 'f') {
+			i = unsetvar(*ap);
+			ret |= i;
+			if (!(i & 2))
+				continue;
+		}
+		if (flag != 'v')
+			unsetfunc(*ap);
+	}
+	return ret & 1;
+}
+
+static const unsigned char timescmd_str[] ALIGN1 = {
+	' ',  offsetof(struct tms, tms_utime),
+	'\n', offsetof(struct tms, tms_stime),
+	' ',  offsetof(struct tms, tms_cutime),
+	'\n', offsetof(struct tms, tms_cstime),
+	0
+};
+static int FAST_FUNC
+timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	unsigned long clk_tck, s, t;
+	const unsigned char *p;
+	struct tms buf;
+
+	clk_tck = sysconf(_SC_CLK_TCK);
+	times(&buf);
+
+	p = timescmd_str;
+	do {
+		t = *(clock_t *)(((char *) &buf) + p[1]);
+		s = t / clk_tck;
+		t = t % clk_tck;
+		out1fmt("%lum%lu.%03lus%c",
+			s / 60, s % 60,
+			(t * 1000) / clk_tck,
+			p[0]);
+		p += 2;
+	} while (*p);
+
+	return 0;
+}
+
+#if ENABLE_SH_MATH_SUPPORT
+/*
+ * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
+ * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
+ *
+ * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ */
+static int FAST_FUNC
+letcmd(int argc UNUSED_PARAM, char **argv)
+{
+	arith_t i;
+
+	argv++;
+	if (!*argv)
+		ash_msg_and_raise_error("expression expected");
+	do {
+		i = ash_arith(*argv);
+	} while (*++argv);
+
+	return !i;
+}
+#endif
+
+/*
+ * The read builtin. Options:
+ *      -r              Do not interpret '\' specially
+ *      -s              Turn off echo (tty only)
+ *      -n NCHARS       Read NCHARS max
+ *      -p PROMPT       Display PROMPT on stderr (if input is from tty)
+ *      -t SECONDS      Timeout after SECONDS (tty or pipe only)
+ *      -u FD           Read from given FD instead of fd 0
+ * This uses unbuffered input, which may be avoidable in some cases.
+ * TODO: bash also has:
+ *      -a ARRAY        Read into array[0],[1],etc
+ *      -d DELIM        End on DELIM char, not newline
+ *      -e              Use line editing (tty only)
+ */
+static int FAST_FUNC
+readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	char *opt_n = NULL;
+	char *opt_p = NULL;
+	char *opt_t = NULL;
+	char *opt_u = NULL;
+	int read_flags = 0;
+	const char *r;
+	int i;
+
+	while ((i = nextopt("p:u:rt:n:s")) != '\0') {
+		switch (i) {
+		case 'p':
+			opt_p = optionarg;
+			break;
+		case 'n':
+			opt_n = optionarg;
+			break;
+		case 's':
+			read_flags |= BUILTIN_READ_SILENT;
+			break;
+		case 't':
+			opt_t = optionarg;
+			break;
+		case 'r':
+			read_flags |= BUILTIN_READ_RAW;
+			break;
+		case 'u':
+			opt_u = optionarg;
+			break;
+		default:
+			break;
+		}
+	}
+
+	r = shell_builtin_read(setvar2,
+		argptr,
+		bltinlookup("IFS"), /* can be NULL */
+		read_flags,
+		opt_n,
+		opt_p,
+		opt_t,
+		opt_u
+	);
+
+	if ((uintptr_t)r > 1)
+		ash_msg_and_raise_error(r);
+
+	return (uintptr_t)r;
+}
+
+static int FAST_FUNC
+umaskcmd(int argc UNUSED_PARAM, char **argv)
+{
+	static const char permuser[3] ALIGN1 = "ugo";
+	static const char permmode[3] ALIGN1 = "rwx";
+	static const short permmask[] ALIGN2 = {
+		S_IRUSR, S_IWUSR, S_IXUSR,
+		S_IRGRP, S_IWGRP, S_IXGRP,
+		S_IROTH, S_IWOTH, S_IXOTH
+	};
+
+	/* TODO: use bb_parse_mode() instead */
+
+	char *ap;
+	mode_t mask;
+	int i;
+	int symbolic_mode = 0;
+
+	while (nextopt("S") != '\0') {
+		symbolic_mode = 1;
+	}
+
+	INT_OFF;
+	mask = umask(0);
+	umask(mask);
+	INT_ON;
+
+	ap = *argptr;
+	if (ap == NULL) {
+		if (symbolic_mode) {
+			char buf[18];
+			char *p = buf;
+
+			for (i = 0; i < 3; i++) {
+				int j;
+
+				*p++ = permuser[i];
+				*p++ = '=';
+				for (j = 0; j < 3; j++) {
+					if ((mask & permmask[3 * i + j]) == 0) {
+						*p++ = permmode[j];
+					}
+				}
+				*p++ = ',';
+			}
+			*--p = 0;
+			puts(buf);
+		} else {
+			out1fmt("%.4o\n", mask);
+		}
+	} else {
+		if (isdigit((unsigned char) *ap)) {
+			mask = 0;
+			do {
+				if (*ap >= '8' || *ap < '0')
+					ash_msg_and_raise_error(msg_illnum, argv[1]);
+				mask = (mask << 3) + (*ap - '0');
+			} while (*++ap != '\0');
+			umask(mask);
+		} else {
+			mask = ~mask & 0777;
+			if (!bb_parse_mode(ap, &mask)) {
+				ash_msg_and_raise_error("illegal mode: %s", ap);
+			}
+			umask(~mask & 0777);
+		}
+	}
+	return 0;
+}
+
+static int FAST_FUNC
+ulimitcmd(int argc UNUSED_PARAM, char **argv)
+{
+	return shell_builtin_ulimit(argv);
+}
+
+/* ============ main() and helpers */
+
+/*
+ * Called to exit the shell.
+ */
+static void
+exitshell(void)
+{
+	struct jmploc loc;
+	char *p;
+	int status;
+
+	status = exitstatus;
+	TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
+	if (setjmp(loc.loc)) {
+		if (exception_type == EXEXIT)
+/* dash bug: it just does _exit(exitstatus) here
+ * but we have to do setjobctl(0) first!
+ * (bug is still not fixed in dash-0.5.3 - if you run dash
+ * under Midnight Commander, on exit from dash MC is backgrounded) */
+			status = exitstatus;
+		goto out;
+	}
+	exception_handler = &loc;
+	p = trap[0];
+	if (p) {
+		trap[0] = NULL;
+		evalstring(p, 0);
+		free(p);
+	}
+	flush_stdout_stderr();
+ out:
+	setjobctl(0);
+	_exit(status);
+	/* NOTREACHED */
+}
+
+static void
+init(void)
+{
+	/* from input.c: */
+	/* we will never free this */
+	basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
+
+	/* from trap.c: */
+	signal(SIGCHLD, SIG_DFL);
+	/* bash re-enables SIGHUP which is SIG_IGNed on entry.
+	 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
+	 */
+	signal(SIGHUP, SIG_DFL);
+
+	/* from var.c: */
+	{
+		char **envp;
+		const char *p;
+		struct stat st1, st2;
+
+		initvar();
+		for (envp = environ; envp && *envp; envp++) {
+			if (strchr(*envp, '=')) {
+				setvareq(*envp, VEXPORT|VTEXTFIXED);
+			}
+		}
+
+		setvar("PPID", utoa(getppid()), 0);
+
+		p = lookupvar("PWD");
+		if (p) {
+			if (*p != '/' || stat(p, &st1) || stat(".", &st2)
+			 || 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.
+ */
+static void
+procargs(char **argv)
+{
+	int i;
+	const char *xminusc;
+	char **xargv;
+
+	xargv = argv;
+	arg0 = xargv[0];
+	/* if (xargv[0]) - mmm, this is always true! */
+		xargv++;
+	for (i = 0; i < NOPTS; i++)
+		optlist[i] = 2;
+	argptr = xargv;
+	if (options(/*cmdline:*/ 1)) {
+		/* it already printed err message */
+		raise_exception(EXERROR);
+	}
+	xargv = argptr;
+	xminusc = minusc;
+	if (*xargv == NULL) {
+		if (xminusc)
+			ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
+		sflag = 1;
+	}
+	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
+		iflag = 1;
+	if (mflag == 2)
+		mflag = iflag;
+	for (i = 0; i < NOPTS; i++)
+		if (optlist[i] == 2)
+			optlist[i] = 0;
+#if DEBUG == 2
+	debug = 1;
+#endif
+	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
+	if (xminusc) {
+		minusc = *xargv++;
+		if (*xargv)
+			goto setarg0;
+	} else if (!sflag) {
+		setinputfile(*xargv, 0);
+ setarg0:
+		arg0 = *xargv++;
+		commandname = arg0;
+	}
+
+	shellparam.p = xargv;
+#if ENABLE_ASH_GETOPTS
+	shellparam.optind = 1;
+	shellparam.optoff = -1;
+#endif
+	/* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
+	while (*xargv) {
+		shellparam.nparam++;
+		xargv++;
+	}
+	optschanged();
+}
+
+/*
+ * Read /etc/profile or .profile.
+ */
+static void
+read_profile(const char *name)
+{
+	int skip;
+
+	if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
+		return;
+	skip = cmdloop(0);
+	popfile();
+	if (skip)
+		exitshell();
+}
+
+/*
+ * This routine is called when an error or an interrupt occurs in an
+ * interactive shell and control is returned to the main command loop.
+ */
+static void
+reset(void)
+{
+	/* from eval.c: */
+	evalskip = 0;
+	loopnest = 0;
+	/* from input.c: */
+	g_parsefile->left_in_buffer = 0;
+	g_parsefile->left_in_line = 0;      /* clear input buffer */
+	popallfiles();
+	/* from parser.c: */
+	tokpushback = 0;
+	checkkwd = 0;
+	/* from redir.c: */
+	clearredir(/*drop:*/ 0);
+}
+
+#if PROFILE
+static short profile_buf[16384];
+extern int etext();
+#endif
+
+/*
+ * Main routine.  We initialize things, parse the arguments, execute
+ * profiles if we're a login shell, and then call cmdloop to execute
+ * commands.  The setjmp call sets up the location to jump to when an
+ * exception occurs.  When an exception occurs the variable "state"
+ * is used to figure out how far we had gotten.
+ */
+int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ash_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *shinit;
+	volatile smallint state;
+	struct jmploc jmploc;
+	struct stackmark smark;
+
+	/* Initialize global data */
+	INIT_G_misc();
+	INIT_G_memstack();
+	INIT_G_var();
+#if ENABLE_ASH_ALIAS
+	INIT_G_alias();
+#endif
+	INIT_G_cmdtable();
+
+#if PROFILE
+	monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
+#endif
+
+#if ENABLE_FEATURE_EDITING
+	line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
+#endif
+	state = 0;
+	if (setjmp(jmploc.loc)) {
+		smallint e;
+		smallint s;
+
+		reset();
+
+		e = exception_type;
+		if (e == EXERROR)
+			exitstatus = 2;
+		s = state;
+		if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
+			exitshell();
+		}
+		if (e == EXINT) {
+			outcslow('\n', stderr);
+		}
+
+		popstackmark(&smark);
+		FORCE_INT_ON; /* enable interrupts */
+		if (s == 1)
+			goto state1;
+		if (s == 2)
+			goto state2;
+		if (s == 3)
+			goto state3;
+		goto state4;
+	}
+	exception_handler = &jmploc;
+#if DEBUG
+	opentrace();
+	TRACE(("Shell args: "));
+	trace_puts_args(argv);
+#endif
+	rootpid = getpid();
+
+	init();
+	setstackmark(&smark);
+	procargs(argv);
+
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+	if (iflag) {
+		const char *hp = lookupvar("HISTFILE");
+		if (!hp) {
+			hp = lookupvar("HOME");
+			if (hp) {
+				char *defhp = concat_path_file(hp, ".ash_history");
+				setvar("HISTFILE", defhp, 0);
+				free(defhp);
+			}
+		}
+	}
+#endif
+	if (argv[0] && argv[0][0] == '-')
+		isloginsh = 1;
+	if (isloginsh) {
+		state = 1;
+		read_profile("/etc/profile");
+ state1:
+		state = 2;
+		read_profile(".profile");
+	}
+ state2:
+	state = 3;
+	if (
+#ifndef linux
+	 getuid() == geteuid() && getgid() == getegid() &&
+#endif
+	 iflag
+	) {
+		shinit = lookupvar("ENV");
+		if (shinit != NULL && *shinit != '\0') {
+			read_profile(shinit);
+		}
+	}
+ state3:
+	state = 4;
+	if (minusc) {
+		/* evalstring pushes parsefile stack.
+		 * Ensure we don't falsely claim that 0 (stdin)
+		 * is one of stacked source fds.
+		 * Testcase: ash -c 'exec 1>&0' must not complain. */
+		// if (!sflag) g_parsefile->pf_fd = -1;
+		// ^^ not necessary since now we special-case fd 0
+		// in is_hidden_fd() to not be considered "hidden fd"
+		evalstring(minusc, 0);
+	}
+
+	if (sflag || minusc == NULL) {
+#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
+		if (iflag) {
+			const char *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 */
+		cmdloop(1);
+	}
+#if PROFILE
+	monitor(0);
+#endif
+#ifdef GPROF
+	{
+		extern void _mcleanup(void);
+		_mcleanup();
+	}
+#endif
+	TRACE(("End of main reached\n"));
+	exitshell();
+	/* NOTREACHED */
+}
+
+
+/*-
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/shell/ash_doc.txt b/busybox-1.19.3/shell/ash_doc.txt
new file mode 100644
index 0000000..2aa4443
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_doc.txt
@@ -0,0 +1,122 @@
+	Wait + signals
+
+We had some bugs here which are hard to test in testsuite.
+
+Bug 1280 (http://busybox.net/bugs/view.php?id=1280):
+was misbehaving in interactive ash. Correct behavior:
+
+$ sleep 20 &
+$ wait
+^C
+$ wait
+^C
+$ wait
+^C
+...
+
+
+Bug 1984 (http://busybox.net/bugs/view.php?id=1984):
+traps were not triggering:
+
+trap_handler_usr () {
+    echo trap usr
+}
+trap_handler_int () {
+    echo trap int
+}
+trap trap_handler_usr USR1
+trap trap_handler_int INT
+sleep 3600 &
+echo "Please do: kill -USR1 $$"
+echo "or: kill -INT $$"
+while true; do wait; echo wait interrupted; done
+
+
+Bug 189 (https://bugs.busybox.net/show_bug.cgi?id=189)
+
+func() {
+    sleep 1
+}
+while (true); do
+    func
+    echo Looping
+done
+
+^C was observed to make ash processes geometrically multiply (!) instead
+of exiting. (true) in subshell does not seem to matter, as another user
+reports the same with:
+
+trap "echo USR1" USR1
+while true; do
+    echo Sleeping
+    sleep 5
+done
+
+Compat note.
+Bash version 3.2.0(1) exits this script at the receipt of SIGINT
+_only_ if it had two last children die from it.
+The following trace was obtained while periodically running
+"killall -SIGINT sleep; sleep 0.1; kill -SIGINT <bash_PID>":
+
+23:48:32.376707 clone(...) = 13528
+23:48:32.388706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted)
+23:48:32.459761 --- SIGINT (Interrupt) @ 0 (0) ---
+    kill -SIGINT <bash_PID> is ignored, back to waiting:
+23:48:32.463706 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 13528
+    sleep exited with 0
+23:48:37.377557 --- SIGCHLD (Child exited) @ 0 (0) ---
+23:48:37.378451 clone(...) = 13538
+23:48:37.390708 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13538
+    sleep was killed by "killall -SIGINT sleep"
+23:48:38.523944 --- SIGCHLD (Child exited) @ 0 (0) ---
+23:48:38.524861 clone(...) = 13542
+23:48:38.538706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted)
+23:48:38.624761 --- SIGINT (Interrupt) @ 0 (0) ---
+    kill -SIGINT <bash_PID> is ignored, back to waiting:
+23:48:38.628706 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 13542
+    sleep exited with 0
+23:48:43.525674 --- SIGCHLD (Child exited) @ 0 (0) ---
+23:48:43.526563 clone(...) = 13545
+23:48:43.538709 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13545
+    sleep was killed by "killall -SIGINT sleep"
+23:48:44.466848 --- SIGCHLD (Child exited) @ 0 (0) ---
+23:48:44.467735 clone(...) = 13549
+23:48:44.481706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted)
+23:48:44.567757 --- SIGINT (Interrupt) @ 0 (0) ---
+    kill -SIGINT <bash_PID> is ignored, back to waiting:
+23:48:44.571706 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 13549
+    sleep exited with 0
+23:48:49.468553 --- SIGCHLD (Child exited) @ 0 (0) ---
+23:48:49.469445 clone(...) = 13551
+23:48:49.481708 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13551
+    sleep was killed by "killall -SIGINT sleep"
+23:48:50.515837 --- SIGCHLD (Child exited) @ 0 (0) ---
+23:48:50.516718 clone(...) = 13555
+23:48:50.530706 waitpid(-1, 0xffc832ec, 0) = ? ERESTARTSYS (To be restarted)
+23:48:50.615761 --- SIGINT (Interrupt) @ 0 (0) ---
+    kill -SIGINT <bash_PID> is ignored, back to waiting:
+23:48:50.619705 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 13555
+    sleep was killed by "killall -SIGINT sleep".
+    This is the second one in a row. Kill ourself:
+23:48:51.504604 kill(13515, SIGINT)     = 0
+23:48:51.504689 --- SIGINT (Interrupt) @ 0 (0) ---
+23:48:51.504915 +++ killed by SIGINT +++
+
+As long as there is at least one "sleep 5" which exited successfully
+(not killed by SIGINT), bash continues. This is not documented anywhere
+AFAIKS.
+
+Why keyboard ^C acts differently?
+
+00:08:07.655985 clone(...) = 14270
+00:08:07.669707 waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0) = 14270
+00:08:12.656872 --- SIGCHLD (Child exited) @ 0 (0) ---
+00:08:12.657743 clone(...) = 14273
+00:08:12.671708 waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}], 0) = 14273
+00:08:13.810778 --- SIGINT (Interrupt) @ 0 (0) ---
+00:08:13.818705 kill(14269, SIGINT)     = 0
+00:08:13.820103 --- SIGINT (Interrupt) @ 0 (0) ---
+00:08:13.820925 +++ killed by SIGINT +++
+
+Perhaps because at the moment bash got SIGINT it had no children?
+(it did not manage to spawn new sleep yet, see the trace)
diff --git a/busybox-1.19.3/shell/ash_ptr_hack.c b/busybox-1.19.3/shell/ash_ptr_hack.c
new file mode 100644
index 0000000..f698408
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_ptr_hack.c
@@ -0,0 +1,29 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Copyright (C) 2008 by Denys Vlasenko <vda.linux@googlemail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+struct globals_misc;
+struct globals_memstack;
+struct globals_var;
+
+#ifndef GCC_COMBINE
+
+/* We cheat here. They are declared as const ptr in ash.c,
+ * but here we make them live in R/W memory */
+struct globals_misc     *ash_ptr_to_globals_misc;
+struct globals_memstack *ash_ptr_to_globals_memstack;
+struct globals_var      *ash_ptr_to_globals_var;
+
+#else
+
+/* gcc -combine will see through and complain */
+/* Using alternative method which is more likely to break
+ * on weird architectures, compilers, linkers and so on */
+struct globals_misc     *const ash_ptr_to_globals_misc __attribute__ ((section (".data")));
+struct globals_memstack *const ash_ptr_to_globals_memstack __attribute__ ((section (".data")));
+struct globals_var      *const ash_ptr_to_globals_var __attribute__ ((section (".data")));
+
+#endif
diff --git a/busybox-1.19.3/shell/ash_test/ash-alias/alias.right b/busybox-1.19.3/shell/ash_test/ash-alias/alias.right
new file mode 100644
index 0000000..0667b21
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-alias/alias.right
@@ -0,0 +1,4 @@
+alias: 0
+alias: 0
+./alias.tests: line 25: qfoo: not found
+quux
diff --git a/busybox-1.19.3/shell/ash_test/ash-alias/alias.tests b/busybox-1.19.3/shell/ash_test/ash-alias/alias.tests
new file mode 100755
index 0000000..8d07b0b
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-alias/alias.tests
@@ -0,0 +1,37 @@
+# place holder for future alias testing
+#ash# shopt -s expand_aliases
+
+# alias/unalias tests originally in builtins.tests
+
+unalias -a
+# this should return success, according to POSIX.2
+alias
+echo alias: $?
+alias foo=bar
+unalias foo
+# this had better return success, according to POSIX.2
+alias
+echo alias: $?
+
+# bug in all versions through bash-2.05b
+
+unalias qfoo qbar qbaz quux 2>/dev/null
+
+alias qfoo=qbar
+alias qbar=qbaz
+alias qbaz=quux
+alias quux=qfoo
+
+qfoo
+
+unalias qfoo qbar qbaz quux
+
+unalias -a
+
+alias foo='echo '
+alias bar=baz
+alias baz=quux
+
+foo bar
+
+unalias foo bar baz
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/README.ash b/busybox-1.19.3/shell/ash_test/ash-arith/README.ash
new file mode 100644
index 0000000..7da22ef
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/README.ash
@@ -0,0 +1 @@
+there is no support for (( )) constructs in ash
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith-bash1.right b/busybox-1.19.3/shell/ash_test/ash-arith/arith-bash1.right
new file mode 100644
index 0000000..b261da1
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith-bash1.right
@@ -0,0 +1,2 @@
+1
+0
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith-bash1.tests b/busybox-1.19.3/shell/ash_test/ash-arith/arith-bash1.tests
new file mode 100755
index 0000000..b37b730
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith-bash1.tests
@@ -0,0 +1,5 @@
+# checks for [[ ]]
+
+# && and ||
+[[ a && "" ]]; echo $?
+[[ a || "" ]]; echo $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith-for.right b/busybox-1.19.3/shell/ash_test/ash-arith/arith-for.right
new file mode 100644
index 0000000..88dbc15
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith-for.right
@@ -0,0 +1,74 @@
+0
+1
+2
+0
+1
+2
+0
+1
+2
+0
+2
+4
+fx is a function
+fx ()
+{
+    i=0;
+    for ((1; i < 3; i++ ))
+    do
+        echo $i;
+    done;
+    for ((i=0; 1; i++ ))
+    do
+        if (( i >= 3 )); then
+            break;
+        fi;
+        echo $i;
+    done;
+    for ((i=0; i<3; 1))
+    do
+        echo $i;
+        (( i++ ));
+    done;
+    i=0;
+    for ((1; 1; 1))
+    do
+        if (( i > 2 )); then
+            break;
+        fi;
+        echo $i;
+        (( i++ ));
+    done;
+    i=0;
+    for ((1; 1; 1))
+    do
+        if (( i > 2 )); then
+            break;
+        fi;
+        echo $i;
+        (( i++ ));
+    done
+}
+0
+1
+2
+0
+1
+2
+0
+1
+2
+0
+1
+2
+0
+1
+2
+./arith-for.tests: line 77: syntax error: arithmetic expression required
+./arith-for.tests: line 77: syntax error: `(( i=0; "i < 3" ))'
+2
+./arith-for.tests: line 83: syntax error: `;' unexpected
+./arith-for.tests: line 83: syntax error: `(( i=0; i < 3; i++; 7 ))'
+2
+20
+20
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith-for.testsx b/busybox-1.19.3/shell/ash_test/ash-arith/arith-for.testsx
new file mode 100755
index 0000000..4fa30ff
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith-for.testsx
@@ -0,0 +1,94 @@
+fx()
+{
+i=0
+for (( ; i < 3; i++ ))
+do
+	echo $i
+done
+
+for (( i=0; ; i++ ))
+do
+	if (( i >= 3 )); then
+		break;
+	fi
+	echo $i
+done
+
+for (( i=0; i<3; ))
+do
+	echo $i
+	(( i++ ))
+done
+
+i=0
+for (( ; ; ))
+do
+	if (( i > 2 )); then
+		break;
+	fi
+	echo $i;
+	(( i++ ))
+done
+
+i=0
+for ((;;))
+do
+	if (( i > 2 )); then
+		break;
+	fi
+	echo $i;
+	(( i++ ))
+done
+}
+
+for (( i=0; "i < 3" ; i++ ))
+do
+	echo $i
+done
+
+i=0
+for (( ; "i < 3"; i++ ))
+do
+	echo $i
+done
+
+for (( i=0; ; i++ ))
+do
+	if (( i >= 3 )); then
+		break;
+	fi
+	echo $i
+done
+
+for ((i = 0; ;i++ ))
+do
+	echo $i
+	if (( i < 3 )); then
+		(( i++ ))
+		continue;
+	fi
+	break
+done
+
+type fx
+fx
+
+# errors
+for (( i=0; "i < 3" ))
+do
+	echo $i
+done
+echo $?
+
+for (( i=0; i < 3; i++; 7 ))
+do
+	echo $i
+done
+echo $?
+
+# one-liners added in post-bash-2.04
+for     ((i=0; i < 20; i++)) do : ; done
+echo $i
+
+for     ((i=0; i < 20; i++)) { : ; }
+echo $i
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith.right b/busybox-1.19.3/shell/ash_test/ash-arith/arith.right
new file mode 100644
index 0000000..9b9ca8e
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith.right
@@ -0,0 +1,138 @@
+Format: 'expected actual'
+163 163
+4 4
+16 16
+8 8
+2 2
+4 4
+2 2
+2 2
+1 1
+0 0
+0 0
+0 0
+1 1
+1 1
+2 2
+-3 -3
+-2 -2
+1 1
+0 0
+2 2
+131072 131072
+29 29
+33 33
+49 49
+1 1
+1 1
+0 0
+0 0
+1 1
+1 1
+1 1
+2 2
+3 3
+1 1
+58 58
+2 2
+60 60
+1 1
+256 256
+16 16
+62 62
+4 4
+29 29
+5 5
+-4 -4
+4 4
+1 1
+32 32
+32 32
+1 1
+1 1
+32 32
+20 20
+30 30
+20 20
+30 30
+./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: arithmetic syntax error
+./arith.tests: line 165: divide by zero
+./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: arithmetic syntax error
+16 16
+./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: arithmetic syntax error
+./arith.tests: line 208: arithmetic syntax error
+9 9
+9 9
+9 9
+7 7
+7
+4 4
+32767 32767
+32768 32768
+131072 131072
+2147483647 2147483647
+1 1
+4 4
+4 4
+5 5
+5 5
+4 4
+3 3
+3 3
+4 4
+4 4
+./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: 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
+4 4
+0 0
+3 3
+7 7
+2 2
+-2 -2
+1 1
+./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: arithmetic syntax error
+./arith2.sub: line 47: arithmetic syntax error
+8 12
+./arith.tests: line 290: arithmetic syntax error
+42
+42
+42
+./arith.tests: line 302: a[b[c]d]=e: not found
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith.tests b/busybox-1.19.3/shell/ash_test/ash-arith/arith.tests
new file mode 100755
index 0000000..d65758e
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith.tests
@@ -0,0 +1,302 @@
+#ash# set +o posix
+#ash# declare -i iv jv
+
+echo "Format: 'expected actual'"
+
+iv=$(( 3 + 5 * 32 ))
+echo 163 $iv
+#ash# iv=iv+3
+#ash# echo 166 $iv
+iv=2
+jv=iv
+
+let "jv *= 2"
+echo 4 $jv
+jv=$(( $jv << 2 ))
+echo 16 $jv
+
+let jv="$jv / 2"
+echo 8 $jv
+#ash# jv="jv >> 2"
+      let jv="jv >> 2"
+echo 2 $jv
+
+iv=$((iv+ $jv))
+echo 4 $iv
+echo 2 $((iv -= jv))
+echo 2 $iv
+echo 1 $(( iv == jv ))
+echo 0 $(( iv != $jv ))
+echo 0 $(( iv < jv ))
+echo 0 $(( $iv > $jv ))
+echo 1 $(( iv <= $jv ))
+echo 1 $(( $iv >= jv ))
+
+echo 2 $jv
+echo -3 $(( ~$jv ))
+echo -2 $(( ~1 ))
+echo 1 $(( ! 0 ))
+
+echo 0 $(( jv % 2 ))
+echo 2 $(( $iv % 4 ))
+
+echo 131072 $(( iv <<= 16 ))
+echo 29 $(( iv %= 33 ))
+
+echo 33 $(( 33 & 55 ))
+echo 49 $(( 33 | 17 ))
+
+echo 1 $(( iv && $jv ))
+echo 1 $(( $iv || jv ))
+
+echo 0 $(( iv && 0 ))
+echo 0 $(( iv & 0 ))
+echo 1 $(( iv && 1 ))
+echo 1 $(( iv & 1 ))
+
+echo 1 $(( $jv || 0 ))
+echo 2 $(( jv | 0 ))
+echo 3 $(( jv | 1 ))
+echo 1 $(( $jv || 1 ))
+
+let 'iv *= jv'
+echo 58 $iv
+echo 2 $jv
+let "jv += $iv"
+echo 60 $jv
+
+echo 1 $(( jv /= iv ))
+echo 256 $(( jv <<= 8 ))
+echo 16 $(( jv >>= 4 ))
+
+echo 62 $(( iv |= 4 ))
+echo 4 $(( iv &= 4 ))
+
+echo 29 $(( iv += (jv + 9)))
+echo 5 $(( (iv + 4) % 7 ))
+
+# unary plus, minus
+echo -4 $(( +4 - 8 ))
+echo 4 $(( -4 + 8 ))
+
+# conditional expressions
+echo 1 $(( 4<5 ? 1 : 32))
+echo 32 $(( 4>5 ? 1 : 32))
+echo 32 $(( 4>(2+3) ? 1 : 32))
+echo 1 $(( 4<(2+3) ? 1 : 32))
+echo 1 $(( (2+2)<(2+3) ? 1 : 32))
+echo 32 $(( (2+2)>(2+3) ? 1 : 32))
+
+# check that the unevaluated part of the ternary operator does not do
+# evaluation or assignment
+x=i+=2
+y=j+=2
+#ash# declare -i i=1 j=1
+      i=1
+      j=1
+echo 20 $((1 ? 20 : (x+=2)))
+#ash# echo $i,$x             # ash mishandles this
+echo 30 $((0 ? (y+=2) : 30))
+#ash# echo $j,$y             # ash mishandles this
+
+x=i+=2
+y=j+=2
+#ash# declare -i i=1 j=1
+      i=1
+      j=1
+echo 20 $((1 ? 20 : (x+=2)))
+#ash# echo $i,$x             # ash mishandles this
+echo 30 $((0 ? (y+=2) : 30))
+#ash# echo $i,$y             # ash mishandles this
+
+# check precedence of assignment vs. conditional operator
+# should be an error
+#ash# declare -i x=2
+      x=2
+#ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash:
+(  y=$((1 ? 20 : x+=2))  )
+
+# check precedence of assignment vs. conditional operator
+#ash# declare -i x=2
+      x=2
+# ash says "line NNN: syntax error: 0 ? x+=2 : 20"
+#ash# echo 20 $((0 ? x+=2 : 20))
+
+# associativity of assignment-operator operator
+#ash# declare -i i=1 j=2 k=3
+i=1
+j=2
+k=3
+echo 6 $((i += j += k))
+echo 6,5,3 $i,$j,$k
+
+# octal, hex
+echo 263 $(( 0x100 | 007 ))
+echo 255 $(( 0xff ))
+#ash# echo 255 $(( 16#ff ))
+#ash# echo 127 $(( 16#FF/2 ))
+#ash# echo 36 $(( 8#44 ))
+
+echo 40 $(( 8 ^ 32 ))
+
+#ash# # other bases
+#ash# echo 10 $(( 16#a ))
+#ash# echo 10 $(( 32#a ))
+#ash# echo 10 $(( 56#a ))
+#ash# echo 10 $(( 64#a ))
+#ash#
+#ash# echo 10 $(( 16#A ))
+#ash# echo 10 $(( 32#A ))
+#ash# echo 36 $(( 56#A ))
+#ash# echo 36 $(( 64#A ))
+#ash#
+#ash# echo 62 $(( 64#@ ))
+#ash# echo 63 $(( 64#_ ))
+
+#ash# # weird bases (error)
+#ash# echo $(( 3425#56 ))
+
+#ash# # missing number after base
+#ash# echo 0 $(( 2# ))
+
+# these should generate errors
+(  echo $(( 7 = 43 ))      )
+#ash# echo $(( 2#44 ))
+(  echo $(( 44 / 0 ))      )
+(  let 'jv += $iv'         )
+(  echo $(( jv += \$iv ))  )
+(  let 'rv = 7 + (43 * 6'  )
+
+#ash# # more errors
+#ash# declare -i i
+#ash# i=0#4
+#ash# i=2#110#11
+
+((echo abc; echo def;); echo ghi)
+
+#ash# if (((4+4) + (4 + 7))); then
+#ash# 	echo ok
+#ash# fi
+
+#ash# (())	# make sure the null expression works OK
+
+#ash# a=(0 2 4 6)
+#ash# echo 6 $(( a[1] + a[2] ))
+#ash# echo 1 $(( (a[1] + a[2]) == a[3] ))
+#ash# (( (a[1] + a[2]) == a[3] )) ; echo 0 $?
+
+# test pushing and popping the expression stack
+unset A
+A="4 + "
+(  echo A $(( ( 4 + A ) + 4 ))  )
+A="3 + 5"
+echo 16 $(( ( 4 + A ) + 4 ))
+
+# badly-formed conditional expressions
+(  echo $(( 4 ? : $A ))  )
+(  echo $(( 1 ? 20 ))    )
+(  echo $(( 4 ? 20 : ))  )
+
+# precedence and short-circuit evaluation
+B=9
+echo 9 $B
+
+# error
+(  echo $(( 0 && B=42 )); echo 9 $B  )
+
+# error
+(  echo $(( 1 || B=88 )); echo 9 $B  )
+
+# ash mistakenly evaluates B=... below
+#ash# echo 0 $(( 0 && (B=42) ))
+echo 9 $B
+#ash# echo 0 $(( (${$} - $$) && (B=42) ))
+echo 9 $B
+#ash# echo 1 $(( 1 || (B=88) ))
+echo 9 $B
+
+
+# until command with (( )) command
+x=7
+
+echo 7 $x
+#ash# until (( x == 4 ))
+      until test "$x" = 4
+do
+	echo $x
+	x=4
+done
+
+echo 4 $x
+
+# exponentiation
+echo 32767 $(( 2**15 - 1))
+echo 32768 $(( 2**(16-1)))
+echo 131072 $(( 2**16*2 ))
+echo 2147483647 $(( 2**31-1))
+echo 1 $(( 2**0 ))
+
+# {pre,post}-{inc,dec}rement and associated errors
+
+x=4
+
+echo 4 $x
+echo 4 $(( x++ ))
+echo 5 $x
+echo 5 $(( x-- ))
+echo 4 $x
+
+echo 3 $(( --x ))
+echo 3 $x
+
+echo 4 $(( ++x ))
+echo 4 $x
+
+# bash 3.2 apparently thinks that ++7 is 7
+#ash# echo 7 $(( ++7 ))
+(  echo $(( 7-- ))    )
+
+(  echo $(( --x=7 ))  )
+(  echo $(( ++x=7 ))  )
+
+(  echo $(( x++=7 ))  )
+(  echo $(( x--=7 ))  )
+
+echo 4 $x
+
+echo 7 $(( +7 ))
+echo -7 $(( -7 ))
+
+# bash 3.2 apparently thinks that ++7 is 7
+#ash# echo $(( ++7 ))
+#ash# echo $(( --7 ))
+
+${THIS_SH} ./arith1.sub
+${THIS_SH} ./arith2.sub
+
+x=4
+y=7
+
+#ash# (( x=8 , y=12 ))
+      x=8
+      y=12
+echo $x $y
+
+#ash# # should be an error
+#ash# (( x=9 y=41 ))
+
+# These are errors
+unset b
+(  echo $((a b))  )
+#ash# ((a b))
+
+n=42
+printf "%d\n" $n
+printf "%i\n" $n
+#ash# echo $(( 8#$(printf "%o\n" $n) ))
+printf "%u\n" $n
+#ash# echo $(( 16#$(printf "%x\n" $n) ))
+#ash# echo $(( 16#$(printf "%X\n" $n) ))
+
+# causes longjmp botches through bash-2.05b
+a[b[c]d]=e
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith1.sub b/busybox-1.19.3/shell/ash_test/ash-arith/arith1.sub
new file mode 100755
index 0000000..80aa999
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith1.sub
@@ -0,0 +1,40 @@
+# test of redone post-increment and post-decrement code
+(  echo $(( 4-- ))   )
+(  echo $(( 4++ ))   )
+(  echo $(( 4 -- ))  )
+(  echo $(( 4 ++ ))  )
+
+#ash# (( array[0]++ ))
+#ash# echo ${array}
+
+#ash# (( array[0] ++ ))
+#ash# echo ${array}
+
+#ash# (( a++ ))
+#ash# echo $a
+#ash# (( a ++ ))
+#ash# echo $a
+      a=2
+
+echo 6 $(( a ++ + 4 ))
+echo 3 $a
+
+echo 7 $(( a+++4 ))
+echo 4 $a
+
+echo 0 $(( a---4 ))
+echo 3 $a
+
+echo 7 $(( a -- + 4 ))
+echo 2 $a
+
+echo -2 $(( a -- - 4 ))
+echo 1 $a
+
+#ash# (( ++ + 7 ))
+
+#ash# (( ++ ))
+(  echo $(( +++7 ))  )
+# bash 3.2 apparently thinks that ++ +7 is 7
+#ash# echo $(( ++ + 7 ))
+#ash# (( -- ))
diff --git a/busybox-1.19.3/shell/ash_test/ash-arith/arith2.sub b/busybox-1.19.3/shell/ash_test/ash-arith/arith2.sub
new file mode 100755
index 0000000..f7e3c92
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-arith/arith2.sub
@@ -0,0 +1,57 @@
+# bash 3.2 apparently thinks that ++7 is 7 etc
+(  echo $(( --7 ))   )
+(  echo $(( ++7 ))   )
+(  echo $(( -- 7 ))  )
+(  echo $(( ++ 7 ))  )
+
+#ash# ((++array[0] ))
+#ash# echo 1 $array
+#ash# (( ++ array[0] ))
+#ash# echo 2 $array
+
+#ash# (( ++a ))
+#ash# echo 1 $a
+#ash# (( ++ a ))
+#ash# echo 2 $a
+
+#ash# (( --a ))
+#ash# echo 1 $a
+#ash# (( -- a ))
+#ash# echo 0 $a
+      a=0
+
+echo 5 $(( 4 + ++a ))
+echo 1 $a
+
+# ash doesn't handle it right...
+#ash# echo 6 $(( 4+++a ))
+#ash# echo 2 $a
+      a=2
+
+# ash doesn't handle it right...
+#ash# echo 3 $(( 4---a ))
+#ash# echo 1 $a
+      a=1
+
+echo 4 $(( 4 - -- a ))
+echo 0 $a
+
+#ash# (( -- ))
+# bash 3.2 apparently thinks that ---7 is -7
+#ash# echo $(( ---7 ))
+(  echo $(( -- - 7 ))  )
+
+#ash# (( ++ ))
+# bash 3.2: 7
+#ash# echo 7 $(( ++7 ))
+(  echo $(( ++ + 7 ))  )
+
+# bash 3.2: -7
+#ash# echo -7 $(( ++-7 ))
+# bash 3.2: -7
+#ash# echo -7 $(( ++ - 7 ))
+
+# bash 3.2: 7
+#ash# echo 7 $(( +--7 ))
+# bash 3.2: 7
+#ash# echo 7 $(( -- + 7 ))
diff --git a/busybox-1.19.3/shell/ash_test/ash-heredoc/heredoc.right b/busybox-1.19.3/shell/ash_test/ash-heredoc/heredoc.right
new file mode 100644
index 0000000..baf1151
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-heredoc/heredoc.right
@@ -0,0 +1,21 @@
+there
+one - alpha
+two - beta
+three - gamma
+hi\
+there$a
+stuff
+hi\
+there
+EO\
+F
+hi
+tab 1
+tab 2
+tab 3
+abc
+def ghi
+jkl mno
+fff is a shell function
+hi
+there
diff --git a/busybox-1.19.3/shell/ash_test/ash-heredoc/heredoc.tests b/busybox-1.19.3/shell/ash_test/ash-heredoc/heredoc.tests
new file mode 100755
index 0000000..b3cdc3f
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-heredoc/heredoc.tests
@@ -0,0 +1,94 @@
+# check order and content of multiple here docs
+
+cat << EOF1 << EOF2
+hi
+EOF1
+there
+EOF2
+
+while read line1; do
+	read line2 <&3
+	echo $line1 - $line2
+done <<EOF1 3<<EOF2
+one
+two
+three
+EOF1
+alpha
+beta
+gamma
+EOF2
+
+
+# check quoted here-doc is protected
+
+a=foo
+cat << 'EOF'
+hi\
+there$a
+stuff
+EOF
+
+# check that quoted here-documents don't have \newline processing done
+
+cat << 'EOF'
+hi\
+there
+EO\
+F
+EOF
+true
+
+# check that \newline is removed at start of here-doc
+cat << EO\
+F
+hi
+EOF
+
+#ash# # check that \newline removal works for here-doc delimiter
+#ash# cat << EOF
+#ash# hi
+#ash# EO\
+#ash# F
+
+# check operation of tab removal in here documents
+cat <<- EOF
+	tab 1
+	tab 2
+	tab 3
+	EOF
+
+# check appending of text to file from here document
+rm -f /tmp/bash-zzz
+cat > /tmp/bash-zzz << EOF
+abc
+EOF
+cat >> /tmp/bash-zzz << EOF
+def ghi
+jkl mno
+EOF
+cat /tmp/bash-zzz
+rm -f /tmp/bash-zzz
+
+# make sure command printing puts the here-document as the last redirection
+# on the line, and the function export code preserves syntactic correctness
+fff()
+{
+  ed /tmp/foo <<ENDOFINPUT >/dev/null
+/^name/d
+w
+q
+ENDOFINPUT
+aa=1
+}
+
+type fff
+#ash# export -f fff
+#ash# ${THIS_SH} -c 'type fff'
+
+# check that end of file delimits a here-document
+# THIS MUST BE LAST!
+
+cat << EOF
+hi
+there
diff --git a/busybox-1.19.3/shell/ash_test/ash-invert/invert.right b/busybox-1.19.3/shell/ash_test/ash-invert/invert.right
new file mode 100644
index 0000000..5a9239a
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-invert/invert.right
@@ -0,0 +1,10 @@
+1
+1
+1
+0
+0
+1
+0
+1
+0
+1
diff --git a/busybox-1.19.3/shell/ash_test/ash-invert/invert.tests b/busybox-1.19.3/shell/ash_test/ash-invert/invert.tests
new file mode 100755
index 0000000..8393d95
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-invert/invert.tests
@@ -0,0 +1,19 @@
+# tests of return value inversion
+# placeholder for future expansion
+
+# user subshells (...) did this wrong in bash versions before 2.04
+
+! ( echo hello | grep h >/dev/null 2>&1 ); echo $?
+! echo hello | grep h >/dev/null 2>&1 ; echo $?
+
+! true ; echo $?
+! false; echo $?
+
+! (false) ; echo $?
+! (true); echo $?
+
+! true | false ; echo $?
+! false | true ; echo $?
+
+! (true | false) ; echo $?
+! (false | true) ; echo $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/echo_write_error.right b/busybox-1.19.3/shell/ash_test/ash-misc/echo_write_error.right
new file mode 100644
index 0000000..3e91a13
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/echo_write_error.right
@@ -0,0 +1,2 @@
+ash: write error: Broken pipe
+Ok: 1
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/echo_write_error.tests b/busybox-1.19.3/shell/ash_test/ash-misc/echo_write_error.tests
new file mode 100644
index 0000000..0a40c9f
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/echo_write_error.tests
@@ -0,0 +1,7 @@
+trap "" PIPE
+
+{
+sleep 1
+echo Cant write this - get EPIPE
+echo Ok: $? >&2
+} | { true; }
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/last_amp.right b/busybox-1.19.3/shell/ash_test/ash-misc/last_amp.right
new file mode 100644
index 0000000..3da21ae
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/last_amp.right
@@ -0,0 +1,2 @@
+3
+End
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/last_amp.tests b/busybox-1.19.3/shell/ash_test/ash-misc/last_amp.tests
new file mode 100755
index 0000000..1609376
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/last_amp.tests
@@ -0,0 +1,8 @@
+$THIS_SH -c 'echo 3&'
+d=`date`
+while test "`date`" = "$d"; do true; done
+d1=`date`
+$THIS_SH -c 'sleep 1&'
+d2=`date`
+test "$d1" = "$d2" || echo BAD
+echo End
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/nulltick1.right b/busybox-1.19.3/shell/ash_test/ash-misc/nulltick1.right
new file mode 100644
index 0000000..f90b820
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/nulltick1.right
@@ -0,0 +1,3 @@
+Test 1
+Test 2
+Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/nulltick1.tests b/busybox-1.19.3/shell/ash_test/ash-misc/nulltick1.tests
new file mode 100755
index 0000000..f81923d
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/nulltick1.tests
@@ -0,0 +1,3 @@
+echo Test ` ` 1
+echo Test `</dev/null` 2
+echo Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/shift1.right b/busybox-1.19.3/shell/ash_test/ash-misc/shift1.right
new file mode 100644
index 0000000..b53453c
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/shift1.right
@@ -0,0 +1,9 @@
+2 3 4
+0: shift: line 1: Illegal number: -1
+1 2 3 4
+2 3 4
+3 4
+4
+
+1 2 3 4
+1 2 3 4
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/shift1.tests b/busybox-1.19.3/shell/ash_test/ash-misc/shift1.tests
new file mode 100755
index 0000000..0992d9b
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/shift1.tests
@@ -0,0 +1,10 @@
+$THIS_SH -c 'shift;    echo "$@"' 0 1 2 3 4
+#We do abort on -1, but then we abort. bash executes echo.
+$THIS_SH -c 'shift -1; echo "$@"' 0 1 2 3 4
+$THIS_SH -c 'shift  0; echo "$@"' 0 1 2 3 4
+$THIS_SH -c 'shift  1; echo "$@"' 0 1 2 3 4
+$THIS_SH -c 'shift  2; echo "$@"' 0 1 2 3 4
+$THIS_SH -c 'shift  3; echo "$@"' 0 1 2 3 4
+$THIS_SH -c 'shift  4; echo "$@"' 0 1 2 3 4
+$THIS_SH -c 'shift  5; echo "$@"' 0 1 2 3 4
+$THIS_SH -c 'shift  6; echo "$@"' 0 1 2 3 4
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/source1.right b/busybox-1.19.3/shell/ash_test/ash-misc/source1.right
new file mode 100644
index 0000000..0ab7c54
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/source1.right
@@ -0,0 +1,2 @@
+Sourced ok
+Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/source1.tests b/busybox-1.19.3/shell/ash_test/ash-misc/source1.tests
new file mode 100755
index 0000000..e2e75b2
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/source1.tests
@@ -0,0 +1,5 @@
+echo "echo Sourced ok" >../sourced.sh
+PATH="..:$PATH"
+. sourced.sh
+rm ../sourced.sh
+echo Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/source2.right b/busybox-1.19.3/shell/ash_test/ash-misc/source2.right
new file mode 100644
index 0000000..ce7171c
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/source2.right
@@ -0,0 +1 @@
+Done: 0
diff --git a/busybox-1.19.3/shell/ash_test/ash-misc/source2.tests b/busybox-1.19.3/shell/ash_test/ash-misc/source2.tests
new file mode 100755
index 0000000..1870cdf
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-misc/source2.tests
@@ -0,0 +1,3 @@
+false
+. /dev/null
+echo Done: $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right
new file mode 100644
index 0000000..b212c24
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.right
@@ -0,0 +1,10 @@
+192\.168\.0\.1
+192\.168\.0\.1[
+192\.168\.0\.1[
+192\\.168\\.0\\.1[
+192\.168\.0\.1[
+192\.168\.0\.1
+192\.168\.0\.1[
+192\.168\.0\.1[
+192\\.168\\.0\\.1[
+192\.168\.0\.1[
diff --git a/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
new file mode 100755
index 0000000..3fa2f18
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_repl_slash_bash1.tests
@@ -0,0 +1,21 @@
+# The bug here was triggered by:
+# * performin pathname expansion because we see [
+# * replace operator did not escape \ in replace string
+
+IP=192.168.0.1
+
+rm -f '192.168.0.1['
+echo "${IP//./\\.}"
+echo "${IP//./\\.}"'[' # bug was here
+echo "${IP//./\\.}["   # bug was here
+echo "${IP//./\\\\.}[" # bug was here
+echo "192\.168\.0\.1["
+
+echo >'192.168.0.1['
+echo "${IP//./\\.}"
+echo "${IP//./\\.}"'[' # bug was here
+echo "${IP//./\\.}["   # bug was here
+echo "${IP//./\\\\.}[" # bug was here
+echo "192\.168\.0\.1["
+
+rm -f '192.168.0.1['
diff --git a/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_squote_bash1.right b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_squote_bash1.right
new file mode 100644
index 0000000..57536b1
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_squote_bash1.right
@@ -0,0 +1,9 @@
+a	b
+a
+b c
+def
+a'b c"d e\f
+a3b c3b e33f
+a\80b c08b
+a3b c30b
+x	y
diff --git a/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_squote_bash1.tests b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_squote_bash1.tests
new file mode 100755
index 0000000..93a56ca
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-quoting/dollar_squote_bash1.tests
@@ -0,0 +1,7 @@
+echo $'a\tb'
+echo $'a\nb' $'c\nd''ef'
+echo $'a\'b' $'c\"d' $'e\\f'
+echo $'a\63b' $'c\063b' $'e\0633f'
+echo $'a\80b' $'c\608b'
+echo $'a\x33b' $'c\x330b'
+echo $'x\x9y'
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_REPLY.right b/busybox-1.19.3/shell/ash_test/ash-read/read_REPLY.right
new file mode 100644
index 0000000..59f5d54
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_REPLY.right
@@ -0,0 +1,5 @@
+test 1: |  abc1  def  |
+test 2: |  \abc2  d\ef  |
+test 3: |abc3  def|
+test 4: |\abc4  d\ef|
+Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_REPLY.tests b/busybox-1.19.3/shell/ash_test/ash-read/read_REPLY.tests
new file mode 100755
index 0000000..ba20cae
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_REPLY.tests
@@ -0,0 +1,5 @@
+echo '  \abc1  d\ef  ' | ( read         ; echo "test 1: |$REPLY|" )
+echo '  \abc2  d\ef  ' | ( read -r      ; echo "test 2: |$REPLY|" )
+echo '  \abc3  d\ef  ' | ( read    REPLY; echo "test 3: |$REPLY|" )
+echo '  \abc4  d\ef  ' | ( read -r REPLY; echo "test 4: |$REPLY|" )
+echo Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_ifs.right b/busybox-1.19.3/shell/ash_test/ash-read/read_ifs.right
new file mode 100644
index 0000000..b523344
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_ifs.right
@@ -0,0 +1,10 @@
+test 1: .a. .b. .c.
+test 2: .a. .b. .c.
+test 3: .a. .. .b,c.
+test 4: .a. .. .b,c.
+test 5: .a. .. .c.
+test 6: .a. .. .c. .d.
+test 7: .a. .. .b,c,d  ,  ,.
+test 8: .. .a. .b. .c.
+test 9: .a. .b. .c. ..
+test A: .. .a. .. .b. .c.
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_ifs.tests b/busybox-1.19.3/shell/ash_test/ash-read/read_ifs.tests
new file mode 100755
index 0000000..6e83112
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_ifs.tests
@@ -0,0 +1,10 @@
+printf 'a\t\tb\tc\n' | ( IFS=$(printf "\t") read a b c;  echo "test 1: .$a. .$b. .$c." )
+printf 'a\t\tb\tc\n' | ( IFS=$(printf " \t") read a b c; echo "test 2: .$a. .$b. .$c." )
+printf 'a,,b,c\n'    | ( IFS="," read a b c;  echo "test 3: .$a. .$b. .$c." )
+printf 'a,,b,c\n'    | ( IFS=" ," read a b c; echo "test 4: .$a. .$b. .$c." )
+printf 'a ,, c\n'    | ( IFS=" ," read a b c; echo "test 5: .$a. .$b. .$c." )
+printf 'a ,, c d\n'  | ( IFS=" ," read a b c d;     echo "test 6: .$a. .$b. .$c. .$d." )
+printf ' a,,b,c,d  ,  ,\n' | ( IFS=" ," read a b c; echo "test 7: .$a. .$b. .$c." )
+printf '\t,\ta\t,\tb\tc'   | ( IFS=$(printf " \t,") read a b c d;   echo "test 8: .$a. .$b. .$c. .$d." )
+printf '\t\ta\t,\tb\tc'    | ( IFS=$(printf " \t,") read a b c d;   echo "test 9: .$a. .$b. .$c. .$d." )
+printf '\t,\ta\t,,\tb\tc'  | ( IFS=$(printf " \t,") read a b c d e; echo "test A: .$a. .$b. .$c. .$d. .$e." )
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_n.right b/busybox-1.19.3/shell/ash_test/ash-read/read_n.right
new file mode 100644
index 0000000..1f81af0
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_n.right
@@ -0,0 +1,3 @@
+test
+tes
+tes
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_n.tests b/busybox-1.19.3/shell/ash_test/ash-read/read_n.tests
new file mode 100755
index 0000000..12423ba
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_n.tests
@@ -0,0 +1,3 @@
+echo 'test' | (read reply; echo "$reply")
+echo 'test' | (read -n 3 reply; echo "$reply")
+echo 'test' | (read -n3 reply; echo "$reply")
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_r.right b/busybox-1.19.3/shell/ash_test/ash-read/read_r.right
new file mode 100644
index 0000000..3536bf7
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_r.right
@@ -0,0 +1,2 @@
+testbest
+test\
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_r.tests b/busybox-1.19.3/shell/ash_test/ash-read/read_r.tests
new file mode 100755
index 0000000..2c4cc61
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_r.tests
@@ -0,0 +1,2 @@
+echo -e 'test\\\nbest' | (read reply; echo "$reply")
+echo -e 'test\\\nbest' | (read -r reply; echo "$reply")
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_t.right b/busybox-1.19.3/shell/ash_test/ash-read/read_t.right
new file mode 100644
index 0000000..04126cb
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_t.right
@@ -0,0 +1,4 @@
+><
+><
+>test<
+>test<
diff --git a/busybox-1.19.3/shell/ash_test/ash-read/read_t.tests b/busybox-1.19.3/shell/ash_test/ash-read/read_t.tests
new file mode 100755
index 0000000..d65f1ae
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-read/read_t.tests
@@ -0,0 +1,10 @@
+# bash 3.2 outputs:
+
+# ><
+{ echo -n 'te'; sleep 2; echo 'st'; }   | (read -t 1 reply; echo ">$reply<")
+# ><
+{               sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<")
+# >test<
+{ echo -n 'te'; sleep 1; echo 'st'; }   | (read -t 2 reply; echo ">$reply<")
+# >test<
+{               sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<")
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir.right
new file mode 100644
index 0000000..c1a6e72
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir.right
@@ -0,0 +1,2 @@
+ash: write error: Bad file descriptor
+TEST
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir.tests
new file mode 100755
index 0000000..7a1a668
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir.tests
@@ -0,0 +1,6 @@
+# test: closed fds should stay closed
+exec 1>&-
+echo TEST >TEST
+echo JUNK # lost: stdout is closed
+cat TEST >&2
+rm TEST
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir2.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir2.right
new file mode 100644
index 0000000..d86bac9
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir2.right
@@ -0,0 +1 @@
+OK
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir2.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir2.tests
new file mode 100755
index 0000000..61ccea3
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir2.tests
@@ -0,0 +1,5 @@
+# ash once couldn't redirect above fd#9
+exec 1>/dev/null
+(echo LOST1 >&22) 22>&1
+(echo LOST2 >&22) 22>&1
+(echo OK >&22) 22>&2
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir3.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir3.right
new file mode 100644
index 0000000..fd641a8
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir3.right
@@ -0,0 +1,3 @@
+TEST
+./redir3.tests: line 4: 9: Bad file descriptor
+Output to fd#9: 1
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir3.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir3.tests
new file mode 100755
index 0000000..e37d5e4
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir3.tests
@@ -0,0 +1,5 @@
+# redirects to closed descriptors should not leave these descriptors
+# open afterwards
+echo TEST 9>/dev/null
+echo MUST ERROR OUT >&9
+echo "Output to fd#9: $?"
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir4.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir4.right
new file mode 100644
index 0000000..d86bac9
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir4.right
@@ -0,0 +1 @@
+OK
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir4.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir4.tests
new file mode 100755
index 0000000..4bdf5ae
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir4.tests
@@ -0,0 +1,72 @@
+# ash uses fd 10 (usually) for reading the script
+exec 13>&-
+exec 12>&-
+exec 11>&-
+exec 10>&-
+# some amount of input is prefetched.
+# make sure final echo is far enough to not be prefetched.
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+###############################################################
+echo "OK"
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir5.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir5.right
new file mode 100644
index 0000000..9d08777
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir5.right
@@ -0,0 +1,2 @@
+./redir5.tests: line 2: 10: Bad file descriptor
+OK
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir5.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir5.tests
new file mode 100755
index 0000000..91b0c1f
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir5.tests
@@ -0,0 +1,3 @@
+# ash uses fd 10 (usually) for reading the script
+echo LOST >&10
+echo OK
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir6.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir6.right
new file mode 100644
index 0000000..ed754df
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir6.right
@@ -0,0 +1,2 @@
+Hello
+OK
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir6.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir6.tests
new file mode 100755
index 0000000..33b6d4c
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir6.tests
@@ -0,0 +1,3 @@
+# we had a bug where this would hang
+(head -n 1 <redir6.right)
+echo OK
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir7.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir7.right
new file mode 100644
index 0000000..6430b02
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir7.right
@@ -0,0 +1,3 @@
+Ok
+Ok
+Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir7.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir7.tests
new file mode 100755
index 0000000..ca3979a
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir7.tests
@@ -0,0 +1,12 @@
+# Chars above 0x7f are used as special codes.
+# 0x81 is CTLESC (see ash.c).
+# The bug was that quoting and unquoting of them
+# was out of sync for redirect filenames.
+
+>unicode.sh
+echo -e 'echo Ok >uni\x81code' >>unicode.sh
+echo -e 'cat uni\x81code' >>unicode.sh
+echo -e 'cat uni?code' >>unicode.sh
+. ./unicode.sh
+rm uni*code*
+echo Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir8.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir8.right
new file mode 100644
index 0000000..6430b02
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir8.right
@@ -0,0 +1,3 @@
+Ok
+Ok
+Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir8.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir8.tests
new file mode 100755
index 0000000..8cb42c0
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir8.tests
@@ -0,0 +1,15 @@
+# Chars above 0x7f are used as special codes.
+# 0x81 is CTLESC (see ash.c).
+# The bug was that quoting and unquoting of them
+# was out of sync for redirect filenames.
+
+# Subcase when redirect filename is specified in a variable.
+
+>unicode.sh
+echo -e 'v=uni\x81code' >>unicode.sh
+echo -e 'echo Ok >"$v"' >>unicode.sh
+echo -e 'cat uni\x81code' >>unicode.sh
+echo -e 'cat uni?code' >>unicode.sh
+. ./unicode.sh
+rm uni*code*
+echo Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir9.right b/busybox-1.19.3/shell/ash_test/ash-redir/redir9.right
new file mode 100644
index 0000000..34c2512
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir9.right
@@ -0,0 +1,2 @@
+Ok
+Done:0
diff --git a/busybox-1.19.3/shell/ash_test/ash-redir/redir9.tests b/busybox-1.19.3/shell/ash_test/ash-redir/redir9.tests
new file mode 100755
index 0000000..8befa61
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-redir/redir9.tests
@@ -0,0 +1,4 @@
+echo Ok >file.tmp
+cat 0<>file.tmp
+echo Done:$?
+rm file.tmp
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/reap1.right b/busybox-1.19.3/shell/ash_test/ash-signals/reap1.right
new file mode 100644
index 0000000..7326d96
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/reap1.right
@@ -0,0 +1 @@
+Ok
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/reap1.tests b/busybox-1.19.3/shell/ash_test/ash-signals/reap1.tests
new file mode 100755
index 0000000..bf1a1f9
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/reap1.tests
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+# Must not find us alive
+{ sleep 2; kill -9 $$; } 2>/dev/null &
+
+sleep 1 &
+PID=$!
+
+# We must exit the loop in one second.
+# We had bug 5304: builtins never waited for exited children
+while kill -0 $PID >/dev/null 2>&1; do
+    true
+done
+echo Ok
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/savetrap.right b/busybox-1.19.3/shell/ash_test/ash-signals/savetrap.right
new file mode 100644
index 0000000..a59225b
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/savetrap.right
@@ -0,0 +1,8 @@
+trap -- 'echo Exiting' EXIT
+trap -- 'echo WINCH!' WINCH
+trap -- 'echo Exiting' EXIT
+trap -- 'echo WINCH!' WINCH
+trap -- 'echo Exiting' EXIT
+trap -- 'echo WINCH!' WINCH
+Done
+Exiting
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/savetrap.tests b/busybox-1.19.3/shell/ash_test/ash-signals/savetrap.tests
new file mode 100755
index 0000000..c2b312f
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/savetrap.tests
@@ -0,0 +1,9 @@
+trap 'echo Exiting' EXIT
+trap 'echo WINCH!' SIGWINCH
+v=` trap   `
+echo "$v"
+v=$(	trap )
+echo "$v"
+v=`trap`
+echo "$v"
+echo Done
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/sigint1.right b/busybox-1.19.3/shell/ash_test/ash-signals/sigint1.right
new file mode 100644
index 0000000..a9094b0
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/sigint1.right
@@ -0,0 +1 @@
+Sending SIGINT to main shell PID
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/sigint1.tests b/busybox-1.19.3/shell/ash_test/ash-signals/sigint1.tests
new file mode 100755
index 0000000..3d483d3
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/sigint1.tests
@@ -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/busybox-1.19.3/shell/ash_test/ash-signals/signal1.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal1.right
new file mode 100644
index 0000000..cf403ac
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal1.right
@@ -0,0 +1,20 @@
+got signal
+trap -- 'echo got signal' USR1
+sent 1 signal
+got signal
+wait interrupted
+trap -- 'echo got signal' USR1
+sent 2 signal
+got signal
+wait interrupted
+trap -- 'echo got signal' USR1
+sent 3 signal
+got signal
+wait interrupted
+trap -- 'echo got signal' USR1
+sent 4 signal
+got signal
+wait interrupted
+trap -- 'echo got signal' USR1
+sent 5 signal
+sleep completed
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal1.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal1.tests
new file mode 100755
index 0000000..28bfc6a
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal1.tests
@@ -0,0 +1,28 @@
+trap "echo got signal" USR1
+
+for try in 1 2 3 4 5; do
+    kill -USR1 $$
+    sleep 0.2
+    echo "sent $try signal"
+done &
+
+# Ensure "wait" has something to wait for
+sleep 2 &
+
+# Ensure we do not execute "trap" below before "kill -USR1" above
+# (was getting failure on loaded machine without this)
+sleep 0.1
+
+sleeping=true
+while $sleeping; do
+    trap
+    if wait %%; then
+        echo "sleep completed"
+        sleeping=false
+    elif [ $? == 127 ]; then
+        echo "BUG: no processes to wait for?!"
+        sleeping=false
+    else
+        echo "wait interrupted"
+    fi
+done
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal2.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal2.right
new file mode 100644
index 0000000..a2af919
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal2.right
@@ -0,0 +1,3 @@
+child sleeps
+child exits as expected
+parent exits
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal2.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal2.tests
new file mode 100755
index 0000000..df639ca
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal2.tests
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+$THIS_SH -c '
+cleanup() {
+    echo "child exits as expected"
+    exit
+}
+trap cleanup HUP
+echo "child sleeps"
+sleep 1
+echo "BAD exit from child!"
+' &
+
+child=$!
+sleep 0.1 # let child install handler first
+kill -HUP $child
+wait
+echo "parent exits"
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal3.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal3.right
new file mode 100644
index 0000000..3113ba5
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal3.right
@@ -0,0 +1,4 @@
+child sleeps
+child got HUP
+child exits
+parent exits
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal3.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal3.tests
new file mode 100755
index 0000000..b56c2d9
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal3.tests
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+$THIS_SH -c '
+hup() {
+    echo "child got HUP"
+}
+trap hup HUP
+echo "child sleeps"
+sleep 1
+echo "child exits"
+' &
+
+child=$!
+sleep 0.1 # let child install handler first
+kill -HUP $child
+wait
+echo "parent exits"
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal4.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal4.right
new file mode 100644
index 0000000..3260584
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal4.right
@@ -0,0 +1,4 @@
+./signal4.tests: trap: line 3: BADNAME: invalid signal specification
+1
+Trapped
+Ok
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal4.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal4.tests
new file mode 100755
index 0000000..6f1c4a9
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal4.tests
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+trap "echo Trapped" BADNAME TERM; echo $?
+kill $$
+echo Ok
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal5.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal5.right
new file mode 100644
index 0000000..7cfd411
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal5.right
@@ -0,0 +1,12 @@
+Sleeping
+Sleeping
+Waiting
+2 sec passed, sending USR1 to parent
+USR1 received
+Wait exit code: 138
+Waiting
+3 sec passed, sending USR1 to parent
+USR1 received
+Wait exit code: 138
+Waiting
+Wait returned 0
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal5.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal5.tests
new file mode 100755
index 0000000..179bcdd
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal5.tests
@@ -0,0 +1,14 @@
+trap "echo USR1 received" USR1
+stub() {
+    echo "Sleeping"
+    sleep $1
+    echo "$1 sec passed, sending USR1 to parent"
+    kill -USR1 $$
+}
+stub 3 &
+stub 2 &
+sleep 1
+until { echo "Waiting"; wait; } do
+    echo "Wait exit code: $?"
+done
+echo "Wait returned 0"
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal6.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal6.right
new file mode 100644
index 0000000..df4d930
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal6.right
@@ -0,0 +1,2 @@
+got TERM
+Done: 0
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal6.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal6.tests
new file mode 100755
index 0000000..3ce1510
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal6.tests
@@ -0,0 +1,2 @@
+{ trap "echo got TERM" TERM; sleep 3; }& sleep 1; kill $!; wait
+echo Done: $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal7.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal7.right
new file mode 100644
index 0000000..ba7453e
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal7.right
@@ -0,0 +1 @@
+Bug detected: 0
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal7.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal7.tests
new file mode 100755
index 0000000..c2b1381
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal7.tests
@@ -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/busybox-1.19.3/shell/ash_test/ash-signals/signal8.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal8.right
new file mode 100644
index 0000000..39572f3
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal8.right
@@ -0,0 +1,3 @@
+Removing traps
+End of exit_func
+Done: 0
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal8.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal8.tests
new file mode 100755
index 0000000..731af74
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal8.tests
@@ -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/busybox-1.19.3/shell/ash_test/ash-signals/signal9.right b/busybox-1.19.3/shell/ash_test/ash-signals/signal9.right
new file mode 100644
index 0000000..39572f3
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal9.right
@@ -0,0 +1,3 @@
+Removing traps
+End of exit_func
+Done: 0
diff --git a/busybox-1.19.3/shell/ash_test/ash-signals/signal9.tests b/busybox-1.19.3/shell/ash_test/ash-signals/signal9.tests
new file mode 100755
index 0000000..18e7101
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-signals/signal9.tests
@@ -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: $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-standalone/noexec_gets_no_env.right b/busybox-1.19.3/shell/ash_test/ash-standalone/noexec_gets_no_env.right
new file mode 100644
index 0000000..8522dff
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-standalone/noexec_gets_no_env.right
@@ -0,0 +1,4 @@
+VAR7=VAL
+0
+VAR8=VAL
+0
diff --git a/busybox-1.19.3/shell/ash_test/ash-standalone/noexec_gets_no_env.tests b/busybox-1.19.3/shell/ash_test/ash-standalone/noexec_gets_no_env.tests
new file mode 100755
index 0000000..0d347bd
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-standalone/noexec_gets_no_env.tests
@@ -0,0 +1,5 @@
+export VAR7=VAL
+env | grep ^VAR7=
+echo $?
+VAR8=VAL env | grep ^VAR8=
+echo $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-standalone/nofork_trashes_getopt.right b/busybox-1.19.3/shell/ash_test/ash-standalone/nofork_trashes_getopt.right
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-standalone/nofork_trashes_getopt.right
@@ -0,0 +1 @@
+0
diff --git a/busybox-1.19.3/shell/ash_test/ash-standalone/nofork_trashes_getopt.tests b/busybox-1.19.3/shell/ash_test/ash-standalone/nofork_trashes_getopt.tests
new file mode 100755
index 0000000..f42c507
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-standalone/nofork_trashes_getopt.tests
@@ -0,0 +1,6 @@
+# In this test, rm is NOFORK and it modifies getopt internal state
+rm -f non_existent_file
+# Subsequent hexdump is run as NOEXEC, and thus still uses this state
+hexdump </dev/null
+# Did hexdump segfault etc?
+echo $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-standalone/var_standalone1.right b/busybox-1.19.3/shell/ash_test/ash-standalone/var_standalone1.right
new file mode 100644
index 0000000..37457fd
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-standalone/var_standalone1.right
@@ -0,0 +1 @@
+Done: 1
diff --git a/busybox-1.19.3/shell/ash_test/ash-standalone/var_standalone1.tests b/busybox-1.19.3/shell/ash_test/ash-standalone/var_standalone1.tests
new file mode 100755
index 0000000..1e905ba
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-standalone/var_standalone1.tests
@@ -0,0 +1,2 @@
+VAR=42 $THIS_SH -c 'unset VAR; env | grep ^VAR'
+echo Done: $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var1.right b/busybox-1.19.3/shell/ash_test/ash-vars/var1.right
new file mode 100644
index 0000000..2a01291
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var1.right
@@ -0,0 +1,6 @@
+a=a A=a
+a=a A=a
+a= A=
+a= A=
+a=a A=a
+a=a A=a
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var1.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var1.tests
new file mode 100755
index 0000000..802e489
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var1.tests
@@ -0,0 +1,14 @@
+# check that first assignment has proper effect on second one
+
+(
+a=a A=$a
+echo a=$a A=$A
+)
+(a=a A=$a; echo a=$a A=$A)
+(a=a A=$a echo a=$a A=$A)
+(a=a A=$a /bin/echo a=$a A=$A)
+
+f() { echo a=$a A=$A; }
+
+(a=a A=$a f)
+(a=a A=$a; f)
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var2.right b/busybox-1.19.3/shell/ash_test/ash-vars/var2.right
new file mode 100644
index 0000000..8fed138
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var2.right
@@ -0,0 +1 @@
+bus/usb/1/2
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var2.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var2.tests
new file mode 100755
index 0000000..07feaeb
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var2.tests
@@ -0,0 +1 @@
+X=usbdev1.2 X=${X#usbdev} B=${X%%.*} D=${X#*.}; echo bus/usb/$B/$D
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_bash1.right b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash1.right
new file mode 100644
index 0000000..c0a0769
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash1.right
@@ -0,0 +1,14 @@
+
+
+f
+bcdef
+abcdef
+abcdef
+bcde
+abcd
+abcd
+abcdef
+bcdef
+abcdef
+abcdef
+abcdef
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_bash1.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash1.tests
new file mode 100755
index 0000000..24d3c9a
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash1.tests
@@ -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/busybox-1.19.3/shell/ash_test/ash-vars/var_bash2.right b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash2.right
new file mode 100644
index 0000000..acba5c6
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash2.right
@@ -0,0 +1,10 @@
+abc123xcba123
+abx123dcba123
+abx123dxba123
+abcx23dcba123
+abcxxxdcbaxxx
+abx
+xba123
+abx23
+abc23dcba123
+abcdcba
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_bash2.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash2.tests
new file mode 100755
index 0000000..29c526c
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash2.tests
@@ -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/busybox-1.19.3/shell/ash_test/ash-vars/var_bash3.right b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash3.right
new file mode 100644
index 0000000..a97c850
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash3.right
@@ -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/busybox-1.19.3/shell/ash_test/ash-vars/var_bash3.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash3.tests
new file mode 100755
index 0000000..146dbb6
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash3.tests
@@ -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/busybox-1.19.3/shell/ash_test/ash-vars/var_bash4.right b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash4.right
new file mode 100644
index 0000000..600e853
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash4.right
@@ -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/busybox-1.19.3/shell/ash_test/ash-vars/var_bash4.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash4.tests
new file mode 100755
index 0000000..d547061
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash4.tests
@@ -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/busybox-1.19.3/shell/ash_test/ash-vars/var_bash5.right b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash5.right
new file mode 100644
index 0000000..278ed32
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash5.right
@@ -0,0 +1,4 @@
+a/
+a/d
+a/e/f
+Done: 0
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_bash5.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash5.tests
new file mode 100755
index 0000000..7f482a5
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_bash5.tests
@@ -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: $?
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_leak.right b/busybox-1.19.3/shell/ash_test/ash-vars/var_leak.right
new file mode 100644
index 0000000..01a5e32
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_leak.right
@@ -0,0 +1,4 @@
+should be empty: ''
+should be empty: ''
+should be not empty: 'val2'
+should be not empty: 'val3'
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_leak.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var_leak.tests
new file mode 100755
index 0000000..5242e24
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_leak.tests
@@ -0,0 +1,23 @@
+# cat is an external program, variable should not leak out of it.
+# this currently fails with CONFIG_FEATURE_SH_NOFORK=y
+VAR=''
+VAR=val0 cat /dev/null
+echo "should be empty: '$VAR'"
+
+# true is a regular builtin, variable should not leak out of it.
+VAR=''
+VAR=val1 true
+echo "should be empty: '$VAR'"
+
+# ash follows the "special builtin leaks variables" rule here:
+# exec is a special builtin. (bash does not do it)
+VAR=''
+VAR=val2 exec 2>&1
+echo "should be not empty: '$VAR'"
+
+# ash follows the "function call is a special builtin" rule here
+# (bash does not do it)
+f() { true; }
+VAR=''
+VAR=val3 f
+echo "should be not empty: '$VAR'"
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_posix1.right b/busybox-1.19.3/shell/ash_test/ash-vars/var_posix1.right
new file mode 100644
index 0000000..55f3579
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_posix1.right
@@ -0,0 +1,17 @@
+abcdcd
+abcdcd
+abcdcd
+cdcd
+babcdcd
+babcdcd
+ababcdcd
+
+ababcd
+ababcd
+ababcd
+abab
+ababcdc
+ababcdc
+ababcdcd
+
+end
diff --git a/busybox-1.19.3/shell/ash_test/ash-vars/var_posix1.tests b/busybox-1.19.3/shell/ash_test/ash-vars/var_posix1.tests
new file mode 100755
index 0000000..4139e2c
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/ash-vars/var_posix1.tests
@@ -0,0 +1,21 @@
+var=ababcdcd
+
+echo ${var#ab}
+echo ${var##ab}
+echo ${var#a*b}
+echo ${var##a*b}
+echo ${var#?}
+echo ${var##?}
+echo ${var#*}
+echo ${var##*}
+
+echo ${var%cd}
+echo ${var%%cd}
+echo ${var%c*d}
+echo ${var%%c*d}
+echo ${var%?}
+echo ${var%%?}
+echo ${var%*}
+echo ${var%%*}
+
+echo end
diff --git a/busybox-1.19.3/shell/ash_test/printenv.c b/busybox-1.19.3/shell/ash_test/printenv.c
new file mode 100644
index 0000000..c4ccda8
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/printenv.c
@@ -0,0 +1,67 @@
+/* printenv -- minimal clone of BSD printenv(1).
+
+   usage: printenv [varname]
+
+   Chet Ramey
+   chet@po.cwru.edu
+*/
+
+/* Copyright (C) 1997-2002 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash 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, or (at your option) any later
+   version.
+
+   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
+   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <stdlib.h>
+#include <string.h>
+
+extern char **environ;
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  register char **envp, *eval;
+  int len;
+
+  argv++;
+  argc--;
+
+  /* printenv */
+  if (argc == 0)
+    {
+      for (envp = environ; *envp; envp++)
+	puts (*envp);
+      exit(EXIT_SUCCESS);
+    }
+
+  /* printenv varname */
+  len = strlen (*argv);
+  for (envp = environ; *envp; envp++)
+    {
+      if (**argv == **envp && strncmp (*envp, *argv, len) == 0)
+	{
+	  eval = *envp + len;
+	  /* If the environment variable doesn't have an `=', ignore it. */
+	  if (*eval == '=')
+	    {
+	      puts (eval + 1);
+	      exit(EXIT_SUCCESS);
+	    }
+	}
+    }
+  exit(EXIT_FAILURE);
+}
diff --git a/busybox-1.19.3/shell/ash_test/recho.c b/busybox-1.19.3/shell/ash_test/recho.c
new file mode 100644
index 0000000..42a5fea
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/recho.c
@@ -0,0 +1,58 @@
+/*
+   recho -- really echo args, bracketed with <> and with invisible chars
+	    made visible.
+
+   Chet Ramey
+   chet@po.cwru.edu
+*/
+
+/* Copyright (C) 2002-2005 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash 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, or (at your option) any later
+   version.
+
+   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
+   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void strprint();
+
+int main(int argc, char **argv)
+{
+	int i;
+
+	for (i = 1; i < argc; i++) {
+		printf("argv[%d] = <", i);
+		strprint(argv[i]);
+		printf(">\n");
+	}
+	exit(EXIT_SUCCESS);
+}
+
+void strprint(char *str)
+{
+	unsigned char *s;
+
+	for (s = (unsigned char *)str; s && *s; s++) {
+		if (*s < ' ') {
+			putchar('^');
+			putchar(*s+64);
+		} else if (*s == 127) {
+			putchar('^');
+			putchar('?');
+		} else
+			putchar(*s);
+	}
+}
diff --git a/busybox-1.19.3/shell/ash_test/run-all b/busybox-1.19.3/shell/ash_test/run-all
new file mode 100755
index 0000000..ad93e25
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/run-all
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+TOPDIR=$PWD
+
+test -x ash || {
+    echo "No ./ash - creating a link to ../../busybox"
+    ln -s ../../busybox ash
+}
+test -x printenv || gcc -O2 -o printenv printenv.c || exit $?
+test -x recho    || gcc -O2 -o recho    recho.c    || exit $?
+test -x zecho    || gcc -O2 -o zecho    zecho.c    || exit $?
+
+PATH="$PWD:$PATH" # for ash and recho/zecho/printenv
+export PATH
+
+THIS_SH="$PWD/ash"
+export THIS_SH
+
+do_test()
+{
+    test -d "$1" || return 0
+#   echo do_test "$1"
+    # $1 but with / replaced by # so that it can be used as filename part
+    noslash=`echo "$1" | sed 's:/:#:g'`
+    (
+    cd "$1" || { echo "cannot cd $1!"; exit 1; }
+    for x in run-*; do
+	test -f "$x" || continue
+	case "$x" in
+	    "$0"|run-minimal|run-gprof) ;;
+	    *.orig|*~) ;;
+	    #*) echo $x ; sh $x ;;
+	    *)
+	    sh "$x" >"$TOPDIR/$noslash-$x.fail" 2>&1 && \
+	    { echo "$1/$x: ok"; rm "$TOPDIR/$noslash-$x.fail"; } || echo "$1/$x: fail";
+	    ;;
+	esac
+    done
+    # Many bash run-XXX scripts just do this,
+    # no point in duplication it all over the place
+    for x in *.tests; do
+	test -x "$x" || continue
+	name="${x%%.tests}"
+	test -f "$name.right" || continue
+	{
+	    "$THIS_SH" "./$x" >"$name.xx" 2>&1
+	    diff -u "$name.xx" "$name.right" >"$TOPDIR/$noslash-$x.fail" \
+	    && rm -f "$name.xx" "$TOPDIR/$noslash-$x.fail"
+	} && echo "$1/$x: ok" || echo "$1/$x: fail"
+    done
+    )
+}
+
+# main part of this script
+# Usage: run-all [directories]
+
+if [ $# -lt 1 ]; then
+    # All sub directories
+    modules=`ls -d ash-*`
+    # If you want to test ash against hush and msh testsuites
+    # (have to copy hush_test and msh_test dirs to current dir first):
+    #modules=`ls -d ash-* hush_test/hush-* msh_test/msh-*`
+
+    for module in $modules; do
+	do_test $module
+    done
+else
+    while [ $# -ge 1 ]; do
+	if [ -d $1 ]; then
+	    do_test $1
+	fi
+	shift
+    done
+fi
diff --git a/busybox-1.19.3/shell/ash_test/zecho.c b/busybox-1.19.3/shell/ash_test/zecho.c
new file mode 100644
index 0000000..cbaa59b
--- /dev/null
+++ b/busybox-1.19.3/shell/ash_test/zecho.c
@@ -0,0 +1,36 @@
+/* zecho - bare-bones echo */
+
+/* Copyright (C) 1996-2002 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash 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, or (at your option) any later
+   version.
+
+   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
+   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv)
+{
+	argv++;
+
+	while (*argv) {
+		(void)printf("%s", *argv);
+		if (*++argv)
+			putchar(' ');
+	}
+
+	putchar('\n');
+	exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/shell/brace.txt b/busybox-1.19.3/shell/brace.txt
new file mode 100644
index 0000000..664861b
--- /dev/null
+++ b/busybox-1.19.3/shell/brace.txt
@@ -0,0 +1,50 @@
+Brace Expansion
+
+Brace expansion is a mechanism by which arbitrary strings may be gener-
+ated.   This  mechanism is similar to pathname expansion, but the file-
+names generated need not exist.  Patterns to be brace expanded take the
+form of an optional preamble, followed by either a series of comma-sep-
+arated strings or a sequence expression between a pair of braces,  fol-
+lowed  by  an  optional  postscript.   The preamble is prefixed to each
+string contained within the braces, and the postscript is then appended
+to each resulting string, expanding left to right.
+
+Brace  expansions  may  be nested.  The results of each expanded string
+are not sorted;  left  to  right  order  is  preserved.   For  example,
+a{d,c,b}e expands into `ade ace abe'.
+
+A  sequence  expression takes the form {x..y}, where x and y are either
+integers or single characters.  When integers are supplied, the expres-
+sion  expands  to each number between x and y, inclusive.  When charac-
+ters are supplied, the expression expands  to  each  character  lexico-
+graphically between x and y, inclusive.  Note that both x and y must be
+of the same type.
+
+Brace expansion is performed before any other expansions, and any char-
+acters  special to other expansions are preserved in the result.  It is
+strictly textual.  Bash does not apply any syntactic interpretation  to
+the context of the expansion or the text between the braces.
+
+A  correctly-formed  brace  expansion must contain unquoted opening and
+closing braces, and at least one unquoted comma  or  a  valid  sequence
+expression.   Any incorrectly formed brace expansion is left unchanged.
+A { or , may be quoted with a backslash to prevent its being considered
+part  of  a brace expression.  To avoid conflicts with parameter expan-
+sion, the string ${ is not considered eligible for brace expansion.
+
+This construct is typically used as shorthand when the common prefix of
+the strings to be generated is longer than in the above example:
+
+       mkdir /usr/local/src/bash/{old,new,dist,bugs}
+or
+       chown root /usr/{ucb/{ex,edit},lib/{ex?.?*,how_ex}}
+
+Brace  expansion  introduces  a  slight incompatibility with historical
+versions of sh.  sh does not treat opening or closing braces  specially
+when  they  appear as part of a word, and preserves them in the output.
+Bash removes braces from words as a  consequence  of  brace  expansion.
+For  example,  a word entered to sh as file{1,2} appears identically in
+the output.  The same word is output as file1 file2 after expansion  by
+bash.   If strict compatibility with sh is desired, start bash with the
++B option or disable brace expansion with the +B option to the set com-
+mand
diff --git a/busybox-1.19.3/shell/cttyhack.c b/busybox-1.19.3/shell/cttyhack.c
new file mode 100644
index 0000000..37ea137
--- /dev/null
+++ b/busybox-1.19.3/shell/cttyhack.c
@@ -0,0 +1,168 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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:
+
+//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 */
+	unsigned short v_signal;        /* signal to send */
+	unsigned short v_state;         /* vt bitmask */
+};
+enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
+
+/* From <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 */
+	int	reserved[1];
+};
+
+int cttyhack_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cttyhack_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+	char console[sizeof(int)*3 + 16];
+	union {
+		struct vt_stat vt;
+		struct serial_struct sr;
+		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 {
+		/* We don't have ctty (or don't have "/dev/tty" node...) */
+		do {
+#ifdef __linux__
+			int s = open_read_close("/sys/class/tty/console/active",
+				console + 5, sizeof(console) - 5);
+			if (s > 0) {
+				/* found active console via sysfs (Linux 2.6.38+)
+				 * sysfs string looks like "ttyS0\n" so zap the newline:
+				 */
+				console[4 + s] = '\0';
+				break;
+			}
+
+			if (ioctl(0, VT_GETSTATE, &u.vt) == 0) {
+				/* this is linux virtual tty */
+				sprintf(console + 8, "S%d" + 1, u.vt.v_active);
+				break;
+			}
+#endif
+#ifdef TIOCGSERIAL
+			if (ioctl(0, TIOCGSERIAL, &u.sr) == 0) {
+				/* this is a serial console, asuming it is named /dev/ttySn */
+				sprintf(console + 8, "S%d", u.sr.line);
+				break;
+			}
+#endif
+			/* nope, could not find it */
+			goto ret;
+		} while (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,
+		 * steal it from them:
+		 */
+		ioctl(0, TIOCSCTTY, 1);
+	}
+
+ret:
+	BB_EXECVP_or_die(argv);
+}
diff --git a/busybox-1.19.3/shell/hush.c b/busybox-1.19.3/shell/hush.c
new file mode 100644
index 0000000..de0af9c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush.c
@@ -0,0 +1,9136 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * A prototype Bourne shell grammar parser.
+ * Intended to follow the original Thompson and Ritchie
+ * "small and simple is beautiful" philosophy, which
+ * incidentally is a good match to today's BusyBox.
+ *
+ * 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
+ *      execution engine, the builtins, and much of the underlying
+ *      support has been adapted from busybox-0.49pre's lash, which is
+ *      Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *      written by Erik Andersen <andersen@codepoet.org>.  That, in turn,
+ *      is based in part on ladsh.c, by Michael K. Johnson and Erik W.
+ *      Troan, which they placed in the public domain.  I don't know
+ *      how much of the Johnson/Troan code has survived the repeated
+ *      rewrites.
+ *
+ * Other credits:
+ *      o_addchr derived from similar w_addchar function in glibc-2.2.
+ *      parse_redirect, redirect_opt_num, and big chunks of main
+ *      and many builtins derived from contributions by Erik Andersen.
+ *      Miscellaneous bugfixes from Matt Kraai.
+ *
+ * There are two big (and related) architecture differences between
+ * this parser and the lash parser.  One is that this version is
+ * actually designed from the ground up to understand nearly all
+ * of the Bourne grammar.  The second, consequential change is that
+ * the parser and input reader have been turned inside out.  Now,
+ * the parser is in control, and asks for input as needed.  The old
+ * way had the input reader in control, and it asked for parsing to
+ * take place as needed.  The new way makes it much easier to properly
+ * handle the recursion implicit in the various substitutions, especially
+ * across continuation lines.
+ *
+ * TODOs:
+ *      grep for "TODO" and fix (some of them are easy)
+ *      special variables (done: PWD, PPID, RANDOM)
+ *      tilde expansion
+ *      aliases
+ *      follow IFS rules more precisely, including update semantics
+ *      builtins mandated by standards we don't support:
+ *          [un]alias, command, fc, getopts, newgrp, readonly, times
+ *      make complex ${var%...} constructs support optional
+ *      make here documents optional
+ *
+ * Bash compat TODO:
+ *      redirection of stdout+stderr: &> and >&
+ *      reserved words: function select
+ *      advanced test: [[ ]]
+ *      process substitution: <(list) and >(list)
+ *      =~: regex operator
+ *      let EXPR [EXPR...]
+ *          Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION)
+ *          If the last arg evaluates to 0, let returns 1; 0 otherwise.
+ *          NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used)
+ *      ((EXPR))
+ *          The EXPR is evaluated according to ARITHMETIC EVALUATION.
+ *          This is exactly equivalent to let "EXPR".
+ *      $[EXPR]: synonym for $((EXPR))
+ *
+ * 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:
+ *              $ ls i=`echo 'a  b'`     # ls has two args: "i=a" and "b"
+ *              ls: cannot access i=a: No such file or directory
+ *              ls: cannot access b: No such file or directory
+ *          Note1: same applies to local builtin.
+ *          Note2: bash 3.2.33(1) does this only if export word itself
+ *          is not quoted:
+ *              $ export i=`echo 'aaa  bbb'`; echo "$i"
+ *              aaa  bbb
+ *              $ "export" i=`echo 'aaa  bbb'`; echo "$i"
+ *              aaa
+ */
+#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"
+#if ENABLE_HUSH_RANDOM_SUPPORT
+# include "random.h"
+#else
+# define CLEAR_RANDOM_T(rnd) ((void)0)
+#endif
+#ifndef PIPE_BUF
+# define PIPE_BUF 4096  /* amount of buffering in a pipe */
+#endif
+
+/* Not every libc has sighandler_t. Fix it */
+typedef void (*hush_sighandler_t)(int);
+#define sighandler_t hush_sighandler_t
+
+//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 BUILD_AS_NOMMU 0
+/* Enable/disable sanity checks. Ok to enable in production,
+ * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
+ * Keeping 1 for now even in released versions.
+ */
+#define HUSH_DEBUG 1
+/* Slightly bigger (+200 bytes), but faster hush.
+ * So far it only enables a trick with counting SIGCHLDs and forks,
+ * which allows us to do fewer waitpid's.
+ * (we can detect a case where neither forks were done nor SIGCHLDs happened
+ * and therefore waitpid will return the same result as last time)
+ */
+#define ENABLE_HUSH_FAST 0
+/* TODO: implement simplified code for users which do not need ${var%...} ops
+ * So far ${var%...} ops are always enabled:
+ */
+#define ENABLE_HUSH_DOLLAR_OPS 1
+
+
+#if BUILD_AS_NOMMU
+# undef BB_MMU
+# undef USE_FOR_NOMMU
+# undef USE_FOR_MMU
+# define BB_MMU 0
+# define USE_FOR_NOMMU(...) __VA_ARGS__
+# define USE_FOR_MMU(...)
+#endif
+
+#include "NUM_APPLETS.h"
+#if NUM_APPLETS == 1
+/* STANDALONE does not make sense, and won't compile */
+# undef CONFIG_FEATURE_SH_STANDALONE
+# undef ENABLE_FEATURE_SH_STANDALONE
+# undef IF_FEATURE_SH_STANDALONE
+# undef IF_NOT_FEATURE_SH_STANDALONE
+# define ENABLE_FEATURE_SH_STANDALONE 0
+# define IF_FEATURE_SH_STANDALONE(...)
+# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
+#endif
+
+#if !ENABLE_HUSH_INTERACTIVE
+# undef ENABLE_FEATURE_EDITING
+# define ENABLE_FEATURE_EDITING 0
+# undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
+# define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
+#endif
+
+/* Do we support ANY keywords? */
+#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
+# define HAS_KEYWORDS 1
+# define IF_HAS_KEYWORDS(...) __VA_ARGS__
+# define IF_HAS_NO_KEYWORDS(...)
+#else
+# define HAS_KEYWORDS 0
+# define IF_HAS_KEYWORDS(...)
+# define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
+#endif
+
+/* If you comment out one of these below, it will be #defined later
+ * to perform debug printfs to stderr: */
+#define debug_printf(...)        do {} while (0)
+/* Finer-grained debug switches */
+#define debug_printf_parse(...)  do {} while (0)
+#define debug_print_tree(a, b)   do {} while (0)
+#define debug_printf_exec(...)   do {} while (0)
+#define debug_printf_env(...)    do {} while (0)
+#define debug_printf_jobs(...)   do {} while (0)
+#define debug_printf_expand(...) do {} while (0)
+#define debug_printf_varexp(...) do {} while (0)
+#define debug_printf_glob(...)   do {} while (0)
+#define debug_printf_list(...)   do {} while (0)
+#define debug_printf_subst(...)  do {} while (0)
+#define debug_printf_clean(...)  do {} while (0)
+
+#define ERR_PTR ((void*)(long)1)
+
+#define JOB_STATUS_FORMAT    "[%d] %-22s %.40s\n"
+
+#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
+
+struct variable;
+
+static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
+
+/* This supports saving pointers malloced in vfork child,
+ * to be freed in the parent.
+ */
+#if !BB_MMU
+typedef struct nommu_save_t {
+	char **new_env;
+	struct variable *old_vars;
+	char **argv;
+	char **argv_from_re_execing;
+} nommu_save_t;
+#endif
+
+enum {
+	RES_NONE  = 0,
+#if ENABLE_HUSH_IF
+	RES_IF    ,
+	RES_THEN  ,
+	RES_ELIF  ,
+	RES_ELSE  ,
+	RES_FI    ,
+#endif
+#if ENABLE_HUSH_LOOPS
+	RES_FOR   ,
+	RES_WHILE ,
+	RES_UNTIL ,
+	RES_DO    ,
+	RES_DONE  ,
+#endif
+#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
+	RES_IN    ,
+#endif
+#if ENABLE_HUSH_CASE
+	RES_CASE  ,
+	/* three pseudo-keywords support contrived "case" syntax: */
+	RES_CASE_IN,   /* "case ... IN", turns into RES_MATCH when IN is observed */
+	RES_MATCH ,    /* "word)" */
+	RES_CASE_BODY, /* "this command is inside CASE" */
+	RES_ESAC  ,
+#endif
+	RES_XXXX  ,
+	RES_SNTX
+};
+
+typedef struct o_string {
+	char *data;
+	int length; /* position where data is appended */
+	int maxlen;
+	int o_expflags;
+	/* At least some part of the string was inside '' or "",
+	 * possibly empty one: word"", wo''rd etc. */
+	smallint has_quoted_part;
+	smallint has_empty_slot;
+	smallint o_assignment; /* 0:maybe, 1:yes, 2:no */
+} o_string;
+enum {
+	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,
+	/* 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 }
+
+#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 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;
+} in_str;
+#define i_getch(input) ((input)->get(input))
+#define i_peek(input) ((input)->peek(input))
+
+/* The descrip member of this structure is only used to make
+ * debugging output pretty */
+static const struct {
+	int mode;
+	signed char default_fd;
+	char descrip[3];
+} redir_table[] = {
+	{ O_RDONLY,                  0, "<"  },
+	{ O_CREAT|O_TRUNC|O_WRONLY,  1, ">"  },
+	{ O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
+	{ O_CREAT|O_RDWR,            1, "<>" },
+	{ O_RDONLY,                  0, "<<" },
+/* Should not be needed. Bogus default_fd helps in debugging */
+/*	{ O_RDONLY,                 77, "<<" }, */
+};
+
+struct redir_struct {
+	struct redir_struct *next;
+	char *rd_filename;          /* filename */
+	int rd_fd;                  /* fd to redirect */
+	/* fd to redirect to, or -3 if rd_fd is to be closed (n>&-) */
+	int rd_dup;
+	smallint rd_type;           /* (enum redir_type) */
+	/* note: for heredocs, rd_filename contains heredoc delimiter,
+	 * and subsequently heredoc itself; and rd_dup is a bitmask:
+	 * bit 0: do we need to trim leading tabs?
+	 * bit 1: is heredoc quoted (<<'delim' syntax) ?
+	 */
+};
+typedef enum redir_type {
+	REDIRECT_INPUT     = 0,
+	REDIRECT_OVERWRITE = 1,
+	REDIRECT_APPEND    = 2,
+	REDIRECT_IO        = 3,
+	REDIRECT_HEREDOC   = 4,
+	REDIRECT_HEREDOC2  = 5, /* REDIRECT_HEREDOC after heredoc is loaded */
+
+	REDIRFD_CLOSE      = -3,
+	REDIRFD_SYNTAX_ERR = -2,
+	REDIRFD_TO_FILE    = -1,
+	/* otherwise, rd_fd is redirected to rd_dup */
+
+	HEREDOC_SKIPTABS = 1,
+	HEREDOC_QUOTED   = 2,
+} 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
+#if ENABLE_HUSH_BASH_COMPAT
+/* used for "[[ EXPR ]]" */
+# define CMD_SINGLEWORD_NOGLOB 2
+#endif
+#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
+	char *group_as_string;
+#endif
+#if ENABLE_HUSH_FUNCTIONS
+	struct function *child_func;
+/* This field is used to prevent a bug here:
+ * while...do f1() {a;}; f1; f1() {b;}; f1; done
+ * When we execute "f1() {a;}" cmd, we create new function and clear
+ * cmd->group, cmd->group_as_string, cmd->argv[0].
+ * When we execute "f1() {b;}", we notice that f1 exists,
+ * and that its "parent cmd" struct is still "alive",
+ * we put those fields back into cmd->xxx
+ * (struct function has ->parent_cmd ptr to facilitate that).
+ * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
+ * Without this trick, loop would execute a;b;b;b;...
+ * instead of correct sequence a;b;a;b;...
+ * When command is freed, it severs the link
+ * (sets ->child_func->parent_cmd to NULL).
+ */
+#endif
+	char **argv;                /* command name and arguments */
+/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
+ * and on execution these are substituted with their values.
+ * Substitution can make _several_ words out of one argv[n]!
+ * Example: argv[0]=='.^C*^C.' here: echo .$*.
+ * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
+ */
+	struct redir_struct *redirects; /* I/O redirections */
+};
+/* Is there anything in this command at all? */
+#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 */
+	int alive_cmds;             /* number of commands running (not exited) */
+	int stopped_cmds;           /* number of commands alive, but stopped */
+#if ENABLE_HUSH_JOB
+	int jobid;                  /* job number */
+	pid_t pgrp;                 /* process group ID for the job */
+	char *cmdtext;              /* name of job */
+#endif
+	struct command *cmds;       /* array of commands in pipe */
+	smallint followup;          /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
+	IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
+	IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
+};
+typedef enum pipe_style {
+	PIPE_SEQ = 1,
+	PIPE_AND = 2,
+	PIPE_OR  = 3,
+	PIPE_BG  = 4,
+} pipe_style;
+/* Is there anything in this pipe at all? */
+#define IS_NULL_PIPE(pi) \
+	((pi)->num_cmds == 0 IF_HAS_KEYWORDS( && (pi)->res_word == RES_NONE))
+
+/* This holds pointers to the various results of parsing */
+struct parse_context {
+	/* linked list of pipes */
+	struct pipe *list_head;
+	/* last pipe (being constructed right now) */
+	struct pipe *pipe;
+	/* last command in pipe (being constructed right now) */
+	struct command *command;
+	/* last redirect in command->redirects list */
+	struct redir_struct *pending_redirect;
+#if !BB_MMU
+	o_string as_string;
+#endif
+#if HAS_KEYWORDS
+	smallint ctx_res_w;
+	smallint ctx_inverted; /* "! cmd | cmd" */
+#if ENABLE_HUSH_CASE
+	smallint ctx_dsemicolon; /* ";;" seen */
+#endif
+	/* bitmask of FLAG_xxx, for figuring out valid reserved words */
+	int old_flag;
+	/* group we are enclosed in:
+	 * example: "if pipe1; pipe2; then pipe3; fi"
+	 * when we see "if" or "then", we malloc and copy current context,
+	 * and make ->stack point to it. then we parse pipeN.
+	 * when closing "then" / fi" / whatever is found,
+	 * we move list_head into ->stack->command->group,
+	 * copy ->stack into current context, and delete ->stack.
+	 * (parsing of { list } and ( list ) doesn't use this method)
+	 */
+	struct parse_context *stack;
+#endif
+};
+
+/* On program start, environ points to initial environment.
+ * putenv adds new pointers into it, unsetenv removes them.
+ * Neither of these (de)allocates the strings.
+ * setenv allocates new strings in malloc space and does putenv,
+ * and thus setenv is unusable (leaky) for shell's purposes */
+#define setenv(...) setenv_is_leaky_dont_use()
+struct variable {
+	struct variable *next;
+	char *varstr;        /* points to "name=" portion */
+#if ENABLE_HUSH_LOCAL
+	unsigned func_nest_level;
+#endif
+	int max_len;         /* if > 0, name is part of initial env; else name is malloced */
+	smallint flg_export; /* putenv should be done on this var */
+	smallint flg_read_only;
+};
+
+enum {
+	BC_BREAK = 1,
+	BC_CONTINUE = 2,
+};
+
+#if ENABLE_HUSH_FUNCTIONS
+struct function {
+	struct function *next;
+	char *name;
+	struct command *parent_cmd;
+	struct pipe *body;
+# if !BB_MMU
+	char *body_as_string;
+# endif
+};
+#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 {
+	/* interactive_fd != 0 means we are an interactive shell.
+	 * If we are, then saved_tty_pgrp can also be != 0, meaning
+	 * that controlling tty is available. With saved_tty_pgrp == 0,
+	 * job control still works, but terminal signals
+	 * (^C, ^Z, ^Y, ^\) won't work at all, and background
+	 * process groups can only be created with "cmd &".
+	 * With saved_tty_pgrp != 0, hush will use tcsetpgrp()
+	 * to give tty to the foreground process group,
+	 * and will take it back when the group is stopped (^Z)
+	 * or killed (^C).
+	 */
+#if ENABLE_HUSH_INTERACTIVE
+	/* 'interactive_fd' is a fd# open to ctty, if we have one
+	 * _AND_ if we decided to act interactively */
+	int interactive_fd;
+	const char *PS1;
+	const char *PS2;
+# define G_interactive_fd (G.interactive_fd)
+#else
+# define G_interactive_fd 0
+#endif
+#if ENABLE_FEATURE_EDITING
+	line_input_t *line_input_state;
+#endif
+	pid_t root_pid;
+	pid_t root_ppid;
+	pid_t last_bg_pid;
+#if ENABLE_HUSH_RANDOM_SUPPORT
+	random_t random_gen;
+#endif
+#if ENABLE_HUSH_JOB
+	int run_list_level;
+	int last_jobid;
+	pid_t saved_tty_pgrp;
+	struct pipe *job_list;
+# define G_saved_tty_pgrp (G.saved_tty_pgrp)
+#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;
+#endif
+#if ENABLE_HUSH_FUNCTIONS
+	/* 0: outside of a function (or sourced file)
+	 * -1: inside of a function, ok to use return builtin
+	 * 1: return is invoked, skip all till end of func
+	 */
+	smallint flag_return_in_progress;
+#endif
+	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;
+	/* how many non-NULL argv's we have. NB: $# + 1 */
+	int global_argc;
+	char **global_argv;
+#if !BB_MMU
+	char *argv0_for_re_execing;
+#endif
+#if ENABLE_HUSH_LOOPS
+	unsigned depth_break_continue;
+	unsigned depth_of_loop;
+#endif
+	const char *ifs;
+	const char *cwd;
+	struct variable *top_var;
+	char **expanded_assignments;
+#if ENABLE_HUSH_FUNCTIONS
+	struct function *top_func;
+# if ENABLE_HUSH_LOCAL
+	struct variable **shadowed_vars_pp;
+	unsigned func_nest_level;
+# endif
+#endif
+	/* Signal and trap handling */
+#if ENABLE_HUSH_FAST
+	unsigned count_SIGCHLD;
+	unsigned handled_SIGCHLD;
+	smallint we_have_children;
+#endif
+	/* 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 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)
+/* Not #defining name to G.name - this quickly gets unwieldy
+ * (too many defines). Also, I actually prefer to see when a variable
+ * 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)
+
+
+/* Function prototypes for builtins */
+static int builtin_cd(char **argv) FAST_FUNC;
+static int builtin_echo(char **argv) FAST_FUNC;
+static int builtin_eval(char **argv) FAST_FUNC;
+static int builtin_exec(char **argv) FAST_FUNC;
+static int builtin_exit(char **argv) FAST_FUNC;
+static int builtin_export(char **argv) FAST_FUNC;
+#if ENABLE_HUSH_JOB
+static int builtin_fg_bg(char **argv) FAST_FUNC;
+static int builtin_jobs(char **argv) FAST_FUNC;
+#endif
+#if ENABLE_HUSH_HELP
+static int builtin_help(char **argv) FAST_FUNC;
+#endif
+#if ENABLE_HUSH_LOCAL
+static int builtin_local(char **argv) FAST_FUNC;
+#endif
+#if HUSH_DEBUG
+static int builtin_memleak(char **argv) FAST_FUNC;
+#endif
+#if ENABLE_PRINTF
+static int builtin_printf(char **argv) FAST_FUNC;
+#endif
+static int builtin_pwd(char **argv) FAST_FUNC;
+static int builtin_read(char **argv) FAST_FUNC;
+static int builtin_set(char **argv) FAST_FUNC;
+static int builtin_shift(char **argv) FAST_FUNC;
+static int builtin_source(char **argv) FAST_FUNC;
+static int builtin_test(char **argv) FAST_FUNC;
+static int builtin_trap(char **argv) FAST_FUNC;
+static int builtin_type(char **argv) FAST_FUNC;
+static int builtin_true(char **argv) FAST_FUNC;
+static int builtin_umask(char **argv) FAST_FUNC;
+static int builtin_unset(char **argv) FAST_FUNC;
+static int builtin_wait(char **argv) FAST_FUNC;
+#if ENABLE_HUSH_LOOPS
+static int builtin_break(char **argv) FAST_FUNC;
+static int builtin_continue(char **argv) FAST_FUNC;
+#endif
+#if ENABLE_HUSH_FUNCTIONS
+static int builtin_return(char **argv) FAST_FUNC;
+#endif
+
+/* Table of built-in functions.  They can be forked or not, depending on
+ * context: within pipes, they fork.  As simple commands, they do not.
+ * When used in non-forking context, they can change global variables
+ * in the parent shell process.  If forked, of course they cannot.
+ * For example, 'unset foo | whatever' will parse and run, but foo will
+ * still be set at the end. */
+struct built_in_command {
+	const char *b_cmd;
+	int (*b_function)(char **argv) FAST_FUNC;
+#if ENABLE_HUSH_HELP
+	const char *b_descr;
+# define BLTIN(cmd, func, help) { cmd, func, help }
+#else
+# define BLTIN(cmd, func, help) { cmd, func }
+#endif
+};
+
+static const struct built_in_command bltins1[] = {
+	BLTIN("."        , builtin_source  , "Run commands in a file"),
+	BLTIN(":"        , builtin_true    , NULL),
+#if ENABLE_HUSH_JOB
+	BLTIN("bg"       , builtin_fg_bg   , "Resume a job in the background"),
+#endif
+#if ENABLE_HUSH_LOOPS
+	BLTIN("break"    , builtin_break   , "Exit from a loop"),
+#endif
+	BLTIN("cd"       , builtin_cd      , "Change directory"),
+#if ENABLE_HUSH_LOOPS
+	BLTIN("continue" , builtin_continue, "Start new loop iteration"),
+#endif
+	BLTIN("eval"     , builtin_eval    , "Construct and run shell command"),
+	BLTIN("exec"     , builtin_exec    , "Execute command, don't return to shell"),
+	BLTIN("exit"     , builtin_exit    , "Exit"),
+	BLTIN("export"   , builtin_export  , "Set environment variables"),
+#if ENABLE_HUSH_JOB
+	BLTIN("fg"       , builtin_fg_bg   , "Bring job into the foreground"),
+#endif
+#if ENABLE_HUSH_HELP
+	BLTIN("help"     , builtin_help    , NULL),
+#endif
+#if ENABLE_HUSH_JOB
+	BLTIN("jobs"     , builtin_jobs    , "List jobs"),
+#endif
+#if ENABLE_HUSH_LOCAL
+	BLTIN("local"    , builtin_local   , "Set local variables"),
+#endif
+#if HUSH_DEBUG
+	BLTIN("memleak"  , builtin_memleak , NULL),
+#endif
+	BLTIN("read"     , builtin_read    , "Input into variable"),
+#if ENABLE_HUSH_FUNCTIONS
+	BLTIN("return"   , builtin_return  , "Return from a function"),
+#endif
+	BLTIN("set"      , builtin_set     , "Set/unset positional parameters"),
+	BLTIN("shift"    , builtin_shift   , "Shift positional parameters"),
+#if ENABLE_HUSH_BASH_COMPAT
+	BLTIN("source"   , builtin_source  , "Run commands in a file"),
+#endif
+	BLTIN("trap"     , builtin_trap    , "Trap signals"),
+	BLTIN("type"     , builtin_type    , "Show command type"),
+	BLTIN("ulimit"   , shell_builtin_ulimit  , "Control resource limits"),
+	BLTIN("umask"    , builtin_umask   , "Set file creation mask"),
+	BLTIN("unset"    , builtin_unset   , "Unset variables"),
+	BLTIN("wait"     , builtin_wait    , "Wait for process"),
+};
+/* For now, echo and test are unconditionally enabled.
+ * Maybe make it configurable? */
+static const struct built_in_command bltins2[] = {
+	BLTIN("["        , builtin_test    , NULL),
+	BLTIN("echo"     , builtin_echo    , NULL),
+#if ENABLE_PRINTF
+	BLTIN("printf"   , builtin_printf  , NULL),
+#endif
+	BLTIN("pwd"      , builtin_pwd     , NULL),
+	BLTIN("test"     , builtin_test    , NULL),
+};
+
+
+/* Debug printouts.
+ */
+#if HUSH_DEBUG
+/* prevent disasters with G.debug_indent < 0 */
+# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
+# define debug_enter() (G.debug_indent++)
+# define debug_leave() (G.debug_indent--)
+#else
+# define indent()      ((void)0)
+# define debug_enter() ((void)0)
+# define debug_leave() ((void)0)
+#endif
+
+#ifndef debug_printf
+# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
+#endif
+
+#ifndef debug_printf_parse
+# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
+#endif
+
+#ifndef debug_printf_exec
+#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
+#endif
+
+#ifndef debug_printf_env
+# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
+#endif
+
+#ifndef debug_printf_jobs
+# 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(), fdprintf(2, __VA_ARGS__))
+# define DEBUG_EXPAND 1
+#else
+# define DEBUG_EXPAND 0
+#endif
+
+#ifndef debug_printf_varexp
+# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
+#endif
+
+#ifndef debug_printf_glob
+# 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(), fdprintf(2, __VA_ARGS__))
+#endif
+
+#ifndef debug_printf_subst
+# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
+#endif
+
+#ifndef debug_printf_clean
+# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
+# define DEBUG_CLEAN 1
+#else
+# define DEBUG_CLEAN 0
+#endif
+
+#if DEBUG_EXPAND
+static void debug_print_strings(const char *prefix, char **vv)
+{
+	indent();
+	fdprintf(2, "%s:\n", prefix);
+	while (*vv)
+		fdprintf(2, " '%s'\n", *vv++);
+}
+#else
+# define debug_print_strings(prefix, vv) ((void)0)
+#endif
+
+
+/* Leak hunting. Use hush_leaktool.sh for post-processing.
+ */
+#if LEAK_HUNTING
+static void *xxmalloc(int lineno, size_t size)
+{
+	void *ptr = xmalloc((size + 0xff) & ~0xff);
+	fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
+	return ptr;
+}
+static void *xxrealloc(int lineno, void *ptr, size_t size)
+{
+	ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
+	fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
+	return ptr;
+}
+static char *xxstrdup(int lineno, const char *str)
+{
+	char *ptr = xstrdup(str);
+	fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
+	return ptr;
+}
+static void xxfree(void *ptr)
+{
+	fdprintf(2, "free %p\n", ptr);
+	free(ptr);
+}
+# define xmalloc(s)     xxmalloc(__LINE__, s)
+# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
+# define xstrdup(s)     xxstrdup(__LINE__, s)
+# define free(p)        xxfree(p)
+#endif
+
+
+/* Syntax and runtime errors. They always abort scripts.
+ * In interactive use they usually discard unparsed and/or unexecuted commands
+ * and return to the prompt.
+ * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
+ */
+#if HUSH_DEBUG < 2
+# define die_if_script(lineno, ...)             die_if_script(__VA_ARGS__)
+# define syntax_error(lineno, msg)              syntax_error(msg)
+# define syntax_error_at(lineno, msg)           syntax_error_at(msg)
+# define syntax_error_unterm_ch(lineno, ch)     syntax_error_unterm_ch(ch)
+# define syntax_error_unterm_str(lineno, s)     syntax_error_unterm_str(s)
+# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
+#endif
+
+static void die_if_script(unsigned lineno, const char *fmt, ...)
+{
+	va_list p;
+
+#if HUSH_DEBUG >= 2
+	bb_error_msg("hush.c:%u", lineno);
+#endif
+	va_start(p, fmt);
+	bb_verror_msg(fmt, p, NULL);
+	va_end(p);
+	if (!G_interactive_fd)
+		xfunc_die();
+}
+
+static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
+{
+	if (msg)
+		bb_error_msg("syntax error: %s", msg);
+	else
+		bb_error_msg("syntax error");
+}
+
+static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
+{
+	bb_error_msg("syntax error at '%s'", msg);
+}
+
+static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
+{
+	bb_error_msg("syntax error: unterminated %s", s);
+}
+
+static void syntax_error_unterm_ch(unsigned lineno, char ch)
+{
+	char msg[2] = { ch, '\0' };
+	syntax_error_unterm_str(lineno, msg);
+}
+
+static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
+{
+	char msg[2];
+	msg[0] = ch;
+	msg[1] = '\0';
+	bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
+}
+
+#if HUSH_DEBUG < 2
+# undef die_if_script
+# undef syntax_error
+# undef syntax_error_at
+# undef syntax_error_unterm_ch
+# undef syntax_error_unterm_str
+# undef syntax_error_unexpected_ch
+#else
+# define die_if_script(...)             die_if_script(__LINE__, __VA_ARGS__)
+# define syntax_error(msg)              syntax_error(__LINE__, msg)
+# define syntax_error_at(msg)           syntax_error_at(__LINE__, msg)
+# define syntax_error_unterm_ch(ch)     syntax_error_unterm_ch(__LINE__, ch)
+# define syntax_error_unterm_str(s)     syntax_error_unterm_str(__LINE__, s)
+# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
+#endif
+
+
+#if ENABLE_HUSH_INTERACTIVE
+static void cmdedit_update_prompt(void);
+#else
+# define cmdedit_update_prompt() ((void)0)
+#endif
+
+
+/* Utility functions
+ */
+/* Replace each \x with x in place, return ptr past NUL. */
+static char *unbackslash(char *src)
+{
+	char *dst = src = strchrnul(src, '\\');
+	while (1) {
+		if (*src == '\\')
+			src++;
+		if ((*dst++ = *src++) == '\0')
+			break;
+	}
+	return dst;
+}
+
+static char **add_strings_to_strings(char **strings, char **add, int need_to_dup)
+{
+	int i;
+	unsigned count1;
+	unsigned count2;
+	char **v;
+
+	v = strings;
+	count1 = 0;
+	if (v) {
+		while (*v) {
+			count1++;
+			v++;
+		}
+	}
+	count2 = 0;
+	v = add;
+	while (*v) {
+		count2++;
+		v++;
+	}
+	v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*));
+	v[count1 + count2] = NULL;
+	i = count2;
+	while (--i >= 0)
+		v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
+	return v;
+}
+#if LEAK_HUNTING
+static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
+{
+	char **ptr = add_strings_to_strings(strings, add, need_to_dup);
+	fdprintf(2, "line %d: add_strings_to_strings %p\n", lineno, ptr);
+	return ptr;
+}
+#define add_strings_to_strings(strings, add, need_to_dup) \
+	xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
+#endif
+
+/* Note: takes ownership of "add" ptr (it is not strdup'ed) */
+static char **add_string_to_strings(char **strings, char *add)
+{
+	char *v[2];
+	v[0] = add;
+	v[1] = NULL;
+	return add_strings_to_strings(strings, v, /*dup:*/ 0);
+}
+#if LEAK_HUNTING
+static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
+{
+	char **ptr = add_string_to_strings(strings, add);
+	fdprintf(2, "line %d: add_string_to_strings %p\n", lineno, ptr);
+	return ptr;
+}
+#define add_string_to_strings(strings, add) \
+	xx_add_string_to_strings(__LINE__, strings, add)
+#endif
+
+static void free_strings(char **strings)
+{
+	char **v;
+
+	if (!strings)
+		return;
+	v = strings;
+	while (*v) {
+		free(*v);
+		v++;
+	}
+	free(strings);
+}
+
+
+/* Helpers for setting new $n and restoring them back
+ */
+typedef struct save_arg_t {
+	char *sv_argv0;
+	char **sv_g_argv;
+	int sv_g_argc;
+	smallint sv_g_malloced;
+} save_arg_t;
+
+static void save_and_replace_G_args(save_arg_t *sv, char **argv)
+{
+	int n;
+
+	sv->sv_argv0 = argv[0];
+	sv->sv_g_argv = G.global_argv;
+	sv->sv_g_argc = G.global_argc;
+	sv->sv_g_malloced = G.global_args_malloced;
+
+	argv[0] = G.global_argv[0]; /* retain $0 */
+	G.global_argv = argv;
+	G.global_args_malloced = 0;
+
+	n = 1;
+	while (*++argv)
+		n++;
+	G.global_argc = n;
+}
+
+static void restore_G_args(save_arg_t *sv, char **argv)
+{
+	char **pp;
+
+	if (G.global_args_malloced) {
+		/* someone ran "set -- arg1 arg2 ...", undo */
+		pp = G.global_argv;
+		while (*++pp) /* note: does not free $0 */
+			free(*pp);
+		free(G.global_argv);
+	}
+	argv[0] = sv->sv_argv0;
+	G.global_argv = sv->sv_g_argv;
+	G.global_argc = sv->sv_g_argc;
+	G.global_args_malloced = sv->sv_g_malloced;
+}
+
+
+/* Basic theory of signal handling in shell
+ * ========================================
+ * This does not describe what hush does, rather, it is current understanding
+ * what it _should_ do. If it doesn't, it's a bug.
+ * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap
+ *
+ * Signals are handled only after each pipe ("cmd | cmd | cmd" thing)
+ * is finished or backgrounded. It is the same in interactive and
+ * non-interactive shells, and is the same regardless of whether
+ * a user trap handler is installed or a shell special one is in effect.
+ * ^C or ^Z from keyboard seems to execute "at once" because it usually
+ * backgrounds (i.e. stops) or kills all members of currently running
+ * pipe.
+ *
+ * Wait builtin in interruptible by signals for which user trap is set
+ * or by SIGINT in interactive shell.
+ *
+ * Trap handlers will execute even within trap handlers. (right?)
+ *
+ * User trap handlers are forgotten when subshell ("(cmd)") is entered,
+ * except for handlers set to '' (empty string).
+ *
+ * If job control is off, backgrounded commands ("cmd &")
+ * have SIGINT, SIGQUIT set to SIG_IGN.
+ *
+ * Commands which are run in command substitution ("`cmd`")
+ * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN.
+ *
+ * Ordinary commands have signals set to SIG_IGN/DFL as inherited
+ * by the shell from its parent.
+ *
+ * Signals which differ from SIG_DFL action
+ * (note: child (i.e., [v]forked) shell is not an interactive shell):
+ *
+ * SIGQUIT: ignore
+ * SIGTERM (interactive): ignore
+ * SIGHUP (interactive):
+ *    send SIGCONT to stopped jobs, send SIGHUP to all jobs and exit
+ * SIGTTIN, SIGTTOU, SIGTSTP (if job control is on): ignore
+ *    Note that ^Z is handled not by trapping SIGTSTP, but by seeing
+ *    that all pipe members are stopped. Try this in bash:
+ *    while :; do :; done - ^Z does not background it
+ *    (while :; do :; done) - ^Z backgrounds it
+ * SIGINT (interactive): wait for last pipe, ignore the rest
+ *    of the command line, show prompt. NB: ^C does not send SIGINT
+ *    to interactive shell while shell is waiting for a pipe,
+ *    since shell is bg'ed (is not in foreground process group).
+ *    Example 1: this waits 5 sec, but does not execute ls:
+ *    "echo $$; sleep 5; ls -l" + "kill -INT <pid>"
+ *    Example 2: this does not wait and does not execute ls:
+ *    "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?)
+ *
+ * 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
+ * above, and all signals which have traps set.
+ * After each pipe execution, we extract any pending signals via sigtimedwait()
+ * and act on them.
+ *
+ * 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 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:
+ *    unblock signals with special interactive handling
+ *    (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.
+ *    Restore blocked signal set to one inherited by shell just prior to exec.
+ *
+ * Note: as a result, we do not use signal handlers much. The only uses
+ * are to count SIGCHLDs
+ * and to restore tty pgrp on signal-induced exit.
+ *
+ * Note 2 (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.
+ *
+ * Problem: the above approach makes it unwieldy to catch signals while
+ * we are in read builtin, of 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 rocorded 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
+		| (1 << SIGTERM)
+		| (1 << SIGINT)
+		| (1 << SIGHUP)
+		,
+	SPECIAL_JOBSTOP_SIGS = 0
+#if ENABLE_HUSH_JOB
+		| (1 << SIGTTIN)
+		| (1 << SIGTTOU)
+		| (1 << SIGTSTP)
+#endif
+		,
+};
+
+static void record_pending_signo(int sig)
+{
+	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
+
+/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
+# define disable_restore_tty_pgrp_on_exit() (die_sleep = 0)
+/* After [v]fork, in parent: restore tty pgrp on xfunc death */
+# define enable_restore_tty_pgrp_on_exit()  (die_sleep = -1)
+
+/* Restores tty foreground process group, and exits.
+ * May be called as signal handler for fatal signal
+ * (will resend signal to itself, producing correct exit state)
+ * or called directly with -EXITCODE.
+ * We also call it if xfunc is exiting. */
+static void sigexit(int sig) NORETURN;
+static void sigexit(int sig)
+{
+	/* 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) {
+		/* 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)
+		_exit(- sig);
+
+	kill_myself_with_sig(sig); /* does not return */
+}
+#else
+
+# define disable_restore_tty_pgrp_on_exit() ((void)0)
+# define enable_restore_tty_pgrp_on_exit()  ((void)0)
+
+#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)
+{
+	fflush_all();
+	if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) {
+		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);
+	}
+
+#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));
+#else
+	exit(exitcode);
+#endif
+}
+
+
+//TODO: return a mask of ALL handled sigs?
+static int check_and_run_traps(void)
+{
+	int last_sig = 0;
+
+	while (1) {
+		int sig;
+
+		if (sigisemptyset(&G.pending_set))
+			break;
+		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 */
+				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);
+				G.last_exitcode = save_rcode;
+				last_sig = sig;
+			} /* else: "" trap, ignoring signal */
+			continue;
+		}
+		/* not a trap: special action */
+		switch (sig) {
+		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: {
+			struct pipe *job;
+			/* bash is observed to signal whole process groups,
+			 * not individual processes */
+			for (job = G.job_list; job; job = job->next) {
+				if (job->pgrp <= 0)
+					continue;
+				debug_printf_exec("HUPing pgrp %d\n", job->pgrp);
+				if (kill(- job->pgrp, SIGHUP) == 0)
+					kill(- job->pgrp, SIGCONT);
+			}
+			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;
+		}
+	}
+	return last_sig;
+}
+
+
+static const char *get_cwd(int force)
+{
+	if (force || G.cwd == NULL) {
+		/* xrealloc_getcwd_or_warn(arg) calls free(arg),
+		 * we must not try to free(bb_msg_unknown) */
+		if (G.cwd == bb_msg_unknown)
+			G.cwd = NULL;
+		G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
+		if (!G.cwd)
+			G.cwd = bb_msg_unknown;
+	}
+	return G.cwd;
+}
+
+
+/*
+ * Shell and environment variable support
+ */
+static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
+{
+	struct variable **pp;
+	struct variable *cur;
+
+	pp = &G.top_var;
+	while ((cur = *pp) != NULL) {
+		if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
+			return pp;
+		pp = &cur->next;
+	}
+	return NULL;
+}
+
+static const char* FAST_FUNC get_local_var_value(const char *name)
+{
+	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)
+		return utoa(next_random(&G.random_gen));
+#endif
+	return NULL;
+}
+
+/* str holds "NAME=VAL" and is expected to be malloced.
+ * We take ownership of it.
+ * flg_export:
+ *  0: do not change export flag
+ *     (if creating new variable, flag will be 0)
+ *  1: set export flag and putenv the variable
+ * -1: clear export flag and unsetenv the variable
+ * flg_read_only is set only when we handle -R var=val
+ */
+#if !BB_MMU && ENABLE_HUSH_LOCAL
+/* all params are used */
+#elif BB_MMU && ENABLE_HUSH_LOCAL
+#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
+	set_local_var(str, flg_export, local_lvl)
+#elif BB_MMU && !ENABLE_HUSH_LOCAL
+#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
+	set_local_var(str, flg_export)
+#elif !BB_MMU && !ENABLE_HUSH_LOCAL
+#define set_local_var(str, flg_export, local_lvl, flg_read_only) \
+	set_local_var(str, flg_export, flg_read_only)
+#endif
+static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_only)
+{
+	struct variable **var_pp;
+	struct variable *cur;
+	char *eq_sign;
+	int name_len;
+
+	eq_sign = strchr(str, '=');
+	if (!eq_sign) { /* not expected to ever happen? */
+		free(str);
+		return -1;
+	}
+
+	name_len = eq_sign - str + 1; /* including '=' */
+	var_pp = &G.top_var;
+	while ((cur = *var_pp) != NULL) {
+		if (strncmp(cur->varstr, str, name_len) != 0) {
+			var_pp = &cur->next;
+			continue;
+		}
+		/* We found an existing var with this name */
+		if (cur->flg_read_only) {
+#if !BB_MMU
+			if (!flg_read_only)
+#endif
+				bb_error_msg("%s: readonly variable", str);
+			free(str);
+			return -1;
+		}
+		if (flg_export == -1) { // "&& cur->flg_export" ?
+			debug_printf_env("%s: unsetenv '%s'\n", __func__, str);
+			*eq_sign = '\0';
+			unsetenv(str);
+			*eq_sign = '=';
+		}
+#if ENABLE_HUSH_LOCAL
+		if (cur->func_nest_level < local_lvl) {
+			/* New variable is declared as local,
+			 * and existing one is global, or local
+			 * from enclosing function.
+			 * Remove and save old one: */
+			*var_pp = cur->next;
+			cur->next = *G.shadowed_vars_pp;
+			*G.shadowed_vars_pp = cur;
+			/* bash 3.2.33(1) and exported vars:
+			 * # export z=z
+			 * # f() { local z=a; env | grep ^z; }
+			 * # f
+			 * z=a
+			 * # env | grep ^z
+			 * z=z
+			 */
+			if (cur->flg_export)
+				flg_export = 1;
+			break;
+		}
+#endif
+		if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) {
+ free_and_exp:
+			free(str);
+			goto exp;
+		}
+		if (cur->max_len != 0) {
+			if (cur->max_len >= strlen(str)) {
+				/* This one is from startup env, reuse space */
+				strcpy(cur->varstr, str);
+				goto free_and_exp;
+			}
+		} else {
+			/* max_len == 0 signifies "malloced" var, which we can
+			 * (and has to) free */
+			free(cur->varstr);
+		}
+		cur->max_len = 0;
+		goto set_str_and_exp;
+	}
+
+	/* Not found - create new variable struct */
+	cur = xzalloc(sizeof(*cur));
+#if ENABLE_HUSH_LOCAL
+	cur->func_nest_level = local_lvl;
+#endif
+	cur->next = *var_pp;
+	*var_pp = cur;
+
+ set_str_and_exp:
+	cur->varstr = str;
+#if !BB_MMU
+	cur->flg_read_only = flg_read_only;
+#endif
+ exp:
+	if (flg_export == 1)
+		cur->flg_export = 1;
+	if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
+		cmdedit_update_prompt();
+	if (cur->flg_export) {
+		if (flg_export == -1) {
+			cur->flg_export = 0;
+			/* unsetenv was already done */
+		} else {
+			debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr);
+			return putenv(cur->varstr);
+		}
+	}
+	return 0;
+}
+
+/* Used at startup and after each cd */
+static void set_pwd_var(int exp)
+{
+	set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)),
+		/*exp:*/ exp, /*lvl:*/ 0, /*ro:*/ 0);
+}
+
+static int unset_local_var_len(const char *name, int name_len)
+{
+	struct variable *cur;
+	struct variable **var_pp;
+
+	if (!name)
+		return EXIT_SUCCESS;
+	var_pp = &G.top_var;
+	while ((cur = *var_pp) != NULL) {
+		if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
+			if (cur->flg_read_only) {
+				bb_error_msg("%s: readonly variable", name);
+				return EXIT_FAILURE;
+			}
+			*var_pp = cur->next;
+			debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
+			bb_unsetenv(cur->varstr);
+			if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
+				cmdedit_update_prompt();
+			if (!cur->max_len)
+				free(cur->varstr);
+			free(cur);
+			return EXIT_SUCCESS;
+		}
+		var_pp = &cur->next;
+	}
+	return EXIT_SUCCESS;
+}
+
+static int unset_local_var(const char *name)
+{
+	return unset_local_var_len(name, strlen(name));
+}
+
+static void unset_vars(char **strings)
+{
+	char **v;
+
+	if (!strings)
+		return;
+	v = strings;
+	while (*v) {
+		const char *eq = strchrnul(*v, '=');
+		unset_local_var_len(*v, (int)(eq - *v));
+		v++;
+	}
+	free(strings);
+}
+
+static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
+{
+	char *var = xasprintf("%s=%s", name, val);
+	set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
+}
+
+
+/*
+ * Helpers for "var1=val1 var2=val2 cmd" feature
+ */
+static void add_vars(struct variable *var)
+{
+	struct variable *next;
+
+	while (var) {
+		next = var->next;
+		var->next = G.top_var;
+		G.top_var = var;
+		if (var->flg_export) {
+			debug_printf_env("%s: restoring exported '%s'\n", __func__, var->varstr);
+			putenv(var->varstr);
+		} else {
+			debug_printf_env("%s: restoring variable '%s'\n", __func__, var->varstr);
+		}
+		var = next;
+	}
+}
+
+static struct variable *set_vars_and_save_old(char **strings)
+{
+	char **s;
+	struct variable *old = NULL;
+
+	if (!strings)
+		return old;
+	s = strings;
+	while (*s) {
+		struct variable *var_p;
+		struct variable **var_pp;
+		char *eq;
+
+		eq = strchr(*s, '=');
+		if (eq) {
+			var_pp = get_ptr_to_local_var(*s, eq - *s);
+			if (var_pp) {
+				/* Remove variable from global linked list */
+				var_p = *var_pp;
+				debug_printf_env("%s: removing '%s'\n", __func__, var_p->varstr);
+				*var_pp = var_p->next;
+				/* Add it to returned list */
+				var_p->next = old;
+				old = var_p;
+			}
+			set_local_var(*s, /*exp:*/ 1, /*lvl:*/ 0, /*ro:*/ 0);
+		}
+		s++;
+	}
+	return old;
+}
+
+
+/*
+ * in_str support
+ */
+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;
+}
+
+static int FAST_FUNC static_peek(struct in_str *i)
+{
+	return *i->p;
+}
+
+#if ENABLE_HUSH_INTERACTIVE
+
+static void cmdedit_update_prompt(void)
+{
+	if (ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
+		G.PS1 = get_local_var_value("PS1");
+		if (G.PS1 == NULL)
+			G.PS1 = "\\w \\$ ";
+		G.PS2 = get_local_var_value("PS2");
+	} else {
+		G.PS1 = NULL;
+	}
+	if (G.PS2 == NULL)
+		G.PS2 = "> ";
+}
+
+static const char *setup_prompt_string(int promptmode)
+{
+	const char *prompt_str;
+	debug_printf("setup_prompt_string %d ", promptmode);
+	if (!ENABLE_FEATURE_EDITING_FANCY_PROMPT) {
+		/* Set up the prompt */
+		if (promptmode == 0) { /* PS1 */
+			free((char*)G.PS1);
+			/* bash uses $PWD value, even if it is set by user.
+			 * It uses current dir only if PWD is unset.
+			 * We always use current dir. */
+			G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
+			prompt_str = G.PS1;
+		} else
+			prompt_str = G.PS2;
+	} else
+		prompt_str = (promptmode == 0) ? G.PS1 : G.PS2;
+	debug_printf("result '%s'\n", prompt_str);
+	return prompt_str;
+}
+
+static void get_user_input(struct in_str *i)
+{
+	int r;
+	const char *prompt_str;
+
+	prompt_str = setup_prompt_string(i->promptmode);
+# if ENABLE_FEATURE_EDITING
+	/* 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:
+		 */
+		reinit_unicode(get_local_var_value("LANG"));
+
+		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(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();
+	} while (r == 0 || G.flag_SIGINT); /* repeat if ^C or SIGINT */
+	i->eof_flag = (r < 0);
+	if (i->eof_flag) { /* EOF/error detected */
+		G.user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */
+		G.user_input_buf[1] = '\0';
+	}
+# else
+	do {
+		G.flag_SIGINT = 0;
+		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 */
+	} while (G.flag_SIGINT);
+	i->eof_flag = (r == EOF);
+# endif
+	i->p = G.user_input_buf;
+}
+
+#endif  /* INTERACTIVE */
+
+/* This is the magic location that prints prompts
+ * and gets data back from the user */
+static int FAST_FUNC file_get(struct in_str *i)
+{
+	int ch;
+
+	/* If there is data waiting, eat it up */
+	if (i->p && *i->p) {
+#if ENABLE_HUSH_INTERACTIVE
+ take_cached:
+#endif
+		ch = *i->p++;
+		if (i->eof_flag && !*i->p)
+			ch = EOF;
+		/* note: ch is never NUL */
+	} else {
+		/* 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->file == stdin) {
+			do {
+				get_user_input(i);
+			} while (!*i->p); /* need non-empty line */
+			i->promptmode = 1; /* PS2 */
+			goto take_cached;
+		}
+#endif
+		do ch = fgetc(i->file); while (ch == '\0');
+	}
+	debug_printf("file_get: got '%c' %d\n", ch, ch);
+	i->last_char = ch;
+	return ch;
+}
+
+/* All callers guarantee this routine will never
+ * be used right after a newline, so prompting is not needed.
+ */
+static int FAST_FUNC file_peek(struct in_str *i)
+{
+	int ch;
+	if (i->p && *i->p) {
+		if (i->eof_flag && !i->p[1])
+			return EOF;
+		return *i->p;
+		/* note: ch is never NUL */
+	}
+	do ch = fgetc(i->file); while (ch == '\0');
+	i->eof_flag = (ch == EOF);
+	i->peek_buf[0] = ch;
+	i->peek_buf[1] = '\0';
+	i->p = i->peek_buf;
+	debug_printf("file_peek: got '%c' %d\n", ch, ch);
+	return ch;
+}
+
+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;
+	/* i->promptmode = 0; - PS1 (memset did it) */
+	i->file = f;
+	/* 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;
+	/* i->promptmode = 0; - PS1 (memset did it) */
+	i->p = s;
+	/* i->eof_flag = 0; */
+}
+
+
+/*
+ * o_string support
+ */
+#define B_CHUNK  (32 * sizeof(char*))
+
+static void o_reset_to_empty_unquoted(o_string *o)
+{
+	o->length = 0;
+	o->has_quoted_part = 0;
+	if (o->data)
+		o->data[0] = '\0';
+}
+
+static void o_free(o_string *o)
+{
+	free(o->data);
+	memset(o, 0, sizeof(*o));
+}
+
+static ALWAYS_INLINE void o_free_unsafe(o_string *o)
+{
+	free(o->data);
+}
+
+static void o_grow_by(o_string *o, int len)
+{
+	if (o->length + len > o->maxlen) {
+		o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK);
+		o->data = xrealloc(o->data, 1 + o->maxlen);
+	}
+}
+
+static void o_addchr(o_string *o, int ch)
+{
+	debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
+	o_grow_by(o, 1);
+	o->data[o->length] = ch;
+	o->length++;
+	o->data[o->length] = '\0';
+}
+
+static void o_addblock(o_string *o, const char *str, int len)
+{
+	o_grow_by(o, len);
+	memcpy(&o->data[o->length], str, len);
+	o->length += len;
+	o->data[o->length] = '\0';
+}
+
+static void o_addstr(o_string *o, const char *str)
+{
+	o_addblock(o, str, strlen(str));
+}
+
+#if !BB_MMU
+static void nommu_addchr(o_string *o, int ch)
+{
+	if (o)
+		o_addchr(o, ch);
+}
+#else
+# define nommu_addchr(o, str) ((void)0)
+#endif
+
+static void o_addstr_with_NUL(o_string *o, const char *str)
+{
+	o_addblock(o, str, strlen(str) + 1);
+}
+
+/*
+ * 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),
+ * but NOT brace expansion! Thus, there should be TWO independent
+ * quoting mechanisms on $v expansion side: one protects
+ * $v from brace expansion, and other additionally protects "$v" against globbing.
+ * We have only second one.
+ */
+
+#if ENABLE_HUSH_BRACE_EXPANSION
+# define MAYBE_BRACES "{}"
+#else
+# define MAYBE_BRACES ""
+#endif
+
+/* My analysis of quoting semantics tells me that state information
+ * is associated with a destination, not a source.
+ */
+static void o_addqchr(o_string *o, int ch)
+{
+	int sz = 1;
+	char *found = strchr("*?[\\" MAYBE_BRACES, ch);
+	if (found)
+		sz++;
+	o_grow_by(o, sz);
+	if (found) {
+		o->data[o->length] = '\\';
+		o->length++;
+	}
+	o->data[o->length] = ch;
+	o->length++;
+	o->data[o->length] = '\0';
+}
+
+static void o_addQchr(o_string *o, int ch)
+{
+	int sz = 1;
+	if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
+	 && strchr("*?[\\" MAYBE_BRACES, ch)
+	) {
+		sz++;
+		o->data[o->length] = '\\';
+		o->length++;
+	}
+	o_grow_by(o, sz);
+	o->data[o->length] = ch;
+	o->length++;
+	o->data[o->length] = '\0';
+}
+
+static void o_addqblock(o_string *o, const char *str, int len)
+{
+	while (len) {
+		char ch;
+		int sz;
+		int ordinary_cnt = strcspn(str, "*?[\\" MAYBE_BRACES);
+		if (ordinary_cnt > len) /* paranoia */
+			ordinary_cnt = len;
+		o_addblock(o, str, ordinary_cnt);
+		if (ordinary_cnt == len)
+			return; /* NUL is already added by o_addblock */
+		str += ordinary_cnt;
+		len -= ordinary_cnt + 1; /* we are processing + 1 char below */
+
+		ch = *str++;
+		sz = 1;
+		if (ch) { /* it is necessarily one of "*?[\\" MAYBE_BRACES */
+			sz++;
+			o->data[o->length] = '\\';
+			o->length++;
+		}
+		o_grow_by(o, sz);
+		o->data[o->length] = ch;
+		o->length++;
+	}
+	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.
+ * It contains char* list[] at the beginning, which is grown in 16 element
+ * increments. Actual string data starts at the next multiple of 16 * (char*).
+ * list[i] contains an INDEX (int!) into this string data.
+ * It means that if list[] needs to grow, data needs to be moved higher up
+ * but list[i]'s need not be modified.
+ * NB: remembering how many list[i]'s you have there is crucial.
+ * o_finalize_list() operation post-processes this structure - calculates
+ * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well.
+ */
+#if DEBUG_EXPAND || DEBUG_GLOB
+static void debug_print_list(const char *prefix, o_string *o, int n)
+{
+	char **list = (char**)o->data;
+	int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
+	int i = 0;
+
+	indent();
+	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();
+		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)(uintptr_t)list[n - 1] + string_start;
+		indent();
+		fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
+	}
+}
+#else
+# define debug_print_list(prefix, o, n) ((void)0)
+#endif
+
+/* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value
+ * in list[n] so that it points past last stored byte so far.
+ * It returns n+1. */
+static int o_save_ptr_helper(o_string *o, int n)
+{
+	char **list = (char**)o->data;
+	int string_start;
+	int string_len;
+
+	if (!o->has_empty_slot) {
+		string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
+		string_len = o->length - string_start;
+		if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */
+			debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start);
+			/* list[n] points to string_start, make space for 16 more pointers */
+			o->maxlen += 0x10 * sizeof(list[0]);
+			o->data = xrealloc(o->data, o->maxlen + 1);
+			list = (char**)o->data;
+			memmove(list + n + 0x10, list + n, string_len);
+			o->length += 0x10 * sizeof(list[0]);
+		} else {
+			debug_printf_list("list[%d]=%d string_start=%d\n",
+					n, string_len, string_start);
+		}
+	} else {
+		/* We have empty slot at list[n], reuse without growth */
+		string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */
+		string_len = o->length - string_start;
+		debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n",
+				n, string_len, string_start);
+		o->has_empty_slot = 0;
+	}
+	o->has_quoted_part = 0;
+	list[n] = (char*)(uintptr_t)string_len;
+	return n + 1;
+}
+
+/* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */
+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)(uintptr_t)list[n-1]) + string_start;
+}
+
+#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
+ * existing files. Need to re-implement it.
+ */
+
+/* Helper */
+static int glob_needed(const char *s)
+{
+	while (*s) {
+		if (*s == '\\') {
+			if (!s[1])
+				return 0;
+			s += 2;
+			continue;
+		}
+		if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
+			return 1;
+		s++;
+	}
+	return 0;
+}
+/* Return pointer to next closing brace or to comma */
+static const char *next_brace_sub(const char *cp)
+{
+	unsigned depth = 0;
+	cp++;
+	while (*cp != '\0') {
+		if (*cp == '\\') {
+			if (*++cp == '\0')
+				break;
+			cp++;
+			continue;
+		}
+		if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
+			break;
+		if (*cp++ == '{')
+			depth++;
+	}
+
+	return *cp != '\0' ? cp : NULL;
+}
+/* Recursive brace globber. Note: may garble pattern[]. */
+static int glob_brace(char *pattern, o_string *o, int n)
+{
+	char *new_pattern_buf;
+	const char *begin;
+	const char *next;
+	const char *rest;
+	const char *p;
+	size_t rest_len;
+
+	debug_printf_glob("glob_brace('%s')\n", pattern);
+
+	begin = pattern;
+	while (1) {
+		if (*begin == '\0')
+			goto simple_glob;
+		if (*begin == '{') {
+			/* Find the first sub-pattern and at the same time
+			 * find the rest after the closing brace */
+			next = next_brace_sub(begin);
+			if (next == NULL) {
+				/* An illegal expression */
+				goto simple_glob;
+			}
+			if (*next == '}') {
+				/* "{abc}" with no commas - illegal
+				 * brace expr, disregard and skip it */
+				begin = next + 1;
+				continue;
+			}
+			break;
+		}
+		if (*begin == '\\' && begin[1] != '\0')
+			begin++;
+		begin++;
+	}
+	debug_printf_glob("begin:%s\n", begin);
+	debug_printf_glob("next:%s\n", next);
+
+	/* Now find the end of the whole brace expression */
+	rest = next;
+	while (*rest != '}') {
+		rest = next_brace_sub(rest);
+		if (rest == NULL) {
+			/* An illegal expression */
+			goto simple_glob;
+		}
+		debug_printf_glob("rest:%s\n", rest);
+	}
+	rest_len = strlen(++rest) + 1;
+
+	/* We are sure the brace expression is well-formed */
+
+	/* Allocate working buffer large enough for our work */
+	new_pattern_buf = xmalloc(strlen(pattern));
+
+	/* We have a brace expression.  BEGIN points to the opening {,
+	 * NEXT points past the terminator of the first element, and REST
+	 * points past the final }.  We will accumulate result names from
+	 * recursive runs for each brace alternative in the buffer using
+	 * GLOB_APPEND.  */
+
+	p = begin + 1;
+	while (1) {
+		/* Construct the new glob expression */
+		memcpy(
+			mempcpy(
+				mempcpy(new_pattern_buf,
+					/* We know the prefix for all sub-patterns */
+					pattern, begin - pattern),
+				p, next - p),
+			rest, rest_len);
+
+		/* Note: glob_brace() may garble new_pattern_buf[].
+		 * That's why we re-copy prefix every time (1st memcpy above).
+		 */
+		n = glob_brace(new_pattern_buf, o, n);
+		if (*next == '}') {
+			/* We saw the last entry */
+			break;
+		}
+		p = next + 1;
+		next = next_brace_sub(next);
+	}
+	free(new_pattern_buf);
+	return n;
+
+ simple_glob:
+	{
+		int gr;
+		glob_t globdata;
+
+		memset(&globdata, 0, sizeof(globdata));
+		gr = glob(pattern, 0, NULL, &globdata);
+		debug_printf_glob("glob('%s'):%d\n", pattern, gr);
+		if (gr != 0) {
+			if (gr == GLOB_NOMATCH) {
+				globfree(&globdata);
+				/* NB: garbles parameter */
+				unbackslash(pattern);
+				o_addstr_with_NUL(o, pattern);
+				debug_printf_glob("glob pattern '%s' is literal\n", pattern);
+				return o_save_ptr_helper(o, n);
+			}
+			if (gr == GLOB_NOSPACE)
+				bb_error_msg_and_die(bb_msg_memory_exhausted);
+			/* GLOB_ABORTED? Only happens with GLOB_ERR flag,
+			 * but we didn't specify it. Paranoia again. */
+			bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
+		}
+		if (globdata.gl_pathv && globdata.gl_pathv[0]) {
+			char **argv = globdata.gl_pathv;
+			while (1) {
+				o_addstr_with_NUL(o, *argv);
+				n = o_save_ptr_helper(o, n);
+				argv++;
+				if (!*argv)
+					break;
+			}
+		}
+		globfree(&globdata);
+	}
+	return n;
+}
+/* Performs globbing on last list[],
+ * saving each result as a new list[].
+ */
+static int perform_glob(o_string *o, int n)
+{
+	char *pattern, *copy;
+
+	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);
+	debug_printf_glob("glob pattern '%s'\n", pattern);
+	if (!glob_needed(pattern)) {
+		/* unbackslash last string in o in place, fix length */
+		o->length = unbackslash(pattern) - o->data;
+		debug_printf_glob("glob pattern '%s' is literal\n", pattern);
+		return o_save_ptr_helper(o, n);
+	}
+
+	copy = xstrdup(pattern);
+	/* "forget" pattern in o */
+	o->length = pattern - o->data;
+	n = glob_brace(copy, o, n);
+	free(copy);
+	if (DEBUG_GLOB)
+		debug_print_list("perform_glob returning", o, n);
+	return n;
+}
+
+#else /* !HUSH_BRACE_EXPANSION */
+
+/* Helper */
+static int glob_needed(const char *s)
+{
+	while (*s) {
+		if (*s == '\\') {
+			if (!s[1])
+				return 0;
+			s += 2;
+			continue;
+		}
+		if (*s == '*' || *s == '[' || *s == '?')
+			return 1;
+		s++;
+	}
+	return 0;
+}
+/* Performs globbing on last list[],
+ * saving each result as a new list[].
+ */
+static int perform_glob(o_string *o, int n)
+{
+	glob_t globdata;
+	int gr;
+	char *pattern;
+
+	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);
+	debug_printf_glob("glob pattern '%s'\n", pattern);
+	if (!glob_needed(pattern)) {
+ literal:
+		/* unbackslash last string in o in place, fix length */
+		o->length = unbackslash(pattern) - o->data;
+		debug_printf_glob("glob pattern '%s' is literal\n", pattern);
+		return o_save_ptr_helper(o, n);
+	}
+
+	memset(&globdata, 0, sizeof(globdata));
+	/* Can't use GLOB_NOCHECK: it does not unescape the string.
+	 * If we glob "*.\*" and don't find anything, we need
+	 * to fall back to using literal "*.*", but GLOB_NOCHECK
+	 * will return "*.\*"!
+	 */
+	gr = glob(pattern, 0, NULL, &globdata);
+	debug_printf_glob("glob('%s'):%d\n", pattern, gr);
+	if (gr != 0) {
+		if (gr == GLOB_NOMATCH) {
+			globfree(&globdata);
+			goto literal;
+		}
+		if (gr == GLOB_NOSPACE)
+			bb_error_msg_and_die(bb_msg_memory_exhausted);
+		/* GLOB_ABORTED? Only happens with GLOB_ERR flag,
+		 * but we didn't specify it. Paranoia again. */
+		bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
+	}
+	if (globdata.gl_pathv && globdata.gl_pathv[0]) {
+		char **argv = globdata.gl_pathv;
+		/* "forget" pattern in o */
+		o->length = pattern - o->data;
+		while (1) {
+			o_addstr_with_NUL(o, *argv);
+			n = o_save_ptr_helper(o, n);
+			argv++;
+			if (!*argv)
+				break;
+		}
+	}
+	globfree(&globdata);
+	if (DEBUG_GLOB)
+		debug_print_list("perform_glob returning", o, n);
+	return n;
+}
+
+#endif /* !HUSH_BRACE_EXPANSION */
+
+/* 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_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 perform_glob(o, n); /* o_save_ptr_helper is inside */
+	}
+	return o_save_ptr_helper(o, n);
+}
+
+/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */
+static char **o_finalize_list(o_string *o, int n)
+{
+	char **list;
+	int string_start;
+
+	n = o_save_ptr(o, n); /* force growth for list[n] if necessary */
+	if (DEBUG_EXPAND)
+		debug_print_list("finalized", o, n);
+	debug_printf_expand("finalized n:%d\n", n);
+	list = (char**)o->data;
+	string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
+	list[--n] = NULL;
+	while (n) {
+		n--;
+		list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
+	}
+	return list;
+}
+
+static void free_pipe_list(struct pipe *pi);
+
+/* 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
+#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
+
+	next = pi->next;
+	free(pi);
+	return next;
+}
+
+static void free_pipe_list(struct pipe *pi)
+{
+	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);
+	}
+}
+
+
+/*** Parsing routines ***/
+
+#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;
+
+	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 */
+
+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;
+}
+
+/* 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)
+{
+	/* 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 (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 */
+}
+
+static void done_pipe(struct parse_context *ctx, pipe_style type)
+{
+	int not_null;
+
+	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
+
+	/* 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);
+	}
+	debug_printf_parse("done_pipe return\n");
+}
+
+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);
+}
+
+/* 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 ),
+};
+
+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.
+	 */
+	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;
+
+	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;
+
+	if (word->has_quoted_part)
+		return 0;
+	r = match_reserved_word(word);
+	if (!r)
+		return 0;
+
+	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;
+		}
+		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);
+	}
+
+	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 (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);
+	}
+	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)
+{
+	struct command *command = ctx->command;
+
+	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;
+	}
+
+	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 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);
+	}
+
+#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
+
+	o_reset_to_empty_unquoted(word);
+
+	debug_printf_parse("done_word return 0\n");
+	return 0;
+}
+
+
+/* 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)
+{
+	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;
+}
+
+/* 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)
+{
+	struct command *command = ctx->command;
+	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);
+		}
+	}
+
+	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);
+		}
+	}
+
+	/* 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;
+	}
+	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 ->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;
+
+	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;
+}
+
+#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;
+
+	goto jump_in;
+
+	while (1) {
+		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');
+			}
+		}
+		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;
+	}
+}
+
+/* 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)
+{
+	struct pipe *pi = ctx->list_head;
+
+	while (pi && heredoc_cnt) {
+		int i;
+		struct command *cmd = pi->cmds;
+
+		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;
+
+			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;
+
+					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;
+			}
+			cmd++;
+		}
+		pi = pi->next;
+	}
+#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;
+}
+
+
+static int run_list(struct pipe *pi);
+#if BB_MMU
+#define parse_stream(pstring, input, end_trigger) \
+	parse_stream(input, end_trigger)
+#endif
+static struct pipe *parse_stream(char **pstring,
+		struct in_str *input,
+		int end_trigger);
+
+
+#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)
+{
+	/* 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;
+
+	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;
+		}
+		/* it is "word(..." or "word (..." */
+		do
+			ch = i_getch(input);
+		while (ch == ' ' || ch == '\t');
+		if (ch != ')') {
+			syntax_error_unexpected_ch(ch);
+			return 1;
+		}
+		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;
+		}
+		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->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
+ 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);
+	}
+
+	{
+#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 */
+}
+
+#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)
+{
+	while (1) {
+		int ch = i_getch(input);
+		if (ch == EOF) {
+			syntax_error_unterm_ch('\'');
+			return 0;
+		}
+		if (ch == '\'')
+			return 1;
+		o_addchr(dest, ch);
+	}
+}
+/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
+static int add_till_double_quote(o_string *dest, struct in_str *input)
+{
+	while (1) {
+		int ch = i_getch(input);
+		if (ch == EOF) {
+			syntax_error_unterm_ch('"');
+			return 0;
+		}
+		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 == '$') ...
+	}
+}
+/* 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)
+{
+	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, '\\');
+			}
+		}
+		if (ch == EOF) {
+			syntax_error_unterm_ch('`');
+			return 0;
+		}
+		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]} 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)
+{
+	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);
+
+	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;
+			}
+		}
+		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;
+		}
+	}
+	return ch;
+}
+#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 parse_dollar(as_string, dest, input, quote_mask) \
+	parse_dollar(dest, input, quote_mask)
+#define as_string NULL
+#endif
+static int parse_dollar(o_string *as_string,
+		o_string *dest,
+		struct in_str *input, unsigned char quote_mask)
+{
+	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);
+
+		ch = i_getch(input); /* eat '{' */
+		nommu_addchr(as_string, ch);
+
+		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;
+
+		/* 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... */
+
+		while (1) {
+			unsigned pos;
+
+			o_addchr(dest, ch);
+			debug_printf_parse(": '%c'\n", ch);
+
+			ch = i_getch(input);
+			nommu_addchr(as_string, ch);
+			if (ch == '}')
+				break;
+
+			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;
+	}
+#if ENABLE_SH_MATH_SUPPORT || ENABLE_HUSH_TICK
+	case '(': {
+		unsigned pos;
+
+		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
+# 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
+	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
+}
+
+#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 !ENABLE_HUSH_BASH_COMPAT
+	const int process_bkslash = 1;
+#endif
+	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 */
+		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 (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;
+	}
+	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
+}
+
+/*
+ * 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)
+{
+	struct parse_context ctx;
+	o_string dest = NULL_O_STRING;
+	int heredoc_cnt;
+
+	/* 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();
+
+	/* If very first arg is "" or '', dest.data may end up NULL.
+	 * Preventing this: */
+	o_addchr(&dest, '\0');
+	dest.length = 0;
+
+	/* 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
+	 */
+
+	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;
+
+		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;
+
+			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;
+			}
+
+			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
+			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);
+
+		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;
+		}
+		is_special = strchr(is_special, ch);
+		is_blank = strchr(defifs, ch);
+
+		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]);
+			}
+			continue;
+		}
+
+		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;
+					}
+				}
+				/* 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;
+				debug_printf_parse("dest.o_assignment='%s'\n", assignment_flag[dest.o_assignment]);
+				ch = ';';
+				/* note: if (is_blank) 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.has_quoted_part    /* ""} */
+			) {
+				goto ordinary_char;
+			}
+			if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
+				goto skip_end_trigger;
+			/* else: } does terminate a group */
+		}
+
+		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
+		) {
+			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;
+			}
+			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
+				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;
+
+		/* 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 (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;
+			}
+#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) */
+			}
+			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
+				ch = i_getch(input); /* eat it */
+				continue; /* back to top of while (1) */
+			}
+			break;
+		}
+
+		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]);
+		}
+
+		/* Note: nommu_addchr(&ctx.as_string, ch) is already done */
+
+		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;)
+
+			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
+ 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 {
+				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)" */
+#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);
+#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.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) */
+
+ 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);
+#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();
+		return ERR_PTR;
+	}
+}
+
+
+/*** 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). */
+
+/* A horde of its helpers come first: */
+
+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++);
+			}
+		}
+	}
+}
+
+/* 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)
+{
+	int last_is_ifs = 0;
+
+	while (1) {
+		int word_len;
+
+		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;
+		}
+
+		/* 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])
+		) {
+			o_addchr(output, '\0');
+			debug_print_list("expand_on_ifs", output, n);
+			n = o_save_ptr(output, n);
+		}
+	}
+
+	if (ended_with_ifs)
+		*ended_with_ifs = last_is_ifs;
+	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.
+ */
+#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 (!strchr(str, '$')
+	 && !strchr(str, '\\')
+#if ENABLE_HUSH_TICK
+	 && !strchr(str, '`')
+#endif
+	) {
+		return NULL;
+	}
+
+	/* 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 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
+
+#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;
+		}
+		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);
+
+	while (1) {
+		int size;
+		char *s = strstr_pattern(val, pattern, &size);
+		if (!s)
+			break;
+
+		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
+
+/* 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 */
+				}
+			}
+		}
+#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);
+			}
+		}
+#endif
+		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
+			{
+				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;
+				}
+
+				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 "-=+?" */
+
+		*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_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);
+					}
+				}
+			} 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;
+
+			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
+		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
+		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;
+}
+
+static char **expand_variables(char **argv, unsigned expflags)
+{
+	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++;
+	}
+	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);
+}
+
+#if ENABLE_HUSH_BASH_COMPAT
+static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
+{
+	return expand_variables(argv, EXP_FLAG_SINGLEWORD);
+}
+#endif
+
+/* 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 char *expand_string_to_string(const char *str, int do_unbackslash)
+{
+#if !ENABLE_HUSH_BASH_COMPAT
+	const int do_unbackslash = 1;
+#endif
+	char *argv[2], **list;
+
+	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);
+	}
+
+	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;
+}
+
+/* Used for "eval" builtin */
+static char* expand_strvec_to_string(char **argv)
+{
+	char **list;
+
+	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;
+}
+
+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));
+	}
+	G.expanded_assignments = NULL;
+	return p;
+}
+
+
+static void switch_off_special_sigs(unsigned mask)
+{
+	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 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 void reset_traps_to_defaults(void)
+{
+	/* 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.
+	 */
+	mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
+	if (!G.traps && !mask)
+		return; /* already no traps and no special sigs */
+
+	/* 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));
+	}
+}
+
+#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 (!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;
+	}
+
+	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;
+		}
+	}
+
+	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;
+
+ 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 */
+
+
+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
+	}
+}
+
+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 in_str input;
+	setup_file_in_str(&input, f);
+	parse_and_run_stream(&input, ';');
+}
+
+#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
+
+	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] == '\0'
+		) {
+			static const char *const argv[] = { NULL, NULL };
+			builtin_trap((char**)argv);
+			exit(0); /* not _exit() - we need to fflush */
+		}
+# 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
+	}
+
+	/* 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
+	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;
+		}
+		while (eol_cnt) {
+			o_addchr(dest, '\n');
+			eol_cnt--;
+		}
+		o_addQchr(dest, ch);
+	}
+
+	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
+
+	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);
+
+	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);
+
+	/* 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, /*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;
+		}
+
+		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[])
+{
+	int i, fd;
+	for (i = 0; i < 3; i++) {
+		fd = squirrel[i];
+		if (fd != -1) {
+			/* We simply die on error */
+			xmove_fd(fd, i);
+		}
+	}
+}
+
+static char *find_in_path(const char *arg)
+{
+	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! */
+
+		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 (access(ret, F_OK) == 0)
+			break;
+
+		if (*end == '\0') {
+			free(ret);
+			return NULL;
+		}
+		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;
+		}
+		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)]);
+}
+
+#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;
+}
+
+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;
+}
+
+/* Note: takes ownership on name ptr */
+static struct function *new_function(char *name)
+{
+	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
+		}
+	} else {
+		debug_printf_exec("remembering new function '%s'\n", name);
+		funcp = *funcpp = xzalloc(sizeof(*funcp));
+		/*funcp->next = NULL;*/
+	}
+
+	funcp->name = name;
+	return funcp;
+}
+
+static void unset_func(const char *name)
+{
+	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
+		}
+		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
+	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
+	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;
+			}
+			/* 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 */
+
+
+#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)
+{
+#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
+}
+
+
+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 */
+}
+
+#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);
+	}
+}
+#else
+# define dump_cmd_in_x_mode(argv) ((void)0)
+#endif
+
+#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
+
+/* 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;
+
+	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);
+		}
+	}
+#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);
+		}
+	}
+#endif
+
+#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_FEATURE_SH_STANDALONE || BB_MMU
+ skip:
+#endif
+	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
+		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
+	}
+
+	/* Case when we are here: ... | >file */
+	debug_printf_exec("pseudo_exec'ed null command\n");
+	_exit(EXIT_SUCCESS);
+}
+
+#if ENABLE_HUSH_JOB
+static const char *get_cmdtext(struct pipe *pi)
+{
+	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;
+}
+
+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));
+
+	*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;
+		}
+		if (fg_pipe == NULL) { /* is WNOHANG set? */
+			/* We have children, but they did not exit
+			 * or stop yet (we saw no SIGCHLD) */
+			return 0;
+		}
+		/* 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;
+
+#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);
+			}
+#endif
+			break;
+		}
+		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) {
+			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->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) {
+					/* 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 */
+		}
+
+#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;
+			}
+		}
+		/* 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);
+			}
+		} 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,
+ * 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 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)
+{
+	static const char *const null_ptr = NULL;
+
+	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;
+
+	debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
+	debug_enter();
+
+	/* 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;
+
+	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;
+		}
+		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 };
+#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" */
+#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++;
+				}
+			}
+			/* 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;
+
+#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++;
+			}
+			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
+					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
+			}
+ 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;
+		}
+
+		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;
+			}
+		}
+		/* 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;
+	next_infd = 0;
+
+	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
+		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");
+		}
+
+		/* pipes are inserted between pairs of commands */
+		pipefds.rd = 0;
+		pipefds.wr = 1;
+		if (cmd_no < pi->num_cmds)
+			xpiped_pair(pipefds);
+
+		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);
+				}
+			}
+#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
+		/* 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
+		}
+
+		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;
+	}
+
+	if (!pi->alive_cmds) {
+		debug_leave();
+		debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
+		return 1;
+	}
+
+	debug_leave();
+	debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
+	return -1;
+}
+
+/* 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
+	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
+
+	debug_printf_exec("run_list start lvl %d\n", G.run_list_level);
+	debug_enter();
+
+#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;
+			}
+			/* "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..."? */
+			) {
+				syntax_error("malformed for");
+				debug_leave();
+				debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
+				return 1;
+			}
+		}
+	}
+#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 ENABLE_HUSH_JOB
+	G.run_list_level++;
+#endif
+
+#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;
+				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;
+			}
+		}
+#endif
+#if ENABLE_HUSH_LOOPS
+		if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
+			if (!for_lcur) {
+				/* first loop through for */
+
+				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;
+
+				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 */
+		}
+#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, /*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;
+				}
+				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;
+		}
+
+		/* 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();
+#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;
+				}
+#endif
+#if ENABLE_HUSH_FUNCTIONS
+				if (G.flag_return_in_progress == 1) {
+					/* same as "goto check_jobs_and_break" */
+					checkjobs(NULL);
+					break;
+				}
+#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();
+#if ENABLE_HUSH_JOB
+				if (G.run_list_level == 1)
+					insert_bg_job(pi);
+#endif
+				/* 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
+				{ /* 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;
+			}
+		}
+
+		/* Analyze how result affects subsequent commands */
+#if ENABLE_HUSH_IF
+		if (rword == RES_IF || rword == RES_ELIF)
+			cond_code = rcode;
+#endif
+#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");
+					goto check_jobs_and_break;
+				}
+			}
+			if (rword == RES_UNTIL) {
+				if (!rcode) {
+					debug_printf_exec(": until expr is true: breaking\n");
+ check_jobs_and_break:
+					checkjobs(NULL);
+					break;
+				}
+			}
+		}
+#endif
+
+ 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);
+#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;
+}
+
+/* Select which version we will use */
+static int run_and_free_list(struct pipe *pi)
+{
+	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 install_sighandlers(unsigned mask)
+{
+	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 install_special_sighandlers(void)
+{
+	unsigned mask;
+
+	/* Which signals are shell-special? */
+	mask = (1 << SIGQUIT) | (1 << SIGCHLD);
+	if (G_interactive_fd) {
+		mask |= SPECIAL_INTERACTIVE_SIGS;
+		if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
+			mask |= SPECIAL_JOBSTOP_SIGS;
+	}
+	/* 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);
+	}
+}
+
+#if ENABLE_HUSH_JOB
+/* helper */
+/* Set handlers to restore tty pgrp and exit */
+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 */
+		+ (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
+	 */
+		/*+ (1 << SIGHUP )*/
+		/*+ (1 << SIGTERM)*/
+		/*+ (1 << SIGINT )*/
+	;
+	G_fatal_sig_mask = mask;
+
+	install_sighandlers(mask);
+}
+#endif
+
+static int set_mode(int state, char mode, const char *o_opt)
+{
+	int idx;
+	switch (mode) {
+	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;
+}
+
+int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int hush_main(int argc, char **argv)
+{
+	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 != 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 */
+	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 */
+	G.top_var = shell_ver;
+	cur_var = G.top_var;
+	e = environ;
+	if (e) while (*e) {
+		char *value = strchr(*e, '=');
+		if (value) { /* paranoia */
+			cur_var->next = xzalloc(sizeof(*cur_var));
+			cur_var = cur_var->next;
+			cur_var->varstr = *e;
+			cur_var->max_len = strlen(*e);
+			cur_var->flg_export = 1;
+		}
+		e++;
+	}
+	/* (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);
+	/* bash also exports SHLVL and _,
+	 * and sets (but doesn't export) the following variables:
+	 * BASH=/bin/bash
+	 * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu")
+	 * BASH_VERSION='3.2.0(1)-release'
+	 * HOSTTYPE=i386
+	 * MACHTYPE=i386-pc-linux-gnu
+	 * OSTYPE=linux-gnu
+	 * HOSTNAME=<xxxxxxxxxx>
+	 * PPID=<NNNNN> - we also do it elsewhere
+	 * EUID=<NNNNN>
+	 * UID=<NNNNN>
+	 * GROUPS=()
+	 * LINES=<NNN>
+	 * COLUMNS=<NNN>
+	 * BASH_ARGC=()
+	 * BASH_ARGV=()
+	 * BASH_LINENO=()
+	 * BASH_SOURCE=()
+	 * DIRSTACK=()
+	 * PIPESTATUS=([0]="0")
+	 * HISTFILE=/<xxx>/.bash_history
+	 * HISTFILESIZE=500
+	 * HISTSIZE=500
+	 * MAILCHECK=60
+	 * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
+	 * SHELL=/bin/bash
+	 * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
+	 * TERM=dumb
+	 * OPTERR=1
+	 * OPTIND=1
+	 * IFS=$' \t\n'
+	 * PS1='\s-\v\$ '
+	 * PS2='> '
+	 * PS4='+ '
+	 */
+
+#if ENABLE_FEATURE_EDITING
+	G.line_input_state = new_line_input_t(FOR_SHELL);
+# if MAX_HISTORY > 0 && ENABLE_HUSH_SAVEHISTORY
+	{
+		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
+#endif
+
+	/* Initialize some more globals to non-zero values */
+	cmdedit_update_prompt();
+
+	if (setjmp(die_jmp)) {
+		/* xfunc has failed! die die die */
+		/* no EXIT traps, this is an escape hatch! */
+		G.exiting = 1;
+		hush_exit(xfunc_error_retval);
+	}
+
+	/* Shell is non-interactive at first. We need to call
+	 * 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 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:xinsl"
+#if !BB_MMU
+				"<:$:R:V:"
+# if ENABLE_HUSH_FUNCTIONS
+				"F:"
+# endif
+#endif
+		);
+		if (opt <= 0)
+			break;
+		switch (opt) {
+		case 'c':
+			/* Possibilities:
+			 * sh ... -c 'script'
+			 * sh ... -c 'script' ARG0 [ARG1...]
+			 * On NOMMU, if builtin_argc != 0,
+			 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
+			 * "" needs to be replaced with NULL
+			 * and BARGV vector fed to builtin function.
+			 * Note: the form without ARG0 never happens:
+			 * sh ... -c 'builtin' BARGV... ""
+			 */
+			if (!G.root_pid) {
+				G.root_pid = getpid();
+				G.root_ppid = getppid();
+			}
+			G.global_argv = argv + optind;
+			G.global_argc = argc - optind;
+			if (builtin_argc) {
+				/* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
+				const struct built_in_command *x;
+
+				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;
+			}
+			if (!G.global_argv[0]) {
+				/* -c 'script' (no params): prevent empty $0 */
+				G.global_argv--; /* points to argv[i] of 'script' */
+				G.global_argv[0] = argv[0];
+				G.global_argc++;
+			} /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
+			install_special_sighandlers();
+			parse_and_run_string(optarg);
+			goto final_return;
+		case 'i':
+			/* Well, we cannot just declare interactiveness,
+			 * we have to have some stuff (ctty, etc) */
+			/* G_interactive_fd++; */
+			break;
+		case 's':
+			/* "-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);
+			_exit(0);
+		case '$': {
+			unsigned long long empty_trap_mask;
+
+			G.root_pid = bb_strtou(optarg, &optarg, 16);
+			optarg++;
+			G.root_ppid = bb_strtou(optarg, &optarg, 16);
+			optarg++;
+			G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
+			optarg++;
+			G.last_exitcode = bb_strtou(optarg, &optarg, 16);
+			optarg++;
+			builtin_argc = bb_strtou(optarg, &optarg, 16);
+			optarg++;
+			empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
+			if (empty_trap_mask != 0) {
+				int sig;
+				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(""); */
+						install_sighandler(sig, SIG_IGN);
+					}
+				}
+			}
+# if ENABLE_HUSH_LOOPS
+			optarg++;
+			G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
+# endif
+			break;
+		}
+		case 'R':
+		case 'V':
+			set_local_var(xstrdup(optarg), /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ opt == 'R');
+			break;
+# if ENABLE_HUSH_FUNCTIONS
+		case 'F': {
+			struct function *funcp = new_function(optarg);
+			/* funcp->name is already set to optarg */
+			/* funcp->body is set to NULL. It's a special case. */
+			funcp->body_as_string = argv[optind];
+			optind++;
+			break;
+		}
+# endif
+#endif
+		case 'n':
+		case 'x':
+			if (set_mode(1, opt, NULL) == 0) /* no error */
+				break;
+		default:
+#ifndef BB_VER
+			fprintf(stderr, "Usage: sh [FILE]...\n"
+					"   or: sh -c command [args]...\n\n");
+			exit(EXIT_FAILURE);
+#else
+			bb_show_usage();
+#endif
+		}
+	} /* 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 (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));
+			install_special_sighandlers();
+			parse_and_run_file(input);
+			fclose(input);
+		}
+		/* bash: after sourcing /etc/profile,
+		 * tries to source (in the given order):
+		 * ~/.bash_profile, ~/.bash_login, ~/.profile,
+		 * stopping on first found. --noprofile turns this off.
+		 * bash also sources ~/.bash_logout on exit.
+		 * If called as sh, skips .bash_XXX files.
+		 */
+	}
+
+	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.
+		 */
+		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));
+		install_special_sighandlers();
+		parse_and_run_file(input);
+#if ENABLE_FEATURE_CLEAN_UP
+		fclose(input);
+#endif
+		goto final_return;
+	}
+
+	/* Up to here, shell was non-interactive. Now it may become one.
+	 * NB: don't forget to (re)run install_special_sighandlers() as needed.
+	 */
+
+	/* A shell is interactive if the '-i' flag was given,
+	 * or if all of the following conditions are met:
+	 *    no -c command
+	 *    no arguments remaining or the -s flag given
+	 *    standard input is a terminal
+	 *    standard output is a terminal
+	 * Refer to Posix.2, the description of the 'sh' utility.
+	 */
+#if ENABLE_HUSH_JOB
+	if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
+		G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
+		debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
+		if (G_saved_tty_pgrp < 0)
+			G_saved_tty_pgrp = 0;
+
+		/* try to dup stdin to high fd#, >= 255 */
+		G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
+		if (G_interactive_fd < 0) {
+			/* try to dup to any fd */
+			G_interactive_fd = dup(STDIN_FILENO);
+			if (G_interactive_fd < 0) {
+				/* give up */
+				G_interactive_fd = 0;
+				G_saved_tty_pgrp = 0;
+			}
+		}
+// TODO: track & disallow any attempts of user
+// to (inadvertently) close/redirect G_interactive_fd
+	}
+	debug_printf("interactive_fd:%d\n", G_interactive_fd);
+	if (G_interactive_fd) {
+		close_on_exec_on(G_interactive_fd);
+
+		if (G_saved_tty_pgrp) {
+			/* If we were run as 'hush &', sleep until we are
+			 * in the foreground (tty pgrp == our pgrp).
+			 * If we get started under a job aware app (like bash),
+			 * make sure we are now in charge so we don't fight over
+			 * who gets the foreground */
+			while (1) {
+				pid_t shell_pgrp = getpgrp();
+				G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
+				if (G_saved_tty_pgrp == shell_pgrp)
+					break;
+				/* send TTIN to ourself (should stop us) */
+				kill(- shell_pgrp, SIGTTIN);
+			}
+		}
+
+		/* Install more signal handlers */
+		install_special_sighandlers();
+
+		if (G_saved_tty_pgrp) {
+			/* Set other signals to restore saved_tty_pgrp */
+			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); */
+			/* Grab control of the terminal */
+			tcsetpgrp(G_interactive_fd, getpid());
+		}
+		/* -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 */
+	} else {
+		install_special_sighandlers();
+	}
+#elif ENABLE_HUSH_INTERACTIVE
+	/* No job control compiled in, only prompt/line editing */
+	if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
+		G_interactive_fd = fcntl(STDIN_FILENO, F_DUPFD, 255);
+		if (G_interactive_fd < 0) {
+			/* try to dup to any fd */
+			G_interactive_fd = dup(STDIN_FILENO);
+			if (G_interactive_fd < 0)
+				/* give up */
+				G_interactive_fd = 0;
+		}
+	}
+	if (G_interactive_fd) {
+		close_on_exec_on(G_interactive_fd);
+	}
+	install_special_sighandlers();
+#else
+	/* We have interactiveness code disabled */
+	install_special_sighandlers();
+#endif
+	/* bash:
+	 * if interactive but not a login shell, sources ~/.bashrc
+	 * (--norc turns this off, --rcfile <file> overrides)
+	 */
+
+	if (!ENABLE_FEATURE_SH_EXTRA_QUIET && G_interactive_fd) {
+		/* note: ash and hush share this string */
+		printf("\n\n%s %s\n"
+			IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
+			"\n",
+			bb_banner,
+			"hush - the humble shell"
+		);
+	}
+
+	parse_and_run_file(stdin);
+
+ final_return:
+	hush_exit(G.last_exitcode);
+}
+
+
+#if ENABLE_MSH
+int msh_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int msh_main(int argc, char **argv)
+{
+	//bb_error_msg("msh is deprecated, please use hush instead");
+	return hush_main(argc, argv);
+}
+#endif
+
+
+/*
+ * Built-ins
+ */
+static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
+{
+	return 0;
+}
+
+static int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
+{
+	int argc = 0;
+	while (*argv) {
+		argc++;
+		argv++;
+	}
+	return applet_main_func(argc, argv - argc);
+}
+
+static int FAST_FUNC builtin_test(char **argv)
+{
+	return run_applet_main(argv, test_main);
+}
+
+static int FAST_FUNC builtin_echo(char **argv)
+{
+	return run_applet_main(argv, echo_main);
+}
+
+#if ENABLE_PRINTF
+static int FAST_FUNC builtin_printf(char **argv)
+{
+	return run_applet_main(argv, printf_main);
+}
+#endif
+
+static char **skip_dash_dash(char **argv)
+{
+	argv++;
+	if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0')
+		argv++;
+	return argv;
+}
+
+static int FAST_FUNC builtin_eval(char **argv)
+{
+	int rcode = EXIT_SUCCESS;
+
+	argv = skip_dash_dash(argv);
+	if (*argv) {
+		char *str = expand_strvec_to_string(argv);
+		/* bash:
+		 * eval "echo Hi; done" ("done" is syntax error):
+		 * "echo Hi" will not execute too.
+		 */
+		parse_and_run_string(str);
+		free(str);
+		rcode = G.last_exitcode;
+	}
+	return rcode;
+}
+
+static int FAST_FUNC builtin_cd(char **argv)
+{
+	const char *newdir;
+
+	argv = skip_dash_dash(argv);
+	newdir = argv[0];
+	if (newdir == NULL) {
+		/* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
+		 * bash says "bash: cd: HOME not set" and does nothing
+		 * (exitcode 1)
+		 */
+		const char *home = get_local_var_value("HOME");
+		newdir = home ? home : "/";
+	}
+	if (chdir(newdir)) {
+		/* Mimic bash message exactly */
+		bb_perror_msg("cd: %s", newdir);
+		return EXIT_FAILURE;
+	}
+	/* Read current dir (get_cwd(1) is inside) and set PWD.
+	 * Note: do not enforce exporting. If PWD was unset or unexported,
+	 * set it again, but do not export. bash does the same.
+	 */
+	set_pwd_var(/*exp:*/ 0);
+	return EXIT_SUCCESS;
+}
+
+static int FAST_FUNC builtin_exec(char **argv)
+{
+	argv = skip_dash_dash(argv);
+	if (argv[0] == NULL)
+		return EXIT_SUCCESS; /* bash does this */
+
+	/* 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)
+		tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
+
+	/* TODO: if exec fails, bash does NOT exit! We do.
+	 * We'll need to undo trap cleanup (it's inside execvp_or_die)
+	 * and tcsetpgrp, and this is inherently racy.
+	 */
+	execvp_or_die(argv);
+}
+
+static int FAST_FUNC builtin_exit(char **argv)
+{
+	debug_printf_exec("%s()\n", __func__);
+
+	/* interactive bash:
+	 * # trap "echo EEE" EXIT
+	 * # exit
+	 * exit
+	 * There are stopped jobs.
+	 * (if there are _stopped_ jobs, running ones don't count)
+	 * # exit
+	 * exit
+	 # EEE (then bash exits)
+	 *
+	 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
+	 */
+
+	/* note: EXIT trap is run by hush_exit */
+	argv = skip_dash_dash(argv);
+	if (argv[0] == NULL)
+		hush_exit(G.last_exitcode);
+	/* mimic bash: exit 123abc == exit 255 + error msg */
+	xfunc_error_retval = 255;
+	/* bash: exit -2 == exit 254, no error msg */
+	hush_exit(xatoi(argv[0]) & 0xff);
+}
+
+static void print_escaped(const char *s)
+{
+	if (*s == '\'')
+		goto squote;
+	do {
+		const char *p = strchrnul(s, '\'');
+		/* print 'xxxx', possibly just '' */
+		printf("'%.*s'", (int)(p - s), s);
+		if (*p == '\0')
+			break;
+		s = p;
+ squote:
+		/* s points to '; print "'''...'''" */
+		putchar('"');
+		do putchar('\''); while (*++s == '\'');
+		putchar('"');
+	} while (*s);
+}
+
+#if !ENABLE_HUSH_LOCAL
+#define helper_export_local(argv, exp, lvl) \
+	helper_export_local(argv, exp)
+#endif
+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 (*name_end == '\0') {
+			struct variable *var, **vpp;
+
+			vpp = get_ptr_to_local_var(name, name_end - name);
+			var = vpp ? *vpp : NULL;
+
+			if (exp == -1) { /* unexporting? */
+				/* export -n NAME (without =VALUE) */
+				if (var) {
+					var->flg_export = 0;
+					debug_printf_env("%s: unsetenv '%s'\n", __func__, name);
+					unsetenv(name);
+				} /* else: export -n NOT_EXISTING_VAR: no-op */
+				continue;
+			}
+			if (exp == 1) { /* exporting? */
+				/* export NAME (without =VALUE) */
+				if (var) {
+					var->flg_export = 1;
+					debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr);
+					putenv(var->varstr);
+					continue;
+				}
+			}
+			/* Exporting non-existing variable.
+			 * bash does not put it in environment,
+			 * but remembers that it is exported,
+			 * and does put it in env when it is set later.
+			 * We just set it to "" and export. */
+			/* Or, it's "local NAME" (without =VALUE).
+			 * bash sets the value to "". */
+			name = xasprintf("%s=", name);
+		} else {
+			/* (Un)exporting/making local NAME=VALUE */
+			name = xstrdup(name);
+		}
+		set_local_var(name, /*exp:*/ exp, /*lvl:*/ lvl, /*ro:*/ 0);
+	} while (*++argv);
+}
+
+static int FAST_FUNC builtin_export(char **argv)
+{
+	unsigned opt_unexport;
+
+#if ENABLE_HUSH_EXPORT_N
+	/* "!": do not abort on errors */
+	opt_unexport = getopt32(argv, "!n");
+	if (opt_unexport == (uint32_t)-1)
+		return EXIT_FAILURE;
+	argv += optind;
+#else
+	opt_unexport = 0;
+	argv++;
+#endif
+
+	if (argv[0] == NULL) {
+		char **e = environ;
+		if (e) {
+			while (*e) {
+#if 0
+				puts(*e++);
+#else
+				/* ash emits: export VAR='VAL'
+				 * bash: declare -x VAR="VAL"
+				 * we follow ash example */
+				const char *s = *e++;
+				const char *p = strchr(s, '=');
+
+				if (!p) /* wtf? take next variable */
+					continue;
+				/* export var= */
+				printf("export %.*s", (int)(p - s) + 1, s);
+				print_escaped(p + 1);
+				putchar('\n');
+#endif
+			}
+			/*fflush_all(); - done after each builtin anyway */
+		}
+		return EXIT_SUCCESS;
+	}
+
+	helper_export_local(argv, (opt_unexport ? -1 : 1), 0);
+
+	return EXIT_SUCCESS;
+}
+
+#if ENABLE_HUSH_LOCAL
+static int FAST_FUNC builtin_local(char **argv)
+{
+	if (G.func_nest_level == 0) {
+		bb_error_msg("%s: not in a function", argv[0]);
+		return EXIT_FAILURE; /* bash compat */
+	}
+	helper_export_local(argv, 0, G.func_nest_level);
+	return EXIT_SUCCESS;
+}
+#endif
+
+static int FAST_FUNC builtin_trap(char **argv)
+{
+	int sig;
+	char *new_cmd;
+
+	if (!G.traps)
+		G.traps = xzalloc(sizeof(G.traps[0]) * NSIG);
+
+	argv++;
+	if (!*argv) {
+		int i;
+		/* No args: print all trapped */
+		for (i = 0; i < NSIG; ++i) {
+			if (G.traps[i]) {
+				printf("trap -- ");
+				print_escaped(G.traps[i]);
+				/* note: bash adds "SIG", but only if invoked
+				 * as "bash". If called as "sh", or if set -o posix,
+				 * then it prints short signal names.
+				 * We are printing short names: */
+				printf(" %s\n", get_signame(i));
+			}
+		}
+		/*fflush_all(); - done after each builtin anyway */
+		return EXIT_SUCCESS;
+	}
+
+	new_cmd = NULL;
+	/* If first arg is a number: reset all specified signals */
+	sig = bb_strtou(*argv, NULL, 10);
+	if (errno == 0) {
+		int ret;
+ process_sig_list:
+		ret = EXIT_SUCCESS;
+		while (*argv) {
+			sighandler_t handler;
+
+			sig = get_signum(*argv++);
+			if (sig < 0 || sig >= NSIG) {
+				ret = EXIT_FAILURE;
+				/* Mimic bash message exactly */
+				bb_perror_msg("trap: %s: invalid signal specification", argv[-1]);
+				continue;
+			}
+
+			free(G.traps[sig]);
+			G.traps[sig] = xstrdup(new_cmd);
+
+			debug_printf("trap: setting SIG%s (%i) to '%s'\n",
+				get_signame(sig), sig, G.traps[sig]);
+
+			/* There is no signal for 0 (EXIT) */
+			if (sig == 0)
+				continue;
+
+			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);
+		}
+		return ret;
+	}
+
+	if (!argv[1]) { /* no second arg */
+		bb_error_msg("trap: invalid arguments");
+		return EXIT_FAILURE;
+	}
+
+	/* First arg is "-": reset all specified to default */
+	/* First arg is "--": skip it, the rest is "handler SIGs..." */
+	/* Everything else: set arg as signal handler
+	 * (includes "" case, which ignores signal) */
+	if (argv[0][0] == '-') {
+		if (argv[0][1] == '\0') { /* "-" */
+			/* new_cmd remains NULL: "reset these sigs" */
+			goto reset_traps;
+		}
+		if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */
+			argv++;
+		}
+		/* else: "-something", no special meaning */
+	}
+	new_cmd = *argv;
+ reset_traps:
+	argv++;
+	goto process_sig_list;
+}
+
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
+static int FAST_FUNC builtin_type(char **argv)
+{
+	int ret = EXIT_SUCCESS;
+
+	while (*++argv) {
+		const char *type;
+		char *path = NULL;
+
+		if (0) {} /* make conditional compile easier below */
+		/*else if (find_alias(*argv))
+			type = "an alias";*/
+#if ENABLE_HUSH_FUNCTIONS
+		else if (find_function(*argv))
+			type = "a function";
+#endif
+		else if (find_builtin(*argv))
+			type = "a shell builtin";
+		else if ((path = find_in_path(*argv)) != NULL)
+			type = path;
+		else {
+			bb_error_msg("type: %s: not found", *argv);
+			ret = EXIT_FAILURE;
+			continue;
+		}
+
+		printf("%s is %s\n", *argv, type);
+		free(path);
+	}
+
+	return ret;
+}
+
+#if ENABLE_HUSH_JOB
+/* built-in 'fg' and 'bg' handler */
+static int FAST_FUNC builtin_fg_bg(char **argv)
+{
+	int i, jobnum;
+	struct pipe *pi;
+
+	if (!G_interactive_fd)
+		return EXIT_FAILURE;
+
+	/* If they gave us no args, assume they want the last backgrounded task */
+	if (!argv[1]) {
+		for (pi = G.job_list; pi; pi = pi->next) {
+			if (pi->jobid == G.last_jobid) {
+				goto found;
+			}
+		}
+		bb_error_msg("%s: no current job", argv[0]);
+		return EXIT_FAILURE;
+	}
+	if (sscanf(argv[1], "%%%d", &jobnum) != 1) {
+		bb_error_msg("%s: bad argument '%s'", argv[0], argv[1]);
+		return EXIT_FAILURE;
+	}
+	for (pi = G.job_list; pi; pi = pi->next) {
+		if (pi->jobid == jobnum) {
+			goto found;
+		}
+	}
+	bb_error_msg("%s: %d: no such job", argv[0], jobnum);
+	return EXIT_FAILURE;
+ found:
+	/* TODO: bash prints a string representation
+	 * of job being foregrounded (like "sleep 1 | cat") */
+	if (argv[0][0] == 'f' && G_saved_tty_pgrp) {
+		/* Put the job into the foreground.  */
+		tcsetpgrp(G_interactive_fd, pi->pgrp);
+	}
+
+	/* Restart the processes in the job */
+	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;
+
+	i = kill(- pi->pgrp, SIGCONT);
+	if (i < 0) {
+		if (errno == ESRCH) {
+			delete_finished_bg_job(pi);
+			return EXIT_SUCCESS;
+		}
+		bb_perror_msg("kill (SIGCONT)");
+	}
+
+	if (argv[0][0] == 'f') {
+		remove_bg_job(pi);
+		return checkjobs_and_fg_shell(pi);
+	}
+	return EXIT_SUCCESS;
+}
+#endif
+
+#if ENABLE_HUSH_HELP
+static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
+{
+	const struct built_in_command *x;
+
+	printf(
+		"Built-in commands:\n"
+		"------------------\n");
+	for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
+		if (x->b_descr)
+			printf("%-10s%s\n", x->b_cmd, x->b_descr);
+	}
+	bb_putchar('\n');
+	return EXIT_SUCCESS;
+}
+#endif
+
+#if ENABLE_HUSH_JOB
+static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
+{
+	struct pipe *job;
+	const char *status_string;
+
+	for (job = G.job_list; job; job = job->next) {
+		if (job->alive_cmds == job->stopped_cmds)
+			status_string = "Stopped";
+		else
+			status_string = "Running";
+
+		printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
+	}
+	return EXIT_SUCCESS;
+}
+#endif
+
+#if HUSH_DEBUG
+static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
+{
+	void *p;
+	unsigned long l;
+
+# ifdef M_TRIM_THRESHOLD
+	/* Optional. Reduces probability of false positives */
+	malloc_trim(0);
+# endif
+	/* Crude attempt to find where "free memory" starts,
+	 * sans fragmentation. */
+	p = malloc(240);
+	l = (unsigned long)p;
+	free(p);
+	p = malloc(3400);
+	if (l < (unsigned long)p) l = (unsigned long)p;
+	free(p);
+
+	if (!G.memleak_value)
+		G.memleak_value = l;
+
+	l -= G.memleak_value;
+	if ((long)l < 0)
+		l = 0;
+	l /= 1024;
+	if (l > 127)
+		l = 127;
+
+	/* Exitcode is "how many kilobytes we leaked since 1st call" */
+	return l;
+}
+#endif
+
+static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
+{
+	puts(get_cwd(0));
+	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;
+	char *opt_n = NULL;
+	char *opt_p = NULL;
+	char *opt_t = NULL;
+	char *opt_u = NULL;
+	const char *ifs;
+	int read_flags;
+
+	/* "!": do not abort on errors.
+	 * Option string must start with "sr" to match BUILTIN_READ_xxx
+	 */
+	read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
+	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,
+		ifs,
+		read_flags,
+		opt_n,
+		opt_p,
+		opt_t,
+		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;
+	}
+
+	return (uintptr_t)r;
+}
+
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
+ * built-in 'set' handler
+ * SUSv3 says:
+ * set [-abCefhmnuvx] [-o option] [argument...]
+ * set [+abCefhmnuvx] [+o option] [argument...]
+ * set -- [argument...]
+ * set -o
+ * set +o
+ * Implementations shall support the options in both their hyphen and
+ * plus-sign forms. These options can also be specified as options to sh.
+ * Examples:
+ * Write out all variables and their values: set
+ * Set $1, $2, and $3 and set "$#" to 3: set c a b
+ * Turn on the -x and -v options: set -xv
+ * Unset all positional parameters: set --
+ * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
+ * Set the positional parameters to the expansion of x, even if x expands
+ * with a leading '-' or '+': set -- $x
+ *
+ * So far, we only support "set -- [argument...]" and some of the short names.
+ */
+static int FAST_FUNC builtin_set(char **argv)
+{
+	int n;
+	char **pp, **g_argv;
+	char *arg = *++argv;
+
+	if (arg == NULL) {
+		struct variable *e;
+		for (e = G.top_var; e; e = e->next)
+			puts(e->varstr);
+		return EXIT_SUCCESS;
+	}
+
+	do {
+		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], argv[1]))
+				goto error;
+			if (arg[n] == 'o' && argv[1])
+				argv++;
+		}
+	} while ((arg = *++argv) != NULL);
+	/* Now argv[0] is 1st argument */
+
+	if (arg == NULL)
+		return EXIT_SUCCESS;
+ set_argv:
+
+	/* NB: G.global_argv[0] ($0) is never freed/changed */
+	g_argv = G.global_argv;
+	if (G.global_args_malloced) {
+		pp = g_argv;
+		while (*++pp)
+			free(*pp);
+		g_argv[1] = NULL;
+	} else {
+		G.global_args_malloced = 1;
+		pp = xzalloc(sizeof(pp[0]) * 2);
+		pp[0] = g_argv[0]; /* retain $0 */
+		g_argv = pp;
+	}
+	/* This realloc's G.global_argv */
+	G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
+
+	n = 1;
+	while (*++pp)
+		n++;
+	G.global_argc = n;
+
+	return EXIT_SUCCESS;
+
+	/* Nothing known, so abort */
+ error:
+	bb_error_msg("set: %s: invalid option", arg);
+	return EXIT_FAILURE;
+}
+
+static int FAST_FUNC builtin_shift(char **argv)
+{
+	int n = 1;
+	argv = skip_dash_dash(argv);
+	if (argv[0]) {
+		n = atoi(argv[0]);
+	}
+	if (n >= 0 && n < G.global_argc) {
+		if (G.global_args_malloced) {
+			int m = 1;
+			while (m <= n)
+				free(G.global_argv[m++]);
+		}
+		G.global_argc -= n;
+		memmove(&G.global_argv[1], &G.global_argv[n+1],
+				G.global_argc * sizeof(G.global_argv[0]));
+		return EXIT_SUCCESS;
+	}
+	return EXIT_FAILURE;
+}
+
+static int FAST_FUNC builtin_source(char **argv)
+{
+	char *arg_path, *filename;
+	FILE *input;
+	save_arg_t sv;
+#if ENABLE_HUSH_FUNCTIONS
+	smallint sv_flg;
+#endif
+
+	argv = skip_dash_dash(argv);
+	filename = argv[0];
+	if (!filename) {
+		/* bash says: "bash: .: filename argument required" */
+		return 2; /* bash compat */
+	}
+	arg_path = NULL;
+	if (!strchr(filename, '/')) {
+		arg_path = find_in_path(filename);
+		if (arg_path)
+			filename = arg_path;
+	}
+	input = fopen_or_warn(filename, "r");
+	free(arg_path);
+	if (!input) {
+		/* bb_perror_msg("%s", *argv); - done by fopen_or_warn */
+		return EXIT_FAILURE;
+	}
+	close_on_exec_on(fileno(input));
+
+#if ENABLE_HUSH_FUNCTIONS
+	sv_flg = G.flag_return_in_progress;
+	/* "we are inside sourced file, ok to use return" */
+	G.flag_return_in_progress = -1;
+#endif
+	save_and_replace_G_args(&sv, argv);
+
+	parse_and_run_file(input);
+	fclose(input);
+
+	restore_G_args(&sv, argv);
+#if ENABLE_HUSH_FUNCTIONS
+	G.flag_return_in_progress = sv_flg;
+#endif
+
+	return G.last_exitcode;
+}
+
+static int FAST_FUNC builtin_umask(char **argv)
+{
+	int rc;
+	mode_t mask;
+
+	mask = umask(0);
+	argv = skip_dash_dash(argv);
+	if (argv[0]) {
+		mode_t old_mask = mask;
+
+		mask ^= 0777;
+		rc = bb_parse_mode(argv[0], &mask);
+		mask ^= 0777;
+		if (rc == 0) {
+			mask = old_mask;
+			/* bash messages:
+			 * bash: umask: 'q': invalid symbolic mode operator
+			 * bash: umask: 999: octal number out of range
+			 */
+			bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
+		}
+	} else {
+		rc = 1;
+		/* Mimic bash */
+		printf("%04o\n", (unsigned) mask);
+		/* fall through and restore mask which we set to 0 */
+	}
+	umask(mask);
+
+	return !rc; /* rc != 0 - success */
+}
+
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
+static int FAST_FUNC builtin_unset(char **argv)
+{
+	int ret;
+	unsigned opts;
+
+	/* "!": do not abort on errors */
+	/* "+": stop at 1st non-option */
+	opts = getopt32(argv, "!+vf");
+	if (opts == (unsigned)-1)
+		return EXIT_FAILURE;
+	if (opts == 3) {
+		bb_error_msg("unset: -v and -f are exclusive");
+		return EXIT_FAILURE;
+	}
+	argv += optind;
+
+	ret = EXIT_SUCCESS;
+	while (*argv) {
+		if (!(opts & 2)) { /* not -f */
+			if (unset_local_var(*argv)) {
+				/* unset <nonexistent_var> doesn't fail.
+				 * Error is when one tries to unset RO var.
+				 * Message was printed by unset_local_var. */
+				ret = EXIT_FAILURE;
+			}
+		}
+#if ENABLE_HUSH_FUNCTIONS
+		else {
+			unset_func(*argv);
+		}
+#endif
+		argv++;
+	}
+	return ret;
+}
+
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
+static int FAST_FUNC builtin_wait(char **argv)
+{
+	int ret = EXIT_SUCCESS;
+	int status;
+
+	argv = skip_dash_dash(argv);
+	if (argv[0] == NULL) {
+		/* Don't care about wait results */
+		/* Note 1: must wait until there are no more children */
+		/* Note 2: must be interruptible */
+		/* Examples:
+		 * $ sleep 3 & sleep 6 & wait
+		 * [1] 30934 sleep 3
+		 * [2] 30935 sleep 6
+		 * [1] Done                   sleep 3
+		 * [2] Done                   sleep 6
+		 * $ sleep 3 & sleep 6 & wait
+		 * [1] 30936 sleep 3
+		 * [2] 30937 sleep 6
+		 * [1] Done                   sleep 3
+		 * ^C <-- after ~4 sec from keyboard
+		 * $
+		 */
+		while (1) {
+			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;
+			}
+			/* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
+		}
+		return ret;
+	}
+
+	/* This is probably buggy wrt interruptible-ness */
+	while (*argv) {
+		pid_t pid = bb_strtou(*argv, NULL, 10);
+		if (errno) {
+			/* mimic bash message */
+			bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
+			return EXIT_FAILURE;
+		}
+		if (waitpid(pid, &status, 0) == pid) {
+			if (WIFSIGNALED(status))
+				ret = 128 + WTERMSIG(status);
+			else if (WIFEXITED(status))
+				ret = WEXITSTATUS(status);
+			else /* wtf? */
+				ret = EXIT_FAILURE;
+		} else {
+			bb_perror_msg("wait %s", *argv);
+			ret = 127;
+		}
+		argv++;
+	}
+
+	return ret;
+}
+
+#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
+static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
+{
+	if (argv[1]) {
+		def = bb_strtou(argv[1], NULL, 10);
+		if (errno || def < def_min || argv[2]) {
+			bb_error_msg("%s: bad arguments", argv[0]);
+			def = UINT_MAX;
+		}
+	}
+	return def;
+}
+#endif
+
+#if ENABLE_HUSH_LOOPS
+static int FAST_FUNC builtin_break(char **argv)
+{
+	unsigned depth;
+	if (G.depth_of_loop == 0) {
+		bb_error_msg("%s: only meaningful in a loop", argv[0]);
+		return EXIT_SUCCESS; /* bash compat */
+	}
+	G.flag_break_continue++; /* BC_BREAK = 1 */
+
+	G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
+	if (depth == UINT_MAX)
+		G.flag_break_continue = BC_BREAK;
+	if (G.depth_of_loop < depth)
+		G.depth_break_continue = G.depth_of_loop;
+
+	return EXIT_SUCCESS;
+}
+
+static int FAST_FUNC builtin_continue(char **argv)
+{
+	G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
+	return builtin_break(argv);
+}
+#endif
+
+#if ENABLE_HUSH_FUNCTIONS
+static int FAST_FUNC builtin_return(char **argv)
+{
+	int rc;
+
+	if (G.flag_return_in_progress != -1) {
+		bb_error_msg("%s: not in a function or sourced script", argv[0]);
+		return EXIT_FAILURE; /* bash compat */
+	}
+
+	G.flag_return_in_progress = 1;
+
+	/* bash:
+	 * out of range: wraps around at 256, does not error out
+	 * non-numeric param:
+	 * f() { false; return qwe; }; f; echo $?
+	 * bash: return: qwe: numeric argument required  <== we do this
+	 * 255  <== we also do this
+	 */
+	rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
+	return rc;
+}
+#endif
diff --git a/busybox-1.19.3/shell/hush_doc.txt b/busybox-1.19.3/shell/hush_doc.txt
new file mode 100644
index 0000000..c68dc24
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_doc.txt
@@ -0,0 +1,143 @@
+2008-07-14
+
+	Command parsing
+
+Command parsing results in a list of "pipe" structures.
+This list correspond not only to usual "pipe1 || pipe2 && pipe3"
+lists, but it also controls execution of if, while, etc statements.
+Every such statement is a list for hush. List consists of pipes.
+
+struct pipe fields:
+  smallint res_word - "none" for normal commands,
+                      "if" for if condition etc
+  struct child_prog progs[] - array of commands in pipe
+  smallint followup - how this pipe is related to next: is it
+                      "pipe; pipe", "pipe & pipe" "pipe && pipe",
+                      "pipe || pipe"?
+
+Blocks of commands { pipe; pipe; } and (pipe; pipe) are represented
+as one pipe struct with one progs[0] element which is a "group" -
+struct child_prog can contain a list of pipes. Sometimes these
+"groups" are created implicitly, e.g. every control
+statement (if, while, etc) sits inside its own group.
+
+res_word controls statement execution. Examples:
+
+"echo Hello" -
+pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
+pipe 1 res_word=NONE followup=SEQ
+
+"echo foo || echo bar" -
+pipe 0 res_word=NONE followup=OR  prog[0] 'echo' 'foo'
+pipe 1 res_word=NONE followup=SEQ prog[0] 'echo' 'bar'
+pipe 2 res_word=NONE followup=SEQ
+
+"if true; then echo Hello; true; fi" -
+res_word=NONE followup=SEQ
+ prog 0 group {}:
+  pipe 0 res_word=IF followup=SEQ prog[0] 'true'
+  pipe 1 res_word=THEN followup=SEQ prog[0] 'echo' 'Hello'
+  pipe 2 res_word=THEN followup=SEQ prog[0] 'true'
+  pipe 3 res_word=FI followup=SEQ
+  pipe 4 res_word=NONE followup=(null)
+pipe 1 res_word=NONE followup=SEQ
+
+Above you see that if is a list, and it sits in a {} group
+implicitly created by hush. Also note two THEN res_word's -
+it is explained below.
+
+"if true; then { echo Hello; true; }; fi" -
+pipe 0 res_word=NONE followup=SEQ
+ prog 0 group {}:
+  pipe 0 res_word=IF followup=SEQ prog[0] 'true'
+  pipe 1 res_word=THEN followup=SEQ
+   prog 0 group {}:
+    pipe 0 res_word=NONE followup=SEQ prog[0] 'echo' 'Hello'
+    pipe 1 res_word=NONE followup=SEQ prog[0] 'true'
+    pipe 2 res_word=NONE followup=SEQ
+  pipe 2 res_word=NONE followup=(null)
+pipe 1 res_word=NONE followup=SEQ
+
+"for v in a b; do echo $v; true; done" -
+pipe 0 res_word=NONE followup=SEQ
+ prog 0 group {}:
+  pipe 0 res_word=FOR followup=SEQ prog[0] 'v'
+  pipe 1 res_word=IN followup=SEQ prog[0] 'a' 'b'
+  pipe 2 res_word=DO followup=SEQ prog[0] 'echo' '$v'
+  pipe 3 res_word=DO followup=SEQ prog[0] 'true'
+  pipe 4 res_word=DONE followup=SEQ
+  pipe 5 res_word=NONE followup=(null)
+pipe 1 res_word=NONE followup=SEQ
+
+Note how "THEN" and "DO" does not just mark the first pipe,
+it "sticks" to all pipes in the body. This is used when
+hush executes parsed pipes.
+
+Dummy trailing pipes with no commands are artifacts of imperfect
+parsing algorithm - done_pipe() appends new pipe struct beforehand
+and last one ends up empty and unused.
+
+"for" and "case" statements (ab)use progs[] to keep their data
+instead of argv vector progs[] usually do. "for" keyword is forcing
+pipe termination after first word, which makes hush see
+"for v in..." as "for v; in...". "case" keyword does the same.
+Other judiciuosly placed hacks make hush see
+"case word in a) cmd1;; b) cmd2;; esac" as if it was
+"case word; match a; cmd; match b; cmd2; esac"
+("match" is a fictitious keyword here):
+
+"case word in a) cmd1;; b) cmd2; esac" -
+pipe 0 res_word=NONE followup=1 SEQ
+ prog 0 group {}:
+  pipe 0 res_word=CASE followup=SEQ prog[0] 'word'
+  pipe 1 res_word=MATCH followup=SEQ prog[0] 'a'
+  pipe 2 res_word=CASEI followup=SEQ prog[0] 'cmd1'
+  pipe 3 res_word=MATCH followup=SEQ prog[0] 'b'
+  pipe 4 res_word=CASEI followup=SEQ prog[0] 'cmd2'
+  pipe 5 res_word=CASEI followup=SEQ prog[0] 'cmd3'
+  pipe 6 res_word=ESAC followup=SEQ
+  pipe 7 res_word=NONE followup=(null)
+pipe 1 res_word=NONE followup=SEQ
+
+
+2008-01
+
+	Command execution
+
+/* callsite: process_command_subs */
+generate_stream_from_list(struct pipe *head) - handles `cmds`
+  create UNIX pipe
+  [v]fork
+  child:
+  redirect pipe output to stdout
+  _exit(run_list(head));   /* leaks memory */
+  parent:
+  return UNIX pipe's output fd
+  /* head is freed by the caller */
+
+/* callsite: parse_and_run_stream */
+run_and_free_list(struct pipe *)
+  run_list(struct pipe *)
+  free_pipe_list(struct pipe *)
+
+/* callsites: generate_stream_from_list, run_and_free_list, pseudo_exec, run_pipe */
+run_list(struct pipe *) - handles "cmd; cmd2 && cmd3", while/for/do loops
+  run_pipe - for every pipe in list
+
+/* callsite: run_list */
+run_pipe - runs "cmd1 | cmd2 | cmd3 [&]"
+  run_list - used if only one cmd and it is of the form "{cmds;}"
+  forks for every cmd if more than one cmd or if & is there
+  pseudo_exec - runs each "cmdN" (handles builtins etc)
+
+/* callsite: run_pipe */
+pseudo_exec - runs "cmd" (handles builtins etc)
+  exec - execs external programs
+  run_list - used if cmdN is "(cmds)" or "{cmds;}"
+  /* problem: putenv's malloced strings into environ -
+  ** with vfork they will leak into parent process
+  */
+  /* problem with ENABLE_FEATURE_SH_STANDALONE:
+  ** run_applet_no_and_exit(a, argv) uses exit - this can interfere
+  ** with vfork - switch to _exit there?
+  */
diff --git a/busybox-1.19.3/shell/hush_leaktool.sh b/busybox-1.19.3/shell/hush_leaktool.sh
new file mode 100755
index 0000000..ca35ec1
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_leaktool.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# hush's stderr with leak debug enabled
+output=output
+
+freelist=`grep 'free 0x' "$output" | cut -d' ' -f2 | sort | uniq | xargs`
+
+grep -v free "$output" >"$output.leaked"
+
+i=8
+list=
+for freed in $freelist; do
+    list="$list -e $freed"
+    test $((--i)) != 0 && continue
+    echo Dropping $list
+    grep -F -v $list <"$output.leaked" >"$output.temp"
+    mv "$output.temp" "$output.leaked"
+    i=8
+    list=
+done
+if test "$list"; then
+    echo Dropping $list
+    grep -F -v $list <"$output.leaked" >"$output.temp"
+    mv "$output.temp" "$output.leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-arith/arith.right b/busybox-1.19.3/shell/hush_test/hush-arith/arith.right
new file mode 100644
index 0000000..8a201fb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-arith/arith.right
@@ -0,0 +1,147 @@
+Format: 'expected actual'
+163 163
+4 4
+16 16
+8 8
+2 2
+4 4
+2 2
+2 2
+1 1
+0 0
+0 0
+0 0
+1 1
+1 1
+2 2
+-3 -3
+-2 -2
+1 1
+0 0
+2 2
+131072 131072
+29 29
+33 33
+49 49
+1 1
+1 1
+0 0
+0 0
+1 1
+1 1
+1 1
+2 2
+3 3
+1 1
+58 58
+2 2
+60 60
+1 1
+256 256
+16 16
+62 62
+4 4
+29 29
+5 5
+unary plus, minus
+-4 -4
+4 4
+conditional expressions
+1 1
+32 32
+32 32
+1 1
+1 1
+32 32
+check that parentheses in `cmd` are interpreted correctly
+3 3
+check that the unevaluated part of the ternary operator does not do evaluation or assignment
+20 20
+30 30
+20 20
+30 30
+check precedence of assignment vs. conditional operator
+hush: arithmetic syntax error
+check precedence of assignment vs. conditional operator
+associativity of assignment-operator operator
+6 6
+6,5,3 6,5,3
+octal, hex
+263 263
+255 255
+40 40
+hush: arithmetic syntax error
+hush: divide by zero
+hush: can't execute 'let': No such file or directory
+hush: arithmetic syntax error
+hush: can't execute 'let': No such file or directory
+abc
+def
+ghi
+hush: arithmetic syntax error
+16 16
+hush: arithmetic syntax error
+hush: malformed ?: operator
+hush: arithmetic syntax error
+9 9
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+9 9
+9 9
+9 9
+7 7
+7
+4 4
+32767 32767
+32768 32768
+131072 131072
+2147483647 2147483647
+1 1
+4 4
+4 4
+5 5
+5 5
+4 4
+3 3
+3 3
+4 4
+4 4
+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: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+hush: arithmetic syntax error
+6 6
+3 3
+7 7
+4 4
+0 0
+3 3
+7 7
+2 2
+-2 -2
+1 1
+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: arithmetic syntax error
+hush: arithmetic syntax error
+8 12
+hush: arithmetic syntax error
+42
+42
+42
+hush: can't execute 'a[b[c]d]=e': No such file or directory
diff --git a/busybox-1.19.3/shell/hush_test/hush-arith/arith.tests b/busybox-1.19.3/shell/hush_test/hush-arith/arith.tests
new file mode 100755
index 0000000..bc6b341
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-arith/arith.tests
@@ -0,0 +1,305 @@
+#ash# set +o posix
+#ash# declare -i iv jv
+
+echo "Format: 'expected actual'"
+
+iv=$(( 3 + 5 * 32 ))
+echo 163 $iv
+#ash# iv=iv+3
+#ash# echo 166 $iv
+iv=2
+jv=iv
+
+: $((jv *= 2)) ##hush## let "jv *= 2"
+echo 4 $jv
+jv=$(( $jv << 2 ))
+echo 16 $jv
+
+: $((jv=$jv / 2)) ##hush## let jv="$jv / 2"
+echo 8 $jv
+#ash# jv="jv >> 2"
+: $((jv=jv >> 2)) ##hush## let jv="jv >> 2"
+echo 2 $jv
+
+iv=$((iv+ $jv))
+echo 4 $iv
+echo 2 $((iv -= jv))
+echo 2 $iv
+echo 1 $(( iv == jv ))
+echo 0 $(( iv != $jv ))
+echo 0 $(( iv < jv ))
+echo 0 $(( $iv > $jv ))
+echo 1 $(( iv <= $jv ))
+echo 1 $(( $iv >= jv ))
+
+echo 2 $jv
+echo -3 $(( ~$jv ))
+echo -2 $(( ~1 ))
+echo 1 $(( ! 0 ))
+
+echo 0 $(( jv % 2 ))
+echo 2 $(( $iv % 4 ))
+
+echo 131072 $(( iv <<= 16 ))
+echo 29 $(( iv %= 33 ))
+
+echo 33 $(( 33 & 55 ))
+echo 49 $(( 33 | 17 ))
+
+echo 1 $(( iv && $jv ))
+echo 1 $(( $iv || jv ))
+
+echo 0 $(( iv && 0 ))
+echo 0 $(( iv & 0 ))
+echo 1 $(( iv && 1 ))
+echo 1 $(( iv & 1 ))
+
+echo 1 $(( $jv || 0 ))
+echo 2 $(( jv | 0 ))
+echo 3 $(( jv | 1 ))
+echo 1 $(( $jv || 1 ))
+
+: $((iv *= jv)) ##hush## let 'iv *= jv'
+echo 58 $iv
+echo 2 $jv
+: $((jv += $iv)) ##hush## let "jv += $iv"
+echo 60 $jv
+
+echo 1 $(( jv /= iv ))
+echo 256 $(( jv <<= 8 ))
+echo 16 $(( jv >>= 4 ))
+
+echo 62 $(( iv |= 4 ))
+echo 4 $(( iv &= 4 ))
+
+echo 29 $(( iv += (jv + 9)))
+echo 5 $(( (iv + 4) % 7 ))
+
+echo unary plus, minus
+echo -4 $(( +4 - 8 ))
+echo 4 $(( -4 + 8 ))
+
+echo conditional expressions
+echo 1 $(( 4<5 ? 1 : 32))
+echo 32 $(( 4>5 ? 1 : 32))
+echo 32 $(( 4>(2+3) ? 1 : 32))
+echo 1 $(( 4<(2+3) ? 1 : 32))
+echo 1 $(( (2+2)<(2+3) ? 1 : 32))
+echo 32 $(( (2+2)>(2+3) ? 1 : 32))
+
+echo 'check that parentheses in `cmd` are interpreted correctly'
+# \x28 is '('
+echo 3 $(( ( `printf '(\x28 1'` + `echo 2\)\)` ) ))
+
+echo check that the unevaluated part of the ternary operator does not do evaluation or assignment
+x=i+=2
+y=j+=2
+#ash# declare -i i=1 j=1
+      i=1
+      j=1
+echo 20 $((1 ? 20 : (x+=2)))
+#ash# echo $i,$x             # ash mishandles this
+echo 30 $((0 ? (y+=2) : 30))
+#ash# echo $j,$y             # ash mishandles this
+
+x=i+=2
+y=j+=2
+#ash# declare -i i=1 j=1
+      i=1
+      j=1
+echo 20 $((1 ? 20 : (x+=2)))
+#ash# echo $i,$x             # ash mishandles this
+echo 30 $((0 ? (y+=2) : 30))
+#ash# echo $i,$y             # ash mishandles this
+
+echo check precedence of assignment vs. conditional operator
+# should be an error
+#ash# declare -i x=2
+      x=2
+#ashnote# bash reports error but continues, ash aborts - using subshell to 'emulate' bash:
+(  y=$((1 ? 20 : x+=2))  )
+
+echo check precedence of assignment vs. conditional operator
+#ash# declare -i x=2
+      x=2
+# ash says "line NNN: syntax error: 0 ? x+=2 : 20"
+#ash# echo 20 $((0 ? x+=2 : 20))
+
+echo associativity of assignment-operator operator
+#ash# declare -i i=1 j=2 k=3
+i=1
+j=2
+k=3
+echo 6 $((i += j += k))
+echo 6,5,3 $i,$j,$k
+
+echo octal, hex
+echo 263 $(( 0x100 | 007 ))
+echo 255 $(( 0xff ))
+#ash# echo 255 $(( 16#ff ))
+#ash# echo 127 $(( 16#FF/2 ))
+#ash# echo 36 $(( 8#44 ))
+
+echo 40 $(( 8 ^ 32 ))
+
+#ash# # other bases
+#ash# echo 10 $(( 16#a ))
+#ash# echo 10 $(( 32#a ))
+#ash# echo 10 $(( 56#a ))
+#ash# echo 10 $(( 64#a ))
+#ash#
+#ash# echo 10 $(( 16#A ))
+#ash# echo 10 $(( 32#A ))
+#ash# echo 36 $(( 56#A ))
+#ash# echo 36 $(( 64#A ))
+#ash#
+#ash# echo 62 $(( 64#@ ))
+#ash# echo 63 $(( 64#_ ))
+
+#ash# # weird bases (error)
+#ash# echo $(( 3425#56 ))
+
+#ash# # missing number after base
+#ash# echo 0 $(( 2# ))
+
+# these should generate errors
+(  echo $(( 7 = 43 ))      )
+#ash# echo $(( 2#44 ))
+(  echo $(( 44 / 0 ))      )
+(  let 'jv += $iv'         )
+(  echo $(( jv += \$iv ))  )
+(  let 'rv = 7 + (43 * 6'  )
+
+#ash# # more errors
+#ash# declare -i i
+#ash# i=0#4
+#ash# i=2#110#11
+
+((echo abc; echo def;); echo ghi)
+
+#ash# if (((4+4) + (4 + 7))); then
+#ash# 	echo ok
+#ash# fi
+
+#ash# (())	# make sure the null expression works OK
+
+#ash# a=(0 2 4 6)
+#ash# echo 6 $(( a[1] + a[2] ))
+#ash# echo 1 $(( (a[1] + a[2]) == a[3] ))
+#ash# (( (a[1] + a[2]) == a[3] )) ; echo 0 $?
+
+# test pushing and popping the expression stack
+unset A
+A="4 + "
+(  echo A $(( ( 4 + A ) + 4 ))  )
+A="3 + 5"
+echo 16 $(( ( 4 + A ) + 4 ))
+
+# badly-formed conditional expressions
+(  echo $(( 4 ? : $A ))  )
+(  echo $(( 1 ? 20 ))    )
+(  echo $(( 4 ? 20 : ))  )
+
+# precedence and short-circuit evaluation
+B=9
+echo 9 $B
+
+# error
+(  echo $(( 0 && B=42 )); echo 9 $B  )
+
+# error
+(  echo $(( 1 || B=88 )); echo 9 $B  )
+
+# ash mistakenly evaluates B=... below
+#ash# echo 0 $(( 0 && (B=42) ))
+echo 9 $B
+#ash# echo 0 $(( (${$} - $$) && (B=42) ))
+echo 9 $B
+#ash# echo 1 $(( 1 || (B=88) ))
+echo 9 $B
+
+
+# until command with (( )) command
+x=7
+
+echo 7 $x
+#ash# until (( x == 4 ))
+      until test "$x" = 4
+do
+	echo $x
+	x=4
+done
+
+echo 4 $x
+
+# exponentiation
+echo 32767 $(( 2**15 - 1))
+echo 32768 $(( 2**(16-1)))
+echo 131072 $(( 2**16*2 ))
+echo 2147483647 $(( 2**31-1))
+echo 1 $(( 2**0 ))
+
+# {pre,post}-{inc,dec}rement and associated errors
+
+x=4
+
+echo 4 $x
+echo 4 $(( x++ ))
+echo 5 $x
+echo 5 $(( x-- ))
+echo 4 $x
+
+echo 3 $(( --x ))
+echo 3 $x
+
+echo 4 $(( ++x ))
+echo 4 $x
+
+# bash 3.2 apparently thinks that ++7 is 7
+#ash# echo 7 $(( ++7 ))
+(  echo $(( 7-- ))    )
+
+(  echo $(( --x=7 ))  )
+(  echo $(( ++x=7 ))  )
+
+(  echo $(( x++=7 ))  )
+(  echo $(( x--=7 ))  )
+
+echo 4 $x
+
+echo 7 $(( +7 ))
+echo -7 $(( -7 ))
+
+# bash 3.2 apparently thinks that ++7 is 7
+#ash# echo $(( ++7 ))
+#ash# echo $(( --7 ))
+
+${THIS_SH} ./arith1.sub
+${THIS_SH} ./arith2.sub
+
+x=4
+y=7
+
+#ash# (( x=8 , y=12 ))
+      x=8
+      y=12
+echo $x $y
+
+#ash# # should be an error
+#ash# (( x=9 y=41 ))
+
+# These are errors
+unset b
+(  echo $((a b))  )
+#ash# ((a b))
+
+n=42
+printf "%d\n" $n
+printf "%i\n" $n
+#ash# echo $(( 8#$(printf "%o\n" $n) ))
+printf "%u\n" $n
+#ash# echo $(( 16#$(printf "%x\n" $n) ))
+#ash# echo $(( 16#$(printf "%X\n" $n) ))
+
+# causes longjmp botches through bash-2.05b
+a[b[c]d]=e
diff --git a/busybox-1.19.3/shell/hush_test/hush-arith/arith1.sub b/busybox-1.19.3/shell/hush_test/hush-arith/arith1.sub
new file mode 100755
index 0000000..80aa999
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-arith/arith1.sub
@@ -0,0 +1,40 @@
+# test of redone post-increment and post-decrement code
+(  echo $(( 4-- ))   )
+(  echo $(( 4++ ))   )
+(  echo $(( 4 -- ))  )
+(  echo $(( 4 ++ ))  )
+
+#ash# (( array[0]++ ))
+#ash# echo ${array}
+
+#ash# (( array[0] ++ ))
+#ash# echo ${array}
+
+#ash# (( a++ ))
+#ash# echo $a
+#ash# (( a ++ ))
+#ash# echo $a
+      a=2
+
+echo 6 $(( a ++ + 4 ))
+echo 3 $a
+
+echo 7 $(( a+++4 ))
+echo 4 $a
+
+echo 0 $(( a---4 ))
+echo 3 $a
+
+echo 7 $(( a -- + 4 ))
+echo 2 $a
+
+echo -2 $(( a -- - 4 ))
+echo 1 $a
+
+#ash# (( ++ + 7 ))
+
+#ash# (( ++ ))
+(  echo $(( +++7 ))  )
+# bash 3.2 apparently thinks that ++ +7 is 7
+#ash# echo $(( ++ + 7 ))
+#ash# (( -- ))
diff --git a/busybox-1.19.3/shell/hush_test/hush-arith/arith2.sub b/busybox-1.19.3/shell/hush_test/hush-arith/arith2.sub
new file mode 100755
index 0000000..f7e3c92
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-arith/arith2.sub
@@ -0,0 +1,57 @@
+# bash 3.2 apparently thinks that ++7 is 7 etc
+(  echo $(( --7 ))   )
+(  echo $(( ++7 ))   )
+(  echo $(( -- 7 ))  )
+(  echo $(( ++ 7 ))  )
+
+#ash# ((++array[0] ))
+#ash# echo 1 $array
+#ash# (( ++ array[0] ))
+#ash# echo 2 $array
+
+#ash# (( ++a ))
+#ash# echo 1 $a
+#ash# (( ++ a ))
+#ash# echo 2 $a
+
+#ash# (( --a ))
+#ash# echo 1 $a
+#ash# (( -- a ))
+#ash# echo 0 $a
+      a=0
+
+echo 5 $(( 4 + ++a ))
+echo 1 $a
+
+# ash doesn't handle it right...
+#ash# echo 6 $(( 4+++a ))
+#ash# echo 2 $a
+      a=2
+
+# ash doesn't handle it right...
+#ash# echo 3 $(( 4---a ))
+#ash# echo 1 $a
+      a=1
+
+echo 4 $(( 4 - -- a ))
+echo 0 $a
+
+#ash# (( -- ))
+# bash 3.2 apparently thinks that ---7 is -7
+#ash# echo $(( ---7 ))
+(  echo $(( -- - 7 ))  )
+
+#ash# (( ++ ))
+# bash 3.2: 7
+#ash# echo 7 $(( ++7 ))
+(  echo $(( ++ + 7 ))  )
+
+# bash 3.2: -7
+#ash# echo -7 $(( ++-7 ))
+# bash 3.2: -7
+#ash# echo -7 $(( ++ - 7 ))
+
+# bash 3.2: 7
+#ash# echo 7 $(( +--7 ))
+# bash 3.2: 7
+#ash# echo 7 $(( -- + 7 ))
diff --git a/busybox-1.19.3/shell/hush_test/hush-bugs/and_or_and_backgrounding.right b/busybox-1.19.3/shell/hush_test/hush-bugs/and_or_and_backgrounding.right
new file mode 100644
index 0000000..90ce63e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-bugs/and_or_and_backgrounding.right
@@ -0,0 +1,4 @@
+First
+Second
+Third
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests b/busybox-1.19.3/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests
new file mode 100755
index 0000000..05acfb8
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-bugs/and_or_and_backgrounding.tests
@@ -0,0 +1,31 @@
+# UNFIXED BUG: hush thinks that ; && || & have the same precedence.
+# According to this doc, && || have higher precedence than ; &.
+# See example below.
+# Precedence of ; is not a problem in practice. Precedence of & is.
+#
+#http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
+#
+#2.9.3 Lists
+#
+#An AND-OR list is a sequence of one or more pipelines separated by
+#the operators "&&" and "||" .
+#
+#A list is a sequence of one or more AND-OR lists separated by the operators
+#';' and '&' and optionally terminated by ';', '&', or <newline>.
+#
+#The operators "&&" and "||" shall have equal precedence and shall be
+#evaluated with left associativity. For example, both of the following
+#commands write solely bar to standard output:
+#
+#    false && echo foo || echo bar
+#    true || echo foo && echo bar
+#
+#A ';' or <newline> terminator shall cause the preceding AND-OR list
+#to be executed sequentially; an '&' shall cause asynchronous execution
+#of the preceding AND-OR list.
+
+echo First && sleep 0.2 && echo Third &
+sleep 0.1
+echo Second
+wait
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-bugs/export_exp.right b/busybox-1.19.3/shell/hush_test/hush-bugs/export_exp.right
new file mode 100644
index 0000000..17a2e93
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-bugs/export_exp.right
@@ -0,0 +1,7 @@
+aa0 bb0
+a=aa0 b=bb0
+aa1 bb1
+a=aa1 b=bb1
+zzz=zzz
+zz=*
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-bugs/export_exp.tests.disabled b/busybox-1.19.3/shell/hush_test/hush-bugs/export_exp.tests.disabled
new file mode 100644
index 0000000..0913fd3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-bugs/export_exp.tests.disabled
@@ -0,0 +1,22 @@
+# 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
+echo $a $b
+echo $c
+
+# only 1st arg should be expanded in multiple words
+export `echo a=aa1 b=bb1` c=`echo a=aa1 b=bb1`
+echo $a $b
+echo $c
+
+>zz=zz
+>zzz=zzz
+# only 1st arg should be globbed
+export zzz* zz=*
+env | grep ^zz | sort
+rm -rf zz=zz zzz=zzz
+
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-glob/bash_brace1.right b/busybox-1.19.3/shell/hush_test/hush-glob/bash_brace1.right
new file mode 100644
index 0000000..63365c9
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/bash_brace1.right
@@ -0,0 +1,4 @@
+bash_brace1.tests
+*{b,b}race1.t*
+bash_brace1.tests bash_brace1.tests
+Done: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-glob/bash_brace1.tests b/busybox-1.19.3/shell/hush_test/hush-glob/bash_brace1.tests
new file mode 100755
index 0000000..eb2f0e9
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/bash_brace1.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-glob/glob1.right b/busybox-1.19.3/shell/hush_test/hush-glob/glob1.right
new file mode 100644
index 0000000..f29ab4e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob1.right
@@ -0,0 +1,2 @@
+glob1.tests
+glob1.tests
diff --git a/busybox-1.19.3/shell/hush_test/hush-glob/glob1.tests b/busybox-1.19.3/shell/hush_test/hush-glob/glob1.tests
new file mode 100755
index 0000000..f980ce0
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob1.tests
@@ -0,0 +1,2 @@
+echo *glob1?t[e]sts*
+echo "glob1"?'t'[e]s*
diff --git a/busybox-1.19.3/shell/hush_test/hush-glob/glob2.right b/busybox-1.19.3/shell/hush_test/hush-glob/glob2.right
new file mode 100644
index 0000000..7a70c22
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob2.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-glob/glob2.tests b/busybox-1.19.3/shell/hush_test/hush-glob/glob2.tests
new file mode 100755
index 0000000..4dbc925
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob2.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-glob/glob_and_assign.right b/busybox-1.19.3/shell/hush_test/hush-glob/glob_and_assign.right
new file mode 100644
index 0000000..d46e443
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob_and_assign.right
@@ -0,0 +1,6 @@
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
+*.tmp
+ZVAR=z.tmp z.tmp
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
+ZVAR=z.tmp ZVAR=*.tmp ZVAR=[z].tmp
diff --git a/busybox-1.19.3/shell/hush_test/hush-glob/glob_and_assign.tests b/busybox-1.19.3/shell/hush_test/hush-glob/glob_and_assign.tests
new file mode 100755
index 0000000..0b158f2
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob_and_assign.tests
@@ -0,0 +1,10 @@
+>ZVAR=z.tmp
+>z.tmp
+ZVAR=*.tmp echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+ZVAR=*.tmp /bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+ZVAR=*.tmp
+echo "$ZVAR"
+echo $ZVAR
+echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+/bin/echo ZVAR=*.tmp "ZVAR=*.tmp" "ZVAR=[z].tmp"
+rm ZVAR=z.tmp z.tmp
diff --git a/busybox-1.19.3/shell/hush_test/hush-glob/glob_redir.right b/busybox-1.19.3/shell/hush_test/hush-glob/glob_redir.right
new file mode 100644
index 0000000..fbd0309
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob_redir.right
@@ -0,0 +1,2 @@
+z.tmp:
+?.tmp: TEST
diff --git a/busybox-1.19.3/shell/hush_test/hush-glob/glob_redir.tests b/busybox-1.19.3/shell/hush_test/hush-glob/glob_redir.tests
new file mode 100755
index 0000000..621d120
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-glob/glob_redir.tests
@@ -0,0 +1,9 @@
+# Redirections are not globbed.
+# bash:
+# if run as "sh", they are not globbed, but
+# if run as "bash", they are!
+>z.tmp
+echo TEST >?.tmp
+echo 'z.tmp:' `cat 'z.tmp'`
+echo '?.tmp:' `cat '?.tmp'`
+rm 'z.tmp' '?.tmp'
diff --git a/busybox-1.19.3/shell/hush_test/hush-leak/leak_argv1.right b/busybox-1.19.3/shell/hush_test/hush-leak/leak_argv1.right
new file mode 100644
index 0000000..1d4d6ff
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-leak/leak_argv1.right
@@ -0,0 +1,2 @@
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-leak/leak_argv1.tests b/busybox-1.19.3/shell/hush_test/hush-leak/leak_argv1.tests
new file mode 100755
index 0000000..bb371bc
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-leak/leak_argv1.tests
@@ -0,0 +1,80 @@
+# Warm up
+i=1
+while test $i != X; do
+    set -- a b c d e f g h i j k l m n o p q r s t u v w x y z
+    shift
+    shift 2
+    shift 5
+    shift 11
+    set -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+    shift 3
+    shift 7
+    i=1$i
+    if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi
+    if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi
+    if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi
+    if test $i = 1111111111111111111111111111111111111111111114; then i=5; fi
+    if test $i = 1111111111111111111111111111111111111111111115; then i=6; fi
+    if test $i = 1111111111111111111111111111111111111111111116; then i=7; fi
+    if test $i = 1111111111111111111111111111111111111111111117; then i=8; fi
+    if test $i = 1111111111111111111111111111111111111111111118; then i=9; fi
+    if test $i = 1111111111111111111111111111111111111111111119; then i=a; fi
+    if test $i = 111111111111111111111111111111111111111111111a; then i=b; fi
+    if test $i = 111111111111111111111111111111111111111111111b; then i=c; fi
+    if test $i = 111111111111111111111111111111111111111111111c; then i=d; fi
+    if test $i = 111111111111111111111111111111111111111111111d; then i=e; fi
+    if test $i = 111111111111111111111111111111111111111111111e; then i=f; fi
+    if test $i = 111111111111111111111111111111111111111111111f; then i=g; fi
+    if test $i = 111111111111111111111111111111111111111111111g; then i=h; fi
+    if test $i = 111111111111111111111111111111111111111111111h; then i=i; fi
+    if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi
+    if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi
+done
+
+unset i
+set --
+memleak
+
+echo "Measuring memory leak..."
+i=1
+while test $i != X; do
+    set -- a b c d e f g h i j k l m n o p q r s t u v w x y z
+    shift
+    shift 2
+    shift 5
+    shift 11
+    set -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
+    shift 3
+    shift 7
+    i=1$i
+    if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi
+    if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi
+    if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi
+    if test $i = 1111111111111111111111111111111111111111111114; then i=5; fi
+    if test $i = 1111111111111111111111111111111111111111111115; then i=6; fi
+    if test $i = 1111111111111111111111111111111111111111111116; then i=7; fi
+    if test $i = 1111111111111111111111111111111111111111111117; then i=8; fi
+    if test $i = 1111111111111111111111111111111111111111111118; then i=9; fi
+    if test $i = 1111111111111111111111111111111111111111111119; then i=a; fi
+    if test $i = 111111111111111111111111111111111111111111111a; then i=b; fi
+    if test $i = 111111111111111111111111111111111111111111111b; then i=c; fi
+    if test $i = 111111111111111111111111111111111111111111111c; then i=d; fi
+    if test $i = 111111111111111111111111111111111111111111111d; then i=e; fi
+    if test $i = 111111111111111111111111111111111111111111111e; then i=f; fi
+    if test $i = 111111111111111111111111111111111111111111111f; then i=g; fi
+    if test $i = 111111111111111111111111111111111111111111111g; then i=h; fi
+    if test $i = 111111111111111111111111111111111111111111111h; then i=i; fi
+    if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi
+    if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi
+done
+
+unset i
+set --
+memleak
+
+kb=$?
+if test $kb -le 4; then
+    echo Ok #$kb
+else
+    echo "Bad: $kb kb leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/and-or.right b/busybox-1.19.3/shell/hush_test/hush-misc/and-or.right
new file mode 100644
index 0000000..f9fa5fb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/and-or.right
@@ -0,0 +1,18 @@
+a1
+a4
+b1
+b3
+b4
+b6
+c4
+c5
+c7
+c8
+ff1
+ff3
+ft2
+ft3
+tf2
+tf3
+tt2
+tt4
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/and-or.tests b/busybox-1.19.3/shell/hush_test/hush-misc/and-or.tests
new file mode 100755
index 0000000..485458a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/and-or.tests
@@ -0,0 +1,34 @@
+false || echo a1
+false && echo a2
+true || echo a3
+true && echo a4
+
+false || echo b1 || echo b2
+false || echo b3 && echo b4
+false && echo b5 || echo b6
+false && echo b7 && echo b8
+
+true || echo c1 || echo c2
+true || echo c3 && echo c4
+true && echo c5 || echo c6
+true && echo c7 && echo c8
+
+false || false || echo ff1
+false || false && echo ff2
+false && false || echo ff3
+false && false && echo ff4
+
+false || true || echo ft1
+false || true && echo ft2
+false && true || echo ft3
+false && true && echo ft4
+
+true || false || echo tf1
+true || false && echo tf2
+true && false || echo tf3
+true && false && echo tf4
+
+true || true || echo tt1
+true || true && echo tt2
+true && true || echo tt3
+true && true && echo tt4
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/assignment1.right b/busybox-1.19.3/shell/hush_test/hush-misc/assignment1.right
new file mode 100644
index 0000000..d0a13d3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment1.right
@@ -0,0 +1,9 @@
+if1:0
+while1:0
+until1:0
+if2:0
+while2:0
+until2:0
+if3:0
+while3:0
+until3:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/assignment1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/assignment1.tests
new file mode 100755
index 0000000..033b352
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment1.tests
@@ -0,0 +1,42 @@
+# Assignments after some keywords should still work
+
+if a=1 true; then a=1 true; elif a=1 true; then a=1 true; else a=1 true; fi
+echo if1:$?
+while a=1 true; do a=1 true; break; done
+echo while1:$?
+until a=1 false; do a=1 true; break; done
+echo until1:$?
+
+if a=1 true
+ then a=1 true
+ elif a=1 true
+ then a=1 true
+ else a=1 true
+ fi
+echo if2:$?
+while a=1 true
+ do a=1 true
+ break
+ done
+echo while2:$?
+until a=1 false
+ do a=1 true
+ break
+ done
+echo until2:$?
+
+if
+ a=1 true; then
+ a=1 true; elif
+ a=1 true; then
+ a=1 true; else
+ a=1 true; fi
+echo if3:$?
+while
+ a=1 true; do
+ a=1 true; break; done
+echo while3:$?
+until
+ a=1 false; do
+ a=1 true; break; done
+echo until3:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/assignment2.rigth b/busybox-1.19.3/shell/hush_test/hush-misc/assignment2.rigth
new file mode 100644
index 0000000..591552c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment2.rigth
@@ -0,0 +1,2 @@
+hush: can't exec 'a=b': No such file or directory
+1
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/assignment2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/assignment2.tests
new file mode 100755
index 0000000..540e01e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment2.tests
@@ -0,0 +1,4 @@
+# This must not be interpreted as an assignment
+a''=b true
+echo $?
+# (buglet: $? should be 127. it is currently 1)
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/assignment3.right b/busybox-1.19.3/shell/hush_test/hush-misc/assignment3.right
new file mode 100644
index 0000000..0f02d7c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment3.right
@@ -0,0 +1,2 @@
+Done:0
+abc=123
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/assignment3.tests b/busybox-1.19.3/shell/hush_test/hush-misc/assignment3.tests
new file mode 100755
index 0000000..790129b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment3.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-misc/assignment4.right b/busybox-1.19.3/shell/hush_test/hush-misc/assignment4.right
new file mode 100644
index 0000000..31c896f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment4.right
@@ -0,0 +1 @@
+Done:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/assignment4.tests b/busybox-1.19.3/shell/hush_test/hush-misc/assignment4.tests
new file mode 100755
index 0000000..6f46d0a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/assignment4.tests
@@ -0,0 +1,3 @@
+# There was a bug where we misinterpreted assignments after 'do':
+for i in 1; do eval b=; done
+echo Done:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break1.right b/busybox-1.19.3/shell/hush_test/hush-misc/break1.right
new file mode 100644
index 0000000..04a4b17
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break1.right
@@ -0,0 +1,2 @@
+A
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/break1.tests
new file mode 100755
index 0000000..3a6b060
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break1.tests
@@ -0,0 +1,2 @@
+while true; do echo A; break; echo B; done
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break2.right b/busybox-1.19.3/shell/hush_test/hush-misc/break2.right
new file mode 100644
index 0000000..8a15cb9
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break2.right
@@ -0,0 +1,3 @@
+A
+AA
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/break2.tests
new file mode 100755
index 0000000..7da9faf
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break2.tests
@@ -0,0 +1,6 @@
+while true; do
+    echo A
+    while true; do echo AA; break 2; echo BB; done
+    echo B
+done
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break3.right b/busybox-1.19.3/shell/hush_test/hush-misc/break3.right
new file mode 100644
index 0000000..04a4b17
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break3.right
@@ -0,0 +1,2 @@
+A
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break3.tests b/busybox-1.19.3/shell/hush_test/hush-misc/break3.tests
new file mode 100755
index 0000000..d138dca
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break3.tests
@@ -0,0 +1,2 @@
+v=break; while true; do echo A; $v; echo B; break; echo C; done
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break4.right b/busybox-1.19.3/shell/hush_test/hush-misc/break4.right
new file mode 100644
index 0000000..6f41c14
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break4.right
@@ -0,0 +1,6 @@
+A
+AA
+TRUE
+A
+AA
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break4.tests b/busybox-1.19.3/shell/hush_test/hush-misc/break4.tests
new file mode 100755
index 0000000..67da288
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break4.tests
@@ -0,0 +1,12 @@
+cond=true
+while $cond; do
+    echo A
+    if test "$cond" = true; then
+	cond='echo TRUE'
+    else
+	cond=false
+    fi
+    while true; do echo AA; continue 2; echo BB; done
+    echo B
+done
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break5.right b/busybox-1.19.3/shell/hush_test/hush-misc/break5.right
new file mode 100644
index 0000000..0b9df2a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break5.right
@@ -0,0 +1,13 @@
+A
+B
+0
+A:a
+B
+D
+A:b
+B
+D
+A:c
+B
+D
+0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/break5.tests b/busybox-1.19.3/shell/hush_test/hush-misc/break5.tests
new file mode 100755
index 0000000..273e040
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/break5.tests
@@ -0,0 +1,4 @@
+while true; do echo A; { echo B; break; echo C; }; echo D; done
+echo $?
+for v in a b c; do echo A:$v; (echo B; break; echo C); echo D; done
+echo $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/builtin1.right b/busybox-1.19.3/shell/hush_test/hush-misc/builtin1.right
new file mode 100644
index 0000000..2e55ecb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/builtin1.right
@@ -0,0 +1,2 @@
+VARIABLE=export
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/builtin1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/builtin1.tests
new file mode 100755
index 0000000..1a2941f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/builtin1.tests
@@ -0,0 +1,6 @@
+# builtins, unlike keywords like "while", can be constructed
+# with substitutions
+VARIABLE=export
+$VARIABLE VARIABLE
+env | grep ^VARIABLE
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/case1.right b/busybox-1.19.3/shell/hush_test/hush-misc/case1.right
new file mode 100644
index 0000000..4afb2f5
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/case1.right
@@ -0,0 +1,22 @@
+OK_1
+OK_1
+OK_21
+OK_22
+OK_23
+OK_31
+OK_32
+OK_41
+OK_42
+OK_43
+OK_44
+OK_51
+OK_52
+OK_53
+OK_sub1
+OK_sub2
+OK_sub3
+OK_sub4
+OK_sub5
+OK_sub6
+OK_esac1
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/case1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/case1.tests
new file mode 100755
index 0000000..d72b57f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/case1.tests
@@ -0,0 +1,40 @@
+case w in a) echo SKIP;; w) echo OK_1;; w) echo WRONG;; esac
+
+case w in
+ a) echo SKIP;;
+ w)echo OK_1 ;;
+ w)
+ echo WRONG
+ ;;
+esac
+
+t=w
+case $t in a) echo SKIP;; w) echo OK_21;; w) echo WRONG;; esac;
+case "$t" in a) echo SKIP;; w) echo OK_22;; w) echo WRONG;; esac;
+case w in a) echo SKIP;; $t) echo OK_23;; "$t") echo WRONG;; esac;
+
+case '' in a) echo SKIP;; w) echo WRONG;; *) echo OK_31;; esac;
+case '' in a) echo SKIP;; '') echo OK_32;; *) echo WRONG;; esac;
+
+case `echo w` in a) echo SKIP;; w) echo OK_41;; w) echo WRONG;; esac;
+case "`echo w`" in a) echo SKIP;; w) echo OK_42;; w) echo WRONG;; esac;
+case `echo w w` in a) echo SKIP;; w) echo WRONG;; 'w w') echo OK_43;; esac;
+case `echo w w` in a) echo SKIP;; w) echo WRONG;; w*) echo OK_44;; esac;
+
+case w in `echo w`) echo OK_51;; `echo WRONG >&2`w) echo WRONG;; esac;
+case w in `echo OK_52 >&2`) echo SKIP;; `echo`w) echo OK_53;; esac;
+
+# parsing cases in subshells can easily get messy
+ case m in  m) echo OK_sub1;; esac
+ case m in (m) echo OK_sub2;; esac
+(case m in  m) echo OK_sub3;; esac)
+(case m in (m) echo OK_sub4;; esac)
+(
+ case m in  m) echo OK_sub5;; esac
+)
+(
+ case m in (m) echo OK_sub6;; esac
+)
+(case esac in "esac") echo OK_esac1;; esac)
+
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/colon.right b/busybox-1.19.3/shell/hush_test/hush-misc/colon.right
new file mode 100644
index 0000000..2a87d02
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/colon.right
@@ -0,0 +1,2 @@
+0
+OK: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/colon.tests b/busybox-1.19.3/shell/hush_test/hush-misc/colon.tests
new file mode 100755
index 0000000..cb8ab53
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/colon.tests
@@ -0,0 +1,5 @@
+false
+:
+echo $?
+(while :; do exit; done)
+echo OK: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/compound.right b/busybox-1.19.3/shell/hush_test/hush-misc/compound.right
new file mode 100644
index 0000000..757d42f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/compound.right
@@ -0,0 +1,14 @@
+new group
+0
+1
+2
+3
+4
+5
+6
+new group
+new group
+0
+1
+2
+3
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/compound.tests b/busybox-1.19.3/shell/hush_test/hush-misc/compound.tests
new file mode 100755
index 0000000..a5e85c3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/compound.tests
@@ -0,0 +1,21 @@
+echo new group
+echo 0;  { :; }
+echo 1;  { : ;}
+echo 2; ({ :; })
+echo 3; ({ : ;})
+echo 4;  ( :  )
+echo 5;  ( :; )
+echo 6;  ( : ;)
+# not sure if POSIX requires these, but bash accepts them ...
+#echo 7; {( :  )}
+#echo 8; {( :; )}
+#echo 9; {( : ;)}
+
+echo new group
+#echo 0;  {(:);}
+
+echo new group
+echo 0;  (:)
+echo 1;  (:;)
+echo 2;  (:);
+echo 3;  (:;);
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/continue1.right b/busybox-1.19.3/shell/hush_test/hush-misc/continue1.right
new file mode 100644
index 0000000..c4a5565
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/continue1.right
@@ -0,0 +1,8 @@
+A:a
+A:b
+A:c
+OK1
+A:a
+A:b
+A:c
+OK2
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/continue1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/continue1.tests
new file mode 100755
index 0000000..72d3566
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/continue1.tests
@@ -0,0 +1,4 @@
+for v in a b c; do echo A:$v; continue 666; done
+echo OK1
+for v in a b c; do echo A:$v; continue 666; done
+echo OK2
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/continue2.right b/busybox-1.19.3/shell/hush_test/hush-misc/continue2.right
new file mode 100644
index 0000000..49d3ebd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/continue2.right
@@ -0,0 +1 @@
+Ok:1
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/continue2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/continue2.tests
new file mode 100755
index 0000000..c2df071
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/continue2.tests
@@ -0,0 +1,3 @@
+e=''
+(while test $e && exit 1; true; do e=1; continue; done)
+echo Ok:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/continue3.right b/busybox-1.19.3/shell/hush_test/hush-misc/continue3.right
new file mode 100644
index 0000000..aa47d0d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/continue3.right
@@ -0,0 +1,2 @@
+0
+0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/continue3.tests b/busybox-1.19.3/shell/hush_test/hush-misc/continue3.tests
new file mode 100755
index 0000000..0aff867
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/continue3.tests
@@ -0,0 +1,3 @@
+# Test that "continue" does affect exitcode (sets to 0)
+e=''
+while echo $?; test $e && exit; true; do e=1; false; continue; done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/echo_write_error.right b/busybox-1.19.3/shell/hush_test/hush-misc/echo_write_error.right
new file mode 100644
index 0000000..ddcad43
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/echo_write_error.right
@@ -0,0 +1,2 @@
+hush: write error: Broken pipe
+Ok: 1
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/echo_write_error.tests b/busybox-1.19.3/shell/hush_test/hush-misc/echo_write_error.tests
new file mode 100755
index 0000000..0a40c9f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/echo_write_error.tests
@@ -0,0 +1,7 @@
+trap "" PIPE
+
+{
+sleep 1
+echo Cant write this - get EPIPE
+echo Ok: $? >&2
+} | { true; }
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/empty_args.right b/busybox-1.19.3/shell/hush_test/hush-misc/empty_args.right
new file mode 100644
index 0000000..38ed8b8
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/empty_args.right
@@ -0,0 +1,6 @@
+Null 0th arg:
+hush: can't execute '': No such file or directory
+127
+Null 1st arg:
+0
+Null arg in exec:
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/empty_args.tests b/busybox-1.19.3/shell/hush_test/hush-misc/empty_args.tests
new file mode 100755
index 0000000..efce549
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/empty_args.tests
@@ -0,0 +1,9 @@
+echo Null 0th arg:
+""
+echo $?
+echo Null 1st arg:
+# printf without args would print usage info
+printf ""
+echo $?
+echo Null arg in exec:
+exec printf ""
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/empty_for.right b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for.right
new file mode 100644
index 0000000..290d39b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for.right
@@ -0,0 +1 @@
+OK: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/empty_for.tests b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for.tests
new file mode 100755
index 0000000..0cb52e8
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for.tests
@@ -0,0 +1,3 @@
+false
+for a in; do echo "HELLO"; done
+echo OK: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/empty_for2.right b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for2.right
new file mode 100644
index 0000000..1acee9e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for2.right
@@ -0,0 +1,4 @@
+PARAM:abc
+PARAM:d e
+PARAM:123
+OK: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/empty_for2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for2.tests
new file mode 100755
index 0000000..2b12ec2
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/empty_for2.tests
@@ -0,0 +1,6 @@
+if test $# = 0; then
+    exec "$THIS_SH" $0 abc "d e" 123
+fi
+false
+for v; do echo "PARAM:$v"; done
+echo OK: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/env_and_func.right b/busybox-1.19.3/shell/hush_test/hush-misc/env_and_func.right
new file mode 100644
index 0000000..4a15450
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/env_and_func.right
@@ -0,0 +1,2 @@
+var=val
+var=old
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/env_and_func.tests b/busybox-1.19.3/shell/hush_test/hush-misc/env_and_func.tests
new file mode 100755
index 0000000..1d4eaf3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/env_and_func.tests
@@ -0,0 +1,4 @@
+var=old
+f() { echo "var=$var"; }
+var=val f
+echo "var=$var"
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/exec.right b/busybox-1.19.3/shell/hush_test/hush-misc/exec.right
new file mode 100644
index 0000000..a0de608
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/exec.right
@@ -0,0 +1,6 @@
+pass fd out open
+pass fd out dup
+pass fd out close
+pass fd in open
+pass fd in dup
+pass fd in close
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/exec.tests b/busybox-1.19.3/shell/hush_test/hush-misc/exec.tests
new file mode 100755
index 0000000..6de50fa
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/exec.tests
@@ -0,0 +1,30 @@
+# make sure we have a way of checking these things
+cd /proc/$$/fd || cd /dev/fd || exit 1
+
+[ -e 44 ] && exit 1
+exec 44>/dev/null
+[ -e 44 ] || exit 1
+echo pass fd out open
+
+[ -e 55 ] && exit 1
+exec 55>&44
+[ -e 55 ] || exit 1
+echo pass fd out dup
+
+exec 44>&-
+[ -e 44 ] && exit 1
+echo pass fd out close
+
+[ -e 66 ] && exit 1
+exec 66</dev/null
+[ -e 66 ] || exit 1
+echo pass fd in open
+
+[ -e 77 ] && exit 1
+exec 77<&66
+[ -e 77 ] || exit 1
+echo pass fd in dup
+
+exec 66<&-
+[ -e 66 ] && exit 1
+echo pass fd in close
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/exit1.right b/busybox-1.19.3/shell/hush_test/hush-misc/exit1.right
new file mode 100644
index 0000000..dd2cfc2
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/exit1.right
@@ -0,0 +1 @@
+Once
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/exit1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/exit1.tests
new file mode 100755
index 0000000..41e0d09
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/exit1.tests
@@ -0,0 +1,4 @@
+trap "echo Not shown" EXIT
+(exit)  # must be silent
+trap "echo Once; exit" EXIT
+{ exit; }
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/export-n.right b/busybox-1.19.3/shell/hush_test/hush-misc/export-n.right
new file mode 100644
index 0000000..3d55bf7
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/export-n.right
@@ -0,0 +1,10 @@
+export aaa1="'''"
+export aaa2=''
+export aaa3="'''"'abc'
+export aaa8='8'
+aaa9=9
+aaa10=10
+Nothing:
+Nothing:
+Nothing:
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/export-n.tests b/busybox-1.19.3/shell/hush_test/hush-misc/export-n.tests
new file mode 100755
index 0000000..5252a1e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/export-n.tests
@@ -0,0 +1,37 @@
+export aaa1="'''"
+export aaa2=""
+export aaa3="'''abc"
+export | grep aaa.=
+
+export -n aaa1
+unset aaa2; export -n aaa2="ghi"
+export -n aaa3="klm"
+export | grep aaa.=
+
+export aaa4=4 aaa5=5
+export -n aaa4=4n
+export -n aaa5
+export | grep aaa.=
+
+export aaa5=5 aaa6=6 aaa7=7 aaa8=8
+export -n aaa5 aaa6=6n aaa7
+export | grep aaa.=
+
+aaa9=9
+export -n aaa9
+set | grep ^aaa9=
+
+export aaa10=10
+export -n aaa10
+set | grep ^aaa10=
+
+
+export EXPORTED=qwe
+export -nnnnnn nnnnnn; echo "Nothing:"; env | grep nnnnnn
+
+export -n EXPORTED=123; echo "Nothing:"; env | grep ^EXPORTED
+
+export EXPORTED=qwe
+export -n EXPORTED; EXPORTED=123; echo "Nothing:"; env | grep ^EXPORTED
+
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/export.right b/busybox-1.19.3/shell/hush_test/hush-misc/export.right
new file mode 100644
index 0000000..b93e503
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/export.right
@@ -0,0 +1,6 @@
+export aaa1="'''"
+export aaa2=''
+export aaa3="'''"'abc'
+export aaa4='def'"'''"
+export aaa5="'''"'abc'"'''"'def'"'''"
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/export.tests b/busybox-1.19.3/shell/hush_test/hush-misc/export.tests
new file mode 100755
index 0000000..87a27ec
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/export.tests
@@ -0,0 +1,7 @@
+export aaa1="'''"
+export aaa2=""
+export aaa3="'''abc"
+export aaa4="def'''"
+export aaa5="'''abc'''def'''"
+export | grep aaa.=
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/for_with_bslashes.right b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_bslashes.right
new file mode 100644
index 0000000..02d9669
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_bslashes.right
@@ -0,0 +1,8 @@
+a
+b\c
+b\\c
+b"c
+b'c
+b$c
+b`true`c
+Zero:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/for_with_bslashes.tests b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_bslashes.tests
new file mode 100755
index 0000000..363f3d8
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_bslashes.tests
@@ -0,0 +1,10 @@
+# UNFIXED BUG.
+# commented-out words contain ^C character.
+# It's a SPECIAL_VAR_SYMBOL, for now hush does not escape it.
+# When it is fixed, update this test.
+
+for a in 'a' 'b\c' 'b\\c' 'b"c' "b'c" 'b$c' 'b`true`c' ### 'b#c'
+do
+    echo $a
+done
+echo Zero:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/for_with_keywords.right b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_keywords.right
new file mode 100644
index 0000000..eb04e9a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_keywords.right
@@ -0,0 +1,4 @@
+do
+done
+then
+OK: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/for_with_keywords.tests b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_keywords.tests
new file mode 100755
index 0000000..a8b8e42
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/for_with_keywords.tests
@@ -0,0 +1,2 @@
+for if in do done then; do echo $if; done
+echo OK: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func1.right b/busybox-1.19.3/shell/hush_test/hush-misc/func1.right
new file mode 100644
index 0000000..e21665a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func1.right
@@ -0,0 +1,6 @@
+Hello
+Zero: 0
+One: 1 Param1: World
+Zero: 0 Param1: Restored
+Multi line function
+One: 1
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func1.tests
new file mode 100755
index 0000000..ffb269f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func1.tests
@@ -0,0 +1,16 @@
+f() { echo Hello; }
+g () { echo One: $# Param1: $1; }
+h ( )
+{
+    echo -n 'Multi ' && echo -n 'line '
+    echo function
+    false
+}
+
+f
+echo Zero: $?
+set -- Restored
+{ g World; }
+echo Zero: $? Param1: $1
+( h )
+echo One: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func2.right b/busybox-1.19.3/shell/hush_test/hush-misc/func2.right
new file mode 100644
index 0000000..f2a041d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func2.right
@@ -0,0 +1,5 @@
+First 0
+Second 0
+First 1
+Second 1
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func2.tests
new file mode 100755
index 0000000..763203f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func2.tests
@@ -0,0 +1,9 @@
+i=0
+while test $i != 2; do
+    f() { echo First $i; }
+    f
+    f() { echo Second $i; }
+    f
+    : $((i++))
+done
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func3.right b/busybox-1.19.3/shell/hush_test/hush-misc/func3.right
new file mode 100644
index 0000000..b6d7345
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func3.right
@@ -0,0 +1,4 @@
+One:1
+Zero:0
+One:1
+Five:5
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func3.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func3.tests
new file mode 100755
index 0000000..fa6f26a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func3.tests
@@ -0,0 +1,8 @@
+f() { false; return; echo BAD; };
+{ f; echo One:$?; }; echo Zero:$?
+
+f() { false; return; };
+f; echo One:$?
+
+f() { return 5; };
+f; echo Five:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func4.right b/busybox-1.19.3/shell/hush_test/hush-misc/func4.right
new file mode 100644
index 0000000..0c87e31
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func4.right
@@ -0,0 +1,2 @@
+24
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func4.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func4.tests
new file mode 100755
index 0000000..74c1b9a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func4.tests
@@ -0,0 +1,7 @@
+func() {
+	eval "echo \"\${val_${1}}\""
+}
+
+val_x=24
+(func x)
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func5.right b/busybox-1.19.3/shell/hush_test/hush-misc/func5.right
new file mode 100644
index 0000000..01e79c3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func5.right
@@ -0,0 +1,3 @@
+1
+2
+3
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func5.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func5.tests
new file mode 100755
index 0000000..9c5f9fa
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func5.tests
@@ -0,0 +1,9 @@
+f() { echo $1; }
+f 1
+
+# hush fails on this syntax, but i've never seen anyone use it ...
+#f() ( echo $1; )
+f 2
+
+#f() ( echo $1 )
+f 3
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func_args1.right b/busybox-1.19.3/shell/hush_test/hush-misc/func_args1.right
new file mode 100644
index 0000000..2dfb962
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func_args1.right
@@ -0,0 +1,5 @@
+params: a b c
+'f 1 2 3' called
+params: a b c
+'f 1 2 3' called
+params: a b c
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func_args1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func_args1.tests
new file mode 100755
index 0000000..157921f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func_args1.tests
@@ -0,0 +1,10 @@
+# UNFIXED BUG
+
+f() { echo "'f $1 $2 $3' called"; }
+
+set -- a b c
+echo "params: $1 $2 $3"
+f 1 2 3
+echo "params: $1 $2 $3"
+true | f 1 2 3
+echo "params: $1 $2 $3"
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func_local1.right b/busybox-1.19.3/shell/hush_test/hush-misc/func_local1.right
new file mode 100644
index 0000000..3121783
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func_local1.right
@@ -0,0 +1,3 @@
+z=a
+z=z
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func_local1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func_local1.tests
new file mode 100755
index 0000000..1d594e2
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func_local1.tests
@@ -0,0 +1,5 @@
+export z=z
+f() { local z=a; env | grep ^z; }
+f
+env | grep ^z
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func_local2.right b/busybox-1.19.3/shell/hush_test/hush-misc/func_local2.right
new file mode 100644
index 0000000..fe9343a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func_local2.right
@@ -0,0 +1,14 @@
+1
+2
+1
+2
+1
+1
+2
+2
+3
+2
+2
+3
+1
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/func_local2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/func_local2.tests
new file mode 100755
index 0000000..1a9ae55
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/func_local2.tests
@@ -0,0 +1,7 @@
+x=1
+f() { echo $x; local x=$((x+1)); echo $x; }
+g() { f; echo $x; f; local x=$((x+1)); f; echo $x; f; }
+f
+g
+echo $x
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc1.right b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc1.right
new file mode 100644
index 0000000..7fc68f3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc1.right
@@ -0,0 +1,5 @@
+qwe
+asd
+123
+456
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc1.tests
new file mode 100755
index 0000000..2eeb472
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc1.tests
@@ -0,0 +1,9 @@
+cat <<000; cat <<www; cat <<eee
+000
+qwe
+asd
+www
+123
+456
+eee
+echo Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc2.right b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc2.right
new file mode 100644
index 0000000..74110e3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc2.right
@@ -0,0 +1,9 @@
+exit EOF-f
+"
+echo 1
+echo Hello World
+moo	 
+ EOF-f
+EOF-f   f
+EOF-f 
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc2.tests
new file mode 100755
index 0000000..e619bde
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc2.tests
@@ -0,0 +1,12 @@
+f=1
+  cat <<- EOF-f
+		exit EOF-f
+"
+echo $f
+echo `echo Hello World`
+		moo	 
+ EOF-f
+EOF-f   f
+EOF-f 
+EOF-f
+echo Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc3.right b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc3.right
new file mode 100644
index 0000000..6ed517f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc3.right
@@ -0,0 +1,9 @@
+exit EOF-f
+"
+echo $f
+echo `echo Hello World`
+moo	 
+ EOF-f
+EOF-f   f
+EOF-f 
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc3.tests b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc3.tests
new file mode 100755
index 0000000..938577a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc3.tests
@@ -0,0 +1,12 @@
+f=1
+  cat <<- EOF-f""
+		exit EOF-f
+"
+echo $f
+echo `echo Hello World`
+		moo	 
+ EOF-f
+EOF-f   f
+EOF-f 
+EOF-f
+echo Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_backslash1.right b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_backslash1.right
new file mode 100644
index 0000000..6a61148
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_backslash1.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_backslash1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_backslash1.tests
new file mode 100755
index 0000000..501af54
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_backslash1.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_huge.right b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_huge.right
new file mode 100644
index 0000000..11740f6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_huge.right
@@ -0,0 +1,3 @@
+546ed3f5c81c780d3ab86ada14824237  -
+546ed3f5c81c780d3ab86ada14824237  -
+End
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_huge.tests b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_huge.tests
new file mode 100755
index 0000000..c2ec281
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/heredoc_huge.tests
@@ -0,0 +1,9 @@
+# This creates 120k heredoc
+echo 'cat <<HERE | md5sum' >"$0.tmp"
+yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp"
+echo 'HERE' >>"$0.tmp"
+
+yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum
+. "$0.tmp"
+rm "$0.tmp"
+echo End
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/if_false_exitcode.right b/busybox-1.19.3/shell/hush_test/hush-misc/if_false_exitcode.right
new file mode 100644
index 0000000..7b24a35
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/if_false_exitcode.right
@@ -0,0 +1 @@
+Ok:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/if_false_exitcode.tests b/busybox-1.19.3/shell/hush_test/hush-misc/if_false_exitcode.tests
new file mode 100755
index 0000000..01b36b1
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/if_false_exitcode.tests
@@ -0,0 +1,2 @@
+if false; then echo Bad; fi
+echo Ok:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/nommu1.right b/busybox-1.19.3/shell/hush_test/hush-misc/nommu1.right
new file mode 100644
index 0000000..d206a85
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/nommu1.right
@@ -0,0 +1,7 @@
+Ok
+Ok
+Ok
+Ok
+Ok
+Ok
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/nommu1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/nommu1.tests
new file mode 100755
index 0000000..e14ada5
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/nommu1.tests
@@ -0,0 +1,12 @@
+(echo \
+Ok)
+( (echo \
+Ok) )
+( ( (echo \
+Ok) ) )
+
+(echo \Ok)
+( (echo \Ok) )
+( ( (echo \Ok) ) )
+
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/nommu2.right b/busybox-1.19.3/shell/hush_test/hush-misc/nommu2.right
new file mode 100644
index 0000000..fb8ba8b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/nommu2.right
@@ -0,0 +1,5 @@
+Ok
+Ok
+Ok
+Ok
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/nommu2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/nommu2.tests
new file mode 100755
index 0000000..61ed5ce
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/nommu2.tests
@@ -0,0 +1,5 @@
+echo Not shown | if true; then echo $(echo Ok); fi
+echo Not shown | if true; then echo `echo Ok`; fi
+echo Not shown | ( if true; then echo $(echo Ok); fi )
+echo Not shown | ( if true; then echo `echo Ok`; fi )
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/opts1.right b/busybox-1.19.3/shell/hush_test/hush-misc/opts1.right
new file mode 100644
index 0000000..4da7573
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/opts1.right
@@ -0,0 +1,2 @@
+Param1: >-10qwertyuiop<
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/opts1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/opts1.tests
new file mode 100755
index 0000000..45a23d6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/opts1.tests
@@ -0,0 +1,5 @@
+if test $# = 0; then
+    exec "$THIS_SH" $0 -10qwertyuiop
+fi
+echo "Param1: >$1<"
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/pid.right b/busybox-1.19.3/shell/hush_test/hush-misc/pid.right
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/pid.right
@@ -0,0 +1 @@
+0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/pid.tests b/busybox-1.19.3/shell/hush_test/hush-misc/pid.tests
new file mode 100755
index 0000000..eaeaa71
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/pid.tests
@@ -0,0 +1 @@
+test `(echo $$)` = `echo $$`; echo $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/pipefail.right b/busybox-1.19.3/shell/hush_test/hush-misc/pipefail.right
new file mode 100644
index 0000000..5845d89
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/pipefail.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-misc/pipefail.tests b/busybox-1.19.3/shell/hush_test/hush-misc/pipefail.tests
new file mode 100755
index 0000000..9df8418
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/pipefail.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-misc/read.right b/busybox-1.19.3/shell/hush_test/hush-misc/read.right
new file mode 100644
index 0000000..0e50e2a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/read.right
@@ -0,0 +1,4 @@
+read
+cat
+echo "REPLY=$REPLY"
+REPLY=exec <read.tests
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/read.tests b/busybox-1.19.3/shell/hush_test/hush-misc/read.tests
new file mode 100755
index 0000000..ff1acbd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/read.tests
@@ -0,0 +1,4 @@
+exec <read.tests
+read
+cat
+echo "REPLY=$REPLY"
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir1.right b/busybox-1.19.3/shell/hush_test/hush-misc/redir1.right
new file mode 100644
index 0000000..15515d1
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir1.right
@@ -0,0 +1,12 @@
+Test 0:  var:ok
+File created:ok
+Test 1:  var:ok
+File created:ok
+Test 2:  var:ok
+File created:ok
+Test 3:  var:ok
+File created:ok
+Test 4:  var:ok
+File created:ok
+Test 5:  var:ok
+File created:ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/redir1.tests
new file mode 100755
index 0000000..ef2fbfb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir1.tests
@@ -0,0 +1,40 @@
+rm shell_test_$$ 2>/dev/null
+var=bad
+>shell_test_$$ var=ok
+echo "Test 0:  var:$var"
+test -f shell_test_$$ && echo "File created:ok"
+
+rm shell_test_$$ 2>/dev/null
+var=bad
+var=ok >shell_test_$$
+echo "Test 1:  var:$var"
+test -f shell_test_$$ && echo "File created:ok"
+
+rm shell_test_$$ 2>/dev/null
+var=ok
+true | var=bad >shell_test_$$
+echo "Test 2:  var:$var"
+test -f shell_test_$$ && echo "File created:ok"
+
+rm shell_test_$$ 2>/dev/null
+var=bad
+{ var=ok >shell_test_$$; }
+echo "Test 3:  var:$var"
+test -f shell_test_$$ && echo "File created:ok"
+
+rm shell_test_$$ 2>/dev/null
+var=ok
+{ var=bad >shell_test_$$; } &
+# cant use usleep as it isnt standard in $PATH --
+# we fail when testing busybox compiled solely as "hush"
+wait
+echo "Test 4:  var:$var"
+test -f shell_test_$$ && echo "File created:ok"
+
+rm shell_test_$$ 2>/dev/null
+var=ok
+( var=bad >shell_test_$$ )
+echo "Test 5:  var:$var"
+test -f shell_test_$$ && echo "File created:ok"
+
+rm shell_test_$$ 2>/dev/null
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir2.right b/busybox-1.19.3/shell/hush_test/hush-misc/redir2.right
new file mode 100644
index 0000000..7326d96
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir2.right
@@ -0,0 +1 @@
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/redir2.tests
new file mode 100755
index 0000000..81983ca
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir2.tests
@@ -0,0 +1,2 @@
+echo NOT SHOWN \2>/dev/null
+echo Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir3.right b/busybox-1.19.3/shell/hush_test/hush-misc/redir3.right
new file mode 100644
index 0000000..3d20bbf
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir3.right
@@ -0,0 +1,14 @@
+hush: can't open '/does/not/exist': No such file or directory
+One:1
+hush: can't open '/cant/be/created': No such file or directory
+One:1
+Ok
+hush: can't open '/cant/be/created': No such file or directory
+Zero:0
+hush: can't open '/cant/be/created': No such file or directory
+One:1
+hush: can't open '/cant/be/created': No such file or directory
+One:1
+hush: can't open '/cant/be/created': No such file or directory
+Zero:0
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir3.tests b/busybox-1.19.3/shell/hush_test/hush-misc/redir3.tests
new file mode 100755
index 0000000..7c28e43
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir3.tests
@@ -0,0 +1,9 @@
+echo Error >/does/not/exist; echo One:$?
+t=BAD
+t=Ok >>/cant/be/created; echo One:$?
+echo $t
+! >/cant/be/created; echo Zero:$?
+exec >/cant/be/created; echo One:$?
+exec /bin/true >/cant/be/created; echo One:$?
+! exec /bin/true >/cant/be/created; echo Zero:$?
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir4.right b/busybox-1.19.3/shell/hush_test/hush-misc/redir4.right
new file mode 100644
index 0000000..ead25f6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir4.right
@@ -0,0 +1,18 @@
+shell_test
+\shell_test
+\shell_test
+\shell_test
+Here1
+Ok1
+Here2
+Ok2
+Here3
+Ok3
+Here4
+Ok4
+Now with variable refs
+shell_test_1
+\shell_test_1
+\shell_test_1
+\shell_test_1
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir4.tests b/busybox-1.19.3/shell/hush_test/hush-misc/redir4.tests
new file mode 100755
index 0000000..c50b8ce
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir4.tests
@@ -0,0 +1,85 @@
+rm *shell_test* 2>/dev/null
+
+>\shell_test
+echo *shell_test*
+rm *shell_test*
+
+>\\shell_test
+echo *shell_test*
+rm *shell_test*
+
+>"\shell_test"
+echo *shell_test*
+rm *shell_test*
+
+>"\\shell_test"
+echo *shell_test*
+rm *shell_test*
+
+
+cat <<\shell_test
+Here1
+shell_test
+echo Ok1
+
+cat <<\\shell_test
+Here2
+\shell_test
+echo Ok2
+
+cat <<"\shell_test"
+Here3
+\shell_test
+echo Ok3
+
+cat <<"\\shell_test"
+Here4
+\shell_test
+echo Ok4
+
+
+echo Now with variable refs
+i=1
+
+
+>\shell_test_$i
+echo *shell_test*
+rm *shell_test*
+
+>\\shell_test_$i
+echo *shell_test*
+rm *shell_test*
+
+>"\shell_test_$i"
+echo *shell_test*
+rm *shell_test*
+
+>"\\shell_test_$i"
+echo *shell_test*
+rm *shell_test*
+
+echo Done;exit
+# UNFIXED BUG. bash apparently will expand $i even in terminating delimiter.
+# http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
+# does not mandate this behavior.
+# This is not likely to be used much in real-world.
+
+cat <<\shell_test_$i
+Here1
+shell_test_$i
+echo Ok1
+
+cat <<\\shell_test_$i
+Here2
+\shell_test_$i
+echo Ok2
+
+cat <<"\shell_test_$i"
+Here3
+\shell_test_$i
+echo Ok3
+
+cat <<"\\shell_test_$i"
+Here4
+\shell_test_$i
+echo Ok4
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir5.right b/busybox-1.19.3/shell/hush_test/hush-misc/redir5.right
new file mode 100644
index 0000000..52cce4f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir5.right
@@ -0,0 +1,4 @@
+Backgrounded pipes shall have their stdin redirected to /dev/null
+Zero:0
+Zero:0
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir5.tests b/busybox-1.19.3/shell/hush_test/hush-misc/redir5.tests
new file mode 100755
index 0000000..957f9c8
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir5.tests
@@ -0,0 +1,13 @@
+echo "Backgrounded pipes shall have their stdin redirected to /dev/null"
+
+# 1. bash does not redirect stdin to /dev/null if it is interactive.
+# hush does it always (this is allowed by standards).
+
+# 2. Failure will result in this script hanging
+
+cat & wait; echo Zero:$?
+
+# This does not work for bash! bash bug?
+cat | cat & wait; echo Zero:$?
+
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir6.right b/busybox-1.19.3/shell/hush_test/hush-misc/redir6.right
new file mode 100644
index 0000000..a97c4bd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir6.right
@@ -0,0 +1,4 @@
+Testing multiple redirections to same fd
+Hello
+Done1
+Done2
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/redir6.tests b/busybox-1.19.3/shell/hush_test/hush-misc/redir6.tests
new file mode 100755
index 0000000..c639ebb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/redir6.tests
@@ -0,0 +1,5 @@
+echo "Testing multiple redirections to same fd"
+# bug was making us lose fd #1 after this:
+echo Hello >/dev/null 1>&2
+echo Done1
+echo Done2 >&2
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/return1.right b/busybox-1.19.3/shell/hush_test/hush-misc/return1.right
new file mode 100644
index 0000000..7b24a35
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/return1.right
@@ -0,0 +1 @@
+Ok:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/return1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/return1.tests
new file mode 100755
index 0000000..eeb92ef
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/return1.tests
@@ -0,0 +1,4 @@
+echo "true && return; echo Should not be printed" >return_sourced
+. ./return_sourced
+rm return_sourced
+echo Ok:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/shift.right b/busybox-1.19.3/shell/hush_test/hush-misc/shift.right
new file mode 100644
index 0000000..d281e35
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/shift.right
@@ -0,0 +1,6 @@
+./shift.tests abc d e
+./shift.tests d e 123
+./shift.tests d e 123
+./shift.tests
+./shift.tests
+./shift.tests
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/shift.tests b/busybox-1.19.3/shell/hush_test/hush-misc/shift.tests
new file mode 100755
index 0000000..53ef249
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/shift.tests
@@ -0,0 +1,14 @@
+if test $# = 0; then
+    exec "$THIS_SH" $0 abc "d e" 123
+fi
+echo $0 $1 $2
+shift
+echo $0 $1 $2
+shift 999
+echo $0 $1 $2
+shift 2
+echo $0 $1 $2
+shift 2
+echo $0 $1 $2
+shift
+echo $0 $1 $2
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/sig_exitcode.right b/busybox-1.19.3/shell/hush_test/hush-misc/sig_exitcode.right
new file mode 100644
index 0000000..d5f000a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/sig_exitcode.right
@@ -0,0 +1,5 @@
+KILL
+137:137
+KILL
+0:0
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/sig_exitcode.tests b/busybox-1.19.3/shell/hush_test/hush-misc/sig_exitcode.tests
new file mode 100755
index 0000000..7879dc8
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/sig_exitcode.tests
@@ -0,0 +1,9 @@
+exec 2>&1
+
+$THIS_SH -c 'kill -9 $$'
+echo 137:$?
+
+! $THIS_SH -c 'kill -9 $$'
+echo 0:$?
+
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/sigint1.right b/busybox-1.19.3/shell/hush_test/hush-misc/sigint1.right
new file mode 100644
index 0000000..a9094b0
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/sigint1.right
@@ -0,0 +1 @@
+Sending SIGINT to main shell PID
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/sigint1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/sigint1.tests
new file mode 100755
index 0000000..3d483d3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/sigint1.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-misc/source1.right b/busybox-1.19.3/shell/hush_test/hush-misc/source1.right
new file mode 100644
index 0000000..d425603
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/source1.right
@@ -0,0 +1,5 @@
+hush: syntax error: unterminated ${name}
+line2
+Ok1:0
+hush: syntax error: unterminated '
+Ok2:1
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/source1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/source1.tests
new file mode 100755
index 0000000..c138883
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/source1.tests
@@ -0,0 +1,10 @@
+echo 'echo ${^}
+echo line2' >sourced1
+. ./sourced1
+echo Ok1:$?
+
+echo "echo '" >sourced1
+. ./sourced1
+echo Ok2:$?
+
+rm sourced1
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err.right b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err.right
new file mode 100644
index 0000000..680e796
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err.right
@@ -0,0 +1,4 @@
+shown
+hush: syntax error: unterminated '
+test
+not shown
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err.tests b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err.tests
new file mode 100755
index 0000000..d10ed42
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err.tests
@@ -0,0 +1,3 @@
+echo shown
+echo test `echo 'aa`
+echo not shown
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err_negate.right b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err_negate.right
new file mode 100644
index 0000000..8c70106
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err_negate.right
@@ -0,0 +1,2 @@
+bash 3.2 fails this
+hush: syntax error: ! ! command
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err_negate.tests b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err_negate.tests
new file mode 100755
index 0000000..d61b1b0
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/syntax_err_negate.tests
@@ -0,0 +1,2 @@
+echo bash 3.2 fails this
+! ! true
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/until1.right b/busybox-1.19.3/shell/hush_test/hush-misc/until1.right
new file mode 100644
index 0000000..be2daad
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/until1.right
@@ -0,0 +1,3 @@
+1
+1
+Ok:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/until1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/until1.tests
new file mode 100755
index 0000000..10ab283
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/until1.tests
@@ -0,0 +1,11 @@
+x=1
+until test "$x" = 4; do echo $x; x=4; done
+
+# We had a bug in multi-line form
+x=1
+until test "$x" = 4; do
+        echo $x
+        x=4
+done
+
+echo Ok:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while1.right b/busybox-1.19.3/shell/hush_test/hush-misc/while1.right
new file mode 100644
index 0000000..7c4d7be
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while1.right
@@ -0,0 +1 @@
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while1.tests b/busybox-1.19.3/shell/hush_test/hush-misc/while1.tests
new file mode 100755
index 0000000..11e201e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while1.tests
@@ -0,0 +1,2 @@
+while false; do echo NOT SHOWN; done
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while2.right b/busybox-1.19.3/shell/hush_test/hush-misc/while2.right
new file mode 100644
index 0000000..07207cc
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while2.right
@@ -0,0 +1,2 @@
+Hello
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while2.tests b/busybox-1.19.3/shell/hush_test/hush-misc/while2.tests
new file mode 100755
index 0000000..2247adc
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while2.tests
@@ -0,0 +1,2 @@
+while echo Hello; false; do echo NOT SHOWN; done
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while3.right b/busybox-1.19.3/shell/hush_test/hush-misc/while3.right
new file mode 100644
index 0000000..7c4d7be
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while3.right
@@ -0,0 +1 @@
+OK:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while3.tests b/busybox-1.19.3/shell/hush_test/hush-misc/while3.tests
new file mode 100755
index 0000000..9132b5f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while3.tests
@@ -0,0 +1,4 @@
+while false; do
+    # bash will require at least ":" here...
+done
+echo OK:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while_in_subshell.right b/busybox-1.19.3/shell/hush_test/hush-misc/while_in_subshell.right
new file mode 100644
index 0000000..290d39b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while_in_subshell.right
@@ -0,0 +1 @@
+OK: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-misc/while_in_subshell.tests b/busybox-1.19.3/shell/hush_test/hush-misc/while_in_subshell.tests
new file mode 100755
index 0000000..def8e09
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-misc/while_in_subshell.tests
@@ -0,0 +1,2 @@
+(while true; do exit; done)
+echo OK: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/argv0.right b/busybox-1.19.3/shell/hush_test/hush-parsing/argv0.right
new file mode 100644
index 0000000..d86bac9
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/argv0.right
@@ -0,0 +1 @@
+OK
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/argv0.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/argv0.tests
new file mode 100755
index 0000000..f5c40f6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/argv0.tests
@@ -0,0 +1,4 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" arg
+fi
+echo OK
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/brace1.right b/busybox-1.19.3/shell/hush_test/hush-parsing/brace1.right
new file mode 100644
index 0000000..8619f45
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/brace1.right
@@ -0,0 +1,7 @@
+{abc}
+{
+}
+hush: can't execute '{cmd': No such file or directory
+hush: can't execute '{': No such file or directory
+hush: can't execute '{': No such file or directory
+Done: 127
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/brace1.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/brace1.tests
new file mode 100755
index 0000000..2b45927
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/brace1.tests
@@ -0,0 +1,7 @@
+echo {abc}
+echo {
+echo }
+{cmd
+""{
+{""
+echo Done: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/brace2.right b/busybox-1.19.3/shell/hush_test/hush-parsing/brace2.right
new file mode 100644
index 0000000..37a9666
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/brace2.right
@@ -0,0 +1,3 @@
+{q,w}
+{q,w}
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/brace2.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/brace2.tests
new file mode 100755
index 0000000..ef75f0b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/brace2.tests
@@ -0,0 +1,5 @@
+v='{q,w}'
+# Should not brace-expand v value
+echo $v
+echo "$v"
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/comment1.right b/busybox-1.19.3/shell/hush_test/hush-parsing/comment1.right
new file mode 100644
index 0000000..a102b1d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/comment1.right
@@ -0,0 +1,2 @@
+Nothing:
+String: #should-be-echoed
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/comment1.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/comment1.tests
new file mode 100755
index 0000000..d268860
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/comment1.tests
@@ -0,0 +1,2 @@
+echo Nothing: #should-not-be-echoed
+echo String: ""#should-be-echoed
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/eol1.right b/busybox-1.19.3/shell/hush_test/hush-parsing/eol1.right
new file mode 100644
index 0000000..31c896f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/eol1.right
@@ -0,0 +1 @@
+Done:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/eol1.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/eol1.tests
new file mode 100755
index 0000000..f1b55e8
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/eol1.tests
@@ -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:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape1.right b/busybox-1.19.3/shell/hush_test/hush-parsing/escape1.right
new file mode 100644
index 0000000..1899b87
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape1.right
@@ -0,0 +1,4 @@
+\
+a\b
+\\
+c\\d
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape1.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/escape1.tests
new file mode 100755
index 0000000..25ac96b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape1.tests
@@ -0,0 +1,6 @@
+test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77
+
+echo "\\"
+echo a"\\"b
+echo '\\'
+echo c'\\'d
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape2.right b/busybox-1.19.3/shell/hush_test/hush-parsing/escape2.right
new file mode 100644
index 0000000..f55fd4a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape2.right
@@ -0,0 +1,4 @@
+*?[a]*
+a*?[a]*b
+*?[a]*
+c*?[a]*d
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape2.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/escape2.tests
new file mode 100755
index 0000000..ee71801
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape2.tests
@@ -0,0 +1,4 @@
+echo "*?[a]*"
+echo a"*?[a]*"b
+echo '*?[a]*'
+echo c'*?[a]*'d
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape3.right b/busybox-1.19.3/shell/hush_test/hush-parsing/escape3.right
new file mode 100644
index 0000000..da02a97
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape3.right
@@ -0,0 +1,23 @@
+v: a \ b \\ c \\\ d \\\\ e
+v: a \ b \\ c \\\ d \\\\ e
+Unquoted:
+.a.
+.\.
+.b.
+.\\.
+.c.
+.\\\.
+.d.
+.\\\\.
+.e.
+Quoted:
+.a.
+.\.
+.b.
+.\\.
+.c.
+.\\\.
+.d.
+.\\\\.
+.e.
+done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape3.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/escape3.tests
new file mode 100755
index 0000000..18705bd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape3.tests
@@ -0,0 +1,10 @@
+test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77
+
+v='a \ b \\ c \\\ d \\\\ e'
+echo v: $v
+echo v: "$v"
+echo Unquoted:
+for a in $v; do echo .$a.; done
+echo Quoted:
+for a in $v; do echo ".$a."; done
+echo done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape4.right b/busybox-1.19.3/shell/hush_test/hush-parsing/escape4.right
new file mode 100644
index 0000000..5de3e0c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape4.right
@@ -0,0 +1,2 @@
+Ok
+End
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape4.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/escape4.tests
new file mode 100755
index 0000000..df8bf0a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape4.tests
@@ -0,0 +1,6 @@
+i\
+f tr\
+ue; th\
+en echo "O\
+k"; fi; echo "\
+End"
\ No newline at end of file
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape5.right b/busybox-1.19.3/shell/hush_test/hush-parsing/escape5.right
new file mode 100644
index 0000000..3cdd393
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape5.right
@@ -0,0 +1,9 @@
+a\nb\nc\n
+a
+b
+c
+a\nb\nc\n
+a
+b
+c
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/escape5.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/escape5.tests
new file mode 100755
index 0000000..337a98e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/escape5.tests
@@ -0,0 +1,7 @@
+v="a\nb\nc\n"
+echo "$v"
+printf "$v"
+v='a\nb\nc\n'
+echo "$v"
+printf "$v"
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/group1.right b/busybox-1.19.3/shell/hush_test/hush-parsing/group1.right
new file mode 100644
index 0000000..6a7c4be
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/group1.right
@@ -0,0 +1 @@
+word} }
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/group1.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/group1.tests
new file mode 100755
index 0000000..f063fbc
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/group1.tests
@@ -0,0 +1 @@
+{ echo word} }; }
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/group2.right b/busybox-1.19.3/shell/hush_test/hush-parsing/group2.right
new file mode 100644
index 0000000..df4d930
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/group2.right
@@ -0,0 +1,2 @@
+got TERM
+Done: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/group2.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/group2.tests
new file mode 100755
index 0000000..d991785
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/group2.tests
@@ -0,0 +1,3 @@
+# Bug was in handling of "}&" without space
+{ trap "echo got TERM" TERM; sleep 2; }& sleep 1; kill $!; wait
+echo Done: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/groups_and_keywords1.right b/busybox-1.19.3/shell/hush_test/hush-parsing/groups_and_keywords1.right
new file mode 100644
index 0000000..4c46650
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/groups_and_keywords1.right
@@ -0,0 +1,11 @@
+Semicolons after } can be omitted 1:
+foo
+bar
+Semicolons after } can be omitted 2:
+foo
+bar
+Semicolons after fi can be omitted:
+foo
+bar
+baz
+Done:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/groups_and_keywords1.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/groups_and_keywords1.tests
new file mode 100755
index 0000000..01944d7
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/groups_and_keywords1.tests
@@ -0,0 +1,10 @@
+echo "Semicolons after } can be omitted 1:"
+if { echo foo; } then { echo bar; } fi
+
+echo "Semicolons after } can be omitted 2:"
+while { echo foo; } do { echo bar; break; } done
+
+echo "Semicolons after fi can be omitted:"
+while if echo foo; then echo bar; fi do echo baz; break; done
+
+echo Done:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/negate.right b/busybox-1.19.3/shell/hush_test/hush-parsing/negate.right
new file mode 100644
index 0000000..61d2ecd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/negate.right
@@ -0,0 +1,36 @@
+! printing !
+0
+1
+1
+0
+0
+0
+!
+a
+b
+c
+! 1
+a 1
+b 1
+c 1
+! 1
+a 1
+b 1
+c 1
+0
+0
+0
+0
+1
+1
+1
+1
+0
+0
+0
+0
+1
+1
+1
+1
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/negate.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/negate.tests
new file mode 100755
index 0000000..51151cb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/negate.tests
@@ -0,0 +1,19 @@
+echo ! printing !
+! false
+echo $?
+! true
+echo $?
+if ! false; then false; echo $?; fi
+echo $?
+if ! false; then ! false; echo $?; fi
+echo $?
+PRINTF=`which printf`
+for a in ! a b c; do echo $a; done
+for a in ! a b c; do ! printf "$a "; echo $?; done
+test x"$PRINTF" = x"" && exit 1
+for a in ! a b c; do ! "$PRINTF" "$a "; echo $?; done
+for a in ! a b c; do ! printf "$a " | false; echo $?; done
+for a in ! a b c; do ! printf "$a " | true; echo $?; done
+for a in ! a b c; do ! { printf "$a " | false; }; echo $?; done
+for a in ! a b c; do ! { printf "$a " | true; }; echo $?; done
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/noeol.right b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol.right
new file mode 100644
index 0000000..e427984
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol.right
@@ -0,0 +1 @@
+HELLO
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/noeol.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol.tests
new file mode 100755
index 0000000..a93113a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol.tests
@@ -0,0 +1,2 @@
+# next line has no EOL!
+echo HELLO
\ No newline at end of file
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/noeol2.right b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol2.right
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol2.right
@@ -0,0 +1 @@
+1
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/noeol2.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol2.tests
new file mode 100755
index 0000000..1220f05
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol2.tests
@@ -0,0 +1,7 @@
+# last line has no EOL!
+if true
+then
+  echo 1
+else
+  echo 2
+fi
\ No newline at end of file
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/noeol3.right b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol3.right
new file mode 100644
index 0000000..56f8515
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol3.right
@@ -0,0 +1 @@
+hush: syntax error: unterminated "
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/noeol3.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol3.tests
new file mode 100755
index 0000000..ec958ed
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/noeol3.tests
@@ -0,0 +1,2 @@
+# last line has no EOL!
+echo "unterminated
\ No newline at end of file
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/process_subst.right b/busybox-1.19.3/shell/hush_test/hush-parsing/process_subst.right
new file mode 100644
index 0000000..397bc80
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/process_subst.right
@@ -0,0 +1,3 @@
+TESTzzBEST
+TEST$(echo zz)BEST
+TEST'BEST
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/process_subst.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/process_subst.tests
new file mode 100755
index 0000000..21996bc
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/process_subst.tests
@@ -0,0 +1,3 @@
+echo "TEST`echo zz;echo;echo`BEST"
+echo "TEST`echo '$(echo zz)'`BEST"
+echo "TEST`echo "'"`BEST"
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote1.right b/busybox-1.19.3/shell/hush_test/hush-parsing/quote1.right
new file mode 100644
index 0000000..cb38205
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote1.right
@@ -0,0 +1 @@
+'1'
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote1.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/quote1.tests
new file mode 100755
index 0000000..f558954
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote1.tests
@@ -0,0 +1,2 @@
+a=1
+echo "'$a'"
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote2.right b/busybox-1.19.3/shell/hush_test/hush-parsing/quote2.right
new file mode 100644
index 0000000..3bc9edc
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote2.right
@@ -0,0 +1 @@
+>1
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote2.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/quote2.tests
new file mode 100755
index 0000000..bd966f3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote2.tests
@@ -0,0 +1,2 @@
+a=1
+echo ">$a"
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote3.right b/busybox-1.19.3/shell/hush_test/hush-parsing/quote3.right
new file mode 100644
index 0000000..bbe46df
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote3.right
@@ -0,0 +1,12 @@
+Testing: in ""
+..
+Testing: in ''
+..
+Testing: in $empty
+Testing: in $empty""
+..
+Testing: in $empty''
+..
+Testing: in "$empty"
+..
+Finished
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote3.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/quote3.tests
new file mode 100755
index 0000000..b5fd597
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote3.tests
@@ -0,0 +1,21 @@
+empty=''
+
+echo 'Testing: in ""'
+for a in ""; do echo ".$a."; done
+
+echo 'Testing: in '"''"
+for a in ''; do echo ".$a."; done
+
+echo 'Testing: in $empty'
+for a in $empty; do echo ".$a."; done
+
+echo 'Testing: in $empty""'
+for a in $empty""; do echo ".$a."; done
+
+echo 'Testing: in $empty'"''"
+for a in $empty''; do echo ".$a."; done
+
+echo 'Testing: in "$empty"'
+for a in "$empty"; do echo ".$a."; done
+
+echo Finished
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote4.right b/busybox-1.19.3/shell/hush_test/hush-parsing/quote4.right
new file mode 100644
index 0000000..b2901ea
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote4.right
@@ -0,0 +1 @@
+a b
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/quote4.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/quote4.tests
new file mode 100755
index 0000000..f1dabfa
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/quote4.tests
@@ -0,0 +1,2 @@
+a_b='a b'
+echo "$a_b"
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/redir_space.right b/busybox-1.19.3/shell/hush_test/hush-parsing/redir_space.right
new file mode 100644
index 0000000..0842952
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/redir_space.right
@@ -0,0 +1,3 @@
+z1.tmp: 1
+z2.tmp: 1
+"z1.tmp z2.tmp": TEST 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/redir_space.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/redir_space.tests
new file mode 100755
index 0000000..c0b5430
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/redir_space.tests
@@ -0,0 +1,6 @@
+v='z1.tmp z2.tmp'
+echo TEST >$v
+echo 'z1.tmp:' `cat 'z1.tmp' 2>/dev/null; echo $?`
+echo 'z2.tmp:' `cat 'z2.tmp' 2>/dev/null; echo $?`
+echo '"z1.tmp z2.tmp":' `cat 'z1.tmp z2.tmp' 2>/dev/null; echo $?`
+rm z*.tmp
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted.right b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted.right
new file mode 100644
index 0000000..b56323f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted.right
@@ -0,0 +1,8 @@
+.1 abc d e f.
+.1.
+.abc.
+.d e f.
+.-1 abc d e f-.
+.-1.
+.abc.
+.d e f-.
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted.tests
new file mode 100755
index 0000000..2fe49b1
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted.tests
@@ -0,0 +1,8 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" 1 abc 'd e f'
+fi
+
+for a in "$*"; do echo ".$a."; done
+for a in "$@"; do echo ".$a."; done
+for a in "-$*-"; do echo ".$a."; done
+for a in "-$@-"; do echo ".$a."; done
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted2.right b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted2.right
new file mode 100644
index 0000000..1bff408
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted2.right
@@ -0,0 +1,8 @@
+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:
+Empty:
diff --git a/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted2.tests b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted2.tests
new file mode 100755
index 0000000..7c5ff45
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-parsing/starquoted2.tests
@@ -0,0 +1,19 @@
+if test $# != 0; then
+    exec "$THIS_SH" "$0"
+fi
+
+# No params!
+for a in "$*"; do echo Should be printed; done
+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 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:
+printf 'Empty:%s\n' "$@"
+printf "Empty:%s\n" "$@"
+printf "Empty:%s\\n" "$@"
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/emptytick.right b/busybox-1.19.3/shell/hush_test/hush-psubst/emptytick.right
new file mode 100644
index 0000000..2500cab
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/emptytick.right
@@ -0,0 +1,17 @@
+0
+0
+hush: can't execute '': No such file or directory
+127
+hush: can't execute '': No such file or directory
+127
+0
+0
+0
+0
+hush: can't execute '': No such file or directory
+127
+hush: can't execute '': No such file or directory
+127
+0
+0
+hush: can't execute '': No such file or directory
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/emptytick.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/emptytick.tests
new file mode 100755
index 0000000..eaffafb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/emptytick.tests
@@ -0,0 +1,16 @@
+true;  ``; echo $?
+false; ``; echo $?
+true;  `""`; echo $?
+false; `""`; echo $?
+true;  `     `; echo $?
+false; `     `; echo $?
+
+true;  $(); echo $?
+false; $(); echo $?
+true;  $(""); echo $?
+false; $(""); echo $?
+true;  $(     ); echo $?
+false; $(     ); echo $?
+
+exec ''; echo $?
+echo Not reached
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/falsetick.right b/busybox-1.19.3/shell/hush_test/hush-psubst/falsetick.right
new file mode 100644
index 0000000..0b98fb7
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/falsetick.right
@@ -0,0 +1,27 @@
+0
+0
+0
+0
+2
+2
+2
+2
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+hush: can't open '/does/not/exist': No such file or directory
+1
+Done: a=b
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/falsetick.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/falsetick.tests
new file mode 100755
index 0000000..44d2eae
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/falsetick.tests
@@ -0,0 +1,22 @@
+# Exitcode 0 (`` has no exitcode, but assignment has):
+true;  a=``; echo $?
+false; a=``; echo $?
+true;  a=$(); echo $?
+false; a=$(); echo $?
+# Exitcode 2 (`cmd` expansion sets exitcode after assignment set it to 0):
+true;  a=`exit 2`; echo $?
+false; a=`exit 2`; echo $?
+true;  a=$(exit 2); echo $?
+false; a=$(exit 2); echo $?
+# Exitcode 1 (redirect sets exitcode to 1 on error after them):
+true;  a=`` >/does/not/exist; echo $?
+false; a=`` >/does/not/exist; echo $?
+true;  a=$() >/does/not/exist; echo $?
+false; a=$() >/does/not/exist; echo $?
+true;  a=`exit 2` >/does/not/exist; echo $?
+false; a=`exit 2` >/does/not/exist; echo $?
+true;  a=$(exit 2) >/does/not/exist; echo $?
+false; a=$(exit 2) >/does/not/exist; echo $?
+# ...and assignment still happens despite redirect error:
+true;  a=$(echo b) >/does/not/exist; echo $?
+echo "Done: a=$a"
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick.right b/busybox-1.19.3/shell/hush_test/hush-psubst/tick.right
new file mode 100644
index 0000000..6ed281c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick.right
@@ -0,0 +1,2 @@
+1
+1
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/tick.tests
new file mode 100755
index 0000000..1f749a9
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick.tests
@@ -0,0 +1,4 @@
+true
+false; echo `echo $?`
+true
+{ false; echo `echo $?`; }
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick2.right b/busybox-1.19.3/shell/hush_test/hush-psubst/tick2.right
new file mode 100644
index 0000000..216c883
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick2.right
@@ -0,0 +1 @@
+BAZ
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick2.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/tick2.tests
new file mode 100755
index 0000000..db4e944
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick2.tests
@@ -0,0 +1,5 @@
+if false; then
+    echo "FOO"
+    tmp=`echo BAR >&2`
+fi
+echo BAZ
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick3.right b/busybox-1.19.3/shell/hush_test/hush-psubst/tick3.right
new file mode 100644
index 0000000..00f267a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick3.right
@@ -0,0 +1,6 @@
+\TESTZZBEST
+$TEST
+Q
+a\bc
+11-$a-\t-\-\"-`-\--\z-\*-\?-22 33-$a-\t-\-"-`-\--\z-\*-\?-44
+done:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick3.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/tick3.tests
new file mode 100755
index 0000000..3aeb241
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick3.tests
@@ -0,0 +1,14 @@
+test "$CONFIG_FEATURE_FANCY_ECHO" = "y" || exit 77
+
+TEST=Q
+# \` is special
+echo `echo '\'TEST\`echo ZZ\`BEST`
+# \$ and \\ are special
+echo `echo \\$TEST`
+echo `echo \$TEST`
+echo a`echo \\\\b`c
+
+# \" is not special if in unquoted `cmd` (passed verbatim WITH \),
+# but is special in quoted one
+echo `echo 11'-$a-\t-\\-\"-\`-\--\z-\*-\?-'22` "`echo 33'-$a-\t-\\-\"-\`-\--\z-\*-\?-'44`"
+echo done:$?
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick4.right b/busybox-1.19.3/shell/hush_test/hush-psubst/tick4.right
new file mode 100644
index 0000000..d8030ea
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick4.right
@@ -0,0 +1,7 @@
+(TEST) BEST
+TEST) BEST
+((TEST) BEST
+)
+abc
+a)c
+OK: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick4.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/tick4.tests
new file mode 100755
index 0000000..f2305fb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick4.tests
@@ -0,0 +1,7 @@
+echo $(echo '(TEST)' BEST)
+echo $(echo 'TEST)' BEST)
+echo $(echo \(\(TEST\) BEST)
+echo $(echo \))
+echo $(echo a"`echo "b"`"c )
+echo $(echo a"`echo ")"`"c )
+echo OK: $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick5.right b/busybox-1.19.3/shell/hush_test/hush-psubst/tick5.right
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick5.right
@@ -0,0 +1 @@
+1
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick5.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/tick5.tests
new file mode 100755
index 0000000..bd160c9
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick5.tests
@@ -0,0 +1 @@
+true; echo `false` $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick_huge.right b/busybox-1.19.3/shell/hush_test/hush-psubst/tick_huge.right
new file mode 100644
index 0000000..11740f6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick_huge.right
@@ -0,0 +1,3 @@
+546ed3f5c81c780d3ab86ada14824237  -
+546ed3f5c81c780d3ab86ada14824237  -
+End
diff --git a/busybox-1.19.3/shell/hush_test/hush-psubst/tick_huge.tests b/busybox-1.19.3/shell/hush_test/hush-psubst/tick_huge.tests
new file mode 100755
index 0000000..acce92f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-psubst/tick_huge.tests
@@ -0,0 +1,7 @@
+# This creates 120k file
+yes "123456789 123456789 123456789 123456789" | head -3000 >>"$0.tmp"
+
+echo "`cat $0.tmp`" | md5sum
+rm "$0.tmp"
+yes "123456789 123456789 123456789 123456789" | head -3000 | md5sum
+echo End
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_REPLY.right b/busybox-1.19.3/shell/hush_test/hush-read/read_REPLY.right
new file mode 100644
index 0000000..59f5d54
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_REPLY.right
@@ -0,0 +1,5 @@
+test 1: |  abc1  def  |
+test 2: |  \abc2  d\ef  |
+test 3: |abc3  def|
+test 4: |\abc4  d\ef|
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_REPLY.tests b/busybox-1.19.3/shell/hush_test/hush-read/read_REPLY.tests
new file mode 100755
index 0000000..ba20cae
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_REPLY.tests
@@ -0,0 +1,5 @@
+echo '  \abc1  d\ef  ' | ( read         ; echo "test 1: |$REPLY|" )
+echo '  \abc2  d\ef  ' | ( read -r      ; echo "test 2: |$REPLY|" )
+echo '  \abc3  d\ef  ' | ( read    REPLY; echo "test 3: |$REPLY|" )
+echo '  \abc4  d\ef  ' | ( read -r REPLY; echo "test 4: |$REPLY|" )
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_ifs.right b/busybox-1.19.3/shell/hush_test/hush-read/read_ifs.right
new file mode 100644
index 0000000..b523344
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_ifs.right
@@ -0,0 +1,10 @@
+test 1: .a. .b. .c.
+test 2: .a. .b. .c.
+test 3: .a. .. .b,c.
+test 4: .a. .. .b,c.
+test 5: .a. .. .c.
+test 6: .a. .. .c. .d.
+test 7: .a. .. .b,c,d  ,  ,.
+test 8: .. .a. .b. .c.
+test 9: .a. .b. .c. ..
+test A: .. .a. .. .b. .c.
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_ifs.tests b/busybox-1.19.3/shell/hush_test/hush-read/read_ifs.tests
new file mode 100755
index 0000000..6e83112
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_ifs.tests
@@ -0,0 +1,10 @@
+printf 'a\t\tb\tc\n' | ( IFS=$(printf "\t") read a b c;  echo "test 1: .$a. .$b. .$c." )
+printf 'a\t\tb\tc\n' | ( IFS=$(printf " \t") read a b c; echo "test 2: .$a. .$b. .$c." )
+printf 'a,,b,c\n'    | ( IFS="," read a b c;  echo "test 3: .$a. .$b. .$c." )
+printf 'a,,b,c\n'    | ( IFS=" ," read a b c; echo "test 4: .$a. .$b. .$c." )
+printf 'a ,, c\n'    | ( IFS=" ," read a b c; echo "test 5: .$a. .$b. .$c." )
+printf 'a ,, c d\n'  | ( IFS=" ," read a b c d;     echo "test 6: .$a. .$b. .$c. .$d." )
+printf ' a,,b,c,d  ,  ,\n' | ( IFS=" ," read a b c; echo "test 7: .$a. .$b. .$c." )
+printf '\t,\ta\t,\tb\tc'   | ( IFS=$(printf " \t,") read a b c d;   echo "test 8: .$a. .$b. .$c. .$d." )
+printf '\t\ta\t,\tb\tc'    | ( IFS=$(printf " \t,") read a b c d;   echo "test 9: .$a. .$b. .$c. .$d." )
+printf '\t,\ta\t,,\tb\tc'  | ( IFS=$(printf " \t,") read a b c d e; echo "test A: .$a. .$b. .$c. .$d. .$e." )
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_n.right b/busybox-1.19.3/shell/hush_test/hush-read/read_n.right
new file mode 100644
index 0000000..1f81af0
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_n.right
@@ -0,0 +1,3 @@
+test
+tes
+tes
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_n.tests b/busybox-1.19.3/shell/hush_test/hush-read/read_n.tests
new file mode 100755
index 0000000..12423ba
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_n.tests
@@ -0,0 +1,3 @@
+echo 'test' | (read reply; echo "$reply")
+echo 'test' | (read -n 3 reply; echo "$reply")
+echo 'test' | (read -n3 reply; echo "$reply")
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_r.right b/busybox-1.19.3/shell/hush_test/hush-read/read_r.right
new file mode 100644
index 0000000..3536bf7
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_r.right
@@ -0,0 +1,2 @@
+testbest
+test\
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_r.tests b/busybox-1.19.3/shell/hush_test/hush-read/read_r.tests
new file mode 100755
index 0000000..2c4cc61
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_r.tests
@@ -0,0 +1,2 @@
+echo -e 'test\\\nbest' | (read reply; echo "$reply")
+echo -e 'test\\\nbest' | (read -r reply; echo "$reply")
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_t.right b/busybox-1.19.3/shell/hush_test/hush-read/read_t.right
new file mode 100644
index 0000000..04126cb
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_t.right
@@ -0,0 +1,4 @@
+><
+><
+>test<
+>test<
diff --git a/busybox-1.19.3/shell/hush_test/hush-read/read_t.tests b/busybox-1.19.3/shell/hush_test/hush-read/read_t.tests
new file mode 100755
index 0000000..d65f1ae
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-read/read_t.tests
@@ -0,0 +1,10 @@
+# bash 3.2 outputs:
+
+# ><
+{ echo -n 'te'; sleep 2; echo 'st'; }   | (read -t 1 reply; echo ">$reply<")
+# ><
+{               sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<")
+# >test<
+{ echo -n 'te'; sleep 1; echo 'st'; }   | (read -t 2 reply; echo ">$reply<")
+# >test<
+{               sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<")
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/catch.right b/busybox-1.19.3/shell/hush_test/hush-trap/catch.right
new file mode 100644
index 0000000..80a062c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/catch.right
@@ -0,0 +1,5 @@
+sending USR2
+caught
+sending USR2
+sending USR2
+USR2
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/catch.tests b/busybox-1.19.3/shell/hush_test/hush-trap/catch.tests
new file mode 100755
index 0000000..d2a21d1
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/catch.tests
@@ -0,0 +1,20 @@
+# avoid ugly warnings about signals not being caught
+trap ":" USR1 USR2
+
+"$THIS_SH" -c '
+trap "echo caught" USR2
+echo "sending USR2"
+kill -USR2 $$
+
+trap "" USR2
+echo "sending USR2"
+kill -USR2 $$
+
+trap "-" USR2
+echo "sending USR2"
+kill -USR2 $$
+
+echo "not reached"
+'
+
+trap "-" USR1 USR2
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/exit.right b/busybox-1.19.3/shell/hush_test/hush-trap/exit.right
new file mode 100644
index 0000000..3d00725
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/exit.right
@@ -0,0 +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
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/exit.tests b/busybox-1.19.3/shell/hush_test/hush-trap/exit.tests
new file mode 100755
index 0000000..2061105
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/exit.tests
@@ -0,0 +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/busybox-1.19.3/shell/hush_test/hush-trap/save-ret.right b/busybox-1.19.3/shell/hush_test/hush-trap/save-ret.right
new file mode 100644
index 0000000..a3e12ce
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/save-ret.right
@@ -0,0 +1,2 @@
+YEAH
+0
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/save-ret.tests b/busybox-1.19.3/shell/hush_test/hush-trap/save-ret.tests
new file mode 100755
index 0000000..0786b6d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/save-ret.tests
@@ -0,0 +1,4 @@
+# make sure we do not corrupt $? across traps
+trap "echo YEAH; false" USR1
+kill -USR1 $$
+echo $?
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/savetrap.right b/busybox-1.19.3/shell/hush_test/hush-trap/savetrap.right
new file mode 100644
index 0000000..a59225b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/savetrap.right
@@ -0,0 +1,8 @@
+trap -- 'echo Exiting' EXIT
+trap -- 'echo WINCH!' WINCH
+trap -- 'echo Exiting' EXIT
+trap -- 'echo WINCH!' WINCH
+trap -- 'echo Exiting' EXIT
+trap -- 'echo WINCH!' WINCH
+Done
+Exiting
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/savetrap.tests b/busybox-1.19.3/shell/hush_test/hush-trap/savetrap.tests
new file mode 100755
index 0000000..c2b312f
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/savetrap.tests
@@ -0,0 +1,9 @@
+trap 'echo Exiting' EXIT
+trap 'echo WINCH!' SIGWINCH
+v=` trap   `
+echo "$v"
+v=$(	trap )
+echo "$v"
+v=`trap`
+echo "$v"
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/signal7.right b/busybox-1.19.3/shell/hush_test/hush-trap/signal7.right
new file mode 100644
index 0000000..ba7453e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/signal7.right
@@ -0,0 +1 @@
+Bug detected: 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/signal7.tests b/busybox-1.19.3/shell/hush_test/hush-trap/signal7.tests
new file mode 100755
index 0000000..c2b1381
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/signal7.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-trap/signal_read1.right b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read1.right
new file mode 100644
index 0000000..2870a8e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read1.right
@@ -0,0 +1 @@
+Got HUP:0
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/signal_read1.tests b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read1.tests
new file mode 100755
index 0000000..1105479
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read1.tests
@@ -0,0 +1,5 @@
+(sleep 1; kill -HUP $$) &
+trap 'echo "Got HUP:$?"; exit' HUP
+while true; do
+	read ignored
+done
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/signal_read2.right b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read2.right
new file mode 100644
index 0000000..71a6bc1
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read2.right
@@ -0,0 +1,2 @@
+HUP
+Done:129
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/signal_read2.tests b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read2.tests
new file mode 100755
index 0000000..eab5b9b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/signal_read2.tests
@@ -0,0 +1,7 @@
+$THIS_SH -c '
+(sleep 1; kill -HUP $$) &
+while true; do
+	read ignored
+done
+'
+echo "Done:$?"
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/subshell.right b/busybox-1.19.3/shell/hush_test/hush-trap/subshell.right
new file mode 100644
index 0000000..f865b93
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/subshell.right
@@ -0,0 +1,21 @@
+trap -- '' HUP
+trap -- '' QUIT
+trap -- '' SYS
+Ok
+trap -- '' HUP
+trap -- '' QUIT
+trap -- '' SYS
+Ok
+trap -- '' HUP
+trap -- '' QUIT
+trap -- '' SYS
+Ok
+trap -- '' HUP
+trap -- '' QUIT
+trap -- '' SYS
+Ok
+trap -- '' HUP
+trap -- '' QUIT
+trap -- '' SYS
+TERM
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/subshell.tests b/busybox-1.19.3/shell/hush_test/hush-trap/subshell.tests
new file mode 100755
index 0000000..d877f2b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/subshell.tests
@@ -0,0 +1,19 @@
+# Non-empty traps should be reset in subshell
+
+# HUP is special in interactive shells
+trap '' HUP
+# QUIT is always special
+trap '' QUIT
+# SYS is not special
+trap '' SYS
+# WINCH is harmless
+trap 'bad: caught WINCH' WINCH
+# With TERM we'll check whether it is reset
+trap 'bad: caught TERM'  TERM
+
+(trap; "$THIS_SH" -c 'kill -HUP   $PPID'; echo Ok)
+(trap; "$THIS_SH" -c 'kill -QUIT  $PPID'; echo Ok)
+(trap; "$THIS_SH" -c 'kill -SYS   $PPID'; echo Ok)
+(trap; "$THIS_SH" -c 'kill -WINCH $PPID'; echo Ok)
+(trap; "$THIS_SH" -c 'kill -TERM  $PPID'; echo Bad: TERM is not reset)
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/usage.right b/busybox-1.19.3/shell/hush_test/hush-trap/usage.right
new file mode 100644
index 0000000..c0dbd6c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/usage.right
@@ -0,0 +1,14 @@
+___
+___
+___
+trap -- 'a' EXIT
+trap -- 'a' INT
+trap -- 'a' USR1
+trap -- 'a' USR2
+___
+___
+trap -- 'a' USR1
+trap -- 'a' USR2
+___
+___
+trap -- 'a' USR2
diff --git a/busybox-1.19.3/shell/hush_test/hush-trap/usage.tests b/busybox-1.19.3/shell/hush_test/hush-trap/usage.tests
new file mode 100755
index 0000000..d29c6e7
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-trap/usage.tests
@@ -0,0 +1,23 @@
+# no output -- default state
+echo ___
+trap
+
+# assign some traps
+echo ___
+trap "a" EXIT INT USR1 USR2
+
+# show them all
+echo ___
+trap
+
+# clear one
+echo ___
+trap 0 INT
+echo ___
+trap
+
+# clear another
+echo ___
+trap "-" USR1
+echo ___
+trap
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/empty.right b/busybox-1.19.3/shell/hush_test/hush-vars/empty.right
new file mode 100644
index 0000000..2cb3c70
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/empty.right
@@ -0,0 +1,3 @@
+a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
+a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
+a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/empty.tests b/busybox-1.19.3/shell/hush_test/hush-vars/empty.tests
new file mode 100755
index 0000000..a9c247e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/empty.tests
@@ -0,0 +1,5 @@
+e=
+
+echo a b c d e f 1 2 3 4 5 6 7 8 9 0 A B C D E F
+echo a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F
+echo $e a $e b $e c $e d $e e $e f $e 1 $e 2 $e 3 $e 4 $e 5 $e 6 $e 7 $e 8 $e 9 $e 0 $e A $e B $e C $e D $e E $e F
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/glob_and_vars.right b/busybox-1.19.3/shell/hush_test/hush-vars/glob_and_vars.right
new file mode 100644
index 0000000..3ac7ec5
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/glob_and_vars.right
@@ -0,0 +1 @@
+./glob_and_vars.right ./glob_and_vars.tests
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/glob_and_vars.tests b/busybox-1.19.3/shell/hush_test/hush-vars/glob_and_vars.tests
new file mode 100755
index 0000000..482cf9d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/glob_and_vars.tests
@@ -0,0 +1,2 @@
+v=.
+echo $v/glob_and_vars.[tr]*
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_alt.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_alt.right
new file mode 100644
index 0000000..67f18d6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_alt.right
@@ -0,0 +1,8 @@
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+__ __
+_ _ _ _ _
+_aaaa _ _ _word _word
+_ _ _ _ _
+_ _ _ _word _
+_fff _ _ _word _word
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_alt.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_alt.tests
new file mode 100755
index 0000000..3b646b1
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_alt.tests
@@ -0,0 +1,22 @@
+# first try some invalid patterns (do in subshell due to parsing error)
+"$THIS_SH" -c 'echo ${+}  ; echo moo'
+"$THIS_SH" -c 'echo ${:+} ; echo moo'
+
+# now some funky ones. (bash doesn't accept ${#+})
+echo _${#+}_ _${#:+}_
+
+# now some valid ones
+set --
+echo _$1 _${1+} _${1:+} _${1+word} _${1:+word}
+
+set -- aaaa
+echo _$1 _${1+} _${1:+} _${1+word} _${1:+word}
+
+unset f
+echo _$f _${f+} _${f:+} _${f+word} _${f:+word}
+
+f=
+echo _$f _${f+} _${f:+} _${f+word} _${f:+word}
+
+f=fff
+echo _$f _${f+} _${f:+} _${f+word} _${f:+word}
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_assign.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_assign.right
new file mode 100644
index 0000000..d5b2580
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_assign.right
@@ -0,0 +1,27 @@
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+0
+0
+hush: $1: cannot assign in this way
+hush: $1: cannot assign in this way
+hush: $1: cannot assign in this way
+hush: $1: cannot assign in this way
+_aa
+_aa
+_aa
+_aa
+_
+_
+_
+_word
+_word
+_
+_
+_
+_
+_word
+_fff
+_fff
+_fff
+_fff
+_fff
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_assign.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_assign.tests
new file mode 100755
index 0000000..149cb20
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_assign.tests
@@ -0,0 +1,38 @@
+# first try some invalid patterns (do in subshell due to parsing error)
+"$THIS_SH" -c 'echo ${=}'
+"$THIS_SH" -c 'echo ${:=}'
+
+# now some funky ones
+"$THIS_SH" -c 'echo ${#=}'
+"$THIS_SH" -c 'echo ${#:=}'
+
+# should error out
+"$THIS_SH" -c 'set --; echo _${1=}'
+"$THIS_SH" -c 'set --; echo _${1:=}'
+"$THIS_SH" -c 'set --; echo _${1=word}'
+"$THIS_SH" -c 'set --; echo _${1:=word}'
+
+# should not error
+"$THIS_SH" -c 'set aa; echo _${1=}'
+"$THIS_SH" -c 'set aa; echo _${1:=}'
+"$THIS_SH" -c 'set aa; echo _${1=word}'
+"$THIS_SH" -c 'set aa; echo _${1:=word}'
+
+# should work fine
+unset f; echo _$f
+unset f; echo _${f=}
+unset f; echo _${f:=}
+unset f; echo _${f=word}
+unset f; echo _${f:=word}
+
+f=; echo _$f
+f=; echo _${f=}
+f=; echo _${f:=}
+f=; echo _${f=word}
+f=; echo _${f:=word}
+
+f=fff; echo _$f
+f=fff; echo _${f=}
+f=fff; echo _${f:=}
+f=fff; echo _${f=word}
+f=fff; echo _${f:=word}
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_bash_substring.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_bash_substring.right
new file mode 100644
index 0000000..2f4c51d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_bash_substring.right
@@ -0,0 +1,64 @@
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+0123456789
+1    =||
+1:1  =||
+1:1:2=||
+1::2 =||
+1:1: =||
+1::  =||
+1    =|0123|
+1:1  =|123|
+1:1:2=|12|
+1::2 =|01|
+1:1: =||
+1::  =||
+f    =||
+f:1  =||
+f:1:2=||
+f::2 =||
+f:1: =||
+f::  =||
+f    =||
+f:1  =||
+f:1:2=||
+f::2 =||
+f:1: =||
+f::  =||
+f    =|a|
+f:1  =||
+f:1:2=||
+f::2 =|a|
+f:1: =||
+f::  =||
+f    =|0123456789|
+f:1  =|123456789|
+f:1:2=|12|
+f::2 =|01|
+f:1: =||
+f::  =||
+Substrings from special vars
+?    =|0|
+?:1  =||
+?:1:2=||
+?::2 =|0|
+?:1: =||
+?::  =||
+#    =|11|
+#:1  =|1|
+#:1:2=|1|
+#::2 =|11|
+#:1: =||
+#::  =||
+Substrings with expressions
+f            =|01234567|
+f:1+1:2+2    =|2345|
+f:-1:2+2     =|01234567|
+f:1:f        =|1234567|
+f:1:$f       =|1234567|
+f:1:${f}     =|1234567|
+f:1:${f:3:1} =|123|
+f:1:1`echo 1`=|1|
+Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_bash_substring.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_bash_substring.tests
new file mode 100755
index 0000000..5c9552d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_bash_substring.tests
@@ -0,0 +1,83 @@
+# first try some invalid patterns
+# do all of these in subshells since it's supposed to error out
+export var=0123456789
+"$THIS_SH" -c 'echo ${:}'
+"$THIS_SH" -c 'echo ${::}'
+"$THIS_SH" -c 'echo ${:1}'
+"$THIS_SH" -c 'echo ${::1}'
+
+#this also is not valid in bash, but we accept it:
+"$THIS_SH" -c 'echo ${var:}'
+
+# then some funky ones
+# UNFIXED BUG: this should work: "$THIS_SH" -c 'echo ${?:0}'
+
+# now some valid ones
+set --; echo "1    =|${1}|"
+set --; echo "1:1  =|${1:1}|"
+set --; echo "1:1:2=|${1:1:2}|"
+set --; echo "1::2 =|${1::2}|"
+set --; echo "1:1: =|${1:1:}|"
+set --; echo "1::  =|${1::}|"
+
+set -- 0123; echo "1    =|${1}|"
+set -- 0123; echo "1:1  =|${1:1}|"
+set -- 0123; echo "1:1:2=|${1:1:2}|"
+set -- 0123; echo "1::2 =|${1::2}|"
+set -- 0123; echo "1:1: =|${1:1:}|"
+set -- 0123; echo "1::  =|${1::}|"
+
+unset f; echo "f    =|$f|"
+unset f; echo "f:1  =|${f:1}|"
+unset f; echo "f:1:2=|${f:1:2}|"
+unset f; echo "f::2 =|${f::2}|"
+unset f; echo "f:1: =|${f:1:}|"
+unset f; echo "f::  =|${f::}|"
+
+f=; echo "f    =|$f|"
+f=; echo "f:1  =|${f:1}|"
+f=; echo "f:1:2=|${f:1:2}|"
+f=; echo "f::2 =|${f::2}|"
+f=; echo "f:1: =|${f:1:}|"
+f=; echo "f::  =|${f::}|"
+
+f=a; echo "f    =|$f|"
+f=a; echo "f:1  =|${f:1}|"
+f=a; echo "f:1:2=|${f:1:2}|"
+f=a; echo "f::2 =|${f::2}|"
+f=a; echo "f:1: =|${f:1:}|"
+f=a; echo "f::  =|${f::}|"
+
+f=0123456789; echo "f    =|$f|"
+f=0123456789; echo "f:1  =|${f:1}|"
+f=0123456789; echo "f:1:2=|${f:1:2}|"
+f=0123456789; echo "f::2 =|${f::2}|"
+f=0123456789; echo "f:1: =|${f:1:}|"
+f=0123456789; echo "f::  =|${f::}|"
+
+echo "Substrings from special vars"
+echo '?    '"=|$?|"
+echo '?:1  '"=|${?:1}|"
+echo '?:1:2'"=|${?:1:2}|"
+echo '?::2 '"=|${?::2}|"
+echo '?:1: '"=|${?:1:}|"
+echo '?::  '"=|${?::}|"
+set -- 1 2 3 4 5 6 7 8 9 10 11
+echo '#    '"=|$#|"
+echo '#:1  '"=|${#:1}|"
+echo '#:1:2'"=|${#:1:2}|"
+echo '#::2 '"=|${#::2}|"
+echo '#:1: '"=|${#:1:}|"
+echo '#::  '"=|${#::}|"
+
+echo "Substrings with expressions"
+f=01234567; echo 'f            '"=|$f|"
+f=01234567; echo 'f:1+1:2+2    '"=|${f:1+1:2+2}|"
+f=01234567; echo 'f:-1:2+2     '"=|${f:-1:2+2}|"
+f=01234567; echo 'f:1:f        '"=|${f:1:f}|"
+f=01234567; echo 'f:1:$f       '"=|${f:1:$f}|"
+f=01234567; echo 'f:1:${f}     '"=|${f:1:${f}}|"
+f=01234567; echo 'f:1:${f:3:1} '"=|${f:1:${f:3:1}}|"
+f=01234567; echo 'f:1:1`echo 1`'"=|${f:1:`echo 1`}|"
+
+echo Done
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_default.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_default.right
new file mode 100644
index 0000000..acc7172
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_default.right
@@ -0,0 +1,8 @@
+hush: syntax error: unterminated ${name}
+hush: syntax error: unterminated ${name}
+_0 _0
+_ _ _ _word _word
+_aaaa _aaaa _aaaa _aaaa _aaaa
+_ _ _ _word _word
+_ _ _ _ _word
+_fff _fff _fff _fff _fff
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_default.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_default.tests
new file mode 100755
index 0000000..1ea0517
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_default.tests
@@ -0,0 +1,22 @@
+# first try some invalid patterns (do in subshell due to parsing error)
+"$THIS_SH" -c 'echo ${-}'
+"$THIS_SH" -c 'echo ${:-}'
+
+# now some funky ones
+echo _${#-} _${#:-}
+
+# now some valid ones
+set --
+echo _$1 _${1-} _${1:-} _${1-word} _${1:-word}
+
+set -- aaaa
+echo _$1 _${1-} _${1:-} _${1-word} _${1:-word}
+
+unset f
+echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
+
+f=
+echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
+
+f=fff
+echo _$f _${f-} _${f:-} _${f-word} _${f:-word}
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_indicate_error.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_indicate_error.right
new file mode 100644
index 0000000..06fcc51
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_indicate_error.right
@@ -0,0 +1,43 @@
+hush: syntax error: unterminated ${name}
+0
+0
+====
+_
+hush: 1: parameter null or not set
+hush: 1: parameter null or not set
+hush: 1: message1
+hush: 1: message1
+hush: 1: unset!
+hush: 1: null or unset!
+====
+_aaaa
+_aaaa
+_aaaa
+_aaaa
+_aaaa
+_aaaa
+_aaaa
+====
+_
+hush: f: parameter null or not set
+hush: f: parameter null or not set
+hush: f: message3
+hush: f: message3
+hush: f: unset!
+hush: f: null or unset!
+====
+_
+_
+hush: f: parameter null or not set
+_
+hush: f: message4
+_
+hush: f: null or unset!
+====
+_fff
+_fff
+_fff
+_fff
+_fff
+_fff
+_fff
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_indicate_error.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_indicate_error.tests
new file mode 100755
index 0000000..be14b1e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_indicate_error.tests
@@ -0,0 +1,60 @@
+# do all of these in subshells since it's supposed to error out
+
+# first try some invalid patterns
+#"$THIS_SH" -c 'echo ${?}' -- this is valid as it's the same as $?
+"$THIS_SH" -c 'echo ${:?}'
+
+# then some funky ones
+# note: bash prints 1 - treats it as "length of $#"? We print 0
+"$THIS_SH" -c 'echo ${#?}'
+# bash prints 0
+"$THIS_SH" -c 'echo ${#:?}'
+
+# now some valid ones
+export msg_unset="unset!"
+export msg_null_or_unset="null or unset!"
+
+echo ====
+"$THIS_SH" -c 'set --; echo _$1'
+"$THIS_SH" -c 'set --; echo _${1?}'
+"$THIS_SH" -c 'set --; echo _${1:?}'
+"$THIS_SH" -c 'set --; echo _${1?message1}'
+"$THIS_SH" -c 'set --; echo _${1:?message1}'
+"$THIS_SH" -c 'set --; echo _${1?$msg_unset}'
+"$THIS_SH" -c 'set --; echo _${1:?$msg_null_or_unset}'
+
+echo ====
+"$THIS_SH" -c 'set -- aaaa; echo _$1'
+"$THIS_SH" -c 'set -- aaaa; echo _${1?}'
+"$THIS_SH" -c 'set -- aaaa; echo _${1:?}'
+"$THIS_SH" -c 'set -- aaaa; echo _${1?word}'
+"$THIS_SH" -c 'set -- aaaa; echo _${1:?word}'
+"$THIS_SH" -c 'set -- aaaa; echo _${1?$msg_unset}'
+"$THIS_SH" -c 'set -- aaaa; echo _${1:?$msg_null_or_unset}'
+
+echo ====
+"$THIS_SH" -c 'unset f; echo _$f'
+"$THIS_SH" -c 'unset f; echo _${f?}'
+"$THIS_SH" -c 'unset f; echo _${f:?}'
+"$THIS_SH" -c 'unset f; echo _${f?message3}'
+"$THIS_SH" -c 'unset f; echo _${f:?message3}'
+"$THIS_SH" -c 'unset f; echo _${f?$msg_unset}'
+"$THIS_SH" -c 'unset f; echo _${f:?$msg_null_or_unset}'
+
+echo ====
+"$THIS_SH" -c 'f=; echo _$f'
+"$THIS_SH" -c 'f=; echo _${f?}'
+"$THIS_SH" -c 'f=; echo _${f:?}'
+"$THIS_SH" -c 'f=; echo _${f?word}'
+"$THIS_SH" -c 'f=; echo _${f:?message4}'
+"$THIS_SH" -c 'f=; echo _${f?$msg_unset}'
+"$THIS_SH" -c 'f=; echo _${f:?$msg_null_or_unset}'
+
+echo ====
+"$THIS_SH" -c 'f=fff; echo _$f'
+"$THIS_SH" -c 'f=fff; echo _${f?}'
+"$THIS_SH" -c 'f=fff; echo _${f:?}'
+"$THIS_SH" -c 'f=fff; echo _${f?word}'
+"$THIS_SH" -c 'f=fff; echo _${f:?word}'
+"$THIS_SH" -c 'f=fff; echo _${f?$msg_unset}'
+"$THIS_SH" -c 'f=fff; echo _${f:?$msg_null_or_unset}'
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_len.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_len.right
new file mode 100644
index 0000000..96e8cb5
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_len.right
@@ -0,0 +1,9 @@
+0
+0
+1
+Make sure len parsing doesnt break arg count
+0 0
+4 4
+Testing len op
+4 3 2 1 0 0
+0 3 0
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_len.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_len.tests
new file mode 100755
index 0000000..fe20a45
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_expand_len.tests
@@ -0,0 +1,17 @@
+"$THIS_SH" -c 'echo $#'
+"$THIS_SH" -c 'echo $#' arg0
+"$THIS_SH" -c 'echo $#' arg0 arg1
+
+echo Make sure len parsing doesnt break arg count
+set --
+echo $# ${#}
+set -- aaaa bbb cc d
+echo $# ${#}
+
+echo Testing len op
+echo ${#1} ${#2} ${#3} ${#4} ${#5} ${#6}
+
+unset e
+f=abc
+g=
+echo ${#e} ${#f} ${#g}
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_glob.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_glob.right
new file mode 100644
index 0000000..bdee8fe
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_glob.right
@@ -0,0 +1,4 @@
+param_glob.tests
+param_glob.tests
+param_glob.t*
+param_glob.t*
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_glob.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_glob.tests
new file mode 100755
index 0000000..4d74fee
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_glob.tests
@@ -0,0 +1,9 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" 'param_glob.t*'
+    echo NOT SHOWN
+    exit
+fi
+echo $*
+echo $@
+echo "$*"
+echo "$@"
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_subshell.right b/busybox-1.19.3/shell/hush_test/hush-vars/param_subshell.right
new file mode 100644
index 0000000..f3c3767
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_subshell.right
@@ -0,0 +1,7 @@
+1=1
+2=2
+3=3
+4=4
+5=5
+6=6
+7=7
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/param_subshell.tests b/busybox-1.19.3/shell/hush_test/hush-vars/param_subshell.tests
new file mode 100755
index 0000000..27fdc5b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/param_subshell.tests
@@ -0,0 +1,15 @@
+if test $# = 0; then
+    "$THIS_SH" "$0" 1 2 3 4 5 6 7 8 9
+    exit
+fi
+echo 1=$1
+{ echo 2=$2; }
+{ echo 3=$3; } &
+# cant use usleep as it isnt standard in $PATH --
+# we fail when testing busybox compiled solely as "hush"
+wait
+( echo 4=$4 )
+( echo 5=$5 ) &
+wait
+true | echo 6=$6 | cat
+true | { echo 7=$7; } | cat
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/star.right b/busybox-1.19.3/shell/hush_test/hush-vars/star.right
new file mode 100644
index 0000000..0ecc55b
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/star.right
@@ -0,0 +1,6 @@
+.1.
+.abc.
+.d.
+.e.
+.f.
+.1 abc d e f.
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/star.tests b/busybox-1.19.3/shell/hush_test/hush-vars/star.tests
new file mode 100755
index 0000000..5554c40
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/star.tests
@@ -0,0 +1,8 @@
+if test $# = 0; then
+    exec "$THIS_SH" star.tests 1 abc 'd e f'
+fi
+# 'd e f' should be split into 3 separate args:
+for a in $*; do echo ".$a."; done
+
+# must produce .1 abc d e f.
+for a in "$*"; do echo ".$a."; done
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/unset.right b/busybox-1.19.3/shell/hush_test/hush-vars/unset.right
new file mode 100644
index 0000000..1fbe76a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/unset.right
@@ -0,0 +1,18 @@
+0
+unset: invalid option -- m
+1
+0
+___
+0 f g
+0 g
+0
+___
+0 f g
+0
+0 f g
+0
+___
+hush: HUSH_VERSION: readonly variable
+1 f g
+hush: HUSH_VERSION: readonly variable
+1
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/unset.tests b/busybox-1.19.3/shell/hush_test/hush-vars/unset.tests
new file mode 100755
index 0000000..f59ce59
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/unset.tests
@@ -0,0 +1,36 @@
+# check invalid options are rejected
+unset -
+echo $?
+unset -m a b c
+echo $?
+
+# check funky usage
+unset
+echo $?
+
+# check normal usage
+echo ___
+f=f g=g
+echo $? $f $g
+unset f
+echo $? $f $g
+unset g
+echo $? $f $g
+
+echo ___
+f=f g=g
+echo $? $f $g
+unset f g
+echo $? $f $g
+f=f g=g
+echo $? $f $g
+unset -v f g
+echo $? $f $g
+
+# check read only vars
+echo ___
+f=f g=g
+unset HUSH_VERSION
+echo $? $f $g
+unset f HUSH_VERSION g
+echo $? $f $g
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var1.right b/busybox-1.19.3/shell/hush_test/hush-vars/var1.right
new file mode 100644
index 0000000..194e7db
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var1.right
@@ -0,0 +1,4 @@
+http://busybox.net
+http://busybox.net_abc
+1 1
+1 1
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var1.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var1.tests
new file mode 100755
index 0000000..48a6782
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var1.tests
@@ -0,0 +1,9 @@
+URL=http://busybox.net
+
+echo $URL
+echo ${URL}_abc
+
+true
+false; echo $? ${?}
+true
+{ false; echo $? ${?}; }
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var2.right b/busybox-1.19.3/shell/hush_test/hush-vars/var2.right
new file mode 100644
index 0000000..40bf4bf
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var2.right
@@ -0,0 +1,2 @@
+http://busybox.net
+http://busybox.net_abc
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var2.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var2.tests
new file mode 100755
index 0000000..1292410
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var2.tests
@@ -0,0 +1,4 @@
+_1=http://busybox.net
+
+echo $_1
+echo ${_1}_abc
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var3.right b/busybox-1.19.3/shell/hush_test/hush-vars/var3.right
new file mode 100644
index 0000000..40e67fd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var3.right
@@ -0,0 +1,2 @@
+hush: invalid number '1q'
+hush: syntax error: unterminated ${name}
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var3.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var3.tests
new file mode 100755
index 0000000..aea36d6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var3.tests
@@ -0,0 +1,4 @@
+# reject invalid vars
+"$THIS_SH" -c 'echo ${1q}'
+"$THIS_SH" -c 'echo ${&}'
+#"$THIS_SH" -c 'echo ${$}' -- this is valid as it's the same as $$
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_bash1.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash1.right
new file mode 100644
index 0000000..c0a0769
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash1.right
@@ -0,0 +1,14 @@
+
+
+f
+bcdef
+abcdef
+abcdef
+bcde
+abcd
+abcd
+abcdef
+bcdef
+abcdef
+abcdef
+abcdef
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_bash1.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash1.tests
new file mode 100755
index 0000000..24d3c9a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash1.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash2.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash2.right
new file mode 100644
index 0000000..acba5c6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash2.right
@@ -0,0 +1,10 @@
+abc123xcba123
+abx123dcba123
+abx123dxba123
+abcx23dcba123
+abcxxxdcbaxxx
+abx
+xba123
+abx23
+abc23dcba123
+abcdcba
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_bash2.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash2.tests
new file mode 100755
index 0000000..29c526c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash2.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash3.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash3.right
new file mode 100644
index 0000000..a97c850
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash3.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash3.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash3.tests
new file mode 100755
index 0000000..146dbb6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash3.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash4.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash4.right
new file mode 100644
index 0000000..0ef1bf6
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash4.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash4.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash4.tests
new file mode 100755
index 0000000..32aa2b3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash4.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash5.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash5.right
new file mode 100644
index 0000000..1990902
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash5.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash5.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash5.tests
new file mode 100755
index 0000000..5748b4a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash5.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_bash6.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash6.right
new file mode 100644
index 0000000..63fc23d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash6.right
@@ -0,0 +1,5 @@
+Expected Actual
+a*z    : a*z
+\z     : \z
+a1z a2z: a1z a2z
+z      : z
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_bash6.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash6.tests
new file mode 100755
index 0000000..cf2e4f0
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_bash6.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_assign.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_assign.right
new file mode 100644
index 0000000..352210d
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_assign.right
@@ -0,0 +1,5 @@
+. .
+.abc d e.
+.abc d e.
+.abc d e.
+.abc d e.
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_assign.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_assign.tests
new file mode 100755
index 0000000..18cdc74
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_assign.tests
@@ -0,0 +1,15 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" abc "d e"
+fi
+
+space=' '
+echo .$space.
+
+a=$*
+echo .$a.
+a=$@
+echo .$a.
+a="$*"
+echo .$a.
+a="$@"
+echo .$a.
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_redir.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_redir.right
new file mode 100644
index 0000000..423299c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_redir.right
@@ -0,0 +1,3 @@
+TEST1
+TEST2
+TEST3
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_redir.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_redir.tests
new file mode 100755
index 0000000..bda6bdd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_in_redir.tests
@@ -0,0 +1,13 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" abc "d e"
+fi
+
+echo TEST1 >"$1.out"
+echo TEST2 >"$2.out"
+# bash says: "$@.out": ambiguous redirect
+# ash handles it as if it is '$*' - we do the same
+echo TEST3 >"$@.out"
+
+cat abc.out "d e.out" "abc d e.out"
+
+rm abc.out "d e.out" "abc d e.out"
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_on_ifs.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_on_ifs.right
new file mode 100644
index 0000000..2ed2069
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_on_ifs.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_on_ifs.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_on_ifs.tests
new file mode 100755
index 0000000..a12ff8e
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_expand_on_ifs.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_in_pipes.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_in_pipes.right
new file mode 100644
index 0000000..faf65be
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_in_pipes.right
@@ -0,0 +1,6 @@
+b=1
+b=2
+b=3
+b=4
+b=5
+b=6
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_in_pipes.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_in_pipes.tests
new file mode 100755
index 0000000..3f8cd27
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_in_pipes.tests
@@ -0,0 +1,7 @@
+b=1 env | grep ^b=
+true | b=2 env | grep ^b=
+a=1 true | b=3 env | grep ^b=
+
+(b=4 env) | grep ^b=
+(true | b=5 env) | grep ^b=
+(a=1 true | b=6 env) | grep ^b=
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_leaks.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_leaks.right
new file mode 100644
index 0000000..d86bac9
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_leaks.right
@@ -0,0 +1 @@
+OK
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_leaks.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_leaks.tests
new file mode 100755
index 0000000..27c8c65
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_leaks.tests
@@ -0,0 +1,14 @@
+# external program
+a=b /bin/true
+env | grep ^a=
+
+# builtin
+a=b true
+env | grep ^a=
+
+# exec with redirection only
+# in bash, this leaks!
+a=b exec 1>&1
+env | grep ^a=
+
+echo OK
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_posix1.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_posix1.right
new file mode 100644
index 0000000..7ff618a
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_posix1.right
@@ -0,0 +1,41 @@
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+Empty:
+abcdcd
+abcdcd
+abcdcd
+cdcd
+babcdcd
+babcdcd
+ababcdcd
+Empty:
+ababcdcd}_tail
+ababcdcd_tail
+ababcd
+ababcd
+ababcd
+abab
+ababcdc
+ababcdc
+ababcdcd
+Empty:
+ababcdcd}_tail
+ababcdcd_tail
+ababcdcd
+ab
+ab
+ab
+End
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_posix1.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_posix1.tests
new file mode 100755
index 0000000..82abe81
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_posix1.tests
@@ -0,0 +1,52 @@
+unset var
+
+echo Empty:${var#}
+echo Empty:${var##}
+echo Empty:${var#*}
+echo Empty:${var##*}
+echo Empty:${var%}
+echo Empty:${var%%}
+echo Empty:${var%*}
+echo Empty:${var%%*}
+
+var=
+
+echo Empty:${var#}
+echo Empty:${var##}
+echo Empty:${var#*}
+echo Empty:${var##*}
+echo Empty:${var%}
+echo Empty:${var%%}
+echo Empty:${var%*}
+echo Empty:${var%%*}
+
+var=ababcdcd
+
+echo ${var#ab}
+echo ${var##ab}
+echo ${var#a*b}
+echo ${var##a*b}
+echo ${var#?}
+echo ${var##?}
+echo ${var#*}
+echo Empty:${var##*}
+echo ${var#}}_tail
+echo ${var#\}}_tail
+
+echo ${var%cd}
+echo ${var%%cd}
+echo ${var%c*d}
+echo ${var%%c*d}
+echo ${var%?}
+echo ${var%%?}
+echo ${var%*}
+echo Empty:${var%%*}
+echo ${var#}}_tail
+echo ${var#\}}_tail
+echo ${var%\\*}
+
+a=ab}; echo ${a%\}};
+a=abc; c=c; echo ${a%${c}}
+a=ab{{c; echo ${a%`echo {{c`}
+
+echo End
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_preserved.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_preserved.right
new file mode 100644
index 0000000..2a9917c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_preserved.right
@@ -0,0 +1,4 @@
+a=b
+a=b
+a=b
+OK
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_preserved.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_preserved.tests
new file mode 100755
index 0000000..1bddd87
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_preserved.tests
@@ -0,0 +1,16 @@
+export a=b
+
+# external program
+a=c /bin/true
+env | grep ^a=
+
+# builtin
+a=d true
+env | grep ^a=
+
+# exec with redirection only
+# in bash, this leaks!
+a=e exec 1>&1
+env | grep ^a=
+
+echo OK
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_serial.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_serial.right
new file mode 100644
index 0000000..42aa330
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_serial.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_serial.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_serial.tests
new file mode 100755
index 0000000..6b4a4cd
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_serial.tests
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_subst_in_for.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_subst_in_for.right
new file mode 100644
index 0000000..c8aca1c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_subst_in_for.right
@@ -0,0 +1,40 @@
+Testing: in x y z
+.x.
+.y.
+.z.
+Testing: in u $empty v
+.u.
+.v.
+Testing: in u " $empty" v
+.u.
+. .
+.v.
+Testing: in u $empty $empty$a v
+.u.
+.a.
+.v.
+Testing: in $a_b
+.a.
+.b.
+Testing: in $*
+.abc.
+.d.
+.e.
+Testing: in $@
+.abc.
+.d.
+.e.
+Testing: in -$*-
+.-abc.
+.d.
+.e-.
+Testing: in -$@-
+.-abc.
+.d.
+.e-.
+Testing: in $a_b -$a_b-
+.a.
+.b.
+.-a.
+.b-.
+Finished
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_subst_in_for.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_subst_in_for.tests
new file mode 100755
index 0000000..433c606
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_subst_in_for.tests
@@ -0,0 +1,40 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" abc "d e"
+fi
+
+echo 'Testing: in x y z'
+for a in x y z; do echo ".$a."; done
+
+echo 'Testing: in u $empty v'
+empty=''
+for a in u $empty v; do echo ".$a."; done
+
+echo 'Testing: in u " $empty" v'
+empty=''
+for a in u " $empty" v; do echo ".$a."; done
+
+echo 'Testing: in u $empty $empty$a v'
+a='a'
+for a in u $empty $empty$a v; do echo ".$a."; done
+
+echo 'Testing: in $a_b'
+a_b='a b'
+for a in $a_b; do echo ".$a."; done
+
+echo 'Testing: in $*'
+for a in $*; do echo ".$a."; done
+
+echo 'Testing: in $@'
+for a in $@; do echo ".$a."; done
+
+echo 'Testing: in -$*-'
+for a in -$*-; do echo ".$a."; done
+
+echo 'Testing: in -$@-'
+for a in -$@-; do echo ".$a."; done
+
+echo 'Testing: in $a_b -$a_b-'
+a_b='a b'
+for a in $a_b -$a_b-; do echo ".$a."; done
+
+echo Finished
diff --git a/busybox-1.19.3/shell/hush_test/hush-vars/var_unbackslash.right b/busybox-1.19.3/shell/hush_test/hush-vars/var_unbackslash.right
new file mode 100644
index 0000000..8bc8347
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_unbackslash.right
@@ -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/busybox-1.19.3/shell/hush_test/hush-vars/var_unbackslash.tests b/busybox-1.19.3/shell/hush_test/hush-vars/var_unbackslash.tests
new file mode 100755
index 0000000..bb52af3
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-vars/var_unbackslash.tests
@@ -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: $?"
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all1.right b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all1.right
new file mode 100644
index 0000000..c6f0334
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all1.right
@@ -0,0 +1,3 @@
+Warm up
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all1.tests b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all1.tests
new file mode 100755
index 0000000..f8207cf
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all1.tests
@@ -0,0 +1,148 @@
+# "Check many leaks" test #1
+# Cramming all kinds of weird commands in here.
+# As you find leaks, please create separate, small test
+# for each leak.
+# Narrowing down the leak using this large test may be difficult.
+# It is intended to be a blanket "is everything ok?" test
+
+echo "Warm up"
+i=1
+l=1
+t=1
+export t
+while test $i != 99; do
+    t=value1_$i; t=value2_$i true; t=value3_$i /bin/true; t=value4_$i exec 1>&1
+    { t=value3_$i /bin/true; } </dev/null
+    if true; t=valueA_$i false >>/dev/null; true; then
+	: << HERE >/dev/null; true <<HERE
+Hello builtin :
+HERE
+Hello $i true
+HERE
+    elif false; then
+	true; echo Doesnt run
+    else
+	{ true; }; echo Doesnt run too >>/foo/bar
+    fi
+    { : /bin/*; }
+    unset var
+    echo >/dev/null ${var#}
+    echo >/dev/null ${var##}
+    echo >/dev/null ${var#*}
+    echo >/dev/null ${var##*}
+    echo >/dev/null ${var%}
+    echo >/dev/null ${var%%}
+    echo >/dev/null ${var%*}
+    echo >/dev/null ${var%%*}
+    var=
+    echo >/dev/null ${var#}
+    echo >/dev/null ${var##}
+    echo >/dev/null ${var#*}
+    echo >/dev/null ${var##*}
+    echo >/dev/null ${var%}
+    echo >/dev/null ${var%%}
+    echo >/dev/null ${var%*}
+    echo >/dev/null ${var%%*}
+    var=ababcdcd
+    echo >/dev/null ${var#ab}
+    echo >/dev/null ${var##ab}
+    echo >/dev/null ${var#a*b}
+    echo >/dev/null ${var##a*b}
+    echo >/dev/null ${var#?}
+    echo >/dev/null ${var##?}
+    echo >/dev/null ${var#*}
+    echo >/dev/null ${var##*}
+    echo >/dev/null ${var%cd}
+    echo >/dev/null ${var%%cd}
+    echo >/dev/null ${var%c*d}
+    echo >/dev/null ${var%%c*d}
+    echo >/dev/null ${var%?}
+    echo >/dev/null ${var%%?}
+    echo >/dev/null ${var%*}
+    echo >/dev/null ${var%%*}
+    set -- par1_$i par2_$i par3_$i par4_$i
+    trap "echo trap$i" WINCH
+    f() { true; true; true; true; true; true; true; true; }
+    f() { true; true; true; true; true; true; true; true; echo $1; }
+    i=iii$i t=ttt$i z=zzz$i f >/dev/null
+    : $((i++))
+done
+unset i l t
+unset -f f
+
+memleak
+
+echo "Measuring memory leak..."
+# Please copy the entire block from above verbatim
+i=1
+l=1
+t=1
+export t
+while test $i != 99; do
+    t=value1_$i; t=value2_$i true; t=value3_$i /bin/true; t=value4_$i exec 1>&1
+    { t=value3_$i /bin/true; } </dev/null
+    if true; t=valueA_$i false >>/dev/null; true; then
+	: << HERE >/dev/null; true <<HERE
+Hello builtin :
+HERE
+Hello $i true
+HERE
+    elif false; then
+	true; echo Doesnt run
+    else
+	{ true; }; echo Doesnt run too >>/foo/bar
+    fi
+    { : /bin/*; }
+    unset var
+    echo >/dev/null ${var#}
+    echo >/dev/null ${var##}
+    echo >/dev/null ${var#*}
+    echo >/dev/null ${var##*}
+    echo >/dev/null ${var%}
+    echo >/dev/null ${var%%}
+    echo >/dev/null ${var%*}
+    echo >/dev/null ${var%%*}
+    var=
+    echo >/dev/null ${var#}
+    echo >/dev/null ${var##}
+    echo >/dev/null ${var#*}
+    echo >/dev/null ${var##*}
+    echo >/dev/null ${var%}
+    echo >/dev/null ${var%%}
+    echo >/dev/null ${var%*}
+    echo >/dev/null ${var%%*}
+    var=ababcdcd
+    echo >/dev/null ${var#ab}
+    echo >/dev/null ${var##ab}
+    echo >/dev/null ${var#a*b}
+    echo >/dev/null ${var##a*b}
+    echo >/dev/null ${var#?}
+    echo >/dev/null ${var##?}
+    echo >/dev/null ${var#*}
+    echo >/dev/null ${var##*}
+    echo >/dev/null ${var%cd}
+    echo >/dev/null ${var%%cd}
+    echo >/dev/null ${var%c*d}
+    echo >/dev/null ${var%%c*d}
+    echo >/dev/null ${var%?}
+    echo >/dev/null ${var%%?}
+    echo >/dev/null ${var%*}
+    echo >/dev/null ${var%%*}
+    set -- par1_$i par2_$i par3_$i par4_$i
+    trap "echo trap$i" WINCH
+    f() { true; true; true; true; true; true; true; true; }
+    f() { true; true; true; true; true; true; true; true; echo $1; }
+    i=iii$i t=ttt$i z=zzz$i f >/dev/null
+    : $((i++))
+done
+unset i l t
+unset -f f
+
+
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok #$kb
+else
+    echo "Bad: $kb kb leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all2.right b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all2.right
new file mode 100644
index 0000000..c6f0334
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all2.right
@@ -0,0 +1,3 @@
+Warm up
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all2.tests b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all2.tests
new file mode 100755
index 0000000..d51ea80
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_all2.tests
@@ -0,0 +1,93 @@
+# "Check many leaks" test #2
+# Cramming all kinds of weird commands in here.
+# As you find leaks, please create separate, small test
+# for each leak.
+# Narrowing down the leak using this large test may be difficult.
+# It is intended to be a blanket "is everything ok?" test
+
+echo "Warm up"
+local_var="local val"
+export dev_null="/dev/null"
+>$dev_null
+echo hi1 $local_var `echo ho` >>/dev/null
+echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null
+{ echo hi4 $local_var `echo ho` 1<>/dev/null; }
+( echo hi4 $local_var `echo ho` 1<>/dev/null )
+if echo $local_var; false
+    then echo not run
+    elif false <$dev_null
+    then none
+    else cat 0<>$dev_null 1<>"$dev_null"
+fi >>/dev/null
+{
+    if echo $local_var; then cat <<HERE
+Hi cat
+HERE
+    fi >>/dev/null
+} 1<>/dev/null
+while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done
+( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null
+f() { echo $1; }
+f >/dev/null
+
+memleak
+
+echo "Measuring memory leak..."
+# Please copy the entire block from above verbatim
+local_var="local val"
+export dev_null="/dev/null"
+>$dev_null
+echo hi1 $local_var `echo ho` >>/dev/null
+echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null
+{ echo hi4 $local_var `echo ho` 1<>/dev/null; }
+( echo hi4 $local_var `echo ho` 1<>/dev/null )
+if echo $local_var; false
+    then echo not run
+    elif false <$dev_null
+    then none
+    else cat 0<>$dev_null 1<>"$dev_null"
+fi >>/dev/null
+{
+    if echo $local_var; then cat <<HERE
+Hi cat
+HERE
+    fi >>/dev/null
+} 1<>/dev/null
+while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done
+( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null
+f() { echo $1; }
+f >/dev/null
+
+# And same again
+
+local_var="local val"
+export dev_null="/dev/null"
+>$dev_null
+echo hi1 $local_var `echo ho` >>/dev/null
+echo hi2 $local_var </dev/null | echo 2>&- | cat 1<>/dev/null
+{ echo hi4 $local_var `echo ho` 1<>/dev/null; }
+( echo hi4 $local_var `echo ho` 1<>/dev/null )
+if echo $local_var; false
+    then echo not run
+    elif false <$dev_null
+    then none
+    else cat 0<>$dev_null 1<>"$dev_null"
+fi >>/dev/null
+{
+    if echo $local_var; then cat <<HERE
+Hi cat
+HERE
+    fi >>/dev/null
+} 1<>/dev/null
+while { echo $dev_null >>$dev_null; }; do cat <"$dev_null"; break; done
+( until { echo $dev_null >>$dev_null | false; }; do cat <"$dev_null"; break; done ) <$dev_null
+f() { echo $1; }
+f >/dev/null
+
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok #$kb
+else
+    echo "Bad: $kb kb (or more) leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_empty_tick.right b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_empty_tick.right
new file mode 100644
index 0000000..c6f0334
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_empty_tick.right
@@ -0,0 +1,3 @@
+Warm up
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_empty_tick.tests b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_empty_tick.tests
new file mode 100755
index 0000000..ae37579
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_empty_tick.tests
@@ -0,0 +1,28 @@
+echo "Warm up"
+i=1
+while test $i != 9; do
+    `true`
+    : $((i++))
+done
+
+memleak
+
+echo "Measuring memory leak..."
+i=1
+while test $i != 199; do
+    `true`
+    : $((i++))
+done
+i=1
+while test $i != 199; do
+    `true`
+    : $((i++))
+done
+
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok #$kb
+else
+    echo "Bad: $kb kb (or more) leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_heredoc1.right b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_heredoc1.right
new file mode 100644
index 0000000..c6f0334
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_heredoc1.right
@@ -0,0 +1,3 @@
+Warm up
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_heredoc1.tests b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_heredoc1.tests
new file mode 100755
index 0000000..26cbb28
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_heredoc1.tests
@@ -0,0 +1,34 @@
+echo "Warm up"
+i=1
+while test $i != 99; do
+    : <<HERE
+Hello $i `echo builtin_$i`
+HERE
+    : $((i++))
+done
+
+memleak
+
+echo "Measuring memory leak..."
+i=1
+while test $i != 99; do
+    : <<HERE
+Hello $i `echo builtin_$i`
+HERE
+    : $((i++))
+done
+i=1
+while test $i != 99; do
+    : <<HERE
+Hello $i `echo builtin_$i`
+HERE
+    : $((i++))
+done
+
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok #$kb
+else
+    echo "Bad: $kb kb (or more) leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var.right b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var.right
new file mode 100644
index 0000000..1d4d6ff
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var.right
@@ -0,0 +1,2 @@
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var.tests b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var.tests
new file mode 100755
index 0000000..41c09e4
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var.tests
@@ -0,0 +1,47 @@
+echo "Measuring memory leak..."
+i=1
+while test $i != X; do
+    unset t
+    t=111111111111111111111111111111111111111111111111111111111111111111111111
+    export t
+    unset t
+    t=111111111111111111111111111111111111111111111111111111111111111111111111
+    export t
+    unset t
+    t=111111111111111111111111111111111111111111111111111111111111111111111111
+    export t
+    unset t
+    t=111111111111111111111111111111111111111111111111111111111111111111111111
+    export t
+    unset t
+    t=111111111111111111111111111111111111111111111111111111111111111111111111
+    export t
+    i=1$i
+    if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi
+    if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi
+    if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi
+    if test $i = 1111111111111111111111111111111111111111111114; then i=5; fi
+    if test $i = 1111111111111111111111111111111111111111111115; then i=6; fi
+    if test $i = 1111111111111111111111111111111111111111111116; then i=7; fi
+    if test $i = 1111111111111111111111111111111111111111111117; then i=8; fi
+    if test $i = 1111111111111111111111111111111111111111111118; then i=9; fi
+    if test $i = 1111111111111111111111111111111111111111111119; then i=a; fi
+    if test $i = 111111111111111111111111111111111111111111111a; then i=b; fi
+    if test $i = 111111111111111111111111111111111111111111111b; then i=c; fi
+    if test $i = 111111111111111111111111111111111111111111111c; then i=d; fi
+    if test $i = 111111111111111111111111111111111111111111111d; then i=e; fi
+    if test $i = 111111111111111111111111111111111111111111111e; then i=f; fi
+    if test $i = 111111111111111111111111111111111111111111111f; then i=g; fi
+    if test $i = 111111111111111111111111111111111111111111111g; then i=h; fi
+    if test $i = 111111111111111111111111111111111111111111111h; then i=i; fi
+    if test $i = 111111111111111111111111111111111111111111111i; then i=j; fi
+    if test $i = 111111111111111111111111111111111111111111111j; then i=X; fi
+    memleak
+done
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok
+else
+    echo "Bad: $kb kb (or more) leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var2.right b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var2.right
new file mode 100644
index 0000000..c6f0334
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var2.right
@@ -0,0 +1,3 @@
+Warm up
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var2.tests b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var2.tests
new file mode 100755
index 0000000..611666c
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var2.tests
@@ -0,0 +1,43 @@
+echo "Warm up"
+t=1
+export t
+i=1
+while test $i != X; do
+    t=111111111111111111111111111111111111111111111111111111111111111111111110$i
+    t=111111111111111111111111111111111111111111111111111111111111111111111111$i true
+    t=111111111111111111111111111111111111111111111111111111111111111111111112$i /bin/true
+    t=111111111111111111111111111111111111111111111111111111111111111111111113$i exec 1>&1
+    i=1$i
+    if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi
+    if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi
+    if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi
+    if test $i = 1111111111111111111111111111111111111111111114; then i=X; fi
+done
+unset t i
+
+memleak
+
+echo "Measuring memory leak..."
+t=1
+export t
+i=1
+while test $i != X; do
+    t=111111111111111111111111111111111111111111111111111111111111111111111110$i
+    t=111111111111111111111111111111111111111111111111111111111111111111111111$i true
+    t=111111111111111111111111111111111111111111111111111111111111111111111112$i /bin/true
+    t=111111111111111111111111111111111111111111111111111111111111111111111113$i exec 1>&1
+    i=1$i
+    if test $i = 1111111111111111111111111111111111111111111111; then i=2; fi
+    if test $i = 1111111111111111111111111111111111111111111112; then i=3; fi
+    if test $i = 1111111111111111111111111111111111111111111113; then i=4; fi
+    if test $i = 1111111111111111111111111111111111111111111114; then i=X; fi
+done
+unset t i
+
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok
+else
+    echo "Bad: $kb kb (or more) leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var3.right b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var3.right
new file mode 100644
index 0000000..c6f0334
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var3.right
@@ -0,0 +1,3 @@
+Warm up
+Measuring memory leak...
+Ok
diff --git a/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var3.tests b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var3.tests
new file mode 100755
index 0000000..9554c42
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/hush-z_slow/leak_var3.tests
@@ -0,0 +1,41 @@
+# Was seen leaking on NOMMU build
+
+echo "Warm up"
+i=1; t=1; export t
+while test $i != 400; do
+    t=valueA_$i true
+    : $((i++))
+done
+
+memleak
+echo "Measuring memory leak..."
+
+# Please copy the entire block from above verbatim
+i=1; t=1; export t
+while test $i != 400; do
+    t=valueA_$i true
+    : $((i++))
+done
+i=1; t=1; export t
+while test $i != 400; do
+    t=valueA_$i true
+    : $((i++))
+done
+i=1; t=1; export t
+while test $i != 400; do
+    t=valueA_$i true
+    : $((i++))
+done
+i=1; t=1; export t
+while test $i != 400; do
+    t=valueA_$i true
+    : $((i++))
+done
+
+memleak
+kb=$?
+if test $kb -le 4; then
+    echo Ok #$kb
+else
+    echo "Bad: $kb kb (or more) leaked"
+fi
diff --git a/busybox-1.19.3/shell/hush_test/run-all b/busybox-1.19.3/shell/hush_test/run-all
new file mode 100755
index 0000000..64a7abc
--- /dev/null
+++ b/busybox-1.19.3/shell/hush_test/run-all
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+unset LANG LANGUAGE
+unset LC_COLLATE
+unset LC_CTYPE
+unset LC_MONETARY
+unset LC_MESSAGES
+unset LC_NUMERIC
+unset LC_TIME
+unset LC_ALL
+
+if test ! -x hush; then
+	if test ! -x ../../busybox; then
+		echo "Can't run tests. Put hush binary into this directory (`pwd`)"
+		exit 1
+	fi
+	echo "No ./hush - creating a link to ../../busybox"
+	ln -s ../../busybox hush
+fi
+if test ! -f .config; then
+	if test ! -f ../../.config; then
+		echo "Missing .config file"
+		exit 1
+	fi
+	cp ../../.config . || exit 1
+fi
+
+eval $(sed -e '/^#/d' -e '/^$/d' -e 's:^:export :' .config)
+
+PATH="`pwd`:$PATH" # for hush and recho/zecho/printenv
+export PATH
+
+THIS_SH="`pwd`/hush"
+export THIS_SH
+
+do_test()
+{
+	test -d "$1" || return 0
+	d=${d%/}
+#	echo Running tests in directory "$1"
+	(
+	tret=0
+	cd "$1" || { echo "cannot cd $1!"; exit 1; }
+	for x in run-*; do
+		test -f "$x" || continue
+		case "$x" in
+			"$0"|run-minimal|run-gprof) ;;
+			*.orig|*~) ;;
+			#*) echo $x ; sh $x ;;
+			*)
+			echo -n "$1/$x:"
+			sh "$x" >"../$1-$x.fail" 2>&1 && \
+			{ { echo " ok"; rm "../$1-$x.fail"; } || echo " fail"; }
+			;;
+		esac
+	done
+	# Many bash run-XXX scripts just do this,
+	# no point in duplication it all over the place
+	for x in *.tests; do
+	test -x "$x" || continue
+	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
+		sed -i \
+			-e "/: invalid option /s:'::g" \
+			"$name.xx"
+		test $? -eq 77 && rm -f "../$1-$x.fail" && exit 77
+		diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
+	)
+	case $? in
+		0)  echo " ok";;
+		77) echo " skip (feature disabled)";;
+		*)  echo " fail"; tret=1;;
+	esac
+	done
+	exit ${tret}
+	)
+}
+
+# Main part of this script
+# Usage: run-all [directories]
+
+ret=0
+
+if [ $# -lt 1 ]; then
+	# All sub directories
+	modules=`ls -d hush-*`
+
+	for module in $modules; do
+	do_test $module || ret=1
+	done
+else
+	while [ $# -ge 1 ]; do
+	if [ -d $1 ]; then
+		do_test $1 || ret=1
+	fi
+	shift
+	done
+fi
+
+exit ${ret}
diff --git a/busybox-1.19.3/shell/match.c b/busybox-1.19.3/shell/match.c
new file mode 100644
index 0000000..fee3cf2
--- /dev/null
+++ b/busybox-1.19.3/shell/match.c
@@ -0,0 +1,148 @@
+/*
+ * ##/%% variable matching code ripped out of ash shell for code sharing
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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.
+ *
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
+ * was re-ported from NetBSD and debianized.
+ */
+#ifdef STANDALONE
+# include <stdbool.h>
+# include <stdio.h>
+# 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"
+
+char* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags)
+{
+	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 != end) {
+		char c;
+		int r;
+
+		c = *loc;
+		if (flags & SCAN_MATCH_LEFT_HALF) {
+			*loc = '\0';
+			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;
+		}
+
+		if (flags & SCAN_MOVE_FROM_LEFT) {
+			loc++;
+		} else {
+			loc--;
+		}
+	}
+	return NULL;
+}
+
+#ifdef STANDALONE
+int main(int argc, char *argv[])
+{
+	char *string;
+	char *op;
+	char *pattern;
+	char *loc;
+
+	setvbuf(stdout, NULL, _IONBF, 0);
+
+	if (!argv[1]) {
+		puts(
+			"Usage: match <test> [test...]\n\n"
+			"Where a <test> is the form: <string><op><match>\n"
+			"This is to test the shell ${var#val} expression type.\n\n"
+			"e.g. `match 'abc#a*'` -> bc"
+		);
+		return 1;
+	}
+
+	while (*++argv) {
+		size_t off;
+		unsigned scan_flags;
+
+		string = *argv;
+		off = strcspn(string, "#%");
+		if (!off) {
+			printf("invalid format\n");
+			continue;
+		}
+		op = string + off;
+		scan_flags = pick_scan(op[0], op[1]);
+
+		printf("'%s': flags:%x, ", string, scan_flags);
+		pattern = op + 1;
+		if (op[0] == op[1])
+			pattern++;
+		op[0] = '\0';
+
+		loc = scan_and_match(string, pattern, scan_flags);
+
+		if (scan_flags & SCAN_MATCH_LEFT_HALF) {
+			printf("'%s'\n", loc);
+		} else {
+			if (loc)
+				*loc = '\0';
+			printf("'%s'\n", string);
+		}
+	}
+
+	return 0;
+}
+#endif
diff --git a/busybox-1.19.3/shell/match.h b/busybox-1.19.3/shell/match.h
new file mode 100644
index 0000000..aa393ed
--- /dev/null
+++ b/busybox-1.19.3/shell/match.h
@@ -0,0 +1,34 @@
+/* match.h - interface to shell ##/%% matching code */
+
+#ifndef SHELL_MATCH_H
+#define SHELL_MATCH_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+//TODO! Why ash.c still uses internal version?!
+
+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* FAST_FUNC scan_and_match(char *string, const char *pattern, unsigned flags);
+
+static inline unsigned pick_scan(char op1, char op2)
+{
+	unsigned scan_flags;
+	if (op1 == '#') {
+		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
+
+#endif
diff --git a/busybox-1.19.3/shell/math.c b/busybox-1.19.3/shell/math.c
new file mode 100644
index 0000000..760645d
--- /dev/null
+++ b/busybox-1.19.3/shell/math.c
@@ -0,0 +1,750 @@
+/*
+ * Arithmetic code ripped out of ash shell for code sharing.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Original BSD copyright notice is retained at the end of this file.
+ *
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
+ * was re-ported from NetBSD and debianized.
+ *
+ * rewrite arith.y to micro stack based cryptic algorithm by
+ * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
+ *
+ * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
+ * dynamic variables.
+ *
+ * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
+ * used in busybox and size optimizations,
+ * rewrote arith (see notes to this), added locale support,
+ * rewrote dynamic variables.
+ *
+ * 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.
+ */
+
+/* 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
+ * 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).
+ */
+
+/*
+ * Aug 24, 2001              Manuel Novoa III
+ *
+ * Reduced the generated code size by about 30% (i386) and fixed several bugs.
+ *
+ * 1) In arith_apply():
+ *    a) Cached values of *numptr and &(numptr[-1]).
+ *    b) Removed redundant test for zero denominator.
+ *
+ * 2) In arith():
+ *    a) Eliminated redundant code for processing operator tokens by moving
+ *       to a table-based implementation.  Also folded handling of parens
+ *       into the table.
+ *    b) Combined all 3 loops which called arith_apply to reduce generated
+ *       code size at the cost of speed.
+ *
+ * 3) The following expressions were treated as valid by the original code:
+ *       1()  ,    0!  ,    1 ( *3 )   .
+ *    These bugs have been fixed by internally enclosing the expression in
+ *    parens and then checking that all binary ops and right parens are
+ *    preceded by a valid expression (NUM_TOKEN).
+ *
+ * Note: It may be desirable to replace Aaron's test for whitespace with
+ * ctype's isspace() if it is used by another busybox applet or if additional
+ * whitespace chars should be considered.  Look below the "#include"s for a
+ * precompiler test.
+ */
+/*
+ * Aug 26, 2001              Manuel Novoa III
+ *
+ * Return 0 for null expressions.  Pointed out by Vladimir Oleynik.
+ *
+ * Merge in Aaron's comments previously posted to the busybox list,
+ * modified slightly to take account of my changes to the code.
+ *
+ */
+/*
+ *  (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * - allow access to variable,
+ *   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
+ * - 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 lookupvar (math_state->lookupvar)
+#define setvar    (math_state->setvar   )
+//#define endofname (math_state->endofname)
+
+typedef unsigned char operator;
+
+/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
+ * 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)
+
+#define TOK_COMMA               tok_decl(1,0)
+
+/* 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)
+
+#define TOK_MUL_ASSIGN          tok_decl(3,0)
+#define TOK_DIV_ASSIGN          tok_decl(3,1)
+#define TOK_REM_ASSIGN          tok_decl(3,2)
+
+#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (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_OR                  tok_decl(5,0)
+
+#define TOK_AND                 tok_decl(6,0)
+
+#define TOK_BOR                 tok_decl(7,0)
+
+#define TOK_BXOR                tok_decl(8,0)
+
+#define TOK_BAND                tok_decl(9,0)
+
+#define TOK_EQ                  tok_decl(10,0)
+#define TOK_NE                  tok_decl(10,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_LSHIFT              tok_decl(12,0)
+#define TOK_RSHIFT              tok_decl(12,1)
+
+#define TOK_ADD                 tok_decl(13,0)
+#define TOK_SUB                 tok_decl(13,1)
+
+#define TOK_MUL                 tok_decl(14,0)
+#define TOK_DIV                 tok_decl(14,1)
+#define TOK_REM                 tok_decl(14,2)
+
+/* Exponent is right associative */
+#define TOK_EXPONENT            tok_decl(15,1)
+
+/* Unary operators */
+#define UNARYPREC               16
+#define TOK_BNOT                tok_decl(UNARYPREC,0)
+#define TOK_NOT                 tok_decl(UNARYPREC,1)
+
+#define TOK_UMINUS              tok_decl(UNARYPREC+1,0)
+#define TOK_UPLUS               tok_decl(UNARYPREC+1,1)
+
+#define PREC_PRE                (UNARYPREC+2)
+
+#define TOK_PRE_INC             tok_decl(PREC_PRE, 0)
+#define TOK_PRE_DEC             tok_decl(PREC_PRE, 1)
+
+#define PREC_POST               (UNARYPREC+3)
+
+#define TOK_POST_INC            tok_decl(PREC_POST, 0)
+#define TOK_POST_DEC            tok_decl(PREC_POST, 1)
+
+#define SPEC_PREC               (UNARYPREC+4)
+
+#define TOK_NUM                 tok_decl(SPEC_PREC, 0)
+#define TOK_RPAREN              tok_decl(SPEC_PREC, 1)
+
+static int
+is_assign_op(operator op)
+{
+	operator prec = PREC(op);
+	fix_assignment_prec(prec);
+	return prec == PREC(TOK_ASSIGN)
+	|| prec == PREC_PRE
+	|| prec == PREC_POST;
+}
+
+static int
+is_right_associative(operator prec)
+{
+	return prec == PREC(TOK_ASSIGN)
+	|| prec == PREC(TOK_EXPONENT)
+	|| prec == PREC(TOK_CONDITIONAL);
+}
+
+
+typedef struct {
+	arith_t val;
+	/* 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;
+} remembered_name;
+
+
+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) {
+			remembered_name *cur;
+			remembered_name cur_save;
+
+			/* 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) {
+					/* Yes */
+					return "expression recursion loop detected";
+				}
+			}
+
+			/* push current var name */
+			cur = math_state->list_of_recursed_names;
+			cur_save.var = t->var;
+			cur_save.next = cur;
+			math_state->list_of_recursed_names = &cur_save;
+
+			/* recursively evaluate p as expression */
+			t->val = evaluate_string(math_state, p);
+
+			/* pop current var name */
+			math_state->list_of_recursed_names = cur;
+
+			return math_state->errmsg;
+		}
+		/* 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 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)
+{
+#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;
+
+	top_of_stack = NUMPTR - 1;
+
+	/* Resolve name to value, if needed */
+	err = arith_lookup_val(math_state, top_of_stack);
+	if (err)
+		return err;
+
+	rez = top_of_stack->val;
+	if (op == TOK_UMINUS)
+		rez = -rez;
+	else if (op == TOK_NOT)
+		rez = !rez;
+	else if (op == TOK_BNOT)
+		rez = ~rez;
+	else if (op == TOK_POST_INC || op == TOK_PRE_INC)
+		rez++;
+	else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
+		rez--;
+	else if (op != TOK_UPLUS) {
+		/* Binary operators */
+		arith_t right_side_val;
+		char bad_second_val;
+
+		/* 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";
+		}
+
+		top_of_stack--; /* now points to left side */
+
+		if (op != TOK_ASSIGN) {
+			/* Resolve left side value (unless the op is '=') */
+			err = arith_lookup_val(math_state, top_of_stack);
+			if (err)
+				return err;
+		}
+
+		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;
+		}
+		else if (op == TOK_BOR || op == TOK_OR_ASSIGN)
+			rez |= right_side_val;
+		else if (op == TOK_OR)
+			rez = right_side_val || rez;
+		else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
+			rez &= right_side_val;
+		else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
+			rez ^= right_side_val;
+		else if (op == TOK_AND)
+			rez = rez && right_side_val;
+		else if (op == TOK_EQ)
+			rez = (rez == right_side_val);
+		else if (op == TOK_NE)
+			rez = (rez != right_side_val);
+		else if (op == TOK_GE)
+			rez = (rez >= right_side_val);
+		else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
+			rez >>= right_side_val;
+		else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
+			rez <<= right_side_val;
+		else if (op == TOK_GT)
+			rez = (rez > right_side_val);
+		else if (op == TOK_LT)
+			rez = (rez < right_side_val);
+		else if (op == TOK_LE)
+			rez = (rez <= right_side_val);
+		else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
+			rez *= right_side_val;
+		else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
+			rez += right_side_val;
+		else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
+			rez -= right_side_val;
+		else if (op == TOK_ASSIGN || op == TOK_COMMA)
+			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 /= right_side_val;
+		else if (op == TOK_REM || op == TOK_REM_ASSIGN)
+			rez %= right_side_val;
+	}
+
+	if (is_assign_op(op)) {
+		char buf[sizeof(arith_t)*3 + 2];
+
+		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_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++;
+	}
+
+	top_of_stack->val = rez;
+	/* Erase var name, it is just a number now */
+	top_of_stack->var = NULL;
+	return NULL;
+ err:
+	return "arithmetic syntax error";
+#undef NUMPTR
+}
+
+/* longest must be first */
+static const char op_tokens[] ALIGN1 = {
+	'<','<','=',0, TOK_LSHIFT_ASSIGN,
+	'>','>','=',0, TOK_RSHIFT_ASSIGN,
+	'<','<',    0, TOK_LSHIFT,
+	'>','>',    0, TOK_RSHIFT,
+	'|','|',    0, TOK_OR,
+	'&','&',    0, TOK_AND,
+	'!','=',    0, TOK_NE,
+	'<','=',    0, TOK_LE,
+	'>','=',    0, TOK_GE,
+	'=','=',    0, TOK_EQ,
+	'|','=',    0, TOK_OR_ASSIGN,
+	'&','=',    0, TOK_AND_ASSIGN,
+	'*','=',    0, TOK_MUL_ASSIGN,
+	'/','=',    0, TOK_DIV_ASSIGN,
+	'%','=',    0, TOK_REM_ASSIGN,
+	'+','=',    0, TOK_PLUS_ASSIGN,
+	'-','=',    0, TOK_MINUS_ASSIGN,
+	'-','-',    0, TOK_POST_DEC,
+	'^','=',    0, TOK_XOR_ASSIGN,
+	'+','+',    0, TOK_POST_INC,
+	'*','*',    0, TOK_EXPONENT,
+	'!',        0, TOK_NOT,
+	'<',        0, TOK_LT,
+	'>',        0, TOK_GT,
+	'=',        0, TOK_ASSIGN,
+	'|',        0, TOK_BOR,
+	'&',        0, TOK_BAND,
+	'*',        0, TOK_MUL,
+	'/',        0, TOK_DIV,
+	'%',        0, TOK_REM,
+	'+',        0, TOK_ADD,
+	'-',        0, TOK_SUB,
+	'^',        0, TOK_BXOR,
+	/* uniq */
+	'~',        0, TOK_BNOT,
+	',',        0, TOK_COMMA,
+	'?',        0, TOK_CONDITIONAL,
+	':',        0, TOK_CONDITIONAL_SEP,
+	')',        0, TOK_RPAREN,
+	'(',        0, TOK_LPAREN,
+	0
+};
+#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7])
+
+const char* FAST_FUNC
+endofname(const char *name)
+{
+	if (!is_name(*name))
+		return name;
+	while (*++name) {
+		if (!is_in_name(*name))
+			break;
+	}
+	return name;
+}
+
+static arith_t FAST_FUNC
+evaluate_string(arith_state_t *math_state, const char *expr)
+{
+	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. */
+	var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0]));
+	var_or_num_t *numstackptr = numstack;
+	/* Stack of operator tokens */
+	operator *const stack = alloca(expr_len * sizeof(stack[0]));
+	operator *stackptr = stack;
+
+	/* 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 (expr == start_expr) {
+				/* Null expression */
+				numstack->val = 0;
+				goto ret;
+			}
+
+			/* This is only reached after all tokens have been extracted from the
+			 * input stream. If there are still tokens on the operator stack, they
+			 * are to be applied in order. At the end, there should be a final
+			 * result on the integer stack */
+
+			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 */
+				goto err;
+			}
+			if (numstack->var) {
+				/* expression is $((var)) only, lookup now */
+				errmsg = arith_lookup_val(math_state, numstack);
+			}
+			goto ret;
+		}
+
+		p = endofname(expr);
+		if (p != expr) {
+			/* 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->second_val_present = 0;
+			numstackptr++;
+			lasttok = TOK_NUM;
+			continue;
+		}
+
+		if (isdigit(arithval)) {
+			/* Number */
+			numstackptr->var = NULL;
+			errno = 0;
+			numstackptr->val = strto_arith_t(expr, (char**) &expr, 0);
+			if (errno)
+				numstackptr->val = 0; /* bash compat */
+			goto num;
+		}
+
+		/* 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++;
+			}
+			/* No match, go to next element of op_tokens[] */
+			while (*p)
+				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;
+			}
+		}
+ 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)
+			lasttok = TOK_NUM;
+
+		/* Plus and minus are binary (not unary) _only_ if the last
+		 * token was a number, or a right paren (which pretends to be
+		 * a number, since it evaluates to one). Think about it.
+		 * It makes sense. */
+		if (lasttok != TOK_NUM) {
+			switch (op) {
+			case TOK_ADD:
+				op = TOK_UPLUS;
+				break;
+			case TOK_SUB:
+				op = TOK_UMINUS;
+				break;
+			case TOK_POST_INC:
+				op = TOK_PRE_INC;
+				break;
+			case TOK_POST_DEC:
+				op = TOK_PRE_DEC;
+				break;
+			}
+		}
+		/* 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
+		 * stack until we find an operator with a lesser priority than the
+		 * 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.
+		 */
+		prec = PREC(op);
+		if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
+			/* not left paren or unary */
+			if (lasttok != TOK_NUM) {
+				/* binary op must be preceded by a num */
+				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 (prev_op == TOK_LPAREN) {
+						/* Any operator directly after a
+						 * close paren should consider itself binary */
+						lasttok = TOK_NUM;
+						goto next;
+					}
+				} else {
+					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;
+					}
+				}
+				errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr);
+				if (errmsg)
+					goto err_with_custom_msg;
+			}
+			if (op == TOK_RPAREN)
+				goto err;
+		}
+
+		/* Push this operator to the stack and remember it */
+		*stackptr++ = lasttok = op;
+ 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);
+}
+
+/*
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * 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. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/shell/math.h b/busybox-1.19.3/shell/math.h
new file mode 100644
index 0000000..2d305eb
--- /dev/null
+++ b/busybox-1.19.3/shell/math.h
@@ -0,0 +1,97 @@
+/* math.h - interface to shell math "library" -- this allows shells to share
+ *          the implementation of arithmetic $((...)) expansions.
+ *
+ * This aims to be a POSIX shell math library as documented here:
+ *	http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04
+ *
+ * See math.c for internal documentation.
+ */
+
+/* The math library has just one function:
+ *
+ * arith_t arith(arith_state_t *state, const char *expr);
+ *
+ * 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 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 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
+ *
+ *	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
+ *
+ *	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
+ *
+ *	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
+#define SHELL_MATH_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+#if ENABLE_SH_MATH_SUPPORT_64
+typedef long long arith_t;
+#define ARITH_FMT "%lld"
+#define strto_arith_t strtoull
+#else
+typedef long arith_t;
+#define ARITH_FMT "%ld"
+#define strto_arith_t strtoul
+#endif
+
+/* ash's and hush's endofname is the same, so... */
+# 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);
+
+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 const char* FAST_FUNC (*arith_var_endofname_t)(const char *name);
+
+typedef struct arith_state_t {
+	const char           *errmsg;
+	arith_var_lookup_t    lookupvar;
+	arith_var_set_t       setvar;
+//	arith_var_endofname_t endofname;
+	void                 *list_of_recursed_names;
+} arith_state_t;
+
+arith_t FAST_FUNC arith(arith_state_t *state, const char *expr);
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/noeol3.right b/busybox-1.19.3/shell/msh_test/msh-bugs/noeol3.right
new file mode 100644
index 0000000..56f8515
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/noeol3.right
@@ -0,0 +1 @@
+hush: syntax error: unterminated "
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/noeol3.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/noeol3.tests
new file mode 100755
index 0000000..ec958ed
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/noeol3.tests
@@ -0,0 +1,2 @@
+# last line has no EOL!
+echo "unterminated
\ No newline at end of file
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/process_subst.right b/busybox-1.19.3/shell/msh_test/msh-bugs/process_subst.right
new file mode 100644
index 0000000..397bc80
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/process_subst.right
@@ -0,0 +1,3 @@
+TESTzzBEST
+TEST$(echo zz)BEST
+TEST'BEST
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/process_subst.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/process_subst.tests
new file mode 100755
index 0000000..21996bc
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/process_subst.tests
@@ -0,0 +1,3 @@
+echo "TEST`echo zz;echo;echo`BEST"
+echo "TEST`echo '$(echo zz)'`BEST"
+echo "TEST`echo "'"`BEST"
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/read.right b/busybox-1.19.3/shell/msh_test/msh-bugs/read.right
new file mode 100644
index 0000000..0e50e2a
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/read.right
@@ -0,0 +1,4 @@
+read
+cat
+echo "REPLY=$REPLY"
+REPLY=exec <read.tests
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/read.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/read.tests
new file mode 100755
index 0000000..ff1acbd
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/read.tests
@@ -0,0 +1,4 @@
+exec <read.tests
+read
+cat
+echo "REPLY=$REPLY"
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/shift.right b/busybox-1.19.3/shell/msh_test/msh-bugs/shift.right
new file mode 100644
index 0000000..d281e35
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/shift.right
@@ -0,0 +1,6 @@
+./shift.tests abc d e
+./shift.tests d e 123
+./shift.tests d e 123
+./shift.tests
+./shift.tests
+./shift.tests
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/shift.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/shift.tests
new file mode 100755
index 0000000..53ef249
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/shift.tests
@@ -0,0 +1,14 @@
+if test $# = 0; then
+    exec "$THIS_SH" $0 abc "d e" 123
+fi
+echo $0 $1 $2
+shift
+echo $0 $1 $2
+shift 999
+echo $0 $1 $2
+shift 2
+echo $0 $1 $2
+shift 2
+echo $0 $1 $2
+shift
+echo $0 $1 $2
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/starquoted.right b/busybox-1.19.3/shell/msh_test/msh-bugs/starquoted.right
new file mode 100644
index 0000000..b56323f
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/starquoted.right
@@ -0,0 +1,8 @@
+.1 abc d e f.
+.1.
+.abc.
+.d e f.
+.-1 abc d e f-.
+.-1.
+.abc.
+.d e f-.
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/starquoted.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/starquoted.tests
new file mode 100755
index 0000000..2fe49b1
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/starquoted.tests
@@ -0,0 +1,8 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" 1 abc 'd e f'
+fi
+
+for a in "$*"; do echo ".$a."; done
+for a in "$@"; do echo ".$a."; done
+for a in "-$*-"; do echo ".$a."; done
+for a in "-$@-"; do echo ".$a."; done
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/syntax_err.right b/busybox-1.19.3/shell/msh_test/msh-bugs/syntax_err.right
new file mode 100644
index 0000000..08a270c
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/syntax_err.right
@@ -0,0 +1,2 @@
+shown
+hush: syntax error: unterminated '
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/syntax_err.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/syntax_err.tests
new file mode 100755
index 0000000..d10ed42
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/syntax_err.tests
@@ -0,0 +1,3 @@
+echo shown
+echo test `echo 'aa`
+echo not shown
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_assign.right b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_assign.right
new file mode 100644
index 0000000..352210d
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_assign.right
@@ -0,0 +1,5 @@
+. .
+.abc d e.
+.abc d e.
+.abc d e.
+.abc d e.
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_assign.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_assign.tests
new file mode 100755
index 0000000..18cdc74
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_assign.tests
@@ -0,0 +1,15 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" abc "d e"
+fi
+
+space=' '
+echo .$space.
+
+a=$*
+echo .$a.
+a=$@
+echo .$a.
+a="$*"
+echo .$a.
+a="$@"
+echo .$a.
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_redir.right b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_redir.right
new file mode 100644
index 0000000..423299c
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_redir.right
@@ -0,0 +1,3 @@
+TEST1
+TEST2
+TEST3
diff --git a/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_redir.tests b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_redir.tests
new file mode 100755
index 0000000..bda6bdd
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-bugs/var_expand_in_redir.tests
@@ -0,0 +1,13 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" abc "d e"
+fi
+
+echo TEST1 >"$1.out"
+echo TEST2 >"$2.out"
+# bash says: "$@.out": ambiguous redirect
+# ash handles it as if it is '$*' - we do the same
+echo TEST3 >"$@.out"
+
+cat abc.out "d e.out" "abc d e.out"
+
+rm abc.out "d e.out" "abc d e.out"
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_EACCES.right b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_EACCES.right
new file mode 100644
index 0000000..6e5480b
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_EACCES.right
@@ -0,0 +1,2 @@
+./: can't execute
+126
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_EACCES.tests b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_EACCES.tests
new file mode 100755
index 0000000..26b5c61
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_EACCES.tests
@@ -0,0 +1,2 @@
+./
+echo $?
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_ENOENT.right b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_ENOENT.right
new file mode 100644
index 0000000..dd49d2c
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_ENOENT.right
@@ -0,0 +1,2 @@
+./does_not_exist_for_sure: not found
+127
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_ENOENT.tests b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_ENOENT.tests
new file mode 100755
index 0000000..7f1b88a
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/exitcode_ENOENT.tests
@@ -0,0 +1,2 @@
+./does_not_exist_for_sure
+echo $?
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/many_continues.right b/busybox-1.19.3/shell/msh_test/msh-execution/many_continues.right
new file mode 100644
index 0000000..d86bac9
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/many_continues.right
@@ -0,0 +1 @@
+OK
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/many_continues.tests b/busybox-1.19.3/shell/msh_test/msh-execution/many_continues.tests
new file mode 100755
index 0000000..86c729a
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/many_continues.tests
@@ -0,0 +1,15 @@
+if test $# = 0; then
+    # Child will kill us in 1 second
+    "$THIS_SH" "$0" $$ &
+
+    # Loop many, many times
+    trap 'echo OK; exit 0' 15
+    while true; do
+	continue
+    done
+    echo BAD
+    exit 1
+fi
+
+sleep 1
+kill $1
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/nested_break.right b/busybox-1.19.3/shell/msh_test/msh-execution/nested_break.right
new file mode 100644
index 0000000..4e8b6b0
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/nested_break.right
@@ -0,0 +1,8 @@
+A
+B
+iteration
+C
+A
+B
+iteration
+D
diff --git a/busybox-1.19.3/shell/msh_test/msh-execution/nested_break.tests b/busybox-1.19.3/shell/msh_test/msh-execution/nested_break.tests
new file mode 100755
index 0000000..1a954d2
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-execution/nested_break.tests
@@ -0,0 +1,17 @@
+# Testcase for http://bugs.busybox.net/view.php?id=846
+
+n=0
+while :
+do
+	echo A
+	while :
+	do
+		echo B
+		break
+	done
+	echo iteration
+	[ $n = 1 ] && break
+	echo C
+	n=`expr $n + 1`
+done
+echo D
diff --git a/busybox-1.19.3/shell/msh_test/msh-misc/tick.right b/busybox-1.19.3/shell/msh_test/msh-misc/tick.right
new file mode 100644
index 0000000..6ed281c
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-misc/tick.right
@@ -0,0 +1,2 @@
+1
+1
diff --git a/busybox-1.19.3/shell/msh_test/msh-misc/tick.tests b/busybox-1.19.3/shell/msh_test/msh-misc/tick.tests
new file mode 100755
index 0000000..1f749a9
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-misc/tick.tests
@@ -0,0 +1,4 @@
+true
+false; echo `echo $?`
+true
+{ false; echo `echo $?`; }
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/argv0.right b/busybox-1.19.3/shell/msh_test/msh-parsing/argv0.right
new file mode 100644
index 0000000..d86bac9
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/argv0.right
@@ -0,0 +1 @@
+OK
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/argv0.tests b/busybox-1.19.3/shell/msh_test/msh-parsing/argv0.tests
new file mode 100755
index 0000000..f5c40f6
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/argv0.tests
@@ -0,0 +1,4 @@
+if test $# = 0; then
+    exec "$THIS_SH" "$0" arg
+fi
+echo OK
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/noeol.right b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol.right
new file mode 100644
index 0000000..e427984
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol.right
@@ -0,0 +1 @@
+HELLO
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/noeol.tests b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol.tests
new file mode 100755
index 0000000..a93113a
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol.tests
@@ -0,0 +1,2 @@
+# next line has no EOL!
+echo HELLO
\ No newline at end of file
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/noeol2.right b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol2.right
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol2.right
@@ -0,0 +1 @@
+1
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/noeol2.tests b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol2.tests
new file mode 100755
index 0000000..1220f05
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/noeol2.tests
@@ -0,0 +1,7 @@
+# last line has no EOL!
+if true
+then
+  echo 1
+else
+  echo 2
+fi
\ No newline at end of file
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote1.right b/busybox-1.19.3/shell/msh_test/msh-parsing/quote1.right
new file mode 100644
index 0000000..cb38205
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote1.right
@@ -0,0 +1 @@
+'1'
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote1.tests b/busybox-1.19.3/shell/msh_test/msh-parsing/quote1.tests
new file mode 100755
index 0000000..f558954
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote1.tests
@@ -0,0 +1,2 @@
+a=1
+echo "'$a'"
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote2.right b/busybox-1.19.3/shell/msh_test/msh-parsing/quote2.right
new file mode 100644
index 0000000..3bc9edc
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote2.right
@@ -0,0 +1 @@
+>1
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote2.tests b/busybox-1.19.3/shell/msh_test/msh-parsing/quote2.tests
new file mode 100755
index 0000000..bd966f3
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote2.tests
@@ -0,0 +1,2 @@
+a=1
+echo ">$a"
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote3.right b/busybox-1.19.3/shell/msh_test/msh-parsing/quote3.right
new file mode 100644
index 0000000..069a46e
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote3.right
@@ -0,0 +1,3 @@
+Testing: in $empty""
+..
+Finished
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote3.tests b/busybox-1.19.3/shell/msh_test/msh-parsing/quote3.tests
new file mode 100755
index 0000000..075e785
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote3.tests
@@ -0,0 +1,8 @@
+if test $# = 0; then
+    exec "$THIS_SH" quote3.tests abc "d e"
+fi
+
+echo 'Testing: in $empty""'
+empty=''
+for a in $empty""; do echo ".$a."; done
+echo Finished
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote4.right b/busybox-1.19.3/shell/msh_test/msh-parsing/quote4.right
new file mode 100644
index 0000000..b2901ea
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote4.right
@@ -0,0 +1 @@
+a b
diff --git a/busybox-1.19.3/shell/msh_test/msh-parsing/quote4.tests b/busybox-1.19.3/shell/msh_test/msh-parsing/quote4.tests
new file mode 100755
index 0000000..f1dabfa
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-parsing/quote4.tests
@@ -0,0 +1,2 @@
+a_b='a b'
+echo "$a_b"
diff --git a/busybox-1.19.3/shell/msh_test/msh-vars/star.right b/busybox-1.19.3/shell/msh_test/msh-vars/star.right
new file mode 100644
index 0000000..0ecc55b
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-vars/star.right
@@ -0,0 +1,6 @@
+.1.
+.abc.
+.d.
+.e.
+.f.
+.1 abc d e f.
diff --git a/busybox-1.19.3/shell/msh_test/msh-vars/star.tests b/busybox-1.19.3/shell/msh_test/msh-vars/star.tests
new file mode 100755
index 0000000..5554c40
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-vars/star.tests
@@ -0,0 +1,8 @@
+if test $# = 0; then
+    exec "$THIS_SH" star.tests 1 abc 'd e f'
+fi
+# 'd e f' should be split into 3 separate args:
+for a in $*; do echo ".$a."; done
+
+# must produce .1 abc d e f.
+for a in "$*"; do echo ".$a."; done
diff --git a/busybox-1.19.3/shell/msh_test/msh-vars/var.right b/busybox-1.19.3/shell/msh_test/msh-vars/var.right
new file mode 100644
index 0000000..14b2314
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-vars/var.right
@@ -0,0 +1,4 @@
+http://busybox.net
+http://busybox.net_abc
+1
+1
diff --git a/busybox-1.19.3/shell/msh_test/msh-vars/var.tests b/busybox-1.19.3/shell/msh_test/msh-vars/var.tests
new file mode 100755
index 0000000..0a63696
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-vars/var.tests
@@ -0,0 +1,9 @@
+URL=http://busybox.net
+
+echo $URL
+echo ${URL}_abc
+
+true
+false; echo $?
+true
+{ false; echo $?; }
diff --git a/busybox-1.19.3/shell/msh_test/msh-vars/var_subst_in_for.right b/busybox-1.19.3/shell/msh_test/msh-vars/var_subst_in_for.right
new file mode 100644
index 0000000..c8aca1c
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-vars/var_subst_in_for.right
@@ -0,0 +1,40 @@
+Testing: in x y z
+.x.
+.y.
+.z.
+Testing: in u $empty v
+.u.
+.v.
+Testing: in u " $empty" v
+.u.
+. .
+.v.
+Testing: in u $empty $empty$a v
+.u.
+.a.
+.v.
+Testing: in $a_b
+.a.
+.b.
+Testing: in $*
+.abc.
+.d.
+.e.
+Testing: in $@
+.abc.
+.d.
+.e.
+Testing: in -$*-
+.-abc.
+.d.
+.e-.
+Testing: in -$@-
+.-abc.
+.d.
+.e-.
+Testing: in $a_b -$a_b-
+.a.
+.b.
+.-a.
+.b-.
+Finished
diff --git a/busybox-1.19.3/shell/msh_test/msh-vars/var_subst_in_for.tests b/busybox-1.19.3/shell/msh_test/msh-vars/var_subst_in_for.tests
new file mode 100755
index 0000000..4d1c112
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/msh-vars/var_subst_in_for.tests
@@ -0,0 +1,40 @@
+if test $# = 0; then
+    exec "$THIS_SH" var_subst_in_for.tests abc "d e"
+fi
+
+echo 'Testing: in x y z'
+for a in x y z; do echo ".$a."; done
+
+echo 'Testing: in u $empty v'
+empty=''
+for a in u $empty v; do echo ".$a."; done
+
+echo 'Testing: in u " $empty" v'
+empty=''
+for a in u " $empty" v; do echo ".$a."; done
+
+echo 'Testing: in u $empty $empty$a v'
+a='a'
+for a in u $empty $empty$a v; do echo ".$a."; done
+
+echo 'Testing: in $a_b'
+a_b='a b'
+for a in $a_b; do echo ".$a."; done
+
+echo 'Testing: in $*'
+for a in $*; do echo ".$a."; done
+
+echo 'Testing: in $@'
+for a in $@; do echo ".$a."; done
+
+echo 'Testing: in -$*-'
+for a in -$*-; do echo ".$a."; done
+
+echo 'Testing: in -$@-'
+for a in -$@-; do echo ".$a."; done
+
+echo 'Testing: in $a_b -$a_b-'
+a_b='a b'
+for a in $a_b -$a_b-; do echo ".$a."; done
+
+echo Finished
diff --git a/busybox-1.19.3/shell/msh_test/run-all b/busybox-1.19.3/shell/msh_test/run-all
new file mode 100755
index 0000000..29f62a5
--- /dev/null
+++ b/busybox-1.19.3/shell/msh_test/run-all
@@ -0,0 +1,64 @@
+#!/bin/sh
+
+test -x msh || {
+    echo "No ./msh - creating a link to ../../busybox"
+    ln -s ../../busybox msh
+}
+
+PATH="$PWD:$PATH" # for msh
+export PATH
+
+THIS_SH="$PWD/msh"
+export THIS_SH
+
+do_test()
+{
+    test -d "$1" || return 0
+#   echo Running tests in directory "$1"
+    (
+    cd "$1" || { echo "cannot cd $1!"; exit 1; }
+    for x in run-*; do
+	test -f "$x" || continue
+	case "$x" in
+	    "$0"|run-minimal|run-gprof) ;;
+	    *.orig|*~) ;;
+	    #*) echo $x ; sh $x ;;
+	    *)
+	    sh "$x" >"../$1-$x.fail" 2>&1 && \
+	    { echo "$1/$x: ok"; rm "../$1-$x.fail"; } || echo "$1/$x: fail";
+	    ;;
+	esac
+    done
+    # Many bash run-XXX scripts just do this,
+    # no point in duplication it all over the place
+    for x in *.tests; do
+	test -x "$x" || continue
+	name="${x%%.tests}"
+	test -f "$name.right" || continue
+#	echo Running test: "$name.right"
+	{
+	    "$THIS_SH" "./$x" >"$name.xx" 2>&1
+	    diff -u "$name.xx" "$name.right" >"../$1-$x.fail" && rm -f "$name.xx" "../$1-$x.fail"
+	} && echo "$1/$x: ok" || echo "$1/$x: fail"
+    done
+    )
+}
+
+# Main part of this script
+# Usage: run-all [directories]
+
+if [ $# -lt 1 ]; then
+    # All sub directories
+    modules=`ls -d msh-*`
+
+    for module in $modules; do
+	do_test $module
+    done
+else
+    while [ $# -ge 1 ]; do
+	if [ -d $1 ]; then
+	    do_test $1
+	fi
+	shift
+    done
+fi
diff --git a/busybox-1.19.3/shell/random.c b/busybox-1.19.3/shell/random.c
new file mode 100644
index 0000000..853ab08
--- /dev/null
+++ b/busybox-1.19.3/shell/random.c
@@ -0,0 +1,44 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $RANDOM support.
+ *
+ * Copyright (C) 2009 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "random.h"
+
+uint32_t FAST_FUNC
+next_random(random_t *rnd)
+{
+	/* Galois LFSR parameter */
+	/* Taps at 32 31 29 1: */
+	enum { MASK = 0x8000000b };
+	/* Another example - taps at 32 31 30 10: */
+	/* MASK = 0x00400007 */
+
+	uint32_t t;
+
+	if (UNINITED_RANDOM_T(rnd)) {
+		/* Can use monotonic_ns() for better randomness but for now
+		 * it is not used anywhere else in busybox... so avoid bloat
+		 */
+		INIT_RANDOM_T(rnd, getpid(), monotonic_us());
+	}
+
+	/* LCG has period of 2^32 and alternating lowest bit */
+	rnd->LCG = 1664525 * rnd->LCG + 1013904223;
+	/* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */
+	t = (rnd->galois_LFSR << 1);
+	if (rnd->galois_LFSR < 0) /* if we just shifted 1 out of msb... */
+		t ^= MASK;
+	rnd->galois_LFSR = t;
+	/* Both are weak, combining them gives better randomness
+	 * and ~2^64 period. & 0x7fff is probably bash compat
+	 * for $RANDOM range. Combining with subtraction is
+	 * just for fun. + and ^ would work equally well. */
+	t = (t - rnd->LCG) & 0x7fff;
+
+	return t;
+}
diff --git a/busybox-1.19.3/shell/random.h b/busybox-1.19.3/shell/random.h
new file mode 100644
index 0000000..180c48a
--- /dev/null
+++ b/busybox-1.19.3/shell/random.h
@@ -0,0 +1,33 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * $RANDOM support.
+ *
+ * Copyright (C) 2009 Denys Vlasenko
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+#ifndef SHELL_RANDOM_H
+#define SHELL_RANDOM_H 1
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+typedef struct random_t {
+	/* Random number generators */
+	int32_t galois_LFSR; /* Galois LFSR (fast but weak). signed! */
+	uint32_t LCG;        /* LCG (fast but weak) */
+} random_t;
+
+#define UNINITED_RANDOM_T(rnd) \
+	((rnd)->galois_LFSR == 0)
+
+#define INIT_RANDOM_T(rnd, nonzero, v) \
+	((rnd)->galois_LFSR = (nonzero), (rnd)->LCG = (v))
+
+#define CLEAR_RANDOM_T(rnd) \
+	((rnd)->galois_LFSR = 0)
+
+uint32_t next_random(random_t *rnd) FAST_FUNC;
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/shell/shell_common.c b/busybox-1.19.3/shell/shell_common.c
new file mode 100644
index 0000000..bbc22ed
--- /dev/null
+++ b/busybox-1.19.3/shell/shell_common.c
@@ -0,0 +1,493 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Adapted from ash applet code
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
+ * was re-ported from NetBSD and debianized.
+ *
+ * Copyright (c) 2010 Denys Vlasenko
+ * Split from ash.c
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "shell_common.h"
+
+const char defifsvar[] ALIGN1 = "IFS= \t\n";
+
+
+int FAST_FUNC is_well_formed_var_name(const char *s, char terminator)
+{
+	if (!s || !(isalpha(*s) || *s == '_'))
+		return 0;
+
+	do
+		s++;
+	while (isalnum(*s) || *s == '_');
+
+	return *s == terminator;
+}
+
+/* read builtin */
+
+/* Needs to be interruptible: shell mush 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
+//after "=", then pass buffer to setvar() to consume.
+const char* FAST_FUNC
+shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
+	char       **argv,
+	const char *ifs,
+	int        read_flags,
+	const char *opt_n,
+	const char *opt_p,
+	const char *opt_t,
+	const char *opt_u
+)
+{
+	unsigned err;
+	unsigned end_ms; /* -t TIMEOUT */
+	int fd; /* -u FD */
+	int nchars; /* -n NUM */
+	char **pp;
+	char *buffer;
+	struct termios tty, old_tty;
+	const char *retval;
+	int bufpos; /* need to be able to hold -1 */
+	int startword;
+	smallint backslash;
+
+	errno = err = 0;
+
+	pp = argv;
+	while (*pp) {
+		if (!is_well_formed_var_name(*pp, '\0')) {
+			/* Mimic bash message */
+			bb_error_msg("read: '%s': not a valid identifier", *pp);
+			return (const char *)(uintptr_t)1;
+		}
+		pp++;
+	}
+
+	nchars = 0; /* if != 0, -n is in effect */
+	if (opt_n) {
+		nchars = bb_strtou(opt_n, NULL, 10);
+		if (nchars < 0 || errno)
+			return "invalid count";
+		/* note: "-n 0": off (bash 3.2 does this too) */
+	}
+	end_ms = 0;
+	if (opt_t) {
+		end_ms = bb_strtou(opt_t, NULL, 10);
+		if (errno || end_ms > UINT_MAX / 2048)
+			return "invalid timeout";
+		end_ms *= 1000;
+#if 0 /* even bash has no -t N.NNN support */
+		ts.tv_sec = bb_strtou(opt_t, &p, 10);
+		ts.tv_usec = 0;
+		/* EINVAL means number is ok, but not terminated by NUL */
+		if (*p == '.' && errno == EINVAL) {
+			char *p2;
+			if (*++p) {
+				int scale;
+				ts.tv_usec = bb_strtou(p, &p2, 10);
+				if (errno)
+					return "invalid timeout";
+				scale = p2 - p;
+				/* normalize to usec */
+				if (scale > 6)
+					return "invalid timeout";
+				while (scale++ < 6)
+					ts.tv_usec *= 10;
+			}
+		} else if (ts.tv_sec < 0 || errno) {
+			return "invalid timeout";
+		}
+		if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */
+			return "invalid timeout";
+		}
+#endif /* if 0 */
+	}
+	fd = STDIN_FILENO;
+	if (opt_u) {
+		fd = bb_strtou(opt_u, NULL, 10);
+		if (fd < 0 || errno)
+			return "invalid file descriptor";
+	}
+
+	if (opt_p && isatty(fd)) {
+		fputs(opt_p, stderr);
+		fflush_all();
+	}
+
+	if (ifs == NULL)
+		ifs = defifs;
+
+	if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
+		tcgetattr(fd, &tty);
+		old_tty = tty;
+		if (nchars) {
+			tty.c_lflag &= ~ICANON;
+			tty.c_cc[VMIN] = nchars < 256 ? nchars : 255;
+		}
+		if (read_flags & BUILTIN_READ_SILENT) {
+			tty.c_lflag &= ~(ECHO | ECHOK | ECHONL);
+		}
+		/* This forces execution of "restoring" tcgetattr later */
+		read_flags |= BUILTIN_READ_SILENT;
+		/* if tcgetattr failed, tcsetattr will fail too.
+		 * Ignoring, it's harmless. */
+		tcsetattr(fd, TCSANOW, &tty);
+	}
+
+	retval = (const char *)(uintptr_t)0;
+	startword = 1;
+	backslash = 0;
+	if (end_ms) /* NB: end_ms stays nonzero: */
+		end_ms = ((unsigned)monotonic_ms() + end_ms) | 1;
+	buffer = NULL;
+	bufpos = 0;
+	do {
+		char c;
+		struct pollfd pfd[1];
+		int timeout;
+
+		if ((bufpos & 0xff) == 0)
+			buffer = xrealloc(buffer, bufpos + 0x100);
+
+		timeout = -1;
+		if (end_ms) {
+			timeout = end_ms - (unsigned)monotonic_ms();
+			if (timeout <= 0) { /* already late? */
+				retval = (const char *)(uintptr_t)1;
+				goto ret;
+			}
+		}
+
+		/* 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;
+		if (backslash) {
+			backslash = 0;
+			if (c != '\n')
+				goto put;
+			continue;
+		}
+		if (!(read_flags & BUILTIN_READ_RAW) && c == '\\') {
+			backslash = 1;
+			continue;
+		}
+		if (c == '\n')
+			break;
+
+		/* $IFS splitting. NOT done if we run "read"
+		 * without variable names (bash compat).
+		 * Thus, "read" and "read REPLY" are not the same.
+		 */
+		if (argv[0]) {
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05 */
+			const char *is_ifs = strchr(ifs, c);
+			if (startword && is_ifs) {
+				if (isspace(c))
+					continue;
+				/* it is a non-space ifs char */
+				startword--;
+				if (startword == 1) /* first one? */
+					continue; /* yes, it is not next word yet */
+			}
+			startword = 0;
+			if (argv[1] != NULL && is_ifs) {
+				buffer[bufpos] = '\0';
+				bufpos = 0;
+				setvar(*argv, buffer);
+				argv++;
+				/* can we skip one non-space ifs char? (2: yes) */
+				startword = isspace(c) ? 2 : 1;
+				continue;
+			}
+		}
+ put:
+		bufpos++;
+	} while (--nchars);
+
+	if (argv[0]) {
+		/* Remove trailing space $IFS chars */
+		while (--bufpos >= 0 && isspace(buffer[bufpos]) && strchr(ifs, buffer[bufpos]) != NULL)
+			continue;
+		buffer[bufpos + 1] = '\0';
+		/* Use the remainder as a value for the next variable */
+		setvar(*argv, buffer);
+		/* Set the rest to "" */
+		while (*++argv)
+			setvar(*argv, "");
+	} else {
+		/* Note: no $IFS removal */
+		buffer[bufpos] = '\0';
+		setvar("REPLY", buffer);
+	}
+
+ ret:
+	free(buffer);
+	if (read_flags & BUILTIN_READ_SILENT)
+		tcsetattr(fd, TCSANOW, &old_tty);
+
+	errno = err;
+	return retval;
+}
+
+/* ulimit builtin */
+
+struct limits {
+	uint8_t cmd;            /* RLIMIT_xxx fit into it */
+	uint8_t factor_shift;   /* shift by to get rlim_{cur,max} values */
+	char option;
+	const char *name;
+};
+
+static const struct limits limits_tbl[] = {
+#ifdef RLIMIT_FSIZE
+	{ RLIMIT_FSIZE,		9,	'f',	"file size (blocks)" },
+#endif
+#ifdef RLIMIT_CPU
+	{ RLIMIT_CPU,		0,	't',	"cpu time (seconds)" },
+#endif
+#ifdef RLIMIT_DATA
+	{ RLIMIT_DATA,		10,	'd',	"data seg size (kb)" },
+#endif
+#ifdef RLIMIT_STACK
+	{ RLIMIT_STACK,		10,	's',	"stack size (kb)" },
+#endif
+#ifdef RLIMIT_CORE
+	{ RLIMIT_CORE,		9,	'c',	"core file size (blocks)" },
+#endif
+#ifdef RLIMIT_RSS
+	{ RLIMIT_RSS,		10,	'm',	"resident set size (kb)" },
+#endif
+#ifdef RLIMIT_MEMLOCK
+	{ RLIMIT_MEMLOCK,	10,	'l',	"locked memory (kb)" },
+#endif
+#ifdef RLIMIT_NPROC
+	{ RLIMIT_NPROC,		0,	'p',	"processes" },
+#endif
+#ifdef RLIMIT_NOFILE
+	{ RLIMIT_NOFILE,	0,	'n',	"file descriptors" },
+#endif
+#ifdef RLIMIT_AS
+	{ RLIMIT_AS,		10,	'v',	"address space (kb)" },
+#endif
+#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 {
+	OPT_hard = (1 << 0),
+	OPT_soft = (1 << 1),
+};
+
+/* "-": treat args as parameters of option with ASCII code 1 */
+static const char ulimit_opt_string[] = "-HSa"
+#ifdef RLIMIT_FSIZE
+			"f::"
+#endif
+#ifdef RLIMIT_CPU
+			"t::"
+#endif
+#ifdef RLIMIT_DATA
+			"d::"
+#endif
+#ifdef RLIMIT_STACK
+			"s::"
+#endif
+#ifdef RLIMIT_CORE
+			"c::"
+#endif
+#ifdef RLIMIT_RSS
+			"m::"
+#endif
+#ifdef RLIMIT_MEMLOCK
+			"l::"
+#endif
+#ifdef RLIMIT_NPROC
+			"p::"
+#endif
+#ifdef RLIMIT_NOFILE
+			"n::"
+#endif
+#ifdef RLIMIT_AS
+			"v::"
+#endif
+#ifdef RLIMIT_LOCKS
+			"w::"
+#endif
+#ifdef RLIMIT_NICE
+			"e::"
+#endif
+#ifdef RLIMIT_RTPRIO
+			"r::"
+#endif
+			;
+
+static void printlim(unsigned opts, const struct rlimit *limit,
+			const struct limits *l)
+{
+	rlim_t val;
+
+	val = limit->rlim_max;
+	if (!(opts & OPT_hard))
+		val = limit->rlim_cur;
+
+	if (val == RLIM_INFINITY)
+		printf("unlimited\n");
+	else {
+		val >>= l->factor_shift;
+		printf("%llu\n", (long long) val);
+	}
+}
+
+int FAST_FUNC
+shell_builtin_ulimit(char **argv)
+{
+	unsigned opts;
+	unsigned argc;
+
+	/* We can't use getopt32: need to handle commands like
+	 * ulimit 123 -c2 -l 456
+	 */
+
+	/* In case getopt was already called:
+	 * reset the libc getopt() function, which keeps internal state.
+	 */
+#ifdef __GLIBC__
+	optind = 0;
+#else /* BSD style */
+	optind = 1;
+	/* optreset = 1; */
+#endif
+	/* optarg = NULL; opterr = 0; optopt = 0; - do we need this?? */
+
+	argc = 1;
+	while (argv[argc])
+		argc++;
+
+	opts = 0;
+	while (1) {
+		struct rlimit limit;
+		const struct limits *l;
+		int opt_char = getopt(argc, argv, ulimit_opt_string);
+
+		if (opt_char == -1)
+			break;
+		if (opt_char == 'H') {
+			opts |= OPT_hard;
+			continue;
+		}
+		if (opt_char == 'S') {
+			opts |= OPT_soft;
+			continue;
+		}
+
+		if (opt_char == 'a') {
+			for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
+				getrlimit(l->cmd, &limit);
+				printf("-%c: %-30s ", l->option, l->name);
+				printlim(opts, &limit, l);
+			}
+			continue;
+		}
+
+		if (opt_char == 1)
+			opt_char = 'f';
+		for (l = limits_tbl; l != &limits_tbl[ARRAY_SIZE(limits_tbl)]; l++) {
+			if (opt_char == l->option) {
+				char *val_str;
+
+				getrlimit(l->cmd, &limit);
+
+				val_str = optarg;
+				if (!val_str && argv[optind] && argv[optind][0] != '-')
+					val_str = argv[optind++]; /* ++ skips NN in "-c NN" case */
+				if (val_str) {
+					rlim_t val;
+
+					if (strcmp(val_str, "unlimited") == 0)
+						val = RLIM_INFINITY;
+					else {
+						if (sizeof(val) == sizeof(int))
+							val = bb_strtou(val_str, NULL, 10);
+						else if (sizeof(val) == sizeof(long))
+							val = bb_strtoul(val_str, NULL, 10);
+						else
+							val = bb_strtoull(val_str, NULL, 10);
+						if (errno) {
+							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)
+						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) {
+						bb_perror_msg("error setting limit");
+						return EXIT_FAILURE;
+					}
+				} else {
+					printlim(opts, &limit, l);
+				}
+				break;
+			}
+		} /* for (every possible opt) */
+
+		if (l == &limits_tbl[ARRAY_SIZE(limits_tbl)]) {
+			/* bad option. getopt already complained. */
+			break;
+		}
+
+	} /* while (there are options) */
+
+	return 0;
+}
diff --git a/busybox-1.19.3/shell/shell_common.h b/busybox-1.19.3/shell/shell_common.h
new file mode 100644
index 0000000..f06bc41
--- /dev/null
+++ b/busybox-1.19.3/shell/shell_common.h
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Adapted from ash applet code
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kenneth Almquist.
+ *
+ * Copyright (c) 1989, 1991, 1993, 1994
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
+ * was re-ported from NetBSD and debianized.
+ *
+ * Copyright (c) 2010 Denys Vlasenko
+ * Split from ash.c
+ *
+ * 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" */
+#define defifs (defifsvar + 4)
+
+int FAST_FUNC is_well_formed_var_name(const char *s, char terminator);
+
+/* Builtins */
+
+enum {
+	BUILTIN_READ_SILENT = 1 << 0,
+	BUILTIN_READ_RAW    = 1 << 1,
+};
+const char* FAST_FUNC
+shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
+	char       **argv,
+	const char *ifs,
+	int        read_flags,
+	const char *opt_n,
+	const char *opt_p,
+	const char *opt_t,
+	const char *opt_u
+);
+
+int FAST_FUNC
+shell_builtin_ulimit(char **argv);
+
+POP_SAVED_FUNCTION_VISIBILITY
+
+#endif
diff --git a/busybox-1.19.3/sysklogd/Config.src b/busybox-1.19.3/sysklogd/Config.src
new file mode 100644
index 0000000..b7a494e
--- /dev/null
+++ b/busybox-1.19.3/sysklogd/Config.src
@@ -0,0 +1,153 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "System Logging Utilities"
+
+INSERT
+
+config SYSLOGD
+	bool "syslogd"
+	default y
+	help
+	  The syslogd utility is used to record logs of all the
+	  significant events that occur on a system. Every
+	  message that is logged records the date and time of the
+	  event, and will generally also record the name of the
+	  application that generated the message. When used in
+	  conjunction with klogd, messages from the Linux kernel
+	  can also be recorded. This is terribly useful,
+	  especially for finding what happened when something goes
+	  wrong. And something almost always will go wrong if
+	  you wait long enough....
+
+config FEATURE_ROTATE_LOGFILE
+	bool "Rotate message files"
+	default y
+	depends on SYSLOGD
+	help
+	  This enables syslogd to rotate the message files
+	  on his own. No need to use an external rotatescript.
+
+config FEATURE_REMOTE_LOG
+	bool "Remote Log support"
+	default y
+	depends on SYSLOGD
+	help
+	  When you enable this feature, the syslogd utility can
+	  be used to send system log messages to another system
+	  connected via a network. This allows the remote
+	  machine to log all the system messages, which can be
+	  terribly useful for reducing the number of serial
+	  cables you use. It can also be a very good security
+	  measure to prevent system logs from being tampered with
+	  by an intruder.
+
+config FEATURE_SYSLOGD_DUP
+	bool "Support -D (drop dups) option"
+	default y
+	depends on SYSLOGD
+	help
+	  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
+	range 256 20000
+	depends on SYSLOGD
+	help
+	  This option sets the size of the syslog read buffer.
+	  Actual memory usage increases around five times the
+	  change done here.
+
+config FEATURE_IPC_SYSLOG
+	bool "Circular Buffer support"
+	default y
+	depends on SYSLOGD
+	help
+	  When you enable this feature, the syslogd utility will
+	  use a circular buffer to record system log messages.
+	  When the buffer is filled it will continue to overwrite
+	  the oldest messages. This can be very useful for
+	  systems with little or no permanent storage, since
+	  otherwise system logs can eventually fill up your
+	  entire filesystem, which may cause your system to
+	  break badly.
+
+config FEATURE_IPC_SYSLOG_BUFFER_SIZE
+	int "Circular buffer size in Kbytes (minimum 4KB)"
+	default 16
+	range 4 2147483647
+	depends on FEATURE_IPC_SYSLOG
+	help
+	  This option sets the size of the circular buffer
+	  used to record system log messages.
+
+config LOGREAD
+	bool "logread"
+	default y
+	depends on FEATURE_IPC_SYSLOG
+	help
+	  If you enabled Circular Buffer support, you almost
+	  certainly want to enable this feature as well. This
+	  utility will allow you to read the messages that are
+	  stored in the syslogd circular buffer.
+
+config FEATURE_LOGREAD_REDUCED_LOCKING
+	bool "Double buffering"
+	default y
+	depends on LOGREAD
+	help
+	  'logread' ouput to slow serial terminals can have
+	  side effects on syslog because of the semaphore.
+	  This option make logread to double buffer copy
+	  from circular buffer, minimizing semaphore
+	  contention at some minor memory expense.
+
+config KLOGD
+	bool "klogd"
+	default y
+	help
+	  klogd is a utility which intercepts and logs all
+	  messages from the Linux kernel and sends the messages
+	  out to the 'syslogd' utility so they can be logged. If
+	  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
+	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
+	select FEATURE_SYSLOG
+	help
+	    The logger utility allows you to send arbitrary text
+	    messages to the system log (i.e. the 'syslogd' utility) so
+	    they can be logged. This is generally used to help locate
+	    problems that occur within programs and scripts.
+
+endmenu
diff --git a/busybox-1.19.3/sysklogd/Kbuild.src b/busybox-1.19.3/sysklogd/Kbuild.src
new file mode 100644
index 0000000..d386cc2
--- /dev/null
+++ b/busybox-1.19.3/sysklogd/Kbuild.src
@@ -0,0 +1,13 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_KLOGD)		+= klogd.o
+lib-$(CONFIG_LOGGER)		+= syslogd_and_logger.o
+lib-$(CONFIG_LOGREAD)		+= logread.o
+lib-$(CONFIG_SYSLOGD)		+= syslogd_and_logger.o
diff --git a/busybox-1.19.3/sysklogd/klogd.c b/busybox-1.19.3/sysklogd/klogd.c
new file mode 100644
index 0000000..efa0e53
--- /dev/null
+++ b/busybox-1.19.3/sysklogd/klogd.c
@@ -0,0 +1,264 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini klogd implementation for busybox
+ *
+ * Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>.
+ * Changes: Made this a standalone busybox module which uses standalone
+ * syslog() client interface.
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
+ *
+ * "circular buffer" Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * 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>
+
+
+/* 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" */
+}
+
+#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),
+	OPT_LEVEL      = (1 << 0),
+	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;
+
+	opt = getopt32(argv, "c:n", &opt_c);
+	if (opt & OPT_LEVEL) {
+		/* Valid levels are between 1 and 8 */
+		i = xatou_range(opt_c, 1, 8);
+	}
+	if (!(opt & OPT_FOREGROUND)) {
+		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
+	}
+
+	logmode = LOGMODE_SYSLOG;
+
+	/* 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
+	 */
+
+	if (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);
+
+	used = 0;
+	while (!bb_got_signal) {
+		int n;
+		int priority;
+		char *start;
+
+		/* "2 -- Read from the log." */
+		start = log_buffer + used;
+		n = klogd_read(start, KLOGD_LOGBUF_SIZE-1 - used);
+		if (n < 0) {
+			if (errno == EINTR)
+				continue;
+			bb_perror_msg(READ_ERROR);
+			break;
+		}
+		start[n] = '\0';
+
+		/* 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 */
+
+				/* 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;
+				}
+				/* buffer is full, log it anyway */
+				used = 0;
+				newline = NULL;
+			} else {
+				*newline++ = '\0';
+			}
+
+			/* Extract the priority */
+			priority = LOG_INFO;
+			if (*start == '<') {
+				start++;
+				if (*start) {
+					/* kernel never generates multi-digit prios */
+					priority = (*start - '0');
+					start++;
+				}
+				if (*start == '>')
+					start++;
+			}
+			/* Log (only non-empty lines) */
+			if (*start)
+				syslog(priority, "%s", start);
+
+			if (!newline)
+				break;
+			start = newline;
+		}
+	}
+
+	klogd_close();
+	syslog(LOG_NOTICE, "klogd: exiting");
+	if (bb_got_signal)
+		kill_myself_with_sig(bb_got_signal);
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/sysklogd/logger.c b/busybox-1.19.3/sysklogd/logger.c
new file mode 100644
index 0000000..5a70277
--- /dev/null
+++ b/busybox-1.19.3/sysklogd/logger.c
@@ -0,0 +1,166 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini logger implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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"
+#define SYSLOG_NAMES
+#define SYSLOG_NAMES_CONST
+#include <syslog.h>
+*/
+
+/* Decode a symbolic name to a numeric value
+ * this function is based on code
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+static int decode(char *name, const CODE *codetab)
+{
+	const CODE *c;
+
+	if (isdigit(*name))
+		return atoi(name);
+	for (c = codetab; c->c_name; c++) {
+		if (!strcasecmp(name, c->c_name)) {
+			return c->c_val;
+		}
+	}
+
+	return -1;
+}
+
+/* Decode a symbolic name to a numeric value
+ * this function is based on code
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * Original copyright notice is retained at the end of this file.
+ */
+static int pencode(char *s)
+{
+	char *save;
+	int lev, fac = LOG_USER;
+
+	for (save = s; *s && *s != '.'; ++s)
+		;
+	if (*s) {
+		*s = '\0';
+		fac = decode(save, facilitynames);
+		if (fac < 0)
+			bb_error_msg_and_die("unknown %s name: %s", "facility", save);
+		*s++ = '.';
+	} else {
+		s = save;
+	}
+	lev = decode(s, prioritynames);
+	if (lev < 0)
+		bb_error_msg_and_die("unknown %s name: %s", "priority", save);
+	return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+#define strbuf bb_common_bufsiz1
+
+int logger_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int logger_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *str_p, *str_t;
+	int opt;
+	int i = 0;
+
+	/* Fill out the name string early (may be overwritten later) */
+	str_t = uid2uname_utoa(geteuid());
+
+	/* Parse any options */
+	opt = getopt32(argv, "p:st:", &str_p, &str_t);
+
+	if (opt & 0x2) /* -s */
+		i |= LOG_PERROR;
+	//if (opt & 0x4) /* -t */
+	openlog(str_t, i, 0);
+	i = LOG_USER | LOG_NOTICE;
+	if (opt & 0x1) /* -p */
+		i = pencode(str_p);
+
+	argv += optind;
+	if (!argv[0]) {
+		while (fgets(strbuf, COMMON_BUFSIZE, stdin)) {
+			if (strbuf[0]
+			 && NOT_LONE_CHAR(strbuf, '\n')
+			) {
+				/* Neither "" nor "\n" */
+				syslog(i, "%s", strbuf);
+			}
+		}
+	} else {
+		char *message = NULL;
+		int len = 0;
+		int pos = 0;
+		do {
+			len += strlen(*argv) + 1;
+			message = xrealloc(message, len + 1);
+			sprintf(message + pos, " %s", *argv),
+			pos = len;
+		} while (*++argv);
+		syslog(i, "%s", message + 1); /* skip leading " " */
+	}
+
+	closelog();
+	return EXIT_SUCCESS;
+}
+
+/* Clean up. Needed because we are included from syslogd_and_logger.c */
+#undef strbuf
+
+/*-
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * This is the original license statement for the decode and pencode functions.
+ *
+ * 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. 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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
diff --git a/busybox-1.19.3/sysklogd/logread.c b/busybox-1.19.3/sysklogd/logread.c
new file mode 100644
index 0000000..9939569
--- /dev/null
+++ b/busybox-1.19.3/sysklogd/logread.c
@@ -0,0 +1,191 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * circular buffer syslog implementation for busybox
+ *
+ * Copyright (C) 2000 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * 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>
+#include <sys/shm.h>
+
+#define DEBUG 0
+
+/* our shared key (syslogd.c and logread.c must be in sync) */
+enum { KEY_ID = 0x414e4547 }; /* "GENA" */
+
+struct shbuf_ds {
+	int32_t size;           // size of data - 1
+	int32_t tail;           // end of message list
+	char data[1];           // messages
+};
+
+static const struct sembuf init_sem[3] = {
+	{0, -1, IPC_NOWAIT | SEM_UNDO},
+	{1, 0}, {0, +1, SEM_UNDO}
+};
+
+struct globals {
+	struct sembuf SMrup[1]; // {0, -1, IPC_NOWAIT | SEM_UNDO},
+	struct sembuf SMrdn[2]; // {1, 0}, {0, +1, SEM_UNDO}
+	struct shbuf_ds *shbuf;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define SMrup (G.SMrup)
+#define SMrdn (G.SMrdn)
+#define shbuf (G.shbuf)
+#define INIT_G() do { \
+	memcpy(SMrup, init_sem, sizeof(init_sem)); \
+} while (0)
+
+static void error_exit(const char *str) NORETURN;
+static void error_exit(const char *str)
+{
+	//release all acquired resources
+	shmdt(shbuf);
+	bb_perror_msg_and_die(str);
+}
+
+/*
+ * sem_up - up()'s a semaphore.
+ */
+static void sem_up(int semid)
+{
+	if (semop(semid, SMrup, 1) == -1)
+		error_exit("semop[SMrup]");
+}
+
+static void interrupted(int sig UNUSED_PARAM)
+{
+	signal(SIGINT, SIG_IGN);
+	shmdt(shbuf);
+	exit(EXIT_SUCCESS);
+}
+
+int logread_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int logread_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned cur;
+	int log_semid; /* ipc semaphore id */
+	int log_shmid; /* ipc shared memory id */
+	smallint follow = getopt32(argv, "f");
+
+	INIT_G();
+
+	log_shmid = shmget(KEY_ID, 0, 0);
+	if (log_shmid == -1)
+		bb_perror_msg_and_die("can't find syslogd buffer");
+
+	/* 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");
+
+	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);
+
+	/* Suppose atomic memory read */
+	/* Max possible value for tail is shbuf->size - 1 */
+	cur = shbuf->tail;
+
+	/* Loop for logread -f, one pass if there was no -f */
+	do {
+		unsigned shbuf_size;
+		unsigned shbuf_tail;
+		const char *shbuf_data;
+#if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
+		int i;
+		int len_first_part;
+		int len_total = len_total; /* for gcc */
+		char *copy = copy; /* for gcc */
+#endif
+		if (semop(log_semid, SMrdn, 2) == -1)
+			error_exit("semop[SMrdn]");
+
+		/* Copy the info, helps gcc to realize that it doesn't change */
+		shbuf_size = shbuf->size;
+		shbuf_tail = shbuf->tail;
+		shbuf_data = shbuf->data; /* pointer! */
+
+		if (DEBUG)
+			printf("cur:%d tail:%i size:%i\n",
+					cur, shbuf_tail, shbuf_size);
+
+		if (!follow) {
+			/* advance to oldest complete message */
+			/* find NUL */
+			cur += strlen(shbuf_data + cur);
+			if (cur >= shbuf_size) { /* last byte in buffer? */
+				cur = strnlen(shbuf_data, shbuf_tail);
+				if (cur == shbuf_tail)
+					goto unlock; /* no complete messages */
+			}
+			/* advance to first byte of the message */
+			cur++;
+			if (cur >= shbuf_size) /* last byte in buffer? */
+				cur = 0;
+		} else { /* logread -f */
+			if (cur == shbuf_tail) {
+				sem_up(log_semid);
+				fflush_all();
+				sleep(1); /* TODO: replace me with a sleep_on */
+				continue;
+			}
+		}
+
+		/* Read from cur to tail */
+#if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
+		len_first_part = len_total = shbuf_tail - cur;
+		if (len_total < 0) {
+			/* message wraps: */
+			/* [SECOND PART.........FIRST PART] */
+			/*  ^data      ^tail    ^cur      ^size */
+			len_total += shbuf_size;
+		}
+		copy = xmalloc(len_total + 1);
+		if (len_first_part < 0) {
+			/* message wraps (see above) */
+			len_first_part = shbuf_size - cur;
+			memcpy(copy + len_first_part, shbuf_data, shbuf_tail);
+		}
+		memcpy(copy, shbuf_data + cur, len_first_part);
+		copy[len_total] = '\0';
+		cur = shbuf_tail;
+#else
+		while (cur != shbuf_tail) {
+			fputs(shbuf_data + cur, stdout);
+			cur += strlen(shbuf_data + cur) + 1;
+			if (cur >= shbuf_size)
+				cur = 0;
+		}
+#endif
+ unlock:
+		/* release the lock on the log chain */
+		sem_up(log_semid);
+
+#if ENABLE_FEATURE_LOGREAD_REDUCED_LOCKING
+		for (i = 0; i < len_total; i += strlen(copy + i) + 1) {
+			fputs(copy + i, stdout);
+		}
+		free(copy);
+#endif
+	} while (follow);
+
+	shmdt(shbuf);
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/sysklogd/syslogd.c b/busybox-1.19.3/sysklogd/syslogd.c
new file mode 100644
index 0000000..d7cce01
--- /dev/null
+++ b/busybox-1.19.3/sysklogd/syslogd.c
@@ -0,0 +1,1035 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini syslogd implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Copyright (C) 2000 by Karl M. Hegbloom <karlheg@debian.org>
+ *
+ * "circular buffer" Copyright (C) 2001 by Gennady Feldman <gfeldman@gena01.com>
+ *
+ * Maintainer: Gennady Feldman <gfeldman@gena01.com> as of Mar 12, 2001
+ *
+ * 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:     "\n	-u		Log time stamps in Coordinated Universal Time (UTC)"
+//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 IP or hostname on PORT (default PORT=514/UDP)"
+//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:
+//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"
+#define SYSLOG_NAMES
+#define SYSLOG_NAMES_CONST
+#include <syslog.h>
+*/
+
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#if ENABLE_FEATURE_REMOTE_LOG
+#include <netinet/in.h>
+#endif
+
+#if ENABLE_FEATURE_IPC_SYSLOG
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/shm.h>
+#endif
+
+
+#define DEBUG 0
+
+/* MARK code is not very useful, is bloat, and broken:
+ * can deadlock if alarmed to make MARK while writing to IPC buffer
+ * (semaphores are down but do_mark routine tries to down them again) */
+#undef SYSLOGD_MARK
+
+/* Write locking does not seem to be useful either */
+#undef SYSLOGD_WRLOCK
+
+enum {
+	MAX_READ = CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE,
+	DNS_WAIT_SEC = 2 * 60,
+};
+
+/* Semaphore operation structures */
+struct shbuf_ds {
+	int32_t size;   /* size of data - 1 */
+	int32_t tail;   /* end of message list */
+	char data[1];   /* data/messages */
+};
+
+#if ENABLE_FEATURE_REMOTE_LOG
+typedef struct {
+	int remoteFD;
+	unsigned last_dns_resolve;
+	len_and_sockaddr *remoteAddr;
+	const char *remoteHostname;
+} 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 \
+	logFile_t logFile;                      \
+	/* interval between marks in seconds */ \
+	/*int markInterval;*/                   \
+	/* level of messages to be logged */    \
+	int logLevel;                           \
+IF_FEATURE_ROTATE_LOGFILE( \
+	/* max size of file before rotation */  \
+	unsigned logFileSize;                   \
+	/* number of rotated message files */   \
+	unsigned logFileRotate;                 \
+) \
+IF_FEATURE_IPC_SYSLOG( \
+	int shmid; /* ipc shared memory id */   \
+	int s_semid; /* ipc semaphore id */     \
+	int shm_size;                           \
+	struct sembuf SMwup[1];                 \
+	struct sembuf SMwdn[3];                 \
+) \
+IF_FEATURE_SYSLOGD_CFG( \
+	logRule_t *log_rules; \
+)
+
+struct init_globals {
+	GLOBALS
+};
+
+struct globals {
+	GLOBALS
+
+#if ENABLE_FEATURE_REMOTE_LOG
+	llist_t *remoteHosts;
+#endif
+#if ENABLE_FEATURE_IPC_SYSLOG
+	struct shbuf_ds *shbuf;
+#endif
+	time_t last_log_time;
+	/* localhost's name. We print only first 64 chars */
+	char *hostname;
+
+	/* We recv into recvbuf... */
+	char recvbuf[MAX_READ * (1 + ENABLE_FEATURE_SYSLOGD_DUP)];
+	/* ...then copy to parsebuf, escaping control chars */
+	/* (can grow x2 max) */
+	char parsebuf[MAX_READ*2];
+	/* ...then sprintf into printbuf, adding timestamp (26 chars),
+	 * host (64), fac.prio (20) to the message */
+	/* (growth by: 26 + 64 + 20 + delims = ~121) */
+	char printbuf[MAX_READ*2 + 139];
+};
+
+static const struct init_globals init_data = {
+	.logFile = {
+		.path = "/var/log/messages",
+		.fd = -1,
+	},
+#ifdef SYSLOGD_MARK
+	.markInterval = 20 * 60,
+#endif
+	.logLevel = 8,
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+	.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 */
+	.SMwup = { {1, -1, IPC_NOWAIT} },
+	.SMwdn = { {0, 0}, {1, 0}, {1, +1} },
+#endif
+};
+
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(memcpy(xzalloc(sizeof(G)), &init_data, sizeof(init_data))); \
+} while (0)
+
+
+/* Options */
+enum {
+	OPTBIT_mark = 0, // -m
+	OPTBIT_nofork, // -n
+	OPTBIT_outfile, // -O
+	OPTBIT_loglevel, // -l
+	OPTBIT_small, // -S
+	OPTBIT_utc, // -u
+	IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize   ,)	// -s
+	IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt  ,)	// -b
+	IF_FEATURE_REMOTE_LOG(    OPTBIT_remotelog  ,)	// -R
+	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
+
+	OPT_mark        = 1 << OPTBIT_mark    ,
+	OPT_nofork      = 1 << OPTBIT_nofork  ,
+	OPT_outfile     = 1 << OPTBIT_outfile ,
+	OPT_loglevel    = 1 << OPTBIT_loglevel,
+	OPT_small       = 1 << OPTBIT_small   ,
+	OPT_utc			= 1 << OPTBIT_utc	  ,
+	OPT_filesize    = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize   )) + 0,
+	OPT_rotatecnt   = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt  )) + 0,
+	OPT_remotelog   = IF_FEATURE_REMOTE_LOG(    (1 << OPTBIT_remotelog  )) + 0,
+	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,
+};
+#define OPTION_STR "m:nO:l:Su" \
+	IF_FEATURE_ROTATE_LOGFILE("s:" ) \
+	IF_FEATURE_ROTATE_LOGFILE("b:" ) \
+	IF_FEATURE_REMOTE_LOG(    "R:" ) \
+	IF_FEATURE_REMOTE_LOG(    "L"  ) \
+	IF_FEATURE_IPC_SYSLOG(    "C::") \
+	IF_FEATURE_SYSLOGD_DUP(   "D"  ) \
+	IF_FEATURE_SYSLOGD_CFG(   "f:"  )
+#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) \
+	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_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, parser->lineno);
+}
+#endif
+
+/* circular buffer variables/structures */
+#if ENABLE_FEATURE_IPC_SYSLOG
+
+#if CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE < 4
+#error Sorry, you must set the syslogd buffer size to at least 4KB.
+#error Please check CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE
+#endif
+
+/* our shared key (syslogd.c and logread.c must be in sync) */
+enum { KEY_ID = 0x414e4547 }; /* "GENA" */
+
+static void ipcsyslog_cleanup(void)
+{
+	if (G.shmid != -1) {
+		shmdt(G.shbuf);
+	}
+	if (G.shmid != -1) {
+		shmctl(G.shmid, IPC_RMID, NULL);
+	}
+	if (G.s_semid != -1) {
+		semctl(G.s_semid, 0, IPC_RMID, 0);
+	}
+}
+
+static void ipcsyslog_init(void)
+{
+	if (DEBUG)
+		printf("shmget(%x, %d,...)\n", (int)KEY_ID, G.shm_size);
+
+	G.shmid = shmget(KEY_ID, G.shm_size, IPC_CREAT | 0644);
+	if (G.shmid == -1) {
+		bb_perror_msg_and_die("shmget");
+	}
+
+	G.shbuf = shmat(G.shmid, NULL, 0);
+	if (G.shbuf == (void*) -1L) { /* shmat has bizarre error return */
+		bb_perror_msg_and_die("shmat");
+	}
+
+	memset(G.shbuf, 0, G.shm_size);
+	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) */
+	G.s_semid = semget(KEY_ID, 2, IPC_CREAT | IPC_EXCL | 1023);
+	if (G.s_semid == -1) {
+		if (errno == EEXIST) {
+			G.s_semid = semget(KEY_ID, 2, 0);
+			if (G.s_semid != -1)
+				return;
+		}
+		bb_perror_msg_and_die("semget");
+	}
+}
+
+/* Write message to shared mem buffer */
+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");
+	}
+
+	/* Circular Buffer Algorithm:
+	 * --------------------------
+	 * tail == position where to store next syslog message.
+	 * tail's max value is (shbuf->size - 1)
+	 * Last byte of buffer is never used and remains NUL.
+	 */
+	len = strlen(msg) + 1; /* length with NUL included */
+ again:
+	old_tail = G.shbuf->tail;
+	new_tail = old_tail + len;
+	if (new_tail < G.shbuf->size) {
+		/* store message, set new tail */
+		memcpy(G.shbuf->data + old_tail, msg, len);
+		G.shbuf->tail = new_tail;
+	} else {
+		/* k == available buffer space ahead of old tail */
+		int k = G.shbuf->size - old_tail;
+		/* copy what fits to the end of buffer, and repeat */
+		memcpy(G.shbuf->data + old_tail, msg, k);
+		msg += k;
+		len -= k;
+		G.shbuf->tail = 0;
+		goto again;
+	}
+	if (semop(G.s_semid, G.SMwup, 1) == -1) {
+		bb_perror_msg_and_die("SMwup");
+	}
+	if (DEBUG)
+		printf("tail:%d\n", G.shbuf->tail);
+}
+#else
+void ipcsyslog_cleanup(void);
+void ipcsyslog_init(void);
+void log_to_shmem(const char *msg);
+#endif /* FEATURE_IPC_SYSLOG */
+
+/* Print a message to the log file. */
+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 (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
+		 * _at most_ once a second.
+		 */
+		if (!now)
+			now = time(NULL);
+		if (G.last_log_time != now) {
+			G.last_log_time = now;
+			close(log_file->fd);
+			goto reopen;
+		}
+	} else {
+ reopen:
+		log_file->fd = open(log_file->path, O_WRONLY | O_CREAT
+					| O_NOCTTY | O_APPEND | O_NONBLOCK,
+					0666);
+		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)
+				fd = 2; /* then stderr, dammit */
+			full_write(fd, msg, len);
+			if (fd != 2)
+				close(fd);
+			return;
+		}
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+		{
+			struct stat statf;
+			log_file->isRegular = (fstat(log_file->fd, &statf) == 0 && S_ISREG(statf.st_mode));
+			/* bug (mostly harmless): can wrap around if file > 4gb */
+			log_file->size = statf.st_size;
+		}
+#endif
+	}
+
+#ifdef SYSLOGD_WRLOCK
+	fl.l_whence = SEEK_SET;
+	fl.l_start = 0;
+	fl.l_len = 1;
+	fl.l_type = F_WRLCK;
+	fcntl(log_file->fd, F_SETLKW, &fl);
+#endif
+
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+	if (G.logFileSize && log_file->isRegular && log_file->size > G.logFileSize) {
+		if (G.logFileRotate) { /* always 0..99 */
+			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", log_file->path, i);
+				if (i == 0) break;
+				sprintf(oldFile, "%s.%d", log_file->path, --i);
+				/* ignore errors - file might be missing */
+				rename(oldFile, newFile);
+			}
+			/* newFile == "f.0" now */
+			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(log_file->fd, F_SETLKW, &fl);
+#endif
+			close(log_file->fd);
+			goto reopen;
+		}
+		ftruncate(log_file->fd, 0);
+	}
+	log_file->size +=
+#endif
+			full_write(log_file->fd, msg, len);
+#ifdef SYSLOGD_WRLOCK
+	fl.l_type = F_UNLCK;
+	fcntl(log_file->fd, F_SETLKW, &fl);
+#endif
+}
+
+static void parse_fac_prio_20(int pri, char *res20)
+{
+	const CODE *c_pri, *c_fac;
+
+	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);
+}
+
+static struct tm * generate_time(struct timeval *tvp, struct tm *tmp)
+{
+	struct tm *tm;
+
+	gettimeofday(tvp, NULL);
+
+	if (option_mask32 & OPT_utc) {
+		tm = gmtime_r(&tvp->tv_sec, tmp);
+	} else {
+		tm = localtime_r(&tvp->tv_sec, tmp);
+	}
+
+	return tm;
+}
+
+/* len parameter is used only for "is there a timestamp?" check.
+ * NB: some callers cheat and supply len==0 when they know
+ * that there is no timestamp, short-circuiting the test. */
+static void timestamp_and_log(int pri, char *msg, int len)
+{
+	char timestamp[27];
+	struct timeval tvnow;
+	struct tm parsed, tmnow, *tmp;
+	size_t n;
+
+	/* Jan 18 00:11:22 msg... */
+	/* 01234567890123456 */
+	if (len < 16 || msg[3] != ' ' || msg[6] != ' '
+	 || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
+	) {
+		tmp = generate_time(&tvnow, &tmnow);
+	} else {
+		struct tm local;
+		tvnow.tv_sec = time(NULL);
+		localtime_r(&tvnow.tv_sec, &local);
+		if (strptime(msg, "%h %e %T", &parsed) != NULL) {
+
+			parsed.tm_gmtoff	= local.tm_gmtoff;
+			parsed.tm_zone		= local.tm_zone;
+			parsed.tm_year		= local.tm_year;
+			parsed.tm_isdst		= local.tm_isdst;
+			tvnow.tv_sec = mktime(&parsed);
+			tvnow.tv_usec = 0;
+
+			if (option_mask32 & OPT_utc) {
+				tmp = gmtime_r(&tvnow.tv_sec, &tmnow);
+			} else {
+				tmp = &parsed;
+			}
+		} else {
+			tmp  = generate_time(&tvnow, &tmnow);
+		}
+		msg += 16;
+	}
+	n = strftime(timestamp, sizeof(timestamp), "%F %T", tmp);
+	snprintf(timestamp + n, sizeof(timestamp) - n, ".%06ld", tvnow.tv_usec);
+
+	if (option_mask32 & OPT_small)
+		sprintf(G.printbuf, "%s %s\n", timestamp, msg);
+	else {
+		char res[20];
+		parse_fac_prio_20(pri, res);
+		sprintf(G.printbuf, "%s %.64s %s %s\n", timestamp, G.hostname, res, msg);
+	}
+
+	/* Log message locally (to file or shared mem) */
+#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(tvnow.tv_sec, 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(tvnow.tv_sec, G.printbuf, &G.logFile);
+	}
+}
+
+static void timestamp_and_log_internal(const char *msg)
+{
+	/* -L, or no -R */
+	if (ENABLE_FEATURE_REMOTE_LOG && !(option_mask32 & OPT_locallog))
+		return;
+	timestamp_and_log(LOG_SYSLOG | LOG_INFO, (char*)msg, 0);
+}
+
+/* tmpbuf[len] is a NUL byte (set by caller), but there can be other,
+ * embedded NULs. Split messages on each of these NULs, parse prio,
+ * escape control chars and log each locally. */
+static void split_escape_and_log(char *tmpbuf, int len)
+{
+	char *p = tmpbuf;
+
+	tmpbuf += len;
+	while (p < tmpbuf) {
+		char c;
+		char *q = G.parsebuf;
+		int pri = (LOG_USER | LOG_NOTICE);
+
+		if (*p == '<') {
+			/* Parse the magic priority number */
+			pri = bb_strtou(p + 1, &p, 10);
+			if (*p == '>')
+				p++;
+			if (pri & ~(LOG_FACMASK | LOG_PRIMASK))
+				pri = (LOG_USER | LOG_NOTICE);
+		}
+
+		while ((c = *p++)) {
+			if (c == '\n')
+				c = ' ';
+			if (!(c & ~0x1f) && c != '\t') {
+				*q++ = '^';
+				c += '@'; /* ^@, ^A, ^B... */
+			}
+			*q++ = c;
+		}
+		*q = '\0';
+
+		/* Now log it */
+		timestamp_and_log(pri, G.parsebuf, q - G.parsebuf);
+	}
+}
+
+#ifdef SYSLOGD_MARK
+static void do_mark(int sig)
+{
+	if (G.markInterval) {
+		timestamp_and_log_internal("-- MARK --");
+		alarm(G.markInterval);
+	}
+}
+#endif
+
+/* Don't inline: prevent struct sockaddr_un to take up space on stack
+ * permanently */
+static NOINLINE int create_socket(void)
+{
+	struct sockaddr_un sunx;
+	int sock_fd;
+	char *dev_log_name;
+
+#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");
+	if (dev_log_name) {
+		safe_strncpy(sunx.sun_path, dev_log_name, sizeof(sunx.sun_path));
+		free(dev_log_name);
+	}
+	unlink(sunx.sun_path);
+
+	sock_fd = xsocket(AF_UNIX, SOCK_DGRAM, 0);
+	xbind(sock_fd, (struct sockaddr *) &sunx, sizeof(sunx));
+	chmod("/dev/log", 0666);
+
+	return sock_fd;
+}
+
+#if ENABLE_FEATURE_REMOTE_LOG
+static int try_to_resolve_remote(remoteHost_t *rh)
+{
+	if (!rh->remoteAddr) {
+		unsigned now = monotonic_sec();
+
+		/* Don't resolve name too often - DNS timeouts can be big */
+		if ((now - rh->last_dns_resolve) < DNS_WAIT_SEC)
+			return -1;
+		rh->last_dns_resolve = now;
+		rh->remoteAddr = host2sockaddr(rh->remoteHostname, 514);
+		if (!rh->remoteAddr)
+			return -1;
+	}
+	return xsocket(rh->remoteAddr->u.sa.sa_family, SOCK_DGRAM, 0);
+}
+#endif
+
+static void do_syslogd(void) NORETURN;
+static void do_syslogd(void)
+{
+	int sock_fd;
+#if ENABLE_FEATURE_REMOTE_LOG
+	llist_t *item;
+#endif
+#if ENABLE_FEATURE_SYSLOGD_DUP
+	int last_sz = -1;
+	char *last_buf;
+	char *recvbuf = G.recvbuf;
+#else
+#define recvbuf (G.recvbuf)
+#endif
+
+	/* Set up signal handlers (so that they interrupt read()) */
+	signal_no_SA_RESTART_empty_mask(SIGTERM, record_signo);
+	signal_no_SA_RESTART_empty_mask(SIGINT, record_signo);
+	//signal_no_SA_RESTART_empty_mask(SIGQUIT, record_signo);
+	signal(SIGHUP, SIG_IGN);
+#ifdef SYSLOGD_MARK
+	signal(SIGALRM, do_mark);
+	alarm(G.markInterval);
+#endif
+	sock_fd = create_socket();
+
+	if (ENABLE_FEATURE_IPC_SYSLOG && (option_mask32 & OPT_circularlog)) {
+		ipcsyslog_init();
+	}
+
+	timestamp_and_log_internal("syslogd started: BusyBox v" BB_VER);
+
+	while (!bb_got_signal) {
+		ssize_t sz;
+
+#if ENABLE_FEATURE_SYSLOGD_DUP
+		last_buf = recvbuf;
+		if (recvbuf == G.recvbuf)
+			recvbuf = G.recvbuf + MAX_READ;
+		else
+			recvbuf = G.recvbuf;
+#endif
+ read_again:
+		sz = read(sock_fd, recvbuf, MAX_READ - 1);
+		if (sz < 0) {
+			if (!bb_got_signal)
+				bb_perror_msg("read from /dev/log");
+			break;
+		}
+
+		/* Drop trailing '\n' and NULs (typically there is one NUL) */
+		while (1) {
+			if (sz == 0)
+				goto read_again;
+			/* man 3 syslog says: "A trailing newline is added when needed".
+			 * However, neither glibc nor uclibc do this:
+			 * syslog(prio, "test")   sends "test\0" to /dev/log,
+			 * syslog(prio, "test\n") sends "test\n\0".
+			 * IOW: newline is passed verbatim!
+			 * I take it to mean that it's syslogd's job
+			 * to make those look identical in the log files. */
+			if (recvbuf[sz-1] != '\0' && recvbuf[sz-1] != '\n')
+				break;
+			sz--;
+		}
+#if ENABLE_FEATURE_SYSLOGD_DUP
+		if ((option_mask32 & OPT_dup) && (sz == last_sz))
+			if (memcmp(last_buf, recvbuf, sz) == 0)
+				continue;
+		last_sz = sz;
+#endif
+#if ENABLE_FEATURE_REMOTE_LOG
+		/* Stock syslogd sends it '\n'-terminated
+		 * over network, mimic that */
+		recvbuf[sz] = '\n';
+
+		/* We are not modifying log messages in any way before send */
+		/* Remote site cannot trust _us_ anyway and need to do validation again */
+		for (item = G.remoteHosts; item != NULL; item = item->link) {
+			remoteHost_t *rh = (remoteHost_t *)item->data;
+
+			if (rh->remoteFD == -1) {
+				rh->remoteFD = try_to_resolve_remote(rh);
+				if (rh->remoteFD == -1)
+					continue;
+			}
+
+			/* 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)) {
+			recvbuf[sz] = '\0'; /* ensure it *is* NUL terminated */
+			split_escape_and_log(recvbuf, sz);
+		}
+	} /* while (!bb_got_signal) */
+
+	timestamp_and_log_internal("syslogd exiting");
+	puts("syslogd exiting");
+	if (ENABLE_FEATURE_IPC_SYSLOG)
+		ipcsyslog_cleanup();
+	kill_myself_with_sig(bb_got_signal);
+#undef recvbuf
+}
+
+int syslogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int syslogd_main(int argc UNUSED_PARAM, char **argv)
+{
+	int opts;
+	char OPTION_DECL;
+#if ENABLE_FEATURE_REMOTE_LOG
+	llist_t *remoteAddrList = NULL;
+#endif
+
+	INIT_G();
+
+	/* No non-option params, -R can occur multiple times */
+	opt_complementary = "=0" IF_FEATURE_REMOTE_LOG(":R::");
+	opts = getopt32(argv, OPTION_STR, OPTION_PARAM);
+#if ENABLE_FEATURE_REMOTE_LOG
+	while (remoteAddrList) {
+		remoteHost_t *rh = xzalloc(sizeof(*rh));
+		rh->remoteHostname = llist_pop(&remoteAddrList);
+		rh->remoteFD = -1;
+		rh->last_dns_resolve = monotonic_sec() - DNS_WAIT_SEC - 1;
+		llist_add_to(&G.remoteHosts, rh);
+	}
+#endif
+
+#ifdef SYSLOGD_MARK
+	if (opts & OPT_mark) // -m
+		G.markInterval = xatou_range(opt_m, 0, INT_MAX/60) * 60;
+#endif
+	//if (opts & OPT_nofork) // -n
+	//if (opts & OPT_outfile) // -O
+	if (opts & OPT_loglevel) // -l
+		G.logLevel = xatou_range(opt_l, 1, 8);
+	//if (opts & OPT_small) // -S
+#if ENABLE_FEATURE_ROTATE_LOGFILE
+	if (opts & OPT_filesize) // -s
+		G.logFileSize = xatou_range(opt_s, 0, INT_MAX/1024) * 1024;
+	if (opts & OPT_rotatecnt) // -b
+		G.logFileRotate = xatou_range(opt_b, 0, 99);
+#endif
+#if ENABLE_FEATURE_IPC_SYSLOG
+	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();
+	*strchrnul(G.hostname, '.') = '\0';
+
+	if (!(opts & OPT_nofork)) {
+		bb_daemonize_or_rexec(DAEMON_CHDIR_ROOT, argv);
+	}
+	//umask(0); - why??
+	write_pidfile("/var/run/syslogd.pid");
+	do_syslogd();
+	/* return EXIT_SUCCESS; */
+}
+
+/* Clean up. Needed because we are included from syslogd_and_logger.c */
+#undef DEBUG
+#undef SYSLOGD_MARK
+#undef SYSLOGD_WRLOCK
+#undef G
+#undef GLOBALS
+#undef INIT_G
+#undef OPTION_STR
+#undef OPTION_DECL
+#undef OPTION_PARAM
diff --git a/busybox-1.19.3/sysklogd/syslogd_and_logger.c b/busybox-1.19.3/sysklogd/syslogd_and_logger.c
new file mode 100644
index 0000000..0964f23
--- /dev/null
+++ b/busybox-1.19.3/sysklogd/syslogd_and_logger.c
@@ -0,0 +1,51 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * prioritynames[] and facilitynames[]
+ *
+ * Copyright (C) 2008 by Denys Vlasenko <vda.linux@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#include "libbb.h"
+#define SYSLOG_NAMES
+#define SYSLOG_NAMES_CONST
+#include <syslog.h>
+
+#if 0
+/* For the record: with SYSLOG_NAMES <syslog.h> defines
+ * (not declares) the following:
+ */
+typedef struct _code {
+	/*const*/ char *c_name;
+	int c_val;
+} CODE;
+/*const*/ CODE prioritynames[] = {
+    { "alert", LOG_ALERT },
+...
+    { NULL, -1 }
+};
+/* same for facilitynames[] */
+
+/* This MUST occur only once per entire executable,
+ * therefore we can't just do it in syslogd.c and logger.c -
+ * there will be two copies of it.
+ *
+ * We cannot even do it in separate file and then just reference
+ * prioritynames[] from syslogd.c and logger.c - bare <syslog.h>
+ * will not emit extern decls for prioritynames[]! Attempts to
+ * emit "matching" struct _code declaration defeat the whole purpose
+ * of <syslog.h>.
+ *
+ * For now, syslogd.c and logger.c are simply compiled into
+ * one object file.
+ */
+#endif
+
+#if ENABLE_SYSLOGD
+#include "syslogd.c"
+#endif
+
+#if ENABLE_LOGGER
+#include "logger.c"
+#endif
diff --git a/busybox-1.19.3/testsuite/README b/busybox-1.19.3/testsuite/README
new file mode 100644
index 0000000..b943a12
--- /dev/null
+++ b/busybox-1.19.3/testsuite/README
@@ -0,0 +1,52 @@
+To run the test suite, change to this directory and run "./runtest".  It will
+run all of the test cases, and list those with unexpected outcomes.  Adding the
+-v option will cause it to show expected outcomes as well.  To only run the test
+cases for particular applets:
+
+./runtest <applet1> <applet2>...
+
+Set SKIP_KNOWN_BUGS environment variable to any non-empty value
+to exclude tests which are known to fail.
+
+Set SKIP_INTERNET_TESTS to exclude tests which require working
+internet connection.
+
+
+Common causes of false positives:
+
+For busybox built against uclibc, /etc/TZ does not exist or does not match
+host system timezone setting. For glibc based host systems, timezone settings
+are in /etc/localtime.
+
+LANG and LC_xxx environment variables set to non-C locale.
+
+
+Developer's notes:
+
+The test cases for an applet reside in the subdirectory of the applet name.
+The name of the test case should be the assertion that is tested.
+The test case should be a shell fragment that returns successfully
+if the test case passes, and unsuccessfully otherwise.
+
+If the test case relies on a certain feature, it should include the string
+"FEATURE: " followed by the name of the feature in a comment.  If it is always
+expected to fail, it should include the string "XFAIL" in a comment.
+
+
+For the entire testsuite, the copyright is as follows:
+
+Copyright (C) 2001, 2002  Matt Kraai
+
+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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
diff --git a/busybox-1.19.3/testsuite/TODO b/busybox-1.19.3/testsuite/TODO
new file mode 100644
index 0000000..b8957f4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/TODO
@@ -0,0 +1,26 @@
+This testsuite is quite obviously a work in progress.  As such,
+there are a number of good extensions.  If you are looking for
+something to do, feel free to tackle one or more of the following:
+
+Moving to the new format.
+	The old way was "lots of little tests files in a directory", which
+	doesn't interact well with source control systems.  The new test
+	format is command.tests files that use testing.sh.
+
+Every busybox applet needs a corresponding applet.tests.
+
+Full SUSv3 test suite.
+	Let's make the Linux Test Project jealous, shall we?  Don't just
+	audit programs for standards compliance, _prove_ it with a regression
+	test harness.
+
+	http://www.opengroup.org/onlinepubs/009695399/utilities/
+
+Some tests need root access.
+	It's hard to test things like mount or init as a normal user.
+	Possibly User Mode Linux could be used for this, or perhaps
+	Erik's buildroot.
+
+libbb unit testing
+	Being able to test the functions of libbb individually may
+	help to prevent regressions.
diff --git a/busybox-1.19.3/testsuite/all_sourcecode.tests b/busybox-1.19.3/testsuite/all_sourcecode.tests
new file mode 100755
index 0000000..7dcbbe8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/all_sourcecode.tests
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+# Tests for the sourcecode base itself.
+# Copyright 2006 by Mike Frysinger <vapier@gentoo.org>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+[ -n "$srcdir" ] || srcdir=$(pwd)
+. ./testing.sh
+
+
+#
+# if we don't have the sourcecode available, let's just bail
+#
+[ -s "$srcdir/../Makefile" ] || exit 0
+[ -s "$srcdir/../include/applets.h" ] || exit 0
+
+
+#
+# make sure all usage strings are properly escaped.  oftentimes people miss
+# an escape sequence so we end up with:
+# #define foo_usage \
+#       " this line is ok" \
+#       " as is this line"
+#       " but this one is broken as the \ is missing from above"
+#
+${CROSS_COMPILE}cpp -dD -P $srcdir/../include/usage.h \
+	| sed -e '/^#define/d' -e '/^$/d' > src.usage.escaped
+testing "Usage strings escaped" "cat src.usage.escaped" "" "" ""
+rm -f src.usage.escaped
+
+
+#
+# verify the applet order is correct in applets.h, otherwise
+# applets won't be called properly.
+#
+sed -n -e 's:^//::' -e '/^IF_[A-Z]*(APPLET/{s:,.*::;s:.*(::;s:"::g;p}' \
+	$srcdir/../include/applets.h > applet.order.current
+LC_ALL=C sort applet.order.current > applet.order.correct
+testing "Applet order" "diff -u applet.order.current applet.order.correct" "" "" ""
+rm -f applet.order.current applet.order.correct
+
+
+#
+# check for misc common typos
+#
+find $srcdir/../ \
+	'(' -type d -a '(' -name .svn -o -name testsuite ')' -prune ')' \
+	-o '(' -type f -a -print0 ')' | xargs -0 \
+	grep -I \
+		-e '\<compatability\>' \
+		-e '\<compatable\>' \
+		-e '\<fordeground\>' \
+		-e '\<depency\>' -e '\<dependancy\>' -e '\<dependancies\>' \
+		-e '\<defalt\>' \
+		-e '\<remaing\>' \
+		-e '\<queueing\>' \
+		-e '\<detatch\>' \
+		-e '\<sempahore\>' \
+		-e '\<reprenstative\>' \
+		-e '\<overriden\>' \
+		-e '\<readed\>' \
+		-e '\<formated\>' \
+		-e '\<algorithic\>' \
+		-e '\<deamon\>' \
+		-e '\<derefernce\>' \
+		-e '\<acomadate\>' \
+		| sed -e "s:^$srcdir/\.\./::g" > src.typos
+testing "Common typos" "cat src.typos" "" "" ""
+rm -f src.typos
+
+
+#
+# don't allow obsolete functions
+#
+find $srcdir/.. '(' -name '*.c' -o -name '*.h' ')' -print0 | xargs -0 \
+	grep -E -e '\<(bcmp|bcopy|bzero|getwd|index|mktemp|rindex|utime|sigblock|siggetmask|sigsetmask)\>[[:space:]]*\(' \
+	| sed -e "s:^$srcdir/\.\./::g" > src.obsolete.funcs
+testing "Obsolete function usage" "cat src.obsolete.funcs" "" "" ""
+rm -f src.obsolete.funcs
+
+
+#
+# don't allow obsolete headers
+#
+find $srcdir/.. '(' -name '*.c' -o -name '*.h' ')' -print0 | xargs -0 \
+	grep -E -e '\<(malloc|memory|sys/(errno|fcntl|signal|stropts|termios|unistd))\.h\>' \
+	| sed -e "s:^$srcdir/\.\./::g" > src.obsolete.headers
+testing "Obsolete headers" "cat src.obsolete.headers" "" "" ""
+rm -f src.obsolete.headers
+
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/ar.tests b/busybox-1.19.3/testsuite/ar.tests
new file mode 100755
index 0000000..0a8eb9b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ar.tests
@@ -0,0 +1,27 @@
+#!/bin/sh
+# Copyright 2010 Nokia Corporation
+# Written by Alexander Shishkin
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+
+optional FEATURE_AR_CREATE
+
+rm test.a 2>/dev/null
+testing "ar creates archives" \
+       "ar rc test.a README && ar p test.a README | md5sum" \
+       "$(md5sum <README)\n" \
+       "" \
+       ""
+rm test.a
+
+testing "ar replaces things in archives" \
+       "echo 'blah!' >file1 && echo 'blast!' >file2 && ar cr test.a README file1 file2 && mv file2 file1 && ar cr test.a file1 && ar p test.a file1" \
+       "blast!\n" \
+       "" \
+       ""
+rm test.a file1 file1 2>/dev/null
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/ash.tests b/busybox-1.19.3/testsuite/ash.tests
new file mode 100755
index 0000000..2a99245
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ash.tests
@@ -0,0 +1,83 @@
+#!/bin/sh
+#
+# These are not ash tests, we use ash as a way to test lineedit!
+#
+# Copyright 2010 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+test -f "$bindir/.config" && . "$bindir/.config"
+
+test x"CONFIG_SCRIPT" = x"y" || exit 0
+test x"CONFIG_HEXDUMP" = x"y" || exit 0
+test x"CONFIG_FEATURE_DEVPTS" = x"y" || exit 0
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+if test x"$CONFIG_UNICODE_PRESERVE_BROKEN" = x"y"; then
+testing "One byte which is not valid unicode char followed by valid input" \
+	"script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \
+	"\
+00000000  ff 2d 0a                                          |.-.|
+00000003
+" \
+	"" \
+	"echo \xff- | hexdump -C >ash.output; exit; exit; exit; exit\n"
+
+testing "30 bytes which are not valid unicode chars followed by valid input" \
+	"script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \
+	"\
+00000000  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
+00000010  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff 2d 0a  |..............-.|
+00000020
+" \
+	"" \
+	"echo \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff- | hexdump -C >ash.output; exit; exit; exit; exit\n"
+else
+testing "One byte which is not valid unicode char followed by valid input" \
+	"script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \
+	"\
+00000000  3f 2d 0a                                          |?-.|
+00000003
+" \
+	"" \
+	"echo \xff- | hexdump -C >ash.output; exit; exit; exit; exit\n"
+
+testing "30 bytes which are not valid unicode chars followed by valid input" \
+	"script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \
+	"\
+00000000  3f 3f 3f 3f 3f 3f 3f 3f  3f 3f 3f 3f 3f 3f 3f 3f  |????????????????|
+00000010  3f 3f 3f 3f 3f 3f 3f 3f  3f 3f 3f 3f 3f 3f 2d 0a  |??????????????-.|
+00000020
+" \
+	"" \
+	"echo \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff- | hexdump -C >ash.output; exit; exit; exit; exit\n"
+fi
+
+
+# Not sure this behavior is perfect: we lose all invalid input which precedes
+# arrow keys and such. In this example, \xff\xff are lost
+testing "2 bytes which are not valid unicode chars followed by left arrow key" \
+	"script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \
+	"\
+00000000  3d 2d 0a                                          |=-.|
+00000003
+" \
+	"" \
+	"echo =+\xff\xff\x1b\x5b\x44- | hexdump -C >ash.output; exit; exit; exit; exit\n"
+
+# ash should see "echo \xff\n",pause -> execute it as "echo ?" (which is
+# not checked by the test), then read and execute the rest: "echo A | ..."
+# The bug was that ash was eating the beginning of "echo A" despite the pause.
+testing "Invalid unicode chars followed by a pause do not eat next chars" \
+	"{ $ECHO -ne 'echo \xff\n'; sleep 1; $ECHO -ne 'echo A | hexdump -C >ash.output; exit; exit; exit; exit\n'; } \
+         | script -q -c 'ash' /dev/null >/dev/null; cat ash.output" \
+	"\
+00000000  41 0a                                             |A.|
+00000002
+" \
+	"" ""
+
+rm ash.output
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/awk.tests b/busybox-1.19.3/testsuite/awk.tests
new file mode 100755
index 0000000..0afe9b9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/awk.tests
@@ -0,0 +1,205 @@
+#!/bin/sh
+
+# Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "description" "command" "result" "infile" "stdin"
+
+testing "awk -F case 0" "awk -F '[#]' '{ print NF }'" ""    "" ""
+testing "awk -F case 1" "awk -F '[#]' '{ print NF }'" "0\n" "" "\n"
+testing "awk -F case 2" "awk -F '[#]' '{ print NF }'" "2\n" "" "#\n"
+testing "awk -F case 3" "awk -F '[#]' '{ print NF }'" "3\n" "" "#abc#\n"
+testing "awk -F case 4" "awk -F '[#]' '{ print NF }'" "3\n" "" "#abc#zz\n"
+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"
+
+# 4294967295 = 0xffffffff
+testing "awk bitwise op"  "awk '{ print or(4294967295,1) }'" "4.29497e+09\n" "" "\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 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--"
+
+# '@(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" \
+	"awk 'gsub(\"@(samp|code|file)\{\",\"\");'; echo \$?" "0\n" "" "Hi\n"
+
+optional TAR BUNZIP2 FEATURE_SEAMLESS_BZ2
+test x"$SKIP" != x"1" && tar xjf awk_t1.tar.bz2
+testing "awk 'gcc build bug'" \
+	"awk -f awk_t1_opt-functions.awk -f awk_t1_opth-gen.awk <awk_t1_input | md5sum" \
+	"f842e256461a5ab1ec60b58d16f1114f  -\n" \
+	"" ""
+rm -rf awk_t1_* 2>/dev/null
+SKIP=
+
+Q='":"'
+
+testing "awk NF in BEGIN" \
+	"awk 'BEGIN { print ${Q} NF ${Q} \$0 ${Q} \$1 ${Q} \$2 ${Q} }'" \
+	":0::::\n" \
+	"" ""
+
+prg='
+function b(tmp) {
+	tmp = 0;
+	print "" tmp; #this line causes the bug
+	return tmp;
+}
+function c(tmpc) {
+	tmpc = b(); return tmpc;
+}
+BEGIN {
+	print (c() ? "string" : "number");
+}'
+testing "awk string cast (bug 725)" \
+	"awk '$prg'" \
+	"0\nnumber\n" \
+	"" ""
+
+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
+  u["b"]=1
+  u["c"]=1
+  v["d"]=1
+  v["e"]=1
+  v["f"]=1
+  for (l in u) {
+    print "outer1", l;
+    for (l in v) {
+      print " inner", l;
+    }
+    print "outer2", l;
+  }
+  print "end", l;
+  l="a"
+  exit;
+}'
+testing "awk nested loops with the same variable" \
+	"awk '$prg'" \
+	"\
+outer1 a
+ inner d
+ inner e
+ inner f
+outer2 f
+outer1 b
+ inner d
+ inner e
+ inner f
+outer2 f
+outer1 c
+ inner d
+ inner e
+ inner f
+outer2 f
+end f
+" \
+	"" ""
+
+prg='
+BEGIN {
+  u["a"]=1
+  u["b"]=1
+  u["c"]=1
+  v["d"]=1
+  v["e"]=1
+  v["f"]=1
+  for (l in u) {
+    print "outer1", l;
+    for (l in v) {
+      print " inner", l;
+      break;
+    }
+    print "outer2", l;
+  }
+  print "end", l;
+  l="a"
+  exit;
+}'
+# It's not just buggy, it enters infinite loop. Thus disabled
+false && test x"$SKIP_KNOWN_BUGS" = x"" && testing "awk nested loops with the same variable and break" \
+	"awk '$prg'" \
+	"\
+outer1 a
+ inner d
+outer2 d
+outer1 b
+ inner d
+outer2 d
+outer1 c
+ inner d
+outer2 d
+end d
+" \
+	"" ""
+
+prg='
+function f() {
+  for (l in v) {
+    print " inner", l;
+    return;
+  }
+}
+
+BEGIN {
+  u["a"]=1
+  u["b"]=1
+  u["c"]=1
+  v["d"]=1
+  v["e"]=1
+  v["f"]=1
+  for (l in u) {
+    print "outer1", l;
+    f();
+    print "outer2", l;
+  }
+  print "end", l;
+  l="a"
+  exit;
+}'
+# It's not just buggy, it enters infinite loop. Thus disabled
+false && test x"$SKIP_KNOWN_BUGS" = x"" && testing "awk nested loops with the same variable and return" \
+	"awk '$prg'" \
+	"\
+outer1 a
+ inner d
+outer2 d
+outer1 b
+ inner d
+outer2 d
+outer1 c
+ inner d
+outer2 d
+end d
+" \
+	"" ""
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/awk_t1.tar.bz2 b/busybox-1.19.3/testsuite/awk_t1.tar.bz2
new file mode 100644
index 0000000..0fb8a07
--- /dev/null
+++ b/busybox-1.19.3/testsuite/awk_t1.tar.bz2
Binary files differ
diff --git a/busybox-1.19.3/testsuite/basename/basename-does-not-remove-identical-extension b/busybox-1.19.3/testsuite/basename/basename-does-not-remove-identical-extension
new file mode 100644
index 0000000..4448fde
--- /dev/null
+++ b/busybox-1.19.3/testsuite/basename/basename-does-not-remove-identical-extension
@@ -0,0 +1 @@
+test xfoo = x`busybox basename foo foo`
diff --git a/busybox-1.19.3/testsuite/basename/basename-works b/busybox-1.19.3/testsuite/basename/basename-works
new file mode 100644
index 0000000..7140e99
--- /dev/null
+++ b/busybox-1.19.3/testsuite/basename/basename-works
@@ -0,0 +1 @@
+test x$(basename $(pwd)) = x$(busybox basename $(pwd))
diff --git a/busybox-1.19.3/testsuite/bunzip2.tests b/busybox-1.19.3/testsuite/bunzip2.tests
new file mode 100755
index 0000000..fcfce1a
--- /dev/null
+++ b/busybox-1.19.3/testsuite/bunzip2.tests
@@ -0,0 +1,557 @@
+#!/bin/sh
+# Used by both gunzip and bunzip2 tests
+
+FAILCOUNT=0
+
+if test "${0##*/}" = "gunzip.tests"; then
+    unpack=gunzip
+    ext=gz
+elif test "${0##*/}" = "bunzip2.tests"; then
+    unpack=bunzip2
+    ext=bz2
+else
+    echo "WTF? argv0='$0'"
+    exit 1
+fi
+
+bb="busybox "
+
+unset LC_ALL
+unset LC_MESSAGES
+unset LANG
+unset LANGUAGE
+
+hello_gz() {
+# Gzipped "HELLO\n"
+#_________________________ vvv vvv vvv vvv - mtime
+$ECHO -ne "\x1f\x8b\x08\x00\x85\x1d\xef\x45\x02\x03\xf3\x70\xf5\xf1\xf1\xe7"
+$ECHO -ne "\x02\x00\x6e\xd7\xac\xfd\x06\x00\x00\x00"
+}
+
+hello_bz2() {
+# Bzipped "HELLO\n"
+$ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x5b\xb8\xe8\xa3\x00\x00"
+$ECHO -ne "\x01\x44\x00\x00\x10\x02\x44\xa0\x00\x30\xcd\x00\xc3\x46\x29\x97"
+$ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3"
+}
+
+# We had bunzip2 error on this .bz2 file (fixed in rev 22521)
+test1_bz2()
+{
+$ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\xbf\x4b\x95\xe7\x00\x15"
+$ECHO -ne "\xa1\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+$ECHO -ne "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xef\xff\xff\xfb\xff\xff\xff"
+$ECHO -ne "\xff\xff\xff\xe0\x16\xe6\x37\xb7\x77\xb0\xfb\x22\xb5\x81\x40\xf5"
+$ECHO -ne "\xa7\x69\xa4\x47\xab\x61\x4d\x6a\x3d\xef\x75\x7b\x22\xaf\x71\x9b"
+$ECHO -ne "\xb3\x5a\x93\xbb\x78\x00\x79\xbd\xc0\x07\xae\x1b\xdb\xc5\xc6\x6b"
+$ECHO -ne "\x7a\x7b\xd3\xd7\x38\xbb\x70\x5e\xf0\xd1\x08\x9a\x64\x26\x53\x68"
+$ECHO -ne "\x03\x53\x4d\x32\x30\xd2\x61\x31\x13\x68\xd1\xa6\x4c\x41\x89\x84"
+$ECHO -ne "\x31\x4f\x40\xd2\x79\x14\xf0\xa3\xda\x1a\x00\x65\x4f\x53\xd9\x28"
+$ECHO -ne "\xd3\xf4\x4c\x13\x26\x4c\x4c\x64\x34\x9a\x6a\x7a\x99\x3c\x12\x78"
+$ECHO -ne "\xd2\x68\xd4\xda\x6a\x79\x32\x64\xd1\xa8\x1b\x13\x01\x4f\x29\xea"
+$ECHO -ne "\x6c\xa3\xd2\x07\x94\x06\xa6\x44\x01\x4f\x11\xa3\x4d\x13\x4d\x31"
+$ECHO -ne "\x32\x4c\x98\x0c\x9a\xa6\xda\x29\x3d\xa4\xf1\x24\xfd\x1a\xa3\x7a"
+$ECHO -ne "\x93\xf4\xa7\xb5\x3d\x51\xe2\x47\x94\xf2\x8f\x29\xb2\x9b\x29\xe9"
+$ECHO -ne "\x34\x79\x4f\x46\x9e\x84\x6a\x69\xea\x69\xa7\xa9\xb5\x03\x27\xa8"
+$ECHO -ne "\xf1\x40\x32\x7a\x13\x10\x00\x3d\x41\x90\x00\xd0\x1e\x84\x0d\x1b"
+$ECHO -ne "\x53\x41\xa3\x21\x93\xd0\x83\x53\x10\x99\x34\x24\xf5\x32\x99\x34"
+$ECHO -ne "\xd2\x7a\x86\xca\x6c\x28\xda\x6d\x29\xa6\x4d\x31\x0c\xd4\x7a\x69"
+$ECHO -ne "\x1e\x93\x23\xca\x1e\x93\x4d\x03\x26\x9a\x68\x01\xa0\xc9\xa0\x1a"
+$ECHO -ne "\x00\x34\x00\x00\x69\xa0\xf4\x80\x0d\x00\x00\x34\x06\x86\x80\x34"
+$ECHO -ne "\x00\x00\x00\x34\x00\x48\x88\x84\x53\x68\x4f\x45\x3d\x51\xfa\x4d"
+$ECHO -ne "\x4c\xda\x9a\x8d\xb5\x4c\xd4\xf2\x35\x1b\x51\xb4\xd3\x14\xf5\x0f"
+$ECHO -ne "\x50\xf5\x0f\x24\xd3\x32\x23\xd4\xcd\x21\xa6\xd4\xd0\xd0\x69\xa0"
+$ECHO -ne "\xf4\x8d\x3d\x43\xd3\x51\xea\x6c\x90\xd0\x68\xf4\x40\x00\x07\xa8"
+$ECHO -ne "\x19\x3d\x47\xa8\x1e\xa0\xd0\x34\x00\x0d\x1a\x06\x80\x01\xe9\x00"
+$ECHO -ne "\x64\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+$ECHO -ne "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+$ECHO -ne "\x00\x00\x48\x92\x1a\x46\x9a\x02\x9e\x24\xc5\x4f\xc9\x91\x4f\x29"
+$ECHO -ne "\xec\xa9\xea\x34\xd3\xd2\x68\x68\x00\xf4\xd4\xf5\x19\x00\x34\x00"
+$ECHO -ne "\x06\x4d\x00\xd0\x1a\x1a\x03\x20\x01\xa0\x00\xf5\x00\x0d\x06\x9b"
+$ECHO -ne "\x50\x36\xa3\x35\x00\x03\x40\x03\x40\x68\xf5\x34\x00\x00\x00\x71"
+$ECHO -ne "\xe4\x3c\x0c\x0f\x0f\x3d\x23\x9a\x7f\x31\x80\xae\x1a\xe1\x09\x03"
+$ECHO -ne "\x2c\x61\x63\x18\xfc\x1a\x28\x0e\x9a\x85\xc3\x86\x96\x11\x94\x88"
+$ECHO -ne "\x0f\x92\x11\x8a\x1a\xb4\x61\x8c\x38\x30\xf3\xa9\xfb\xbe\xcd\x8d"
+$ECHO -ne "\xc4\xb7\x7a\x52\xad\x92\x9f\xde\xe6\x75\x74\xb7\xbb\x0b\xf5\x4c"
+$ECHO -ne "\x97\xd9\x49\xc8\x63\x9b\xa6\xba\x95\xe8\x46\x70\x11\x71\x55\x67"
+$ECHO -ne "\x17\xe3\xab\x91\x7d\xbe\x0d\x56\x78\x61\x98\x41\x28\x2c\xb5\xa3"
+$ECHO -ne "\xd1\xb8\x76\x80\xc8\xad\x9b\x79\x6f\x8a\x7a\x84\x44\x35\x24\x64"
+$ECHO -ne "\xa0\xde\x18\x80\x11\x14\xcd\xb0\xc6\xe4\x31\x49\x71\x53\xaf\x5d"
+$ECHO -ne "\x20\xc9\x59\xdc\xa2\x53\x06\xf0\x4f\xc8\x09\x21\x92\x6b\xf2\x37"
+$ECHO -ne "\x32\xcb\x75\x91\x75\x41\xc0\xe8\x47\x02\xb3\x8e\x0b\x17\x47\x42"
+$ECHO -ne "\x79\x21\x3c\xec\x6c\xb0\x84\x24\x96\x45\x4a\x8e\xbb\xbe\xc2\xe0"
+$ECHO -ne "\x16\x85\x76\x43\x26\xd5\xd0\x58\xdd\xf7\x83\x65\x44\x8f\xbe\x6c"
+$ECHO -ne "\x72\xe1\x5b\x1a\x0e\x3a\xbb\x51\xcf\xbd\x9b\x3a\xd0\xd5\x39\x5b"
+$ECHO -ne "\x23\x7d\xc9\x0b\x08\x0a\x23\x7b\x3a\x00\x67\xa1\x76\xa5\x19\xab"
+$ECHO -ne "\x48\xbd\x54\xaa\x8f\xaf\xb6\xe8\xd5\x91\x0f\x3e\x4b\x3a\x8d\xc9"
+$ECHO -ne "\x48\x02\xc2\x6b\xfc\xef\x0a\x9b\xf1\x67\xd0\x45\x48\x45\x05\xc0"
+$ECHO -ne "\x07\xc4\x47\x96\x6e\x79\xac\x31\x49\xcf\x1f\xa8\x3b\xdc\x1d\x44"
+$ECHO -ne "\x83\x84\x04\x49\x9f\x25\x01\x4b\x41\x80\x14\x1b\x9d\xaf\xfc\xb5"
+$ECHO -ne "\x46\x22\xca\x96\x4e\xd5\xe3\x08\x7d\xab\x17\x0b\x65\xcd\xa7\xd4"
+$ECHO -ne "\x5e\xd1\x8a\x27\xc8\x60\xe9\x17\xa3\xc9\xb4\x26\xf8\x58\xb2\x4a"
+$ECHO -ne "\x15\x52\x8e\x15\x20\x72\xb2\x0e\x4f\x64\xcb\x2b\xa3\xd9\xf0\xa6"
+$ECHO -ne "\x6f\x0b\x50\xed\x4e\xa4\x28\xe8\xe0\x05\x2d\x15\x26\x2c\x3d\x9f"
+$ECHO -ne "\x87\xc4\xd1\xd3\x69\xe2\x1c\xea\x41\x99\xbd\x43\x59\x9f\x06\x57"
+$ECHO -ne "\x06\xb4\x72\xe7\x45\x2c\xd2\xcf\x5f\x66\xa5\xeb\x58\x6a\xc0\x37"
+$ECHO -ne "\x82\x81\xf6\xdc\x1c\x35\x5b\xc6\xf1\x92\x4e\xe0\xe2\xd2\x12\x82"
+$ECHO -ne "\x92\x97\x14\xe5\xac\x49\x9f\xfd\x16\x46\xc6\xc2\xf1\x48\x86\x05"
+$ECHO -ne "\xe6\x74\xac\x9a\x52\x49\x64\x45\x25\x43\x08\x06\x03\x63\x49\x91"
+$ECHO -ne "\x9a\x92\x96\x5b\xe3\x2f\x11\x51\xcd\xe3\xa3\x9f\x3e\x8f\x9f\xab"
+$ECHO -ne "\x92\xbc\x6d\x36\xa3\xd1\x71\xa4\x4a\xb6\xe1\x49\xb8\xdc\x2c\x90"
+$ECHO -ne "\xb2\xd6\xfb\xa6\xc6\xd9\xa4\x5b\x9b\xe5\xd6\x59\xb3\x76\x37\xeb"
+$ECHO -ne "\xa6\x3e\xf0\x4c\x98\x1d\x73\x04\x01\x06\x8c\x10\x00\x3b\x2b\x04"
+$ECHO -ne "\x55\xf4\xfd\xec\x9d\xad\xc7\x2b\xbd\xe3\xda\x4b\xee\x28\x5d\x7a"
+$ECHO -ne "\xbe\xa6\xb9\xe0\x81\x15\xa6\x09\x26\x52\x60\xcc\x03\x30\x66\x60"
+$ECHO -ne "\xb9\xd0\x79\xfd\xb6\xb3\x85\xac\xd1\xc4\x4c\xbf\x80\x20\x44\x45"
+$ECHO -ne "\x7f\x72\x27\xff\x14\xc2\xc0\x81\x02\xab\x32\x20\x43\x46\x06\x7f"
+$ECHO -ne "\xb7\xc2\xb9\xf6\x39\x7b\x0b\x0c\xcb\xe7\x6e\x03\xe3\x20\x46\x82"
+$ECHO -ne "\x4a\x01\x23\xbb\xb0\x0c\xb5\x6f\xf7\xfb\xfc\xf5\xf2\x3c\x8e\x7e"
+$ECHO -ne "\xcb\x77\x6f\x7e\xc3\x71\x7c\x44\x3f\xbc\x3c\x54\xb8\x40\x27\x78"
+$ECHO -ne "\x63\x4d\x83\x22\x6a\x0a\x00\x0e\x8d\xa5\xfa\x5e\xe5\x89\x55\xa4"
+$ECHO -ne "\x18\x60\xc2\xa6\xd6\x17\x98\x23\xf0\x07\x44\x45\x18\xa4\x68\xd9"
+$ECHO -ne "\xcc\x0d\xe3\x81\x06\x09\x0c\x17\xaf\x52\xad\x85\x83\x5d\x09\x30"
+$ECHO -ne "\x0d\xa9\xb3\xe6\x45\x85\x9e\x26\x47\xab\x7d\x14\xe1\x8a\xb9\xfe"
+$ECHO -ne "\x0a\x0f\x8d\x68\x73\x73\x5c\xa3\x0b\xb5\x29\x0c\xd8\xde\xc2\x30"
+$ECHO -ne "\xbe\x61\xce\xbd\x4d\x3a\x03\xef\xe9\x45\xef\xeb\x07\xe8\x6b\x7d"
+$ECHO -ne "\xd2\xf4\x92\x8f\x91\xa1\x6e\x85\x2b\x73\xaf\x01\x8a\xd2\x0f\x52"
+$ECHO -ne "\xed\x65\x9f\xe6\x15\x47\xb2\x71\xd3\xbc\xee\xde\xff\x10\xfa\x4d"
+$ECHO -ne "\x7f\x9d\x5a\x4b\x13\x4a\x92\xd6\x85\xb2\xef\xe1\xbb\x92\x80\x4a"
+$ECHO -ne "\x45\x70\xa0\x4e\xe6\xf3\x39\x9a\xf6\x55\xee\x80\xc5\xa0\xff\x9d"
+$ECHO -ne "\xb6\x66\xe6\xcc\x81\xb2\xdc\xd6\x39\xb7\x06\x2c\xd6\x3b\x27\x0e"
+$ECHO -ne "\x5d\x01\x92\x5c\xf3\xbe\x3d\x46\x8a\x46\xa4\xd4\x03\x21\x86\x8e"
+$ECHO -ne "\x68\x05\x3b\xf0\x66\x69\x4c\x61\xf0\x39\x1c\x9d\xe2\x74\x3b\x5f"
+$ECHO -ne "\xd7\x87\xdc\xd3\xeb\x59\x50\xb6\x6d\x75\xc9\x5b\xdc\x4d\xb7\x29"
+$ECHO -ne "\x0c\x64\x9c\x5c\x22\xd1\x44\xd7\x01\x68\x0a\x26\x25\x7d\x6a\x76"
+$ECHO -ne "\x1c\x1b\xbf\x7a\xa5\xeb\x42\x8f\x2f\x93\xa3\xc1\xca\xe3\x9f\x46"
+$ECHO -ne "\xfd\x77\x07\x27\x2d\xaf\xbb\x1a\x13\x5b\x86\x94\x00\x90\x86\xc1"
+$ECHO -ne "\x24\x8d\x86\x22\x56\xbe\x06\xe1\xa1\x44\x4c\x36\xe2\x22\x08\x21"
+$ECHO -ne "\xb2\x20\x6d\xb6\xdb\x6c\x6e\x26\x26\x06\xc0\x26\x09\x94\x09\x75"
+$ECHO -ne "\x2c\x10\x4b\x44\xb0\x1b\x44\x26\x11\x58\x10\xdf\x2c\xc1\x55\x8a"
+$ECHO -ne "\xad\xb2\xa3\x08\x67\x34\xe5\x83\x95\x0a\x08\x82\xc1\x8a\x06\xdd"
+$ECHO -ne "\xb1\x32\x14\xa5\x27\x78\xca\xb6\xd1\x57\x5a\xc9\x2a\x06\x05\x29"
+$ECHO -ne "\x0c\x88\x28\xd2\x86\xa5\xa9\x69\x51\x81\x46\xa1\xa4\x81\xb1\x8d"
+$ECHO -ne "\xb1\xb0\x01\x83\x49\x4b\x15\x1a\x6e\x13\x24\x68\x54\x60\x4b\x4e"
+$ECHO -ne "\x21\x39\x82\x1c\x8d\x02\x6d\x23\xc3\x30\x25\x83\x69\x05\x11\x05"
+$ECHO -ne "\x4d\x24\x04\x4e\x0c\x53\x81\x25\xce\x34\x10\xd0\x04\xd4\x98\xa1"
+$ECHO -ne "\x21\x0b\x7e\xc4\x09\x11\x30\x82\x8f\x68\xc4\x13\x48\x0a\x30\x17"
+$ECHO -ne "\x4f\xaf\x80\x52\xd0\x36\x22\xd6\x43\x48\x15\xf6\xa1\x82\x84\xdc"
+$ECHO -ne "\x44\x34\x07\x52\xc4\x2c\x56\xb7\xaf\xa8\x3b\xb1\x08\x4b\x6b\x6c"
+$ECHO -ne "\x24\x05\xce\x1a\x10\xe2\x02\x31\x22\x25\xb8\x23\x65\xd0\x52\x9b"
+$ECHO -ne "\x4a\xcb\x64\xae\xbd\xe8\xda\x30\xb4\x25\x79\xa4\xbc\xe6\xe0\xf3"
+$ECHO -ne "\xde\x82\x23\x84\xce\xe5\xb9\xc9\xe9\xeb\x69\x78\x2f\xbc\x76\x6d"
+$ECHO -ne "\x58\x86\xc4\xa5\x82\xfa\xad\x61\x75\x62\x0c\xb6\x9b\x00\xdf\x30"
+$ECHO -ne "\x4a\xd6\x83\xaa\x60\x8a\x33\x7c\xd2\x12\xf5\x6c\x48\x52\xc5\x85"
+$ECHO -ne "\xe2\x6f\x37\x73\xc7\xbc\xad\xea\x27\x27\xa8\xef\xf7\xef\x59\x17"
+$ECHO -ne "\x65\xb6\xe1\xd8\xdd\xb5\x93\x42\xd0\x29\x5a\x18\x76\x08\xdb\xe5"
+$ECHO -ne "\x38\xf9\xa8\xe4\xa1\xa2\xd4\x40\xa0\xfd\x45\x18\x4b\x3c\xa6\x85"
+$ECHO -ne "\x02\x94\x8c\x88\xa9\x71\x87\x40\x96\x4d\x23\x26\xf4\x17\x44\xb8"
+$ECHO -ne "\x78\x1e\x71\xe3\x3b\xee\xc6\x4b\x87\x88\xfd\x2b\xb5\x8b\x1b\x53"
+$ECHO -ne "\x0b\xab\xd6\x47\x23\xa7\xcf\x78\x3a\x25\xd7\x3c\x77\xb3\x8e\x00"
+$ECHO -ne "\x37\x83\x11\xbb\x68\xf5\xed\x0a\x1a\x4d\xa3\x90\x68\xea\xed\x49"
+$ECHO -ne "\x8d\xb6\x80\x69\x83\x67\xcf\x65\x5a\xec\xda\x12\xe6\x5a\x47\x5a"
+$ECHO -ne "\x3c\x63\x50\x41\x73\x40\x83\xc7\x69\xbc\x46\xa7\xb1\xed\x79\x3c"
+$ECHO -ne "\xfe\xdf\x27\x4b\xbe\xeb\x68\xec\x83\x00\x6b\x7b\x08\x4a\x6e\x0c"
+$ECHO -ne "\x2d\x16\xba\x1a\x96\xa1\x03\xc3\x63\x9e\x7a\xce\x8b\xe2\xae\x51"
+$ECHO -ne "\xfb\x7d\xed\x5d\xfb\xbc\xed\x04\x6f\x1f\x21\xfc\x69\x3c\xb1\xaa"
+$ECHO -ne "\xdf\xbf\xa0\xab\xc3\xcc\x6a\xbf\xe7\x96\xbe\x36\xb3\x23\x32\x1c"
+$ECHO -ne "\xb5\x18\x44\x54\x51\x81\xb4\x63\xc7\x99\x84\x06\xcb\xaf\x5b\x05"
+$ECHO -ne "\x4f\x82\xf5\x93\xb4\xc3\xcf\xdb\x65\xb8\x8d\xae\xa1\xc2\xf0\xdf"
+$ECHO -ne "\xa7\xe5\xf3\x37\xd2\x57\x73\x0d\x89\xb8\x21\x10\x9a\x43\xe9\xe0"
+$ECHO -ne "\x09\x1a\x40\x49\xa0\xcc\x03\x30\x46\x66\x66\x0c\x12\x48\x89\xff"
+$ECHO -ne "\x57\xe8\xd2\x7c\x3e\x8d\x9e\x46\x7f\x97\xfc\x3b\x12\x95\xd2\xdf"
+$ECHO -ne "\x2f\xb1\xc8\x7d\x61\xdb\xb2\x8a\xdd\xbf\xf3\x7e\x08\xcc\xad\x16"
+$ECHO -ne "\xbe\x45\x13\xf2\x7f\x14\x5a\x79\x2e\xb5\xbb\x78\x0c\x22\xc6\x10"
+$ECHO -ne "\x31\xce\x9c\x6b\x1d\x48\x11\x16\x4c\xdf\x98\x12\xf3\x41\x05\x81"
+$ECHO -ne "\xd3\x24\x94\x92\x37\x51\x5d\xdc\x51\x08\xd3\x73\xba\x89\x42\x3f"
+$ECHO -ne "\xcb\x5c\x4c\xb2\x16\xcb\x04\xcd\x86\xb2\x05\x8a\xc3\x56\xc8\x83"
+$ECHO -ne "\x0b\x2e\x90\x31\x86\x5c\x68\xb9\x8d\xbc\xbf\xf2\xe2\xd2\xb0\x0b"
+$ECHO -ne "\x76\x2b\x3d\x79\xba\x3f\x9b\xe3\x8e\xc4\xf5\xed\xe0\xf7\xdd\xdb"
+$ECHO -ne "\x97\x5f\x9a\xb3\xfc\x50\xbf\x89\xf4\x7a\x38\xa3\x44\x0c\x50\x5d"
+$ECHO -ne "\x7c\xbb\x65\x47\xf1\x33\xd6\x67\xa4\xe0\xf0\x68\x58\xe9\x6c\x40"
+$ECHO -ne "\x02\x6b\x01\x20\x40\x84\x89\x80\x08\xcc\x52\xa0\x20\x81\x98\x16"
+$ECHO -ne "\xa1\x90\xf8\xcd\xbe\x1e\xc7\x6b\x1d\xb5\x81\x6b\x04\xdb\x4c\x43"
+$ECHO -ne "\x1a\xbc\xd4\x0d\xb6\x0d\xb3\x82\xc8\xc7\xf0\x13\xa8\xc5\x20\xd5"
+$ECHO -ne "\xbd\xb4\xc0\x5a\xdd\xe8\xd1\x31\x4f\xad\x88\x63\x30\x44\x0d\xd5"
+$ECHO -ne "\xc6\x56\x96\x28\xe2\xe8\xa8\xa9\x10\xdb\x1a\xa3\x21\xa6\xc5\xe6"
+$ECHO -ne "\xf5\xb2\xa4\x6d\x8d\xb4\x31\xb5\xc3\xec\x3e\x8f\xd0\xeb\x35\xce"
+$ECHO -ne "\xdb\x02\x9c\x4e\x96\xcd\x40\x14\xcd\x97\xb9\x0a\xe3\x09\xf5\x49"
+$ECHO -ne "\xfe\x1e\xc7\xc5\x57\xb9\x96\xe9\xf5\x8a\x56\xdf\x63\xda\x8a\xea"
+$ECHO -ne "\x41\x97\x74\x7b\xa6\x57\x99\x8d\xb0\x78\x60\xe4\x04\xd7\xe4\xbf"
+$ECHO -ne "\x89\x71\xa5\xc8\x93\x42\x02\x53\x7a\x6a\x9d\x99\xc9\xd3\x2b\x87"
+$ECHO -ne "\x75\xb2\x8f\x19\x86\x28\x2b\xc3\x2b\x67\x95\x72\xfb\x13\x39\xb5"
+$ECHO -ne "\xca\x8c\x43\x88\x1c\xdc\x47\xb6\xdb\x05\xaf\x8e\x31\x54\xb8\xbd"
+$ECHO -ne "\x98\x8b\x1d\x1f\x17\x87\x9d\x6d\x05\xca\xa8\x90\x49\x10\xbb\x67"
+$ECHO -ne "\x2f\x92\x61\x43\xfe\xe2\xd6\x18\x6d\x2a\xc0\x14\x96\x9a\x2a\x65"
+$ECHO -ne "\x48\x04\xc7\x2d\x76\xa6\x1f\xc5\x79\x36\x28\x69\x6f\x09\xb6\x90"
+$ECHO -ne "\xc3\x55\x6d\x98\xf0\xbd\xce\xb1\x37\xf4\xc4\x90\x1c\xdf\x5a\x27"
+$ECHO -ne "\xbc\x24\x38\x52\x75\xc0\xee\xc9\x05\x5a\xd7\x2b\x61\xfd\xba\xfb"
+$ECHO -ne "\xea\x9f\x65\x39\x9f\xe7\xc9\xc3\x0e\xa9\x3a\x20\x50\x87\xb6\x08"
+$ECHO -ne "\xc7\x80\x92\xe2\x60\x21\xd2\x2d\x51\x12\xf8\x46\x60\xbd\xf4\x65"
+$ECHO -ne "\xd5\x7b\x1a\xa7\x79\xb7\x73\x79\xe9\x0d\x60\x34\xc3\xb0\x58\xc8"
+$ECHO -ne "\xcc\x42\x7b\xb0\x56\x8c\xde\x66\x72\x23\xc2\x59\xe6\x9f\x83\x6a"
+$ECHO -ne "\xef\x4a\x9e\x1e\xf3\xd5\xde\x52\x32\x14\x8a\x2d\x0b\xf0\x1e\x5b"
+$ECHO -ne "\x7c\x4a\x34\x4d\x72\x4f\x1d\x8f\x97\xe8\xc9\xcd\xe2\xb9\x03\x36"
+$ECHO -ne "\x9f\x89\x97\xc3\x19\x8d\x8d\x84\x41\x0c\x03\x34\x18\x41\x20\x10"
+$ECHO -ne "\x26\x4c\x10\x18\x50\x5e\xd7\x93\x1f\x31\xf7\x54\xb3\x43\x4d\xd7"
+$ECHO -ne "\x48\x69\xcf\x7d\x29\x2f\x7f\x8f\x11\xf2\x4c\x3f\xcd\xe7\xa2\xe1"
+$ECHO -ne "\x09\x9a\x1a\x6c\xc6\xf3\xcf\x33\xe5\xb5\x8f\x6e\x41\xf1\x80\x07"
+$ECHO -ne "\x4d\x7f\xbe\x1b\x37\xdd\xe3\x64\xb8\xa2\x59\x90\x2c\xa2\xbe\xf4"
+$ECHO -ne "\x82\x2a\x80\x46\x4d\x1a\x8c\x88\x5a\x18\x30\x64\x0a\x5a\x57\x37"
+$ECHO -ne "\x63\xe9\x6d\x8a\x6d\x5f\x88\x5e\x6d\x41\x33\x60\xfd\x45\x90\x7e"
+$ECHO -ne "\x15\xaa\x95\x6f\xbd\xfc\xe9\x0b\x34\xe4\x3b\xa8\x41\x78\x1c\x55"
+$ECHO -ne "\x62\x5d\xb2\x19\xdd\xeb\x69\xeb\xef\xe1\xbf\x7b\xeb\x62\x23\x8a"
+$ECHO -ne "\x61\x14\x9f\x22\x53\x08\x6a\x31\xba\x30\x24\x1e\x54\x83\xae\xbd"
+$ECHO -ne "\x87\xa1\x71\xf0\x3c\x7d\x94\xa1\x2c\xea\xff\x84\x76\x77\xd2\xc9"
+$ECHO -ne "\x9f\x2f\x9c\xc7\x83\x3f\x89\x5d\x1b\x5c\xc3\x0f\xfa\xd2\x93\x32"
+$ECHO -ne "\xfc\xed\xa6\x26\x86\x98\x1b\x05\x10\x20\x27\x4c\x95\x3f\x6d\x94"
+$ECHO -ne "\x82\x5a\xa8\x68\x72\xae\xd7\xae\xdb\xaf\x26\xb6\x5a\x89\x30\xe7"
+$ECHO -ne "\xd0\x5a\x7c\xc6\x66\xfa\xc3\x85\x7d\x26\xee\xb3\x34\xc2\xac\x70"
+$ECHO -ne "\x88\x03\x15\xc6\xee\x55\x45\xde\x1c\x10\x01\xe6\x3b\x36\x26\x11"
+$ECHO -ne "\xbe\xec\x54\xea\xd8\x20\x1d\x35\x00\xd1\x8c\x00\xe8\xf5\x21\x97"
+$ECHO -ne "\x26\x06\x69\x87\x55\xa3\xc8\xf6\x58\xcc\x60\x12\x30\x0b\x8a\x50"
+$ECHO -ne "\x01\x57\x30\xdc\x9a\x01\xd4\xa4\xcd\xd6\x69\x23\x0f\xc3\xb8\x85"
+$ECHO -ne "\x12\xbb\x8e\xdf\xc5\xf1\xf3\x7c\xc9\x7a\x24\x25\x07\x9c\x86\x97"
+$ECHO -ne "\x68\xb5\xad\x0b\xba\x2e\xe8\x6f\x7f\xa1\xed\x4f\x0c\x34\x7b\xc8"
+$ECHO -ne "\x84\x10\x08\x2a\xcc\x19\x59\xbd\xbc\xe4\x3d\xa8\xd9\x35\xaf\x8b"
+$ECHO -ne "\xa7\x0a\xad\x42\xe8\x02\x90\xe6\x8e\x76\x5d\x0f\x3b\x87\xb8\xe4"
+$ECHO -ne "\x65\x4e\x5f\x0d\xe8\x26\xaf\x2a\x94\x9a\x2e\x21\x9a\x19\xb9\xa0"
+$ECHO -ne "\x8d\x26\x78\xa1\x4b\x6e\xf6\xd7\x29\x66\xdb\x49\x09\xa0\xca\x4d"
+$ECHO -ne "\x32\xb0\x31\xf5\x73\xe1\x67\xce\xe0\x5a\x79\x84\xa4\x22\xd4\xc9"
+$ECHO -ne "\x43\x59\x08\xa8\xd5\x5e\x8c\x72\x61\x70\x9a\xa6\x42\xc0\x42\x22"
+$ECHO -ne "\x2d\xd0\xbe\xb1\x49\x6e\x36\xbb\x8d\x8f\x03\x9b\xb4\xdb\x5a\x77"
+$ECHO -ne "\x3e\x29\x91\xc6\x73\x88\xef\x8c\xf7\xde\xe2\x2b\xc2\xce\xcd\x8c"
+$ECHO -ne "\x92\x60\x96\x29\x89\x99\x62\x99\x81\x36\x9b\x50\xc8\x70\xd6\x8d"
+$ECHO -ne "\xaf\x6b\x30\xba\xc7\x7a\xca\x4c\x56\x66\x66\x2d\xc7\xa5\xf7\x63"
+$ECHO -ne "\xa4\x55\x8d\xd4\x92\xdb\x2b\x6b\xb1\xa1\x96\x99\xd9\x25\xdb\x14"
+$ECHO -ne "\x1c\x49\x04\x67\x25\x45\x0a\x50\x1d\x20\xd8\x8d\xcf\xe7\x03\x20"
+$ECHO -ne "\xf0\xd7\xc0\xcc\x84\x20\x68\x4a\x63\x41\xa4\x6c\x32\x08\xa2\x37"
+$ECHO -ne "\x03\x6b\x42\x12\xbe\xa9\x4e\x9b\x97\x16\x92\x48\x56\x32\xae\x2c"
+$ECHO -ne "\x10\xc6\x31\x14\x8c\xcc\xd6\x23\x09\xf4\x64\x15\x9e\xf1\x35\x75"
+$ECHO -ne "\x98\x3a\x0c\x12\x29\xaa\xb7\x2b\x82\xe0\xf9\xcd\x05\xed\xb8\xbe"
+$ECHO -ne "\xb6\xf5\x6f\xcc\x41\xbc\x3a\x81\x39\x3b\x03\xe8\xb2\xab\xb6\xaa"
+$ECHO -ne "\xed\xa8\x58\xdf\xca\x06\xba\x64\x7b\xc4\xef\xec\x23\x38\x77\xec"
+$ECHO -ne "\xcf\xd7\xd2\xeb\x75\x3d\x26\xe2\xfa\x66\x49\x0b\x4a\xdc\xe3\x48"
+$ECHO -ne "\x64\x33\xc4\xb3\x93\xda\xdd\x3c\x08\x83\x7d\x91\x78\xe5\x61\x57"
+$ECHO -ne "\x67\x37\x73\xe1\x05\xbb\x96\x3e\x26\xc7\x6c\x44\xb5\xfb\x80\xb2"
+$ECHO -ne "\xd9\xa0\x99\x6b\xbf\x74\x62\xb7\xf7\x14\xec\x07\x12\xfc\xe6\x1b"
+$ECHO -ne "\xf1\x1d\x24\xfe\xd0\xb9\x61\x76\x56\xa0\xa5\x8c\x63\xce\x96\x5d"
+$ECHO -ne "\x65\x4f\xae\xcc\x7d\x86\x2d\xd7\x74\x96\x67\xb7\x5c\x94\xa6\x30"
+$ECHO -ne "\xbd\xb3\x84\x56\x93\x1e\x44\xc5\x43\x5f\x65\x9d\x1a\x92\xb1\x9a"
+$ECHO -ne "\x4c\x46\x1f\xd2\x64\x54\xb6\x4e\x7e\xb2\x71\x75\xf6\xce\xac\xdc"
+$ECHO -ne "\x5a\xa1\xd4\xf1\xf5\x71\x6a\x93\x50\xd2\x8b\xb2\xb1\x7f\xaf\x20"
+$ECHO -ne "\xd2\xc9\xce\xeb\xfb\x1d\x4a\xff\x26\x89\xa2\x60\xed\x8a\xeb\xa7"
+$ECHO -ne "\x6e\x92\xea\xb7\xef\x7a\xcc\xd9\x4b\xbb\x3e\xad\xc6\x7a\xfa\xbb"
+$ECHO -ne "\xe0\x25\x0c\x0f\xe2\x14\xf9\x2e\x0b\x5f\xd4\xbd\x8f\x5a\xae\xb6"
+$ECHO -ne "\xca\xc1\x5a\x89\x4c\x74\x36\xd3\x32\xab\x87\xa7\x7d\x57\x7f\x45"
+$ECHO -ne "\x1a\x1d\x45\xcc\xc8\xf1\x36\x8c\x4d\x6e\xc9\x01\xb8\x7a\x99\xdc"
+$ECHO -ne "\x4d\x9a\xa1\xc3\x7a\x81\xac\xa9\x40\x20\xc1\x18\xc7\x1e\x0d\xeb"
+$ECHO -ne "\xf7\x53\x9b\xcb\xe2\x64\x4e\x17\x1c\x6a\xd7\x74\x6b\xe4\x4b\xe7"
+$ECHO -ne "\x5f\x06\x31\xac\xe7\x5c\x64\x93\x05\x69\x13\x1a\x34\x52\x3e\x1a"
+$ECHO -ne "\xc8\xf6\xed\xde\x5e\x79\xf4\xe2\x04\xc3\xb6\xb3\x49\xdc\x7a\xe3"
+$ECHO -ne "\x52\x12\x1b\x32\xd9\xe2\x5c\x95\x5f\x69\x01\xde\x77\x16\x34\xf7"
+$ECHO -ne "\xda\x43\x2c\x56\x77\x21\xcc\x86\xc4\x4a\x14\xb8\x29\x28\x0a\xf1"
+$ECHO -ne "\x79\x8a\x9e\x94\x86\x6c\x6a\x6c\x0f\x15\xe6\xb4\x57\x92\xfc\x1f"
+$ECHO -ne "\x6d\x98\xbf\x2f\x0b\x97\xb3\x4b\xec\xe6\xd3\xf7\x94\xe4\x2c\xf3"
+$ECHO -ne "\x20\xfa\x42\x69\xd9\xb6\xb2\x96\x31\x09\x6b\xcb\xd2\x92\x14\x40"
+$ECHO -ne "\x69\x75\x9a\x83\x49\x44\xed\xe0\xdd\xa3\x3d\x09\xe0\xe4\xcf\x4f"
+$ECHO -ne "\x3b\x12\x84\xb6\x47\x2b\x1b\x7e\xc2\xac\x8d\xf6\x1c\x74\x26\xb0"
+$ECHO -ne "\x2a\x27\xf6\x03\xe3\xf9\xe3\xbb\x4a\x1a\x6f\xa2\xf4\x10\xb0\xe5"
+$ECHO -ne "\x70\x9a\x9b\xdf\xac\x42\x6a\xdc\x80\xc3\x80\x0c\x84\x26\xf0\x23"
+$ECHO -ne "\xd6\x5b\xcb\x8b\x3b\xe1\x65\xfe\xba\xad\x85\xe9\x1d\x88\xa4\xf8"
+$ECHO -ne "\x05\xb8\x58\x0b\xb1\x13\xa8\xd8\xea\xfd\x07\x68\xee\x6b\x5c\x88"
+$ECHO -ne "\x17\x49\x89\x0e\xa5\x7a\xe6\xa0\x9c\x3a\x06\x2d\x71\x84\x2c\xd2"
+$ECHO -ne "\x1b\x07\xfd\x43\x9b\x48\x9b\xae\x60\x54\x5d\xd3\x2b\xf1\xc0\x0d"
+$ECHO -ne "\x49\x01\x64\x34\x36\x77\x2f\x0e\xe7\x72\x35\x48\x2f\x05\xaa\xd5"
+$ECHO -ne "\xb4\x98\x77\xa3\x19\xa2\xf4\xb8\x11\xa7\xa6\x24\x91\xac\x1e\x09"
+$ECHO -ne "\x38\x04\xc6\xff\x0b\x7d\x36\xc2\xcb\xb8\x9c\x7e\x7b\x49\x8c\x4e"
+$ECHO -ne "\xbb\x37\x19\x18\x83\xc5\x23\x03\x6c\xcb\x51\x24\xe5\x42\x85\xc7"
+$ECHO -ne "\x73\x13\x2c\xc8\x22\x28\x50\x83\xbc\x3a\x8e\x60\xac\xb1\xda\x18"
+$ECHO -ne "\x24\x6d\x64\xd0\xa9\x65\xcd\xd6\x5a\xa7\xaa\xc6\xed\x32\x76\x7b"
+$ECHO -ne "\x07\x90\xb4\x7b\x5d\x16\x88\x9b\xd7\x5e\x0a\xb7\xbf\xbf\xc4\x5d"
+$ECHO -ne "\x1c\xbd\x39\xf3\x17\xae\x50\xaa\xc7\xa4\xe9\xad\xa5\xac\x04\xd9"
+$ECHO -ne "\xa4\x27\x5f\x79\x75\x29\x10\x69\x75\xe9\x06\x53\x7c\x66\x8b\x83"
+$ECHO -ne "\xf7\x7c\xfd\xcd\x16\xc3\x8c\x8e\x51\x6f\xcb\x68\x0a\x9c\x39\x39"
+$ECHO -ne "\xb9\x0b\x6a\x16\xc5\x4a\x22\xc0\x31\x09\x22\x28\xa0\x65\x69\x05"
+$ECHO -ne "\x30\x90\xc1\x18\x22\x05\x9e\xad\xa9\xc3\x54\x3e\x27\xa9\xc4\x41"
+$ECHO -ne "\x2c\x39\x03\xd2\x8e\x3f\x91\x9a\x4c\xc8\x68\x14\xe4\x1c\xa6\x5f"
+$ECHO -ne "\x0b\x57\x27\x09\x8a\x7d\xff\x47\x63\xa7\x5a\x29\x82\xa0\x3a\x28"
+$ECHO -ne "\x30\x9a\x2b\xf3\x69\x63\x18\xcd\xe2\x32\x66\x3c\xd7\x79\xdd\x12"
+$ECHO -ne "\x86\x34\xc6\x9e\x75\x05\x87\x39\x23\x72\x97\x71\x27\x64\xcd\xd9"
+$ECHO -ne "\xa6\x2e\x61\xd2\x37\xe4\xae\xc6\xc9\x81\xc0\x2e\x9f\xc6\xf9\x7f"
+$ECHO -ne "\xd9\x5e\xd0\xa9\x09\x97\x35\xa2\xe3\x4f\xe9\x19\x7c\xa5\xc7\x4d"
+$ECHO -ne "\x2d\x92\xec\xd6\xef\xda\x55\xf3\xa2\x95\x17\x1b\xce\xbe\x6b\x74"
+$ECHO -ne "\x70\xee\xdb\xa8\x42\x26\xb1\xcc\xc1\x31\x0a\x67\x92\x13\x9d\x9c"
+$ECHO -ne "\x12\x18\xa4\x08\x4d\x4d\xfc\x7c\xeb\x59\x6b\x22\x03\xaa\x97\xc3"
+$ECHO -ne "\x27\xa5\x21\x35\x68\xd2\x57\x54\xca\x58\x38\x82\xc5\x05\xa0\x71"
+$ECHO -ne "\x01\x1b\xce\x57\x1e\x20\xbf\x89\x96\x2a\x31\x8e\x6e\xaf\x7f\x35"
+$ECHO -ne "\x08\x10\xd9\x0e\x8a\x78\xb0\x48\x98\xa4\x64\x14\xa2\xcf\x23\x2d"
+$ECHO -ne "\x0a\x7b\x84\xe5\xfd\x29\x49\x15\x3d\x75\x39\xfd\xaa\xd6\xa4\xb9"
+$ECHO -ne "\x05\x12\x57\x31\x04\xdc\x26\x34\x16\x3f\xa7\x03\x32\x1d\x4b\x1d"
+$ECHO -ne "\x78\xdc\x9b\x79\x96\x9a\x87\x6e\xb4\x80\xaa\x01\x19\x33\x92\xb0"
+$ECHO -ne "\x16\xc9\x94\x9c\xe7\xa5\x63\xe6\x18\x13\xb2\x34\xbd\x98\x41\xd6"
+$ECHO -ne "\xa4\xc8\xb9\x6e\x06\x9c\x72\xf8\x49\xab\xd5\x47\x9e\xa1\xe6\xde"
+$ECHO -ne "\x62\xd0\xec\xaf\xbf\x1b\x8a\xaf\x63\xa0\x29\xbe\x3d\x87\xa0\x22"
+$ECHO -ne "\xce\x46\x4e\x18\x30\x7b\x3c\x3d\x86\xe1\x9e\xb6\x59\xef\x1c\x43"
+$ECHO -ne "\x65\xd0\x3d\x53\xd0\x41\x20\x40\xb7\x2b\xb1\xdd\x52\x2c\xdd\x68"
+$ECHO -ne "\x44\xc1\xbe\x40\x72\x61\xd7\x25\x5d\xf5\x69\xce\x3a\x3b\x2e\x9b"
+$ECHO -ne "\x13\x19\x79\x1a\xf0\xee\xb0\xe7\x17\x44\x45\xe8\x2d\x59\x50\xbc"
+$ECHO -ne "\x40\x67\x66\x12\x20\xcc\x43\x8a\x9c\x1d\xde\xac\x2d\x00\x76\xb2"
+$ECHO -ne "\x98\x8a\xa9\xde\x1c\xb6\x8b\x32\x19\x67\x1c\x67\x95\x41\x40\x60"
+$ECHO -ne "\xf3\x13\x44\xb8\xc5\x18\xa7\xca\xdd\x8c\x5a\x8f\x72\x69\xf1\x31"
+$ECHO -ne "\xa9\xd2\xeb\xac\x3e\x2f\xdc\xc7\xe0\x00\x78\x5d\x72\xff\x01\x95"
+$ECHO -ne "\x86\x4a\x90\x2b\xf8\x10\xc5\xc2\xd1\x9d\x7a\xc3\x65\xb1\xfd\x2d"
+$ECHO -ne "\x09\x0b\xcd\xdf\x03\x80\x3e\x44\x81\x65\x49\x4f\x50\x7e\x1f\x75"
+$ECHO -ne "\x97\xc6\x05\xda\x5a\xe9\xf6\xee\xe5\x66\xcc\x5e\x17\xe2\x8c\xb2"
+$ECHO -ne "\x06\x5b\xdd\x41\x0d\x26\xcc\x87\x0d\x37\x2e\x2d\x35\xe0\x5d\x93"
+$ECHO -ne "\xc5\xdf\x2d\xb4\xa2\xb1\x1b\x0e\x9b\xe6\x76\xb4\x28\x69\x5c\xe9"
+$ECHO -ne "\x4e\x27\x6f\x52\xcb\x4d\xb3\xc8\xaa\xea\xd3\x1a\x57\x00\xdf\x20"
+$ECHO -ne "\x2d\x42\xea\x6a\x18\x0a\xac\xae\x9a\x32\x08\x23\x99\xb7\xd8\xe5"
+$ECHO -ne "\x75\x3a\x65\x8b\x2f\xaa\x4f\x7b\x68\xd5\x66\x76\xf4\xec\x3d\xdb"
+$ECHO -ne "\xe9\x37\xdb\x69\x40\x6d\x35\x4f\x77\xfa\x8f\x07\x60\xac\x8e\x3b"
+$ECHO -ne "\x89\x46\x3c\x16\xd4\x4b\x6e\x71\x4f\x00\x10\x22\x14\x12\xca\x72"
+$ECHO -ne "\xe0\x6c\x54\x2f\x0e\x32\x8c\xba\x53\xad\x51\x48\xaf\xee\xb2\xca"
+$ECHO -ne "\x93\x4a\x46\x24\x1f\x09\x83\x69\x1c\x3f\x72\x50\x70\xff\x10\x74"
+$ECHO -ne "\x21\xef\x4a\x08\x38\x25\x4c\x54\xb6\x34\x83\x64\x99\x22\x0f\x02"
+$ECHO -ne "\x49\x58\x50\x74\xa3\xbe\xc2\x17\x05\xa7\x60\x55\xc4\x21\x52\x0c"
+$ECHO -ne "\x57\xee\x0f\x64\x6a\xa9\x73\x25\xa1\x2a\x94\x1d\x00\xca\x65\xc4"
+$ECHO -ne "\x39\xfc\x53\xa8\xe7\x4c\x07\x44\x5f\x29\x19\x98\x08\x16\x53\x1a"
+$ECHO -ne "\xba\xee\x8e\x2e\x16\x97\x66\x5b\x7c\xb5\x63\x2d\x31\x18\xdb\x64"
+$ECHO -ne "\xc5\x69\x15\xa9\xe8\x23\x5f\x92\xdb\x75\x60\x90\x6a\xbf\xb5\xba"
+$ECHO -ne "\xe5\xa5\x70\xce\x26\xd0\xc1\x63\xcb\x0e\x21\x67\x1e\x8e\x20\x32"
+$ECHO -ne "\xa1\x2d\x51\xfc\x32\xa0\xc9\xd0\x32\x91\x9a\xda\x45\x73\x2e\x97"
+$ECHO -ne "\x09\x17\x0c\xea\xe4\x89\x94\xe8\xad\x64\xd6\x78\x02\x07\x79\x06"
+$ECHO -ne "\xa4\x01\xce\xd0\xcc\x33\x20\x8d\xc9\x2d\x67\xdf\x85\x06\xb5\x21"
+$ECHO -ne "\x74\x61\x49\x99\x98\xec\x28\x06\xc4\xbd\x25\xb5\x62\x2d\xb0\xba"
+$ECHO -ne "\x5f\x4c\xc4\x33\x85\x42\x58\x11\xd4\xff\x27\x21\x3c\x57\x9e\xd9"
+$ECHO -ne "\xc4\xb1\x6d\x8d\x4a\x8c\x8a\x80\x6c\x1e\x16\x5f\xc1\xc4\x68\x4a"
+$ECHO -ne "\xca\x20\xb1\x40\x10\x1b\x1b\x6c\xf7\x82\xf8\xd4\x35\x29\x10\x76"
+$ECHO -ne "\x7d\x3a\x4d\x4d\x49\x9b\x62\x65\x66\xd4\xda\x81\x24\xca\x4a\x48"
+$ECHO -ne "\x48\x2f\x83\x48\xd1\x09\xdf\x2f\x17\x8b\xc5\x37\x89\x94\x15\xb1"
+$ECHO -ne "\x36\x58\xcd\x80\xb4\x19\xc5\xc6\xda\xda\x16\x95\x82\x14\xc5\x19"
+$ECHO -ne "\x61\x6e\xb5\xcc\x27\xb5\xf3\xdb\xef\x6e\x44\x37\xbf\xdc\x11\xf9"
+$ECHO -ne "\xa0\xf2\x78\x30\x85\xc0\xc0\x07\x67\x02\x66\x56\x7c\x76\xee\x7a"
+$ECHO -ne "\x97\x6e\x02\x5e\x08\xc0\x35\x02\x4a\x87\x39\x4c\xd6\xc4\xe0\x99"
+$ECHO -ne "\xcd\xd9\xda\x2c\x49\x18\x5c\x22\xb6\x51\x4b\xa0\x58\x8b\x7a\x55"
+$ECHO -ne "\x61\xdc\xa5\x21\x83\x1d\x47\x9c\x0b\xf4\x74\xba\x08\x85\xe4\xc8"
+$ECHO -ne "\x80\xbe\x80\x46\xfc\x46\x85\x60\x64\xa6\xc4\xc1\xae\x69\x67\x0b"
+$ECHO -ne "\x8e\xac\xa2\xc0\xf4\x6b\x6f\x7a\x9e\x00\xdd\x4d\x59\x57\x4a\x78"
+$ECHO -ne "\x08\x64\x08\x84\x80\x50\x34\xb1\x3b\xc7\x71\x3f\x3e\x1c\x1d\x4e"
+$ECHO -ne "\x4e\xa9\xb0\x32\x02\x10\x8e\x88\x71\xed\x87\x2c\x32\x4d\x57\x05"
+$ECHO -ne "\xf1\xba\xa0\xf9\x61\x30\x4b\x18\x65\x6e\x38\xf9\x41\xdd\xf1\x48"
+$ECHO -ne "\x63\x38\x50\x10\xc1\xac\x1b\xf2\x5b\xaa\x15\xf4\x89\x0e\xe9\x77"
+$ECHO -ne "\x80\x56\x50\x18\x81\x71\xd8\xdb\x0d\x6a\xce\xd2\xb6\x76\xbd\x35"
+$ECHO -ne "\xf0\x96\xe1\x06\x8b\x09\xab\x83\x21\x10\x10\x30\x68\x30\xad\xe0"
+$ECHO -ne "\xc2\x62\xa2\x99\x0b\x92\x17\x19\xab\xe3\x7a\xd1\x90\xae\x5c\x2b"
+$ECHO -ne "\x6e\xbe\x31\xec\x72\x78\x03\x7a\x85\x70\xe0\x67\x36\xe0\xdb\x63"
+$ECHO -ne "\x6e\xed\x26\x94\xcc\x9b\x4e\xa8\x23\x57\x56\xe1\x49\x61\x31\x5e"
+$ECHO -ne "\xc8\x2b\x81\x05\x23\x18\xdb\x68\x34\x0b\x6c\xf1\xfc\xc7\xdd\xdf"
+$ECHO -ne "\x1a\x39\xf8\xf6\x72\xb9\x4d\xc9\x80\xbf\x23\x93\x24\x76\xdd\x6d"
+$ECHO -ne "\x0a\x8f\x18\xe1\x81\x8f\x48\x7b\x48\x2e\xd0\xb5\xd0\xcb\xa1\x46"
+$ECHO -ne "\xae\x1c\x26\x02\xd2\xe0\xf4\x56\x8c\x8a\x01\x97\x4e\x5f\xd1\xde"
+$ECHO -ne "\x9a\x10\x31\x0d\x4c\xbc\x40\x06\xc5\x04\x92\x91\x88\x81\x58\x5d"
+$ECHO -ne "\x55\x13\xab\x4f\xaa\xbd\xee\xa0\x6a\x80\xb2\x83\xd0\x46\x31\xa0"
+$ECHO -ne "\xbc\x2c\xf9\x0d\xad\xe2\x62\xb0\xac\xa4\x91\x84\xb8\x31\x99\xb9"
+$ECHO -ne "\x45\xb3\x47\x1e\xc2\x96\xc9\x9d\xcc\xd3\xcc\x71\xc4\xf3\x9a\x92"
+$ECHO -ne "\x2b\xac\xc3\x8c\xe1\xdc\x40\x66\x64\xe8\x24\x35\x50\x26\x68\x0b"
+$ECHO -ne "\x79\x96\x81\xb6\x36\xc7\xa4\x82\x0d\x32\x65\xc3\x4c\x61\x49\x32"
+$ECHO -ne "\x09\x14\x22\xac\x37\x69\x34\xb4\x6c\xdd\xbc\x95\x54\x6b\x59\x53"
+$ECHO -ne "\xc6\x50\x32\x09\x99\x14\x8c\x18\x74\xcc\x05\x86\x7a\x06\x48\x50"
+$ECHO -ne "\x6e\xe0\xaa\x41\xbb\xb0\xbc\x19\xaa\x2c\x12\x9c\xcd\xa5\x1c\x6d"
+$ECHO -ne "\x19\x0a\x62\x02\xfe\xd3\x4a\xcc\x7c\x6a\xa5\x72\x06\x35\xfb\x8d"
+$ECHO -ne "\xf9\xab\x1e\x0b\x29\x73\x70\xb5\xe8\xf6\x54\xb6\x4c\xc8\xea\x30"
+$ECHO -ne "\x8c\xaf\xd0\xd3\xb0\x20\x59\x80\x61\x40\xc8\x19\x99\x6d\x97\xb3"
+$ECHO -ne "\xca\x66\x1e\x16\x3d\xa7\x74\xa6\x58\xf0\xd4\x00\x67\xdc\xbb\x8a"
+$ECHO -ne "\x4a\x7b\x75\xa4\x6e\x89\xc4\x44\x44\x3d\x72\xb4\x52\x8a\xc0\xc2"
+$ECHO -ne "\x11\x40\x22\x9a\x14\x09\x66\xc2\x03\xcc\x04\x86\x02\x03\xa6\x8a"
+$ECHO -ne "\xab\x60\xe0\xe8\xdc\x2b\x5d\x0d\x73\xb5\x8f\x74\xc6\xce\xdb\xb5"
+$ECHO -ne "\xa8\xe7\x95\x3f\x8b\xaf\xb9\x87\xbc\x63\xab\x84\xea\x93\x1e\x9d"
+$ECHO -ne "\xb4\xe0\x83\xc8\x4a\xc9\xc7\xb9\xc7\xf2\xc6\x25\x10\x58\xc0\x21"
+$ECHO -ne "\x64\xa1\x08\xd3\x10\x2f\x94\x40\x5a\x56\x17\xa1\x0f\xa6\xfb\xda"
+$ECHO -ne "\xd3\x12\x42\x31\x71\x09\xa5\x2e\x8b\xd1\x69\x5c\x99\x5b\x09\x52"
+$ECHO -ne "\xc6\x9b\x5a\x18\x0c\x06\x47\x42\x8a\xc3\xad\xef\x9a\xe9\x9d\xf6"
+$ECHO -ne "\x2b\x81\x72\x48\x05\x20\x16\x10\xa3\xc3\xc5\xd2\x71\x0e\xca\x04"
+$ECHO -ne "\x17\xef\xdf\x39\x64\x26\x4c\x9f\x22\xb4\x13\x1c\x3d\xe7\x55\x40"
+$ECHO -ne "\x2e\xd1\x91\x28\xc8\x1c\x68\x69\x65\x97\x13\x75\xfe\x5b\x5c\xb1"
+$ECHO -ne "\x9b\x5a\xf7\xd2\x02\xb2\x0b\x41\x36\x67\xe7\xa9\x10\x80\xd0\x5c"
+$ECHO -ne "\x64\x08\x67\xda\x56\x36\x53\x4a\xa8\xca\x16\x88\xc5\x79\xdd\x3e"
+$ECHO -ne "\x87\x71\x13\x39\xae\xfd\x2a\x93\x6e\xbb\x96\x02\x39\xea\xda\x5a"
+$ECHO -ne "\x87\xb8\xfa\x54\x2c\x49\xa3\xa0\xbb\xa5\xc4\x10\x5c\xd2\x10\x10"
+$ECHO -ne "\x0c\x88\xb2\xd4\xf4\x67\x6a\x93\x5b\xbb\x20\x06\xdc\x75\x7f\x3a"
+$ECHO -ne "\x9b\xa3\xb0\x76\x98\xd9\x77\x32\x97\xa5\xdc\x64\xa4\x7b\xa5\xae"
+$ECHO -ne "\xaa\x15\x2d\x59\x0c\xc1\x7a\x40\xd2\xc2\xbb\x45\x10\xe1\x9a\x46"
+$ECHO -ne "\x52\x91\xe4\x24\x21\x9c\x46\xee\x05\x57\x44\x5e\x41\xad\x5a\x08"
+$ECHO -ne "\x46\x0b\xa0\xdf\xb4\x59\x7a\xe4\x41\xa3\x0a\x59\x5e\x2b\x17\x20"
+$ECHO -ne "\x19\x02\x6c\xe6\xe2\x48\x85\x99\xb3\xba\xfc\x9c\xe3\xcd\xf9\x31"
+$ECHO -ne "\x5b\xf1\x86\x64\x9d\x8f\x93\x24\xa3\x29\x38\x94\xcb\x1e\x71\x87"
+$ECHO -ne "\x54\xf2\x27\x22\x4e\x57\x26\x9a\x82\xb5\x6e\x6c\x1c\xad\x2d\x2b"
+$ECHO -ne "\x22\x62\x0a\xd0\x23\x5d\x5a\x75\x15\xae\xa0\x26\x04\x21\x6d\x2c"
+$ECHO -ne "\xfe\x06\xd9\x60\x61\x20\x8e\xea\xef\xba\x59\x03\x64\xda\xe5\xb2"
+$ECHO -ne "\x30\xc0\x9c\xdc\xcf\x11\x77\xe9\x23\x54\x33\xb8\xe9\x05\xab\x4c"
+$ECHO -ne "\x5b\xb5\x4b\x2d\x03\x0c\x51\xc5\x80\x11\x51\xac\xeb\x8d\x4c\x25"
+$ECHO -ne "\x21\x98\x79\xb0\x38\x99\x9c\xbc\xe2\x96\xe9\x4a\xd0\xad\x56\x6a"
+$ECHO -ne "\x65\xe1\xd4\x90\x12\x4a\xa5\x48\x06\xc6\x48\x31\xac\xaf\x21\x0a"
+$ECHO -ne "\x56\xa2\x90\x12\xd7\x53\xa8\x48\x03\x75\x2e\x36\x14\xf4\x50\x89"
+$ECHO -ne "\x7c\x49\x4e\x4a\x2a\xbc\x46\xc3\x2d\x16\x4e\x42\x25\x28\xd4\xf2"
+$ECHO -ne "\x01\x47\xa3\x5a\xd1\x6a\x0c\x1c\x35\x9c\x52\xc8\x2d\xeb\xab\x15"
+$ECHO -ne "\x2a\x99\x51\x45\x22\x9c\x77\xaf\x85\x77\xbc\x84\xb2\xf5\x99\xe9"
+$ECHO -ne "\xd8\xd9\xc1\x90\x83\x02\xeb\xde\x8f\x91\x82\xa3\x0c\x73\x1a\x05"
+$ECHO -ne "\x6b\x9c\x98\x28\xc5\x56\x55\xe9\xf3\x74\x24\x81\x48\xaf\x21\xb4"
+$ECHO -ne "\x84\x2d\x6b\x45\x88\xc2\x90\xb1\x12\x12\x60\xc8\x6a\xec\x61\x33"
+$ECHO -ne "\xa2\xc3\xb3\x58\xbf\x29\x1c\x48\x97\x7a\xea\x20\x65\x03\x7f\x22"
+$ECHO -ne "\x45\xa5\xdd\x03\x5c\x52\xdc\x30\x85\xde\xd9\x47\x5e\xeb\x31\x65"
+$ECHO -ne "\x0b\xf3\x13\x80\xae\x3e\x07\x52\x2a\x47\xb9\x7b\x7c\xa8\x41\x79"
+$ECHO -ne "\x95\x2e\xcf\x3d\x60\x08\xe6\x26\x00\xd1\x82\x60\x70\x45\xa1\x4a"
+$ECHO -ne "\x48\xa2\x18\x76\x35\xb5\xe8\x6c\x0d\x42\xd2\xba\x2c\x13\x5b\x25"
+$ECHO -ne "\x27\xd4\x8d\x73\x7a\xf8\xd9\xfe\xf3\xd3\x81\x73\x83\xe8\x65\x60"
+$ECHO -ne "\xf3\x5b\x18\x07\x15\x04\x60\x67\x51\xca\xab\x91\x85\xdc\x61\x3a"
+$ECHO -ne "\xe9\x72\xc2\xd1\xa4\x68\xe2\x00\xc8\x0c\x5e\x82\xa0\x0b\x82\x16"
+$ECHO -ne "\x40\x82\xb2\x98\x7b\x76\xf8\x5b\xb5\xf6\x8d\xfb\xc9\x36\x8c\x1e"
+$ECHO -ne "\xa6\xc9\xb5\x95\xd8\x36\x28\x36\xee\xa9\xa2\x72\x66\x58\xf5\x90"
+$ECHO -ne "\x02\x95\xee\xa8\xfc\x79\xfa\x6f\x66\xf6\x47\xd6\x4f\x10\x95\x86"
+$ECHO -ne "\x54\x0d\xa0\x22\x26\x01\xbe\x63\xc5\xf1\xac\x36\x4a\xe1\x0f\x6b"
+$ECHO -ne "\xe7\xba\x4f\x20\x17\xc0\xf9\x02\x5d\xc4\xc0\xaf\xf0\x1f\x88\x54"
+$ECHO -ne "\xe4\x6e\xc0\xa2\xbb\x59\x6f\x82\x21\xbb\x50\x9c\x4e\x92\x83\x24"
+$ECHO -ne "\x23\xf8\x28\x4c\x45\x79\x88\x71\x3b\x05\x79\x98\x4f\xa1\x00\x2d"
+$ECHO -ne "\x6e\x68\x08\x46\x3b\xfb\xf0\x5c\x22\xf3\x42\x40\x7d\x5e\x67\x3f"
+$ECHO -ne "\xdf\xff\x2e\x73\x0d\x31\xb5\xc0\x78\x4c\x0f\x85\x0f\xdb\xe6\x2b"
+$ECHO -ne "\x86\x0f\xb3\x8f\x9d\x1a\xe6\xe2\xdb\x32\x68\xfb\x5a\x79\x0a\xf4"
+$ECHO -ne "\x25\x28\x01\x39\xd2\x7c\x05\x7b\x0b\x18\xbb\x9c\x68\x31\x8d\xb0"
+$ECHO -ne "\xfc\x69\x2d\x17\x2c\x33\x62\xa6\x24\x6f\x0e\xcc\xdc\xff\x7b\xdf"
+$ECHO -ne "\x78\xd3\x88\x5c\x4a\xbd\xeb\x14\xda\xe0\x0e\xd3\xf3\x5b\xd2\xaa"
+$ECHO -ne "\xd1\x64\x82\x42\x0c\xfe\x54\x90\x40\xeb\x75\x13\x63\x68\xb5\x52"
+$ECHO -ne "\x0a\xd9\x3b\xc5\xd0\x14\xe1\xd2\x35\xab\x2b\x8b\xed\x21\xf3\xe1"
+$ECHO -ne "\xcb\xed\xbc\x3e\x64\x81\x63\x5f\xaa\xf3\xb8\x05\x10\x29\xe1\x67"
+$ECHO -ne "\xae\x0a\x16\xbb\x71\x05\x02\x46\xba\xef\x30\xda\xe7\x3a\x18\x4b"
+$ECHO -ne "\xcb\x0e\x97\xd1\xe9\xf5\x7a\x73\xc1\x60\x91\xaf\x63\x4a\xc8\x57"
+$ECHO -ne "\xa6\xb0\x09\xb4\x95\x98\x87\xa9\xc4\x5e\x04\xe7\xef\xbf\xe5\x8c"
+$ECHO -ne "\x39\x8e\xe2\xdd\x9a\xc0\xbf\x37\x9c\x38\xa3\xad\xc6\x14\x86\x3f"
+$ECHO -ne "\x7d\xc1\x9d\xb5\xbb\x68\x58\x4c\x14\x03\xf1\x36\xd4\xf2\xce\xb6"
+$ECHO -ne "\xbc\x9d\xb3\x31\x55\x68\x0c\x5f\xd0\x30\xe2\x05\xae\x68\x4e\x11"
+$ECHO -ne "\x5a\x28\x15\x68\x8f\xac\xa3\x1b\x89\xad\x48\x89\x08\x08\xa3\x3b"
+$ECHO -ne "\x26\x5e\x32\x1d\x6d\x73\x2b\x20\x90\x08\x4a\x12\xb0\x1f\xf1\xa3"
+$ECHO -ne "\x37\xf8\xec\x30\x21\x06\xf3\xbb\x3b\x46\xf9\xaa\xeb\x1a\x31\x33"
+$ECHO -ne "\x2b\xa5\x87\x4d\x4d\xbc\x13\xda\xa9\xa0\x4c\xca\x0b\x46\xa6\x09"
+$ECHO -ne "\x41\x73\xbb\x3f\x71\x9f\x6a\x88\x41\x02\x20\x3f\x54\x6d\x42\xc8"
+$ECHO -ne "\x70\x6e\x64\xd0\x50\x88\x83\xc4\x33\x78\x6d\x04\xb6\x43\xed\x5e"
+$ECHO -ne "\x72\x71\x6b\xd5\x65\x80\xcf\x66\x46\x29\x10\xdb\x79\xd1\xa9\x95"
+$ECHO -ne "\xb7\x9d\xf4\xa9\x93\xb4\x5a\x00\xc6\xb2\xad\xbf\x7e\xaa\xe6\x8d"
+$ECHO -ne "\x8c\xc0\x6b\xf5\xc2\xad\x00\xfb\x08\x63\xe2\xb5\xb1\xe0\x76\xac"
+$ECHO -ne "\x0a\xe4\x72\xb5\x72\xbc\x24\x6b\xef\x62\x0f\xaa\xb9\x28\xd2\x3c"
+$ECHO -ne "\xf6\x3e\x26\x20\x8b\xcc\xd2\x61\xcd\xd4\xc7\xed\x39\xa8\x39\x4e"
+$ECHO -ne "\x7e\x05\x97\xeb\x29\x24\x3a\xb2\xc9\xbd\xad\xcf\xf0\x22\xbc\xdd"
+$ECHO -ne "\xb8\x8c\x2e\x6a\xbf\x4f\x67\x2f\xfc\x07\xd0\x53\x0c\x54\x30\x35"
+$ECHO -ne "\xf8\xf1\x76\x45\x26\x5a\x86\x11\x1e\xeb\x58\xc0\x2d\x6c\x3d\x87"
+$ECHO -ne "\xa6\xca\x8e\x89\x4f\x75\x88\xf9\xb9\x9a\x99\x8b\x68\x22\xe2\x52"
+$ECHO -ne "\x4d\x46\x8d\x44\x31\x2b\xc1\xb0\x19\xa7\x90\xfb\x95\xda\x19\x2f"
+$ECHO -ne "\x6e\x0d\xe2\xc1\x85\xd0\x1f\x9b\xd3\xae\x33\xe3\x55\xa4\x77\xf2"
+$ECHO -ne "\xf1\xd7\xa8\xf0\x57\x30\xc4\x3b\xe6\x55\x97\xf9\xe3\x89\x82\xda"
+$ECHO -ne "\xae\x02\x45\xb1\x86\x8c\x84\x4c\xb2\xcf\x82\xdb\x4e\x04\x45\xcc"
+$ECHO -ne "\x19\x53\x9e\x2f\x95\xa9\xc7\xa8\x08\x17\x61\xc1\x8c\x26\x7f\x9b"
+$ECHO -ne "\x07\x8c\xe7\x77\x2d\x12\xd2\xcd\xc6\x97\xcf\x29\x3a\x1e\xac\x2b"
+$ECHO -ne "\x69\xb9\xb4\x61\xee\xeb\xb3\xae\x60\x18\xa0\x3a\xe5\xc0\xb9\x58"
+$ECHO -ne "\x38\x4d\x32\x57\x81\x89\x99\x29\x73\xdd\x47\x43\x2f\x1e\x39\xc6"
+$ECHO -ne "\x06\xbf\x7f\x64\x9e\x91\xc3\x9f\x18\x1b\xba\xf8\xb5\x29\x5d\xe3"
+$ECHO -ne "\x46\x7e\xb5\x1a\xfd\x9b\xb0\x1b\x85\x06\xc3\xc5\x09\xdb\x82\xd0"
+$ECHO -ne "\xd1\xff\xe1\x0f\xeb\x37\x1d\xce\x65\x6d\x26\x55\xe0\x20\x00\xc4"
+$ECHO -ne "\x36\x2f\xba\x86\x26\xc6\x7b\xa4\xe9\xb1\x41\x20\x04\x11\xeb\x24"
+$ECHO -ne "\x3c\x72\xbf\xd3\xc5\xb3\xbd\xce\x14\x45\x2d\x50\x01\x00\x26\x39"
+$ECHO -ne "\x3c\x85\x17\x0e\x42\x66\x8a\x1c\x94\xa8\x90\x02\xc4\x42\xd8\xd1"
+$ECHO -ne "\xcc\x94\x7a\x25\xad\xfd\x8d\xa4\x0e\xe0\xcb\x92\x5e\x6f\x14\x2b"
+$ECHO -ne "\x29\xbd\xc0\x81\x20\x3f\x0b\x2c\x7a\x2c\xe7\xba\x6d\x99\x14\xbe"
+$ECHO -ne "\xd5\x39\xc8\x6f\x2e\xbd\x79\x59\x19\x75\xb6\xf5\x4f\x12\xf6\x8e"
+$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
+    hello_$ext >t2.$ext
+}
+
+check() {
+    eval $2 >t_actual 2>&1
+    if $ECHO -ne "$expected" | cmp - t_actual; then
+	echo "PASS: $1"
+    else
+	echo "FAIL: $1"
+	FAILCOUNT=$((FAILCOUNT + 1))
+    fi
+}
+
+mkdir testdir 2>/dev/null
+(
+cd testdir || { echo "cannot cd testdir!"; exit 1; }
+
+expected="$unpack: z: No such file or directory
+1
+HELLO
+"
+prep; check "$unpack: doesnt exist" "${bb}$unpack z t1.$ext; echo \$?; cat t1"
+
+
+expected="$unpack: t.zz: unknown suffix - ignored
+1
+HELLO
+"
+prep; >t.zz; check "$unpack: unknown suffix" "${bb}$unpack t.zz t1.$ext; echo \$?; cat t1"
+
+
+# In this case file "t1" exists, and we skip t1.gz and unpack t2.gz
+expected="$unpack: can't open 't1': File exists
+1
+HELLO
+"
+prep; >t1; check "$unpack: already exists" "${bb}$unpack t1.$ext t2.$ext; echo \$?; cat t1 t2"
+
+
+# From old testsuite
+expected="HELLO\n0\n"
+prep; check "$unpack: stream unpack" "cat t1.$ext | ${bb}$unpack; echo \$?"
+
+expected="ok\n"
+prep; check "$unpack: delete src" "${bb}$unpack t2.$ext; test ! -f t2.$ext && echo ok"
+
+)
+rm -rf testdir
+
+# This test is only for bunzip2
+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"
+    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: pbzip_4m_zeros file"
+	FAILCOUNT=$((FAILCOUNT + 1))
+    fi
+fi
+
+exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
diff --git a/busybox-1.19.3/testsuite/bunzip2/bunzip2-reads-from-standard-input b/busybox-1.19.3/testsuite/bunzip2/bunzip2-reads-from-standard-input
new file mode 100644
index 0000000..e212a12
--- /dev/null
+++ b/busybox-1.19.3/testsuite/bunzip2/bunzip2-reads-from-standard-input
@@ -0,0 +1,2 @@
+echo foo | bzip2 | busybox bunzip2 > output
+echo foo | cmp - output
diff --git a/busybox-1.19.3/testsuite/bunzip2/bunzip2-removes-compressed-file b/busybox-1.19.3/testsuite/bunzip2/bunzip2-removes-compressed-file
new file mode 100644
index 0000000..f1d1550
--- /dev/null
+++ b/busybox-1.19.3/testsuite/bunzip2/bunzip2-removes-compressed-file
@@ -0,0 +1,3 @@
+echo foo | bzip2 >foo.bz2
+busybox bunzip2 foo.bz2
+test ! -f foo.bz2
diff --git a/busybox-1.19.3/testsuite/bunzip2/bzcat-does-not-remove-compressed-file b/busybox-1.19.3/testsuite/bunzip2/bzcat-does-not-remove-compressed-file
new file mode 100644
index 0000000..7d4016e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/bunzip2/bzcat-does-not-remove-compressed-file
@@ -0,0 +1,3 @@
+echo foo | bzip2 >foo.bz2
+busybox bzcat foo.bz2
+test -f foo.bz2
diff --git a/busybox-1.19.3/testsuite/busybox.tests b/busybox-1.19.3/testsuite/busybox.tests
new file mode 100755
index 0000000..04fea3e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/busybox.tests
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+# Tests for busybox applet itself.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+HELPDUMP=`true | busybox 2>&1 | cat`
+
+# We need to test under calling the binary under other names.
+
+optional FEATURE_VERBOSE_USAGE
+testing "busybox --help busybox" "true | busybox --help busybox 2>&1 | cat" "$HELPDUMP\n\n" "" ""
+SKIP=
+
+ln -s `which busybox` busybox-suffix
+for i in busybox ./busybox-suffix
+do
+	# The gratuitous "\n"s are due to a shell idiosyncrasy:
+	# environment variables seem to strip trailing whitespace.
+
+	testing "" "$i" "$HELPDUMP\n\n" "" ""
+
+	testing "$i unknown" "$i unknown 2>&1" \
+		"unknown: applet not found\n" "" ""
+
+	testing "$i --help" "$i --help 2>&1" "$HELPDUMP\n\n" "" ""
+
+	optional FEATURE_VERBOSE_USAGE CAT
+	testing "" "$i cat" "moo" "" "moo"
+	testing "$i --help cat" "$i --help cat 2>&1 | grep print" \
+		"Concatenate FILEs and print them to stdout\n" "" ""
+	SKIP=
+
+	testing "$i --help unknown" "$i --help unknown 2>&1" \
+		"unknown: applet not found\n" "" ""
+done
+rm busybox-suffix
+
+ln -s `which busybox` unknown
+
+testing "busybox as unknown name" "./unknown 2>&1" \
+	"unknown: applet not found\n" "" ""
+rm unknown
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/bzcat.tests b/busybox-1.19.3/testsuite/bzcat.tests
new file mode 100755
index 0000000..5b4f3f4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/bzcat.tests
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+FAILCOUNT=0
+
+ext=bz2
+
+bb="busybox "
+
+unset LC_ALL
+unset LC_MESSAGES
+unset LANG
+unset LANGUAGE
+
+hello_gz() {
+    # Gzipped "HELLO\n"
+    #_________________________ vvv vvv vvv vvv - mtime
+    $ECHO -ne "\x1f\x8b\x08\x00\x85\x1d\xef\x45\x02\x03\xf3\x70\xf5\xf1\xf1\xe7"
+    $ECHO -ne "\x02\x00\x6e\xd7\xac\xfd\x06\x00\x00\x00"
+}
+
+hello_bz2() {
+    # Bzipped "HELLO\n"
+    $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x5b\xb8\xe8\xa3\x00\x00"
+    $ECHO -ne "\x01\x44\x00\x00\x10\x02\x44\xa0\x00\x30\xcd\x00\xc3\x46\x29\x97"
+    $ECHO -ne "\x17\x72\x45\x38\x50\x90\x5b\xb8\xe8\xa3"
+}
+
+prep() {
+    rm -f t*
+    hello_$ext >t1.$ext
+    hello_$ext >t2.$ext
+}
+
+check() {
+    eval $2 >t_actual 2>&1
+    if $ECHO -ne "$expected" | cmp - t_actual; then
+	echo "PASS: $1"
+    else
+	echo "FAIL: $1"
+	FAILCOUNT=$((FAILCOUNT + 1))
+    fi
+}
+
+mkdir testdir 2>/dev/null
+(
+cd testdir || { echo "cannot cd testdir!"; exit 1; }
+
+expected="HELLO\nok\n"
+prep; check "bzcat: dont delete src" "${bb}bzcat t2.bz2; test -f t2.bz2 && echo ok"
+
+)
+rm -rf testdir
+
+exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
diff --git a/busybox-1.19.3/testsuite/cal.tests b/busybox-1.19.3/testsuite/cal.tests
new file mode 100755
index 0000000..5509cf0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cal.tests
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Copyright 2010 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+test -f "$bindir/.config" && . "$bindir/.config"
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+testing "cal 2000" "cal 1 2000" "\
+    January 2000
+Su Mo Tu We Th Fr Sa
+                   1
+ 2  3  4  5  6  7  8
+ 9 10 11 12 13 14 15
+16 17 18 19 20 21 22
+23 24 25 26 27 28 29
+30 31
+" "" ""
+
+test x"$CONFIG_LOCALE_SUPPORT" = x"y" \
+&& test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
+&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \
+&& test x"$CONFIG_UNICODE_WIDE_WCHARS" = x"y" \
+&& test x"$CONFIG_STATIC" != x"y" \
+&& test x"$CONFIG_CROSS_COMPILER_PREFIX" = x"" \
+&& testing "unicode cal 2000" "LANG=zh_TW.utf8 cal 1 2000" "\
+    一月 2000
+日 一 二 三 四 五 六
+                   1
+ 2  3  4  5  6  7  8
+ 9 10 11 12 13 14 15
+16 17 18 19 20 21 22
+23 24 25 26 27 28 29
+30 31
+" "" ""
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/cat/cat-prints-a-file b/busybox-1.19.3/testsuite/cat/cat-prints-a-file
new file mode 100644
index 0000000..e3f35a8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cat/cat-prints-a-file
@@ -0,0 +1,3 @@
+echo I WANT > foo
+busybox cat foo >bar
+cmp foo bar
diff --git a/busybox-1.19.3/testsuite/cat/cat-prints-a-file-and-standard-input b/busybox-1.19.3/testsuite/cat/cat-prints-a-file-and-standard-input
new file mode 100644
index 0000000..bc92318
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cat/cat-prints-a-file-and-standard-input
@@ -0,0 +1,7 @@
+echo I WANT > foo
+echo SOMETHING | busybox cat foo - >bar
+cat >baz <<EOF
+I WANT
+SOMETHING
+EOF
+cmp bar baz
diff --git a/busybox-1.19.3/testsuite/cmp/cmp-detects-difference b/busybox-1.19.3/testsuite/cmp/cmp-detects-difference
new file mode 100644
index 0000000..b9bb628
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cmp/cmp-detects-difference
@@ -0,0 +1,9 @@
+echo foo >foo
+echo bar >bar
+set +e
+busybox cmp -s foo bar
+if [ $? != 0 ] ; then
+	exit 0;
+fi
+
+exit 1;
diff --git a/busybox-1.19.3/testsuite/comm.tests b/busybox-1.19.3/testsuite/comm.tests
new file mode 100755
index 0000000..2f48168
--- /dev/null
+++ b/busybox-1.19.3/testsuite/comm.tests
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# Copyright 2008 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "description" "command" "result" "infile" "stdin"
+
+testing "comm test 1" "comm input -"              "\t123\n""456\n""abc\n""\tdef\n" "456\nabc\n" "123\ndef\n"
+testing "comm test 2" "comm - input"              "123\n""\t456\n""\tabc\n""def\n" "456\nabc\n" "123\ndef\n"
+testing "comm test 3" "comm input -"              "abc\n""\tdef\n""xyz\n"          "abc\nxyz\n" "def\n"
+testing "comm test 4" "comm - input"              "\tabc\n""def\n""\txyz\n"        "abc\nxyz\n" "def\n"
+testing "comm test 5" "comm input -"              "123\n""abc\n""\tdef\n"          "123\nabc\n" "def\n"
+testing "comm test 6" "comm - input"              "\t123\n""\tabc\n""def\n"        "123\nabc\n" "def\n"
+testing "comm unterminated line 1" "comm input -" "abc\n""\tdef\n"                 "abc"        "def"
+testing "comm unterminated line 2" "comm - input" "\tabc\n""def\n"                 "abc"        "def"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/cp.tests b/busybox-1.19.3/testsuite/cp.tests
new file mode 100755
index 0000000..33cd876
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp.tests
@@ -0,0 +1,206 @@
+#!/bin/sh
+# Copyright 2010 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# Opening quote in "omitting directory 'dir'" message:
+sq='`'  # GNU cp: `
+sq="'"  # bbox cp: '
+
+rm -rf cp.testdir >/dev/null
+
+mkdir cp.testdir
+mkdir cp.testdir/dir
+> cp.testdir/dir/file
+ln -s file cp.testdir/dir/file_symlink
+
+> cp.testdir/file
+ln -s file cp.testdir/file_symlink
+ln -s dir cp.testdir/dir_symlink
+
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp" '\
+cd cp.testdir || exit 1; cp * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file         && test   -f file             || echo BAD: file
+test ! -L file_symlink && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir          && test ! -e dir              || echo BAD: dir
+test ! -L dir_symlink  && test ! -e dir_symlink      || echo BAD: dir_symlink
+' "\
+cp: omitting directory ${sq}dir'
+cp: omitting directory ${sq}dir_symlink'
+1
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -d" '\
+cd cp.testdir || exit 1; cp -d * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file         && test   -f file             || echo BAD: file
+test   -L file_symlink && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir          && test ! -e dir              || echo BAD: dir
+test   -L dir_symlink  && test ! -e dir_symlink      || echo BAD: dir_symlink
+' "\
+cp: omitting directory ${sq}dir'
+1
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -P" '\
+cd cp.testdir || exit 1; cp -P * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file         && test   -f file             || echo BAD: file
+test   -L file_symlink && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir          && test ! -e dir              || echo BAD: dir
+test   -L dir_symlink  && test ! -e dir_symlink      || echo BAD: dir_symlink
+' "\
+cp: omitting directory ${sq}dir'
+1
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -L" '\
+cd cp.testdir || exit 1; cp -L * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file         && test   -f file             || echo BAD: file
+test ! -L file_symlink && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir          && test ! -e dir              || echo BAD: dir
+test ! -L dir_symlink  && test ! -e dir_symlink      || echo BAD: dir_symlink
+' "\
+cp: omitting directory ${sq}dir'
+cp: omitting directory ${sq}dir_symlink'
+1
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -H" '\
+cd cp.testdir || exit 1; cp -H * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file         && test   -f file             || echo BAD: file
+test ! -L file_symlink && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir          && test ! -e dir              || echo BAD: dir
+test ! -L dir_symlink  && test ! -e dir_symlink      || echo BAD: dir_symlink
+' "\
+cp: omitting directory ${sq}dir'
+cp: omitting directory ${sq}dir_symlink'
+1
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -R" '\
+cd cp.testdir || exit 1; cp -R * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test   -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test   -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test   -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -Rd" '\
+cd cp.testdir || exit 1; cp -Rd * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test   -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test   -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test   -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -RP" '\
+cd cp.testdir || exit 1; cp -RP * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test   -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test   -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test   -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -RL" '\
+cd cp.testdir || exit 1; cp -RL * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test ! -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test ! -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test ! -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+# GNU coreutils 7.2 says:
+# cp: will not create hard link `../cp.testdir2/dir_symlink' to directory `../cp.testdir2/dir'
+test x"$SKIP_KNOWN_BUGS" = x"" && \
+testing "cp -RH" '\
+cd cp.testdir || exit 1; cp -RH * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test ! -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test ! -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test   -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+# GNU coreutils 7.2 says:
+# cp: will not create hard link `../cp.testdir2/dir_symlink' to directory `../cp.testdir2/dir'
+test x"$SKIP_KNOWN_BUGS" = x"" && \
+testing "cp -RHP" '\
+cd cp.testdir || exit 1; cp -RHP * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test ! -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test ! -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test   -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+testing "cp -RHL" '\
+cd cp.testdir || exit 1; cp -RHL * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test ! -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test ! -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test ! -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+rm -rf cp.testdir2 >/dev/null && mkdir cp.testdir2 || exit 1
+# Wow! "cp -RLH" is not the same as "cp -RHL" (prev test)!
+# GNU coreutils 7.2 says:
+# cp: will not create hard link `../cp.testdir2/dir_symlink' to directory `../cp.testdir2/dir'
+test x"$SKIP_KNOWN_BUGS" = x"" && \
+testing "cp -RLH" '\
+cd cp.testdir || exit 1; cp -RLH * ../cp.testdir2 2>&1; echo $?; cd ../cp.testdir2 || exit 1
+test ! -L file             && test   -f file             || echo BAD: file
+test ! -L file_symlink     && test   -f file_symlink     || echo BAD: file_symlink
+test ! -L dir              && test   -d dir              || echo BAD: dir
+test ! -L dir_symlink      && test   -d dir_symlink      || echo BAD: dir_symlink
+test ! -L dir/file         && test   -f dir/file         || echo BAD: dir/file
+test ! -L dir/file_symlink && test   -f dir/file_symlink || echo BAD: dir/file_symlink
+' "\
+0
+" "" ""
+
+
+# Clean up
+rm -rf cp.testdir cp.testdir2 2>/dev/null
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/cp/cp-RHL-does_not_preserve-links b/busybox-1.19.3/testsuite/cp/cp-RHL-does_not_preserve-links
new file mode 100644
index 0000000..eed6c3e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-RHL-does_not_preserve-links
@@ -0,0 +1,6 @@
+mkdir a
+>a/file
+ln -s file a/link
+busybox cp -RHL a b
+test ! -L b/link
+#sh </dev/tty >/dev/tty 2>&1
diff --git a/busybox-1.19.3/testsuite/cp/cp-a-files-to-dir b/busybox-1.19.3/testsuite/cp/cp-a-files-to-dir
new file mode 100644
index 0000000..b199ef9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-a-files-to-dir
@@ -0,0 +1,15 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+# why???
+#TZ=UTC0 touch -d '2000-01-30 05:24:08' dir1/file3
+mkdir there
+busybox cp -a file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! dir1/file3 -ot there/dir1/file3
+test ! dir1/file3 -nt there/dir1/file3
diff --git a/busybox-1.19.3/testsuite/cp/cp-a-preserves-links b/busybox-1.19.3/testsuite/cp/cp-a-preserves-links
new file mode 100644
index 0000000..0c0cd96
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-a-preserves-links
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox cp -a bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/busybox-1.19.3/testsuite/cp/cp-copies-empty-file b/busybox-1.19.3/testsuite/cp/cp-copies-empty-file
new file mode 100644
index 0000000..ad25aa1
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-copies-empty-file
@@ -0,0 +1,3 @@
+touch foo
+busybox cp foo bar
+cmp foo bar
diff --git a/busybox-1.19.3/testsuite/cp/cp-copies-large-file b/busybox-1.19.3/testsuite/cp/cp-copies-large-file
new file mode 100644
index 0000000..c2225c6
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-copies-large-file
@@ -0,0 +1,3 @@
+dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
+busybox cp foo bar
+cmp foo bar
diff --git a/busybox-1.19.3/testsuite/cp/cp-copies-small-file b/busybox-1.19.3/testsuite/cp/cp-copies-small-file
new file mode 100644
index 0000000..d52a887
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-copies-small-file
@@ -0,0 +1,3 @@
+echo I WANT > foo
+busybox cp foo bar
+cmp foo bar
diff --git a/busybox-1.19.3/testsuite/cp/cp-d-files-to-dir b/busybox-1.19.3/testsuite/cp/cp-d-files-to-dir
new file mode 100644
index 0000000..9571a56
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-d-files-to-dir
@@ -0,0 +1,11 @@
+echo file number one > file1
+echo file number two > file2
+touch file3
+ln -s file2 link1
+mkdir there
+busybox cp -d file1 file2 file3 link1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
diff --git a/busybox-1.19.3/testsuite/cp/cp-dev-file b/busybox-1.19.3/testsuite/cp/cp-dev-file
new file mode 100644
index 0000000..055f0d9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-dev-file
@@ -0,0 +1,2 @@
+busybox cp /dev/null foo
+test -f foo
diff --git a/busybox-1.19.3/testsuite/cp/cp-dir-create-dir b/busybox-1.19.3/testsuite/cp/cp-dir-create-dir
new file mode 100644
index 0000000..a8d7b50
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-dir-create-dir
@@ -0,0 +1,4 @@
+mkdir bar
+touch bar/baz
+busybox cp -R bar foo
+test -f foo/baz
diff --git a/busybox-1.19.3/testsuite/cp/cp-dir-existing-dir b/busybox-1.19.3/testsuite/cp/cp-dir-existing-dir
new file mode 100644
index 0000000..4c788ba
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-dir-existing-dir
@@ -0,0 +1,5 @@
+mkdir bar
+touch bar/baz
+mkdir foo
+busybox cp -R bar foo
+test -f foo/bar/baz
diff --git a/busybox-1.19.3/testsuite/cp/cp-does-not-copy-unreadable-file b/busybox-1.19.3/testsuite/cp/cp-does-not-copy-unreadable-file
new file mode 100644
index 0000000..e17e8e6
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-does-not-copy-unreadable-file
@@ -0,0 +1,11 @@
+touch foo
+chmod a-r foo
+set +e
+if test `id -u` = 0; then
+    # run as user with nonzero uid
+    setuidgid 1 busybox cp foo bar
+else
+    busybox cp foo bar
+fi
+set -e
+test ! -f bar
diff --git a/busybox-1.19.3/testsuite/cp/cp-files-to-dir b/busybox-1.19.3/testsuite/cp/cp-files-to-dir
new file mode 100644
index 0000000..fdb8191
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-files-to-dir
@@ -0,0 +1,11 @@
+echo file number one > file1
+echo file number two > file2
+touch file3
+ln -s file2 link1
+mkdir there
+busybox cp file1 file2 file3 link1 there
+test -f there/file1
+test -f there/file2
+test ! -s there/file3
+test -f there/link1
+cmp there/file2 there/link1
diff --git a/busybox-1.19.3/testsuite/cp/cp-follows-links b/busybox-1.19.3/testsuite/cp/cp-follows-links
new file mode 100644
index 0000000..2d9f05e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-follows-links
@@ -0,0 +1,4 @@
+touch foo
+ln -s foo bar
+busybox cp bar baz
+test -f baz
diff --git a/busybox-1.19.3/testsuite/cp/cp-parents b/busybox-1.19.3/testsuite/cp/cp-parents
new file mode 100644
index 0000000..e27c162
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-parents
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_CP_LONG_OPTIONS
+mkdir -p foo/bar/baz
+touch foo/bar/baz/file
+mkdir dir
+busybox cp --parents foo/bar/baz/file dir
+test -f dir/foo/bar/baz/file
diff --git a/busybox-1.19.3/testsuite/cp/cp-preserves-hard-links b/busybox-1.19.3/testsuite/cp/cp-preserves-hard-links
new file mode 100644
index 0000000..4de7b85
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-preserves-hard-links
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
+touch foo
+ln foo bar
+mkdir baz
+busybox cp -d foo bar baz
+test baz/foo -ef baz/bar
diff --git a/busybox-1.19.3/testsuite/cp/cp-preserves-links b/busybox-1.19.3/testsuite/cp/cp-preserves-links
new file mode 100644
index 0000000..301dc5f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-preserves-links
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox cp -d bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/busybox-1.19.3/testsuite/cp/cp-preserves-source-file b/busybox-1.19.3/testsuite/cp/cp-preserves-source-file
new file mode 100644
index 0000000..f0f5065
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cp/cp-preserves-source-file
@@ -0,0 +1,3 @@
+touch foo
+busybox cp foo bar
+test -f foo
diff --git a/busybox-1.19.3/testsuite/cpio.tests b/busybox-1.19.3/testsuite/cpio.tests
new file mode 100755
index 0000000..4cd441a
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cpio.tests
@@ -0,0 +1,133 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# 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
+# grep: remove "total NNN" lines
+FILTER_LS="sed 's/  */ /g' | cut -d' ' -f 1-5,9- | grep -v '^total '"
+
+
+# newc cpio archive of directory cpio.testdir with empty x and y hardlinks
+hexdump="\
+\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x64\x1e\x91\x8c\x00\x00\
+\x48\x7f\x80\x4c\x48\x08\x00\x28\x01\xff\xe0\x3f\x24\x14\x00\x0e\
+\x20\xdc\x60\x20\x00\x92\x11\xea\xa0\x1a\x00\x00\x00\x03\x20\x8a\
+\x93\xd4\x9a\x68\x1a\x0d\x1e\x91\xa1\xa0\x06\x98\xe3\x5c\x2f\xd9\
+\x26\xa1\x25\x24\x20\xed\x47\xc7\x21\x40\x2b\x6e\xf2\xe6\xfe\x98\
+\x13\x68\xa8\xbd\x82\xb2\x4f\x26\x02\x24\x16\x5b\x22\x16\x72\x74\
+\x15\xcd\xc1\xa6\x9e\xa6\x5e\x6c\x16\x37\x35\x01\x99\xc4\x81\x21\
+\x29\x28\x4b\x69\x51\xa9\x3c\x1a\x9b\x0a\xe1\xe4\xb4\xaf\x85\x73\
+\xba\x23\x10\x59\xe8\xb3\xe1\xa1\x63\x05\x8c\x4f\xc5\xdc\x91\x4e\
+\x14\x24\x19\x07\xa4\x63\x00"
+
+user=$(id -u)
+group=$(id -g)
+
+rm -rf cpio.testdir cpio.testdir2 2>/dev/null
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+optional FEATURE_LS_SORTFILES FEATURE_LS_TIMESTAMPS
+testing "cpio extracts zero-sized hardlinks" \
+"$ECHO -ne '$hexdump' | bzcat | cpio -i 2>&1; echo \$?;
+ls -ln cpio.testdir | $FILTER_LS" \
+"\
+1 blocks
+0
+-rw-r--r-- 2 $user $group 0 x
+-rw-r--r-- 2 $user $group 0 y
+" "" ""
+SKIP=
+
+
+test x"$SKIP_KNOWN_BUGS" = x"" && {
+# Currently fails. Numerous buglets: "1 blocks" versus "1 block",
+# does not list cpio.testdir/x and cpio.testdir/y
+testing "cpio lists hardlinks" \
+"$ECHO -ne '$hexdump' | bzcat | cpio -t 2>&1; echo \$?" \
+"\
+cpio.testdir
+cpio.testdir/x
+cpio.testdir/y
+1 blocks
+0
+" "" ""
+}
+
+
+# More complex case
+rm -rf cpio.testdir cpio.testdir2 2>/dev/null
+mkdir cpio.testdir
+touch cpio.testdir/solo
+touch cpio.testdir/empty
+echo x >cpio.testdir/nonempty
+ln cpio.testdir/empty cpio.testdir/empty1
+ln cpio.testdir/nonempty cpio.testdir/nonempty1
+mkdir cpio.testdir2
+
+optional FEATURE_CPIO_O LONG_OPTS FEATURE_LS_SORTFILES FEATURE_LS_TIMESTAMPS
+testing "cpio extracts zero-sized hardlinks 2" \
+"find cpio.testdir | cpio -H newc --create | (cd cpio.testdir2 && cpio -i 2>&1); echo \$?;
+ls -ln cpio.testdir2/cpio.testdir | $FILTER_LS" \
+"\
+2 blocks
+0
+-rw-r--r-- 2 $user $group 0 empty
+-rw-r--r-- 2 $user $group 0 empty1
+-rw-r--r-- 2 $user $group 2 nonempty
+-rw-r--r-- 2 $user $group 2 nonempty1
+-rw-r--r-- 1 $user $group 0 solo
+" "" ""
+SKIP=
+
+# Was trying to create "/usr/bin", correct is "usr/bin".
+rm -rf cpio.testdir
+optional FEATURE_CPIO_P
+testing "cpio -p with absolute paths" \
+"echo /usr/bin | cpio -dp cpio.testdir 2>&1; echo \$?;
+ls cpio.testdir" \
+"\
+1 blocks
+0
+usr
+" "" ""
+SKIP=
+
+# chown on a link was affecting file, dropping its suid/sgid bits
+rm -rf cpio.testdir
+optional FEATURE_CPIO_O FEATURE_STAT_FORMAT
+mkdir cpio.testdir
+touch cpio.testdir/file
+chmod 6755 cpio.testdir/file  # sets suid/sgid bits
+ln -sf file cpio.testdir/link
+testing "cpio restores suid/sgid bits" \
+"cd cpio.testdir && { echo file; echo link; } | cpio -ovHnewc >pack.cpio && rm ???? && cpio -idmvu <pack.cpio 2>/dev/null;
+ stat -c '%a %n' file" \
+"\
+file
+link
+6755 file
+" "" ""
+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
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/cut.tests b/busybox-1.19.3/testsuite/cut.tests
new file mode 100755
index 0000000..1103402
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cut.tests
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./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 "cut '-' (stdin) and multi file handling" \
+	"cut -d' ' -f2 - input" \
+	"over\n""quick\n" \
+	"the quick brown fox\n" \
+	"jumps over the lazy dog\n" \
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/cut/cut-cuts-a-character b/busybox-1.19.3/testsuite/cut/cut-cuts-a-character
new file mode 100644
index 0000000..d6c5efa
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cut/cut-cuts-a-character
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 3) = c
diff --git a/busybox-1.19.3/testsuite/cut/cut-cuts-a-closed-range b/busybox-1.19.3/testsuite/cut/cut-cuts-a-closed-range
new file mode 100644
index 0000000..9680b76
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cut/cut-cuts-a-closed-range
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 1-2) = ab
diff --git a/busybox-1.19.3/testsuite/cut/cut-cuts-a-field b/busybox-1.19.3/testsuite/cut/cut-cuts-a-field
new file mode 100644
index 0000000..e200b6b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cut/cut-cuts-a-field
@@ -0,0 +1 @@
+test $($ECHO -e "f1\tf2\tf3" | busybox cut -f 2) = f2
diff --git a/busybox-1.19.3/testsuite/cut/cut-cuts-an-open-range b/busybox-1.19.3/testsuite/cut/cut-cuts-an-open-range
new file mode 100644
index 0000000..1fbf277
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cut/cut-cuts-an-open-range
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c -3) = abc
diff --git a/busybox-1.19.3/testsuite/cut/cut-cuts-an-unclosed-range b/busybox-1.19.3/testsuite/cut/cut-cuts-an-unclosed-range
new file mode 100644
index 0000000..a2b0cdb
--- /dev/null
+++ b/busybox-1.19.3/testsuite/cut/cut-cuts-an-unclosed-range
@@ -0,0 +1 @@
+test $(echo abcd | busybox cut -c 3-) = cd
diff --git a/busybox-1.19.3/testsuite/date/date-@-works b/busybox-1.19.3/testsuite/date/date-@-works
new file mode 100644
index 0000000..03b4c7f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/date/date-@-works
@@ -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`"
diff --git a/busybox-1.19.3/testsuite/date/date-R-works b/busybox-1.19.3/testsuite/date/date-R-works
new file mode 100644
index 0000000..81378cc
--- /dev/null
+++ b/busybox-1.19.3/testsuite/date/date-R-works
@@ -0,0 +1,13 @@
+# 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"`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
diff --git a/busybox-1.19.3/testsuite/date/date-format-works b/busybox-1.19.3/testsuite/date/date-format-works
new file mode 100644
index 0000000..f2a2091
--- /dev/null
+++ b/busybox-1.19.3/testsuite/date/date-format-works
@@ -0,0 +1,4 @@
+# TODO: gnu date doesn't accept '2000.11.22-11:22:33' format,
+# but accepts '2000-11-22 11:22:33'. We must follow.
+test x"01/01/99" = x"`busybox date -d 1999.01.01-11:22:33 '+%d/%m/%y'`"
+test x"22/11/00" = x"`busybox date -d 2000.11.22-11:22:33 '+%d/%m/%y'`"
diff --git a/busybox-1.19.3/testsuite/date/date-u-works b/busybox-1.19.3/testsuite/date/date-u-works
new file mode 100644
index 0000000..eea6e5a
--- /dev/null
+++ b/busybox-1.19.3/testsuite/date/date-u-works
@@ -0,0 +1 @@
+test x"Sat Jan  1 11:22:33 UTC 2000" = x"`TZ=CET-1CEST-2 busybox date -u -d 2000.01.01-11:22:33`"
diff --git a/busybox-1.19.3/testsuite/date/date-works b/busybox-1.19.3/testsuite/date/date-works
new file mode 100644
index 0000000..901c485
--- /dev/null
+++ b/busybox-1.19.3/testsuite/date/date-works
@@ -0,0 +1,44 @@
+dt=`busybox date`
+# Expected format: Fri Apr 25 03:47:55 CEST 2008
+dt=`echo "$dt" | sed 's/^[^ ][^ ][^ ] [^ ][^ ][^ ] [ 0123][0-9] [012][0-9]:[0-5][0-9]:[0-6][0-9] [A-Z][A-Z]* [012][0-9][0-9][0-9]$/OK/'`
+test x"$dt" = x"OK"
+
+dt=`busybox date -d 1:2`
+dt=`echo "$dt" | cut -b12-19`
+test x"$dt" = x"01:02:00"
+
+dt=`busybox date -d 1:2:3`
+dt=`echo "$dt" | cut -b12-19`
+test x"$dt" = x"01:02:03"
+
+dt=`busybox date -d 1.2-3:4`
+dt=`echo "$dt" | cut -b5-19`
+test x"$dt" = x"Jan  2 03:04:00"
+
+dt=`busybox date -d 1.2-3:4:5`
+dt=`echo "$dt" | cut -b5-19`
+test x"$dt" = x"Jan  2 03:04:05"
+
+dt=`busybox date -d 1999.1.2-3:4`
+dt=`echo "$dt" | cut -b1-19`
+test x"$dt" = x"Sat Jan  2 03:04:00"
+
+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 '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"
+
+dt=`busybox date -d 200001231133`
+dt=`echo "$dt" | cut -b1-19`
+test x"$dt" = x"Sun Jan 23 11:33:00"
+
+dt=`busybox date -d 200001231133.30`
+dt=`echo "$dt" | cut -b1-19`
+test x"$dt" = x"Sun Jan 23 11:33:30"
diff --git a/busybox-1.19.3/testsuite/date/date-works-1 b/busybox-1.19.3/testsuite/date/date-works-1
new file mode 100644
index 0000000..cb5cea2
--- /dev/null
+++ b/busybox-1.19.3/testsuite/date/date-works-1
@@ -0,0 +1,134 @@
+unset LANG
+unset LANGUAGE
+unset LC_TIME
+unset LC_ALL
+
+dt=`busybox date -d 1:2 +%T`
+test x"$dt" = x"01:02:00"
+
+dt=`busybox date -d 1:2:3 +%T`
+test x"$dt" = x"01:02:03"
+
+host_date=/bin/date
+
+# date (GNU coreutils) 6.10 reports:
+#	date: invalid date '1.2-3:4'
+# busybox 1.11.0.svn date reports:
+#	date: invalid date '1/2 3:4'
+
+# TODO: (1) compare with strings, not "host date"
+# (2) implement d/m[/y] hh:mm[:ss] fmt in date applet
+#hdt=`$host_date -d '1/2 3:4'`
+#dt=`busybox date -d 1.2-3:4`
+#test x"$hdt" = x"$dt"
+
+#hdt=`$host_date -d '1/2 3:4:5'`
+#dt=`busybox date -d 1.2-3:4:5`
+#test x"$hdt" = x"$dt"
+
+#hdt=`$host_date -d '1/2/1999 3:4'`
+#dt=`busybox date -d 1999.1.2-3:4`
+#test x"$hdt" = x"$dt"
+
+#hdt=`$host_date -d '1/2/1999 3:4:5'`
+#dt=`busybox date -d 1999.1.2-3:4:5`
+#test x"$hdt" = x"$dt"
+
+hdt=`$host_date -d '1999-1-2 3:4:5'`
+dt=`busybox date -d '1999-1-2 3:4:5'`
+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"
+
+# date (GNU coreutils) 6.10 reports:
+#	date: invalid date '012311332000'
+dt=`busybox date -d 200001231133 +%c`
+test x"$dt" = x"Sun Jan 23 11:33:00 2000"
+
+# date (GNU coreutils) 6.10 reports:
+#	date: invalid date '012311332000'
+dt=`busybox date -d 200001231133.30 +%c`
+test x"$dt" = x"Sun Jan 23 11:33:30 2000"
+
+lcbbd="LC_ALL=C busybox date"
+wd=$(eval $lcbbd +%a)		# weekday name
+mn=$(eval $lcbbd +%b)		# month name
+dm=$(eval $lcbbd +%e)		# day of month, space padded
+h=$(eval $lcbbd +%H)		# hour, zero padded
+m=$(eval $lcbbd +%M)		# minute, zero padded
+s=$(eval $lcbbd +%S)		# second, zero padded
+z=$(eval $lcbbd -u +%Z)		# time zone abbreviation
+y=$(eval $lcbbd +%Y)		# year
+
+res=OK
+case $wd in
+	Sun)
+		;;
+	Mon)
+		;;
+	Tue)
+		;;
+	Wed)
+		;;
+	Thu)
+		;;
+	Fri)
+		;;
+	Sat)
+		;;
+	*)
+		res=BAD
+		;;
+esac
+
+case $mn in
+	Jan)
+		;;
+	Feb)
+		;;
+	Mar)
+		;;
+	Apr)
+		;;
+	May)
+		;;
+	Jun)
+		;;
+	Jul)
+		;;
+	Aug)
+		;;
+	Sep)
+		;;
+	Oct)
+		;;
+	Nov)
+		;;
+	Dec)
+		;;
+	*)
+		res=BAD
+		;;
+esac
+
+dm=${dm# *}
+[ $dm -ge 1 ] && [ $dm -le 31 ] || res=BAD
+h=${h#0}
+[ $h -ge 0 ] && [ $h -le 23 ] || res=BAD
+m=${m#0}
+[ $m -ge 0 ] && [ $m -le 59 ] || res=BAD
+s=${s#0}
+[ $s -ge 0 ] && [ $s -le 59 ] || res=BAD
+[ $z = UTC ] || res=BAD
+[ $y -ge 1970 ] || res=BAD
+
+test x"$res" = xOK
+
+# This should error out (by showing usage text). Testing for that
+dt=`busybox date -d 012311332000.30 %+c 2>&1 | head -n 1`
+test x"${dt##BusyBox * multi-call binary*}" = x""
diff --git a/busybox-1.19.3/testsuite/dd/dd-accepts-if b/busybox-1.19.3/testsuite/dd/dd-accepts-if
new file mode 100644
index 0000000..03d1af8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dd/dd-accepts-if
@@ -0,0 +1,2 @@
+echo I WANT >foo
+test "$(busybox dd if=foo 2>/dev/null)" = "I WANT"
diff --git a/busybox-1.19.3/testsuite/dd/dd-accepts-of b/busybox-1.19.3/testsuite/dd/dd-accepts-of
new file mode 100644
index 0000000..84405e6
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dd/dd-accepts-of
@@ -0,0 +1,2 @@
+echo I WANT | busybox dd of=foo 2>/dev/null
+echo I WANT | cmp foo -
diff --git a/busybox-1.19.3/testsuite/dd/dd-copies-from-standard-input-to-standard-output b/busybox-1.19.3/testsuite/dd/dd-copies-from-standard-input-to-standard-output
new file mode 100644
index 0000000..d890eb0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dd/dd-copies-from-standard-input-to-standard-output
@@ -0,0 +1 @@
+test "$(echo I WANT | busybox dd 2>/dev/null)" = "I WANT"
diff --git a/busybox-1.19.3/testsuite/dd/dd-prints-count-to-standard-error b/busybox-1.19.3/testsuite/dd/dd-prints-count-to-standard-error
new file mode 100644
index 0000000..2187dc0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dd/dd-prints-count-to-standard-error
@@ -0,0 +1,2 @@
+echo I WANT | busybox dd of=foo >/dev/null 2>bar
+grep -q records bar
diff --git a/busybox-1.19.3/testsuite/dd/dd-reports-write-errors b/busybox-1.19.3/testsuite/dd/dd-reports-write-errors
new file mode 100644
index 0000000..4920600
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dd/dd-reports-write-errors
@@ -0,0 +1,2 @@
+busybox dd if="$0" of=/dev/full 2>/dev/null || status=$?
+test $status = 1
diff --git a/busybox-1.19.3/testsuite/diff.tests b/busybox-1.19.3/testsuite/diff.tests
new file mode 100755
index 0000000..6de4648
--- /dev/null
+++ b/busybox-1.19.3/testsuite/diff.tests
@@ -0,0 +1,217 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "commands" "expected result" "file input" "stdin"
+
+# diff outputs date/time in the header, which should not be analysed
+# NB: sed has tab character in s command!
+TRIM_TAB="sed 's/	.*//'"
+
+testing "diff of stdin" \
+	"diff -u - input | $TRIM_TAB" \
+"\
+--- -
++++ input
+@@ -1 +1,3 @@
++qwe
+ asd
++zxc
+" \
+	"qwe\nasd\nzxc\n" \
+	"asd\n"
+
+testing "diff of stdin, no newline in the file" \
+	"diff -u - input | $TRIM_TAB" \
+"\
+--- -
++++ input
+@@ -1 +1,3 @@
++qwe
+ asd
++zxc
+\\ No newline at end of file
+" \
+	"qwe\nasd\nzxc" \
+	"asd\n"
+
+# we also test that stdin is in fact NOT read
+testing "diff of stdin, twice" \
+	'diff - -; echo $?; wc -c' \
+	"0\n5\n" \
+	"" \
+	"stdin"
+
+testing "diff of empty file against nonempty one" \
+	"diff -u - input | $TRIM_TAB" \
+"\
+--- -
++++ input
+@@ -0,0 +1 @@
++a
+" \
+	"a\n" \
+	""
+
+testing "diff -b treats EOF as whitespace" \
+	'diff -ub - input; echo $?' \
+	"0\n" \
+	"abc" \
+	"abc "
+
+testing "diff -b treats all spaces as equal" \
+	'diff -ub - input; echo $?' \
+	"0\n" \
+	"a \t c\n" \
+	"a\t \tc\n"
+
+testing "diff -B ignores changes whose lines are all blank" \
+	'diff -uB - input; echo $?' \
+	"0\n" \
+	"a\n" \
+	"\na\n\n"
+
+testing "diff -B does not ignore changes whose lines are not all blank" \
+	"diff -uB - input | $TRIM_TAB" \
+"\
+--- -
++++ input
+@@ -1,3 +1 @@
+-
+-b
+-
++a
+" \
+	"a\n" \
+	"\nb\n\n"
+
+testing "diff always takes context from old file" \
+	"diff -ub - input | $TRIM_TAB" \
+"\
+--- -
++++ input
+@@ -1 +1,3 @@
++abc
+ a c
++def
+" \
+	"abc\na  c\ndef\n" \
+	"a c\n"
+
+# testing "test name" "commands" "expected result" "file input" "stdin"
+
+# clean up
+rm -rf diff1 diff2
+
+mkdir diff1 diff2 diff2/subdir
+echo qwe >diff1/-
+echo asd >diff2/subdir/-
+optional FEATURE_DIFF_DIR
+testing "diff diff1 diff2/subdir" \
+	"diff -ur diff1 diff2/subdir | $TRIM_TAB" \
+"\
+--- diff1/-
++++ diff2/subdir/-
+@@ -1 +1 @@
+-qwe
++asd
+" \
+	"" ""
+SKIP=
+
+# using directory structure from prev test...
+optional FEATURE_DIFF_DIR
+testing "diff dir dir2/file/-" \
+	"diff -ur diff1 diff2/subdir/- | $TRIM_TAB" \
+"\
+--- diff1/-
++++ diff2/subdir/-
+@@ -1 +1 @@
+-qwe
++asd
+" \
+	"" ""
+SKIP=
+
+# using directory structure from prev test...
+mkdir diff1/test
+mkfifo diff2/subdir/test
+optional FEATURE_DIFF_DIR
+testing "diff of dir and fifo" \
+	"diff -ur diff1 diff2/subdir | $TRIM_TAB" \
+"\
+--- diff1/-
++++ diff2/subdir/-
+@@ -1 +1 @@
+-qwe
++asd
+Only in diff2/subdir: test
+" \
+	"" ""
+SKIP=
+
+# using directory structure from prev test...
+rmdir diff1/test
+echo >diff1/test
+optional FEATURE_DIFF_DIR
+testing "diff of file and fifo" \
+	"diff -ur diff1 diff2/subdir | $TRIM_TAB" \
+"\
+--- diff1/-
++++ diff2/subdir/-
+@@ -1 +1 @@
+-qwe
++asd
+File diff2/subdir/test is not a regular file or directory and was skipped
+" \
+	"" ""
+SKIP=
+
+# using directory structure from prev test...
+mkfifo diff1/test2
+optional FEATURE_DIFF_DIR
+testing "diff -rN does not read non-regular files" \
+	"diff -urN diff1 diff2/subdir | $TRIM_TAB" \
+"\
+--- diff1/-
++++ diff2/subdir/-
+@@ -1 +1 @@
+-qwe
++asd
+File diff2/subdir/test is not a regular file or directory and was skipped
+File diff1/test2 is not a regular file or directory and was skipped
+" \
+	"" ""
+SKIP=
+
+# clean up
+rm -rf diff1 diff2
+
+# NOT using directory structure from prev test...
+mkdir diff1 diff2
+echo qwe >diff1/-
+echo rty >diff2/-
+optional FEATURE_DIFF_DIR
+testing "diff diff1 diff2/" \
+	"diff -ur diff1 diff2/ | $TRIM_TAB; diff -ur .///diff1 diff2//// | $TRIM_TAB" \
+"\
+--- diff1/-
++++ diff2/-
+@@ -1 +1 @@
+-qwe
++rty
+--- .///diff1/-
++++ diff2////-
+@@ -1 +1 @@
+-qwe
++rty
+" \
+	"" ""
+SKIP=
+
+# clean up
+rm -rf diff1 diff2
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/dirname/dirname-handles-absolute-path b/busybox-1.19.3/testsuite/dirname/dirname-handles-absolute-path
new file mode 100644
index 0000000..ca1a51b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dirname/dirname-handles-absolute-path
@@ -0,0 +1 @@
+test $(busybox dirname /foo/bar/baz) = /foo/bar
diff --git a/busybox-1.19.3/testsuite/dirname/dirname-handles-empty-path b/busybox-1.19.3/testsuite/dirname/dirname-handles-empty-path
new file mode 100644
index 0000000..04134a5
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dirname/dirname-handles-empty-path
@@ -0,0 +1 @@
+test $(busybox dirname '') = .
diff --git a/busybox-1.19.3/testsuite/dirname/dirname-handles-multiple-slashes b/busybox-1.19.3/testsuite/dirname/dirname-handles-multiple-slashes
new file mode 100644
index 0000000..286f253
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dirname/dirname-handles-multiple-slashes
@@ -0,0 +1 @@
+test $(busybox dirname foo/bar///baz) = foo/bar
diff --git a/busybox-1.19.3/testsuite/dirname/dirname-handles-relative-path b/busybox-1.19.3/testsuite/dirname/dirname-handles-relative-path
new file mode 100644
index 0000000..ffe4ab4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dirname/dirname-handles-relative-path
@@ -0,0 +1 @@
+test $(busybox dirname foo/bar/baz) = foo/bar
diff --git a/busybox-1.19.3/testsuite/dirname/dirname-handles-root b/busybox-1.19.3/testsuite/dirname/dirname-handles-root
new file mode 100644
index 0000000..6bd62b8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dirname/dirname-handles-root
@@ -0,0 +1 @@
+test $(busybox dirname /) = /
diff --git a/busybox-1.19.3/testsuite/dirname/dirname-handles-single-component b/busybox-1.19.3/testsuite/dirname/dirname-handles-single-component
new file mode 100644
index 0000000..24f9ae1
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dirname/dirname-handles-single-component
@@ -0,0 +1 @@
+test $(busybox dirname foo) = .
diff --git a/busybox-1.19.3/testsuite/dirname/dirname-works b/busybox-1.19.3/testsuite/dirname/dirname-works
new file mode 100644
index 0000000..d673dd9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/dirname/dirname-works
@@ -0,0 +1 @@
+test x$(dirname $(pwd)) = x$(busybox dirname $(pwd))
diff --git a/busybox-1.19.3/testsuite/du/du-h-works b/busybox-1.19.3/testsuite/du/du-h-works
new file mode 100644
index 0000000..1c77b65
--- /dev/null
+++ b/busybox-1.19.3/testsuite/du/du-h-works
@@ -0,0 +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 file`" = x"1.0M	file"
diff --git a/busybox-1.19.3/testsuite/du/du-k-works b/busybox-1.19.3/testsuite/du/du-k-works
new file mode 100644
index 0000000..229a948
--- /dev/null
+++ b/busybox-1.19.3/testsuite/du/du-k-works
@@ -0,0 +1,6 @@
+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
+test x"`busybox du -k .`" = x"80	." \
+  -o x"`busybox du -k .`" = x"88	."
diff --git a/busybox-1.19.3/testsuite/du/du-l-works b/busybox-1.19.3/testsuite/du/du-l-works
new file mode 100644
index 0000000..426ee89
--- /dev/null
+++ b/busybox-1.19.3/testsuite/du/du-l-works
@@ -0,0 +1,11 @@
+# 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"148	." \
+  -o x"`busybox du -l .`" = x"152	." \
+  -o x"`busybox du -l .`" = x"156	."
diff --git a/busybox-1.19.3/testsuite/du/du-m-works b/busybox-1.19.3/testsuite/du/du-m-works
new file mode 100644
index 0000000..9fa7437
--- /dev/null
+++ b/busybox-1.19.3/testsuite/du/du-m-works
@@ -0,0 +1,4 @@
+# FEATURE: CONFIG_FEATURE_HUMAN_READABLE
+
+dd if=/dev/zero of=file bs=1M count=1 2>/dev/null
+test x"`busybox du -m .`" = x"1	."
diff --git a/busybox-1.19.3/testsuite/du/du-s-works b/busybox-1.19.3/testsuite/du/du-s-works
new file mode 100644
index 0000000..534432c
--- /dev/null
+++ b/busybox-1.19.3/testsuite/du/du-s-works
@@ -0,0 +1,8 @@
+# FEATURE: CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+
+d=/bin
+du -s "$d" > logfile.gnu
+busybox du -s "$d" > logfile.bb
+cmp logfile.gnu logfile.bb && exit 0
+diff -u logfile.gnu logfile.bb
+exit 1
diff --git a/busybox-1.19.3/testsuite/du/du-works b/busybox-1.19.3/testsuite/du/du-works
new file mode 100644
index 0000000..e320f1d
--- /dev/null
+++ b/busybox-1.19.3/testsuite/du/du-works
@@ -0,0 +1,8 @@
+# FEATURE: CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
+
+d=/bin
+du "$d" > logfile.gnu
+busybox du "$d" > logfile.bb
+cmp logfile.gnu logfile.bb && exit 0
+diff -u logfile.gnu logfile.bb
+exit 1
diff --git a/busybox-1.19.3/testsuite/echo/echo-does-not-print-newline b/busybox-1.19.3/testsuite/echo/echo-does-not-print-newline
new file mode 100644
index 0000000..2857c0d
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-does-not-print-newline
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FANCY_ECHO
+
+test `busybox echo -n word | wc -c` -eq 4
diff --git a/busybox-1.19.3/testsuite/echo/echo-prints-argument b/busybox-1.19.3/testsuite/echo/echo-prints-argument
new file mode 100644
index 0000000..479dac8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-argument
@@ -0,0 +1 @@
+test xfubar = x`busybox echo fubar`
diff --git a/busybox-1.19.3/testsuite/echo/echo-prints-arguments b/busybox-1.19.3/testsuite/echo/echo-prints-arguments
new file mode 100644
index 0000000..4e4e3b4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-arguments
@@ -0,0 +1 @@
+test "`busybox echo foo bar`" = "foo bar"
diff --git a/busybox-1.19.3/testsuite/echo/echo-prints-dash b/busybox-1.19.3/testsuite/echo/echo-prints-dash
new file mode 100644
index 0000000..ddcdbad
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-dash
@@ -0,0 +1 @@
+test "`busybox echo - | od -t x1 | head -n 1`" = "0000000 2d 0a"
diff --git a/busybox-1.19.3/testsuite/echo/echo-prints-newline b/busybox-1.19.3/testsuite/echo/echo-prints-newline
new file mode 100644
index 0000000..838671e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-newline
@@ -0,0 +1 @@
+test `busybox echo word | wc -c` -eq 5
diff --git a/busybox-1.19.3/testsuite/echo/echo-prints-non-opts b/busybox-1.19.3/testsuite/echo/echo-prints-non-opts
new file mode 100644
index 0000000..c7d1e20
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-non-opts
@@ -0,0 +1 @@
+test "`busybox echo -neEZ | od -t x1 | head -n 1`" = "0000000 2d 6e 65 45 5a 0a"
diff --git a/busybox-1.19.3/testsuite/echo/echo-prints-slash-zero b/busybox-1.19.3/testsuite/echo/echo-prints-slash-zero
new file mode 100644
index 0000000..d97ed8e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-slash-zero
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FANCY_ECHO
+
+test "`busybox echo -e -n 'msg\n\0' | od -t x1 | head -n 1`" = "0000000 6d 73 67 0a 00"
diff --git a/busybox-1.19.3/testsuite/echo/echo-prints-slash_00041 b/busybox-1.19.3/testsuite/echo/echo-prints-slash_00041
new file mode 100644
index 0000000..9cffabd
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-slash_00041
@@ -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/busybox-1.19.3/testsuite/echo/echo-prints-slash_0041 b/busybox-1.19.3/testsuite/echo/echo-prints-slash_0041
new file mode 100644
index 0000000..b07429d
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-slash_0041
@@ -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/busybox-1.19.3/testsuite/echo/echo-prints-slash_041 b/busybox-1.19.3/testsuite/echo/echo-prints-slash_041
new file mode 100644
index 0000000..1d70cec
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-slash_041
@@ -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/busybox-1.19.3/testsuite/echo/echo-prints-slash_41 b/busybox-1.19.3/testsuite/echo/echo-prints-slash_41
new file mode 100644
index 0000000..6d8999b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/echo/echo-prints-slash_41
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FANCY_ECHO
+
+test "`busybox echo -ne '\41z' | od -t x1 | head -n 1`" = "0000000 21 7a"
diff --git a/busybox-1.19.3/testsuite/expand.tests b/busybox-1.19.3/testsuite/expand.tests
new file mode 100755
index 0000000..0682c29
--- /dev/null
+++ b/busybox-1.19.3/testsuite/expand.tests
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+test -f "$bindir/.config" && . "$bindir/.config"
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+testing "expand" \
+	"expand" \
+	"        12345678        12345678\n" \
+	"" \
+	"\t12345678\t12345678\n"
+
+test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
+&& test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \
+&& testing "expand with unicode characher 0x394" \
+	"expand" \
+	"Δ       12345ΔΔΔ        12345678\n" \
+	"" \
+	"Δ\t12345ΔΔΔ\t12345678\n"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/expand/expand-works-like-GNU b/busybox-1.19.3/testsuite/expand/expand-works-like-GNU
new file mode 100644
index 0000000..b0278d8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/expand/expand-works-like-GNU
@@ -0,0 +1,20 @@
+# FEATURE: CONFIG_UNEXPAND
+
+rm -f foo bar
+$ECHO -e "\ty" | expand -t 3 ../../busybox > foo
+$ECHO -e "\ty" | busybox unexpand -t 3 ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
+rm -f foo bar
+$ECHO -e "\ty\tx" | expand -it 3 ../../busybox > foo
+$ECHO -e "\ty\tx" | busybox unexpand -it 3 ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
diff --git a/busybox-1.19.3/testsuite/expr/expr-big b/busybox-1.19.3/testsuite/expr/expr-big
new file mode 100644
index 0000000..23dbbb3
--- /dev/null
+++ b/busybox-1.19.3/testsuite/expr/expr-big
@@ -0,0 +1,16 @@
+# busybox expr
+
+# 3*1000*1000*1000 overflows 32-bit signed int
+res=`busybox expr 0 '<' 3000000000`
+[ x"$res" = x1 ] || exit 1
+
+# 9223372036854775807 = 2^31-1
+res=`busybox expr 0 '<' 9223372036854775807`
+[ x"$res" = x1 ] || exit 1
+# coreutils fails this one!
+res=`busybox expr -9223372036854775800 '<' 9223372036854775807`
+[ x"$res" = x1 ] || exit 1
+
+# This one works only by chance
+# res=`busybox expr 0 '<' 9223372036854775808`
+# [ x"$res" = x1 ] || exit 1
diff --git a/busybox-1.19.3/testsuite/expr/expr-works b/busybox-1.19.3/testsuite/expr/expr-works
new file mode 100644
index 0000000..5a0fffb
--- /dev/null
+++ b/busybox-1.19.3/testsuite/expr/expr-works
@@ -0,0 +1,58 @@
+# busybox expr
+busybox expr 1 \| 1
+busybox expr 1 \| 0
+busybox expr 0 \| 1
+busybox expr 1 \& 1
+busybox expr 0 \< 1
+busybox expr 1 \> 0
+busybox expr 0 \<= 1
+busybox expr 1 \<= 1
+busybox expr 1 \>= 0
+busybox expr 1 \>= 1
+busybox expr 1 + 2
+busybox expr 2 - 1
+busybox expr 2 \* 3
+busybox expr 12 / 2
+busybox expr 12 % 5
+
+
+set +e
+busybox expr 0 \| 0
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
+
+busybox expr 1 \& 0
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
+
+busybox expr 0 \& 1
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
+
+busybox expr 0 \& 0
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
+
+busybox expr 1 \< 0
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
+
+busybox expr 0 \> 1
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
+
+busybox expr 1 \<= 0
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
+
+busybox expr 0 \>= 1
+if [ $? != 1 ] ; then
+	exit 1;
+fi;
diff --git a/busybox-1.19.3/testsuite/false/false-is-silent b/busybox-1.19.3/testsuite/false/false-is-silent
new file mode 100644
index 0000000..8a9aa0c
--- /dev/null
+++ b/busybox-1.19.3/testsuite/false/false-is-silent
@@ -0,0 +1 @@
+busybox false 2>&1 | cmp - /dev/null
diff --git a/busybox-1.19.3/testsuite/false/false-returns-failure b/busybox-1.19.3/testsuite/false/false-returns-failure
new file mode 100644
index 0000000..1a061f2
--- /dev/null
+++ b/busybox-1.19.3/testsuite/false/false-returns-failure
@@ -0,0 +1 @@
+! busybox false
diff --git a/busybox-1.19.3/testsuite/find/find-supports-minus-xdev b/busybox-1.19.3/testsuite/find/find-supports-minus-xdev
new file mode 100644
index 0000000..c807fc8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/find/find-supports-minus-xdev
@@ -0,0 +1,3 @@
+# FEATURE: CONFIG_FEATURE_FIND_XDEV
+
+busybox find . -xdev >/dev/null 2>&1
diff --git a/busybox-1.19.3/testsuite/fold.tests b/busybox-1.19.3/testsuite/fold.tests
new file mode 100755
index 0000000..ecf8b9c
--- /dev/null
+++ b/busybox-1.19.3/testsuite/fold.tests
@@ -0,0 +1,62 @@
+#!/bin/sh
+# Copyright 2009 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+test -f "$bindir/.config" && . "$bindir/.config"
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+testing "fold -s" "fold -w 7 -s" \
+        "123456\n\t\nasdf" \
+        "" \
+        "123456\tasdf" \
+
+testing "fold -w1" "fold -w1" \
+	"q\nq\n \nw\n \ne\ne\ne\n \nr\n \nt\nt\nt\nt\n \ny" \
+	"" \
+	"qq w eee r tttt y" \
+
+testing "fold with NULs" "fold -sw22" \
+	"\
+The NUL is here:>\0< \n\
+and another one is \n\
+here:>\0< - they must \n\
+be preserved
+" \
+	"" \
+	"The NUL is here:>\0< and another one \
+is here:>\0< - they must be preserved
+" \
+
+# The text was taken from English and Ukrainian wikipedia pages
+test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
+&& test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \
+&& testing "fold -sw66 with unicode input" "fold -sw66" \
+	"\
+The Andromeda Galaxy (pronounced /ænˈdrɒmədə/, also known as \n\
+Messier 31, M31, or NGC224; often referred to as the Great \n\
+Andromeda Nebula in older texts) is a spiral galaxy approximately \n\
+2,500,000 light-years (1.58×10^11 AU) away in the constellation \n\
+Andromeda. It is the nearest spiral galaxy to our own, the Milky \n\
+Way.\n\
+Галактика або Туманність Андромеди (також відома як M31 за \n\
+каталогом Мессьє та NGC224 за Новим загальним каталогом) — \n\
+спіральна галактика, що знаходиться на відстані приблизно у 2,5 \n\
+мільйони світлових років від нашої планети у сузір'ї Андромеди. \n\
+На початку ХХІ ст. в центрі галактики виявлено чорну дірку." \
+	"" \
+	"\
+The Andromeda Galaxy (pronounced /ænˈdrɒmədə/, also known as \
+Messier 31, M31, or NGC224; often referred to as the Great \
+Andromeda Nebula in older texts) is a spiral galaxy approximately \
+2,500,000 light-years (1.58×10^11 AU) away in the constellation \
+Andromeda. It is the nearest spiral galaxy to our own, the Milky \
+Way.
+Галактика або Туманність Андромеди (також відома як M31 за \
+каталогом Мессьє та NGC224 за Новим загальним каталогом) — \
+спіральна галактика, що знаходиться на відстані приблизно у 2,5 \
+мільйони світлових років від нашої планети у сузір'ї Андромеди. \
+На початку ХХІ ст. в центрі галактики виявлено чорну дірку."
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/grep.tests b/busybox-1.19.3/testsuite/grep.tests
new file mode 100755
index 0000000..ffce033
--- /dev/null
+++ b/busybox-1.19.3/testsuite/grep.tests
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+# AUDIT:
+
+. ./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
+
+# Test exit status
+
+testing "grep (exit with error)" "grep nonexistent 2> /dev/null ; echo \$?" \
+	"1\n" "" ""
+testing "grep (exit success)" "grep grep $0 > /dev/null 2>&1 ; echo \$?" "0\n" \
+	"" ""
+# Test various data sources and destinations
+
+testing "grep (default to stdin)" "grep two" "two\n" "" \
+	"one\ntwo\nthree\nthree\nthree\n"
+testing "grep - (specify stdin)" "grep two -" "two\n" "" \
+	"one\ntwo\nthree\nthree\nthree\n"
+testing "grep input (specify file)" "grep two input" "two\n" \
+	"one\ntwo\nthree\nthree\nthree\n" ""
+
+# GNU grep 2.5.3 outputs a new line character after the located string
+# even if there is no new line character in the input
+testing "grep (no newline at EOL)" "grep bug input" "bug\n" "bug" ""
+
+>empty
+testing "grep two files" "grep two input empty 2>/dev/null" \
+	"input:two\n" "one\ntwo\nthree\nthree\nthree\n" ""
+rm empty
+
+testing "grep - infile (specify stdin and file)" "grep two - input" \
+	"(standard input):two\ninput:two\n" "one\ntwo\nthree\n" \
+	"one\ntwo\ntoo\nthree\nthree\n"
+
+# Check if we see the correct return value if both stdin and non-existing file
+# are given.
+testing "grep - nofile (specify stdin and nonexisting file)" \
+	"grep two - nonexistent 2> /dev/null ; echo \$?" \
+	"(standard input):two\n(standard input):two\n2\n" \
+	"" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "grep -q - nofile (specify stdin and nonexisting file, no match)" \
+	"grep -q nomatch - nonexistent 2> /dev/null ; echo \$?" \
+	"2\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+# SUSv3: If the -q option is specified, the exit status shall be zero
+#        if an input line is selected, even if an error was detected.
+testing "grep -q - nofile (specify stdin and nonexisting file, match)" \
+	"grep -q two - nonexistent ; echo \$?" \
+	"0\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+
+# Test various command line options
+# -s no error messages
+testing "grep -s nofile (nonexisting file, no match)" \
+	"grep -s nomatch nonexistent ; echo \$?" "2\n" "" ""
+testing "grep -s nofile - (stdin and nonexisting file, match)" \
+	"grep -s domatch nonexistent - ; echo \$?" \
+	"(standard input):domatch\n2\n" "" "nomatch\ndomatch\nend\n"
+
+optional EXTRA_COMPAT
+testing "grep handles NUL in files" "grep -a foo input" "\0foo\n" "\0foo\n\n" ""
+testing "grep handles NUL on stdin" "grep -a foo" "\0foo\n" "" "\0foo\n\n"
+
+testing "grep matches NUL" "grep . input > /dev/null 2>&1 ; echo \$?" \
+	"0\n" "\0\n" ""
+SKIP=
+
+# -e regex
+testing "grep handles multiple regexps" "grep -e one -e two input ; echo \$?" \
+	"one\ntwo\n0\n" "one\ntwo\n" ""
+testing "grep -F handles multiple expessions" "grep -F -e one -e two input ; echo \$?" \
+	"one\ntwo\n0\n" "one\ntwo\n" ""
+testing "grep -F handles -i" "grep -F -i foo input ; echo \$?" \
+	"FOO\n0\n" "FOO\n" ""
+
+# -f file/-
+testing "grep can read regexps from stdin" "grep -f - input ; echo \$?" \
+	"two\nthree\n0\n" "tw\ntwo\nthree\n" "tw.\nthr\n"
+
+optional FEATURE_GREP_EGREP_ALIAS
+testing "grep -E supports extended regexps" "grep -E fo+" "foo\n" "" \
+	"b\ar\nfoo\nbaz"
+testing "grep is also egrep" "egrep foo" "foo\n" "" "foo\nbar\n"
+testing "egrep is not case insensitive" \
+	"egrep foo ; [ \$? -ne 0 ] && echo yes" "yes\n" "" "FOO\n"
+testing "grep -E -o prints all matches" \
+	"grep -E -o '([[:xdigit:]]{2}[:-]){5}[[:xdigit:]]{2}'" \
+	"00:19:3E:00:AA:5E\n00:1D:60:3D:3A:FB\n00:22:43:49:FB:AA\n" \
+	"" "00:19:3E:00:AA:5E 00:1D:60:3D:3A:FB 00:22:43:49:FB:AA\n"
+SKIP=
+
+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"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/gunzip.tests b/busybox-1.19.3/testsuite/gunzip.tests
new file mode 100755
index 0000000..68c0755
--- /dev/null
+++ b/busybox-1.19.3/testsuite/gunzip.tests
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./bunzip2.tests
diff --git a/busybox-1.19.3/testsuite/gunzip/gunzip-reads-from-standard-input b/busybox-1.19.3/testsuite/gunzip/gunzip-reads-from-standard-input
new file mode 100644
index 0000000..7c498c0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/gunzip/gunzip-reads-from-standard-input
@@ -0,0 +1,2 @@
+echo foo | gzip | busybox gunzip > output
+echo foo | cmp - output
diff --git a/busybox-1.19.3/testsuite/gzip/gzip-accepts-multiple-files b/busybox-1.19.3/testsuite/gzip/gzip-accepts-multiple-files
new file mode 100644
index 0000000..8f0d9c8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/gzip/gzip-accepts-multiple-files
@@ -0,0 +1,3 @@
+touch foo bar
+busybox gzip foo bar
+test -f foo.gz -a -f bar.gz
diff --git a/busybox-1.19.3/testsuite/gzip/gzip-accepts-single-minus b/busybox-1.19.3/testsuite/gzip/gzip-accepts-single-minus
new file mode 100644
index 0000000..8b51fdf
--- /dev/null
+++ b/busybox-1.19.3/testsuite/gzip/gzip-accepts-single-minus
@@ -0,0 +1 @@
+echo foo | busybox gzip - >/dev/null
diff --git a/busybox-1.19.3/testsuite/gzip/gzip-removes-original-file b/busybox-1.19.3/testsuite/gzip/gzip-removes-original-file
new file mode 100644
index 0000000..b9cb995
--- /dev/null
+++ b/busybox-1.19.3/testsuite/gzip/gzip-removes-original-file
@@ -0,0 +1,3 @@
+touch foo
+busybox gzip foo
+test ! -f foo
diff --git a/busybox-1.19.3/testsuite/head/head-n-works b/busybox-1.19.3/testsuite/head/head-n-works
new file mode 100644
index 0000000..db43255
--- /dev/null
+++ b/busybox-1.19.3/testsuite/head/head-n-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+head -n 2 "$d/README" > logfile.gnu
+busybox head -n 2 "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/head/head-works b/busybox-1.19.3/testsuite/head/head-works
new file mode 100644
index 0000000..56ad3e3
--- /dev/null
+++ b/busybox-1.19.3/testsuite/head/head-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+head "$d/README" > logfile.gnu
+busybox head "$d/README" > logfile.bb
+cmp logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/hostid/hostid-works b/busybox-1.19.3/testsuite/hostid/hostid-works
new file mode 100644
index 0000000..bcfd717
--- /dev/null
+++ b/busybox-1.19.3/testsuite/hostid/hostid-works
@@ -0,0 +1,8 @@
+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"
diff --git a/busybox-1.19.3/testsuite/hostname/hostname-d-works b/busybox-1.19.3/testsuite/hostname/hostname-d-works
new file mode 100644
index 0000000..54c0aac
--- /dev/null
+++ b/busybox-1.19.3/testsuite/hostname/hostname-d-works
@@ -0,0 +1,3 @@
+f=$(busybox hostname -f).
+d=$(busybox hostname -d)
+test x"${f#*.}" = x"$d${d:+.}"
diff --git a/busybox-1.19.3/testsuite/hostname/hostname-i-works b/busybox-1.19.3/testsuite/hostname/hostname-i-works
new file mode 100644
index 0000000..7299bff
--- /dev/null
+++ b/busybox-1.19.3/testsuite/hostname/hostname-i-works
@@ -0,0 +1,9 @@
+test x"$SKIP_KNOWN_BUGS" != x"" && exit
+
+# Observed bug:
+# # ./busybox hostname -i
+# 127.0.0.1
+# # hostname -i
+# 127.0.0.1 10.0.0.2 10.32.10.45
+
+test x$(hostname -i) = x$(busybox hostname -i)
diff --git a/busybox-1.19.3/testsuite/hostname/hostname-s-works b/busybox-1.19.3/testsuite/hostname/hostname-s-works
new file mode 100644
index 0000000..172b944
--- /dev/null
+++ b/busybox-1.19.3/testsuite/hostname/hostname-s-works
@@ -0,0 +1 @@
+test x$(hostname -s) = x$(busybox hostname -s)
diff --git a/busybox-1.19.3/testsuite/hostname/hostname-works b/busybox-1.19.3/testsuite/hostname/hostname-works
new file mode 100644
index 0000000..f51a406
--- /dev/null
+++ b/busybox-1.19.3/testsuite/hostname/hostname-works
@@ -0,0 +1 @@
+test x$(hostname) = x$(busybox hostname)
diff --git a/busybox-1.19.3/testsuite/id/id-g-works b/busybox-1.19.3/testsuite/id/id-g-works
new file mode 100644
index 0000000..671fc53
--- /dev/null
+++ b/busybox-1.19.3/testsuite/id/id-g-works
@@ -0,0 +1 @@
+test x$(id -g) = x$(busybox id -g)
diff --git a/busybox-1.19.3/testsuite/id/id-u-works b/busybox-1.19.3/testsuite/id/id-u-works
new file mode 100644
index 0000000..2358cb0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/id/id-u-works
@@ -0,0 +1 @@
+test x$(id -u) = x$(busybox id -u)
diff --git a/busybox-1.19.3/testsuite/id/id-un-works b/busybox-1.19.3/testsuite/id/id-un-works
new file mode 100644
index 0000000..db390e7
--- /dev/null
+++ b/busybox-1.19.3/testsuite/id/id-un-works
@@ -0,0 +1 @@
+test x$(id -un) = x$(busybox id -un)
diff --git a/busybox-1.19.3/testsuite/id/id-ur-works b/busybox-1.19.3/testsuite/id/id-ur-works
new file mode 100644
index 0000000..6b0fcb0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/id/id-ur-works
@@ -0,0 +1 @@
+test x$(id -ur) = x$(busybox id -ur)
diff --git a/busybox-1.19.3/testsuite/ln/ln-creates-hard-links b/busybox-1.19.3/testsuite/ln/ln-creates-hard-links
new file mode 100644
index 0000000..2f6e23f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ln/ln-creates-hard-links
@@ -0,0 +1,4 @@
+echo file number one > file1
+busybox ln file1 link1
+test -f file1
+test -f link1
diff --git a/busybox-1.19.3/testsuite/ln/ln-creates-soft-links b/busybox-1.19.3/testsuite/ln/ln-creates-soft-links
new file mode 100644
index 0000000..e875e4c
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ln/ln-creates-soft-links
@@ -0,0 +1,4 @@
+echo file number one > file1
+busybox ln -s file1 link1
+test -L link1
+test xfile1 = x`readlink link1`
diff --git a/busybox-1.19.3/testsuite/ln/ln-force-creates-hard-links b/busybox-1.19.3/testsuite/ln/ln-force-creates-hard-links
new file mode 100644
index 0000000..c96b7d6
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ln/ln-force-creates-hard-links
@@ -0,0 +1,5 @@
+echo file number one > file1
+echo file number two > link1
+busybox ln -f file1 link1
+test -f file1
+test -f link1
diff --git a/busybox-1.19.3/testsuite/ln/ln-force-creates-soft-links b/busybox-1.19.3/testsuite/ln/ln-force-creates-soft-links
new file mode 100644
index 0000000..cab8d1d
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ln/ln-force-creates-soft-links
@@ -0,0 +1,5 @@
+echo file number one > file1
+echo file number two > link1
+busybox ln -f -s file1 link1
+test -L link1
+test xfile1 = x`readlink link1`
diff --git a/busybox-1.19.3/testsuite/ln/ln-preserves-hard-links b/busybox-1.19.3/testsuite/ln/ln-preserves-hard-links
new file mode 100644
index 0000000..47fb989
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ln/ln-preserves-hard-links
@@ -0,0 +1,8 @@
+echo file number one > file1
+echo file number two > link1
+set +e
+busybox ln file1 link1
+if [ $? != 0 ] ; then
+	exit 0;
+fi
+exit 1;
diff --git a/busybox-1.19.3/testsuite/ln/ln-preserves-soft-links b/busybox-1.19.3/testsuite/ln/ln-preserves-soft-links
new file mode 100644
index 0000000..3a49bed
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ln/ln-preserves-soft-links
@@ -0,0 +1,8 @@
+echo file number one > file1
+echo file number two > link1
+set +e
+busybox ln -s file1 link1
+if [ $? != 0 ] ; then
+	exit 0;
+fi
+exit 1;
diff --git a/busybox-1.19.3/testsuite/ls.mk_uni_tests b/busybox-1.19.3/testsuite/ls.mk_uni_tests
new file mode 100644
index 0000000..da0c29f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ls.mk_uni_tests
@@ -0,0 +1,111 @@
+# DO NOT EDIT THIS FILE! MOST TEXT EDITORS WILL DAMAGE IT!
+>'0001_1__Some_correct_UTF-8_text___________________________________________|'
+>'0002_2__Boundary_condition_test_cases_____________________________________|'
+>'0003_2.1__First_possible_sequence_of_a_certain_length_____________________|'
+>'0004_2.1.2__2_bytes__U-00000080_:________"€"______________________________|'
+>'0005_2.1.3__3_bytes__U-00000800_:________"ࠀ"______________________________|'
+>'0006_2.1.4__4_bytes__U-00010000_:________"𐀀"______________________________|'
+>'0007_2.1.5__5_bytes__U-00200000_:________"øˆ€€€"______________________________|'
+>'0008_2.1.6__6_bytes__U-04000000_:________"ü„€€€€"______________________________|'
+>'0009_2.2__Last_possible_sequence_of_a_certain_length______________________|'
+>'0010_2.2.1__1_byte___U-0000007F_:________""______________________________|'
+>'0011_2.2.2__2_bytes__U-000007FF_:________"߿"______________________________|'
+>'0012_2.2.3__3_bytes__U-0000FFFF_:________"￿"______________________________|'
+>'0013_2.2.4__4_bytes__U-001FFFFF_:________"÷¿¿¿"______________________________|'
+>'0014_2.2.5__5_bytes__U-03FFFFFF_:________"û¿¿¿¿"______________________________|'
+>'0015_2.2.6__6_bytes__U-7FFFFFFF_:________"ý¿¿¿¿¿"______________________________|'
+>'0016_2.3__Other_boundary_conditions_______________________________________|'
+>'0017_2.3.1__U-0000D7FF_=_ed_9f_bf_=_"퟿"___________________________________|'
+>'0018_2.3.2__U-0000E000_=_ee_80_80_=_""___________________________________|'
+>'0019_2.3.3__U-0000FFFD_=_ef_bf_bd_=_"�"___________________________________|'
+>'0020_2.3.4__U-0010FFFF_=_f4_8f_bf_bf_=_"􏿿"________________________________|'
+>'0021_2.3.5__U-00110000_=_f4_90_80_80_=_"ô€€"________________________________|'
+>'0022_3__Malformed_sequences_______________________________________________|'
+>'0023_3.1__Unexpected_continuation_bytes___________________________________|'
+>'0024_3.1.1__First_continuation_byte_0x80:_"€"_____________________________|'
+>'0025_3.1.2__Last__continuation_byte_0xbf:_"¿"_____________________________|'
+>'0026_3.1.3__2_continuation_bytes:_"€¿"____________________________________|'
+>'0027_3.1.4__3_continuation_bytes:_"€¿€"___________________________________|'
+>'0028_3.1.5__4_continuation_bytes:_"€¿€¿"__________________________________|'
+>'0029_3.1.6__5_continuation_bytes:_"€¿€¿€"_________________________________|'
+>'0030_3.1.7__6_continuation_bytes:_"€¿€¿€¿"________________________________|'
+>'0031_3.1.8__7_continuation_bytes:_"€¿€¿€¿€"_______________________________|'
+>'0032_3.1.9__Sequence_of_all_64_possible_continuation_bytes__0x80-0xbf_:___|'
+>'0033____"€‚ƒ„…†‡ˆ‰Š‹ŒŽ_________________________________________________|'
+>'0034_____‘’“”•–—˜™š›œžŸ_________________________________________________|'
+>'0035_____ ¡¢£¤¥¦§¨©ª«¬­®¯_________________________________________________|'
+>'0036_____°±²³´µ¶·¸¹º»¼½¾¿"________________________________________________|'
+>'0037_3.2__Lonely_start_characters_________________________________________|'
+>'0038_3.2.1__All_32_first_bytes_of_2-byte_sequences__0xc0-0xdf_,___________|'
+>'0039________each_followed_by_a_space_character:___________________________|'
+>'0040____"À_Á_Â_Ã_Ä_Å_Æ_Ç_È_É_Ê_Ë_Ì_Í_Î_Ï__________________________________|'
+>'0041_____Ð_Ñ_Ò_Ó_Ô_Õ_Ö_×_Ø_Ù_Ú_Û_Ü_Ý_Þ_ß_"________________________________|'
+>'0042_3.2.2__All_16_first_bytes_of_3-byte_sequences__0xe0-0xef_,___________|'
+>'0043________each_followed_by_a_space_character:___________________________|'
+>'0044____"à_á_â_ã_ä_å_æ_ç_è_é_ê_ë_ì_í_î_ï_"________________________________|'
+>'0045_3.2.3__All_8_first_bytes_of_4-byte_sequences__0xf0-0xf7_,____________|'
+>'0046________each_followed_by_a_space_character:___________________________|'
+>'0047____"ð_ñ_ò_ó_ô_õ_ö_÷_"________________________________________________|'
+>'0048_3.2.4__All_4_first_bytes_of_5-byte_sequences__0xf8-0xfb_,____________|'
+>'0049________each_followed_by_a_space_character:___________________________|'
+>'0050____"ø_ù_ú_û_"________________________________________________________|'
+>'0051_3.2.5__All_2_first_bytes_of_6-byte_sequences__0xfc-0xfd_,____________|'
+>'0052________each_followed_by_a_space_character:___________________________|'
+>'0053____"ü_ý_"____________________________________________________________|'
+>'0054_3.3__Sequences_with_last_continuation_byte_missing___________________|'
+>'0055_3.3.1__2-byte_sequence_with_last_byte_missing__U+0000_:_____"À"______|'
+>'0056_3.3.2__3-byte_sequence_with_last_byte_missing__U+0000_:_____"à€"______|'
+>'0057_3.3.3__4-byte_sequence_with_last_byte_missing__U+0000_:_____"ð€€"______|'
+>'0058_3.3.4__5-byte_sequence_with_last_byte_missing__U+0000_:_____"ø€€€"______|'
+>'0059_3.3.5__6-byte_sequence_with_last_byte_missing__U+0000_:_____"ü€€€€"______|'
+>'0060_3.3.6__2-byte_sequence_with_last_byte_missing__U-000007FF_:_"ß"______|'
+>'0061_3.3.7__3-byte_sequence_with_last_byte_missing__U-0000FFFF_:_"ï¿"______|'
+>'0062_3.3.8__4-byte_sequence_with_last_byte_missing__U-001FFFFF_:_"÷¿¿"______|'
+>'0063_3.3.9__5-byte_sequence_with_last_byte_missing__U-03FFFFFF_:_"û¿¿¿"______|'
+>'0064_3.3.10_6-byte_sequence_with_last_byte_missing__U-7FFFFFFF_:_"ý¿¿¿¿"______|'
+>'0065_3.4__Concatenation_of_incomplete_sequences___________________________|'
+>'0066____"Àà€ð€€ø€€€ü€€€€ßï¿÷¿¿û¿¿¿ý¿¿¿¿"______________________________________________________|'
+>'0067_3.5__Impossible_bytes________________________________________________|'
+>'0068_3.5.1__fe_=_"þ"______________________________________________________|'
+>'0069_3.5.2__ff_=_"ÿ"______________________________________________________|'
+>'0070_3.5.3__fe_fe_ff_ff_=_"þþÿÿ"__________________________________________|'
+>'0071_4__Overlong_sequences________________________________________________|'
+>'0072_4.1__Examples_of_an_overlong_ASCII_character_________________________|'
+>'0073_4.1.1_U+002F_=_c0_af_____________=_"À¯"_______________________________|'
+>'0074_4.1.2_U+002F_=_e0_80_af__________=_"à€¯"_______________________________|'
+>'0075_4.1.3_U+002F_=_f0_80_80_af_______=_"ð€€¯"_______________________________|'
+>'0076_4.1.4_U+002F_=_f8_80_80_80_af____=_"ø€€€¯"_______________________________|'
+>'0077_4.1.5_U+002F_=_fc_80_80_80_80_af_=_"ü€€€€¯"_______________________________|'
+>'0078_4.2__Maximum_overlong_sequences______________________________________|'
+>'0079_4.2.1__U-0000007F_=_c1_bf_____________=_"Á¿"__________________________|'
+>'0080_4.2.2__U-000007FF_=_e0_9f_bf__________=_"àŸ¿"__________________________|'
+>'0081_4.2.3__U-0000FFFF_=_f0_8f_bf_bf_______=_"ð¿¿"__________________________|'
+>'0082_4.2.4__U-001FFFFF_=_f8_87_bf_bf_bf____=_"ø‡¿¿¿"__________________________|'
+>'0083_4.2.5__U-03FFFFFF_=_fc_83_bf_bf_bf_bf_=_"üƒ¿¿¿¿"__________________________|'
+>'0084_4.3__Overlong_representation_of_the_NUL_character____________________|'
+>'0085_4.3.1__U+0000_=_c0_80_____________=_""______________________________|'
+>'0086_4.3.2__U+0000_=_e0_80_80__________=_"à€€"______________________________|'
+>'0087_4.3.3__U+0000_=_f0_80_80_80_______=_"ð€€€"______________________________|'
+>'0088_4.3.4__U+0000_=_f8_80_80_80_80____=_"ø€€€€"______________________________|'
+>'0089_4.3.5__U+0000_=_fc_80_80_80_80_80_=_"ü€€€€€"______________________________|'
+>'0090_5__Illegal_code_positions____________________________________________|'
+>'0091_5.1_Single_UTF-16_surrogates_________________________________________|'
+>'0092_5.1.1__U+D800_=_ed_a0_80_=_"í €"_______________________________________|'
+>'0093_5.1.2__U+DB7F_=_ed_ad_bf_=_"í­¿"_______________________________________|'
+>'0094_5.1.3__U+DB80_=_ed_ae_80_=_"í®€"_______________________________________|'
+>'0095_5.1.4__U+DBFF_=_ed_af_bf_=_"í¯¿"_______________________________________|'
+>'0096_5.1.5__U+DC00_=_ed_b0_80_=_"í°€"_______________________________________|'
+>'0097_5.1.6__U+DF80_=_ed_be_80_=_"í¾€"_______________________________________|'
+>'0098_5.1.7__U+DFFF_=_ed_bf_bf_=_"í¿¿"_______________________________________|'
+>'0099_5.2_Paired_UTF-16_surrogates_________________________________________|'
+>'0100_5.2.1__U+D800_U+DC00_=_ed_a0_80_ed_b0_80_=_"𐀀"______________________|'
+>'0101_5.2.2__U+D800_U+DFFF_=_ed_a0_80_ed_bf_bf_=_"𐏿"______________________|'
+>'0102_5.2.3__U+DB7F_U+DC00_=_ed_ad_bf_ed_b0_80_=_"󯰀"______________________|'
+>'0103_5.2.4__U+DB7F_U+DFFF_=_ed_ad_bf_ed_bf_bf_=_"í­¿í¿¿"______________________|'
+>'0104_5.2.5__U+DB80_U+DC00_=_ed_ae_80_ed_b0_80_=_"󰀀"______________________|'
+>'0105_5.2.6__U+DB80_U+DFFF_=_ed_ae_80_ed_bf_bf_=_"󰏿"______________________|'
+>'0106_5.2.7__U+DBFF_U+DC00_=_ed_af_bf_ed_b0_80_=_"􏰀"______________________|'
+>'0107_5.2.8__U+DBFF_U+DFFF_=_ed_af_bf_ed_bf_bf_=_"􏿿"______________________|'
+>'0108_5.3_Other_illegal_code_positions_____________________________________|'
+>'0109_5.3.1__U+FFFE_=_ef_bf_be_=_"￾"_______________________________________|'
+>'0110_5.3.2__U+FFFF_=_ef_bf_bf_=_"￿"_______________________________________|'
diff --git a/busybox-1.19.3/testsuite/ls.tests b/busybox-1.19.3/testsuite/ls.tests
new file mode 100755
index 0000000..9309d36
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ls.tests
@@ -0,0 +1,268 @@
+#!/bin/sh
+# Copyright 2010 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+test -f "$bindir/.config" && . "$bindir/.config"
+
+rm -rf ls.testdir 2>/dev/null
+mkdir ls.testdir || exit 1
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+# With Unicode provided by libc locale, I'm not sure this test can pass.
+# I suspect we might fail to skip exactly correct number of bytes
+# over broked unicode sequences.
+test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
+&& test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \
+&& test x"$CONFIG_SUBST_WCHAR" = x"63" \
+&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"767" \
+&& test x"$CONFIG_FEATURE_LS_SORTFILES" = x"y" \
+&& testing "ls unicode test with codepoints limited to 767" \
+"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \
+'0001_1__Some_correct_UTF-8_text___________________________________________|
+0002_2__Boundary_condition_test_cases_____________________________________|
+0003_2.1__First_possible_sequence_of_a_certain_length_____________________|
+0004_2.1.2__2_bytes__U-00000080_:________"?"______________________________|
+0005_2.1.3__3_bytes__U-00000800_:________"?"______________________________|
+0006_2.1.4__4_bytes__U-00010000_:________"?"______________________________|
+0007_2.1.5__5_bytes__U-00200000_:________"?"______________________________|
+0008_2.1.6__6_bytes__U-04000000_:________"?"______________________________|
+0009_2.2__Last_possible_sequence_of_a_certain_length______________________|
+0010_2.2.1__1_byte___U-0000007F_:________"?"______________________________|
+0011_2.2.2__2_bytes__U-000007FF_:________"?"______________________________|
+0012_2.2.3__3_bytes__U-0000FFFF_:________"?"______________________________|
+0013_2.2.4__4_bytes__U-001FFFFF_:________"?"______________________________|
+0014_2.2.5__5_bytes__U-03FFFFFF_:________"?"______________________________|
+0015_2.2.6__6_bytes__U-7FFFFFFF_:________"?"______________________________|
+0016_2.3__Other_boundary_conditions_______________________________________|
+0017_2.3.1__U-0000D7FF_=_ed_9f_bf_=_"?"___________________________________|
+0018_2.3.2__U-0000E000_=_ee_80_80_=_"?"___________________________________|
+0019_2.3.3__U-0000FFFD_=_ef_bf_bd_=_"?"___________________________________|
+0020_2.3.4__U-0010FFFF_=_f4_8f_bf_bf_=_"?"________________________________|
+0021_2.3.5__U-00110000_=_f4_90_80_80_=_"?"________________________________|
+0022_3__Malformed_sequences_______________________________________________|
+0023_3.1__Unexpected_continuation_bytes___________________________________|
+0024_3.1.1__First_continuation_byte_0x80:_"?"_____________________________|
+0025_3.1.2__Last__continuation_byte_0xbf:_"?"_____________________________|
+0026_3.1.3__2_continuation_bytes:_"??"____________________________________|
+0027_3.1.4__3_continuation_bytes:_"???"___________________________________|
+0028_3.1.5__4_continuation_bytes:_"????"__________________________________|
+0029_3.1.6__5_continuation_bytes:_"?????"_________________________________|
+0030_3.1.7__6_continuation_bytes:_"??????"________________________________|
+0031_3.1.8__7_continuation_bytes:_"???????"_______________________________|
+0032_3.1.9__Sequence_of_all_64_possible_continuation_bytes__0x80-0xbf_:___|
+0033____"????????????????_________________________________________________|
+0034_____????????????????_________________________________________________|
+0035_____????????????????_________________________________________________|
+0036_____????????????????"________________________________________________|
+0037_3.2__Lonely_start_characters_________________________________________|
+0038_3.2.1__All_32_first_bytes_of_2-byte_sequences__0xc0-0xdf_,___________|
+0039________each_followed_by_a_space_character:___________________________|
+0040____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?__________________________________|
+0041_____?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________|
+0042_3.2.2__All_16_first_bytes_of_3-byte_sequences__0xe0-0xef_,___________|
+0043________each_followed_by_a_space_character:___________________________|
+0044____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________|
+0045_3.2.3__All_8_first_bytes_of_4-byte_sequences__0xf0-0xf7_,____________|
+0046________each_followed_by_a_space_character:___________________________|
+0047____"?_?_?_?_?_?_?_?_"________________________________________________|
+0048_3.2.4__All_4_first_bytes_of_5-byte_sequences__0xf8-0xfb_,____________|
+0049________each_followed_by_a_space_character:___________________________|
+0050____"?_?_?_?_"________________________________________________________|
+0051_3.2.5__All_2_first_bytes_of_6-byte_sequences__0xfc-0xfd_,____________|
+0052________each_followed_by_a_space_character:___________________________|
+0053____"?_?_"____________________________________________________________|
+0054_3.3__Sequences_with_last_continuation_byte_missing___________________|
+0055_3.3.1__2-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0056_3.3.2__3-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0057_3.3.3__4-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0058_3.3.4__5-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0059_3.3.5__6-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0060_3.3.6__2-byte_sequence_with_last_byte_missing__U-000007FF_:_"?"______|
+0061_3.3.7__3-byte_sequence_with_last_byte_missing__U-0000FFFF_:_"?"______|
+0062_3.3.8__4-byte_sequence_with_last_byte_missing__U-001FFFFF_:_"?"______|
+0063_3.3.9__5-byte_sequence_with_last_byte_missing__U-03FFFFFF_:_"?"______|
+0064_3.3.10_6-byte_sequence_with_last_byte_missing__U-7FFFFFFF_:_"?"______|
+0065_3.4__Concatenation_of_incomplete_sequences___________________________|
+0066____"??????????"______________________________________________________|
+0067_3.5__Impossible_bytes________________________________________________|
+0068_3.5.1__fe_=_"?"______________________________________________________|
+0069_3.5.2__ff_=_"?"______________________________________________________|
+0070_3.5.3__fe_fe_ff_ff_=_"????"__________________________________________|
+0071_4__Overlong_sequences________________________________________________|
+0072_4.1__Examples_of_an_overlong_ASCII_character_________________________|
+0073_4.1.1_U+002F_=_c0_af_____________=_"?"_______________________________|
+0074_4.1.2_U+002F_=_e0_80_af__________=_"?"_______________________________|
+0075_4.1.3_U+002F_=_f0_80_80_af_______=_"?"_______________________________|
+0076_4.1.4_U+002F_=_f8_80_80_80_af____=_"?"_______________________________|
+0077_4.1.5_U+002F_=_fc_80_80_80_80_af_=_"?"_______________________________|
+0078_4.2__Maximum_overlong_sequences______________________________________|
+0079_4.2.1__U-0000007F_=_c1_bf_____________=_"?"__________________________|
+0080_4.2.2__U-000007FF_=_e0_9f_bf__________=_"?"__________________________|
+0081_4.2.3__U-0000FFFF_=_f0_8f_bf_bf_______=_"?"__________________________|
+0082_4.2.4__U-001FFFFF_=_f8_87_bf_bf_bf____=_"?"__________________________|
+0083_4.2.5__U-03FFFFFF_=_fc_83_bf_bf_bf_bf_=_"?"__________________________|
+0084_4.3__Overlong_representation_of_the_NUL_character____________________|
+0085_4.3.1__U+0000_=_c0_80_____________=_"?"______________________________|
+0086_4.3.2__U+0000_=_e0_80_80__________=_"?"______________________________|
+0087_4.3.3__U+0000_=_f0_80_80_80_______=_"?"______________________________|
+0088_4.3.4__U+0000_=_f8_80_80_80_80____=_"?"______________________________|
+0089_4.3.5__U+0000_=_fc_80_80_80_80_80_=_"?"______________________________|
+0090_5__Illegal_code_positions____________________________________________|
+0091_5.1_Single_UTF-16_surrogates_________________________________________|
+0092_5.1.1__U+D800_=_ed_a0_80_=_"?"_______________________________________|
+0093_5.1.2__U+DB7F_=_ed_ad_bf_=_"?"_______________________________________|
+0094_5.1.3__U+DB80_=_ed_ae_80_=_"?"_______________________________________|
+0095_5.1.4__U+DBFF_=_ed_af_bf_=_"?"_______________________________________|
+0096_5.1.5__U+DC00_=_ed_b0_80_=_"?"_______________________________________|
+0097_5.1.6__U+DF80_=_ed_be_80_=_"?"_______________________________________|
+0098_5.1.7__U+DFFF_=_ed_bf_bf_=_"?"_______________________________________|
+0099_5.2_Paired_UTF-16_surrogates_________________________________________|
+0100_5.2.1__U+D800_U+DC00_=_ed_a0_80_ed_b0_80_=_"??"______________________|
+0101_5.2.2__U+D800_U+DFFF_=_ed_a0_80_ed_bf_bf_=_"??"______________________|
+0102_5.2.3__U+DB7F_U+DC00_=_ed_ad_bf_ed_b0_80_=_"??"______________________|
+0103_5.2.4__U+DB7F_U+DFFF_=_ed_ad_bf_ed_bf_bf_=_"??"______________________|
+0104_5.2.5__U+DB80_U+DC00_=_ed_ae_80_ed_b0_80_=_"??"______________________|
+0105_5.2.6__U+DB80_U+DFFF_=_ed_ae_80_ed_bf_bf_=_"??"______________________|
+0106_5.2.7__U+DBFF_U+DC00_=_ed_af_bf_ed_b0_80_=_"??"______________________|
+0107_5.2.8__U+DBFF_U+DFFF_=_ed_af_bf_ed_bf_bf_=_"??"______________________|
+0108_5.3_Other_illegal_code_positions_____________________________________|
+0109_5.3.1__U+FFFE_=_ef_bf_be_=_"?"_______________________________________|
+0110_5.3.2__U+FFFF_=_ef_bf_bf_=_"?"_______________________________________|
+' "" ""
+
+# Currently fails on "0080_4.2.2__U-000007FF_=_e0_9f_bf" line
+test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
+&& test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \
+&& test x"$CONFIG_SUBST_WCHAR" = x"63" \
+&& test x"$CONFIG_LAST_SUPPORTED_WCHAR" = x"0" \
+&& testing "ls unicode test with unlimited codepoints" \
+"(cd ls.testdir && sh ../ls.mk_uni_tests) && ls -1 ls.testdir" \
+'0001_1__Some_correct_UTF-8_text___________________________________________|
+0002_2__Boundary_condition_test_cases_____________________________________|
+0003_2.1__First_possible_sequence_of_a_certain_length_____________________|
+0004_2.1.2__2_bytes__U-00000080_:________"?"______________________________|
+0005_2.1.3__3_bytes__U-00000800_:________"ࠀ"______________________________|
+0006_2.1.4__4_bytes__U-00010000_:________"𐀀"______________________________|
+0007_2.1.5__5_bytes__U-00200000_:________"?"______________________________|
+0008_2.1.6__6_bytes__U-04000000_:________"?"______________________________|
+0009_2.2__Last_possible_sequence_of_a_certain_length______________________|
+0010_2.2.1__1_byte___U-0000007F_:________"?"______________________________|
+0011_2.2.2__2_bytes__U-000007FF_:________"߿"______________________________|
+0012_2.2.3__3_bytes__U-0000FFFF_:________"?"______________________________|
+0013_2.2.4__4_bytes__U-001FFFFF_:________"?"______________________________|
+0014_2.2.5__5_bytes__U-03FFFFFF_:________"?"______________________________|
+0015_2.2.6__6_bytes__U-7FFFFFFF_:________"?"______________________________|
+0016_2.3__Other_boundary_conditions_______________________________________|
+0017_2.3.1__U-0000D7FF_=_ed_9f_bf_=_"퟿"___________________________________|
+0018_2.3.2__U-0000E000_=_ee_80_80_=_"?"___________________________________|
+0019_2.3.3__U-0000FFFD_=_ef_bf_bd_=_"�"___________________________________|
+0020_2.3.4__U-0010FFFF_=_f4_8f_bf_bf_=_"?"________________________________|
+0021_2.3.5__U-00110000_=_f4_90_80_80_=_"?"________________________________|
+0022_3__Malformed_sequences_______________________________________________|
+0023_3.1__Unexpected_continuation_bytes___________________________________|
+0024_3.1.1__First_continuation_byte_0x80:_"?"_____________________________|
+0025_3.1.2__Last__continuation_byte_0xbf:_"?"_____________________________|
+0026_3.1.3__2_continuation_bytes:_"??"____________________________________|
+0027_3.1.4__3_continuation_bytes:_"???"___________________________________|
+0028_3.1.5__4_continuation_bytes:_"????"__________________________________|
+0029_3.1.6__5_continuation_bytes:_"?????"_________________________________|
+0030_3.1.7__6_continuation_bytes:_"??????"________________________________|
+0031_3.1.8__7_continuation_bytes:_"???????"_______________________________|
+0032_3.1.9__Sequence_of_all_64_possible_continuation_bytes__0x80-0xbf_:___|
+0033____"????????????????_________________________________________________|
+0034_____????????????????_________________________________________________|
+0035_____????????????????_________________________________________________|
+0036_____????????????????"________________________________________________|
+0037_3.2__Lonely_start_characters_________________________________________|
+0038_3.2.1__All_32_first_bytes_of_2-byte_sequences__0xc0-0xdf_,___________|
+0039________each_followed_by_a_space_character:___________________________|
+0040____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?__________________________________|
+0041_____?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________|
+0042_3.2.2__All_16_first_bytes_of_3-byte_sequences__0xe0-0xef_,___________|
+0043________each_followed_by_a_space_character:___________________________|
+0044____"?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_?_"________________________________|
+0045_3.2.3__All_8_first_bytes_of_4-byte_sequences__0xf0-0xf7_,____________|
+0046________each_followed_by_a_space_character:___________________________|
+0047____"?_?_?_?_?_?_?_?_"________________________________________________|
+0048_3.2.4__All_4_first_bytes_of_5-byte_sequences__0xf8-0xfb_,____________|
+0049________each_followed_by_a_space_character:___________________________|
+0050____"?_?_?_?_"________________________________________________________|
+0051_3.2.5__All_2_first_bytes_of_6-byte_sequences__0xfc-0xfd_,____________|
+0052________each_followed_by_a_space_character:___________________________|
+0053____"?_?_"____________________________________________________________|
+0054_3.3__Sequences_with_last_continuation_byte_missing___________________|
+0055_3.3.1__2-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0056_3.3.2__3-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0057_3.3.3__4-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0058_3.3.4__5-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0059_3.3.5__6-byte_sequence_with_last_byte_missing__U+0000_:_____"?"______|
+0060_3.3.6__2-byte_sequence_with_last_byte_missing__U-000007FF_:_"?"______|
+0061_3.3.7__3-byte_sequence_with_last_byte_missing__U-0000FFFF_:_"?"______|
+0062_3.3.8__4-byte_sequence_with_last_byte_missing__U-001FFFFF_:_"?"______|
+0063_3.3.9__5-byte_sequence_with_last_byte_missing__U-03FFFFFF_:_"?"______|
+0064_3.3.10_6-byte_sequence_with_last_byte_missing__U-7FFFFFFF_:_"?"______|
+0065_3.4__Concatenation_of_incomplete_sequences___________________________|
+0066____"??????????"______________________________________________________|
+0067_3.5__Impossible_bytes________________________________________________|
+0068_3.5.1__fe_=_"?"______________________________________________________|
+0069_3.5.2__ff_=_"?"______________________________________________________|
+0070_3.5.3__fe_fe_ff_ff_=_"????"__________________________________________|
+0071_4__Overlong_sequences________________________________________________|
+0072_4.1__Examples_of_an_overlong_ASCII_character_________________________|
+0073_4.1.1_U+002F_=_c0_af_____________=_"?"_______________________________|
+0074_4.1.2_U+002F_=_e0_80_af__________=_"?"_______________________________|
+0075_4.1.3_U+002F_=_f0_80_80_af_______=_"?"_______________________________|
+0076_4.1.4_U+002F_=_f8_80_80_80_af____=_"?"_______________________________|
+0077_4.1.5_U+002F_=_fc_80_80_80_80_af_=_"?"_______________________________|
+0078_4.2__Maximum_overlong_sequences______________________________________|
+0079_4.2.1__U-0000007F_=_c1_bf_____________=_"?"__________________________|
+0080_4.2.2__U-000007FF_=_e0_9f_bf__________=_"?"__________________________|
+0081_4.2.3__U-0000FFFF_=_f0_8f_bf_bf_______=_"?"__________________________|
+0082_4.2.4__U-001FFFFF_=_f8_87_bf_bf_bf____=_"?"__________________________|
+0083_4.2.5__U-03FFFFFF_=_fc_83_bf_bf_bf_bf_=_"?"__________________________|
+0084_4.3__Overlong_representation_of_the_NUL_character____________________|
+0085_4.3.1__U+0000_=_c0_80_____________=_"?"______________________________|
+0086_4.3.2__U+0000_=_e0_80_80__________=_"?"______________________________|
+0087_4.3.3__U+0000_=_f0_80_80_80_______=_"?"______________________________|
+0088_4.3.4__U+0000_=_f8_80_80_80_80____=_"?"______________________________|
+0089_4.3.5__U+0000_=_fc_80_80_80_80_80_=_"?"______________________________|
+0090_5__Illegal_code_positions____________________________________________|
+0091_5.1_Single_UTF-16_surrogates_________________________________________|
+0092_5.1.1__U+D800_=_ed_a0_80_=_"?"_______________________________________|
+0093_5.1.2__U+DB7F_=_ed_ad_bf_=_"?"_______________________________________|
+0094_5.1.3__U+DB80_=_ed_ae_80_=_"?"_______________________________________|
+0095_5.1.4__U+DBFF_=_ed_af_bf_=_"?"_______________________________________|
+0096_5.1.5__U+DC00_=_ed_b0_80_=_"?"_______________________________________|
+0097_5.1.6__U+DF80_=_ed_be_80_=_"?"_______________________________________|
+0098_5.1.7__U+DFFF_=_ed_bf_bf_=_"?"_______________________________________|
+0099_5.2_Paired_UTF-16_surrogates_________________________________________|
+0100_5.2.1__U+D800_U+DC00_=_ed_a0_80_ed_b0_80_=_"??"______________________|
+0101_5.2.2__U+D800_U+DFFF_=_ed_a0_80_ed_bf_bf_=_"??"______________________|
+0102_5.2.3__U+DB7F_U+DC00_=_ed_ad_bf_ed_b0_80_=_"??"______________________|
+0103_5.2.4__U+DB7F_U+DFFF_=_ed_ad_bf_ed_bf_bf_=_"??"______________________|
+0104_5.2.5__U+DB80_U+DC00_=_ed_ae_80_ed_b0_80_=_"??"______________________|
+0105_5.2.6__U+DB80_U+DFFF_=_ed_ae_80_ed_bf_bf_=_"??"______________________|
+0106_5.2.7__U+DBFF_U+DC00_=_ed_af_bf_ed_b0_80_=_"??"______________________|
+0107_5.2.8__U+DBFF_U+DFFF_=_ed_af_bf_ed_bf_bf_=_"??"______________________|
+0108_5.3_Other_illegal_code_positions_____________________________________|
+0109_5.3.1__U+FFFE_=_ef_bf_be_=_"?"_______________________________________|
+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
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/ls/ls-1-works b/busybox-1.19.3/testsuite/ls/ls-1-works
new file mode 100644
index 0000000..71ab9a6
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ls/ls-1-works
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_LS_SORTFILES
+
+[ -n "$d" ] || d=..
+LC_ALL=C ls -1 "$d" > logfile.gnu
+LC_ALL=C busybox ls -1 "$d" > logfile.bb
+diff -ubw logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/ls/ls-h-works b/busybox-1.19.3/testsuite/ls/ls-h-works
new file mode 100644
index 0000000..60016ba
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ls/ls-h-works
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_LS_SORTFILES CONFIG_FEATURE_HUMAN_READABLE
+
+[ -n "$d" ] || d=..
+LC_ALL=C ls -h "$d" > logfile.gnu
+LC_ALL=C busybox ls -h "$d" > logfile.bb
+diff -ubw logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/ls/ls-l-works b/busybox-1.19.3/testsuite/ls/ls-l-works
new file mode 100644
index 0000000..ce08810
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ls/ls-l-works
@@ -0,0 +1,8 @@
+test x"$SKIP_KNOWN_BUGS" != x"" && exit
+
+# busybox does not emit "total NNN" line
+
+[ -n "$d" ] || d=..
+LC_ALL=C ls -l "$d" > logfile.gnu
+LC_ALL=C busybox ls -l "$d" > logfile.bb
+diff -ubw logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/ls/ls-s-works b/busybox-1.19.3/testsuite/ls/ls-s-works
new file mode 100644
index 0000000..8bf5c64
--- /dev/null
+++ b/busybox-1.19.3/testsuite/ls/ls-s-works
@@ -0,0 +1,8 @@
+test x"$SKIP_KNOWN_BUGS" != x"" && exit
+
+# busybox does not emit "total NNN" line
+
+[ -n "$d" ] || d=..
+LC_ALL=C ls -1s "$d" > logfile.gnu
+LC_ALL=C busybox ls -1s "$d" > logfile.bb
+diff -ubw logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/makedevs.device_table.txt b/busybox-1.19.3/testsuite/makedevs.device_table.txt
new file mode 100644
index 0000000..88ac209
--- /dev/null
+++ b/busybox-1.19.3/testsuite/makedevs.device_table.txt
@@ -0,0 +1,172 @@
+# When building a target filesystem, it is desirable to not have to
+# become root and then run 'mknod' a thousand times.  Using a device
+# table you can create device nodes and directories "on the fly".
+#
+# This is a sample device table file for use with genext2fs.  You can
+# do all sorts of interesting things with a device table file.  For
+# example, if you want to adjust the permissions on a particular file
+# you can just add an entry like:
+#   /sbin/foobar        f       2755    0       0       -       -       -       -       -
+# and (assuming the file /sbin/foobar exists) it will be made setuid
+# root (regardless of what its permissions are on the host filesystem).
+# Furthermore, you can use a single table entry to create a many device
+# minors.  For example, if I wanted to create /dev/hda and /dev/hda[0-15]
+# I could just use the following two table entries:
+#   /dev/hda    b       640     0       0       3       0       0       0       -
+#   /dev/hda    b       640     0       0       3       1       1       1       15
+#
+# Device table entries take the form of:
+# <name>    <type>      <mode>  <uid>   <gid>   <major> <minor> <start> <inc>   <count>
+# where name is the file name,  type can be one of:
+#       f       A regular file
+#       d       Directory
+#       c       Character special device file
+#       b       Block special device file
+#       p       Fifo (named pipe)
+# uid is the user id for the target file, gid is the group id for the
+# target file.  The rest of the entries (major, minor, etc) apply only
+# to device special files.
+
+# Have fun
+# -Erik Andersen <andersen@codepoet.org>
+#
+
+#<name>		<type>	<mode>	<uid>	<gid>	<major>	<minor>	<start>	<inc>	<count>
+/dev		d	755	0	0	-	-	-	-	-
+/dev/pts	d	755	0	0	-	-	-	-	-
+/dev/shm	d	755	0	0	-	-	-	-	-
+/tmp		d	1777	0	0	-	-	-	-	-
+/etc		d	755	0	0	-	-	-	-	-
+/home/default	d	2755	1000	1000	-	-	-	-	-
+#<name>					<type>	<mode>	<uid>	<gid>	<major>	<minor>	<start>	<inc>	<count>
+###/bin/busybox				f	4755	0	0	-	-	-	-	-
+###/etc/shadow				f	600	0	0	-	-	-	-	-
+###/etc/passwd				f	644	0	0	-	-	-	-	-
+/etc/network/if-up.d			d	755	0	0	-	-	-	-	-
+/etc/network/if-pre-up.d		d	755	0	0	-	-	-	-	-
+/etc/network/if-down.d			d	755	0	0	-	-	-	-	-
+/etc/network/if-post-down.d		d	755	0	0	-	-	-	-	-
+###/usr/share/udhcpc/default.script	f	755	0	0	-	-	-	-	-
+# uncomment this to allow starting x as non-root
+#/usr/X11R6/bin/Xfbdev		f	4755	0	0	-	-	-	-	-
+
+# Normal system devices
+# <name>    <type>      <mode>  <uid>   <gid>   <major> <minor> <start> <inc>   <count>
+/dev/mem	c	640	0	0	1	1	0	0	-
+/dev/kmem	c	640	0	0	1	2	0	0	-
+/dev/null	c	666	0	0	1	3	0	0	-
+/dev/zero	c	666	0	0	1	5	0	0	-
+/dev/random	c	666	0	0	1	8	0	0	-
+/dev/urandom	c	666	0	0	1	9	0	0	-
+/dev/ram	b	640	0	0	1	1	0	0	-
+/dev/ram	b	640	0	0	1	0	0	1	4
+/dev/loop	b	640	0	0	7	0	0	1	2
+/dev/rtc	c	640	0	0	10	135	-	-	-
+/dev/console	c	666	0	0	5	1	-	-	-
+/dev/tty	c	666	0	0	5	0	-	-	-
+/dev/tty	c	666	0	0	4	0	0	1	8
+/dev/ttyp	c	666	0	0	3	0	0	1	10
+/dev/ptyp	c       666     0       0       2       0       0       1       10
+/dev/ptmx	c	666	0	0	5	2	-	-	-
+/dev/ttyP	c	666	0	0	57	0	0	1	4
+/dev/ttyS	c	666	0	0	4	64	0	1	4
+/dev/fb		c	640	0	5	29	0	0	32	4
+#/dev/ttySA	c	666	0	0	204	5	0	1	3
+/dev/psaux	c	666	0	0	10	1	0	0	-
+#/dev/ppp	c	666	0	0	108	0	-	-	-
+
+# Input stuff
+/dev/input	d	755	0	0	-	-	-	-	-
+/dev/input/mice	c	640	0	0	13	63	0	0	-
+/dev/input/mouse c	660	0	0	13	32	0	1	4
+/dev/input/event c	660	0	0	13	64	0	1	4
+#/dev/input/js	c	660	0	0	13	0	0	1	4
+
+
+# MTD stuff
+/dev/mtd	c	640	0	0	90	0	0	2	4
+/dev/mtdblock	b	640	0	0	31	0	0	1	4
+
+#Tun/tap driver
+/dev/net	d	755	0	0	-	-	-	-	-
+/dev/net/tun	c	660	0	0	10	200	-	-	-
+
+# Audio stuff
+#/dev/audio	c	666	0	29	14	4	-	-	-
+#/dev/audio1	c	666	0	29	14	20	-	-	-
+#/dev/dsp	c	666	0	29	14	3	-	-	-
+#/dev/dsp1	c	666	0	29	14	19	-	-	-
+#/dev/sndstat	c	666	0	29	14	6	-	-	-
+
+# User-mode Linux stuff
+#/dev/ubda	b	640	0	0	98	0	0	0	-
+#/dev/ubda	b	640	0	0	98	1	1	1	15
+
+# IDE Devices
+/dev/hda	b	640	0	0	3	0	0	0	-
+/dev/hda	b	640	0	0	3	1	1	1	15
+/dev/hdb	b	640	0	0	3	64	0	0	-
+/dev/hdb	b	640	0	0	3	65	1	1	15
+#/dev/hdc	b	640	0	0	22	0	0	0	-
+#/dev/hdc	b	640	0	0	22	1	1	1	15
+#/dev/hdd	b	640	0	0	22	64	0	0	-
+#/dev/hdd	b	640	0	0	22	65	1	1	15
+#/dev/hde	b	640	0	0	33	0	0	0	-
+#/dev/hde	b	640	0	0	33	1	1	1	15
+#/dev/hdf	b	640	0	0	33	64	0	0	-
+#/dev/hdf	b	640	0	0	33	65	1	1	15
+#/dev/hdg	b	640	0	0	34	0	0	0	-
+#/dev/hdg	b	640	0	0	34	1	1	1	15
+#/dev/hdh	b	640	0	0	34	64	0	0	-
+#/dev/hdh	b	640	0	0	34	65	1	1	15
+
+# SCSI Devices
+#/dev/sda	b	640	0	0	8	0	0	0	-
+#/dev/sda	b	640	0	0	8	1	1	1	15
+#/dev/sdb	b	640	0	0	8	16	0	0	-
+#/dev/sdb	b	640	0	0	8	17	1	1	15
+#/dev/sdc	b	640	0	0	8	32	0	0	-
+#/dev/sdc	b	640	0	0	8	33	1	1	15
+#/dev/sdd	b	640	0	0	8	48	0	0	-
+#/dev/sdd	b	640	0	0	8	49	1	1	15
+#/dev/sde	b	640	0	0	8	64	0	0	-
+#/dev/sde	b	640	0	0	8	65	1	1	15
+#/dev/sdf	b	640	0	0	8	80	0	0	-
+#/dev/sdf	b	640	0	0	8	81	1	1	15
+#/dev/sdg	b	640	0	0	8	96	0	0	-
+#/dev/sdg	b	640	0	0	8	97	1	1	15
+#/dev/sdh	b	640	0	0	8	112	0	0	-
+#/dev/sdh	b	640	0	0	8	113	1	1	15
+#/dev/sg	c	640	0	0	21	0	0	1	15
+#/dev/scd	b	640	0	0	11	0	0	1	15
+#/dev/st	c	640	0	0	9	0	0	1	8
+#/dev/nst	c	640	0	0	9	128	0	1	8
+#/dev/st	c	640	0	0	9	32	1	1	4
+#/dev/st	c	640	0	0	9	64	1	1	4
+#/dev/st	c	640	0	0	9	96	1	1	4
+
+# Floppy disk devices
+#/dev/fd	b	640	0	0	2	0	0	1	2
+#/dev/fd0d360	b	640	0	0	2	4	0	0	-
+#/dev/fd1d360	b	640	0	0	2	5	0	0	-
+#/dev/fd0h1200	b	640	0	0	2	8	0	0	-
+#/dev/fd1h1200	b	640	0	0	2	9	0	0	-
+#/dev/fd0u1440	b	640	0	0	2	28	0	0	-
+#/dev/fd1u1440	b	640	0	0	2	29	0	0	-
+#/dev/fd0u2880	b	640	0	0	2	32	0	0	-
+#/dev/fd1u2880	b	640	0	0	2	33	0	0	-
+
+# All the proprietary cdrom devices in the world
+#/dev/aztcd	b	640	0	0	29	0	0	0	-
+#/dev/bpcd	b	640	0	0	41	0	0	0	-
+#/dev/capi20	c	640	0	0	68	0	0	1	2
+#/dev/cdu31a	b	640	0	0	15	0	0	0	-
+#/dev/cdu535	b	640	0	0	24	0	0	0	-
+#/dev/cm206cd	b	640	0	0	32	0	0	0	-
+#/dev/sjcd	b	640	0	0	18	0	0	0	-
+#/dev/sonycd	b	640	0	0	15	0	0	0	-
+#/dev/gscd	b	640	0	0	16	0	0	0	-
+#/dev/sbpcd	b	640	0	0	25	0	0	0	-
+#/dev/sbpcd	b	640	0	0	25	0	0	1	4
+#/dev/mcd	b	640	0	0	23	0	0	0	-
+#/dev/optcd	b	640	0	0	17	0	0	0	-
diff --git a/busybox-1.19.3/testsuite/makedevs.tests b/busybox-1.19.3/testsuite/makedevs.tests
new file mode 100755
index 0000000..fd12460
--- /dev/null
+++ b/busybox-1.19.3/testsuite/makedevs.tests
@@ -0,0 +1,150 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# 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
+FILTER_LS="sed -e 's/,  */,/g' -e 's/  */ /g' | cut -d' ' -f 1-5,9-"
+# cut: remove size+date
+FILTER_LS2="sed -e 's/,  */,/g' -e 's/  */ /g' | cut -d' ' -f 1-4,9-"
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+rm -rf makedevs.testdir
+mkdir makedevs.testdir
+
+optional FEATURE_MAKEDEVS_TABLE FEATURE_FIND_NOT FEATURE_FIND_TYPE FEATURE_LS_RECURSIVE FEATURE_LS_SORTFILES
+testing "makedevs -d ../makedevs.device_table.txt ." \
+	"(cd makedevs.testdir && makedevs -d ../makedevs.device_table.txt . 2>&1);
+	find makedevs.testdir ! -type d | sort | xargs ls -lnR | $FILTER_LS" \
+"\
+rootdir=.
+table='../makedevs.device_table.txt'
+crw-rw-rw- 1 0 0 5,1 makedevs.testdir/dev/console
+crw-r----- 1 0 5 29,0 makedevs.testdir/dev/fb0
+crw-r----- 1 0 5 29,32 makedevs.testdir/dev/fb1
+crw-r----- 1 0 5 29,64 makedevs.testdir/dev/fb2
+crw-r----- 1 0 5 29,96 makedevs.testdir/dev/fb3
+brw-r----- 1 0 0 3,0 makedevs.testdir/dev/hda
+brw-r----- 1 0 0 3,1 makedevs.testdir/dev/hda1
+brw-r----- 1 0 0 3,10 makedevs.testdir/dev/hda10
+brw-r----- 1 0 0 3,11 makedevs.testdir/dev/hda11
+brw-r----- 1 0 0 3,12 makedevs.testdir/dev/hda12
+brw-r----- 1 0 0 3,13 makedevs.testdir/dev/hda13
+brw-r----- 1 0 0 3,14 makedevs.testdir/dev/hda14
+brw-r----- 1 0 0 3,15 makedevs.testdir/dev/hda15
+brw-r----- 1 0 0 3,2 makedevs.testdir/dev/hda2
+brw-r----- 1 0 0 3,3 makedevs.testdir/dev/hda3
+brw-r----- 1 0 0 3,4 makedevs.testdir/dev/hda4
+brw-r----- 1 0 0 3,5 makedevs.testdir/dev/hda5
+brw-r----- 1 0 0 3,6 makedevs.testdir/dev/hda6
+brw-r----- 1 0 0 3,7 makedevs.testdir/dev/hda7
+brw-r----- 1 0 0 3,8 makedevs.testdir/dev/hda8
+brw-r----- 1 0 0 3,9 makedevs.testdir/dev/hda9
+brw-r----- 1 0 0 3,64 makedevs.testdir/dev/hdb
+brw-r----- 1 0 0 3,65 makedevs.testdir/dev/hdb1
+brw-r----- 1 0 0 3,74 makedevs.testdir/dev/hdb10
+brw-r----- 1 0 0 3,75 makedevs.testdir/dev/hdb11
+brw-r----- 1 0 0 3,76 makedevs.testdir/dev/hdb12
+brw-r----- 1 0 0 3,77 makedevs.testdir/dev/hdb13
+brw-r----- 1 0 0 3,78 makedevs.testdir/dev/hdb14
+brw-r----- 1 0 0 3,79 makedevs.testdir/dev/hdb15
+brw-r----- 1 0 0 3,66 makedevs.testdir/dev/hdb2
+brw-r----- 1 0 0 3,67 makedevs.testdir/dev/hdb3
+brw-r----- 1 0 0 3,68 makedevs.testdir/dev/hdb4
+brw-r----- 1 0 0 3,69 makedevs.testdir/dev/hdb5
+brw-r----- 1 0 0 3,70 makedevs.testdir/dev/hdb6
+brw-r----- 1 0 0 3,71 makedevs.testdir/dev/hdb7
+brw-r----- 1 0 0 3,72 makedevs.testdir/dev/hdb8
+brw-r----- 1 0 0 3,73 makedevs.testdir/dev/hdb9
+crw-rw---- 1 0 0 13,64 makedevs.testdir/dev/input/event0
+crw-rw---- 1 0 0 13,65 makedevs.testdir/dev/input/event1
+crw-rw---- 1 0 0 13,66 makedevs.testdir/dev/input/event2
+crw-rw---- 1 0 0 13,67 makedevs.testdir/dev/input/event3
+crw-r----- 1 0 0 13,63 makedevs.testdir/dev/input/mice
+crw-rw---- 1 0 0 13,32 makedevs.testdir/dev/input/mouse0
+crw-rw---- 1 0 0 13,33 makedevs.testdir/dev/input/mouse1
+crw-rw---- 1 0 0 13,34 makedevs.testdir/dev/input/mouse2
+crw-rw---- 1 0 0 13,35 makedevs.testdir/dev/input/mouse3
+crw-r----- 1 0 0 1,2 makedevs.testdir/dev/kmem
+brw-r----- 1 0 0 7,0 makedevs.testdir/dev/loop0
+brw-r----- 1 0 0 7,1 makedevs.testdir/dev/loop1
+crw-r----- 1 0 0 1,1 makedevs.testdir/dev/mem
+crw-r----- 1 0 0 90,0 makedevs.testdir/dev/mtd0
+crw-r----- 1 0 0 90,2 makedevs.testdir/dev/mtd1
+crw-r----- 1 0 0 90,4 makedevs.testdir/dev/mtd2
+crw-r----- 1 0 0 90,6 makedevs.testdir/dev/mtd3
+brw-r----- 1 0 0 31,0 makedevs.testdir/dev/mtdblock0
+brw-r----- 1 0 0 31,1 makedevs.testdir/dev/mtdblock1
+brw-r----- 1 0 0 31,2 makedevs.testdir/dev/mtdblock2
+brw-r----- 1 0 0 31,3 makedevs.testdir/dev/mtdblock3
+crw-rw---- 1 0 0 10,200 makedevs.testdir/dev/net/tun
+crw-rw-rw- 1 0 0 1,3 makedevs.testdir/dev/null
+crw-rw-rw- 1 0 0 10,1 makedevs.testdir/dev/psaux
+crw-rw-rw- 1 0 0 5,2 makedevs.testdir/dev/ptmx
+crw-rw-rw- 1 0 0 2,0 makedevs.testdir/dev/ptyp0
+crw-rw-rw- 1 0 0 2,1 makedevs.testdir/dev/ptyp1
+crw-rw-rw- 1 0 0 2,2 makedevs.testdir/dev/ptyp2
+crw-rw-rw- 1 0 0 2,3 makedevs.testdir/dev/ptyp3
+crw-rw-rw- 1 0 0 2,4 makedevs.testdir/dev/ptyp4
+crw-rw-rw- 1 0 0 2,5 makedevs.testdir/dev/ptyp5
+crw-rw-rw- 1 0 0 2,6 makedevs.testdir/dev/ptyp6
+crw-rw-rw- 1 0 0 2,7 makedevs.testdir/dev/ptyp7
+crw-rw-rw- 1 0 0 2,8 makedevs.testdir/dev/ptyp8
+crw-rw-rw- 1 0 0 2,9 makedevs.testdir/dev/ptyp9
+brw-r----- 1 0 0 1,1 makedevs.testdir/dev/ram
+brw-r----- 1 0 0 1,0 makedevs.testdir/dev/ram0
+brw-r----- 1 0 0 1,1 makedevs.testdir/dev/ram1
+brw-r----- 1 0 0 1,2 makedevs.testdir/dev/ram2
+brw-r----- 1 0 0 1,3 makedevs.testdir/dev/ram3
+crw-rw-rw- 1 0 0 1,8 makedevs.testdir/dev/random
+crw-r----- 1 0 0 10,135 makedevs.testdir/dev/rtc
+crw-rw-rw- 1 0 0 5,0 makedevs.testdir/dev/tty
+crw-rw-rw- 1 0 0 4,0 makedevs.testdir/dev/tty0
+crw-rw-rw- 1 0 0 4,1 makedevs.testdir/dev/tty1
+crw-rw-rw- 1 0 0 4,2 makedevs.testdir/dev/tty2
+crw-rw-rw- 1 0 0 4,3 makedevs.testdir/dev/tty3
+crw-rw-rw- 1 0 0 4,4 makedevs.testdir/dev/tty4
+crw-rw-rw- 1 0 0 4,5 makedevs.testdir/dev/tty5
+crw-rw-rw- 1 0 0 4,6 makedevs.testdir/dev/tty6
+crw-rw-rw- 1 0 0 4,7 makedevs.testdir/dev/tty7
+crw-rw-rw- 1 0 0 57,0 makedevs.testdir/dev/ttyP0
+crw-rw-rw- 1 0 0 57,1 makedevs.testdir/dev/ttyP1
+crw-rw-rw- 1 0 0 57,2 makedevs.testdir/dev/ttyP2
+crw-rw-rw- 1 0 0 57,3 makedevs.testdir/dev/ttyP3
+crw-rw-rw- 1 0 0 4,64 makedevs.testdir/dev/ttyS0
+crw-rw-rw- 1 0 0 4,65 makedevs.testdir/dev/ttyS1
+crw-rw-rw- 1 0 0 4,66 makedevs.testdir/dev/ttyS2
+crw-rw-rw- 1 0 0 4,67 makedevs.testdir/dev/ttyS3
+crw-rw-rw- 1 0 0 3,0 makedevs.testdir/dev/ttyp0
+crw-rw-rw- 1 0 0 3,1 makedevs.testdir/dev/ttyp1
+crw-rw-rw- 1 0 0 3,2 makedevs.testdir/dev/ttyp2
+crw-rw-rw- 1 0 0 3,3 makedevs.testdir/dev/ttyp3
+crw-rw-rw- 1 0 0 3,4 makedevs.testdir/dev/ttyp4
+crw-rw-rw- 1 0 0 3,5 makedevs.testdir/dev/ttyp5
+crw-rw-rw- 1 0 0 3,6 makedevs.testdir/dev/ttyp6
+crw-rw-rw- 1 0 0 3,7 makedevs.testdir/dev/ttyp7
+crw-rw-rw- 1 0 0 3,8 makedevs.testdir/dev/ttyp8
+crw-rw-rw- 1 0 0 3,9 makedevs.testdir/dev/ttyp9
+crw-rw-rw- 1 0 0 1,9 makedevs.testdir/dev/urandom
+crw-rw-rw- 1 0 0 1,5 makedevs.testdir/dev/zero
+" \
+	"" ""
+SKIP=
+
+# clean up
+rm -rf makedevs.testdir
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/md5sum.tests b/busybox-1.19.3/testsuite/md5sum.tests
new file mode 100755
index 0000000..1068b08
--- /dev/null
+++ b/busybox-1.19.3/testsuite/md5sum.tests
@@ -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++))
+done | "$sum"
+)`
+
+if test x"$result" = x"$expected  -"; then
+    echo "PASS: $sum"
+    exit 0
+fi
+
+echo "FAIL: $sum (r:$result exp:$expected)"
+exit 1
diff --git a/busybox-1.19.3/testsuite/md5sum/md5sum-verifies-non-binary-file b/busybox-1.19.3/testsuite/md5sum/md5sum-verifies-non-binary-file
new file mode 100644
index 0000000..1df818e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/md5sum/md5sum-verifies-non-binary-file
@@ -0,0 +1,5 @@
+# FEATURE: CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
+
+touch foo
+md5sum foo > bar
+busybox md5sum -c bar
diff --git a/busybox-1.19.3/testsuite/mdev.tests b/busybox-1.19.3/testsuite/mdev.tests
new file mode 100755
index 0000000..7320e17
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mdev.tests
@@ -0,0 +1,226 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# ls -ln is showing date. Need to remove that, it's variable
+# sed: (1) "maj, min" -> "maj,min" (2) coalesce spaces
+# cut: remove date
+FILTER_LS="grep -v '^total ' | sed -e 's/,  */,/g' -e 's/  */ /g' | cut -d' ' -f 1-5,9-"
+# 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"
+
+rm -rf mdev.testdir
+mkdir mdev.testdir
+# We need mdev executable to be in chroot jail!
+# (will still fail with dynamically linked one, though...)
+cp ../busybox mdev.testdir/mdev
+mkdir mdev.testdir/bin
+cp ../busybox mdev.testdir/bin/sh 2>/dev/null # for testing cmd feature
+mkdir mdev.testdir/etc
+mkdir mdev.testdir/dev
+mkdir -p mdev.testdir/sys/block/sda
+echo "8:0" >mdev.testdir/sys/block/sda/dev
+
+# env - PATH=$PATH: on some systems chroot binary won't otherwise be found
+
+optional STATIC FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME
+testing "mdev add /block/sda" \
+	"env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+	ls -ln mdev.testdir/dev | $FILTER_LS" \
+"\
+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
+optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME
+testing "mdev stops on first rule" \
+	"env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+	ls -ln mdev.testdir/dev | $FILTER_LS" \
+"\
+brw-rw-rw- 1 1 1 8,0 sda
+" \
+	"" ""
+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
+optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME
+testing "mdev does not stop on dash-rule" \
+	"env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+	ls -ln mdev.testdir/dev | $FILTER_LS" \
+"\
+br--r--r-- 1 2 2 8,0 sda
+" \
+	"" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
+echo "\$MODALIAS=qw  1:1 666" >mdev.testdir/etc/mdev.conf
+echo "\$MODALIAS=qw. 2:2 444" >>mdev.testdir/etc/mdev.conf
+echo "\$MODALIAS=qw. 3:3 400" >>mdev.testdir/etc/mdev.conf
+optional STATIC FEATURE_MDEV_CONF FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME
+testing "mdev \$ENVVAR=regex match" \
+	"env - PATH=$PATH ACTION=add DEVPATH=/block/sda MODALIAS=qwe chroot mdev.testdir /mdev 2>&1;
+	ls -ln mdev.testdir/dev | $FILTER_LS" \
+"\
+br--r--r-- 1 2 2 8,0 sda
+" \
+	"" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
+echo "sda 0:0 444 >disk/scsiA" >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/symlink rule '>bar/baz'" \
+	"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 2 0 0 disk
+lrwxrwxrwx 1 0 0 sda -> disk/scsiA
+
+mdev.testdir/dev/disk:
+br--r--r-- 1 0 0 scsiA
+" \
+	"" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
+echo "sda 0:0 444 >disk/" >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/symlink rule '>bar/'" \
+	"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 2 0 0 disk
+lrwxrwxrwx 1 0 0 sda -> disk/sda
+
+mdev.testdir/dev/disk:
+br--r--r-- 1 0 0 sda
+" \
+	"" ""
+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
+testing "mdev regexp substring match + replace" \
+	"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 2 0 0 sd
+lrwxrwxrwx 1 0 0 sda -> sd/a_sda
+
+mdev.testdir/dev/sd:
+brw-r--r-- 1 0 0 a_sda
+" \
+	"" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
+echo "sda 0:0 644 @echo @echo TEST" >mdev.testdir/etc/mdev.conf
+optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH
+testing "mdev command" \
+	"env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+	ls -lnR mdev.testdir/dev | $FILTER_LS" \
+"\
+@echo TEST
+mdev.testdir/dev:
+brw-r--r-- 1 0 0 8,0 sda
+" \
+	"" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
+echo "sda 0:0 644 =block/ @echo @echo TEST:\$MDEV" >mdev.testdir/etc/mdev.conf
+optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_EXEC FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_SH_IS_ASH
+testing "mdev move and command" \
+	"env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+	ls -lnR mdev.testdir/dev | $FILTER_LS2" \
+"\
+@echo TEST:block/sda
+mdev.testdir/dev:
+drwxr-xr-x 2 0 0 block
+
+mdev.testdir/dev/block:
+brw-r--r-- 1 0 0 sda
+" \
+	"" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
+echo "@8,0 0:1 644" >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
+testing "mdev #maj,min and no explicit uid" \
+	"env - PATH=$PATH ACTION=add DEVPATH=/block/sda chroot mdev.testdir /mdev 2>&1;
+	ls -lnR mdev.testdir/dev | $FILTER_LS" \
+"\
+mdev.testdir/dev:
+brw-r--r-- 1 0 1 8,0 sda
+" \
+	"" ""
+SKIP=
+
+# continuing to use directory structure from prev test
+rm -rf mdev.testdir/dev/*
+mkdir -p mdev.testdir/sys/class/tty/capi
+echo "191:0" >mdev.testdir/sys/class/tty/capi/dev
+mkdir -p mdev.testdir/sys/class/tty/capi1
+echo "191:1" >mdev.testdir/sys/class/tty/capi1/dev
+mkdir -p mdev.testdir/sys/class/tty/capi20
+echo "191:20" >mdev.testdir/sys/class/tty/capi20/dev
+echo "capi            0:0 0660 =capi20"      >mdev.testdir/etc/mdev.conf
+echo "capi([0-9])     0:0 0660 =capi20.0%1" >>mdev.testdir/etc/mdev.conf
+echo "capi([0-9]*)    0:0 0660 =capi20.%1"  >>mdev.testdir/etc/mdev.conf
+# mdev invocation with DEVPATH=/class/tty/capi20 was deleting /dev/capi20
+optional STATIC FEATURE_MDEV_CONF FEATURE_MDEV_RENAME FEATURE_MDEV_RENAME_REGEXP FEATURE_LS_RECURSIVE FEATURE_LS_TIMESTAMPS FEATURE_LS_USERNAME FEATURE_LS_SORTFILES
+testing "move rule does not delete node with name == device_name" \
+	"\
+	env - PATH=$PATH ACTION=add DEVPATH=/class/tty/capi chroot mdev.testdir /mdev 2>&1;
+	env - PATH=$PATH ACTION=add DEVPATH=/class/tty/capi1 chroot mdev.testdir /mdev 2>&1;
+	env - PATH=$PATH ACTION=add DEVPATH=/class/tty/capi20 chroot mdev.testdir /mdev 2>&1;
+	ls -lnR mdev.testdir/dev | $FILTER_LS" \
+"\
+mdev.testdir/dev:
+crw-rw---- 1 0 0 191,0 capi20
+crw-rw---- 1 0 0 191,1 capi20.01
+crw-rw---- 1 0 0 191,20 capi20.20
+" \
+	"" ""
+SKIP=
+
+# clean up
+rm -rf mdev.testdir
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/mkdir/mkdir-makes-a-directory b/busybox-1.19.3/testsuite/mkdir/mkdir-makes-a-directory
new file mode 100644
index 0000000..6ca5c4d
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mkdir/mkdir-makes-a-directory
@@ -0,0 +1,2 @@
+busybox mkdir foo
+test -d foo
diff --git a/busybox-1.19.3/testsuite/mkdir/mkdir-makes-parent-directories b/busybox-1.19.3/testsuite/mkdir/mkdir-makes-parent-directories
new file mode 100644
index 0000000..992facb
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mkdir/mkdir-makes-parent-directories
@@ -0,0 +1,2 @@
+busybox mkdir -p foo/bar
+test -d foo -a -d foo/bar
diff --git a/busybox-1.19.3/testsuite/mkfs.minix.tests b/busybox-1.19.3/testsuite/mkfs.minix.tests
new file mode 100755
index 0000000..8a33c16
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mkfs.minix.tests
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# mkfs.minix tests.
+# Copyright 2007 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+testing "mkfs.minix" \
+	"dd if=/dev/zero of=input bs=1k count=1024 2>/dev/null; mkfs.minix input; md5sum <input" \
+"352 inodes\n"\
+"1024 blocks\n"\
+"Firstdatazone=15 (15)\n"\
+"Zonesize=1024\n"\
+"Maxsize=268966912\n"\
+"4f35f7afeba07d56055bed1f29ae20b7  -\n" \
+	"" \
+	""
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/mount.testroot b/busybox-1.19.3/testsuite/mount.testroot
new file mode 100755
index 0000000..4f34c57
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mount.testroot
@@ -0,0 +1,183 @@
+#!/bin/sh
+
+# SUSv3 compliant mount and umount tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+if [ -z "$TESTDIR" ]
+then
+  echo 'Need $TESTDIR'
+  exit 1
+fi
+
+cd "$TESTDIR"
+
+. testing.sh
+
+# If we aren't PID 1, barf.
+
+#if [ $$ -ne 1 ]
+#then
+#  echo "SKIPPED: mount test requires emulation environment"
+#  exit 0
+#fi
+
+# Run tests within the chroot environment.
+dochroot bash rm ls ln cat ps mknod mkdir dd grep cmp diff tail \
+	mkfs.ext2 mkfs.vfat mount umount losetup wc << EOF
+#!/bin/bash
+
+. /testing.sh
+
+mknod /dev/loop0 b 7 0
+mknod /dev/loop1 b 7 1
+
+# We need /proc to do much.  Make sure we can mount it explicitly.
+
+testing "mount no proc [GNUFAIL]" "mount 2> /dev/null || echo yes" "yes\n" "" ""
+testing "mount /proc" "mount -t proc /proc /proc && ls -d /proc/1" \
+	"/proc/1\n" "" ""
+
+# Make sure the last thing in the list is /proc
+
+testing "mount list1" "mount | tail -n 1" "/proc on /proc type proc (rw)\n" \
+	"" ""
+
+
+# Create an ext2 image
+
+mkdir -p images/{ext2.dir,vfat.dir,test1,test2,test3}
+dd if=/dev/zero of=images/ext2.img bs=1M count=1 2> /dev/null
+mkfs.ext2 -F -b 1024 images/ext2.img > /dev/null 2> /dev/null
+dd if=/dev/zero of=images/vfat.img bs=1M count=1 2> /dev/null
+mkfs.vfat images/vfat.img > /dev/null
+
+# Test mount it
+
+testing "mount vfat image (explicitly autodetect type)" \
+	"mount -t auto images/vfat.img images/vfat.dir && mount | tail -n 1 | grep -o 'vfat.dir type vfat'" \
+	"vfat.dir type vfat\n" "" ""
+testing "mount ext2 image (autodetect type)" \
+	"mount images/ext2.img images/ext2.dir 2> /dev/null && mount | tail -n 1" \
+	"/dev/loop1 on /images/ext2.dir type ext2 (rw)\n" "" ""
+testing "mount remount ext2 image noatime" \
+	"mount -o remount,noatime images/ext2.dir && mount | tail -n 1" \
+	"/dev/loop1 on /images/ext2.dir type ext2 (rw,noatime)\n" "" ""
+testing "mount remount ext2 image ro remembers noatime" \
+	"mount -o remount,ro images/ext2.dir && mount | tail -n 1" \
+	"/dev/loop1 on /images/ext2.dir type ext2 (ro,noatime)\n" "" ""
+
+umount -d images/vfat.dir
+umount -d images/ext2.dir
+
+testing "mount umount freed loop device" \
+	"mount images/ext2.img images/ext2.dir && mount | tail -n 1" \
+	"/dev/loop0 on /images/ext2.dir type ext2 (rw)\n" "" ""
+
+testing "mount block device" \
+	"mount -t ext2 /dev/loop0 images/test1 && mount | tail -n 1" \
+	"/dev/loop0 on /images/test1 type ext2 (rw)\n" "" ""
+
+umount -d images/ext2.dir images/test1
+
+testing "mount remount nonexistent directory" \
+	"mount -o remount,noatime images/ext2.dir 2> /dev/null || echo yes" \
+	"yes\n" "" ""
+
+# Fun with mount -a
+
+testing "mount -a no fstab" "mount -a 2>/dev/null || echo yes" "yes\n" "" ""
+
+umount /proc
+
+# The first field is space delimited, the rest tabs.
+
+cat > /etc/fstab << FSTAB
+/proc             /proc			proc	defaults	0	0
+# Autodetect loop, and provide flags with commas in them.
+/images/ext2.img  /images/ext2.dir	ext2	noatime,nodev	0	0
+# autodetect filesystem, flags without commas.
+/images/vfat.img  /images/vfat.dir	auto	ro		0	0
+# A block device
+/dev/loop2        /images/test1		auto	defaults	0	0
+# tmpfs, filesystem specific flag.
+walrus		  /images/test2		tmpfs	size=42		0	0
+# Autodetect a bind mount.
+/images/test2     /images/test3		auto	defaults	0	0
+FSTAB
+
+# Put something on loop2.
+mknod /dev/loop2 b 7 2
+cat images/ext2.img > images/ext2-2.img
+losetup /dev/loop2 images/ext2-2.img
+
+testing "mount -a" "mount -a && echo hello > /images/test2/abc && cat /images/test3/abc && (mount | wc -l)" "hello\n8\n" "" ""
+
+testing "umount -a" "umount -a && ls /proc" "" "" ""
+
+#/bin/bash < /dev/tty > /dev/tty 2> /dev/tty
+mknod /dev/console c 5 1
+/bin/bash < /dev/console > /dev/console 2> /dev/console
+EOF
+
+exit 0
+
+# Run some tests
+
+losetup nonexistent device (should return error 2)
+losetup unbound loop device (should return error 1)
+losetup bind file to loop device
+losetup bound loop device (display)  (should return error 0)
+losetup filename (error)
+losetup nofile (file not found)
+losetup -d
+losetup bind with offset
+losetup -f (print first loop device)
+losetup -f filename (associate file with first loop device)
+losetup -o (past end of file) -f filename
+
+mount -a
+  with multiple entries in fstab
+  with duplicate entries in fstab
+  with relative paths in fstab
+  with user entries in fstab
+mount -o async,sync,atime,noatime,dev,nodev,exec,noexec,loop,suid,nosuid,remount,ro,rw,bind,move
+mount -r
+mount -o rw -r
+mount -w -o ro
+mount -t auto
+
+mount with relative path in fstab
+mount block device
+mount char device
+mount file (autoloop)
+mount directory (autobind)
+
+
+testing "umount with no /proc"
+testing "umount curdir"
+
+# The basic tests.  These should work even with the small busybox.
+
+testing "sort" "input" "a\nb\nc\n" "c\na\nb\n" ""
+testing "sort #2" "input" "010\n1\n3\n" "3\n1\n010\n" ""
+testing "sort stdin" "" "a\nb\nc\n" "" "b\na\nc\n"
+testing "sort numeric" "-n input" "1\n3\n010\n" "3\n1\n010\n" ""
+testing "sort reverse" "-r input" "wook\nwalrus\npoint\npabst\naargh\n" \
+	"point\nwook\npabst\naargh\nwalrus\n" ""
+
+optional FEATURE_MOUNT_LOOP
+testing "umount -D"
+
+optional FEATURE_MTAB_SUPPORT
+optional FEATURE_MOUNT_NFS
+# No idea what to test here.
+
+optional UMOUNT
+optional FEATURE_UMOUNT_ALL
+testing "umount -a"
+testing "umount -r"
+testing "umount -l"
+testing "umount -f"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/mount.tests b/busybox-1.19.3/testsuite/mount.tests
new file mode 100755
index 0000000..c5891be
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mount.tests
@@ -0,0 +1,86 @@
+#!/bin/sh
+# Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+test -f "$bindir/.config" && . "$bindir/.config"
+
+test "`id -u`" = 0 || {
+	echo "SKIPPED: mount (must be root to test this)"
+	exit 0
+}
+
+if test x"$CONFIG_MKFS_MINIX" != x"y" \
+|| test x"$CONFIG_FEATURE_MINIX2" != x"y" \
+|| test x"$CONFIG_FEATURE_MOUNT_LOOP" != x"y" \
+|| test x"$CONFIG_FEATURE_MOUNT_FLAGS" != x"y" \
+|| test x"$CONFIG_FEATURE_DEVFS" = x"y" \
+; then
+	echo "SKIPPED: mount"
+	exit 0
+fi
+
+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; }
+modprobe minix 2>/dev/null
+mkdir "$testdir" 2>/dev/null
+umount -d "$testdir" 2>/dev/null
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+#   file input will be file called "input"
+#   test can create a file "actual" instead of writing to stdout
+
+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[, ]'" \
+	"1\n""1\n" \
+	"" ""
+
+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=
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/msh/msh-supports-underscores-in-variable-names b/busybox-1.19.3/testsuite/msh/msh-supports-underscores-in-variable-names
new file mode 100644
index 0000000..9c7834b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/msh/msh-supports-underscores-in-variable-names
@@ -0,0 +1 @@
+test "`busybox msh -c 'FOO_BAR=foo; echo $FOO_BAR'`" = foo
diff --git a/busybox-1.19.3/testsuite/mv/mv-files-to-dir b/busybox-1.19.3/testsuite/mv/mv-files-to-dir
new file mode 100644
index 0000000..2b567f7
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-files-to-dir
@@ -0,0 +1,16 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+TZ=UTC0 touch -d '2000-01-30 05:24:08' dir1/file3
+mkdir there
+busybox mv file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test -f there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! -e file1
+test ! -e file2
+test ! -e link1
+test ! -e dir1/file3
diff --git a/busybox-1.19.3/testsuite/mv/mv-follows-links b/busybox-1.19.3/testsuite/mv/mv-follows-links
new file mode 100644
index 0000000..1fb355b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-follows-links
@@ -0,0 +1,4 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -f baz
diff --git a/busybox-1.19.3/testsuite/mv/mv-moves-empty-file b/busybox-1.19.3/testsuite/mv/mv-moves-empty-file
new file mode 100644
index 0000000..48afca4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-moves-empty-file
@@ -0,0 +1,4 @@
+touch foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox-1.19.3/testsuite/mv/mv-moves-file b/busybox-1.19.3/testsuite/mv/mv-moves-file
new file mode 100644
index 0000000..edb4c37
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-moves-file
@@ -0,0 +1,3 @@
+touch foo
+busybox mv foo bar
+test ! -f foo -a -f bar
diff --git a/busybox-1.19.3/testsuite/mv/mv-moves-hardlinks b/busybox-1.19.3/testsuite/mv/mv-moves-hardlinks
new file mode 100644
index 0000000..eaa8215
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-moves-hardlinks
@@ -0,0 +1,4 @@
+touch foo
+ln foo bar
+busybox mv bar baz
+test ! -f bar -a -f baz
diff --git a/busybox-1.19.3/testsuite/mv/mv-moves-large-file b/busybox-1.19.3/testsuite/mv/mv-moves-large-file
new file mode 100644
index 0000000..77d088f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-moves-large-file
@@ -0,0 +1,4 @@
+dd if=/dev/zero of=foo seek=10k count=1 2>/dev/null
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox-1.19.3/testsuite/mv/mv-moves-small-file b/busybox-1.19.3/testsuite/mv/mv-moves-small-file
new file mode 100644
index 0000000..065c7f1
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-moves-small-file
@@ -0,0 +1,4 @@
+echo I WANT > foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox-1.19.3/testsuite/mv/mv-moves-symlinks b/busybox-1.19.3/testsuite/mv/mv-moves-symlinks
new file mode 100644
index 0000000..c413af0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-moves-symlinks
@@ -0,0 +1,6 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -f foo
+test ! -e bar
+test -L baz
diff --git a/busybox-1.19.3/testsuite/mv/mv-moves-unreadable-files b/busybox-1.19.3/testsuite/mv/mv-moves-unreadable-files
new file mode 100644
index 0000000..bc9c313
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-moves-unreadable-files
@@ -0,0 +1,5 @@
+touch foo
+chmod a-r foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox-1.19.3/testsuite/mv/mv-preserves-hard-links b/busybox-1.19.3/testsuite/mv/mv-preserves-hard-links
new file mode 100644
index 0000000..b3ba3aa
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-preserves-hard-links
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_PRESERVE_HARDLINKS
+touch foo
+ln foo bar
+mkdir baz
+busybox mv foo bar baz
+test baz/foo -ef baz/bar
diff --git a/busybox-1.19.3/testsuite/mv/mv-preserves-links b/busybox-1.19.3/testsuite/mv/mv-preserves-links
new file mode 100644
index 0000000..ea565d2
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-preserves-links
@@ -0,0 +1,5 @@
+touch foo
+ln -s foo bar
+busybox mv bar baz
+test -L baz
+test xfoo = x`readlink baz`
diff --git a/busybox-1.19.3/testsuite/mv/mv-refuses-mv-dir-to-subdir b/busybox-1.19.3/testsuite/mv/mv-refuses-mv-dir-to-subdir
new file mode 100644
index 0000000..3bad131
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-refuses-mv-dir-to-subdir
@@ -0,0 +1,23 @@
+echo file number one > file1
+echo file number two > file2
+ln -s file2 link1
+mkdir dir1
+TZ=UTC0 touch -d '2000-01-30 05:24:08' dir1/file3
+mkdir there
+busybox mv file1 file2 link1 dir1 there
+test -f there/file1
+test -f there/file2
+test -f there/dir1/file3
+test -L there/link1
+test xfile2 = x`readlink there/link1`
+test ! -e file1
+test ! -e file2
+test ! -e link1
+test ! -e dir1/file3
+set +e
+busybox mv there there/dir1
+if [ $? != 0 ] ; then
+	exit 0;
+fi
+
+exit 1;
diff --git a/busybox-1.19.3/testsuite/mv/mv-removes-source-file b/busybox-1.19.3/testsuite/mv/mv-removes-source-file
new file mode 100644
index 0000000..48afca4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/mv/mv-removes-source-file
@@ -0,0 +1,4 @@
+touch foo
+busybox mv foo bar
+test ! -e foo
+test -f bar
diff --git a/busybox-1.19.3/testsuite/od.tests b/busybox-1.19.3/testsuite/od.tests
new file mode 100755
index 0000000..7a9da3e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/od.tests
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "commands" "expected result" "file input" "stdin"
+
+optional DESKTOP
+testing "od -b" \
+	"od -b" \
+"\
+0000000 110 105 114 114 117
+0000005
+" \
+	"" "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
diff --git a/busybox-1.19.3/testsuite/parse.tests b/busybox-1.19.3/testsuite/parse.tests
new file mode 100755
index 0000000..904e1a1
--- /dev/null
+++ b/busybox-1.19.3/testsuite/parse.tests
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+# Copyright 2008 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+COLLAPSE=$(( 0x00010000))
+TRIM=$((     0x00020000))
+GREEDY=$((   0x00040000))
+MIN_DIE=$((  0x00100000))
+KEEP_COPY=$((0x00200000))
+ESCAPE=$((   0x00400000))
+NORMAL=$((   COLLAPSE | TRIM | GREEDY))
+
+# testing "description" "command" "result" "infile" "stdin"
+
+testing "parse mdev.conf" \
+	"parse -n 4 -m 3 -f $((NORMAL)) -" \
+	"[sda][0:0][644][@echo @echo TEST]\n" \
+	"-" \
+	" sda 0:0 644 @echo @echo TEST # echo trap\n"
+
+testing "parse notrim" \
+	"parse -n 4 -m 3 -f $((NORMAL - TRIM - COLLAPSE)) -" \
+	"[][sda][0:0][644 @echo @echo TEST ]\n" \
+	"-" \
+	" sda 0:0 644 @echo @echo TEST \n"
+
+FILE=__parse
+cat >$FILE <<EOF
+#
+# Device         Point               System                       Options
+#_______________________________________________________________
+/dev/hdb3       /                       ext2                 defaults      1          0
+   /dev/hdb1       /dosc               hpfs                 ro      1          0
+ /dev/fd0          /dosa              vfat                  rw,user,noauto,nohide	 0		0
+	/dev/fd1          /dosb              vfat                  rw,user,noauto,nohide	 0		0
+#
+ /dev/cdrom     /cdrom            iso9660          ro,user,noauto,nohide	 0		0
+/dev/hdb5       /redhat            ext2                 rw,root,noauto,nohide	 0		0 #sssd
+	/dev/hdb6       /win2home     ntfs                  rw,root,noauto,nohide	 0		0# ssdsd
+/dev/hdb7       /win2skul        ntfs                  rw,root,noauto,nohide none	 0		0
+none     /dev/pts           devpts             gid=5,mode=620                 0    0 
+     none                /proc               proc                defaults     0          0
+EOF
+
+cat >$FILE.res <<EOF
+[/dev/hdb3][/][ext2][defaults][1][0]
+[/dev/hdb1][/dosc][hpfs][ro][1][0]
+[/dev/fd0][/dosa][vfat][rw,user,noauto,nohide][0][0]
+[/dev/fd1][/dosb][vfat][rw,user,noauto,nohide][0][0]
+[/dev/cdrom][/cdrom][iso9660][ro,user,noauto,nohide][0][0]
+[/dev/hdb5][/redhat][ext2][rw,root,noauto,nohide][0][0]
+[/dev/hdb6][/win2home][ntfs][rw,root,noauto,nohide][0][0]
+[/dev/hdb7][/win2skul][ntfs][rw,root,noauto,nohide][none][0		0]
+[none][/dev/pts][devpts][gid=5,mode=620][0][0]
+[none][/proc][proc][defaults][0][0]
+EOF
+
+testing "parse polluted fstab" \
+	"parse -n 6 -m 6 $FILE" \
+	"`cat $FILE.res`\n" \
+	"" \
+	""
+cp ../examples/inittab $FILE
+cat >$FILE.res <<EOF
+[][][sysinit][/etc/init.d/rcS]
+[][][askfirst][-/bin/sh]
+[tty2][][askfirst][-/bin/sh]
+[tty3][][askfirst][-/bin/sh]
+[tty4][][askfirst][-/bin/sh]
+[tty4][][respawn][/sbin/getty 38400 tty5]
+[tty5][][respawn][/sbin/getty 38400 tty6]
+[][][restart][/sbin/init]
+[][][ctrlaltdel][/sbin/reboot]
+[][][shutdown][/bin/umount -a -r]
+[][][shutdown][/sbin/swapoff -a]
+EOF
+
+testing "parse inittab from examples" \
+	"parse -n 4 -m 4 -f $((NORMAL - TRIM - COLLAPSE)) -d'#:' $FILE" \
+	"`cat $FILE.res`\n" \
+	"" \
+	""
+
+cp ../examples/udhcp/udhcpd.conf $FILE
+cat >$FILE.res <<EOF
+[start][192.168.0.20]
+[end][192.168.0.254]
+[interface][eth0]
+[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]
+[option][domain][local]
+[option][lease][864000]
+[option][0x08][01020304]
+EOF
+
+testing "parse udhcpd.conf from examples" \
+	"parse -n 127 $FILE" \
+	"`cat $FILE.res`\n" \
+	"" \
+	""
+
+rm -f $FILE $FILE.res
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/patch.tests b/busybox-1.19.3/testsuite/patch.tests
new file mode 100755
index 0000000..c604b9c
--- /dev/null
+++ b/busybox-1.19.3/testsuite/patch.tests
@@ -0,0 +1,217 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command(s)" "expected result" "file input" "stdin"
+
+testing "patch with old_file == new_file" \
+	'patch 2>&1; echo $?; cat input' \
+"\
+patching file input
+0
+qwe
+asd
+zxc
+" \
+"\
+qwe
+zxc
+" \
+"\
+--- input	Jan 01 01:01:01 2000
++++ input	Jan 01 01:01:01 2000
+@@ -1,2 +1,3 @@
+ qwe
++asd
+ zxc
+" \
+
+testing "patch with nonexistent old_file" \
+	'patch 2>&1; echo $?; cat input' \
+"\
+patching file input
+0
+qwe
+asd
+zxc
+" \
+"\
+qwe
+zxc
+" \
+"\
+--- input.doesnt_exist	Jan 01 01:01:01 2000
++++ input	Jan 01 01:01:01 2000
+@@ -1,2 +1,3 @@
+ qwe
++asd
+ zxc
+" \
+
+testing "patch -R with nonexistent old_file" \
+	'patch -R 2>&1; echo $?; cat input' \
+"\
+patching file input
+0
+qwe
+zxc
+" \
+"\
+qwe
+asd
+zxc
+" \
+"\
+--- input.doesnt_exist	Jan 01 01:01:01 2000
++++ input	Jan 01 01:01:01 2000
+@@ -1,2 +1,3 @@
+ qwe
++asd
+ 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
+1
+abc
+def
+123
+" \
+"\
+abc
+def
+123
+" \
+"\
+--- input.old	Jan 01 01:01:01 2000
++++ input	Jan 01 01:01:01 2000
+@@ -1,2 +1,3 @@
+ abc
++def
+ 123
+" \
+
+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
+1
+abc
+123
+456
+" \
+"\
+abc
+123
+456
+" \
+"\
+--- input.old	Jan 01 01:01:01 2000
++++ input	Jan 01 01:01:01 2000
+@@ -1,2 +1,3 @@
+ abc
+ 123
++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
+" \
+
+rm input.orig 2>/dev/null
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/pidof.tests b/busybox-1.19.3/testsuite/pidof.tests
new file mode 100755
index 0000000..2a06d2b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/pidof.tests
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# pidof tests.
+# Copyright 2005 by Bernhard Reutner-Fischer
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+# AUDIT:
+
+. ./testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+testing "pidof (exit with error)" \
+	"pidof veryunlikelyoccuringbinaryname ; echo \$?" "1\n" "" ""
+testing "pidof (exit with success)" "pidof pidof > /dev/null; echo \$?" \
+	"0\n" "" ""
+# We can get away with this because it says #!/bin/sh up top.
+
+testing "pidof this" "pidof pidof.tests | grep -o -w $$" "$$\n" "" ""
+
+optional FEATURE_PIDOF_SINGLE
+testing "pidof -s" "pidof -s init" "1\n" "" ""
+SKIP=
+
+optional FEATURE_PIDOF_OMIT FEATURE_PIDOF_SINGLE
+# This test fails now because process name matching logic has changed,
+# but new logic is not "wrong" either... see find_pid_by_name.c comments
+#testing "pidof -o %PPID" "pidof -o %PPID pidof.tests | grep -o -w $$" "" "" ""
+testing "pidof -o %PPID NOP" "pidof -o %PPID -s init" "1\n" "" ""
+testing "pidof -o init" "pidof -o 1 init | grep -o -w 1" "" "" ""
+SKIP=
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/printf.tests b/busybox-1.19.3/testsuite/printf.tests
new file mode 100755
index 0000000..9a3c874
--- /dev/null
+++ b/busybox-1.19.3/testsuite/printf.tests
@@ -0,0 +1,108 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# Need this in order to not execute shell builtin
+bb="busybox "
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+testing "printf produces no further output 1" \
+	"${bb}printf '\c' foo" \
+	"" \
+	"" ""
+
+testing "printf produces no further output 2" \
+	"${bb}printf '%s\c' foo bar" \
+	"foo" \
+	"" ""
+
+testing "printf repeatedly uses pattern for each argv" \
+	"${bb}printf '%s\n' foo \$HOME" \
+	"foo\n$HOME\n" \
+	"" ""
+
+testing "printf understands %b escaped_string" \
+	"${bb}printf '%b' 'a\tb' 'c\\d\n' 2>&1; echo \$?" \
+	"a\tbc\\d\n""0\n" \
+	"" ""
+
+testing "printf understands %d '\"x' \"'y\" \"'zTAIL\"" \
+	"${bb}printf '%d\n' '\"x' \"'y\" \"'zTAIL\" 2>&1; echo \$?" \
+	"120\n""121\n""122\n""0\n" \
+	"" ""
+
+testing "printf understands %s '\"x' \"'y\" \"'zTAIL\"" \
+	"${bb}printf '%s\n' '\"x' \"'y\" \"'zTAIL\" 2>&1; echo \$?" \
+	"\"x\n""'y\n""'zTAIL\n""0\n" \
+	"" ""
+
+testing "printf understands %23.12f" \
+	"${bb}printf '|%23.12f|\n' 5.25 2>&1; echo \$?" \
+	"|         5.250000000000|\n""0\n" \
+	"" ""
+
+testing "printf understands %*.*f" \
+	"${bb}printf '|%*.*f|\n' 23 12 5.25 2>&1; echo \$?" \
+	"|         5.250000000000|\n""0\n" \
+	"" ""
+
+testing "printf understands %*f with negative width" \
+	"${bb}printf '|%*f|\n' -23 5.25 2>&1; echo \$?" \
+	"|5.250000               |\n""0\n" \
+	"" ""
+
+testing "printf understands %.*f with negative precision" \
+	"${bb}printf '|%.*f|\n' -12 5.25 2>&1; echo \$?" \
+	"|5.250000|\n""0\n" \
+	"" ""
+
+testing "printf understands %*.*f with negative width/precision" \
+	"${bb}printf '|%*.*f|\n' -23 -12 5.25 2>&1; echo \$?" \
+	"|5.250000               |\n""0\n" \
+	"" ""
+
+testing "printf understands %zd" \
+	"${bb}printf '%zd\n' -5 2>&1; echo \$?" \
+	"-5\n""0\n" \
+	"" ""
+
+testing "printf understands %ld" \
+	"${bb}printf '%ld\n' -5 2>&1; echo \$?" \
+	"-5\n""0\n" \
+	"" ""
+
+testing "printf understands %Ld" \
+	"${bb}printf '%Ld\n' -5 2>&1; echo \$?" \
+	"-5\n""0\n" \
+	"" ""
+
+# "FIXED" now to act compatibly
+## We are "more correct" here than bash/coreutils: they happily print -2
+## as if it is a huge unsigned number
+#testing "printf handles %u -N" \
+#	"${bb}printf '%u\n' 1 -2 3 2>&1; echo \$?" \
+#	"1\n""printf: -2: invalid number\n""0\n""3\n""0\n" \
+#	"" ""
+
+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: invalid number 'bad'\n""0\n"\
+"3\n""printf: invalid number '123bad'\n""0\n"\
+"4\n""1\n" \
+	"" ""
+
+testing "printf aborts on bare %" \
+	"${bb}printf '%' a b c 2>&1; echo \$?" \
+	"printf: %: invalid format\n""1\n" \
+	"" ""
+
+testing "printf aborts on %r" \
+	"${bb}printf '%r' a b c 2>&1; echo \$?" \
+	"printf: %r: invalid format\n""1\n" \
+	"" ""
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/pwd/pwd-prints-working-directory b/busybox-1.19.3/testsuite/pwd/pwd-prints-working-directory
new file mode 100644
index 0000000..8575347
--- /dev/null
+++ b/busybox-1.19.3/testsuite/pwd/pwd-prints-working-directory
@@ -0,0 +1 @@
+test $(pwd) = $(busybox pwd)
diff --git a/busybox-1.19.3/testsuite/readlink.tests b/busybox-1.19.3/testsuite/readlink.tests
new file mode 100755
index 0000000..c7fc8ad
--- /dev/null
+++ b/busybox-1.19.3/testsuite/readlink.tests
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Readlink tests.
+# Copyright 2006 by Natanael Copa <n@tanael.org>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+TESTDIR=readlink_testdir
+TESTFILE="$TESTDIR/testfile"
+TESTLINK="testlink"
+FAILLINK="$TESTDIR/$TESTDIR/testlink"
+
+# create the dir and test files
+mkdir -p "./$TESTDIR"
+touch "./$TESTFILE"
+ln -s "./$TESTFILE" "./$TESTLINK"
+
+testing "readlink on a file" "readlink ./$TESTFILE" "" "" ""
+testing "readlink on a link" "readlink ./$TESTLINK" "./$TESTFILE\n" "" ""
+
+optional FEATURE_READLINK_FOLLOW
+
+testing "readlink -f on a file" "readlink -f ./$TESTFILE" "$PWD/$TESTFILE\n" "" ""
+testing "readlink -f on a link" "readlink -f ./$TESTLINK" "$PWD/$TESTFILE\n" "" ""
+testing "readlink -f on an invalid link" "readlink -f ./$FAILLINK" "" "" ""
+testing "readlink -f on a wierd dir" "readlink -f $TESTDIR/../$TESTFILE" "$PWD/$TESTFILE\n" "" ""
+
+
+# clean up
+rm -r "$TESTLINK" "$TESTDIR"
+
+exit $((FAILCOUNT <= 255 ? FAILCOUNT : 255))
diff --git a/busybox-1.19.3/testsuite/rm/rm-removes-file b/busybox-1.19.3/testsuite/rm/rm-removes-file
new file mode 100644
index 0000000..46571a9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/rm/rm-removes-file
@@ -0,0 +1,3 @@
+touch foo
+busybox rm foo
+test ! -f foo
diff --git a/busybox-1.19.3/testsuite/rmdir/rmdir-removes-parent-directories b/busybox-1.19.3/testsuite/rmdir/rmdir-removes-parent-directories
new file mode 100644
index 0000000..222f5de
--- /dev/null
+++ b/busybox-1.19.3/testsuite/rmdir/rmdir-removes-parent-directories
@@ -0,0 +1,3 @@
+mkdir -p foo/bar
+busybox rmdir -p foo/bar
+test ! -d foo
diff --git a/busybox-1.19.3/testsuite/runtest b/busybox-1.19.3/testsuite/runtest
new file mode 100755
index 0000000..51575d9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/runtest
@@ -0,0 +1,171 @@
+#!/bin/sh
+# Usage:
+# runtest [applet1] [applet2...]
+
+. ./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".
+# Option -e will make testcase stop on the first failed command.
+run_applet_testcase()
+{
+	local applet="$1"
+	local testcase="$2"
+
+	local status=0
+	local uc_applet=$(echo "$applet" | tr a-z A-Z)
+	local testname="$testcase"
+
+	testname="${testname##*/}" # take basename
+	if grep "^# CONFIG_$uc_applet is not set$" "$bindir/.config" >/dev/null; then
+		echo "UNTESTED: $testname"
+		return 0
+	fi
+
+	if grep "^# FEATURE: " "$testcase" >/dev/null; then
+		local feature=$(sed -ne 's/^# FEATURE: //p' "$testcase")
+
+		for f in $feature; do
+			if grep "^# $f is not set$" "$bindir/.config" >/dev/null; then
+				echo "UNTESTED: $testname"
+				return 0
+			fi
+		done
+	fi
+
+	rm -rf ".tmpdir.$applet"
+	mkdir -p ".tmpdir.$applet"
+	cd ".tmpdir.$applet" || return 1
+
+#	echo "Running testcase $testcase"
+	d="$tsdir" \
+		sh -x -e "$testcase" >"$testname.stdout.txt" 2>&1 || status=$?
+	if [ $status -ne 0 ]; then
+		echo "FAIL: $testname"
+		if [ x"$VERBOSE" != x ]; then
+			cat "$testname.stdout.txt"
+		fi
+	else
+		echo "PASS: $testname"
+	fi
+
+	cd ..
+	rm -rf ".tmpdir.$applet"
+
+	return $status
+}
+
+# Run all old-style tests for given applet
+run_oldstyle_applet_tests()
+{
+	local applet="$1"
+	local status=0
+
+	for testcase in "$tsdir/$applet"/*; do
+		# switch on basename of $testcase
+		case "${testcase##*/}" in
+			.*)     continue ;;    # .svn, .git etc
+			*~)     continue ;;    # backup files
+			"CVS")  continue ;;
+			\#*)    continue ;;    # CVS merge residues
+			*.mine) continue ;;    # svn-produced junk
+			*.r[0-9]*) continue ;; # svn-produced junk
+		esac
+		run_applet_testcase "$applet" "$testcase" || {
+			status=1
+			total_failed=$((total_failed + 1))
+		}
+	done
+	return $status
+}
+
+
+
+lcwd=$(pwd)
+[ x"$tsdir" != x"" ] || tsdir="$lcwd"
+[ x"$bindir" != x"" ] || bindir="${lcwd%/*}" # one directory up from $lcwd
+PATH="$bindir:$PATH"
+export bindir   # some tests need to look at $bindir/.config
+
+if [ x"$VERBOSE" = x ]; then
+	export VERBOSE=
+fi
+
+if [ x"$1" = x"-v" ]; then
+	export VERBOSE=1
+	shift
+fi
+
+implemented=$(
+	printf "busybox " # always implemented
+	"$bindir/busybox" 2>&1 |
+	while read line; do
+		if [ x"$line" = x"Currently defined functions:" ]; then
+			xargs | sed 's/,//g'
+			break
+		fi
+	done
+	)
+
+applets="$implemented"
+if [ $# -ne 0 ]; then
+	applets="$@"
+fi
+
+# Populate a directory with links to all busybox applets
+
+LINKSDIR="$bindir/runtest-tempdir-links"
+
+# Comment this line out if you have put a different binary in $LINKSDIR
+# (say, a "standard" tool's binary) in order to run tests against it:
+rm -rf "$LINKSDIR" 2>/dev/null
+
+mkdir "$LINKSDIR" 2>/dev/null
+for i in $implemented; do
+	# Note: if $LINKSDIR/applet exists, we do not overwrite it.
+	# Useful if one wants to run tests against a standard utility,
+	# not an applet.
+	ln -s "$bindir/busybox" "$LINKSDIR/$i" 2>/dev/null
+done
+
+# Set up option flags so tests can be selective.
+export OPTIONFLAGS=:$(
+	sed -nr 's/^CONFIG_//p' "$bindir/.config" |
+	sed 's/=.*//' | xargs | sed 's/ /:/g'
+	):
+
+status=0
+for applet in $applets; do
+	# Any old-style tests for this applet?
+	if [ -d "$tsdir/$applet" ]; then
+		run_oldstyle_applet_tests "$applet" || status=1
+	fi
+
+	# Is this a new-style test?
+	if [ -f "$applet.tests" ]; then
+		if [ ! -e "$LINKSDIR/$applet" ]; then
+			# (avoiding bash'ism "${applet:0:4}")
+			if ! echo "$applet" | grep "^all_" >/dev/null; then
+				echo "SKIPPED: $applet (not built)"
+				continue
+			fi
+		fi
+#		echo "Running test $tsdir/$applet.tests"
+		PATH="$LINKSDIR:$tsdir:$bindir:$PATH" \
+			"$tsdir/$applet.tests"
+		rc=$?
+		total_failed=$((total_failed + rc))
+		test $rc -ne 0 && status=1
+	fi
+done
+
+# Leaving the dir makes it somewhat easier to run failed test by hand
+#rm -rf "$LINKSDIR"
+
+if [ $status -ne 0 ] && [ x"$VERBOSE" = x ]; then
+	echo "$total_failed failure(s) detected; running with -v (verbose) will give more info"
+fi
+exit $status
diff --git a/busybox-1.19.3/testsuite/rx.tests b/busybox-1.19.3/testsuite/rx.tests
new file mode 100755
index 0000000..37fa02e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/rx.tests
@@ -0,0 +1,27 @@
+#!/bin/sh
+# Copyright 2009 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+# Simple one-block file transfer
+# rx => 'C'
+# rx <= SOH <blockno> <255-blockno> <128 byte padded with x1A> <crc> <crc>
+# rx => ACK
+# rx <= EOT
+# rx => ACK
+testing "rx" \
+	"rx rx.OUTFILE | hexdump -vC && cat rx.OUTFILE" \
+"\
+00000000  43 06 06                                          |C..|\n\
+00000003\n\
+???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????" \
+	"" "\01\01\0376\
+???????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????\
+\x1A\x1A\x1A\x1A\x1A\x4B\xB0\04"
+
+rm -f rx.OUTFILE 2>/dev/null
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/sed.tests b/busybox-1.19.3/testsuite/sed.tests
new file mode 100755
index 0000000..ba163e9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/sed.tests
@@ -0,0 +1,300 @@
+#!/bin/sh
+
+# SUSv3 compliant sed tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "description" "commands" "result" "infile" "stdin"
+
+# Corner cases
+testing "sed no files (stdin)" 'sed ""' "hello\n" "" "hello\n"
+testing "sed explicit stdin" 'sed "" -' "hello\n" "" "hello\n"
+testing "sed handles empty lines" "sed -e 's/\$/@/'" "@\n" "" "\n"
+testing "sed stdin twice" 'sed "" - -' "hello" "" "hello"
+
+# Trailing EOF.
+#	Match $, at end of each file or all files?
+
+# -e corner cases
+#	without -e
+#	multiple -e
+#		interact with a
+#	-eee arg1 arg2 arg3
+# -f corner cases
+#	-e -f -e
+# -n corner cases
+#	no newline at EOF?
+# -r corner cases
+#	Just make sure it works.
+# -i corner cases:
+#	sed -i -
+#	permissions
+#	-i on a symlink
+#	on a directory
+#       With $ last-line test
+# Continue with \
+#       End of script with trailing \
+
+# command list
+testing "sed accepts blanks before command" "sed -e '1 d'" "" "" ""
+testing "sed accepts newlines in -e" "sed -e 'i\
+1
+a\
+3'" "1\n2\n3\n" "" "2\n"
+testing "sed accepts multiple -e" "sed -e 'i\' -e '1' -e 'a\' -e '3'" \
+	"1\n2\n3\n" "" "2\n"
+
+# substitutions
+testing "sed -n" "sed -n -e s/foo/bar/ -e s/bar/baz/" "" "" "foo\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"
+testing "sed s [delimiter]" "sed -e 's@[@]@@'" "onetwo" "" "one@two"
+testing "sed s with \\t (GNU ext)" "sed 's/\t/ /'" "one two" "" "one\ttwo"
+
+# branch
+testing "sed b (branch)" "sed -e 'b one;p;: one'" "foo\n" "" "foo\n"
+testing "sed b (branch with no label jumps to end)" "sed -e 'b;p'" \
+	"foo\n" "" "foo\n"
+
+# test and branch
+testing "sed t (test/branch)" "sed -e 's/a/1/;t one;p;: one;p'" \
+	"1\n1\nb\nb\nb\nc\nc\nc\n" "" "a\nb\nc\n"
+testing "sed t (test/branch clears test bit)" "sed -e 's/a/b/;:loop;t loop'" \
+	"b\nb\nc\n" "" "a\nb\nc\n"
+testing "sed T (!test/branch)" "sed -e 's/a/1/;T notone;p;: notone;p'" \
+	"1\n1\n1\nb\nb\nc\nc\n" "" "a\nb\nc\n"
+
+test x"$SKIP_KNOWN_BUGS" = x"" && {
+# Normal sed end-of-script doesn't print "c" because n flushed the pattern
+# space.  If n hits EOF, pattern space is empty when script ends.
+# 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"
+}
+# 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"
+
+# Multiple lines in pattern space
+testing "sed N (stops at end of input) and P (prints to first newline only)" \
+	"sed -n 'N;P;p'" "a\na\nb\n" "" "a\nb\nc\n"
+
+# Hold space
+testing "sed G (append hold space to pattern space)" 'sed G' "a\n\nb\n\nc\n\n" \
+	"" "a\nb\nc\n"
+#testing "sed g/G (swap/append hold and patter space)"
+#testing "sed g (swap hold/pattern space)"
+
+testing "sed d ends script iteration" \
+	"sed -e '/ook/d;s/ook/ping/p;i woot'" "" "" "ook\n"
+testing "sed d ends script iteration (2)" \
+	"sed -e '/ook/d;a\' -e 'bang'" "woot\nbang\n" "" "ook\nwoot\n"
+
+# Multiple files, with varying newlines and NUL bytes
+test x"$SKIP_KNOWN_BUGS" = x"" && {
+testing "sed embedded NUL" "sed -e 's/woo/bang/'" "\0bang\0woo\0" "" \
+	"\0woo\0woo\0"
+}
+testing "sed embedded NUL g" "sed -e 's/woo/bang/g'" "bang\0bang\0" "" \
+	"woo\0woo\0"
+test x"$SKIP_KNOWN_BUGS" = x"" && {
+$ECHO -e "/woo/a he\0llo" > sed.commands
+testing "sed NUL in command" "sed -f sed.commands" "woo\nhe\0llo\n" "" "woo"
+rm sed.commands
+}
+
+# sed has funky behavior with newlines at the end of file.  Test lots of
+# corner cases with the optional newline appending behavior.
+
+testing "sed normal newlines" "sed -e 's/woo/bang/' input -" "bang\nbang\n" \
+	"woo\n" "woo\n"
+testing "sed leave off trailing newline" "sed -e 's/woo/bang/' input -" \
+	"bang\nbang" "woo\n" "woo"
+testing "sed autoinsert newline" "sed -e 's/woo/bang/' input -" "bang\nbang" \
+	"woo" "woo"
+testing "sed empty file plus cat" "sed -e 's/nohit//' input -" "one\ntwo" \
+	"" "one\ntwo"
+testing "sed cat plus empty file" "sed -e 's/nohit//' input -" "one\ntwo" \
+	"one\ntwo" ""
+test x"$SKIP_KNOWN_BUGS" = x"" && {
+testing "sed append autoinserts newline" "sed -e '/woot/a woo' -" \
+	"woot\nwoo\n" "" "woot"
+}
+testing "sed insert doesn't autoinsert newline" "sed -e '/woot/i woo' -" \
+	"woo\nwoot" "" "woot"
+testing "sed print autoinsert newlines" "sed -e 'p' -" "one\none" "" "one"
+testing "sed print autoinsert newlines two files" "sed -e 'p' input -" \
+	"one\none\ntwo\ntwo" "one" "two"
+testing "sed noprint, no match, no newline" "sed -ne 's/woo/bang/' input" \
+	"" "no\n" ""
+testing "sed selective matches with one nl" "sed -ne 's/woo/bang/p' input -" \
+	"a bang\nc bang\n" "a woo\nb no" "c woo\nd no"
+testing "sed selective matches insert newline" \
+	"sed -ne 's/woo/bang/p' input -" "a bang\nb bang\nd bang" \
+	"a woo\nb woo" "c no\nd woo"
+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"
+rm outputw
+testing "sed trailing NUL" \
+	"sed 's/i/z/' input -" \
+	"a\0b\0\nc" "a\0b\0" "c"
+testing "sed escaped newline in command" \
+	"sed 's/a/z\\
+z/' input" \
+	"z\nz" "a" ""
+
+# Test end-of-file matching behavior
+
+testing "sed match EOF" "sed -e '"'$p'"'" "hello\nthere\nthere" "" \
+	"hello\nthere"
+testing "sed match EOF two files" "sed -e '"'$p'"' input -" \
+	"one\ntwo\nthree\nfour\nfour" "one\ntwo" "three\nfour"
+# sed match EOF inline: gnu sed 4.1.5 outputs this:
+#00000000  6f 6e 65 0a 6f 6f 6b 0a  6f 6f 6b 0a 74 77 6f 0a  |one.ook.ook.two.|
+#00000010  0a 74 68 72 65 65 0a 6f  6f 6b 0a 6f 6f 6b 0a 66  |.three.ook.ook.f|
+#00000020  6f 75 72                                          |our|
+# which looks buggy to me.
+$ECHO -ne "three\nfour" > input2
+testing "sed match EOF inline" \
+	"sed -e '"'$i ook'"' -i input input2 && cat input input2" \
+	"one\nook\ntwothree\nook\nfour" "one\ntwo" ""
+rm input2
+
+# Test lie-to-autoconf
+
+testing "sed lie-to-autoconf" "sed --version | grep -o 'GNU sed version '" \
+	"GNU sed version \n" "" ""
+
+# Jump to nonexistent label
+test x"$SKIP_KNOWN_BUGS" = x"" && {
+# Incompatibility: illegal jump is not detected if input is ""
+# (that is, no lines at all). GNU sed 4.1.5 complains even in this case
+testing "sed nonexistent label" "sed -e 'b walrus' 2>/dev/null || echo yes" \
+	"yes\n" "" ""
+}
+
+testing "sed backref from empty s uses range regex" \
+	"sed -e '/woot/s//eep \0 eep/'" "eep woot eep" "" "woot"
+
+testing "sed backref from empty s uses range regex with newline" \
+	"sed -e '/woot/s//eep \0 eep/'" "eep woot eep\n" "" "woot\n"
+
+# -i with no filename
+
+touch ./-  # Detect gnu failure mode here.
+testing "sed -i with no arg [GNUFAIL]" "sed -e '' -i 2> /dev/null || echo yes" \
+	"yes\n" "" ""
+rm ./-     # Clean up
+
+testing "sed s/xxx/[/" "sed -e 's/xxx/[/'" "[\n" "" "xxx\n"
+
+# Ponder this a bit more, why "woo not found" from gnu version?
+#testing "sed doesn't substitute in deleted line" \
+#	"sed -e '/ook/d;s/ook//;t woo;a bang;'" "bang" "" "ook\n"
+
+# This makes both seds very unhappy.  Why?
+#testing "sed -g (exhaustive)" "sed -e 's/[[:space:]]*/,/g'" ",1,2,3,4,5," \
+#	"" "12345"
+
+# 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'" \
+	"0\nx\n2\ny\n" "" "0\n1\n2\n3\n"
+
+testing "sed d does not break n,m matching" \
+	"sed -n '1d;1,3p'" \
+	"second\nthird\n" "" "first\nsecond\nthird\nfourth\n"
+
+testing "sed d does not break n,regex matching" \
+	"sed -n '1d;1,/hir/p'" \
+	"second\nthird\n" "" "first\nsecond\nthird\nfourth\n"
+
+testing "sed d does not break n,regex matching #2" \
+	"sed -n '1,5d;1,/hir/p'" \
+	"second2\nthird2\n" "" \
+	"first\nsecond\nthird\nfourth\n""first2\nsecond2\nthird2\nfourth2\n"
+
+testing "sed 2d;2,1p (gnu compat)" \
+	"sed -n '2d;2,1p'" \
+	"third\n" "" \
+	"first\nsecond\nthird\nfourth\n"
+
+# Regex means: "match / at BOL or nothing, then one or more not-slashes".
+# The bug was that second slash in /usr/lib was treated as "at BOL" too.
+testing "sed beginning (^) matches only once" \
+	"sed 's,\(^/\|\)[^/][^/]*,>\0<,g'" \
+	">/usr</>lib<\n" "" \
+	"/usr/lib\n"
+
+testing "sed c" \
+	"sed 'crepl'" \
+	"repl\nrepl\n" "" \
+	"first\nsecond\n"
+
+testing "sed nested {}s" \
+	"sed '/asd/ { p; /s/ { s/s/c/ }; p; q }'" \
+	"qwe\nasd\nacd\nacd\n" "" \
+	"qwe\nasd\nzxc\n"
+
+testing "sed a cmd ended by double backslash" \
+	"sed -e '/| one /a \\
+	| three \\\\' -e '/| one-/a \\
+	| three-* \\\\'" \
+'	| one \\
+	| three \\
+	| two \\
+' '' \
+'	| one \\
+	| two \\
+'
+
+# 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 "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 "description" "commands" "result" "infile" "stdin"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/seq.tests b/busybox-1.19.3/testsuite/seq.tests
new file mode 100755
index 0000000..1e1116c
--- /dev/null
+++ b/busybox-1.19.3/testsuite/seq.tests
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+# SUSv3 compliant seq tests.
+# Copyright 2006 by Rob Landley <rob@landley.net>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+# AUDIT: Full SUSv3 coverage (except internationalization).
+
+. ./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
+
+# Test exit status
+
+testing "seq (exit with error)" "seq 2> /dev/null || echo yes" "yes\n" "" ""
+testing "seq (exit with error)" "seq 1 2 3 4 2> /dev/null || echo yes" \
+	"yes\n" "" ""
+testing "seq one argument" "seq 3" "1\n2\n3\n" "" ""
+testing "seq two arguments" "seq 5 7" "5\n6\n7\n" "" ""
+testing "seq two arguments reversed" "seq 7 5" "" "" ""
+testing "seq two arguments equal" "seq 3 3" "3\n" "" ""
+testing "seq two arguments equal, arbitrary negative step" "seq 1 -15 1" \
+	"1\n" "" ""
+testing "seq two arguments equal, arbitrary positive step" "seq 1 +15 1" \
+	"1\n" "" ""
+testing "seq count up by 2" "seq 4 2 8" "4\n6\n8\n" "" ""
+testing "seq count down by 2" "seq 8 -2 4" "8\n6\n4\n" "" ""
+testing "seq count wrong way #1" "seq 4 -2 8" "" "" ""
+testing "seq count wrong way #2" "seq 8 2 4" "" "" ""
+testing "seq count by .3" "seq 3 .3 4" "3.0\n3.3\n3.6\n3.9\n" "" ""
+testing "seq count by .30" "seq 3 .30 4" "3.00\n3.30\n3.60\n3.90\n" "" ""
+testing "seq count by .30 to 4.000" "seq 3 .30 4.000" "3.00\n3.30\n3.60\n3.90\n" "" ""
+testing "seq count by -.9" "seq .7 -.9 -2.2" "0.7\n-0.2\n-1.1\n-2.0\n" "" ""
+testing "seq count by zero" "seq 4 0 8 | head -n 10" "4\n4\n4\n4\n4\n4\n4\n4\n4\n4\n" "" ""
+
+testing "seq one argument with padding" "seq -w 003" "001\n002\n003\n" "" ""
+testing "seq two arguments with padding" "seq -w 005 7" "005\n006\n007\n" "" ""
+testing "seq count down by 3 with padding" "seq -w 8 -3 04" "08\n05\n" "" ""
+# Looks like a bug in coreutils 6.10: it uses width one less than needed
+# These tests contain the expected "fixed" output
+testing "seq count by .3 with padding 1" "seq -w 09 .3 11" "09.0\n09.3\n09.6\n09.9\n10.2\n10.5\n10.8\n" "" ""
+testing "seq count by .3 with padding 2" "seq -w 03 .3 0004" "0003.0\n0003.3\n0003.6\n0003.9\n" "" ""
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/sha1sum.tests b/busybox-1.19.3/testsuite/sha1sum.tests
new file mode 100755
index 0000000..a968fa8
--- /dev/null
+++ b/busybox-1.19.3/testsuite/sha1sum.tests
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./md5sum.tests sha1sum d41337e834377140ae7f98460d71d908598ef04f
diff --git a/busybox-1.19.3/testsuite/sha256sum.tests b/busybox-1.19.3/testsuite/sha256sum.tests
new file mode 100755
index 0000000..d2dd78f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/sha256sum.tests
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./md5sum.tests sha256sum 8e1d3ed57ebc130f0f72508446559eeae06451ae6d61b1e8ce46370cfb8963c3
diff --git a/busybox-1.19.3/testsuite/sha512sum.tests b/busybox-1.19.3/testsuite/sha512sum.tests
new file mode 100755
index 0000000..3bc7cae
--- /dev/null
+++ b/busybox-1.19.3/testsuite/sha512sum.tests
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+. ./md5sum.tests sha512sum fe413e0f177324d1353893ca0772ceba83fd41512ba63895a0eebb703ef9feac2fb4e92b2cb430b3bda41b46b0cb4ea8307190a5cc795157cfb680a9cd635d0f
diff --git a/busybox-1.19.3/testsuite/sort.tests b/busybox-1.19.3/testsuite/sort.tests
new file mode 100755
index 0000000..91b282e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/sort.tests
@@ -0,0 +1,131 @@
+#!/bin/sh
+
+# SUSv3 compliant sort tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# The basic tests.  These should work even with the small busybox.
+
+testing "sort" "sort input" "a\nb\nc\n" "c\na\nb\n" ""
+testing "sort #2" "sort input" "010\n1\n3\n" "3\n1\n010\n" ""
+testing "sort stdin" "sort" "a\nb\nc\n" "" "b\na\nc\n"
+testing "sort numeric" "sort -n input" "1\n3\n010\n" "3\n1\n010\n" ""
+testing "sort reverse" "sort -r input" "wook\nwalrus\npoint\npabst\naargh\n" \
+	"point\nwook\npabst\naargh\nwalrus\n" ""
+
+# These tests require the full option set.
+
+optional FEATURE_SORT_BIG
+# Longish chunk of data re-used by the next few tests
+
+data="42	1	3	woot
+42	1	010	zoology
+egg	1	2	papyrus
+7	3	42	soup
+999	3	0	algebra
+"
+
+# testing "description" "command(s)" "result" "infile" "stdin"
+
+# Sorting with keys
+
+testing "sort one key" "sort -k4,4 input" \
+"999	3	0	algebra
+egg	1	2	papyrus
+7	3	42	soup
+42	1	3	woot
+42	1	010	zoology
+" "$data" ""
+
+testing "sort key range with numeric option" "sort -k2,3n input" \
+"42	1	010	zoology
+42	1	3	woot
+egg	1	2	papyrus
+7	3	42	soup
+999	3	0	algebra
+" "$data" ""
+
+test x"$SKIP_KNOWN_BUGS" = x"" && {
+# Busybox is definitely doing these wrong.  FIXME
+testing "sort key range with numeric option and global reverse" \
+"sort -k2,3n -r input" \
+"egg	1	2	papyrus
+42	1	3	woot
+42	1	010	zoology
+999	3	0	algebra
+7	3	42	soup
+" "$data" ""
+
+testing "sort key range with multiple options" "sort -k2,3rn input" \
+"7	3	42	soup
+999	3	0	algebra
+42	1	010	zoology
+42	1	3	woot
+egg	1	2	papyrus
+" "$data" ""
+}
+
+testing "sort key range with two -k options" "sort -k 2,2n -k 1,1r input" "\
+d 2
+b 2
+c 3
+" "\
+c 3
+b 2
+d 2
+" ""
+
+testing "sort with non-default leading delim 1" "sort -n -k2 -t/ input" "\
+/a/2
+/b/1
+" "\
+/a/2
+/b/1
+" ""
+
+testing "sort with non-default leading delim 2" "sort -n -k3 -t/ input" "\
+/b/1
+/a/2
+" "\
+/b/1
+/a/2
+" ""
+
+testing "sort with non-default leading delim 3" "sort -n -k3 -t/ input" "\
+//a/2
+//b/1
+" "\
+//a/2
+//b/1
+" ""
+
+testing "sort -u should consider field only when discarding" "sort -u -k2 input" "\
+a c
+" "\
+a c
+b c
+" ""
+
+testing "sort -z outputs NUL terminated lines" "sort -z input" "\
+one\0three\0two\0\
+" "\
+one\0two\0three\0\
+" ""
+
+testing "sort key doesn't strip leading blanks, disables fallback global sort" \
+"sort -n -k2 -t ' '" " a \n 1 \n 2 \n" "" " 2 \n 1 \n a \n"
+
+testing "sort file in place" \
+"sort -o input input && cat input" "\
+111
+222
+" "\
+222
+111
+" ""
+
+# testing "description" "command(s)" "result" "infile" "stdin"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/start-stop-daemon.tests b/busybox-1.19.3/testsuite/start-stop-daemon.tests
new file mode 100755
index 0000000..d07aeef
--- /dev/null
+++ b/busybox-1.19.3/testsuite/start-stop-daemon.tests
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "cmd" "expected result" "file input" "stdin"
+
+testing "start-stop-daemon -x without -a" \
+	'start-stop-daemon -S -x true 2>&1; echo $?' \
+	"0\n" \
+	"" ""
+
+testing "start-stop-daemon -a without -x" \
+	'start-stop-daemon -S -a false 2>&1; echo $?' \
+	"1\n" \
+	"" ""
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/strings/strings-works-like-GNU b/busybox-1.19.3/testsuite/strings/strings-works-like-GNU
new file mode 100644
index 0000000..2d64710
--- /dev/null
+++ b/busybox-1.19.3/testsuite/strings/strings-works-like-GNU
@@ -0,0 +1,9 @@
+rm -f foo bar
+strings -af ../../busybox > foo
+busybox strings -af ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
diff --git a/busybox-1.19.3/testsuite/sum.tests b/busybox-1.19.3/testsuite/sum.tests
new file mode 100755
index 0000000..b9f4cbf
--- /dev/null
+++ b/busybox-1.19.3/testsuite/sum.tests
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# unit test for sum.
+# Copyright 2007 by Bernhard Reutner-Fischer
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
+
+# AUDIT: Unit tests for sum
+
+. ./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 "sum -r file doesn't print file's name" \
+        "sum -r $0 | grep -c $0 && echo wrongly_printed_filename || echo yes" \
+	"0\nyes\n" "" ""
+testing "sum -r file file does print both names" \
+        "sum -r $0 $0 | grep -c $0 && echo yes || echo wrongly_omitted_filename" \
+	"2\nyes\n" "" ""
+testing "sum -s file does print file's name" \
+        "sum -s $0 | grep -c $0 && echo yes || echo wrongly_omitted_filename" \
+	"1\nyes\n" "" ""
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/tail.tests b/busybox-1.19.3/testsuite/tail.tests
new file mode 100755
index 0000000..305a83b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tail.tests
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+# Copyright 2009 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+#   file input will be file called "input"
+#   test can create a file "actual" instead of writing to stdout
+
+testing "tail: +N with N > file length" \
+	"tail -c +55 2>&1; echo \$?" \
+	"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
diff --git a/busybox-1.19.3/testsuite/tail/tail-n-works b/busybox-1.19.3/testsuite/tail/tail-n-works
new file mode 100644
index 0000000..0e1319f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tail/tail-n-works
@@ -0,0 +1,4 @@
+$ECHO -ne "abc\ndef\n123\n" >input
+$ECHO -ne "def\n123\n" >logfile.ok
+busybox tail -n 2 input > logfile.bb
+cmp logfile.ok logfile.bb
diff --git a/busybox-1.19.3/testsuite/tail/tail-works b/busybox-1.19.3/testsuite/tail/tail-works
new file mode 100644
index 0000000..50eca13
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tail/tail-works
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_INCLUDE_SUSv2
+
+$ECHO -ne "abc\ndef\n123\n" >input
+$ECHO -ne "def\n123\n" >logfile.ok
+busybox tail -2 input > logfile.bb
+cmp logfile.ok logfile.bb
diff --git a/busybox-1.19.3/testsuite/tar.tests b/busybox-1.19.3/testsuite/tar.tests
new file mode 100755
index 0000000..39ece5f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar.tests
@@ -0,0 +1,195 @@
+#!/bin/sh
+# Copyright 2009 by Denys Vlasenko
+# 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"
+
+optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
+testing "tar hardlinks and repeated files" '\
+rm -rf input_* test.tar 2>/dev/null
+>input_hard1
+ln input_hard1 input_hard2
+mkdir input_dir
+>input_dir/file
+chmod -R 644 *
+chmod    755 input_dir
+tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input
+tar tvf test.tar | sed "s/.*[0-9] input/input/"
+tar xf test.tar 2>&1
+echo Ok: $?
+ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+' "\
+input
+input_dir/
+input_dir/file
+input_hard1
+input_hard2 -> input_hard1
+input_hard1 -> input_hard1
+input_dir/
+input_dir/file
+input
+Ok: 0
+-rw-r--r-- input_dir/file
+drwxr-xr-x input_dir
+-rw-r--r-- input_hard1
+-rw-r--r-- input_hard2
+" \
+"" ""
+SKIP=
+
+optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
+testing "tar hardlinks mode" '\
+rm -rf input_* test.tar 2>/dev/null
+>input_hard1
+chmod 741 input_hard1
+ln input_hard1 input_hard2
+mkdir input_dir
+ln input_hard1 input_dir
+ln input_hard2 input_dir
+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.*hard" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+' "\
+input_dir/input_hard1
+input_dir/input_hard2 -> input_dir/input_hard1
+input_hard1 -> input_dir/input_hard1
+input_hard2 -> input_dir/input_hard1
+Ok: 0
+-rwxr----x input_dir/input_hard1
+-rwxr----x input_dir/input_hard2
+-rwxr----x input_hard1
+-rwxr----x input_hard2
+" \
+"" ""
+SKIP=
+
+optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES
+testing "tar symlinks mode" '\
+rm -rf input_* test.tar 2>/dev/null
+>input_file
+chmod 741 input_file
+ln -s input_file input_soft
+mkdir input_dir
+ln input_file input_dir
+ln input_soft input_dir
+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_[fs]" | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+' "\
+input_dir/input_file
+input_dir/input_soft -> input_file
+input_file -> input_dir/input_file
+input_soft -> input_dir/input_soft
+Ok: 0
+-rwxr----x input_dir/input_file
+lrwxrwxrwx input_file
+-rwxr----x input_file
+lrwxrwxrwx input_file
+" \
+"" ""
+SKIP=
+
+optional FEATURE_TAR_CREATE FEATURE_TAR_LONG_OPTIONS
+testing "tar --overwrite" "\
+rm -rf input_* test.tar 2>/dev/null
+ln input input_hard
+tar cf test.tar input_hard
+echo WRONG >input
+# --overwrite opens 'input_hard' without unlinking,
+# thus 'input_hard' still linked to 'input' and we write 'Ok' into it
+tar xf test.tar --overwrite 2>&1 && cat input
+" "\
+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=
+
+# 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
diff --git a/busybox-1.19.3/testsuite/tar/tar-archives-multiple-files b/busybox-1.19.3/testsuite/tar/tar-archives-multiple-files
new file mode 100644
index 0000000..245d9e9
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-archives-multiple-files
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo bar
+busybox tar cf foo.tar foo bar
+rm foo bar
+tar xf foo.tar
+test -f foo -a -f bar
diff --git a/busybox-1.19.3/testsuite/tar/tar-complains-about-missing-file b/busybox-1.19.3/testsuite/tar/tar-complains-about-missing-file
new file mode 100644
index 0000000..26e8cbb
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-complains-about-missing-file
@@ -0,0 +1,3 @@
+touch foo
+tar cf foo.tar foo
+! busybox tar xf foo.tar bar
diff --git a/busybox-1.19.3/testsuite/tar/tar-demands-at-least-one-ctx b/busybox-1.19.3/testsuite/tar/tar-demands-at-least-one-ctx
new file mode 100644
index 0000000..85e7f60
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-demands-at-least-one-ctx
@@ -0,0 +1 @@
+! busybox tar v
diff --git a/busybox-1.19.3/testsuite/tar/tar-demands-at-most-one-ctx b/busybox-1.19.3/testsuite/tar/tar-demands-at-most-one-ctx
new file mode 100644
index 0000000..130d0e7
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-demands-at-most-one-ctx
@@ -0,0 +1 @@
+! busybox tar tx
diff --git a/busybox-1.19.3/testsuite/tar/tar-extracts-all-subdirs b/busybox-1.19.3/testsuite/tar/tar-extracts-all-subdirs
new file mode 100644
index 0000000..f5351f4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-extracts-all-subdirs
@@ -0,0 +1,12 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+mkdir -p foo/{1,2,3}
+mkdir -p foo/1/{10,11}
+mkdir -p foo/1/10/{100,101,102}
+tar cf foo.tar -C foo .
+rm -rf foo/*
+busybox tar xf foo.tar -C foo ./1/10
+find foo | sort >logfile.bb
+rm -rf foo/*
+tar xf foo.tar -C foo ./1/10
+find foo | sort >logfile.gnu
+diff -u logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/tar/tar-extracts-file b/busybox-1.19.3/testsuite/tar/tar-extracts-file
new file mode 100644
index 0000000..ca72f24
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-extracts-file
@@ -0,0 +1,5 @@
+touch foo
+tar cf foo.tar foo
+rm foo
+busybox tar xf foo.tar
+test -f foo
diff --git a/busybox-1.19.3/testsuite/tar/tar-extracts-from-standard-input b/busybox-1.19.3/testsuite/tar/tar-extracts-from-standard-input
new file mode 100644
index 0000000..a30e9f0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-extracts-from-standard-input
@@ -0,0 +1,5 @@
+touch foo
+tar cf foo.tar foo
+rm foo
+cat foo.tar | busybox tar x
+test -f foo
diff --git a/busybox-1.19.3/testsuite/tar/tar-extracts-multiple-files b/busybox-1.19.3/testsuite/tar/tar-extracts-multiple-files
new file mode 100644
index 0000000..7897d81
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-extracts-multiple-files
@@ -0,0 +1,6 @@
+touch foo bar
+tar cf foo.tar foo bar
+rm foo bar
+busybox tar -xf foo.tar
+test -f foo
+test -f bar
diff --git a/busybox-1.19.3/testsuite/tar/tar-extracts-to-standard-output b/busybox-1.19.3/testsuite/tar/tar-extracts-to-standard-output
new file mode 100644
index 0000000..ca48e36
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-extracts-to-standard-output
@@ -0,0 +1,3 @@
+echo foo > foo
+tar cf foo.tar foo
+cat foo.tar | busybox tar Ox | cmp foo -
diff --git a/busybox-1.19.3/testsuite/tar/tar-handles-cz-options b/busybox-1.19.3/testsuite/tar/tar-handles-cz-options
new file mode 100644
index 0000000..95c628d
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-handles-cz-options
@@ -0,0 +1,5 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+# FEATURE: CONFIG_FEATURE_SEAMLESS_GZ
+touch foo
+busybox tar czf foo.tar.gz foo
+gzip -d foo.tar.gz
diff --git a/busybox-1.19.3/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list b/busybox-1.19.3/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list
new file mode 100644
index 0000000..5033642
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-handles-empty-include-and-non-empty-exclude-list
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo
+tar cf foo.tar foo
+echo foo >foo.exclude
+busybox tar xf foo.tar -X foo.exclude
diff --git a/busybox-1.19.3/testsuite/tar/tar-handles-exclude-and-extract-lists b/busybox-1.19.3/testsuite/tar/tar-handles-exclude-and-extract-lists
new file mode 100644
index 0000000..2de0f0e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-handles-exclude-and-extract-lists
@@ -0,0 +1,8 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo bar baz
+tar cf foo.tar foo bar baz
+echo foo >foo.exclude
+rm foo bar baz
+busybox tar xf foo.tar foo bar -X foo.exclude
+test ! -f foo -a -f bar -a ! -f baz
diff --git a/busybox-1.19.3/testsuite/tar/tar-handles-multiple-X-options b/busybox-1.19.3/testsuite/tar/tar-handles-multiple-X-options
new file mode 100644
index 0000000..155b27e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-handles-multiple-X-options
@@ -0,0 +1,10 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+touch foo
+touch bar
+tar cf foo.tar foo bar
+echo foo > foo.exclude
+echo bar > bar.exclude
+rm foo bar
+busybox tar xf foo.tar -X foo.exclude -X bar.exclude
+test ! -f foo -a ! -f bar
diff --git a/busybox-1.19.3/testsuite/tar/tar-handles-nested-exclude b/busybox-1.19.3/testsuite/tar/tar-handles-nested-exclude
new file mode 100644
index 0000000..39013a1
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar-handles-nested-exclude
@@ -0,0 +1,9 @@
+# FEATURE: CONFIG_FEATURE_TAR_FROM
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+mkdir foo
+touch foo/bar
+tar cf foo.tar foo
+rm -rf foo
+echo foo/bar >foobar.exclude
+busybox tar xf foo.tar foo -X foobar.exclude
+test -d foo -a ! -f foo/bar
diff --git a/busybox-1.19.3/testsuite/tar/tar_with_link_with_size b/busybox-1.19.3/testsuite/tar/tar_with_link_with_size
new file mode 100644
index 0000000..774cd56
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar_with_link_with_size
@@ -0,0 +1,31 @@
+# FEATURE: CONFIG_FEATURE_TAR_UNAME_GNAME
+
+# This tarball contains a softlink with size field != 0.
+# If not ignored, it makes hext header to be skipped
+# and data to be read as a header.
+# GNU tar 1.15.1 has a bug here: tf won't work, but xf will.
+tar1_bz2()
+{
+    $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x14\x44\xe3\xdd\x00\x00"
+    $ECHO -ne "\x9a\xfb\x90\xca\x18\x00\xc0\x40\x03\xff\x80\x08\x00\x7b\xe3\xff"
+    $ECHO -ne "\x80\x04\x00\x00\x08\x30\x00\xd6\xb3\x09\x45\x19\x0d\x0d\x41\x84"
+    $ECHO -ne "\x1a\x68\xd0\x7a\x99\x90\x4a\x0a\x6d\x4c\xa3\x20\x7a\x41\xa0\x00"
+    $ECHO -ne "\x00\x55\x25\x34\x1a\x34\xd0\x00\x64\x64\x1a\x32\x3f\x76\x3c\x1c"
+    $ECHO -ne "\xd3\x3c\xa0\x84\x9b\x88\x05\x70\x90\xbb\x18\x28\x39\x29\xb3\x30"
+    $ECHO -ne "\xa8\x0a\x21\x70\x0c\x01\x32\x3b\xbe\xde\xd7\x13\x2e\xbd\x2a\x9c"
+    $ECHO -ne "\xa8\x42\x2a\x91\x15\xe2\xa1\xcd\x24\x37\x9c\x91\xaa\xc7\x14\xdb"
+    $ECHO -ne "\x4c\x08\xaa\xaf\x12\xeb\x6c\x37\x96\xb0\xa4\x25\x0c\xb4\x4b\xc5"
+    $ECHO -ne "\x52\x70\x3b\x25\x4c\x0e\x46\x67\x51\x54\x89\x13\x13\xf0\xa8\xe9"
+    $ECHO -ne "\x68\x4e\x8c\x81\xfc\x79\xe0\xb0\xd8\x79\x34\x94\x71\xa2\x0c\xbe"
+    $ECHO -ne "\x93\x61\x82\x95\x10\x88\xd1\xa6\x69\xaa\x38\x9c\xb6\xc2\xb2\x94"
+    $ECHO -ne "\x90\xc3\x82\x29\xe8\x8c\xb8\x95\x83\x32\x40\x61\x11\x11\xd3\xaa"
+    $ECHO -ne "\x3f\x8b\xb9\x22\x9c\x28\x48\x0a\x22\x71\xee\x80"
+}
+res1="\
+lrwxrwxrwx user/group         0 2008-07-19 15:02:37 firmware-372/sources/native/bin/chroot-setup.sh -> qemu-setup.sh
+-rwxr-xr-x user/group       512 2008-07-19 15:02:37 firmware-372/sources/native/bin/qemu-setup.sh"
+
+export TZ=UTC-2
+
+t=`tar1_bz2 | bunzip2 | busybox tar tvf -`
+test x"$res1" = x"$t"
diff --git a/busybox-1.19.3/testsuite/tar/tar_with_prefix_fields b/busybox-1.19.3/testsuite/tar/tar_with_prefix_fields
new file mode 100644
index 0000000..8c1fda7
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tar/tar_with_prefix_fields
@@ -0,0 +1,264 @@
+# FEATURE: CONFIG_FEATURE_TAR_UNAME_GNAME
+# FEATURE: CONFIG_DESKTOP
+
+tar1_bz2()
+{
+    $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x12\xd1\x86\x30\x00\x0c"
+    $ECHO -ne "\xb8\x7f\x80\xff\x50\x08\xa0\x5e\xff\xff\xfd\x7f\xff\xff\xee\xff"
+    $ECHO -ne "\xff\xff\xfa\x00\x08\x60\x0f\xc5\x3e\xf4\xdc\x00\x00\x59\x25\xbd"
+    $ECHO -ne "\xb8\x7a\x02\xb5\x82\x78\x25\xb0\x89\x54\x10\x11\x44\x8b\x36\x36"
+    $ECHO -ne "\xc8\x97\x0d\x34\x9a\x21\xa9\x36\xa9\xed\x32\x02\x8d\xa6\x81\x8a"
+    $ECHO -ne "\x79\x13\x4d\x1a\x03\x10\x69\xa0\xd3\x40\x64\x0f\x44\x68\x3d\x41"
+    $ECHO -ne "\x2a\x7a\x20\x09\xa1\x34\x9a\x09\xa4\xc8\xf5\x4f\x46\xa6\x86\x32"
+    $ECHO -ne "\x4c\x08\x00\x00\xd0\x06\x9a\x00\xd3\xd4\x11\x49\xa7\xb5\x20\x1a"
+    $ECHO -ne "\x7a\x80\x00\x00\x00\x00\xd0\x68\x00\x00\x00\x00\x00\x49\xa8\x89"
+    $ECHO -ne "\xa9\x31\x4f\x22\xa7\xea\x9b\x61\x53\xf4\x93\xf2\xa3\x47\xa4\xd3"
+    $ECHO -ne "\x41\xea\x06\x41\xa0\x0d\x00\x00\x00\x1e\xa0\x70\x34\xd3\x4d\x06"
+    $ECHO -ne "\x86\x86\x86\x46\x80\x64\x01\xa1\xa0\x34\xd1\x90\x00\x03\x09\x88"
+    $ECHO -ne "\x0d\x04\x89\x08\x00\x82\x7a\x4c\x42\x64\xc9\x3d\x1a\x29\xe9\xa2"
+    $ECHO -ne "\x3d\x46\x9e\x46\x9a\x13\x26\x9e\x53\x10\x01\x91\xea\x68\x19\xf0"
+    $ECHO -ne "\x73\xf2\xe0\xd1\x3c\x80\x01\xb1\x48\x44\x08\x9a\xba\xf3\x9e\x87"
+    $ECHO -ne "\xec\xc4\x4b\x02\x92\x80\x75\x00\x56\x42\x88\x10\x68\xcc\x06\x22"
+    $ECHO -ne "\x7c\x2b\xa7\xc8\x21\x91\x13\xe5\x72\xc0\xe6\x0c\x03\x10\xf2\x89"
+    $ECHO -ne "\x9c\x67\x6b\xc3\xe6\xae\x98\x85\x0a\x7f\x25\x2e\x3d\x84\x5b\xeb"
+    $ECHO -ne "\xf3\xff\xb3\x52\xf7\x6e\xf6\x92\xd6\x33\x5f\x4f\xd1\x3d\xb7\xc4"
+    $ECHO -ne "\x0d\x50\x02\x49\x01\xaf\xd0\x69\xbb\xd3\xe9\x63\x0a\x68\x36\x92"
+    $ECHO -ne "\xf2\x03\x1d\xf2\xe2\x35\xbc\x73\xd4\x44\xf6\xa0\xe0\x31\xd7\x7d"
+    $ECHO -ne "\x56\x96\xcb\x52\xfc\x79\xe0\xeb\xf7\x34\xd8\xda\x18\x72\x30\x94"
+    $ECHO -ne "\x53\x45\xf5\x54\x56\x6c\x0b\x50\xa0\xbc\xbd\xcc\xd8\x21\xab\x7b"
+    $ECHO -ne "\xa8\xa4\xe4\x78\x25\x73\xbf\x4b\x30\x38\x71\xe9\x3c\x14\x5d\xa3"
+    $ECHO -ne "\x12\x04\x6b\x37\x9d\xe5\xce\xa5\xd9\xd1\xa5\x69\x09\x08\xc4\x48"
+    $ECHO -ne "\x4b\x34\x58\x81\x15\x18\x88\xac\x11\x51\x88\x35\x0d\xd3\x13\x18"
+    $ECHO -ne "\x67\x73\x20\x5c\x28\x03\x26\xcd\x6d\x20\x90\xba\xa4\x12\xb3\x08"
+    $ECHO -ne "\x27\x74\x6a\x99\xdf\xb1\x20\x3d\x85\xe7\x5f\xab\x0e\x2e\xdc\x23"
+    $ECHO -ne "\x99\xe1\xef\x34\x68\xcd\xa9\xb0\xbf\xda\xec\x81\xdd\x66\xca\x21"
+    $ECHO -ne "\x13\x47\xd7\xca\x48\xcf\xeb\x25\xbb\x79\x6d\x40\xd0\xe4\x69\x3c"
+    $ECHO -ne "\x8f\x09\x1e\x7b\xaa\x4b\x91\x39\xac\xd6\xd2\x0c\x85\x1d\xf7\x70"
+    $ECHO -ne "\x1f\x1e\x58\xbb\x22\x11\x29\x39\x14\x4d\x58\x81\x9f\xd7\x1e\x22"
+    $ECHO -ne "\x21\x91\x0a\x40\xd1\x87\x29\x99\x93\xf4\xf3\x25\x48\xbb\xb4\x24"
+    $ECHO -ne "\x2a\x1c\xa7\x28\xc1\x68\x08\x25\x00\xaa\x3d\xee\xae\xc1\xe1\x4f"
+    $ECHO -ne "\xe6\x9a\x26\x6b\xcf\xb1\x3e\xb9\x85\x04\xf4\xef\xff\x7a\x2f\x2a"
+    $ECHO -ne "\x04\x08\xe0\x4c\xb8\xbd\x8b\x81\xbf\xa2\xbe\x82\x52\x9b\x40\x63"
+    $ECHO -ne "\xe6\xf3\xb3\xe4\xe6\xe5\x94\x4a\xdd\xc3\x1b\xaf\x61\xf3\xbf\x5b"
+    $ECHO -ne "\x6d\xaa\xaa\x27\xe8\x50\x8d\x23\x97\xa4\xbd\xc3\xd2\xe6\xb5\x66"
+    $ECHO -ne "\x9a\x1a\x8e\x45\x2a\xed\x0b\x79\xb8\x89\x38\x4a\x04\x85\x0d\x1e"
+    $ECHO -ne "\x2b\x77\x51\x91\x5f\x9f\xe0\x2a\x49\x56\xd3\xa1\xde\xf6\xd7\x88"
+    $ECHO -ne "\x5a\x61\xe5\x04\x54\xdf\xa3\x92\xeb\xbf\x75\x39\xce\xfa\xf5\xde"
+    $ECHO -ne "\x30\xd7\x56\xd1\x7d\x2c\xdf\xda\x3e\x1c\xc8\xc2\x93\x61\x21\x20"
+    $ECHO -ne "\xb2\x22\x6d\xbe\x39\x52\x64\xf6\xb3\x91\x21\x86\xdb\x67\x72\x8f"
+    $ECHO -ne "\x49\xad\xe4\x93\x39\x5c\x34\x8f\x58\xdb\x58\xd3\x3c\x1e\x4c\x6c"
+    $ECHO -ne "\xbb\x70\x6f\x42\xcf\x9e\xbf\xb1\xcb\xa9\x8d\x05\xe7\xea\xea\xd7"
+    $ECHO -ne "\x3c\x67\x31\x69\x44\x33\xa4\x92\x9c\x65\xa4\x89\x5a\xae\xcf\xc9"
+    $ECHO -ne "\x55\x43\x62\x6d\xbf\x05\x3c\xd1\x0f\x01\x4a\xb5\x1d\xbb\x2c\xfb"
+    $ECHO -ne "\xa6\xb7\xb3\xb1\x1d\x66\xd3\xeb\x22\xd0\xb5\x5a\x4b\xc4\x47\x47"
+    $ECHO -ne "\x5a\x49\x85\x18\xbc\x15\x39\x3b\x92\xee\x51\x98\x33\x34\x5d\xb5"
+    $ECHO -ne "\xbb\x8b\x94\x8c\xde\x8e\x3f\x3d\x09\x4f\xba\xd3\xf6\x79\x74\x8e"
+    $ECHO -ne "\x82\x0d\x56\x85\xa2\xc7\xc6\xa6\x89\x29\x26\xa3\x53\x5e\x52\xf5"
+    $ECHO -ne "\x56\x74\x8b\x17\x82\xed\x7a\x8b\x68\x61\xa5\xc9\x7c\xde\x9f\x68"
+    $ECHO -ne "\x27\x4d\xea\x65\x68\x6f\x7d\x5e\x88\x73\x87\x6c\x92\xf2\xa9\x15"
+    $ECHO -ne "\x4e\xee\x4d\x41\xbb\x98\x5d\x8a\xaf\xcb\x11\x7b\x2a\xce\xf4\x1e"
+    $ECHO -ne "\x3a\x28\x48\x14\xfe\x7f\x09\x45\x48\xf1\x5b\xc1\xcb\xcd\x91\xba"
+    $ECHO -ne "\x3b\xe2\x7d\x57\x85\x66\x68\xec\x51\x82\x97\x88\xeb\x94\x3b\x78"
+    $ECHO -ne "\x6c\xf4\xf1\x3e\x38\x8d\x22\x16\xab\x3b\x13\xb3\x1b\x39\x94\x0e"
+    $ECHO -ne "\xa8\x26\xb7\x8d\xe9\x7d\x66\x23\x4b\x65\x07\xb7\x2b\xc9\x96\xb6"
+    $ECHO -ne "\x99\x12\x22\xbc\x90\xda\x51\xbc\xfd\x97\xa5\x7d\xbc\x12\xa6\x72"
+    $ECHO -ne "\xd3\xe3\x8c\xc7\x58\xe1\xf8\x28\xf4\x46\x49\x14\xd0\x9d\xb6\xed"
+    $ECHO -ne "\xce\x99\xc6\xbc\xed\xa3\xab\xa0\x8c\x9d\xce\x1a\x1a\xc2\xe6\x77"
+    $ECHO -ne "\xba\xae\xba\xd6\xc9\xb2\xd1\x65\x24\x7b\x0d\xd4\xf2\xac\x28\xc3"
+    $ECHO -ne "\x1c\xbe\x4a\x54\xe3\x0f\x8d\xad\xb2\x37\x9e\x1f\x81\x72\x2d\xab"
+    $ECHO -ne "\x8f\xb1\xcd\xf7\xb4\x51\x2f\x1d\xf8\xad\x77\x14\x37\xd2\x1a\x9a"
+    $ECHO -ne "\xc0\xf2\x48\xc6\x4c\x8d\xd3\x8d\xf1\xd9\x2e\x2c\xdd\x7a\x98\x3c"
+    $ECHO -ne "\x24\x76\xb9\x9d\x27\xcd\x71\x7d\x6c\xc7\x1f\x0a\x74\x8a\x6e\x54"
+    $ECHO -ne "\xec\x5a\xa1\x77\x60\x80\xef\x00\xa4\x5f\x9e\x8b\x2f\x02\x72\x9c"
+    $ECHO -ne "\x46\xd8\x79\x92\x4c\x8f\x4e\x37\xed\x0c\x58\xab\x44\xee\x1d\xd1"
+    $ECHO -ne "\xa1\xb0\xa5\x1f\xaf\xb0\x39\x01\x26\xb2\x4a\x20\x68\x4a\x18\x23"
+    $ECHO -ne "\xc3\x03\x84\x22\x18\xdb\x6d\x83\x60\xc1\x12\x09\x21\x84\x22\x48"
+    $ECHO -ne "\x7f\x1e\x17\xf5\xbe\xce\x4c\x4f\x9f\x9f\xee\xf4\xfe\xef\x9a\x34"
+    $ECHO -ne "\x91\x8f\x36\x1d\xbc\x73\xd7\xeb\xc8\x2e\x81\x25\xfa\x18\x76\x35"
+    $ECHO -ne "\x1f\x16\xdb\x20\x4b\x74\x6d\x94\x4e\xe5\x36\xed\xf5\x5d\x59\xaf"
+    $ECHO -ne "\x46\x70\xea\x03\xac\x50\xbb\x26\xab\x39\x9a\x4b\x6b\x09\x8c\x6d"
+    $ECHO -ne "\x34\xcf\xed\xaa\xf7\x56\x40\xf2\xab\x07\xca\x22\x71\x97\xc7\x35"
+    $ECHO -ne "\xe8\x06\x90\x7b\xec\xc3\x9f\xa4\xde\xd9\xdb\x43\xf1\xd5\x06\x58"
+    $ECHO -ne "\x72\x9e\x1f\x08\xb6\xc2\x05\x0d\x25\xfe\x7a\x85\xe5\x10\x12\x68"
+    $ECHO -ne "\x18\x7e\x8c\xa0\xfa\xb4\xc4\xc7\x4e\xa9\xf2\x13\xd7\xc2\x52\xb5"
+    $ECHO -ne "\xe3\x72\x37\x31\x1e\x4f\x99\xfd\xac\x97\x08\x88\x71\x88\xeb\x1a"
+    $ECHO -ne "\xf9\xa1\x10\x9c\x44\x08\x56\x4a\x77\xaa\x0f\x19\x5f\x5f\xb3\x95"
+    $ECHO -ne "\xee\x9b\x9f\x5b\xb5\xc9\x0a\xf4\x28\x16\x25\x34\x6c\x72\xda\x92"
+    $ECHO -ne "\xb4\x2c\xbd\x5e\xb1\xe8\xe5\x0f\x68\xf3\x44\x8a\xd5\xfa\x73\x5c"
+    $ECHO -ne "\x89\x2e\x99\x7d\xed\xe3\x5b\x3f\x48\x97\xeb\xb6\x76\x5c\xa5\x9d"
+    $ECHO -ne "\xef\x12\x1e\x42\x89\x52\xad\x28\x90\xe5\x2b\x88\xa0\x4f\x11\x92"
+    $ECHO -ne "\xcd\xcc\x63\x40\x1a\xc7\x10\x0c\x2f\xcd\x01\xf2\x07\x38\xac\x14"
+    $ECHO -ne "\xe5\x90\xc0\x30\x21\xe2\xe3\x72\x0e\x3e\x04\xc8\x9e\xa7\x00\xdb"
+    $ECHO -ne "\x91\xdd\x9d\x80\xa4\x69\x2a\x48\x37\x97\xa4\x26\x5d\xae\x84\x1e"
+    $ECHO -ne "\x88\xf4\x83\x04\x24\xc9\x1f\x94\x61\x25\xf9\x82\xdd\xed\x2d\x96"
+    $ECHO -ne "\xad\x06\x45\xdd\x88\xd7\x50\x40\x14\xdc\x7c\xdb\x0f\x53\x96\x27"
+    $ECHO -ne "\xcb\x67\xac\xa6\xc1\x15\x2f\xc3\xdb\x2c\xca\x94\xb3\xf3\xd1\x6a"
+    $ECHO -ne "\xba\x34\x83\xd1\xcc\x40\x3e\x76\xa1\x69\x7f\x49\x33\xdc\xa7\x3c"
+    $ECHO -ne "\x6a\x67\x15\xab\xdb\x52\xa0\xb8\xa6\x1e\xce\xe3\xaf\xf4\xa2\x62"
+    $ECHO -ne "\x35\x0f\x03\x40\x8e\x20\x12\x9c\xb6\x34\x71\x3a\x15\x5d\xe5\x34"
+    $ECHO -ne "\xa8\xd4\x05\x99\x6b\x9a\xb6\x41\x0b\x78\xc4\xd8\xd9\x7a\x65\xdc"
+    $ECHO -ne "\xdb\xe3\x42\xd5\x66\xf9\xb4\x83\x7e\xc0\xf4\x01\xc4\xcc\x3b\x0e"
+    $ECHO -ne "\x15\xdc\x15\xc2\x3e\x04\x2f\xfc\x6b\x72\xeb\xf6\xaa\x16\x20\xde"
+    $ECHO -ne "\xd3\x3a\xb1\x10\xc6\x3c\xe8\x2b\xb8\xea\xda\x19\x6e\x36\xaa\xa4"
+    $ECHO -ne "\x23\x6d\xa0\x40\xd1\x5a\x0b\x7e\xa4\xd5\x2d\xcb\xa9\x15\x35\xba"
+    $ECHO -ne "\x93\x92\x45\x41\xb0\x1a\xd1\x13\x31\xb6\x44\x98\x78\x28\x15\xe4"
+    $ECHO -ne "\xae\xba\x58\xd1\x75\x36\x34\x1a\xd8\x28\xf1\x4a\x4c\xbc\x1b\xa8"
+    $ECHO -ne "\xf7\x57\x92\xbc\xe2\xb5\xda\xb6\xa6\x1d\x83\x37\x96\x43\x20\x84"
+    $ECHO -ne "\xcb\xb6\xd9\x3f\xeb\xfa\xa0\xfe\x9a\x7d\xee\x47\x98\xc4\xe7\xc4"
+    $ECHO -ne "\xbd\xc6\xf0\x6d\xb2\x26\x10\x1e\x78\xef\xf3\x28\x3e\x35\xe6\xe4"
+    $ECHO -ne "\xe6\xf3\x0f\x26\x34\x13\x85\xd0\xcf\x55\x0f\x8b\xd7\xe9\xf4\xdf"
+    $ECHO -ne "\x70\x68\xc0\xb5\x30\x3c\xb1\x01\xe8\x28\xae\x80\x26\x01\x8b\x15"
+    $ECHO -ne "\x0f\x80\x48\x18\x4b\xe2\xed\x59\x92\x31\xcf\xd2\x8f\x42\xbf\xee"
+    $ECHO -ne "\xbd\x07\x91\x24\xc6\x66\x5e\x8c\x9a\x48\x63\xe7\xac\x8a\x1e\xc5"
+    $ECHO -ne "\x69\x16\x8d\xac\x67\xdc\x75\x75\x82\xca\x19\x28\x36\x4d\x10\xf9"
+    $ECHO -ne "\x41\xcb\x15\x05\x64\xc7\xb0\xc3\x64\xf3\x48\x71\x60\xf2\xbd\xcc"
+    $ECHO -ne "\x37\xb1\x36\xbc\xa7\x2e\x6b\x20\x11\x51\x42\xe1\x8a\x29\xac\x44"
+    $ECHO -ne "\x8f\x63\x56\x23\xd4\xd4\x07\xb4\x60\xa4\xb8\xcd\xee\x49\xa5\x42"
+    $ECHO -ne "\xcc\x52\x00\x6f\xdc\x44\x20\x57\x7d\x36\xd7\x48\x1a\x22\x2c\xd0"
+    $ECHO -ne "\x19\x43\x51\x5e\x1c\x8c\x5f\x70\xc2\x6b\xcf\xea\xd4\x97\x61\x72"
+    $ECHO -ne "\x33\xc3\x9a\xd4\x06\xf1\x8a\x9a\xfe\x21\x83\x0b\xea\xf1\xfa\x2c"
+    $ECHO -ne "\x52\x23\x2c\xb8\xc1\xe6\xc8\x9d\x9c\x5f\x8f\xf2\x4a\x86\x76\x92"
+    $ECHO -ne "\x78\x0f\x7d\x9d\x09\x38\xce\xe1\x9a\xf3\x60\xed\x65\x0b\x1a\x68"
+    $ECHO -ne "\xa6\x52\x39\x18\x1e\x45\xe3\x5d\xe0\x7d\xfb\xc6\xcc\x44\x18\x93"
+    $ECHO -ne "\xe9\x71\xa8\x18\x0d\x74\x48\x8a\x18\x0b\x61\xbf\xe1\xa9\x0e\x4c"
+    $ECHO -ne "\xad\x1b\xaf\x1a\x37\x39\x92\x4d\xcc\x96\x87\x46\x0d\x83\x06\x33"
+    $ECHO -ne "\x53\x35\xd9\x2c\x36\x98\x28\x1c\x52\xb1\x89\x55\x56\xcc\x37\x20"
+    $ECHO -ne "\x89\x84\x0e\x3d\x27\x2f\xc6\xfa\x78\x04\xe1\xd5\xc6\x90\x49\x16"
+    $ECHO -ne "\xfe\x0a\x16\x6f\x11\x54\x42\x22\xa1\x90\x2d\x19\x91\x28\x05\xf2"
+    $ECHO -ne "\x30\x6c\x14\x16\xd6\x8a\xce\xf6\xcd\x7c\x64\x76\x42\xe9\x28\xe9"
+    $ECHO -ne "\x1c\xd1\xb8\x9e\xcd\x53\xb2\x6b\x8d\x57\x57\x2a\xb8\x59\x58\x8c"
+    $ECHO -ne "\xd3\x12\x57\xa6\xe3\x48\x70\xf5\x55\x0f\x76\xb5\x27\x08\xd1\xa0"
+    $ECHO -ne "\xf8\x60\x09\xa1\xf2\x30\x43\x4a\x30\x46\xf7\x96\x19\xe9\x3a\x44"
+    $ECHO -ne "\xc0\xd8\xa8\x51\xae\x50\x92\x81\x81\xda\x10\xd3\x18\x62\x94\xd0"
+    $ECHO -ne "\x9e\x54\x0b\x22\xcc\xd0\xfe\x0c\x36\x44\x4d\x4d\x40\x5c\xa8\x35"
+    $ECHO -ne "\xb6\x53\x9c\x36\x9c\x5a\x0e\x0e\xb0\x5c\x29\x2a\x35\x66\xaa\x3a"
+    $ECHO -ne "\xcb\x23\x7b\xbb\xc8\x60\xbc\xb4\x28\xf4\x6e\xfe\x86\xfc\x16\x85"
+    $ECHO -ne "\x0c\xe0\x1d\xcf\xfd\x12\x28\xc6\x60\xd0\xe6\x2f\x76\xf0\x1a\x5b"
+    $ECHO -ne "\xfa\xa6\xc6\xea\x58\xbb\x26\x37\x84\xdd\x85\xd5\x37\x82\x76\xd9"
+    $ECHO -ne "\x14\x7a\xca\xed\x13\x72\xc3\xe1\xb9\x69\x45\xd4\xec\x44\x94\x26"
+    $ECHO -ne "\x8e\x0b\x90\xb6\x8b\x1f\x1e\x01\x96\x5a\xb9\x51\xa6\x27\xa2\x9b"
+    $ECHO -ne "\x38\xd9\x25\x32\x9b\x54\xfc\x45\xd1\xa8\x59\x35\x1a\xb0\xb2\x1a"
+    $ECHO -ne "\xc8\x88\x15\x42\x98\x50\x99\x12\x9e\xf5\x59\xb2\x5c\xc5\xa7\x34"
+    $ECHO -ne "\x35\xca\xb3\xed\xdc\xc9\x9f\x3e\x77\x8f\x6c\xde\xc8\x41\x6a\xc5"
+    $ECHO -ne "\x24\x85\x04\xa1\x2f\xe3\x47\x8c\x47\xd4\xdb\x74\x8c\xb6\x4c\xef"
+    $ECHO -ne "\xed\xad\x9f\x86\x31\xd8\xc8\x07\xc5\x11\x1c\x39\x3a\xf8\x75\x73"
+    $ECHO -ne "\xae\x78\x7d\x1d\x36\x5b\xd1\x23\x5d\x84\x17\x5d\x4b\xac\xd3\x70"
+    $ECHO -ne "\x8a\x83\x48\x48\x83\x7b\x5c\x99\x9e\x56\xbb\xfc\x0c\x4b\x04\xcf"
+    $ECHO -ne "\x83\x5d\xf8\x31\x2c\xc4\x5c\xa1\x68\x6a\x56\xe1\x7f\xbe\xd6\x59"
+    $ECHO -ne "\x6c\x55\xb0\x63\x41\xeb\x88\x69\xb6\x9b\x50\xc4\x31\xea\xb0\xd7"
+    $ECHO -ne "\xe2\xfb\x7b\xeb\xbb\x52\xc4\x97\x23\xe9\x16\x29\x18\x50\x4d\x0e"
+    $ECHO -ne "\x68\x62\xfb\x3f\xd9\x07\xb9\x89\x4d\x58\x7c\x32\x6d\x12\x3e\x9b"
+    $ECHO -ne "\x3a\x14\xee\xac\x3c\x8d\x09\x62\x30\x8e\xe0\x86\x84\xb9\xf3\x0d"
+    $ECHO -ne "\xf8\xad\x42\xa6\xbb\x7d\xd1\xf2\xf3\xc0\xe2\x32\xc4\x40\xaa\x8a"
+    $ECHO -ne "\x2a\xe9\xa9\x45\x83\x23\xf6\x90\x05\x24\x59\x22\x84\x50\x82\xc0"
+    $ECHO -ne "\x58\x41\x42\x18\x91\x3d\xd8\x80\xb1\x26\x68\xb2\xa8\xc0\x21\x14"
+    $ECHO -ne "\x18\xdf\x3a\x86\x25\x85\x56\xab\x20\x38\xcd\xdc\x98\x6e\x07\xc4"
+    $ECHO -ne "\x6b\x16\x55\xe0\x41\xe0\x41\xda\x29\x62\x8d\xba\xce\xa2\xcb\xfc"
+    $ECHO -ne "\x70\x78\x99\xf9\x16\x0b\x5a\x0c\xc5\xad\x18\xeb\xf0\xb5\xc9\x25"
+    $ECHO -ne "\x82\x16\xe0\x5d\xc1\xc4\xc6\xf0\x84\x6a\x45\x7d\xdb\x28\x46\xab"
+    $ECHO -ne "\xef\x32\xc9\x49\x50\x51\x60\x77\x1c\xfd\x58\x9c\x01\x3b\x7a\xfa"
+    $ECHO -ne "\x49\x47\x3e\x87\x1c\x39\xa6\x6a\xa4\xb7\x39\x93\xac\xac\xb0\x39"
+    $ECHO -ne "\x2f\xbc\xab\x9b\x52\x96\x24\x46\xc1\x95\xe4\x31\x89\x37\x18\xc8"
+    $ECHO -ne "\x2c\x22\x32\x2a\x8f\xb6\x58\x77\x57\x77\x2f\x09\xd0\x7c\xed\x74"
+    $ECHO -ne "\xaa\x7c\x86\x25\x45\x0c\x43\x4d\x31\xb0\x63\x40\xcf\x86\xfe\x75"
+    $ECHO -ne "\x76\xe0\xee\x99\xb5\x71\xe2\x4e\xe5\xc1\xf9\x2e\x48\xe2\xa6\x1b"
+    $ECHO -ne "\x28\xa5\xa3\xbe\xff\x37\xd1\xdd\x66\xa2\xe8\xd3\x88\x4d\x13\xd5"
+    $ECHO -ne "\x68\x51\x27\x41\xc3\x6c\x1b\x48\x67\x6a\xdf\x25\x2a\x40\xa1\x87"
+    $ECHO -ne "\x1d\x54\xb7\xe3\x91\xc2\x6b\x5b\xb9\x8c\xd5\x10\x11\x10\x16\xab"
+    $ECHO -ne "\x6b\xbe\x65\x6b\x73\xa7\x35\xa1\x09\x60\x60\xed\x96\x39\xc9\x40"
+    $ECHO -ne "\x5d\xdc\xee\x60\x49\x0c\x68\x18\x34\xb2\x6f\x2a\x95\x14\x29\x95"
+    $ECHO -ne "\x5b\x59\xd2\x1f\x63\x2a\xbe\xfd\xae\x09\x5c\xee\x11\xb5\x29\x36"
+    $ECHO -ne "\xca\xdf\x28\x8c\x65\x42\x46\x74\x0c\x39\x68\x30\xac\x2c\x2f\xd0"
+    $ECHO -ne "\x9b\xb3\x92\x19\x90\xa1\x07\xcc\xf6\xde\x64\x5f\x6f\xd7\xb6\xcc"
+    $ECHO -ne "\xe0\x70\x0f\x0b\xd2\x0e\x77\xa1\x70\xe3\x56\x90\x4b\x28\x58\xd0"
+    $ECHO -ne "\xd1\xe1\x9d\x18\x98\xba\x6b\x36\x54\xa9\x54\x09\x63\x49\x18\x55"
+    $ECHO -ne "\x60\xba\x11\xb1\x0a\x14\x45\x1f\xae\x08\x50\x09\x33\x00\xa2\xb2"
+    $ECHO -ne "\x71\x81\x75\x89\xb7\xb9\x0c\x73\xc0\x4c\x32\x89\x72\xac\xa9\xa3"
+    $ECHO -ne "\x47\x5f\x7d\x4e\x1b\x4d\xb9\xea\x84\x45\x00\x37\x3c\xb3\x7b\xf8"
+    $ECHO -ne "\xe7\x0f\xaa\x33\x1a\x9b\xc2\x0c\x35\x8a\xd4\x04\x46\x42\xcb\xab"
+    $ECHO -ne "\xaa\xc7\xe5\xc9\x20\x6e\x21\xa6\x8c\xed\x61\x86\x42\x87\x03\x25"
+    $ECHO -ne "\xde\x2c\x4a\x85\xcb\xb4\x36\xc9\xd4\x72\x60\x62\xc2\x19\xd0\x30"
+    $ECHO -ne "\x16\x6d\x58\x61\x62\x16\xe8\xd2\x0e\xd0\xf3\xdb\x53\x37\x07\x37"
+    $ECHO -ne "\x40\xc3\xe5\x5b\x9d\x16\x45\x60\x8e\xfb\x12\xc4\x5f\x9f\xdd\xe1"
+    $ECHO -ne "\x45\x5d\x45\x36\x21\xa0\xc0\xb8\x11\x98\x0f\x64\x98\x67\x1c\x11"
+    $ECHO -ne "\xa9\xa1\x65\x10\xb9\x22\x12\x91\x10\x9b\x10\x6f\x95\x2e\x34\x91"
+    $ECHO -ne "\x64\x82\xa4\x05\x02\xfc\x4a\x9f\x9c\x4d\x6c\x8d\x67\x26\x90\x63"
+    $ECHO -ne "\x04\x12\x6f\x0e\x55\x3c\x8e\xf2\x8d\xb4\x6b\x3d\xac\xcf\x84\x2e"
+    $ECHO -ne "\x60\x0f\x40\x62\x88\x3a\xcf\xbd\xea\xad\x40\x4c\x29\xe1\xb5\xb6"
+    $ECHO -ne "\x3e\x15\x86\xd5\xbe\xad\x27\xde\x2b\x32\xef\xcf\x97\x88\x8b\x17"
+    $ECHO -ne "\x80\x43\x0e\x20\x79\x3b\x36\x73\xb8\xad\x12\x0e\x87\x59\x5f\xd3"
+    $ECHO -ne "\x3c\x8c\x84\xc8\x54\x2b\x94\xc9\x2e\x36\x8b\x32\x48\xf1\xe3\x08"
+    $ECHO -ne "\xf0\x36\xc0\xb8\xc2\xa2\xa9\xe2\x52\x02\xf1\x9b\xcb\xde\xcc\xb5"
+    $ECHO -ne "\x5a\x6c\x05\x06\x31\x44\x41\x88\xa3\x05\x04\x16\x0d\x4a\x85\x20"
+    $ECHO -ne "\x79\xda\x89\x82\x1d\x5f\x5a\x11\x88\x89\x06\x05\xf5\xf4\xed\x75"
+    $ECHO -ne "\x62\x39\x37\x69\x11\x32\x3e\x8d\xe4\x60\x62\x52\xc9\xad\x82\x9a"
+    $ECHO -ne "\x9a\x2f\x06\x41\x26\xb4\x48\x70\x39\x2b\x8a\xb1\x5a\x53\xc6\x48"
+    $ECHO -ne "\x57\x17\xf5\xd8\x5a\xc6\x19\x83\x06\x0b\x9b\x04\xb8\xf5\xaf\x23"
+    $ECHO -ne "\x45\x87\x48\x50\x6d\x16\xea\xb4\x20\xb8\x49\x92\x6b\x0c\x76\x14"
+    $ECHO -ne "\x48\x53\xa1\x29\x74\xf6\xd7\x49\x44\x39\xba\xbd\x63\xa6\xf2\x81"
+    $ECHO -ne "\x8f\x5b\x5e\x46\x0a\x34\x95\x31\xc0\xdd\x60\x50\xd6\x0a\xa6\x29"
+    $ECHO -ne "\x3d\x36\x3a\xc7\xb8\xcf\x25\x5e\xf7\x82\x55\x88\xc2\x8b\x30\xd2"
+    $ECHO -ne "\x97\x90\x49\x94\xde\xe5\xaa\xeb\x42\x8c\x94\x2a\xa0\x0d\x9b\xb5"
+    $ECHO -ne "\x59\xbe\xcb\x35\xa3\x37\x54\x76\x35\x98\xcb\x1f\x13\x3f\x4a\xd1"
+    $ECHO -ne "\x45\x87\x67\xed\x66\x02\x06\x49\x1c\x59\x51\x1f\x4d\x85\x03\x46"
+    $ECHO -ne "\x65\x86\x4e\x4c\x6a\xd2\x24\x31\x5b\x6a\x3b\x19\x49\xe1\x83\x14"
+    $ECHO -ne "\xc1\xf0\x56\x61\x93\x8b\x33\x13\x54\x6f\x78\x4c\xa0\x85\xf0\xb3"
+    $ECHO -ne "\x17\xaa\xf2\x67\x02\x0c\x31\xed\x5e\x98\x02\x28\xc4\xe5\x87\xa2"
+    $ECHO -ne "\x70\xd1\xd5\x9c\xf8\xec\x19\x88\xa9\x0d\x0e\x4a\xe3\x47\x83\x6e"
+    $ECHO -ne "\xf7\x70\x3e\xa4\xa9\xc0\x4c\xfa\x2b\x3b\xd7\x6f\x96\xc3\x6d\x6d"
+    $ECHO -ne "\x71\x2a\x8d\x62\xf1\xd3\xdd\xb0\x33\xd4\x67\x19\x0d\x30\x85\x3a"
+    $ECHO -ne "\x06\x04\x85\x00\x48\x5d\x53\x35\xa0\x31\x56\x84\x82\xa5\xac\x22"
+    $ECHO -ne "\x02\x44\xaf\x6d\xc0\x61\x59\x23\x96\x72\x5a\x81\x9e\x0c\xe5\x79"
+    $ECHO -ne "\xda\xd0\x42\x5c\x89\xa5\x00\xec\x56\x41\x64\x8a\x11\x60\x79\xb1"
+    $ECHO -ne "\xed\x55\x16\x54\xe6\x51\x03\x34\x14\x60\x31\xd2\xf0\x0b\xce\xf2"
+    $ECHO -ne "\x4e\x4c\x45\x8c\xeb\x2a\x82\x3a\xa8\x52\xce\x8f\x4e\xf1\x89\xea"
+    $ECHO -ne "\x44\x91\x66\xdd\x6b\x49\xa3\x83\x0b\x19\x0e\x66\x5f\x02\x22\x58"
+    $ECHO -ne "\xe7\xc0\xa8\xce\x55\x48\xa6\x04\xf3\x03\xac\x62\xb2\xc0\xaa\xa0"
+    $ECHO -ne "\x09\xae\x5b\x96\xd0\xdd\xa9\x1f\xfb\x2d\x3d\xf5\x02\xe1\x86\x02"
+    $ECHO -ne "\x3e\xda\xd0\x5d\xba\x16\x39\xcd\x75\xa2\x47\x26\x74\x25\xa8\x5e"
+    $ECHO -ne "\xf3\x36\x0c\x37\x19\x17\x06\x66\xd0\x0b\x42\x41\x0a\xa0\xde\x93"
+    $ECHO -ne "\xd7\xb4\x9f\xfb\xc7\x4f\x65\x54\xda\xb8\x8b\x23\xde\x9c\x57\xcf"
+    $ECHO -ne "\x2d\x2a\x12\xda\xcc\xf6\x73\x83\x02\x4c\x0e\x42\x88\xda\x27\xb9"
+    $ECHO -ne "\xcb\x04\xb6\x07\x26\x78\xa1\xa1\x09\xa3\x6a\x86\xbd\x9d\xd4\xf9"
+    $ECHO -ne "\xc0\x81\xa6\x49\xa9\x72\xeb\x56\xbd\xf9\xea\x89\x4f\xae\x72\x28"
+    $ECHO -ne "\xb6\x57\x35\xbe\x94\xad\xc0\xff\x1e\xf2\x35\x24\xa0\x45\xd5\x09"
+    $ECHO -ne "\xc0\xe0\x10\xd0\x17\x90\xe2\xff\x8b\xb9\x22\x9c\x28\x48\x09\x68"
+    $ECHO -ne "\xc3\x18\x00"
+}
+
+tar2_bz2()
+{
+    $ECHO -ne "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\x16\x08\xfd\x60\x00\x00"
+    $ECHO -ne "\x01\xff\x80\x48\x80\x00\xa0\x40\x03\xff\xc0\x72\x00\x89\x40\xff"
+    $ECHO -ne "\xe7\xdf\xc0\x20\x00\x92\x11\x53\xf5\x13\xd3\x53\x7a\x41\xa8\xf2"
+    $ECHO -ne "\x9a\x60\x21\x9a\x40\xf4\x9b\xd2\x0d\x09\xa5\x3c\xa6\x4f\x44\xf5"
+    $ECHO -ne "\x34\x34\xd1\xb5\x01\xa0\x3d\x41\xa4\xe1\xeb\x4c\x5a\x01\x47\x3b"
+    $ECHO -ne "\xd1\x67\x1a\x4c\x3b\x21\x84\x23\x2c\x5c\xf7\xe0\xbd\x2a\xa4\xea"
+    $ECHO -ne "\xdc\xdb\x71\x80\x26\x98\x21\x0e\x76\x21\x30\xce\xe4\xad\x8c\xb5"
+    $ECHO -ne "\x68\x62\x35\xa1\xfd\x8e\x7b\x51\x70\x96\xb1\x2c\xa2\x99\x6c\xa1"
+    $ECHO -ne "\xc2\xcd\xea\xa7\x5e\x6b\x91\x4f\x73\x96\xe4\x48\x3c\xe7\x8c\x0f"
+    $ECHO -ne "\x03\x64\x5b\x7a\x43\xc1\x68\x86\x41\x83\x46\x0b\xba\xaa\x6a\x9b"
+    $ECHO -ne "\x59\x34\xf1\x1c\x08\x69\x1d\x41\xfb\x4a\x96\x1b\x14\x9e\x32\x89"
+    $ECHO -ne "\x69\x5f\x63\x9a\x22\xe4\x96\x34\xff\x12\x20\xd0\x25\x70\xdc\x5d"
+    $ECHO -ne "\xc9\x14\xe1\x42\x40\x58\x23\xf5\x80"
+}
+
+# NB: tar emits "tar: short read" on stderr because these test tars are
+# also lacking proper terminating zeroed blocks. But exitcode is 0.
+# This is intended.
+
+export TZ=UTC-1
+
+# Case 1: long name, with path in prefix field
+res1='-rw-r--r-- fm3/users      9869 2007-03-12 10:44:54 VirtualBox-1.5.6_OSE/src/libs/xpcom18a4/ipc/ipcd/extensions/transmngr/public/ipcITransactionService.idl'
+t=`tar1_bz2 | bunzip2 | busybox tar tvf -`
+test x"$res1" = x"$t"
+t=`tar1_bz2 | bunzip2 | busybox tar tv`
+test x"$res1" = x"$t"
+
+# Case 2: long dir name, with ENTIRE path in prefix field (name = "")
+res2='drwxr-xr-x fm3/users         0 2008-02-19 16:33:20 VirtualBox-1.5.6_OSE/src/VBox/Additions/linux/x11include/4.3/programs/Xserver/hw/xfree86/xf24_32bpp/'
+t=`tar2_bz2 | bunzip2 | busybox tar tvf -`
+test x"$res2" = x"$t"
+t=`tar2_bz2 | bunzip2 | busybox tar tv`
+test x"$res2" = x"$t"
diff --git a/busybox-1.19.3/testsuite/taskset.tests b/busybox-1.19.3/testsuite/taskset.tests
new file mode 100755
index 0000000..3fb5c90
--- /dev/null
+++ b/busybox-1.19.3/testsuite/taskset.tests
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Copyright 2006 Bernhard Reutner-Fischer
+# Licensed under GPLv2 or later, see file LICENSE in this source tree.
+
+. ./testing.sh
+a="taskset"
+
+# testing "test name"              "opts" "expected result" "file inp" "stdin"
+testing "taskset (get from pid 1)" "$a -p 1 >/dev/null;echo \$?" "0\n" "" ""
+testing "taskset (invalid pid)"    "$a -p 0 >/dev/null 2>&1;echo \$?" "1\n" "" ""
+testing "taskset (set_aff, needs CAP_SYS_NICE)" \
+	"$a 0x1 $SHELL -c '$a -p \$\$ | grep \"current affinity mask: 1\" >/dev/null'; echo \$?" \
+	"0\n" "" ""
+
+unset a
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/tee/tee-appends-input b/busybox-1.19.3/testsuite/tee/tee-appends-input
new file mode 100644
index 0000000..cff20bf
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tee/tee-appends-input
@@ -0,0 +1,5 @@
+echo i\'m a little teapot >foo
+cp foo bar
+echo i\'m a little teapot >>foo
+echo i\'m a little teapot | busybox tee -a bar >/dev/null
+cmp foo bar
diff --git a/busybox-1.19.3/testsuite/tee/tee-tees-input b/busybox-1.19.3/testsuite/tee/tee-tees-input
new file mode 100644
index 0000000..26e2173
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tee/tee-tees-input
@@ -0,0 +1,3 @@
+echo i\'m a little teapot >foo
+echo i\'m a little teapot | busybox tee bar >baz
+cmp foo bar && cmp foo baz
diff --git a/busybox-1.19.3/testsuite/test.tests b/busybox-1.19.3/testsuite/test.tests
new file mode 100755
index 0000000..2c92e34
--- /dev/null
+++ b/busybox-1.19.3/testsuite/test.tests
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# Copyright 2007 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+#   file input will be file called "input"
+#   test can create a file "actual" instead of writing to stdout
+
+# Need to call 'busybox test', otherwise shell builtin is used
+
+testing "test: should be false (1)" \
+	"busybox test; echo \$?" \
+	"1\n" \
+	"" ""
+
+testing "test '': should be false (1)" \
+	"busybox test ''; echo \$?" \
+	"1\n" \
+	"" ""
+
+testing "test !: should be true (0)" \
+	"busybox test !; echo \$?" \
+	"0\n" \
+	"" ""
+
+testing "test a: should be true (0)" \
+	"busybox test a; echo \$?" \
+	"0\n" \
+	"" ""
+
+testing "test --help: should be true (0)" \
+	"busybox test --help; echo \$?" \
+	"0\n" \
+	"" ""
+
+testing "test -f: should be true (0)" \
+	"busybox test -f; echo \$?" \
+	"0\n" \
+	"" ""
+
+testing "test ! -f: should be false (1)" \
+	"busybox test ! -f; echo \$?" \
+	"1\n" \
+	"" ""
+
+testing "test a = a: should be true (0)" \
+	"busybox test a = a; echo \$?" \
+	"0\n" \
+	"" ""
+
+testing "test -lt = -gt: should be false (1)" \
+	"busybox test -lt = -gt; echo \$?" \
+	"1\n" \
+	"" ""
+
+testing "test a -a !: should be true (0)" \
+	"busybox test a -a !; echo \$?" \
+	"0\n" \
+	"" ""
+
+testing "test -f = a -o b: should be true (0)" \
+	"busybox test -f = a -o b; echo \$?" \
+	"0\n" \
+	"" ""
+
+testing "test ! a = b -a ! c = c: should be false (1)" \
+	"busybox test ! a = b -a ! c = c; echo \$?" \
+	"1\n" \
+	"" ""
+
+testing "test ! a = b -a ! c = d: should be true (0)" \
+	"busybox test ! a = b -a ! c = d; echo \$?" \
+	"0\n" \
+	"" ""
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/testing.sh b/busybox-1.19.3/testsuite/testing.sh
new file mode 100644
index 0000000..e7e64e5
--- /dev/null
+++ b/busybox-1.19.3/testsuite/testing.sh
@@ -0,0 +1,170 @@
+# Simple test harness infrastructure for BusyBox
+#
+# Copyright 2005 by Rob Landley
+#
+# License is GPLv2, see LICENSE in the busybox tarball for full license text.
+
+# This file defines two functions, "testing" and "optional"
+# and a couple more...
+
+# The following environment variables may be set to enable optional behavior
+# in "testing":
+#    VERBOSE - Print the diff -u of each failed test case.
+#    DEBUG - Enable command tracing.
+#    SKIP - do not perform this test (this is set by "optional")
+#
+# The "testing" function takes five arguments:
+#	$1) Test description
+#	$2) Command(s) to run. May have pipes, redirects, etc
+#	$3) Expected result on stdout
+#	$4) Data to be written to file "input"
+#	$5) Data to be written to stdin
+#
+# The exit value of testing is the exit value of $2 it ran.
+#
+# The environment variable "FAILCOUNT" contains a cumulative total of the
+# number of failed tests.
+
+# The "optional" function is used to skip certain tests, ala:
+#   optional FEATURE_THINGY
+#
+# The "optional" function checks the environment variable "OPTIONFLAGS",
+# which is either empty (in which case it always clears SKIP) or
+# else contains a colon-separated list of features (in which case the function
+# clears SKIP if the flag was found, or sets it to 1 if the flag was not found).
+
+export FAILCOUNT=0
+export SKIP=
+
+# Helper for helpers. Oh my...
+
+test x"$ECHO" != x"" || {
+	ECHO="echo"
+	test x"`echo -ne`" = x"" || {
+		# Compile and use a replacement 'echo' which understands -e -n
+		ECHO="$PWD/echo-ne"
+		test -x "$ECHO" || {
+			gcc -Os -o "$ECHO" ../scripts/echo.c || exit 1
+		}
+	}
+	export ECHO
+}
+
+# Helper functions
+
+optional()
+{
+	SKIP=
+	while test "$1"; do
+		if test x"${OPTIONFLAGS/*:$1:*/y}" != x"y"; then
+			SKIP=1
+			return
+		fi
+		shift
+	done
+}
+
+# The testing function
+
+testing()
+{
+  NAME="$1"
+  [ -n "$1" ] || NAME="$2"
+
+  if [ $# -ne 5 ]
+  then
+    echo "Test $NAME has wrong number of arguments: $# (must be 5)" >&2
+    exit 1
+  fi
+
+  [ -z "$DEBUG" ] || set -x
+
+  if [ -n "$SKIP" ]
+  then
+    echo "SKIPPED: $NAME"
+    return 0
+  fi
+
+  $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=$?
+
+  if cmp expected actual >/dev/null 2>/dev/null
+  then
+    echo "PASS: $NAME"
+  else
+    FAILCOUNT=$(($FAILCOUNT + 1))
+    echo "FAIL: $NAME"
+    [ -z "$VERBOSE" ] || diff -u expected actual
+  fi
+  rm -f input expected actual
+
+  [ -z "$DEBUG" ] || set +x
+
+  return $RETVAL
+}
+
+# Recursively grab an executable and all the libraries needed to run it.
+# Source paths beginning with / will be copied into destpath, otherwise
+# the file is assumed to already be there and only its library dependencies
+# are copied.
+
+mkchroot()
+{
+  [ $# -lt 2 ] && return
+
+  $ECHO -n .
+
+  dest=$1
+  shift
+  for i in "$@"
+  do
+    #bashism: [ "${i:0:1}" == "/" ] || i=$(which $i)
+    i=$(which $i) # no-op for /bin/prog
+    [ -f "$dest/$i" ] && continue
+    if [ -e "$i" ]
+    then
+      d=`echo "$i" | grep -o '.*/'` &&
+      mkdir -p "$dest/$d" &&
+      cat "$i" > "$dest/$i" &&
+      chmod +x "$dest/$i"
+    else
+      echo "Not found: $i"
+    fi
+    mkchroot "$dest" $(ldd "$i" | egrep -o '/.* ')
+  done
+}
+
+# Set up a chroot environment and run commands within it.
+# Needed commands listed on command line
+# Script fed to stdin.
+
+dochroot()
+{
+  mkdir tmpdir4chroot
+  mount -t ramfs tmpdir4chroot tmpdir4chroot
+  mkdir -p tmpdir4chroot/{etc,sys,proc,tmp,dev}
+  cp -L testing.sh tmpdir4chroot
+
+  # Copy utilities from command line arguments
+
+  $ECHO -n "Setup chroot"
+  mkchroot tmpdir4chroot $*
+  echo
+
+  mknod tmpdir4chroot/dev/tty c 5 0
+  mknod tmpdir4chroot/dev/null c 1 3
+  mknod tmpdir4chroot/dev/zero c 1 5
+
+  # Copy script from stdin
+
+  cat > tmpdir4chroot/test.sh
+  chmod +x tmpdir4chroot/test.sh
+  chroot tmpdir4chroot /test.sh
+  umount -l tmpdir4chroot
+  rmdir tmpdir4chroot
+}
diff --git a/busybox-1.19.3/testsuite/touch/touch-creates-file b/busybox-1.19.3/testsuite/touch/touch-creates-file
new file mode 100644
index 0000000..4b49354
--- /dev/null
+++ b/busybox-1.19.3/testsuite/touch/touch-creates-file
@@ -0,0 +1,2 @@
+busybox touch foo
+test -f foo
diff --git a/busybox-1.19.3/testsuite/touch/touch-does-not-create-file b/busybox-1.19.3/testsuite/touch/touch-does-not-create-file
new file mode 100644
index 0000000..8852592
--- /dev/null
+++ b/busybox-1.19.3/testsuite/touch/touch-does-not-create-file
@@ -0,0 +1,2 @@
+busybox touch -c foo
+test ! -f foo
diff --git a/busybox-1.19.3/testsuite/touch/touch-touches-files-after-non-existent-file b/busybox-1.19.3/testsuite/touch/touch-touches-files-after-non-existent-file
new file mode 100644
index 0000000..a869ec2
--- /dev/null
+++ b/busybox-1.19.3/testsuite/touch/touch-touches-files-after-non-existent-file
@@ -0,0 +1,3 @@
+touch -t 198001010000 bar
+busybox touch -c foo bar
+test x"`find bar -mtime -1`" = xbar
diff --git a/busybox-1.19.3/testsuite/tr.tests b/busybox-1.19.3/testsuite/tr.tests
new file mode 100755
index 0000000..5cca299
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tr.tests
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright 2009 by Denys Vlasenko <vda.linux@googlemail.com>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "description" "arguments" "result" "infile" "stdin"
+
+testing "tr does not treat [] in [a-z] as special" \
+	"tr '[q-z]' '_Q-Z+'" \
+	"_QWe+" "" "[qwe]"
+
+testing "tr understands 0-9A-F" \
+	"tr -cd '[0-9A-F]'" \
+	"19AF" "" "19AFH\n"
+
+optional FEATURE_TR_CLASSES
+testing "tr understands [:xdigit:]" \
+	"tr -cd '[:xdigit:]'" \
+	"19AF" "" "19AFH\n"
+SKIP=
+
+optional FEATURE_TR_CLASSES
+testing "tr does not stop after [:digit:]" \
+	"tr '[:digit:]y-z' 111111111123" \
+	"111abcx23\n" "" "789abcxyz\n"
+SKIP=
+
+optional FEATURE_TR_CLASSES
+testing "tr has correct xdigit sequence" \
+	"tr '[:xdigit:]Gg' 1111111151242222333330xX" \
+	"#1111111151242222x333330X\n" "" \
+	"#0123456789ABCDEFGabcdefg\n"
+SKIP=
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/tr/tr-d-alnum-works b/busybox-1.19.3/testsuite/tr/tr-d-alnum-works
new file mode 100644
index 0000000..6540ea5
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tr/tr-d-alnum-works
@@ -0,0 +1,6 @@
+# FEATURE: CONFIG_FEATURE_TR_CLASSES
+
+echo testing | tr -d '[[:alnum:]]' > logfile.gnu
+echo testing | busybox tr -d '[[:alnum:]]' > logfile.bb
+
+diff -u logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/tr/tr-d-works b/busybox-1.19.3/testsuite/tr/tr-d-works
new file mode 100644
index 0000000..a86bfbd
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tr/tr-d-works
@@ -0,0 +1,4 @@
+echo testing | tr -d aeiou > logfile.gnu
+echo testing | busybox tr -d aeiou > logfile.bb
+
+diff -u logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/tr/tr-non-gnu b/busybox-1.19.3/testsuite/tr/tr-non-gnu
new file mode 100644
index 0000000..ffa6951
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tr/tr-non-gnu
@@ -0,0 +1 @@
+echo fdhrnzvfu bffvsentr | busybox tr '[a-z]' '[n-z][a-m]'
diff --git a/busybox-1.19.3/testsuite/tr/tr-rejects-wrong-class b/busybox-1.19.3/testsuite/tr/tr-rejects-wrong-class
new file mode 100644
index 0000000..1d488a3
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tr/tr-rejects-wrong-class
@@ -0,0 +1,21 @@
+# FEATURE: CONFIG_FEATURE_TR_CLASSES
+
+echo t12esting | tr -d '[[:alpha:]]' > logfile.gnu
+echo t12esting | tr -d '[:alpha:]'  >> logfile.gnu
+echo t12esting | tr -d '[[:alpha:]' >> logfile.gnu
+echo t12esting | tr -d '[[:alpha:' >> logfile.gnu
+echo t12esting | tr -d '[[:alpha' >> logfile.gnu
+echo t12esting | tr -d '[:alpha:]' >> logfile.gnu
+echo t12esting | tr -d '[:alpha:' >> logfile.gnu
+echo t12esting | tr -d '[:alpha' >> logfile.gnu
+
+echo t12esting | busybox tr -d '[[:alpha:]]' > logfile.bb
+echo t12esting | busybox tr -d '[:alpha:]'  >> logfile.bb
+echo t12esting | busybox tr -d '[[:alpha:]' >> logfile.bb
+echo t12esting | busybox tr -d '[[:alpha:' >> logfile.bb
+echo t12esting | busybox tr -d '[[:alpha' >> logfile.bb
+echo t12esting | busybox tr -d '[:alpha:]' >> logfile.bb
+echo t12esting | busybox tr -d '[:alpha:' >> logfile.bb
+echo t12esting | busybox tr -d '[:alpha' >> logfile.bb
+
+diff -u logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/tr/tr-works b/busybox-1.19.3/testsuite/tr/tr-works
new file mode 100644
index 0000000..5e4a30e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/tr/tr-works
@@ -0,0 +1,28 @@
+# FEATURE: CONFIG_FEATURE_TR_CLASSES
+
+run_tr ()
+{
+	$ECHO -n "echo '$1' | tr '$2' '$3': "
+	echo "$1" | $bb tr "$2" "$3"
+	echo
+}
+tr_test ()
+{
+	run_tr "cbaab"		abc 		zyx
+	run_tr "TESTING A B C" 	'[A-Z]' 	'[a-z]'
+	run_tr "abc[]" 		"a[b" 		AXB
+	run_tr abc		'[:alpha:]' 	A-ZA-Z
+	run_tr abc56		'[:alnum:]' 	A-ZA-Zxxxxxxxxxx
+	run_tr 012		'[:digit:]' 	abcdefghi
+	run_tr abc56		'[:lower:]' 	'[:upper:]'
+	run_tr " 	"	'[:space:]' 	12345
+	run_tr " 	"	'[:blank:]' 	12
+	run_tr 'a b'		'[= =]' 	X
+	run_tr "[:"		'[:' 		ab
+	run_tr " 	.,:"	'[:punct:]'	12
+	run_tr " 	.,:"	'[:cntrl:]'	12
+}
+
+bb=        tr_test > logfile.gnu
+bb=busybox tr_test > logfile.bb
+diff -u logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/testsuite/true/true-is-silent b/busybox-1.19.3/testsuite/true/true-is-silent
new file mode 100644
index 0000000..1d1bdb2
--- /dev/null
+++ b/busybox-1.19.3/testsuite/true/true-is-silent
@@ -0,0 +1 @@
+busybox true 2>&1 | cmp - /dev/null
diff --git a/busybox-1.19.3/testsuite/true/true-returns-success b/busybox-1.19.3/testsuite/true/true-returns-success
new file mode 100644
index 0000000..cdf2d55
--- /dev/null
+++ b/busybox-1.19.3/testsuite/true/true-returns-success
@@ -0,0 +1 @@
+busybox true
diff --git a/busybox-1.19.3/testsuite/umlwrapper.sh b/busybox-1.19.3/testsuite/umlwrapper.sh
new file mode 100755
index 0000000..e55e4db
--- /dev/null
+++ b/busybox-1.19.3/testsuite/umlwrapper.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Wrapper for User Mode Linux emulation environment
+
+RUNFILE="$(pwd)/${1}.testroot"
+if [ -z "$RUNFILE" ] || [ ! -x "$RUNFILE" ]
+then
+  echo "Can't run '$RUNFILE'"
+  exit 1
+fi
+
+shift
+
+if [ -z $(which linux) ]
+then
+  echo "No User Mode Linux."
+  exit 1;
+fi
+
+linux rootfstype=hostfs rw init="$RUNFILE" TESTDIR=`pwd` PATH="$PATH" $* quiet
diff --git a/busybox-1.19.3/testsuite/unexpand.tests b/busybox-1.19.3/testsuite/unexpand.tests
new file mode 100755
index 0000000..7b326dc
--- /dev/null
+++ b/busybox-1.19.3/testsuite/unexpand.tests
@@ -0,0 +1,39 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+test -f "$bindir/.config" && . "$bindir/.config"
+
+# testing "test name" "options" "expected result" "file input" "stdin"
+
+testing "unexpand case 1" "unexpand" \
+	"\t12345678\n" "" "        12345678\n" \
+
+testing "unexpand case 2" "unexpand" \
+	"\t 12345678\n" "" "         12345678\n" \
+
+testing "unexpand case 3" "unexpand" \
+	"\t  12345678\n" "" "          12345678\n" \
+
+testing "unexpand case 4" "unexpand" \
+	"\t12345678\n" "" "       \t12345678\n" \
+
+testing "unexpand case 5" "unexpand" \
+	"\t12345678\n" "" "      \t12345678\n" \
+
+testing "unexpand case 6" "unexpand" \
+	"\t12345678\n" "" "     \t12345678\n" \
+
+testing "unexpand case 7" "unexpand" \
+	"123\t 45678\n" "" "123 \t 45678\n" \
+
+testing "unexpand case 8" "unexpand" \
+	"a b\n" "" "a b\n" \
+
+test x"$CONFIG_UNICODE_SUPPORT" = x"y" \
+&& test x"$CONFIG_UNICODE_USING_LOCALE" != x"y" \
+&& testing "unexpand with unicode characher 0x394" "unexpand" \
+	"1ΔΔΔ5\t99999\n" "" "1ΔΔΔ5   99999\n"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/unexpand/unexpand-works-like-GNU b/busybox-1.19.3/testsuite/unexpand/unexpand-works-like-GNU
new file mode 100644
index 0000000..a525836
--- /dev/null
+++ b/busybox-1.19.3/testsuite/unexpand/unexpand-works-like-GNU
@@ -0,0 +1,52 @@
+rm -f foo bar
+echo "       y" | unexpand ../../busybox > foo
+echo "       y" | busybox unexpand ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
+rm -f foo bar
+echo "        y" | unexpand ../../busybox > foo
+echo "        y" | busybox unexpand ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
+echo "       y       y" | unexpand ../../busybox > foo
+echo "       y       y" | busybox unexpand ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
+rm -f foo bar
+echo "        y        y" | unexpand ../../busybox > foo
+echo "        y        y" | busybox unexpand ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
+echo "       y       y" | unexpand -a ../../busybox > foo
+echo "       y       y" | busybox unexpand -a ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
+rm -f foo bar
+echo "        y        y" | unexpand -a ../../busybox > foo
+echo "        y        y" | busybox unexpand -a ../../busybox > bar
+set +e
+test ! -f foo -a -f bar
+if [ $? = 0 ] ; then
+	set -e
+	diff -q foo bar
+fi
diff --git a/busybox-1.19.3/testsuite/uniq.tests b/busybox-1.19.3/testsuite/uniq.tests
new file mode 100755
index 0000000..83bf382
--- /dev/null
+++ b/busybox-1.19.3/testsuite/uniq.tests
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+# SUSv3 compliant uniq tests.
+# Copyright 2005 by Rob Landley <rob@landley.net>
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+# AUDIT: Full SUSv3 coverage (except internationalization).
+
+. ./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
+
+# Test exit status
+
+testing "uniq (exit with error)" "uniq nonexistent 2> /dev/null || echo yes" \
+	"yes\n" "" ""
+testing "uniq (exit success)" "uniq /dev/null && echo yes" "yes\n" "" ""
+
+# Test various data sources and destinations
+
+testing "uniq (default to stdin)" "uniq" "one\ntwo\nthree\n" "" \
+	"one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "uniq - (specify stdin)" "uniq -" "one\ntwo\nthree\n" "" \
+	"one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "uniq input (specify file)" "uniq input" "one\ntwo\nthree\n" \
+	"one\ntwo\ntwo\nthree\nthree\nthree\n" ""
+
+testing "uniq input outfile (two files)" "uniq input actual > /dev/null" \
+	"one\ntwo\nthree\n" "one\ntwo\ntwo\nthree\nthree\nthree\n" ""
+testing "uniq (stdin) outfile" "uniq - actual" \
+	"one\ntwo\nthree\n" "" "one\ntwo\ntwo\nthree\nthree\nthree\n"
+# Note: SUSv3 doesn't seem to require support for "-" output, but we do anyway.
+testing "uniq input - (specify stdout)" "uniq input -" \
+	"one\ntwo\nthree\n" "one\ntwo\ntwo\nthree\nthree\nthree\n" ""
+
+
+#-f skip fields
+#-s skip chars
+#-c occurrences
+#-d dups only
+#-u
+#-w max chars
+
+# Test various command line options
+
+# Leading whitespace is a minor technical violation of the spec,
+# but since gnu does it...
+testing "uniq -c (occurrence count)" "uniq -c | sed 's/^[ \t]*//'" \
+	"1 one\n2 two\n3 three\n" "" \
+	"one\ntwo\ntwo\nthree\nthree\nthree\n"
+testing "uniq -d (dups only)" "uniq -d" "two\nthree\n" "" \
+	"one\ntwo\ntwo\nthree\nthree\nthree\n"
+
+testing "uniq -f -s (skip fields and chars)" "uniq -f2 -s 3" \
+"cc	dd	ee8
+aa	bb	cc9
+" "" \
+"cc	dd	ee8
+bb	cc	dd8
+aa	bb	cc9
+"
+testing "uniq -w (compare max characters)" "uniq -w 2" \
+"cc1
+" "" \
+"cc1
+cc2
+cc3
+"
+
+testing "uniq -s -w (skip fields and compare max chars)" \
+"uniq -s 2 -w 2" \
+"aaccaa
+" "" \
+"aaccaa
+aaccbb
+bbccaa
+"
+
+# -d is "Suppress the writing fo lines that are not repeated in the input."
+# -u is "Suppress the writing of lines that are repeated in the input."
+# Therefore, together this means they should produce no output.
+testing "uniq -u and -d produce no output" "uniq -d -u" "" "" \
+	"one\ntwo\ntwo\nthree\nthree\nthree\n"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/unzip.tests b/busybox-1.19.3/testsuite/unzip.tests
new file mode 100755
index 0000000..8677a03
--- /dev/null
+++ b/busybox-1.19.3/testsuite/unzip.tests
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+# Tests for unzip.
+# Copyright 2006 Rob Landley <rob@landley.net>
+# Copyright 2006 Glenn McGrath
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./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
+
+# Create a scratch directory
+
+mkdir temp
+cd temp
+
+# Create test file to work with.
+
+mkdir foo
+touch foo/bar
+zip foo.zip foo foo/bar > /dev/null
+rm -f foo/bar
+rmdir foo
+
+# Test that unzipping just foo doesn't create bar.
+testing "unzip (subdir only)" "unzip -q foo.zip foo/ && test -d foo && test ! -f foo/bar && echo yes" "yes\n" "" ""
+
+rmdir foo
+rm foo.zip
+
+# Clean up scratch directory.
+
+cd ..
+rm -rf temp
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/uptime/uptime-works b/busybox-1.19.3/testsuite/uptime/uptime-works
new file mode 100644
index 0000000..6b168ab
--- /dev/null
+++ b/busybox-1.19.3/testsuite/uptime/uptime-works
@@ -0,0 +1 @@
+busybox uptime
diff --git a/busybox-1.19.3/testsuite/uuencode.tests b/busybox-1.19.3/testsuite/uuencode.tests
new file mode 100755
index 0000000..cd6191b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/uuencode.tests
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# unit test for uuencode to test functionality.
+# Copyright 2006 by Erik Hovland <erik@hovland.org>
+# 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
+
+# Test setup of standard input
+umask 0
+testing "uuencode sets standard input mode correctly" \
+        "uuencode foo </dev/null | head -n 1 | grep -q 666 && echo yes" "yes\n" "" ""
+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" \
+        "" "The fast grey fox jumped over the lazy brown dog.\n"
+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"
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/wc/wc-counts-all b/busybox-1.19.3/testsuite/wc/wc-counts-all
new file mode 100644
index 0000000..7083645
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wc/wc-counts-all
@@ -0,0 +1,2 @@
+# 1 line, 4 words, 20 chars.
+test "`echo i\'m a little teapot | busybox wc | sed 's/  */ /g' | sed 's/^ //'`" = '1 4 20'
diff --git a/busybox-1.19.3/testsuite/wc/wc-counts-characters b/busybox-1.19.3/testsuite/wc/wc-counts-characters
new file mode 100644
index 0000000..7558646
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wc/wc-counts-characters
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -c` -eq 20
diff --git a/busybox-1.19.3/testsuite/wc/wc-counts-lines b/busybox-1.19.3/testsuite/wc/wc-counts-lines
new file mode 100644
index 0000000..5be6ed0
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wc/wc-counts-lines
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -l` -eq 1
diff --git a/busybox-1.19.3/testsuite/wc/wc-counts-words b/busybox-1.19.3/testsuite/wc/wc-counts-words
new file mode 100644
index 0000000..331650e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wc/wc-counts-words
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -w` -eq 4
diff --git a/busybox-1.19.3/testsuite/wc/wc-prints-longest-line-length b/busybox-1.19.3/testsuite/wc/wc-prints-longest-line-length
new file mode 100644
index 0000000..78831fc
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wc/wc-prints-longest-line-length
@@ -0,0 +1 @@
+test `echo i\'m a little teapot | busybox wc -L` -eq 19
diff --git a/busybox-1.19.3/testsuite/wget/wget--O-overrides--P b/busybox-1.19.3/testsuite/wget/wget--O-overrides--P
new file mode 100644
index 0000000..40a3a96
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wget/wget--O-overrides--P
@@ -0,0 +1,5 @@
+test x"$SKIP_INTERNET_TESTS" != x"" && exit
+
+mkdir foo
+busybox wget -q -O index.html -P foo http://www.google.com/
+test -s index.html
diff --git a/busybox-1.19.3/testsuite/wget/wget-handles-empty-path b/busybox-1.19.3/testsuite/wget/wget-handles-empty-path
new file mode 100644
index 0000000..01d60bd
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wget/wget-handles-empty-path
@@ -0,0 +1,3 @@
+test x"$SKIP_INTERNET_TESTS" != x"" && exit
+
+busybox wget http://www.google.com
diff --git a/busybox-1.19.3/testsuite/wget/wget-retrieves-google-index b/busybox-1.19.3/testsuite/wget/wget-retrieves-google-index
new file mode 100644
index 0000000..f9dbb8b
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wget/wget-retrieves-google-index
@@ -0,0 +1,4 @@
+test x"$SKIP_INTERNET_TESTS" != x"" && exit
+
+busybox wget -q -O foo http://www.google.com/
+test -s foo
diff --git a/busybox-1.19.3/testsuite/wget/wget-supports--P b/busybox-1.19.3/testsuite/wget/wget-supports--P
new file mode 100644
index 0000000..bfe4ac4
--- /dev/null
+++ b/busybox-1.19.3/testsuite/wget/wget-supports--P
@@ -0,0 +1,5 @@
+test x"$SKIP_INTERNET_TESTS" != x"" && exit
+
+mkdir foo
+busybox wget -q -P foo http://www.google.com/
+test -s foo/index.html
diff --git a/busybox-1.19.3/testsuite/which/which-uses-default-path b/busybox-1.19.3/testsuite/which/which-uses-default-path
new file mode 100644
index 0000000..63ceb9f
--- /dev/null
+++ b/busybox-1.19.3/testsuite/which/which-uses-default-path
@@ -0,0 +1,4 @@
+BUSYBOX=$(type -p busybox)
+SAVED_PATH=$PATH
+unset PATH
+$BUSYBOX which ls
diff --git a/busybox-1.19.3/testsuite/xargs.tests b/busybox-1.19.3/testsuite/xargs.tests
new file mode 100755
index 0000000..2d0a201
--- /dev/null
+++ b/busybox-1.19.3/testsuite/xargs.tests
@@ -0,0 +1,44 @@
+#!/bin/sh
+# Copyright 2008 by Denys Vlasenko
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+. ./testing.sh
+
+# testing "test name" "command" "expected result" "file input" "stdin"
+
+testing "xargs -E _ stops on underscore" \
+	"xargs -E _" \
+	"a\n" \
+	"" "a\n_\nb\n"
+
+testing "xargs -E ''" \
+	"xargs -E ''" \
+	"a _ b\n" \
+	"" "a\n_\nb\n"
+
+testing "xargs -e without param" \
+	"xargs -e" \
+	"a _ b\n" \
+	"" "a\n_\nb\n"
+
+testing "xargs does not stop on underscore ('new' GNU behavior)" \
+	"xargs" \
+	"a _ b\n" \
+	"" "a\n_\nb\n"
+
+testing "xargs -s7 can take one-char input" \
+	"xargs -s7 echo" \
+	"a\n" \
+	"" "a\n"
+
+testing "xargs -sNUM test 1" \
+	"xargs -ts25 echo 2>&1 >/dev/null" \
+	"echo 1 2 3 4 5 6 7 8 9 0\n""echo 1 2 3 4 5 6 7 8 9\n""echo 00\n" \
+	"" "1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 00\n"
+
+testing "xargs -sNUM test 2" \
+	"xargs -ts25 echo 1 2>&1 >/dev/null" \
+	"echo 1 2 3 4 5 6 7 8 9 0\n""echo 1 2 3 4 5 6 7 8 9\n""echo 1 00\n" \
+	"" "2 3 4 5 6 7 8 9 0 2 3 4 5 6 7 8 9 00\n"
+
+exit $FAILCOUNT
diff --git a/busybox-1.19.3/testsuite/xargs/xargs-works b/busybox-1.19.3/testsuite/xargs/xargs-works
new file mode 100644
index 0000000..c95869e
--- /dev/null
+++ b/busybox-1.19.3/testsuite/xargs/xargs-works
@@ -0,0 +1,4 @@
+[ -n "$d" ] || d=..
+find "$d" -name \*works -type f | xargs md5sum > logfile.gnu
+find "$d" -name \*works -type f | busybox xargs md5sum > logfile.bb
+diff -u logfile.gnu logfile.bb
diff --git a/busybox-1.19.3/util-linux/Config.src b/busybox-1.19.3/util-linux/Config.src
new file mode 100644
index 0000000..bb45705
--- /dev/null
+++ b/busybox-1.19.3/util-linux/Config.src
@@ -0,0 +1,975 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Linux System Utilities"
+
+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
+	  used and _is_ a standard) or in binary form from specified evdevs
+	  (just use /dev/input/event*).
+
+	  It parses the event to retrieve ACTION and a possible PARAMETER.
+	  It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
+	  (if the resulting path is a directory) or directly as an executable.
+
+	  N.B. acpid relies on run-parts so have the latter installed.
+
+config FEATURE_ACPID_COMPAT
+	bool "Accept and ignore redundant options"
+	default y
+	depends on ACPID
+	help
+	  Accept and ignore compatibility options -g -m -s -S -v.
+
+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
+	  the kernel ring buffer. You can use dmesg to print the kernel's ring
+	  buffer, clear the kernel ring buffer, change the size of the kernel
+	  ring buffer, and change the priority level at which kernel messages
+	  are also logged to the system console. Enable this option if you
+	  wish to enable the 'dmesg' utility.
+
+config FEATURE_DMESG_PRETTY
+	bool "Pretty dmesg output"
+	default y
+	depends on DMESG
+	help
+	  If you wish to scrub the syslog level from the output, say 'Y' here.
+	  The syslog level is a string prefixed to every line with the form
+	  "<#>".
+
+	  With this option you will see:
+	    # dmesg
+	    Linux version 2.6.17.4 .....
+	    BIOS-provided physical RAM map:
+	     BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+
+	  Without this option you will see:
+	    # dmesg
+	    <5>Linux version 2.6.17.4 .....
+	    <6>BIOS-provided physical RAM map:
+	    <6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
+
+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
+	  interface to access a graphics display. Enable this option
+	  if you wish to enable the 'fbset' utility.
+
+config FEATURE_FBSET_FANCY
+	bool "Turn on extra fbset options"
+	default y
+	depends on FBSET
+	help
+	  This option enables extended fbset options, allowing one to set the
+	  framebuffer size, color depth, etc. interface to access a graphics
+	  display. Enable this option if you wish to enable extended fbset
+	  options.
+
+config FEATURE_FBSET_READMODE
+	bool "Turn on fbset readmode support"
+	default y
+	depends on FBSET
+	help
+	  This option allows fbset to read the video mode database stored by
+	  default as /etc/fb.modes, which can be used to set frame buffer
+	  device to pre-defined video modes.
+
+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
+	  hardware disk-change switch has been actuated, which causes Linux to
+	  forget anything it has cached from the previous media. If you have
+	  such a slightly-broken drive, you will need to run fdflush every time
+	  you change a disk. Most people have working hardware and can safely
+	  leave this disabled.
+
+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
+	  can be used to list and edit the set of partitions or BSD style
+	  'disk slices' that are defined on a hard drive.
+
+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.
+
+config FEATURE_FDISK_WRITABLE
+	bool "Write support"
+	default y
+	depends on FDISK
+	help
+	  Enabling this option allows you to create or change a partition table
+	  and write those changes out to disk. If you leave this option
+	  disabled, you will only be able to view the partition table.
+
+config FEATURE_AIX_LABEL
+	bool "Support AIX disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to create or change AIX disklabels.
+	  Most people can safely leave this option disabled.
+
+config FEATURE_SGI_LABEL
+	bool "Support SGI disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to create or change SGI disklabels.
+	  Most people can safely leave this option disabled.
+
+config FEATURE_SUN_LABEL
+	bool "Support SUN disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to create or change SUN disklabels.
+	  Most people can safely leave this option disabled.
+
+config FEATURE_OSF_LABEL
+	bool "Support BSD disklabels"
+	default n
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  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
+	depends on FDISK && FEATURE_FDISK_WRITABLE
+	help
+	  Enabling this option allows you to do terribly unsafe things like
+	  define arbitrary drive geometry, move the beginning of data in a
+	  partition, and similarly evil things. Unless you have a very good
+	  reason you would be wise to leave this disabled.
+
+config FINDFS
+	bool "findfs"
+	default y
+	select PLATFORM_LINUX
+	select VOLUMEID
+	help
+	  Prints the name of a filesystem with given label or UUID.
+	  WARNING:
+	  With all submodules selected, it will add ~8k to busybox.
+
+config FLOCK
+	bool "flock"
+	default y
+	help
+	  Manage locks from shell scripts
+
+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
+	  ramdisk. For example, if you boot Linux into a ramdisk and later
+	  pivot_root, you may want to free the memory that is allocated to the
+	  ramdisk. If you have no use for freeing memory from a ramdisk, leave
+	  this disabled.
+
+config FSCK_MINIX
+	bool "fsck_minix"
+	default y
+	help
+	  The minix filesystem is a nice, small, compact, read-write filesystem
+	  with little overhead. It is not a journaling filesystem however and
+	  can experience corruption if it is not properly unmounted or if the
+	  power goes off in the middle of a write. This utility allows you to
+	  check for and attempt to repair any corruption that occurs to a minix
+	  filesystem.
+
+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
+	  filesystems this utility will do the job for you.
+
+config FEATURE_MINIX2
+	bool "Support Minix fs v2 (fsck_minix/mkfs_minix)"
+	default y
+	depends on FSCK_MINIX || MKFS_MINIX
+	help
+	  If you wish to be able to create version 2 minix filesystems, enable
+	  this. If you enabled 'mkfs_minix' then you almost certainly want to
+	  be using the version 2 filesystem support.
+
+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.
+
+config MKFS_VFAT
+	bool "mkfs_vfat"
+	default y
+	select PLATFORM_LINUX
+	help
+	  Utility to create FAT32 filesystems.
+
+config GETOPT
+	bool "getopt"
+	default y
+	help
+	  The getopt utility is used to break up (parse) options in command
+	  lines to make it easy to write complex shell scripts that also check
+	  for legal (and illegal) options. If you want to write horribly
+	  complex shell scripts, or use some horribly complex shell script
+	  written by others, this utility may be for you. Most people will
+	  wisely leave this disabled.
+
+config FEATURE_GETOPT_LONG
+	bool "Support option -l"
+	default y if LONG_OPTS
+	depends on GETOPT
+	help
+	  Enable support for long options (option -l).
+
+config HEXDUMP
+	bool "hexdump"
+	default y
+	help
+	  The hexdump utility is used to display binary data in a readable
+	  way that is comparable to the output from most hex editors.
+
+config FEATURE_HEXDUMP_REVERSE
+	bool "Support -R, reverse of 'hexdump -Cv'"
+	default y
+	depends on HEXDUMP
+	help
+	  The hexdump utility is used to display binary data in an ascii
+	  readable way. This option creates binary data from an ascii input.
+	  NB: this option is non-standard. It's unwise to use it in scripts
+	  aimed to be portable.
+
+config HD
+	bool "hd"
+	default y
+	depends on HEXDUMP
+	help
+	  hd is an alias to hexdump -C.
+
+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
+	  shutdown in the hardware clock, so the hardware will keep the
+	  correct time when Linux is _not_ running.
+
+config FEATURE_HWCLOCK_LONG_OPTIONS
+	bool "Support long options (--hctosys,...)"
+	default y
+	depends on HWCLOCK && LONG_OPTS
+	help
+	  By default, the hwclock utility only uses short options. If you
+	  are overly fond of its long options, such as --hctosys, --utc, etc)
+	  then enable this option.
+
+config FEATURE_HWCLOCK_ADJTIME_FHS
+	bool "Use FHS /var/lib/hwclock/adjtime"
+	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
+	  at /var/lib/hwclock/adjtime instead of /etc/adjtime. If you wish
+	  to use the FHS behavior, answer Y here, otherwise answer N for the
+	  classic /etc/adjtime path.
+
+	  pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO
+
+config IPCRM
+	bool "ipcrm"
+	default y
+	help
+	  The ipcrm utility allows the removal of System V interprocess
+	  communication (IPC) objects and the associated data structures
+	  from the system.
+
+config IPCS
+	bool "ipcs"
+	default y
+	select PLATFORM_LINUX
+	help
+	  The ipcs utility is used to provide information on the currently
+	  allocated System V interprocess (IPC) objects in the system.
+
+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
+	  version does not currently support enabling data encryption.
+
+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.
+
+	  This version uses sysfs (/sys/bus/pci/devices) only.
+
+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
+	select PLATFORM_LINUX
+	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
+	help
+	  The mkswap utility is used to configure a file or disk partition as
+	  Linux swap space. This allows Linux to use the entire file or
+	  partition as if it were additional RAM, which can greatly increase
+	  the capability of low-memory machines. This additional memory is
+	  much slower than real RAM, but can be very helpful at preventing your
+	  applications being killed by the Linux out of memory (OOM) killer.
+	  Once you have created swap space using 'mkswap' you need to enable
+	  the swap space using the 'swapon' utility.
+
+config FEATURE_MKSWAP_UUID
+	bool "UUID support"
+	default y
+	depends on MKSWAP
+	help
+	  Generate swap spaces with universally unique identifiers.
+
+config MORE
+	bool "more"
+	default y
+	help
+	  more is a simple utility which allows you to read text one screen
+	  sized page at a time. If you want to read text that is larger than
+	  the screen, and you are using anything faster than a 300 baud modem,
+	  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 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
+	  particular part of the tree. A filesystem can either live on a block
+	  device, or it can be accessible over the network, as is the case with
+	  NFS filesystems. Most people using BusyBox will also want to enable
+	  the 'mount' utility.
+
+config FEATURE_MOUNT_FAKE
+	bool "Support option -f"
+	default y
+	depends on MOUNT
+	help
+	  Enable support for faking a file system mount.
+
+config FEATURE_MOUNT_VERBOSE
+	bool "Support option -v"
+	default y
+	depends on MOUNT
+	help
+	  Enable multi-level -v[vv...] verbose messages. Useful if you
+	  debug mount problems and want to see what is exactly passed
+	  to the kernel.
+
+config FEATURE_MOUNT_HELPERS
+	bool "Support mount helpers"
+	default n
+	depends on MOUNT
+	help
+	  Enable mounting of virtual file systems via external helpers.
+	  E.g. "mount obexfs#-b00.11.22.33.44.55 /mnt" will in effect call
+	  "obexfs -b00.11.22.33.44.55 /mnt"
+	  Also "mount -t sometype [-o opts] fs /mnt" will try
+	  "sometype [-o opts] fs /mnt" if simple mount syscall fails.
+	  The idea is to use such virtual filesystems in /etc/fstab.
+
+config FEATURE_MOUNT_LABEL
+	bool "Support specifying devices by label or UUID"
+	default y
+	depends on MOUNT
+	select VOLUMEID
+	help
+	  This allows for specifying a device by label or uuid, rather than by
+	  name. This feature utilizes the same functionality as blkid/findfs.
+	  This also enables label or uuid support for swapon.
+
+config FEATURE_MOUNT_NFS
+	bool "Support mounting NFS file systems"
+	default y
+	depends on MOUNT
+	select FEATURE_HAVE_RPC
+	select FEATURE_SYSLOG
+	help
+	  Enable mounting of NFS file systems.
+
+config FEATURE_MOUNT_CIFS
+	bool "Support mounting CIFS/SMB file systems"
+	default y
+	depends on MOUNT
+	help
+	  Enable support for samba mounts.
+
+config FEATURE_MOUNT_FLAGS
+	depends on MOUNT
+	bool "Support lots of -o flags in mount"
+	default y
+	help
+	  Without this, mount only supports ro/rw/remount. With this, it
+	  supports nosuid, suid, dev, nodev, exec, noexec, sync, async, atime,
+	  noatime, diratime, nodiratime, loud, bind, move, shared, slave,
+	  private, unbindable, rshared, rslave, rprivate, and runbindable.
+
+config FEATURE_MOUNT_FSTAB
+	depends on MOUNT
+	bool "Support /etc/fstab and -a"
+	default y
+	help
+	  Support mount all and looking for files in /etc/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
+	  of wild and crazy things with your Linux system and is far more
+	  powerful than 'chroot'.
+
+	  Note: This is for initrd in linux 2.4. Under initramfs (introduced
+	  in linux 2.6) use switch_root instead.
+
+config RDATE
+	bool "rdate"
+	default y
+	help
+	  The rdate utility allows you to synchronize the date and time of your
+	  system clock with the date and time of a remote networked system using
+	  the RFC868 protocol, which is built into the inetd daemon on most
+	  systems.
+
+config RDEV
+	bool "rdev"
+	default y
+	help
+	  Print the device node associated with the filesystem mounted at '/'.
+
+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.
+
+config SCRIPT
+	bool "script"
+	default y
+	help
+	  The script makes typescript of terminal session.
+
+config SCRIPTREPLAY
+	bool "scriptreplay"
+	default y
+	help
+	  This program replays a typescript, using timing information
+	  given by script -t.
+
+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
+	  this util on a system that supports both 64bit and 32bit userland
+	  (like amd64/x86, ppc64/ppc, sparc64/sparc, etc...).
+
+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
+	  to enable your swap space with the 'swapon' utility. The 'swapoff'
+	  utility is used, typically at system shutdown, to disable any swap
+	  space. If you are not using any swap space, you can leave this
+	  option disabled.
+
+config FEATURE_SWAPON_PRI
+	bool "Support priority option -p"
+	default y
+	depends on SWAPONOFF
+	help
+	  Enable support for setting swap device priority in swapon.
+
+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
+	  pivot_root. (Stop reading here if you don't care why.)
+
+	  Booting with initramfs extracts a gzipped cpio archive into rootfs
+	  (which is a variant of ramfs/tmpfs). Because rootfs can't be moved
+	  or unmounted*, pivot_root will not work from initramfs. Instead,
+	  switch_root deletes everything out of rootfs (including itself),
+	  does a mount --move that overmounts rootfs with the new root, and
+	  then execs the specified init program.
+
+	  * Because the Linux kernel uses rootfs internally as the starting
+	  and ending point for searching through the kernel's doubly linked
+	  list of active mount points. That's why.
+
+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
+	  'umount' utility is the tool to use. If you enabled the 'mount'
+	  utility, you almost certainly also want to enable 'umount'.
+
+config FEATURE_UMOUNT_ALL
+	bool "Support option -a"
+	default y
+	depends on UMOUNT
+	help
+	  Support -a option to unmount all currently mounted filesystems.
+
+comment "Common options for mount/umount"
+	depends on MOUNT || UMOUNT
+
+config FEATURE_MOUNT_LOOP
+	bool "Support loopback mounts"
+	default y
+	depends on MOUNT || UMOUNT
+	help
+	  Enabling this feature allows automatic mounting of files (containing
+	  filesystem images) via the linux kernel's loopback devices.
+	  The mount command will detect you are trying to mount a file instead
+	  of a block device, and transparently associate the file with a
+	  loopback device. The umount command will also free that loopback
+	  device.
+
+	  You can still use the 'losetup' utility (to manually associate files
+	  with loop devices) if you need to do something advanced, such as
+	  specify an offset or cryptographic options to the loopback device.
+	  (If you don't want umount to free the loop device, use "umount -D".)
+
+config FEATURE_MOUNT_LOOP_CREATE
+	bool "Create new loopback devices if needed"
+	default y
+	depends on FEATURE_MOUNT_LOOP
+	help
+	  Linux kernels >= 2.6.24 support unlimited loopback devices. They are
+	  allocated for use when trying to use a loop device. The loop device
+	  must however exist.
+
+	  This feature lets mount to try to create next /dev/loopN device
+	  if it does not find a free one.
+
+config FEATURE_MTAB_SUPPORT
+	bool "Support for the old /etc/mtab file"
+	default n
+	depends on MOUNT || UMOUNT
+	select FEATURE_MOUNT_FAKE
+	help
+	  Historically, Unix systems kept track of the currently mounted
+	  partitions in the file "/etc/mtab". These days, the kernel exports
+	  the list of currently mounted partitions in "/proc/mounts", rendering
+	  the old mtab file obsolete. (In modern systems, /etc/mtab should be
+	  a symlink to /proc/mounts.)
+
+	  The only reason to have mount maintain an /etc/mtab file itself is if
+	  your stripped-down embedded system does not have a /proc directory.
+	  If you must use this, keep in mind it's inherently brittle (for
+	  example a mount under chroot won't update it), can't handle modern
+	  features like separate per-process filesystem namespaces, requires
+	  that your /etc directory be writable, tends to get easily confused
+	  by --bind or --move mounts, won't update if you rename a directory
+	  that contains a mount point, and so on. (In brief: avoid.)
+
+	  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
+
+endmenu
diff --git a/busybox-1.19.3/util-linux/Kbuild.src b/busybox-1.19.3/util-linux/Kbuild.src
new file mode 100644
index 0000000..c06d911
--- /dev/null
+++ b/busybox-1.19.3/util-linux/Kbuild.src
@@ -0,0 +1,47 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under GPLv2, see file LICENSE in this source tree.
+
+lib-y:=
+
+INSERT
+lib-$(CONFIG_ACPID)             += acpid.o
+lib-$(CONFIG_BLKID)             += blkid.o
+lib-$(CONFIG_DMESG)             += dmesg.o
+lib-$(CONFIG_FBSET)             += fbset.o
+lib-$(CONFIG_FDFLUSH)           += freeramdisk.o
+lib-$(CONFIG_FDFORMAT)          += fdformat.o
+lib-$(CONFIG_FDISK)             += fdisk.o
+lib-$(CONFIG_FINDFS)            += findfs.o
+lib-$(CONFIG_FLOCK)             += flock.o
+lib-$(CONFIG_FREERAMDISK)       += freeramdisk.o
+lib-$(CONFIG_FSCK_MINIX)        += fsck_minix.o
+lib-$(CONFIG_GETOPT)            += getopt.o
+lib-$(CONFIG_HEXDUMP)           += hexdump.o
+lib-$(CONFIG_HWCLOCK)           += hwclock.o
+lib-$(CONFIG_IPCRM)             += ipcrm.o
+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
+lib-$(CONFIG_MKFS_VFAT)         += mkfs_vfat.o
+lib-$(CONFIG_MKSWAP)            += mkswap.o
+lib-$(CONFIG_MORE)              += more.o
+lib-$(CONFIG_MOUNT)             += mount.o
+lib-$(CONFIG_PIVOT_ROOT)        += pivot_root.o
+lib-$(CONFIG_RDATE)             += rdate.o
+lib-$(CONFIG_RDEV)              += rdev.o
+lib-$(CONFIG_READPROFILE)       += readprofile.o
+lib-$(CONFIG_RTCWAKE)           += rtcwake.o
+lib-$(CONFIG_SCRIPT)            += script.o
+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
diff --git a/busybox-1.19.3/util-linux/acpid.c b/busybox-1.19.3/util-linux/acpid.c
new file mode 100644
index 0000000..6e7321b
--- /dev/null
+++ b/busybox-1.19.3/util-linux/acpid.c
@@ -0,0 +1,337 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * simple ACPI events listener
+ *
+ * Copyright (C) 2008 by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define acpid_trivial_usage
+//usage:       "[-d] [-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	-c DIR	Config directory [/etc/acpi]"
+//usage:     "\n	-d	Don't daemonize, (implies -f)"
+//usage:     "\n	-e FILE	/proc event file [/proc/acpi/event]"
+//usage:     "\n	-f	Run in foreground"
+//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
+#ifndef EV_KEY
+# define EV_KEY        0x01
+#endif
+#ifndef SW_LID
+# define SW_LID        0x00
+#endif
+#ifndef SW_RFKILL_ALL
+# define SW_RFKILL_ALL 0x03
+#endif
+#ifndef KEY_POWER
+# define KEY_POWER      116     /* SC System Power Down */
+#endif
+#ifndef KEY_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" },
+};
+
+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
+ * from /proc/acpi/event (though it is marked deprecated,
+ * it is still widely used and _is_ a standard) or in binary form
+ * from specified evdevs (just use /dev/input/event*).
+ * It parses the event to retrieve ACTION and a possible PARAMETER.
+ * It then spawns /etc/acpi/<ACTION>[/<PARAMETER>] either via run-parts
+ * (if the resulting path is a directory) or directly.
+ * If the resulting path does not exist it logs it via perror
+ * and continues listening.
+ */
+
+static void process_event(const char *event)
+{
+	struct stat st;
+	char *handler = xasprintf("./%s", event);
+	const char *args[] = { "run-parts", handler, NULL };
+
+	// debug info
+	if (option_mask32 & OPT_d) {
+		bb_error_msg("%s", event);
+	}
+
+	// spawn handler
+	// N.B. run-parts would require scripts to have #!/bin/sh
+	// handler is directory? -> use run-parts
+	// handler is file? -> run it directly
+	if (0 == stat(event, &st))
+		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] [-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 UNUSED_PARAM, char **argv)
+{
+	struct input_event ev;
+	int nfd;
+	int opts;
+	struct pollfd *pfd;
+	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 = "/var/run/acpid.pid";
+#endif
+
+	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)
+	);
+
+	if (!(opts & OPT_f)) {
+		bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv);
+	}
+
+	if (!(opts & OPT_d)) {
+		openlog(applet_name, LOG_PID, LOG_DAEMON);
+		logmode = LOGMODE_SYSLOG | LOGMODE_STDIO;
+	} else {
+		xmove_fd(xopen(opt_logfile, O_WRONLY | O_CREAT | O_TRUNC), STDOUT_FILENO);
+	}
+
+	parse_conf_file(opt_action);
+	parse_map_file(opt_map);
+
+	xchdir(opt_dir);
+
+	bb_signals((1 << SIGCHLD), SIG_IGN);
+	bb_signals(BB_FATAL_SIGS, record_signo);
+
+	pfd = NULL;
+	nfd = 0;
+	while (1) {
+		int fd;
+		char *dev_event;
+
+		dev_event = xasprintf((option_mask32 & 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;
+		}
+		pfd = xrealloc_vector(pfd, 1, nfd);
+		pfd[nfd].fd = fd;
+		pfd[nfd].events = POLLIN;
+		nfd++;
+	}
+
+	write_pidfile(opt_pidfile);
+
+	while (poll(pfd, nfd, -1) > 0) {
+		int i;
+		for (i = 0; i < nfd; i++) {
+			const char *event = NULL;
+
+			memset(&ev, 0, sizeof(ev));
+
+			if (!(pfd[i].revents & POLLIN))
+				continue;
+
+			if (option_mask32 & OPT_e) {
+				char *buf;
+				int len;
+
+				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);
+			} else {
+				if (sizeof(ev) != full_read(pfd[i].fd, &ev, sizeof(ev)))
+					continue;
+
+				if (ev.value != 1 && ev.value != 0)
+					continue;
+
+				event = find_action(&ev, NULL);
+			}
+			if (!event)
+				continue;
+			// spawn event handler
+			process_event(event);
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		while (nfd--) {
+			if (pfd[nfd].fd) {
+				close(pfd[nfd].fd);
+			}
+		}
+		free(pfd);
+	}
+	remove_pidfile(opt_pidfile);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/blkid.c b/busybox-1.19.3/util-linux/blkid.c
new file mode 100644
index 0000000..c30360c
--- /dev/null
+++ b/busybox-1.19.3/util-linux/blkid.c
@@ -0,0 +1,30 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Print UUIDs on all filesystems
+ *
+ * Copyright (C) 2008 Denys Vlasenko.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define blkid_trivial_usage
+//usage:       ""
+//usage:#define blkid_full_usage "\n\n"
+//usage:       "Print UUIDs of all filesystems"
+
+#include "libbb.h"
+#include "volume_id.h"
+
+//TODO: extend to take BLOCKDEV args, and show TYPE="fstype"
+
+int blkid_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int blkid_main(int argc UNUSED_PARAM, char **argv)
+{
+	while (*++argv) {
+		/* Note: bogus device names don't cause any error messages */
+		add_to_uuid_cache(*argv);
+	}
+
+	display_uuid_cache();
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/blockdev.c b/busybox-1.19.3/util-linux/blockdev.c
new file mode 100644
index 0000000..e25e529
--- /dev/null
+++ b/busybox-1.19.3/util-linux/blockdev.c
@@ -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;
+}
diff --git a/busybox-1.19.3/util-linux/dmesg.c b/busybox-1.19.3/util-linux/dmesg.c
new file mode 100644
index 0000000..6505da5
--- /dev/null
+++ b/busybox-1.19.3/util-linux/dmesg.c
@@ -0,0 +1,87 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *
+ * dmesg - display/control kernel ring buffer.
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ * Copyright 2006 Bernhard Reutner-Fischer <rep.nop@aon.at>
+ *
+ * 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"
+
+int dmesg_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int dmesg_main(int argc UNUSED_PARAM, char **argv)
+{
+	int len, level;
+	char *buf;
+	unsigned opts;
+	enum {
+		OPT_c = 1 << 0,
+		OPT_s = 1 << 1,
+		OPT_n = 1 << 2
+	};
+
+	opt_complementary = "s+:n+"; /* numeric */
+	opts = getopt32(argv, "cs:n:", &len, &level);
+	if (opts & OPT_n) {
+		if (klogctl(8, NULL, (long) level))
+			bb_perror_msg_and_die("klogctl");
+		return EXIT_SUCCESS;
+	}
+
+	if (!(opts & OPT_s))
+		len = klogctl(10, NULL, 0); /* read ring buffer size */
+	if (len < 16*1024)
+		len = 16*1024;
+	if (len > 16*1024*1024)
+		len = 16*1024*1024;
+
+	buf = xmalloc(len);
+	len = klogctl(3 + (opts & OPT_c), buf, len); /* read ring buffer */
+	if (len < 0)
+		bb_perror_msg_and_die("klogctl");
+	if (len == 0)
+		return EXIT_SUCCESS;
+
+
+	if (ENABLE_FEATURE_DMESG_PRETTY) {
+		int last = '\n';
+		int in = 0;
+
+		/* Skip <#> at the start of lines */
+		while (1) {
+			if (last == '\n' && buf[in] == '<') {
+				in += 3;
+				if (in >= len)
+					break;
+			}
+			last = buf[in];
+			putchar(last);
+			in++;
+			if (in >= len)
+				break;
+		}
+		/* Make sure we end with a newline */
+		if (last != '\n')
+			bb_putchar('\n');
+	} else {
+		full_write(STDOUT_FILENO, buf, len);
+		if (buf[len-1] != '\n')
+			bb_putchar('\n');
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) free(buf);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/fbset.c b/busybox-1.19.3/util-linux/fbset.c
new file mode 100644
index 0000000..196c2aa
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fbset.c
@@ -0,0 +1,511 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini fbset implementation for busybox
+ *
+ * Copyright (C) 1999 by Randolph Chung <tausq@debian.org>
+ *
+ * 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
+ * the GPL, and is (c) 1995-1999 by:
+ *     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
+#define DEFAULTFBMODE "/etc/fb.modes"
+
+/* Stuff stolen from the kernel's fb.h */
+#define FB_ACTIVATE_ALL 64
+enum {
+	FBIOGET_VSCREENINFO = 0x4600,
+	FBIOPUT_VSCREENINFO = 0x4601
+};
+
+struct fb_bitfield {
+	uint32_t offset;                /* beginning of bitfield */
+	uint32_t length;                /* length of bitfield */
+	uint32_t msb_right;             /* !=0: Most significant bit is right */
+};
+struct fb_var_screeninfo {
+	uint32_t xres;                  /* visible resolution */
+	uint32_t yres;
+	uint32_t xres_virtual;          /* virtual resolution */
+	uint32_t yres_virtual;
+	uint32_t xoffset;               /* offset from virtual to visible */
+	uint32_t yoffset;               /* resolution */
+
+	uint32_t bits_per_pixel;
+	uint32_t grayscale;             /* !=0 Graylevels instead of colors */
+
+	struct fb_bitfield red;         /* bitfield in fb mem if true color, */
+	struct fb_bitfield green;       /* else only length is significant */
+	struct fb_bitfield blue;
+	struct fb_bitfield transp;      /* transparency */
+
+	uint32_t nonstd;                /* !=0 Non standard pixel format */
+
+	uint32_t activate;              /* see FB_ACTIVATE_x */
+
+	uint32_t height;                /* height of picture in mm */
+	uint32_t width;                 /* width of picture in mm */
+
+	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) */
+	uint32_t left_margin;           /* time from sync to picture */
+	uint32_t right_margin;          /* time from picture to sync */
+	uint32_t upper_margin;          /* time from sync to picture */
+	uint32_t lower_margin;
+	uint32_t hsync_len;             /* length of horizontal sync */
+	uint32_t vsync_len;             /* length of vertical sync */
+	uint32_t sync;                  /* see FB_SYNC_x */
+	uint32_t vmode;                 /* see FB_VMODE_x */
+	uint32_t reserved[6];           /* Reserved for future compatibility */
+};
+
+static void copy_if_gt0(uint32_t *src, uint32_t *dst, unsigned cnt)
+{
+	do {
+		if ((int32_t) *src > 0)
+			*dst = *src;
+		src++;
+		dst++;
+	} while (--cnt);
+}
+
+static NOINLINE void copy_changed_values(
+		struct fb_var_screeninfo *base,
+		struct fb_var_screeninfo *set)
+{
+	//if ((int32_t) set->xres > 0) base->xres = set->xres;
+	//if ((int32_t) set->yres > 0) base->yres = set->yres;
+	//if ((int32_t) set->xres_virtual > 0)   base->xres_virtual = set->xres_virtual;
+	//if ((int32_t) set->yres_virtual > 0)   base->yres_virtual = set->yres_virtual;
+	copy_if_gt0(&set->xres, &base->xres, 4);
+
+	if ((int32_t) set->bits_per_pixel > 0) base->bits_per_pixel = set->bits_per_pixel;
+	//copy_if_gt0(&set->bits_per_pixel, &base->bits_per_pixel, 1);
+
+	//if ((int32_t) set->pixclock > 0)       base->pixclock = set->pixclock;
+	//if ((int32_t) set->left_margin > 0)    base->left_margin = set->left_margin;
+	//if ((int32_t) set->right_margin > 0)   base->right_margin = set->right_margin;
+	//if ((int32_t) set->upper_margin > 0)   base->upper_margin = set->upper_margin;
+	//if ((int32_t) set->lower_margin > 0)   base->lower_margin = set->lower_margin;
+	//if ((int32_t) set->hsync_len > 0) base->hsync_len = set->hsync_len;
+	//if ((int32_t) set->vsync_len > 0) base->vsync_len = set->vsync_len;
+	//if ((int32_t) set->sync > 0)  base->sync = set->sync;
+	//if ((int32_t) set->vmode > 0) base->vmode = set->vmode;
+	copy_if_gt0(&set->pixclock, &base->pixclock, 9);
+}
+
+
+enum {
+	CMD_FB = 1,
+	CMD_DB = 2,
+	CMD_GEOMETRY = 3,
+	CMD_TIMING = 4,
+	CMD_ACCEL = 5,
+	CMD_HSYNC = 6,
+	CMD_VSYNC = 7,
+	CMD_LACED = 8,
+	CMD_DOUBLE = 9,
+/*	CMD_XCOMPAT =     10, */
+	CMD_ALL = 11,
+	CMD_INFO = 12,
+	CMD_SHOW = 13,
+	CMD_CHANGE = 14,
+
+#if ENABLE_FEATURE_FBSET_FANCY
+	CMD_XRES = 100,
+	CMD_YRES = 101,
+	CMD_VXRES = 102,
+	CMD_VYRES = 103,
+	CMD_DEPTH = 104,
+	CMD_MATCH = 105,
+	CMD_PIXCLOCK = 106,
+	CMD_LEFT = 107,
+	CMD_RIGHT = 108,
+	CMD_UPPER = 109,
+	CMD_LOWER = 110,
+	CMD_HSLEN = 111,
+	CMD_VSLEN = 112,
+	CMD_CSYNC = 113,
+	CMD_GSYNC = 114,
+	CMD_EXTSYNC = 115,
+	CMD_BCAST = 116,
+	CMD_RGBA = 117,
+	CMD_STEP = 118,
+	CMD_MOVE = 119,
+#endif
+};
+
+static const struct cmdoptions_t {
+	const char name[9];
+	const unsigned char param_count;
+	const unsigned char code;
+} g_cmdoptions[] = {
+	/*"12345678" + NUL */
+	{ "fb"      , 1, CMD_FB       },
+	{ "db"      , 1, CMD_DB       },
+	{ "a"       , 0, CMD_ALL      },
+	{ "i"       , 0, CMD_INFO     },
+	{ "g"       , 5, CMD_GEOMETRY },
+	{ "t"       , 7, CMD_TIMING   },
+	{ "accel"   , 1, CMD_ACCEL    },
+	{ "hsync"   , 1, CMD_HSYNC    },
+	{ "vsync"   , 1, CMD_VSYNC    },
+	{ "laced"   , 1, CMD_LACED    },
+	{ "double"  , 1, CMD_DOUBLE   },
+	{ "show"    , 0, CMD_SHOW     },
+	{ "s"       , 0, CMD_SHOW     },
+#if ENABLE_FEATURE_FBSET_FANCY
+	{ "all"     , 0, CMD_ALL      },
+	{ "xres"    , 1, CMD_XRES     },
+	{ "yres"    , 1, CMD_YRES     },
+	{ "vxres"   , 1, CMD_VXRES    },
+	{ "vyres"   , 1, CMD_VYRES    },
+	{ "depth"   , 1, CMD_DEPTH    },
+	{ "match"   , 0, CMD_MATCH    },
+	{ "geometry", 5, CMD_GEOMETRY },
+	{ "pixclock", 1, CMD_PIXCLOCK },
+	{ "left"    , 1, CMD_LEFT     },
+	{ "right"   , 1, CMD_RIGHT    },
+	{ "upper"   , 1, CMD_UPPER    },
+	{ "lower"   , 1, CMD_LOWER    },
+	{ "hslen"   , 1, CMD_HSLEN    },
+	{ "vslen"   , 1, CMD_VSLEN    },
+	{ "timings" , 7, CMD_TIMING   },
+	{ "csync"   , 1, CMD_CSYNC    },
+	{ "gsync"   , 1, CMD_GSYNC    },
+	{ "extsync" , 1, CMD_EXTSYNC  },
+	{ "bcast"   , 1, CMD_BCAST    },
+	{ "rgba"    , 1, CMD_RGBA     },
+	{ "step"    , 1, CMD_STEP     },
+	{ "move"    , 1, CMD_MOVE     },
+#endif
+};
+
+/* taken from linux/fb.h */
+enum {
+	FB_SYNC_HOR_HIGH_ACT = 1,       /* horizontal sync high active */
+	FB_SYNC_VERT_HIGH_ACT = 2,      /* vertical sync high active */
+#if ENABLE_FEATURE_FBSET_READMODE
+	FB_VMODE_INTERLACED = 1,        /* interlaced */
+	FB_VMODE_DOUBLE = 2,            /* double scan */
+	FB_SYNC_EXT = 4,                /* external sync */
+	FB_SYNC_COMP_HIGH_ACT = 8,      /* composite sync high active */
+#endif
+};
+
+#if ENABLE_FEATURE_FBSET_READMODE
+static void ss(uint32_t *x, uint32_t flag, char *buf, const char *what)
+{
+	if (strcmp(buf, what) == 0)
+		*x &= ~flag;
+	else
+		*x |= flag;
+}
+
+/* Mode db file contains mode definitions like this:
+ * mode "800x600-48-lace"
+ *     # D: 36.00 MHz, H: 33.835 kHz, V: 96.39 Hz
+ *     geometry 800 600 800 600 8
+ *     timings 27778 56 80 79 11 128 12
+ *     laced true
+ *     hsync high
+ *     vsync high
+ * endmode
+ */
+static int read_mode_db(struct fb_var_screeninfo *base, const char *fn,
+					const char *mode)
+{
+	char *token[2], *p, *s;
+	parser_t *parser = config_open(fn);
+
+	while (config_read(parser, token, 2, 1, "# \t\r", PARSE_NORMAL)) {
+		if (strcmp(token[0], "mode") != 0 || !token[1])
+			continue;
+		p = strstr(token[1], mode);
+		if (!p)
+			continue;
+		s = p + strlen(mode);
+		//bb_info_msg("CHECK[%s][%s][%d]", mode, p-1, *s);
+		/* exact match? */
+		if (((!*s || isspace(*s)) && '"' != s[-1]) /* end-of-token */
+		 || ('"' == *s && '"' == p[-1]) /* ends with " but starts with " too! */
+		) {
+			//bb_info_msg("FOUND[%s][%s][%s][%d]", token[1], p, mode, isspace(*s));
+			break;
+		}
+	}
+
+	if (!token[0])
+		return 0;
+
+	while (config_read(parser, token, 2, 1, "# \t", PARSE_NORMAL)) {
+		int i;
+
+//bb_info_msg("???[%s][%s]", token[0], token[1]);
+		if (strcmp(token[0], "endmode") == 0) {
+//bb_info_msg("OK[%s]", mode);
+			return 1;
+		}
+		p = token[1];
+		i = index_in_strings(
+			"geometry\0timings\0interlaced\0double\0vsync\0hsync\0csync\0extsync\0",
+			token[0]);
+		switch (i) {
+		case 0:
+			if (sizeof(int) == sizeof(base->xres)) {
+				sscanf(p, "%d %d %d %d %d",
+					&base->xres, &base->yres,
+					&base->xres_virtual, &base->yres_virtual,
+					&base->bits_per_pixel);
+			} else {
+				int base_xres, base_yres;
+				int base_xres_virtual, base_yres_virtual;
+				int base_bits_per_pixel;
+				sscanf(p, "%d %d %d %d %d",
+					&base_xres, &base_yres,
+					&base_xres_virtual, &base_yres_virtual,
+					&base_bits_per_pixel);
+				base->xres = base_xres;
+				base->yres = base_yres;
+				base->xres_virtual = base_xres_virtual;
+				base->yres_virtual = base_yres_virtual;
+				base->bits_per_pixel = base_bits_per_pixel;
+			}
+//bb_info_msg("GEO[%s]", p);
+			break;
+		case 1:
+			if (sizeof(int) == sizeof(base->xres)) {
+				sscanf(p, "%d %d %d %d %d %d %d",
+					&base->pixclock,
+					&base->left_margin, &base->right_margin,
+					&base->upper_margin, &base->lower_margin,
+					&base->hsync_len, &base->vsync_len);
+			} else {
+				int base_pixclock;
+				int base_left_margin, base_right_margin;
+				int base_upper_margin, base_lower_margin;
+				int base_hsync_len, base_vsync_len;
+				sscanf(p, "%d %d %d %d %d %d %d",
+					&base_pixclock,
+					&base_left_margin, &base_right_margin,
+					&base_upper_margin, &base_lower_margin,
+					&base_hsync_len, &base_vsync_len);
+				base->pixclock = base_pixclock;
+				base->left_margin = base_left_margin;
+				base->right_margin = base_right_margin;
+				base->upper_margin = base_upper_margin;
+				base->lower_margin = base_lower_margin;
+				base->hsync_len = base_hsync_len;
+				base->vsync_len = base_vsync_len;
+			}
+//bb_info_msg("TIM[%s]", p);
+			break;
+		case 2:
+		case 3: {
+			static const uint32_t syncs[] = {FB_VMODE_INTERLACED, FB_VMODE_DOUBLE};
+			ss(&base->vmode, syncs[i-2], p, "false");
+//bb_info_msg("VMODE[%s]", p);
+			break;
+		}
+		case 4:
+		case 5:
+		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);
+			break;
+		}
+		case 7:
+			ss(&base->sync, FB_SYNC_EXT, p, "false");
+//bb_info_msg("EXTSYNC[%s]", p);
+			break;
+		}
+	}
+	return 0;
+}
+#endif
+
+static NOINLINE void showmode(struct fb_var_screeninfo *v)
+{
+	double drate = 0, hrate = 0, vrate = 0;
+
+	if (v->pixclock) {
+		drate = 1e12 / v->pixclock;
+		hrate = drate / (v->left_margin + v->xres + v->right_margin + v->hsync_len);
+		vrate = hrate / (v->upper_margin + v->yres + v->lower_margin + v->vsync_len);
+	}
+	printf("\nmode \"%ux%u-%u\"\n"
+#if ENABLE_FEATURE_FBSET_FANCY
+	"\t# D: %.3f MHz, H: %.3f kHz, V: %.3f Hz\n"
+#endif
+	"\tgeometry %u %u %u %u %u\n"
+	"\ttimings %u %u %u %u %u %u %u\n"
+	"\taccel %s\n"
+	"\trgba %u/%u,%u/%u,%u/%u,%u/%u\n"
+	"endmode\n\n",
+		v->xres, v->yres, (int) (vrate + 0.5),
+#if ENABLE_FEATURE_FBSET_FANCY
+		drate / 1e6, hrate / 1e3, vrate,
+#endif
+		v->xres, v->yres, v->xres_virtual, v->yres_virtual, v->bits_per_pixel,
+		v->pixclock, v->left_margin, v->right_margin, v->upper_margin, v->lower_margin,
+			v->hsync_len, v->vsync_len,
+		(v->accel_flags > 0 ? "true" : "false"),
+		v->red.length, v->red.offset, v->green.length, v->green.offset,
+			v->blue.length, v->blue.offset, v->transp.length, v->transp.offset);
+}
+
+int fbset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fbset_main(int argc, char **argv)
+{
+	enum {
+		OPT_CHANGE   = (1 << 0),
+		OPT_SHOW     = (1 << 1),
+		OPT_READMODE = (1 << 2),
+		OPT_ALL      = (1 << 3),
+	};
+	struct fb_var_screeninfo var_old, var_set;
+	int fh, i;
+	unsigned options = 0;
+
+	const char *fbdev = DEFAULTFBDEV;
+	const char *modefile = DEFAULTFBMODE;
+	char *thisarg;
+	char *mode = mode; /* for compiler */
+
+	memset(&var_set, 0xff, sizeof(var_set)); /* set all to -1 */
+
+	/* parse cmd args.... why do they have to make things so difficult? */
+	argv++;
+	argc--;
+	for (; argc > 0 && (thisarg = *argv) != NULL; argc--, argv++) {
+		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)
+				bb_show_usage();
+
+			switch (g_cmdoptions[i].code) {
+			case CMD_FB:
+				fbdev = argv[1];
+				break;
+			case CMD_DB:
+				modefile = argv[1];
+				break;
+			case CMD_ALL:
+				options |= OPT_ALL;
+				break;
+			case CMD_SHOW:
+				options |= OPT_SHOW;
+				break;
+			case CMD_GEOMETRY:
+				var_set.xres = xatou32(argv[1]);
+				var_set.yres = xatou32(argv[2]);
+				var_set.xres_virtual = xatou32(argv[3]);
+				var_set.yres_virtual = xatou32(argv[4]);
+				var_set.bits_per_pixel = xatou32(argv[5]);
+				break;
+			case CMD_TIMING:
+				var_set.pixclock = xatou32(argv[1]);
+				var_set.left_margin = xatou32(argv[2]);
+				var_set.right_margin = xatou32(argv[3]);
+				var_set.upper_margin = xatou32(argv[4]);
+				var_set.lower_margin = xatou32(argv[5]);
+				var_set.hsync_len = xatou32(argv[6]);
+				var_set.vsync_len = xatou32(argv[7]);
+				break;
+			case CMD_ACCEL:
+				break;
+			case CMD_HSYNC:
+				var_set.sync |= FB_SYNC_HOR_HIGH_ACT;
+				break;
+			case CMD_VSYNC:
+				var_set.sync |= FB_SYNC_VERT_HIGH_ACT;
+				break;
+#if ENABLE_FEATURE_FBSET_FANCY
+			case CMD_XRES:
+				var_set.xres = xatou32(argv[1]);
+				break;
+			case CMD_YRES:
+				var_set.yres = xatou32(argv[1]);
+				break;
+			case CMD_DEPTH:
+				var_set.bits_per_pixel = xatou32(argv[1]);
+				break;
+#endif
+			}
+			switch (g_cmdoptions[i].code) {
+			case CMD_FB:
+			case CMD_DB:
+			case CMD_ALL:
+			case CMD_SHOW:
+				break;
+			default:
+				/* other commands imply changes */
+				options |= OPT_CHANGE;
+			}
+			argc -= g_cmdoptions[i].param_count;
+			argv += g_cmdoptions[i].param_count;
+			goto contin;
+		}
+		bb_show_usage();
+ contin: ;
+	}
+
+	fh = xopen(fbdev, O_RDONLY);
+	xioctl(fh, FBIOGET_VSCREENINFO, &var_old);
+
+	if (options & OPT_READMODE) {
+#if ENABLE_FEATURE_FBSET_READMODE
+		if (!read_mode_db(&var_old, modefile, mode)) {
+			bb_error_msg_and_die("unknown video mode '%s'", mode);
+		}
+		options |= OPT_CHANGE;
+#endif
+	}
+
+	if (options & OPT_CHANGE) {
+		copy_changed_values(&var_old, &var_set);
+		if (options & OPT_ALL)
+			var_old.activate = FB_ACTIVATE_ALL;
+		xioctl(fh, FBIOPUT_VSCREENINFO, &var_old);
+	}
+
+	if (options == 0 || (options & OPT_SHOW))
+		showmode(&var_old);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fh);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/fdformat.c b/busybox-1.19.3/util-linux/fdformat.c
new file mode 100644
index 0000000..2f0854a
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fdformat.c
@@ -0,0 +1,137 @@
+/* vi: set sw=4 ts=4: */
+/* 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 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"
+
+
+/* Stuff extracted from linux/fd.h */
+struct floppy_struct {
+	unsigned int	size,		/* nr of sectors total */
+			sect,		/* sectors per track */
+			head,		/* nr of heads */
+			track,		/* nr of tracks */
+			stretch;	/* !=0 means double track steps */
+#define FD_STRETCH 1
+#define FD_SWAPSIDES 2
+
+	unsigned char	gap,		/* gap1 size */
+
+			rate,		/* data rate. |= 0x40 for perpendicular */
+#define FD_2M 0x4
+#define FD_SIZECODEMASK 0x38
+#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8)
+#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
+			     512 : 128 << FD_SIZECODE(floppy) )
+#define FD_PERP 0x40
+
+			spec1,		/* stepping rate, head unload time */
+			fmt_gap;	/* gap2 size */
+	const char	* name; /* used only for predefined formats */
+};
+struct format_descr {
+	unsigned int device,head,track;
+};
+#define FDFMTBEG _IO(2,0x47)
+#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. */
+
+int fdformat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fdformat_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd, n, cyl, read_bytes, verify;
+	unsigned char *data;
+	struct stat st;
+	struct floppy_struct param;
+	struct format_descr descr;
+
+	opt_complementary = "=1"; /* must have 1 param */
+	verify = !getopt32(argv, "n");
+	argv += optind;
+
+	xstat(*argv, &st);
+	if (!S_ISBLK(st.st_mode)) {
+		bb_error_msg_and_die("%s: not a block device", *argv);
+		/* do not test major - perhaps this was an USB floppy */
+	}
+
+	/* O_RDWR for formatting and verifying */
+	fd = xopen(*argv, O_RDWR);
+
+	/* 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",
+		(param.head == 2) ? "Double" : "Single",
+		param.track, param.sect, param.size >> 1);
+
+	/* FORMAT */
+	printf("Formatting... ");
+	xioctl(fd, FDFMTBEG, NULL);
+
+	/* n == track */
+	for (n = 0; n < param.track; n++) {
+		descr.head = 0;
+		descr.track = n;
+		xioctl(fd, FDFMTTRK, &descr);
+		printf("%3d\b\b\b", n);
+		if (param.head == 2) {
+			descr.head = 1;
+			xioctl(fd, FDFMTTRK, &descr);
+		}
+	}
+
+	xioctl(fd, FDFMTEND, NULL);
+	printf("done\n");
+
+	/* VERIFY */
+	if (verify) {
+		/* n == cyl_size */
+		n = param.sect*param.head*512;
+
+		data = xmalloc(n);
+		printf("Verifying... ");
+		for (cyl = 0; cyl < param.track; cyl++) {
+			printf("%3d\b\b\b", cyl);
+			read_bytes = safe_read(fd, data, n);
+			if (read_bytes != n) {
+				if (read_bytes < 0) {
+					bb_perror_msg(bb_msg_read_error);
+				}
+				bb_error_msg_and_die("problem reading cylinder %d, "
+					"expected %d, read %d", cyl, n, read_bytes);
+				// FIXME: maybe better seek & continue??
+			}
+			/* 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);
+				}
+			}
+		}
+		/* There is no point in freeing blocks at the end of a program, because
+		all of the program's space is given back to the system when the process
+		terminates.*/
+
+		if (ENABLE_FEATURE_CLEAN_UP) free(data);
+
+		printf("done\n");
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+
+	/* Don't bother closing.  Exit does
+	 * that, so we can save a few bytes */
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/fdisk.c b/busybox-1.19.3/util-linux/fdisk.c
new file mode 100644
index 0000000..c0be15a
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fdisk.c
@@ -0,0 +1,3122 @@
+/* vi: set sw=4 ts=4: */
+/* fdisk.c -- Partition table manipulator for Linux.
+ *
+ * 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 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
+#endif
+#include <assert.h>             /* assert */
+#include <sys/mount.h>
+#if !defined(BLKSSZGET)
+# define BLKSSZGET _IO(0x12, 104)
+#endif
+#if !defined(BLKGETSIZE64)
+# define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#endif
+#include "libbb.h"
+
+#if BB_LITTLE_ENDIAN
+# define inline_if_little_endian ALWAYS_INLINE
+#else
+# define inline_if_little_endian /* nothing */
+#endif
+
+
+/* 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 DEFAULT_SECTOR_SIZE      512
+#define DEFAULT_SECTOR_SIZE_STR "512"
+#define MAX_SECTOR_SIZE         2048
+#define SECTOR_SIZE              512 /* still used in osf/sgi/sun code */
+#define MAXIMUM_PARTS             60
+
+#define ACTIVE_FLAG             0x80
+
+#define EXTENDED                0x05
+#define WIN98_EXTENDED          0x0f
+#define LINUX_PARTITION         0x81
+#define LINUX_SWAP              0x82
+#define LINUX_NATIVE            0x83
+#define LINUX_EXTENDED          0x85
+#define LINUX_LVM               0x8e
+#define LINUX_RAID              0xfd
+
+
+enum {
+	OPT_b = 1 << 0,
+	OPT_C = 1 << 1,
+	OPT_H = 1 << 2,
+	OPT_l = 1 << 3,
+	OPT_S = 1 << 4,
+	OPT_u = 1 << 5,
+	OPT_s = (1 << 6) * ENABLE_FEATURE_FDISK_BLKSIZE,
+};
+
+
+typedef unsigned long long ullong;
+/* Used for sector numbers. Partition formats we know
+ * do not support more than 2^32 sectors
+ */
+typedef uint32_t sector_t;
+#if UINT_MAX == 4294967295
+# define SECT_FMT ""
+#elif ULONG_MAX == 4294967295
+# define SECT_FMT "l"
+#else
+# error Cant detect sizeof(uint32_t)
+#endif
+
+struct hd_geometry {
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned short cylinders;
+	unsigned long start;
+};
+
+#define HDIO_GETGEO     0x0301  /* get device geometry */
+
+static const char msg_building_new_label[] ALIGN1 =
+"Building a new %s. Changes will remain in memory only,\n"
+"until you decide to write them. After that the previous content\n"
+"won't be recoverable.\n\n";
+
+static const char msg_part_already_defined[] ALIGN1 =
+"Partition %u is already defined, delete it before re-adding\n";
+
+
+struct partition {
+	unsigned char boot_ind;         /* 0x80 - active */
+	unsigned char head;             /* starting head */
+	unsigned char sector;           /* starting sector */
+	unsigned char cyl;              /* starting cylinder */
+	unsigned char sys_ind;          /* what partition type */
+	unsigned char end_head;         /* end head */
+	unsigned char end_sector;       /* end sector */
+	unsigned char end_cyl;          /* end cylinder */
+	unsigned char start4[4];        /* starting sector counting from 0 */
+	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_GPT
+};
+
+#define LABEL_IS_DOS	(LABEL_DOS == current_label_type)
+
+#if ENABLE_FEATURE_SUN_LABEL
+#define LABEL_IS_SUN	(LABEL_SUN == current_label_type)
+#define STATIC_SUN static
+#else
+#define LABEL_IS_SUN	0
+#define STATIC_SUN extern
+#endif
+
+#if ENABLE_FEATURE_SGI_LABEL
+#define LABEL_IS_SGI	(LABEL_SGI == current_label_type)
+#define STATIC_SGI static
+#else
+#define LABEL_IS_SGI	0
+#define STATIC_SGI extern
+#endif
+
+#if ENABLE_FEATURE_AIX_LABEL
+#define LABEL_IS_AIX	(LABEL_AIX == current_label_type)
+#define STATIC_AIX static
+#else
+#define LABEL_IS_AIX	0
+#define STATIC_AIX extern
+#endif
+
+#if ENABLE_FEATURE_OSF_LABEL
+#define LABEL_IS_OSF	(LABEL_OSF == current_label_type)
+#define STATIC_OSF static
+#else
+#define LABEL_IS_OSF	0
+#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);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void change_units(void);
+static void reread_partition_table(int leave);
+static void delete_partition(int i);
+static unsigned get_partition(int warn, unsigned max);
+static void list_types(const char *const *sys);
+static sector_t read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg);
+#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
+static int get_boot(void);
+#endif
+
+#define PLURAL   0
+#define SINGULAR 1
+
+static sector_t get_start_sect(const struct partition *p);
+static sector_t get_nr_sects(const struct partition *p);
+
+/* DOS partition types */
+
+static const char *const i386_sys_types[] = {
+	"\x00" "Empty",
+	"\x01" "FAT12",
+	"\x04" "FAT16 <32M",
+	"\x05" "Extended",         /* DOS 3.3+ extended partition */
+	"\x06" "FAT16",            /* DOS 16-bit >=32M */
+	"\x07" "HPFS/NTFS",        /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+	"\x0a" "OS/2 Boot Manager",/* OS/2 Boot Manager */
+	"\x0b" "Win95 FAT32",
+	"\x0c" "Win95 FAT32 (LBA)",/* LBA really is 'Extended Int 13h' */
+	"\x0e" "Win95 FAT16 (LBA)",
+	"\x0f" "Win95 Ext'd (LBA)",
+	"\x11" "Hidden FAT12",
+	"\x12" "Compaq diagnostics",
+	"\x14" "Hidden FAT16 <32M",
+	"\x16" "Hidden FAT16",
+	"\x17" "Hidden HPFS/NTFS",
+	"\x1b" "Hidden Win95 FAT32",
+	"\x1c" "Hidden W95 FAT32 (LBA)",
+	"\x1e" "Hidden W95 FAT16 (LBA)",
+	"\x3c" "Part.Magic recovery",
+	"\x41" "PPC PReP Boot",
+	"\x42" "SFS",
+	"\x63" "GNU HURD or SysV", /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+	"\x80" "Old Minix",        /* Minix 1.4a and earlier */
+	"\x81" "Minix / old Linux",/* Minix 1.4b and later */
+	"\x82" "Linux swap",       /* also Solaris */
+	"\x83" "Linux",
+	"\x84" "OS/2 hidden C: drive",
+	"\x85" "Linux extended",
+	"\x86" "NTFS volume set",
+	"\x87" "NTFS volume set",
+	"\x8e" "Linux LVM",
+	"\x9f" "BSD/OS",           /* BSDI */
+	"\xa0" "Thinkpad hibernation",
+	"\xa5" "FreeBSD",          /* various BSD flavours */
+	"\xa6" "OpenBSD",
+	"\xa8" "Darwin UFS",
+	"\xa9" "NetBSD",
+	"\xab" "Darwin boot",
+	"\xb7" "BSDI fs",
+	"\xb8" "BSDI swap",
+	"\xbe" "Solaris boot",
+	"\xeb" "BeOS fs",
+	"\xee" "EFI GPT",                    /* Intel EFI GUID Partition Table */
+	"\xef" "EFI (FAT-12/16/32)",         /* Intel EFI System Partition */
+	"\xf0" "Linux/PA-RISC boot",         /* Linux/PA-RISC boot loader */
+	"\xf2" "DOS secondary",              /* DOS 3.3+ secondary */
+	"\xfd" "Linux raid autodetect",      /* New (2.2.x) raid partition with
+						autodetect using persistent
+						superblock */
+#if 0 /* ENABLE_WEIRD_PARTITION_TYPES */
+	"\x02" "XENIX root",
+	"\x03" "XENIX usr",
+	"\x08" "AIX",              /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+	"\x09" "AIX bootable",     /* AIX data or Coherent */
+	"\x10" "OPUS",
+	"\x18" "AST SmartSleep",
+	"\x24" "NEC DOS",
+	"\x39" "Plan 9",
+	"\x40" "Venix 80286",
+	"\x4d" "QNX4.x",
+	"\x4e" "QNX4.x 2nd part",
+	"\x4f" "QNX4.x 3rd part",
+	"\x50" "OnTrack DM",
+	"\x51" "OnTrack DM6 Aux1", /* (or Novell) */
+	"\x52" "CP/M",             /* CP/M or Microport SysV/AT */
+	"\x53" "OnTrack DM6 Aux3",
+	"\x54" "OnTrackDM6",
+	"\x55" "EZ-Drive",
+	"\x56" "Golden Bow",
+	"\x5c" "Priam Edisk",
+	"\x61" "SpeedStor",
+	"\x64" "Novell Netware 286",
+	"\x65" "Novell Netware 386",
+	"\x70" "DiskSecure Multi-Boot",
+	"\x75" "PC/IX",
+	"\x93" "Amoeba",
+	"\x94" "Amoeba BBT",       /* (bad block table) */
+	"\xa7" "NeXTSTEP",
+	"\xbb" "Boot Wizard hidden",
+	"\xc1" "DRDOS/sec (FAT-12)",
+	"\xc4" "DRDOS/sec (FAT-16 < 32M)",
+	"\xc6" "DRDOS/sec (FAT-16)",
+	"\xc7" "Syrinx",
+	"\xda" "Non-FS data",
+	"\xdb" "CP/M / CTOS / ...",/* CP/M or Concurrent CP/M or
+	                              Concurrent DOS or CTOS */
+	"\xde" "Dell Utility",     /* Dell PowerEdge Server utilities */
+	"\xdf" "BootIt",           /* BootIt EMBRM */
+	"\xe1" "DOS access",       /* DOS access or SpeedStor 12-bit FAT
+	                              extended partition */
+	"\xe3" "DOS R/O",          /* DOS R/O or SpeedStor */
+	"\xe4" "SpeedStor",        /* SpeedStor 16-bit FAT extended
+	                              partition < 1024 cyl. */
+	"\xf1" "SpeedStor",
+	"\xf4" "SpeedStor",        /* SpeedStor large partition */
+	"\xfe" "LANstep",          /* SpeedStor >1024 cyl. or LANstep */
+	"\xff" "BBT",              /* Xenix Bad Block Table */
+#endif
+	NULL
+};
+
+enum {
+	dev_fd = 3                  /* the disk */
+};
+
+/* Globals */
+struct globals {
+	char *line_ptr;
+
+	const char *disk_device;
+	int g_partitions; // = 4;       /* maximum partition + 1 */
+	unsigned units_per_sector; // = 1;
+	unsigned sector_size; // = DEFAULT_SECTOR_SIZE;
+	unsigned user_set_sector_size;
+	unsigned sector_offset; // = 1;
+	unsigned g_heads, g_sectors, g_cylinders;
+	smallint /* enum label_type */ current_label_type;
+	smallint display_in_cyl_units; // = 1;
+#if ENABLE_FEATURE_OSF_LABEL
+	smallint possibly_osf_label;
+#endif
+
+	smallint listing;               /* no aborts for fdisk -l */
+	smallint dos_compatible_flag; // = 1;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	//int dos_changed;
+	smallint nowarn;                /* no warnings for fdisk -l/-s */
+#endif
+	int ext_index;                  /* the prime extended partition */
+	unsigned user_cylinders, user_heads, user_sectors;
+	unsigned pt_heads, pt_sectors;
+	unsigned kern_heads, kern_sectors;
+	sector_t extended_offset;       /* offset of link pointers */
+	sector_t total_number_of_sectors;
+
+	jmp_buf listingbuf;
+	char line_buffer[80];
+	char partname_buffer[80];
+	/* Raw disk label. For DOS-type partition tables the MBR,
+	 * with descriptions of the primary partitions. */
+	char MBRbuffer[MAX_SECTOR_SIZE];
+	/* Partition tables */
+	struct pte ptes[MAXIMUM_PARTS];
+};
+#define G (*ptr_to_globals)
+#define line_ptr             (G.line_ptr            )
+#define disk_device          (G.disk_device         )
+#define g_partitions         (G.g_partitions        )
+#define units_per_sector     (G.units_per_sector    )
+#define sector_size          (G.sector_size         )
+#define user_set_sector_size (G.user_set_sector_size)
+#define sector_offset        (G.sector_offset       )
+#define g_heads              (G.g_heads             )
+#define g_sectors            (G.g_sectors           )
+#define g_cylinders          (G.g_cylinders         )
+#define current_label_type   (G.current_label_type  )
+#define display_in_cyl_units (G.display_in_cyl_units)
+#define possibly_osf_label   (G.possibly_osf_label  )
+#define listing                 (G.listing                )
+#define dos_compatible_flag     (G.dos_compatible_flag    )
+#define nowarn                  (G.nowarn                 )
+#define ext_index               (G.ext_index              )
+#define user_cylinders          (G.user_cylinders         )
+#define user_heads              (G.user_heads             )
+#define user_sectors            (G.user_sectors           )
+#define pt_heads                (G.pt_heads               )
+#define pt_sectors              (G.pt_sectors             )
+#define kern_heads              (G.kern_heads             )
+#define kern_sectors            (G.kern_sectors           )
+#define extended_offset         (G.extended_offset        )
+#define total_number_of_sectors (G.total_number_of_sectors)
+#define listingbuf      (G.listingbuf     )
+#define line_buffer     (G.line_buffer    )
+#define partname_buffer (G.partname_buffer)
+#define MBRbuffer       (G.MBRbuffer      )
+#define ptes            (G.ptes           )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	sector_size = DEFAULT_SECTOR_SIZE; \
+	sector_offset = 1; \
+	g_partitions = 4; \
+	display_in_cyl_units = 1; \
+	units_per_sector = 1; \
+	dos_compatible_flag = 1; \
+} while (0)
+
+
+/* TODO: move to libbb? */
+/* TODO: return unsigned long long, FEATURE_FDISK_BLKSIZE _can_ handle
+ * disks > 2^32 sectors
+ */
+static sector_t bb_BLKGETSIZE_sectors(int fd)
+{
+	uint64_t v64;
+	unsigned long longsectors;
+
+	if (ioctl(fd, BLKGETSIZE64, &v64) == 0) {
+		/* Got bytes, convert to 512 byte sectors */
+		v64 >>= 9;
+		if (v64 != (sector_t)v64) {
+ ret_trunc:
+			/* Not only DOS, but all other partition tables
+			 * we support can't record more than 32 bit
+			 * sector counts or offsets
+			 */
+			bb_error_msg("device has more than 2^32 sectors, can't use all of them");
+			v64 = (uint32_t)-1L;
+		}
+		return v64;
+	}
+	/* Needs temp of type long */
+	if (ioctl(fd, BLKGETSIZE, &longsectors)) {
+		/* Perhaps this is a disk image */
+		off_t sz = lseek(fd, 0, SEEK_END);
+		longsectors = 0;
+		if (sz > 0)
+			longsectors = (uoff_t)sz / sector_size;
+		lseek(fd, 0, SEEK_SET);
+	}
+	if (sizeof(long) > sizeof(sector_t)
+	 && longsectors != (sector_t)longsectors
+	) {
+		goto ret_trunc;
+	}
+	return longsectors;
+}
+
+
+#define IS_EXTENDED(i) \
+	((i) == EXTENDED || (i) == WIN98_EXTENDED || (i) == LINUX_EXTENDED)
+
+#define cround(n)       (display_in_cyl_units ? ((n)/units_per_sector)+1 : (n))
+
+#define scround(x)      (((x)+units_per_sector-1)/units_per_sector)
+
+#define pt_offset(b, n) \
+	((struct partition *)((b) + 0x1be + (n) * sizeof(struct partition)))
+
+#define sector(s)       ((s) & 0x3f)
+
+#define cylinder(s, c)  ((c) | (((s) & 0xc0) << 2))
+
+#define hsc2sector(h,s,c) \
+	(sector(s) - 1 + sectors * ((h) + heads * cylinder(s,c)))
+
+static void
+close_dev_fd(void)
+{
+	/* Not really closing, but making sure it is open, and to harmless place */
+	xmove_fd(xopen(bb_dev_null, O_RDONLY), dev_fd);
+}
+
+/*
+ * Return partition name - uses static storage
+ */
+static const char *
+partname(const char *dev, int pno, int lth)
+{
+	const char *p;
+	int w, wp;
+	int bufsiz;
+	char *bufp;
+
+	bufp = partname_buffer;
+	bufsiz = sizeof(partname_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;
+}
+
+static ALWAYS_INLINE struct partition *
+get_part_table(int i)
+{
+	return ptes[i].part_table;
+}
+
+static const char *
+str_units(int n)
+{      /* n==1: use singular */
+	if (n == 1)
+		return display_in_cyl_units ? "cylinder" : "sector";
+	return display_in_cyl_units ? "cylinders" : "sectors";
+}
+
+static int
+valid_part_table_flag(const char *mbuffer)
+{
+	return (mbuffer[510] == 0x55 && (uint8_t)mbuffer[511] == 0xaa);
+}
+
+static void fdisk_fatal(const char *why)
+{
+	if (listing) {
+		close_dev_fd();
+		longjmp(listingbuf, 1);
+	}
+	bb_error_msg_and_die(why, disk_device);
+}
+
+static void
+seek_sector(sector_t secno)
+{
+#if ENABLE_FDISK_SUPPORT_LARGE_DISKS
+	off64_t off = (off64_t)secno * sector_size;
+	if (lseek64(dev_fd, off, SEEK_SET) == (off64_t) -1)
+		fdisk_fatal(unable_to_seek);
+#else
+	uint64_t off = (uint64_t)secno * sector_size;
+	if (off > MAXINT(off_t)
+	 || lseek(dev_fd, (off_t)off, SEEK_SET) == (off_t) -1
+	) {
+		fdisk_fatal(unable_to_seek);
+	}
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/* Read line; return 0 or first printable char */
+static int
+read_line(const char *prompt)
+{
+	int sz;
+
+	sz = read_line_input(NULL, prompt, line_buffer, sizeof(line_buffer), /*timeout*/ -1);
+	if (sz <= 0)
+		exit(EXIT_SUCCESS); /* Ctrl-D or Ctrl-C */
+
+	if (line_buffer[sz-1] == '\n')
+		line_buffer[--sz] = '\0';
+
+	line_ptr = line_buffer;
+	while (*line_ptr != '\0' && (unsigned char)*line_ptr <= ' ')
+		line_ptr++;
+	return *line_ptr;
+}
+
+static void
+set_all_unchanged(void)
+{
+	int i;
+
+	for (i = 0; i < MAXIMUM_PARTS; i++)
+		ptes[i].changed = 0;
+}
+
+static ALWAYS_INLINE void
+set_changed(int i)
+{
+	ptes[i].changed = 1;
+}
+
+static ALWAYS_INLINE void
+write_part_table_flag(char *b)
+{
+	b[510] = 0x55;
+	b[511] = 0xaa;
+}
+
+static char
+read_nonempty(const char *mesg)
+{
+	while (!read_line(mesg))
+		continue;
+	return *line_ptr;
+}
+
+static char
+read_maybe_empty(const char *mesg)
+{
+	if (!read_line(mesg)) {
+		line_ptr = line_buffer;
+		line_ptr[0] = '\n';
+		line_ptr[1] = '\0';
+	}
+	return line_ptr[0];
+}
+
+static int
+read_hex(const char *const *sys)
+{
+	unsigned long v;
+	while (1) {
+		read_nonempty("Hex code (type L to list codes): ");
+		if ((line_ptr[0] | 0x20) == 'l') {
+			list_types(sys);
+			continue;
+		}
+		v = bb_strtoul(line_ptr, NULL, 16);
+		if (v <= 0xff)
+			return v;
+	}
+}
+
+static void
+write_sector(sector_t secno, const void *buf)
+{
+	seek_sector(secno);
+	xwrite(dev_fd, buf, sector_size);
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+#include "fdisk_aix.c"
+
+struct sun_partition {
+	unsigned char info[128];   /* Informative text string */
+	unsigned char spare0[14];
+	struct sun_info {
+		unsigned char spare1;
+		unsigned char id;
+		unsigned char spare2;
+		unsigned char flags;
+	} infos[8];
+	unsigned char spare1[246]; /* Boot information etc. */
+	unsigned short rspeed;     /* Disk rotational speed */
+	unsigned short pcylcount;  /* Physical cylinder count */
+	unsigned short sparecyl;   /* extra sects per cylinder */
+	unsigned char spare2[4];   /* More magic... */
+	unsigned short ilfact;     /* Interleave factor */
+	unsigned short ncyl;       /* Data cylinder count */
+	unsigned short nacyl;      /* Alt. cylinder count */
+	unsigned short ntrks;      /* Tracks per cylinder */
+	unsigned short nsect;      /* Sectors per track */
+	unsigned char spare3[4];   /* Even more magic... */
+	struct sun_partinfo {
+		uint32_t start_cylinder;
+		uint32_t num_sectors;
+	} partitions[8];
+	unsigned short magic;      /* Magic number */
+	unsigned short csum;       /* Label xor'd checksum */
+} FIX_ALIASING;
+typedef struct sun_partition sun_partition;
+#define sunlabel ((sun_partition *)MBRbuffer)
+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)
+{
+	return (x << 8) | (x >> 8);
+}
+
+static uint32_t
+fdisk_swap32(uint32_t x)
+{
+	return (x << 24) |
+	       ((x & 0xFF00) << 8) |
+	       ((x & 0xFF0000) >> 8) |
+	       (x >> 24);
+}
+#endif
+
+STATIC_SGI const char *const sgi_sys_types[];
+STATIC_SGI unsigned sgi_get_num_sectors(int i);
+STATIC_SGI int sgi_get_sysid(int i);
+STATIC_SGI void sgi_delete_partition(int i);
+STATIC_SGI void sgi_change_sysid(int i, int sys);
+STATIC_SGI void sgi_list_table(int xtra);
+#if ENABLE_FEATURE_FDISK_ADVANCED
+STATIC_SGI void sgi_set_xcyl(void);
+#endif
+STATIC_SGI int verify_sgi(int verbose);
+STATIC_SGI void sgi_add_partition(int n, int sys);
+STATIC_SGI void sgi_set_swappartition(int i);
+STATIC_SGI const char *sgi_get_bootfile(void);
+STATIC_SGI void sgi_set_bootfile(const char* aFile);
+STATIC_SGI void create_sgiinfo(void);
+STATIC_SGI void sgi_write_table(void);
+STATIC_SGI void sgi_set_bootpartition(int i);
+#include "fdisk_sgi.c"
+
+STATIC_SUN const char *const sun_sys_types[];
+STATIC_SUN void sun_delete_partition(int i);
+STATIC_SUN void sun_change_sysid(int i, int sys);
+STATIC_SUN void sun_list_table(int xtra);
+STATIC_SUN void add_sun_partition(int n, int sys);
+#if ENABLE_FEATURE_FDISK_ADVANCED
+STATIC_SUN void sun_set_alt_cyl(void);
+STATIC_SUN void sun_set_ncyl(int cyl);
+STATIC_SUN void sun_set_xcyl(void);
+STATIC_SUN void sun_set_ilfact(void);
+STATIC_SUN void sun_set_rspeed(void);
+STATIC_SUN void sun_set_pcylcount(void);
+#endif
+STATIC_SUN void toggle_sunflags(int i, unsigned char mask);
+STATIC_SUN void verify_sun(void);
+STATIC_SUN void sun_write_table(void);
+#include "fdisk_sun.c"
+
+
+static inline_if_little_endian unsigned
+read4_little_endian(const unsigned char *cp)
+{
+	uint32_t v;
+	move_from_unaligned32(v, cp);
+	return SWAP_LE32(v);
+}
+
+static sector_t
+get_start_sect(const struct partition *p)
+{
+	return read4_little_endian(p->start4);
+}
+
+static sector_t
+get_nr_sects(const struct partition *p)
+{
+	return read4_little_endian(p->size4);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/* start_sect and nr_sects are stored little endian on all machines */
+/* moreover, they are not aligned correctly */
+static inline_if_little_endian void
+store4_little_endian(unsigned char *cp, unsigned val)
+{
+	uint32_t v = SWAP_LE32(val);
+	move_to_unaligned32(cp, v);
+}
+
+static void
+set_start_sect(struct partition *p, unsigned start_sect)
+{
+	store4_little_endian(p->start4, start_sect);
+}
+
+static void
+set_nr_sects(struct partition *p, unsigned nr_sects)
+{
+	store4_little_endian(p->size4, nr_sects);
+}
+#endif
+
+/* Allocate a buffer and read a partition table sector */
+static void
+read_pte(struct pte *pe, sector_t offset)
+{
+	pe->offset_from_dev_start = offset;
+	pe->sectorbuffer = xzalloc(sector_size);
+	seek_sector(offset);
+	/* xread would make us abort - bad for fdisk -l */
+	if (full_read(dev_fd, pe->sectorbuffer, sector_size) != sector_size)
+		fdisk_fatal(unable_to_read);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	pe->changed = 0;
+#endif
+	pe->part_table = pe->ext_pointer = NULL;
+}
+
+static sector_t
+get_partition_start_from_dev_start(const struct pte *pe)
+{
+	return pe->offset_from_dev_start + get_start_sect(pe->part_table);
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/*
+ * Avoid warning about DOS partitions when no DOS partition was changed.
+ * Here a heuristic "is probably dos partition".
+ * We might also do the opposite and warn in all cases except
+ * for "is probably nondos partition".
+ */
+#ifdef UNUSED
+static int
+is_dos_partition(int t)
+{
+	return (t == 1 || t == 4 || t == 6 ||
+		t == 0x0b || t == 0x0c || t == 0x0e ||
+		t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
+		t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
+		t == 0xc1 || t == 0xc4 || t == 0xc6);
+}
+#endif
+
+static void
+menu(void)
+{
+	puts("Command Action");
+	if (LABEL_IS_SUN) {
+		puts("a\ttoggle a read only flag");           /* sun */
+		puts("b\tedit bsd disklabel");
+		puts("c\ttoggle the mountable flag");         /* sun */
+		puts("d\tdelete a partition");
+		puts("l\tlist known partition types");
+		puts("n\tadd a new partition");
+		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 */
+		puts("t\tchange a partition's system id");
+		puts("u\tchange display/entry units");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+#if ENABLE_FEATURE_FDISK_ADVANCED
+		puts("x\textra functionality (experts only)");
+#endif
+	} else if (LABEL_IS_SGI) {
+		puts("a\tselect bootable partition");    /* sgi flavour */
+		puts("b\tedit bootfile entry");          /* sgi */
+		puts("c\tselect sgi swap partition");    /* sgi flavour */
+		puts("d\tdelete a partition");
+		puts("l\tlist known partition types");
+		puts("n\tadd a new partition");
+		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 */
+		puts("t\tchange a partition's system id");
+		puts("u\tchange display/entry units");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	} else if (LABEL_IS_AIX) {
+		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");
+		puts("c\ttoggle the dos compatibility flag");
+		puts("d\tdelete a partition");
+		puts("l\tlist known partition types");
+		puts("n\tadd a new partition");
+		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 */
+		puts("t\tchange a partition's system id");
+		puts("u\tchange display/entry units");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+#if ENABLE_FEATURE_FDISK_ADVANCED
+		puts("x\textra functionality (experts only)");
+#endif
+	}
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+xmenu(void)
+{
+	puts("Command Action");
+	if (LABEL_IS_SUN) {
+		puts("a\tchange number of alternate cylinders");      /*sun*/
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tchange number of extra sectors per cylinder");/*sun*/
+		puts("h\tchange number of heads");
+		puts("i\tchange interleave factor");                  /*sun*/
+		puts("o\tchange rotation speed (rpm)");               /*sun*/
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+		puts("y\tchange number of physical cylinders");       /*sun*/
+	} else if (LABEL_IS_SGI) {
+		puts("b\tmove beginning of data in a partition"); /* !sun */
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tlist extended partitions");          /* !sun */
+		puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+		puts("h\tchange number of heads");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	} else if (LABEL_IS_AIX) {
+		puts("b\tmove beginning of data in a partition"); /* !sun */
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tlist extended partitions");          /* !sun */
+		puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+		puts("h\tchange number of heads");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	} else {
+		puts("b\tmove beginning of data in a partition"); /* !sun */
+		puts("c\tchange number of cylinders");
+		puts("d\tprint the raw data in the partition table");
+		puts("e\tlist extended partitions");          /* !sun */
+		puts("f\tfix partition order");               /* !sun, !aix, !sgi */
+#if ENABLE_FEATURE_SGI_LABEL
+		puts("g\tcreate an IRIX (SGI) partition table");/* sgi */
+#endif
+		puts("h\tchange number of heads");
+		puts("p\tprint the partition table");
+		puts("q\tquit without saving changes");
+		puts("r\treturn to main menu");
+		puts("s\tchange number of sectors/track");
+		puts("v\tverify the partition table");
+		puts("w\twrite table to disk and exit");
+	}
+}
+#endif /* ADVANCED mode */
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static const char *const *
+get_sys_types(void)
+{
+	return (
+		LABEL_IS_SUN ? sun_sys_types :
+		LABEL_IS_SGI ? sgi_sys_types :
+		i386_sys_types);
+}
+#else
+#define get_sys_types() i386_sys_types
+#endif
+
+static const char *
+partition_type(unsigned char type)
+{
+	int i;
+	const char *const *types = get_sys_types();
+
+	for (i = 0; types[i]; i++)
+		if ((unsigned char)types[i][0] == type)
+			return types[i] + 1;
+
+	return "Unknown";
+}
+
+static int
+is_cleared_partition(const struct partition *p)
+{
+	/* We consider partition "cleared" only if it has only zeros */
+	const char *cp = (const char *)p;
+	int cnt = sizeof(*p);
+	char bits = 0;
+	while (--cnt >= 0)
+		bits |= *cp++;
+	return (bits == 0);
+}
+
+static void
+clear_partition(struct partition *p)
+{
+	if (p)
+		memset(p, 0, sizeof(*p));
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static int
+get_sysid(int i)
+{
+	return LABEL_IS_SUN ? sunlabel->infos[i].id :
+			(LABEL_IS_SGI ? sgi_get_sysid(i) :
+				ptes[i].part_table->sys_ind);
+}
+
+static void
+list_types(const char *const *sys)
+{
+	enum { COLS = 3 };
+
+	unsigned last[COLS];
+	unsigned done, next, size;
+	int i;
+
+	for (size = 0; sys[size]; size++)
+		continue;
+
+	done = 0;
+	for (i = COLS-1; i >= 0; i--) {
+		done += (size + i - done) / (i + 1);
+		last[COLS-1 - i] = done;
+	}
+
+	i = done = next = 0;
+	do {
+		printf("%c%2x %-22.22s", i ? ' ' : '\n',
+			(unsigned char)sys[next][0],
+			sys[next] + 1);
+		next = last[i++] + done;
+		if (i >= COLS || next >= last[i]) {
+			i = 0;
+			next = ++done;
+		}
+	} while (done < last[0]);
+	bb_putchar('\n');
+}
+
+#define set_hsc(h, s, c, sector) do \
+{ \
+	s = sector % g_sectors + 1;  \
+	sector /= g_sectors;         \
+	h = sector % g_heads;        \
+	sector /= g_heads;           \
+	c = sector & 0xff;           \
+	s |= (sector >> 2) & 0xc0;   \
+} while (0)
+
+static void set_hsc_start_end(struct partition *p, sector_t start, sector_t stop)
+{
+	if (dos_compatible_flag && (start / (g_sectors * g_heads) > 1023))
+		start = g_heads * g_sectors * 1024 - 1;
+	set_hsc(p->head, p->sector, p->cyl, start);
+
+	if (dos_compatible_flag && (stop / (g_sectors * g_heads) > 1023))
+		stop = g_heads * g_sectors * 1024 - 1;
+	set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
+}
+
+static void
+set_partition(int i, int doext, sector_t start, sector_t stop, int sysid)
+{
+	struct partition *p;
+	sector_t offset;
+
+	if (doext) {
+		p = ptes[i].ext_pointer;
+		offset = extended_offset;
+	} else {
+		p = ptes[i].part_table;
+		offset = ptes[i].offset_from_dev_start;
+	}
+	p->boot_ind = 0;
+	p->sys_ind = sysid;
+	set_start_sect(p, start - offset);
+	set_nr_sects(p, stop - start + 1);
+	set_hsc_start_end(p, start, stop);
+	ptes[i].changed = 1;
+}
+#endif
+
+static int
+warn_geometry(void)
+{
+	if (g_heads && g_sectors && g_cylinders)
+		return 0;
+
+	printf("Unknown value(s) for:");
+	if (!g_heads)
+		printf(" heads");
+	if (!g_sectors)
+		printf(" sectors");
+	if (!g_cylinders)
+		printf(" cylinders");
+	printf(
+#if ENABLE_FEATURE_FDISK_WRITABLE
+		" (settable in the extra functions menu)"
+#endif
+		"\n");
+	return 1;
+}
+
+static void
+update_units(void)
+{
+	int cyl_units = g_heads * g_sectors;
+
+	if (display_in_cyl_units && cyl_units)
+		units_per_sector = cyl_units;
+	else
+		units_per_sector = 1;   /* in sectors */
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+warn_cylinders(void)
+{
+	if (LABEL_IS_DOS && g_cylinders > 1024 && !nowarn)
+		printf("\n"
+"The number of cylinders for this disk is set to %u.\n"
+"There is nothing wrong with that, but this is larger than 1024,\n"
+"and could in certain setups cause problems with:\n"
+"1) software that runs at boot time (e.g., old versions of LILO)\n"
+"2) booting and partitioning software from other OSs\n"
+"   (e.g., DOS FDISK, OS/2 FDISK)\n",
+			g_cylinders);
+}
+#endif
+
+static void
+read_extended(int ext)
+{
+	int i;
+	struct pte *pex;
+	struct partition *p, *q;
+
+	ext_index = ext;
+	pex = &ptes[ext];
+	pex->ext_pointer = pex->part_table;
+
+	p = pex->part_table;
+	if (!get_start_sect(p)) {
+		printf("Bad offset in primary extended partition\n");
+		return;
+	}
+
+	while (IS_EXTENDED(p->sys_ind)) {
+		struct pte *pe = &ptes[g_partitions];
+
+		if (g_partitions >= MAXIMUM_PARTS) {
+			/* This is not a Linux restriction, but
+			   this program uses arrays of size MAXIMUM_PARTS.
+			   Do not try to 'improve' this test. */
+			struct pte *pre = &ptes[g_partitions - 1];
+#if ENABLE_FEATURE_FDISK_WRITABLE
+			printf("Warning: deleting partitions after %u\n",
+				g_partitions);
+			pre->changed = 1;
+#endif
+			clear_partition(pre->ext_pointer);
+			return;
+		}
+
+		read_pte(pe, extended_offset + get_start_sect(p));
+
+		if (!extended_offset)
+			extended_offset = get_start_sect(p);
+
+		q = p = pt_offset(pe->sectorbuffer, 0);
+		for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
+			if (IS_EXTENDED(p->sys_ind)) {
+				if (pe->ext_pointer)
+					printf("Warning: extra link "
+						"pointer in partition table"
+						" %u\n", g_partitions + 1);
+				else
+					pe->ext_pointer = p;
+			} else if (p->sys_ind) {
+				if (pe->part_table)
+					printf("Warning: ignoring extra "
+						  "data in partition table"
+						  " %u\n", g_partitions + 1);
+				else
+					pe->part_table = p;
+			}
+		}
+
+		/* very strange code here... */
+		if (!pe->part_table) {
+			if (q != pe->ext_pointer)
+				pe->part_table = q;
+			else
+				pe->part_table = q + 1;
+		}
+		if (!pe->ext_pointer) {
+			if (q != pe->part_table)
+				pe->ext_pointer = q;
+			else
+				pe->ext_pointer = q + 1;
+		}
+
+		p = pe->ext_pointer;
+		g_partitions++;
+	}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	/* remove empty links */
+ remove:
+	for (i = 4; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+
+		if (!get_nr_sects(pe->part_table)
+		 && (g_partitions > 5 || ptes[4].part_table->sys_ind)
+		) {
+			printf("Omitting empty partition (%u)\n", i+1);
+			delete_partition(i);
+			goto remove;    /* numbering changed */
+		}
+	}
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+create_doslabel(void)
+{
+	printf(msg_building_new_label, "DOS disklabel");
+
+	current_label_type = LABEL_DOS;
+#if ENABLE_FEATURE_OSF_LABEL
+	possibly_osf_label = 0;
+#endif
+	g_partitions = 4;
+
+	memset(&MBRbuffer[510 - 4*16], 0, 4*16);
+	write_part_table_flag(MBRbuffer);
+	extended_offset = 0;
+	set_all_unchanged();
+	set_changed(0);
+	get_boot(CREATE_EMPTY_DOS);
+}
+#endif
+
+static void
+get_sectorsize(void)
+{
+	if (!user_set_sector_size) {
+		int arg;
+		if (ioctl(dev_fd, BLKSSZGET, &arg) == 0)
+			sector_size = arg;
+		if (sector_size != DEFAULT_SECTOR_SIZE)
+			printf("Note: sector size is %u "
+				"(not " DEFAULT_SECTOR_SIZE_STR ")\n",
+				sector_size);
+	}
+}
+
+static void
+get_kernel_geometry(void)
+{
+	struct hd_geometry geometry;
+
+	if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
+		kern_heads = geometry.heads;
+		kern_sectors = geometry.sectors;
+		/* never use geometry.cylinders - it is truncated */
+	}
+}
+
+static void
+get_partition_table_geometry(void)
+{
+	const unsigned char *bufp = (const unsigned char *)MBRbuffer;
+	struct partition *p;
+	int i, h, s, hh, ss;
+	int first = 1;
+	int bad = 0;
+
+	if (!(valid_part_table_flag((char*)bufp)))
+		return;
+
+	hh = ss = 0;
+	for (i = 0; i < 4; i++) {
+		p = pt_offset(bufp, i);
+		if (p->sys_ind != 0) {
+			h = p->end_head + 1;
+			s = (p->end_sector & 077);
+			if (first) {
+				hh = h;
+				ss = s;
+				first = 0;
+			} else if (hh != h || ss != s)
+				bad = 1;
+		}
+	}
+
+	if (!first && !bad) {
+		pt_heads = hh;
+		pt_sectors = ss;
+	}
+}
+
+static void
+get_geometry(void)
+{
+	int sec_fac;
+
+	get_sectorsize();
+	sec_fac = sector_size / 512;
+#if ENABLE_FEATURE_SUN_LABEL
+	guess_device_type();
+#endif
+	g_heads = g_cylinders = g_sectors = 0;
+	kern_heads = kern_sectors = 0;
+	pt_heads = pt_sectors = 0;
+
+	get_kernel_geometry();
+	get_partition_table_geometry();
+
+	g_heads = user_heads ? user_heads :
+		pt_heads ? pt_heads :
+		kern_heads ? kern_heads : 255;
+	g_sectors = user_sectors ? user_sectors :
+		pt_sectors ? pt_sectors :
+		kern_sectors ? kern_sectors : 63;
+	total_number_of_sectors = bb_BLKGETSIZE_sectors(dev_fd);
+
+	sector_offset = 1;
+	if (dos_compatible_flag)
+		sector_offset = g_sectors;
+
+	g_cylinders = total_number_of_sectors / (g_heads * g_sectors * sec_fac);
+	if (!g_cylinders)
+		g_cylinders = user_cylinders;
+}
+
+/*
+ * Opens disk_device and optionally reads MBR.
+ *    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
+ *    1: I/O error
+ */
+#if ENABLE_FEATURE_SUN_LABEL || ENABLE_FEATURE_FDISK_WRITABLE
+static int get_boot(enum action what)
+#else
+static int get_boot(void)
+#define get_boot(what) get_boot()
+#endif
+{
+	int i, fd;
+
+	g_partitions = 4;
+	for (i = 0; i < 4; i++) {
+		struct pte *pe = &ptes[i];
+		pe->part_table = pt_offset(MBRbuffer, i);
+		pe->ext_pointer = NULL;
+		pe->offset_from_dev_start = 0;
+		pe->sectorbuffer = MBRbuffer;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+		pe->changed = (what == CREATE_EMPTY_DOS);
+#endif
+	}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+// ALERT! highly idiotic design!
+// We end up here when we call get_boot() recursively
+// via get_boot() [table is bad] -> create_doslabel() -> get_boot(CREATE_EMPTY_DOS).
+// or get_boot() [table is bad] -> create_sunlabel() -> get_boot(CREATE_EMPTY_SUN).
+// (just factor out re-init of ptes[0,1,2,3] in a separate fn instead?)
+// So skip opening device _again_...
+	if (what == CREATE_EMPTY_DOS  IF_FEATURE_SUN_LABEL(|| what == CREATE_EMPTY_SUN))
+		goto created_table;
+
+	fd = open(disk_device, (option_mask32 & OPT_l) ? O_RDONLY : O_RDWR);
+
+	if (fd < 0) {
+		fd = open(disk_device, O_RDONLY);
+		if (fd < 0) {
+			if (what == TRY_ONLY)
+				return 1;
+			fdisk_fatal(unable_to_open);
+		}
+		printf("'%s' is opened for read only\n", disk_device);
+	}
+	xmove_fd(fd, dev_fd);
+	if (512 != full_read(dev_fd, MBRbuffer, 512)) {
+		if (what == TRY_ONLY) {
+			close_dev_fd();
+			return 1;
+		}
+		fdisk_fatal(unable_to_read);
+	}
+#else
+	fd = open(disk_device, O_RDONLY);
+	if (fd < 0)
+		return 1;
+	if (512 != full_read(fd, MBRbuffer, 512)) {
+		close(fd);
+		return 1;
+	}
+	xmove_fd(fd, dev_fd);
+#endif
+
+	get_geometry();
+	update_units();
+
+#if ENABLE_FEATURE_SUN_LABEL
+	if (check_sun_label())
+		return 0;
+#endif
+#if ENABLE_FEATURE_SGI_LABEL
+	if (check_sgi_label())
+		return 0;
+#endif
+#if ENABLE_FEATURE_AIX_LABEL
+	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;
+		if (!valid_part_table_flag(MBRbuffer)) {
+			current_label_type = LABEL_OSF;
+			return 0;
+		}
+		printf("This disk has both DOS and BSD magic.\n"
+			 "Give the 'b' command to go to BSD mode.\n");
+	}
+#endif
+
+#if !ENABLE_FEATURE_FDISK_WRITABLE
+	if (!valid_part_table_flag(MBRbuffer))
+		return -1;
+#else
+	if (!valid_part_table_flag(MBRbuffer)) {
+		if (what == OPEN_MAIN) {
+			printf("Device contains neither a valid DOS "
+				  "partition table, nor Sun, SGI, OSF or GPT "
+				  "disklabel\n");
+#ifdef __sparc__
+			IF_FEATURE_SUN_LABEL(create_sunlabel();)
+#else
+			create_doslabel();
+#endif
+			return 0;
+		}
+		/* TRY_ONLY: */
+		return -1;
+	}
+ created_table:
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+	IF_FEATURE_FDISK_WRITABLE(warn_cylinders();)
+	warn_geometry();
+
+	for (i = 0; i < 4; i++) {
+		if (IS_EXTENDED(ptes[i].part_table->sys_ind)) {
+			if (g_partitions != 4)
+				printf("Ignoring extra extended "
+					"partition %u\n", i + 1);
+			else
+				read_extended(i);
+		}
+	}
+
+	for (i = 3; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+		if (!valid_part_table_flag(pe->sectorbuffer)) {
+			printf("Warning: invalid flag 0x%02x,0x%02x of partition "
+				"table %u will be corrected by w(rite)\n",
+				pe->sectorbuffer[510],
+				pe->sectorbuffer[511],
+				i + 1);
+			IF_FEATURE_FDISK_WRITABLE(pe->changed = 1;)
+		}
+	}
+
+	return 0;
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+/*
+ * Print the message MESG, then read an integer between LOW and HIGH (inclusive).
+ * If the user hits Enter, DFLT is returned.
+ * Answers like +10 are interpreted as offsets from BASE.
+ *
+ * There is no default if DFLT is not between LOW and HIGH.
+ */
+static sector_t
+read_int(sector_t low, sector_t dflt, sector_t high, sector_t base, const char *mesg)
+{
+	sector_t value;
+	int default_ok = 1;
+	const char *fmt = "%s (%u-%u, default %u): ";
+
+	if (dflt < low || dflt > high) {
+		fmt = "%s (%u-%u): ";
+		default_ok = 0;
+	}
+
+	while (1) {
+		int use_default = default_ok;
+
+		/* ask question and read answer */
+		do {
+			printf(fmt, mesg, low, high, dflt);
+			read_maybe_empty("");
+		} while (*line_ptr != '\n' && !isdigit(*line_ptr)
+		 && *line_ptr != '-' && *line_ptr != '+');
+
+		if (*line_ptr == '+' || *line_ptr == '-') {
+			int minus = (*line_ptr == '-');
+			int absolute = 0;
+
+			value = atoi(line_ptr + 1);
+
+			/* (1) if 2nd char is digit, use_default = 0.
+			 * (2) move line_ptr to first non-digit. */
+			while (isdigit(*++line_ptr))
+				use_default = 0;
+
+			switch (*line_ptr) {
+			case 'c':
+			case 'C':
+				if (!display_in_cyl_units)
+					value *= g_heads * g_sectors;
+				break;
+			case 'K':
+				absolute = 1024;
+				break;
+			case 'k':
+				absolute = 1000;
+				break;
+			case 'm':
+			case 'M':
+				absolute = 1000000;
+				break;
+			case 'g':
+			case 'G':
+				absolute = 1000000000;
+				break;
+			default:
+				break;
+			}
+			if (absolute) {
+				ullong bytes;
+				unsigned long unit;
+
+				bytes = (ullong) value * absolute;
+				unit = sector_size * units_per_sector;
+				bytes += unit/2; /* round */
+				bytes /= unit;
+				value = bytes;
+			}
+			if (minus)
+				value = -value;
+			value += base;
+		} else {
+			value = atoi(line_ptr);
+			while (isdigit(*line_ptr)) {
+				line_ptr++;
+				use_default = 0;
+			}
+		}
+		if (use_default) {
+			value = dflt;
+			printf("Using default value %u\n", value);
+		}
+		if (value >= low && value <= high)
+			break;
+		printf("Value is out of range\n");
+	}
+	return value;
+}
+
+static unsigned
+get_partition(int warn, unsigned max)
+{
+	struct pte *pe;
+	unsigned i;
+
+	i = read_int(1, 0, max, 0, "Partition number") - 1;
+	pe = &ptes[i];
+
+	if (warn) {
+		if ((!LABEL_IS_SUN && !LABEL_IS_SGI && !pe->part_table->sys_ind)
+		 || (LABEL_IS_SUN && (!sunlabel->partitions[i].num_sectors || !sunlabel->infos[i].id))
+		 || (LABEL_IS_SGI && !sgi_get_num_sectors(i))
+		) {
+			printf("Warning: partition %u has empty type\n", i+1);
+		}
+	}
+	return i;
+}
+
+static int
+get_existing_partition(int warn, unsigned max)
+{
+	int pno = -1;
+	unsigned i;
+
+	for (i = 0; i < max; i++) {
+		struct pte *pe = &ptes[i];
+		struct partition *p = pe->part_table;
+
+		if (p && !is_cleared_partition(p)) {
+			if (pno >= 0)
+				goto not_unique;
+			pno = i;
+		}
+	}
+	if (pno >= 0) {
+		printf("Selected partition %u\n", pno+1);
+		return pno;
+	}
+	printf("No partition is defined yet!\n");
+	return -1;
+
+ not_unique:
+	return get_partition(warn, max);
+}
+
+static int
+get_nonexisting_partition(int warn, unsigned max)
+{
+	int pno = -1;
+	unsigned i;
+
+	for (i = 0; i < max; i++) {
+		struct pte *pe = &ptes[i];
+		struct partition *p = pe->part_table;
+
+		if (p && is_cleared_partition(p)) {
+			if (pno >= 0)
+				goto not_unique;
+			pno = i;
+		}
+	}
+	if (pno >= 0) {
+		printf("Selected partition %u\n", pno+1);
+		return pno;
+	}
+	printf("All primary partitions have been defined already!\n");
+	return -1;
+
+ not_unique:
+	return get_partition(warn, max);
+}
+
+
+static void
+change_units(void)
+{
+	display_in_cyl_units = !display_in_cyl_units;
+	update_units();
+	printf("Changing display/entry units to %s\n",
+		str_units(PLURAL));
+}
+
+static void
+toggle_active(int i)
+{
+	struct pte *pe = &ptes[i];
+	struct partition *p = pe->part_table;
+
+	if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
+		printf("WARNING: Partition %u is an extended partition\n", i + 1);
+	p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
+	pe->changed = 1;
+}
+
+static void
+toggle_dos_compatibility_flag(void)
+{
+	dos_compatible_flag = 1 - dos_compatible_flag;
+	if (dos_compatible_flag) {
+		sector_offset = g_sectors;
+		printf("DOS Compatibility flag is set\n");
+	} else {
+		sector_offset = 1;
+		printf("DOS Compatibility flag is not set\n");
+	}
+}
+
+static void
+delete_partition(int i)
+{
+	struct pte *pe = &ptes[i];
+	struct partition *p = pe->part_table;
+	struct partition *q = pe->ext_pointer;
+
+/* Note that for the fifth partition (i == 4) we don't actually
+ * decrement partitions.
+ */
+
+	if (warn_geometry())
+		return;         /* C/H/S not set */
+	pe->changed = 1;
+
+	if (LABEL_IS_SUN) {
+		sun_delete_partition(i);
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		sgi_delete_partition(i);
+		return;
+	}
+
+	if (i < 4) {
+		if (IS_EXTENDED(p->sys_ind) && i == ext_index) {
+			g_partitions = 4;
+			ptes[ext_index].ext_pointer = NULL;
+			extended_offset = 0;
+		}
+		clear_partition(p);
+		return;
+	}
+
+	if (!q->sys_ind && i > 4) {
+		/* the last one in the chain - just delete */
+		--g_partitions;
+		--i;
+		clear_partition(ptes[i].ext_pointer);
+		ptes[i].changed = 1;
+	} else {
+		/* not the last one - further ones will be moved down */
+		if (i > 4) {
+			/* delete this link in the chain */
+			p = ptes[i-1].ext_pointer;
+			*p = *q;
+			set_start_sect(p, get_start_sect(q));
+			set_nr_sects(p, get_nr_sects(q));
+			ptes[i-1].changed = 1;
+		} else if (g_partitions > 5) {    /* 5 will be moved to 4 */
+			/* the first logical in a longer chain */
+			pe = &ptes[5];
+
+			if (pe->part_table) /* prevent SEGFAULT */
+				set_start_sect(pe->part_table,
+						get_partition_start_from_dev_start(pe) -
+						extended_offset);
+			pe->offset_from_dev_start = extended_offset;
+			pe->changed = 1;
+		}
+
+		if (g_partitions > 5) {
+			g_partitions--;
+			while (i < g_partitions) {
+				ptes[i] = ptes[i+1];
+				i++;
+			}
+		} else {
+			/* the only logical: clear only */
+			clear_partition(ptes[i].part_table);
+		}
+	}
+}
+
+static void
+change_sysid(void)
+{
+	int i, sys, origsys;
+	struct partition *p;
+
+	/* If sgi_label then don't use get_existing_partition,
+	   let the user select a partition, since get_existing_partition()
+	   only works for Linux like partition tables. */
+	if (!LABEL_IS_SGI) {
+		i = get_existing_partition(0, g_partitions);
+	} else {
+		i = get_partition(0, g_partitions);
+	}
+	if (i == -1)
+		return;
+	p = ptes[i].part_table;
+	origsys = sys = get_sysid(i);
+
+	/* if changing types T to 0 is allowed, then
+	   the reverse change must be allowed, too */
+	if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN && !get_nr_sects(p))	{
+		printf("Partition %u does not exist yet!\n", i + 1);
+		return;
+	}
+	while (1) {
+		sys = read_hex(get_sys_types());
+
+		if (!sys && !LABEL_IS_SGI && !LABEL_IS_SUN) {
+			printf("Type 0 means free space to many systems\n"
+				   "(but not to Linux). Having partitions of\n"
+				   "type 0 is probably unwise.\n");
+			/* break; */
+		}
+
+		if (!LABEL_IS_SUN && !LABEL_IS_SGI) {
+			if (IS_EXTENDED(sys) != IS_EXTENDED(p->sys_ind)) {
+				printf("You cannot change a partition into"
+					   " an extended one or vice versa\n");
+				break;
+			}
+		}
+
+		if (sys < 256) {
+#if ENABLE_FEATURE_SUN_LABEL
+			if (LABEL_IS_SUN && i == 2 && sys != SUN_WHOLE_DISK)
+				printf("Consider leaving partition 3 "
+					   "as Whole disk (5),\n"
+					   "as SunOS/Solaris expects it and "
+					   "even Linux likes it\n\n");
+#endif
+#if ENABLE_FEATURE_SGI_LABEL
+			if (LABEL_IS_SGI &&
+				(
+					(i == 10 && sys != SGI_ENTIRE_DISK) ||
+					(i == 8 && sys != 0)
+				)
+			) {
+				printf("Consider leaving partition 9 "
+					   "as volume header (0),\nand "
+					   "partition 11 as entire volume (6)"
+					   "as IRIX expects it\n\n");
+			}
+#endif
+			if (sys == origsys)
+				break;
+			if (LABEL_IS_SUN) {
+				sun_change_sysid(i, sys);
+			} else if (LABEL_IS_SGI) {
+				sgi_change_sysid(i, sys);
+			} else
+				p->sys_ind = sys;
+
+			printf("Changed system type of partition %u "
+				"to %x (%s)\n", i + 1, sys,
+				partition_type(sys));
+			ptes[i].changed = 1;
+			//if (is_dos_partition(origsys) || is_dos_partition(sys))
+			//	dos_changed = 1;
+			break;
+		}
+	}
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+
+/* check_consistency() and linear2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct.  1991). */
+
+static void
+linear2chs(unsigned ls, unsigned *c, unsigned *h, unsigned *s)
+{
+	int spc = g_heads * g_sectors;
+
+	*c = ls / spc;
+	ls = ls % spc;
+	*h = ls / g_sectors;
+	*s = ls % g_sectors + 1;  /* sectors count from 1 */
+}
+
+static void
+check_consistency(const struct partition *p, int partition)
+{
+	unsigned pbc, pbh, pbs;          /* physical beginning c, h, s */
+	unsigned pec, peh, pes;          /* physical ending c, h, s */
+	unsigned lbc, lbh, lbs;          /* logical beginning c, h, s */
+	unsigned lec, leh, les;          /* logical ending c, h, s */
+
+	if (!g_heads || !g_sectors || (partition >= 4))
+		return;         /* do not check extended partitions */
+
+/* physical beginning c, h, s */
+	pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
+	pbh = p->head;
+	pbs = p->sector & 0x3f;
+
+/* physical ending c, h, s */
+	pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
+	peh = p->end_head;
+	pes = p->end_sector & 0x3f;
+
+/* compute logical beginning (c, h, s) */
+	linear2chs(get_start_sect(p), &lbc, &lbh, &lbs);
+
+/* compute logical ending (c, h, s) */
+	linear2chs(get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
+
+/* Same physical / logical beginning? */
+	if (g_cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+		printf("Partition %u has different physical/logical "
+			"beginnings (non-Linux?):\n", partition + 1);
+		printf("     phys=(%u, %u, %u) ", pbc, pbh, pbs);
+		printf("logical=(%u, %u, %u)\n", lbc, lbh, lbs);
+	}
+
+/* Same physical / logical ending? */
+	if (g_cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
+		printf("Partition %u has different physical/logical "
+			"endings:\n", partition + 1);
+		printf("     phys=(%u, %u, %u) ", pec, peh, pes);
+		printf("logical=(%u, %u, %u)\n", lec, leh, les);
+	}
+
+/* Ending on cylinder boundary? */
+	if (peh != (g_heads - 1) || pes != g_sectors) {
+		printf("Partition %u does not end on cylinder boundary\n",
+			partition + 1);
+	}
+}
+
+static void
+list_disk_geometry(void)
+{
+	ullong bytes = ((ullong)total_number_of_sectors << 9);
+	long megabytes = bytes / 1000000;
+
+	if (megabytes < 10000)
+		printf("\nDisk %s: %lu MB, %llu bytes\n",
+			disk_device, megabytes, bytes);
+	else
+		printf("\nDisk %s: %lu.%lu GB, %llu bytes\n",
+			disk_device, megabytes/1000, (megabytes/100)%10, bytes);
+	printf("%u heads, %u sectors/track, %u cylinders",
+		   g_heads, g_sectors, g_cylinders);
+	if (units_per_sector == 1)
+		printf(", total %"SECT_FMT"u sectors",
+			total_number_of_sectors / (sector_size/512));
+	printf("\nUnits = %s of %u * %u = %u bytes\n\n",
+		str_units(PLURAL),
+		units_per_sector, sector_size, units_per_sector * sector_size);
+}
+
+/*
+ * Check whether partition entries are ordered by their starting positions.
+ * Return 0 if OK. Return i if partition i should have been earlier.
+ * Two separate checks: primary and logical partitions.
+ */
+static int
+wrong_p_order(int *prev)
+{
+	const struct pte *pe;
+	const struct partition *p;
+	sector_t last_p_start_pos = 0, p_start_pos;
+	unsigned i, last_i = 0;
+
+	for (i = 0; i < g_partitions; i++) {
+		if (i == 4) {
+			last_i = 4;
+			last_p_start_pos = 0;
+		}
+		pe = &ptes[i];
+		p = pe->part_table;
+		if (p->sys_ind) {
+			p_start_pos = get_partition_start_from_dev_start(pe);
+
+			if (last_p_start_pos > p_start_pos) {
+				if (prev)
+					*prev = last_i;
+				return i;
+			}
+
+			last_p_start_pos = p_start_pos;
+			last_i = i;
+		}
+	}
+	return 0;
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+/*
+ * Fix the chain of logicals.
+ * extended_offset is unchanged, the set of sectors used is unchanged
+ * The chain is sorted so that sectors increase, and so that
+ * starting sectors increase.
+ *
+ * After this it may still be that cfdisk doesnt like the table.
+ * (This is because cfdisk considers expanded parts, from link to
+ * end of partition, and these may still overlap.)
+ * Now
+ *   sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
+ * may help.
+ */
+static void
+fix_chain_of_logicals(void)
+{
+	int j, oj, ojj, sj, sjj;
+	struct partition *pj,*pjj,tmp;
+
+	/* Stage 1: sort sectors but leave sector of part 4 */
+	/* (Its sector is the global extended_offset.) */
+ stage1:
+	for (j = 5; j < g_partitions - 1; j++) {
+		oj = ptes[j].offset_from_dev_start;
+		ojj = ptes[j+1].offset_from_dev_start;
+		if (oj > ojj) {
+			ptes[j].offset_from_dev_start = ojj;
+			ptes[j+1].offset_from_dev_start = oj;
+			pj = ptes[j].part_table;
+			set_start_sect(pj, get_start_sect(pj)+oj-ojj);
+			pjj = ptes[j+1].part_table;
+			set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
+			set_start_sect(ptes[j-1].ext_pointer,
+					   ojj-extended_offset);
+			set_start_sect(ptes[j].ext_pointer,
+					   oj-extended_offset);
+			goto stage1;
+		}
+	}
+
+	/* Stage 2: sort starting sectors */
+ stage2:
+	for (j = 4; j < g_partitions - 1; j++) {
+		pj = ptes[j].part_table;
+		pjj = ptes[j+1].part_table;
+		sj = get_start_sect(pj);
+		sjj = get_start_sect(pjj);
+		oj = ptes[j].offset_from_dev_start;
+		ojj = ptes[j+1].offset_from_dev_start;
+		if (oj+sj > ojj+sjj) {
+			tmp = *pj;
+			*pj = *pjj;
+			*pjj = tmp;
+			set_start_sect(pj, ojj+sjj-oj);
+			set_start_sect(pjj, oj+sj-ojj);
+			goto stage2;
+		}
+	}
+
+	/* Probably something was changed */
+	for (j = 4; j < g_partitions; j++)
+		ptes[j].changed = 1;
+}
+
+
+static void
+fix_partition_table_order(void)
+{
+	struct pte *pei, *pek;
+	int i,k;
+
+	if (!wrong_p_order(NULL)) {
+		printf("Ordering is already correct\n\n");
+		return;
+	}
+
+	while ((i = wrong_p_order(&k)) != 0 && i < 4) {
+		/* partition i should have come earlier, move it */
+		/* We have to move data in the MBR */
+		struct partition *pi, *pk, *pe, pbuf;
+		pei = &ptes[i];
+		pek = &ptes[k];
+
+		pe = pei->ext_pointer;
+		pei->ext_pointer = pek->ext_pointer;
+		pek->ext_pointer = pe;
+
+		pi = pei->part_table;
+		pk = pek->part_table;
+
+		memmove(&pbuf, pi, sizeof(struct partition));
+		memmove(pi, pk, sizeof(struct partition));
+		memmove(pk, &pbuf, sizeof(struct partition));
+
+		pei->changed = pek->changed = 1;
+	}
+
+	if (i)
+		fix_chain_of_logicals();
+
+	printf("Done.\n");
+}
+#endif
+
+static void
+list_table(int xtra)
+{
+	const struct partition *p;
+	int i, w;
+
+	if (LABEL_IS_SUN) {
+		sun_list_table(xtra);
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		sgi_list_table(xtra);
+		return;
+	}
+	if (LABEL_IS_GPT) {
+		gpt_list_table(xtra);
+		return;
+	}
+
+	list_disk_geometry();
+
+	if (LABEL_IS_OSF) {
+		xbsd_print_disklabel(xtra);
+		return;
+	}
+
+	/* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
+	   but if the device name ends in a digit, say /dev/foo1,
+	   then the partition is called /dev/foo1p3. */
+	w = strlen(disk_device);
+	if (w && isdigit(disk_device[w-1]))
+		w++;
+	if (w < 5)
+		w = 5;
+
+	//            1 12345678901 12345678901 12345678901  12
+	printf("%*s Boot      Start         End      Blocks  Id System\n",
+		   w+1, "Device");
+
+	for (i = 0; i < g_partitions; i++) {
+		const struct pte *pe = &ptes[i];
+		sector_t psects;
+		sector_t pblocks;
+		unsigned podd;
+
+		p = pe->part_table;
+		if (!p || is_cleared_partition(p))
+			continue;
+
+		psects = get_nr_sects(p);
+		pblocks = psects;
+		podd = 0;
+
+		if (sector_size < 1024) {
+			pblocks /= (1024 / sector_size);
+			podd = psects % (1024 / sector_size);
+		}
+		if (sector_size > 1024)
+			pblocks *= (sector_size / 1024);
+
+		printf("%s  %c %11"SECT_FMT"u %11"SECT_FMT"u %11"SECT_FMT"u%c %2x %s\n",
+			partname(disk_device, i+1, w+2),
+			!p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG /* boot flag */
+				? '*' : '?',
+			cround(get_partition_start_from_dev_start(pe)),           /* start */
+			cround(get_partition_start_from_dev_start(pe) + psects    /* end */
+				- (psects ? 1 : 0)),
+			pblocks, podd ? '+' : ' ', /* odd flag on end */
+			p->sys_ind,                                     /* type id */
+			partition_type(p->sys_ind));                    /* type name */
+
+		check_consistency(p, i);
+	}
+
+	/* Is partition table in disk order? It need not be, but... */
+	/* partition table entries are not checked for correct order
+	 * if this is a sgi, sun or aix labeled disk... */
+	if (LABEL_IS_DOS && wrong_p_order(NULL)) {
+		/* FIXME */
+		printf("\nPartition table entries are not in disk order\n");
+	}
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+x_list_table(int extend)
+{
+	const struct pte *pe;
+	const struct partition *p;
+	int i;
+
+	printf("\nDisk %s: %u heads, %u sectors, %u cylinders\n\n",
+		disk_device, g_heads, g_sectors, g_cylinders);
+	printf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl      Start       Size ID\n");
+	for (i = 0; i < g_partitions; i++) {
+		pe = &ptes[i];
+		p = (extend ? pe->ext_pointer : pe->part_table);
+		if (p != NULL) {
+			printf("%2u %02x%4u%4u%5u%4u%4u%5u%11"SECT_FMT"u%11"SECT_FMT"u %02x\n",
+				i + 1, p->boot_ind, p->head,
+				sector(p->sector),
+				cylinder(p->sector, p->cyl), p->end_head,
+				sector(p->end_sector),
+				cylinder(p->end_sector, p->end_cyl),
+				get_start_sect(p), get_nr_sects(p),
+				p->sys_ind);
+			if (p->sys_ind)
+				check_consistency(p, i);
+		}
+	}
+}
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+fill_bounds(sector_t *first, sector_t *last)
+{
+	unsigned i;
+	const struct pte *pe = &ptes[0];
+	const struct partition *p;
+
+	for (i = 0; i < g_partitions; pe++,i++) {
+		p = pe->part_table;
+		if (!p->sys_ind || IS_EXTENDED(p->sys_ind)) {
+			first[i] = 0xffffffff;
+			last[i] = 0;
+		} else {
+			first[i] = get_partition_start_from_dev_start(pe);
+			last[i] = first[i] + get_nr_sects(p) - 1;
+		}
+	}
+}
+
+static void
+check(int n, unsigned h, unsigned s, unsigned c, sector_t start)
+{
+	sector_t total, real_s, real_c;
+
+	real_s = sector(s) - 1;
+	real_c = cylinder(s, c);
+	total = (real_c * g_sectors + real_s) * g_heads + h;
+	if (!total)
+		printf("Partition %u contains sector 0\n", n);
+	if (h >= g_heads)
+		printf("Partition %u: head %u greater than maximum %u\n",
+			n, h + 1, g_heads);
+	if (real_s >= g_sectors)
+		printf("Partition %u: sector %u greater than "
+			"maximum %u\n", n, s, g_sectors);
+	if (real_c >= g_cylinders)
+		printf("Partition %u: cylinder %"SECT_FMT"u greater than "
+			"maximum %u\n", n, real_c + 1, g_cylinders);
+	if (g_cylinders <= 1024 && start != total)
+		printf("Partition %u: previous sectors %"SECT_FMT"u disagrees with "
+			"total %"SECT_FMT"u\n", n, start, total);
+}
+
+static void
+verify(void)
+{
+	int i, j;
+	sector_t total = 1;
+	sector_t first[g_partitions], last[g_partitions];
+	struct partition *p;
+
+	if (warn_geometry())
+		return;
+
+	if (LABEL_IS_SUN) {
+		verify_sun();
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		verify_sgi(1);
+		return;
+	}
+
+	fill_bounds(first, last);
+	for (i = 0; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+
+		p = pe->part_table;
+		if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
+			check_consistency(p, i);
+			if (get_partition_start_from_dev_start(pe) < first[i])
+				printf("Warning: bad start-of-data in "
+					"partition %u\n", i + 1);
+			check(i + 1, p->end_head, p->end_sector, p->end_cyl,
+				last[i]);
+			total += last[i] + 1 - first[i];
+			for (j = 0; j < i; j++) {
+				if ((first[i] >= first[j] && first[i] <= last[j])
+				 || ((last[i] <= last[j] && last[i] >= first[j]))) {
+					printf("Warning: partition %u overlaps "
+						"partition %u\n", j + 1, i + 1);
+					total += first[i] >= first[j] ?
+						first[i] : first[j];
+					total -= last[i] <= last[j] ?
+						last[i] : last[j];
+				}
+			}
+		}
+	}
+
+	if (extended_offset) {
+		struct pte *pex = &ptes[ext_index];
+		sector_t e_last = get_start_sect(pex->part_table) +
+			get_nr_sects(pex->part_table) - 1;
+
+		for (i = 4; i < g_partitions; i++) {
+			total++;
+			p = ptes[i].part_table;
+			if (!p->sys_ind) {
+				if (i != 4 || i + 1 < g_partitions)
+					printf("Warning: partition %u "
+						"is empty\n", i + 1);
+			} else if (first[i] < extended_offset || last[i] > e_last) {
+				printf("Logical partition %u not entirely in "
+					"partition %u\n", i + 1, ext_index + 1);
+			}
+		}
+	}
+
+	if (total > g_heads * g_sectors * g_cylinders)
+		printf("Total allocated sectors %u greater than the maximum "
+			"%u\n", total, g_heads * g_sectors * g_cylinders);
+	else {
+		total = g_heads * g_sectors * g_cylinders - total;
+		if (total != 0)
+			printf("%"SECT_FMT"u unallocated sectors\n", total);
+	}
+}
+
+static void
+add_partition(int n, int sys)
+{
+	char mesg[256];         /* 48 does not suffice in Japanese */
+	int i, num_read = 0;
+	struct partition *p = ptes[n].part_table;
+	struct partition *q = ptes[ext_index].part_table;
+	sector_t limit, temp;
+	sector_t start, stop = 0;
+	sector_t first[g_partitions], last[g_partitions];
+
+	if (p && p->sys_ind) {
+		printf(msg_part_already_defined, n + 1);
+		return;
+	}
+	fill_bounds(first, last);
+	if (n < 4) {
+		start = sector_offset;
+		if (display_in_cyl_units || !total_number_of_sectors)
+			limit = (sector_t) g_heads * g_sectors * g_cylinders - 1;
+		else
+			limit = total_number_of_sectors - 1;
+		if (extended_offset) {
+			first[ext_index] = extended_offset;
+			last[ext_index] = get_start_sect(q) +
+				get_nr_sects(q) - 1;
+		}
+	} else {
+		start = extended_offset + sector_offset;
+		limit = get_start_sect(q) + get_nr_sects(q) - 1;
+	}
+	if (display_in_cyl_units)
+		for (i = 0; i < g_partitions; i++)
+			first[i] = (cround(first[i]) - 1) * units_per_sector;
+
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	do {
+		temp = start;
+		for (i = 0; i < g_partitions; i++) {
+			int lastplusoff;
+
+			if (start == ptes[i].offset_from_dev_start)
+				start += sector_offset;
+			lastplusoff = last[i] + ((n < 4) ? 0 : sector_offset);
+			if (start >= first[i] && start <= lastplusoff)
+				start = lastplusoff + 1;
+		}
+		if (start > limit)
+			break;
+		if (start >= temp+units_per_sector && num_read) {
+			printf("Sector %"SECT_FMT"u is already allocated\n", temp);
+			temp = start;
+			num_read = 0;
+		}
+		if (!num_read && start == temp) {
+			sector_t saved_start;
+
+			saved_start = start;
+			start = read_int(cround(saved_start), cround(saved_start), cround(limit), 0, mesg);
+			if (display_in_cyl_units) {
+				start = (start - 1) * units_per_sector;
+				if (start < saved_start)
+					start = saved_start;
+			}
+			num_read = 1;
+		}
+	} while (start != temp || !num_read);
+	if (n > 4) {                    /* NOT for fifth partition */
+		struct pte *pe = &ptes[n];
+
+		pe->offset_from_dev_start = start - sector_offset;
+		if (pe->offset_from_dev_start == extended_offset) { /* must be corrected */
+			pe->offset_from_dev_start++;
+			if (sector_offset == 1)
+				start++;
+		}
+	}
+
+	for (i = 0; i < g_partitions; i++) {
+		struct pte *pe = &ptes[i];
+
+		if (start < pe->offset_from_dev_start && limit >= pe->offset_from_dev_start)
+			limit = pe->offset_from_dev_start - 1;
+		if (start < first[i] && limit >= first[i])
+			limit = first[i] - 1;
+	}
+	if (start > limit) {
+		printf("No free sectors available\n");
+		if (n > 4)
+			g_partitions--;
+		return;
+	}
+	if (cround(start) == cround(limit)) {
+		stop = limit;
+	} else {
+		snprintf(mesg, sizeof(mesg),
+			 "Last %s or +size or +sizeM or +sizeK",
+			 str_units(SINGULAR));
+		stop = read_int(cround(start), cround(limit), cround(limit), cround(start), mesg);
+		if (display_in_cyl_units) {
+			stop = stop * units_per_sector - 1;
+			if (stop >limit)
+				stop = limit;
+		}
+	}
+
+	set_partition(n, 0, start, stop, sys);
+	if (n > 4)
+		set_partition(n - 1, 1, ptes[n].offset_from_dev_start, stop, EXTENDED);
+
+	if (IS_EXTENDED(sys)) {
+		struct pte *pe4 = &ptes[4];
+		struct pte *pen = &ptes[n];
+
+		ext_index = n;
+		pen->ext_pointer = p;
+		pe4->offset_from_dev_start = extended_offset = start;
+		pe4->sectorbuffer = xzalloc(sector_size);
+		pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
+		pe4->ext_pointer = pe4->part_table + 1;
+		pe4->changed = 1;
+		g_partitions = 5;
+	}
+}
+
+static void
+add_logical(void)
+{
+	if (g_partitions > 5 || ptes[4].part_table->sys_ind) {
+		struct pte *pe = &ptes[g_partitions];
+
+		pe->sectorbuffer = xzalloc(sector_size);
+		pe->part_table = pt_offset(pe->sectorbuffer, 0);
+		pe->ext_pointer = pe->part_table + 1;
+		pe->offset_from_dev_start = 0;
+		pe->changed = 1;
+		g_partitions++;
+	}
+	add_partition(g_partitions - 1, LINUX_NATIVE);
+}
+
+static void
+new_partition(void)
+{
+	int i, free_primary = 0;
+
+	if (warn_geometry())
+		return;
+
+	if (LABEL_IS_SUN) {
+		add_sun_partition(get_partition(0, g_partitions), LINUX_NATIVE);
+		return;
+	}
+	if (LABEL_IS_SGI) {
+		sgi_add_partition(get_partition(0, g_partitions), LINUX_NATIVE);
+		return;
+	}
+	if (LABEL_IS_AIX) {
+		printf("Sorry - this fdisk cannot handle AIX disk labels.\n"
+"If you want to add DOS-type partitions, create a new empty DOS partition\n"
+"table first (use 'o'). This will destroy the present disk contents.\n");
+		return;
+	}
+
+	for (i = 0; i < 4; i++)
+		free_primary += !ptes[i].part_table->sys_ind;
+
+	if (!free_primary && g_partitions >= MAXIMUM_PARTS) {
+		printf("The maximum number of partitions has been created\n");
+		return;
+	}
+
+	if (!free_primary) {
+		if (extended_offset)
+			add_logical();
+		else
+			printf("You must delete some partition and add "
+				 "an extended partition first\n");
+	} else {
+		char c, line[80];
+		snprintf(line, sizeof(line),
+			"Command action\n"
+			"   %s\n"
+			"   p   primary partition (1-4)\n",
+			(extended_offset ?
+			"l   logical (5 or over)" : "e   extended"));
+		while (1) {
+			c = read_nonempty(line);
+			if ((c | 0x20) == 'p') {
+				i = get_nonexisting_partition(0, 4);
+				if (i >= 0)
+					add_partition(i, LINUX_NATIVE);
+				return;
+			}
+			if (c == 'l' && extended_offset) {
+				add_logical();
+				return;
+			}
+			if (c == 'e' && !extended_offset) {
+				i = get_nonexisting_partition(0, 4);
+				if (i >= 0)
+					add_partition(i, EXTENDED);
+				return;
+			}
+			printf("Invalid partition number "
+					 "for type '%c'\n", c);
+		}
+	}
+}
+
+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;
+
+	if (LABEL_IS_DOS) {
+		for (i = 0; i < 3; i++)
+			if (ptes[i].changed)
+				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);
+			}
+		}
+	}
+	else if (LABEL_IS_SGI) {
+		/* no test on change? the printf below might be mistaken */
+		sgi_write_table();
+	}
+	else if (LABEL_IS_SUN) {
+		for (i = 0; i < 8; i++) {
+			if (ptes[i].changed) {
+				sun_write_table();
+				break;
+			}
+		}
+	}
+
+	printf("The partition table has been altered.\n");
+	reread_partition_table(1);
+}
+#endif /* FEATURE_FDISK_WRITABLE */
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+#define MAX_PER_LINE    16
+static void
+print_buffer(char *pbuffer)
+{
+	int i,l;
+
+	for (i = 0, l = 0; i < sector_size; i++, l++) {
+		if (l == 0)
+			printf("0x%03X:", i);
+		printf(" %02X", (unsigned char) pbuffer[i]);
+		if (l == MAX_PER_LINE - 1) {
+			bb_putchar('\n');
+			l = -1;
+		}
+	}
+	if (l > 0)
+		bb_putchar('\n');
+	bb_putchar('\n');
+}
+
+static void
+print_raw(void)
+{
+	int i;
+
+	printf("Device: %s\n", disk_device);
+	if (LABEL_IS_SGI || LABEL_IS_SUN)
+		print_buffer(MBRbuffer);
+	else {
+		for (i = 3; i < g_partitions; i++)
+			print_buffer(ptes[i].sectorbuffer);
+	}
+}
+
+static void
+move_begin(unsigned i)
+{
+	struct pte *pe = &ptes[i];
+	struct partition *p = pe->part_table;
+	sector_t new, first, nr_sects;
+
+	if (warn_geometry())
+		return;
+	nr_sects = get_nr_sects(p);
+	if (!p->sys_ind || !nr_sects || IS_EXTENDED(p->sys_ind)) {
+		printf("Partition %u has no data area\n", i + 1);
+		return;
+	}
+	first = get_partition_start_from_dev_start(pe); /* == pe->offset_from_dev_start + get_start_sect(p) */
+	new = read_int(0 /*was:first*/, first, first + nr_sects - 1, first, "New beginning of data");
+	if (new != first) {
+		sector_t new_relative = new - pe->offset_from_dev_start;
+		nr_sects += (get_start_sect(p) - new_relative);
+		set_start_sect(p, new_relative);
+		set_nr_sects(p, nr_sects);
+		read_nonempty("Recalculate C/H/S values? (Y/N): ");
+		if ((line_ptr[0] | 0x20) == 'y')
+			set_hsc_start_end(p, new, new + nr_sects - 1);
+		pe->changed = 1;
+	}
+}
+
+static void
+xselect(void)
+{
+	char c;
+
+	while (1) {
+		bb_putchar('\n');
+		c = 0x20 | read_nonempty("Expert command (m for help): ");
+		switch (c) {
+		case 'a':
+			if (LABEL_IS_SUN)
+				sun_set_alt_cyl();
+			break;
+		case 'b':
+			if (LABEL_IS_DOS)
+				move_begin(get_partition(0, g_partitions));
+			break;
+		case 'c':
+			user_cylinders = g_cylinders =
+				read_int(1, g_cylinders, 1048576, 0,
+					"Number of cylinders");
+			if (LABEL_IS_SUN)
+				sun_set_ncyl(g_cylinders);
+			if (LABEL_IS_DOS)
+				warn_cylinders();
+			break;
+		case 'd':
+			print_raw();
+			break;
+		case 'e':
+			if (LABEL_IS_SGI)
+				sgi_set_xcyl();
+			else if (LABEL_IS_SUN)
+				sun_set_xcyl();
+			else if (LABEL_IS_DOS)
+				x_list_table(1);
+			break;
+		case 'f':
+			if (LABEL_IS_DOS)
+				fix_partition_table_order();
+			break;
+		case 'g':
+#if ENABLE_FEATURE_SGI_LABEL
+			create_sgilabel();
+#endif
+			break;
+		case 'h':
+			user_heads = g_heads = read_int(1, g_heads, 256, 0, "Number of heads");
+			update_units();
+			break;
+		case 'i':
+			if (LABEL_IS_SUN)
+				sun_set_ilfact();
+			break;
+		case 'o':
+			if (LABEL_IS_SUN)
+				sun_set_rspeed();
+			break;
+		case 'p':
+			if (LABEL_IS_SUN)
+				list_table(1);
+			else
+				x_list_table(0);
+			break;
+		case 'q':
+			if (ENABLE_FEATURE_CLEAN_UP)
+				close_dev_fd();
+			bb_putchar('\n');
+			exit(EXIT_SUCCESS);
+		case 'r':
+			return;
+		case 's':
+			user_sectors = g_sectors = read_int(1, g_sectors, 63, 0, "Number of sectors");
+			if (dos_compatible_flag) {
+				sector_offset = g_sectors;
+				printf("Warning: setting sector offset for DOS "
+					"compatiblity\n");
+			}
+			update_units();
+			break;
+		case 'v':
+			verify();
+			break;
+		case 'w':
+			write_table();  /* does not return */
+			break;
+		case 'y':
+			if (LABEL_IS_SUN)
+				sun_set_pcylcount();
+			break;
+		default:
+			xmenu();
+		}
+	}
+}
+#endif /* ADVANCED mode */
+
+static int
+is_ide_cdrom_or_tape(const 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_for_read(buf);
+	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 void
+open_list_and_close(const char *device, int user_specified)
+{
+	int gb;
+
+	disk_device = device;
+	if (setjmp(listingbuf))
+		return;
+	if (!user_specified)
+		if (is_ide_cdrom_or_tape(device))
+			return;
+
+	/* Open disk_device, save file descriptor to dev_fd */
+	errno = 0;
+	gb = get_boot(TRY_ONLY);
+	if (gb > 0) {   /* I/O error */
+		/* Ignore other errors, since we try IDE
+		   and SCSI hard disks which may not be
+		   installed on the system. */
+		if (user_specified || errno == EACCES)
+			bb_perror_msg("can't open '%s'", device);
+		return;
+	}
+
+	if (gb < 0) { /* no DOS signature */
+		list_disk_geometry();
+		if (LABEL_IS_AIX)
+			goto ret;
+#if ENABLE_FEATURE_OSF_LABEL
+		if (bsd_trydev(device) < 0)
+#endif
+			printf("Disk %s doesn't contain a valid "
+				"partition table\n", device);
+	} else {
+		list_table(0);
+#if ENABLE_FEATURE_FDISK_WRITABLE
+		if (!LABEL_IS_SUN && g_partitions > 4) {
+			delete_partition(ext_index);
+		}
+#endif
+	}
+ ret:
+	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];
+	int ma, mi, sz;
+
+	procpt = fopen_or_warn("/proc/partitions", "r");
+
+	while (fgets(line, sizeof(line), procpt)) {
+		if (sscanf(line, " %u %u %u %[^\n ]",
+				&ma, &mi, &sz, ptname) != 4)
+			continue;
+
+		sprintf(devname, "/dev/%s", ptname);
+		if (is_whole_disk(devname))
+			open_list_and_close(devname, 0);
+	}
+#if ENABLE_FEATURE_CLEAN_UP
+	fclose(procpt);
+#endif
+}
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+static void
+unknown_command(int c)
+{
+	printf("%c: unknown command\n", c);
+}
+#endif
+
+int fdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fdisk_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	/*
+	 *  fdisk -v
+	 *  fdisk -l [-b sectorsize] [-u] device ...
+	 *  fdisk -s [partition] ...
+	 *  fdisk [-b sectorsize] [-u] device
+	 *
+	 * Options -C, -H, -S set the geometry.
+	 */
+	INIT_G();
+
+	close_dev_fd(); /* needed: fd 3 must not stay closed */
+
+	opt_complementary = "b+:C+:H+:S+"; /* numeric params */
+	opt = getopt32(argv, "b:C:H:lS:u" IF_FEATURE_FDISK_BLKSIZE("s"),
+				&sector_size, &user_cylinders, &user_heads, &user_sectors);
+	argv += optind;
+	if (opt & OPT_b) {
+		/* Ugly: this sector size is really per device,
+		 * so cannot be combined with multiple disks,
+		 * and the same goes for the C/H/S options.
+		 */
+		if (sector_size < 512
+		 || sector_size > 0x10000
+		 || (sector_size & (sector_size-1)) /* not power of 2 */
+		) {
+			bb_show_usage();
+		}
+		sector_offset = 2;
+		user_set_sector_size = 1;
+	}
+	if (user_heads <= 0 || user_heads >= 256)
+		user_heads = 0;
+	if (user_sectors <= 0 || user_sectors >= 64)
+		user_sectors = 0;
+	if (opt & OPT_u)
+		display_in_cyl_units = 0; // -u
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	if (opt & OPT_l) {
+		nowarn = 1;
+#endif
+		if (*argv) {
+			listing = 1;
+			do {
+				open_list_and_close(*argv, 1);
+			} while (*++argv);
+		} else {
+			/* we don't have device names, */
+			/* use /proc/partitions instead */
+			list_devs_in_proc_partititons();
+		}
+		return 0;
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	}
+#endif
+
+#if ENABLE_FEATURE_FDISK_BLKSIZE
+	if (opt & OPT_s) {
+		int j;
+
+		nowarn = 1;
+		if (!argv[0])
+			bb_show_usage();
+		for (j = 0; argv[j]; j++) {
+			unsigned long long size;
+			fd = xopen(argv[j], O_RDONLY);
+			size = bb_BLKGETSIZE_sectors(fd) / 2;
+			close(fd);
+			if (argv[1])
+				printf("%llu\n", size);
+			else
+				printf("%s: %llu\n", argv[j], size);
+		}
+		return 0;
+	}
+#endif
+
+#if ENABLE_FEATURE_FDISK_WRITABLE
+	if (!argv[0] || argv[1])
+		bb_show_usage();
+
+	disk_device = argv[0];
+	get_boot(OPEN_MAIN);
+
+	if (LABEL_IS_OSF) {
+		/* OSF label, and no DOS label */
+		printf("Detected an OSF/1 disklabel on %s, entering "
+			"disklabel mode\n", disk_device);
+		bsd_select();
+		/*Why do we do this?  It seems to be counter-intuitive*/
+		current_label_type = LABEL_DOS;
+		/* If we return we may want to make an empty DOS label? */
+	}
+
+	while (1) {
+		int c;
+		bb_putchar('\n');
+		c = 0x20 | read_nonempty("Command (m for help): ");
+		switch (c) {
+		case 'a':
+			if (LABEL_IS_DOS)
+				toggle_active(get_partition(1, g_partitions));
+			else if (LABEL_IS_SUN)
+				toggle_sunflags(get_partition(1, g_partitions),
+						0x01);
+			else if (LABEL_IS_SGI)
+				sgi_set_bootpartition(
+					get_partition(1, g_partitions));
+			else
+				unknown_command(c);
+			break;
+		case 'b':
+			if (LABEL_IS_SGI) {
+				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')
+					printf("Boot file unchanged\n");
+				else
+					sgi_set_bootfile(line_ptr);
+			}
+#if ENABLE_FEATURE_OSF_LABEL
+			else
+				bsd_select();
+#endif
+			break;
+		case 'c':
+			if (LABEL_IS_DOS)
+				toggle_dos_compatibility_flag();
+			else if (LABEL_IS_SUN)
+				toggle_sunflags(get_partition(1, g_partitions),
+						0x10);
+			else if (LABEL_IS_SGI)
+				sgi_set_swappartition(
+						get_partition(1, g_partitions));
+			else
+				unknown_command(c);
+			break;
+		case 'd':
+			{
+				int j;
+			/* If sgi_label then don't use get_existing_partition,
+			   let the user select a partition, since
+			   get_existing_partition() only works for Linux-like
+			   partition tables */
+				if (!LABEL_IS_SGI) {
+					j = get_existing_partition(1, g_partitions);
+				} else {
+					j = get_partition(1, g_partitions);
+				}
+				if (j >= 0)
+					delete_partition(j);
+			}
+			break;
+		case 'i':
+			if (LABEL_IS_SGI)
+				create_sgiinfo();
+			else
+				unknown_command(c);
+		case 'l':
+			list_types(get_sys_types());
+			break;
+		case 'm':
+			menu();
+			break;
+		case 'n':
+			new_partition();
+			break;
+		case 'o':
+			create_doslabel();
+			break;
+		case 'p':
+			list_table(0);
+			break;
+		case 'q':
+			if (ENABLE_FEATURE_CLEAN_UP)
+				close_dev_fd();
+			bb_putchar('\n');
+			return 0;
+		case 's':
+#if ENABLE_FEATURE_SUN_LABEL
+			create_sunlabel();
+#endif
+			break;
+		case 't':
+			change_sysid();
+			break;
+		case 'u':
+			change_units();
+			break;
+		case 'v':
+			verify();
+			break;
+		case 'w':
+			write_table();  /* does not return */
+			break;
+#if ENABLE_FEATURE_FDISK_ADVANCED
+		case 'x':
+			if (LABEL_IS_SGI) {
+				printf("\n\tSorry, no experts menu for SGI "
+					"partition tables available\n\n");
+			} else
+				xselect();
+			break;
+#endif
+		default:
+			unknown_command(c);
+			menu();
+		}
+	}
+	return 0;
+#endif /* FEATURE_FDISK_WRITABLE */
+}
diff --git a/busybox-1.19.3/util-linux/fdisk_aix.c b/busybox-1.19.3/util-linux/fdisk_aix.c
new file mode 100644
index 0000000..ee5df50
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fdisk_aix.c
@@ -0,0 +1,74 @@
+#if ENABLE_FEATURE_AIX_LABEL
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+typedef struct {
+	unsigned int   magic;        /* expect AIX_LABEL_MAGIC */
+	unsigned int   fillbytes1[124];
+	unsigned int   physical_volume_id;
+	unsigned int   fillbytes2[124];
+} aix_partition;
+
+#define AIX_LABEL_MAGIC         0xc9c2d4c1
+#define AIX_LABEL_MAGIC_SWAPPED 0xc1d4c2c9
+#define AIX_INFO_MAGIC          0x00072959
+#define AIX_INFO_MAGIC_SWAPPED  0x59290700
+
+#define aixlabel ((aix_partition *)MBRbuffer)
+
+
+/*
+  Changes:
+  * 1999-03-20 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+  *     Internationalization
+  *
+  * 2003-03-20 Phillip Kesling <pkesling@sgi.com>
+  *      Some fixes
+*/
+
+static smallint aix_other_endian; /* bool */
+static smallint aix_volumes = 1; /* max 15 */
+
+/*
+ * only dealing with free blocks here
+ */
+
+static void
+aix_info(void)
+{
+	puts("\n"
+"There is a valid AIX label on this disk.\n"
+"Unfortunately Linux cannot handle these disks at the moment.\n"
+"Nevertheless some advice:\n"
+"1. fdisk will destroy its contents on write.\n"
+"2. Be sure that this disk is NOT a still vital part of a volume group.\n"
+"   (Otherwise you may erase the other disks as well, if unmirrored.)\n"
+"3. Before deleting this physical volume be sure to remove the disk\n"
+"   logically from your AIX machine. (Otherwise you become an AIXpert).\n"
+	);
+}
+
+static int
+check_aix_label(void)
+{
+	if (aixlabel->magic != AIX_LABEL_MAGIC
+	 && aixlabel->magic != AIX_LABEL_MAGIC_SWAPPED
+	) {
+		current_label_type = 0;
+		aix_other_endian = 0;
+		return 0;
+	}
+	aix_other_endian = (aixlabel->magic == AIX_LABEL_MAGIC_SWAPPED);
+	update_units();
+	current_label_type = LABEL_AIX;
+	g_partitions = 1016;
+	aix_volumes = 15;
+	aix_info();
+	/*aix_nolabel();*/              /* %% */
+	/*aix_label = 1;*/              /* %% */
+	return 1;
+}
+#endif  /* AIX_LABEL */
diff --git a/busybox-1.19.3/util-linux/fdisk_gpt.c b/busybox-1.19.3/util-linux/fdisk_gpt.c
new file mode 100644
index 0000000..d43d9c7
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fdisk_gpt.c
@@ -0,0 +1,195 @@
+#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];
+
+	numstr6[5] = '\0';
+
+	smart_ulltoa5(total_number_of_sectors, numstr6, " KMGTPEZY");
+	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),
+				numstr6, " KMGTPEZY");
+			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 */
diff --git a/busybox-1.19.3/util-linux/fdisk_osf.c b/busybox-1.19.3/util-linux/fdisk_osf.c
new file mode 100644
index 0000000..65e6bd7
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fdisk_osf.c
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (c) 1987, 1988 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
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgment:
+ *      This product includes software developed by the University of
+ *      California, Berkeley and its contributors.
+ * 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
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ */
+
+#if ENABLE_FEATURE_OSF_LABEL
+
+#ifndef BSD_DISKMAGIC
+#define BSD_DISKMAGIC     ((uint32_t) 0x82564557)
+#endif
+
+#ifndef BSD_MAXPARTITIONS
+#define BSD_MAXPARTITIONS 16
+#endif
+
+#define BSD_LINUX_BOOTDIR "/usr/ucb/mdec"
+
+#if defined(__alpha__) \
+ || defined(__powerpc__) \
+ || defined(__ia64__) \
+ || defined(__hppa__)
+# define BSD_LABELSECTOR   0
+# define BSD_LABELOFFSET   64
+#else
+# define BSD_LABELSECTOR   1
+# define BSD_LABELOFFSET   0
+#endif
+
+#define BSD_BBSIZE        8192          /* size of boot area, with label */
+#define BSD_SBSIZE        8192          /* max size of fs superblock */
+
+struct xbsd_disklabel {
+	uint32_t   d_magic;                /* the magic number */
+	int16_t    d_type;                 /* drive type */
+	int16_t    d_subtype;              /* controller/d_type specific */
+	char       d_typename[16];         /* type name, e.g. "eagle" */
+	char       d_packname[16];         /* pack identifier */
+	/* disk geometry: */
+	uint32_t   d_secsize;              /* # of bytes per sector */
+	uint32_t   d_nsectors;             /* # of data sectors per track */
+	uint32_t   d_ntracks;              /* # of tracks per cylinder */
+	uint32_t   d_ncylinders;           /* # of data cylinders per unit */
+	uint32_t   d_secpercyl;            /* # of data sectors per cylinder */
+	uint32_t   d_secperunit;           /* # of data sectors per unit */
+	/*
+	 * Spares (bad sector replacements) below
+	 * are not counted in d_nsectors or d_secpercyl.
+	 * Spare sectors are assumed to be physical sectors
+	 * which occupy space at the end of each track and/or cylinder.
+	 */
+	uint16_t   d_sparespertrack;       /* # of spare sectors per track */
+	uint16_t   d_sparespercyl;         /* # of spare sectors per cylinder */
+	/*
+	 * Alternate cylinders include maintenance, replacement,
+	 * configuration description areas, etc.
+	 */
+	uint32_t   d_acylinders;           /* # of alt. cylinders per unit */
+
+	/* hardware characteristics: */
+	/*
+	 * d_interleave, d_trackskew and d_cylskew describe perturbations
+	 * in the media format used to compensate for a slow controller.
+	 * Interleave is physical sector interleave, set up by the formatter
+	 * or controller when formatting.  When interleaving is in use,
+	 * logically adjacent sectors are not physically contiguous,
+	 * but instead are separated by some number of sectors.
+	 * It is specified as the ratio of physical sectors traversed
+	 * per logical sector.  Thus an interleave of 1:1 implies contiguous
+	 * layout, while 2:1 implies that logical sector 0 is separated
+	 * by one sector from logical sector 1.
+	 * d_trackskew is the offset of sector 0 on track N
+	 * relative to sector 0 on track N-1 on the same cylinder.
+	 * Finally, d_cylskew is the offset of sector 0 on cylinder N
+	 * relative to sector 0 on cylinder N-1.
+	 */
+	uint16_t   d_rpm;                  /* rotational speed */
+	uint16_t   d_interleave;           /* hardware sector interleave */
+	uint16_t   d_trackskew;            /* sector 0 skew, per track */
+	uint16_t   d_cylskew;              /* sector 0 skew, per cylinder */
+	uint32_t   d_headswitch;           /* head switch time, usec */
+	uint32_t   d_trkseek;              /* track-to-track seek, usec */
+	uint32_t   d_flags;                /* generic flags */
+#define NDDATA 5
+	uint32_t   d_drivedata[NDDATA];    /* drive-type specific information */
+#define NSPARE 5
+	uint32_t   d_spare[NSPARE];        /* reserved for future use */
+	uint32_t   d_magic2;               /* the magic number (again) */
+	uint16_t   d_checksum;             /* xor of data incl. partitions */
+	/* filesystem and partition information: */
+	uint16_t   d_npartitions;          /* number of partitions in following */
+	uint32_t   d_bbsize;               /* size of boot area at sn0, bytes */
+	uint32_t   d_sbsize;               /* max size of fs superblock, bytes */
+	struct xbsd_partition { /* the partition table */
+		uint32_t   p_size;         /* number of sectors in partition */
+		uint32_t   p_offset;       /* starting sector */
+		uint32_t   p_fsize;        /* filesystem basic fragment size */
+		uint8_t    p_fstype;       /* filesystem type, see below */
+		uint8_t    p_frag;         /* filesystem fragments per block */
+		uint16_t   p_cpg;          /* filesystem cylinders per group */
+	} d_partitions[BSD_MAXPARTITIONS]; /* actually may be more */
+};
+
+/* d_type values: */
+#define BSD_DTYPE_SMD           1               /* SMD, XSMD; VAX hp/up */
+#define BSD_DTYPE_MSCP          2               /* MSCP */
+#define BSD_DTYPE_DEC           3               /* other DEC (rk, rl) */
+#define BSD_DTYPE_SCSI          4               /* SCSI */
+#define BSD_DTYPE_ESDI          5               /* ESDI interface */
+#define BSD_DTYPE_ST506         6               /* ST506 etc. */
+#define BSD_DTYPE_HPIB          7               /* CS/80 on HP-IB */
+#define BSD_DTYPE_HPFL          8               /* HP Fiber-link */
+#define BSD_DTYPE_FLOPPY        10              /* floppy */
+
+/* d_subtype values: */
+#define BSD_DSTYPE_INDOSPART    0x8             /* is inside dos partition */
+#define BSD_DSTYPE_DOSPART(s)   ((s) & 3)       /* dos partition number */
+#define BSD_DSTYPE_GEOMETRY     0x10            /* drive params in label */
+
+static const char *const xbsd_dktypenames[] = {
+	"unknown",
+	"SMD",
+	"MSCP",
+	"old DEC",
+	"SCSI",
+	"ESDI",
+	"ST506",
+	"HP-IB",
+	"HP-FL",
+	"type 9",
+	"floppy",
+	0
+};
+
+
+/*
+ * Filesystem type and version.
+ * Used to interpret other filesystem-specific
+ * per-partition information.
+ */
+#define BSD_FS_UNUSED   0               /* unused */
+#define BSD_FS_SWAP     1               /* swap */
+#define BSD_FS_V6       2               /* Sixth Edition */
+#define BSD_FS_V7       3               /* Seventh Edition */
+#define BSD_FS_SYSV     4               /* System V */
+#define BSD_FS_V71K     5               /* V7 with 1K blocks (4.1, 2.9) */
+#define BSD_FS_V8       6               /* Eighth Edition, 4K blocks */
+#define BSD_FS_BSDFFS   7               /* 4.2BSD fast file system */
+#define BSD_FS_BSDLFS   9               /* 4.4BSD log-structured file system */
+#define BSD_FS_OTHER    10              /* in use, but unknown/unsupported */
+#define BSD_FS_HPFS     11              /* OS/2 high-performance file system */
+#define BSD_FS_ISO9660  12              /* ISO-9660 filesystem (cdrom) */
+#define BSD_FS_ISOFS    BSD_FS_ISO9660
+#define BSD_FS_BOOT     13              /* partition contains bootstrap */
+#define BSD_FS_ADOS     14              /* AmigaDOS fast file system */
+#define BSD_FS_HFS      15              /* Macintosh HFS */
+#define BSD_FS_ADVFS    16              /* Digital Unix AdvFS */
+
+/* this is annoying, but it's also the way it is :-( */
+#ifdef __alpha__
+#define BSD_FS_EXT2     8               /* ext2 file system */
+#else
+#define BSD_FS_MSDOS    8               /* MS-DOS file system */
+#endif
+
+static const char *const xbsd_fstypes[] = {
+	"\x00" "unused",            /* BSD_FS_UNUSED  */
+	"\x01" "swap",              /* BSD_FS_SWAP    */
+	"\x02" "Version 6",         /* BSD_FS_V6      */
+	"\x03" "Version 7",         /* BSD_FS_V7      */
+	"\x04" "System V",          /* BSD_FS_SYSV    */
+	"\x05" "4.1BSD",            /* BSD_FS_V71K    */
+	"\x06" "Eighth Edition",    /* BSD_FS_V8      */
+	"\x07" "4.2BSD",            /* BSD_FS_BSDFFS  */
+#ifdef __alpha__
+	"\x08" "ext2",              /* BSD_FS_EXT2    */
+#else
+	"\x08" "MS-DOS",            /* BSD_FS_MSDOS   */
+#endif
+	"\x09" "4.4LFS",            /* BSD_FS_BSDLFS  */
+	"\x0a" "unknown",           /* BSD_FS_OTHER   */
+	"\x0b" "HPFS",              /* BSD_FS_HPFS    */
+	"\x0c" "ISO-9660",          /* BSD_FS_ISO9660 */
+	"\x0d" "boot",              /* BSD_FS_BOOT    */
+	"\x0e" "ADOS",              /* BSD_FS_ADOS    */
+	"\x0f" "HFS",               /* BSD_FS_HFS     */
+	"\x10" "AdvFS",             /* BSD_FS_ADVFS   */
+	NULL
+};
+
+
+/*
+ * flags shared by various drives:
+ */
+#define BSD_D_REMOVABLE 0x01            /* removable media */
+#define BSD_D_ECC       0x02            /* supports ECC */
+#define BSD_D_BADSECT   0x04            /* supports bad sector forw. */
+#define BSD_D_RAMDISK   0x08            /* disk emulator */
+#define BSD_D_CHAIN     0x10            /* can do back-back transfers */
+#define BSD_D_DOSPART   0x20            /* within MSDOS partition */
+
+/*
+   Changes:
+   19990319 - Arnaldo Carvalho de Melo <acme@conectiva.com.br> - i18n/nls
+
+   20000101 - David Huggins-Daines <dhuggins@linuxcare.com> - Better
+   support for OSF/1 disklabels on Alpha.
+   Also fixed unaligned accesses in alpha_bootblock_checksum()
+*/
+
+#define FREEBSD_PARTITION       0xa5
+#define NETBSD_PARTITION        0xa9
+
+static void xbsd_delete_part(void);
+static void xbsd_new_part(void);
+static void xbsd_write_disklabel(void);
+static int xbsd_create_disklabel(void);
+static void xbsd_edit_disklabel(void);
+static void xbsd_write_bootstrap(void);
+static void xbsd_change_fstype(void);
+static int xbsd_get_part_index(int max);
+static int xbsd_check_new_partition(int *i);
+static void xbsd_list_types(void);
+static uint16_t xbsd_dkcksum(struct xbsd_disklabel *lp);
+static int xbsd_initlabel(struct partition *p);
+static int xbsd_readlabel(struct partition *p);
+static int xbsd_writelabel(struct partition *p);
+
+#if defined(__alpha__)
+static void alpha_bootblock_checksum(char *boot);
+#endif
+
+#if !defined(__alpha__)
+static int xbsd_translate_fstype(int linux_type);
+static void xbsd_link_part(void);
+static struct partition *xbsd_part;
+static int xbsd_part_index;
+#endif
+
+
+/* Group big globals data and allocate it in one go */
+struct bsd_globals {
+/* We access this through a uint64_t * when checksumming */
+/* hopefully xmalloc gives us required alignment */
+	char disklabelbuffer[BSD_BBSIZE];
+	struct xbsd_disklabel xbsd_dlabel;
+};
+
+static struct bsd_globals *bsd_globals_ptr;
+
+#define disklabelbuffer (bsd_globals_ptr->disklabelbuffer)
+#define xbsd_dlabel     (bsd_globals_ptr->xbsd_dlabel)
+
+
+/* Code */
+
+#define bsd_cround(n) \
+	(display_in_cyl_units ? ((n)/xbsd_dlabel.d_secpercyl) + 1 : (n))
+
+/*
+ * Test whether the whole disk has BSD disk label magic.
+ *
+ * Note: often reformatting with DOS-type label leaves the BSD magic,
+ * so this does not mean that there is a BSD disk label.
+ */
+static int
+check_osf_label(void)
+{
+	if (xbsd_readlabel(NULL) == 0)
+		return 0;
+	return 1;
+}
+
+static int
+bsd_trydev(const char * dev)
+{
+	if (xbsd_readlabel(NULL) == 0)
+		return -1;
+	printf("\nBSD label for device: %s\n", dev);
+	xbsd_print_disklabel(0);
+	return 0;
+}
+
+static void
+bsd_menu(void)
+{
+	puts("Command Action");
+	puts("d\tdelete a BSD partition");
+	puts("e\tedit drive data");
+	puts("i\tinstall bootstrap");
+	puts("l\tlist known filesystem types");
+	puts("n\tadd a new BSD partition");
+	puts("p\tprint BSD partition table");
+	puts("q\tquit without saving changes");
+	puts("r\treturn to main menu");
+	puts("s\tshow complete disklabel");
+	puts("t\tchange a partition's filesystem id");
+	puts("u\tchange units (cylinders/sectors)");
+	puts("w\twrite disklabel to disk");
+#if !defined(__alpha__)
+	puts("x\tlink BSD partition to non-BSD partition");
+#endif
+}
+
+#if !defined(__alpha__)
+static int
+hidden(int type)
+{
+	return type ^ 0x10;
+}
+
+static int
+is_bsd_partition_type(int type)
+{
+	return (type == FREEBSD_PARTITION ||
+		type == hidden(FREEBSD_PARTITION) ||
+		type == NETBSD_PARTITION ||
+		type == hidden(NETBSD_PARTITION));
+}
+#endif
+
+static void
+bsd_select(void)
+{
+#if !defined(__alpha__)
+	int t, ss;
+	struct partition *p;
+
+	for (t = 0; t < 4; t++) {
+		p = get_part_table(t);
+		if (p && is_bsd_partition_type(p->sys_ind)) {
+			xbsd_part = p;
+			xbsd_part_index = t;
+			ss = get_start_sect(xbsd_part);
+			if (ss == 0) {
+				printf("Partition %s has invalid starting sector 0\n",
+					partname(disk_device, t+1, 0));
+				return;
+			}
+				printf("Reading disklabel of %s at sector %u\n",
+					partname(disk_device, t+1, 0), ss + BSD_LABELSECTOR);
+			if (xbsd_readlabel(xbsd_part) == 0)
+				if (xbsd_create_disklabel() == 0)
+					return;
+				break;
+		}
+	}
+
+	if (t == 4) {
+		printf("There is no *BSD partition on %s\n", disk_device);
+		return;
+	}
+
+#elif defined(__alpha__)
+
+	if (xbsd_readlabel(NULL) == 0)
+		if (xbsd_create_disklabel() == 0)
+			exit(EXIT_SUCCESS);
+
+#endif
+
+	while (1) {
+		bb_putchar('\n');
+		switch (tolower(read_nonempty("BSD disklabel command (m for help): "))) {
+		case 'd':
+			xbsd_delete_part();
+			break;
+		case 'e':
+			xbsd_edit_disklabel();
+			break;
+		case 'i':
+			xbsd_write_bootstrap();
+			break;
+		case 'l':
+			xbsd_list_types();
+			break;
+		case 'n':
+			xbsd_new_part();
+			break;
+		case 'p':
+			xbsd_print_disklabel(0);
+			break;
+		case 'q':
+			if (ENABLE_FEATURE_CLEAN_UP)
+				close_dev_fd();
+			exit(EXIT_SUCCESS);
+		case 'r':
+			return;
+		case 's':
+			xbsd_print_disklabel(1);
+			break;
+		case 't':
+			xbsd_change_fstype();
+			break;
+		case 'u':
+			change_units();
+			break;
+		case 'w':
+			xbsd_write_disklabel();
+			break;
+#if !defined(__alpha__)
+		case 'x':
+			xbsd_link_part();
+			break;
+#endif
+		default:
+			bsd_menu();
+			break;
+		}
+	}
+}
+
+static void
+xbsd_delete_part(void)
+{
+	int i;
+
+	i = xbsd_get_part_index(xbsd_dlabel.d_npartitions);
+	xbsd_dlabel.d_partitions[i].p_size   = 0;
+	xbsd_dlabel.d_partitions[i].p_offset = 0;
+	xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+	if (xbsd_dlabel.d_npartitions == i + 1)
+		while (xbsd_dlabel.d_partitions[xbsd_dlabel.d_npartitions-1].p_size == 0)
+			xbsd_dlabel.d_npartitions--;
+}
+
+static void
+xbsd_new_part(void)
+{
+	off_t begin, end;
+	char mesg[256];
+	int i;
+
+	if (!xbsd_check_new_partition(&i))
+		return;
+
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__)
+	begin = get_start_sect(xbsd_part);
+	end = begin + get_nr_sects(xbsd_part) - 1;
+#else
+	begin = 0;
+	end = xbsd_dlabel.d_secperunit - 1;
+#endif
+
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	begin = read_int(bsd_cround(begin), bsd_cround(begin), bsd_cround(end),
+		0, mesg);
+
+	if (display_in_cyl_units)
+		begin = (begin - 1) * xbsd_dlabel.d_secpercyl;
+
+	snprintf(mesg, sizeof(mesg), "Last %s or +size or +sizeM or +sizeK",
+		str_units(SINGULAR));
+	end = read_int(bsd_cround(begin), bsd_cround(end), bsd_cround(end),
+		bsd_cround(begin), mesg);
+
+	if (display_in_cyl_units)
+		end = end * xbsd_dlabel.d_secpercyl - 1;
+
+	xbsd_dlabel.d_partitions[i].p_size   = end - begin + 1;
+	xbsd_dlabel.d_partitions[i].p_offset = begin;
+	xbsd_dlabel.d_partitions[i].p_fstype = BSD_FS_UNUSED;
+}
+
+static void
+xbsd_print_disklabel(int show_all)
+{
+	struct xbsd_disklabel *lp = &xbsd_dlabel;
+	struct xbsd_partition *pp;
+	int i, j;
+
+	if (show_all) {
+		static const int d_masks[] = { BSD_D_REMOVABLE, BSD_D_ECC, BSD_D_BADSECT };
+
+#if defined(__alpha__)
+		printf("# %s:\n", disk_device);
+#else
+		printf("# %s:\n", partname(disk_device, xbsd_part_index+1, 0));
+#endif
+		if ((unsigned) lp->d_type < ARRAY_SIZE(xbsd_dktypenames)-1)
+			printf("type: %s\n", xbsd_dktypenames[lp->d_type]);
+		else
+			printf("type: %u\n", lp->d_type);
+		printf("disk: %.*s\n", (int) sizeof(lp->d_typename), lp->d_typename);
+		printf("label: %.*s\n", (int) sizeof(lp->d_packname), lp->d_packname);
+		printf("flags: ");
+		print_flags_separated(d_masks, "removable\0""ecc\0""badsect\0", lp->d_flags, " ");
+		bb_putchar('\n');
+		/* On various machines the fields of *lp are short/int/long */
+		/* In order to avoid problems, we cast them all to long. */
+		printf("bytes/sector: %lu\n", (long) lp->d_secsize);
+		printf("sectors/track: %lu\n", (long) lp->d_nsectors);
+		printf("tracks/cylinder: %lu\n", (long) lp->d_ntracks);
+		printf("sectors/cylinder: %lu\n", (long) lp->d_secpercyl);
+		printf("cylinders: %lu\n", (long) lp->d_ncylinders);
+		printf("rpm: %u\n", lp->d_rpm);
+		printf("interleave: %u\n", lp->d_interleave);
+		printf("trackskew: %u\n", lp->d_trackskew);
+		printf("cylinderskew: %u\n", lp->d_cylskew);
+		printf("headswitch: %lu\t\t# milliseconds\n",
+			(long) lp->d_headswitch);
+		printf("track-to-track seek: %lu\t# milliseconds\n",
+			(long) lp->d_trkseek);
+		printf("drivedata: ");
+		for (i = NDDATA - 1; i >= 0; i--)
+			if (lp->d_drivedata[i])
+				break;
+		if (i < 0)
+			i = 0;
+		for (j = 0; j <= i; j++)
+			printf("%lu ", (long) lp->d_drivedata[j]);
+	}
+	printf("\n%u partitions:\n", lp->d_npartitions);
+	printf("#       start       end      size     fstype   [fsize bsize   cpg]\n");
+	pp = lp->d_partitions;
+	for (i = 0; i < lp->d_npartitions; i++, pp++) {
+		if (pp->p_size) {
+			if (display_in_cyl_units && lp->d_secpercyl) {
+				printf("  %c: %8lu%c %8lu%c %8lu%c  ",
+					'a' + i,
+					(unsigned long) pp->p_offset / lp->d_secpercyl + 1,
+					(pp->p_offset % lp->d_secpercyl) ? '*' : ' ',
+					(unsigned long) (pp->p_offset + pp->p_size + lp->d_secpercyl - 1) / lp->d_secpercyl,
+					((pp->p_offset + pp->p_size) % lp->d_secpercyl) ? '*' : ' ',
+					(long) pp->p_size / lp->d_secpercyl,
+					(pp->p_size % lp->d_secpercyl) ? '*' : ' '
+				);
+			} else {
+				printf("  %c: %8lu  %8lu  %8lu   ",
+					'a' + i,
+					(long) pp->p_offset,
+					(long) pp->p_offset + pp->p_size - 1,
+					(long) pp->p_size
+				);
+			}
+
+			if ((unsigned) pp->p_fstype < ARRAY_SIZE(xbsd_fstypes)-1)
+				printf("%8.8s", xbsd_fstypes[pp->p_fstype]);
+			else
+				printf("%8x", pp->p_fstype);
+
+			switch (pp->p_fstype) {
+			case BSD_FS_UNUSED:
+				printf("    %5lu %5lu %5.5s ",
+					(long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, "");
+				break;
+			case BSD_FS_BSDFFS:
+				printf("    %5lu %5lu %5u ",
+					(long) pp->p_fsize, (long) pp->p_fsize * pp->p_frag, pp->p_cpg);
+				break;
+			default:
+				printf("%22.22s", "");
+				break;
+			}
+			bb_putchar('\n');
+		}
+	}
+}
+
+static void
+xbsd_write_disklabel(void)
+{
+#if defined(__alpha__)
+	printf("Writing disklabel to %s\n", disk_device);
+	xbsd_writelabel(NULL);
+#else
+	printf("Writing disklabel to %s\n",
+		partname(disk_device, xbsd_part_index + 1, 0));
+	xbsd_writelabel(xbsd_part);
+#endif
+	reread_partition_table(0);      /* no exit yet */
+}
+
+static int
+xbsd_create_disklabel(void)
+{
+	char c;
+
+#if defined(__alpha__)
+	printf("%s contains no disklabel\n", disk_device);
+#else
+	printf("%s contains no disklabel\n",
+		partname(disk_device, xbsd_part_index + 1, 0));
+#endif
+
+	while (1) {
+		c = read_nonempty("Do you want to create a disklabel? (y/n) ");
+		if ((c|0x20) == 'y') {
+			if (xbsd_initlabel(
+#if defined(__alpha__) || defined(__powerpc__) || defined(__hppa__) || \
+	defined(__s390__) || defined(__s390x__)
+				NULL
+#else
+				xbsd_part
+#endif
+			) == 1) {
+				xbsd_print_disklabel(1);
+				return 1;
+			}
+			return 0;
+		}
+		if ((c|0x20) == 'n')
+			return 0;
+	}
+}
+
+static int
+edit_int(int def, const char *mesg)
+{
+	mesg = xasprintf("%s (%u): ", mesg, def);
+	do {
+		if (!read_line(mesg))
+			goto ret;
+	} while (!isdigit(*line_ptr));
+	def = atoi(line_ptr);
+ ret:
+	free((char*)mesg);
+	return def;
+}
+
+static void
+xbsd_edit_disklabel(void)
+{
+	struct xbsd_disklabel *d;
+
+	d = &xbsd_dlabel;
+
+#if defined(__alpha__) || defined(__ia64__)
+	d->d_secsize    = edit_int(d->d_secsize     , "bytes/sector");
+	d->d_nsectors   = edit_int(d->d_nsectors    , "sectors/track");
+	d->d_ntracks    = edit_int(d->d_ntracks     , "tracks/cylinder");
+	d->d_ncylinders = edit_int(d->d_ncylinders  , "cylinders");
+#endif
+
+	/* d->d_secpercyl can be != d->d_nsectors * d->d_ntracks */
+	while (1) {
+		d->d_secpercyl = edit_int(d->d_nsectors * d->d_ntracks,
+				"sectors/cylinder");
+		if (d->d_secpercyl <= d->d_nsectors * d->d_ntracks)
+			break;
+
+		printf("Must be <= sectors/track * tracks/cylinder (default)\n");
+	}
+	d->d_rpm        = edit_int(d->d_rpm       , "rpm");
+	d->d_interleave = edit_int(d->d_interleave, "interleave");
+	d->d_trackskew  = edit_int(d->d_trackskew , "trackskew");
+	d->d_cylskew    = edit_int(d->d_cylskew   , "cylinderskew");
+	d->d_headswitch = edit_int(d->d_headswitch, "headswitch");
+	d->d_trkseek    = edit_int(d->d_trkseek   , "track-to-track seek");
+
+	d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+}
+
+static int
+xbsd_get_bootstrap(char *path, void *ptr, int size)
+{
+	int fdb;
+
+	fdb = open_or_warn(path, O_RDONLY);
+	if (fdb < 0) {
+		return 0;
+	}
+	if (full_read(fdb, ptr, size) < 0) {
+		bb_simple_perror_msg(path);
+		close(fdb);
+		return 0;
+	}
+	printf(" ... %s\n", path);
+	close(fdb);
+	return 1;
+}
+
+static void
+sync_disks(void)
+{
+	printf("Syncing disks\n");
+	sync();
+	/* sleep(4); What? */
+}
+
+static void
+xbsd_write_bootstrap(void)
+{
+	char path[MAXPATHLEN];
+	const char *bootdir = BSD_LINUX_BOOTDIR;
+	const char *dkbasename;
+	struct xbsd_disklabel dl;
+	char *d, *p, *e;
+	int sector;
+
+	if (xbsd_dlabel.d_type == BSD_DTYPE_SCSI)
+		dkbasename = "sd";
+	else
+		dkbasename = "wd";
+
+	snprintf(path, sizeof(path), "Bootstrap: %sboot -> boot%s (%s): ",
+		dkbasename, dkbasename, dkbasename);
+	if (read_line(path)) {
+		dkbasename = line_ptr;
+	}
+	snprintf(path, sizeof(path), "%s/%sboot", bootdir, dkbasename);
+	if (!xbsd_get_bootstrap(path, disklabelbuffer, (int) xbsd_dlabel.d_secsize))
+		return;
+
+/* We need a backup of the disklabel (xbsd_dlabel might have changed). */
+	d = &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE];
+	memmove(&dl, d, sizeof(struct xbsd_disklabel));
+
+/* The disklabel will be overwritten by 0's from bootxx anyway */
+	memset(d, 0, sizeof(struct xbsd_disklabel));
+
+	snprintf(path, sizeof(path), "%s/boot%s", bootdir, dkbasename);
+	if (!xbsd_get_bootstrap(path, &disklabelbuffer[xbsd_dlabel.d_secsize],
+			(int) xbsd_dlabel.d_bbsize - xbsd_dlabel.d_secsize))
+		return;
+
+	e = d + sizeof(struct xbsd_disklabel);
+	for (p = d; p < e; p++)
+		if (*p) {
+			printf("Bootstrap overlaps with disk label!\n");
+			exit(EXIT_FAILURE);
+		}
+
+	memmove(d, &dl, sizeof(struct xbsd_disklabel));
+
+#if defined(__powerpc__) || defined(__hppa__)
+	sector = 0;
+#elif defined(__alpha__)
+	sector = 0;
+	alpha_bootblock_checksum(disklabelbuffer);
+#else
+	sector = get_start_sect(xbsd_part);
+#endif
+
+	seek_sector(sector);
+	xwrite(dev_fd, disklabelbuffer, BSD_BBSIZE);
+
+#if defined(__alpha__)
+	printf("Bootstrap installed on %s\n", disk_device);
+#else
+	printf("Bootstrap installed on %s\n",
+		partname(disk_device, xbsd_part_index+1, 0));
+#endif
+
+	sync_disks();
+}
+
+static void
+xbsd_change_fstype(void)
+{
+	int i;
+
+	i = xbsd_get_part_index(xbsd_dlabel.d_npartitions);
+	xbsd_dlabel.d_partitions[i].p_fstype = read_hex(xbsd_fstypes);
+}
+
+static int
+xbsd_get_part_index(int max)
+{
+	char prompt[sizeof("Partition (a-%c): ") + 16];
+	char l;
+
+	snprintf(prompt, sizeof(prompt), "Partition (a-%c): ", 'a' + max - 1);
+	do
+		l = tolower(read_nonempty(prompt));
+	while (l < 'a' || l > 'a' + max - 1);
+	return l - 'a';
+}
+
+static int
+xbsd_check_new_partition(int *i)
+{
+	/* room for more? various BSD flavours have different maxima */
+	if (xbsd_dlabel.d_npartitions == BSD_MAXPARTITIONS) {
+		int t;
+
+		for (t = 0; t < BSD_MAXPARTITIONS; t++)
+			if (xbsd_dlabel.d_partitions[t].p_size == 0)
+				break;
+
+		if (t == BSD_MAXPARTITIONS) {
+			printf("The maximum number of partitions has been created\n");
+			return 0;
+		}
+	}
+
+	*i = xbsd_get_part_index(BSD_MAXPARTITIONS);
+
+	if (*i >= xbsd_dlabel.d_npartitions)
+		xbsd_dlabel.d_npartitions = (*i) + 1;
+
+	if (xbsd_dlabel.d_partitions[*i].p_size != 0) {
+		printf("This partition already exists\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static void
+xbsd_list_types(void)
+{
+	list_types(xbsd_fstypes);
+}
+
+static uint16_t
+xbsd_dkcksum(struct xbsd_disklabel *lp)
+{
+	uint16_t *start, *end;
+	uint16_t sum = 0;
+
+	start = (uint16_t *) lp;
+	end = (uint16_t *) &lp->d_partitions[lp->d_npartitions];
+	while (start < end)
+		sum ^= *start++;
+	return sum;
+}
+
+static int
+xbsd_initlabel(struct partition *p)
+{
+	struct xbsd_disklabel *d = &xbsd_dlabel;
+	struct xbsd_partition *pp;
+
+	get_geometry();
+	memset(d, 0, sizeof(struct xbsd_disklabel));
+
+	d->d_magic = BSD_DISKMAGIC;
+
+	if (strncmp(disk_device, "/dev/sd", 7) == 0)
+		d->d_type = BSD_DTYPE_SCSI;
+	else
+		d->d_type = BSD_DTYPE_ST506;
+
+#if !defined(__alpha__)
+	d->d_flags = BSD_D_DOSPART;
+#else
+	d->d_flags = 0;
+#endif
+	d->d_secsize = SECTOR_SIZE;           /* bytes/sector  */
+	d->d_nsectors = g_sectors;            /* sectors/track */
+	d->d_ntracks = g_heads;               /* tracks/cylinder (heads) */
+	d->d_ncylinders = g_cylinders;
+	d->d_secpercyl  = g_sectors * g_heads;/* sectors/cylinder */
+	if (d->d_secpercyl == 0)
+		d->d_secpercyl = 1;           /* avoid segfaults */
+	d->d_secperunit = d->d_secpercyl * d->d_ncylinders;
+
+	d->d_rpm = 3600;
+	d->d_interleave = 1;
+	d->d_trackskew = 0;
+	d->d_cylskew = 0;
+	d->d_headswitch = 0;
+	d->d_trkseek = 0;
+
+	d->d_magic2 = BSD_DISKMAGIC;
+	d->d_bbsize = BSD_BBSIZE;
+	d->d_sbsize = BSD_SBSIZE;
+
+#if !defined(__alpha__)
+	d->d_npartitions = 4;
+	pp = &d->d_partitions[2]; /* Partition C should be NetBSD partition */
+
+	pp->p_offset = get_start_sect(p);
+	pp->p_size   = get_nr_sects(p);
+	pp->p_fstype = BSD_FS_UNUSED;
+	pp = &d->d_partitions[3]; /* Partition D should be whole disk */
+
+	pp->p_offset = 0;
+	pp->p_size   = d->d_secperunit;
+	pp->p_fstype = BSD_FS_UNUSED;
+#else
+	d->d_npartitions = 3;
+	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;
+#endif
+
+	return 1;
+}
+
+/*
+ * Read a xbsd_disklabel from sector 0 or from the starting sector of p.
+ * If it has the right magic, return 1.
+ */
+static int
+xbsd_readlabel(struct partition *p)
+{
+	struct xbsd_disklabel *d;
+	int t, sector;
+
+	if (!bsd_globals_ptr)
+		bsd_globals_ptr = xzalloc(sizeof(*bsd_globals_ptr));
+
+	d = &xbsd_dlabel;
+
+	/* p is used only to get the starting sector */
+#if !defined(__alpha__)
+	sector = (p ? get_start_sect(p) : 0);
+#else
+	sector = 0;
+#endif
+
+	seek_sector(sector);
+	if (BSD_BBSIZE != full_read(dev_fd, disklabelbuffer, BSD_BBSIZE))
+		fdisk_fatal(unable_to_read);
+
+	memmove(d, &disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+		   sizeof(struct xbsd_disklabel));
+
+	if (d->d_magic != BSD_DISKMAGIC || d->d_magic2 != BSD_DISKMAGIC)
+		return 0;
+
+	for (t = d->d_npartitions; t < BSD_MAXPARTITIONS; t++) {
+		d->d_partitions[t].p_size   = 0;
+		d->d_partitions[t].p_offset = 0;
+		d->d_partitions[t].p_fstype = BSD_FS_UNUSED;
+	}
+
+	if (d->d_npartitions > BSD_MAXPARTITIONS)
+		printf("Warning: too many partitions (%u, maximum is %u)\n",
+			d->d_npartitions, BSD_MAXPARTITIONS);
+	return 1;
+}
+
+static int
+xbsd_writelabel(struct partition *p)
+{
+	struct xbsd_disklabel *d = &xbsd_dlabel;
+	unsigned int sector;
+
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__hppa__)
+	sector = get_start_sect(p) + BSD_LABELSECTOR;
+#else
+	(void)p; /* silence warning */
+	sector = BSD_LABELSECTOR;
+#endif
+
+	d->d_checksum = 0;
+	d->d_checksum = xbsd_dkcksum(d);
+
+	/* This is necessary if we want to write the bootstrap later,
+	   otherwise we'd write the old disklabel with the bootstrap.
+	*/
+	memmove(&disklabelbuffer[BSD_LABELSECTOR * SECTOR_SIZE + BSD_LABELOFFSET],
+		d, sizeof(struct xbsd_disklabel));
+
+#if defined(__alpha__) && BSD_LABELSECTOR == 0
+	alpha_bootblock_checksum(disklabelbuffer);
+	seek_sector(0);
+	xwrite(dev_fd, disklabelbuffer, BSD_BBSIZE);
+#else
+	seek_sector(sector);
+	lseek(dev_fd, BSD_LABELOFFSET, SEEK_CUR);
+	xwrite(dev_fd, d, sizeof(*d));
+#endif
+	sync_disks();
+	return 1;
+}
+
+
+#if !defined(__alpha__)
+static int
+xbsd_translate_fstype(int linux_type)
+{
+	switch (linux_type) {
+	case 0x01: /* DOS 12-bit FAT   */
+	case 0x04: /* DOS 16-bit <32M  */
+	case 0x06: /* DOS 16-bit >=32M */
+	case 0xe1: /* DOS access       */
+	case 0xe3: /* DOS R/O          */
+	case 0xf2: /* DOS secondary    */
+		return BSD_FS_MSDOS;
+	case 0x07: /* OS/2 HPFS        */
+		return BSD_FS_HPFS;
+	default:
+		return BSD_FS_OTHER;
+	}
+}
+
+static void
+xbsd_link_part(void)
+{
+	int k, i;
+	struct partition *p;
+
+	k = get_partition(1, g_partitions);
+
+	if (!xbsd_check_new_partition(&i))
+		return;
+
+	p = get_part_table(k);
+
+	xbsd_dlabel.d_partitions[i].p_size   = get_nr_sects(p);
+	xbsd_dlabel.d_partitions[i].p_offset = get_start_sect(p);
+	xbsd_dlabel.d_partitions[i].p_fstype = xbsd_translate_fstype(p->sys_ind);
+}
+#endif
+
+#if defined(__alpha__)
+static void
+alpha_bootblock_checksum(char *boot)
+{
+	uint64_t *dp, sum;
+	int i;
+
+	dp = (uint64_t *)boot;
+	sum = 0;
+	for (i = 0; i < 63; i++)
+		sum += dp[i];
+	dp[63] = sum;
+}
+#endif /* __alpha__ */
+
+/* Undefine 'global' tricks */
+#undef disklabelbuffer
+#undef xbsd_dlabel
+
+#endif /* OSF_LABEL */
diff --git a/busybox-1.19.3/util-linux/fdisk_sgi.c b/busybox-1.19.3/util-linux/fdisk_sgi.c
new file mode 100644
index 0000000..785fc66
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fdisk_sgi.c
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#if ENABLE_FEATURE_SGI_LABEL
+
+#define SGI_DEBUG 0
+
+#define SGI_VOLHDR      0x00
+/* 1 and 2 were used for drive types no longer supported by SGI */
+#define SGI_SWAP        0x03
+/* 4 and 5 were for filesystem types SGI haven't ever supported on MIPS CPUs */
+#define SGI_VOLUME      0x06
+#define SGI_EFS         0x07
+#define SGI_LVOL        0x08
+#define SGI_RLVOL       0x09
+#define SGI_XFS         0x0a
+#define SGI_XFSLOG      0x0b
+#define SGI_XLV         0x0c
+#define SGI_XVM         0x0d
+#define SGI_ENTIRE_DISK SGI_VOLUME
+
+struct device_parameter { /* 48 bytes */
+	unsigned char  skew;
+	unsigned char  gap1;
+	unsigned char  gap2;
+	unsigned char  sparecyl;
+	unsigned short pcylcount;
+	unsigned short head_vol0;
+	unsigned short ntrks;   /* tracks in cyl 0 or vol 0 */
+	unsigned char  cmd_tag_queue_depth;
+	unsigned char  unused0;
+	unsigned short unused1;
+	unsigned short nsect;   /* sectors/tracks in cyl 0 or vol 0 */
+	unsigned short bytes;
+	unsigned short ilfact;
+	unsigned int   flags;   /* controller flags */
+	unsigned int   datarate;
+	unsigned int   retries_on_error;
+	unsigned int   ms_per_word;
+	unsigned short xylogics_gap1;
+	unsigned short xylogics_syncdelay;
+	unsigned short xylogics_readdelay;
+	unsigned short xylogics_gap2;
+	unsigned short xylogics_readgate;
+	unsigned short xylogics_writecont;
+};
+
+/*
+ * controller flags
+ */
+#define SECTOR_SLIP     0x01
+#define SECTOR_FWD      0x02
+#define TRACK_FWD       0x04
+#define TRACK_MULTIVOL  0x08
+#define IGNORE_ERRORS   0x10
+#define RESEEK          0x20
+#define ENABLE_CMDTAGQ  0x40
+
+typedef struct {
+	unsigned int   magic;            /* expect SGI_LABEL_MAGIC */
+	unsigned short boot_part;        /* active boot partition */
+	unsigned short swap_part;        /* active swap partition */
+	unsigned char  boot_file[16];    /* name of the bootfile */
+	struct device_parameter devparam;       /*  1 * 48 bytes */
+	struct volume_directory {               /* 15 * 16 bytes */
+		unsigned char vol_file_name[8]; /* a character array */
+		unsigned int  vol_file_start;   /* number of logical block */
+		unsigned int  vol_file_size;    /* number of bytes */
+	} directory[15];
+	struct sgi_partinfo {                   /* 16 * 12 bytes */
+		unsigned int num_sectors;       /* number of blocks */
+		unsigned int start_sector;      /* must be cylinder aligned */
+		unsigned int id;
+	} partitions[16];
+	unsigned int   csum;
+	unsigned int   fillbytes;
+} sgi_partition;
+
+typedef struct {
+	unsigned int   magic;           /* looks like a magic number */
+	unsigned int   a2;
+	unsigned int   a3;
+	unsigned int   a4;
+	unsigned int   b1;
+	unsigned short b2;
+	unsigned short b3;
+	unsigned int   c[16];
+	unsigned short d[3];
+	unsigned char  scsi_string[50];
+	unsigned char  serial[137];
+	unsigned short check1816;
+	unsigned char  installer[225];
+} sgiinfo;
+
+#define SGI_LABEL_MAGIC         0x0be5a941
+#define SGI_LABEL_MAGIC_SWAPPED 0x41a9e50b
+#define SGI_INFO_MAGIC          0x00072959
+#define SGI_INFO_MAGIC_SWAPPED  0x59290700
+
+#define SGI_SSWAP16(x) (sgi_other_endian ? fdisk_swap16(x) : (uint16_t)(x))
+#define SGI_SSWAP32(x) (sgi_other_endian ? fdisk_swap32(x) : (uint32_t)(x))
+
+#define sgilabel ((sgi_partition *)MBRbuffer)
+#define sgiparam (sgilabel->devparam)
+
+/*
+ *
+ * fdisksgilabel.c
+ *
+ * Copyright (C) Andreas Neuper, Sep 1998.
+ *      This file may be modified and redistributed under
+ *      the terms of the GNU Public License.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *      Internationalization
+ */
+
+
+static smallint sgi_other_endian; /* bool */
+static smallint sgi_volumes = 1; /* max 15 */
+
+/*
+ * only dealing with free blocks here
+ */
+
+typedef struct {
+	unsigned int first;
+	unsigned int last;
+} freeblocks;
+static freeblocks freelist[17]; /* 16 partitions can produce 17 vacant slots */
+
+static void
+setfreelist(int i, unsigned int f, unsigned int l)
+{
+	freelist[i].first = f;
+	freelist[i].last = l;
+}
+
+static void
+add2freelist(unsigned int f, unsigned int l)
+{
+	int i;
+	for (i = 0; i < 17; i++)
+		if (freelist[i].last == 0)
+			break;
+	setfreelist(i, f, l);
+}
+
+static void
+clearfreelist(void)
+{
+	int i;
+
+	for (i = 0; i < 17; i++)
+		setfreelist(i, 0, 0);
+}
+
+static unsigned int
+isinfreelist(unsigned int b)
+{
+	int i;
+
+	for (i = 0; i < 17; i++)
+		if (freelist[i].first <= b && freelist[i].last >= b)
+			return freelist[i].last;
+	return 0;
+}
+	/* return last vacant block of this stride (never 0). */
+	/* the '>=' is not quite correct, but simplifies the code */
+/*
+ * end of free blocks section
+ */
+
+static const char *const sgi_sys_types[] = {
+/* SGI_VOLHDR   */	"\x00" "SGI volhdr"  ,
+/* 0x01         */	"\x01" "SGI trkrepl" ,
+/* 0x02         */	"\x02" "SGI secrepl" ,
+/* SGI_SWAP     */	"\x03" "SGI raw"     ,
+/* 0x04         */	"\x04" "SGI bsd"     ,
+/* 0x05         */	"\x05" "SGI sysv"    ,
+/* SGI_ENTIRE_DISK  */	"\x06" "SGI volume"  ,
+/* SGI_EFS      */	"\x07" "SGI efs"     ,
+/* 0x08         */	"\x08" "SGI lvol"    ,
+/* 0x09         */	"\x09" "SGI rlvol"   ,
+/* SGI_XFS      */	"\x0a" "SGI xfs"     ,
+/* SGI_XFSLOG   */	"\x0b" "SGI xfslog"  ,
+/* SGI_XLV      */	"\x0c" "SGI xlv"     ,
+/* SGI_XVM      */	"\x0d" "SGI xvm"     ,
+/* LINUX_SWAP   */	"\x82" "Linux swap"  ,
+/* LINUX_NATIVE */	"\x83" "Linux native",
+/* LINUX_LVM    */	"\x8d" "Linux LVM"   ,
+/* LINUX_RAID   */	"\xfd" "Linux RAID"  ,
+			NULL
+};
+
+
+static int
+sgi_get_nsect(void)
+{
+	return SGI_SSWAP16(sgilabel->devparam.nsect);
+}
+
+static int
+sgi_get_ntrks(void)
+{
+	return SGI_SSWAP16(sgilabel->devparam.ntrks);
+}
+
+static unsigned int
+two_s_complement_32bit_sum(unsigned int* base, int size /* in bytes */)
+{
+	int i = 0;
+	unsigned int sum = 0;
+
+	size /= sizeof(unsigned int);
+	for (i = 0; i < size; i++)
+		sum -= SGI_SSWAP32(base[i]);
+	return sum;
+}
+
+void BUG_bad_sgi_partition_size(void);
+
+static int
+check_sgi_label(void)
+{
+	if (sizeof(sgi_partition) > 512) {
+		/* According to MIPS Computer Systems, Inc the label
+		 * must not contain more than 512 bytes */
+		BUG_bad_sgi_partition_size();
+	}
+
+	if (sgilabel->magic != SGI_LABEL_MAGIC
+	 && sgilabel->magic != SGI_LABEL_MAGIC_SWAPPED
+	) {
+		current_label_type = LABEL_DOS;
+		return 0;
+	}
+
+	sgi_other_endian = (sgilabel->magic == SGI_LABEL_MAGIC_SWAPPED);
+	/*
+	 * test for correct checksum
+	 */
+	if (two_s_complement_32bit_sum((unsigned int*)sgilabel,
+				sizeof(*sgilabel))) {
+		printf("Detected sgi disklabel with wrong checksum\n");
+	}
+	update_units();
+	current_label_type = LABEL_SGI;
+	g_partitions = 16;
+	sgi_volumes = 15;
+	return 1;
+}
+
+static unsigned int
+sgi_get_start_sector(int i)
+{
+	return SGI_SSWAP32(sgilabel->partitions[i].start_sector);
+}
+
+static unsigned int
+sgi_get_num_sectors(int i)
+{
+	return SGI_SSWAP32(sgilabel->partitions[i].num_sectors);
+}
+
+static int
+sgi_get_sysid(int i)
+{
+	return SGI_SSWAP32(sgilabel->partitions[i].id);
+}
+
+static int
+sgi_get_bootpartition(void)
+{
+	return SGI_SSWAP16(sgilabel->boot_part);
+}
+
+static int
+sgi_get_swappartition(void)
+{
+	return SGI_SSWAP16(sgilabel->swap_part);
+}
+
+static void
+sgi_list_table(int xtra)
+{
+	int i, w, wd;
+	int kpi = 0;                /* kernel partition ID */
+
+	if (xtra) {
+		printf("\nDisk %s (SGI disk label): %u heads, %u sectors\n"
+			"%u cylinders, %u physical cylinders\n"
+			"%u extra sects/cyl, interleave %u:1\n"
+			"%s\n"
+			"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, g_cylinders,
+			SGI_SSWAP16(sgiparam.pcylcount),
+			SGI_SSWAP16(sgiparam.sparecyl),
+			SGI_SSWAP16(sgiparam.ilfact),
+			(char *)sgilabel,
+			str_units(PLURAL), units_per_sector);
+	} else {
+		printf("\nDisk %s (SGI disk label): "
+			"%u heads, %u sectors, %u cylinders\n"
+			"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, g_cylinders,
+			str_units(PLURAL), units_per_sector );
+	}
+
+	w = strlen(disk_device);
+	wd = sizeof("Device") - 1;
+	if (w < wd)
+	w = wd;
+
+	printf("----- partitions -----\n"
+		"Pt# %*s  Info     Start       End   Sectors  Id  System\n",
+		w + 2, "Device");
+	for (i = 0; i < g_partitions; i++) {
+		if (sgi_get_num_sectors(i) || SGI_DEBUG) {
+			uint32_t start = sgi_get_start_sector(i);
+			uint32_t len = sgi_get_num_sectors(i);
+			kpi++;              /* only count nonempty partitions */
+			printf(
+			"%2u: %s %4s %9lu %9lu %9lu  %2x  %s\n",
+/* fdisk part number */	i+1,
+/* device */            partname(disk_device, kpi, w+3),
+/* flags */             (sgi_get_swappartition() == i) ? "swap" :
+/* flags */             (sgi_get_bootpartition() == i) ? "boot" : "    ",
+/* start */             (long) scround(start),
+/* end */               (long) scround(start+len)-1,
+/* no odd flag on end */(long) len,
+/* type id */           sgi_get_sysid(i),
+/* type name */         partition_type(sgi_get_sysid(i)));
+		}
+	}
+	printf("----- Bootinfo -----\nBootfile: %s\n"
+		"----- Directory Entries -----\n",
+		sgilabel->boot_file);
+	for (i = 0; i < sgi_volumes; i++) {
+		if (sgilabel->directory[i].vol_file_size) {
+			uint32_t start = SGI_SSWAP32(sgilabel->directory[i].vol_file_start);
+			uint32_t len = SGI_SSWAP32(sgilabel->directory[i].vol_file_size);
+			unsigned char *name = sgilabel->directory[i].vol_file_name;
+
+			printf("%2u: %-10s sector%5u size%8u\n",
+				i, (char*)name, (unsigned int) start, (unsigned int) len);
+		}
+	}
+}
+
+static void
+sgi_set_bootpartition(int i)
+{
+	sgilabel->boot_part = SGI_SSWAP16(((short)i));
+}
+
+static unsigned int
+sgi_get_lastblock(void)
+{
+	return g_heads * g_sectors * g_cylinders;
+}
+
+static void
+sgi_set_swappartition(int i)
+{
+	sgilabel->swap_part = SGI_SSWAP16(((short)i));
+}
+
+static int
+sgi_check_bootfile(const char* aFile)
+{
+	if (strlen(aFile) < 3) /* "/a\n" is minimum */ {
+		printf("\nInvalid Bootfile!\n"
+			"\tThe bootfile must be an absolute non-zero pathname,\n"
+			"\te.g. \"/unix\" or \"/unix.save\".\n");
+		return 0;
+	}
+	if (strlen(aFile) > 16) {
+		printf("\nName of Bootfile too long (>16 bytes)\n");
+		return 0;
+	}
+	if (aFile[0] != '/') {
+		printf("\nBootfile must have a fully qualified pathname\n");
+		return 0;
+	}
+	if (strncmp(aFile, (char*)sgilabel->boot_file, 16)) {
+		printf("\nBe aware, that the bootfile is not checked for existence.\n"
+			 "\tSGI's default is \"/unix\" and for backup \"/unix.save\".\n");
+		/* filename is correct and did change */
+		return 1;
+	}
+	return 0;   /* filename did not change */
+}
+
+static const char *
+sgi_get_bootfile(void)
+{
+	return (char*)sgilabel->boot_file;
+}
+
+static void
+sgi_set_bootfile(const char* aFile)
+{
+	int i = 0;
+
+	if (sgi_check_bootfile(aFile)) {
+		while (i < 16) {
+			if ((aFile[i] != '\n')  /* in principle caught again by next line */
+			 && (strlen(aFile) > i))
+				sgilabel->boot_file[i] = aFile[i];
+			else
+				sgilabel->boot_file[i] = 0;
+			i++;
+		}
+		printf("\n\tBootfile is changed to \"%s\"\n", sgilabel->boot_file);
+	}
+}
+
+static void
+create_sgiinfo(void)
+{
+	/* I keep SGI's habit to write the sgilabel to the second block */
+	sgilabel->directory[0].vol_file_start = SGI_SSWAP32(2);
+	sgilabel->directory[0].vol_file_size = SGI_SSWAP32(sizeof(sgiinfo));
+	strncpy((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8);
+}
+
+static sgiinfo *fill_sgiinfo(void);
+
+static void
+sgi_write_table(void)
+{
+	sgilabel->csum = 0;
+	sgilabel->csum = SGI_SSWAP32(two_s_complement_32bit_sum(
+			(unsigned int*)sgilabel, sizeof(*sgilabel)));
+	assert(two_s_complement_32bit_sum(
+		(unsigned int*)sgilabel, sizeof(*sgilabel)) == 0);
+
+	write_sector(0, sgilabel);
+	if (!strncmp((char*)sgilabel->directory[0].vol_file_name, "sgilabel", 8)) {
+		/*
+		 * keep this habit of first writing the "sgilabel".
+		 * I never tested whether it works without (AN 981002).
+		 */
+		sgiinfo *info = fill_sgiinfo();
+		int infostartblock = SGI_SSWAP32(sgilabel->directory[0].vol_file_start);
+		write_sector(infostartblock, info);
+		free(info);
+	}
+}
+
+static int
+compare_start(int *x, int *y)
+{
+	/*
+	 * sort according to start sectors
+	 * and prefers largest partition:
+	 * entry zero is entire disk entry
+	 */
+	unsigned int i = *x;
+	unsigned int j = *y;
+	unsigned int a = sgi_get_start_sector(i);
+	unsigned int b = sgi_get_start_sector(j);
+	unsigned int c = sgi_get_num_sectors(i);
+	unsigned int d = sgi_get_num_sectors(j);
+
+	if (a == b)
+		return (d > c) ? 1 : (d == c) ? 0 : -1;
+	return (a > b) ? 1 : -1;
+}
+
+
+static int
+verify_sgi(int verbose)
+{
+	int Index[16];      /* list of valid partitions */
+	int sortcount = 0;  /* number of used partitions, i.e. non-zero lengths */
+	int entire = 0, i = 0;
+	unsigned int start = 0;
+	long long gap = 0;      /* count unused blocks */
+	unsigned int lastblock = sgi_get_lastblock();
+
+	clearfreelist();
+	for (i = 0; i < 16; i++) {
+		if (sgi_get_num_sectors(i) != 0) {
+			Index[sortcount++] = i;
+			if (sgi_get_sysid(i) == SGI_ENTIRE_DISK) {
+				if (entire++ == 1) {
+					if (verbose)
+						printf("More than one entire disk entry present\n");
+				}
+			}
+		}
+	}
+	if (sortcount == 0) {
+		if (verbose)
+			printf("No partitions defined\n");
+		return (lastblock > 0) ? 1 : (lastblock == 0) ? 0 : -1;
+	}
+	qsort(Index, sortcount, sizeof(Index[0]), (void*)compare_start);
+	if (sgi_get_sysid(Index[0]) == SGI_ENTIRE_DISK) {
+		if ((Index[0] != 10) && verbose)
+			printf("IRIX likes when Partition 11 covers the entire disk\n");
+		if ((sgi_get_start_sector(Index[0]) != 0) && verbose)
+			printf("The entire disk partition should start "
+				"at block 0,\n"
+				"not at diskblock %u\n",
+				sgi_get_start_sector(Index[0]));
+		if (SGI_DEBUG)      /* I do not understand how some disks fulfil it */
+			if ((sgi_get_num_sectors(Index[0]) != lastblock) && verbose)
+				printf("The entire disk partition is only %u diskblock large,\n"
+					"but the disk is %u diskblocks long\n",
+					sgi_get_num_sectors(Index[0]), lastblock);
+			lastblock = sgi_get_num_sectors(Index[0]);
+	} else {
+		if (verbose)
+			printf("One Partition (#11) should cover the entire disk\n");
+		if (SGI_DEBUG > 2)
+			printf("sysid=%u\tpartition=%u\n",
+				sgi_get_sysid(Index[0]), Index[0]+1);
+	}
+	for (i = 1, start = 0; i < sortcount; i++) {
+		int cylsize = sgi_get_nsect() * sgi_get_ntrks();
+
+		if ((sgi_get_start_sector(Index[i]) % cylsize) != 0) {
+			if (SGI_DEBUG)      /* I do not understand how some disks fulfil it */
+				if (verbose)
+					printf("Partition %u does not start on cylinder boundary\n",
+						Index[i]+1);
+		}
+		if (sgi_get_num_sectors(Index[i]) % cylsize != 0) {
+			if (SGI_DEBUG)      /* I do not understand how some disks fulfil it */
+				if (verbose)
+					printf("Partition %u does not end on cylinder boundary\n",
+						Index[i]+1);
+		}
+		/* We cannot handle several "entire disk" entries. */
+		if (sgi_get_sysid(Index[i]) == SGI_ENTIRE_DISK) continue;
+		if (start > sgi_get_start_sector(Index[i])) {
+			if (verbose)
+				printf("Partitions %u and %u overlap by %u sectors\n",
+					Index[i-1]+1, Index[i]+1,
+					start - sgi_get_start_sector(Index[i]));
+			if (gap > 0) gap = -gap;
+			if (gap == 0) gap = -1;
+		}
+		if (start < sgi_get_start_sector(Index[i])) {
+			if (verbose)
+				printf("Unused gap of %u sectors - sectors %u-%u\n",
+					sgi_get_start_sector(Index[i]) - start,
+					start, sgi_get_start_sector(Index[i])-1);
+			gap += sgi_get_start_sector(Index[i]) - start;
+			add2freelist(start, sgi_get_start_sector(Index[i]));
+		}
+		start = sgi_get_start_sector(Index[i])
+			   + sgi_get_num_sectors(Index[i]);
+		if (SGI_DEBUG > 1) {
+			if (verbose)
+				printf("%2u:%12u\t%12u\t%12u\n", Index[i],
+					sgi_get_start_sector(Index[i]),
+					sgi_get_num_sectors(Index[i]),
+					sgi_get_sysid(Index[i]));
+		}
+	}
+	if (start < lastblock) {
+		if (verbose)
+			printf("Unused gap of %u sectors - sectors %u-%u\n",
+				lastblock - start, start, lastblock-1);
+		gap += lastblock - start;
+		add2freelist(start, lastblock);
+	}
+	/*
+	 * Done with arithmetics
+	 * Go for details now
+	 */
+	if (verbose) {
+		if (!sgi_get_num_sectors(sgi_get_bootpartition())) {
+			printf("\nThe boot partition does not exist\n");
+		}
+		if (!sgi_get_num_sectors(sgi_get_swappartition())) {
+			printf("\nThe swap partition does not exist\n");
+		} else {
+			if ((sgi_get_sysid(sgi_get_swappartition()) != SGI_SWAP)
+			 && (sgi_get_sysid(sgi_get_swappartition()) != LINUX_SWAP))
+				printf("\nThe swap partition has no swap type\n");
+		}
+		if (sgi_check_bootfile("/unix"))
+			printf("\tYou have chosen an unusual boot file name\n");
+	}
+	return (gap > 0) ? 1 : (gap == 0) ? 0 : -1;
+}
+
+static int
+sgi_gaps(void)
+{
+	/*
+	 * returned value is:
+	 *  = 0 : disk is properly filled to the rim
+	 *  < 0 : there is an overlap
+	 *  > 0 : there is still some vacant space
+	 */
+	return verify_sgi(0);
+}
+
+static void
+sgi_change_sysid(int i, int sys)
+{
+	if (sgi_get_num_sectors(i) == 0) { /* caught already before, ... */
+		printf("Sorry you may change the Tag of non-empty partitions\n");
+		return;
+	}
+	if ((sys != SGI_ENTIRE_DISK) && (sys != SGI_VOLHDR)
+	 && (sgi_get_start_sector(i) < 1)
+	) {
+		read_maybe_empty(
+			"It is highly recommended that the partition at offset 0\n"
+			"is of type \"SGI volhdr\", the IRIX system will rely on it to\n"
+			"retrieve from its directory standalone tools like sash and fx.\n"
+			"Only the \"SGI volume\" entire disk section may violate this.\n"
+			"Type YES if you are sure about tagging this partition differently.\n");
+		if (strcmp(line_ptr, "YES\n") != 0)
+			return;
+	}
+	sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+}
+
+/* returns partition index of first entry marked as entire disk */
+static int
+sgi_entire(void)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		if (sgi_get_sysid(i) == SGI_VOLUME)
+			return i;
+	return -1;
+}
+
+static void
+sgi_set_partition(int i, unsigned int start, unsigned int length, int sys)
+{
+	sgilabel->partitions[i].id = SGI_SSWAP32(sys);
+	sgilabel->partitions[i].num_sectors = SGI_SSWAP32(length);
+	sgilabel->partitions[i].start_sector = SGI_SSWAP32(start);
+	set_changed(i);
+	if (sgi_gaps() < 0)     /* rebuild freelist */
+		printf("Partition overlap detected\n");
+}
+
+static void
+sgi_set_entire(void)
+{
+	int n;
+
+	for (n = 10; n < g_partitions; n++) {
+		if (!sgi_get_num_sectors(n) ) {
+			sgi_set_partition(n, 0, sgi_get_lastblock(), SGI_VOLUME);
+			break;
+		}
+	}
+}
+
+static void
+sgi_set_volhdr(void)
+{
+	int n;
+
+	for (n = 8; n < g_partitions; n++) {
+	if (!sgi_get_num_sectors(n)) {
+		/*
+		 * 5 cylinders is an arbitrary value I like
+		 * IRIX 5.3 stored files in the volume header
+		 * (like sash, symmon, fx, ide) with ca. 3200
+		 * sectors.
+		 */
+		if (g_heads * g_sectors * 5 < sgi_get_lastblock())
+			sgi_set_partition(n, 0, g_heads * g_sectors * 5, SGI_VOLHDR);
+			break;
+		}
+	}
+}
+
+static void
+sgi_delete_partition(int i)
+{
+	sgi_set_partition(i, 0, 0, 0);
+}
+
+static void
+sgi_add_partition(int n, int sys)
+{
+	char mesg[256];
+	unsigned int first = 0, last = 0;
+
+	if (n == 10) {
+		sys = SGI_VOLUME;
+	} else if (n == 8) {
+		sys = 0;
+	}
+	if (sgi_get_num_sectors(n)) {
+		printf(msg_part_already_defined, n + 1);
+		return;
+	}
+	if ((sgi_entire() == -1) && (sys != SGI_VOLUME)) {
+		printf("Attempting to generate entire disk entry automatically\n");
+		sgi_set_entire();
+		sgi_set_volhdr();
+	}
+	if ((sgi_gaps() == 0) && (sys != SGI_VOLUME)) {
+		printf("The entire disk is already covered with partitions\n");
+		return;
+	}
+	if (sgi_gaps() < 0) {
+		printf("You got a partition overlap on the disk. Fix it first!\n");
+		return;
+	}
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	while (1) {
+		if (sys == SGI_VOLUME) {
+			last = sgi_get_lastblock();
+			first = read_int(0, 0, last-1, 0, mesg);
+			if (first != 0) {
+				printf("It is highly recommended that eleventh partition\n"
+						"covers the entire disk and is of type 'SGI volume'\n");
+			}
+		} else {
+			first = freelist[0].first;
+			last  = freelist[0].last;
+			first = read_int(scround(first), scround(first), scround(last)-1,
+				0, mesg);
+		}
+		if (display_in_cyl_units)
+			first *= units_per_sector;
+		else
+			first = first; /* align to cylinder if you know how ... */
+		if (!last )
+			last = isinfreelist(first);
+		if (last != 0)
+			break;
+		printf("You will get a partition overlap on the disk. "
+				"Fix it first!\n");
+	}
+	snprintf(mesg, sizeof(mesg), " Last %s", str_units(SINGULAR));
+	last = read_int(scround(first), scround(last)-1, scround(last)-1,
+			scround(first), mesg)+1;
+	if (display_in_cyl_units)
+		last *= units_per_sector;
+	else
+		last = last; /* align to cylinder if You know how ... */
+	if ( (sys == SGI_VOLUME) && (first != 0 || last != sgi_get_lastblock() ) )
+		printf("It is highly recommended that eleventh partition\n"
+			"covers the entire disk and is of type 'SGI volume'\n");
+	sgi_set_partition(n, first, last-first, sys);
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+static void
+create_sgilabel(void)
+{
+	struct hd_geometry geometry;
+	struct {
+		unsigned int start;
+		unsigned int nsect;
+		int sysid;
+	} old[4];
+	int i = 0;
+	long longsectors;               /* the number of sectors on the device */
+	int res;                        /* the result from the ioctl */
+	int sec_fac;                    /* the sector factor */
+
+	sec_fac = sector_size / 512;    /* determine the sector factor */
+
+	printf(msg_building_new_label, "SGI disklabel");
+
+	sgi_other_endian = BB_LITTLE_ENDIAN;
+	res = ioctl(dev_fd, BLKGETSIZE, &longsectors);
+	if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
+		g_heads = geometry.heads;
+		g_sectors = geometry.sectors;
+		if (res == 0) {
+			/* the get device size ioctl was successful */
+			g_cylinders = longsectors / (g_heads * g_sectors);
+			g_cylinders /= sec_fac;
+		} else {
+			/* otherwise print error and use truncated version */
+			g_cylinders = geometry.cylinders;
+			printf(
+"Warning: BLKGETSIZE ioctl failed on %s.  Using geometry cylinder value of %u.\n"
+"This value may be truncated for devices > 33.8 GB.\n", disk_device, g_cylinders);
+		}
+	}
+	for (i = 0; i < 4; i++) {
+		old[i].sysid = 0;
+		if (valid_part_table_flag(MBRbuffer)) {
+			if (get_part_table(i)->sys_ind) {
+				old[i].sysid = get_part_table(i)->sys_ind;
+				old[i].start = get_start_sect(get_part_table(i));
+				old[i].nsect = get_nr_sects(get_part_table(i));
+				printf("Trying to keep parameters of partition %u\n", i);
+				if (SGI_DEBUG)
+					printf("ID=%02x\tSTART=%u\tLENGTH=%u\n",
+				old[i].sysid, old[i].start, old[i].nsect);
+			}
+		}
+	}
+
+	memset(MBRbuffer, 0, sizeof(MBRbuffer));
+	/* fields with '//' are already zeroed out by memset above */
+
+	sgilabel->magic = SGI_SSWAP32(SGI_LABEL_MAGIC);
+	//sgilabel->boot_part = SGI_SSWAP16(0);
+	sgilabel->swap_part = SGI_SSWAP16(1);
+
+	//memset(sgilabel->boot_file, 0, 16);
+	strcpy((char*)sgilabel->boot_file, "/unix"); /* sizeof(sgilabel->boot_file) == 16 > 6 */
+
+	//sgilabel->devparam.skew                     = (0);
+	//sgilabel->devparam.gap1                     = (0);
+	//sgilabel->devparam.gap2                     = (0);
+	//sgilabel->devparam.sparecyl                 = (0);
+	sgilabel->devparam.pcylcount                = SGI_SSWAP16(geometry.cylinders);
+	//sgilabel->devparam.head_vol0                = SGI_SSWAP16(0);
+	/* tracks/cylinder (heads) */
+	sgilabel->devparam.ntrks                    = SGI_SSWAP16(geometry.heads);
+	//sgilabel->devparam.cmd_tag_queue_depth      = (0);
+	//sgilabel->devparam.unused0                  = (0);
+	//sgilabel->devparam.unused1                  = SGI_SSWAP16(0);
+	/* sectors/track */
+	sgilabel->devparam.nsect                    = SGI_SSWAP16(geometry.sectors);
+	sgilabel->devparam.bytes                    = SGI_SSWAP16(512);
+	sgilabel->devparam.ilfact                   = SGI_SSWAP16(1);
+	sgilabel->devparam.flags                    = SGI_SSWAP32(TRACK_FWD|
+							IGNORE_ERRORS|RESEEK);
+	//sgilabel->devparam.datarate                 = SGI_SSWAP32(0);
+	sgilabel->devparam.retries_on_error         = SGI_SSWAP32(1);
+	//sgilabel->devparam.ms_per_word              = SGI_SSWAP32(0);
+	//sgilabel->devparam.xylogics_gap1            = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_syncdelay       = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_readdelay       = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_gap2            = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_readgate        = SGI_SSWAP16(0);
+	//sgilabel->devparam.xylogics_writecont       = SGI_SSWAP16(0);
+	//memset( &(sgilabel->directory), 0, sizeof(struct volume_directory)*15 );
+	//memset( &(sgilabel->partitions), 0, sizeof(struct sgi_partinfo)*16 );
+	current_label_type = LABEL_SGI;
+	g_partitions = 16;
+	sgi_volumes = 15;
+	sgi_set_entire();
+	sgi_set_volhdr();
+	for (i = 0; i < 4; i++) {
+		if (old[i].sysid) {
+			sgi_set_partition(i, old[i].start, old[i].nsect, old[i].sysid);
+		}
+	}
+}
+
+static void
+sgi_set_xcyl(void)
+{
+	/* do nothing in the beginning */
+}
+#endif /* FEATURE_FDISK_ADVANCED */
+
+/* _____________________________________________________________
+ */
+
+static sgiinfo *
+fill_sgiinfo(void)
+{
+	sgiinfo *info = xzalloc(sizeof(sgiinfo));
+
+	info->magic = SGI_SSWAP32(SGI_INFO_MAGIC);
+	info->b1 = SGI_SSWAP32(-1);
+	info->b2 = SGI_SSWAP16(-1);
+	info->b3 = SGI_SSWAP16(1);
+	/* You may want to replace this string !!!!!!! */
+	strcpy( (char*)info->scsi_string, "IBM OEM 0662S12         3 30" );
+	strcpy( (char*)info->serial, "0000" );
+	info->check1816 = SGI_SSWAP16(18*256 +16 );
+	strcpy( (char*)info->installer, "Sfx version 5.3, Oct 18, 1994" );
+	return info;
+}
+#endif /* SGI_LABEL */
diff --git a/busybox-1.19.3/util-linux/fdisk_sun.c b/busybox-1.19.3/util-linux/fdisk_sun.c
new file mode 100644
index 0000000..e7fcc06
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fdisk_sun.c
@@ -0,0 +1,729 @@
+/*
+ * fdisk_sun.c
+ *
+ * I think this is mostly, or entirely, due to
+ *      Jakub Jelinek (jj@sunsite.mff.cuni.cz), July 1996
+ *
+ * Merged with fdisk for other architectures, aeb, June 1998.
+ *
+ * Sat Mar 20 EST 1999 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *      Internationalization
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+#if ENABLE_FEATURE_SUN_LABEL
+
+#define SUNOS_SWAP 3
+#define SUN_WHOLE_DISK 5
+
+#define SUN_LABEL_MAGIC          0xDABE
+#define SUN_LABEL_MAGIC_SWAPPED  0xBEDA
+#define SUN_SSWAP16(x) (sun_other_endian ? fdisk_swap16(x) : (uint16_t)(x))
+#define SUN_SSWAP32(x) (sun_other_endian ? fdisk_swap32(x) : (uint32_t)(x))
+
+/* Copied from linux/major.h */
+#define FLOPPY_MAJOR    2
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
+static smallint sun_other_endian;
+static smallint scsi_disk;
+static smallint floppy;
+
+#ifndef IDE0_MAJOR
+#define IDE0_MAJOR 3
+#endif
+#ifndef IDE1_MAJOR
+#define IDE1_MAJOR 22
+#endif
+
+static void
+guess_device_type(void)
+{
+	struct stat bootstat;
+
+	if (fstat(dev_fd, &bootstat) < 0) {
+		scsi_disk = 0;
+		floppy = 0;
+	} else if (S_ISBLK(bootstat.st_mode)
+		&& (major(bootstat.st_rdev) == IDE0_MAJOR ||
+		    major(bootstat.st_rdev) == IDE1_MAJOR)) {
+		scsi_disk = 0;
+		floppy = 0;
+	} else if (S_ISBLK(bootstat.st_mode)
+		&& major(bootstat.st_rdev) == FLOPPY_MAJOR) {
+		scsi_disk = 0;
+		floppy = 1;
+	} else {
+		scsi_disk = 1;
+		floppy = 0;
+	}
+}
+
+static const char *const sun_sys_types[] = {
+	"\x00" "Empty"       , /* 0            */
+	"\x01" "Boot"        , /* 1            */
+	"\x02" "SunOS root"  , /* 2            */
+	"\x03" "SunOS swap"  , /* SUNOS_SWAP   */
+	"\x04" "SunOS usr"   , /* 4            */
+	"\x05" "Whole disk"  , /* SUN_WHOLE_DISK   */
+	"\x06" "SunOS stand" , /* 6            */
+	"\x07" "SunOS var"   , /* 7            */
+	"\x08" "SunOS home"  , /* 8            */
+	"\x82" "Linux swap"  , /* LINUX_SWAP   */
+	"\x83" "Linux native", /* LINUX_NATIVE */
+	"\x8e" "Linux LVM"   , /* 0x8e         */
+/* New (2.2.x) raid partition with autodetect using persistent superblock */
+	"\xfd" "Linux raid autodetect", /* 0xfd         */
+	NULL
+};
+
+
+static void
+set_sun_partition(int i, unsigned start, unsigned stop, int sysid)
+{
+	sunlabel->infos[i].id = sysid;
+	sunlabel->partitions[i].start_cylinder =
+		SUN_SSWAP32(start / (g_heads * g_sectors));
+	sunlabel->partitions[i].num_sectors =
+		SUN_SSWAP32(stop - start);
+	set_changed(i);
+}
+
+static int
+check_sun_label(void)
+{
+	unsigned short *ush;
+	int csum;
+
+	if (sunlabel->magic != SUN_LABEL_MAGIC
+	 && sunlabel->magic != SUN_LABEL_MAGIC_SWAPPED
+	) {
+		current_label_type = LABEL_DOS;
+		sun_other_endian = 0;
+		return 0;
+	}
+	sun_other_endian = (sunlabel->magic == SUN_LABEL_MAGIC_SWAPPED);
+	ush = ((unsigned short *) (sunlabel + 1)) - 1;
+	for (csum = 0; ush >= (unsigned short *)sunlabel;) csum ^= *ush--;
+	if (csum) {
+		printf("Detected sun disklabel with wrong checksum.\n"
+"Probably you'll have to set all the values,\n"
+"e.g. heads, sectors, cylinders and partitions\n"
+"or force a fresh label (s command in main menu)\n");
+	} else {
+		g_heads = SUN_SSWAP16(sunlabel->ntrks);
+		g_cylinders = SUN_SSWAP16(sunlabel->ncyl);
+		g_sectors = SUN_SSWAP16(sunlabel->nsect);
+	}
+	update_units();
+	current_label_type = LABEL_SUN;
+	g_partitions = 8;
+	return 1;
+}
+
+static const struct sun_predefined_drives {
+	const char *vendor;
+	const char *model;
+	unsigned short sparecyl;
+	unsigned short ncyl;
+	unsigned short nacyl;
+	unsigned short pcylcount;
+	unsigned short ntrks;
+	unsigned short nsect;
+	unsigned short rspeed;
+} sun_drives[] = {
+	{ "Quantum","ProDrive 80S",1,832,2,834,6,34,3662},
+	{ "Quantum","ProDrive 105S",1,974,2,1019,6,35,3662},
+	{ "CDC","Wren IV 94171-344",3,1545,2,1549,9,46,3600},
+	{ "IBM","DPES-31080",0,4901,2,4903,4,108,5400},
+	{ "IBM","DORS-32160",0,1015,2,1017,67,62,5400},
+	{ "IBM","DNES-318350",0,11199,2,11474,10,320,7200},
+	{ "SEAGATE","ST34371",0,3880,2,3882,16,135,7228},
+	{ "","SUN0104",1,974,2,1019,6,35,3662},
+	{ "","SUN0207",4,1254,2,1272,9,36,3600},
+	{ "","SUN0327",3,1545,2,1549,9,46,3600},
+	{ "","SUN0340",0,1538,2,1544,6,72,4200},
+	{ "","SUN0424",2,1151,2,2500,9,80,4400},
+	{ "","SUN0535",0,1866,2,2500,7,80,5400},
+	{ "","SUN0669",5,1614,2,1632,15,54,3600},
+	{ "","SUN1.0G",5,1703,2,1931,15,80,3597},
+	{ "","SUN1.05",0,2036,2,2038,14,72,5400},
+	{ "","SUN1.3G",6,1965,2,3500,17,80,5400},
+	{ "","SUN2.1G",0,2733,2,3500,19,80,5400},
+	{ "IOMEGA","Jaz",0,1019,2,1021,64,32,5394},
+};
+
+static const struct sun_predefined_drives *
+sun_autoconfigure_scsi(void)
+{
+	const struct sun_predefined_drives *p = NULL;
+
+#ifdef SCSI_IOCTL_GET_IDLUN
+	unsigned int id[2];
+	char buffer[2048];
+	char buffer2[2048];
+	FILE *pfd;
+	char *vendor;
+	char *model;
+	char *q;
+	int i;
+
+	if (ioctl(dev_fd, SCSI_IOCTL_GET_IDLUN, &id))
+		return NULL;
+
+	sprintf(buffer,
+		"Host: scsi%u Channel: %02u Id: %02u Lun: %02u\n",
+		/* This is very wrong (works only if you have one HBA),
+		   but I haven't found a way how to get hostno
+		   from the current kernel */
+		0,
+		(id[0]>>16) & 0xff,
+		id[0] & 0xff,
+		(id[0]>>8) & 0xff
+	);
+	pfd = fopen_for_read("/proc/scsi/scsi");
+	if (!pfd) {
+		return NULL;
+	}
+	while (fgets(buffer2, 2048, pfd)) {
+		if (strcmp(buffer, buffer2))
+			continue;
+		if (!fgets(buffer2, 2048, pfd))
+			break;
+		q = strstr(buffer2, "Vendor: ");
+		if (!q)
+			break;
+		q += 8;
+		vendor = q;
+		q = strstr(q, " ");
+		*q++ = '\0';   /* truncate vendor name */
+		q = strstr(q, "Model: ");
+		if (!q)
+			break;
+		*q = '\0';
+		q += 7;
+		model = q;
+		q = strstr(q, " Rev: ");
+		if (!q)
+			break;
+		*q = '\0';
+		for (i = 0; i < ARRAY_SIZE(sun_drives); i++) {
+			if (*sun_drives[i].vendor && strcasecmp(sun_drives[i].vendor, vendor))
+				continue;
+			if (!strstr(model, sun_drives[i].model))
+				continue;
+			printf("Autoconfigure found a %s%s%s\n",
+					sun_drives[i].vendor,
+					(*sun_drives[i].vendor) ? " " : "",
+					sun_drives[i].model);
+			p = sun_drives + i;
+			break;
+		}
+		break;
+	}
+	fclose(pfd);
+#endif
+	return p;
+}
+
+static void
+create_sunlabel(void)
+{
+	struct hd_geometry geometry;
+	unsigned ndiv;
+	unsigned char c;
+	const struct sun_predefined_drives *p = NULL;
+
+	printf(msg_building_new_label, "sun disklabel");
+
+	sun_other_endian = BB_LITTLE_ENDIAN;
+	memset(MBRbuffer, 0, sizeof(MBRbuffer));
+	sunlabel->magic = SUN_SSWAP16(SUN_LABEL_MAGIC);
+	if (!floppy) {
+		unsigned i;
+		puts("Drive type\n"
+		 "   ?   auto configure\n"
+		 "   0   custom (with hardware detected defaults)");
+		for (i = 0; i < ARRAY_SIZE(sun_drives); i++) {
+			printf("   %c   %s%s%s\n",
+				i + 'a', sun_drives[i].vendor,
+				(*sun_drives[i].vendor) ? " " : "",
+				sun_drives[i].model);
+		}
+		while (1) {
+			c = read_nonempty("Select type (? for auto, 0 for custom): ");
+			if (c == '0') {
+				break;
+			}
+			if (c >= 'a' && c < 'a' + ARRAY_SIZE(sun_drives)) {
+				p = sun_drives + c - 'a';
+				break;
+			}
+			if (c >= 'A' && c < 'A' + ARRAY_SIZE(sun_drives)) {
+				p = sun_drives + c - 'A';
+				break;
+			}
+			if (c == '?' && scsi_disk) {
+				p = sun_autoconfigure_scsi();
+				if (p)
+					break;
+				printf("Autoconfigure failed\n");
+			}
+		}
+	}
+	if (!p || floppy) {
+		if (!ioctl(dev_fd, HDIO_GETGEO, &geometry)) {
+			g_heads = geometry.heads;
+			g_sectors = geometry.sectors;
+			g_cylinders = geometry.cylinders;
+		} else {
+			g_heads = 0;
+			g_sectors = 0;
+			g_cylinders = 0;
+		}
+		if (floppy) {
+			sunlabel->nacyl = 0;
+			sunlabel->pcylcount = SUN_SSWAP16(g_cylinders);
+			sunlabel->rspeed = SUN_SSWAP16(300);
+			sunlabel->ilfact = SUN_SSWAP16(1);
+			sunlabel->sparecyl = 0;
+		} else {
+			g_heads = read_int(1, g_heads, 1024, 0, "Heads");
+			g_sectors = read_int(1, g_sectors, 1024, 0, "Sectors/track");
+		if (g_cylinders)
+			g_cylinders = read_int(1, g_cylinders - 2, 65535, 0, "Cylinders");
+		else
+			g_cylinders = read_int(1, 0, 65535, 0, "Cylinders");
+			sunlabel->nacyl = SUN_SSWAP16(read_int(0, 2, 65535, 0, "Alternate cylinders"));
+			sunlabel->pcylcount = SUN_SSWAP16(read_int(0, g_cylinders + SUN_SSWAP16(sunlabel->nacyl), 65535, 0, "Physical cylinders"));
+			sunlabel->rspeed = SUN_SSWAP16(read_int(1, 5400, 100000, 0, "Rotation speed (rpm)"));
+			sunlabel->ilfact = SUN_SSWAP16(read_int(1, 1, 32, 0, "Interleave factor"));
+			sunlabel->sparecyl = SUN_SSWAP16(read_int(0, 0, g_sectors, 0, "Extra sectors per cylinder"));
+		}
+	} else {
+		sunlabel->sparecyl = SUN_SSWAP16(p->sparecyl);
+		sunlabel->ncyl = SUN_SSWAP16(p->ncyl);
+		sunlabel->nacyl = SUN_SSWAP16(p->nacyl);
+		sunlabel->pcylcount = SUN_SSWAP16(p->pcylcount);
+		sunlabel->ntrks = SUN_SSWAP16(p->ntrks);
+		sunlabel->nsect = SUN_SSWAP16(p->nsect);
+		sunlabel->rspeed = SUN_SSWAP16(p->rspeed);
+		sunlabel->ilfact = SUN_SSWAP16(1);
+		g_cylinders = p->ncyl;
+		g_heads = p->ntrks;
+		g_sectors = p->nsect;
+		puts("You may change all the disk params from the x menu");
+	}
+
+	snprintf((char *)(sunlabel->info), sizeof(sunlabel->info),
+		"%s%s%s cyl %u alt %u hd %u sec %u",
+		p ? p->vendor : "", (p && *p->vendor) ? " " : "",
+		p ? p->model : (floppy ? "3,5\" floppy" : "Linux custom"),
+		g_cylinders, SUN_SSWAP16(sunlabel->nacyl), g_heads, g_sectors);
+
+	sunlabel->ntrks = SUN_SSWAP16(g_heads);
+	sunlabel->nsect = SUN_SSWAP16(g_sectors);
+	sunlabel->ncyl = SUN_SSWAP16(g_cylinders);
+	if (floppy)
+		set_sun_partition(0, 0, g_cylinders * g_heads * g_sectors, LINUX_NATIVE);
+	else {
+		if (g_cylinders * g_heads * g_sectors >= 150 * 2048) {
+			ndiv = g_cylinders - (50 * 2048 / (g_heads * g_sectors)); /* 50M swap */
+		} else
+			ndiv = g_cylinders * 2 / 3;
+		set_sun_partition(0, 0, ndiv * g_heads * g_sectors, LINUX_NATIVE);
+		set_sun_partition(1, ndiv * g_heads * g_sectors, g_cylinders * g_heads * g_sectors, LINUX_SWAP);
+		sunlabel->infos[1].flags |= 0x01; /* Not mountable */
+	}
+	set_sun_partition(2, 0, g_cylinders * g_heads * g_sectors, SUN_WHOLE_DISK);
+	{
+		unsigned short *ush = (unsigned short *)sunlabel;
+		unsigned short csum = 0;
+		while (ush < (unsigned short *)(&sunlabel->csum))
+			csum ^= *ush++;
+		sunlabel->csum = csum;
+	}
+
+	set_all_unchanged();
+	set_changed(0);
+	get_boot(CREATE_EMPTY_SUN);
+}
+
+static void
+toggle_sunflags(int i, unsigned char mask)
+{
+	if (sunlabel->infos[i].flags & mask)
+		sunlabel->infos[i].flags &= ~mask;
+	else
+		sunlabel->infos[i].flags |= mask;
+	set_changed(i);
+}
+
+static void
+fetch_sun(unsigned *starts, unsigned *lens, unsigned *start, unsigned *stop)
+{
+	int i, continuous = 1;
+
+	*start = 0;
+	*stop = g_cylinders * g_heads * g_sectors;
+	for (i = 0; i < g_partitions; i++) {
+		if (sunlabel->partitions[i].num_sectors
+		 && sunlabel->infos[i].id
+		 && sunlabel->infos[i].id != SUN_WHOLE_DISK) {
+			starts[i] = SUN_SSWAP32(sunlabel->partitions[i].start_cylinder) * g_heads * g_sectors;
+			lens[i] = SUN_SSWAP32(sunlabel->partitions[i].num_sectors);
+			if (continuous) {
+				if (starts[i] == *start)
+					*start += lens[i];
+				else if (starts[i] + lens[i] >= *stop)
+					*stop = starts[i];
+				else
+					continuous = 0;
+					/* There will be probably more gaps
+					  than one, so lets check afterwards */
+			}
+		} else {
+			starts[i] = 0;
+			lens[i] = 0;
+		}
+	}
+}
+
+static unsigned *verify_sun_starts;
+
+static int
+verify_sun_cmp(int *a, int *b)
+{
+	if (*a == -1) return 1;
+	if (*b == -1) return -1;
+	if (verify_sun_starts[*a] > verify_sun_starts[*b]) return 1;
+	return -1;
+}
+
+static void
+verify_sun(void)
+{
+	unsigned starts[8], lens[8], start, stop;
+	int i,j,k,starto,endo;
+	int array[8];
+
+	verify_sun_starts = starts;
+	fetch_sun(starts, lens, &start, &stop);
+	for (k = 0; k < 7; k++) {
+		for (i = 0; i < 8; i++) {
+			if (k && (lens[i] % (g_heads * g_sectors))) {
+				printf("Partition %u doesn't end on cylinder boundary\n", i+1);
+			}
+			if (lens[i]) {
+				for (j = 0; j < i; j++)
+					if (lens[j]) {
+						if (starts[j] == starts[i]+lens[i]) {
+							starts[j] = starts[i]; lens[j] += lens[i];
+							lens[i] = 0;
+						} else if (starts[i] == starts[j]+lens[j]){
+							lens[j] += lens[i];
+							lens[i] = 0;
+						} else if (!k) {
+							if (starts[i] < starts[j]+lens[j]
+							 && starts[j] < starts[i]+lens[i]) {
+								starto = starts[i];
+								if (starts[j] > starto)
+									starto = starts[j];
+								endo = starts[i]+lens[i];
+								if (starts[j]+lens[j] < endo)
+									endo = starts[j]+lens[j];
+								printf("Partition %u overlaps with others in "
+									"sectors %u-%u\n", i+1, starto, endo);
+							}
+						}
+					}
+			}
+		}
+	}
+	for (i = 0; i < 8; i++) {
+		if (lens[i])
+			array[i] = i;
+		else
+			array[i] = -1;
+	}
+	qsort(array, ARRAY_SIZE(array), sizeof(array[0]),
+		(int (*)(const void *,const void *)) verify_sun_cmp);
+	if (array[0] == -1) {
+		printf("No partitions defined\n");
+		return;
+	}
+	stop = g_cylinders * g_heads * g_sectors;
+	if (starts[array[0]])
+		printf("Unused gap - sectors %u-%u\n", 0, starts[array[0]]);
+	for (i = 0; i < 7 && array[i+1] != -1; i++) {
+		printf("Unused gap - sectors %u-%u\n", starts[array[i]]+lens[array[i]], starts[array[i+1]]);
+	}
+	start = starts[array[i]] + lens[array[i]];
+	if (start < stop)
+		printf("Unused gap - sectors %u-%u\n", start, stop);
+}
+
+static void
+add_sun_partition(int n, int sys)
+{
+	unsigned start, stop, stop2;
+	unsigned starts[8], lens[8];
+	int whole_disk = 0;
+
+	char mesg[256];
+	int i, first, last;
+
+	if (sunlabel->partitions[n].num_sectors && sunlabel->infos[n].id) {
+		printf(msg_part_already_defined, n + 1);
+		return;
+	}
+
+	fetch_sun(starts, lens, &start, &stop);
+	if (stop <= start) {
+		if (n == 2)
+			whole_disk = 1;
+		else {
+			printf("Other partitions already cover the whole disk.\n"
+				"Delete/shrink them before retry.\n");
+			return;
+		}
+	}
+	snprintf(mesg, sizeof(mesg), "First %s", str_units(SINGULAR));
+	while (1) {
+		if (whole_disk)
+			first = read_int(0, 0, 0, 0, mesg);
+		else
+			first = read_int(scround(start), scround(stop)+1,
+					 scround(stop), 0, mesg);
+		if (display_in_cyl_units)
+			first *= units_per_sector;
+		else
+			/* Starting sector has to be properly aligned */
+			first = (first + g_heads * g_sectors - 1) / (g_heads * g_sectors);
+		if (n == 2 && first != 0)
+			printf("\
+It is highly recommended that the third partition covers the whole disk\n\
+and is of type 'Whole disk'\n");
+		/* ewt asks to add: "don't start a partition at cyl 0"
+		   However, edmundo@rano.demon.co.uk writes:
+		   "In addition to having a Sun partition table, to be able to
+		   boot from the disc, the first partition, /dev/sdX1, must
+		   start at cylinder 0. This means that /dev/sdX1 contains
+		   the partition table and the boot block, as these are the
+		   first two sectors of the disc. Therefore you must be
+		   careful what you use /dev/sdX1 for. In particular, you must
+		   not use a partition starting at cylinder 0 for Linux swap,
+		   as that would overwrite the partition table and the boot
+		   block. You may, however, use such a partition for a UFS
+		   or EXT2 file system, as these file systems leave the first
+		   1024 bytes undisturbed. */
+		/* On the other hand, one should not use partitions
+		   starting at block 0 in an md, or the label will
+		   be trashed. */
+		for (i = 0; i < g_partitions; i++)
+			if (lens[i] && starts[i] <= first && starts[i] + lens[i] > first)
+				break;
+		if (i < g_partitions && !whole_disk) {
+			if (n == 2 && !first) {
+				whole_disk = 1;
+				break;
+			}
+			printf("Sector %u is already allocated\n", first);
+		} else
+			break;
+	}
+	stop = g_cylinders * g_heads * g_sectors;
+	stop2 = stop;
+	for (i = 0; i < g_partitions; i++) {
+		if (starts[i] > first && starts[i] < stop)
+			stop = starts[i];
+	}
+	snprintf(mesg, sizeof(mesg),
+		"Last %s or +size or +sizeM or +sizeK",
+		str_units(SINGULAR));
+	if (whole_disk)
+		last = read_int(scround(stop2), scround(stop2), scround(stop2),
+				0, mesg);
+	else if (n == 2 && !first)
+		last = read_int(scround(first), scround(stop2), scround(stop2),
+				scround(first), mesg);
+	else
+		last = read_int(scround(first), scround(stop), scround(stop),
+				scround(first), mesg);
+	if (display_in_cyl_units)
+		last *= units_per_sector;
+	if (n == 2 && !first) {
+		if (last >= stop2) {
+			whole_disk = 1;
+			last = stop2;
+		} else if (last > stop) {
+			printf(
+"You haven't covered the whole disk with the 3rd partition,\n"
+"but your value %u %s covers some other partition.\n"
+"Your entry has been changed to %u %s\n",
+				scround(last), str_units(SINGULAR),
+				scround(stop), str_units(SINGULAR));
+			last = stop;
+		}
+	} else if (!whole_disk && last > stop)
+		last = stop;
+
+	if (whole_disk)
+		sys = SUN_WHOLE_DISK;
+	set_sun_partition(n, first, last, sys);
+}
+
+static void
+sun_delete_partition(int i)
+{
+	unsigned int nsec;
+
+	if (i == 2
+	 && sunlabel->infos[i].id == SUN_WHOLE_DISK
+	 && !sunlabel->partitions[i].start_cylinder
+	 && (nsec = SUN_SSWAP32(sunlabel->partitions[i].num_sectors)) == g_heads * g_sectors * g_cylinders)
+		printf("If you want to maintain SunOS/Solaris compatibility, "
+			"consider leaving this\n"
+			"partition as Whole disk (5), starting at 0, with %u "
+			"sectors\n", nsec);
+	sunlabel->infos[i].id = 0;
+	sunlabel->partitions[i].num_sectors = 0;
+}
+
+static void
+sun_change_sysid(int i, int sys)
+{
+	if (sys == LINUX_SWAP && !sunlabel->partitions[i].start_cylinder) {
+		read_maybe_empty(
+			"It is highly recommended that the partition at offset 0\n"
+			"is UFS, EXT2FS filesystem or SunOS swap. Putting Linux swap\n"
+			"there may destroy your partition table and bootblock.\n"
+			"Type YES if you're very sure you would like that partition\n"
+			"tagged with 82 (Linux swap): ");
+		if (strcmp (line_ptr, "YES\n"))
+			return;
+	}
+	switch (sys) {
+	case SUNOS_SWAP:
+	case LINUX_SWAP:
+		/* swaps are not mountable by default */
+		sunlabel->infos[i].flags |= 0x01;
+		break;
+	default:
+		/* assume other types are mountable;
+		   user can change it anyway */
+		sunlabel->infos[i].flags &= ~0x01;
+		break;
+	}
+	sunlabel->infos[i].id = sys;
+}
+
+static void
+sun_list_table(int xtra)
+{
+	int i, w;
+
+	w = strlen(disk_device);
+	if (xtra)
+		printf(
+		"\nDisk %s (Sun disk label): %u heads, %u sectors, %u rpm\n"
+		"%u cylinders, %u alternate cylinders, %u physical cylinders\n"
+		"%u extra sects/cyl, interleave %u:1\n"
+		"%s\n"
+		"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, SUN_SSWAP16(sunlabel->rspeed),
+			g_cylinders, SUN_SSWAP16(sunlabel->nacyl),
+			SUN_SSWAP16(sunlabel->pcylcount),
+			SUN_SSWAP16(sunlabel->sparecyl),
+			SUN_SSWAP16(sunlabel->ilfact),
+			(char *)sunlabel,
+			str_units(PLURAL), units_per_sector);
+	else
+		printf(
+	"\nDisk %s (Sun disk label): %u heads, %u sectors, %u cylinders\n"
+	"Units = %s of %u * 512 bytes\n\n",
+			disk_device, g_heads, g_sectors, g_cylinders,
+			str_units(PLURAL), units_per_sector);
+
+	printf("%*s Flag    Start       End    Blocks   Id  System\n",
+		w + 1, "Device");
+	for (i = 0; i < g_partitions; i++) {
+		if (sunlabel->partitions[i].num_sectors) {
+			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 */
+				(sunlabel->infos[i].flags & 0x01) ? 'u' : ' ',  /* flags */
+				(sunlabel->infos[i].flags & 0x10) ? 'r' : ' ',
+				(long) scround(start),                          /* start */
+				(long) scround(start+len),                      /* end */
+				(long) len / 2, len & 1 ? '+' : ' ',            /* odd flag on end */
+				sunlabel->infos[i].id,                          /* type id */
+				partition_type(sunlabel->infos[i].id));         /* type name */
+		}
+	}
+}
+
+#if ENABLE_FEATURE_FDISK_ADVANCED
+
+static void
+sun_set_alt_cyl(void)
+{
+	sunlabel->nacyl =
+		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->nacyl), 65535, 0,
+				"Number of alternate cylinders"));
+}
+
+static void
+sun_set_ncyl(int cyl)
+{
+	sunlabel->ncyl = SUN_SSWAP16(cyl);
+}
+
+static void
+sun_set_xcyl(void)
+{
+	sunlabel->sparecyl =
+		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->sparecyl), g_sectors, 0,
+				"Extra sectors per cylinder"));
+}
+
+static void
+sun_set_ilfact(void)
+{
+	sunlabel->ilfact =
+		SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->ilfact), 32, 0,
+				"Interleave factor"));
+}
+
+static void
+sun_set_rspeed(void)
+{
+	sunlabel->rspeed =
+		SUN_SSWAP16(read_int(1, SUN_SSWAP16(sunlabel->rspeed), 100000, 0,
+				"Rotation speed (rpm)"));
+}
+
+static void
+sun_set_pcylcount(void)
+{
+	sunlabel->pcylcount =
+		SUN_SSWAP16(read_int(0, SUN_SSWAP16(sunlabel->pcylcount), 65535, 0,
+				"Number of physical cylinders"));
+}
+#endif /* FEATURE_FDISK_ADVANCED */
+
+static void
+sun_write_table(void)
+{
+	unsigned short *ush = (unsigned short *)sunlabel;
+	unsigned short csum = 0;
+
+	while (ush < (unsigned short *)(&sunlabel->csum))
+		csum ^= *ush++;
+	sunlabel->csum = csum;
+	write_sector(0, sunlabel);
+}
+#endif /* SUN_LABEL */
diff --git a/busybox-1.19.3/util-linux/findfs.c b/busybox-1.19.3/util-linux/findfs.c
new file mode 100644
index 0000000..49e8979
--- /dev/null
+++ b/busybox-1.19.3/util-linux/findfs.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Support functions for mounting devices by label/uuid
+ *
+ * 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 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"
+
+int findfs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int findfs_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *dev = *++argv;
+
+	if (!dev)
+		bb_show_usage();
+
+	if (strncmp(dev, "/dev/", 5) == 0) {
+		/* Just pass any /dev/xxx name right through.
+		 * This might aid in some scripts being able
+		 * to call this unconditionally */
+		dev = NULL;
+	} else {
+		/* Otherwise, handle LABEL=xxx and UUID=xxx,
+		 * fail on anything else */
+		if (!resolve_mount_spec(argv))
+			bb_show_usage();
+	}
+
+	if (*argv != dev) {
+		puts(*argv);
+		return 0;
+	}
+	return 1;
+}
diff --git a/busybox-1.19.3/util-linux/flock.c b/busybox-1.19.3/util-linux/flock.c
new file mode 100644
index 0000000..e9be4ee
--- /dev/null
+++ b/busybox-1.19.3/util-linux/flock.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 Timo Teras <timo.teras@iki.fi>
+ *
+ * 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"
+
+int flock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int flock_main(int argc UNUSED_PARAM, char **argv)
+{
+	int mode, opt, fd;
+	enum {
+		OPT_s = (1 << 0),
+		OPT_x = (1 << 1),
+		OPT_n = (1 << 2),
+		OPT_u = (1 << 3),
+		OPT_c = (1 << 4),
+	};
+
+#if ENABLE_LONG_OPTS
+	static const char getopt_longopts[] ALIGN1 =
+		"shared\0"      No_argument       "s"
+		"exclusive\0"   No_argument       "x"
+		"unlock\0"      No_argument       "u"
+		"nonblock\0"    No_argument       "n"
+		;
+	applet_long_options = getopt_longopts;
+#endif
+	opt_complementary = "-1";
+
+	opt = getopt32(argv, "+sxnu");
+	argv += optind;
+
+	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);
+		if (fd < 0)
+			bb_perror_msg_and_die("can't open '%s'", argv[0]);
+		//TODO? close_on_exec_on(fd);
+	} else {
+		fd = xatoi_positive(argv[0]);
+	}
+	argv++;
+
+	/* If it is "flock FILE -c PROG", then -c isn't caught by getopt32:
+	 * we use "+" in order to support "flock -opt FILE PROG -with-opts",
+	 * we need to remove -c by hand.
+	 * TODO: in upstream, -c 'PROG ARGS' means "run sh -c 'PROG ARGS'"
+	 */
+	if (argv[0]
+	 && argv[0][0] == '-'
+	 && (  (argv[0][1] == 'c' && !argv[0][2])
+	    || (ENABLE_LONG_OPTS && strcmp(argv[0] + 1, "-command") == 0)
+	    )
+	) {
+		argv++;
+	}
+
+	if (OPT_s == LOCK_SH && OPT_x == LOCK_EX && OPT_n == LOCK_NB && OPT_u == LOCK_UN) {
+		/* With suitably matched constants, mode setting is much simpler */
+		mode = opt & (LOCK_SH + LOCK_EX + LOCK_NB + LOCK_UN);
+		if (!(mode & ~LOCK_NB))
+			mode |= LOCK_EX;
+	} else {
+		if (opt & OPT_u)
+			mode = LOCK_UN;
+		else if (opt & OPT_s)
+			mode = LOCK_SH;
+		else
+			mode = LOCK_EX;
+		if (opt & OPT_n)
+			mode |= LOCK_NB;
+	}
+
+	if (flock(fd, mode) != 0) {
+		if (errno == EWOULDBLOCK)
+			return EXIT_FAILURE;
+		bb_perror_nomsg_and_die();
+	}
+
+	if (argv[0])
+		return spawn_and_wait(argv);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/freeramdisk.c b/busybox-1.19.3/util-linux/freeramdisk.c
new file mode 100644
index 0000000..a89ae1a
--- /dev/null
+++ b/busybox-1.19.3/util-linux/freeramdisk.c
@@ -0,0 +1,45 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * freeramdisk and fdflush implementations for busybox
+ *
+ * Copyright (C) 2000 and written by Emanuele Caratti <wiz@iol.it>
+ * 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 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"
+
+/* From <linux/fd.h> */
+#define FDFLUSH  _IO(2,0x4b)
+
+int freeramdisk_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int freeramdisk_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+
+	fd = xopen(single_argv(argv), O_RDWR);
+
+	// Act like freeramdisk, fdflush, or both depending on configuration.
+	ioctl_or_perror_and_die(fd, (ENABLE_FREERAMDISK && applet_name[1] == 'r')
+			|| !ENABLE_FDFLUSH ? BLKFLSBUF : FDFLUSH, NULL, "%s", argv[1]);
+
+	if (ENABLE_FEATURE_CLEAN_UP) close(fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/fsck_minix.c b/busybox-1.19.3/util-linux/fsck_minix.c
new file mode 100644
index 0000000..1508ecb
--- /dev/null
+++ b/busybox-1.19.3/util-linux/fsck_minix.c
@@ -0,0 +1,1316 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * fsck.c - a file system consistency checker for Linux.
+ *
+ * (C) 1991, 1992 Linus Torvalds.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * 09.11.91  -  made the first rudimentary functions
+ *
+ * 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
+ *		correction-code, and changed some functions.
+ *
+ * 15.11.91  -  More correction code. Hopefully it notices most
+ *		cases now, and tries to do something about them.
+ *
+ * 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
+ *		kernel bug ate my enhanced fsck in february.
+ *
+ * 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
+ *
+ * Sat Oct  9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
+ *                           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.
+ *
+ * 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)
+ *
+ * 01.07.96  - Fixed the v2 fs stuff to use the right #defines and such
+ *	       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
+ *             i386 and m64k must use assembly to get it fast >:-)
+ *	       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>.
+ *
+ * 06.11.96  - Added v2 code submitted by Joerg Dorchain, but written by
+ *             Andreas Schwab.
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ *
+ * I've had no time to add comments - hopefully the function names
+ * are comments enough. As with all file system checkers, this assumes
+ * the file system is quiescent - don't use it on a mounted device
+ * unless you can be sure nobody is writing to it (and remember that the
+ * kernel can write to it when it searches for files).
+ *
+ * Usage: fsck [-larvsm] device
+ *	-l for a listing of all the filenames
+ *	-a for automatic repairs (not implemented)
+ *	-r for repairs (interactive) (not implemented)
+ *	-v for verbose (tells how many files)
+ *	-s for superblock info
+ *	-m for minix-like "mode not cleared" warnings
+ *	-f force filesystem check even if filesystem marked as valid
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * 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"
+
+#ifndef BLKGETSIZE
+#define BLKGETSIZE _IO(0x12,96)    /* return device size */
+#endif
+
+struct BUG_bad_inode_size {
+	char BUG_bad_inode1_size[(INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
+#if ENABLE_FEATURE_MINIX2
+	char BUG_bad_inode2_size[(INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE) ? -1 : 1];
+#endif
+};
+
+enum {
+#ifdef UNUSED
+	MINIX1_LINK_MAX = 250,
+	MINIX2_LINK_MAX = 65530,
+	MINIX_I_MAP_SLOTS = 8,
+	MINIX_Z_MAP_SLOTS = 64,
+	MINIX_V1 = 0x0001,      /* original minix fs */
+	MINIX_V2 = 0x0002,      /* minix V2 fs */
+#endif
+	MINIX_NAME_MAX = 255,         /* # chars in a file name */
+};
+
+
+#if !ENABLE_FEATURE_MINIX2
+enum { version2 = 0 };
+#endif
+
+enum { MAX_DEPTH = 32 };
+
+enum { dev_fd = 3 };
+
+struct globals {
+#if ENABLE_FEATURE_MINIX2
+	smallint version2;
+#endif
+	smallint changed;  /* is filesystem modified? */
+	smallint errors_uncorrected;  /* flag if some error was not corrected */
+	smallint termios_set;
+	smallint dirsize;
+	smallint namelen;
+	const char *device_name;
+	int directory, regular, blockdev, chardev, links, symlinks, total;
+	char *inode_buffer;
+
+	char *inode_map;
+	char *zone_map;
+
+	unsigned char *inode_count;
+	unsigned char *zone_count;
+
+	/* File-name data */
+	int name_depth;
+	char *name_component[MAX_DEPTH+1];
+
+	/* Bigger stuff */
+	struct termios sv_termios;
+	char superblock_buffer[BLOCK_SIZE];
+	char add_zone_ind_blk[BLOCK_SIZE];
+	char add_zone_dind_blk[BLOCK_SIZE];
+	IF_FEATURE_MINIX2(char add_zone_tind_blk[BLOCK_SIZE];)
+	char check_file_blk[BLOCK_SIZE];
+
+	/* File-name data */
+	char current_name[MAX_DEPTH * MINIX_NAME_MAX];
+};
+#define G (*ptr_to_globals)
+#if ENABLE_FEATURE_MINIX2
+#define version2           (G.version2           )
+#endif
+#define changed            (G.changed            )
+#define errors_uncorrected (G.errors_uncorrected )
+#define termios_set        (G.termios_set        )
+#define dirsize            (G.dirsize            )
+#define namelen            (G.namelen            )
+#define device_name        (G.device_name        )
+#define directory          (G.directory          )
+#define regular            (G.regular            )
+#define blockdev           (G.blockdev           )
+#define chardev            (G.chardev            )
+#define links              (G.links              )
+#define symlinks           (G.symlinks           )
+#define total              (G.total              )
+#define inode_buffer       (G.inode_buffer       )
+#define inode_map          (G.inode_map          )
+#define zone_map           (G.zone_map           )
+#define inode_count        (G.inode_count        )
+#define zone_count         (G.zone_count         )
+#define name_depth         (G.name_depth         )
+#define name_component     (G.name_component     )
+#define sv_termios         (G.sv_termios         )
+#define superblock_buffer  (G.superblock_buffer )
+#define add_zone_ind_blk   (G.add_zone_ind_blk   )
+#define add_zone_dind_blk  (G.add_zone_dind_blk  )
+#define add_zone_tind_blk  (G.add_zone_tind_blk  )
+#define check_file_blk     (G.check_file_blk     )
+#define current_name       (G.current_name       )
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+	dirsize = 16; \
+	namelen = 14; \
+	current_name[0] = '/'; \
+	/*current_name[1] = '\0';*/ \
+	name_component[0] = &current_name[0]; \
+} while (0)
+
+
+#define OPTION_STR "larvsmf"
+enum {
+	OPT_l = (1 << 0),
+	OPT_a = (1 << 1),
+	OPT_r = (1 << 2),
+	OPT_v = (1 << 3),
+	OPT_s = (1 << 4),
+	OPT_w = (1 << 5),
+	OPT_f = (1 << 6),
+};
+#define OPT_list      (option_mask32 & OPT_l)
+#define OPT_automatic (option_mask32 & OPT_a)
+#define OPT_repair    (option_mask32 & OPT_r)
+#define OPT_verbose   (option_mask32 & OPT_v)
+#define OPT_show      (option_mask32 & OPT_s)
+#define OPT_warn_mode (option_mask32 & OPT_w)
+#define OPT_force     (option_mask32 & OPT_f)
+/* non-automatic repairs requested? */
+#define OPT_manual    ((option_mask32 & (OPT_a|OPT_r)) == OPT_r)
+
+
+#define Inode1 (((struct minix1_inode *) inode_buffer)-1)
+#define Inode2 (((struct minix2_inode *) inode_buffer)-1)
+
+#define Super (*(struct minix_superblock *)(superblock_buffer))
+
+#if ENABLE_FEATURE_MINIX2
+# define ZONES    ((unsigned)(version2 ? Super.s_zones : Super.s_nzones))
+#else
+# define ZONES    ((unsigned)(Super.s_nzones))
+#endif
+#define INODES    ((unsigned)Super.s_ninodes)
+#define IMAPS     ((unsigned)Super.s_imap_blocks)
+#define ZMAPS     ((unsigned)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned)Super.s_firstdatazone)
+#define ZONESIZE  ((unsigned)Super.s_log_zone_size)
+#define MAXSIZE   ((unsigned)Super.s_max_size)
+#define MAGIC     (Super.s_magic)
+
+/* gcc likes this more (code is smaller) than macro variant */
+static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
+{
+	return (size + n-1) / n;
+}
+
+#if !ENABLE_FEATURE_MINIX2
+#define INODE_BLOCKS            div_roundup(INODES, MINIX1_INODES_PER_BLOCK)
+#else
+#define INODE_BLOCKS            div_roundup(INODES, \
+                                (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK))
+#endif
+
+#define INODE_BUFFER_SIZE       (INODE_BLOCKS * BLOCK_SIZE)
+#define NORM_FIRSTZONE          (2 + IMAPS + ZMAPS + INODE_BLOCKS)
+
+/* Before you ask "where they come from?": */
+/* setbit/clrbit are supplied by sys/param.h */
+
+static int minix_bit(const char *a, unsigned i)
+{
+	return (a[i >> 3] & (1<<(i & 7)));
+}
+
+static void minix_setbit(char *a, unsigned i)
+{
+	setbit(a, i);
+	changed = 1;
+}
+static void minix_clrbit(char *a, unsigned i)
+{
+	clrbit(a, i);
+	changed = 1;
+}
+
+/* Note: do not assume 0/1, it is 0/nonzero */
+#define zone_in_use(x)  (minix_bit(zone_map,(x)-FIRSTZONE+1))
+#define inode_in_use(x) (minix_bit(inode_map,(x)))
+
+#define mark_inode(x)   (minix_setbit(inode_map,(x)))
+#define unmark_inode(x) (minix_clrbit(inode_map,(x)))
+
+#define mark_zone(x)    (minix_setbit(zone_map,(x)-FIRSTZONE+1))
+#define unmark_zone(x)  (minix_clrbit(zone_map,(x)-FIRSTZONE+1))
+
+
+static void recursive_check(unsigned ino);
+#if ENABLE_FEATURE_MINIX2
+static void recursive_check2(unsigned ino);
+#endif
+
+static void die(const char *str) NORETURN;
+static void die(const char *str)
+{
+	if (termios_set)
+		tcsetattr_stdin_TCSANOW(&sv_termios);
+	bb_error_msg_and_die("%s", str);
+}
+
+static void push_filename(const char *name)
+{
+	//  /dir/dir/dir/file
+	//  ^   ^   ^
+	// [0] [1] [2] <-name_component[i]
+	if (name_depth < MAX_DEPTH) {
+		int len;
+		char *p = name_component[name_depth];
+		*p++ = '/';
+		len = sprintf(p, "%.*s", namelen, name);
+		name_component[name_depth + 1] = p + len;
+	}
+	name_depth++;
+}
+
+static void pop_filename(void)
+{
+	name_depth--;
+	if (name_depth < MAX_DEPTH) {
+		*name_component[name_depth] = '\0';
+		if (!name_depth) {
+			current_name[0] = '/';
+			current_name[1] = '\0';
+		}
+	}
+}
+
+static int ask(const char *string, int def)
+{
+	int c;
+
+	if (!OPT_repair) {
+		bb_putchar('\n');
+		errors_uncorrected = 1;
+		return 0;
+	}
+	if (OPT_automatic) {
+		bb_putchar('\n');
+		if (!def)
+			errors_uncorrected = 1;
+		return def;
+	}
+	printf(def ? "%s (y/n)? " : "%s (n/y)? ", string);
+	for (;;) {
+		fflush_all();
+		c = getchar();
+		if (c == EOF) {
+			if (!def)
+				errors_uncorrected = 1;
+			return def;
+		}
+		if (c == '\n')
+			break;
+		c |= 0x20; /* tolower */
+		if (c == 'y') {
+			def = 1;
+			break;
+		}
+		if (c == 'n') {
+			def = 0;
+			break;
+		}
+	}
+	if (def)
+		printf("y\n");
+	else {
+		printf("n\n");
+		errors_uncorrected = 1;
+	}
+	return def;
+}
+
+/*
+ * Make certain that we aren't checking a filesystem that is on a
+ * mounted partition.  Code adapted from e2fsck, Copyright (C) 1993,
+ * 1994 Theodore Ts'o.  Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+	if (find_mount_point(device_name, 0)) {
+		int cont;
+#if ENABLE_FEATURE_MTAB_SUPPORT
+		/*
+		 * If the root is mounted read-only, then /etc/mtab is
+		 * probably not correct; so we won't issue a warning based on
+		 * it.
+		 */
+		int fd = open(bb_path_mtab_file, O_RDWR);
+
+		if (fd < 0 && errno == EROFS)
+			return;
+		close(fd);
+#endif
+		printf("%s is mounted. ", device_name);
+		cont = 0;
+		if (isatty(0) && isatty(1))
+			cont = ask("Do you really want to continue", 0);
+		if (!cont) {
+			printf("Check aborted\n");
+			exit(EXIT_SUCCESS);
+		}
+	}
+}
+
+/*
+ * check_zone_nr checks to see that *nr is a valid zone nr. If it
+ * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
+ * if an error was corrected, and returns the zone (0 for no zone
+ * or a bad zone-number).
+ */
+static int check_zone_nr2(uint32_t *nr, smallint *corrected)
+{
+	const char *msg;
+	if (!*nr)
+		return 0;
+	if (*nr < FIRSTZONE)
+		msg = "< FIRSTZONE";
+	else if (*nr >= ZONES)
+		msg = ">= ZONES";
+	else
+		return *nr;
+	printf("Zone nr %s in file '%s'. ", msg, current_name);
+	if (ask("Remove block", 1)) {
+		*nr = 0;
+		*corrected = 1;
+	}
+	return 0;
+}
+
+static int check_zone_nr(uint16_t *nr, smallint *corrected)
+{
+	uint32_t nr32 = *nr;
+	int r = check_zone_nr2(&nr32, corrected);
+	*nr = (uint16_t)nr32;
+	return r;
+}
+
+/*
+ * read-block reads block nr into the buffer at addr.
+ */
+static void read_block(unsigned nr, void *addr)
+{
+	if (!nr) {
+		memset(addr, 0, BLOCK_SIZE);
+		return;
+	}
+	xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
+	if (BLOCK_SIZE != full_read(dev_fd, addr, BLOCK_SIZE)) {
+		printf("%s: bad block %u in file '%s'\n",
+				bb_msg_read_error, nr, current_name);
+		errors_uncorrected = 1;
+		memset(addr, 0, BLOCK_SIZE);
+	}
+}
+
+/*
+ * write_block writes block nr to disk.
+ */
+static void write_block(unsigned nr, void *addr)
+{
+	if (!nr)
+		return;
+	if (nr < FIRSTZONE || nr >= ZONES) {
+		printf("Internal error: trying to write bad block\n"
+			   "Write request ignored\n");
+		errors_uncorrected = 1;
+		return;
+	}
+	xlseek(dev_fd, BLOCK_SIZE * nr, SEEK_SET);
+	if (BLOCK_SIZE != full_write(dev_fd, addr, BLOCK_SIZE)) {
+		printf("%s: bad block %u in file '%s'\n",
+				bb_msg_write_error, nr, current_name);
+		errors_uncorrected = 1;
+	}
+}
+
+/*
+ * map_block calculates the absolute block nr of a block in a file.
+ * It sets 'changed' if the inode has needed changing, and re-writes
+ * any indirect blocks with errors.
+ */
+static int map_block(struct minix1_inode *inode, unsigned blknr)
+{
+	uint16_t ind[BLOCK_SIZE >> 1];
+	int block, result;
+	smallint blk_chg;
+
+	if (blknr < 7)
+		return check_zone_nr(inode->i_zone + blknr, &changed);
+	blknr -= 7;
+	if (blknr < 512) {
+		block = check_zone_nr(inode->i_zone + 7, &changed);
+		goto common;
+	}
+	blknr -= 512;
+	block = check_zone_nr(inode->i_zone + 8, &changed);
+	read_block(block, ind); /* double indirect */
+	blk_chg = 0;
+	result = check_zone_nr(&ind[blknr / 512], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	block = result;
+ common:
+	read_block(block, ind);
+	blk_chg = 0;
+	result = check_zone_nr(&ind[blknr % 512], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	return result;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static int map_block2(struct minix2_inode *inode, unsigned blknr)
+{
+	uint32_t ind[BLOCK_SIZE >> 2];
+	int block, result;
+	smallint blk_chg;
+
+	if (blknr < 7)
+		return check_zone_nr2(inode->i_zone + blknr, &changed);
+	blknr -= 7;
+	if (blknr < 256) {
+		block = check_zone_nr2(inode->i_zone + 7, &changed);
+		goto common2;
+	}
+	blknr -= 256;
+	if (blknr < 256 * 256) {
+		block = check_zone_nr2(inode->i_zone + 8, &changed);
+		goto common1;
+	}
+	blknr -= 256 * 256;
+	block = check_zone_nr2(inode->i_zone + 9, &changed);
+	read_block(block, ind); /* triple indirect */
+	blk_chg = 0;
+	result = check_zone_nr2(&ind[blknr / (256 * 256)], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	block = result;
+ common1:
+	read_block(block, ind); /* double indirect */
+	blk_chg = 0;
+	result = check_zone_nr2(&ind[(blknr / 256) % 256], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	block = result;
+ common2:
+	read_block(block, ind);
+	blk_chg = 0;
+	result = check_zone_nr2(&ind[blknr % 256], &blk_chg);
+	if (blk_chg)
+		write_block(block, ind);
+	return result;
+}
+#endif
+
+static void write_superblock(void)
+{
+	/*
+	 * Set the state of the filesystem based on whether or not there
+	 * are uncorrected errors.  The filesystem valid flag is
+	 * unconditionally set if we get this far.
+	 */
+	Super.s_state |= MINIX_VALID_FS | MINIX_ERROR_FS;
+	if (!errors_uncorrected)
+		Super.s_state &= ~MINIX_ERROR_FS;
+
+	xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
+	if (BLOCK_SIZE != full_write(dev_fd, superblock_buffer, BLOCK_SIZE))
+		die("can't write superblock");
+}
+
+static void write_tables(void)
+{
+	write_superblock();
+
+	if (IMAPS * BLOCK_SIZE != write(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
+		die("can't write inode map");
+	if (ZMAPS * BLOCK_SIZE != write(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
+		die("can't write zone map");
+	if (INODE_BUFFER_SIZE != write(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
+		die("can't write inodes");
+}
+
+static void get_dirsize(void)
+{
+	int block;
+	char blk[BLOCK_SIZE];
+	int size;
+
+#if ENABLE_FEATURE_MINIX2
+	if (version2)
+		block = Inode2[MINIX_ROOT_INO].i_zone[0];
+	else
+#endif
+		block = Inode1[MINIX_ROOT_INO].i_zone[0];
+	read_block(block, blk);
+	for (size = 16; size < BLOCK_SIZE; size <<= 1) {
+		if (strcmp(blk + size + 2, "..") == 0) {
+			dirsize = size;
+			namelen = size - 2;
+			return;
+		}
+	}
+	/* use defaults */
+}
+
+static void read_superblock(void)
+{
+	xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
+	if (BLOCK_SIZE != full_read(dev_fd, superblock_buffer, BLOCK_SIZE))
+		die("can't read superblock");
+	/* already initialized to:
+	namelen = 14;
+	dirsize = 16;
+	version2 = 0;
+	*/
+	if (MAGIC == MINIX1_SUPER_MAGIC) {
+	} else if (MAGIC == MINIX1_SUPER_MAGIC2) {
+		namelen = 30;
+		dirsize = 32;
+#if ENABLE_FEATURE_MINIX2
+	} else if (MAGIC == MINIX2_SUPER_MAGIC) {
+		version2 = 1;
+	} else if (MAGIC == MINIX2_SUPER_MAGIC2) {
+		namelen = 30;
+		dirsize = 32;
+		version2 = 1;
+#endif
+	} else
+		die("bad magic number in superblock");
+	if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
+		die("only 1k blocks/zones supported");
+	if (IMAPS * BLOCK_SIZE * 8 < INODES + 1)
+		die("bad s_imap_blocks field in superblock");
+	if (ZMAPS * BLOCK_SIZE * 8 < ZONES - FIRSTZONE + 1)
+		die("bad s_zmap_blocks field in superblock");
+}
+
+static void read_tables(void)
+{
+	inode_map = xzalloc(IMAPS * BLOCK_SIZE);
+	zone_map = xzalloc(ZMAPS * BLOCK_SIZE);
+	inode_buffer = xmalloc(INODE_BUFFER_SIZE);
+	inode_count = xmalloc(INODES + 1);
+	zone_count = xmalloc(ZONES);
+	if (IMAPS * BLOCK_SIZE != read(dev_fd, inode_map, IMAPS * BLOCK_SIZE))
+		die("can't read inode map");
+	if (ZMAPS * BLOCK_SIZE != read(dev_fd, zone_map, ZMAPS * BLOCK_SIZE))
+		die("can't read zone map");
+	if (INODE_BUFFER_SIZE != read(dev_fd, inode_buffer, INODE_BUFFER_SIZE))
+		die("can't read inodes");
+	if (NORM_FIRSTZONE != FIRSTZONE) {
+		printf("warning: firstzone!=norm_firstzone\n");
+		errors_uncorrected = 1;
+	}
+	get_dirsize();
+	if (OPT_show) {
+		printf("%u inodes\n"
+			"%u blocks\n"
+			"Firstdatazone=%u (%u)\n"
+			"Zonesize=%u\n"
+			"Maxsize=%u\n"
+			"Filesystem state=%u\n"
+			"namelen=%u\n\n",
+			INODES,
+			ZONES,
+			FIRSTZONE, NORM_FIRSTZONE,
+			BLOCK_SIZE << ZONESIZE,
+			MAXSIZE,
+			Super.s_state,
+			namelen);
+	}
+}
+
+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 "
+					"for file '%s'\n", nr, current_name);
+			if (OPT_repair) {
+				if (ask("Mark as 'in use'", 1))
+					mark_inode(nr);
+				else
+					errors_uncorrected = 1;
+			}
+		}
+		if (S_ISDIR(i_mode))
+			directory++;
+		else if (S_ISREG(i_mode))
+			regular++;
+		else if (S_ISCHR(i_mode))
+			chardev++;
+		else if (S_ISBLK(i_mode))
+			blockdev++;
+		else if (S_ISLNK(i_mode))
+			symlinks++;
+		else if (S_ISSOCK(i_mode));
+		else if (S_ISFIFO(i_mode));
+		else {
+			printf("%s has mode %05o\n", current_name, i_mode);
+		}
+	} else
+		links++;
+	if (!++inode_count[nr]) {
+		printf("Warning: inode count too big\n");
+		inode_count[nr]--;
+		errors_uncorrected = 1;
+	}
+}
+
+static struct minix1_inode *get_inode(unsigned nr)
+{
+	struct minix1_inode *inode;
+
+	if (!nr || nr > INODES)
+		return NULL;
+	inode = Inode1 + nr;
+	get_inode_common(nr, inode->i_mode);
+	return inode;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static struct minix2_inode *get_inode2(unsigned nr)
+{
+	struct minix2_inode *inode;
+
+	if (!nr || nr > INODES)
+		return NULL;
+	inode = Inode2 + nr;
+	get_inode_common(nr, inode->i_mode);
+	return inode;
+}
+#endif
+
+static void check_root(void)
+{
+	struct minix1_inode *inode = Inode1 + MINIX_ROOT_INO;
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		die("root inode isn't a directory");
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_root2(void)
+{
+	struct minix2_inode *inode = Inode2 + MINIX_ROOT_INO;
+
+	if (!inode || !S_ISDIR(inode->i_mode))
+		die("root inode isn't a directory");
+}
+#else
+void check_root2(void);
+#endif
+
+static int add_zone_common(int block, smallint *corrected)
+{
+	if (!block)
+		return 0;
+	if (zone_count[block]) {
+		printf("Already used block is reused in file '%s'. ",
+				current_name);
+		if (ask("Clear", 1)) {
+			block = 0;
+			*corrected = 1;
+			return -1; /* "please zero out *znr" */
+		}
+	}
+	if (!zone_in_use(block)) {
+		printf("Block %d in file '%s' is marked as 'unused'. ",
+				block, current_name);
+		if (ask("Correct", 1))
+			mark_zone(block);
+	}
+	if (!++zone_count[block])
+		zone_count[block]--;
+	return block;
+}
+
+static int add_zone(uint16_t *znr, smallint *corrected)
+{
+	int block;
+
+	block = check_zone_nr(znr, corrected);
+	block = add_zone_common(block, corrected);
+	if (block == -1) {
+		*znr = 0;
+		block = 0;
+	}
+	return block;
+}
+
+#if ENABLE_FEATURE_MINIX2
+static int add_zone2(uint32_t *znr, smallint *corrected)
+{
+	int block;
+
+	block = check_zone_nr2(znr, corrected);
+	block = add_zone_common(block, corrected);
+	if (block == -1) {
+		*znr = 0;
+		block = 0;
+	}
+	return block;
+}
+#endif
+
+static void add_zone_ind(uint16_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_ind_blk);
+	for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+		add_zone(i + (uint16_t *) add_zone_ind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_ind_blk);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void add_zone_ind2(uint32_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone2(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_ind_blk);
+	for (i = 0; i < BLOCK_SIZE >> 2; i++)
+		add_zone2(i + (uint32_t *) add_zone_ind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_ind_blk);
+}
+#endif
+
+static void add_zone_dind(uint16_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_dind_blk);
+	for (i = 0; i < (BLOCK_SIZE >> 1); i++)
+		add_zone_ind(i + (uint16_t *) add_zone_dind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_dind_blk);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void add_zone_dind2(uint32_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone2(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_dind_blk);
+	for (i = 0; i < BLOCK_SIZE >> 2; i++)
+		add_zone_ind2(i + (uint32_t *) add_zone_dind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_dind_blk);
+}
+
+static void add_zone_tind2(uint32_t *znr, smallint *corrected)
+{
+	int i;
+	int block;
+	smallint chg_blk = 0;
+
+	block = add_zone2(znr, corrected);
+	if (!block)
+		return;
+	read_block(block, add_zone_tind_blk);
+	for (i = 0; i < BLOCK_SIZE >> 2; i++)
+		add_zone_dind2(i + (uint32_t *) add_zone_tind_blk, &chg_blk);
+	if (chg_blk)
+		write_block(block, add_zone_tind_blk);
+}
+#endif
+
+static void check_zones(unsigned i)
+{
+	struct minix1_inode *inode;
+
+	if (!i || i > INODES)
+		return;
+	if (inode_count[i] > 1)		/* have we counted this file already? */
+		return;
+	inode = Inode1 + i;
+	if (!S_ISDIR(inode->i_mode)
+	 && !S_ISREG(inode->i_mode)
+	 && !S_ISLNK(inode->i_mode)
+	) {
+		return;
+	}
+	for (i = 0; i < 7; i++)
+		add_zone(i + inode->i_zone, &changed);
+	add_zone_ind(7 + inode->i_zone, &changed);
+	add_zone_dind(8 + inode->i_zone, &changed);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_zones2(unsigned i)
+{
+	struct minix2_inode *inode;
+
+	if (!i || i > INODES)
+		return;
+	if (inode_count[i] > 1)		/* have we counted this file already? */
+		return;
+	inode = Inode2 + i;
+	if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode)
+		&& !S_ISLNK(inode->i_mode))
+		return;
+	for (i = 0; i < 7; i++)
+		add_zone2(i + inode->i_zone, &changed);
+	add_zone_ind2(7 + inode->i_zone, &changed);
+	add_zone_dind2(8 + inode->i_zone, &changed);
+	add_zone_tind2(9 + inode->i_zone, &changed);
+}
+#endif
+
+static void check_file(struct minix1_inode *dir, unsigned offset)
+{
+	struct minix1_inode *inode;
+	int ino;
+	char *name;
+	int block;
+
+	block = map_block(dir, offset / BLOCK_SIZE);
+	read_block(block, check_file_blk);
+	name = check_file_blk + (offset % BLOCK_SIZE) + 2;
+	ino = *(uint16_t *) (name - 2);
+	if (ino > INODES) {
+		printf("%s contains a bad inode number for file '%.*s'. ",
+				current_name, namelen, name);
+		if (ask("Remove", 1)) {
+			*(uint16_t *) (name - 2) = 0;
+			write_block(block, check_file_blk);
+		}
+		ino = 0;
+	}
+	push_filename(name);
+	inode = get_inode(ino);
+	pop_filename();
+	if (!offset) {
+		if (inode && LONE_CHAR(name, '.'))
+			return;
+		printf("%s: bad directory: '.' isn't first\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (offset == dirsize) {
+		if (inode && strcmp("..", name) == 0)
+			return;
+		printf("%s: bad directory: '..' isn't second\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (!inode)
+		return;
+	push_filename(name);
+	if (OPT_list) {
+		if (OPT_verbose)
+			printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+		printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
+	}
+	check_zones(ino);
+	if (inode && S_ISDIR(inode->i_mode))
+		recursive_check(ino);
+	pop_filename();
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_file2(struct minix2_inode *dir, unsigned offset)
+{
+	struct minix2_inode *inode;
+	int ino;
+	char *name;
+	int block;
+
+	block = map_block2(dir, offset / BLOCK_SIZE);
+	read_block(block, check_file_blk);
+	name = check_file_blk + (offset % BLOCK_SIZE) + 2;
+	ino = *(uint16_t *) (name - 2);
+	if (ino > INODES) {
+		printf("%s contains a bad inode number for file '%.*s'. ",
+				current_name, namelen, name);
+		if (ask("Remove", 1)) {
+			*(uint16_t *) (name - 2) = 0;
+			write_block(block, check_file_blk);
+		}
+		ino = 0;
+	}
+	push_filename(name);
+	inode = get_inode2(ino);
+	pop_filename();
+	if (!offset) {
+		if (inode && LONE_CHAR(name, '.'))
+			return;
+		printf("%s: bad directory: '.' isn't first\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (offset == dirsize) {
+		if (inode && strcmp("..", name) == 0)
+			return;
+		printf("%s: bad directory: '..' isn't second\n", current_name);
+		errors_uncorrected = 1;
+	}
+	if (!inode)
+		return;
+	push_filename(name);
+	if (OPT_list) {
+		if (OPT_verbose)
+			printf("%6d %07o %3d ", ino, inode->i_mode, inode->i_nlinks);
+		printf("%s%s\n", current_name, S_ISDIR(inode->i_mode) ? ":" : "");
+	}
+	check_zones2(ino);
+	if (inode && S_ISDIR(inode->i_mode))
+		recursive_check2(ino);
+	pop_filename();
+}
+#endif
+
+static void recursive_check(unsigned ino)
+{
+	struct minix1_inode *dir;
+	unsigned offset;
+
+	dir = Inode1 + ino;
+	if (!S_ISDIR(dir->i_mode))
+		die("internal error");
+	if (dir->i_size < 2 * dirsize) {
+		printf("%s: bad directory: size<32", current_name);
+		errors_uncorrected = 1;
+	}
+	for (offset = 0; offset < dir->i_size; offset += dirsize)
+		check_file(dir, offset);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void recursive_check2(unsigned ino)
+{
+	struct minix2_inode *dir;
+	unsigned offset;
+
+	dir = Inode2 + ino;
+	if (!S_ISDIR(dir->i_mode))
+		die("internal error");
+	if (dir->i_size < 2 * dirsize) {
+		printf("%s: bad directory: size<32", current_name);
+		errors_uncorrected = 1;
+	}
+	for (offset = 0; offset < dir->i_size; offset += dirsize)
+		check_file2(dir, offset);
+}
+#endif
+
+static int bad_zone(int i)
+{
+	char buffer[BLOCK_SIZE];
+
+	xlseek(dev_fd, BLOCK_SIZE * i, SEEK_SET);
+	return (BLOCK_SIZE != full_read(dev_fd, buffer, BLOCK_SIZE));
+}
+
+static void check_counts(void)
+{
+	int i;
+
+	for (i = 1; i <= INODES; i++) {
+		if (OPT_warn_mode && Inode1[i].i_mode && !inode_in_use(i)) {
+			printf("Inode %d has non-zero mode. ", i);
+			if (ask("Clear", 1)) {
+				Inode1[i].i_mode = 0;
+				changed = 1;
+			}
+		}
+		if (!inode_count[i]) {
+			if (!inode_in_use(i))
+				continue;
+			printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
+			if (ask("Clear", 1))
+				unmark_inode(i);
+			continue;
+		}
+		if (!inode_in_use(i)) {
+			printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
+			if (ask("Set", 1))
+				mark_inode(i);
+		}
+		if (Inode1[i].i_nlinks != inode_count[i]) {
+			printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
+				i, Inode1[i].i_mode, Inode1[i].i_nlinks,
+				inode_count[i]);
+			if (ask("Set i_nlinks to count", 1)) {
+				Inode1[i].i_nlinks = inode_count[i];
+				changed = 1;
+			}
+		}
+	}
+	for (i = FIRSTZONE; i < ZONES; i++) {
+		if ((zone_in_use(i) != 0) == zone_count[i])
+			continue;
+		if (!zone_count[i]) {
+			if (bad_zone(i))
+				continue;
+			printf("Zone %d is marked 'in use', but no file uses it. ", i);
+			if (ask("Unmark", 1))
+				unmark_zone(i);
+			continue;
+		}
+		printf("Zone %d: %sin use, counted=%d\n",
+			   i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+	}
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check_counts2(void)
+{
+	int i;
+
+	for (i = 1; i <= INODES; i++) {
+		if (OPT_warn_mode && Inode2[i].i_mode && !inode_in_use(i)) {
+			printf("Inode %d has non-zero mode. ", i);
+			if (ask("Clear", 1)) {
+				Inode2[i].i_mode = 0;
+				changed = 1;
+			}
+		}
+		if (!inode_count[i]) {
+			if (!inode_in_use(i))
+				continue;
+			printf("Unused inode %d is marked as 'used' in the bitmap. ", i);
+			if (ask("Clear", 1))
+				unmark_inode(i);
+			continue;
+		}
+		if (!inode_in_use(i)) {
+			printf("Inode %d is used, but marked as 'unused' in the bitmap. ", i);
+			if (ask("Set", 1))
+				mark_inode(i);
+		}
+		if (Inode2[i].i_nlinks != inode_count[i]) {
+			printf("Inode %d (mode=%07o), i_nlinks=%d, counted=%d. ",
+				i, Inode2[i].i_mode, Inode2[i].i_nlinks,
+				inode_count[i]);
+			if (ask("Set i_nlinks to count", 1)) {
+				Inode2[i].i_nlinks = inode_count[i];
+				changed = 1;
+			}
+		}
+	}
+	for (i = FIRSTZONE; i < ZONES; i++) {
+		if ((zone_in_use(i) != 0) == zone_count[i])
+			continue;
+		if (!zone_count[i]) {
+			if (bad_zone(i))
+				continue;
+			printf("Zone %d is marked 'in use', but no file uses it. ", i);
+			if (ask("Unmark", 1))
+				unmark_zone(i);
+			continue;
+		}
+		printf("Zone %d: %sin use, counted=%d\n",
+			   i, zone_in_use(i) ? "" : "not ", zone_count[i]);
+	}
+}
+#endif
+
+static void check(void)
+{
+	memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+	memset(zone_count, 0, ZONES * sizeof(*zone_count));
+	check_zones(MINIX_ROOT_INO);
+	recursive_check(MINIX_ROOT_INO);
+	check_counts();
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void check2(void)
+{
+	memset(inode_count, 0, (INODES + 1) * sizeof(*inode_count));
+	memset(zone_count, 0, ZONES * sizeof(*zone_count));
+	check_zones2(MINIX_ROOT_INO);
+	recursive_check2(MINIX_ROOT_INO);
+	check_counts2();
+}
+#else
+void check2(void);
+#endif
+
+int fsck_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int fsck_minix_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct termios tmp;
+	int retcode = 0;
+
+	xfunc_error_retval = 8;
+
+	INIT_G();
+
+	opt_complementary = "=1:ar"; /* one argument; -a assumes -r */
+	getopt32(argv, OPTION_STR);
+	argv += optind;
+	device_name = argv[0];
+
+	check_mount();  /* trying to check a mounted filesystem? */
+	if (OPT_manual) {
+		if (!isatty(0) || !isatty(1))
+			die("need terminal for interactive repairs");
+	}
+	xmove_fd(xopen(device_name, OPT_repair ? O_RDWR : O_RDONLY), dev_fd);
+
+	/*sync(); paranoia? */
+	read_superblock();
+
+	/*
+	 * Determine whether or not we should continue with the checking.
+	 * This is based on the status of the filesystem valid and error
+	 * flags and whether or not the -f switch was specified on the
+	 * command line.
+	 */
+	printf("%s: %s\n", applet_name, bb_banner);
+
+	if (!(Super.s_state & MINIX_ERROR_FS)
+	 && (Super.s_state & MINIX_VALID_FS) && !OPT_force
+	) {
+		if (OPT_repair)
+			printf("%s is clean, check is skipped\n", device_name);
+		return 0;
+	} else if (OPT_force)
+		printf("Forcing filesystem check on %s\n", device_name);
+	else if (OPT_repair)
+		printf("Filesystem on %s is dirty, needs checking\n",
+			   device_name);
+
+	read_tables();
+
+	if (OPT_manual) {
+		tcgetattr(0, &sv_termios);
+		tmp = sv_termios;
+		tmp.c_lflag &= ~(ICANON | ECHO);
+		tcsetattr_stdin_TCSANOW(&tmp);
+		termios_set = 1;
+	}
+
+	if (version2) {
+		check_root2();
+		check2();
+	} else {
+		check_root();
+		check();
+	}
+
+	if (OPT_verbose) {
+		int i, free_cnt;
+
+		for (i = 1, free_cnt = 0; i <= INODES; i++)
+			if (!inode_in_use(i))
+				free_cnt++;
+		printf("\n%6u inodes used (%u%%)\n", (INODES - free_cnt),
+			   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);
+	}
+	if (changed) {
+		write_tables();
+		printf("FILE SYSTEM HAS BEEN CHANGED\n");
+		sync();
+	} else if (OPT_repair)
+		write_superblock();
+
+	if (OPT_manual)
+		tcsetattr_stdin_TCSANOW(&sv_termios);
+
+	if (changed)
+		retcode += 3;
+	if (errors_uncorrected)
+		retcode += 4;
+	return retcode;
+}
diff --git a/busybox-1.19.3/util-linux/getopt.c b/busybox-1.19.3/util-linux/getopt.c
new file mode 100644
index 0000000..c45edf8
--- /dev/null
+++ b/busybox-1.19.3/util-linux/getopt.c
@@ -0,0 +1,403 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * 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 source tree.
+ */
+
+/*
+ * Version 1.0-b4: Tue Sep 23 1997. First public release.
+ * Version 1.0: Wed Nov 19 1997.
+ *   Bumped up the version number to 1.0
+ *   Fixed minor typo (CSH instead of TCSH)
+ * Version 1.0.1: Tue Jun 3 1998
+ *   Fixed sizeof instead of strlen bug
+ *   Bumped up the version number to 1.0.1
+ * Version 1.0.2: Thu Jun 11 1998 (not present)
+ *   Fixed gcc-2.8.1 warnings
+ *   Fixed --version/-V option (not present)
+ * Version 1.0.5: Tue Jun 22 1999
+ *   Make -u option work (not present)
+ * Version 1.0.6: Tue Jun 27 2000
+ *   No important changes
+ * Version 1.1.0: Tue Jun 30 2000
+ *   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
+ *  Removed parse_error(), using bb_error_msg() from Busybox instead
+ *  Replaced our_malloc with xmalloc and our_realloc with xrealloc
+ *
+ */
+
+//usage:#define getopt_trivial_usage
+//usage:       "[OPTIONS]"
+//usage:#define getopt_full_usage "\n\n"
+//usage:	IF_LONG_OPTS(
+//usage:       "	-a,--alternative		Allow long options starting with single -"
+//usage:     "\n	-l,--longoptions=longopts	Long options to be recognized"
+//usage:     "\n	-n,--name=progname		The name under which errors are reported"
+//usage:     "\n	-o,--options=optstring		Short options to be recognized"
+//usage:     "\n	-q,--quiet			Disable error reporting by getopt(3)"
+//usage:     "\n	-Q,--quiet-output		No normal output"
+//usage:     "\n	-s,--shell=shell		Set shell quoting conventions"
+//usage:     "\n	-T,--test			Test for getopt(1) version"
+//usage:     "\n	-u,--unquoted			Don't quote the output"
+//usage:	)
+//usage:	IF_NOT_LONG_OPTS(
+//usage:       "	-a		Allow long options starting with single -"
+//usage:     "\n	-l longopts	Long options to be recognized"
+//usage:     "\n	-n progname	The name under which errors are reported"
+//usage:     "\n	-o optstring	Short options to be recognized"
+//usage:     "\n	-q		Disable error reporting by getopt(3)"
+//usage:     "\n	-Q		No normal output"
+//usage:     "\n	-s shell	Set shell quoting conventions"
+//usage:     "\n	-T		Test for getopt(1) version"
+//usage:     "\n	-u		Don't quote the output"
+//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 '+'
+   mode */
+enum {
+	NON_OPT = 1,
+#if ENABLE_FEATURE_GETOPT_LONG
+/* LONG_OPT is the code that is returned when a long option is found. */
+	LONG_OPT = 2
+#endif
+};
+
+/* For finding activated option flags. Must match getopt32 call! */
+enum {
+	OPT_o	= 0x1,	// -o
+	OPT_n	= 0x2,	// -n
+	OPT_q	= 0x4,	// -q
+	OPT_Q	= 0x8,	// -Q
+	OPT_s	= 0x10,	// -s
+	OPT_T	= 0x20,	// -T
+	OPT_u	= 0x40,	// -u
+#if ENABLE_FEATURE_GETOPT_LONG
+	OPT_a	= 0x80,	// -a
+	OPT_l	= 0x100, // -l
+#endif
+	SHELL_IS_TCSH = 0x8000, /* hijack this bit for other purposes */
+};
+
+/* 0 is getopt_long, 1 is getopt_long_only */
+#define alternative  (option_mask32 & OPT_a)
+
+#define quiet_errors (option_mask32 & OPT_q)
+#define quiet_output (option_mask32 & OPT_Q)
+#define quote        (!(option_mask32 & OPT_u))
+#define shell_TCSH   (option_mask32 & SHELL_IS_TCSH)
+
+/*
+ * This function 'normalizes' a single argument: it puts single quotes around
+ * it and escapes other special characters. If quote is false, it just
+ * returns its argument.
+ * Bash only needs special treatment for single quotes; tcsh also recognizes
+ * exclamation marks within single quotes, and nukes whitespace.
+ * This function returns a pointer to a buffer that is overwritten by
+ * each call.
+ */
+static const char *normalize(const char *arg)
+{
+	char *bufptr;
+#if ENABLE_FEATURE_CLEAN_UP
+	static char *BUFFER = NULL;
+	free(BUFFER);
+#else
+	char *BUFFER;
+#endif
+
+	if (!quote) { /* Just copy arg */
+		BUFFER = xstrdup(arg);
+		return BUFFER;
+	}
+
+	/* Each character in arg may take up to four characters in the result:
+	   For a quote we need a closing quote, a backslash, a quote and an
+	   opening quote! We need also the global opening and closing quote,
+	   and one extra character for '\0'. */
+	BUFFER = xmalloc(strlen(arg)*4 + 3);
+
+	bufptr = BUFFER;
+	*bufptr ++= '\'';
+
+	while (*arg) {
+		if (*arg == '\'') {
+			/* Quote: replace it with: '\'' */
+			*bufptr ++= '\'';
+			*bufptr ++= '\\';
+			*bufptr ++= '\'';
+			*bufptr ++= '\'';
+		} else if (shell_TCSH && *arg == '!') {
+			/* Exclamation mark: replace it with: \! */
+			*bufptr ++= '\'';
+			*bufptr ++= '\\';
+			*bufptr ++= '!';
+			*bufptr ++= '\'';
+		} else if (shell_TCSH && *arg == '\n') {
+			/* Newline: replace it with: \n */
+			*bufptr ++= '\\';
+			*bufptr ++= 'n';
+		} else if (shell_TCSH && isspace(*arg)) {
+			/* Non-newline whitespace: replace it with \<ws> */
+			*bufptr ++= '\'';
+			*bufptr ++= '\\';
+			*bufptr ++= *arg;
+			*bufptr ++= '\'';
+		} else
+			/* Just copy */
+			*bufptr ++= *arg;
+		arg++;
+	}
+	*bufptr ++= '\'';
+	*bufptr ++= '\0';
+	return BUFFER;
+}
+
+/*
+ * Generate the output. argv[0] is the program name (used for reporting errors).
+ * argv[1..] contains the options to be parsed. argc must be the number of
+ * elements in argv (ie. 1 if there are no options, only the program name),
+ * optstr must contain the short options, and longopts the long options.
+ * Other settings are found in global variables.
+ */
+#if !ENABLE_FEATURE_GETOPT_LONG
+#define generate_output(argv,argc,optstr,longopts) \
+	generate_output(argv,argc,optstr)
+#endif
+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;
+
+	/* We used it already in main() in getopt32(),
+	 * we *must* reset getopt(3): */
+#ifdef __GLIBC__
+	optind = 0;
+#else /* BSD style */
+	optind = 1;
+	/* optreset = 1; */
+#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);
+#else
+			getopt(argc, argv, optstr);
+#endif
+		if (opt == -1)
+			break;
+		if (opt == '?' || opt == ':' )
+			exit_code = 1;
+		else if (!quiet_output) {
+#if ENABLE_FEATURE_GETOPT_LONG
+			if (opt == LONG_OPT) {
+				printf(" --%s", longopts[longindex].name);
+				if (longopts[longindex].has_arg)
+					printf(" %s",
+						normalize(optarg ? optarg : ""));
+			} else
+#endif
+			if (opt == NON_OPT)
+				printf(" %s", normalize(optarg));
+			else {
+				printf(" -%c", opt);
+				charptr = strchr(optstr, opt);
+				if (charptr != NULL && *++charptr == ':')
+					printf(" %s",
+						normalize(optarg ? optarg : ""));
+			}
+		}
+	}
+
+	if (!quiet_output) {
+		printf(" --");
+		while (optind < argc)
+			printf(" %s", normalize(argv[optind++]));
+		bb_putchar('\n');
+	}
+	return exit_code;
+}
+
+#if ENABLE_FEATURE_GETOPT_LONG
+/*
+ * Register several long options. options is a string of long options,
+ * separated by commas or whitespace.
+ * This nukes options!
+ */
+static struct option *add_long_options(struct option *long_options, char *options)
+{
+	int long_nr = 0;
+	int arg_opt, tlen;
+	char *tokptr = strtok(options, ", \t\n");
+
+	if (long_options)
+		while (long_options[long_nr].name)
+			long_nr++;
+
+	while (tokptr) {
+		arg_opt = no_argument;
+		tlen = strlen(tokptr);
+		if (tlen) {
+			tlen--;
+			if (tokptr[tlen] == ':') {
+				arg_opt = required_argument;
+				if (tlen && tokptr[tlen-1] == ':') {
+					tlen--;
+					arg_opt = optional_argument;
+				}
+				tokptr[tlen] = '\0';
+				if (tlen == 0)
+					bb_error_msg_and_die("empty long option specified");
+			}
+			long_options = xrealloc_vector(long_options, 4, long_nr);
+			long_options[long_nr].has_arg = arg_opt;
+			/*long_options[long_nr].flag = NULL; - xrealloc_vector did it */
+			long_options[long_nr].val = LONG_OPT;
+			long_options[long_nr].name = xstrdup(tokptr);
+			long_nr++;
+			/*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */
+		}
+		tokptr = strtok(NULL, ", \t\n");
+	}
+	return long_options;
+}
+#endif
+
+static void set_shell(const char *new_shell)
+{
+	if (!strcmp(new_shell, "bash") || !strcmp(new_shell, "sh"))
+		return;
+	if (!strcmp(new_shell, "tcsh") || !strcmp(new_shell, "csh"))
+		option_mask32 |= SHELL_IS_TCSH;
+	else
+		bb_error_msg("unknown shell '%s', assuming bash", new_shell);
+}
+
+
+/* Exit codes:
+ *   0) No errors, successful operation.
+ *   1) getopt(3) returned an error.
+ *   2) A problem with parameter parsing for getopt(1).
+ *   3) Internal error, out of memory
+ *   4) Returned for -T
+ */
+
+#if ENABLE_FEATURE_GETOPT_LONG
+static const char getopt_longopts[] ALIGN1 =
+	"options\0"      Required_argument "o"
+	"longoptions\0"  Required_argument "l"
+	"quiet\0"        No_argument       "q"
+	"quiet-output\0" No_argument       "Q"
+	"shell\0"        Required_argument "s"
+	"test\0"         No_argument       "T"
+	"unquoted\0"     No_argument       "u"
+	"alternative\0"  No_argument       "a"
+	"name\0"         Required_argument "n"
+	;
+#endif
+
+int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int getopt_main(int argc, char **argv)
+{
+	char *optstr = NULL;
+	char *name = NULL;
+	unsigned opt;
+	const char *compatible;
+	char *s_arg;
+#if ENABLE_FEATURE_GETOPT_LONG
+	struct option *long_options = NULL;
+	llist_t *l_arg = NULL;
+#endif
+
+	compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
+
+	if (argc == 1) {
+		if (compatible) {
+			/* For some reason, the original getopt gave no error
+			   when there were no arguments. */
+			printf(" --\n");
+			return 0;
+		}
+		bb_error_msg_and_die("missing optstring argument");
+	}
+
+	if (argv[1][0] != '-' || compatible) {
+		char *s;
+
+		option_mask32 |= OPT_u; /* quoting off */
+		s = xstrdup(argv[1] + strspn(argv[1], "-+"));
+		argv[1] = argv[0];
+		return generate_output(argv+1, argc-1, s, long_options);
+	}
+
+#if !ENABLE_FEATURE_GETOPT_LONG
+	opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
+#else
+	applet_long_options = getopt_longopts;
+	opt_complementary = "l::";
+	opt = getopt32(argv, "+o:n:qQs:Tual:",
+					&optstr, &name, &s_arg, &l_arg);
+	/* Effectuate the read options for the applet itself */
+	while (l_arg) {
+		long_options = add_long_options(long_options, llist_pop(&l_arg));
+	}
+#endif
+
+	if (opt & OPT_s) {
+		set_shell(s_arg);
+	}
+
+	if (opt & OPT_T) {
+		return 4;
+	}
+
+	/* All options controlling the applet have now been parsed */
+	if (!optstr) {
+		if (optind >= argc)
+			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);
+}
diff --git a/busybox-1.19.3/util-linux/hexdump.c b/busybox-1.19.3/util-linux/hexdump.c
new file mode 100644
index 0000000..9a312f9
--- /dev/null
+++ b/busybox-1.19.3/util-linux/hexdump.c
@@ -0,0 +1,174 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * hexdump implementation for busybox
+ * Based on code from util-linux v 2.11l
+ *
+ * Copyright (c) 1989
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * 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"
+
+/* This is a NOEXEC applet. Be very careful! */
+
+static void bb_dump_addfile(dumper_t *dumper, char *name)
+{
+	char *p;
+	FILE *fp;
+	char *buf;
+
+	fp = xfopen_for_read(name);
+	while ((buf = xmalloc_fgetline(fp)) != NULL) {
+		p = skip_whitespace(buf);
+		if (*p && (*p != '#')) {
+			bb_dump_add(dumper, p);
+		}
+		free(buf);
+	}
+	fclose(fp);
+}
+
+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 */
+};
+
+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)
+{
+	dumper_t *dumper = alloc_dumper();
+	const char *p;
+	int ch;
+#if ENABLE_FEATURE_HEXDUMP_REVERSE
+	FILE *fp;
+	smallint rdump = 0;
+#endif
+
+	if (ENABLE_HD && !applet_name[2]) { /* we are "hd" */
+		ch = 'C';
+		goto hd_applet;
+	}
+
+	/* We cannot use getopt32: in hexdump options are cumulative.
+	 * E.g. "hexdump -C -C file" should dump each line twice */
+	while ((ch = getopt(argc, argv, hexdump_opts)) > 0) {
+		p = strchr(hexdump_opts, ch);
+		if (!p)
+			bb_show_usage();
+		if ((p - hexdump_opts) < 5) {
+			bb_dump_add(dumper, add_first);
+			bb_dump_add(dumper, add_strings[(int)(p - hexdump_opts)]);
+		}
+		/* Save a little bit of space below by omitting the 'else's. */
+		if (ch == 'C') {
+ hd_applet:
+			bb_dump_add(dumper, "\"%08.8_Ax\n\"");
+			bb_dump_add(dumper, "\"%08.8_ax  \" 8/1 \"%02x \" \"  \" 8/1 \"%02x \" ");
+			bb_dump_add(dumper, "\"  |\" 16/1 \"%_p\" \"|\\n\"");
+		}
+		if (ch == 'e') {
+			bb_dump_add(dumper, optarg);
+		} /* else */
+		if (ch == 'f') {
+			bb_dump_addfile(dumper, optarg);
+		} /* else */
+		if (ch == 'n') {
+			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);
+		} /* else */
+		if (ch == 'v') {
+			dumper->dump_vflag = ALL;
+		}
+#if ENABLE_FEATURE_HEXDUMP_REVERSE
+		if (ch == 'R') {
+			rdump = 1;
+		}
+#endif
+	}
+
+	if (!dumper->fshead) {
+		bb_dump_add(dumper, add_first);
+		bb_dump_add(dumper, "\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+	}
+
+	argv += optind;
+
+#if !ENABLE_FEATURE_HEXDUMP_REVERSE
+	return bb_dump_dump(dumper, argv);
+#else
+	if (!rdump) {
+		return bb_dump_dump(dumper, argv);
+	}
+
+	/* -R: reverse of 'hexdump -Cv' */
+	fp = stdin;
+	if (!*argv) {
+		argv--;
+		goto jump_in;
+	}
+
+	do {
+		char *buf;
+		fp = xfopen_for_read(*argv);
+ jump_in:
+		while ((buf = xmalloc_fgetline(fp)) != NULL) {
+			p = buf;
+			while (1) {
+				/* skip address or previous byte */
+				while (isxdigit(*p)) p++;
+				while (*p == ' ') p++;
+				/* '|' char will break the line */
+				if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1)
+					break;
+				putchar(ch);
+			}
+			free(buf);
+		}
+		fclose(fp);
+	} while (*++argv);
+
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+#endif
+}
diff --git a/busybox-1.19.3/util-linux/hwclock.c b/busybox-1.19.3/util-linux/hwclock.c
new file mode 100644
index 0000000..6b4e29b
--- /dev/null
+++ b/busybox-1.19.3/util-linux/hwclock.c
@@ -0,0 +1,328 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini hwclock implementation for busybox
+ *
+ * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+*/
+
+#include "libbb.h"
+/* After libbb.h, since it needs sys/types.h on some systems */
+#include <sys/utsname.h>
+#include "rtc_.h"
+
+/* diff code is disabled: it's not sys/hw clock diff, it's some useless
+ * "time between hwclock was started and we saw CMOS tick" quantity.
+ * It's useless since hwclock is started at a random moment,
+ * thus the quantity is also random, useless. Showing 0.000000 does not
+ * deprive us from any useful info.
+ *
+ * SHOW_HWCLOCK_DIFF code in this file shows the difference between system
+ * and hw clock. It is useful, but not compatible with standard hwclock.
+ * Thus disabled.
+ */
+#define SHOW_HWCLOCK_DIFF 0
+
+
+#if !SHOW_HWCLOCK_DIFF
+# define read_rtc(pp_rtcname, sys_tv, utc) read_rtc(pp_rtcname, utc)
+#endif
+static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
+{
+	struct tm tm_time;
+	int fd;
+
+	fd = rtc_xopen(pp_rtcname, O_RDONLY);
+
+	rtc_read_tm(&tm_time, fd);
+
+#if SHOW_HWCLOCK_DIFF
+	{
+		int before = tm_time.tm_sec;
+		while (1) {
+			rtc_read_tm(&tm_time, fd);
+			gettimeofday(sys_tv, NULL);
+			if (before != tm_time.tm_sec)
+				break;
+		}
+	}
+#endif
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	return rtc_tm2time(&tm_time, utc);
+}
+
+static void show_clock(const char **pp_rtcname, int utc)
+{
+#if SHOW_HWCLOCK_DIFF
+	struct timeval sys_tv;
+#endif
+	time_t t = read_rtc(pp_rtcname, &sys_tv, utc);
+
+#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 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 */
+			sys_tv.tv_usec = 999999 - sys_tv.tv_usec;
+		}
+		printf("%s  %ld.%06lu seconds\n", cp, diff, (unsigned long)sys_tv.tv_usec);
+	}
+#endif
+}
+
+static void to_sys_clock(const char **pp_rtcname, int utc)
+{
+	struct timeval tv;
+	struct timezone tz;
+
+	tz.tz_minuteswest = timezone/60 - 60*daylight;
+	tz.tz_dsttime = 0;
+
+	tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
+	tv.tv_usec = 0;
+	if (settimeofday(&tv, &tz))
+		bb_perror_msg_and_die("settimeofday");
+}
+
+static void from_sys_clock(const char **pp_rtcname, int utc)
+{
+#if 1
+	struct timeval tv;
+	struct tm tm_time;
+	int rtc;
+
+	rtc = rtc_xopen(pp_rtcname, O_WRONLY);
+	gettimeofday(&tv, NULL);
+	/* Prepare tm_time */
+	if (sizeof(time_t) == sizeof(tv.tv_sec)) {
+		if (utc)
+			gmtime_r((time_t*)&tv.tv_sec, &tm_time);
+		else
+			localtime_r((time_t*)&tv.tv_sec, &tm_time);
+	} else {
+		time_t t = tv.tv_sec;
+		if (utc)
+			gmtime_r(&t, &tm_time);
+		else
+			localtime_r(&t, &tm_time);
+	}
+#else
+/* Bloated code which tries to set hw clock with better precision.
+ * On x86, even though code does set hw clock within <1ms of exact
+ * whole seconds, apparently hw clock (at least on some machines)
+ * doesn't reset internal fractional seconds to 0,
+ * making all this a pointless excercise.
+ */
+	/* If we see that we are N usec away from whole second,
+	 * we'll sleep for N-ADJ usecs. ADJ corrects for the fact
+	 * that CPU is not infinitely fast.
+	 * On infinitely fast CPU, next wakeup would be
+	 * on (exactly_next_whole_second - ADJ). On real CPUs,
+	 * this difference between current time and whole second
+	 * is less than ADJ (assuming system isn't heavily loaded).
+	 */
+	/* Small value of 256us gives very precise sync for 2+ GHz CPUs.
+	 * Slower CPUs will fail to sync and will go to bigger
+	 * ADJ values. qemu-emulated armv4tl with ~100 MHz
+	 * performance ends up using ADJ ~= 4*1024 and it takes
+	 * 2+ secs (2 tries with successively larger ADJ)
+	 * to sync. Even straced one on the same qemu (very slow)
+	 * takes only 4 tries.
+	 */
+#define TWEAK_USEC 256
+	unsigned adj = TWEAK_USEC;
+	struct tm tm_time;
+	struct timeval tv;
+	int rtc = rtc_xopen(pp_rtcname, O_WRONLY);
+
+	/* Try to catch the moment when whole second is close */
+	while (1) {
+		unsigned rem_usec;
+		time_t t;
+
+		gettimeofday(&tv, NULL);
+
+		t = tv.tv_sec;
+		rem_usec = 1000000 - tv.tv_usec;
+		if (rem_usec < adj) {
+			/* Close enough */
+ small_rem:
+			t++;
+		}
+
+		/* Prepare tm_time from t */
+		if (utc)
+			gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */
+		else
+			localtime_r(&t, &tm_time); /* same */
+
+		if (adj >= 32*1024) {
+			break; /* 32 ms diff and still no luck?? give up trying to sync */
+		}
+
+		/* gmtime/localtime took some time, re-get cur time */
+		gettimeofday(&tv, NULL);
+
+		if (tv.tv_sec < t /* we are still in old second */
+		 || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */
+		) {
+			break; /* good, we are in sync! */
+		}
+
+		rem_usec = 1000000 - tv.tv_usec;
+		if (rem_usec < adj) {
+			t = tv.tv_sec;
+			goto small_rem; /* already close to next sec, don't sleep */
+		}
+
+		/* Try to sync up by sleeping */
+		usleep(rem_usec - adj);
+
+		/* Jump to 1ms diff, then increase fast (x2): EVERY loop
+		 * takes ~1 sec, people won't like slowly converging code here!
+		 */
+	//bb_error_msg("adj:%d tv.tv_usec:%d", adj, (int)tv.tv_usec);
+		if (adj < 512)
+			adj = 512;
+		/* ... and if last "overshoot" does not look insanely big,
+		 * just use it as adj increment. This makes convergence faster.
+		 */
+		if (tv.tv_usec < adj * 8) {
+			adj += tv.tv_usec;
+			continue;
+		}
+		adj *= 2;
+	}
+	/* Debug aid to find "optimal" TWEAK_USEC with nearly exact sync.
+	 * Look for a value which makes tv_usec close to 999999 or 0.
+	 * For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200
+	 */
+	//bb_error_msg("tv.tv_usec:%d", (int)tv.tv_usec);
+#endif
+
+	tm_time.tm_isdst = 0;
+	xioctl(rtc, RTC_SET_TIME, &tm_time);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		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_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)
+{
+	const char *rtcname = NULL;
+	unsigned opt;
+	int utc;
+
+#if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
+	static const char hwclock_longopts[] ALIGN1 =
+		"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"
+		"systz\0"     No_argument "t" /* short opt is non-standard */
+		"rtc\0"       Required_argument "f"
+		;
+	applet_long_options = hwclock_longopts;
+#endif
+	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))
+		utc = (opt & HWCLOCK_OPT_UTC);
+	else
+		utc = rtc_adjtime_is_utc();
+
+	if (opt & HWCLOCK_OPT_HCTOSYS)
+		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);
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/ipcrm.c b/busybox-1.19.3/util-linux/ipcrm.c
new file mode 100644
index 0000000..274050c
--- /dev/null
+++ b/busybox-1.19.3/util-linux/ipcrm.c
@@ -0,0 +1,227 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipcrm.c - utility to allow removal of IPC objects and data structures.
+ *
+ * 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 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() */
+/* X/OPEN tells us to use <sys/{types,ipc,msg}.h> for msgctl() */
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+
+#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+	int val;
+	struct semid_ds *buf;
+	unsigned short *array;
+	struct seminfo *__buf;
+};
+#endif
+
+#define IPCRM_LEGACY 1
+
+
+#if IPCRM_LEGACY
+
+typedef enum type_id {
+	SHM,
+	SEM,
+	MSG
+} type_id;
+
+static int remove_ids(type_id type, char **argv)
+{
+	unsigned long id;
+	int nb_errors = 0;
+	union semun arg;
+
+	arg.val = 0;
+
+	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)
+				ret = msgctl(id, IPC_RMID, NULL);
+			else if (type ==  SHM)
+				ret = shmctl(id, IPC_RMID, NULL);
+
+			if (ret) {
+				bb_perror_msg("can't remove id %s", argv[0]);
+				nb_errors++;
+			}
+		}
+		argv++;
+	}
+
+	return nb_errors;
+}
+#endif /* IPCRM_LEGACY */
+
+
+int ipcrm_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipcrm_main(int argc, char **argv)
+{
+	int c;
+	int error = 0;
+
+	/* if the command is executed without parameters, do nothing */
+	if (argc == 1)
+		return 0;
+#if IPCRM_LEGACY
+	/* check to see if the command is being invoked in the old way if so
+	   then run the old code. Valid commands are msg, shm, sem. */
+	{
+		type_id what = 0; /* silence gcc */
+		char w;
+
+		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')
+		           && argv[1][2] == 'm')
+		     ) && argv[1][3] == '\0'
+		) {
+			if (argc < 3)
+				bb_show_usage();
+
+			if (w == 'h')
+				what = SHM;
+			else if (w == 'm')
+				what = MSG;
+			else if (w == 'e')
+				what = SEM;
+
+			if (remove_ids(what, &argv[2]))
+				fflush_stdout_and_exit(EXIT_FAILURE);
+			printf("resource(s) deleted\n");
+			return 0;
+		}
+	}
+#endif /* IPCRM_LEGACY */
+
+	/* process new syntax to conform with SYSV ipcrm */
+	while ((c = getopt(argc, argv, "q:m:s:Q:M:S:h?")) != -1) {
+		int result;
+		int id = 0;
+		int iskey = isupper(c);
+
+		/* needed to delete semaphores */
+		union semun arg;
+
+		arg.val = 0;
+
+		if ((c == '?') || (c == 'h')) {
+			bb_show_usage();
+		}
+
+		/* we don't need case information any more */
+		c = tolower(c);
+
+		/* make sure the option is in range: allowed are q, m, s */
+		if (c != 'q' && c != 'm' && c != 's') {
+			bb_show_usage();
+		}
+
+		if (iskey) {
+			/* keys are in hex or decimal */
+			key_t key = xstrtoul(optarg, 0);
+
+			if (key == IPC_PRIVATE) {
+				error++;
+				bb_error_msg("illegal key (%s)", optarg);
+				continue;
+			}
+
+			/* convert key to id */
+			id = ((c == 'q') ? msgget(key, 0) :
+				  (c == 'm') ? shmget(key, 0, 0) : semget(key, 0, 0));
+
+			if (id < 0) {
+				const char *errmsg;
+
+				error++;
+				switch (errno) {
+				case EACCES:
+					errmsg = "permission denied for";
+					break;
+				case EIDRM:
+					errmsg = "already removed";
+					break;
+				case ENOENT:
+					errmsg = "invalid";
+					break;
+				default:
+					errmsg = "unknown error in";
+					break;
+				}
+				bb_error_msg("%s %s (%s)", errmsg, "key", optarg);
+				continue;
+			}
+		} else {
+			/* ids are in decimal */
+			id = xatoul(optarg);
+		}
+
+		result = ((c == 'q') ? msgctl(id, IPC_RMID, NULL) :
+				  (c == 'm') ? shmctl(id, IPC_RMID, NULL) :
+				  semctl(id, 0, IPC_RMID, arg));
+
+		if (result) {
+			const char *errmsg;
+			const char *const what = iskey ? "key" : "id";
+
+			error++;
+			switch (errno) {
+			case EACCES:
+			case EPERM:
+				errmsg = "permission denied for";
+				break;
+			case EINVAL:
+				errmsg = "invalid";
+				break;
+			case EIDRM:
+				errmsg = "already removed";
+				break;
+			default:
+				errmsg = "unknown error in";
+				break;
+			}
+			bb_error_msg("%s %s (%s)", errmsg, what, optarg);
+			continue;
+		}
+	}
+
+	/* print usage if we still have some arguments left over */
+	if (optind != argc) {
+		bb_show_usage();
+	}
+
+	/* exit value reflects the number of errors encountered */
+	return error;
+}
diff --git a/busybox-1.19.3/util-linux/ipcs.c b/busybox-1.19.3/util-linux/ipcs.c
new file mode 100644
index 0000000..ee7df5e
--- /dev/null
+++ b/busybox-1.19.3/util-linux/ipcs.c
@@ -0,0 +1,637 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * ipcs.c -- provides information on allocated ipc resources.
+ *
+ * 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 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() */
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+#include "libbb.h"
+
+/*-------------------------------------------------------------------*/
+/* SHM_DEST and SHM_LOCKED are defined in kernel headers,
+   but inside #ifdef __KERNEL__ ... #endif */
+#ifndef SHM_DEST
+/* shm_mode upper byte flags */
+#define SHM_DEST        01000	/* segment will be destroyed on last detach */
+#define SHM_LOCKED      02000	/* segment will not be swapped */
+#endif
+
+/* For older kernels the same holds for the defines below */
+#ifndef MSG_STAT
+#define MSG_STAT	11
+#define MSG_INFO	12
+#endif
+
+#ifndef SHM_STAT
+#define SHM_STAT        13
+#define SHM_INFO        14
+struct shm_info {
+	int used_ids;
+	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
+
+#ifndef SEM_STAT
+#define SEM_STAT	18
+#define SEM_INFO	19
+#endif
+
+/* Some versions of libc only define IPC_INFO when __USE_GNU is defined. */
+#ifndef IPC_INFO
+#define IPC_INFO        3
+#endif
+/*-------------------------------------------------------------------*/
+
+/* The last arg of semctl is a union semun, but where is it defined?
+   X/OPEN tells us to define it ourselves, but until recently
+   Linux include files would also define it. */
+#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
+/* union semun is defined by including <sys/sem.h> */
+#else
+/* according to X/OPEN we have to define it ourselves */
+union semun {
+	int val;
+	struct semid_ds *buf;
+	unsigned short *array;
+	struct seminfo *__buf;
+};
+#endif
+
+/* X/OPEN (Jan 1987) does not define fields key, seq in struct ipc_perm;
+   libc 4/5 does not mention struct ipc_term at all, but includes
+   <linux/ipc.h>, which defines a struct ipc_perm with such fields.
+   glibc-1.09 has no support for sysv ipc.
+   glibc 2 uses __key, __seq */
+#if defined(__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1
+#define KEY __key
+#else
+#define KEY key
+#endif
+
+#define LIMITS 1
+#define STATUS 2
+#define CREATOR 3
+#define TIME 4
+#define PID 5
+
+static char format;
+
+static void print_perms(int id, struct ipc_perm *ipcp)
+{
+	struct passwd *pw;
+	struct group *gr;
+
+	printf("%-10d %-10o", id, ipcp->mode & 0777);
+
+	pw = getpwuid(ipcp->cuid);
+	if (pw)	printf(" %-10s", pw->pw_name);
+	else	printf(" %-10d", ipcp->cuid);
+	gr = getgrgid(ipcp->cgid);
+	if (gr)	printf(" %-10s", gr->gr_name);
+	else	printf(" %-10d", ipcp->cgid);
+
+	pw = getpwuid(ipcp->uid);
+	if (pw)	printf(" %-10s", pw->pw_name);
+	else	printf(" %-10d", ipcp->uid);
+	gr = getgrgid(ipcp->gid);
+	if (gr)	printf(" %-10s\n", gr->gr_name);
+	else	printf(" %-10d\n", ipcp->gid);
+}
+
+
+static NOINLINE void do_shm(void)
+{
+	int maxid, shmid, id;
+	struct shmid_ds shmseg;
+	struct shm_info shm_info;
+	struct shminfo shminfo;
+	struct ipc_perm *ipcp = &shmseg.shm_perm;
+	struct passwd *pw;
+
+	maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info);
+	if (maxid < 0) {
+		printf("kernel not configured for %s\n", "shared memory");
+		return;
+	}
+
+	switch (format) {
+	case LIMITS:
+		printf("------ Shared Memory %s --------\n", "Limits");
+		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 */
+		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);
+		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);
+		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");
+		break;
+
+	case TIME:
+		printf("------ Shared Memory %s --------\n", "Attach/Detach/Change Times");
+		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");
+		break;
+
+	default:
+		printf("------ Shared Memory %s --------\n", "Segments");
+		printf(	  "%-10s %-10s %-10s %-10s %-10s %-10s %-12s\n",
+				  "key", "shmid", "owner", "perms", "bytes", "nattch",
+				  "status");
+		break;
+	}
+
+	for (id = 0; id <= maxid; id++) {
+		shmid = shmctl(id, SHM_STAT, &shmseg);
+		if (shmid < 0)
+			continue;
+		if (format == CREATOR) {
+			print_perms(shmid, ipcp);
+			continue;
+		}
+		pw = getpwuid(ipcp->uid);
+		switch (format) {
+		case TIME:
+			if (pw)
+				printf("%-10d %-10.10s", shmid, pw->pw_name);
+			else
+				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");
+			printf(" %-20.16s", shmseg.shm_dtime
+					  ? ctime(&shmseg.shm_dtime) + 4 : "Not set");
+			printf(" %-20.16s\n", shmseg.shm_ctime
+					  ? ctime(&shmseg.shm_ctime) + 4 : "Not set");
+			break;
+		case PID:
+			if (pw)
+				printf("%-10d %-10.10s", shmid, pw->pw_name);
+			else
+				printf("%-10d %-10d", shmid, ipcp->uid);
+			printf(" %-10d %-10d\n", shmseg.shm_cpid, shmseg.shm_lpid);
+			break;
+
+		default:
+			printf("0x%08x ", ipcp->KEY);
+			if (pw)
+				printf("%-10d %-10.10s", shmid, pw->pw_name);
+			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" : " ");
+			break;
+		}
+	}
+}
+
+
+static NOINLINE void do_sem(void)
+{
+	int maxid, semid, id;
+	struct semid_ds semary;
+	struct seminfo seminfo;
+	struct ipc_perm *ipcp = &semary.sem_perm;
+	struct passwd *pw;
+	union semun arg;
+
+	arg.array = (unsigned short *) (void *) &seminfo;
+	maxid = semctl(0, 0, SEM_INFO, arg);
+	if (maxid < 0) {
+		printf("kernel not configured for %s\n", "semaphores");
+		return;
+	}
+
+	switch (format) {
+	case LIMITS:
+		printf("------ Semaphore %s --------\n", "Limits");
+		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);
+		return;
+
+	case STATUS:
+		printf("------ Semaphore %s --------\n", "Status");
+		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");
+		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");
+		break;
+
+	case PID:
+		break;
+
+	default:
+		printf("------ Semaphore %s --------\n", "Arrays");
+		printf(	  "%-10s %-10s %-10s %-10s %-10s\n",
+				  "key", "semid", "owner", "perms", "nsems");
+		break;
+	}
+
+	for (id = 0; id <= maxid; id++) {
+		arg.buf = (struct semid_ds *) &semary;
+		semid = semctl(id, 0, SEM_STAT, arg);
+		if (semid < 0)
+			continue;
+		if (format == CREATOR) {
+			print_perms(semid, ipcp);
+			continue;
+		}
+		pw = getpwuid(ipcp->uid);
+		switch (format) {
+		case TIME:
+			if (pw)
+				printf("%-8d %-10.10s", semid, pw->pw_name);
+			else
+				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");
+			printf(" %-26.24s\n", semary.sem_ctime
+					  ? ctime(&semary.sem_ctime) : "Not set");
+			break;
+		case PID:
+			break;
+
+		default:
+			printf("0x%08x ", ipcp->KEY);
+			if (pw)
+				printf("%-10d %-10.9s", semid, pw->pw_name);
+			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);
+			break;
+		}
+	}
+}
+
+
+static NOINLINE void do_msg(void)
+{
+	int maxid, msqid, id;
+	struct msqid_ds msgque;
+	struct msginfo msginfo;
+	struct ipc_perm *ipcp = &msgque.msg_perm;
+	struct passwd *pw;
+
+	maxid = msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo);
+	if (maxid < 0) {
+		printf("kernel not configured for %s\n", "message queues");
+		return;
+	}
+
+	switch (format) {
+	case LIMITS:
+		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);
+		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);
+		return;
+
+	case CREATOR:
+		printf("------ Message%s --------\n", " Queues: Creators/Owners");
+		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");
+		break;
+
+	case PID:
+		printf("------ Message%s --------\n", " Queues PIDs");
+		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");
+		break;
+	}
+
+	for (id = 0; id <= maxid; id++) {
+		msqid = msgctl(id, MSG_STAT, &msgque);
+		if (msqid < 0)
+			continue;
+		if (format == CREATOR) {
+			print_perms(msqid, ipcp);
+			continue;
+		}
+		pw = getpwuid(ipcp->uid);
+		switch (format) {
+		case TIME:
+			if (pw)
+				printf("%-8d %-10.10s", msqid, pw->pw_name);
+			else
+				printf("%-8d %-10d", msqid, ipcp->uid);
+			printf(" %-20.16s", msgque.msg_stime
+					  ? ctime(&msgque.msg_stime) + 4 : "Not set");
+			printf(" %-20.16s", msgque.msg_rtime
+					  ? ctime(&msgque.msg_rtime) + 4 : "Not set");
+			printf(" %-20.16s\n", msgque.msg_ctime
+					  ? ctime(&msgque.msg_ctime) + 4 : "Not set");
+			break;
+		case PID:
+			if (pw)
+				printf("%-8d %-10.10s", msqid, pw->pw_name);
+			else
+				printf("%-8d %-10d", msqid, ipcp->uid);
+			printf("  %5d     %5d\n", msgque.msg_lspid, msgque.msg_lrpid);
+			break;
+
+		default:
+			printf("0x%08x ", ipcp->KEY);
+			if (pw)
+				printf("%-10d %-10.10s", msqid, pw->pw_name);
+			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);
+			break;
+		}
+	}
+}
+
+
+static void print_shm(int shmid)
+{
+	struct shmid_ds shmds;
+	struct ipc_perm *ipcp = &shmds.shm_perm;
+
+	if (shmctl(shmid, IPC_STAT, &shmds) == -1) {
+		bb_perror_msg("shmctl");
+		return;
+	}
+
+	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);
+	printf("att_time=%-26.24s\n",
+			  shmds.shm_atime ? ctime(&shmds.shm_atime) : "Not set");
+	printf("det_time=%-26.24s\n",
+			  shmds.shm_dtime ? ctime(&shmds.shm_dtime) : "Not set");
+	printf("change_time=%-26.24s\n\n", ctime(&shmds.shm_ctime));
+}
+
+
+static void print_msg(int msqid)
+{
+	struct msqid_ds buf;
+	struct ipc_perm *ipcp = &buf.msg_perm;
+
+	if (msgctl(msqid, IPC_STAT, &buf) == -1) {
+		bb_perror_msg("msgctl");
+		return;
+	}
+
+	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);
+
+	printf("send_time=%-26.24s\n",
+			  buf.msg_stime ? ctime(&buf.msg_stime) : "Not set");
+	printf("rcv_time=%-26.24s\n",
+			  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");
+}
+
+static void print_sem(int semid)
+{
+	struct semid_ds semds;
+	struct ipc_perm *ipcp = &semds.sem_perm;
+	union semun arg;
+	unsigned int i;
+
+	arg.buf = &semds;
+	if (semctl(semid, 0, IPC_STAT, arg)) {
+		bb_perror_msg("semctl");
+		return;
+	}
+
+	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");
+	printf("ctime = %-26.24s\n"
+			  "%-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++) {
+		int val, ncnt, zcnt, pid;
+
+		val = semctl(semid, i, GETVAL, arg);
+		ncnt = semctl(semid, i, GETNCNT, arg);
+		zcnt = semctl(semid, i, GETZCNT, arg);
+		pid = semctl(semid, i, GETPID, arg);
+		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);
+	}
+	bb_putchar('\n');
+}
+
+int ipcs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int ipcs_main(int argc UNUSED_PARAM, char **argv)
+{
+	int id = 0;
+	unsigned flags = 0;
+	unsigned opt;
+	char *opt_i;
+#define flag_print	(1<<0)
+#define flag_msg	(1<<1)
+#define flag_sem	(1<<2)
+#define flag_shm	(1<<3)
+
+	opt = getopt32(argv, "i:aqsmtcplu", &opt_i);
+	if (opt & 0x1) { // -i
+		id = xatoi(opt_i);
+		flags |= flag_print;
+	}
+	if (opt & 0x2) flags |= flag_msg | flag_sem | flag_shm; // -a
+	if (opt & 0x4) flags |= flag_msg; // -q
+	if (opt & 0x8) flags |= flag_sem; // -s
+	if (opt & 0x10) flags |= flag_shm; // -m
+	if (opt & 0x20) format = TIME; // -t
+	if (opt & 0x40) format = CREATOR; // -c
+	if (opt & 0x80) format = PID; // -p
+	if (opt & 0x100) format = LIMITS; // -l
+	if (opt & 0x200) format = STATUS; // -u
+
+	if (flags & flag_print) {
+		if (flags & flag_shm) {
+			print_shm(id);
+			fflush_stdout_and_exit(EXIT_SUCCESS);
+		}
+		if (flags & flag_sem) {
+			print_sem(id);
+			fflush_stdout_and_exit(EXIT_SUCCESS);
+		}
+		if (flags & flag_msg) {
+			print_msg(id);
+			fflush_stdout_and_exit(EXIT_SUCCESS);
+		}
+		bb_show_usage();
+	}
+
+	if (!(flags & (flag_shm | flag_msg | flag_sem)))
+		flags |= flag_msg | flag_shm | flag_sem;
+	bb_putchar('\n');
+
+	if (flags & flag_shm) {
+		do_shm();
+		bb_putchar('\n');
+	}
+	if (flags & flag_sem) {
+		do_sem();
+		bb_putchar('\n');
+	}
+	if (flags & flag_msg) {
+		do_msg();
+		bb_putchar('\n');
+	}
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
diff --git a/busybox-1.19.3/util-linux/losetup.c b/busybox-1.19.3/util-linux/losetup.c
new file mode 100644
index 0000000..9b7c49f
--- /dev/null
+++ b/busybox-1.19.3/util-linux/losetup.c
@@ -0,0 +1,105 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini losetup implementation for busybox
+ *
+ * Copyright (C) 2002  Matt Kraai.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//usage:#define losetup_trivial_usage
+//usage:       "[-o OFS] LOOPDEV FILE - associate loop devices\n"
+//usage:       "	losetup -d LOOPDEV - disassociate\n"
+//usage:       "	losetup [-f] - show"
+//usage:#define losetup_full_usage "\n\n"
+//usage:       "	-o OFS	Start OFS bytes into FILE"
+//usage:     "\n	-f	Show first free loop device"
+//usage:
+//usage:#define losetup_notes_usage
+//usage:       "No arguments will display all current associations.\n"
+//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"
+
+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;
+	enum {
+		OPT_d = (1 << 0),
+		OPT_o = (1 << 1),
+		OPT_f = (1 << 2),
+	};
+
+	/* max 2 args, all opts are mutually exclusive */
+	opt_complementary = "?2:d--of:o--df:f--do";
+	opt = getopt32(argv, "do:f", &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]) {
+		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]);
+		printf("%s: %s\n", argv[0], s);
+		if (ENABLE_FEATURE_CLEAN_UP)
+			free(s);
+		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)
+				printf("%s: %s\n", dev, s);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(s);
+		}
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/lspci.c b/busybox-1.19.3/util-linux/lspci.c
new file mode 100644
index 0000000..5184858
--- /dev/null
+++ b/busybox-1.19.3/util-linux/lspci.c
@@ -0,0 +1,112 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lspci implementation for busybox
+ *
+ * Copyright (C) 2009  Malek Degachi <malek-degachi@laposte.net>
+ *
+ * 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 {
+	OPT_m = (1 << 0),
+	OPT_k = (1 << 1),
+};
+
+/*
+ * PCI_SLOT_NAME PCI_CLASS: PCI_VID:PCI_DID [PCI_SUBSYS_VID:PCI_SUBSYS_DID] [DRIVER]
+ */
+static int FAST_FUNC fileAction(
+		const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	parser_t *parser;
+	char *tokens[3];
+	char *pci_slot_name = NULL, *driver = NULL;
+	int pci_class = 0, pci_vid = 0, pci_did = 0;
+	int pci_subsys_vid = 0, pci_subsys_did = 0;
+
+	char *uevent_filename = concat_path_file(fileName, "/uevent");
+	parser = config_open2(uevent_filename, fopen_for_read);
+	free(uevent_filename);
+
+	while (config_read(parser, tokens, 3, 2, "\0:=", PARSE_NORMAL)) {
+		if (strcmp(tokens[0], "DRIVER") == 0) {
+			driver = xstrdup(tokens[1]);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_CLASS") == 0) {
+			pci_class = xstrtou(tokens[1], 16)>>8;
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_ID") == 0) {
+			pci_vid = xstrtou(tokens[1], 16);
+			pci_did = xstrtou(tokens[2], 16);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_SUBSYS_ID") == 0) {
+			pci_subsys_vid = xstrtou(tokens[1], 16);
+			pci_subsys_did = xstrtou(tokens[2], 16);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "PCI_SLOT_NAME") == 0) {
+			pci_slot_name = xstrdup(tokens[2]);
+			continue;
+		}
+	}
+	config_close(parser);
+
+
+	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);
+	} else {
+		printf("%s Class %04x: %04x:%04x",
+		       pci_slot_name, pci_class, pci_vid, pci_did);
+	}
+
+	if ((option_mask32 & OPT_k) && driver) {
+		if (option_mask32 & OPT_m) {
+			printf(" \"%s\"", driver);
+		} else {
+			printf(" %s", driver);
+		}
+	}
+	bb_putchar('\n');
+
+	free(driver);
+	free(pci_slot_name);
+
+	return TRUE;
+}
+
+int lspci_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lspci_main(int argc UNUSED_PARAM, char **argv)
+{
+	getopt32(argv, "m" /*non-compat:*/ "k" /*ignored:*/ "nv");
+
+	recursive_action("/sys/bus/pci/devices",
+			ACTION_RECURSE,
+			fileAction,
+			NULL, /* dirAction */
+			NULL, /* userData */
+			0 /* depth */);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/lsusb.c b/busybox-1.19.3/util-linux/lsusb.c
new file mode 100644
index 0000000..540f21e
--- /dev/null
+++ b/busybox-1.19.3/util-linux/lsusb.c
@@ -0,0 +1,75 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * lsusb implementation for busybox
+ *
+ * Copyright (C) 2009  Malek Degachi <malek-degachi@laposte.net>
+ *
+ * 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(
+		const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	parser_t *parser;
+	char *tokens[4];
+	char *busnum = NULL, *devnum = NULL;
+	int product_vid = 0, product_did = 0;
+	char *uevent_filename = concat_path_file(fileName, "/uevent");
+
+	parser = config_open2(uevent_filename, fopen_for_read);
+	free(uevent_filename);
+
+	while (config_read(parser, tokens, 4, 2, "\\/=", PARSE_NORMAL)) {
+		if ((parser->lineno == 1) && strcmp(tokens[0], "DEVTYPE") == 0) {
+			break;
+		}
+
+		if (strcmp(tokens[0], "PRODUCT") == 0) {
+			product_vid = xstrtou(tokens[1], 16);
+			product_did = xstrtou(tokens[2], 16);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "BUSNUM") == 0) {
+			busnum = xstrdup(tokens[1]);
+			continue;
+		}
+
+		if (strcmp(tokens[0], "DEVNUM") == 0) {
+			devnum = xstrdup(tokens[1]);
+			continue;
+		}
+	}
+	config_close(parser);
+
+	if (busnum) {
+		printf("Bus %s Device %s: ID %04x:%04x\n", busnum, devnum, product_vid, product_did);
+		free(busnum);
+		free(devnum);
+	}
+
+	return TRUE;
+}
+
+int lsusb_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int lsusb_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+	/* no options, no getopt */
+
+	recursive_action("/sys/bus/usb/devices",
+			ACTION_RECURSE,
+			fileAction,
+			NULL, /* dirAction */
+			NULL, /* userData */
+			0 /* depth */);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/mdev.c b/busybox-1.19.3/util-linux/mdev.c
new file mode 100644
index 0000000..7cabb1d
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mdev.c
@@ -0,0 +1,653 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mdev - Mini udev for busybox
+ *
+ * Copyright 2005 Rob Landley <rob@landley.net>
+ * Copyright 2005 Frank Sorenson <frank@tuxrocks.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//usage:#define mdev_trivial_usage
+//usage:       "[-s]"
+//usage:#define mdev_full_usage "\n\n"
+//usage:       "	-s	Scan /sys and populate /dev during system boot\n"
+//usage:       "\n"
+//usage:       "It can be run by kernel as a hotplug helper. To activate it:\n"
+//usage:       " echo /sbin/mdev > /proc/sys/kernel/hotplug\n"
+//usage:	IF_FEATURE_MDEV_CONF(
+//usage:       "It uses /etc/mdev.conf with lines\n"
+//usage:       "[-]DEVNAME UID:GID PERM"
+//usage:			IF_FEATURE_MDEV_RENAME(" [>|=PATH]")
+//usage:			IF_FEATURE_MDEV_EXEC(" [@|$|*PROG]")
+//usage:	)
+//usage:
+//usage:#define mdev_notes_usage ""
+//usage:	IF_FEATURE_MDEV_CONFIG(
+//usage:       "The mdev config file contains lines that look like:\n"
+//usage:       "  hd[a-z][0-9]* 0:3 660\n\n"
+//usage:       "That's device name (with regex match), uid:gid, and permissions.\n\n"
+//usage:	IF_FEATURE_MDEV_EXEC(
+//usage:       "Optionally, that can be followed (on the same line) by a special character\n"
+//usage:       "and a command line to run after creating/before deleting the corresponding\n"
+//usage:       "device(s). The environment variable $MDEV indicates the active device node\n"
+//usage:       "(which is useful if it's a regex match). For example:\n\n"
+//usage:       "  hdc root:cdrom 660  *ln -s $MDEV cdrom\n\n"
+//usage:       "The special characters are @ (run after creating), $ (run before deleting),\n"
+//usage:       "and * (run both after creating and before deleting). The commands run in\n"
+//usage:       "the /dev directory, and use system() which calls /bin/sh.\n\n"
+//usage:	)
+//usage:       "Config file parsing stops on the first matching line. If no config\n"
+//usage:       "entry is matched, devices are created with default 0:0 660. (Make\n"
+//usage:       "the last line match .* to override this.)\n\n"
+//usage:	)
+
+#include "libbb.h"
+#include "xregex.h"
+
+/* "mdev -s" scans /sys/class/xxx, looking for directories which have dev
+ * file (it is of the form "M:m\n"). Example: /sys/class/tty/tty0/dev
+ * contains "4:0\n". Directory name is taken as device name, path component
+ * directly after /sys/class/ as subsystem. In this example, "tty0" and "tty".
+ * Then mdev creates the /dev/device_name node.
+ * If /sys/class/.../dev file does not exist, mdev still may act
+ * on this device: see "@|$|*command args..." parameter in config file.
+ *
+ * mdev w/o parameters is called as hotplug helper. It takes device
+ * and subsystem names from $DEVPATH and $SUBSYSTEM, extracts
+ * maj,min from "/sys/$DEVPATH/dev" and also examines
+ * $ACTION ("add"/"delete") and $FIRMWARE.
+ *
+ * If action is "add", mdev creates /dev/device_name similarly to mdev -s.
+ * (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
+ * 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
+ * to load the module and exit:
+ *    $MODALIAS=.* 0:0 660 @modprobe "$MODALIAS"
+ * The kernel will generate another hotplug event when /sys/class/.../dev
+ * file appears.
+ *
+ * When line matches, the device node is created, chmod'ed and chown'ed,
+ * moved to path, and if >path, a symlink to moved node is created,
+ * all this if /sys/class/.../dev exists.
+ *    Examples:
+ *    =loop/      - moves to /dev/loop
+ *    >disk/sda%1 - moves to /dev/disk/sdaN, makes /dev/sdaN a symlink
+ *
+ * Then "command args..." is executed (via sh -c 'command args...').
+ * @:execute on creation, $:on deletion, *:on both.
+ * This happens regardless of /sys/class/.../dev existence.
+ */
+
+struct globals {
+	int root_major, root_minor;
+	char *subsystem;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+
+/* Prevent infinite loops in /sys symlinks */
+#define MAX_SYSFS_DEPTH 3
+
+/* We use additional 64+ bytes in make_device() */
+#define SCRATCH_SIZE 80
+
+/* Builds an alias path.
+ * This function potentionally reallocates the alias parameter.
+ * Only used for ENABLE_FEATURE_MDEV_RENAME
+ */
+static char *build_alias(char *alias, const char *device_name)
+{
+	char *dest;
+
+	/* ">bar/": rename to bar/device_name */
+	/* ">bar[/]baz": rename to bar[/]baz */
+	dest = strrchr(alias, '/');
+	if (dest) { /* ">bar/[baz]" ? */
+		*dest = '\0'; /* mkdir bar */
+		bb_make_directory(alias, 0755, FILEUTILS_RECUR);
+		*dest = '/';
+		if (dest[1] == '\0') { /* ">bar/" => ">bar/device_name" */
+			dest = alias;
+			alias = concat_path_file(alias, device_name);
+			free(dest);
+		}
+	}
+
+	return alias;
+}
+
+/* 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)
+ * path string.
+ * NB2: "mdev -s" may call us many times, do not leak memory/fds!
+ */
+static void make_device(char *path, int delete)
+{
+	char *device_name, *subsystem_slash_devname;
+	int major, minor, type, len;
+	mode_t mode;
+	parser_t *parser;
+
+	/* 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
+	 * because sscanf() will stop at the first nondigit, which \n is.
+	 * 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 (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) {
+			major = -1;
+		}
+	}
+	/* else: for delete, -1 still deletes the node, but < -1 suppresses that */
+
+	/* Determine device name, type, major and minor */
+	device_name = (char*) bb_basename(path);
+	/* 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. */
+	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;
+		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 = '=';
+			}
+			/* 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 */
+				}
+			}
+ 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], /*allow_numeric:*/ 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 = -2;
+				}
+				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++;
+								}
+							}
+							p++;
+							s++;
+						}
+					} else {
+						alias = xstrdup(a + 1);
+					}
+				}
+			}
+
+			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);
+				}
+			}
+		}
+
+		/* End of field parsing */
+
+		/* "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);
+				}
+			}
+
+			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 (delete && major >= -1) {
+				if (ENABLE_FEATURE_MDEV_RENAME && alias) {
+					if (aliaslink == '>')
+						unlink(device_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)
+			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);
+}
+
+/* File callback for /sys/ traversal */
+static int FAST_FUNC fileAction(const char *fileName,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData,
+		int depth UNUSED_PARAM)
+{
+	size_t len = strlen(fileName) - 4; /* can't underflow */
+	char *scratch = userData;
+
+	/* len check is for paranoid reasons */
+	if (strcmp(fileName + len, "/dev") != 0 || len >= PATH_MAX)
+		return FALSE;
+
+	strcpy(scratch, fileName);
+	scratch[len] = '\0';
+	make_device(scratch, /*delete:*/ 0);
+
+	return TRUE;
+}
+
+/* Directory callback for /sys/ traversal */
+static int FAST_FUNC dirAction(const char *fileName UNUSED_PARAM,
+		struct stat *statbuf UNUSED_PARAM,
+		void *userData UNUSED_PARAM,
+		int depth)
+{
+	/* Extract device subsystem -- the name of the directory
+	 * under /sys/class/ */
+	if (1 == depth) {
+		free(G.subsystem);
+		G.subsystem = strrchr(fileName, '/');
+		if (G.subsystem)
+			G.subsystem = xstrdup(G.subsystem + 1);
+	}
+
+	return (depth >= MAX_SYSFS_DEPTH ? SKIP : TRUE);
+}
+
+/* For the full gory details, see linux/Documentation/firmware_class/README
+ *
+ * Firmware loading works like this:
+ * - kernel sets FIRMWARE env var
+ * - userspace checks /lib/firmware/$FIRMWARE
+ * - userspace waits for /sys/$DEVPATH/loading to appear
+ * - userspace writes "1" to /sys/$DEVPATH/loading
+ * - userspace copies /lib/firmware/$FIRMWARE into /sys/$DEVPATH/data
+ * - userspace writes "0" (worked) or "-1" (failed) to /sys/$DEVPATH/loading
+ * - kernel loads firmware into device
+ */
+static void load_firmware(const char *firmware, const char *sysfs_path)
+{
+	int cnt;
+	int firmware_fd, loading_fd, data_fd;
+
+	/* check for /lib/firmware/$FIRMWARE */
+	xchdir("/lib/firmware");
+	firmware_fd = xopen(firmware, O_RDONLY);
+
+	/* in case we goto out ... */
+	data_fd = -1;
+
+	/* 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)
+			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);
+
+	/* tell kernel result by "echo [0|-1] > /sys/$DEVPATH/loading" */
+	if (cnt > 0)
+		full_write(loading_fd, "0", 1);
+	else
+		full_write(loading_fd, "-1", 2);
+
+ out:
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(firmware_fd);
+		close(loading_fd);
+		close(data_fd);
+	}
+}
+
+int mdev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mdev_main(int argc UNUSED_PARAM, char **argv)
+{
+	RESERVE_CONFIG_BUFFER(temp, PATH_MAX + SCRATCH_SIZE);
+
+	/* We can be called as hotplug helper */
+	/* Kernel cannot provide suitable stdio fds for us, do it ourself */
+	bb_sanitize_stdio();
+
+	/* Force the configuration file settings exactly */
+	umask(0);
+
+	xchdir("/dev");
+
+	if (argv[1] && strcmp(argv[1], "-s") == 0) {
+		/* Scan:
+		 * mdev -s
+		 */
+		struct stat st;
+
+		xstat("/", &st);
+		G.root_major = major(st.st_dev);
+		G.root_minor = minor(st.st_dev);
+
+		/* ACTION_FOLLOWLINKS is needed since in newer kernels
+		 * /sys/block/loop* (for example) are symlinks to dirs,
+		 * not real directories.
+		 * (kernel's CONFIG_SYSFS_DEPRECATED makes them real dirs,
+		 * but we can't enforce that on users)
+		 */
+		if (access("/sys/class/block", F_OK) != 0) {
+			/* Scan obsolete /sys/block only if /sys/class/block
+			 * doesn't exist. Otherwise we'll have dupes.
+			 * Also, do not complain if it doesn't exist.
+			 * Some people configure kernel to have no blockdevs.
+			 */
+			recursive_action("/sys/block",
+				ACTION_RECURSE | ACTION_FOLLOWLINKS | ACTION_QUIET,
+				fileAction, dirAction, temp, 0);
+		}
+		recursive_action("/sys/class",
+			ACTION_RECURSE | ACTION_FOLLOWLINKS,
+			fileAction, dirAction, temp, 0);
+	} else {
+		char *fw;
+		char *seq;
+		char *action;
+		char *env_path;
+		static const char keywords[] ALIGN1 = "remove\0add\0";
+		enum { OP_remove = 0, OP_add };
+		smalluint op;
+
+		/* Hotplug:
+		 * env ACTION=... DEVPATH=... SUBSYSTEM=... [SEQNUM=...] mdev
+		 * ACTION can be "add" or "remove"
+		 * DEVPATH is like "/block/sda" or "/class/input/mice"
+		 */
+		action = getenv("ACTION");
+		env_path = getenv("DEVPATH");
+		G.subsystem = getenv("SUBSYSTEM");
+		if (!action || !env_path /*|| !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);
+		}
+
+		snprintf(temp, PATH_MAX, "/sys%s", env_path);
+		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);
+		}
+		else if (op == OP_add) {
+			make_device(temp, /*delete:*/ 0);
+			if (ENABLE_FEATURE_MDEV_LOAD_FIRMWARE) {
+				if (fw)
+					load_firmware(fw, temp);
+			}
+		}
+
+		if (seq) {
+			xopen_xwrite_close("mdev.seq", utoa(xatou(seq) + 1));
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		RELEASE_CONFIG_BUFFER(temp);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/minix.h b/busybox-1.19.3/util-linux/minix.h
new file mode 100644
index 0000000..e0fbcf7
--- /dev/null
+++ b/busybox-1.19.3/util-linux/minix.h
@@ -0,0 +1,98 @@
+/*
+ * This is the original minix inode layout on disk.
+ * Note the 8-bit gid and atime and ctime.
+ */
+struct minix1_inode {
+	uint16_t i_mode;
+	uint16_t i_uid;
+	uint32_t i_size;
+	uint32_t i_time;
+	uint8_t  i_gid;
+	uint8_t  i_nlinks;
+	uint16_t i_zone[9];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+struct minix2_inode {
+	uint16_t i_mode;
+	uint16_t i_nlinks;
+	uint16_t i_uid;
+	uint16_t i_gid;
+	uint32_t i_size;
+	uint32_t i_atime;
+	uint32_t i_mtime;
+	uint32_t i_ctime;
+	uint32_t i_zone[10];
+};
+
+/*
+ * minix superblock data on disk
+ */
+struct minix_superblock {
+	uint16_t s_ninodes;
+	uint16_t s_nzones;
+	uint16_t s_imap_blocks;
+	uint16_t s_zmap_blocks;
+	uint16_t s_firstdatazone;
+	uint16_t s_log_zone_size;
+	uint32_t s_max_size;
+	uint16_t s_magic;
+	uint16_t s_state;
+	uint32_t s_zones;
+};
+
+struct minix_dir_entry {
+	uint16_t inode;
+	char name[];
+};
+
+/* Believe it or not, but mount.h has this one #defined */
+#undef BLOCK_SIZE
+
+enum {
+	BLOCK_SIZE              = 1024,
+	BITS_PER_BLOCK          = BLOCK_SIZE << 3,
+
+	MINIX_ROOT_INO          = 1,
+	MINIX_BAD_INO           = 2,
+
+	MINIX1_SUPER_MAGIC      = 0x137F,       /* original minix fs */
+	MINIX1_SUPER_MAGIC2     = 0x138F,       /* minix fs, 30 char names */
+	MINIX2_SUPER_MAGIC      = 0x2468,       /* minix V2 fs */
+	MINIX2_SUPER_MAGIC2     = 0x2478,       /* minix V2 fs, 30 char names */
+	MINIX_VALID_FS          = 0x0001,       /* clean fs */
+	MINIX_ERROR_FS          = 0x0002,       /* fs has errors */
+
+	INODE_SIZE1             = sizeof(struct minix1_inode),
+	INODE_SIZE2             = sizeof(struct minix2_inode),
+	MINIX1_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix1_inode),
+	MINIX2_INODES_PER_BLOCK = BLOCK_SIZE / sizeof(struct minix2_inode),
+};
+
+/*
+Basic test script for regressions in mkfs/fsck.
+Copies current dir into image (typically bbox build tree).
+
+#!/bin/sh
+tmpdir=/tmp/minixtest-$$
+tmpimg=/tmp/minix-img-$$
+
+mkdir $tmpdir
+dd if=/dev/zero of=$tmpimg bs=1M count=20 || exit
+./busybox mkfs.minix $tmpimg || exit
+mount -o loop $tmpimg $tmpdir || exit
+cp -a "$PWD" $tmpdir
+umount $tmpdir || exit
+./busybox fsck.minix -vfm $tmpimg || exit
+echo "Continue?"
+read junk
+./busybox fsck.minix -vfml $tmpimg || exit
+rmdir $tmpdir
+rm $tmpimg
+
+*/
diff --git a/busybox-1.19.3/util-linux/mkfs_ext2.c b/busybox-1.19.3/util-linux/mkfs_ext2.c
new file mode 100644
index 0000000..f6ccc9c
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mkfs_ext2.c
@@ -0,0 +1,695 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs_ext2: utility to create EXT2 filesystem
+ * inspired by genext2fs
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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
+
+// from e2fsprogs
+#define s_reserved_gdt_blocks s_padding1
+#define s_mkfs_time           s_reserved[0]
+#define s_flags               s_reserved[22]
+
+#define EXT2_HASH_HALF_MD4       1
+#define EXT2_FLAGS_SIGNED_HASH   0x0001
+#define EXT2_FLAGS_UNSIGNED_HASH 0x0002
+
+// storage helpers
+char BUG_wrong_field_size(void);
+#define STORE_LE(field, value) \
+do { \
+	if (sizeof(field) == 4) \
+		field = SWAP_LE32(value); \
+	else if (sizeof(field) == 2) \
+		field = SWAP_LE16(value); \
+	else if (sizeof(field) == 1) \
+		field = (value); \
+	else \
+		BUG_wrong_field_size(); \
+} while (0)
+
+#define FETCH_LE32(field) \
+	(sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
+
+// All fields are little-endian
+struct ext2_dir {
+	uint32_t inode1;
+	uint16_t rec_len1;
+	uint8_t  name_len1;
+	uint8_t  file_type1;
+	char     name1[4];
+	uint32_t inode2;
+	uint16_t rec_len2;
+	uint8_t  name_len2;
+	uint8_t  file_type2;
+	char     name2[4];
+	uint32_t inode3;
+	uint16_t rec_len3;
+	uint8_t  name_len3;
+	uint8_t  file_type3;
+	char     name3[12];
+};
+
+static unsigned int_log2(unsigned arg)
+{
+	unsigned r = 0;
+	while ((arg >>= 1) != 0)
+		r++;
+	return r;
+}
+
+// taken from mkfs_minix.c. libbb candidate?
+// "uint32_t size", since we never use it for anything >32 bits
+static uint32_t div_roundup(uint32_t size, uint32_t n)
+{
+	// Overflow-resistant
+	uint32_t res = size / n;
+	if (res * n != size)
+		res++;
+	return res;
+}
+
+static void allocate(uint8_t *bitmap, uint32_t blocksize, uint32_t start, uint32_t end)
+{
+	uint32_t i;
+
+//bb_info_msg("ALLOC: [%u][%u][%u]: [%u-%u]:=[%x],[%x]", blocksize, start, end, start/8, blocksize - end/8 - 1, (1 << (start & 7)) - 1, (uint8_t)(0xFF00 >> (end & 7)));
+	memset(bitmap, 0, blocksize);
+	i = start / 8;
+	memset(bitmap, 0xFF, i);
+	bitmap[i] = (1 << (start & 7)) - 1; //0..7 => 00000000..01111111
+	i = end / 8;
+	bitmap[blocksize - i - 1] |= 0x7F00 >> (end & 7); //0..7 => 00000000..11111110
+	memset(bitmap + blocksize - i, 0xFF, i); // N.B. no overflow here!
+}
+
+static uint32_t has_super(uint32_t x)
+{
+	// 0, 1 and powers of 3, 5, 7 up to 2^32 limit
+	static const uint32_t supers[] = {
+		0, 1, 3, 5, 7, 9, 25, 27, 49, 81, 125, 243, 343, 625, 729,
+		2187, 2401, 3125, 6561, 15625, 16807, 19683, 59049, 78125,
+		117649, 177147, 390625, 531441, 823543, 1594323, 1953125,
+		4782969, 5764801, 9765625, 14348907, 40353607, 43046721,
+		48828125, 129140163, 244140625, 282475249, 387420489,
+		1162261467, 1220703125, 1977326743, 3486784401/* >2^31 */,
+	};
+	const uint32_t *sp = supers + ARRAY_SIZE(supers);
+	while (1) {
+		sp--;
+		if (x == *sp)
+			return 1;
+		if (x > *sp)
+			return 0;
+	}
+}
+
+#define fd 3	/* predefined output descriptor */
+
+static void PUT(uint64_t off, void *buf, uint32_t size)
+{
+//	bb_info_msg("PUT[%llu]:[%u]", off, size);
+	xlseek(fd, off, SEEK_SET);
+	xwrite(fd, buf, size);
+}
+
+// 128 and 256-byte inodes:
+// 128-byte inode is described by struct ext2_inode.
+// 256-byte one just has these fields appended:
+//      __u16   i_extra_isize;
+//      __u16   i_pad1;
+//      __u32   i_ctime_extra;  /* extra Change time (nsec << 2 | epoch) */
+//      __u32   i_mtime_extra;  /* extra Modification time (nsec << 2 | epoch) */
+//      __u32   i_atime_extra;  /* extra Access time (nsec << 2 | epoch) */
+//      __u32   i_crtime;       /* File creation time */
+//      __u32   i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/
+//      __u32   i_version_hi;   /* high 32 bits for 64-bit version */
+// the rest is padding.
+//
+// linux/ext2_fs.h has "#define i_size_high i_dir_acl" which suggests that even
+// 128-byte inode is capable of describing large files (i_dir_acl is meaningful
+// only for directories, which never need i_size_high).
+//
+// Standard mke2fs creates a filesystem with 256-byte inodes if it is
+// bigger than 0.5GB.
+
+// Standard mke2fs 1.41.9:
+// Usage: mke2fs [-c|-l filename] [-b block-size] [-f fragment-size]
+//	[-i bytes-per-inode] [-I inode-size] [-J journal-options]
+//	[-G meta group size] [-N number-of-inodes]
+//	[-m reserved-blocks-percentage] [-o creator-os]
+//	[-g blocks-per-group] [-L volume-label] [-M last-mounted-directory]
+//	[-O feature[,...]] [-r fs-revision] [-E extended-option[,...]]
+//	[-T fs-type] [-U UUID] [-jnqvFSV] device [blocks-count]
+//
+// Options not commented below are taken but silently ignored:
+enum {
+	OPT_c = 1 << 0,
+	OPT_l = 1 << 1,
+	OPT_b = 1 << 2,		// block size, in bytes
+	OPT_f = 1 << 3,
+	OPT_i = 1 << 4,		// bytes per inode
+	OPT_I = 1 << 5,		// custom inode size, in bytes
+	OPT_J = 1 << 6,
+	OPT_G = 1 << 7,
+	OPT_N = 1 << 8,
+	OPT_m = 1 << 9,		// percentage of blocks reserved for superuser
+	OPT_o = 1 << 10,
+	OPT_g = 1 << 11,
+	OPT_L = 1 << 12,	// label
+	OPT_M = 1 << 13,
+	OPT_O = 1 << 14,
+	OPT_r = 1 << 15,
+	OPT_E = 1 << 16,
+	OPT_T = 1 << 17,
+	OPT_U = 1 << 18,
+	OPT_j = 1 << 19,
+	OPT_n = 1 << 20,	// dry run: do not write anything
+	OPT_q = 1 << 21,
+	OPT_v = 1 << 22,
+	OPT_F = 1 << 23,
+	OPT_S = 1 << 24,
+	//OPT_V = 1 << 25,	// -V version. bbox applets don't support that
+};
+
+int mkfs_ext2_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_ext2_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned i, pos, n;
+	unsigned bs, bpi;
+	unsigned blocksize, blocksize_log2;
+	unsigned inodesize, user_inodesize;
+	unsigned reserved_percent = 5;
+	unsigned long long kilobytes;
+	uint32_t nblocks, nblocks_full;
+	uint32_t nreserved;
+	uint32_t ngroups;
+	uint32_t bytes_per_inode;
+	uint32_t first_block;
+	uint32_t inodes_per_group;
+	uint32_t group_desc_blocks;
+	uint32_t inode_table_blocks;
+	uint32_t lost_and_found_blocks;
+	time_t timestamp;
+	const char *label = "";
+	struct stat st;
+	struct ext2_super_block *sb; // superblock
+	struct ext2_group_desc *gd; // group descriptors
+	struct ext2_inode *inode;
+	struct ext2_dir *dir;
+	uint8_t *buf;
+
+	// using global "option_mask32" instead of local "opts":
+	// we are register starved here
+	opt_complementary = "-1:b+:i+:I+:m+";
+	/*opts =*/ getopt32(argv, "cl:b:f:i:I:J:G:N:m:o:g:L:M:O:r:E:T:U:jnqvFS",
+		/*lbfi:*/ NULL, &bs, NULL, &bpi,
+		/*IJGN:*/ &user_inodesize, NULL, NULL, NULL,
+		/*mogL:*/ &reserved_percent, NULL, NULL, &label,
+		/*MOrE:*/ NULL, NULL, NULL, NULL,
+		/*TU:*/ NULL, NULL);
+	argv += optind; // argv[0] -- device
+
+	// open the device, check the device is a block device
+	xmove_fd(xopen(argv[0], O_WRONLY), fd);
+	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]);
+
+	// check if it is mounted
+	// N.B. what if we format a file? find_mount_point will return false negative since
+	// it is loop block device which is mounted!
+	if (find_mount_point(argv[0], 0))
+		bb_error_msg_and_die("can't format mounted filesystem");
+
+	// get size in kbytes
+	kilobytes = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ !(option_mask32 & OPT_n)) / 1024;
+
+	bytes_per_inode = 16384;
+	if (kilobytes < 512*1024)
+		bytes_per_inode = 4096;
+	if (kilobytes < 3*1024)
+		bytes_per_inode = 8192;
+	if (option_mask32 & OPT_i)
+		bytes_per_inode = bpi;
+
+	// Determine block size and inode size
+	// block size is a multiple of 1024
+	// inode size is a multiple of 128
+	blocksize = 1024;
+	inodesize = sizeof(struct ext2_inode); // 128
+	if (kilobytes >= 512*1024) { // mke2fs 1.41.9 compat
+		blocksize = 4096;
+		inodesize = 256;
+	}
+	if (EXT2_MAX_BLOCK_SIZE > 4096) {
+		// kilobytes >> 22 == size in 4gigabyte chunks.
+		// if size >= 16k gigs, blocksize must be increased.
+		// Try "mke2fs -F image $((16 * 1024*1024*1024))"
+		while ((kilobytes >> 22) >= blocksize)
+			blocksize *= 2;
+	}
+	if (option_mask32 & OPT_b)
+		blocksize = bs;
+	if (blocksize < EXT2_MIN_BLOCK_SIZE
+	 || blocksize > EXT2_MAX_BLOCK_SIZE
+	 || (blocksize & (blocksize - 1)) // not power of 2
+	) {
+		bb_error_msg_and_die("blocksize %u is bad", blocksize);
+	}
+	// Do we have custom inode size?
+	if (option_mask32 & OPT_I) {
+		if (user_inodesize < sizeof(*inode)
+		 || user_inodesize > blocksize
+		 || (user_inodesize & (user_inodesize - 1)) // not power of 2
+		) {
+			bb_error_msg("-%c is bad", 'I');
+		} else {
+			inodesize = user_inodesize;
+		}
+	}
+
+	if ((int32_t)bytes_per_inode < blocksize)
+		bb_error_msg_and_die("-%c is bad", 'i');
+	// number of bits in one block, i.e. 8*blocksize
+#define blocks_per_group (8 * blocksize)
+	first_block = (EXT2_MIN_BLOCK_SIZE == blocksize);
+	blocksize_log2 = int_log2(blocksize);
+
+	// Determine number of blocks
+	kilobytes >>= (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE);
+	nblocks = kilobytes;
+	if (nblocks != kilobytes)
+		bb_error_msg_and_die("block count doesn't fit in 32 bits");
+#define kilobytes kilobytes_unused_after_this
+	// Experimentally, standard mke2fs won't work on images smaller than 60k
+	if (nblocks < 60)
+		bb_error_msg_and_die("need >= 60 blocks");
+
+	// How many reserved blocks?
+	if (reserved_percent > 50)
+		bb_error_msg_and_die("-%c is bad", 'm');
+	nreserved = (uint64_t)nblocks * reserved_percent / 100;
+
+	// N.B. killing e2fsprogs feature! Unused blocks don't account in calculations
+	nblocks_full = nblocks;
+
+	// If last block group is too small, nblocks may be decreased in order
+	// to discard it, and control returns here to recalculate some
+	// parameters.
+	// Note: blocksize and bytes_per_inode are never recalculated.
+ retry:
+	// N.B. a block group can have no more than blocks_per_group blocks
+	ngroups = div_roundup(nblocks - first_block, blocks_per_group);
+
+	group_desc_blocks = div_roundup(ngroups, blocksize / sizeof(*gd));
+	// TODO: reserved blocks must be marked as such in the bitmaps,
+	// or resulting filesystem is corrupt
+	if (ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT) {
+		/*
+		 * From e2fsprogs: Calculate the number of GDT blocks to reserve for online
+		 * filesystem growth.
+		 * The absolute maximum number of GDT blocks we can reserve is determined by
+		 * the number of block pointers that can fit into a single block.
+		 * We set it at 1024x the current filesystem size, or
+		 * the upper block count limit (2^32), whichever is lower.
+		 */
+		uint32_t reserved_group_desc_blocks = 0xFFFFFFFF; // maximum block number
+		if (nblocks < reserved_group_desc_blocks / 1024)
+			reserved_group_desc_blocks = nblocks * 1024;
+		reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks - first_block, blocks_per_group);
+		reserved_group_desc_blocks = div_roundup(reserved_group_desc_blocks, blocksize / sizeof(*gd)) - group_desc_blocks;
+		if (reserved_group_desc_blocks > blocksize / sizeof(uint32_t))
+			reserved_group_desc_blocks = blocksize / sizeof(uint32_t);
+		//TODO: STORE_LE(sb->s_reserved_gdt_blocks, reserved_group_desc_blocks);
+		group_desc_blocks += reserved_group_desc_blocks;
+	}
+
+	{
+		// N.B. e2fsprogs does as follows!
+		uint32_t overhead, remainder;
+		// ninodes is the max number of inodes in this filesystem
+		uint32_t ninodes = ((uint64_t) nblocks_full * blocksize) / bytes_per_inode;
+		if (ninodes < EXT2_GOOD_OLD_FIRST_INO+1)
+			ninodes = EXT2_GOOD_OLD_FIRST_INO+1;
+		inodes_per_group = div_roundup(ninodes, ngroups);
+		// minimum number because the first EXT2_GOOD_OLD_FIRST_INO-1 are reserved
+		if (inodes_per_group < 16)
+			inodes_per_group = 16;
+		// a block group can't have more inodes than blocks
+		if (inodes_per_group > blocks_per_group)
+			inodes_per_group = blocks_per_group;
+		// adjust inodes per group so they completely fill the inode table blocks in the descriptor
+		inodes_per_group = (div_roundup(inodes_per_group * inodesize, blocksize) * blocksize) / inodesize;
+		// make sure the number of inodes per group is a multiple of 8
+		inodes_per_group &= ~7;
+		inode_table_blocks = div_roundup(inodes_per_group * inodesize, blocksize);
+
+		// to be useful, lost+found should occupy at least 2 blocks (but not exceeding 16*1024 bytes),
+		// and at most EXT2_NDIR_BLOCKS. So reserve these blocks right now
+		/* Or e2fsprogs comment verbatim (what does it mean?):
+		 * Ensure that lost+found is at least 2 blocks, so we always
+		 * test large empty blocks for big-block filesystems. */
+		lost_and_found_blocks = MIN(EXT2_NDIR_BLOCKS, 16 >> (blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE));
+
+		// the last group needs more attention: isn't it too small for possible overhead?
+		overhead = (has_super(ngroups - 1) ? (1/*sb*/ + group_desc_blocks) : 0) + 1/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks;
+		remainder = (nblocks - first_block) % blocks_per_group;
+		////can't happen, nblocks >= 60 guarantees this
+		////if ((1 == ngroups)
+		//// && remainder
+		//// && (remainder < overhead + 1/* "/" */ + lost_and_found_blocks)
+		////) {
+		////	bb_error_msg_and_die("way small device");
+		////}
+
+		// Standard mke2fs uses 50. Looks like a bug in our calculation
+		// of "remainder" or "overhead" - we don't match standard mke2fs
+		// when we transition from one group to two groups
+		// (a bit after 8M image size), but it works for two->three groups
+		// transition (at 16M).
+		if (remainder && (remainder < overhead + 50)) {
+//bb_info_msg("CHOP[%u]", remainder);
+			nblocks -= remainder;
+			goto retry;
+		}
+	}
+
+	if (nblocks_full - nblocks)
+		printf("warning: %u blocks unused\n\n", nblocks_full - nblocks);
+	printf(
+		"Filesystem label=%s\n"
+		"OS type: Linux\n"
+		"Block size=%u (log=%u)\n"
+		"Fragment size=%u (log=%u)\n"
+		"%u inodes, %u blocks\n"
+		"%u blocks (%u%%) reserved for the super user\n"
+		"First data block=%u\n"
+		"Maximum filesystem blocks=%u\n"
+		"%u block groups\n"
+		"%u blocks per group, %u fragments per group\n"
+		"%u inodes per group"
+		, label
+		, blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
+		, blocksize, blocksize_log2 - EXT2_MIN_BLOCK_LOG_SIZE
+		, inodes_per_group * ngroups, nblocks
+		, nreserved, reserved_percent
+		, first_block
+		, group_desc_blocks * (blocksize / (unsigned)sizeof(*gd)) * blocks_per_group
+		, ngroups
+		, blocks_per_group, blocks_per_group
+		, inodes_per_group
+	);
+	{
+		const char *fmt = "\nSuperblock backups stored on blocks:\n"
+			"\t%u";
+		pos = first_block;
+		for (i = 1; i < ngroups; i++) {
+			pos += blocks_per_group;
+			if (has_super(i)) {
+				printf(fmt, (unsigned)pos);
+				fmt = ", %u";
+			}
+		}
+	}
+	bb_putchar('\n');
+
+	if (option_mask32 & OPT_n) {
+		if (ENABLE_FEATURE_CLEAN_UP)
+			close(fd);
+		return EXIT_SUCCESS;
+	}
+
+	// TODO: 3/5 refuse if mounted
+	// TODO: 4/5 compat options
+	// TODO: 1/5 sanity checks
+	// TODO: 0/5 more verbose error messages
+	// TODO: 4/5 bigendianness: recheck, wait for ARM reporters
+	// TODO: 2/5 reserved GDT: how to mark but not allocate?
+	// TODO: 3/5 dir_index?
+
+	// fill the superblock
+	sb = xzalloc(1024);
+	STORE_LE(sb->s_rev_level, EXT2_DYNAMIC_REV); // revision 1 filesystem
+	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);
+	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);
+	// first 1024 bytes of the device are for boot record. If block size is 1024 bytes, then
+	// the first block is 1, otherwise 0
+	STORE_LE(sb->s_first_data_block, first_block);
+	// block and inode bitmaps occupy no more than one block, so maximum number of blocks is
+	STORE_LE(sb->s_blocks_per_group, blocks_per_group);
+	STORE_LE(sb->s_frags_per_group, blocks_per_group);
+	// blocks
+	STORE_LE(sb->s_blocks_count, nblocks);
+	// reserve blocks for superuser
+	STORE_LE(sb->s_r_blocks_count, nreserved);
+	// ninodes
+	STORE_LE(sb->s_inodes_per_group, inodes_per_group);
+	STORE_LE(sb->s_inodes_count, inodes_per_group * ngroups);
+	STORE_LE(sb->s_free_inodes_count, inodes_per_group * ngroups - EXT2_GOOD_OLD_FIRST_INO);
+	// timestamps
+	timestamp = time(NULL);
+	STORE_LE(sb->s_mkfs_time, timestamp);
+	STORE_LE(sb->s_wtime, timestamp);
+	STORE_LE(sb->s_lastcheck, timestamp);
+	// misc. Values are chosen to match mke2fs 1.41.9
+	STORE_LE(sb->s_state, 1); // TODO: what's 1?
+	STORE_LE(sb->s_creator_os, EXT2_OS_LINUX);
+	STORE_LE(sb->s_checkinterval, 24*60*60 * 180); // 180 days
+	STORE_LE(sb->s_errors, EXT2_ERRORS_DEFAULT);
+	// mke2fs 1.41.9 also sets EXT3_FEATURE_COMPAT_RESIZE_INODE
+	// and if >= 0.5GB, EXT3_FEATURE_RO_COMPAT_LARGE_FILE.
+	// we use values which match "mke2fs -O ^resize_inode":
+	// in this case 1.41.9 never sets EXT3_FEATURE_RO_COMPAT_LARGE_FILE.
+	STORE_LE(sb->s_feature_compat, EXT2_FEATURE_COMPAT_SUPP
+		| (EXT2_FEATURE_COMPAT_RESIZE_INO * ENABLE_FEATURE_MKFS_EXT2_RESERVED_GDT)
+		| (EXT2_FEATURE_COMPAT_DIR_INDEX * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX)
+	);
+	STORE_LE(sb->s_feature_incompat, EXT2_FEATURE_INCOMPAT_FILETYPE);
+	STORE_LE(sb->s_feature_ro_compat, EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER);
+	STORE_LE(sb->s_flags, EXT2_FLAGS_UNSIGNED_HASH * ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX);
+	generate_uuid(sb->s_uuid);
+	if (ENABLE_FEATURE_MKFS_EXT2_DIR_INDEX) {
+		STORE_LE(sb->s_def_hash_version, EXT2_HASH_HALF_MD4);
+		generate_uuid((uint8_t *)sb->s_hash_seed);
+	}
+	/*
+	 * From e2fsprogs: add "jitter" to the superblock's check interval so that we
+	 * don't check all the filesystems at the same time.  We use a
+	 * kludgy hack of using the UUID to derive a random jitter value.
+	 */
+	STORE_LE(sb->s_max_mnt_count,
+		EXT2_DFL_MAX_MNT_COUNT
+		+ (sb->s_uuid[ARRAY_SIZE(sb->s_uuid)-1] % EXT2_DFL_MAX_MNT_COUNT));
+
+	// write the label
+	safe_strncpy((char *)sb->s_volume_name, label, sizeof(sb->s_volume_name));
+
+	// calculate filesystem skeleton structures
+	gd = xzalloc(group_desc_blocks * blocksize);
+	buf = xmalloc(blocksize);
+	sb->s_free_blocks_count = 0;
+	for (i = 0, pos = first_block, n = nblocks - first_block;
+		i < ngroups;
+		i++, pos += blocks_per_group, n -= blocks_per_group
+	) {
+		uint32_t overhead = pos + (has_super(i) ? (1/*sb*/ + group_desc_blocks) : 0);
+		uint32_t free_blocks;
+		// fill group descriptors
+		STORE_LE(gd[i].bg_block_bitmap, overhead + 0);
+		STORE_LE(gd[i].bg_inode_bitmap, overhead + 1);
+		STORE_LE(gd[i].bg_inode_table, overhead + 2);
+		overhead = overhead - pos + 1/*bbmp*/ + 1/*ibmp*/ + inode_table_blocks;
+		gd[i].bg_free_inodes_count = inodes_per_group;
+		//STORE_LE(gd[i].bg_used_dirs_count, 0);
+		// N.B. both "/" and "/lost+found" are within the first block group
+		// "/" occupies 1 block, "/lost+found" occupies lost_and_found_blocks...
+		if (0 == i) {
+			// ... thus increased overhead for the first block group ...
+			overhead += 1 + lost_and_found_blocks;
+			// ... and 2 used directories
+			STORE_LE(gd[i].bg_used_dirs_count, 2);
+			// well known reserved inodes belong to the first block too
+			gd[i].bg_free_inodes_count -= EXT2_GOOD_OLD_FIRST_INO;
+		}
+
+		// cache free block count of the group
+		free_blocks = (n < blocks_per_group ? n : blocks_per_group) - overhead;
+
+		// mark preallocated blocks as allocated
+//bb_info_msg("ALLOC: [%u][%u][%u]", blocksize, overhead, blocks_per_group - (free_blocks + overhead));
+		allocate(buf, blocksize,
+			// reserve "overhead" blocks
+			overhead,
+			// mark unused trailing blocks
+			blocks_per_group - (free_blocks + overhead)
+		);
+		// dump block bitmap
+		PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
+		STORE_LE(gd[i].bg_free_blocks_count, free_blocks);
+
+		// mark preallocated inodes as allocated
+		allocate(buf, blocksize,
+			// mark reserved inodes
+			inodes_per_group - gd[i].bg_free_inodes_count,
+			// mark unused trailing inodes
+			blocks_per_group - inodes_per_group
+		);
+		// dump inode bitmap
+		//PUT((uint64_t)(FETCH_LE32(gd[i].bg_block_bitmap)) * blocksize, buf, blocksize);
+		//but it's right after block bitmap, so we can just:
+		xwrite(fd, buf, blocksize);
+		STORE_LE(gd[i].bg_free_inodes_count, gd[i].bg_free_inodes_count);
+
+		// count overall free blocks
+		sb->s_free_blocks_count += free_blocks;
+	}
+	STORE_LE(sb->s_free_blocks_count, sb->s_free_blocks_count);
+
+	// dump filesystem skeleton structures
+//	printf("Writing superblocks and filesystem accounting information: ");
+	for (i = 0, pos = first_block; i < ngroups; i++, pos += blocks_per_group) {
+		// dump superblock and group descriptors and their backups
+		if (has_super(i)) {
+			// N.B. 1024 byte blocks are special
+			PUT(((uint64_t)pos * blocksize) + ((0 == i && 1024 != blocksize) ? 1024 : 0),
+					sb, 1024);
+			PUT(((uint64_t)pos * blocksize) + blocksize,
+					gd, group_desc_blocks * blocksize);
+		}
+	}
+
+	// zero boot sectors
+	memset(buf, 0, blocksize);
+	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)
+			PUT((uint64_t)(FETCH_LE32(gd[i].bg_inode_table) + n) * blocksize,
+				buf, blocksize);
+
+	// prepare directory inode
+	inode = (struct ext2_inode *)buf;
+	STORE_LE(inode->i_mode, S_IFDIR | S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH);
+	STORE_LE(inode->i_mtime, timestamp);
+	STORE_LE(inode->i_atime, timestamp);
+	STORE_LE(inode->i_ctime, timestamp);
+	STORE_LE(inode->i_size, blocksize);
+	// inode->i_blocks stores the number of 512 byte data blocks
+	// (512, because it goes directly to struct stat without scaling)
+	STORE_LE(inode->i_blocks, blocksize / 512);
+
+	// dump root dir inode
+	STORE_LE(inode->i_links_count, 3); // "/.", "/..", "/lost+found/.." point to this inode
+	STORE_LE(inode->i_block[0], FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks);
+	PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_ROOT_INO-1) * inodesize,
+				buf, inodesize);
+
+	// dump lost+found dir inode
+	STORE_LE(inode->i_links_count, 2); // both "/lost+found" and "/lost+found/." point to this inode
+	STORE_LE(inode->i_size, lost_and_found_blocks * blocksize);
+	STORE_LE(inode->i_blocks, (lost_and_found_blocks * blocksize) / 512);
+	n = FETCH_LE32(inode->i_block[0]) + 1;
+	for (i = 0; i < lost_and_found_blocks; ++i)
+		STORE_LE(inode->i_block[i], i + n); // use next block
+//bb_info_msg("LAST BLOCK USED[%u]", i + n);
+	PUT(((uint64_t)FETCH_LE32(gd[0].bg_inode_table) * blocksize) + (EXT2_GOOD_OLD_FIRST_INO-1) * inodesize,
+				buf, inodesize);
+
+	// dump directories
+	memset(buf, 0, blocksize);
+	dir = (struct ext2_dir *)buf;
+
+	// dump 2nd+ blocks of "/lost+found"
+	STORE_LE(dir->rec_len1, blocksize); // e2fsck 1.41.4 compat (1.41.9 does not need this)
+	for (i = 1; i < lost_and_found_blocks; ++i)
+		PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1+i) * blocksize,
+				buf, blocksize);
+
+	// dump 1st block of "/lost+found"
+	STORE_LE(dir->inode1, EXT2_GOOD_OLD_FIRST_INO);
+	STORE_LE(dir->rec_len1, 12);
+	STORE_LE(dir->name_len1, 1);
+	STORE_LE(dir->file_type1, EXT2_FT_DIR);
+	dir->name1[0] = '.';
+	STORE_LE(dir->inode2, EXT2_ROOT_INO);
+	STORE_LE(dir->rec_len2, blocksize - 12);
+	STORE_LE(dir->name_len2, 2);
+	STORE_LE(dir->file_type2, EXT2_FT_DIR);
+	dir->name2[0] = '.'; dir->name2[1] = '.';
+	PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 1) * blocksize, buf, blocksize);
+
+	// dump root dir block
+	STORE_LE(dir->inode1, EXT2_ROOT_INO);
+	STORE_LE(dir->rec_len2, 12);
+	STORE_LE(dir->inode3, EXT2_GOOD_OLD_FIRST_INO);
+	STORE_LE(dir->rec_len3, blocksize - 12 - 12);
+	STORE_LE(dir->name_len3, 10);
+	STORE_LE(dir->file_type3, EXT2_FT_DIR);
+	strcpy(dir->name3, "lost+found");
+	PUT((uint64_t)(FETCH_LE32(gd[0].bg_inode_table) + inode_table_blocks + 0) * blocksize, buf, blocksize);
+
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(buf);
+		free(gd);
+		free(sb);
+	}
+
+	xclose(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/mkfs_ext2.txt b/busybox-1.19.3/util-linux/mkfs_ext2.txt
new file mode 100644
index 0000000..273d5b6
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mkfs_ext2.txt
@@ -0,0 +1,77 @@
+Difference between bbox mke2fs and standard one (dumpe2fs comparison):
+
+[upd: inode size has been fixed since then]
+
+Two significant differences:
+- standard mke2fs has resize_inode feature and thus has reserved GDT blocks,
+  which decreases free blocks;
+- inode size: 128/256 (since 679807k is >0.5G, standard mke2fs uses "big" inodes)
+  this affects inode table in block groups
+
+Filesystem volume name:   <none>                                        Filesystem volume name:   <none>
+Last mounted on:          <not available>                               Last mounted on:          <not available>
+Filesystem UUID:          f4839760-c89b-44b7-ac52-08b4e326c0b4          Filesystem UUID:          71179558-8c8e-4a56-ad54-018008f5c358
+Filesystem magic number:  0xEF53                                        Filesystem magic number:  0xEF53
+Filesystem revision #:    1 (dynamic)                                   Filesystem revision #:    1 (dynamic)
+Filesystem features:      ext_attr dir_index filetype sparse_super      Filesystem features:      ext_attr resize_inode dir_index filetype sparse_super large_file
+Filesystem flags:         signed_directory_hash                         Filesystem flags:         signed_directory_hash
+Default mount options:    (none)                                        Default mount options:    (none)
+Filesystem state:         clean                                         Filesystem state:         clean
+Errors behavior:          Continue                                      Errors behavior:          Continue
+Filesystem OS type:       Linux                                         Filesystem OS type:       Linux
+Inode count:              42624                                         Inode count:              42528
+Block count:              169951                                        Block count:              169951
+Reserved block count:     8497                                          Reserved block count:     8497
+Free blocks:              168594                                        Free blocks:              167103
+Free inodes:              42613                                         Free inodes:              42517
+First block:              0                                             First block:              0
+Block size:               4096                                          Block size:               4096
+Fragment size:            4096                                          Fragment size:            4096
+                                                                        Reserved GDT blocks:      41
+Blocks per group:         32768                                         Blocks per group:         32768
+Fragments per group:      32768                                         Fragments per group:      32768
+Inodes per group:         7104                                          Inodes per group:         7088
+Inode blocks per group:   222                                           Inode blocks per group:   443
+Filesystem created:       Wed Oct 21 13:40:55 2009                      Filesystem created:       Wed Oct 21 13:40:55 2009
+Last mount time:          n/a                                           Last mount time:          n/a
+Last write time:          Wed Oct 21 13:40:55 2009                      Last write time:          Wed Oct 21 13:40:55 2009
+Mount count:              0                                             Mount count:              0
+Maximum mount count:      20                                            Maximum mount count:      37
+Last checked:             Wed Oct 21 13:40:55 2009                      Last checked:             Wed Oct 21 13:40:55 2009
+Check interval:           15552000 (6 months)                           Check interval:           15552000 (6 months)
+Next check after:         Mon Apr 19 13:40:55 2010                      Next check after:         Mon Apr 19 13:40:55 2010
+Reserved blocks uid:      0 (user root)                                 Reserved blocks uid:      0 (user root)
+Reserved blocks gid:      0 (group root)                                Reserved blocks gid:      0 (group root)
+First inode:              11                                            First inode:              11
+Inode size:               128                                           Inode size:               256
+                                                                        Required extra isize:     28
+                                                                        Desired extra isize:      28
+Default directory hash:   half_md4                                      Default directory hash:   half_md4
+Directory Hash Seed:      ff94d047-c9ca-4fe5-b553-937a76101a89          Directory Hash Seed:      c270fd43-9868-4a92-ae99-050098e12835
+
+
+Group 0: (Blocks 0-32767)                                               Group 0: (Blocks 0-32767)
+  Primary superblock at 0, Group descriptors at 1-1                       Primary superblock at 0, Group descriptors at 1-1
+                                                                          Reserved GDT blocks at 2-42
+  Block bitmap at 2 (+2), Inode bitmap at 3 (+3)                          Block bitmap at 43 (+43), Inode bitmap at 44 (+44)
+  Inode table at 4-225 (+4)                                               Inode table at 45-487 (+45)
+  32537 free blocks, 7093 free inodes, 2 directories                      32274 free blocks, 7077 free inodes, 2 directories
+  Free blocks: 231-32767                                                  Free blocks: 494-32767
+  Free inodes: 12-7104                                                    Free inodes: 12-7088
+
+Group 1: (Blocks 32768-65535)                                           Group 1: (Blocks 32768-65535)
+  Backup superblock at 32768, Group descriptors at 32769-32769            Backup superblock at 32768, Group descriptors at 32769-32769
+                                                                          Reserved GDT blocks at 32770-32810
+  Block bitmap at 32770 (+2), Inode bitmap at 32771 (+3)                  Block bitmap at 32811 (+43), Inode bitmap at 32812 (+44)
+  Inode table at 32772-32993 (+4)                                         Inode table at 32813-33255 (+45)
+  32542 free blocks, 7104 free inodes, 0 directories                      32280 free blocks, 7088 free inodes, 0 directories
+  Free blocks: 32994-65535                                                Free blocks: 33256-65535
+  Free inodes: 7105-14208                                                 Free inodes: 7089-14176
+
+Group 2: (Blocks 65536-98303)                                           Group 2: (Blocks 65536-98303)
+  Block bitmap at 65536 (+0), Inode bitmap at 65537 (+1)                  Block bitmap at 65536 (+0), Inode bitmap at 65537 (+1)
+  Inode table at 65538-65759 (+2)                                         Inode table at 65538-65980 (+2)
+  32544 free blocks, 7104 free inodes, 0 directories                      32323 free blocks, 7088 free inodes, 0 directories
+  Free blocks: 65760-98303                                                Free blocks: 65981-98303
+  Free inodes: 14209-21312                                                Free inodes: 14177-21264
+...
diff --git a/busybox-1.19.3/util-linux/mkfs_ext2_test.sh b/busybox-1.19.3/util-linux/mkfs_ext2_test.sh
new file mode 100755
index 0000000..f5347cc
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mkfs_ext2_test.sh
@@ -0,0 +1,104 @@
+#!/bin/sh
+
+# Disabling features we do not match exactly:
+system_mke2fs='/sbin/mke2fs -O ^resize_inode'
+bbox_mke2fs='./busybox mke2fs'
+
+gen_image() { # params: mke2fs_invocation image_name
+    >$2
+    dd seek=$((kilobytes-1)) bs=1K count=1 </dev/zero of=$2 >/dev/null 2>&1 || exit 1
+    $1 -F $2 $kilobytes >$2.raw_out 2>&1 || return 1
+    cat $2.raw_out \
+    | grep -v '^mke2fs [0-9]*\.[0-9]*\.[0-9]* ' \
+    | grep -v '^Maximum filesystem' \
+    | grep -v '^Writing inode tables' \
+    | grep -v '^Writing superblocks and filesystem accounting information' \
+    | grep -v '^This filesystem will be automatically checked every' \
+    | grep -v '^180 days, whichever comes first' \
+    | sed 's/blocks* unused./blocks unused/' \
+    | sed 's/block groups*/block groups/' \
+    | sed 's/ *$//' \
+    | sed 's/blocks (.*%) reserved/blocks reserved/' \
+    | grep -v '^$' \
+    >$2.out
+}
+
+test_mke2fs() {
+    echo Testing $kilobytes
+
+    gen_image "$system_mke2fs" image_std || return 1
+    gen_image "$bbox_mke2fs"   image_bb  || return 1
+
+    diff -ua image_bb.out image_std.out >image.out.diff || {
+	cat image.out.diff
+	return 1
+    }
+
+    e2fsck -f -n image_bb >image_bb_e2fsck.out 2>&1 || {
+	echo "e2fsck error on image_bb"
+	cat image_bb_e2fsck.out
+	exit 1
+    }
+}
+
+# -:bbox +:standard
+
+# kilobytes=60 is the minimal allowed size
+kilobytes=60
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = 200 && break
+done
+
+# Transition from one block group to two
+# fails in [8378..8410] range unless -O ^resize_inode
+kilobytes=$((1 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((1 * 8*1024 + 300)) && break
+done
+
+# Transition from 2 block groups to 3
+# works
+kilobytes=$((2 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((2 * 8*1024 + 400)) && break
+done
+
+# Transition from 3 block groups to 4
+# fails in [24825..24922] range unless -O ^resize_inode
+kilobytes=$((3 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((3 * 8*1024 + 500)) && break
+done
+
+# Transition from 4 block groups to 5
+# works
+kilobytes=$((4 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((4 * 8*1024 + 600)) && break
+done
+
+# Transition from 5 block groups to 6
+# fails in [41230..41391] range unless -O ^resize_inode
+kilobytes=$((5 * 8*1024 - 50))
+while true; do
+    test_mke2fs || exit 1
+    : $((kilobytes++))
+    test $kilobytes = $((5 * 8*1024 + 700)) && break
+done
+exit
+
+# Random sizes
+while true; do
+    kilobytes=$(( (RANDOM*RANDOM) % 5000000 + 60))
+    test_mke2fs || exit 1
+done
diff --git a/busybox-1.19.3/util-linux/mkfs_minix.c b/busybox-1.19.3/util-linux/mkfs_minix.c
new file mode 100644
index 0000000..59d7d23
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mkfs_minix.c
@@ -0,0 +1,740 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs.c - make a linux (minix) file-system.
+ *
+ * (C) 1991 Linus Torvalds.
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+/*
+ * DD.MM.YY
+ *
+ * 24.11.91  -	Time began. Used the fsck sources to get started.
+ *
+ * 25.11.91  -	Corrected some bugs. Added support for ".badblocks"
+ *		The algorithm for ".badblocks" is a bit weird, but
+ *		it should work. Oh, well.
+ *
+ * 25.01.92  -	Added the -l option for getting the list of bad blocks
+ *		out of a named file. (Dave Rivers, rivers@ponds.uucp)
+ *
+ * 28.02.92  -	Added %-information when using -c.
+ *
+ * 28.02.93  -	Added support for other namelengths than the original
+ *		14 characters so that I can test the new kernel routines..
+ *
+ * 09.10.93  -	Make exit status conform to that required by fsutil
+ *		(Rik Faith, faith@cs.unc.edu)
+ *
+ * 31.10.93  -	Added inode request feature, for backup floppies: use
+ *		32 inodes, for a news partition use more.
+ *		(Scott Heavner, sdh@po.cwru.edu)
+ *
+ * 03.01.94  -	Added support for file system valid flag.
+ *		(Dr. Wettstein, greg%wind.uucp@plains.nodak.edu)
+ *
+ * 30.10.94  -  added support for v2 filesystem
+ *	        (Andreas Schwab, schwab@issan.informatik.uni-dortmund.de)
+ *
+ * 09.11.94  -	Added test to prevent overwrite of mounted fs adapted
+ *		from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs
+ *		program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 03.20.95  -	Clear first 512 bytes of filesystem to make certain that
+ *		the filesystem is not misidentified as a MS-DOS FAT filesystem.
+ *		(Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * 02.07.96  -  Added small patch from Russell King to make the program a
+ *		good deal more portable (janl@math.uio.no)
+ *
+ * Usage:  mkfs [-c | -l filename ] [-v] [-nXX] [-iXX] device [size-in-blocks]
+ *
+ *	-c for readability checking (SLOW!)
+ *      -l for getting a list of bad blocks from a file.
+ *	-n for namelength (currently the kernel only uses 14 or 30)
+ *	-i for number of inodes
+ *	-v for v2 filesystem
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Modified for BusyBox by Erik Andersen <andersen@debian.org> --
+ *	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>
+
+#include "minix.h"
+
+/* Store the very same times/uids/gids for image consistency */
+#if 1
+# define CUR_TIME 0
+# define GETUID 0
+# define GETGID 0
+#else
+/* Was using this. Is it useful? NB: this will break testsuite */
+# define CUR_TIME time(NULL)
+# define GETUID getuid()
+# define GETGID getgid()
+#endif
+
+enum {
+	MAX_GOOD_BLOCKS         = 512,
+	TEST_BUFFER_BLOCKS      = 16,
+};
+
+#if !ENABLE_FEATURE_MINIX2
+enum { version2 = 0 };
+#endif
+
+enum { dev_fd = 3 };
+
+struct globals {
+#if ENABLE_FEATURE_MINIX2
+	smallint version2;
+#define version2 G.version2
+#endif
+	char *device_name;
+	uint32_t total_blocks;
+	int badblocks;
+	int namelen;
+	int dirsize;
+	int magic;
+	char *inode_buffer;
+	char *inode_map;
+	char *zone_map;
+	int used_good_blocks;
+	unsigned long req_nr_inodes;
+	unsigned currently_testing;
+
+	char root_block[BLOCK_SIZE];
+	char superblock_buffer[BLOCK_SIZE];
+	char boot_block_buffer[512];
+	unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+	/* check_blocks(): buffer[] was the biggest static in entire bbox */
+	char check_blocks_buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+
+	unsigned short ind_block1[BLOCK_SIZE >> 1];
+	unsigned short dind_block1[BLOCK_SIZE >> 1];
+	unsigned long ind_block2[BLOCK_SIZE >> 2];
+	unsigned long dind_block2[BLOCK_SIZE >> 2];
+};
+#define G (*ptr_to_globals)
+#define INIT_G() do { \
+	SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
+} while (0)
+
+static ALWAYS_INLINE unsigned div_roundup(unsigned size, unsigned n)
+{
+	return (size + n-1) / n;
+}
+
+#define INODE_BUF1              (((struct minix1_inode*)G.inode_buffer) - 1)
+#define INODE_BUF2              (((struct minix2_inode*)G.inode_buffer) - 1)
+
+#define SB                      (*(struct minix_superblock*)G.superblock_buffer)
+
+#define SB_INODES               (SB.s_ninodes)
+#define SB_IMAPS                (SB.s_imap_blocks)
+#define SB_ZMAPS                (SB.s_zmap_blocks)
+#define SB_FIRSTZONE            (SB.s_firstdatazone)
+#define SB_ZONE_SIZE            (SB.s_log_zone_size)
+#define SB_MAXSIZE              (SB.s_max_size)
+#define SB_MAGIC                (SB.s_magic)
+
+#if !ENABLE_FEATURE_MINIX2
+# define SB_ZONES               (SB.s_nzones)
+# define INODE_BLOCKS           div_roundup(SB_INODES, MINIX1_INODES_PER_BLOCK)
+#else
+# define SB_ZONES               (version2 ? SB.s_zones : SB.s_nzones)
+# define INODE_BLOCKS           div_roundup(SB_INODES, \
+                                (version2 ? MINIX2_INODES_PER_BLOCK : MINIX1_INODES_PER_BLOCK))
+#endif
+
+#define INODE_BUFFER_SIZE       (INODE_BLOCKS * BLOCK_SIZE)
+#define NORM_FIRSTZONE          (2 + SB_IMAPS + SB_ZMAPS + INODE_BLOCKS)
+
+/* Before you ask "where they come from?": */
+/* setbit/clrbit are supplied by sys/param.h */
+
+static int minix_bit(const char* a, unsigned i)
+{
+	return a[i >> 3] & (1<<(i & 7));
+}
+
+static void minix_setbit(char *a, unsigned i)
+{
+	setbit(a, i);
+}
+static void minix_clrbit(char *a, unsigned i)
+{
+	clrbit(a, i);
+}
+
+/* Note: do not assume 0/1, it is 0/nonzero */
+#define zone_in_use(x)  minix_bit(G.zone_map,(x)-SB_FIRSTZONE+1)
+/*#define inode_in_use(x) minix_bit(G.inode_map,(x))*/
+
+#define mark_inode(x)   minix_setbit(G.inode_map,(x))
+#define unmark_inode(x) minix_clrbit(G.inode_map,(x))
+#define mark_zone(x)    minix_setbit(G.zone_map,(x)-SB_FIRSTZONE+1)
+#define unmark_zone(x)  minix_clrbit(G.zone_map,(x)-SB_FIRSTZONE+1)
+
+#ifndef BLKGETSIZE
+# 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. */
+	SB.s_state |= MINIX_VALID_FS;
+	SB.s_state &= ~MINIX_ERROR_FS;
+
+	msg_eol = "seek to 0 failed";
+	xlseek(dev_fd, 0, SEEK_SET);
+
+	msg_eol = "can't clear boot sector";
+	xwrite(dev_fd, G.boot_block_buffer, 512);
+
+	msg_eol = "seek to BLOCK_SIZE failed";
+	xlseek(dev_fd, BLOCK_SIZE, SEEK_SET);
+
+	msg_eol = "can't write superblock";
+	xwrite(dev_fd, G.superblock_buffer, BLOCK_SIZE);
+
+	msg_eol = "can't write inode map";
+	xwrite(dev_fd, G.inode_map, SB_IMAPS * BLOCK_SIZE);
+
+	msg_eol = "can't write zone map";
+	xwrite(dev_fd, G.zone_map, SB_ZMAPS * BLOCK_SIZE);
+
+	msg_eol = "can't write inodes";
+	xwrite(dev_fd, G.inode_buffer, INODE_BUFFER_SIZE);
+
+	msg_eol = "\n";
+}
+
+static void write_block(int blk, char *buffer)
+{
+	xlseek(dev_fd, blk * BLOCK_SIZE, SEEK_SET);
+	xwrite(dev_fd, buffer, BLOCK_SIZE);
+}
+
+static int get_free_block(void)
+{
+	int blk;
+
+	if (G.used_good_blocks + 1 >= MAX_GOOD_BLOCKS)
+		bb_error_msg_and_die("too many bad blocks");
+	if (G.used_good_blocks)
+		blk = G.good_blocks_table[G.used_good_blocks - 1] + 1;
+	else
+		blk = SB_FIRSTZONE;
+	while (blk < SB_ZONES && zone_in_use(blk))
+		blk++;
+	if (blk >= SB_ZONES)
+		bb_error_msg_and_die("not enough good blocks");
+	G.good_blocks_table[G.used_good_blocks] = blk;
+	G.used_good_blocks++;
+	return blk;
+}
+
+static void mark_good_blocks(void)
+{
+	int blk;
+
+	for (blk = 0; blk < G.used_good_blocks; blk++)
+		mark_zone(G.good_blocks_table[blk]);
+}
+
+static int next(int zone)
+{
+	if (!zone)
+		zone = SB_FIRSTZONE - 1;
+	while (++zone < SB_ZONES)
+		if (zone_in_use(zone))
+			return zone;
+	return 0;
+}
+
+static void make_bad_inode(void)
+{
+	struct minix1_inode *inode = &INODE_BUF1[MINIX_BAD_INO];
+	int i, j, zone;
+	int ind = 0, dind = 0;
+	/* moved to globals to reduce stack usage
+	unsigned short ind_block[BLOCK_SIZE >> 1];
+	unsigned short dind_block[BLOCK_SIZE >> 1];
+	*/
+#define ind_block (G.ind_block1)
+#define dind_block (G.dind_block1)
+
+#define NEXT_BAD (zone = next(zone))
+
+	if (!G.badblocks)
+		return;
+	mark_inode(MINIX_BAD_INO);
+	inode->i_nlinks = 1;
+	/* BTW, setting this makes all images different */
+	/* it's harder to check for bugs then - diff isn't helpful :(... */
+	inode->i_time = CUR_TIME;
+	inode->i_mode = S_IFREG + 0000;
+	inode->i_size = G.badblocks * BLOCK_SIZE;
+	zone = next(0);
+	for (i = 0; i < 7; i++) {
+		inode->i_zone[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[7] = ind = get_free_block();
+	memset(ind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 512; i++) {
+		ind_block[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[8] = dind = get_free_block();
+	memset(dind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 512; i++) {
+		write_block(ind, (char *) ind_block);
+		dind_block[i] = ind = get_free_block();
+		memset(ind_block, 0, BLOCK_SIZE);
+		for (j = 0; j < 512; j++) {
+			ind_block[j] = zone;
+			if (!NEXT_BAD)
+				goto end_bad;
+		}
+	}
+	bb_error_msg_and_die("too many bad blocks");
+ end_bad:
+	if (ind)
+		write_block(ind, (char *) ind_block);
+	if (dind)
+		write_block(dind, (char *) dind_block);
+#undef ind_block
+#undef dind_block
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void make_bad_inode2(void)
+{
+	struct minix2_inode *inode = &INODE_BUF2[MINIX_BAD_INO];
+	int i, j, zone;
+	int ind = 0, dind = 0;
+	/* moved to globals to reduce stack usage
+	unsigned long ind_block[BLOCK_SIZE >> 2];
+	unsigned long dind_block[BLOCK_SIZE >> 2];
+	*/
+#define ind_block (G.ind_block2)
+#define dind_block (G.dind_block2)
+
+	if (!G.badblocks)
+		return;
+	mark_inode(MINIX_BAD_INO);
+	inode->i_nlinks = 1;
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME;
+	inode->i_mode = S_IFREG + 0000;
+	inode->i_size = G.badblocks * BLOCK_SIZE;
+	zone = next(0);
+	for (i = 0; i < 7; i++) {
+		inode->i_zone[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[7] = ind = get_free_block();
+	memset(ind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 256; i++) {
+		ind_block[i] = zone;
+		if (!NEXT_BAD)
+			goto end_bad;
+	}
+	inode->i_zone[8] = dind = get_free_block();
+	memset(dind_block, 0, BLOCK_SIZE);
+	for (i = 0; i < 256; i++) {
+		write_block(ind, (char *) ind_block);
+		dind_block[i] = ind = get_free_block();
+		memset(ind_block, 0, BLOCK_SIZE);
+		for (j = 0; j < 256; j++) {
+			ind_block[j] = zone;
+			if (!NEXT_BAD)
+				goto end_bad;
+		}
+	}
+	/* Could make triple indirect block here */
+	bb_error_msg_and_die("too many bad blocks");
+ end_bad:
+	if (ind)
+		write_block(ind, (char *) ind_block);
+	if (dind)
+		write_block(dind, (char *) dind_block);
+#undef ind_block
+#undef dind_block
+}
+#else
+void make_bad_inode2(void);
+#endif
+
+static void make_root_inode(void)
+{
+	struct minix1_inode *inode = &INODE_BUF1[MINIX_ROOT_INO];
+
+	mark_inode(MINIX_ROOT_INO);
+	inode->i_zone[0] = get_free_block();
+	inode->i_nlinks = 2;
+	inode->i_time = CUR_TIME;
+	if (G.badblocks)
+		inode->i_size = 3 * G.dirsize;
+	else {
+		G.root_block[2 * G.dirsize] = '\0';
+		G.root_block[2 * G.dirsize + 1] = '\0';
+		inode->i_size = 2 * G.dirsize;
+	}
+	inode->i_mode = S_IFDIR + 0755;
+	inode->i_uid = GETUID;
+	if (inode->i_uid)
+		inode->i_gid = GETGID;
+	write_block(inode->i_zone[0], G.root_block);
+}
+
+#if ENABLE_FEATURE_MINIX2
+static void make_root_inode2(void)
+{
+	struct minix2_inode *inode = &INODE_BUF2[MINIX_ROOT_INO];
+
+	mark_inode(MINIX_ROOT_INO);
+	inode->i_zone[0] = get_free_block();
+	inode->i_nlinks = 2;
+	inode->i_atime = inode->i_mtime = inode->i_ctime = CUR_TIME;
+	if (G.badblocks)
+		inode->i_size = 3 * G.dirsize;
+	else {
+		G.root_block[2 * G.dirsize] = '\0';
+		G.root_block[2 * G.dirsize + 1] = '\0';
+		inode->i_size = 2 * G.dirsize;
+	}
+	inode->i_mode = S_IFDIR + 0755;
+	inode->i_uid = GETUID;
+	if (inode->i_uid)
+		inode->i_gid = GETGID;
+	write_block(inode->i_zone[0], G.root_block);
+}
+#else
+void make_root_inode2(void);
+#endif
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable.
+ */
+static size_t do_check(char *buffer, size_t try, unsigned current_block)
+{
+	ssize_t got;
+
+	/* Seek to the correct loc. */
+	msg_eol = "seek failed during testing of blocks";
+	xlseek(dev_fd, current_block * BLOCK_SIZE, SEEK_SET);
+	msg_eol = "\n";
+
+	/* Try the read */
+	got = read(dev_fd, buffer, try * BLOCK_SIZE);
+	if (got < 0)
+		got = 0;
+	try = ((size_t)got) / BLOCK_SIZE;
+
+	if (got & (BLOCK_SIZE - 1))
+		fprintf(stderr, "Short read at block %u\n", (unsigned)(current_block + try));
+	return try;
+}
+
+static void alarm_intr(int alnum UNUSED_PARAM)
+{
+	if (G.currently_testing >= SB_ZONES)
+		return;
+	signal(SIGALRM, alarm_intr);
+	alarm(5);
+	if (!G.currently_testing)
+		return;
+	printf("%d ...", G.currently_testing);
+	fflush_all();
+}
+
+static void check_blocks(void)
+{
+	size_t try, got;
+
+	G.currently_testing = 0;
+	signal(SIGALRM, alarm_intr);
+	alarm(5);
+	while (G.currently_testing < SB_ZONES) {
+		msg_eol = "seek failed in check_blocks";
+		xlseek(dev_fd, G.currently_testing * BLOCK_SIZE, SEEK_SET);
+		msg_eol = "\n";
+		try = TEST_BUFFER_BLOCKS;
+		if (G.currently_testing + try > SB_ZONES)
+			try = SB_ZONES - G.currently_testing;
+		got = do_check(G.check_blocks_buffer, try, G.currently_testing);
+		G.currently_testing += got;
+		if (got == try)
+			continue;
+		if (G.currently_testing < SB_FIRSTZONE)
+			bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
+		mark_zone(G.currently_testing);
+		G.badblocks++;
+		G.currently_testing++;
+	}
+	alarm(0);
+	printf("%d bad block(s)\n", G.badblocks);
+}
+
+static void get_list_blocks(char *filename)
+{
+	FILE *listfile;
+	unsigned long blockno;
+
+	listfile = xfopen_for_read(filename);
+	while (!feof(listfile)) {
+		fscanf(listfile, "%ld\n", &blockno);
+		mark_zone(blockno);
+		G.badblocks++;
+	}
+	printf("%d bad block(s)\n", G.badblocks);
+}
+
+static void setup_tables(void)
+{
+	unsigned long inodes;
+	unsigned norm_firstzone;
+	unsigned sb_zmaps;
+	unsigned i;
+
+	/* memset(G.superblock_buffer, 0, BLOCK_SIZE); */
+	/* memset(G.boot_block_buffer, 0, 512); */
+	SB_MAGIC = G.magic;
+	SB_ZONE_SIZE = 0;
+	SB_MAXSIZE = version2 ? 0x7fffffff : (7 + 512 + 512 * 512) * 1024;
+	if (version2)
+		SB.s_zones = G.total_blocks;
+	else
+		SB.s_nzones = G.total_blocks;
+
+	/* some magic nrs: 1 inode / 3 blocks */
+	if (G.req_nr_inodes == 0)
+		inodes = G.total_blocks / 3;
+	else
+		inodes = G.req_nr_inodes;
+	/* Round up inode count to fill block size */
+	if (version2)
+		inodes = (inodes + MINIX2_INODES_PER_BLOCK - 1) &
+		                 ~(MINIX2_INODES_PER_BLOCK - 1);
+	else
+		inodes = (inodes + MINIX1_INODES_PER_BLOCK - 1) &
+		                 ~(MINIX1_INODES_PER_BLOCK - 1);
+	if (inodes > 65535)
+		inodes = 65535;
+	SB_INODES = inodes;
+	SB_IMAPS = div_roundup(SB_INODES + 1, BITS_PER_BLOCK);
+
+	/* Real bad hack but overwise mkfs.minix can be thrown
+	 * in infinite loop...
+	 * try:
+	 * dd if=/dev/zero of=test.fs count=10 bs=1024
+	 * mkfs.minix -i 200 test.fs
+	 */
+	/* This code is not insane: NORM_FIRSTZONE is not a constant,
+	 * it is calculated from SB_INODES, SB_IMAPS and SB_ZMAPS */
+	i = 999;
+	SB_ZMAPS = 0;
+	do {
+		norm_firstzone = NORM_FIRSTZONE;
+		sb_zmaps = div_roundup(G.total_blocks - norm_firstzone + 1, BITS_PER_BLOCK);
+		if (SB_ZMAPS == sb_zmaps) goto got_it;
+		SB_ZMAPS = sb_zmaps;
+		/* new SB_ZMAPS, need to recalc NORM_FIRSTZONE */
+	} while (--i);
+	bb_error_msg_and_die("incompatible size/inode count, try different -i N");
+ got_it:
+
+	SB_FIRSTZONE = norm_firstzone;
+	G.inode_map = xmalloc(SB_IMAPS * BLOCK_SIZE);
+	G.zone_map = xmalloc(SB_ZMAPS * BLOCK_SIZE);
+	memset(G.inode_map, 0xff, SB_IMAPS * BLOCK_SIZE);
+	memset(G.zone_map, 0xff, SB_ZMAPS * BLOCK_SIZE);
+	for (i = SB_FIRSTZONE; i < SB_ZONES; i++)
+		unmark_zone(i);
+	for (i = MINIX_ROOT_INO; i <= SB_INODES; i++)
+		unmark_inode(i);
+	G.inode_buffer = xzalloc(INODE_BUFFER_SIZE);
+	printf("%ld inodes\n", (long)SB_INODES);
+	printf("%ld blocks\n", (long)SB_ZONES);
+	printf("Firstdatazone=%ld (%ld)\n", (long)SB_FIRSTZONE, (long)norm_firstzone);
+	printf("Zonesize=%d\n", BLOCK_SIZE << SB_ZONE_SIZE);
+	printf("Maxsize=%ld\n", (long)SB_MAXSIZE);
+}
+
+int mkfs_minix_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_minix_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned opt;
+	char *tmp;
+	struct stat statbuf;
+	char *str_i;
+	char *listfile = NULL;
+
+	INIT_G();
+/* default (changed to 30, per Linus's suggestion, Sun Nov 21 08:05:07 1993) */
+	G.namelen = 30;
+	G.dirsize = 32;
+	G.magic = MINIX1_SUPER_MAGIC2;
+
+	if (INODE_SIZE1 * MINIX1_INODES_PER_BLOCK != BLOCK_SIZE)
+		bb_error_msg_and_die("bad inode size");
+#if ENABLE_FEATURE_MINIX2
+	if (INODE_SIZE2 * MINIX2_INODES_PER_BLOCK != BLOCK_SIZE)
+		bb_error_msg_and_die("bad inode size");
+#endif
+
+	opt_complementary = "n+"; /* -n N */
+	opt = getopt32(argv, "ci:l:n:v", &str_i, &listfile, &G.namelen);
+	argv += optind;
+	//if (opt & 1) -c
+	if (opt & 2) G.req_nr_inodes = xatoul(str_i); // -i
+	//if (opt & 4) -l
+	if (opt & 8) { // -n
+		if (G.namelen == 14) G.magic = MINIX1_SUPER_MAGIC;
+		else if (G.namelen == 30) G.magic = MINIX1_SUPER_MAGIC2;
+		else bb_show_usage();
+		G.dirsize = G.namelen + 2;
+	}
+	if (opt & 0x10) { // -v
+#if ENABLE_FEATURE_MINIX2
+		version2 = 1;
+#else
+		bb_error_msg_and_die("not compiled with minix v2 support");
+#endif
+	}
+
+	G.device_name = *argv++;
+	if (!G.device_name)
+		bb_show_usage();
+	if (*argv)
+		G.total_blocks = xatou32(*argv);
+	else
+		G.total_blocks = get_size(G.device_name) / 1024;
+
+	if (G.total_blocks < 10)
+		bb_error_msg_and_die("must have at least 10 blocks");
+
+	if (version2) {
+		G.magic = MINIX2_SUPER_MAGIC2;
+		if (G.namelen == 14)
+			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);
+	xfstat(dev_fd, &statbuf, G.device_name);
+	if (!S_ISBLK(statbuf.st_mode))
+		opt &= ~1; // clear -c (check)
+
+/* 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
+
+	tmp = G.root_block;
+	*(short *) tmp = 1;
+	strcpy(tmp + 2, ".");
+	tmp += G.dirsize;
+	*(short *) tmp = 1;
+	strcpy(tmp + 2, "..");
+	tmp += G.dirsize;
+	*(short *) tmp = 2;
+	strcpy(tmp + 2, ".badblocks");
+
+	setup_tables();
+
+	if (opt & 1) // -c ?
+		check_blocks();
+	else if (listfile)
+		get_list_blocks(listfile);
+
+	if (version2) {
+		make_root_inode2();
+		make_bad_inode2();
+	} else {
+		make_root_inode();
+		make_bad_inode();
+	}
+
+	mark_good_blocks();
+	write_tables();
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/mkfs_reiser.c b/busybox-1.19.3/util-linux/mkfs_reiser.c
new file mode 100644
index 0000000..b4efb9e
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mkfs_reiser.c
@@ -0,0 +1,375 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs_reiser: utility to create ReiserFS filesystem
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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>
+
+char BUG_wrong_field_size(void);
+#define STORE_LE(field, value) \
+do { \
+	if (sizeof(field) == 4) \
+		field = SWAP_LE32(value); \
+	else if (sizeof(field) == 2) \
+		field = SWAP_LE16(value); \
+	else if (sizeof(field) == 1) \
+		field = (value); \
+	else \
+		BUG_wrong_field_size(); \
+} while (0)
+
+#define FETCH_LE32(field) \
+	(sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())
+
+struct journal_params {
+	uint32_t jp_journal_1st_block;      /* where does journal start from on its device */
+	uint32_t jp_journal_dev;            /* journal device st_rdev */
+	uint32_t jp_journal_size;           /* size of the journal on FS creation. used to make sure they don't overflow it */
+	uint32_t jp_journal_trans_max;      /* max number of blocks in a transaction.  */
+	uint32_t jp_journal_magic;          /* random value made on fs creation (this was sb_journal_block_count) */
+	uint32_t jp_journal_max_batch;      /* max number of blocks to batch into a trans */
+	uint32_t jp_journal_max_commit_age; /* in seconds, how old can an async commit be */
+	uint32_t jp_journal_max_trans_age;  /* in seconds, how old can a transaction be */
+};
+
+struct reiserfs_journal_header {
+	uint32_t jh_last_flush_trans_id;    /* id of last fully flushed transaction */
+	uint32_t jh_first_unflushed_offset; /* offset in the log of where to start replay after a crash */
+	uint32_t jh_mount_id;
+	struct journal_params jh_journal;
+	uint32_t jh_last_check_mount_id;    /* the mount id of the fs during the last reiserfsck --check. */
+};
+
+struct reiserfs_super_block {
+	uint32_t sb_block_count;            /* 0 number of block on data device */
+	uint32_t sb_free_blocks;            /* 4 free blocks count */
+	uint32_t sb_root_block;             /* 8 root of the tree */
+
+	struct journal_params sb_journal;   /* 12 */
+
+	uint16_t sb_blocksize;          /* 44 */
+	uint16_t sb_oid_maxsize;        /* 46 max size of object id array, see get_objectid() commentary */
+	uint16_t sb_oid_cursize;        /* 48 current size of object id array */
+	uint16_t sb_umount_state;       /* 50 this is set to 1 when filesystem was umounted, to 2 - when not */
+
+	char s_magic[10];               /* 52 "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */
+	uint16_t sb_fs_state;           /* 62 it is set to used by fsck to mark which phase of rebuilding is done (used for fsck debugging) */
+	uint32_t sb_hash_function_code; /* 64 code of fuction which was/is/will be used to sort names in a directory. See codes in above */
+	uint16_t sb_tree_height;        /* 68 height of filesytem tree. Tree consisting of only one root block has 2 here */
+	uint16_t sb_bmap_nr;            /* 70 amount of bitmap blocks needed to address each block of file system */
+	uint16_t sb_version;            /* 72 this field is only reliable on filesystem with non-standard journal */
+	uint16_t sb_reserved_for_journal;  /* 74 size in blocks of journal area on main device, we need to keep after non-standard journal relocation */
+	uint32_t sb_inode_generation;   /* 76 */
+	uint32_t sb_flags;              /* 80 Right now used only by inode-attributes, if enabled */
+	unsigned char s_uuid[16];       /* 84 filesystem unique identifier */
+	unsigned char s_label[16];      /* 100 filesystem volume label */
+	uint16_t sb_mnt_count;          /* 116 */
+	uint16_t sb_max_mnt_count;      /* 118 */
+	uint32_t sb_lastcheck;          /* 120 */
+	uint32_t sb_check_interval;     /* 124 */
+/* zero filled by mkreiserfs and reiserfs_convert_objectid_map_v1() so any additions must be updated there as well. */
+	char s_unused[76];              /* 128 */
+	/* 204 */
+};
+
+/* Header of a disk block.  More precisely, header of a formatted leaf
+   or internal node, and not the header of an unformatted node. */
+struct block_head {
+	uint16_t blk2_level;        /* Level of a block in the tree. */
+	uint16_t blk2_nr_item;      /* Number of keys/items in a block. */
+	uint16_t blk2_free_space;   /* Block free space in bytes. */
+	uint16_t blk_reserved;
+	uint32_t reserved[4];
+};
+
+#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)
+
+#define REISERFS_3_6_SUPER_MAGIC_STRING "ReIsEr2Fs"
+#define REISERFS_FORMAT_3_6     2
+#define DEFAULT_MAX_MNT_COUNT   30                      /* 30 mounts */
+#define DEFAULT_CHECK_INTERVAL  (180 * 60 * 60 * 24)    /* 180 days */
+
+#define FS_CLEANLY_UMOUNTED     1 /* this was REISERFS_VALID_FS */
+
+#define JOURNAL_MIN_SIZE        512
+/* biggest possible single transaction, don't change for now (8/3/99) */
+#define JOURNAL_TRANS_MAX       1024
+#define JOURNAL_TRANS_MIN       256     /* need to check whether it works */
+#define JOURNAL_DEFAULT_RATIO   8       /* default journal size / max trans length */
+#define JOURNAL_MIN_RATIO       2
+/* max blocks to batch into one transaction, don't make this any bigger than 900 */
+#define JOURNAL_MAX_BATCH       900
+#define JOURNAL_MAX_COMMIT_AGE  30
+
+
+// Standard mkreiserfs 3.6.21:
+//   -b | --block-size N              size of file-system block, in bytes
+//   -j | --journal-device FILE       path to separate device to hold journal
+//   -s | --journal-size N            size of the journal in blocks
+//   -o | --journal-offset N          offset of the journal from the start of
+//                                    the separate device, in blocks
+//   -t | --transaction-max-size N    maximal size of transaction, in blocks
+//   -B | --badblocks file            store all bad blocks given in file on the fs
+//   -h | --hash rupasov|tea|r5       hash function to use by default
+//   -u | --uuid UUID                 store UUID in the superblock
+//   -l | --label LABEL               store LABEL in the superblock
+//   --format 3.5|3.6                 old 3.5 format or newer 3.6
+//   -f | --force                     specified once, make mkreiserfs the whole
+//                                    disk, not block device or mounted partition;
+//                                    specified twice, do not ask for confirmation
+//   -q | --quiet                     quiet work without messages, progress and
+//                                    questions. Useful if run in a script. For use
+//                                    by end users only.
+//   -d | --debug                     print debugging information during mkreiser
+//   -V                               print version and exit
+
+// Options not commented below are taken but silently ignored:
+enum {
+	OPT_b = 1 << 0,
+	OPT_j = 1 << 1,
+	OPT_s = 1 << 2,
+	OPT_o = 1 << 3,
+	OPT_t = 1 << 4,
+	OPT_B = 1 << 5,
+	OPT_h = 1 << 6,
+	OPT_u = 1 << 7,
+	OPT_l = 1 << 8,		// label
+	OPT_f = 1 << 9,		// ask no questions
+	OPT_q = 1 << 10,
+	OPT_d = 1 << 11,
+	//OPT_V = 1 << 12,	// -V version. bbox applets don't support that
+};
+
+int mkfs_reiser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv)
+{
+	unsigned blocksize = 4096;
+	unsigned journal_blocks = 8192;
+	unsigned blocks, bitmap_blocks, i, block;
+	time_t timestamp;
+	const char *label = "";
+	struct stat st;
+	int fd;
+	uint8_t *buf;
+	struct reiserfs_super_block *sb;
+	struct journal_params *jp;
+	struct block_head *root;
+
+	// using global "option_mask32" instead of local "opts":
+	// we are register starved here
+	opt_complementary = "-1:b+";
+	/*opts =*/ getopt32(argv, "b:j:s:o:t:B:h:u:l:fqd",
+		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &label);
+	argv += optind; // argv[0] -- device
+
+	// check the device is a block device
+	fd = xopen(argv[0], O_WRONLY | O_EXCL);
+	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]);
+
+	// check if it is mounted
+	// N.B. what if we format a file? find_mount_point will return false negative since
+	// it is loop block device which is mounted!
+	if (find_mount_point(argv[0], 0))
+		bb_error_msg_and_die("can't format mounted filesystem");
+
+	// open the device, get size in blocks
+	blocks = get_volume_size_in_bytes(fd, argv[1], blocksize, /*extend:*/ 1) / blocksize;
+
+	// block number sanity check
+	// we have a limit: skipped area, super block, journal and root block
+	// all have to be addressed by one first bitmap
+	block = REISERFS_DISK_OFFSET_IN_BYTES / blocksize // boot area
+		+ 1		// sb
+		+ 1		// bitmap#0
+		+ journal_blocks+1	// journal
+	;
+
+	// count overhead
+	bitmap_blocks = (blocks - 1) / (blocksize * 8) + 1;
+	i = block + bitmap_blocks;
+
+	// check overhead
+	if (MIN(blocksize * 8, blocks) < i)
+		bb_error_msg_and_die("need >= %u blocks", i);
+
+	// ask confirmation?
+	// TODO: ???
+
+	// wipe out first REISERFS_DISK_OFFSET_IN_BYTES of device
+	// TODO: do we really need to wipe?!
+	xlseek(fd, REISERFS_DISK_OFFSET_IN_BYTES, SEEK_SET);
+
+	// fill superblock
+	sb = (struct reiserfs_super_block *)xzalloc(blocksize);
+	// block count
+	STORE_LE(sb->sb_block_count, blocks);
+	STORE_LE(sb->sb_free_blocks, blocks - i);
+	// TODO: decypher!
+	STORE_LE(sb->sb_root_block, block);
+	// fill journal related fields
+	jp = &sb->sb_journal;
+	STORE_LE(jp->jp_journal_1st_block, REISERFS_DISK_OFFSET_IN_BYTES / blocksize + 1/*sb*/ + 1/*bmp#0*/);
+	timestamp = time(NULL);
+	srandom(timestamp);
+	STORE_LE(jp->jp_journal_magic, random());
+	STORE_LE(jp->jp_journal_size, journal_blocks);
+	STORE_LE(jp->jp_journal_trans_max, JOURNAL_TRANS_MAX);
+	STORE_LE(jp->jp_journal_max_batch, JOURNAL_MAX_BATCH);
+	STORE_LE(jp->jp_journal_max_commit_age, JOURNAL_MAX_COMMIT_AGE);
+	// sizes
+	STORE_LE(sb->sb_blocksize, blocksize);
+	STORE_LE(sb->sb_oid_maxsize, (blocksize - sizeof(*sb)) / sizeof(uint32_t) / 2 * 2);
+	STORE_LE(sb->sb_oid_cursize, 2); // "." and ".."
+	strcpy(sb->s_magic, REISERFS_3_6_SUPER_MAGIC_STRING);
+	STORE_LE(sb->sb_bmap_nr, (bitmap_blocks > ((1LL << 16) - 1)) ? 0 : bitmap_blocks);
+	// misc
+	STORE_LE(sb->sb_version, REISERFS_FORMAT_3_6);
+	STORE_LE(sb->sb_lastcheck, timestamp);
+	STORE_LE(sb->sb_check_interval, DEFAULT_CHECK_INTERVAL);
+	STORE_LE(sb->sb_mnt_count, 1);
+	STORE_LE(sb->sb_max_mnt_count, DEFAULT_MAX_MNT_COUNT);
+	STORE_LE(sb->sb_umount_state, FS_CLEANLY_UMOUNTED);
+	STORE_LE(sb->sb_tree_height, 2);
+	STORE_LE(sb->sb_hash_function_code, 3); // R5_HASH
+	STORE_LE(sb->sb_flags, 1);
+	//STORE_LE(sb->sb_reserved_for_journal, 0);
+	// create UUID
+	generate_uuid(sb->s_uuid);
+	// write the label
+	safe_strncpy((char *)sb->s_label, label, sizeof(sb->s_label));
+
+	// TODO: EMPIRIC! ENDIANNESS!
+	// superblock has only 204 bytes. What are these?
+	buf = (uint8_t *)sb;
+	buf[205] = 1;
+	buf[209] = 3;
+
+	// put superblock
+	xwrite(fd, sb, blocksize);
+
+	// create bitmaps
+	buf = xzalloc(blocksize);
+
+	// bitmap #0 uses initial "block"+1 blocks
+	i = block + 1;
+	memset(buf, 0xFF, i / 8);
+	buf[i / 8] = (1 << (i & 7)) - 1; //0..7 => 00000000..01111111
+	// mark trailing absent blocks, if any
+	if (blocks < 8*blocksize) {
+		unsigned n = 8*blocksize - blocks;
+		i = n / 8;
+		buf[blocksize - i - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110
+		memset(buf + blocksize - i, 0xFF, i); // N.B. no overflow here!
+	}
+	// put bitmap #0
+	xwrite(fd, buf, blocksize);
+
+	// now go journal blocks
+	memset(buf, 0, blocksize);
+	for (i = 0; i < journal_blocks; i++)
+		xwrite(fd, buf, blocksize);
+	// dump journal control block
+	memcpy(&((struct reiserfs_journal_header *)buf)->jh_journal, &sb->sb_journal, sizeof(sb->sb_journal));
+	xwrite(fd, buf, blocksize);
+
+	// other bitmaps are in every (8*blocksize)-th block
+	// N.B. they use the only block -- namely bitmap itself!
+	buf[0] = 0x01;
+	// put bitmaps
+	for (i = 1; i < bitmap_blocks; i++) {
+		xlseek(fd, i*8*blocksize * blocksize, SEEK_SET);
+		// mark trailing absent blocks, if any
+		if (i == bitmap_blocks - 1 && (blocks % (8*blocksize))) {
+			unsigned n = 8*blocksize - blocks % (8*blocksize);
+			unsigned j = n / 8;
+			buf[blocksize - j - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110
+			memset(buf + blocksize - j, 0xFF, j); // N.B. no overflow here!
+		}
+		xwrite(fd, buf, blocksize);
+	}
+
+	// fill root block
+	// block head
+	memset(buf, 0, blocksize);
+	root = (struct block_head *)buf;
+	STORE_LE(root->blk2_level, 1); // leaf node
+	STORE_LE(root->blk2_nr_item, 2); // "." and ".."
+	STORE_LE(root->blk2_free_space, blocksize - sizeof(struct block_head));
+	// item head
+	// root directory
+	// TODO: EMPIRIC! ENDIANNESS!
+	// TODO: indented assignments seem to be timestamps
+buf[4] = 0134;
+buf[24] = 01;
+buf[28] = 02;
+buf[42] = 054;
+buf[44] = 0324;
+buf[45] = 017;
+buf[46] = 01;
+buf[48] = 01;
+buf[52] = 02;
+buf[56] = 01;
+buf[60] = 0364;
+buf[61] = 01;
+buf[64] = 02;
+buf[66] = 060;
+buf[68] = 0244;
+buf[69] = 017;
+buf[4004] = 01;
+buf[4008] = 01;
+buf[4012] = 02;
+buf[4016] = 050;
+buf[4018] = 04;
+buf[4020] = 02;
+buf[4028] = 01;
+buf[4032] = 040;
+buf[4034] = 04;
+
+buf[4036] = 056; buf[4037] = 056;	// ".."
+buf[4044] = 056;			// "."
+
+buf[4052] = 0355;
+buf[4053] = 0101;
+buf[4056] = 03;
+buf[4060] = 060;
+		buf[4076] = 0173;
+		buf[4077] = 0240;
+	buf[4078] = 0344;
+	buf[4079] = 0112;
+		buf[4080] = 0173;
+		buf[4081] = 0240;
+	buf[4082] = 0344;
+	buf[4083] = 0112;
+		buf[4084] = 0173;
+		buf[4085] = 0240;
+	buf[4086] = 0344;
+	buf[4087] = 0112;
+buf[4088] = 01;
+
+	// put root block
+	xlseek(fd, block * blocksize, SEEK_SET);
+	xwrite(fd, buf, blocksize);
+
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(buf);
+		free(sb);
+	}
+
+	xclose(fd);
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/mkfs_vfat.c b/busybox-1.19.3/util-linux/mkfs_vfat.c
new file mode 100644
index 0000000..7d81ed0
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mkfs_vfat.c
@@ -0,0 +1,630 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * mkfs_vfat: utility to create FAT32 filesystem
+ * inspired by dosfstools
+ *
+ * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ * 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 */
+#include <linux/fd.h>    /* FDGETPRM */
+#include <sys/mount.h>   /* BLKSSZGET */
+#if !defined(BLKSSZGET)
+# define BLKSSZGET _IO(0x12, 104)
+#endif
+//#include <linux/msdos_fs.h>
+
+#define SECTOR_SIZE             512
+
+#define SECTORS_PER_BLOCK	(BLOCK_SIZE / SECTOR_SIZE)
+
+// M$ says the high 4 bits of a FAT32 FAT entry are reserved
+#define EOF_FAT32       0x0FFFFFF8
+#define BAD_FAT32       0x0FFFFFF7
+#define MAX_CLUST_32    0x0FFFFFF0
+
+#define ATTR_VOLUME     8
+
+#define NUM_FATS        2
+
+/* FAT32 filesystem looks like this:
+ * sector -nn...-1: "hidden" sectors, all sectors before this partition
+ * (-h hidden-sectors sets it. Useful only for boot loaders,
+ *  they need to know _disk_ offset in order to be able to correctly
+ *  address sectors relative to start of disk)
+ * sector 0: boot sector
+ * sector 1: info sector
+ * sector 2: set aside for boot code which didn't fit into sector 0
+ * ...(zero-filled sectors)...
+ * sector B: backup copy of sector 0 [B set by -b backup-boot-sector]
+ * sector B+1: backup copy of sector 1
+ * sector B+2: backup copy of sector 2
+ * ...(zero-filled sectors)...
+ * sector R: FAT#1 [R set by -R reserved-sectors]
+ * ...(FAT#1)...
+ * sector R+fat_size: FAT#2
+ * ...(FAT#2)...
+ * sector R+fat_size*2: cluster #2
+ * ...(cluster #2)...
+ * sector R+fat_size*2+clust_size: cluster #3
+ * ...(the rest is filled by clusters till the end)...
+ */
+
+enum {
+// Perhaps this should remain constant
+	info_sector_number = 1,
+// TODO: make these cmdline options
+// dont forget sanity check: backup_boot_sector + 3 <= reserved_sect
+	backup_boot_sector = 3,
+	reserved_sect      = 6,
+};
+
+// how many blocks we try to read while testing
+#define TEST_BUFFER_BLOCKS      16
+
+struct msdos_dir_entry {
+	char     name[11];       /* 000 name and extension */
+	uint8_t  attr;           /* 00b attribute bits */
+	uint8_t  lcase;          /* 00c case for base and extension */
+	uint8_t  ctime_cs;       /* 00d creation time, centiseconds (0-199) */
+	uint16_t ctime;          /* 00e creation time */
+	uint16_t cdate;          /* 010 creation date */
+	uint16_t adate;          /* 012 last access date */
+	uint16_t starthi;        /* 014 high 16 bits of cluster in FAT32 */
+	uint16_t time;           /* 016 time */
+	uint16_t date;           /* 018 date */
+	uint16_t start;          /* 01a first cluster */
+	uint32_t size;           /* 01c file size in bytes */
+} PACKED;
+
+/* Example of boot sector's beginning:
+0000  eb 58 90 4d 53 57 49 4e  34 2e 31 00 02 08 26 00  |...MSWIN4.1...&.|
+0010  02 00 00 00 00 f8 00 00  3f 00 ff 00 3f 00 00 00  |........?...?...|
+0020  54 9b d0 00 0d 34 00 00  00 00 00 00 02 00 00 00  |T....4..........|
+0030  01 00 06 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
+0040  80 00 29 71 df 51 e0 4e  4f 20 4e 41 4d 45 20 20  |..)q.Q.NO NAME  |
+0050  20 20 46 41 54 33 32 20  20 20 33 c9 8e d1 bc f4  |  FAT32   3.....|
+*/
+struct msdos_volume_info { /* (offsets are relative to start of boot sector) */
+	uint8_t  drive_number;    /* 040 BIOS drive number */
+	uint8_t  reserved;        /* 041 unused */
+	uint8_t  ext_boot_sign;	  /* 042 0x29 if fields below exist (DOS 3.3+) */
+	uint32_t volume_id32;     /* 043 volume ID number */
+	char     volume_label[11];/* 047 volume label */
+	char     fs_type[8];      /* 052 typically "FATnn" */
+} PACKED;                         /* 05a end. Total size 26 (0x1a) bytes */
+
+struct msdos_boot_sector {
+	/* We use strcpy to fill both, and gcc-4.4.x complains if they are separate */
+	char     boot_jump_and_sys_id[3+8]; /* 000 short or near jump instruction */
+	/*char   system_id[8];*/     /* 003 name - can be used to special case partition manager volumes */
+	uint16_t bytes_per_sect;     /* 00b bytes per logical sector */
+	uint8_t  sect_per_clust;     /* 00d sectors/cluster */
+	uint16_t reserved_sect;      /* 00e reserved sectors (sector offset of 1st FAT relative to volume start) */
+	uint8_t  fats;               /* 010 number of FATs */
+	uint16_t dir_entries;        /* 011 root directory entries */
+	uint16_t volume_size_sect;   /* 013 volume size in sectors */
+	uint8_t  media_byte;         /* 015 media code */
+	uint16_t sect_per_fat;       /* 016 sectors/FAT */
+	uint16_t sect_per_track;     /* 018 sectors per track */
+	uint16_t heads;              /* 01a number of heads */
+	uint32_t hidden;             /* 01c hidden sectors (sector offset of volume within physical disk) */
+	uint32_t fat32_volume_size_sect; /* 020 volume size in sectors (if volume_size_sect == 0) */
+	uint32_t fat32_sect_per_fat; /* 024 sectors/FAT */
+	uint16_t fat32_flags;        /* 028 bit 8: fat mirroring, low 4: active fat */
+	uint8_t  fat32_version[2];   /* 02a major, minor filesystem version (I see 0,0) */
+	uint32_t fat32_root_cluster; /* 02c first cluster in root directory */
+	uint16_t fat32_info_sector;  /* 030 filesystem info sector (usually 1) */
+	uint16_t fat32_backup_boot;  /* 032 backup boot sector (usually 6) */
+	uint32_t reserved2[3];       /* 034 unused */
+	struct msdos_volume_info vi; /* 040 */
+	char     boot_code[0x200 - 0x5a - 2]; /* 05a */
+#define BOOT_SIGN 0xAA55
+	uint16_t boot_sign;          /* 1fe */
+} PACKED;
+
+#define FAT_FSINFO_SIG1 0x41615252
+#define FAT_FSINFO_SIG2 0x61417272
+struct fat32_fsinfo {
+	uint32_t signature1;         /* 0x52,0x52,0x41,0x61, "RRaA" */
+	uint32_t reserved1[128 - 8];
+	uint32_t signature2;         /* 0x72,0x72,0x61,0x41, "rrAa" */
+	uint32_t free_clusters;      /* free cluster count.  -1 if unknown */
+	uint32_t next_cluster;       /* most recently allocated cluster */
+	uint32_t reserved2[3];
+	uint16_t reserved3;          /* 1fc */
+	uint16_t boot_sign;          /* 1fe */
+} PACKED;
+
+struct bug_check {
+	char BUG1[sizeof(struct msdos_dir_entry  ) == 0x20 ? 1 : -1];
+	char BUG2[sizeof(struct msdos_volume_info) == 0x1a ? 1 : -1];
+	char BUG3[sizeof(struct msdos_boot_sector) == 0x200 ? 1 : -1];
+	char BUG4[sizeof(struct fat32_fsinfo     ) == 0x200 ? 1 : -1];
+};
+
+static const char boot_code[] ALIGN1 =
+	"\x0e"          /* 05a:         push  cs */
+	"\x1f"          /* 05b:         pop   ds */
+	"\xbe\x77\x7c"  /*  write_msg:  mov   si, offset message_txt */
+	"\xac"          /* 05f:         lodsb */
+	"\x22\xc0"      /* 060:         and   al, al */
+	"\x74\x0b"      /* 062:         jz    key_press */
+	"\x56"          /* 064:         push  si */
+	"\xb4\x0e"      /* 065:         mov   ah, 0eh */
+	"\xbb\x07\x00"  /* 067:         mov   bx, 0007h */
+	"\xcd\x10"      /* 06a:         int   10h */
+	"\x5e"          /* 06c:         pop   si */
+	"\xeb\xf0"      /* 06d:         jmp   write_msg */
+	"\x32\xe4"      /*  key_press:  xor   ah, ah */
+	"\xcd\x16"      /* 071:         int   16h */
+	"\xcd\x19"      /* 073:         int   19h */
+	"\xeb\xfe"      /*  foo:        jmp   foo */
+	/* 077: message_txt: */
+	"This is not a bootable disk\r\n";
+
+
+#define MARK_CLUSTER(cluster, value) \
+	((uint32_t *)fat)[cluster] = SWAP_LE32(value)
+
+void BUG_unsupported_field_size(void);
+#define STORE_LE(field, value) \
+do { \
+	if (sizeof(field) == 4) \
+		field = SWAP_LE32(value); \
+	else if (sizeof(field) == 2) \
+		field = SWAP_LE16(value); \
+	else if (sizeof(field) == 1) \
+		field = (value); \
+	else \
+		BUG_unsupported_field_size(); \
+} while (0)
+
+/* compat:
+ * mkdosfs 2.11 (12 Mar 2005)
+ * Usage: mkdosfs [-A] [-c] [-C] [-v] [-I] [-l bad-block-file]
+ *        [-b backup-boot-sector]
+ *        [-m boot-msg-file] [-n volume-name] [-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]
+ *        /dev/name [blocks]
+ */
+int mkfs_vfat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
+{
+	struct stat st;
+	const char *volume_label = "";
+	char *buf;
+	char *device_name;
+	uoff_t volume_size_bytes;
+	uoff_t volume_size_sect;
+	uint32_t total_clust;
+	uint32_t volume_id;
+	int dev;
+	unsigned bytes_per_sect;
+	unsigned sect_per_fat;
+	unsigned opts;
+	uint16_t sect_per_track;
+	uint8_t media_byte;
+	uint8_t sect_per_clust;
+	uint8_t heads;
+	enum {
+		OPT_A = 1 << 0,  // [IGNORED] atari format
+		OPT_b = 1 << 1,	 // [IGNORED] location of backup boot sector
+		OPT_c = 1 << 2,	 // [IGNORED] check filesystem
+		OPT_C = 1 << 3,	 // [IGNORED] create a new file
+		OPT_f = 1 << 4,	 // [IGNORED] number of FATs
+		OPT_F = 1 << 5,	 // [IGNORED, implied 32] choose FAT size
+		OPT_h = 1 << 6,	 // [IGNORED] number of hidden sectors
+		OPT_I = 1 << 7,	 // [IGNORED] don't bark at entire disk devices
+		OPT_i = 1 << 8,	 // [IGNORED] volume ID
+		OPT_l = 1 << 9,	 // [IGNORED] bad block filename
+		OPT_m = 1 << 10, // [IGNORED] message file
+		OPT_n = 1 << 11, // volume label
+		OPT_r = 1 << 12, // [IGNORED] root directory entries
+		OPT_R = 1 << 13, // [IGNORED] number of reserved sectors
+		OPT_s = 1 << 14, // [IGNORED] sectors per cluster
+		OPT_S = 1 << 15, // [IGNORED] sector size
+		OPT_v = 1 << 16, // verbose
+	};
+
+	opt_complementary = "-1";//:b+:f+:F+:h+:r+:R+:s+:S+:vv:c--l:l--c";
+	opts = getopt32(argv, "Ab:cCf:F:h:Ii:l:m:n:r:R:s:S:v",
+		NULL, NULL, NULL, NULL, NULL,
+		NULL, NULL, &volume_label, NULL, NULL, NULL, NULL);
+	argv += optind;
+
+	// cache device name
+	device_name = argv[0];
+	// default volume ID = creation time
+	volume_id = time(NULL);
+
+	dev = xopen(device_name, O_RDWR);
+	xfstat(dev, &st, device_name);
+
+	//
+	// Get image size and sector size
+	//
+	bytes_per_sect = SECTOR_SIZE;
+	if (!S_ISBLK(st.st_mode)) {
+		if (!S_ISREG(st.st_mode)) {
+			if (!argv[1])
+				bb_error_msg_and_die("image size must be specified");
+		}
+		// not a block device, skip bad sectors check
+		opts &= ~OPT_c;
+	} else {
+		int min_bytes_per_sect;
+#if 0
+		unsigned device_num;
+		// for true block devices we do check sanity
+		device_num = st.st_rdev & 0xff3f;
+		// do we allow to format the whole disk device?
+		if (!(opts & OPT_I) && (
+			device_num == 0x0300 || // hda, hdb
+			(device_num & 0xff0f) == 0x0800 || // sd
+			device_num == 0x0d00 || // xd
+			device_num == 0x1600 )  // hdc, hdd
+		)
+			bb_error_msg_and_die("will not try to make filesystem on full-disk device (use -I if wanted)");
+		// can't work on mounted filesystems
+		if (find_mount_point(device_name, 0))
+			bb_error_msg_and_die("can't format mounted filesystem");
+#endif
+		// get true sector size
+		// (parameter must be int*, not long* or size_t*)
+		xioctl(dev, BLKSSZGET, &min_bytes_per_sect);
+		if (min_bytes_per_sect > SECTOR_SIZE) {
+			bytes_per_sect = min_bytes_per_sect;
+			bb_error_msg("for this device sector size is %u", min_bytes_per_sect);
+		}
+	}
+	volume_size_bytes = get_volume_size_in_bytes(dev, argv[1], 1024, /*extend:*/ 1);
+	volume_size_sect = volume_size_bytes / bytes_per_sect;
+
+	//
+	// Find out or guess media parameters
+	//
+	media_byte = 0xf8;
+	heads = 255;
+	sect_per_track = 63;
+	sect_per_clust = 1;
+	{
+		struct hd_geometry geometry;
+		// size (in sectors), sect (per track), head
+		struct floppy_struct param;
+
+		// N.B. whether to use HDIO_GETGEO or HDIO_REQ?
+		if (ioctl(dev, HDIO_GETGEO, &geometry) == 0
+		 && geometry.sectors
+		 && geometry.heads
+		) {
+			// hard drive
+			sect_per_track = geometry.sectors;
+			heads = geometry.heads;
+
+ set_cluster_size:
+			/* For FAT32, try to do the same as M$'s format command
+			 * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20):
+			 * fs size <= 260M: 0.5k clusters
+			 * fs size <=   8G: 4k clusters
+			 * fs size <=  16G: 8k clusters
+			 * fs size >   16G: 16k clusters
+			 */
+			sect_per_clust = 1;
+			if (volume_size_bytes >= 260*1024*1024) {
+				sect_per_clust = 8;
+				/* fight gcc: */
+				/* "error: integer overflow in expression" */
+				/* "error: right shift count >= width of type" */
+				if (sizeof(off_t) > 4) {
+					unsigned t = (volume_size_bytes >> 31 >> 1);
+					if (t >= 8/4)
+						sect_per_clust = 16;
+					if (t >= 16/4)
+						sect_per_clust = 32;
+				}
+			}
+		} else {
+			// floppy, loop, or regular file
+			int not_floppy = ioctl(dev, FDGETPRM, &param);
+			if (not_floppy == 0) {
+				// floppy disk
+				sect_per_track = param.sect;
+				heads = param.head;
+				volume_size_sect = param.size;
+				volume_size_bytes = param.size * SECTOR_SIZE;
+			}
+			// setup the media descriptor byte
+			switch (volume_size_sect) {
+			case 2*360:	// 5.25", 2, 9, 40 - 360K
+				media_byte = 0xfd;
+				break;
+			case 2*720:	// 3.5", 2, 9, 80 - 720K
+			case 2*1200:	// 5.25", 2, 15, 80 - 1200K
+				media_byte = 0xf9;
+				break;
+			default:	// anything else
+				if (not_floppy)
+					goto set_cluster_size;
+			case 2*1440:	// 3.5", 2, 18, 80 - 1440K
+			case 2*2880:	// 3.5", 2, 36, 80 - 2880K
+				media_byte = 0xf0;
+				break;
+			}
+			// not floppy, but size matches floppy exactly.
+			// perhaps it is a floppy image.
+			// we already set media_byte as if it is a floppy,
+			// now set sect_per_track and heads.
+			heads = 2;
+			sect_per_track = (unsigned)volume_size_sect / 160;
+			if (sect_per_track < 9)
+				sect_per_track = 9;
+		}
+	}
+
+	//
+	// Calculate number of clusters, sectors/cluster, sectors/FAT
+	// (an initial guess for sect_per_clust should already be set)
+	//
+	// "mkdosfs -v -F 32 image5k 5" is the minimum:
+	// 2 sectors for FATs and 2 data sectors
+	if ((off_t)(volume_size_sect - reserved_sect) < 4)
+		bb_error_msg_and_die("the image is too small for FAT32");
+	sect_per_fat = 1;
+	while (1) {
+		while (1) {
+			int spf_adj;
+			uoff_t tcl = (volume_size_sect - reserved_sect - NUM_FATS * sect_per_fat) / sect_per_clust;
+			// tcl may be > MAX_CLUST_32 here, but it may be
+			// because sect_per_fat is underestimated,
+			// and with increased sect_per_fat it still may become
+			// <= MAX_CLUST_32. Therefore, we do not check
+			// against MAX_CLUST_32, but against a bigger const:
+			if (tcl > 0x80ffffff)
+				goto next;
+			total_clust = tcl; // fits in uint32_t
+			// Every cluster needs 4 bytes in FAT. +2 entries since
+			// FAT has space for non-existent clusters 0 and 1.
+			// Let's see how many sectors that needs.
+			//May overflow at "*4":
+			//spf_adj = ((total_clust+2) * 4 + bytes_per_sect-1) / bytes_per_sect - sect_per_fat;
+			//Same in the more obscure, non-overflowing form:
+			spf_adj = ((total_clust+2) + (bytes_per_sect/4)-1) / (bytes_per_sect/4) - sect_per_fat;
+#if 0
+			bb_error_msg("sect_per_clust:%u sect_per_fat:%u total_clust:%u",
+					sect_per_clust, sect_per_fat, (int)tcl);
+			bb_error_msg("adjust to sect_per_fat:%d", spf_adj);
+#endif
+			if (spf_adj <= 0) {
+				// do not need to adjust sect_per_fat.
+				// so, was total_clust too big after all?
+				if (total_clust <= MAX_CLUST_32)
+					goto found_total_clust; // no
+				// yes, total_clust is _a bit_ too big
+				goto next;
+			}
+			// adjust sect_per_fat, go back and recalc total_clust
+			// (note: just "sect_per_fat += spf_adj" isn't ok)
+			sect_per_fat += ((unsigned)spf_adj / 2) | 1;
+		}
+ next:
+		if (sect_per_clust == 128)
+			bb_error_msg_and_die("can't make FAT32 with >128 sectors/cluster");
+		sect_per_clust *= 2;
+		sect_per_fat = (sect_per_fat / 2) | 1;
+	}
+ found_total_clust:
+
+	//
+	// Print info
+	//
+	if (opts & OPT_v) {
+		fprintf(stderr,
+			"Device '%s':\n"
+			"heads:%u, sectors/track:%u, bytes/sector:%u\n"
+			"media descriptor:%02x\n"
+			"total sectors:%"OFF_FMT"u, clusters:%u, sectors/cluster:%u\n"
+			"FATs:2, sectors/FAT:%u\n"
+			"volumeID:%08x, label:'%s'\n",
+			device_name,
+			heads, sect_per_track, bytes_per_sect,
+			(int)media_byte,
+			volume_size_sect, (int)total_clust, (int)sect_per_clust,
+			sect_per_fat,
+			(int)volume_id, volume_label
+		);
+	}
+
+	//
+	// Write filesystem image sequentially (no seeking)
+	//
+	{
+		// (a | b) is poor man's max(a, b)
+		unsigned bufsize = reserved_sect;
+		//bufsize |= sect_per_fat; // can be quite large
+		bufsize |= 2; // use this instead
+		bufsize |= sect_per_clust;
+		buf = xzalloc(bufsize * bytes_per_sect);
+	}
+
+	{ // boot and fsinfo sectors, and their copies
+		struct msdos_boot_sector *boot_blk = (void*)buf;
+		struct fat32_fsinfo *info = (void*)(buf + bytes_per_sect);
+
+		strcpy(boot_blk->boot_jump_and_sys_id, "\xeb\x58\x90" "mkdosfs");
+		STORE_LE(boot_blk->bytes_per_sect, bytes_per_sect);
+		STORE_LE(boot_blk->sect_per_clust, sect_per_clust);
+		// cast in needed on big endian to suppress a warning
+		STORE_LE(boot_blk->reserved_sect, (uint16_t)reserved_sect);
+		STORE_LE(boot_blk->fats, 2);
+		//STORE_LE(boot_blk->dir_entries, 0); // for FAT32, stays 0
+		if (volume_size_sect <= 0xffff)
+			STORE_LE(boot_blk->volume_size_sect, volume_size_sect);
+		STORE_LE(boot_blk->media_byte, media_byte);
+		// wrong: this would make Linux think that it's fat12/16:
+		//if (sect_per_fat <= 0xffff)
+		//	STORE_LE(boot_blk->sect_per_fat, sect_per_fat);
+		// works:
+		//STORE_LE(boot_blk->sect_per_fat, 0);
+		STORE_LE(boot_blk->sect_per_track, sect_per_track);
+		STORE_LE(boot_blk->heads, heads);
+		//STORE_LE(boot_blk->hidden, 0);
+		STORE_LE(boot_blk->fat32_volume_size_sect, volume_size_sect);
+		STORE_LE(boot_blk->fat32_sect_per_fat, sect_per_fat);
+		//STORE_LE(boot_blk->fat32_flags, 0);
+		//STORE_LE(boot_blk->fat32_version[2], 0,0);
+		STORE_LE(boot_blk->fat32_root_cluster, 2);
+		STORE_LE(boot_blk->fat32_info_sector, info_sector_number);
+		STORE_LE(boot_blk->fat32_backup_boot, backup_boot_sector);
+		//STORE_LE(boot_blk->reserved2[3], 0,0,0);
+		STORE_LE(boot_blk->vi.ext_boot_sign, 0x29);
+		STORE_LE(boot_blk->vi.volume_id32, volume_id);
+		strncpy(boot_blk->vi.fs_type, "FAT32   ", sizeof(boot_blk->vi.fs_type));
+		strncpy(boot_blk->vi.volume_label, volume_label, sizeof(boot_blk->vi.volume_label));
+		memcpy(boot_blk->boot_code, boot_code, sizeof(boot_code));
+		STORE_LE(boot_blk->boot_sign, BOOT_SIGN);
+
+		STORE_LE(info->signature1, FAT_FSINFO_SIG1);
+		STORE_LE(info->signature2, FAT_FSINFO_SIG2);
+		// we've allocated cluster 2 for the root dir
+		STORE_LE(info->free_clusters, (total_clust - 1));
+		STORE_LE(info->next_cluster, 2);
+		STORE_LE(info->boot_sign, BOOT_SIGN);
+
+		// 1st copy
+		xwrite(dev, buf, bytes_per_sect * backup_boot_sector);
+		// 2nd copy and possibly zero sectors
+		xwrite(dev, buf, bytes_per_sect * (reserved_sect - backup_boot_sector));
+	}
+
+	{ // file allocation tables
+		unsigned i,j;
+		unsigned char *fat = (void*)buf;
+
+		memset(buf, 0, bytes_per_sect * 2);
+		// initial FAT entries
+		MARK_CLUSTER(0, 0x0fffff00 | media_byte);
+		MARK_CLUSTER(1, 0xffffffff);
+		// mark cluster 2 as EOF (used for root dir)
+		MARK_CLUSTER(2, EOF_FAT32);
+		for (i = 0; i < NUM_FATS; i++) {
+			xwrite(dev, buf, bytes_per_sect);
+			for (j = 1; j < sect_per_fat; j++)
+				xwrite(dev, buf + bytes_per_sect, bytes_per_sect);
+		}
+	}
+
+	// root directory
+	// empty directory is just a set of zero bytes
+	memset(buf, 0, sect_per_clust * bytes_per_sect);
+	if (volume_label[0]) {
+		// create dir entry for volume_label
+		struct msdos_dir_entry *de;
+#if 0
+		struct tm tm_time;
+		uint16_t t, d;
+#endif
+		de = (void*)buf;
+		strncpy(de->name, volume_label, sizeof(de->name));
+		STORE_LE(de->attr, ATTR_VOLUME);
+#if 0
+		localtime_r(&create_time, &tm_time);
+		t = (tm_time.tm_sec >> 1) + (tm_time.tm_min << 5) + (tm_time.tm_hour << 11);
+		d = tm_time.tm_mday + ((tm_time.tm_mon+1) << 5) + ((tm_time.tm_year-80) << 9);
+		STORE_LE(de->time, t);
+		STORE_LE(de->date, d);
+		//STORE_LE(de->ctime_cs, 0);
+		de->ctime = de->time;
+		de->cdate = de->date;
+		de->adate = de->date;
+#endif
+	}
+	xwrite(dev, buf, sect_per_clust * bytes_per_sect);
+
+#if 0
+	if (opts & OPT_c) {
+		uoff_t volume_size_blocks;
+		unsigned start_data_sector;
+		unsigned start_data_block;
+		unsigned badblocks = 0;
+		int try, got;
+		off_t currently_testing;
+		char *blkbuf = xmalloc(BLOCK_SIZE * TEST_BUFFER_BLOCKS);
+
+		volume_size_blocks = (volume_size_bytes >> BLOCK_SIZE_BITS);
+		// N.B. the two following vars are in hard sectors, i.e. SECTOR_SIZE byte sectors!
+		start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE);
+		start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK;
+
+		bb_info_msg("searching for bad blocks ");
+		currently_testing = 0;
+		try = TEST_BUFFER_BLOCKS;
+		while (currently_testing < volume_size_blocks) {
+			if (currently_testing + try > volume_size_blocks)
+				try = volume_size_blocks - currently_testing;
+			// perform a test on a block. return the number of blocks
+			// that could be read successfully.
+			// seek to the correct location
+			xlseek(dev, currently_testing * BLOCK_SIZE, SEEK_SET);
+			// try reading
+			got = read(dev, blkbuf, try * BLOCK_SIZE);
+			if (got < 0)
+				got = 0;
+			if (got & (BLOCK_SIZE - 1))
+				bb_error_msg("unexpected values in do_check: probably bugs");
+			got /= BLOCK_SIZE;
+			currently_testing += got;
+			if (got == try) {
+				try = TEST_BUFFER_BLOCKS;
+				continue;
+			}
+			try = 1;
+			if (currently_testing < start_data_block)
+				bb_error_msg_and_die("bad blocks before data-area: cannot make fs");
+
+			// mark all of the sectors in the block as bad
+			for (i = 0; i < SECTORS_PER_BLOCK; i++) {
+				int cluster = (currently_testing * SECTORS_PER_BLOCK + i - start_data_sector) / (int) (sect_per_clust) / (bytes_per_sect / SECTOR_SIZE);
+				if (cluster < 0)
+					bb_error_msg_and_die("invalid cluster number in mark_sector: probably bug!");
+				MARK_CLUSTER(cluster, BAD_FAT32);
+			}
+			badblocks++;
+			currently_testing++;
+		}
+		free(blkbuf);
+		if (badblocks)
+			bb_info_msg("%d bad block(s)", badblocks);
+	}
+#endif
+
+	// cleanup
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(buf);
+		close(dev);
+	}
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/mkswap.c b/busybox-1.19.3/util-linux/mkswap.c
new file mode 100644
index 0000000..b5d2c49
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mkswap.c
@@ -0,0 +1,149 @@
+/* vi: set sw=4 ts=4: */
+/* mkswap.c - format swap device (Linux v1 only)
+ *
+ * Copyright 2006 Rob Landley <rob@landley.net>
+ *
+ * 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
+static void mkswap_selinux_setcontext(int fd, const char *path)
+{
+	struct stat stbuf;
+
+	if (!is_selinux_enabled())
+		return;
+
+	xfstat(fd, &stbuf, path);
+	if (S_ISREG(stbuf.st_mode)) {
+		security_context_t newcon;
+		security_context_t oldcon = NULL;
+		context_t context;
+
+		if (fgetfilecon(fd, &oldcon) < 0) {
+			if (errno != ENODATA)
+				goto error;
+			if (matchpathcon(path, stbuf.st_mode, &oldcon) < 0)
+				goto error;
+		}
+		context = context_new(oldcon);
+		if (!context || context_type_set(context, "swapfile_t"))
+			goto error;
+		newcon = context_str(context);
+		if (!newcon)
+			goto error;
+		/* fsetfilecon_raw is hidden */
+		if (strcmp(oldcon, newcon) != 0 && fsetfilecon(fd, newcon) < 0)
+			goto error;
+		if (ENABLE_FEATURE_CLEAN_UP) {
+			context_free(context);
+			freecon(oldcon);
+		}
+	}
+	return;
+ error:
+	bb_perror_msg_and_die("SELinux relabeling failed");
+}
+#else
+# define mkswap_selinux_setcontext(fd, path) ((void)0)
+#endif
+
+/* from Linux 2.6.23 */
+/*
+ * Magic header for a swap area. ... Note that the first
+ * kilobyte is reserved for boot loader or disk label stuff.
+ */
+struct swap_header_v1 {
+/*	char     bootbits[1024];    Space for disklabel etc. */
+	uint32_t version;        /* second kbyte, word 0 */
+	uint32_t last_page;      /* 1 */
+	uint32_t nr_badpages;    /* 2 */
+	char     sws_uuid[16];   /* 3,4,5,6 */
+	char     sws_volume[16]; /* 7,8,9,10 */
+	uint32_t padding[117];   /* 11..127 */
+	uint32_t badpages[1];    /* 128 */
+	/* total 129 32-bit words in 2nd kilobyte */
+} FIX_ALIASING;
+
+#define NWORDS 129
+#define hdr ((struct swap_header_v1*)bb_common_bufsiz1)
+
+struct BUG_sizes {
+	char swap_header_v1_wrong[sizeof(*hdr)  != (NWORDS * 4) ? -1 : 1];
+	char bufsiz1_is_too_small[COMMON_BUFSIZE < (NWORDS * 4) ? -1 : 1];
+};
+
+/* Stored without terminating NUL */
+static const char SWAPSPACE2[sizeof("SWAPSPACE2")-1] ALIGN1 = "SWAPSPACE2";
+
+int mkswap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mkswap_main(int argc UNUSED_PARAM, char **argv)
+{
+	int fd;
+	unsigned pagesize;
+	off_t len;
+	const char *label = "";
+
+	opt_complementary = "-1"; /* at least one param */
+	/* TODO: -p PAGESZ, -U UUID */
+	getopt32(argv, "L:", &label);
+	argv += optind;
+
+	fd = xopen(argv[0], O_WRONLY);
+
+	/* Figure out how big the device is */
+	len = get_volume_size_in_bytes(fd, argv[1], 1024, /*extend:*/ 1);
+	pagesize = getpagesize();
+	len -= pagesize;
+
+	/* Announce our intentions */
+	printf("Setting up swapspace version 1, size = %"OFF_FMT"u bytes\n", len);
+	mkswap_selinux_setcontext(fd, argv[0]);
+
+	/* 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;
+
+	if (ENABLE_FEATURE_MKSWAP_UUID) {
+		char uuid_string[32];
+		generate_uuid((void*)hdr->sws_uuid);
+		bin2hex(uuid_string, hdr->sws_uuid, 16);
+		/* f.e. UUID=dfd9c173-be52-4d27-99a5-c34c6c2ff55f */
+		printf("UUID=%.8s"  "-%.4s-%.4s-%.4s-%.12s\n",
+			uuid_string,
+			uuid_string+8,
+			uuid_string+8+4,
+			uuid_string+8+4+4,
+			uuid_string+8+4+4+4
+		);
+	}
+	safe_strncpy(hdr->sws_volume, label, 16);
+
+	/* Write the header.  Sync to disk because some kernel versions check
+	 * signature on disk (not in cache) during swapon. */
+	xwrite(fd, hdr, NWORDS * 4);
+	xlseek(fd, pagesize - 10, SEEK_SET);
+	xwrite(fd, SWAPSPACE2, 10);
+	fsync(fd);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/more.c b/busybox-1.19.3/util-linux/more.c
new file mode 100644
index 0000000..efceb71
--- /dev/null
+++ b/busybox-1.19.3/util-linux/more.c
@@ -0,0 +1,213 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini more implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * Latest version blended together by Erik Andersen <andersen@codepoet.org>,
+ * based on the original more implementation by Bruce, and code from the
+ * Debian boot-floppies team.
+ *
+ * Termios corrects by Vladimir Oleynik <dzo@simtreas.ru>
+ *
+ * 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 */
+
+struct globals {
+	int cin_fileno;
+	struct termios initial_settings;
+	struct termios new_settings;
+} FIX_ALIASING;
+#define G (*(struct globals*)bb_common_bufsiz1)
+#define INIT_G() ((void)0)
+#define initial_settings (G.initial_settings)
+#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 getTermSettings(fd, argp) tcgetattr(fd, argp)
+
+static void gotsig(int sig UNUSED_PARAM)
+{
+	/* 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);
+}
+
+#define CONVERTED_TAB_SIZE 8
+
+int more_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int more_main(int argc UNUSED_PARAM, char **argv)
+{
+	int c = c; /* for compiler */
+	int lines;
+	int input = 0;
+	int spaces = 0;
+	int please_display_more_prompt;
+	struct stat st;
+	FILE *file;
+	FILE *cin;
+	int len;
+	unsigned terminal_width;
+	unsigned terminal_height;
+
+	INIT_G();
+
+	argv++;
+	/* Another popular pager, most, detects when stdout
+	 * is not a tty and turns into cat. This makes sense. */
+	if (!isatty(STDOUT_FILENO))
+		return bb_cat(argv);
+	cin = fopen_for_read(CURRENT_TTY);
+	if (!cin)
+		return bb_cat(argv);
+
+	if (ENABLE_FEATURE_USE_TERMIOS) {
+		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_cc[VMIN] = 1;
+		new_settings.c_cc[VTIME] = 0;
+		setTermSettings(cin_fileno, &new_settings);
+		bb_signals(0
+			+ (1 << SIGINT)
+			+ (1 << SIGQUIT)
+			+ (1 << SIGTERM)
+			, gotsig);
+	}
+
+	do {
+		file = stdin;
+		if (*argv) {
+			file = fopen_or_warn(*argv, "r");
+			if (!file)
+				continue;
+		}
+		st.st_size = 0;
+		fstat(fileno(file), &st);
+
+		please_display_more_prompt = 0;
+		/* never returns w, h <= 1 */
+		get_terminal_width_height(fileno(cin), &terminal_width, &terminal_height);
+		terminal_height -= 1;
+
+		len = 0;
+		lines = 0;
+		while (spaces || (c = getc(file)) != EOF) {
+			int wrap;
+			if (spaces)
+				spaces--;
+ loop_top:
+			if (input != 'r' && please_display_more_prompt) {
+				len = printf("--More-- ");
+				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) ((uoff_t)ftello(file) / d),
+						st.st_size);
+				}
+				fflush_all();
+
+				/*
+				 * We've just displayed the "--More--" prompt, so now we need
+				 * to get input from the user.
+				 */
+				for (;;) {
+					input = getc(cin);
+					input = tolower(input);
+					if (!ENABLE_FEATURE_USE_TERMIOS)
+						printf("\033[A"); /* cursor up */
+					/* Erase the last message */
+					printf("\r%*s\r", len, "");
+
+					/* Due to various multibyte escape
+					 * sequences, it's not ok to accept
+					 * any input as a command to scroll
+					 * the screen. We only allow known
+					 * commands, else we show help msg. */
+					if (input == ' ' || input == '\n' || input == 'q' || input == 'r')
+						break;
+					len = printf("(Enter:next line Space:next page Q:quit R:show the rest)");
+				}
+				len = 0;
+				lines = 0;
+				please_display_more_prompt = 0;
+
+				if (input == 'q')
+					goto end;
+
+				/* The user may have resized the terminal.
+				 * Re-read the dimensions. */
+				if (ENABLE_FEATURE_USE_TERMIOS) {
+					get_terminal_width_height(cin_fileno, &terminal_width, &terminal_height);
+					terminal_height -= 1;
+				}
+			}
+
+			/* Crudely convert tabs into spaces, which are
+			 * a bajillion times easier to deal with. */
+			if (c == '\t') {
+				spaces = ((unsigned)~len) % CONVERTED_TAB_SIZE;
+				c = ' ';
+			}
+
+			/*
+			 * There are two input streams to worry about here:
+			 *
+			 * c    : the character we are reading from the file being "mored"
+			 * input: a character received from the keyboard
+			 *
+			 * If we hit a newline in the _file_ stream, we want to test and
+			 * see if any characters have been hit in the _input_ stream. This
+			 * allows the user to quit while in the middle of a file.
+			 */
+			wrap = (++len > terminal_width);
+			if (c == '\n' || wrap) {
+				/* Then outputting this character
+				 * will move us to a new line. */
+				if (++lines >= terminal_height || input == '\n')
+					please_display_more_prompt = 1;
+				len = 0;
+			}
+			if (c != '\n' && wrap) {
+				/* Then outputting this will also put a character on
+				 * the beginning of that new line. Thus we first want to
+				 * display the prompt (if any), so we skip the putchar()
+				 * and go back to the top of the loop, without reading
+				 * a new character. */
+				goto loop_top;
+			}
+			/* 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();
+	} while (*argv && *++argv);
+ end:
+	setTermSettings(cin_fileno, &initial_settings);
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/mount.c b/busybox-1.19.3/util-linux/mount.c
new file mode 100644
index 0000000..05e532c
--- /dev/null
+++ b/busybox-1.19.3/util-linux/mount.c
@@ -0,0 +1,2199 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini mount implementation for busybox
+ *
+ * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
+ * 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 source tree.
+ */
+// Design notes: There is no spec for mount.  Remind me to write one.
+//
+// mount_main() calls singlemount() which calls mount_it_now().
+//
+// mount_main() can loop through /etc/fstab for mount -a
+// 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:     "\n	-r		Read-only mount"
+//usage:     "\n	-w		Read-write mount (default)"
+//usage:     "\n	-t FSTYPE	Filesystem type"
+//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>
+// 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"
+#if ENABLE_FEATURE_MOUNT_LABEL
+# include "volume_id.h"
+#else
+# define resolve_mount_spec(fsname) ((void)0)
+#endif
+
+// Needed for nfs support only
+#include <sys/utsname.h>
+#undef TRUE
+#undef FALSE
+#if ENABLE_FEATURE_MOUNT_NFS
+/* This is just a warning of a common mistake.  Possibly this should be a
+ * uclibc faq entry rather than in busybox... */
+# 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_prot.h>
+# include <rpc/pmap_clnt.h>
+#endif
+
+
+#if defined(__dietlibc__)
+// 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,
+		char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
+{
+	struct mntent* ment = getmntent(stream);
+	return memcpy(result, ment, sizeof(*ment));
+}
+#endif
+
+
+// Not real flags, but we want to be able to check for this.
+enum {
+	MOUNT_USERS  = (1 << 28) * ENABLE_DESKTOP,
+	MOUNT_NOAUTO = (1 << 29),
+	MOUNT_SWAP   = (1 << 30),
+};
+
+
+#define OPTION_STR "o:t:rwanfvsiO:"
+enum {
+	OPT_o = (1 << 0),
+	OPT_t = (1 << 1),
+	OPT_r = (1 << 2),
+	OPT_w = (1 << 3),
+	OPT_a = (1 << 4),
+	OPT_n = (1 << 5),
+	OPT_f = (1 << 6),
+	OPT_v = (1 << 7),
+	OPT_s = (1 << 8),
+	OPT_i = (1 << 9),
+	OPT_O = (1 << 10),
+};
+
+#if ENABLE_FEATURE_MTAB_SUPPORT
+#define USE_MTAB (!(option_mask32 & OPT_n))
+#else
+#define USE_MTAB 0
+#endif
+
+#if ENABLE_FEATURE_MOUNT_FAKE
+#define FAKE_IT (option_mask32 & OPT_f)
+#else
+#define FAKE_IT 0
+#endif
+
+#if ENABLE_FEATURE_MOUNT_HELPERS
+#define HELPERS_ALLOWED (!(option_mask32 & OPT_i))
+#else
+#define HELPERS_ALLOWED 0
+#endif
+
+
+// TODO: more "user" flag compatibility.
+// "user" option (from mount manpage):
+// Only the user that mounted a filesystem can unmount it again.
+// If any user should be able to unmount, then use users instead of user
+// in the fstab line.  The owner option is similar to the user option,
+// with the restriction that the user must be the owner of the special file.
+// This may be useful e.g. for /dev/fd if a login script makes
+// the console user owner of this device.
+
+// Standard mount options (from -o options or --options),
+// with corresponding flags
+static const int32_t mount_options[] = {
+	// MS_FLAGS set a bit.  ~MS_FLAGS disable that bit.  0 flags are NOPs.
+
+	IF_FEATURE_MOUNT_LOOP(
+		/* "loop" */ 0,
+	)
+
+	IF_FEATURE_MOUNT_FSTAB(
+		/* "defaults" */ 0,
+		/* "quiet" 0 - do not filter out, vfat wants to see it */
+		/* "noauto" */ MOUNT_NOAUTO,
+		/* "sw"     */ MOUNT_SWAP,
+		/* "swap"   */ MOUNT_SWAP,
+		IF_DESKTOP(/* "user"  */ MOUNT_USERS,)
+		IF_DESKTOP(/* "users" */ MOUNT_USERS,)
+		/* "_netdev" */ 0,
+	)
+
+	IF_FEATURE_MOUNT_FLAGS(
+		// vfs flags
+		/* "nosuid"      */ MS_NOSUID,
+		/* "suid"        */ ~MS_NOSUID,
+		/* "dev"         */ ~MS_NODEV,
+		/* "nodev"       */ MS_NODEV,
+		/* "exec"        */ ~MS_NOEXEC,
+		/* "noexec"      */ MS_NOEXEC,
+		/* "sync"        */ MS_SYNCHRONOUS,
+		/* "dirsync"     */ MS_DIRSYNC,
+		/* "async"       */ ~MS_SYNCHRONOUS,
+		/* "atime"       */ ~MS_NOATIME,
+		/* "noatime"     */ MS_NOATIME,
+		/* "diratime"    */ ~MS_NODIRATIME,
+		/* "nodiratime"  */ MS_NODIRATIME,
+		/* "mand"        */ MS_MANDLOCK,
+		/* "nomand"      */ ~MS_MANDLOCK,
+		/* "relatime"    */ MS_RELATIME,
+		/* "norelatime"  */ ~MS_RELATIME,
+		/* "loud"        */ ~MS_SILENT,
+		/* "rbind"       */ MS_BIND|MS_RECURSIVE,
+
+		// action flags
+		/* "union"       */ MS_UNION,
+		/* "bind"        */ MS_BIND,
+		/* "move"        */ MS_MOVE,
+		/* "shared"      */ MS_SHARED,
+		/* "slave"       */ MS_SLAVE,
+		/* "private"     */ MS_PRIVATE,
+		/* "unbindable"  */ MS_UNBINDABLE,
+		/* "rshared"     */ MS_SHARED|MS_RECURSIVE,
+		/* "rslave"      */ MS_SLAVE|MS_RECURSIVE,
+		/* "rprivate"    */ MS_PRIVATE|MS_RECURSIVE,
+		/* "runbindable" */ MS_UNBINDABLE|MS_RECURSIVE,
+	)
+
+	// Always understood.
+	/* "ro"      */ MS_RDONLY,  // vfs flag
+	/* "rw"      */ ~MS_RDONLY, // vfs flag
+	/* "remount" */ MS_REMOUNT  // action flag
+};
+
+static const char mount_option_str[] =
+	IF_FEATURE_MOUNT_LOOP(
+		"loop\0"
+	)
+	IF_FEATURE_MOUNT_FSTAB(
+		"defaults\0"
+		// "quiet\0" - do not filter out, vfat wants to see it
+		"noauto\0"
+		"sw\0"
+		"swap\0"
+		IF_DESKTOP("user\0")
+		IF_DESKTOP("users\0")
+		"_netdev\0"
+	)
+	IF_FEATURE_MOUNT_FLAGS(
+		// vfs flags
+		"nosuid\0"
+		"suid\0"
+		"dev\0"
+		"nodev\0"
+		"exec\0"
+		"noexec\0"
+		"sync\0"
+		"dirsync\0"
+		"async\0"
+		"atime\0"
+		"noatime\0"
+		"diratime\0"
+		"nodiratime\0"
+		"mand\0"
+		"nomand\0"
+		"relatime\0"
+		"norelatime\0"
+		"loud\0"
+		"rbind\0"
+
+		// action flags
+		"union\0"
+		"bind\0"
+		"move\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.
+	"ro\0"        // vfs flag
+	"rw\0"        // vfs flag
+	"remount\0"   // action flag
+;
+
+
+struct globals {
+#if ENABLE_FEATURE_MOUNT_NFS
+	smalluint nfs_mount_version;
+#endif
+#if ENABLE_FEATURE_MOUNT_VERBOSE
+	unsigned verbose;
+#endif
+	llist_t *fslist;
+	char getmntent_buf[1];
+} FIX_ALIASING;
+enum { GETMNTENT_BUFSIZE = COMMON_BUFSIZE - offsetof(struct globals, getmntent_buf) };
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define nfs_mount_version (G.nfs_mount_version)
+#if ENABLE_FEATURE_MOUNT_VERBOSE
+#define verbose           (G.verbose          )
+#else
+#define verbose           0
+#endif
+#define fslist            (G.fslist           )
+#define getmntent_buf     (G.getmntent_buf    )
+
+#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,
+		const char *filesystemtype,
+		unsigned long mountflags, const void *data)
+{
+	int rc;
+
+	errno = 0;
+	rc = mount(source, target, filesystemtype, mountflags, data);
+	if (verbose >= 2)
+		bb_perror_msg("mount('%s','%s','%s',0x%08lx,'%s'):%d",
+			source, target, filesystemtype,
+			mountflags, (char*)data, rc);
+	return rc;
+}
+#else
+#define verbose_mount(...) mount(__VA_ARGS__)
+#endif
+
+// Append mount options to string
+static void append_mount_options(char **oldopts, const char *newopts)
+{
+	if (*oldopts && **oldopts) {
+		// Do not insert options which are already there
+		while (newopts[0]) {
+			char *p;
+			int len = strlen(newopts);
+			p = strchr(newopts, ',');
+			if (p) len = p - newopts;
+			p = *oldopts;
+			while (1) {
+				if (!strncmp(p, newopts, len)
+				 && (p[len] == ',' || p[len] == '\0'))
+					goto skip;
+				p = strchr(p,',');
+				if (!p) break;
+				p++;
+			}
+			p = xasprintf("%s,%.*s", *oldopts, len, newopts);
+			free(*oldopts);
+			*oldopts = p;
+ skip:
+			newopts += len;
+			while (newopts[0] == ',') newopts++;
+		}
+	} else {
+		if (ENABLE_FEATURE_CLEAN_UP) free(*oldopts);
+		*oldopts = xstrdup(newopts);
+	}
+}
+
+// Use the mount_options list to parse options into flags.
+// Also update list of unrecognized options if unrecognized != NULL
+static long parse_mount_options(char *options, char **unrecognized)
+{
+	long flags = MS_SILENT;
+
+	// Loop through options
+	for (;;) {
+		unsigned i;
+		char *comma = strchr(options, ',');
+		const char *option_str = mount_option_str;
+
+		if (comma) *comma = '\0';
+
+// FIXME: use hasmntopt()
+		// Find this option in mount_options
+		for (i = 0; i < ARRAY_SIZE(mount_options); i++) {
+			if (strcasecmp(option_str, options) == 0) {
+				long fl = mount_options[i];
+				if (fl < 0)
+					flags &= fl;
+				else
+					flags |= fl;
+				goto found;
+			}
+			option_str += strlen(option_str) + 1;
+		}
+		// 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
+			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 (len) p[len++] = ',';
+			strcpy(p + len, options);
+		}
+ found:
+		if (!comma)
+			break;
+		// Advance to next option
+		*comma = ',';
+		options = ++comma;
+	}
+
+	return flags;
+}
+
+// Return a list of all block device backed filesystems
+static llist_t *get_block_backed_filesystems(void)
+{
+	static const char filesystems[2][sizeof("/proc/filesystems")] = {
+		"/etc/filesystems",
+		"/proc/filesystems",
+	};
+	char *fs, *buf;
+	llist_t *list = NULL;
+	int i;
+	FILE *f;
+
+	for (i = 0; i < 2; i++) {
+		f = fopen_for_read(filesystems[i]);
+		if (!f) continue;
+
+		while ((buf = xmalloc_fgetline(f)) != NULL) {
+			if (strncmp(buf, "nodev", 5) == 0 && isspace(buf[5]))
+				continue;
+			fs = skip_whitespace(buf);
+			if (*fs == '#' || *fs == '*' || !*fs)
+				continue;
+
+			llist_add_to_end(&list, xstrdup(fs));
+			free(buf);
+		}
+		if (ENABLE_FEATURE_CLEAN_UP) fclose(f);
+	}
+
+	return list;
+}
+
+#if ENABLE_FEATURE_CLEAN_UP
+static void delete_block_backed_filesystems(void)
+{
+	llist_free(fslist, free);
+}
+#else
+void delete_block_backed_filesystems(void);
+#endif
+
+// 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)
+{
+	int rc = 0;
+
+	if (FAKE_IT) {
+		if (verbose >= 2)
+			bb_error_msg("would do mount('%s','%s','%s',0x%08lx,'%s')",
+				mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
+				vfsflags, filteropts);
+		goto mtab;
+	}
+
+	// Mount, with fallback to read-only if necessary.
+	for (;;) {
+		errno = 0;
+		rc = verbose_mount(mp->mnt_fsname, mp->mnt_dir, mp->mnt_type,
+				vfsflags, filteropts);
+
+		// If mount failed, try
+		// helper program mount.<mnt_type>
+		if (HELPERS_ALLOWED && rc && mp->mnt_type) {
+			char *args[8];
+			int errno_save = errno;
+			args[0] = xasprintf("mount.%s", mp->mnt_type);
+			rc = 1;
+			if (FAKE_IT)
+				args[rc++] = (char *)"-f";
+			if (ENABLE_FEATURE_MTAB_SUPPORT && !USE_MTAB)
+				args[rc++] = (char *)"-n";
+			args[rc++] = mp->mnt_fsname;
+			args[rc++] = mp->mnt_dir;
+			if (filteropts) {
+				args[rc++] = (char *)"-o";
+				args[rc++] = filteropts;
+			}
+			args[rc] = NULL;
+			rc = spawn_and_wait(args);
+			free(args[0]);
+			if (!rc)
+				break;
+			errno = errno_save;
+		}
+
+		if (!rc || (vfsflags & MS_RDONLY) || (errno != EACCES && errno != EROFS))
+			break;
+		if (!(vfsflags & MS_SILENT))
+			bb_error_msg("%s is write-protected, mounting read-only",
+						mp->mnt_fsname);
+		vfsflags |= MS_RDONLY;
+	}
+
+	// Abort entirely if permission denied.
+
+	if (rc && errno == EPERM)
+		bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+	// If the mount was successful, and we're maintaining an old-style
+	// mtab file by hand, add the new entry to it now.
+ mtab:
+	if (USE_MTAB && !rc && !(vfsflags & MS_REMOUNT)) {
+		char *fsname;
+		FILE *mountTable = setmntent(bb_path_mtab_file, "a+");
+		const char *option_str = mount_option_str;
+		int i;
+
+		if (!mountTable) {
+			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);
+			option_str += strlen(option_str) + 1;
+		}
+
+		// Remove trailing / (if any) from directory we mounted on
+		i = strlen(mp->mnt_dir) - 1;
+		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 = 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
+#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);
+		}
+	}
+ ret:
+	return rc;
+}
+
+#if ENABLE_FEATURE_MOUNT_NFS
+
+/*
+ * Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * 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.
+ *
+ * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
+ * Omit the call to connect() for Linux version 1.3.11 or later.
+ *
+ * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
+ */
+
+#define MOUNTPORT 635
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE 32
+#define FHSIZE3 64
+
+typedef char fhandle[FHSIZE];
+
+typedef struct {
+	unsigned int fhandle3_len;
+	char *fhandle3_val;
+} fhandle3;
+
+enum mountstat3 {
+	MNT_OK = 0,
+	MNT3ERR_PERM = 1,
+	MNT3ERR_NOENT = 2,
+	MNT3ERR_IO = 5,
+	MNT3ERR_ACCES = 13,
+	MNT3ERR_NOTDIR = 20,
+	MNT3ERR_INVAL = 22,
+	MNT3ERR_NAMETOOLONG = 63,
+	MNT3ERR_NOTSUPP = 10004,
+	MNT3ERR_SERVERFAULT = 10006,
+};
+typedef enum mountstat3 mountstat3;
+
+struct fhstatus {
+	unsigned int fhs_status;
+	union {
+		fhandle fhs_fhandle;
+	} fhstatus_u;
+};
+typedef struct fhstatus fhstatus;
+
+struct mountres3_ok {
+	fhandle3 fhandle;
+	struct {
+		unsigned int auth_flavours_len;
+		char *auth_flavours_val;
+	} auth_flavours;
+};
+typedef struct mountres3_ok mountres3_ok;
+
+struct mountres3 {
+	mountstat3 fhs_status;
+	union {
+		mountres3_ok mountinfo;
+	} mountres3_u;
+};
+typedef struct mountres3 mountres3;
+
+typedef char *dirpath;
+
+typedef char *name;
+
+typedef struct mountbody *mountlist;
+
+struct mountbody {
+	name ml_hostname;
+	dirpath ml_directory;
+	mountlist ml_next;
+};
+typedef struct mountbody mountbody;
+
+typedef struct groupnode *groups;
+
+struct groupnode {
+	name gr_name;
+	groups gr_next;
+};
+typedef struct groupnode groupnode;
+
+typedef struct exportnode *exports;
+
+struct exportnode {
+	dirpath ex_dir;
+	groups ex_groups;
+	exports ex_next;
+};
+typedef struct exportnode exportnode;
+
+struct ppathcnf {
+	int pc_link_max;
+	short pc_max_canon;
+	short pc_max_input;
+	short pc_name_max;
+	short pc_path_max;
+	short pc_pipe_buf;
+	uint8_t pc_vdisable;
+	char pc_xxx;
+	short pc_mask[2];
+};
+typedef struct ppathcnf ppathcnf;
+
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+
+#define MOUNTPROC_NULL 0
+#define MOUNTPROC_MNT 1
+#define MOUNTPROC_DUMP 2
+#define MOUNTPROC_UMNT 3
+#define MOUNTPROC_UMNTALL 4
+#define MOUNTPROC_EXPORT 5
+#define MOUNTPROC_EXPORTALL 6
+
+#define MOUNTVERS_POSIX 2
+
+#define MOUNTPROC_PATHCONF 7
+
+#define MOUNT_V3 3
+
+#define MOUNTPROC3_NULL 0
+#define MOUNTPROC3_MNT 1
+#define MOUNTPROC3_DUMP 2
+#define MOUNTPROC3_UMNT 3
+#define MOUNTPROC3_UMNTALL 4
+#define MOUNTPROC3_EXPORT 5
+
+enum {
+#ifndef NFS_FHSIZE
+	NFS_FHSIZE = 32,
+#endif
+#ifndef NFS_PORT
+	NFS_PORT = 2049
+#endif
+};
+
+/*
+ * We want to be able to compile mount on old kernels in such a way
+ * that the binary will work well on more recent kernels.
+ * Thus, if necessary we teach nfsmount.c the structure of new fields
+ * that will come later.
+ *
+ * Moreover, the new kernel includes conflict with glibc includes
+ * so it is easiest to ignore the kernel altogether (at compile time).
+ */
+
+struct nfs2_fh {
+	char                    data[32];
+};
+struct nfs3_fh {
+	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 */
+};
+
+/* bits in the flags field */
+enum {
+	NFS_MOUNT_SOFT = 0x0001,	/* 1 */
+	NFS_MOUNT_INTR = 0x0002,	/* 1 */
+	NFS_MOUNT_SECURE = 0x0004,	/* 1 */
+	NFS_MOUNT_POSIX = 0x0008,	/* 1 */
+	NFS_MOUNT_NOCTO = 0x0010,	/* 1 */
+	NFS_MOUNT_NOAC = 0x0020,	/* 1 */
+	NFS_MOUNT_TCP = 0x0040,		/* 2 */
+	NFS_MOUNT_VER3 = 0x0080,	/* 3 */
+	NFS_MOUNT_KERBEROS = 0x0100,	/* 3 */
+	NFS_MOUNT_NONLM = 0x0200,	/* 3 */
+	NFS_MOUNT_NORDIRPLUS = 0x4000
+};
+
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ *
+ * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
+ * "after #include <errno.h> the symbol errno is reserved for any use,
+ *  it cannot even be used as a struct tag or field name".
+ */
+#ifndef EDQUOT
+# define EDQUOT ENOSPC
+#endif
+/* Convert each NFSERR_BLAH into EBLAH */
+static const uint8_t nfs_err_stat[] = {
+	 1,  2,  5,  6, 13, 17,
+	19, 20, 21, 22, 27, 28,
+	30, 63, 66, 69, 70, 71
+};
+#if ( \
+	EPERM | ENOENT      | EIO      | ENXIO | EACCES| EEXIST | \
+	ENODEV| ENOTDIR     | EISDIR   | EINVAL| EFBIG | ENOSPC | \
+	EROFS | ENAMETOOLONG| ENOTEMPTY| EDQUOT| ESTALE| EREMOTE) < 256
+typedef uint8_t nfs_err_type;
+#else
+typedef uint16_t nfs_err_type;
+#endif
+static const nfs_err_type nfs_err_errnum[] = {
+	EPERM , ENOENT      , EIO      , ENXIO , EACCES, EEXIST,
+	ENODEV, ENOTDIR     , EISDIR   , EINVAL, EFBIG , ENOSPC,
+	EROFS , ENAMETOOLONG, ENOTEMPTY, EDQUOT, ESTALE, EREMOTE
+};
+static char *nfs_strerror(int status)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(nfs_err_stat); i++) {
+		if (nfs_err_stat[i] == status)
+			return strerror(nfs_err_errnum[i]);
+	}
+	return xasprintf("unknown nfs status return value: %d", status);
+}
+
+static bool_t xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+	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;
+	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)
+{
+	return xdr_string(xdrs, objp, MNTPATHLEN);
+}
+
+static bool_t xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
+{
+	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;
+	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)
+{
+	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;
+	if (objp->fhs_status == MNT_OK)
+		return xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo);
+	return TRUE;
+}
+
+#define MAX_NFSPROT ((nfs_mount_version >= 4) ? 3 : 2)
+
+/*
+ * Unfortunately, the kernel prints annoying console messages
+ * in case of an unexpected nfs mount version (instead of
+ * just returning some error).  Therefore we'll have to try
+ * and figure out what version the kernel expects.
+ *
+ * Variables:
+ *	KERNEL_NFS_MOUNT_VERSION: kernel sources at compile time
+ *	NFS_MOUNT_VERSION: these nfsmount sources at compile time
+ *	nfs_mount_version: version this source and running kernel can handle
+ */
+static void
+find_kernel_nfs_mount_version(void)
+{
+	int kernel_version;
+
+	if (nfs_mount_version)
+		return;
+
+	nfs_mount_version = 4; /* default */
+
+	kernel_version = get_linux_version_code();
+	if (kernel_version) {
+		if (kernel_version < KERNEL_VERSION(2,2,18))
+			nfs_mount_version = 3;
+		/* else v4 since 2.3.99pre4 */
+	}
+}
+
+static void
+get_mountport(struct pmap *pm_mnt,
+	struct sockaddr_in *server_addr,
+	long unsigned prog,
+	long unsigned version,
+	long unsigned proto,
+	long unsigned port)
+{
+	struct pmaplist *pmap;
+
+	server_addr->sin_port = PMAPPORT;
+/* glibc 2.4 (still) has pmap_getmaps(struct sockaddr_in *).
+ * I understand it like "IPv6 for this is not 100% ready" */
+	pmap = pmap_getmaps(server_addr);
+
+	if (version > MAX_NFSPROT)
+		version = MAX_NFSPROT;
+	if (!prog)
+		prog = MOUNTPROG;
+	pm_mnt->pm_prog = prog;
+	pm_mnt->pm_vers = version;
+	pm_mnt->pm_prot = proto;
+	pm_mnt->pm_port = port;
+
+	while (pmap) {
+		if (pmap->pml_map.pm_prog != prog)
+			goto next;
+		if (!version && pm_mnt->pm_vers > pmap->pml_map.pm_vers)
+			goto next;
+		if (version > 2 && pmap->pml_map.pm_vers != version)
+			goto next;
+		if (version && version <= 2 && pmap->pml_map.pm_vers > 2)
+			goto next;
+		if (pmap->pml_map.pm_vers > MAX_NFSPROT
+		 || (proto && pm_mnt->pm_prot && pmap->pml_map.pm_prot != proto)
+		 || (port && pmap->pml_map.pm_port != port)
+		) {
+			goto next;
+		}
+		memcpy(pm_mnt, &pmap->pml_map, sizeof(*pm_mnt));
+ next:
+		pmap = pmap->pml_next;
+	}
+	if (!pm_mnt->pm_vers)
+		pm_mnt->pm_vers = MOUNTVERS;
+	if (!pm_mnt->pm_port)
+		pm_mnt->pm_port = MOUNTPORT;
+	if (!pm_mnt->pm_prot)
+		pm_mnt->pm_prot = IPPROTO_TCP;
+}
+
+#if BB_MMU
+static int daemonize(void)
+{
+	int pid = fork();
+	if (pid < 0) /* error */
+		return -errno;
+	if (pid > 0) /* parent */
+		return 0;
+	/* child */
+	close(0);
+	xopen(bb_dev_null, O_RDWR);
+	xdup2(0, 1);
+	xdup2(0, 2);
+	setsid();
+	openlog(applet_name, LOG_PID, LOG_DAEMON);
+	logmode = LOGMODE_SYSLOG;
+	return 1;
+}
+#else
+static inline int daemonize(void) { return -ENOSYS; }
+#endif
+
+/* TODO */
+static inline int we_saw_this_host_before(const char *hostname UNUSED_PARAM)
+{
+	return 0;
+}
+
+/* RPC strerror analogs are terminally idiotic:
+ * *mandatory* prefix and \n at end.
+ * This hopefully helps. Usage:
+ * error_msg_rpc(clnt_*error*(" ")) */
+static void error_msg_rpc(const char *msg)
+{
+	int len;
+	while (msg[0] == ' ' || msg[0] == ':') msg++;
+	len = strlen(msg);
+	while (len && msg[len-1] == '\n') len--;
+	bb_error_msg("%.*s", len, msg);
+}
+
+/* NB: mp->xxx fields may be trashed on exit */
+static NOINLINE int nfsmount(struct mntent *mp, long vfsflags, char *filteropts)
+{
+	CLIENT *mclient;
+	char *hostname;
+	char *pathname;
+	char *mounthost;
+	/* prior to 2.6.23, kernel took NFS options in a form of this struct
+	 * only. 2.6.23+ looks at data->version, and if it's not 1..6,
+	 * then data pointer is interpreted as a string. */
+	struct nfs_mount_data data;
+	char *opt;
+	struct hostent *hp;
+	struct sockaddr_in server_addr;
+	struct sockaddr_in mount_server_addr;
+	int msock, fsock;
+	union {
+		struct fhstatus nfsv2;
+		struct mountres3 nfsv3;
+	} status;
+	int daemonized;
+	char *s;
+	int port;
+	int mountport;
+	int proto;
+#if BB_MMU
+	smallint bg = 0;
+#else
+	enum { bg = 0 };
+#endif
+	int retry;
+	int mountprog;
+	int mountvers;
+	int nfsprog;
+	int nfsvers;
+	int retval;
+	/* these all are one-bit really. gcc 4.3.1 likes this combination: */
+	smallint tcp;
+	smallint soft;
+	int intr;
+	int posix;
+	int nocto;
+	int noac;
+	int nordirplus;
+	int nolock;
+
+	find_kernel_nfs_mount_version();
+
+	daemonized = 0;
+	mounthost = NULL;
+	retval = ETIMEDOUT;
+	msock = fsock = -1;
+	mclient = NULL;
+
+	/* NB: hostname, mounthost, filteropts must be free()d prior to return */
+
+	filteropts = xstrdup(filteropts); /* going to trash it later... */
+
+	hostname = xstrdup(mp->mnt_fsname);
+	/* mount_main() guarantees that ':' is there */
+	s = strchr(hostname, ':');
+	pathname = s + 1;
+	*s = '\0';
+	/* Ignore all but first hostname in replicated mounts
+	   until they can be fully supported. (mack@sgi.com) */
+	s = strchr(hostname, ',');
+	if (s) {
+		*s = '\0';
+		bb_error_msg("warning: multiple hostnames not supported");
+	}
+
+	server_addr.sin_family = AF_INET;
+	if (!inet_aton(hostname, &server_addr.sin_addr)) {
+		hp = gethostbyname(hostname);
+		if (hp == NULL) {
+			bb_herror_msg("%s", hostname);
+			goto fail;
+		}
+		if (hp->h_length != (int)sizeof(struct in_addr)) {
+			bb_error_msg_and_die("only IPv4 is supported");
+		}
+		memcpy(&server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+	}
+
+	memcpy(&mount_server_addr, &server_addr, sizeof(mount_server_addr));
+
+	/* add IP address to mtab options for use when unmounting */
+
+	if (!mp->mnt_opts) { /* TODO: actually mp->mnt_opts is never NULL */
+		mp->mnt_opts = xasprintf("addr=%s", inet_ntoa(server_addr.sin_addr));
+	} else {
+		char *tmp = xasprintf("%s%saddr=%s", mp->mnt_opts,
+					mp->mnt_opts[0] ? "," : "",
+					inet_ntoa(server_addr.sin_addr));
+		free(mp->mnt_opts);
+		mp->mnt_opts = tmp;
+	}
+
+	/* Set default options.
+	 * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
+	 * let the kernel decide.
+	 * timeo is filled in after we know whether it'll be TCP or UDP. */
+	memset(&data, 0, sizeof(data));
+	data.retrans  = 3;
+	data.acregmin = 3;
+	data.acregmax = 60;
+	data.acdirmin = 30;
+	data.acdirmax = 60;
+	data.namlen   = NAME_MAX;
+
+	soft = 0;
+	intr = 0;
+	posix = 0;
+	nocto = 0;
+	nolock = 0;
+	noac = 0;
+	nordirplus = 0;
+	retry = 10000;		/* 10000 minutes ~ 1 week */
+	tcp = 1;			/* nfs-utils uses tcp per default */
+
+	mountprog = MOUNTPROG;
+	mountvers = 0;
+	port = 0;
+	mountport = 0;
+	nfsprog = 100003;
+	nfsvers = 0;
+
+	/* parse options */
+	if (filteropts)	for (opt = strtok(filteropts, ","); opt; opt = strtok(NULL, ",")) {
+		char *opteq = strchr(opt, '=');
+		if (opteq) {
+			int val, idx;
+			static const char options[] ALIGN1 =
+				/* 0 */ "rsize\0"
+				/* 1 */ "wsize\0"
+				/* 2 */ "timeo\0"
+				/* 3 */ "retrans\0"
+				/* 4 */ "acregmin\0"
+				/* 5 */ "acregmax\0"
+				/* 6 */ "acdirmin\0"
+				/* 7 */ "acdirmax\0"
+				/* 8 */ "actimeo\0"
+				/* 9 */ "retry\0"
+				/* 10 */ "port\0"
+				/* 11 */ "mountport\0"
+				/* 12 */ "mounthost\0"
+				/* 13 */ "mountprog\0"
+				/* 14 */ "mountvers\0"
+				/* 15 */ "nfsprog\0"
+				/* 16 */ "nfsvers\0"
+				/* 17 */ "vers\0"
+				/* 18 */ "proto\0"
+				/* 19 */ "namlen\0"
+				/* 20 */ "addr\0";
+
+			*opteq++ = '\0';
+			idx = index_in_strings(options, opt);
+			switch (idx) {
+			case 12: // "mounthost"
+				mounthost = xstrndup(opteq,
+						strcspn(opteq, " \t\n\r,"));
+				continue;
+			case 18: // "proto"
+				if (!strncmp(opteq, "tcp", 3))
+					tcp = 1;
+				else if (!strncmp(opteq, "udp", 3))
+					tcp = 0;
+				else
+					bb_error_msg("warning: unrecognized proto= option");
+				continue;
+			case 20: // "addr" - ignore
+				continue;
+			case -1: // unknown
+				if (vfsflags & MS_REMOUNT)
+					continue;
+			}
+
+			val = xatoi_positive(opteq);
+			switch (idx) {
+			case 0: // "rsize"
+				data.rsize = val;
+				continue;
+			case 1: // "wsize"
+				data.wsize = val;
+				continue;
+			case 2: // "timeo"
+				data.timeo = val;
+				continue;
+			case 3: // "retrans"
+				data.retrans = val;
+				continue;
+			case 4: // "acregmin"
+				data.acregmin = val;
+				continue;
+			case 5: // "acregmax"
+				data.acregmax = val;
+				continue;
+			case 6: // "acdirmin"
+				data.acdirmin = val;
+				continue;
+			case 7: // "acdirmax"
+				data.acdirmax = val;
+				continue;
+			case 8: // "actimeo"
+				data.acregmin = val;
+				data.acregmax = val;
+				data.acdirmin = val;
+				data.acdirmax = val;
+				continue;
+			case 9: // "retry"
+				retry = val;
+				continue;
+			case 10: // "port"
+				port = val;
+				continue;
+			case 11: // "mountport"
+				mountport = val;
+				continue;
+			case 13: // "mountprog"
+				mountprog = val;
+				continue;
+			case 14: // "mountvers"
+				mountvers = val;
+				continue;
+			case 15: // "nfsprog"
+				nfsprog = val;
+				continue;
+			case 16: // "nfsvers"
+			case 17: // "vers"
+				nfsvers = val;
+				continue;
+			case 19: // "namlen"
+				//if (nfs_mount_version >= 2)
+					data.namlen = val;
+				//else
+				//	bb_error_msg("warning: option namlen is not supported\n");
+				continue;
+			default:
+				bb_error_msg("unknown nfs mount parameter: %s=%d", opt, val);
+				goto fail;
+			}
+		}
+		else { /* not of the form opt=val */
+			static const char options[] ALIGN1 =
+				"bg\0"
+				"fg\0"
+				"soft\0"
+				"hard\0"
+				"intr\0"
+				"posix\0"
+				"cto\0"
+				"ac\0"
+				"tcp\0"
+				"udp\0"
+				"lock\0"
+				"rdirplus\0";
+			int val = 1;
+			if (!strncmp(opt, "no", 2)) {
+				val = 0;
+				opt += 2;
+			}
+			switch (index_in_strings(options, opt)) {
+			case 0: // "bg"
+#if BB_MMU
+				bg = val;
+#endif
+				break;
+			case 1: // "fg"
+#if BB_MMU
+				bg = !val;
+#endif
+				break;
+			case 2: // "soft"
+				soft = val;
+				break;
+			case 3: // "hard"
+				soft = !val;
+				break;
+			case 4: // "intr"
+				intr = val;
+				break;
+			case 5: // "posix"
+				posix = val;
+				break;
+			case 6: // "cto"
+				nocto = !val;
+				break;
+			case 7: // "ac"
+				noac = !val;
+				break;
+			case 8: // "tcp"
+				tcp = val;
+				break;
+			case 9: // "udp"
+				tcp = !val;
+				break;
+			case 10: // "lock"
+				if (nfs_mount_version >= 3)
+					nolock = !val;
+				else
+					bb_error_msg("warning: option nolock is not supported");
+				break;
+			case 11: //rdirplus
+				nordirplus = !val;
+				break;
+			default:
+				bb_error_msg("unknown nfs mount option: %s%s", val ? "" : "no", opt);
+				goto fail;
+			}
+		}
+	}
+	proto = (tcp) ? IPPROTO_TCP : IPPROTO_UDP;
+
+	data.flags = (soft ? NFS_MOUNT_SOFT : 0)
+		| (intr ? NFS_MOUNT_INTR : 0)
+		| (posix ? NFS_MOUNT_POSIX : 0)
+		| (nocto ? NFS_MOUNT_NOCTO : 0)
+		| (noac ? NFS_MOUNT_NOAC : 0)
+		| (nordirplus ? NFS_MOUNT_NORDIRPLUS : 0);
+	if (nfs_mount_version >= 2)
+		data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
+	if (nfs_mount_version >= 3)
+		data.flags |= (nolock ? NFS_MOUNT_NONLM : 0);
+	if (nfsvers > MAX_NFSPROT || mountvers > MAX_NFSPROT) {
+		bb_error_msg("NFSv%d not supported", nfsvers);
+		goto fail;
+	}
+	if (nfsvers && !mountvers)
+		mountvers = (nfsvers < 3) ? 1 : nfsvers;
+	if (nfsvers && nfsvers < mountvers) {
+		mountvers = nfsvers;
+	}
+
+	/* Adjust options if none specified */
+	if (!data.timeo)
+		data.timeo = tcp ? 70 : 7;
+
+	data.version = nfs_mount_version;
+
+	if (vfsflags & MS_REMOUNT)
+		goto do_mount;
+
+	/*
+	 * If the previous mount operation on the same host was
+	 * backgrounded, and the "bg" for this mount is also set,
+	 * give up immediately, to avoid the initial timeout.
+	 */
+	if (bg && we_saw_this_host_before(hostname)) {
+		daemonized = daemonize();
+		if (daemonized <= 0) { /* parent or error */
+			retval = -daemonized;
+			goto ret;
+		}
+	}
+
+	/* Create mount daemon client */
+	/* See if the nfs host = mount host. */
+	if (mounthost) {
+		if (mounthost[0] >= '0' && mounthost[0] <= '9') {
+			mount_server_addr.sin_family = AF_INET;
+			mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
+		} else {
+			hp = gethostbyname(mounthost);
+			if (hp == NULL) {
+				bb_herror_msg("%s", mounthost);
+				goto fail;
+			}
+			if (hp->h_length != (int)sizeof(struct in_addr)) {
+				bb_error_msg_and_die("only IPv4 is supported");
+			}
+			mount_server_addr.sin_family = AF_INET;
+			memcpy(&mount_server_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+		}
+	}
+
+	/*
+	 * The following loop implements the mount retries. When the mount
+	 * times out, and the "bg" option is set, we background ourself
+	 * and continue trying.
+	 *
+	 * The case where the mount point is not present and the "bg"
+	 * option is set, is treated as a timeout. This is done to
+	 * support nested mounts.
+	 *
+	 * The "retry" count specified by the user is the number of
+	 * minutes to retry before giving up.
+	 */
+	{
+		struct timeval total_timeout;
+		struct timeval retry_timeout;
+		struct pmap pm_mnt;
+		time_t t;
+		time_t prevt;
+		time_t timeout;
+
+		retry_timeout.tv_sec = 3;
+		retry_timeout.tv_usec = 0;
+		total_timeout.tv_sec = 20;
+		total_timeout.tv_usec = 0;
+/* FIXME: use monotonic()? */
+		timeout = time(NULL) + 60 * retry;
+		prevt = 0;
+		t = 30;
+ retry:
+		/* Be careful not to use too many CPU cycles */
+		if (t - prevt < 30)
+			sleep(30);
+
+		get_mountport(&pm_mnt, &mount_server_addr,
+				mountprog,
+				mountvers,
+				proto,
+				mountport);
+		nfsvers = (pm_mnt.pm_vers < 2) ? 2 : pm_mnt.pm_vers;
+
+		/* contact the mount daemon via TCP */
+		mount_server_addr.sin_port = htons(pm_mnt.pm_port);
+		msock = RPC_ANYSOCK;
+
+		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);
+			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);
+			break;
+		default:
+			mclient = NULL;
+		}
+		if (!mclient) {
+			if (!daemonized && prevt == 0)
+				error_msg_rpc(clnt_spcreateerror(" "));
+		} else {
+			enum clnt_stat clnt_stat;
+
+			/* Try to mount hostname:pathname */
+			mclient->cl_auth = authunix_create_default();
+
+			/* Make pointers in xdr_mountres3 NULL so
+			 * that xdr_array allocates memory for us
+			 */
+			memset(&status, 0, sizeof(status));
+
+			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);
+			else
+				clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
+					      (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 */
+			if (errno != ECONNREFUSED) {
+				error_msg_rpc(clnt_sperror(mclient, " "));
+				goto fail;	/* don't retry */
+			}
+			/* Connection refused */
+			if (!daemonized && prevt == 0) /* print just once */
+				error_msg_rpc(clnt_sperror(mclient, " "));
+			auth_destroy(mclient->cl_auth);
+			clnt_destroy(mclient);
+			mclient = NULL;
+			close(msock);
+			msock = -1;
+		}
+
+		/* Timeout. We are going to retry... maybe */
+		if (!bg)
+			goto fail;
+		if (!daemonized) {
+			daemonized = daemonize();
+			if (daemonized <= 0) { /* parent or error */
+				retval = -daemonized;
+				goto ret;
+			}
+		}
+		prevt = t;
+		t = time(NULL);
+		if (t >= timeout)
+			/* TODO error message */
+			goto fail;
+
+		goto retry;
+	}
+
+ prepare_kernel_data:
+
+	if (nfsvers == 2) {
+		if (status.nfsv2.fhs_status != 0) {
+			bb_error_msg("%s:%s failed, reason given by server: %s",
+				hostname, pathname,
+				nfs_strerror(status.nfsv2.fhs_status));
+			goto fail;
+		}
+		memcpy(data.root.data,
+				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+				NFS_FHSIZE);
+		data.root.size = NFS_FHSIZE;
+		memcpy(data.old_root.data,
+				(char *) status.nfsv2.fhstatus_u.fhs_fhandle,
+				NFS_FHSIZE);
+	} else {
+		fhandle3 *my_fhandle;
+		if (status.nfsv3.fhs_status != 0) {
+			bb_error_msg("%s:%s failed, reason given by server: %s",
+				hostname, pathname,
+				nfs_strerror(status.nfsv3.fhs_status));
+			goto fail;
+		}
+		my_fhandle = &status.nfsv3.mountres3_u.mountinfo.fhandle;
+		memset(data.old_root.data, 0, NFS_FHSIZE);
+		memset(&data.root, 0, sizeof(data.root));
+		data.root.size = my_fhandle->fhandle3_len;
+		memcpy(data.root.data,
+				(char *) my_fhandle->fhandle3_val,
+				my_fhandle->fhandle3_len);
+
+		data.flags |= NFS_MOUNT_VER3;
+	}
+
+	/* Create nfs socket for kernel */
+	if (tcp) {
+		if (nfs_mount_version < 3) {
+			bb_error_msg("NFS over TCP is not supported");
+			goto fail;
+		}
+		fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+	} else
+		fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	if (fsock < 0) {
+		bb_perror_msg("nfs socket");
+		goto fail;
+	}
+	if (bindresvport(fsock, 0) < 0) {
+		bb_perror_msg("nfs bindresvport");
+		goto fail;
+	}
+	if (port == 0) {
+		server_addr.sin_port = PMAPPORT;
+		port = pmap_getport(&server_addr, nfsprog, nfsvers,
+					tcp ? IPPROTO_TCP : IPPROTO_UDP);
+		if (port == 0)
+			port = NFS_PORT;
+	}
+	server_addr.sin_port = htons(port);
+
+	/* Prepare data structure for kernel */
+	data.fd = fsock;
+	memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
+	strncpy(data.hostname, hostname, sizeof(data.hostname));
+
+	/* Clean up */
+	auth_destroy(mclient->cl_auth);
+	clnt_destroy(mclient);
+	close(msock);
+	msock = -1;
+
+	if (bg) {
+		/* We must wait until mount directory is available */
+		struct stat statbuf;
+		int delay = 1;
+		while (stat(mp->mnt_dir, &statbuf) == -1) {
+			if (!daemonized) {
+				daemonized = daemonize();
+				if (daemonized <= 0) { /* parent or error */
+/* FIXME: parent doesn't close fsock - ??! */
+					retval = -daemonized;
+					goto ret;
+				}
+			}
+			sleep(delay);	/* 1, 2, 4, 8, 16, 30, ... */
+			delay *= 2;
+			if (delay > 30)
+				delay = 30;
+		}
+	}
+
+	/* Perform actual mount */
+ do_mount:
+	mp->mnt_type = (char*)"nfs";
+	retval = mount_it_now(mp, vfsflags, (char*)&data);
+	goto ret;
+
+	/* Abort */
+ fail:
+	if (msock >= 0) {
+		if (mclient) {
+			auth_destroy(mclient->cl_auth);
+			clnt_destroy(mclient);
+		}
+		close(msock);
+	}
+	if (fsock >= 0)
+		close(fsock);
+
+ ret:
+	free(hostname);
+	free(mounthost);
+	free(filteropts);
+	return retval;
+}
+
+#else // !ENABLE_FEATURE_MOUNT_NFS
+
+// Never called. Call should be optimized out.
+int nfsmount(struct mntent *mp, long vfsflags, char *filteropts);
+
+#endif // !ENABLE_FEATURE_MOUNT_NFS
+
+// Mount one directory.  Handles CIFS, NFS, loopback, autobind, and filesystem
+// type detection.  Returns 0 for success, nonzero for failure.
+// NB: mp->xxx fields may be trashed on exit
+static int singlemount(struct mntent *mp, int ignore_busy)
+{
+	int rc = -1;
+	long vfsflags;
+	char *loopFile = NULL, *filteropts = NULL;
+	llist_t *fl = NULL;
+	struct stat st;
+
+	errno = 0;
+
+	vfsflags = parse_mount_options(mp->mnt_opts, &filteropts);
+
+	// Treat fstype "auto" as unspecified
+	if (mp->mnt_type && strcmp(mp->mnt_type, "auto") == 0)
+		mp->mnt_type = NULL;
+
+	// Might this be a virtual filesystem?
+	if (ENABLE_FEATURE_MOUNT_HELPERS && strchr(mp->mnt_fsname, '#')) {
+		char *args[35];
+		char *s;
+		int n;
+		// fsname: "cmd#arg1#arg2..."
+		// WARNING: allows execution of arbitrary commands!
+		// Try "mount 'sh#-c#sh' bogus_dir".
+		// It is safe ONLY because non-root
+		// cannot use two-argument mount command
+		// and using one-argument "mount 'sh#-c#sh'" doesn't work:
+		// "mount: can't find sh#-c#sh in /etc/fstab"
+		// (if /etc/fstab has it, it's ok: root sets up /etc/fstab).
+
+		s = mp->mnt_fsname;
+		n = 0;
+		args[n++] = s;
+		while (*s && n < 35 - 2) {
+			if (*s++ == '#' && *s != '#') {
+				s[-1] = '\0';
+				args[n++] = s;
+			}
+		}
+		args[n++] = mp->mnt_dir;
+		args[n] = NULL;
+		rc = spawn_and_wait(args);
+		goto report_error;
+	}
+
+	// Might this be an CIFS filesystem?
+	if (ENABLE_FEATURE_MOUNT_CIFS
+	 && (!mp->mnt_type || strcmp(mp->mnt_type, "cifs") == 0)
+	 && (mp->mnt_fsname[0] == '/' || mp->mnt_fsname[0] == '\\')
+	 && mp->mnt_fsname[0] == mp->mnt_fsname[1]
+	) {
+		int len;
+		char c;
+		len_and_sockaddr *lsa;
+		char *hostname, *dotted, *ip;
+
+		hostname = mp->mnt_fsname + 2;
+		len = strcspn(hostname, "/\\");
+		if (len == 0 || hostname[len] == '\0')
+			goto report_error;
+		c = hostname[len];
+		hostname[len] = '\0';
+		lsa = host2sockaddr(hostname, 0);
+		hostname[len] = c;
+		if (!lsa)
+			goto report_error;
+
+		// Insert "ip=..." option into options
+		dotted = xmalloc_sockaddr2dotted_noport(&lsa->u.sa);
+		if (ENABLE_FEATURE_CLEAN_UP) free(lsa);
+		ip = xasprintf("ip=%s", dotted);
+		if (ENABLE_FEATURE_CLEAN_UP) free(dotted);
+		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);
+
+		goto report_error;
+	}
+
+	// Might this be an NFS filesystem?
+	if (ENABLE_FEATURE_MOUNT_NFS
+	 && (!mp->mnt_type || strcmp(mp->mnt_type, "nfs") == 0)
+	 && strchr(mp->mnt_fsname, ':') != NULL
+	) {
+		rc = nfsmount(mp, vfsflags, filteropts);
+		goto report_error;
+	}
+
+	// Look at the file.  (Not found isn't a failure for remount, or for
+	// a synthetic filesystem like proc or sysfs.)
+	// (We use stat, not lstat, in order to allow
+	// mount symlink_to_file_or_blkdev dir)
+	if (!stat(mp->mnt_fsname, &st)
+	 && !(vfsflags & (MS_REMOUNT | MS_BIND | MS_MOVE))
+	) {
+		// Do we need to allocate a loopback device for it?
+		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 (errno == EPERM || errno == EACCES)
+					bb_error_msg(bb_msg_perm_denied_are_you_root);
+				else
+					bb_perror_msg("can't setup loop device");
+				return errno;
+			}
+
+		// Autodetect bind mounts
+		} else if (S_ISDIR(st.st_mode) && !mp->mnt_type)
+			vfsflags |= MS_BIND;
+	}
+
+	// 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))) {
+		rc = mount_it_now(mp, vfsflags, filteropts);
+	} else {
+		// Loop through filesystem types until mount succeeds
+		// or we run out
+
+		// Initialize list of block backed filesystems.
+		// This has to be done here so that during "mount -a",
+		// mounts after /proc shows up can autodetect.
+		if (!fslist) {
+			fslist = get_block_backed_filesystems();
+			if (ENABLE_FEATURE_CLEAN_UP && fslist)
+				atexit(delete_block_backed_filesystems);
+		}
+
+		for (fl = fslist; fl; fl = fl->link) {
+			mp->mnt_type = fl->data;
+			rc = mount_it_now(mp, vfsflags, filteropts);
+			if (!rc)
+				break;
+		}
+	}
+
+	// If mount failed, clean up loop file (if any).
+	if (ENABLE_FEATURE_MOUNT_LOOP && rc && loopFile) {
+		del_loop(mp->mnt_fsname);
+		if (ENABLE_FEATURE_CLEAN_UP) {
+			free(loopFile);
+			free(mp->mnt_fsname);
+		}
+	}
+
+ report_error:
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(filteropts);
+
+	if (errno == EBUSY && ignore_busy)
+		return 0;
+	if (rc != 0)
+		bb_perror_msg("mounting %s on %s failed", mp->mnt_fsname, mp->mnt_dir);
+	return rc;
+}
+
+// -O support
+//    -O interprets a list of filter options which select whether a mount
+// point will be mounted: only mounts with options matching *all* filtering
+// options will be selected.
+//    By default each -O filter option must be present in the list of mount
+// options, but if it is prefixed by "no" then it must be absent.
+// For example,
+//  -O a,nob,c  matches  -o a,c  but fails to match  -o a,b,c
+//              (and also fails to match  -o a  because  -o c  is absent).
+//
+// It is different from -t in that each option is matched exactly; a leading
+// "no" at the beginning of one option does not negate the rest.
+static int match_opt(const char *fs_opt_in, const char *O_opt)
+{
+	if (!O_opt)
+		return 1;
+
+	while (*O_opt) {
+		const char *fs_opt = fs_opt_in;
+		int O_len;
+		int match;
+
+		// If option begins with "no" then treat as an inverted match:
+		// matching is a failure
+		match = 0;
+		if (O_opt[0] == 'n' && O_opt[1] == 'o') {
+			match = 1;
+			O_opt += 2;
+		}
+		// Isolate the current O option
+		O_len = strchrnul(O_opt, ',') - O_opt;
+		// Check for a match against existing options
+		while (1) {
+			if (strncmp(fs_opt, O_opt, O_len) == 0
+			 && (fs_opt[O_len] == '\0' || fs_opt[O_len] == ',')
+			) {
+				if (match)
+					return 0;  // "no" prefix, but option found
+				match = 1;  // current O option found, go check next one
+				break;
+			}
+			fs_opt = strchr(fs_opt, ',');
+			if (!fs_opt)
+				break;
+			fs_opt++;
+		}
+		if (match == 0)
+			return 0;     // match wanted but not found
+		if (O_opt[O_len] == '\0') // end?
+			break;
+		// Step to the next O option
+		O_opt += O_len + 1;
+	}
+	// If we get here then everything matched
+	return 1;
+}
+
+// Parse options, if necessary parse fstab/mtab, and call singlemount for
+// each directory to be mounted.
+int mount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int mount_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *cmdopts = xzalloc(1);
+	char *fstype = NULL;
+	char *O_optmatch = NULL;
+	char *storage_path;
+	llist_t *lst_o = NULL;
+	const char *fstabname;
+	FILE *fstab;
+	int i, j;
+	int rc = EXIT_SUCCESS;
+	unsigned opt;
+	struct mntent mtpair[2], *mtcur = mtpair;
+	IF_NOT_DESKTOP(const int nonroot = 0;)
+
+	IF_DESKTOP(int nonroot = ) sanitize_env_if_suid();
+
+	// 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++) {
+		if (argv[i][0] == '-' && argv[i][1] == '-')
+			append_mount_options(&cmdopts, argv[i] + 2);
+		else
+			argv[j++] = argv[i];
+	}
+	argv[j] = NULL;
+
+	// Parse remaining options
+	// Max 2 params; -o is a list, -v is a counter
+	opt_complementary = "?2o::" IF_FEATURE_MOUNT_VERBOSE("vv");
+	opt = getopt32(argv, OPTION_STR, &lst_o, &fstype, &O_optmatch
+			IF_FEATURE_MOUNT_VERBOSE(, &verbose));
+	while (lst_o) append_mount_options(&cmdopts, llist_pop(&lst_o)); // -o
+	if (opt & OPT_r) append_mount_options(&cmdopts, "ro"); // -r
+	if (opt & OPT_w) append_mount_options(&cmdopts, "rw"); // -w
+	argv += optind;
+
+	// If we have no arguments, show currently mounted filesystems
+	if (!argv[0]) {
+		if (!(opt & OPT_a)) {
+			FILE *mountTable = setmntent(bb_path_mtab_file, "r");
+
+			if (!mountTable)
+				bb_error_msg_and_die("no %s", bb_path_mtab_file);
+
+			while (getmntent_r(mountTable, &mtpair[0], getmntent_buf,
+								GETMNTENT_BUFSIZE))
+			{
+				// Don't show rootfs. FIXME: why??
+				// util-linux 2.12a happily shows rootfs...
+				//if (strcmp(mtpair->mnt_fsname, "rootfs") == 0) continue;
+
+				if (!fstype || strcmp(mtpair->mnt_type, fstype) == 0)
+					printf("%s on %s type %s (%s)\n", mtpair->mnt_fsname,
+							mtpair->mnt_dir, mtpair->mnt_type,
+							mtpair->mnt_opts);
+			}
+			if (ENABLE_FEATURE_CLEAN_UP)
+				endmntent(mountTable);
+			return EXIT_SUCCESS;
+		}
+		storage_path = NULL;
+	} else {
+		// When we have two arguments, the second is the directory and we can
+		// skip looking at fstab entirely.  We can always abspath() the directory
+		// argument when we get it.
+		if (argv[1]) {
+			if (nonroot)
+				bb_error_msg_and_die(bb_msg_you_must_be_root);
+			mtpair->mnt_fsname = argv[0];
+			mtpair->mnt_dir = argv[1];
+			mtpair->mnt_type = fstype;
+			mtpair->mnt_opts = cmdopts;
+			resolve_mount_spec(&mtpair->mnt_fsname);
+			rc = singlemount(mtpair, /*ignore_busy:*/ 0);
+			return rc;
+		}
+		storage_path = bb_simplify_path(argv[0]); // malloced
+	}
+
+	// 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
+		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))
+	) {
+		// verbose_mount(source, target, type, flags, data)
+		rc = verbose_mount("", argv[0], "", i, "");
+		if (rc)
+			bb_simple_perror_msg_and_die(argv[0]);
+		return rc;
+	}
+
+	// Open either fstab or mtab
+	fstabname = "/etc/fstab";
+	if (i & 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).
+		fstabname = bb_path_mtab_file;
+	}
+	fstab = setmntent(fstabname, "r");
+	if (!fstab)
+		bb_perror_msg_and_die("can't read '%s'", fstabname);
+
+	// Loop through entries until we find what we're looking for
+	memset(mtpair, 0, sizeof(mtpair));
+	for (;;) {
+		struct mntent *mtother = (mtcur==mtpair ? mtpair+1 : mtpair);
+
+		// Get next fstab entry
+		if (!getmntent_r(fstab, mtcur, getmntent_buf
+					+ (mtcur==mtpair ? GETMNTENT_BUFSIZE/2 : 0),
+				GETMNTENT_BUFSIZE/2)
+		) { // End of fstab/mtab is reached
+			mtcur = mtother; // the thing we found last time
+			break;
+		}
+
+		// If we're trying to mount something specific and this isn't it,
+		// skip it.  Note we must match the exact text in fstab (ala
+		// "proc") or a full path from root
+		if (argv[0]) {
+
+			// Is this what we're looking for?
+			if (strcmp(argv[0], mtcur->mnt_fsname) != 0
+			 && strcmp(storage_path, mtcur->mnt_fsname) != 0
+			 && strcmp(argv[0], mtcur->mnt_dir) != 0
+			 && strcmp(storage_path, mtcur->mnt_dir) != 0
+			) {
+				continue; // no
+			}
+
+			// Remember this entry.  Something later may have
+			// overmounted it, and we want the _last_ match.
+			mtcur = mtother;
+
+		// If we're mounting all
+		} else {
+			struct mntent *mp;
+			// No, mount -a won't mount anything,
+			// even user mounts, for mere humans
+			if (nonroot)
+				bb_error_msg_and_die(bb_msg_you_must_be_root);
+
+			// Does type match? (NULL matches always)
+			if (!match_fstype(mtcur, fstype))
+				continue;
+
+			// Skip noauto and swap anyway
+			if ((parse_mount_options(mtcur->mnt_opts, NULL) & (MOUNT_NOAUTO | MOUNT_SWAP))
+			// swap is bogus "fstype", parse_mount_options can't check fstypes
+			 || strcasecmp(mtcur->mnt_type, "swap") == 0
+			) {
+				continue;
+			}
+
+			// Does (at least one) option match?
+			// (NULL matches always)
+			if (!match_opt(mtcur->mnt_opts, O_optmatch))
+				continue;
+
+			resolve_mount_spec(&mtcur->mnt_fsname);
+
+			// NFS mounts want this to be xrealloc-able
+			mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+
+			// If nothing is mounted on this directory...
+			// (otherwise repeated "mount -a" mounts everything again)
+			mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
+			// We do not check fsname match of found mount point -
+			// "/" may have fsname of "/dev/root" while fstab
+			// says "/dev/something_else".
+			if (mp) {
+				if (verbose) {
+					bb_error_msg("according to %s, "
+						"%s is already mounted on %s",
+						bb_path_mtab_file,
+						mp->mnt_fsname, mp->mnt_dir);
+				}
+			} else {
+				// ...mount this thing
+				if (singlemount(mtcur, /*ignore_busy:*/ 1)) {
+					// Count number of failed mounts
+					rc++;
+				}
+			}
+			free(mtcur->mnt_opts);
+		}
+	}
+
+	// End of fstab/mtab is reached.
+	// Were we looking for something specific?
+	if (argv[0]) { // yes
+		long l;
+
+		// If we didn't find anything, complain
+		if (!mtcur->mnt_fsname)
+			bb_error_msg_and_die("can't find %s in %s",
+				argv[0], fstabname);
+
+		// What happens when we try to "mount swap_partition"?
+		// (fstab containts "swap_partition swap swap defaults 0 0")
+		// util-linux-ng 2.13.1 does this:
+		// stat("/sbin/mount.swap", 0x7fff62a3a350) = -1 ENOENT (No such file or directory)
+		// mount("swap_partition", "swap", "swap", MS_MGC_VAL, NULL) = -1 ENOENT (No such file or directory)
+		// lstat("swap", 0x7fff62a3a640)           = -1 ENOENT (No such file or directory)
+		// write(2, "mount: mount point swap does not exist\n", 39) = 39
+		// exit_group(32)                          = ?
+#if 0
+		// In case we want to simply skip swap partitions:
+		l = parse_mount_options(mtcur->mnt_opts, NULL);
+		if ((l & MOUNT_SWAP)
+		// swap is bogus "fstype", parse_mount_options can't check fstypes
+		 || strcasecmp(mtcur->mnt_type, "swap") == 0
+		) {
+			goto ret;
+		}
+#endif
+		if (nonroot) {
+			// fstab must have "users" or "user"
+			l = parse_mount_options(mtcur->mnt_opts, NULL);
+			if (!(l & MOUNT_USERS))
+				bb_error_msg_and_die(bb_msg_you_must_be_root);
+		}
+
+		//util-linux-2.12 does not do this check.
+		//// If nothing is mounted on this directory...
+		//// (otherwise repeated "mount FOO" mounts FOO again)
+		//mp = find_mount_point(mtcur->mnt_dir, /*subdir_too:*/ 0);
+		//if (mp) {
+		//	bb_error_msg("according to %s, "
+		//		"%s is already mounted on %s",
+		//		bb_path_mtab_file,
+		//		mp->mnt_fsname, mp->mnt_dir);
+		//} else {
+			// ...mount the last thing we found
+			mtcur->mnt_opts = xstrdup(mtcur->mnt_opts);
+			append_mount_options(&(mtcur->mnt_opts), cmdopts);
+			resolve_mount_spec(&mtpair->mnt_fsname);
+			rc = singlemount(mtcur, /*ignore_busy:*/ 0);
+			if (ENABLE_FEATURE_CLEAN_UP)
+				free(mtcur->mnt_opts);
+		//}
+	}
+
+ //ret:
+	if (ENABLE_FEATURE_CLEAN_UP)
+		endmntent(fstab);
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		free(storage_path);
+		free(cmdopts);
+	}
+
+//TODO: exitcode should be ORed mask of (from "man mount"):
+// 0 success
+// 1 incorrect invocation or permissions
+// 2 system error (out of memory, cannot fork, no more loop devices)
+// 4 internal mount bug or missing nfs support in mount
+// 8 user interrupt
+//16 problems writing or locking /etc/mtab
+//32 mount failure
+//64 some mount succeeded
+	return rc;
+}
diff --git a/busybox-1.19.3/util-linux/pivot_root.c b/busybox-1.19.3/util-linux/pivot_root.c
new file mode 100644
index 0000000..83f01fa
--- /dev/null
+++ b/busybox-1.19.3/util-linux/pivot_root.c
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * pivot_root.c - Change root file system.  Based on util-linux 2.10s
+ *
+ * busyboxed by Evin Robertson
+ * pivot_root syscall stubbed by Erik Andersen, so it will compile
+ *     regardless of the kernel being used.
+ *
+ * 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);
+
+int pivot_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int pivot_root_main(int argc, char **argv)
+{
+	if (argc != 3)
+		bb_show_usage();
+
+	if (pivot_root(argv[1], argv[2]) < 0) {
+		/* prints "pivot_root: <strerror text>" */
+		bb_perror_nomsg_and_die();
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/rdate.c b/busybox-1.19.3/util-linux/rdate.c
new file mode 100644
index 0000000..1f36d8f
--- /dev/null
+++ b/busybox-1.19.3/util-linux/rdate.c
@@ -0,0 +1,78 @@
+/* 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.
+ *
+ * by Sterling Huxley <sterling@europa.com>
+ *
+ * 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 and time from a remote HOST\n"
+//usage:     "\n	-s	Set the system date and time (default)"
+//usage:     "\n	-p	Print the date and time"
+
+#include "libbb.h"
+
+enum { RFC_868_BIAS = 2208988800UL };
+
+static void socket_timeout(int sig UNUSED_PARAM)
+{
+	bb_error_msg_and_die("timeout connecting to time server");
+}
+
+static time_t askremotedate(const char *host)
+{
+	uint32_t nett;
+	int fd;
+
+	/* Add a timeout for dead or inaccessible servers */
+	alarm(10);
+	signal(SIGALRM, socket_timeout);
+
+	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 */
+		bb_error_msg_and_die("%s did not send the complete time", host);
+	close(fd);
+
+	/* convert from network byte order to local byte order.
+	 * RFC 868 time is the number of seconds
+	 * since 00:00 (midnight) 1 January 1900 GMT
+	 * the RFC 868 time 2,208,988,800 corresponds to 00:00  1 Jan 1970 GMT
+	 * Subtract the RFC 868 time to get Linux epoch
+	 */
+
+	return ntohl(nett) - RFC_868_BIAS;
+}
+
+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;
+
+	opt_complementary = "-1";
+	flags = getopt32(argv, "sp");
+
+	remote_time = askremotedate(argv[optind]);
+
+	if ((flags & 2) == 0) {
+		time_t current_time;
+
+		time(&current_time);
+		if (current_time == remote_time)
+			bb_error_msg("current time matches remote time");
+		else
+			if (stime(&remote_time) < 0)
+				bb_perror_msg_and_die("can't set time of day");
+	}
+
+	if ((flags & 1) == 0)
+		printf("%s", ctime(&remote_time));
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/rdev.c b/busybox-1.19.3/util-linux/rdev.c
new file mode 100644
index 0000000..c6a17ea
--- /dev/null
+++ b/busybox-1.19.3/util-linux/rdev.c
@@ -0,0 +1,34 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * rdev - print device node associated with a filesystem
+ *
+ * Copyright (c) 2008 Nuovation System Designs, LLC
+ *   Grant Erickson <gerickson@nuovations.com>
+ *
+ * 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 root_device = find_block_device("/");
+
+	if (root_device != NULL) {
+		printf("%s /\n", root_device);
+		free(root_device);
+		return EXIT_SUCCESS;
+	}
+	return EXIT_FAILURE;
+}
diff --git a/busybox-1.19.3/util-linux/readprofile.c b/busybox-1.19.3/util-linux/readprofile.c
new file mode 100644
index 0000000..4ed8011
--- /dev/null
+++ b/busybox-1.19.3/util-linux/readprofile.c
@@ -0,0 +1,263 @@
+/* vi: set sw=4 ts=4: */
+/*
+ *  readprofile.c - used to read /proc/profile
+ *
+ *  Copyright (C) 1994,1996 Alessandro Rubini (rubini@ipvvis.unipv.it)
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+/*
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * 1999-09-01 Stephane Eranian <eranian@cello.hpl.hp.com>
+ * - 64bit clean patch
+ * 3Feb2001 Andrew Morton <andrewm@uow.edu.au>
+ * - -M option to write profile multiplier.
+ * 2001-11-07 Werner Almesberger <wa@almesberger.net>
+ * - byte order auto-detection and -n option
+ * 2001-11-09 Werner Almesberger <wa@almesberger.net>
+ * - skip step size (index 0)
+ * 2002-03-09 John Levon <moz@compsoc.man.ac.uk>
+ * - make maplineno do something
+ * 2002-11-28 Mads Martin Joergensen +
+ * - also try /boot/System.map-`uname -r`
+ * 2003-04-09 Werner Almesberger <wa@almesberger.net>
+ * - fixed off-by eight error and improved heuristics in byte order detection
+ * 2003-08-12 Nikita Danilov <Nikita@Namesys.COM>
+ * - added -s option; example of use:
+ * "readprofile -s -m /boot/System.map-test | grep __d_lookup | sort -n -k3"
+ *
+ * Taken from util-linux and adapted for busybox by
+ * 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>
+
+#define S_LEN 128
+
+/* These are the defaults */
+static const char defaultmap[] ALIGN1 = "/boot/System.map";
+static const char defaultpro[] ALIGN1 = "/proc/profile";
+
+int readprofile_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int readprofile_main(int argc UNUSED_PARAM, char **argv)
+{
+	FILE *map;
+	const char *mapFile, *proFile;
+	unsigned long indx = 1;
+	size_t len;
+	uint64_t add0 = 0;
+	unsigned int step;
+	unsigned int *buf, total, fn_len;
+	unsigned long long fn_add, next_add;     /* current and next address */
+	char fn_name[S_LEN], next_name[S_LEN];   /* current and next name */
+	char mapline[S_LEN];
+	char mode[8];
+	int maplineno = 1;
+	int header_printed;
+	int multiplier = 0;
+	unsigned opt;
+	enum {
+		OPT_M = (1 << 0),
+		OPT_m = (1 << 1),
+		OPT_p = (1 << 2),
+		OPT_n = (1 << 3),
+		OPT_a = (1 << 4),
+		OPT_b = (1 << 5),
+		OPT_s = (1 << 6),
+		OPT_i = (1 << 7),
+		OPT_r = (1 << 8),
+		OPT_v = (1 << 9),
+	};
+#define optMult    (opt & OPT_M)
+#define optNative  (opt & OPT_n)
+#define optAll     (opt & OPT_a)
+#define optBins    (opt & OPT_b)
+#define optSub     (opt & OPT_s)
+#define optInfo    (opt & OPT_i)
+#define optReset   (opt & OPT_r)
+#define optVerbose (opt & OPT_v)
+
+#define next (current^1)
+
+	proFile = defaultpro;
+	mapFile = defaultmap;
+
+	opt_complementary = "M+"; /* -M N */
+	opt = getopt32(argv, "M:m:p:nabsirv", &multiplier, &mapFile, &proFile);
+
+	if (opt & (OPT_M|OPT_r)) { /* mult or reset, or both */
+		int fd, to_write;
+
+		/*
+		 * When writing the multiplier, if the length of the write is
+		 * not sizeof(int), the multiplier is not changed
+		 */
+		to_write = sizeof(int);
+		if (!optMult)
+			to_write = 1;  /* sth different from sizeof(int) */
+
+		fd = xopen(defaultpro, O_WRONLY);
+		xwrite(fd, &multiplier, to_write);
+		close(fd);
+		return EXIT_SUCCESS;
+	}
+
+	/*
+	 * Use an fd for the profiling buffer, to skip stdio overhead
+	 */
+	len = MAXINT(ssize_t);
+	buf = xmalloc_xopen_read_close(proFile, &len);
+	if (!optNative) {
+		int entries = len / sizeof(*buf);
+		int big = 0, small = 0, i;
+		unsigned *p;
+
+		for (p = buf+1; p < buf+entries; p++) {
+			if (*p & ~0U << (sizeof(*buf)*4))
+				big++;
+			if (*p & ((1 << (sizeof(*buf)*4))-1))
+				small++;
+		}
+		if (big > small) {
+			bb_error_msg("assuming reversed byte order, "
+				"use -n to force native byte order");
+			for (p = buf; p < buf+entries; p++)
+				for (i = 0; i < sizeof(*buf)/2; i++) {
+					unsigned char *b = (unsigned char *) p;
+					unsigned char tmp;
+
+					tmp = b[i];
+					b[i] = b[sizeof(*buf)-i-1];
+					b[sizeof(*buf)-i-1] = tmp;
+				}
+		}
+	}
+
+	step = buf[0];
+	if (optInfo) {
+		printf("Sampling_step: %i\n", step);
+		return EXIT_SUCCESS;
+	}
+
+	total = 0;
+
+	map = xfopen_for_read(mapFile);
+
+	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);
+
+		if (!strcmp(fn_name, "_stext")) /* only elf works like this */ {
+			add0 = fn_add;
+			break;
+		}
+		maplineno++;
+	}
+
+	if (!add0)
+		bb_error_msg_and_die("can't find \"_stext\" in %s", mapFile);
+
+	/*
+	 * Main loop.
+	 */
+	while (fgets(mapline, S_LEN, map)) {
+		unsigned int this = 0;
+
+		if (sscanf(mapline, "%llx %s %s", &next_add, mode, next_name) != 3)
+			bb_error_msg_and_die("%s(%i): wrong map line",
+					mapFile, maplineno);
+
+		header_printed = 0;
+
+		/* ignore any LEADING (before a '[tT]' symbol is found)
+		   Absolute symbols */
+		if ((*mode == 'A' || *mode == '?') && total == 0) continue;
+		if (*mode != 'T' && *mode != 't'
+		 && *mode != 'W' && *mode != 'w'
+		) {
+			break;  /* only text is profiled */
+		}
+
+		if (indx >= len / sizeof(*buf))
+			bb_error_msg_and_die("profile address out of range. "
+					     "Wrong map file?");
+
+		while (indx < (next_add-add0)/step) {
+			if (optBins && (buf[indx] || optAll)) {
+				if (!header_printed) {
+					printf("%s:\n", fn_name);
+					header_printed = 1;
+				}
+				printf("\t%"PRIx64"\t%u\n", (indx - 1)*step + add0, buf[indx]);
+			}
+			this += buf[indx++];
+		}
+		total += this;
+
+		if (optBins) {
+			if (optVerbose || this > 0)
+				printf("  total\t\t\t\t%u\n", this);
+		} else if ((this || optAll)
+		        && (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);
+			else
+				printf("%6i %-40s %8.4f\n",
+				       this, fn_name, this/(double)fn_len);
+			if (optSub) {
+				unsigned long long scan;
+
+				for (scan = (fn_add-add0)/step + 1;
+				     scan < (next_add-add0)/step; scan++) {
+					unsigned long long addr;
+
+					addr = (scan - 1)*step + add0;
+					printf("\t%#llx\t%s+%#llx\t%u\n",
+					       addr, fn_name, addr - fn_add,
+					       buf[scan]);
+				}
+			}
+		}
+
+		fn_add = next_add;
+		strcpy(fn_name, next_name);
+
+		maplineno++;
+	}
+
+	/* clock ticks, out of kernel text - probably modules */
+	printf("%6i %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));
+	else
+		printf("%6i %-40s %8.4f\n",
+		       total, "total", total/(double)(fn_add-add0));
+
+	fclose(map);
+	free(buf);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/rev.c b/busybox-1.19.3/util-linux/rev.c
new file mode 100644
index 0000000..3c1b22f
--- /dev/null
+++ b/busybox-1.19.3/util-linux/rev.c
@@ -0,0 +1,121 @@
+/*
+ * rev implementation for busybox
+ *
+ * Copyright (C) 2010 Marek Polacek <mmpolacek@gmail.com>
+ *
+ * Licensed under GPLv2, see file LICENSE in this source tree.
+ */
+
+//applet:IF_REV(APPLET(rev, BB_DIR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_REV) += rev.o
+
+//config:config REV
+//config:	bool "rev"
+//config:	default y
+//config:	help
+//config:	  Reverse lines of a file or files.
+
+//usage:#define rev_trivial_usage
+//usage:	"[FILE]..."
+//usage:#define rev_full_usage "\n\n"
+//usage:	"Reverse lines of FILE"
+
+#include "libbb.h"
+#include "unicode.h"
+
+#undef CHAR_T
+#if ENABLE_UNICODE_SUPPORT
+# define CHAR_T wchar_t
+#else
+# define CHAR_T char
+#endif
+
+/* In-place invert */
+static void strrev(CHAR_T *s, int len)
+{
+	int i;
+
+	if (len != 0) {
+		len--;
+		if (len != 0 && s[len] == '\n')
+			len--;
+	}
+
+	for (i = 0; i < len; i++, len--) {
+		CHAR_T c = s[i];
+		s[i] = s[len];
+		s[len] = c;
+	}
+}
+
+int rev_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rev_main(int argc UNUSED_PARAM, char **argv)
+{
+	int retval;
+	size_t bufsize;
+	char *buf;
+
+	init_unicode();
+
+	getopt32(argv, "");
+	argv += optind;
+	if (!argv[0])
+		argv = (char **)&bb_argv_dash;
+
+	retval = EXIT_SUCCESS;
+	bufsize = 256;
+	buf = xmalloc(bufsize);
+	do {
+		size_t pos;
+		FILE *fp;
+
+		fp = fopen_or_warn_stdin(*argv++);
+		if (!fp) {
+			retval = EXIT_FAILURE;
+			continue;
+		}
+
+		pos = 0;
+		while (1) {
+			/* Read one line */
+			buf[bufsize - 1] = 1; /* not 0 */
+			if (!fgets(buf + pos, bufsize - pos, fp))
+				break; /* EOF/error */
+			if (buf[bufsize - 1] == '\0' /* fgets filled entire buffer */
+			 && buf[bufsize - 2] != '\n' /* and did not read '\n' */
+			 && !feof(fp)
+			) {
+				/* Line is too long, extend buffer */
+				pos = bufsize - 1;
+				bufsize += 64 + bufsize / 8;
+				buf = xrealloc(buf, bufsize);
+				continue;
+			}
+
+			/* Process and print it */
+#if ENABLE_UNICODE_SUPPORT
+			{
+				wchar_t *tmp = xmalloc(bufsize * sizeof(wchar_t));
+				/* Convert to wchar_t (might error out!) */
+				int len  = mbstowcs(tmp, buf, bufsize);
+				if (len >= 0) {
+					strrev(tmp, len);
+					/* Convert back to char */
+					wcstombs(buf, tmp, bufsize);
+				}
+				free(tmp);
+			}
+#else
+			strrev(buf, strlen(buf));
+#endif
+			fputs(buf, stdout);
+		}
+		fclose(fp);
+	} while (*argv);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		free(buf);
+
+	fflush_stdout_and_exit(retval);
+}
diff --git a/busybox-1.19.3/util-linux/rtcwake.c b/busybox-1.19.3/util-linux/rtcwake.c
new file mode 100644
index 0000000..735a298
--- /dev/null
+++ b/busybox-1.19.3/util-linux/rtcwake.c
@@ -0,0 +1,226 @@
+/*
+ * rtcwake -- enter a system sleep state until specified wakeup time.
+ *
+ * This version was taken from util-linux and scrubbed down for busybox.
+ *
+ * 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
+ * driver that supports standard driver model wakeup flags.
+ *
+ * This is normally used like the old "apmsleep" utility, to wake from a
+ * suspend state like ACPI S1 (standby) or S3 (suspend-to-RAM).  Most
+ * platforms can implement those without analogues of BIOS, APM, or ACPI.
+ *
+ * On some systems, this can also be used like "nvram-wakeup", waking
+ * from states like ACPI S4 (suspend to disk).  Not all systems have
+ * persistent media that are appropriate for such suspend modes.
+ *
+ * The best way to set the system's RTC is so that it holds the current
+ * time in UTC.  Use the "-l" flag to tell this program that the system
+ * RTC uses a local timezone instead (maybe you dual-boot MS-Windows).
+ * 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"
+
+#define SYS_RTC_PATH   "/sys/class/rtc/%s/device/power/wakeup"
+#define SYS_POWER_PATH "/sys/power/state"
+#define DEFAULT_MODE   "standby"
+
+static NOINLINE bool may_wakeup(const char *rtcname)
+{
+	ssize_t ret;
+	char buf[128];
+
+	/* strip "/dev/" from the rtcname here */
+	rtcname = skip_dev_pfx(rtcname);
+
+	snprintf(buf, sizeof(buf), SYS_RTC_PATH, rtcname);
+	ret = open_read_close(buf, buf, sizeof(buf));
+	if (ret < 0)
+		return false;
+
+	/* wakeup events could be disabled or not supported */
+	return strncmp(buf, "enabled\n", 8) == 0;
+}
+
+static NOINLINE void setup_alarm(int fd, time_t *wakeup, time_t rtc_time)
+{
+	struct tm *ptm;
+	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
+	 * if they need to boot MS-Windows.  Messy...
+	 *
+	 * When running in utc mode this process's timezone is UTC,
+	 * so we'll pass a UTC date to the RTC.
+	 *
+	 * Else mode is local so the time given to the RTC
+	 * will instead use the local time zone.
+	 */
+	ptm = localtime(wakeup);
+
+	wake.time.tm_sec = ptm->tm_sec;
+	wake.time.tm_min = ptm->tm_min;
+	wake.time.tm_hour = ptm->tm_hour;
+	wake.time.tm_mday = ptm->tm_mday;
+	wake.time.tm_mon = ptm->tm_mon;
+	wake.time.tm_year = ptm->tm_year;
+	/* wday, yday, and isdst fields are unused by Linux */
+	wake.time.tm_wday = -1;
+	wake.time.tm_yday = -1;
+	wake.time.tm_isdst = -1;
+
+	/* many rtc alarms only support up to 24 hours from 'now',
+	 * so use the "more than 24 hours" request only if we must
+	 */
+	if ((rtc_time + (24 * 60 * 60)) > *wakeup) {
+		xioctl(fd, RTC_ALM_SET, &wake.time);
+		xioctl(fd, RTC_AIE_ON, 0);
+	} else {
+		/* avoid an extra AIE_ON call */
+		wake.enabled = 1;
+		xioctl(fd, RTC_WKALM_SET, &wake);
+	}
+}
+
+#define RTCWAKE_OPT_AUTO         0x01
+#define RTCWAKE_OPT_LOCAL        0x02
+#define RTCWAKE_OPT_UTC          0x04
+#define RTCWAKE_OPT_DEVICE       0x08
+#define RTCWAKE_OPT_SUSPEND_MODE 0x10
+#define RTCWAKE_OPT_SECONDS      0x20
+#define RTCWAKE_OPT_TIME         0x40
+
+int rtcwake_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int rtcwake_main(int argc UNUSED_PARAM, char **argv)
+{
+	time_t rtc_time;
+
+	unsigned opt;
+	const char *rtcname = NULL;
+	const char *suspend;
+	const char *opt_seconds;
+	const char *opt_time;
+
+	time_t sys_time;
+	time_t alarm_time = 0;
+	unsigned seconds = 0;
+	int utc = -1;
+	int fd;
+
+#if ENABLE_LONG_OPTS
+	static const char rtcwake_longopts[] ALIGN1 =
+		"auto\0"    No_argument "a"
+		"local\0"   No_argument "l"
+		"utc\0"     No_argument "u"
+		"device\0"  Required_argument "d"
+		"mode\0"    Required_argument "m"
+		"seconds\0" Required_argument "s"
+		"time\0"    Required_argument "t"
+		;
+	applet_long_options = rtcwake_longopts;
+#endif
+	opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time);
+
+	/* this is the default
+	if (opt & RTCWAKE_OPT_AUTO)
+		utc = -1;
+	*/
+	if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL))
+		utc = opt & RTCWAKE_OPT_UTC;
+	if (!(opt & RTCWAKE_OPT_SUSPEND_MODE))
+		suspend = DEFAULT_MODE;
+	if (opt & RTCWAKE_OPT_SECONDS)
+		/* alarm time, seconds-to-sleep (relative) */
+		seconds = xatoi(opt_seconds);
+	if (opt & RTCWAKE_OPT_TIME)
+		/* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */
+		alarm_time = xatol(opt_time);
+
+	if (!alarm_time && !seconds)
+		bb_error_msg_and_die("must provide wake time");
+
+	if (utc == -1)
+		utc = rtc_adjtime_is_utc();
+
+	/* the rtcname is relative to /dev */
+	xchdir("/dev");
+
+	/* this RTC must exist and (if we'll sleep) be wakeup-enabled */
+	fd = rtc_xopen(&rtcname, O_RDONLY);
+
+	if (strcmp(suspend, "on") && !may_wakeup(rtcname))
+		bb_error_msg_and_die("%s not enabled for wakeup events", rtcname);
+
+	/* relative or absolute alarm time, normalized to time_t */
+	sys_time = time(NULL);
+	{
+		struct tm tm_time;
+		rtc_read_tm(&tm_time, fd);
+		rtc_time = rtc_tm2time(&tm_time, utc);
+	}
+
+
+	if (alarm_time) {
+		if (alarm_time < sys_time)
+			bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time));
+		alarm_time += sys_time - rtc_time;
+	} else
+		alarm_time = rtc_time + seconds + 1;
+	setup_alarm(fd, &alarm_time, rtc_time);
+
+	sync();
+	printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time));
+	fflush_all();
+	usleep(10 * 1000);
+
+	if (strcmp(suspend, "on"))
+		xopen_xwrite_close(SYS_POWER_PATH, suspend);
+	else {
+		/* "fake" suspend ... we'll do the delay ourselves */
+		unsigned long data;
+
+		do {
+			ssize_t ret = safe_read(fd, &data, sizeof(data));
+			if (ret < 0) {
+				bb_perror_msg("rtc read");
+				break;
+			}
+		} while (!(data & RTC_AF));
+	}
+
+	xioctl(fd, RTC_AIE_OFF, 0);
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close(fd);
+
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/script.c b/busybox-1.19.3/util-linux/script.c
new file mode 100644
index 0000000..8fb991d
--- /dev/null
+++ b/busybox-1.19.3/util-linux/script.c
@@ -0,0 +1,204 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * script implementation for busybox
+ *
+ * pascal.bellard@ads-lu.com
+ *
+ * Based on code from util-linux v 2.12r
+ * Copyright (c) 1980
+ * The Regents of the University of California.  All rights reserved.
+ *
+ * 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;
+int script_main(int argc UNUSED_PARAM, char **argv)
+{
+	int opt;
+	int mode;
+	int child_pid;
+	int attr_ok; /* NB: 0: ok */
+	int winsz_ok;
+	int pty;
+	char pty_line[GETPTY_BUFSIZE];
+	struct termios tt, rtt;
+	struct winsize win;
+	const char *fname = "typescript";
+	const char *shell;
+	char shell_opt[] = "-i";
+	char *shell_arg = NULL;
+	enum {
+		OPT_a = (1 << 0),
+		OPT_c = (1 << 1),
+		OPT_f = (1 << 2),
+		OPT_q = (1 << 3),
+		OPT_t = (1 << 4),
+	};
+
+#if ENABLE_LONG_OPTS
+	static const char getopt_longopts[] ALIGN1 =
+		"append\0"  No_argument       "a"
+		"command\0" Required_argument "c"
+		"flush\0"   No_argument       "f"
+		"quiet\0"   No_argument       "q"
+		IF_SCRIPTREPLAY("timing\0" No_argument "t")
+		;
+
+	applet_long_options = getopt_longopts;
+#endif
+
+	opt_complementary = "?1"; /* max one arg */
+	opt = getopt32(argv, "ac:fq" IF_SCRIPTREPLAY("t") , &shell_arg);
+	//argc -= optind;
+	argv += optind;
+	if (argv[0]) {
+		fname = argv[0];
+	}
+	mode = O_CREAT|O_TRUNC|O_WRONLY;
+	if (opt & OPT_a) {
+		mode = O_CREAT|O_APPEND|O_WRONLY;
+	}
+	if (opt & OPT_c) {
+		shell_opt[1] = 'c';
+	}
+	if (!(opt & OPT_q)) {
+		printf("Script started, file is %s\n", fname);
+	}
+	shell = get_shell_name();
+
+	pty = xgetpty(pty_line);
+
+	/* get current stdin's tty params */
+	attr_ok = tcgetattr(0, &tt);
+	winsz_ok = ioctl(0, TIOCGWINSZ, (char *)&win);
+
+	rtt = tt;
+	cfmakeraw(&rtt);
+	rtt.c_lflag &= ~ECHO;
+	tcsetattr(0, TCSAFLUSH, &rtt);
+
+	/* "script" from util-linux exits when child exits,
+	 * we wouldn't wait for EOF from slave pty
+	 * (output may be produced by grandchildren of child) */
+	signal(SIGCHLD, record_signo);
+
+	/* TODO: SIGWINCH? pass window size changes down to slave? */
+
+	child_pid = xvfork();
+
+	if (child_pid) {
+		/* parent */
+#define buf bb_common_bufsiz1
+		struct pollfd pfd[2];
+		int outfd, count, loop;
+		double oldtime = ENABLE_SCRIPTREPLAY ? time(NULL) : 0;
+		smallint fd_count = 2;
+
+		outfd = xopen(fname, mode);
+		pfd[0].fd = pty;
+		pfd[0].events = POLLIN;
+		pfd[1].fd = STDIN_FILENO;
+		pfd[1].events = POLLIN;
+		ndelay_on(pty); /* this descriptor is not shared, can do this */
+		/* ndelay_on(STDIN_FILENO); - NO, stdin can be shared! Pity :( */
+
+		/* copy stdin to pty master input,
+		 * copy pty master output to stdout and file */
+		/* TODO: don't use full_write's, use proper write buffering */
+		while (fd_count && !bb_got_signal) {
+			/* not safe_poll! we want SIGCHLD to EINTR poll */
+			if (poll(pfd, fd_count, -1) < 0 && errno != EINTR) {
+				/* If child exits too quickly, we may get EIO:
+				 * for example, try "script -c true" */
+				break;
+			}
+			if (pfd[0].revents) {
+				errno = 0;
+				count = safe_read(pty, buf, sizeof(buf));
+				if (count <= 0 && errno != EAGAIN) {
+					/* err/eof from pty: exit */
+					goto restore;
+				}
+				if (count > 0) {
+					if (ENABLE_SCRIPTREPLAY && (opt & OPT_t)) {
+						struct timeval tv;
+						double newtime;
+
+						gettimeofday(&tv, NULL);
+						newtime = tv.tv_sec + (double) tv.tv_usec / 1000000;
+						fprintf(stderr, "%f %u\n", newtime - oldtime, count);
+						oldtime = newtime;
+					}
+					full_write(STDOUT_FILENO, buf, count);
+					full_write(outfd, buf, count);
+					if (opt & OPT_f) {
+						fsync(outfd);
+					}
+				}
+			}
+			if (pfd[1].revents) {
+				count = safe_read(STDIN_FILENO, buf, sizeof(buf));
+				if (count <= 0) {
+					/* err/eof from stdin: don't read stdin anymore */
+					pfd[1].revents = 0;
+					fd_count--;
+				} else {
+					full_write(pty, buf, count);
+				}
+			}
+		}
+		/* If loop was exited because SIGCHLD handler set bb_got_signal,
+		 * there still can be some buffered output. But dont loop forever:
+		 * we won't pump orphaned grandchildren's output indefinitely.
+		 * Testcase: running this in script:
+		 *      exec dd if=/dev/zero bs=1M count=1
+		 * must have "1+0 records in, 1+0 records out" captured too.
+		 * (util-linux's script doesn't do this. buggy :) */
+		loop = 999;
+		/* pty is in O_NONBLOCK mode, we exit as soon as buffer is empty */
+		while (--loop && (count = safe_read(pty, buf, sizeof(buf))) > 0) {
+			full_write(STDOUT_FILENO, buf, count);
+			full_write(outfd, buf, count);
+		}
+ restore:
+		if (attr_ok == 0)
+			tcsetattr(0, TCSAFLUSH, &tt);
+		if (!(opt & OPT_q))
+			printf("Script done, file is %s\n", fname);
+		return EXIT_SUCCESS;
+	}
+
+	/* child: make pty slave to be input, output, error; run shell */
+	close(pty); /* close pty master */
+	/* open pty slave to fd 0,1,2 */
+	close(0);
+	xopen(pty_line, O_RDWR); /* uses fd 0 */
+	xdup2(0, 1);
+	xdup2(0, 2);
+	/* copy our original stdin tty's parameters to pty */
+	if (attr_ok == 0)
+		tcsetattr(0, TCSAFLUSH, &tt);
+	if (winsz_ok == 0)
+		ioctl(0, TIOCSWINSZ, (char *)&win);
+	/* set pty as a controlling tty */
+	setsid();
+	ioctl(0, TIOCSCTTY, 0 /* 0: don't forcibly steal */);
+
+	/* Non-ignored signals revert to SIG_DFL on exec anyway */
+	/*signal(SIGCHLD, SIG_DFL);*/
+	execl(shell, shell, shell_opt, shell_arg, (char *) NULL);
+	bb_simple_perror_msg_and_die(shell);
+}
diff --git a/busybox-1.19.3/util-linux/scriptreplay.c b/busybox-1.19.3/util-linux/scriptreplay.c
new file mode 100644
index 0000000..382f56d
--- /dev/null
+++ b/busybox-1.19.3/util-linux/scriptreplay.c
@@ -0,0 +1,47 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * scriptreplay - play back typescripts, using timing information
+ *
+ * pascal.bellard@ads-lu.com
+ *
+ * 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;
+int scriptreplay_main(int argc UNUSED_PARAM, char **argv)
+{
+	const char *script = "typescript";
+	double delay, factor = 1000000.0;
+	int fd;
+	unsigned long count;
+	FILE *tfp;
+
+	if (!argv[1])
+		bb_show_usage();
+
+	if (argv[2]) {
+		script = argv[2];
+		if (argv[3])
+			factor /= atof(argv[3]);
+	}
+
+	tfp = xfopen_for_read(argv[1]);
+	fd = xopen(script, O_RDONLY);
+	while (fscanf(tfp, "%lf %lu\n", &delay, &count) == 2) {
+		usleep(delay * factor);
+		bb_copyfd_exact_size(fd, STDOUT_FILENO, count);
+	}
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		close(fd);
+		fclose(tfp);
+	}
+	return EXIT_SUCCESS;
+}
diff --git a/busybox-1.19.3/util-linux/setarch.c b/busybox-1.19.3/util-linux/setarch.c
new file mode 100644
index 0000000..7b9421a
--- /dev/null
+++ b/busybox-1.19.3/util-linux/setarch.c
@@ -0,0 +1,61 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * linux32/linux64 allows for changing uname emulation.
+ *
+ * Copyright 2002 Andi Kleen, SuSE Labs.
+ *
+ * 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>
+
+#include "libbb.h"
+
+int setarch_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int setarch_main(int argc UNUSED_PARAM, char **argv)
+{
+	int pers;
+
+	/* Figure out what personality we are supposed to switch to ...
+	 * we can be invoked as either:
+	 * argv[0],argv[1] == "setarch","personality"
+	 * argv[0]         == "personality"
+	 */
+	if (ENABLE_SETARCH && applet_name[0] == 's'
+	 && argv[1] && strncpy(argv[1], "linux", 5)
+	) {
+		applet_name = argv[1];
+		argv++;
+	}
+	if (applet_name[5] == '6') /* linux64 */
+		pers = PER_LINUX;
+	else if (applet_name[5] == '3') /* linux32 */
+		pers = PER_LINUX32;
+	else
+		bb_show_usage();
+
+	argv++;
+	if (argv[0] == NULL)
+		bb_show_usage();
+
+	/* Try to set personality */
+	if (personality(pers) >= 0) {
+		/* Try to execute the program */
+		BB_EXECVP(argv[0], argv);
+	}
+
+	bb_simple_perror_msg_and_die(argv[0]);
+}
diff --git a/busybox-1.19.3/util-linux/swaponoff.c b/busybox-1.19.3/util-linux/swaponoff.c
new file mode 100644
index 0000000..e53e24c
--- /dev/null
+++ b/busybox-1.19.3/util-linux/swaponoff.c
@@ -0,0 +1,141 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini swapon/swapoff implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ *
+ * 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"
+#else
+# 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;
+} FIX_ALIASING;
+#define G (*(struct globals*)&bb_common_bufsiz1)
+#define g_flags (G.flags)
+#else
+#define g_flags 0
+#endif
+
+static int swap_enable_disable(char *device)
+{
+	int status;
+	struct stat st;
+
+	resolve_mount_spec(&device);
+	xstat(device, &st);
+
+#if ENABLE_DESKTOP
+	/* test for holes */
+	if (S_ISREG(st.st_mode))
+		if (st.st_blocks * (off_t)512 < st.st_size)
+			bb_error_msg("warning: swap file has holes");
+#endif
+
+	if (applet_name[5] == 'n')
+		status = swapon(device, g_flags);
+	else
+		status = swapoff(device);
+
+	if (status != 0) {
+		bb_simple_perror_msg(device);
+		return 1;
+	}
+
+	return 0;
+}
+
+static int do_em_all(void)
+{
+	struct mntent *m;
+	FILE *f;
+	int err;
+
+	f = setmntent("/etc/fstab", "r");
+	if (f == NULL)
+		bb_perror_msg_and_die("/etc/fstab");
+
+	err = 0;
+	while ((m = getmntent(f)) != NULL) {
+		if (strcmp(m->mnt_type, MNTTYPE_SWAP) == 0) {
+			/* swapon -a should ignore entries with noauto,
+			 * but swapoff -a should process them */
+			if (applet_name[5] != 'n'
+			 || hasmntopt(m, MNTOPT_NOAUTO) == NULL
+			) {
+				err += swap_enable_disable(m->mnt_fsname);
+			}
+		}
+	}
+
+	if (ENABLE_FEATURE_CLEAN_UP)
+		endmntent(f);
+
+	return err;
+}
+
+int swap_on_off_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int swap_on_off_main(int argc UNUSED_PARAM, char **argv)
+{
+	int ret;
+
+#if !ENABLE_FEATURE_SWAPON_PRI
+	ret = getopt32(argv, "a");
+#else
+	if (applet_name[5] == 'n')
+		opt_complementary = "p+";
+	ret = getopt32(argv, (applet_name[5] == 'n') ? "ap:" : "a", &g_flags);
+
+	if (ret & 2) { // -p
+		g_flags = SWAP_FLAG_PREFER |
+			((g_flags & SWAP_FLAG_PRIO_MASK) << SWAP_FLAG_PRIO_SHIFT);
+		ret &= 1;
+	}
+#endif
+
+	if (ret /* & 1: not needed */) // -a
+		return do_em_all();
+
+	argv += optind;
+	if (!*argv)
+		bb_show_usage();
+
+	/* ret = 0; redundant */
+	do {
+		ret += swap_enable_disable(*argv);
+	} while (*++argv);
+
+	return ret;
+}
diff --git a/busybox-1.19.3/util-linux/switch_root.c b/busybox-1.19.3/util-linux/switch_root.c
new file mode 100644
index 0000000..db6ae35
--- /dev/null
+++ b/busybox-1.19.3/util-linux/switch_root.c
@@ -0,0 +1,219 @@
+/* vi: set sw=4 ts=4: */
+/* Copyright 2005 Rob Landley <rob@landley.net>
+ *
+ * Switch from rootfs to another filesystem as the root of the mount tree.
+ *
+ * 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"
+// Make up for header deficiencies
+#ifndef RAMFS_MAGIC
+# define RAMFS_MAGIC ((unsigned)0x858458f6)
+#endif
+#ifndef TMPFS_MAGIC
+# define TMPFS_MAGIC ((unsigned)0x01021994)
+#endif
+#ifndef MS_MOVE
+# define MS_MOVE     8192
+#endif
+
+// Recursively delete contents of rootfs
+static void delete_contents(const char *directory, dev_t rootdev)
+{
+	DIR *dir;
+	struct dirent *d;
+	struct stat st;
+
+	// Don't descend into other filesystems
+	if (lstat(directory, &st) || st.st_dev != rootdev)
+		return;
+
+	// Recursively delete the contents of directories
+	if (S_ISDIR(st.st_mode)) {
+		dir = opendir(directory);
+		if (dir) {
+			while ((d = readdir(dir))) {
+				char *newdir = d->d_name;
+
+				// Skip . and ..
+				if (DOT_OR_DOTDOT(newdir))
+					continue;
+
+				// Recurse to delete contents
+				newdir = concat_path_file(directory, newdir);
+				delete_contents(newdir, rootdev);
+				free(newdir);
+			}
+			closedir(dir);
+
+			// Directory should now be empty, zap it
+			rmdir(directory);
+		}
+	} else {
+		// It wasn't a directory, zap it
+		unlink(directory);
+	}
+}
+
+int switch_root_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int switch_root_main(int argc UNUSED_PARAM, char **argv)
+{
+	char *newroot, *console = NULL;
+	struct stat st;
+	struct statfs stfs;
+	dev_t rootdev;
+
+	// Parse args (-c console)
+	opt_complementary = "-2"; // minimum 2 params
+	getopt32(argv, "+c:", &console); // '+': stop at first non-option
+	argv += optind;
+	newroot = *argv++;
+
+	// Change to new root directory and verify it's a different fs
+	xchdir(newroot);
+	xstat("/", &st);
+	rootdev = st.st_dev;
+	xstat(".", &st);
+	if (st.st_dev == rootdev || getpid() != 1) {
+		// Show usage, it says new root must be a mountpoint
+		// and we must be PID 1
+		bb_show_usage();
+	}
+
+	// Additional sanity checks: we're about to rm -rf /, so be REALLY SURE
+	// we mean it. I could make this a CONFIG option, but I would get email
+	// from all the people who WILL destroy their filesystems.
+	if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) {
+		bb_error_msg_and_die("/init is not a regular file");
+	}
+	statfs("/", &stfs); // this never fails
+	if ((unsigned)stfs.f_type != RAMFS_MAGIC
+	 && (unsigned)stfs.f_type != TMPFS_MAGIC
+	) {
+		bb_error_msg_and_die("root filesystem is not ramfs/tmpfs");
+	}
+
+	// Zap everything out of rootdev
+	delete_contents("/", rootdev);
+
+	// Overmount / with newdir and chroot into it
+	if (mount(".", "/", NULL, MS_MOVE, NULL)) {
+		// For example, fails when newroot is not a mountpoint
+		bb_perror_msg_and_die("error moving root");
+	}
+	xchroot(".");
+	// The chdir is needed to recalculate "." and ".." links
+	xchdir("/");
+
+	// If a new console specified, redirect stdin/stdout/stderr to it
+	if (console) {
+		close(0);
+		xopen(console, O_RDWR);
+		xdup2(0, 1);
+		xdup2(0, 2);
+	}
+
+	// Exec real init
+	execv(argv[0], argv);
+	bb_perror_msg_and_die("can't execute '%s'", argv[0]);
+}
+
+/*
+From: Rob Landley <rob@landley.net>
+Date: Tue, Jun 16, 2009 at 7:47 PM
+Subject: Re: switch_root...
+
+...
+...
+...
+
+If you're _not_ running out of init_ramfs (if for example you're using initrd
+instead), you probably shouldn't use switch_root because it's the wrong tool.
+
+Basically what the sucker does is something like the following shell script:
+
+ find / -xdev | xargs rm -rf
+ cd "$1"
+ shift
+ mount --move . /
+ exec chroot . "$@"
+
+There are a couple reasons that won't work as a shell script:
+
+1) If you delete the commands out of your $PATH, your shell scripts can't run
+more commands, but you can't start using dynamically linked _new_ commands
+until after you do the chroot because the path to the dynamic linker is wrong.
+So there's a step that needs to be sort of atomic but can't be as a shell
+script.  (You can work around this with static linking or very carefully laid
+out paths and sequencing, but it's brittle, ugly, and non-obvious.)
+
+2) The "find | rm" bit will acually delete everything because the mount points
+still show up (even if their contents don't), and rm -rf will then happily zap
+that.  So the first line is an oversimplification of what you need to do _not_
+to descend into other filesystems and delete their contents.
+
+The reason we do this is to free up memory, by the way.  Since initramfs is a
+ramfs, deleting its contents frees up the memory it uses.  (We leave it with
+one remaining dentry for the new mount point, but that's ok.)
+
+Note that you cannot ever umount rootfs, for approximately the same reason you
+can't kill PID 1.  The kernel tracks mount points as a doubly linked list, and
+the pointer to the start/end of that list always points to an entry that's
+known to be there (rootfs), so it never has to worry about moving that pointer
+and it never has to worry about the list being empty.  (Back around 2.6.13
+there _was_ a bug that let you umount rootfs, and the system locked hard the
+instant you did so endlessly looping to find the end of the mount list and
+never stopping.  They fixed it.)
+
+Oh, and the reason we mount --move _and_ do the chroot is due to the way "/"
+works.  Each process has two special symlinks, ".", and "/".  Each of them
+points to the dentry of a directory, and give you a location paths can start
+from.  (Historically ".." was also special, because you could enter a
+directory via a symlink so backing out to the directory you came from doesn't
+necessarily mean the one physically above where "." points to.  These days I
+think it's just handed off to the filesystem.)
+
+Anyway, path resolution starts with "." or "/" (although the "./" at the start
+of the path may be implicit), meaning it's relative to one of those two
+directories.  Your current directory, and your current root directory.  The
+chdir() syscall changes where "." points to, and the chroot() syscall changes
+where "/" points to.  (Again, both are per-process which is why chroot only
+affects your current process and its child processes.)
+
+Note that chroot() does _not_ change where "." points to, and back before they
+put crazy security checks into the kernel your current directory could be
+somewhere you could no longer access after the chroot.  (The command line
+chroot does a cd as well, the chroot _syscall_ is what I'm talking about.)
+
+The reason mounting something new over / has no obvious effect is the same
+reason mounting something over your current directory has no obvious effect:
+the . and / links aren't recalculated after a mount, so they still point to
+the same dentry they did before, even if that dentry is no longer accessible
+by other means.  Note that "cd ." is a NOP, and "chroot /" is a nop; both look
+up the cached dentry and set it right back.  They don't re-parse any paths,
+because they're what all paths your process uses would be relative to.
+
+That's why the careful sequencing above: we cd into the new mount point before
+we do the mount --move.  Moving the mount point would otherwise make it
+totally inaccessible to is because cd-ing to the old path wouldn't give it to
+us anymore, and cd "/" just gives us the cached dentry from when the process
+was created (in this case the old initramfs one).  But the "." symlink gives
+us the dentry of the filesystem we just moved, so we can then "chroot ." to
+copy that dentry to "/" and get the new filesystem.  If we _didn't_ save that
+dentry in "." we couldn't get it back after the mount --move.
+
+(Yes, this is all screwy and I had to email questions to Linus Torvalds to get
+it straight myself.  I keep meaning to write up a "how mount actually works"
+document someday...)
+*/
diff --git a/busybox-1.19.3/util-linux/umount.c b/busybox-1.19.3/util-linux/umount.c
new file mode 100644
index 0000000..5b716c6
--- /dev/null
+++ b/busybox-1.19.3/util-linux/umount.c
@@ -0,0 +1,198 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini umount implementation for busybox
+ *
+ * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
+ * Copyright (C) 2005 by Rob Landley <rob@landley.net>
+ *
+ * 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	Free loop device if it has been used"
+//usage:	)
+//usage:
+//usage:#define umount_example_usage
+//usage:       "$ umount /dev/hdc1\n"
+
+#include <mntent.h>
+#include <sys/mount.h>
+#include "libbb.h"
+
+#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,
+		char* buffer UNUSED_PARAM, int bufsize UNUSED_PARAM)
+{
+	struct mntent* ment = getmntent(stream);
+	return memcpy(result, ment, sizeof(*ment));
+}
+#endif
+
+/* ignored: -v -d -t -i */
+#define OPTION_STRING           "fldnra" "vdt:i"
+#define OPT_FORCE               (1 << 0) // Same as MNT_FORCE
+#define OPT_LAZY                (1 << 1) // Same as MNT_DETACH
+#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)
+
+int umount_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int umount_main(int argc UNUSED_PARAM, char **argv)
+{
+	int doForce;
+	struct mntent me;
+	FILE *fp;
+	char *fstype = NULL;
+	int status = EXIT_SUCCESS;
+	unsigned opt;
+	struct mtab_list {
+		char *dir;
+		char *device;
+		struct mtab_list *next;
+	} *mtl, *m;
+
+	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
+	 * for umount -a (so we don't have to worry about the list changing while
+	 * we iterate over it, or about getting stuck in a loop on the same failing
+	 * entry.  Notice that this also naturally reverses the list so that -a
+	 * umounts the most recent entries first. */
+	m = mtl = NULL;
+
+	// If we're umounting all, then m points to the start of the list and
+	// the argument list should be empty (which will match all).
+	fp = setmntent(bb_path_mtab_file, "r");
+	if (!fp) {
+		if (opt & OPT_ALL)
+			bb_error_msg_and_die("can't open '%s'", bb_path_mtab_file);
+	} else {
+		while (getmntent_r(fp, &me, bb_common_bufsiz1, sizeof(bb_common_bufsiz1))) {
+			/* Match fstype if passed */
+			if (!match_fstype(&me, fstype))
+				continue;
+			m = xzalloc(sizeof(*m));
+			m->next = mtl;
+			m->device = xstrdup(me.mnt_fsname);
+			m->dir = xstrdup(me.mnt_dir);
+			mtl = m;
+		}
+		endmntent(fp);
+	}
+
+	// If we're not umounting all, we need at least one argument.
+	if (!(opt & OPT_ALL) && !fstype) {
+		if (!argv[0])
+			bb_show_usage();
+		m = NULL;
+	}
+
+	// Loop through everything we're supposed to umount, and do so.
+	for (;;) {
+		int curstat;
+		char *zapit = *argv;
+		char *path;
+
+		// Do we already know what to umount this time through the loop?
+		if (m)
+			path = xstrdup(m->dir);
+		// For umount -a, end of mtab means time to exit.
+		else if (opt & OPT_ALL)
+			break;
+		// Use command line argument (and look it up in mtab list)
+		else {
+			if (!zapit)
+				break;
+			argv++;
+			path = xmalloc_realpath(zapit);
+			if (path) {
+				for (m = mtl; m; m = m->next)
+					if (strcmp(path, m->dir) == 0 || strcmp(path, m->device) == 0)
+						break;
+			}
+		}
+		// If we couldn't find this sucker in /etc/mtab, punt by passing our
+		// command line argument straight to the umount syscall.  Otherwise,
+		// umount the directory even if we were given the block device.
+		if (m) zapit = m->dir;
+
+		// Let's ask the thing nicely to unmount.
+		curstat = umount(zapit);
+
+		// Force the unmount, if necessary.
+		if (curstat && doForce)
+			curstat = umount2(zapit, doForce);
+
+		// If still can't umount, maybe remount read-only?
+		if (curstat) {
+			if ((opt & OPT_REMOUNT) && errno == EBUSY && m) {
+				// 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);
+				if (curstat) {
+					msg = "can't remount %s read-only";
+					status = EXIT_FAILURE;
+				}
+				bb_error_msg(msg, m->device);
+			} else {
+				status = EXIT_FAILURE;
+				bb_perror_msg("can't %sumount %s", (doForce ? "forcibly " : ""), zapit);
+			}
+		} 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)
+				del_loop(m->device);
+			if (ENABLE_FEATURE_MTAB_SUPPORT && !(opt & OPT_NO_MTAB) && m)
+				erase_mtab(m->dir);
+		}
+
+		// Find next matching mtab entry for -a or umount /dev
+		// Note this means that "umount /dev/blah" will unmount all instances
+		// of /dev/blah, not just the most recent.
+		if (m) {
+			while ((m = m->next) != NULL)
+				// NB: if m is non-NULL, path is non-NULL as well
+				if ((opt & OPT_ALL) || strcmp(path, m->device) == 0)
+					break;
+		}
+		free(path);
+	}
+
+	// Free mtab list if necessary
+	if (ENABLE_FEATURE_CLEAN_UP) {
+		while (mtl) {
+			m = mtl->next;
+			free(mtl->device);
+			free(mtl->dir);
+			free(mtl);
+			mtl = m;
+		}
+	}
+
+	return status;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/Kbuild.src b/busybox-1.19.3/util-linux/volume_id/Kbuild.src
new file mode 100644
index 0000000..70da654
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/Kbuild.src
@@ -0,0 +1,45 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# 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
diff --git a/busybox-1.19.3/util-linux/volume_id/btrfs.c b/busybox-1.19.3/util-linux/volume_id/btrfs.c
new file mode 100644
index 0000000..777b809
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/btrfs.c
@@ -0,0 +1,107 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2009 Vladimir Dronnikov <dronnikov@gmail.com>
+ *
+ *	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
+ */
+
+#include "volume_id_internal.h"
+
+#define BTRFS_UUID_SIZE 16
+#define BTRFS_LABEL_SIZE 256
+#define BTRFS_CSUM_SIZE 32
+#define BTRFS_FSID_SIZE 16
+
+#define BTRFS_MAGIC "_BHRfS_M"
+
+struct btrfs_dev_item {
+	uint64_t devid;
+	uint64_t total_bytes;
+	uint64_t bytes_used;
+	uint32_t io_align;
+	uint32_t io_width;
+	uint32_t sector_size;
+	uint64_t type;
+	uint64_t generation;
+	uint64_t start_offset;
+	uint32_t dev_group;
+	uint8_t seek_speed;
+	uint8_t bandwidth;
+	uint8_t uuid[BTRFS_UUID_SIZE];
+	uint8_t fsid[BTRFS_UUID_SIZE];
+} PACKED;
+
+struct btrfs_super_block {
+	uint8_t csum[BTRFS_CSUM_SIZE];
+	uint8_t fsid[BTRFS_FSID_SIZE];	// UUID
+	uint64_t bytenr;
+	uint64_t flags;
+	uint8_t magic[8];
+	uint64_t generation;
+	uint64_t root;
+	uint64_t chunk_root;
+	uint64_t log_root;
+	uint64_t log_root_transid;
+	uint64_t total_bytes;
+	uint64_t bytes_used;
+	uint64_t root_dir_objectid;
+	uint64_t num_devices;
+	uint32_t sectorsize;
+	uint32_t nodesize;
+	uint32_t leafsize;
+	uint32_t stripesize;
+	uint32_t sys_chunk_array_size;
+	uint64_t chunk_root_generation;
+	uint64_t compat_flags;
+	uint64_t compat_ro_flags;
+	uint64_t incompat_flags;
+	uint16_t csum_type;
+	uint8_t root_level;
+	uint8_t chunk_root_level;
+	uint8_t log_root_level;
+	struct btrfs_dev_item dev_item;
+	uint8_t label[BTRFS_LABEL_SIZE];	// LABEL
+	// ...
+} PACKED;
+
+int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/)
+{
+	// btrfs has superblocks at 64K, 64M and 256G
+	// minimum btrfs size is 256M
+	// so we never step out the device if we analyze
+	// the first and the second superblocks
+	struct btrfs_super_block *sb;
+	unsigned off = 64;
+
+	while (off < 64*1024*1024) {
+		off *= 1024;
+		dbg("btrfs: probing at offset 0x%x", off);
+
+		sb = volume_id_get_buffer(id, off, sizeof(*sb));
+		if (sb == NULL)
+			return -1;
+
+		if (memcmp(sb->magic, BTRFS_MAGIC, 8) != 0)
+			return -1;
+	}
+
+	// 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);
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/cramfs.c b/busybox-1.19.3/util-linux/volume_id/cramfs.c
new file mode 100644
index 0000000..28e9970
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/cramfs.c
@@ -0,0 +1,59 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct cramfs_super {
+	uint32_t	magic;
+	uint32_t	size;
+	uint32_t	flags;
+	uint32_t	future;
+	uint8_t		signature[16];
+	struct cramfs_info {
+		uint32_t	crc;
+		uint32_t	edition;
+		uint32_t	blocks;
+		uint32_t	files;
+	} PACKED info;
+	uint8_t		name[16];
+} PACKED;
+
+int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct cramfs_super *cs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	cs = volume_id_get_buffer(id, off, 0x200);
+	if (cs == NULL)
+		return -1;
+
+	if (cs->magic == cpu_to_be32(0x453dcd28)) {
+//		volume_id_set_label_raw(id, cs->name, 16);
+		volume_id_set_label_string(id, cs->name, 16);
+
+//		volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+		IF_FEATURE_BLKID_TYPE(id->type = "cramfs";)
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/ext.c b/busybox-1.19.3/util-linux/volume_id/ext.c
new file mode 100644
index 0000000..b5194a7
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/ext.c
@@ -0,0 +1,76 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+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;
+
+#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*/)
+{
+#define off ((uint64_t)0)
+	struct ext2_super_block *es;
+
+	dbg("ext: probing at offset 0x%llx", (unsigned long long) off);
+
+	es = volume_id_get_buffer(id, off + EXT_SUPERBLOCK_OFFSET, 0x200);
+	if (es == NULL)
+		return -1;
+
+	if (es->magic[0] != 0123 || es->magic[1] != 0357) {
+		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);
+	dbg("ext: label '%s' uuid '%s'", id->label, id->uuid);
+
+#if ENABLE_FEATURE_BLKID_TYPE
+	if ((le32_to_cpu(es->feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0)
+		id->type = "ext3";
+	else
+		id->type = "ext2";
+#endif
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/fat.c b/busybox-1.19.3/util-linux/volume_id/fat.c
new file mode 100644
index 0000000..904fbb2
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/fat.c
@@ -0,0 +1,338 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+/* linux/msdos_fs.h says: */
+#define FAT12_MAX			0xff4
+#define FAT16_MAX			0xfff4
+#define FAT32_MAX			0x0ffffff6
+
+#define FAT_ATTR_VOLUME_ID		0x08
+#define FAT_ATTR_DIR			0x10
+#define FAT_ATTR_LONG_NAME		0x0f
+#define FAT_ATTR_MASK			0x3f
+#define FAT_ENTRY_FREE			0xe5
+
+struct vfat_super_block {
+	uint8_t		boot_jump[3];
+	uint8_t		sysid[8];
+	uint16_t	sector_size_bytes;
+	uint8_t		sectors_per_cluster;
+	uint16_t	reserved_sct;
+	uint8_t		fats;
+	uint16_t	dir_entries;
+	uint16_t	sectors;
+	uint8_t		media;
+	uint16_t	fat_length;
+	uint16_t	secs_track;
+	uint16_t	heads;
+	uint32_t	hidden;
+	uint32_t	total_sect;
+	union {
+		struct fat_super_block {
+			uint8_t		unknown[3];
+			uint8_t		serno[4];
+			uint8_t		label[11];
+			uint8_t		magic[8];
+			uint8_t		dummy2[192];
+			uint8_t		pmagic[2];
+		} PACKED fat;
+		struct fat32_super_block {
+			uint32_t	fat32_length;
+			uint16_t	flags;
+			uint8_t		version[2];
+			uint32_t	root_cluster;
+			uint16_t	insfo_sector;
+			uint16_t	backup_boot;
+			uint16_t	reserved2[6];
+			uint8_t		unknown[3];
+			uint8_t		serno[4];
+			uint8_t		label[11];
+			uint8_t		magic[8];
+			uint8_t		dummy2[164];
+			uint8_t		pmagic[2];
+		} PACKED fat32;
+	} PACKED type;
+} PACKED;
+
+struct vfat_dir_entry {
+	uint8_t		name[11];
+	uint8_t		attr;
+	uint16_t	time_creat;
+	uint16_t	date_creat;
+	uint16_t	time_acc;
+	uint16_t	date_acc;
+	uint16_t	cluster_high;
+	uint16_t	time_write;
+	uint16_t	date_write;
+	uint16_t	cluster_low;
+	uint32_t	size;
+} PACKED;
+
+static uint8_t *get_attr_volume_id(struct vfat_dir_entry *dir, int count)
+{
+	for (;--count >= 0; dir++) {
+		/* end marker */
+		if (dir->name[0] == 0x00) {
+			dbg("end of dir");
+			break;
+		}
+
+		/* empty entry */
+		if (dir->name[0] == FAT_ENTRY_FREE)
+			continue;
+
+		/* long name */
+		if ((dir->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)
+			continue;
+
+		if ((dir->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) {
+			/* labels do not have file data */
+			if (dir->cluster_high != 0 || dir->cluster_low != 0)
+				continue;
+
+			dbg("found ATTR_VOLUME_ID id in root dir");
+			return dir->name;
+		}
+
+		dbg("skip dir entry");
+	}
+
+	return NULL;
+}
+
+int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t fat_partition_off*/)
+{
+#define fat_partition_off ((uint64_t)0)
+	struct vfat_super_block *vs;
+	struct vfat_dir_entry *dir;
+	uint16_t sector_size_bytes;
+	uint16_t dir_entries;
+	uint32_t sect_count;
+	uint16_t reserved_sct;
+	uint32_t fat_size_sct;
+	uint32_t root_cluster;
+	uint32_t dir_size_sct;
+	uint32_t cluster_count;
+	uint64_t root_start_off;
+	uint32_t start_data_sct;
+	uint8_t *buf;
+	uint32_t buf_size;
+	uint8_t *label = NULL;
+	uint32_t next_cluster;
+	int maxloop;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) fat_partition_off);
+
+	vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
+	if (vs == NULL)
+		return -1;
+
+	/* believe only that's fat, don't trust the version
+	 * the cluster_count will tell us
+	 */
+	if (memcmp(vs->sysid, "NTFS", 4) == 0)
+		return -1;
+
+	if (memcmp(vs->type.fat32.magic, "MSWIN", 5) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat32.magic, "FAT32   ", 8) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat.magic, "FAT16   ", 8) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat.magic, "MSDOS", 5) == 0)
+		goto valid;
+
+	if (memcmp(vs->type.fat.magic, "FAT12   ", 8) == 0)
+		goto valid;
+
+	/*
+	 * There are old floppies out there without a magic, so we check
+	 * for well known values and guess if it's a fat volume
+	 */
+
+	/* boot jump address check */
+	if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90)
+	 && vs->boot_jump[0] != 0xe9
+	) {
+		return -1;
+	}
+
+	/* heads check */
+	if (vs->heads == 0)
+		return -1;
+
+	/* cluster size check */
+	if (vs->sectors_per_cluster == 0
+	 || (vs->sectors_per_cluster & (vs->sectors_per_cluster-1))
+	) {
+		return -1;
+	}
+
+	/* media check */
+	if (vs->media < 0xf8 && vs->media != 0xf0)
+		return -1;
+
+	/* fat count*/
+	if (vs->fats != 2)
+		return -1;
+
+ valid:
+	/* sector size check */
+	sector_size_bytes = le16_to_cpu(vs->sector_size_bytes);
+	if (sector_size_bytes != 0x200 && sector_size_bytes != 0x400
+	 && sector_size_bytes != 0x800 && sector_size_bytes != 0x1000
+	) {
+		return -1;
+	}
+
+	dbg("sector_size_bytes 0x%x", sector_size_bytes);
+	dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster);
+
+	reserved_sct = le16_to_cpu(vs->reserved_sct);
+	dbg("reserved_sct 0x%x", reserved_sct);
+
+	sect_count = le16_to_cpu(vs->sectors);
+	if (sect_count == 0)
+		sect_count = le32_to_cpu(vs->total_sect);
+	dbg("sect_count 0x%x", sect_count);
+
+	fat_size_sct = le16_to_cpu(vs->fat_length);
+	if (fat_size_sct == 0)
+		fat_size_sct = le32_to_cpu(vs->type.fat32.fat32_length);
+	fat_size_sct *= vs->fats;
+	dbg("fat_size_sct 0x%x", fat_size_sct);
+
+	dir_entries = le16_to_cpu(vs->dir_entries);
+	dir_size_sct = ((dir_entries * sizeof(struct vfat_dir_entry)) +
+			(sector_size_bytes-1)) / sector_size_bytes;
+	dbg("dir_size_sct 0x%x", dir_size_sct);
+
+	cluster_count = sect_count - (reserved_sct + fat_size_sct + dir_size_sct);
+	cluster_count /= vs->sectors_per_cluster;
+	dbg("cluster_count 0x%x", cluster_count);
+
+//	if (cluster_count < FAT12_MAX) {
+//		strcpy(id->type_version, "FAT12");
+//	} else if (cluster_count < FAT16_MAX) {
+//		strcpy(id->type_version, "FAT16");
+//	} else {
+//		strcpy(id->type_version, "FAT32");
+//		goto fat32;
+//	}
+	if (cluster_count >= FAT16_MAX)
+		goto fat32;
+
+	/* the label may be an attribute in the root directory */
+	root_start_off = (reserved_sct + fat_size_sct) * sector_size_bytes;
+	dbg("root dir start 0x%llx", (unsigned long long) root_start_off);
+	dbg("expected entries 0x%x", dir_entries);
+
+	buf_size = dir_entries * sizeof(struct vfat_dir_entry);
+	buf = volume_id_get_buffer(id, fat_partition_off + root_start_off, buf_size);
+	if (buf == NULL)
+		goto ret;
+
+	label = get_attr_volume_id((struct vfat_dir_entry*) buf, dir_entries);
+
+	vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
+	if (vs == NULL)
+		return -1;
+
+	if (label != NULL && memcmp(label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, label, 11);
+		volume_id_set_label_string(id, label, 11);
+	} else if (memcmp(vs->type.fat.label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, vs->type.fat.label, 11);
+		volume_id_set_label_string(id, vs->type.fat.label, 11);
+	}
+	volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS);
+	goto ret;
+
+ fat32:
+	/* FAT32 root dir is a cluster chain like any other directory */
+	buf_size = vs->sectors_per_cluster * sector_size_bytes;
+	root_cluster = le32_to_cpu(vs->type.fat32.root_cluster);
+	start_data_sct = reserved_sct + fat_size_sct;
+
+	next_cluster = root_cluster;
+	maxloop = 100;
+	while (--maxloop) {
+		uint64_t next_off_sct;
+		uint64_t next_off;
+		uint64_t fat_entry_off;
+		int count;
+
+		dbg("next_cluster 0x%x", (unsigned)next_cluster);
+		next_off_sct = (uint64_t)(next_cluster - 2) * vs->sectors_per_cluster;
+		next_off = (start_data_sct + next_off_sct) * sector_size_bytes;
+		dbg("cluster offset 0x%llx", (unsigned long long) next_off);
+
+		/* get cluster */
+		buf = volume_id_get_buffer(id, fat_partition_off + next_off, buf_size);
+		if (buf == NULL)
+			goto ret;
+
+		dir = (struct vfat_dir_entry*) buf;
+		count = buf_size / sizeof(struct vfat_dir_entry);
+		dbg("expected entries 0x%x", count);
+
+		label = get_attr_volume_id(dir, count);
+		if (label)
+			break;
+
+		/* get FAT entry */
+		fat_entry_off = (reserved_sct * sector_size_bytes) + (next_cluster * sizeof(uint32_t));
+		dbg("fat_entry_off 0x%llx", (unsigned long long)fat_entry_off);
+		buf = volume_id_get_buffer(id, fat_partition_off + fat_entry_off, buf_size);
+		if (buf == NULL)
+			goto ret;
+
+		/* set next cluster */
+		next_cluster = le32_to_cpu(*(uint32_t*)buf) & 0x0fffffff;
+		if (next_cluster < 2 || next_cluster > FAT32_MAX)
+			break;
+	}
+	if (maxloop == 0)
+		dbg("reached maximum follow count of root cluster chain, give up");
+
+	vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
+	if (vs == NULL)
+		return -1;
+
+	if (label != NULL && memcmp(label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, label, 11);
+		volume_id_set_label_string(id, label, 11);
+	} else if (memcmp(vs->type.fat32.label, "NO NAME    ", 11) != 0) {
+//		volume_id_set_label_raw(id, vs->type.fat32.label, 11);
+		volume_id_set_label_string(id, vs->type.fat32.label, 11);
+	}
+	volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS);
+
+ ret:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "vfat";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/get_devname.c b/busybox-1.19.3/util-linux/volume_id/get_devname.c
new file mode 100644
index 0000000..7c99305
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/get_devname.c
@@ -0,0 +1,310 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Support functions for mounting devices by label/uuid
+ *
+ * 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 source tree.
+ */
+#include <sys/mount.h> /* BLKGETSIZE64 */
+#if !defined(BLKGETSIZE64)
+# define BLKGETSIZE64 _IOR(0x12,114,size_t)
+#endif
+#include "volume_id_internal.h"
+
+static struct uuidCache_s {
+	struct uuidCache_s *next;
+//	int major, minor;
+	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, const char **type)
+{
+	int rv = 1;
+	uint64_t size;
+	struct volume_id *vid;
+
+	/* fd is owned by vid now */
+	vid = volume_id_open_node(fd);
+
+	if (ioctl(/*vid->*/fd, BLKGETSIZE64, &size) != 0)
+		size = 0;
+
+	if (volume_id_probe_all(vid, /*0,*/ size) != 0)
+		goto ret;
+
+	if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
+		*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:
+	free_volume_id(vid); /* also closes fd */
+	return rv;
+}
+
+/* NB: we take ownership of (malloc'ed) label and uuid */
+static void
+uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid, const char *type)
+{
+	struct uuidCache_s *last;
+
+	if (!uuidCache) {
+		last = uuidCache = xzalloc(sizeof(*uuidCache));
+	} else {
+		for (last = uuidCache; last->next; last = last->next)
+			continue;
+		last->next = xzalloc(sizeof(*uuidCache));
+		last = last->next;
+	}
+	/*last->next = NULL; - xzalloc did it*/
+//	last->major = major;
+//	last->minor = minor;
+	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,
+ * add a cache entry for this device.
+ * If device node does not exist, it will be temporarily created. */
+static int FAST_FUNC
+uuidcache_check_device(const char *device,
+		struct stat *statbuf,
+		void *userData UNUSED_PARAM,
+		int depth UNUSED_PARAM)
+{
+	/* note: this check rejects links to devices, among other nodes */
+	if (!S_ISBLK(statbuf->st_mode))
+		return TRUE;
+
+	/* Users report that mucking with floppies (especially non-present
+	 * ones) is significant PITA. This is a horribly dirty hack,
+	 * but it is very useful in real world.
+	 * If this will ever need to be enabled, consider using O_NONBLOCK.
+	 */
+	if (major(statbuf->st_rdev) == 2)
+		return TRUE;
+
+	add_to_uuid_cache(device);
+
+	return TRUE;
+}
+
+static void
+uuidcache_init(void)
+{
+	dbg("DBG: uuidCache=%x, uuidCache");
+	if (uuidCache)
+		return;
+
+	/* We were scanning /proc/partitions
+	 * and /proc/sys/dev/cdrom/info here.
+	 * Missed volume managers. I see that "standard" blkid uses these:
+	 * /dev/mapper/control
+	 * /proc/devices
+	 * /proc/evms/volumes
+	 * /proc/lvm/VGs
+	 * 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 */);
+}
+
+#define UUID   1
+#define VOL    2
+
+#ifdef UNUSED
+static char *
+get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
+{
+	struct uuidCache_s *uc;
+
+	uuidcache_init();
+	uc = uuidCache;
+
+	while (uc) {
+		switch (n) {
+		case UUID:
+			if (strcmp(t, uc->uc_uuid) == 0) {
+				*majorPtr = uc->major;
+				*minorPtr = uc->minor;
+				return uc->device;
+			}
+			break;
+		case VOL:
+			if (strcmp(t, uc->label) == 0) {
+				*majorPtr = uc->major;
+				*minorPtr = uc->minor;
+				return uc->device;
+			}
+			break;
+		}
+		uc = uc->next;
+	}
+	return NULL;
+}
+
+static unsigned char
+fromhex(char c)
+{
+	if (isdigit(c))
+		return (c - '0');
+	return ((c|0x20) - 'a' + 10);
+}
+
+static char *
+get_spec_by_uuid(const char *s, int *major, int *minor)
+{
+	unsigned char uuid[16];
+	int i;
+
+	if (strlen(s) != 36 || s[8] != '-' || s[13] != '-'
+	 || s[18] != '-' || s[23] != '-'
+	) {
+		goto bad_uuid;
+	}
+	for (i = 0; i < 16; i++) {
+		if (*s == '-')
+			s++;
+		if (!isxdigit(s[0]) || !isxdigit(s[1]))
+			goto bad_uuid;
+		uuid[i] = ((fromhex(s[0]) << 4) | fromhex(s[1]));
+		s += 2;
+	}
+	return get_spec_by_x(UUID, (char *)uuid, major, minor);
+
+ bad_uuid:
+	fprintf(stderr, _("mount: bad UUID"));
+	return 0;
+}
+
+static char *
+get_spec_by_volume_label(const char *s, int *major, int *minor)
+{
+	return get_spec_by_x(VOL, s, major, minor);
+}
+#endif // UNUSED
+
+/* Used by blkid */
+void display_uuid_cache(void)
+{
+	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);
+#if ENABLE_FEATURE_BLKID_TYPE
+	if (u->type)
+		printf(" TYPE=\"%s\"", u->type);
+#endif
+		bb_putchar('\n');
+		u = u->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;
+	while (uc) {
+		if (uc->label[0] && strcmp(spec, uc->label) == 0) {
+			return xstrdup(uc->device);
+		}
+		uc = uc->next;
+	}
+	return NULL;
+}
+
+char *get_devname_from_uuid(const char *spec)
+{
+	struct uuidCache_s *uc;
+
+	uuidcache_init();
+	uc = uuidCache;
+	while (uc) {
+		/* case of hex numbers doesn't matter */
+		if (strcasecmp(spec, uc->uc_uuid) == 0) {
+			return xstrdup(uc->device);
+		}
+		uc = uc->next;
+	}
+	return NULL;
+}
+
+int resolve_mount_spec(char **fsname)
+{
+	char *tmp = *fsname;
+
+	if (strncmp(*fsname, "UUID=", 5) == 0)
+		tmp = get_devname_from_uuid(*fsname + 5);
+	else if (strncmp(*fsname, "LABEL=", 6) == 0)
+		tmp = get_devname_from_label(*fsname + 6);
+
+	if (tmp == *fsname)
+		return 0; /* no UUID= or LABEL= prefix found */
+
+	if (tmp)
+		*fsname = tmp;
+	return 1;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/hfs.c b/busybox-1.19.3/util-linux/volume_id/hfs.c
new file mode 100644
index 0000000..f3f19db
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/hfs.c
@@ -0,0 +1,292 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct hfs_finder_info{
+	uint32_t	boot_folder;
+	uint32_t	start_app;
+	uint32_t	open_folder;
+	uint32_t	os9_folder;
+	uint32_t	reserved;
+	uint32_t	osx_folder;
+	uint8_t		id[8];
+} PACKED;
+
+struct hfs_mdb {
+	uint8_t		signature[2];
+	uint32_t	cr_date;
+	uint32_t	ls_Mod;
+	uint16_t	atrb;
+	uint16_t	nm_fls;
+	uint16_t	vbm_st;
+	uint16_t	alloc_ptr;
+	uint16_t	nm_al_blks;
+	uint32_t	al_blk_size;
+	uint32_t	clp_size;
+	uint16_t	al_bl_st;
+	uint32_t	nxt_cnid;
+	uint16_t	free_bks;
+	uint8_t		label_len;
+	uint8_t		label[27];
+	uint32_t	vol_bkup;
+	uint16_t	vol_seq_num;
+	uint32_t	wr_cnt;
+	uint32_t	xt_clump_size;
+	uint32_t	ct_clump_size;
+	uint16_t	num_root_dirs;
+	uint32_t	file_count;
+	uint32_t	dir_count;
+	struct hfs_finder_info finder_info;
+	uint8_t		embed_sig[2];
+	uint16_t	embed_startblock;
+	uint16_t	embed_blockcount;
+} PACKED;
+
+struct hfsplus_bnode_descriptor {
+	uint32_t	next;
+	uint32_t	prev;
+	uint8_t		type;
+	uint8_t		height;
+	uint16_t	num_recs;
+	uint16_t	reserved;
+} PACKED;
+
+struct hfsplus_bheader_record {
+	uint16_t	depth;
+	uint32_t	root;
+	uint32_t	leaf_count;
+	uint32_t	leaf_head;
+	uint32_t	leaf_tail;
+	uint16_t	node_size;
+} PACKED;
+
+struct hfsplus_catalog_key {
+	uint16_t	key_len;
+	uint32_t	parent_id;
+	uint16_t	unicode_len;
+	uint8_t		unicode[255 * 2];
+} PACKED;
+
+struct hfsplus_extent {
+	uint32_t	start_block;
+	uint32_t	block_count;
+} PACKED;
+
+#define HFSPLUS_EXTENT_COUNT		8
+struct hfsplus_fork {
+	uint64_t	total_size;
+	uint32_t	clump_size;
+	uint32_t	total_blocks;
+	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+} PACKED;
+
+struct hfsplus_vol_header {
+	uint8_t		signature[2];
+	uint16_t	version;
+	uint32_t	attributes;
+	uint32_t	last_mount_vers;
+	uint32_t	reserved;
+	uint32_t	create_date;
+	uint32_t	modify_date;
+	uint32_t	backup_date;
+	uint32_t	checked_date;
+	uint32_t	file_count;
+	uint32_t	folder_count;
+	uint32_t	blocksize;
+	uint32_t	total_blocks;
+	uint32_t	free_blocks;
+	uint32_t	next_alloc;
+	uint32_t	rsrc_clump_sz;
+	uint32_t	data_clump_sz;
+	uint32_t	next_cnid;
+	uint32_t	write_count;
+	uint64_t	encodings_bmp;
+	struct hfs_finder_info finder_info;
+	struct hfsplus_fork alloc_file;
+	struct hfsplus_fork ext_file;
+	struct hfsplus_fork cat_file;
+	struct hfsplus_fork attr_file;
+	struct hfsplus_fork start_file;
+} PACKED;
+
+#define HFS_SUPERBLOCK_OFFSET		0x400
+#define HFS_NODE_LEAF			0xff
+#define HFSPLUS_POR_CNID		1
+
+int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/)
+{
+	uint64_t off = 0;
+	unsigned blocksize;
+	unsigned cat_block;
+	unsigned ext_block_start;
+	unsigned ext_block_count;
+	int ext;
+	unsigned leaf_node_head;
+	unsigned leaf_node_count;
+	unsigned leaf_node_size;
+	unsigned leaf_block;
+	uint64_t leaf_off;
+	unsigned alloc_block_size;
+	unsigned alloc_first_block;
+	unsigned embed_first_block;
+	unsigned record_count;
+	struct hfsplus_vol_header *hfsplus;
+	struct hfsplus_bnode_descriptor *descr;
+	struct hfsplus_bheader_record *bnode;
+	struct hfsplus_catalog_key *key;
+	unsigned label_len;
+	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+	struct hfs_mdb *hfs;
+	const uint8_t *buf;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	hfs = (struct hfs_mdb *) buf;
+	if (hfs->signature[0] != 'B' || hfs->signature[1] != 'D')
+		goto checkplus;
+
+	/* it may be just a hfs wrapper for hfs+ */
+	if (hfs->embed_sig[0] == 'H' && hfs->embed_sig[1] == '+') {
+		alloc_block_size = be32_to_cpu(hfs->al_blk_size);
+		dbg("alloc_block_size 0x%x", alloc_block_size);
+
+		alloc_first_block = be16_to_cpu(hfs->al_bl_st);
+		dbg("alloc_first_block 0x%x", alloc_first_block);
+
+		embed_first_block = be16_to_cpu(hfs->embed_startblock);
+		dbg("embed_first_block 0x%x", embed_first_block);
+
+		off += (alloc_first_block * 512) +
+		       (embed_first_block * alloc_block_size);
+		dbg("hfs wrapped hfs+ found at offset 0x%llx", (unsigned long long) off);
+
+		buf = volume_id_get_buffer(id, off + HFS_SUPERBLOCK_OFFSET, 0x200);
+		if (buf == NULL)
+			return -1;
+		goto checkplus;
+	}
+
+	if (hfs->label_len > 0 && hfs->label_len < 28) {
+//		volume_id_set_label_raw(id, hfs->label, hfs->label_len);
+		volume_id_set_label_string(id, hfs->label, hfs->label_len) ;
+	}
+
+	volume_id_set_uuid(id, hfs->finder_info.id, UUID_HFS);
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "hfs";)
+
+	return 0;
+
+ checkplus:
+	hfsplus = (struct hfsplus_vol_header *) buf;
+	if (hfs->signature[0] == 'H')
+		if (hfs->signature[1] == '+' || hfs->signature[1] == 'X')
+			goto hfsplus;
+	return -1;
+
+ hfsplus:
+	volume_id_set_uuid(id, hfsplus->finder_info.id, UUID_HFS);
+
+	blocksize = be32_to_cpu(hfsplus->blocksize);
+	dbg("blocksize %u", blocksize);
+
+	memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+	cat_block = be32_to_cpu(extents[0].start_block);
+	dbg("catalog start block 0x%x", cat_block);
+
+	buf = volume_id_get_buffer(id, off + (cat_block * blocksize), 0x2000);
+	if (buf == NULL)
+		goto found;
+
+	bnode = (struct hfsplus_bheader_record *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	leaf_node_head = be32_to_cpu(bnode->leaf_head);
+	dbg("catalog leaf node 0x%x", leaf_node_head);
+
+	leaf_node_size = be16_to_cpu(bnode->node_size);
+	dbg("leaf node size 0x%x", leaf_node_size);
+
+	leaf_node_count = be32_to_cpu(bnode->leaf_count);
+	dbg("leaf node count 0x%x", leaf_node_count);
+	if (leaf_node_count == 0)
+		goto found;
+
+	leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
+
+	/* get physical location */
+	for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
+		ext_block_start = be32_to_cpu(extents[ext].start_block);
+		ext_block_count = be32_to_cpu(extents[ext].block_count);
+		dbg("extent start block 0x%x, count 0x%x", ext_block_start, ext_block_count);
+
+		if (ext_block_count == 0)
+			goto found;
+
+		/* this is our extent */
+		if (leaf_block < ext_block_count)
+			break;
+
+		leaf_block -= ext_block_count;
+	}
+	if (ext == HFSPLUS_EXTENT_COUNT)
+		goto found;
+	dbg("found block in extent %i", ext);
+
+	leaf_off = (ext_block_start + leaf_block) * blocksize;
+
+	buf = volume_id_get_buffer(id, off + leaf_off, leaf_node_size);
+	if (buf == NULL)
+		goto found;
+
+	descr = (struct hfsplus_bnode_descriptor *) buf;
+	dbg("descriptor type 0x%x", descr->type);
+
+	record_count = be16_to_cpu(descr->num_recs);
+	dbg("number of records %u", record_count);
+	if (record_count == 0)
+		goto found;
+
+	if (descr->type != HFS_NODE_LEAF)
+		goto found;
+
+	key = (struct hfsplus_catalog_key *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	dbg("parent id 0x%x", be32_to_cpu(key->parent_id));
+	if (key->parent_id != cpu_to_be32(HFSPLUS_POR_CNID))
+		goto found;
+
+	label_len = be16_to_cpu(key->unicode_len) * 2;
+	dbg("label unicode16 len %i", label_len);
+//	volume_id_set_label_raw(id, key->unicode, label_len);
+	volume_id_set_label_unicode16(id, key->unicode, BE, label_len);
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	id->type = "hfsplus";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/iso9660.c b/busybox-1.19.3/util-linux/volume_id/iso9660.c
new file mode 100644
index 0000000..1d7693a
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/iso9660.c
@@ -0,0 +1,120 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+#define ISO_SUPERBLOCK_OFFSET		0x8000
+#define ISO_SECTOR_SIZE			0x800
+#define ISO_VD_OFFSET			(ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE)
+#define ISO_VD_PRIMARY			0x1
+#define ISO_VD_SUPPLEMENTARY		0x2
+#define ISO_VD_END			0xff
+#define ISO_VD_MAX			16
+
+struct iso_volume_descriptor {
+	uint8_t		vd_type;
+	uint8_t		vd_id[5];
+	uint8_t		vd_version;
+	uint8_t		flags;
+	uint8_t		system_id[32];
+	uint8_t		volume_id[32];
+	uint8_t		unused[8];
+	uint8_t		space_size[8];
+	uint8_t		escape_sequences[8];
+} PACKED;
+
+struct high_sierra_volume_descriptor {
+	uint8_t		foo[8];
+	uint8_t		type;
+	uint8_t		id[4];
+	uint8_t		version;
+} PACKED;
+
+int FAST_FUNC volume_id_probe_iso9660(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	uint8_t *buf;
+	struct iso_volume_descriptor *is;
+	struct high_sierra_volume_descriptor *hs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off + ISO_SUPERBLOCK_OFFSET, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	is = (struct iso_volume_descriptor *) buf;
+
+	if (memcmp(is->vd_id, "CD001", 5) == 0) {
+		int vd_offset;
+		int i;
+
+		dbg("read label from PVD");
+//		volume_id_set_label_raw(id, is->volume_id, 32);
+		volume_id_set_label_string(id, is->volume_id, 32);
+
+		dbg("looking for SVDs");
+		vd_offset = ISO_VD_OFFSET;
+		for (i = 0; i < ISO_VD_MAX; i++) {
+			uint8_t svd_label[64];
+
+			is = volume_id_get_buffer(id, off + vd_offset, 0x200);
+			if (is == NULL || is->vd_type == ISO_VD_END)
+				break;
+			if (is->vd_type != ISO_VD_SUPPLEMENTARY)
+				continue;
+
+			dbg("found SVD at offset 0x%llx", (unsigned long long) (off + vd_offset));
+			if (memcmp(is->escape_sequences, "%/@", 3) == 0
+			 || memcmp(is->escape_sequences, "%/C", 3) == 0
+			 || memcmp(is->escape_sequences, "%/E", 3) == 0
+			) {
+				dbg("Joliet extension found");
+				volume_id_set_unicode16((char *)svd_label, sizeof(svd_label), is->volume_id, BE, 32);
+				if (memcmp(id->label, svd_label, 16) == 0) {
+					dbg("SVD label is identical, use the possibly longer PVD one");
+					break;
+				}
+
+//				volume_id_set_label_raw(id, is->volume_id, 32);
+				volume_id_set_label_string(id, svd_label, 32);
+//				strcpy(id->type_version, "Joliet Extension");
+				goto found;
+			}
+			vd_offset += ISO_SECTOR_SIZE;
+		}
+		goto found;
+	}
+
+	hs = (struct high_sierra_volume_descriptor *) buf;
+
+	if (memcmp(hs->id, "CDROM", 5) == 0) {
+//		strcpy(id->type_version, "High Sierra");
+		goto found;
+	}
+
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "iso9660";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/jfs.c b/busybox-1.19.3/util-linux/volume_id/jfs.c
new file mode 100644
index 0000000..5333af2
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/jfs.c
@@ -0,0 +1,60 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct jfs_super_block {
+	uint8_t		magic[4];
+	uint32_t	version;
+	uint64_t	size;
+	uint32_t	bsize;
+	uint32_t	dummy1;
+	uint32_t	pbsize;
+	uint32_t	dummy2[27];
+	uint8_t		uuid[16];
+	uint8_t		label[16];
+	uint8_t		loguuid[16];
+} PACKED;
+
+#define JFS_SUPERBLOCK_OFFSET			0x8000
+
+int FAST_FUNC volume_id_probe_jfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct jfs_super_block *js;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	js = volume_id_get_buffer(id, off + JFS_SUPERBLOCK_OFFSET, 0x200);
+	if (js == NULL)
+		return -1;
+
+	if (memcmp(js->magic, "JFS1", 4) != 0)
+		return -1;
+
+//	volume_id_set_label_raw(id, js->label, 16);
+	volume_id_set_label_string(id, js->label, 16);
+	volume_id_set_uuid(id, js->uuid, UUID_DCE);
+
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "jfs";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/linux_raid.c b/busybox-1.19.3/util-linux/volume_id/linux_raid.c
new file mode 100644
index 0000000..761e54f
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/linux_raid.c
@@ -0,0 +1,81 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct mdp_super_block {
+	uint32_t	md_magic;
+	uint32_t	major_version;
+	uint32_t	minor_version;
+	uint32_t	patch_version;
+	uint32_t	gvalid_words;
+	uint32_t	set_uuid0;
+	uint32_t	ctime;
+	uint32_t	level;
+	uint32_t	size;
+	uint32_t	nr_disks;
+	uint32_t	raid_disks;
+	uint32_t	md_minor;
+	uint32_t	not_persistent;
+	uint32_t	set_uuid1;
+	uint32_t	set_uuid2;
+	uint32_t	set_uuid3;
+} PACKED;
+
+#define MD_RESERVED_BYTES		0x10000
+#define MD_MAGIC			0xa92b4efc
+
+int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size)
+{
+	typedef uint32_t aliased_uint32_t FIX_ALIASING;
+#define off ((uint64_t)0)
+	uint64_t sboff;
+	uint8_t uuid[16];
+	struct mdp_super_block *mdp;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	sboff = (size & ~(MD_RESERVED_BYTES - 1)) - MD_RESERVED_BYTES;
+	mdp = volume_id_get_buffer(id, off + sboff, 0x800);
+	if (mdp == NULL)
+		return -1;
+
+	if (mdp->md_magic != cpu_to_le32(MD_MAGIC))
+		return -1;
+
+	*(aliased_uint32_t*)uuid = mdp->set_uuid0;
+	memcpy(&uuid[4], &mdp->set_uuid1, 12);
+	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));
+
+	dbg("found raid signature");
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+	IF_FEATURE_BLKID_TYPE(id->type = "linux_raid_member";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/linux_swap.c b/busybox-1.19.3/util-linux/volume_id/linux_swap.c
new file mode 100644
index 0000000..1ee534a
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/linux_swap.c
@@ -0,0 +1,78 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct swap_header_v1_2 {
+	uint8_t		bootbits[1024];
+	uint32_t	version;
+	uint32_t	last_page;
+	uint32_t	nr_badpages;
+	uint8_t		uuid[16];
+	uint8_t		volume_name[16];
+} PACKED;
+
+#define LARGEST_PAGESIZE			0x4000
+
+int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct swap_header_v1_2 *sw;
+	const uint8_t *buf;
+	unsigned page;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	/* the swap signature is at the end of the PAGE_SIZE */
+	for (page = 0x1000; page <= LARGEST_PAGESIZE; page <<= 1) {
+			buf = volume_id_get_buffer(id, off + page-10, 10);
+			if (buf == NULL)
+				return -1;
+
+			if (memcmp(buf, "SWAP-SPACE", 10) == 0) {
+//				id->type_version[0] = '1';
+//				id->type_version[1] = '\0';
+				goto found;
+			}
+
+			if (memcmp(buf, "SWAPSPACE2", 10) == 0
+			 || memcmp(buf, "S1SUSPEND", 9) == 0
+			 || memcmp(buf, "S2SUSPEND", 9) == 0
+			 || memcmp(buf, "ULSUSPEND", 9) == 0
+			) {
+				sw = volume_id_get_buffer(id, off, sizeof(struct swap_header_v1_2));
+				if (sw == NULL)
+					return -1;
+//				id->type_version[0] = '2';
+//				id->type_version[1] = '\0';
+//				volume_id_set_label_raw(id, sw->volume_name, 16);
+				volume_id_set_label_string(id, sw->volume_name, 16);
+				volume_id_set_uuid(id, sw->uuid, UUID_DCE);
+				goto found;
+			}
+	}
+	return -1;
+
+found:
+//	volume_id_set_usage(id, VOLUME_ID_OTHER);
+	IF_FEATURE_BLKID_TYPE(id->type = "swap";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/luks.c b/busybox-1.19.3/util-linux/volume_id/luks.c
new file mode 100644
index 0000000..f9b3766
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/luks.c
@@ -0,0 +1,100 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 W. Michael Petullo <mike@flyn.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
+ */
+
+#include "volume_id_internal.h"
+
+#define LUKS_MAGIC_L             6
+#define UUID_STRING_L           40
+#define LUKS_CIPHERNAME_L       32
+#define LUKS_CIPHERMODE_L       32
+#define LUKS_HASHSPEC_L         32
+#define LUKS_DIGESTSIZE         20
+#define LUKS_SALTSIZE           32
+#define LUKS_NUMKEYS             8
+
+static const uint8_t LUKS_MAGIC[] = { 'L','U','K','S', 0xba, 0xbe };
+
+struct luks_phdr {
+	uint8_t		magic[LUKS_MAGIC_L];
+	uint16_t	version;
+	uint8_t		cipherName[LUKS_CIPHERNAME_L];
+	uint8_t		cipherMode[LUKS_CIPHERMODE_L];
+	uint8_t		hashSpec[LUKS_HASHSPEC_L];
+	uint32_t	payloadOffset;
+	uint32_t	keyBytes;
+	uint8_t		mkDigest[LUKS_DIGESTSIZE];
+	uint8_t		mkDigestSalt[LUKS_SALTSIZE];
+	uint32_t	mkDigestIterations;
+	uint8_t		uuid[UUID_STRING_L];
+	struct {
+		uint32_t	active;
+		uint32_t	passwordIterations;
+		uint8_t		passwordSalt[LUKS_SALTSIZE];
+		uint32_t	keyMaterialOffset;
+		uint32_t	stripes;
+	} keyblock[LUKS_NUMKEYS];
+};
+
+enum {
+	EXPECTED_SIZE_luks_phdr = 0
+		+ 1 * LUKS_MAGIC_L
+		+ 2
+		+ 1 * LUKS_CIPHERNAME_L
+		+ 1 * LUKS_CIPHERMODE_L
+		+ 1 * LUKS_HASHSPEC_L
+		+ 4
+		+ 4
+		+ 1 * LUKS_DIGESTSIZE
+		+ 1 * LUKS_SALTSIZE
+		+ 4
+		+ 1 * UUID_STRING_L
+		+ LUKS_NUMKEYS * (0
+		  + 4
+		  + 4
+		  + 1 * LUKS_SALTSIZE
+		  + 4
+		  + 4
+		  )
+};
+
+struct BUG_bad_size_luks_phdr {
+	char BUG_bad_size_luks_phdr[
+		sizeof(struct luks_phdr) == EXPECTED_SIZE_luks_phdr ?
+		1 : -1];
+};
+
+int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct luks_phdr *header;
+
+	header = volume_id_get_buffer(id, off, sizeof(*header));
+	if (header == NULL)
+		return -1;
+
+	if (memcmp(header->magic, LUKS_MAGIC, LUKS_MAGIC_L))
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_CRYPTO);
+	volume_id_set_uuid(id, header->uuid, UUID_DCE_STRING);
+	IF_FEATURE_BLKID_TYPE(id->type = "crypto_LUKS";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/ntfs.c b/busybox-1.19.3/util-linux/volume_id/ntfs.c
new file mode 100644
index 0000000..547f141
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/ntfs.c
@@ -0,0 +1,194 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct ntfs_super_block {
+	uint8_t		jump[3];
+	uint8_t		oem_id[8];
+	uint16_t	bytes_per_sector;
+	uint8_t		sectors_per_cluster;
+	uint16_t	reserved_sectors;
+	uint8_t		fats;
+	uint16_t	root_entries;
+	uint16_t	sectors;
+	uint8_t		media_type;
+	uint16_t	sectors_per_fat;
+	uint16_t	sectors_per_track;
+	uint16_t	heads;
+	uint32_t	hidden_sectors;
+	uint32_t	large_sectors;
+	uint16_t	unused[2];
+	uint64_t	number_of_sectors;
+	uint64_t	mft_cluster_location;
+	uint64_t	mft_mirror_cluster_location;
+	int8_t		cluster_per_mft_record;
+	uint8_t		reserved1[3];
+	int8_t		cluster_per_index_record;
+	uint8_t		reserved2[3];
+	uint8_t		volume_serial[8];
+	uint16_t	checksum;
+} PACKED;
+
+struct master_file_table_record {
+	uint8_t		magic[4];
+	uint16_t	usa_ofs;
+	uint16_t	usa_count;
+	uint64_t	lsn;
+	uint16_t	sequence_number;
+	uint16_t	link_count;
+	uint16_t	attrs_offset;
+	uint16_t	flags;
+	uint32_t	bytes_in_use;
+	uint32_t	bytes_allocated;
+} PACKED;
+
+struct file_attribute {
+	uint32_t	type;
+	uint32_t	len;
+	uint8_t		non_resident;
+	uint8_t		name_len;
+	uint16_t	name_offset;
+	uint16_t	flags;
+	uint16_t	instance;
+	uint32_t	value_len;
+	uint16_t	value_offset;
+} PACKED;
+
+struct volume_info {
+	uint64_t	reserved;
+	uint8_t		major_ver;
+	uint8_t		minor_ver;
+} PACKED;
+
+#define MFT_RECORD_VOLUME			3
+#define MFT_RECORD_ATTR_VOLUME_NAME		0x60
+#define MFT_RECORD_ATTR_VOLUME_INFO		0x70
+#define MFT_RECORD_ATTR_OBJECT_ID		0x40
+#define MFT_RECORD_ATTR_END			0xffffffffu
+
+int FAST_FUNC volume_id_probe_ntfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	unsigned sector_size;
+	unsigned cluster_size;
+	uint64_t mft_cluster;
+	uint64_t mft_off;
+	unsigned mft_record_size;
+	unsigned attr_type;
+	unsigned attr_off;
+	unsigned attr_len;
+	unsigned val_off;
+	unsigned val_len;
+	struct master_file_table_record *mftr;
+	struct ntfs_super_block *ns;
+	const uint8_t *buf;
+	const uint8_t *val;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	ns = volume_id_get_buffer(id, off, 0x200);
+	if (ns == NULL)
+		return -1;
+
+	if (memcmp(ns->oem_id, "NTFS", 4) != 0)
+		return -1;
+
+	volume_id_set_uuid(id, ns->volume_serial, UUID_NTFS);
+
+	sector_size = le16_to_cpu(ns->bytes_per_sector);
+	cluster_size = ns->sectors_per_cluster * sector_size;
+	mft_cluster = le64_to_cpu(ns->mft_cluster_location);
+	mft_off = mft_cluster * cluster_size;
+
+	if (ns->cluster_per_mft_record < 0)
+		/* size = -log2(mft_record_size); normally 1024 Bytes */
+		mft_record_size = 1 << -ns->cluster_per_mft_record;
+	else
+		mft_record_size = ns->cluster_per_mft_record * cluster_size;
+
+	dbg("sectorsize  0x%x", sector_size);
+	dbg("clustersize 0x%x", cluster_size);
+	dbg("mftcluster  %llu", (unsigned long long) mft_cluster);
+	dbg("mftoffset  0x%llx", (unsigned long long) mft_off);
+	dbg("cluster per mft_record  %i", ns->cluster_per_mft_record);
+	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);
+	if (buf == NULL)
+		goto found;
+
+	mftr = (struct master_file_table_record*) buf;
+
+	dbg("mftr->magic '%c%c%c%c'", mftr->magic[0], mftr->magic[1], mftr->magic[2], mftr->magic[3]);
+	if (memcmp(mftr->magic, "FILE", 4) != 0)
+		goto found;
+
+	attr_off = le16_to_cpu(mftr->attrs_offset);
+	dbg("file $Volume's attributes are at offset %i", attr_off);
+
+	while (1) {
+		struct file_attribute *attr;
+
+		attr = (struct file_attribute*) &buf[attr_off];
+		attr_type = le32_to_cpu(attr->type);
+		attr_len = le16_to_cpu(attr->len);
+		val_off = le16_to_cpu(attr->value_offset);
+		val_len = le32_to_cpu(attr->value_len);
+		attr_off += attr_len;
+
+		if (attr_len == 0)
+			break;
+
+		if (attr_off >= mft_record_size)
+			break;
+
+		if (attr_type == MFT_RECORD_ATTR_END)
+			break;
+
+		dbg("found attribute type 0x%x, len %i, at offset %i",
+		    attr_type, attr_len, attr_off);
+
+//		if (attr_type == MFT_RECORD_ATTR_VOLUME_INFO) {
+//			struct volume_info *info;
+//			dbg("found info, len %i", val_len);
+//			info = (struct volume_info*) (((uint8_t *) attr) + val_off);
+//			snprintf(id->type_version, sizeof(id->type_version)-1,
+//				 "%u.%u", info->major_ver, info->minor_ver);
+//		}
+
+		if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) {
+			dbg("found label, len %i", val_len);
+			if (val_len > VOLUME_ID_LABEL_SIZE)
+				val_len = VOLUME_ID_LABEL_SIZE;
+
+			val = ((uint8_t *) attr) + val_off;
+//			volume_id_set_label_raw(id, val, val_len);
+			volume_id_set_label_unicode16(id, val, LE, val_len);
+		}
+	}
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "ntfs";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/ocfs2.c b/busybox-1.19.3/util-linux/volume_id/ocfs2.c
new file mode 100644
index 0000000..fcdb151
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/ocfs2.c
@@ -0,0 +1,106 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) Andre Masella <andre@masella.no-ip.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
+ */
+
+#include "volume_id_internal.h"
+
+/* All these values are taken from ocfs2-tools's ocfs2_fs.h */
+#define OCFS2_VOL_UUID_LEN			16
+#define OCFS2_MAX_VOL_LABEL_LEN			64
+#define OCFS2_SUPERBLOCK_OFFSET			0x2000
+
+
+/* This is the superblock. The OCFS2 header files have structs in structs.
+This is one has been simplified since we only care about the superblock.
+*/
+
+struct ocfs2_super_block {
+	uint8_t		i_signature[8];			/* Signature for validation */
+	uint32_t	i_generation;			/* Generation number */
+	int16_t		i_suballoc_slot;			/* Slot suballocator this inode belongs to */
+	uint16_t	i_suballoc_bit;			/* Bit offset in suballocator block group */
+	uint32_t	i_reserved0;
+	uint32_t	i_clusters;			/* Cluster count */
+	uint32_t	i_uid;				/* Owner UID */
+	uint32_t	i_gid;				/* Owning GID */
+	uint64_t	i_size;				/* Size in bytes */
+	uint16_t	i_mode;				/* File mode */
+	uint16_t	i_links_count;			/* Links count */
+	uint32_t	i_flags;				/* File flags */
+	uint64_t	i_atime;				/* Access time */
+	uint64_t	i_ctime;				/* Creation time */
+	uint64_t	i_mtime;				/* Modification time */
+	uint64_t	i_dtime;				/* Deletion time */
+	uint64_t	i_blkno;				/* Offset on disk, in blocks */
+	uint64_t	i_last_eb_blk;			/* Pointer to last extent block */
+	uint32_t	i_fs_generation;			/* Generation per fs-instance */
+	uint32_t	i_atime_nsec;
+	uint32_t	i_ctime_nsec;
+	uint32_t	i_mtime_nsec;
+	uint64_t	i_reserved1[9];
+	uint64_t	i_pad1;				/* Generic way to refer to this 64bit union */
+	/* Normally there is a union of the different block types, but we only care about the superblock. */
+	uint16_t	s_major_rev_level;
+	uint16_t	s_minor_rev_level;
+	uint16_t	s_mnt_count;
+	int16_t		s_max_mnt_count;
+	uint16_t	s_state;				/* File system state */
+	uint16_t	s_errors;				/* Behaviour when detecting errors */
+	uint32_t	s_checkinterval;			/* Max time between checks */
+	uint64_t	s_lastcheck;			/* Time of last check */
+	uint32_t	s_creator_os;			/* OS */
+	uint32_t	s_feature_compat;			/* Compatible feature set */
+	uint32_t	s_feature_incompat;		/* Incompatible feature set */
+	uint32_t	s_feature_ro_compat;		/* Readonly-compatible feature set */
+	uint64_t	s_root_blkno;			/* Offset, in blocks, of root directory dinode */
+	uint64_t	s_system_dir_blkno;		/* Offset, in blocks, of system directory dinode */
+	uint32_t	s_blocksize_bits;			/* Blocksize for this fs */
+	uint32_t	s_clustersize_bits;		/* Clustersize for this fs */
+	uint16_t	s_max_slots;			/* Max number of simultaneous mounts before tunefs required */
+	uint16_t	s_reserved1;
+	uint32_t	s_reserved2;
+	uint64_t	s_first_cluster_group;		/* Block offset of 1st cluster group header */
+	uint8_t		s_label[OCFS2_MAX_VOL_LABEL_LEN];	/* Label for mounting, etc. */
+	uint8_t		s_uuid[OCFS2_VOL_UUID_LEN];	/* 128-bit uuid */
+} PACKED;
+
+int FAST_FUNC volume_id_probe_ocfs2(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct ocfs2_super_block *os;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	os = volume_id_get_buffer(id, off + OCFS2_SUPERBLOCK_OFFSET, 0x200);
+	if (os == NULL)
+		return -1;
+
+	if (memcmp(os->i_signature, "OCFSV2", 6) != 0) {
+		return -1;
+	}
+
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	volume_id_set_label_raw(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_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);
+	IF_FEATURE_BLKID_TYPE(id->type = "ocfs2";)
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/reiserfs.c b/busybox-1.19.3/util-linux/volume_id/reiserfs.c
new file mode 100644
index 0000000..67b4a18
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/reiserfs.c
@@ -0,0 +1,113 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2005 Tobias Klauser <tklauser@access.unizh.ch>
+ *
+ *	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
+ */
+
+#include "volume_id_internal.h"
+
+struct reiserfs_super_block {
+	uint32_t	blocks_count;
+	uint32_t	free_blocks;
+	uint32_t	root_block;
+	uint32_t	journal_block;
+	uint32_t	journal_dev;
+	uint32_t	orig_journal_size;
+	uint32_t	dummy2[5];
+	uint16_t	blocksize;
+	uint16_t	dummy3[3];
+	uint8_t		magic[12];
+	uint32_t	dummy4[5];
+	uint8_t		uuid[16];
+	uint8_t		label[16];
+} PACKED;
+
+struct reiser4_super_block {
+	uint8_t		magic[16];
+	uint16_t	dummy[2];
+	uint8_t		uuid[16];
+	uint8_t		label[16];
+	uint64_t	dummy2;
+} PACKED;
+
+#define REISERFS1_SUPERBLOCK_OFFSET		0x2000
+#define REISERFS_SUPERBLOCK_OFFSET		0x10000
+
+int FAST_FUNC volume_id_probe_reiserfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct reiserfs_super_block *rs;
+	struct reiser4_super_block *rs4;
+
+	dbg("reiserfs: probing at offset 0x%llx", (unsigned long long) off);
+
+	rs = volume_id_get_buffer(id, off + REISERFS_SUPERBLOCK_OFFSET, 0x200);
+	if (rs == NULL)
+		return -1;
+
+	if (memcmp(rs->magic, "ReIsErFs", 8) == 0) {
+		dbg("reiserfs: ReIsErFs, no label");
+//		strcpy(id->type_version, "3.5");
+		goto found;
+	}
+	if (memcmp(rs->magic, "ReIsEr2Fs", 9) == 0) {
+		dbg("reiserfs: ReIsEr2Fs");
+//		strcpy(id->type_version, "3.6");
+		goto found_label;
+	}
+	if (memcmp(rs->magic, "ReIsEr3Fs", 9) == 0) {
+		dbg("reiserfs: ReIsEr3Fs");
+//		strcpy(id->type_version, "JR");
+		goto found_label;
+	}
+
+	rs4 = (struct reiser4_super_block *) rs;
+	if (memcmp(rs4->magic, "ReIsEr4", 7) == 0) {
+//		strcpy(id->type_version, "4");
+//		volume_id_set_label_raw(id, rs4->label, 16);
+		volume_id_set_label_string(id, rs4->label, 16);
+		volume_id_set_uuid(id, rs4->uuid, UUID_DCE);
+		dbg("reiserfs: ReIsEr4, label '%s' uuid '%s'", id->label, id->uuid);
+		goto found;
+	}
+
+	rs = volume_id_get_buffer(id, off + REISERFS1_SUPERBLOCK_OFFSET, 0x200);
+	if (rs == NULL)
+		return -1;
+
+	if (memcmp(rs->magic, "ReIsErFs", 8) == 0) {
+		dbg("reiserfs: ReIsErFs, no label");
+//		strcpy(id->type_version, "3.5");
+		goto found;
+	}
+
+	dbg("reiserfs: no signature found");
+	return -1;
+
+ found_label:
+//	volume_id_set_label_raw(id, rs->label, 16);
+	volume_id_set_label_string(id, rs->label, 16);
+	volume_id_set_uuid(id, rs->uuid, UUID_DCE);
+	dbg("reiserfs: label '%s' uuid '%s'", id->label, id->uuid);
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "reiserfs";)
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/romfs.c b/busybox-1.19.3/util-linux/volume_id/romfs.c
new file mode 100644
index 0000000..15653be
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/romfs.c
@@ -0,0 +1,55 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct romfs_super {
+	uint8_t magic[8];
+	uint32_t size;
+	uint32_t checksum;
+	uint8_t name[];
+} PACKED;
+
+int FAST_FUNC volume_id_probe_romfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct romfs_super *rfs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	rfs = volume_id_get_buffer(id, off, 0x200);
+	if (rfs == NULL)
+		return -1;
+
+	if (memcmp(rfs->magic, "-rom1fs-", 4) == 0) {
+		size_t len = strlen((char *)rfs->name);
+
+		if (len) {
+//			volume_id_set_label_raw(id, rfs->name, len);
+			volume_id_set_label_string(id, rfs->name, len);
+		}
+
+//		volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+		IF_FEATURE_BLKID_TYPE(id->type = "romfs";)
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/sysv.c b/busybox-1.19.3/util-linux/volume_id/sysv.c
new file mode 100644
index 0000000..6eb9646
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/sysv.c
@@ -0,0 +1,125 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+#define SYSV_NICINOD			100
+#define SYSV_NICFREE			50
+
+struct sysv_super {
+	uint16_t	s_isize;
+	uint16_t	s_pad0;
+	uint32_t	s_fsize;
+	uint16_t	s_nfree;
+	uint16_t	s_pad1;
+	uint32_t	s_free[SYSV_NICFREE];
+	uint16_t	s_ninode;
+	uint16_t	s_pad2;
+	uint16_t	s_inode[SYSV_NICINOD];
+	uint8_t		s_flock;
+	uint8_t		s_ilock;
+	uint8_t		s_fmod;
+	uint8_t		s_ronly;
+	uint32_t	s_time;
+	uint16_t	s_dinfo[4];
+	uint32_t	s_tfree;
+	uint16_t	s_tinode;
+	uint16_t	s_pad3;
+	uint8_t		s_fname[6];
+	uint8_t		s_fpack[6];
+	uint32_t	s_fill[12];
+	uint32_t	s_state;
+	uint32_t	s_magic;
+	uint32_t	s_type;
+} PACKED;
+
+#define XENIX_NICINOD				100
+#define XENIX_NICFREE				100
+
+struct xenix_super {
+	uint16_t	s_isize;
+	uint32_t	s_fsize;
+	uint16_t	s_nfree;
+	uint32_t	s_free[XENIX_NICFREE];
+	uint16_t	s_ninode;
+	uint16_t	s_inode[XENIX_NICINOD];
+	uint8_t		s_flock;
+	uint8_t		s_ilock;
+	uint8_t		s_fmod;
+	uint8_t		s_ronly;
+	uint32_t	s_time;
+	uint32_t	s_tfree;
+	uint16_t	s_tinode;
+	uint16_t	s_dinfo[4];
+	uint8_t		s_fname[6];
+	uint8_t		s_fpack[6];
+	uint8_t		s_clean;
+	uint8_t		s_fill[371];
+	uint32_t	s_magic;
+	uint32_t	s_type;
+} PACKED;
+
+#define SYSV_SUPERBLOCK_BLOCK			0x01
+#define SYSV_MAGIC				0xfd187e20
+#define XENIX_SUPERBLOCK_BLOCK			0x18
+#define XENIX_MAGIC				0x2b5544
+#define SYSV_MAX_BLOCKSIZE			0x800
+
+int FAST_FUNC volume_id_probe_sysv(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct sysv_super *vs;
+	struct xenix_super *xs;
+	unsigned boff;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	for (boff = 0x200; boff <= SYSV_MAX_BLOCKSIZE; boff <<= 1) {
+		vs = volume_id_get_buffer(id, off + (boff * SYSV_SUPERBLOCK_BLOCK), 0x200);
+		if (vs == NULL)
+			return -1;
+
+		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);
+			IF_FEATURE_BLKID_TYPE(id->type = "sysv");
+			goto found;
+		}
+	}
+
+	for (boff = 0x200; boff <= SYSV_MAX_BLOCKSIZE; boff <<= 1) {
+		xs = volume_id_get_buffer(id, off + (boff + XENIX_SUPERBLOCK_BLOCK), 0x200);
+		if (xs == NULL)
+			return -1;
+
+		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);
+			IF_FEATURE_BLKID_TYPE(id->type = "xenix";)
+			goto found;
+		}
+	}
+
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/udf.c b/busybox-1.19.3/util-linux/volume_id/udf.c
new file mode 100644
index 0000000..cd63c8d
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/udf.c
@@ -0,0 +1,172 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct volume_descriptor {
+	struct descriptor_tag {
+		uint16_t	id;
+		uint16_t	version;
+		uint8_t		checksum;
+		uint8_t		reserved;
+		uint16_t	serial;
+		uint16_t	crc;
+		uint16_t	crc_len;
+		uint32_t	location;
+	} PACKED tag;
+	union {
+		struct anchor_descriptor {
+			uint32_t	length;
+			uint32_t	location;
+		} PACKED anchor;
+		struct primary_descriptor {
+			uint32_t	seq_num;
+			uint32_t	desc_num;
+			struct dstring {
+				uint8_t	clen;
+				uint8_t	c[31];
+			} PACKED ident;
+		} PACKED primary;
+	} PACKED type;
+} PACKED;
+
+struct volume_structure_descriptor {
+	uint8_t		type;
+	uint8_t		id[5];
+	uint8_t		version;
+} PACKED;
+
+#define UDF_VSD_OFFSET			0x8000
+
+int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct volume_descriptor *vd;
+	struct volume_structure_descriptor *vsd;
+	unsigned bs;
+	unsigned b;
+	unsigned type;
+	unsigned count;
+	unsigned loc;
+	unsigned clen;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET, 0x200);
+	if (vsd == NULL)
+		return -1;
+
+	if (memcmp(vsd->id, "NSR02", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "NSR03", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "BEA01", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "BOOT2", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "CD001", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "CDW02", 5) == 0)
+		goto blocksize;
+	if (memcmp(vsd->id, "TEA03", 5) == 0)
+		goto blocksize;
+	return -1;
+
+blocksize:
+	/* search the next VSD to get the logical block size of the volume */
+	for (bs = 0x800; bs < 0x8000; bs += 0x800) {
+		vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET + bs, 0x800);
+		if (vsd == NULL)
+			return -1;
+		dbg("test for blocksize: 0x%x", bs);
+		if (vsd->id[0] != '\0')
+			goto nsr;
+	}
+	return -1;
+
+nsr:
+	/* search the list of VSDs for a NSR descriptor */
+	for (b = 0; b < 64; b++) {
+		vsd = volume_id_get_buffer(id, off + UDF_VSD_OFFSET + (b * bs), 0x800);
+		if (vsd == NULL)
+			return -1;
+
+		dbg("vsd: %c%c%c%c%c",
+		    vsd->id[0], vsd->id[1], vsd->id[2], vsd->id[3], vsd->id[4]);
+
+		if (vsd->id[0] == '\0')
+			return -1;
+		if (memcmp(vsd->id, "NSR02", 5) == 0)
+			goto anchor;
+		if (memcmp(vsd->id, "NSR03", 5) == 0)
+			goto anchor;
+	}
+	return -1;
+
+anchor:
+	/* read anchor volume descriptor */
+	vd = volume_id_get_buffer(id, off + (256 * bs), 0x200);
+	if (vd == NULL)
+		return -1;
+
+	type = le16_to_cpu(vd->tag.id);
+	if (type != 2) /* TAG_ID_AVDP */
+		goto found;
+
+	/* get desriptor list address and block count */
+	count = le32_to_cpu(vd->type.anchor.length) / bs;
+	loc = le32_to_cpu(vd->type.anchor.location);
+	dbg("0x%x descriptors starting at logical secor 0x%x", count, loc);
+
+	/* pick the primary descriptor from the list */
+	for (b = 0; b < count; b++) {
+		vd = volume_id_get_buffer(id, off + ((loc + b) * bs), 0x200);
+		if (vd == NULL)
+			return -1;
+
+		type = le16_to_cpu(vd->tag.id);
+		dbg("descriptor type %i", type);
+
+		/* check validity */
+		if (type == 0)
+			goto found;
+		if (le32_to_cpu(vd->tag.location) != loc + b)
+			goto found;
+
+		if (type == 1) /* TAG_ID_PVD */
+			goto pvd;
+	}
+	goto found;
+
+ pvd:
+//	volume_id_set_label_raw(id, &(vd->type.primary.ident.clen), 32);
+
+	clen = vd->type.primary.ident.clen;
+	dbg("label string charsize=%i bit", clen);
+	if (clen == 8)
+		volume_id_set_label_string(id, vd->type.primary.ident.c, 31);
+	else if (clen == 16)
+		volume_id_set_label_unicode16(id, vd->type.primary.ident.c, BE, 31);
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "udf";)
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_highpoint.c b/busybox-1.19.3/util-linux/volume_id/unused_highpoint.c
new file mode 100644
index 0000000..17b7b32
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_highpoint.c
@@ -0,0 +1,86 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct hpt37x_meta {
+	uint8_t		filler1[32];
+	uint32_t	magic;
+} PACKED;
+
+struct hpt45x_meta {
+	uint32_t	magic;
+} PACKED;
+
+#define HPT37X_CONFIG_OFF		0x1200
+#define HPT37X_MAGIC_OK			0x5a7816f0
+#define HPT37X_MAGIC_BAD		0x5a7816fd
+
+#define HPT45X_MAGIC_OK			0x5a7816f3
+#define HPT45X_MAGIC_BAD		0x5a7816fd
+
+
+int FAST_FUNC volume_id_probe_highpoint_37x_raid(struct volume_id *id, uint64_t off)
+{
+	struct hpt37x_meta *hpt;
+	uint32_t magic;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	hpt = volume_id_get_buffer(id, off + HPT37X_CONFIG_OFF, 0x200);
+	if (hpt == NULL)
+		return -1;
+
+	magic = hpt->magic;
+	if (magic != cpu_to_le32(HPT37X_MAGIC_OK) && magic != cpu_to_le32(HPT37X_MAGIC_BAD))
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "highpoint_raid_member";
+
+	return 0;
+}
+
+int FAST_FUNC volume_id_probe_highpoint_45x_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	struct hpt45x_meta *hpt;
+	uint64_t meta_off;
+	uint32_t magic;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-11) * 0x200;
+	hpt = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (hpt == NULL)
+		return -1;
+
+	magic = hpt->magic;
+	if (magic != cpu_to_le32(HPT45X_MAGIC_OK) && magic != cpu_to_le32(HPT45X_MAGIC_BAD))
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "highpoint_raid_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_hpfs.c b/busybox-1.19.3/util-linux/volume_id/unused_hpfs.c
new file mode 100644
index 0000000..4429524
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_hpfs.c
@@ -0,0 +1,48 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct hpfs_super {
+	uint8_t		magic[4];
+	uint8_t		version;
+} PACKED;
+
+#define HPFS_SUPERBLOCK_OFFSET			0x2000
+
+int FAST_FUNC volume_id_probe_hpfs(struct volume_id *id, uint64_t off)
+{
+	struct hpfs_super *hs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	hs = volume_id_get_buffer(id, off + HPFS_SUPERBLOCK_OFFSET, 0x200);
+	if (hs == NULL)
+		return -1;
+
+	if (memcmp(hs->magic, "\x49\xe8\x95\xf9", 4) == 0) {
+//		sprintf(id->type_version, "%u", hs->version);
+//		volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//		id->type = "hpfs";
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_isw_raid.c b/busybox-1.19.3/util-linux/volume_id/unused_isw_raid.c
new file mode 100644
index 0000000..7ab47b3
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_isw_raid.c
@@ -0,0 +1,58 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct isw_meta {
+	uint8_t		sig[32];
+	uint32_t	check_sum;
+	uint32_t	mpb_size;
+	uint32_t	family_num;
+	uint32_t	generation_num;
+} PACKED;
+
+#define ISW_SIGNATURE		"Intel Raid ISM Cfg Sig. "
+
+
+int FAST_FUNC volume_id_probe_intel_software_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct isw_meta *isw;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-2) * 0x200;
+	isw = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (isw == NULL)
+		return -1;
+
+	if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	memcpy(id->type_version, &isw->sig[sizeof(ISW_SIGNATURE)-1], 6);
+//	id->type = "isw_raid_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_lsi_raid.c b/busybox-1.19.3/util-linux/volume_id/unused_lsi_raid.c
new file mode 100644
index 0000000..e6cc8ed
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_lsi_raid.c
@@ -0,0 +1,52 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct lsi_meta {
+	uint8_t		sig[6];
+} PACKED;
+
+#define LSI_SIGNATURE		"$XIDE$"
+
+int FAST_FUNC volume_id_probe_lsi_mega_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct lsi_meta *lsi;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-1) * 0x200;
+	lsi = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (lsi == NULL)
+		return -1;
+
+	if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "lsi_mega_raid_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_lvm.c b/busybox-1.19.3/util-linux/volume_id/unused_lvm.c
new file mode 100644
index 0000000..2206498
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_lvm.c
@@ -0,0 +1,87 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct lvm1_super_block {
+	uint8_t	id[2];
+} PACKED;
+
+struct lvm2_super_block {
+	uint8_t		id[8];
+	uint64_t	sector_xl;
+	uint32_t	crc_xl;
+	uint32_t	offset_xl;
+	uint8_t		type[8];
+} PACKED;
+
+#define LVM1_SB_OFF			0x400
+
+int FAST_FUNC volume_id_probe_lvm1(struct volume_id *id, uint64_t off)
+{
+	struct lvm1_super_block *lvm;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	lvm = volume_id_get_buffer(id, off + LVM1_SB_OFF, 0x800);
+	if (lvm == NULL)
+		return -1;
+
+	if (lvm->id[0] != 'H' || lvm->id[1] != 'M')
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "LVM1_member";
+
+	return 0;
+}
+
+#define LVM2_LABEL_ID			"LABELONE"
+#define LVM2LABEL_SCAN_SECTORS		4
+
+int FAST_FUNC volume_id_probe_lvm2(struct volume_id *id, uint64_t off)
+{
+	const uint8_t *buf;
+	unsigned soff;
+	struct lvm2_super_block *lvm;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off, LVM2LABEL_SCAN_SECTORS * 0x200);
+	if (buf == NULL)
+		return -1;
+
+
+	for (soff = 0; soff < LVM2LABEL_SCAN_SECTORS * 0x200; soff += 0x200) {
+		lvm = (struct lvm2_super_block *) &buf[soff];
+
+		if (memcmp(lvm->id, LVM2_LABEL_ID, 8) == 0)
+			goto found;
+	}
+
+	return -1;
+
+ found:
+//	memcpy(id->type_version, lvm->type, 8);
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "LVM2_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_mac.c b/busybox-1.19.3/util-linux/volume_id/unused_mac.c
new file mode 100644
index 0000000..e8deb97
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_mac.c
@@ -0,0 +1,123 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct mac_driver_desc {
+	uint8_t		signature[2];
+	uint16_t	block_size;
+	uint32_t	block_count;
+} PACKED;
+
+struct mac_partition {
+	uint8_t		signature[2];
+	uint16_t	res1;
+	uint32_t	map_count;
+	uint32_t	start_block;
+	uint32_t	block_count;
+	uint8_t		name[32];
+	uint8_t		type[32];
+} PACKED;
+
+int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id, uint64_t off)
+{
+	const uint8_t *buf;
+	struct mac_driver_desc *driver;
+	struct mac_partition *part;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	part = (struct mac_partition *) buf;
+	if (part->signature[0] == 'P' && part->signature[1] == 'M' /* "PM" */
+	 && (memcmp(part->type, "Apple_partition_map", 19) == 0)
+	) {
+		/* linux creates an own subdevice for the map
+		 * just return the type if the drive header is missing */
+//		volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE);
+//		id->type = "mac_partition_map";
+		return 0;
+	}
+
+	driver = (struct mac_driver_desc *) buf;
+	if (driver->signature[0] == 'E' && driver->signature[1] == 'R') { /* "ER" */
+		/* we are on a main device, like a CD
+		 * just try to probe the first partition from the map */
+		unsigned bsize = be16_to_cpu(driver->block_size);
+		int part_count;
+		int i;
+
+		/* get first entry of partition table */
+		buf = volume_id_get_buffer(id, off +  bsize, 0x200);
+		if (buf == NULL)
+			return -1;
+
+		part = (struct mac_partition *) buf;
+		if (part->signature[0] != 'P' || part->signature[1] != 'M') /* not "PM" */
+			return -1;
+
+		part_count = be32_to_cpu(part->map_count);
+		dbg("expecting %d partition entries", part_count);
+
+		if (id->partitions != NULL)
+			free(id->partitions);
+		id->partitions = xzalloc(part_count * sizeof(struct volume_id_partition));
+
+		id->partition_count = part_count;
+
+		for (i = 0; i < part_count; i++) {
+			uint64_t poff;
+			uint64_t plen;
+
+			buf = volume_id_get_buffer(id, off + ((i+1) * bsize), 0x200);
+			if (buf == NULL)
+				return -1;
+
+			part = (struct mac_partition *) buf;
+			if (part->signature[0] != 'P' || part->signature[1] != 'M') /* not "PM" */
+				return -1;
+
+			poff = be32_to_cpu(part->start_block) * bsize;
+			plen = be32_to_cpu(part->block_count) * bsize;
+			dbg("found '%s' partition entry at 0x%llx, len 0x%llx",
+					part->type, (unsigned long long) poff,
+					(unsigned long long) plen);
+
+//			id->partitions[i].pt_off = poff;
+//			id->partitions[i].pt_len = plen;
+
+//			if (memcmp(part->type, "Apple_Free", 10) == 0) {
+//				volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_UNUSED);
+//			} else if (memcmp(part->type, "Apple_partition_map", 19) == 0) {
+//				volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_PARTITIONTABLE);
+//			} else {
+//				volume_id_set_usage_part(&id->partitions[i], VOLUME_ID_UNPROBED);
+//			}
+		}
+//		volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE);
+//		id->type = "mac_partition_map";
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_minix.c b/busybox-1.19.3/util-linux/volume_id/unused_minix.c
new file mode 100644
index 0000000..a3e1077
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_minix.c
@@ -0,0 +1,75 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct minix_super_block {
+	uint16_t	s_ninodes;
+	uint16_t	s_nzones;
+	uint16_t	s_imap_blocks;
+	uint16_t	s_zmap_blocks;
+	uint16_t	s_firstdatazone;
+	uint16_t	s_log_zone_size;
+	uint32_t	s_max_size;
+	uint16_t	s_magic;
+	uint16_t	s_state;
+	uint32_t	s_zones;
+} PACKED;
+
+#define MINIX_SUPERBLOCK_OFFSET			0x400
+
+int FAST_FUNC volume_id_probe_minix(struct volume_id *id, uint64_t off)
+{
+	struct minix_super_block *ms;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	ms = volume_id_get_buffer(id, off + MINIX_SUPERBLOCK_OFFSET, 0x200);
+	if (ms == NULL)
+		return -1;
+
+	if (ms->s_magic == cpu_to_le16(0x137f)) {
+//		id->type_version[0] = '1';
+		goto found;
+	}
+
+	if (ms->s_magic == cpu_to_le16(0x1387)) {
+//		id->type_version[0] = '1';
+		goto found;
+	}
+
+	if (ms->s_magic == cpu_to_le16(0x2468)) {
+//		id->type_version[0] = '2';
+		goto found;
+	}
+
+	if (ms->s_magic == cpu_to_le16(0x2478)) {
+//		id->type_version[0] = '2';
+		goto found;
+	}
+
+	return -1;
+
+ found:
+//	id->type_version[1] = '\0';
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	id->type = "minix";
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_msdos.c b/busybox-1.19.3/util-linux/volume_id/unused_msdos.c
new file mode 100644
index 0000000..65fb885
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_msdos.c
@@ -0,0 +1,195 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct msdos_partition_entry {
+	uint8_t		boot_ind;
+	uint8_t		head;
+	uint8_t		sector;
+	uint8_t		cyl;
+	uint8_t		sys_ind;
+	uint8_t		end_head;
+	uint8_t		end_sector;
+	uint8_t		end_cyl;
+	uint32_t	start_sect;
+	uint32_t	nr_sects;
+} PACKED;
+
+#define MSDOS_PARTTABLE_OFFSET		0x1be
+#define MSDOS_SIG_OFF			0x1fe
+#define BSIZE				0x200
+#define DOS_EXTENDED_PARTITION		0x05
+#define LINUX_EXTENDED_PARTITION	0x85
+#define WIN98_EXTENDED_PARTITION	0x0f
+#define LINUX_RAID_PARTITION		0xfd
+#define is_extended(type) \
+	(type == DOS_EXTENDED_PARTITION ||	\
+	 type == WIN98_EXTENDED_PARTITION ||	\
+	 type == LINUX_EXTENDED_PARTITION)
+#define is_raid(type) \
+	(type == LINUX_RAID_PARTITION)
+
+int FAST_FUNC volume_id_probe_msdos_part_table(struct volume_id *id, uint64_t off)
+{
+	const uint8_t *buf;
+	int i;
+	uint64_t poff;
+	uint64_t plen;
+	uint64_t extended = 0;
+	uint64_t current;
+	uint64_t next;
+	int limit;
+	int empty = 1;
+	struct msdos_partition_entry *part;
+	struct volume_id_partition *p;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	buf = volume_id_get_buffer(id, off, 0x200);
+	if (buf == NULL)
+		return -1;
+
+	if (buf[MSDOS_SIG_OFF] != 0x55 || buf[MSDOS_SIG_OFF + 1] != 0xaa)
+		return -1;
+
+	/* check flags on all entries for a valid partition table */
+	part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
+	for (i = 0; i < 4; i++) {
+		if (part[i].boot_ind != 0
+		 && part[i].boot_ind != 0x80
+		) {
+			return -1;
+		}
+
+		if (part[i].nr_sects != 0)
+			empty = 0;
+	}
+	if (empty == 1)
+		return -1;
+
+	if (id->partitions != NULL)
+		free(id->partitions);
+	id->partitions = xzalloc(VOLUME_ID_PARTITIONS_MAX *
+				sizeof(struct volume_id_partition));
+
+	for (i = 0; i < 4; i++) {
+		poff = (uint64_t) le32_to_cpu(part[i].start_sect) * BSIZE;
+		plen = (uint64_t) le32_to_cpu(part[i].nr_sects) * BSIZE;
+
+		if (plen == 0)
+			continue;
+
+		p = &id->partitions[i];
+
+//		p->pt_type_raw = part[i].sys_ind;
+
+		if (is_extended(part[i].sys_ind)) {
+			dbg("found extended partition at 0x%llx", (unsigned long long) poff);
+//			volume_id_set_usage_part(p, VOLUME_ID_PARTITIONTABLE);
+//			p->type = "msdos_extended_partition";
+			if (extended == 0)
+				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);
+
+//			if (is_raid(part[i].sys_ind))
+//				volume_id_set_usage_part(p, VOLUME_ID_RAID);
+//			else
+//				volume_id_set_usage_part(p, VOLUME_ID_UNPROBED);
+		}
+
+//		p->pt_off = off + poff;
+//		p->pt_len = plen;
+		id->partition_count = i+1;
+	}
+
+	next = extended;
+	current = extended;
+	limit = 50;
+
+	/* follow extended partition chain and add data partitions */
+	while (next != 0) {
+		if (limit-- == 0) {
+			dbg("extended chain limit reached");
+			break;
+		}
+
+		buf = volume_id_get_buffer(id, current, 0x200);
+		if (buf == NULL)
+			break;
+
+		part = (struct msdos_partition_entry*) &buf[MSDOS_PARTTABLE_OFFSET];
+
+		if (buf[MSDOS_SIG_OFF] != 0x55 || buf[MSDOS_SIG_OFF + 1] != 0xaa)
+			break;
+
+		next = 0;
+
+		for (i = 0; i < 4; i++) {
+			poff = (uint64_t) le32_to_cpu(part[i].start_sect) * BSIZE;
+			plen = (uint64_t) le32_to_cpu(part[i].nr_sects) * BSIZE;
+
+			if (plen == 0)
+				continue;
+
+			if (is_extended(part[i].sys_ind)) {
+				dbg("found extended partition at 0x%llx", (unsigned long long) poff);
+				if (next == 0)
+					next = extended + 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);
+
+				/* we always start at the 5th entry */
+//				while (id->partition_count < 4)
+//					volume_id_set_usage_part(&id->partitions[id->partition_count++], VOLUME_ID_UNUSED);
+				if (id->partition_count < 4)
+					id->partition_count = 4;
+
+				p = &id->partitions[id->partition_count];
+
+//				if (is_raid(part[i].sys_ind))
+//					volume_id_set_usage_part(p, VOLUME_ID_RAID);
+//				else
+//					volume_id_set_usage_part(p, VOLUME_ID_UNPROBED);
+
+//				p->pt_off = current + poff;
+//				p->pt_len = plen;
+				id->partition_count++;
+
+//				p->pt_type_raw = part[i].sys_ind;
+
+				if (id->partition_count >= VOLUME_ID_PARTITIONS_MAX) {
+					dbg("too many partitions");
+					next = 0;
+				}
+			}
+		}
+
+		current = next;
+	}
+
+//	volume_id_set_usage(id, VOLUME_ID_PARTITIONTABLE);
+//	id->type = "msdos_partition_table";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_nvidia_raid.c b/busybox-1.19.3/util-linux/volume_id/unused_nvidia_raid.c
new file mode 100644
index 0000000..9e84729
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_nvidia_raid.c
@@ -0,0 +1,56 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct nvidia_meta {
+	uint8_t		vendor[8];
+	uint32_t	size;
+	uint32_t	chksum;
+	uint16_t	version;
+} PACKED;
+
+#define NVIDIA_SIGNATURE		"NVIDIA"
+
+int FAST_FUNC volume_id_probe_nvidia_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct nvidia_meta *nv;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-2) * 0x200;
+	nv = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (nv == NULL)
+		return -1;
+
+	if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	snprintf(id->type_version, sizeof(id->type_version)-1, "%u", le16_to_cpu(nv->version));
+//	id->type = "nvidia_raid_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_promise_raid.c b/busybox-1.19.3/util-linux/volume_id/unused_promise_raid.c
new file mode 100644
index 0000000..0b0d006
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_promise_raid.c
@@ -0,0 +1,63 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct promise_meta {
+	uint8_t	sig[24];
+} PACKED;
+
+#define PDC_CONFIG_OFF		0x1200
+#define PDC_SIGNATURE		"Promise Technology, Inc."
+
+int FAST_FUNC volume_id_probe_promise_fasttrack_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	static const unsigned short sectors[] = {
+		63, 255, 256, 16, 399
+	};
+
+	struct promise_meta *pdc;
+	unsigned i;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x40000)
+		return -1;
+
+	for (i = 0; i < ARRAY_SIZE(sectors); i++) {
+		uint64_t meta_off;
+
+		meta_off = ((size / 0x200) - sectors[i]) * 0x200;
+		pdc = volume_id_get_buffer(id, off + meta_off, 0x200);
+		if (pdc == NULL)
+			return -1;
+
+		if (memcmp(pdc->sig, PDC_SIGNATURE, sizeof(PDC_SIGNATURE)-1) == 0)
+			goto found;
+	}
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type = "promise_fasttrack_raid_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_silicon_raid.c b/busybox-1.19.3/util-linux/volume_id/unused_silicon_raid.c
new file mode 100644
index 0000000..d1c439e
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_silicon_raid.c
@@ -0,0 +1,69 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct silicon_meta {
+	uint8_t		unknown0[0x2E];
+	uint8_t		ascii_version[0x36 - 0x2E];
+	uint8_t		diskname[0x56 - 0x36];
+	uint8_t		unknown1[0x60 - 0x56];
+	uint32_t	magic;
+	uint32_t	unknown1a[0x6C - 0x64];
+	uint32_t	array_sectors_low;
+	uint32_t	array_sectors_high;
+	uint8_t		unknown2[0x78 - 0x74];
+	uint32_t	thisdisk_sectors;
+	uint8_t		unknown3[0x100 - 0x7C];
+	uint8_t		unknown4[0x104 - 0x100];
+	uint16_t	product_id;
+	uint16_t	vendor_id;
+	uint16_t	minor_ver;
+	uint16_t	major_ver;
+} PACKED;
+
+#define SILICON_MAGIC		0x2F000000
+
+int FAST_FUNC volume_id_probe_silicon_medley_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct silicon_meta *sil;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-1) * 0x200;
+	sil = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (sil == NULL)
+		return -1;
+
+	if (sil->magic != cpu_to_le32(SILICON_MAGIC))
+		return -1;
+
+//	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));
+//	id->type = "silicon_medley_raid_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_ufs.c b/busybox-1.19.3/util-linux/volume_id/unused_ufs.c
new file mode 100644
index 0000000..9f925d9
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_ufs.c
@@ -0,0 +1,206 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct ufs_super_block {
+	uint32_t	fs_link;
+	uint32_t	fs_rlink;
+	uint32_t	fs_sblkno;
+	uint32_t	fs_cblkno;
+	uint32_t	fs_iblkno;
+	uint32_t	fs_dblkno;
+	uint32_t	fs_cgoffset;
+	uint32_t	fs_cgmask;
+	uint32_t	fs_time;
+	uint32_t	fs_size;
+	uint32_t	fs_dsize;
+	uint32_t	fs_ncg;
+	uint32_t	fs_bsize;
+	uint32_t	fs_fsize;
+	uint32_t	fs_frag;
+	uint32_t	fs_minfree;
+	uint32_t	fs_rotdelay;
+	uint32_t	fs_rps;
+	uint32_t	fs_bmask;
+	uint32_t	fs_fmask;
+	uint32_t	fs_bshift;
+	uint32_t	fs_fshift;
+	uint32_t	fs_maxcontig;
+	uint32_t	fs_maxbpg;
+	uint32_t	fs_fragshift;
+	uint32_t	fs_fsbtodb;
+	uint32_t	fs_sbsize;
+	uint32_t	fs_csmask;
+	uint32_t	fs_csshift;
+	uint32_t	fs_nindir;
+	uint32_t	fs_inopb;
+	uint32_t	fs_nspf;
+	uint32_t	fs_optim;
+	uint32_t	fs_npsect_state;
+	uint32_t	fs_interleave;
+	uint32_t	fs_trackskew;
+	uint32_t	fs_id[2];
+	uint32_t	fs_csaddr;
+	uint32_t	fs_cssize;
+	uint32_t	fs_cgsize;
+	uint32_t	fs_ntrak;
+	uint32_t	fs_nsect;
+	uint32_t	fs_spc;
+	uint32_t	fs_ncyl;
+	uint32_t	fs_cpg;
+	uint32_t	fs_ipg;
+	uint32_t	fs_fpg;
+	struct ufs_csum {
+		uint32_t	cs_ndir;
+		uint32_t	cs_nbfree;
+		uint32_t	cs_nifree;
+		uint32_t	cs_nffree;
+	} PACKED fs_cstotal;
+	int8_t		fs_fmod;
+	int8_t		fs_clean;
+	int8_t		fs_ronly;
+	int8_t		fs_flags;
+	union {
+		struct {
+			int8_t	fs_fsmnt[512];
+			uint32_t	fs_cgrotor;
+			uint32_t	fs_csp[31];
+			uint32_t	fs_maxcluster;
+			uint32_t	fs_cpc;
+			uint16_t	fs_opostbl[16][8];
+		} PACKED fs_u1;
+		struct {
+			int8_t		fs_fsmnt[468];
+			uint8_t		fs_volname[32];
+			uint64_t	fs_swuid;
+			int32_t		fs_pad;
+			uint32_t	fs_cgrotor;
+			uint32_t	fs_ocsp[28];
+			uint32_t	fs_contigdirs;
+			uint32_t	fs_csp;
+			uint32_t	fs_maxcluster;
+			uint32_t	fs_active;
+			int32_t		fs_old_cpc;
+			int32_t		fs_maxbsize;
+			int64_t		fs_sparecon64[17];
+			int64_t		fs_sblockloc;
+			struct ufs2_csum_total {
+				uint64_t	cs_ndir;
+				uint64_t	cs_nbfree;
+				uint64_t	cs_nifree;
+				uint64_t	cs_nffree;
+				uint64_t	cs_numclusters;
+				uint64_t	cs_spare[3];
+			} PACKED fs_cstotal;
+			struct ufs_timeval {
+				int32_t		tv_sec;
+				int32_t		tv_usec;
+			} PACKED fs_time;
+			int64_t		fs_size;
+			int64_t		fs_dsize;
+			uint64_t	fs_csaddr;
+			int64_t		fs_pendingblocks;
+			int32_t		fs_pendinginodes;
+		} PACKED fs_u2;
+	}  fs_u11;
+	union {
+		struct {
+			int32_t		fs_sparecon[53];
+			int32_t		fs_reclaim;
+			int32_t		fs_sparecon2[1];
+			int32_t		fs_state;
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+		} PACKED fs_sun;
+		struct {
+			int32_t		fs_sparecon[53];
+			int32_t		fs_reclaim;
+			int32_t		fs_sparecon2[1];
+			uint32_t	fs_npsect;
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+		} PACKED fs_sunx86;
+		struct {
+			int32_t		fs_sparecon[50];
+			int32_t		fs_contigsumsize;
+			int32_t		fs_maxsymlinklen;
+			int32_t		fs_inodefmt;
+			uint32_t	fs_maxfilesize[2];
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+			int32_t		fs_state;
+		} PACKED fs_44;
+	} fs_u2;
+	int32_t		fs_postblformat;
+	int32_t		fs_nrpos;
+	int32_t		fs_postbloff;
+	int32_t		fs_rotbloff;
+	uint32_t	fs_magic;
+	uint8_t		fs_space[1];
+} PACKED;
+
+#define UFS_MAGIC			0x00011954
+#define UFS2_MAGIC			0x19540119
+#define UFS_MAGIC_FEA			0x00195612
+#define UFS_MAGIC_LFN			0x00095014
+
+int FAST_FUNC volume_id_probe_ufs(struct volume_id *id, uint64_t off)
+{
+	static const short offsets[] = { 0, 8, 64, 256 };
+
+	uint32_t magic;
+	unsigned i;
+	struct ufs_super_block *ufs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	for (i = 0; i < ARRAY_SIZE(offsets); i++) {
+		ufs = volume_id_get_buffer(id, off + (offsets[i] * 0x400), 0x800);
+		if (ufs == NULL)
+			return -1;
+
+		dbg("offset 0x%x", offsets[i] * 0x400);
+		magic = ufs->fs_magic;
+		if ((magic == cpu_to_be32(UFS_MAGIC))
+		 || (magic == cpu_to_be32(UFS2_MAGIC))
+		 || (magic == cpu_to_be32(UFS_MAGIC_FEA))
+		 || (magic == cpu_to_be32(UFS_MAGIC_LFN))
+		) {
+			dbg("magic 0x%08x(be)", magic);
+			goto found;
+		}
+		if ((magic == cpu_to_le32(UFS_MAGIC))
+		 || (magic == cpu_to_le32(UFS2_MAGIC))
+		 || (magic == cpu_to_le32(UFS_MAGIC_FEA))
+		 || (magic == cpu_to_le32(UFS_MAGIC_LFN))
+		) {
+			dbg("magic 0x%08x(le)", magic);
+			goto found;
+		}
+	}
+	return -1;
+
+ found:
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+//	id->type = "ufs";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/unused_via_raid.c b/busybox-1.19.3/util-linux/volume_id/unused_via_raid.c
new file mode 100644
index 0000000..a11eec1
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/unused_via_raid.c
@@ -0,0 +1,68 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct via_meta {
+	uint16_t	signature;
+	uint8_t		version_number;
+	struct via_array {
+		uint16_t	disk_bits;
+		uint8_t		disk_array_ex;
+		uint32_t	capacity_low;
+		uint32_t	capacity_high;
+		uint32_t	serial_checksum;
+	} PACKED array;
+	uint32_t	serial_checksum[8];
+	uint8_t		checksum;
+} PACKED;
+
+#define VIA_SIGNATURE		0xAA55
+
+int FAST_FUNC volume_id_probe_via_raid(struct volume_id *id, uint64_t off, uint64_t size)
+{
+	uint64_t meta_off;
+	struct via_meta *via;
+
+	dbg("probing at offset 0x%llx, size 0x%llx",
+	    (unsigned long long) off, (unsigned long long) size);
+
+	if (size < 0x10000)
+		return -1;
+
+	meta_off = ((size / 0x200)-1) * 0x200;
+
+	via = volume_id_get_buffer(id, off + meta_off, 0x200);
+	if (via == NULL)
+		return -1;
+
+	if (via->signature != cpu_to_le16(VIA_SIGNATURE))
+		return -1;
+
+	if (via->version_number > 1)
+		return -1;
+
+//	volume_id_set_usage(id, VOLUME_ID_RAID);
+//	id->type_version[0] = '0' + via->version_number;
+//	id->type_version[1] = '\0';
+//	id->type = "via_raid_member";
+
+	return 0;
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/util.c b/busybox-1.19.3/util-linux/volume_id/util.c
new file mode 100644
index 0000000..dd75c7b
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/util.c
@@ -0,0 +1,282 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count)
+{
+	unsigned i, j;
+	unsigned c;
+
+	j = 0;
+	for (i = 0; i + 2 <= count; i += 2) {
+		if (endianess == LE)
+			c = (buf[i+1] << 8) | buf[i];
+		else
+			c = (buf[i] << 8) | buf[i+1];
+		if (c == 0) {
+			str[j] = '\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));
+		} else {
+			if (j+3 >= len)
+				break;
+			str[j++] = (uint8_t) (0xe0 | (c >> 12));
+			str[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+			str[j++] = (uint8_t) (0x80 | (c & 0x3f));
+		}
+	}
+	str[j] = '\0';
+}
+
+#ifdef UNUSED
+static const char *usage_to_string(enum volume_id_usage usage_id)
+{
+	switch (usage_id) {
+	case VOLUME_ID_FILESYSTEM:
+		return "filesystem";
+	case VOLUME_ID_PARTITIONTABLE:
+		return "partitiontable";
+	case VOLUME_ID_OTHER:
+		return "other";
+	case VOLUME_ID_RAID:
+		return "raid";
+	case VOLUME_ID_DISKLABEL:
+		return "disklabel";
+	case VOLUME_ID_CRYPTO:
+		return "crypto";
+	case VOLUME_ID_UNPROBED:
+		return "unprobed";
+	case VOLUME_ID_UNUSED:
+		return "unused";
+	}
+	return NULL;
+}
+
+void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id)
+{
+	part->usage_id = usage_id;
+	part->usage = usage_to_string(usage_id);
+}
+
+void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id)
+{
+	id->usage_id = usage_id;
+	id->usage = usage_to_string(usage_id);
+}
+
+void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count)
+{
+	memcpy(id->label_raw, buf, count);
+	id->label_raw_len = count;
+}
+#endif
+
+#ifdef NOT_NEEDED
+static size_t strnlen(const char *s, size_t maxlen)
+{
+	size_t i;
+	if (!maxlen) return 0;
+	if (!s) return 0;
+	for (i = 0; *s && i < maxlen; ++s) ++i;
+	return i;
+}
+#endif
+
+void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count)
+{
+	unsigned i;
+
+	memcpy(id->label, buf, count);
+
+	/* remove trailing whitespace */
+	i = strnlen(id->label, count);
+	while (i--) {
+		if (!isspace(id->label[i]))
+			break;
+	}
+	id->label[i+1] = '\0';
+}
+
+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);
+}
+
+void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format)
+{
+	unsigned i;
+	unsigned count = 0;
+
+	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;
+
+	/* if set, create string in the same format, the native platform uses */
+	for (i = 0; i < count; i++)
+		if (buf[i] != 0)
+			goto set;
+	return; /* all bytes are zero, leave it empty ("") */
+
+set:
+	switch (format) {
+	case UUID_DOS:
+		sprintf(id->uuid, "%02X%02X-%02X%02X",
+			buf[3], buf[2], buf[1], buf[0]);
+		break;
+	case UUID_NTFS:
+		sprintf(id->uuid, "%02X%02X%02X%02X%02X%02X%02X%02X",
+			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",
+			buf[0], buf[1], buf[2], buf[3],
+			buf[4], buf[5],
+			buf[6], buf[7],
+			buf[8], buf[9],
+			buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+		break;
+	case UUID_DCE_STRING:
+		memcpy(id->uuid, buf, count);
+		id->uuid[count] = '\0';
+		break;
+	}
+}
+
+/* Do not use xlseek here. With it, single corrupted filesystem
+ * may result in attempt to seek past device -> exit.
+ * It's better to ignore such fs and continue.  */
+void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len)
+{
+	uint8_t *dst;
+	unsigned small_off;
+	ssize_t read_len;
+
+	dbg("get buffer off 0x%llx(%llu), len 0x%zx",
+		(unsigned long long) off, (unsigned long long) off, len);
+
+	/* check if requested area fits in superblock buffer */
+	if (off + len <= SB_BUFFER_SIZE
+	 /* && off <= SB_BUFFER_SIZE - want this paranoid overflow check? */
+	) {
+		if (id->sbbuf == NULL) {
+			id->sbbuf = xmalloc(SB_BUFFER_SIZE);
+		}
+		small_off = off;
+		dst = id->sbbuf;
+
+		/* check if we need to read */
+		len += off;
+		if (len <= id->sbbuf_len)
+			goto ret; /* we already have it */
+
+		dbg("read sbbuf len:0x%x", (unsigned) len);
+		id->sbbuf_len = len;
+		off = 0;
+		goto do_read;
+	}
+
+	if (len > SEEK_BUFFER_SIZE) {
+		dbg("seek buffer too small %d", SEEK_BUFFER_SIZE);
+		return NULL;
+	}
+	dst = id->seekbuf;
+
+	/* check if we need to read */
+	if ((off >= id->seekbuf_off)
+	 && ((off + len) <= (id->seekbuf_off + id->seekbuf_len))
+	) {
+		small_off = off - id->seekbuf_off; /* can't overflow */
+		goto ret; /* we already have it */
+	}
+
+	id->seekbuf_off = off;
+	id->seekbuf_len = len;
+	id->seekbuf = xrealloc(id->seekbuf, len);
+	small_off = 0;
+	dst = id->seekbuf;
+	dbg("read seekbuf off:0x%llx len:0x%zx",
+				(unsigned long long) off, len);
+ do_read:
+	if (lseek(id->fd, off, SEEK_SET) != off) {
+		dbg("seek(0x%llx) failed", (unsigned long long) off);
+		goto err;
+	}
+	read_len = full_read(id->fd, dst, len);
+	if (read_len != len) {
+		dbg("requested 0x%x bytes, got 0x%x bytes",
+				(unsigned) len, (unsigned) read_len);
+ err:
+		/* No filesystem can be this tiny. It's most likely
+		 * non-associated loop device, empty drive and so on.
+		 * Flag it, making it possible to short circuit future
+		 * accesses. Rationale:
+		 * users complained of slow blkid due to empty floppy drives.
+		 */
+		if (off < 64*1024)
+			id->error = 1;
+		/* id->seekbuf_len or id->sbbuf_len is wrong now! Fixing. */
+		volume_id_free_buffer(id);
+		return NULL;
+	}
+ ret:
+	return dst + small_off;
+}
+
+void volume_id_free_buffer(struct volume_id *id)
+{
+	free(id->sbbuf);
+	id->sbbuf = NULL;
+	id->sbbuf_len = 0;
+	free(id->seekbuf);
+	id->seekbuf = NULL;
+	id->seekbuf_len = 0;
+	id->seekbuf_off = 0; /* paranoia */
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/volume_id.c b/busybox-1.19.3/util-linux/volume_id/volume_id.c
new file mode 100644
index 0000000..f41d4e0
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/volume_id.c
@@ -0,0 +1,246 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+
+/* Some detection routines do not set label or uuid anyway,
+ * so they are disabled. */
+
+/* Looks for partitions, we don't use it: */
+#define ENABLE_FEATURE_VOLUMEID_MAC           0
+/* #define ENABLE_FEATURE_VOLUMEID_MSDOS      0 - NB: this one
+ * was not properly added to probe table anyway - ??! */
+
+/* None of RAIDs have label or uuid, except LinuxRAID: */
+#define ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID 0
+#define ENABLE_FEATURE_VOLUMEID_ISWRAID       0
+#define ENABLE_FEATURE_VOLUMEID_LSIRAID       0
+#define ENABLE_FEATURE_VOLUMEID_LVM           0
+#define ENABLE_FEATURE_VOLUMEID_NVIDIARAID    0
+#define ENABLE_FEATURE_VOLUMEID_PROMISERAID   0
+#define ENABLE_FEATURE_VOLUMEID_SILICONRAID   0
+#define ENABLE_FEATURE_VOLUMEID_VIARAID       0
+
+/* These filesystems also have no label or uuid: */
+#define ENABLE_FEATURE_VOLUMEID_MINIX         0
+#define ENABLE_FEATURE_VOLUMEID_HPFS          0
+#define ENABLE_FEATURE_VOLUMEID_UFS           0
+
+
+typedef int FAST_FUNC (*raid_probe_fptr)(struct volume_id *id, /*uint64_t off,*/ uint64_t size);
+typedef int FAST_FUNC (*probe_fptr)(struct volume_id *id /*, uint64_t off*/);
+
+static const raid_probe_fptr raid1[] = {
+#if ENABLE_FEATURE_VOLUMEID_LINUXRAID
+	volume_id_probe_linux_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_ISWRAID
+	volume_id_probe_intel_software_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_LSIRAID
+	volume_id_probe_lsi_mega_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_VIARAID
+	volume_id_probe_via_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_SILICONRAID
+	volume_id_probe_silicon_medley_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_NVIDIARAID
+	volume_id_probe_nvidia_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_PROMISERAID
+	volume_id_probe_promise_fasttrack_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID
+	volume_id_probe_highpoint_45x_raid,
+#endif
+};
+
+static const probe_fptr raid2[] = {
+#if ENABLE_FEATURE_VOLUMEID_LVM
+	volume_id_probe_lvm1,
+	volume_id_probe_lvm2,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HIGHPOINTRAID
+	volume_id_probe_highpoint_37x_raid,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_LUKS
+	volume_id_probe_luks,
+#endif
+};
+
+/* signature in the first block, only small buffer needed */
+static const probe_fptr fs1[] = {
+#if ENABLE_FEATURE_VOLUMEID_FAT
+	volume_id_probe_vfat,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_MAC
+	volume_id_probe_mac_partition_map,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_XFS
+	volume_id_probe_xfs,
+#endif
+};
+
+/* fill buffer with maximum */
+static const probe_fptr fs2[] = {
+#if ENABLE_FEATURE_VOLUMEID_LINUXSWAP
+	volume_id_probe_linux_swap,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_EXT
+	volume_id_probe_ext,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_BTRFS
+	volume_id_probe_btrfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_REISERFS
+	volume_id_probe_reiserfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_JFS
+	volume_id_probe_jfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_UDF
+	volume_id_probe_udf,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_ISO9660
+	volume_id_probe_iso9660,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HFS
+	volume_id_probe_hfs_hfsplus,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_UFS
+	volume_id_probe_ufs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_NTFS
+	volume_id_probe_ntfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_CRAMFS
+	volume_id_probe_cramfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_ROMFS
+	volume_id_probe_romfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_HPFS
+	volume_id_probe_hpfs,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_SYSV
+	volume_id_probe_sysv,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_MINIX
+	volume_id_probe_minix,
+#endif
+#if ENABLE_FEATURE_VOLUMEID_OCFS2
+	volume_id_probe_ocfs2,
+#endif
+};
+
+int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size)
+{
+	unsigned i;
+
+	/* probe for raid first, cause fs probes may be successful on raid members */
+	if (size) {
+		for (i = 0; i < ARRAY_SIZE(raid1); i++) {
+			if (raid1[i](id, /*off,*/ size) == 0)
+				goto ret;
+			if (id->error)
+				goto ret;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(raid2); i++) {
+		if (raid2[i](id /*,off*/) == 0)
+			goto ret;
+		if (id->error)
+			goto ret;
+	}
+
+	/* signature in the first block, only small buffer needed */
+	for (i = 0; i < ARRAY_SIZE(fs1); i++) {
+		if (fs1[i](id /*,off*/) == 0)
+			goto ret;
+		if (id->error)
+			goto ret;
+	}
+
+	/* fill buffer with maximum */
+	volume_id_get_buffer(id, 0, SB_BUFFER_SIZE);
+
+	for (i = 0; i < ARRAY_SIZE(fs2); i++) {
+		if (fs2[i](id /*,off*/) == 0)
+			goto ret;
+		if (id->error)
+			goto ret;
+	}
+
+ ret:
+	volume_id_free_buffer(id);
+	return (- id->error); /* 0 or -1 */
+}
+
+/* open volume by device node */
+struct volume_id* FAST_FUNC volume_id_open_node(int fd)
+{
+	struct volume_id *id;
+
+	id = xzalloc(sizeof(struct volume_id));
+	id->fd = fd;
+	///* close fd on device close */
+	//id->fd_close = 1;
+	return id;
+}
+
+#ifdef UNUSED
+/* open volume by major/minor */
+struct volume_id* FAST_FUNC volume_id_open_dev_t(dev_t devt)
+{
+	struct volume_id *id;
+	char *tmp_node[VOLUME_ID_PATH_MAX];
+
+	tmp_node = xasprintf("/dev/.volume_id-%u-%u-%u",
+		(unsigned)getpid(), (unsigned)major(devt), (unsigned)minor(devt));
+
+	/* create temporary node to open block device */
+	unlink(tmp_node);
+	if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
+		bb_perror_msg_and_die("can't mknod(%s)", tmp_node);
+
+	id = volume_id_open_node(tmp_node);
+	unlink(tmp_node);
+	free(tmp_node);
+	return id;
+}
+#endif
+
+void FAST_FUNC free_volume_id(struct volume_id *id)
+{
+	if (id == NULL)
+		return;
+
+	//if (id->fd_close != 0) - always true
+		close(id->fd);
+	volume_id_free_buffer(id);
+#ifdef UNUSED_PARTITION_CODE
+	free(id->partitions);
+#endif
+	free(id);
+}
diff --git a/busybox-1.19.3/util-linux/volume_id/volume_id_internal.h b/busybox-1.19.3/util-linux/volume_id/volume_id_internal.h
new file mode 100644
index 0000000..1c64046
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/volume_id_internal.h
@@ -0,0 +1,231 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "libbb.h"
+#include "volume_id.h"
+
+PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
+
+#define dbg(...) ((void)0)
+/* #define dbg(...) bb_error_msg(__VA_ARGS__) */
+
+/* volume_id.h */
+
+#define VOLUME_ID_VERSION		48
+
+#define VOLUME_ID_LABEL_SIZE		64
+#define VOLUME_ID_UUID_SIZE		36
+#define VOLUME_ID_FORMAT_SIZE		32
+#define VOLUME_ID_PARTITIONS_MAX	256
+
+enum volume_id_usage {
+	VOLUME_ID_UNUSED,
+	VOLUME_ID_UNPROBED,
+	VOLUME_ID_OTHER,
+	VOLUME_ID_FILESYSTEM,
+	VOLUME_ID_PARTITIONTABLE,
+	VOLUME_ID_RAID,
+	VOLUME_ID_DISKLABEL,
+	VOLUME_ID_CRYPTO,
+};
+
+#ifdef UNUSED_PARTITION_CODE
+struct volume_id_partition {
+//	const char	*type;
+//	const char	*usage;
+//	smallint	usage_id;
+//	uint8_t		pt_type_raw;
+//	uint64_t	pt_off;
+//	uint64_t	pt_len;
+};
+#endif
+
+struct volume_id {
+	int		fd;
+//	int		fd_close:1;
+	int		error;
+	size_t		sbbuf_len;
+	size_t		seekbuf_len;
+	uint8_t		*sbbuf;
+	uint8_t		*seekbuf;
+	uint64_t	seekbuf_off;
+#ifdef UNUSED_PARTITION_CODE
+	struct volume_id_partition *partitions;
+	size_t		partition_count;
+#endif
+//	uint8_t		label_raw[VOLUME_ID_LABEL_SIZE];
+//	size_t		label_raw_len;
+	char		label[VOLUME_ID_LABEL_SIZE+1];
+//	uint8_t		uuid_raw[VOLUME_ID_UUID_SIZE];
+//	size_t		uuid_raw_len;
+	/* uuid is stored in ASCII (not binary) form here: */
+	char		uuid[VOLUME_ID_UUID_SIZE+1];
+//	char		type_version[VOLUME_ID_FORMAT_SIZE];
+//	smallint	usage_id;
+//	const char	*usage;
+#if ENABLE_FEATURE_BLKID_TYPE
+	const char	*type;
+#endif
+};
+
+struct volume_id* FAST_FUNC volume_id_open_node(int fd);
+int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size);
+void FAST_FUNC free_volume_id(struct volume_id *id);
+
+/* util.h */
+
+/* size of superblock buffer, reiserfs block is at 64k */
+#define SB_BUFFER_SIZE				0x11000
+/* 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)
+#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)
+#endif
+
+enum uuid_format {
+	UUID_DCE_STRING,
+	UUID_DCE,
+	UUID_DOS,
+	UUID_NTFS,
+	UUID_HFS,
+};
+
+enum endian {
+	LE = 0,
+	BE = 1
+};
+
+void volume_id_set_unicode16(char *str, size_t len, const uint8_t *buf, enum endian endianess, size_t count);
+//void volume_id_set_usage(struct volume_id *id, enum volume_id_usage usage_id);
+//void volume_id_set_usage_part(struct volume_id_partition *part, enum volume_id_usage usage_id);
+//void volume_id_set_label_raw(struct volume_id *id, const uint8_t *buf, size_t count);
+void volume_id_set_label_string(struct volume_id *id, const uint8_t *buf, size_t count);
+void volume_id_set_label_unicode16(struct volume_id *id, const uint8_t *buf, enum endian endianess, size_t count);
+void volume_id_set_uuid(struct volume_id *id, const uint8_t *buf, enum uuid_format format);
+void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len);
+void volume_id_free_buffer(struct volume_id *id);
+
+
+/* Probe routines */
+
+/* RAID */
+
+//int FAST_FUNC volume_id_probe_highpoint_37x_raid(struct volume_id *id /*,uint64_t off*/);
+//int FAST_FUNC volume_id_probe_highpoint_45x_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_intel_software_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+int FAST_FUNC volume_id_probe_linux_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_lsi_mega_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_nvidia_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_promise_fasttrack_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_silicon_medley_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_via_raid(struct volume_id *id /*,uint64_t off*/, uint64_t size);
+
+//int FAST_FUNC volume_id_probe_lvm1(struct volume_id *id /*,uint64_t off*/);
+//int FAST_FUNC volume_id_probe_lvm2(struct volume_id *id /*,uint64_t off*/);
+
+/* FS */
+
+int FAST_FUNC volume_id_probe_btrfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_cramfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_ext(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_hfs_hfsplus(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_hpfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_iso9660(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_jfs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_linux_swap(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_luks(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_mac_partition_map(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_minix(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_msdos_part_table(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_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_sysv(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_udf(struct volume_id *id /*,uint64_t off*/);
+
+//int FAST_FUNC volume_id_probe_ufs(struct volume_id *id /*,uint64_t off*/);
+
+int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/);
+
+POP_SAVED_FUNCTION_VISIBILITY
diff --git a/busybox-1.19.3/util-linux/volume_id/xfs.c b/busybox-1.19.3/util-linux/volume_id/xfs.c
new file mode 100644
index 0000000..8474602
--- /dev/null
+++ b/busybox-1.19.3/util-linux/volume_id/xfs.c
@@ -0,0 +1,60 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.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
+ */
+
+#include "volume_id_internal.h"
+
+struct xfs_super_block {
+	uint8_t	magic[4];
+	uint32_t	blocksize;
+	uint64_t	dblocks;
+	uint64_t	rblocks;
+	uint32_t	dummy1[2];
+	uint8_t	uuid[16];
+	uint32_t	dummy2[15];
+	uint8_t	fname[12];
+	uint32_t	dummy3[2];
+	uint64_t	icount;
+	uint64_t	ifree;
+	uint64_t	fdblocks;
+} PACKED;
+
+int FAST_FUNC volume_id_probe_xfs(struct volume_id *id /*,uint64_t off*/)
+{
+#define off ((uint64_t)0)
+	struct xfs_super_block *xs;
+
+	dbg("probing at offset 0x%llx", (unsigned long long) off);
+
+	xs = volume_id_get_buffer(id, off, 0x200);
+	if (xs == NULL)
+		return -1;
+
+	if (memcmp(xs->magic, "XFSB", 4) != 0)
+		return -1;
+
+//	volume_id_set_label_raw(id, xs->fname, 12);
+	volume_id_set_label_string(id, xs->fname, 12);
+	volume_id_set_uuid(id, xs->uuid, UUID_DCE);
+
+//	volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
+	IF_FEATURE_BLKID_TYPE(id->type = "xfs";)
+
+	return 0;
+}
diff --git a/busybox.patches/busybox-50.description b/busybox.patches/busybox-50.description
new file mode 100644
index 0000000..3ee154b
--- /dev/null
+++ b/busybox.patches/busybox-50.description
@@ -0,0 +1 @@
+This patch enables support for enabling -fno-strict-aliasing when compiling with GCC greater than or equal to 4.4.
diff --git a/busybox.patches/busybox-50.patch b/busybox.patches/busybox-50.patch
new file mode 100644
index 0000000..08d388e
--- /dev/null
+++ b/busybox.patches/busybox-50.patch
@@ -0,0 +1,12 @@
+diff -aruN a/Makefile.flags b/Makefile.flags
+--- a/Makefile.flags	2010-08-22 01:21:38.000000000 -0700
++++ b/Makefile.flags	2010-10-08 14:45:16.517324148 -0700
+@@ -41,7 +41,7 @@
+ ## and I am unwilling to do crazy gcc specific ({ void *ppp = ...; })
+ ## stuff in macros. This would obfuscate the code too much.
+ ## Maybe try __attribute__((__may_alias__))?
+-#CFLAGS += $(call cc-ifversion, -eq, 0404, -fno-strict-aliasing)
++CFLAGS += $(call cc-ifversion, -ge, 0404, -fno-strict-aliasing)
+ endif
+ # gcc 3.x emits bogus "old style proto" warning on find.c:alloc_action()
+ CFLAGS += $(call cc-ifversion, -ge, 0400, -Wold-style-definition)
diff --git a/busybox.patches/busybox-51.description b/busybox.patches/busybox-51.description
new file mode 100644
index 0000000..5469618
--- /dev/null
+++ b/busybox.patches/busybox-51.description
@@ -0,0 +1,2 @@
+This patch adds support for logging syslog time stamps in Coordinated 
+Universal Time (UTC).
diff --git a/busybox.patches/busybox-51.patch b/busybox.patches/busybox-51.patch
new file mode 100644
index 0000000..6a97363
--- /dev/null
+++ b/busybox.patches/busybox-51.patch
@@ -0,0 +1,70 @@
+diff -aruN a/sysklogd/syslogd.c c/sysklogd/syslogd.c
+--- a/sysklogd/syslogd.c	2011-10-29 04:43:01.000000000 -0700
++++ c/sysklogd/syslogd.c	2012-01-04 10:11:05.921879376 -0800
+@@ -24,6 +24,7 @@
+ //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:     "\n	-u		Log time stamps in Coordinated Universal Time (UTC)"
+ //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)"
+@@ -205,6 +206,7 @@
+ 	OPTBIT_outfile, // -O
+ 	OPTBIT_loglevel, // -l
+ 	OPTBIT_small, // -S
++	OPTBIT_utc, // -u
+ 	IF_FEATURE_ROTATE_LOGFILE(OPTBIT_filesize   ,)	// -s
+ 	IF_FEATURE_ROTATE_LOGFILE(OPTBIT_rotatecnt  ,)	// -b
+ 	IF_FEATURE_REMOTE_LOG(    OPTBIT_remotelog  ,)	// -R
+@@ -218,6 +220,7 @@
+ 	OPT_outfile     = 1 << OPTBIT_outfile ,
+ 	OPT_loglevel    = 1 << OPTBIT_loglevel,
+ 	OPT_small       = 1 << OPTBIT_small   ,
++	OPT_utc			= 1 << OPTBIT_utc	  ,
+ 	OPT_filesize    = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_filesize   )) + 0,
+ 	OPT_rotatecnt   = IF_FEATURE_ROTATE_LOGFILE((1 << OPTBIT_rotatecnt  )) + 0,
+ 	OPT_remotelog   = IF_FEATURE_REMOTE_LOG(    (1 << OPTBIT_remotelog  )) + 0,
+@@ -226,7 +229,7 @@
+ 	OPT_dup         = IF_FEATURE_SYSLOGD_DUP(   (1 << OPTBIT_dup        )) + 0,
+ 	OPT_cfg         = IF_FEATURE_SYSLOGD_CFG(   (1 << OPTBIT_cfg        )) + 0,
+ };
+-#define OPTION_STR "m:nO:l:S" \
++#define OPTION_STR "m:nO:l:Su" \
+ 	IF_FEATURE_ROTATE_LOGFILE("s:" ) \
+ 	IF_FEATURE_ROTATE_LOGFILE("b:" ) \
+ 	IF_FEATURE_REMOTE_LOG(    "R:" ) \
+@@ -649,10 +652,30 @@
+ 	 || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
+ 	) {
+ 		time(&now);
+-		timestamp = ctime(&now) + 4; /* skip day of week */
++		if (option_mask32 & OPT_utc)
++			timestamp = asctime(gmtime(&now));
++		else
++			timestamp = asctime(localtime(&now));
++		timestamp += 4; /* skip day of week */
+ 	} else {
+-		now = 0;
+-		timestamp = msg;
++		if (option_mask32 & OPT_utc) {
++			struct tm parsed, local;
++			now = time(NULL);
++			localtime_r(&now, &local);
++			if (strptime(msg, "%h %e %T", &parsed) != NULL) {
++				parsed.tm_gmtoff	= local.tm_gmtoff;
++				parsed.tm_zone		= local.tm_zone;
++				parsed.tm_year		= local.tm_year;
++				parsed.tm_isdst		= local.tm_isdst;
++				now = mktime(&parsed);
++				timestamp = asctime(gmtime(&now)) + 4;
++			} else {
++				timestamp = msg;
++			}
++		} else {
++			now = 0;
++			timestamp = msg;
++		}
+ 		msg += 16;
+ 	}
+ 	timestamp[15] = '\0';
diff --git a/busybox.patches/busybox-52.description b/busybox.patches/busybox-52.description
new file mode 100644
index 0000000..6669c67
--- /dev/null
+++ b/busybox.patches/busybox-52.description
@@ -0,0 +1,2 @@
+This patch adds support for converting and logging the message
+reported timestamp format to yyyy-mm-dd hh:mm:ss.uuuuuu.
diff --git a/busybox.patches/busybox-52.patch b/busybox.patches/busybox-52.patch
new file mode 100644
index 0000000..d12177b
--- /dev/null
+++ b/busybox.patches/busybox-52.patch
@@ -0,0 +1,121 @@
+diff -aruN a/sysklogd/syslogd.c b/sysklogd/syslogd.c
+--- a/sysklogd/syslogd.c	2012-01-04 10:13:07.989873065 -0800
++++ b/sysklogd/syslogd.c	2012-01-04 10:08:05.000000000 -0800
+@@ -165,10 +165,10 @@
+ 	/* ...then copy to parsebuf, escaping control chars */
+ 	/* (can grow x2 max) */
+ 	char parsebuf[MAX_READ*2];
+-	/* ...then sprintf into printbuf, adding timestamp (15 chars),
++	/* ...then sprintf into printbuf, adding timestamp (26 chars),
+ 	 * host (64), fac.prio (20) to the message */
+-	/* (growth by: 15 + 64 + 20 + delims = ~110) */
+-	char printbuf[MAX_READ*2 + 128];
++	/* (growth by: 26 + 64 + 20 + delims = ~121) */
++	char printbuf[MAX_READ*2 + 139];
+ };
+ 
+ static const struct init_globals init_data = {
+@@ -638,47 +638,62 @@
+ 	snprintf(res20, 20, "<%d>", pri);
+ }
+ 
++static struct tm * generate_time(struct timeval *tvp, struct tm *tmp)
++{
++	struct tm *tm;
++
++	gettimeofday(tvp, NULL);
++
++	if (option_mask32 & OPT_utc) {
++		tm = gmtime_r(&tvp->tv_sec, tmp);
++	} else {
++		tm = localtime_r(&tvp->tv_sec, tmp);
++	}
++
++	return tm;
++}
++
+ /* len parameter is used only for "is there a timestamp?" check.
+  * NB: some callers cheat and supply len==0 when they know
+  * that there is no timestamp, short-circuiting the test. */
+ static void timestamp_and_log(int pri, char *msg, int len)
+ {
+-	char *timestamp;
+-	time_t now;
++	char timestamp[27];
++	struct timeval tvnow;
++	struct tm parsed, tmnow, *tmp;
++	size_t n;
+ 
+ 	/* Jan 18 00:11:22 msg... */
+ 	/* 01234567890123456 */
+ 	if (len < 16 || msg[3] != ' ' || msg[6] != ' '
+ 	 || msg[9] != ':' || msg[12] != ':' || msg[15] != ' '
+ 	) {
+-		time(&now);
+-		if (option_mask32 & OPT_utc)
+-			timestamp = asctime(gmtime(&now));
+-		else
+-			timestamp = asctime(localtime(&now));
+-		timestamp += 4; /* skip day of week */
++		tmp = generate_time(&tvnow, &tmnow);
+ 	} else {
+-		if (option_mask32 & OPT_utc) {
+-			struct tm parsed, local;
+-			now = time(NULL);
+-			localtime_r(&now, &local);
+-			if (strptime(msg, "%h %e %T", &parsed) != NULL) {
+-				parsed.tm_gmtoff	= local.tm_gmtoff;
+-				parsed.tm_zone		= local.tm_zone;
+-				parsed.tm_year		= local.tm_year;
+-				parsed.tm_isdst		= local.tm_isdst;
+-				now = mktime(&parsed);
+-				timestamp = asctime(gmtime(&now)) + 4;
++		struct tm local;
++		tvnow.tv_sec = time(NULL);
++		localtime_r(&tvnow.tv_sec, &local);
++		if (strptime(msg, "%h %e %T", &parsed) != NULL) {
++
++			parsed.tm_gmtoff	= local.tm_gmtoff;
++			parsed.tm_zone		= local.tm_zone;
++			parsed.tm_year		= local.tm_year;
++			parsed.tm_isdst		= local.tm_isdst;
++			tvnow.tv_sec = mktime(&parsed);
++			tvnow.tv_usec = 0;
++
++			if (option_mask32 & OPT_utc) {
++				tmp = gmtime_r(&tvnow.tv_sec, &tmnow);
+ 			} else {
+-				timestamp = msg;
++				tmp = &parsed;
+ 			}
+ 		} else {
+-			now = 0;
+-			timestamp = msg;
++			tmp  = generate_time(&tvnow, &tmnow);
+ 		}
+ 		msg += 16;
+ 	}
+-	timestamp[15] = '\0';
++	n = strftime(timestamp, sizeof(timestamp), "%F %T", tmp);
++	snprintf(timestamp + n, sizeof(timestamp) - n, ".%06ld", tvnow.tv_usec);
+ 
+ 	if (option_mask32 & OPT_small)
+ 		sprintf(G.printbuf, "%s %s\n", timestamp, msg);
+@@ -698,7 +713,7 @@
+ 
+ 		for (rule = G.log_rules; rule; rule = rule->next) {
+ 			if (rule->enabled_facility_priomap[facility] & prio_bit) {
+-				log_locally(now, G.printbuf, rule->file);
++				log_locally(tvnow.tv_sec, G.printbuf, rule->file);
+ 				match = 1;
+ 			}
+ 		}
+@@ -713,7 +728,7 @@
+ 			return;
+ 		}
+ #endif
+-		log_locally(now, G.printbuf, &G.logFile);
++		log_locally(tvnow.tv_sec, G.printbuf, &G.logFile);
+ 	}
+ }
+ 
diff --git a/busybox.patches/busybox-53.description b/busybox.patches/busybox-53.description
new file mode 100644
index 0000000..1a12725
--- /dev/null
+++ b/busybox.patches/busybox-53.description
@@ -0,0 +1 @@
+This patch returns busybox to 1.17-era behavior in which default configurations could be specified anywhere, not simply relative to the in-package configs directory.
diff --git a/busybox.patches/busybox-53.patch b/busybox.patches/busybox-53.patch
new file mode 100644
index 0000000..309517a
--- /dev/null
+++ b/busybox.patches/busybox-53.patch
@@ -0,0 +1,12 @@
+diff -aruN a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
+--- a/scripts/kconfig/Makefile	2011-09-05 19:35:17.000000000 -0700
++++ b/scripts/kconfig/Makefile	2012-01-04 10:28:30.000000000 -0800
+@@ -87,7 +87,7 @@
+ 	$(MTIME_IS_COARSE) && sleep 1
+ 
+ %_defconfig: $(obj)/conf
+-	$(Q)$< -D configs/$@ Config.in
++	$(Q)$< -D $@ Config.in
+ 	$(MTIME_IS_COARSE) && sleep 1
+ 
+ # Help text used by make help
diff --git a/busybox.patches/busybox-54.description b/busybox.patches/busybox-54.description
new file mode 100644
index 0000000..1f7628f
--- /dev/null
+++ b/busybox.patches/busybox-54.description
@@ -0,0 +1 @@
+This patch fixes a memory leak in the rdev applet
diff --git a/busybox.patches/busybox-54.patch b/busybox.patches/busybox-54.patch
new file mode 100644
index 0000000..bb80a35
--- /dev/null
+++ b/busybox.patches/busybox-54.patch
@@ -0,0 +1,16 @@
+diff -Naur a/util-linux/rdev.c b/util-linux/rdev.c
+--- a/util-linux/rdev.c	2011-09-05 19:35:17.000000000 -0700
++++ b/util-linux/rdev.c	2014-10-24 11:41:55.922803554 -0700
+@@ -23,10 +23,11 @@
+ 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("/");
++	char * const root_device = find_block_device("/");
+ 
+ 	if (root_device != NULL) {
+ 		printf("%s /\n", root_device);
++		free(root_device);
+ 		return EXIT_SUCCESS;
+ 	}
+ 	return EXIT_FAILURE;
diff --git a/busybox.patches/busybox-55.description b/busybox.patches/busybox-55.description
new file mode 100644
index 0000000..b858eba
--- /dev/null
+++ b/busybox.patches/busybox-55.description
@@ -0,0 +1 @@
+This patch fixes the rdev applet for cases where the device associated with the mounted filesystem isn't a /dev device node (i.e: UBIFS, tmpfs, network drive mounts)
diff --git a/busybox.patches/busybox-55.patch b/busybox.patches/busybox-55.patch
new file mode 100644
index 0000000..03067f6
--- /dev/null
+++ b/busybox.patches/busybox-55.patch
@@ -0,0 +1,79 @@
+diff -Naur a/libbb/find_root_device.c b/libbb/find_root_device.c
+--- a/libbb/find_root_device.c	2014-10-27 13:01:18.984487421 -0700
++++ b/libbb/find_root_device.c	2014-10-27 13:36:07.469359713 -0700
+@@ -9,6 +9,15 @@
+ 
+ #include "libbb.h"
+ 
++/* #define DEBUGGING 1 */
++
++#ifdef DEBUGGING
++#define debug(...) do { printf(__VA_ARGS__); } while (0)
++#else
++#define debug(...) ((void)0)
++#endif
++
++
+ /* Find block device /dev/XXX which contains specified file
+  * We handle /dev/dir/dir/dir too, at a cost of ~80 more bytes code */
+ 
+@@ -63,6 +72,46 @@
+ 	return retpath;
+ }
+ 
++#define PROC_MOUNTINFO  "/proc/self/mountinfo"
++/* A major of zero indicates a non-device mount. Use the kernel's show_mountinfo(),
++ * exposed through /proc/self/mountinfo to lookup the device reference. */
++static char *find_device_in_mountinfo(struct arena *ap)
++{
++	char line[1024];
++	char *linePtr;
++	char *retpath = NULL;
++
++	FILE *fp = fopen_for_read(PROC_MOUNTINFO);
++	if (!fp)
++		return NULL;
++
++	debug("Looking for device %u:%u\n", major(ap->dev), minor(ap->dev));
++	while (fgets(line, sizeof(line), fp)) {
++			int mnt_id, parent_mnt_id;
++			unsigned int major, minor;
++			char mnt_typename[1024], mnt_devname[1024];
++
++			linePtr = line;
++			if (sscanf(linePtr, "%i %i %u:%u", &mnt_id, &parent_mnt_id, &major, &minor) != 4) {
++				debug("Couldn't parse line: '%s'\n", line);
++			} else if ((linePtr = strstr(linePtr, " - ")) == NULL) {
++				debug("Couldn't find ' - ': '%s'\n", line);
++			} else if (sscanf(linePtr, " - %s %s ", mnt_typename, mnt_devname) != 2) {
++				debug("Couldn't parse line: '%s'\n", line);
++			} else if ((major(ap->dev) != major) || (minor(ap->dev) != minor)) {
++				debug("Non-matching device %u:%u --> %s\n", major, minor, mnt_devname);
++			} else {
++				debug("Found a match %u:%u --> %s\n", major, minor, mnt_devname);
++				retpath = xstrdup(mnt_devname);
++				break;
++			}
++	}
++
++	fclose(fp);
++
++	return retpath;
++}
++
+ char* FAST_FUNC find_block_device(const char *path)
+ {
+ 	struct arena a;
+@@ -70,6 +119,10 @@
+ 	if (stat(path, &a.st) != 0)
+ 		return NULL;
+ 	a.dev = S_ISBLK(a.st.st_mode) ? a.st.st_rdev : a.st.st_dev;
+-	strcpy(a.devpath, "/dev");
+-	return find_block_device_in_dir(&a);
++	if (major(a.dev) != 0) {
++		strcpy(a.devpath, "/dev");
++		return find_block_device_in_dir(&a);
++	} else {
++		return find_device_in_mountinfo(&a);
++	}
+ }
diff --git a/busybox.tar.bz2 b/busybox.tar.bz2
new file mode 100644
index 0000000..34a1a92
--- /dev/null
+++ b/busybox.tar.bz2
Binary files differ
diff --git a/busybox.url b/busybox.url
new file mode 100644
index 0000000..194e9ca
--- /dev/null
+++ b/busybox.url
@@ -0,0 +1 @@
+http://busybox.net/downloads/busybox-1.19.3.tar.bz2
diff --git a/busybox.version b/busybox.version
new file mode 100644
index 0000000..1b92e58
--- /dev/null
+++ b/busybox.version
@@ -0,0 +1 @@
+1.19.3
diff --git a/configs/compliance_defconfig b/configs/compliance_defconfig
new file mode 100644
index 0000000..64fa814
--- /dev/null
+++ b/configs/compliance_defconfig
@@ -0,0 +1,1009 @@
+#
+# BusyBox Configuration for a Compliance Build Configuration.
+# Matches the Development Configuration.
+# Busybox version: 1.19.3
+#
+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_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 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 is not set
+CONFIG_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=767
+# 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_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=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=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 is not set
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_FEATURE_FAST_TOP=y
+CONFIG_FEATURE_ETC_NETWORKS=y
+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=16
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_REVERSE_SEARCH is not set
+CONFIG_FEATURE_TAB_COMPLETION=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 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=y
+CONFIG_IOCTL_HEX2STR_ERROR=y
+# 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 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 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=y
+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_TAR=y
+CONFIG_FEATURE_TAR_CREATE=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_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+# 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=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=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+# CONFIG_FEATURE_DATE_COMPAT is not set
+CONFIG_ID=y
+# CONFIG_GROUPS is not set
+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 is not set
+CONFIG_WHO=y
+# CONFIG_USERS is not set
+# CONFIG_CAL is not set
+# CONFIG_CATV is not set
+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 is not set
+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=y
+CONFIG_FEATURE_DF_FANCY=y
+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 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 is not set
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=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 is not set
+CONFIG_NOHUP=y
+# CONFIG_OD is not set
+# CONFIG_PRINTENV is not set
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+# CONFIG_REALPATH is not set
+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 is not set
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+# CONFIG_FEATURE_FLOAT_SLEEP is not set
+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_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+# CONFIG_TAC is not set
+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 is not set
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+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 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=y
+CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+# CONFIG_DEFAULT_SETFONT_DIR is not set
+# CONFIG_SETKEYCODES 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
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+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_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 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_FEATURE_VI_OPTIMIZE_CURSOR=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 is not set
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+# CONFIG_FEATURE_FIND_LINKS 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=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=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
+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 is not set
+# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+CONFIG_FEATURE_SHADOWPASSWDS=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_SHA=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 is not set
+# CONFIG_LAST_SYSTEM_ID 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_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+CONFIG_GETTY=y
+CONFIG_LOGIN=y
+# CONFIG_PAM is not set
+# CONFIG_LOGIN_SCRIPTS is not set
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+# CONFIG_CRYPTPW is not set
+CONFIG_CHPASSWD=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+# 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=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
+
+#
+# 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=y
+CONFIG_FEATURE_MODUTILS_SYMBOLS=y
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+
+#
+# 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=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 is not set
+# CONFIG_FDFORMAT is not set
+CONFIG_FDISK=y
+# CONFIG_FDISK_SUPPORT_LARGE_DISKS 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_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+# 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=y
+# CONFIG_GETOPT is not set
+# CONFIG_FEATURE_GETOPT_LONG is not set
+CONFIG_HEXDUMP=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 is not set
+# CONFIG_IPCS is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_LSPCI is not set
+CONFIG_LSUSB=y
+# 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=y
+CONFIG_MOUNT=y
+# 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=y
+CONFIG_FEATURE_MOUNT_CIFS=y
+CONFIG_FEATURE_MOUNT_FLAGS=y
+CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_PIVOT_ROOT=y
+# 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 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 is not set
+# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+CONFIG_FEATURE_VOLUMEID_FAT=y
+# 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 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=y
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+# CONFIG_BEEP is not set
+# CONFIG_FEATURE_BEEP_FREQ is not set
+# CONFIG_FEATURE_BEEP_LENGTH_MS 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=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=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=y
+# CONFIG_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
+CONFIG_FBSPLASH=y
+# 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_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=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+# CONFIG_MAN is not set
+CONFIG_MICROCOM=y
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_MT is not set
+# CONFIG_RAIDAUTORUN is not set
+CONFIG_READAHEAD=y
+# CONFIG_RFKILL is not set
+# CONFIG_RUNLEVEL is not set
+# CONFIG_RX is not set
+CONFIG_SETSID=y
+# CONFIG_STRINGS is not set
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+# CONFIG_TIMEOUT is not set
+CONFIG_TTYSIZE=y
+# CONFIG_VOLNAME is not set
+# 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=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+# CONFIG_WHOIS is not set
+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=y
+CONFIG_ARPING=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_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_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_FEATURE_HTTPD_GZIP 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 is not set
+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 is not set
+# 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_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+CONFIG_NSLOOKUP=y
+# CONFIG_NTPD is not set
+# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_PSCAN=y
+CONFIG_ROUTE=y
+CONFIG_SLATTACH=y
+# 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=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 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 is not set
+# CONFIG_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCP_PORT is not set
+# CONFIG_UDHCP_DEBUG is not set
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+# CONFIG_FEATURE_UDHCP_8021Q is not set
+# CONFIG_UDHCPC_DEFAULT_SCRIPT is not set
+# CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS is not set
+# CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS is not set
+# 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_FEATURE_WGET_TIMEOUT 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 is not set
+# 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_IOSTAT is not set
+# CONFIG_MPSTAT is not set
+# CONFIG_NMETER is not set
+# CONFIG_PMAP is not set
+# CONFIG_POWERTOP is not set
+# CONFIG_PSTREE is not set
+# CONFIG_PWDX is not set
+# CONFIG_SMEMCAP is not set
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=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=y
+CONFIG_BB_SYSCTL=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
+# 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_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 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
+
+#
+# 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 is not set
+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 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
+# CONFIG_HUSH_LOOPS is not set
+# CONFIG_HUSH_CASE is not set
+CONFIG_HUSH_FUNCTIONS=y
+# 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=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 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_KLOGD=y
+CONFIG_FEATURE_KLOGD_KLOGCTL=y
+CONFIG_LOGGER=y
diff --git a/configs/debug_defconfig b/configs/debug_defconfig
new file mode 100644
index 0000000..0c72f1f
--- /dev/null
+++ b/configs/debug_defconfig
@@ -0,0 +1,1008 @@
+#
+# BusyBox Configuration for a Debug Build Configuration
+# Busybox version: 1.19.3
+#
+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_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 is not set
+CONFIG_FEATURE_COMPRESS_USAGE=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 is not set
+CONFIG_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=767
+# 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_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=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=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 is not set
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_FEATURE_FAST_TOP=y
+CONFIG_FEATURE_ETC_NETWORKS=y
+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=16
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_REVERSE_SEARCH is not set
+CONFIG_FEATURE_TAB_COMPLETION=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 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=y
+CONFIG_IOCTL_HEX2STR_ERROR=y
+# 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 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 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=y
+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_TAR=y
+CONFIG_FEATURE_TAR_CREATE=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_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+# 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=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=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+# CONFIG_FEATURE_DATE_COMPAT is not set
+CONFIG_ID=y
+# CONFIG_GROUPS is not set
+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 is not set
+CONFIG_WHO=y
+# CONFIG_USERS is not set
+# CONFIG_CAL is not set
+# CONFIG_CATV is not set
+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 is not set
+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=y
+CONFIG_FEATURE_DF_FANCY=y
+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 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 is not set
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=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 is not set
+CONFIG_NOHUP=y
+# CONFIG_OD is not set
+# CONFIG_PRINTENV is not set
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+# CONFIG_REALPATH is not set
+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 is not set
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+# CONFIG_FEATURE_FLOAT_SLEEP is not set
+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_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+# CONFIG_TAC is not set
+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 is not set
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+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 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=y
+CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+# CONFIG_DEFAULT_SETFONT_DIR is not set
+# CONFIG_SETKEYCODES 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
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+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_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 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_FEATURE_VI_OPTIMIZE_CURSOR=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 is not set
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+# CONFIG_FEATURE_FIND_LINKS 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=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=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
+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 is not set
+# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+CONFIG_FEATURE_SHADOWPASSWDS=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_SHA=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 is not set
+# CONFIG_LAST_SYSTEM_ID 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_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+CONFIG_GETTY=y
+CONFIG_LOGIN=y
+# CONFIG_PAM is not set
+# CONFIG_LOGIN_SCRIPTS is not set
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+# CONFIG_CRYPTPW is not set
+CONFIG_CHPASSWD=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+# 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=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
+
+#
+# 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=y
+CONFIG_FEATURE_MODUTILS_SYMBOLS=y
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+
+#
+# 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=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 is not set
+# CONFIG_FDFORMAT is not set
+CONFIG_FDISK=y
+# CONFIG_FDISK_SUPPORT_LARGE_DISKS 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_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+# 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=y
+# CONFIG_GETOPT is not set
+# CONFIG_FEATURE_GETOPT_LONG is not set
+CONFIG_HEXDUMP=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 is not set
+# CONFIG_IPCS is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_LSPCI is not set
+CONFIG_LSUSB=y
+# 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=y
+CONFIG_MOUNT=y
+# 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=y
+CONFIG_FEATURE_MOUNT_CIFS=y
+CONFIG_FEATURE_MOUNT_FLAGS=y
+CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_PIVOT_ROOT=y
+# 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 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 is not set
+# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+CONFIG_FEATURE_VOLUMEID_FAT=y
+# 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 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=y
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+# CONFIG_BEEP is not set
+# CONFIG_FEATURE_BEEP_FREQ is not set
+# CONFIG_FEATURE_BEEP_LENGTH_MS 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=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=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_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
+CONFIG_FBSPLASH=y
+# 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_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=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+# CONFIG_MAN is not set
+CONFIG_MICROCOM=y
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_MT is not set
+# CONFIG_RAIDAUTORUN is not set
+CONFIG_READAHEAD=y
+# CONFIG_RFKILL is not set
+# CONFIG_RUNLEVEL is not set
+# CONFIG_RX is not set
+CONFIG_SETSID=y
+# CONFIG_STRINGS is not set
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+# CONFIG_TIMEOUT is not set
+CONFIG_TTYSIZE=y
+# CONFIG_VOLNAME is not set
+# 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=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+# CONFIG_WHOIS is not set
+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=y
+CONFIG_ARPING=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_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_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_FEATURE_HTTPD_GZIP 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 is not set
+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 is not set
+# 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_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+CONFIG_NSLOOKUP=y
+# CONFIG_NTPD is not set
+# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_PSCAN=y
+CONFIG_ROUTE=y
+CONFIG_SLATTACH=y
+# 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=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 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 is not set
+# CONFIG_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCP_PORT is not set
+# CONFIG_UDHCP_DEBUG is not set
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+# CONFIG_FEATURE_UDHCP_8021Q is not set
+# CONFIG_UDHCPC_DEFAULT_SCRIPT is not set
+# CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS is not set
+# CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS is not set
+# 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_FEATURE_WGET_TIMEOUT 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 is not set
+# 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_IOSTAT is not set
+# CONFIG_MPSTAT is not set
+# CONFIG_NMETER is not set
+# CONFIG_PMAP is not set
+# CONFIG_POWERTOP is not set
+# CONFIG_PSTREE is not set
+# CONFIG_PWDX is not set
+# CONFIG_SMEMCAP is not set
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=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=y
+CONFIG_BB_SYSCTL=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
+# 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_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 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
+
+#
+# Shells
+#
+CONFIG_ASH=y
+CONFIG_ASH_BASH_COMPAT=y
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+CONFIG_ASH_JOB_CONTROL=y
+# CONFIG_ASH_ALIAS is not set
+# CONFIG_ASH_GETOPTS is not set
+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 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
+# 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=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 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_KLOGD=y
+CONFIG_FEATURE_KLOGD_KLOGCTL=y
+CONFIG_LOGGER=y
diff --git a/configs/development_defconfig b/configs/development_defconfig
new file mode 100644
index 0000000..db305bf
--- /dev/null
+++ b/configs/development_defconfig
@@ -0,0 +1,1008 @@
+#
+# BusyBox Configuration for a Development Build Configuration
+# Busybox version: 1.19.3
+#
+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_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 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 is not set
+CONFIG_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=767
+# 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_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=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=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 is not set
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_FEATURE_FAST_TOP=y
+CONFIG_FEATURE_ETC_NETWORKS=y
+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=16
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_REVERSE_SEARCH is not set
+CONFIG_FEATURE_TAB_COMPLETION=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 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=y
+CONFIG_IOCTL_HEX2STR_ERROR=y
+# 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 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 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=y
+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_TAR=y
+CONFIG_FEATURE_TAR_CREATE=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_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+# 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=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=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+# CONFIG_FEATURE_DATE_COMPAT is not set
+CONFIG_ID=y
+# CONFIG_GROUPS is not set
+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 is not set
+CONFIG_WHO=y
+# CONFIG_USERS is not set
+# CONFIG_CAL is not set
+# CONFIG_CATV is not set
+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 is not set
+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=y
+CONFIG_FEATURE_DF_FANCY=y
+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 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 is not set
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=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 is not set
+CONFIG_NOHUP=y
+# CONFIG_OD is not set
+# CONFIG_PRINTENV is not set
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+# CONFIG_REALPATH is not set
+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 is not set
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+# CONFIG_FEATURE_FLOAT_SLEEP is not set
+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_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+# CONFIG_TAC is not set
+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 is not set
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+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 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=y
+CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+# CONFIG_DEFAULT_SETFONT_DIR is not set
+# CONFIG_SETKEYCODES 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
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+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_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 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_FEATURE_VI_OPTIMIZE_CURSOR=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 is not set
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+# CONFIG_FEATURE_FIND_LINKS 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=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=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
+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 is not set
+# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+CONFIG_FEATURE_SHADOWPASSWDS=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_SHA=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 is not set
+# CONFIG_LAST_SYSTEM_ID 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_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+CONFIG_GETTY=y
+CONFIG_LOGIN=y
+# CONFIG_PAM is not set
+# CONFIG_LOGIN_SCRIPTS is not set
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+# CONFIG_CRYPTPW is not set
+CONFIG_CHPASSWD=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+# 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=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
+
+#
+# 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=y
+CONFIG_FEATURE_MODUTILS_SYMBOLS=y
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+
+#
+# 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=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 is not set
+# CONFIG_FDFORMAT is not set
+CONFIG_FDISK=y
+# CONFIG_FDISK_SUPPORT_LARGE_DISKS 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_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+# 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=y
+# CONFIG_GETOPT is not set
+# CONFIG_FEATURE_GETOPT_LONG is not set
+CONFIG_HEXDUMP=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 is not set
+# CONFIG_IPCS is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_LSPCI is not set
+CONFIG_LSUSB=y
+# 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=y
+CONFIG_MOUNT=y
+# 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=y
+CONFIG_FEATURE_MOUNT_CIFS=y
+CONFIG_FEATURE_MOUNT_FLAGS=y
+CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_PIVOT_ROOT=y
+# 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 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 is not set
+# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+CONFIG_FEATURE_VOLUMEID_FAT=y
+# 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 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=y
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+# CONFIG_BEEP is not set
+# CONFIG_FEATURE_BEEP_FREQ is not set
+# CONFIG_FEATURE_BEEP_LENGTH_MS 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=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=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=y
+# CONFIG_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
+CONFIG_FBSPLASH=y
+# 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_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=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+# CONFIG_MAN is not set
+CONFIG_MICROCOM=y
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_MT is not set
+# CONFIG_RAIDAUTORUN is not set
+CONFIG_READAHEAD=y
+# CONFIG_RFKILL is not set
+# CONFIG_RUNLEVEL is not set
+# CONFIG_RX is not set
+CONFIG_SETSID=y
+# CONFIG_STRINGS is not set
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+# CONFIG_TIMEOUT is not set
+CONFIG_TTYSIZE=y
+# CONFIG_VOLNAME is not set
+# 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=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+# CONFIG_WHOIS is not set
+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=y
+CONFIG_ARPING=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_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_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_FEATURE_HTTPD_GZIP 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 is not set
+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 is not set
+# 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_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+CONFIG_NSLOOKUP=y
+# CONFIG_NTPD is not set
+# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_PSCAN=y
+CONFIG_ROUTE=y
+CONFIG_SLATTACH=y
+# 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=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 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 is not set
+# CONFIG_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCP_PORT is not set
+# CONFIG_UDHCP_DEBUG is not set
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+# CONFIG_FEATURE_UDHCP_8021Q is not set
+# CONFIG_UDHCPC_DEFAULT_SCRIPT is not set
+# CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS is not set
+# CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS is not set
+# 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_FEATURE_WGET_TIMEOUT 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 is not set
+# 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_IOSTAT is not set
+# CONFIG_MPSTAT is not set
+# CONFIG_NMETER is not set
+# CONFIG_PMAP is not set
+# CONFIG_POWERTOP is not set
+# CONFIG_PSTREE is not set
+# CONFIG_PWDX is not set
+# CONFIG_SMEMCAP is not set
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=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=y
+CONFIG_BB_SYSCTL=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
+# 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_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 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
+
+#
+# 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 is not set
+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 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=y
+# 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=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 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_KLOGD=y
+CONFIG_FEATURE_KLOGD_KLOGCTL=y
+CONFIG_LOGGER=y
diff --git a/configs/diagnostics_defconfig b/configs/diagnostics_defconfig
new file mode 100644
index 0000000..da89b7b
--- /dev/null
+++ b/configs/diagnostics_defconfig
@@ -0,0 +1,1008 @@
+#
+# BusyBox Configuration for a Diagnostics Build Configuration
+# Busybox version: 1.19.3
+#
+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_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 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 is not set
+CONFIG_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=767
+# 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_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=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=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 is not set
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+CONFIG_FEATURE_FAST_TOP=y
+CONFIG_FEATURE_ETC_NETWORKS=y
+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=16
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_REVERSE_SEARCH is not set
+CONFIG_FEATURE_TAB_COMPLETION=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 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=y
+CONFIG_IOCTL_HEX2STR_ERROR=y
+# 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 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 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=y
+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_TAR=y
+CONFIG_FEATURE_TAR_CREATE=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_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+# 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=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=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+# CONFIG_FEATURE_DATE_COMPAT is not set
+CONFIG_ID=y
+# CONFIG_GROUPS is not set
+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 is not set
+CONFIG_WHO=y
+# CONFIG_USERS is not set
+# CONFIG_CAL is not set
+# CONFIG_CATV is not set
+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 is not set
+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=y
+CONFIG_FEATURE_DF_FANCY=y
+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 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 is not set
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=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 is not set
+CONFIG_NOHUP=y
+# CONFIG_OD is not set
+# CONFIG_PRINTENV is not set
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+# CONFIG_REALPATH is not set
+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 is not set
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+# CONFIG_FEATURE_FLOAT_SLEEP is not set
+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_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+# CONFIG_TAC is not set
+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 is not set
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+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 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=y
+CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+# CONFIG_DEFAULT_SETFONT_DIR is not set
+# CONFIG_SETKEYCODES 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
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+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_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 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_FEATURE_VI_OPTIMIZE_CURSOR=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 is not set
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+# CONFIG_FEATURE_FIND_LINKS 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=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=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
+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 is not set
+# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+CONFIG_FEATURE_SHADOWPASSWDS=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_SHA=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 is not set
+# CONFIG_LAST_SYSTEM_ID 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_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+CONFIG_GETTY=y
+CONFIG_LOGIN=y
+# CONFIG_PAM is not set
+# CONFIG_LOGIN_SCRIPTS is not set
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+# CONFIG_CRYPTPW is not set
+CONFIG_CHPASSWD=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+# 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=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
+
+#
+# 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=y
+CONFIG_FEATURE_MODUTILS_SYMBOLS=y
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+
+#
+# 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=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 is not set
+# CONFIG_FDFORMAT is not set
+CONFIG_FDISK=y
+# CONFIG_FDISK_SUPPORT_LARGE_DISKS 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_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+# 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=y
+# CONFIG_GETOPT is not set
+# CONFIG_FEATURE_GETOPT_LONG is not set
+CONFIG_HEXDUMP=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 is not set
+# CONFIG_IPCS is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_LSPCI is not set
+CONFIG_LSUSB=y
+# 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=y
+CONFIG_MOUNT=y
+# 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=y
+CONFIG_FEATURE_MOUNT_CIFS=y
+CONFIG_FEATURE_MOUNT_FLAGS=y
+CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_PIVOT_ROOT=y
+# 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 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 is not set
+# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+CONFIG_FEATURE_VOLUMEID_FAT=y
+# 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 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=y
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+# CONFIG_BEEP is not set
+# CONFIG_FEATURE_BEEP_FREQ is not set
+# CONFIG_FEATURE_BEEP_LENGTH_MS 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=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=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=y
+# CONFIG_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
+CONFIG_FBSPLASH=y
+# 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_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=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+# CONFIG_MAN is not set
+CONFIG_MICROCOM=y
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_MT is not set
+# CONFIG_RAIDAUTORUN is not set
+CONFIG_READAHEAD=y
+# CONFIG_RFKILL is not set
+# CONFIG_RUNLEVEL is not set
+# CONFIG_RX is not set
+CONFIG_SETSID=y
+# CONFIG_STRINGS is not set
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+# CONFIG_TIMEOUT is not set
+CONFIG_TTYSIZE=y
+# CONFIG_VOLNAME is not set
+# 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=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+# CONFIG_WHOIS is not set
+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=y
+CONFIG_ARPING=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_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_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_FEATURE_HTTPD_GZIP 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 is not set
+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 is not set
+# 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_NETSTAT=y
+CONFIG_FEATURE_NETSTAT_WIDE=y
+CONFIG_FEATURE_NETSTAT_PRG=y
+CONFIG_NSLOOKUP=y
+# CONFIG_NTPD is not set
+# CONFIG_FEATURE_NTPD_SERVER is not set
+CONFIG_PSCAN=y
+CONFIG_ROUTE=y
+CONFIG_SLATTACH=y
+# 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=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 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 is not set
+# CONFIG_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCP_PORT is not set
+# CONFIG_UDHCP_DEBUG is not set
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+# CONFIG_FEATURE_UDHCP_8021Q is not set
+# CONFIG_UDHCPC_DEFAULT_SCRIPT is not set
+# CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS is not set
+# CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS is not set
+# 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_FEATURE_WGET_TIMEOUT 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 is not set
+# 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_IOSTAT is not set
+# CONFIG_MPSTAT is not set
+# CONFIG_NMETER is not set
+# CONFIG_PMAP is not set
+# CONFIG_POWERTOP is not set
+# CONFIG_PSTREE is not set
+# CONFIG_PWDX is not set
+# CONFIG_SMEMCAP is not set
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=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=y
+CONFIG_BB_SYSCTL=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
+# 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_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 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
+
+#
+# Shells
+#
+CONFIG_ASH=y
+CONFIG_ASH_BASH_COMPAT=y
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+CONFIG_ASH_JOB_CONTROL=y
+# 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_MAIL is not set
+CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
+CONFIG_ASH_RANDOM_SUPPORT=y
+# 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
+# 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=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 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_KLOGD=y
+CONFIG_FEATURE_KLOGD_KLOGCTL=y
+CONFIG_LOGGER=y
diff --git a/configs/release_defconfig b/configs/release_defconfig
new file mode 100644
index 0000000..bf79577
--- /dev/null
+++ b/configs/release_defconfig
@@ -0,0 +1,1008 @@
+#
+# BusyBox Configuration for a Release Build Configuration
+# Busybox version: 1.19.3
+#
+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_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 is not set
+CONFIG_FEATURE_COMPRESS_USAGE=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 is not set
+CONFIG_SUBST_WCHAR=63
+CONFIG_LAST_SUPPORTED_WCHAR=767
+# 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_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=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=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 is not set
+CONFIG_PASSWORD_MINLEN=6
+CONFIG_MD5_SIZE_VS_SPEED=2
+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=16
+# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set
+# CONFIG_FEATURE_REVERSE_SEARCH is not set
+CONFIG_FEATURE_TAB_COMPLETION=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 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=y
+CONFIG_IOCTL_HEX2STR_ERROR=y
+# 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 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 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=y
+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_TAR=y
+CONFIG_FEATURE_TAR_CREATE=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_GNU_EXTENSIONS=y
+CONFIG_FEATURE_TAR_LONG_OPTIONS=y
+# 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=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=y
+CONFIG_FEATURE_DATE_ISOFMT=y
+# CONFIG_FEATURE_DATE_NANO is not set
+# CONFIG_FEATURE_DATE_COMPAT is not set
+CONFIG_ID=y
+# CONFIG_GROUPS is not set
+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 is not set
+CONFIG_WHO=y
+# CONFIG_USERS is not set
+# CONFIG_CAL is not set
+# CONFIG_CATV is not set
+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 is not set
+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=y
+CONFIG_FEATURE_DF_FANCY=y
+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 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 is not set
+CONFIG_FSYNC=y
+CONFIG_HEAD=y
+CONFIG_FEATURE_FANCY_HEAD=y
+CONFIG_HOSTID=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 is not set
+CONFIG_NOHUP=y
+# CONFIG_OD is not set
+# CONFIG_PRINTENV is not set
+CONFIG_PRINTF=y
+CONFIG_PWD=y
+CONFIG_READLINK=y
+CONFIG_FEATURE_READLINK_FOLLOW=y
+# CONFIG_REALPATH is not set
+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 is not set
+CONFIG_SLEEP=y
+CONFIG_FEATURE_FANCY_SLEEP=y
+# CONFIG_FEATURE_FLOAT_SLEEP is not set
+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_STTY=y
+CONFIG_SUM=y
+CONFIG_SYNC=y
+# CONFIG_TAC is not set
+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 is not set
+# CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS is not set
+CONFIG_UNIQ=y
+CONFIG_USLEEP=y
+# CONFIG_UUDECODE is not set
+# CONFIG_UUENCODE is not set
+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 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=y
+CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS=y
+# CONFIG_SETFONT is not set
+# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
+# CONFIG_DEFAULT_SETFONT_DIR is not set
+# CONFIG_SETKEYCODES 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
+
+#
+# Debian Utilities
+#
+CONFIG_MKTEMP=y
+# CONFIG_PIPE_PROGRESS is not set
+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_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 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_FEATURE_VI_OPTIMIZE_CURSOR=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 is not set
+CONFIG_FEATURE_FIND_PATH=y
+CONFIG_FEATURE_FIND_REGEX=y
+# CONFIG_FEATURE_FIND_CONTEXT is not set
+# CONFIG_FEATURE_FIND_LINKS 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=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=y
+# CONFIG_FEATURE_CALL_TELINIT is not set
+CONFIG_TELINIT_PATH=""
+CONFIG_INIT=y
+CONFIG_FEATURE_USE_INITTAB=y
+CONFIG_FEATURE_KILL_REMOVED=y
+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 is not set
+# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
+
+#
+# Login/Password Management Utilities
+#
+# CONFIG_ADD_SHELL is not set
+# CONFIG_REMOVE_SHELL is not set
+CONFIG_FEATURE_SHADOWPASSWDS=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_SHA=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 is not set
+# CONFIG_LAST_SYSTEM_ID 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_DELUSER is not set
+# CONFIG_DELGROUP is not set
+# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
+CONFIG_GETTY=y
+CONFIG_LOGIN=y
+# CONFIG_PAM is not set
+# CONFIG_LOGIN_SCRIPTS is not set
+CONFIG_FEATURE_NOLOGIN=y
+CONFIG_FEATURE_SECURETTY=y
+CONFIG_PASSWD=y
+CONFIG_FEATURE_PASSWD_WEAK_CHECK=y
+# CONFIG_CRYPTPW is not set
+CONFIG_CHPASSWD=y
+CONFIG_SU=y
+CONFIG_FEATURE_SU_SYSLOG=y
+CONFIG_FEATURE_SU_CHECKS_SHELLS=y
+CONFIG_SULOGIN=y
+# 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=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
+
+#
+# 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=y
+CONFIG_FEATURE_MODUTILS_SYMBOLS=y
+CONFIG_DEFAULT_MODULES_DIR="/lib/modules"
+CONFIG_DEFAULT_DEPMOD_FILE="modules.dep"
+
+#
+# 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=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 is not set
+# CONFIG_FDFORMAT is not set
+CONFIG_FDISK=y
+# CONFIG_FDISK_SUPPORT_LARGE_DISKS 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_GPT_LABEL is not set
+CONFIG_FEATURE_FDISK_ADVANCED=y
+# 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=y
+# CONFIG_GETOPT is not set
+# CONFIG_FEATURE_GETOPT_LONG is not set
+CONFIG_HEXDUMP=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 is not set
+# CONFIG_IPCS is not set
+# CONFIG_LOSETUP is not set
+# CONFIG_LSPCI is not set
+CONFIG_LSUSB=y
+# 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=y
+CONFIG_MOUNT=y
+# 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=y
+CONFIG_FEATURE_MOUNT_CIFS=y
+CONFIG_FEATURE_MOUNT_FLAGS=y
+CONFIG_FEATURE_MOUNT_FSTAB=y
+CONFIG_PIVOT_ROOT=y
+# 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 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 is not set
+# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
+# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
+CONFIG_FEATURE_VOLUMEID_FAT=y
+# 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 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=y
+# CONFIG_BBCONFIG is not set
+# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
+# CONFIG_BEEP is not set
+# CONFIG_FEATURE_BEEP_FREQ is not set
+# CONFIG_FEATURE_BEEP_LENGTH_MS 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=y
+CONFIG_CROND=y
+CONFIG_FEATURE_CROND_D=y
+# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
+CONFIG_FEATURE_CROND_DIR="/var/spool/cron"
+CONFIG_CRONTAB=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_EJECT is not set
+# CONFIG_FEATURE_EJECT_SCSI is not set
+CONFIG_FBSPLASH=y
+# 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_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=y
+# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
+CONFIG_FEATURE_MAKEDEVS_TABLE=y
+# CONFIG_MAN is not set
+CONFIG_MICROCOM=y
+# CONFIG_MOUNTPOINT is not set
+# CONFIG_MT is not set
+# CONFIG_RAIDAUTORUN is not set
+CONFIG_READAHEAD=y
+# CONFIG_RFKILL is not set
+# CONFIG_RUNLEVEL is not set
+# CONFIG_RX is not set
+CONFIG_SETSID=y
+# CONFIG_STRINGS is not set
+# CONFIG_TASKSET is not set
+# CONFIG_FEATURE_TASKSET_FANCY is not set
+CONFIG_TIME=y
+# CONFIG_TIMEOUT is not set
+CONFIG_TTYSIZE=y
+# CONFIG_VOLNAME is not set
+# 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=y
+CONFIG_PING6=y
+CONFIG_FEATURE_FANCY_PING=y
+# CONFIG_WHOIS is not set
+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=y
+CONFIG_ARPING=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_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_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_FEATURE_HTTPD_GZIP 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 is not set
+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 is not set
+# 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_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=y
+# 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=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 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 is not set
+# CONFIG_UDHCPC is not set
+# CONFIG_FEATURE_UDHCPC_ARPING is not set
+# CONFIG_FEATURE_UDHCP_PORT is not set
+# CONFIG_UDHCP_DEBUG is not set
+# CONFIG_FEATURE_UDHCP_RFC3397 is not set
+# CONFIG_FEATURE_UDHCP_8021Q is not set
+# CONFIG_UDHCPC_DEFAULT_SCRIPT is not set
+# CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS is not set
+# CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS is not set
+# 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_FEATURE_WGET_TIMEOUT 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 is not set
+# 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_IOSTAT is not set
+# CONFIG_MPSTAT is not set
+# CONFIG_NMETER is not set
+# CONFIG_PMAP is not set
+# CONFIG_POWERTOP is not set
+# CONFIG_PSTREE is not set
+# CONFIG_PWDX is not set
+# CONFIG_SMEMCAP is not set
+CONFIG_UPTIME=y
+# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
+CONFIG_FREE=y
+CONFIG_FUSER=y
+CONFIG_KILL=y
+CONFIG_KILLALL=y
+CONFIG_KILLALL5=y
+# CONFIG_PGREP is not set
+CONFIG_PIDOF=y
+CONFIG_FEATURE_PIDOF_SINGLE=y
+CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=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=y
+CONFIG_BB_SYSCTL=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
+# 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_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 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
+
+#
+# Shells
+#
+CONFIG_ASH=y
+CONFIG_ASH_BASH_COMPAT=y
+# CONFIG_ASH_IDLE_TIMEOUT is not set
+CONFIG_ASH_JOB_CONTROL=y
+# CONFIG_ASH_ALIAS is not set
+# CONFIG_ASH_GETOPTS is not set
+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 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
+# 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=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 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_KLOGD=y
+CONFIG_FEATURE_KLOGD_KLOGCTL=y
+CONFIG_LOGGER=y